/*
* $Id: ModuleEnvelope.java,v 1.2 2002/09/16 08:05:03 jkl Exp $
*
* Copyright (c) 2002 Njet Communications Ltd. All Rights Reserved.
*
* Use is subject to license terms, as defined in
* Anvil Sofware License, Version 1.1. See LICENSE
* file, or http://njet.org/license-1.1.txt
*/
package anvil.script;
import anvil.ErrorListener;
import anvil.ForgingException;
import anvil.parser.InputSource;
import anvil.server.Address;
import anvil.server.CompilerPreferences;
import anvil.server.LocalizationPreferences;
import anvil.server.Resource;
import anvil.server.Loader;
import anvil.server.ZoneClassLoader;
import anvil.script.compiler.CompiledModule;
import anvil.script.compiler.ByteCompiler;
import anvil.script.parser.ScriptParser;
import anvil.script.parser.TemplateParser;
import anvil.script.statements.ModuleStatement;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
/**
* class ModuleEnvelope
*
* @author: Jani Lehtim�ki
*/
public class ModuleEnvelope
{
protected ModuleCache _cache;
protected Address _address;
protected long _lastModified;
protected String _descriptor;
protected ModuleStatement _script1;
protected CompiledModule _script2;
protected int _version = 0;
protected boolean _initialized = false;
public ModuleEnvelope(ModuleCache cache, Address address, InputSource source) throws IOException, ForgingException
{
this(cache, address, source, 0, false);
}
protected ModuleEnvelope(ModuleCache cache, Address address, InputSource source, int version, boolean forceParsing)
throws IOException, ForgingException
{
_cache = cache;
_address = address;
_version = version;
CompilerPreferences prefs = address.getZone().getCompilerPreferences();
String path = anvil.Version.getBuild() + address.getPathinfo();
String postfix = (version!=0) ? "/v_"+version : "";
String descriptor = mangle(path);
boolean usetimestamp = prefs.getUseTimestamp();
if (!(usetimestamp && forceParsing)) {
_lastModified = -1;
_descriptor = descriptor + postfix;
if (!forceParsing) {
if (loadClass()) {
return;
}
}
}
if (usetimestamp) {
_lastModified = source.getLastModified();
_descriptor = addTimestamp(descriptor, _lastModified) + postfix;
if (!forceParsing) {
if (loadClass()) {
return;
}
}
}
parse(source);
}
public long getLastModified()
{
return _lastModified;
}
public Address getAddress()
{
return _address;
}
public String getClassname()
{
return _descriptor.replace('/', '.');
}
public String getDescriptor()
{
return _descriptor;
}
public int getVersion()
{
return _version;
}
public Module getModule()
{
Module script = _script2;
if (script != null) {
return script;
}
return _script1;
}
public boolean isCompiled()
{
return _script2 != null;
}
public void parse(InputSource source) throws IOException, ForgingException
{
URL url = source.getURL();
int length = source.getLength();
int pos = 0;
byte[] content = new byte[(int)length];
InputStream input = null;
try {
input = source.getInputStream();
while(pos < length) {
int read = input.read(content, pos, length - pos);
if (read == 0) {
throw new IOException("Unexcepted end of " + url + " at position "+pos);
}
pos += read;
}
} finally {
try {
input.close();
} catch (Throwable t) {
}
try {
source.close();
} catch (Throwable t) {
}
}
if (pos < length) {
java.util.Arrays.fill(content, pos, length, (byte)' ');
}
boolean is_template = false;
pos = 0;
out:
while(pos < length) {
switch(content[pos++]) {
case (byte)'<':
is_template = true;
break out;
case (byte)' ':
case (byte)'\t':
case (byte)'\r':
case (byte)'\n':
break;
default:
break out;
}
}
if (pos == length) {
throw new IOException("File is empty: " + url);
}
if (is_template) {
TemplateParser parser = new TemplateParser(this,
_address.getZone().getNamespace(), url, content);
_script1 = parser.parseTemplate();
} else {
ScriptParser parser = new ScriptParser(this, url, content);
_script1 = parser.parseScript();
}
_address.getZone().log().info("parsed: " + _address);
}
public ModuleEnvelope forge() throws IOException, ForgingException
{
Resource resource = _address.openResource();
return new ModuleEnvelope(_cache, _address, resource, _version+1, true);
}
public void importExternals(ErrorListener listener)
{
if (_script1 != null) {
_script1.importExternals(listener);
}
}
public void resolve(Resolver resolver)
{
if (_script1 != null) {
resolver.resolveScope(_script1);
}
}
public void verifyPassOne(Verifier verifier)
{
if (_script1 != null) {
verifier.verifyScope(_script1);
}
}
public void verifyPassTwo(Verifier verifier)
{
if (_script1 != null) {
verifier.checkScope(_script1);
}
}
public void check(ErrorListener context)
{
if (_script1 != null) {
_script1.check(context);
}
}
public void compile(Smith smith)
{
if (_script1 != null) {
ByteCompiler c = new ByteCompiler(smith, this);
if (c.compile()) {
_address.getZone().log().info("code generated: " + _descriptor);
} else {
_address.getZone().log().info("code generation failed: " + _descriptor);
}
}
}
public boolean loadClass()
{
if (_script2 != null) {
return true;
}
CompiledModule script = doLoadClass();
if (script != null) {
_script1 = null;
_script2 = script;
return true;
} else {
return false;
}
}
private CompiledModule doLoadClass()
{
String classname = getClassname();
try {
ZoneClassLoader clsldr = _address.getZone().getClassLoader();
Class cls = clsldr.loadClass(classname);
CompiledModule script = (CompiledModule)cls.newInstance();
classname = cls.getName();
_descriptor = classname.replace('.', '/');
_version = script.getVersion();
script.init(_address);
_address.getZone().log().info("code loaded: " + classname + ", version "+_version);
return script;
} catch (NoClassDefFoundError exception) {
//ignore
} catch (ClassNotFoundException exception) {
//ignore
} catch (InstantiationException exception) {
_address.getZone().log().error("Error when loading '"+classname+"'", exception);
} catch (IllegalAccessException exception) {
_address.getZone().log().error("Error when loading '"+classname+"'", exception);
} catch (LinkageError error) {
_address.getZone().log().error("Error when loading '"+classname+"'", error);
}
return null;
}
public void init(ErrorListener listener)
{
synchronized(this) {
if (_initialized) {
return;
}
_initialized = true;
}
CompiledModule script = _script2;
if (script != null) {
Context context = new Context(_address.getZone());
context.push(script, null, 0, 0, false);
try {
script.init(context);
} catch (ScriptException e) {
_initialized = false;
//e.fillInStackTrace();
listener.error(new anvil.Location(_address.getURL()), e.getData().toString());
} catch (Throwable t) {
listener.error(null, t);
anvil.Log.log().error("Initialization failed: "+script.getPathinfo(), t);
_initialized = false;
} finally {
context.pop();
context.destroy();
}
}
}
private static String mangle(String pathinfo)
{
int n = pathinfo.length();
StringBuffer buffer = new StringBuffer(n + 10);
char ch;
int i = 0;
if (n>0 && pathinfo.charAt(0) == '/') {
i++;
}
for(; i<n; i++) {
ch = pathinfo.charAt(i);
if (ch == '/' || ch == '\\') {
buffer.append('/');
buffer.append('_');
} /*else if (ch == '_') {
buffer.append('_');
buffer.append('_');
}*/ else {
if (!Character.isJavaIdentifierPart(ch)) {
buffer.append('_');
/*buffer.append(Integer.toHexString(ch));
buffer.append('_');*/
} else {
buffer.append(ch);
}
}
}
return buffer.toString();
}
protected static void appendWithPrefix(StringBuffer buffer, int i)
{
if (i<10) {
buffer.append('0');
}
buffer.append(i);
}
protected String addTimestamp(String mangled, long lastModified)
{
if (lastModified != -1) {
StringBuffer buffer = new StringBuffer(mangled.length() + 18);
LocalizationPreferences prefs = _address.getZone().getLocalizationPreferences();
Calendar calendar = Calendar.getInstance(prefs.getTimezoneInstance(), prefs.getLocaleInstance());
calendar.setTime(new Date(lastModified));
buffer.append(mangled);
buffer.append('_');
buffer.append(calendar.get(Calendar.YEAR));
appendWithPrefix(buffer, calendar.get(Calendar.MONTH)+1);
appendWithPrefix(buffer, calendar.get(Calendar.DAY_OF_MONTH));
buffer.append('_');
appendWithPrefix(buffer, calendar.get(Calendar.HOUR_OF_DAY));
appendWithPrefix(buffer, calendar.get(Calendar.MINUTE));
appendWithPrefix(buffer, calendar.get(Calendar.SECOND));
mangled = buffer.toString();
}
return mangled;
}
}