This commit was generated by cvs2svn to compensate for changes in r4,
which included commits to RCS files with non-trunk default branches.
This commit is contained in:
parent
af35ca5581
commit
ee13186158
148 changed files with 34934 additions and 0 deletions
541
src/FESI/Interpreter/ClassInfo.java
Normal file
541
src/FESI/Interpreter/ClassInfo.java
Normal file
|
@ -0,0 +1,541 @@
|
|||
// ClassInfo.java
|
||||
// FESI Copyright (c) Jean-Marc Lugrin, 1999
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package FESI.Interpreter;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.Vector;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.beans.*;
|
||||
|
||||
import FESI.Data.*;
|
||||
import FESI.Exceptions.*;
|
||||
|
||||
/**
|
||||
* This class contains the static routines to access the cache of class information
|
||||
* as well as the ClassInfo (class specific cached information). The information
|
||||
* is separated between the bean based information and the low level information.
|
||||
* Even if most classes will have the same information in both cases, they are
|
||||
* likely to be used in only one way, so there space overhead is ususally small.
|
||||
* <P>The cache is filled in a lazy manner, only requested information is cached,
|
||||
* even if this requires more sweeps of the low level information. The rational is
|
||||
* that for small set of information this is not a major drawback, and for large
|
||||
* set of information (for example the list of methods of an AWT component), only
|
||||
* a small subset of it is really used. So the small foorprint is given more importance
|
||||
* than a faster access for seldom cases.
|
||||
* <P>The cache is shared by all instance of the evaluator and class loaders (it is
|
||||
* enough to identify the classes by their Class object). So the access must
|
||||
* be synchronized and no evaluator specific information must be added.
|
||||
* <P>The existence of this cache defeats class garbage collections for classes
|
||||
* accessed via EcmaScript. This should not be too severe a restriction, and can
|
||||
* be lifted in JDK 1.2.
|
||||
*/
|
||||
public class ClassInfo {
|
||||
|
||||
/** Cache of information on all classes, using the class object as a key */
|
||||
static private Hashtable allClassInfo = new Hashtable();
|
||||
|
||||
/** Cache of public methods */
|
||||
private Hashtable publicMethods = null;
|
||||
/** Cache of bean methods */
|
||||
private Hashtable beanMethods = null;
|
||||
/** Cache of bean properties */
|
||||
private Hashtable beanProperties = null;
|
||||
/** Cache of BeanInofo */
|
||||
private BeanInfo beanInfo = null;
|
||||
|
||||
/**
|
||||
* Ensure that ClassInfo objects can only be created via
|
||||
* the factory.
|
||||
*/
|
||||
private ClassInfo() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the specified class has a ClassInfo object in the
|
||||
* cached. Create an empty one if needed. Must be called synchronized.
|
||||
*
|
||||
* @param cls The class for which we look for a ClassInfo
|
||||
* @return the ClassInfo of cls, added to the cache if needed
|
||||
*/
|
||||
private static ClassInfo ensureClassInfo(Class cls) {
|
||||
boolean debug = ESLoader.isDebugJavaAccess();
|
||||
ClassInfo classInfo = (ClassInfo) allClassInfo.get(cls);
|
||||
if (classInfo == null) {
|
||||
if (debug) System.out.println("** Class info for class '" +
|
||||
cls + "' not found in cache, created");
|
||||
classInfo = new ClassInfo();
|
||||
allClassInfo.put(cls, classInfo);
|
||||
}
|
||||
return classInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the property descriptor for the specified field of the specified class,
|
||||
* considered as a bean.
|
||||
*
|
||||
* @param fieldName The name of the property
|
||||
* @param cls The class for which we look for the property.
|
||||
* @return The PropertyDescriptor or null if not found or in case of error
|
||||
*/
|
||||
synchronized public static PropertyDescriptor lookupBeanField(String fieldName, Class cls) {
|
||||
ClassInfo classInfo = ClassInfo.ensureClassInfo(cls);
|
||||
return classInfo.cachedBeanFieldLookup(fieldName, cls);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the property descriptor for the specified field of the specified class,
|
||||
* considered as a bean, in the current ClassInfo cache. Add it to the cache
|
||||
* if needed, after veryfying that the accessors are OK.
|
||||
*
|
||||
* @param fieldName The name of the property
|
||||
* @param cls The class for which we look for the property.
|
||||
* @return The PropertyDescriptor or null if not found or in case of error
|
||||
*/
|
||||
private PropertyDescriptor cachedBeanFieldLookup(String propertyName, Class cls) {
|
||||
boolean debug = ESLoader.isDebugJavaAccess();
|
||||
|
||||
// Check that there is a bean properties cache, chech if the property was cached
|
||||
if (beanProperties != null) {
|
||||
if (debug) System.out.println("** Bean properties for class '" +
|
||||
cls + "' found in cache");
|
||||
PropertyDescriptor descriptor =
|
||||
(PropertyDescriptor) beanProperties.get(propertyName);
|
||||
if (descriptor!= null) {
|
||||
if (debug) System.out.println("** property descriptor '" + propertyName + "' found in cache");
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
// Not in cache
|
||||
if (debug) System.out.println("** No property named '" +
|
||||
propertyName + "' found in cache, lookup started");
|
||||
|
||||
// Do we have a cached BeanInfo ? create it if no
|
||||
if (beanInfo == null) {
|
||||
try {
|
||||
beanInfo = Introspector.getBeanInfo(cls);
|
||||
} catch (IntrospectionException e) {
|
||||
if (debug) System.out.println(" ** Error getting beaninfo: " + e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the property descriptor by name
|
||||
PropertyDescriptor [] allProperties = beanInfo.getPropertyDescriptors();
|
||||
PropertyDescriptor descriptor = null; // none found
|
||||
for (int i=0; i<allProperties.length; i++) {
|
||||
PropertyDescriptor property = allProperties[i];
|
||||
if (debug) System.out.println("** Property examined: " + property.getName());
|
||||
if (!property.getName().equals(propertyName)) continue;
|
||||
descriptor = property;
|
||||
break;
|
||||
}
|
||||
// If we found it, test it and add it to the cache
|
||||
if (descriptor != null) {
|
||||
// Check that it is valid here (to speed up safe usage later)
|
||||
Method readMethod = descriptor.getReadMethod();
|
||||
Method writeMethod = descriptor.getWriteMethod();
|
||||
Class propCls = descriptor.getPropertyType();
|
||||
if (descriptor instanceof IndexedPropertyDescriptor) {
|
||||
// Check indexed access
|
||||
IndexedPropertyDescriptor iDescriptor = (IndexedPropertyDescriptor) descriptor;
|
||||
Method indexedReadMethod = iDescriptor.getIndexedReadMethod();
|
||||
Method indexedWriteMethod = iDescriptor.getIndexedWriteMethod();
|
||||
Class propIndexedCls = iDescriptor.getIndexedPropertyType();
|
||||
if (propIndexedCls == null) {
|
||||
throw new ProgrammingError("getIndexedPropertyType returned null for " + propertyName);
|
||||
}
|
||||
if (propIndexedCls == Void.TYPE) {
|
||||
throw new ProgrammingError("Void indexed property type for '" + propertyName + "' is not allowed");
|
||||
}
|
||||
if (indexedReadMethod != null && indexedReadMethod.getParameterTypes().length != 1) {
|
||||
throw new ProgrammingError("Indexed getter of property ' " + propertyName + "' should have 1 parameter!");
|
||||
}
|
||||
if (indexedWriteMethod != null) {
|
||||
Class [] paramCls = indexedWriteMethod.getParameterTypes();
|
||||
if (paramCls == null || paramCls.length != 2) {
|
||||
throw new ProgrammingError("Indexed setter of property ' " + propertyName + "' should have 2 parameter!");
|
||||
}
|
||||
if (paramCls[0]!=propIndexedCls) { // SHOULD CHECK INSTANCE
|
||||
throw new ProgrammingError("Inconstant parameter type for indexed setter of indexed property' " + propertyName + "', type: " + propCls);
|
||||
}
|
||||
}
|
||||
// Check non indexed access
|
||||
if (propCls != null) {
|
||||
if (!propCls.isArray()) {
|
||||
throw new ProgrammingError("Non array type (" + propCls + ") for array access of indexed property '" + propertyName + "'");
|
||||
}
|
||||
if (propCls.getComponentType()!=propIndexedCls) {
|
||||
throw new ProgrammingError("Type missmatch between array and non array access of indexed property '" + propertyName + "'");
|
||||
}
|
||||
if (readMethod != null && readMethod.getParameterTypes().length != 0) {
|
||||
throw new ProgrammingError("Non indexed getter of indxed property ' " + propertyName + "' is not supposed to have a parameter!");
|
||||
}
|
||||
if (writeMethod != null) {
|
||||
Class [] paramCls = writeMethod.getParameterTypes();
|
||||
if (paramCls == null || paramCls.length != 1) {
|
||||
throw new ProgrammingError("Non indexed setter of indexed property ' " + propertyName + "' should have 1 parameter!");
|
||||
}
|
||||
if (paramCls[0]!=propCls) { // SHOULD CHECK INSTANCE
|
||||
throw new ProgrammingError("Inconstant parameter type for non indexed setter of indexed property' " + propertyName + "', type: " + propCls);
|
||||
}
|
||||
}
|
||||
} // non indexed access
|
||||
} else {
|
||||
if (propCls == null) {
|
||||
throw new ProgrammingError("getPropertyType returned null for " + propertyName);
|
||||
}
|
||||
if (propCls == Void.TYPE) {
|
||||
throw new ProgrammingError("Void property type for '" + propertyName + "' is not allowed");
|
||||
}
|
||||
if (readMethod != null && readMethod.getParameterTypes().length != 0) {
|
||||
throw new ProgrammingError("Non indexed getter of property ' " + propertyName + "' is not supposed to have a parameter!");
|
||||
}
|
||||
if (writeMethod != null) {
|
||||
Class [] paramCls = writeMethod.getParameterTypes();
|
||||
if (paramCls == null || paramCls.length != 1) {
|
||||
throw new ProgrammingError("Non indexed setter of property ' " + propertyName + "' should have 1 parameter!");
|
||||
}
|
||||
if (paramCls[0]!=propCls) { // SHOULD CHECK INSTANCE
|
||||
throw new ProgrammingError("Inconstant parameter type for setter of property' " + propertyName + "', type: " + propCls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add to cache
|
||||
if (debug) System.out.println("** property '" + propertyName + "' + found, add to cache");
|
||||
if (beanProperties==null) {
|
||||
beanProperties = new Hashtable();
|
||||
}
|
||||
beanProperties.put(propertyName, descriptor);
|
||||
} else {
|
||||
if (debug) System.out.println("** No method named '" + propertyName + "' found");
|
||||
}
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of public method in this class or superclass, by name (the
|
||||
* methods may be static or instance!).
|
||||
*
|
||||
* @param functionName The nam of the method being looked up
|
||||
* @param cls The class of the method being looked up
|
||||
* @return The method array or null if none found or in case of error
|
||||
*/
|
||||
synchronized public static Method[] lookupPublicMethod(String functionName, Class cls) throws EcmaScriptException {
|
||||
ClassInfo classInfo = ClassInfo.ensureClassInfo(cls);
|
||||
return classInfo.cachedPublicMethodLookup(functionName, cls);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Look for a method in a list of interface or all superinterfaces
|
||||
*
|
||||
* @param functionName The name of the function to search
|
||||
* @param interfaces The list of interfaces to search
|
||||
* @param paramTypes The type of parameters of the function
|
||||
*
|
||||
* @return The method if found, null otherwise
|
||||
*/
|
||||
private Method getInInterfaces(String functionName, Class [] interfaces, Class[] paramTypes) {
|
||||
boolean debug = ESLoader.isDebugJavaAccess();
|
||||
|
||||
if (debug && interfaces.length>0) {
|
||||
System.out.println("** Looking in " + interfaces.length + " interfaces");
|
||||
}
|
||||
SEARCHININTERFACE:
|
||||
for (int ix=0; ix<interfaces.length; ix++) {
|
||||
Class theInterface=interfaces[ix];
|
||||
if (Modifier.isPublic(theInterface.getModifiers())) {
|
||||
if (debug) {
|
||||
System.out.println("** Looking in public interface: " + theInterface);
|
||||
}
|
||||
try {
|
||||
Method method = theInterface.getDeclaredMethod(functionName,paramTypes);
|
||||
if (Modifier.isPublic(method.getModifiers())) {
|
||||
if (debug) {
|
||||
System.out.println("** Public method found: " + functionName);
|
||||
}
|
||||
return method;
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
if (debug) {
|
||||
System.out.println("** The method has no public declaration in the interface: "+ functionName);
|
||||
}
|
||||
// throw new ProgrammingError("The method has no public declaration in a public class: "+ functionName);
|
||||
} catch (SecurityException e) {
|
||||
throw new ProgrammingError("Access error inspecting method "+ functionName + ": " + e);
|
||||
}
|
||||
} else {
|
||||
if (debug) {
|
||||
System.out.println("** Interface " + theInterface + " is not public - not searching for method");
|
||||
}
|
||||
}
|
||||
|
||||
// Not found, try super interfaces
|
||||
Class [] superInterfaces = theInterface.getInterfaces();
|
||||
Method method = getInInterfaces(functionName, superInterfaces, paramTypes);
|
||||
if (method!=null) {
|
||||
if (debug) System.out.println("** Method found in super interfaces");
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
System.out.println("** No method found in interface and super interfaces");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a public method list, where method matched in name and class.
|
||||
* The method list is cached in this ClassInfo object (which must be
|
||||
* for the specified class).
|
||||
* <P>Only the public variant of a method is returned.
|
||||
* <P>If a method was not found, the cache is not modified, and a new
|
||||
* slow lookup will be done on next search. The case is rare enought
|
||||
* that it is better to keep the code simpler.
|
||||
*
|
||||
* @param functionName The name of the function
|
||||
* @param cls The class in which the function is defined
|
||||
* @return The list of methods or null in case of error or if none found.
|
||||
*/
|
||||
private Method [] cachedPublicMethodLookup(String functionName, Class cls) throws EcmaScriptException {
|
||||
boolean debug = ESLoader.isDebugJavaAccess();
|
||||
if (publicMethods != null) {
|
||||
if (debug) System.out.println("** Method descriptor for class '" +
|
||||
cls + "' found in cache");
|
||||
Method [] methods = (Method []) publicMethods.get(functionName);
|
||||
if (methods!= null) {
|
||||
if (debug) System.out.println("** " + methods.length +
|
||||
" method(s) named '" + functionName + "' found in cache");
|
||||
return methods;
|
||||
}
|
||||
}
|
||||
// Not in cache, find if any matching the same name can be found
|
||||
if (debug) System.out.println("** No method named '" +
|
||||
functionName + "' found in class cache, lookup started");
|
||||
Method [] allMethods = cls.getMethods();
|
||||
Vector methodVector = new Vector(allMethods.length);
|
||||
boolean wasFound = false;
|
||||
for (int i=0; i<allMethods.length; i++) {
|
||||
Method method = allMethods[i];
|
||||
if (debug) System.out.println("** Method examined: " + method.toString());
|
||||
if (!method.getName().equals(functionName)) continue;
|
||||
// Method has same name, some closer examination is needed:
|
||||
// If the class itself is not public, there is an access error if
|
||||
// the method is invoked on it - the identical method in the
|
||||
// superclass must be searched, to be used in the invocation.
|
||||
// I am not too sure of what happens if the method is defined in an
|
||||
// interface...
|
||||
if (!Modifier.isStatic(cls.getModifiers()) && !Modifier.isPublic(cls.getModifiers())) {
|
||||
if (debug) System.out.println("** Class " + cls +
|
||||
" is not public, examining superclasses and interfaces to find proper method descriptor");
|
||||
|
||||
Class[] paramTypes = method.getParameterTypes();
|
||||
SEARCHPUBLIC:
|
||||
for (Class theClass=cls;theClass!=null;theClass=theClass.getSuperclass()) {
|
||||
|
||||
// Look in interfaces first
|
||||
Class [] interfaces = cls.getInterfaces();
|
||||
Method m = getInInterfaces(functionName, interfaces, paramTypes);
|
||||
if (m != null) {
|
||||
method = m;
|
||||
wasFound = true;
|
||||
break SEARCHPUBLIC;
|
||||
}
|
||||
|
||||
// Look in the class
|
||||
if (Modifier.isPublic(theClass.getModifiers())) {
|
||||
if (debug) {
|
||||
System.out.println("** Looking in public class: " + theClass);
|
||||
}
|
||||
try {
|
||||
m = theClass.getDeclaredMethod(functionName,paramTypes);
|
||||
if (Modifier.isPublic(method.getModifiers())) {
|
||||
if (debug) {
|
||||
System.out.println("** Public method found: " + functionName);
|
||||
}
|
||||
method = m;
|
||||
wasFound = true;
|
||||
break SEARCHPUBLIC;
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
if (debug) {
|
||||
System.out.println("** The method has no public declaration in the public class: "+ functionName);
|
||||
}
|
||||
// throw new ProgrammingError("The method has no public declaration in a public class: "+ functionName);
|
||||
} catch (SecurityException e) {
|
||||
throw new ProgrammingError("Access error inspecting method "+ functionName + ": " + e);
|
||||
}
|
||||
} else {
|
||||
if (debug) {
|
||||
System.out.println("** Class " + theClass + " is not public - not searching for method");
|
||||
}
|
||||
}
|
||||
|
||||
} // for SEARCHPUBLIC
|
||||
|
||||
if (!wasFound) {
|
||||
throw new EcmaScriptException("The method '" + functionName + "' has no public declaration in a public class or public interface ");
|
||||
}
|
||||
|
||||
} // if class not public
|
||||
// save it
|
||||
methodVector.addElement(method);
|
||||
}
|
||||
// If we have some, cache them
|
||||
Method [] methods = null;
|
||||
int nmbMethods = methodVector.size();
|
||||
if (nmbMethods>0) {
|
||||
if (debug) System.out.println("** " + nmbMethods + " methods named: '" + functionName + "' + found, add to class cache");
|
||||
methods = new Method[nmbMethods];
|
||||
methodVector.copyInto(methods);
|
||||
if (publicMethods==null) {
|
||||
publicMethods = new Hashtable();
|
||||
}
|
||||
publicMethods.put(functionName, methods);
|
||||
} else {
|
||||
if (debug) System.out.println("** No method named '" + functionName + "' found");
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of public bean method in this bean, by name (the
|
||||
* methods may be static or instance!).
|
||||
*
|
||||
* @param functionName The nam of the method being looked up
|
||||
* @param cls The class of the method being looked up
|
||||
* @return The method array or null if none found or in case of error
|
||||
*/
|
||||
synchronized public static Method[] lookupBeanMethod(String functionName, Class cls) {
|
||||
ClassInfo classInfo = ClassInfo.ensureClassInfo(cls);
|
||||
return classInfo.cachedBeanMethodLookup(functionName, cls);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a bean method list, where method matched in name and class.
|
||||
* The method lists a cached in this ClassInfo object (which must be
|
||||
* for the specified class).
|
||||
* <P>Only the public variant of a method is returned.
|
||||
* <P>If a method was not found, the cache is not modified, and a new
|
||||
* slow lookup will be done on next search. The case is rare enought
|
||||
* that it is better to keep the code simpler.
|
||||
*
|
||||
* @param functionName The name of the function
|
||||
* @param cls The class in which the function is defined
|
||||
* @return The list of methods or null in case of error or if none found.
|
||||
*/
|
||||
private Method [] cachedBeanMethodLookup(String functionName, Class cls) {
|
||||
boolean debug = ESLoader.isDebugJavaAccess();
|
||||
if (beanMethods != null) {
|
||||
if (debug) System.out.println("** Method descriptor for bean '" +
|
||||
cls + "' found in cache");
|
||||
Method [] methods = (Method []) beanMethods.get(functionName);
|
||||
if (methods!= null) {
|
||||
if (debug) System.out.println("** " + methods.length +
|
||||
" method(s) named '" + functionName + "' found in cache");
|
||||
return methods;
|
||||
}
|
||||
}
|
||||
// Not in cache, find if any matching the same name can be found
|
||||
if (debug) System.out.println("** No method named '" +
|
||||
functionName + "' found in bean cache, lookup started");
|
||||
|
||||
// Do we have a cached BeanInfo ? create it if no
|
||||
if (beanInfo == null) {
|
||||
try {
|
||||
beanInfo = Introspector.getBeanInfo(cls);
|
||||
} catch (IntrospectionException e) {
|
||||
if (debug) System.out.println(" ** Error getting beaninfo: " + e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
MethodDescriptor [] allDescriptors = beanInfo.getMethodDescriptors();
|
||||
Vector methodVector = new Vector(allDescriptors.length);
|
||||
for (int i=0; i<allDescriptors.length; i++) {
|
||||
Method method = allDescriptors[i].getMethod();
|
||||
if (debug) System.out.println("** Method examined: " + method.toString());
|
||||
if (!allDescriptors[i].getName().equals(functionName)) continue;
|
||||
// Method has same name, some tuning neede:
|
||||
// If the class itself is not public, there is an access error if
|
||||
// ther method is invoked on it - the identical method in the
|
||||
// superclass must be looked at.
|
||||
// I am not too sure of what happens if the method is defined in an
|
||||
// interface...
|
||||
if (!Modifier.isStatic(cls.getModifiers()) && !Modifier.isPublic(cls.getModifiers())) {
|
||||
if (debug) System.out.println("** Bean class " + cls +
|
||||
" is not public, examining superclasses to find proper method descriptor");
|
||||
SEARCHPUBLIC:
|
||||
for (Class theClass=cls;theClass!=null;theClass=theClass.getSuperclass()) {
|
||||
if (Modifier.isPublic(theClass.getModifiers())) {
|
||||
if (debug) {
|
||||
System.out.println("** Looking in public superlass: " + theClass);
|
||||
}
|
||||
try {
|
||||
Class[] paramTypes = method.getParameterTypes();
|
||||
Method m = theClass.getDeclaredMethod(functionName,paramTypes);
|
||||
if (Modifier.isPublic(method.getModifiers())) {
|
||||
if (debug) {
|
||||
System.out.println("** Public method found: " + functionName);
|
||||
}
|
||||
method = m;
|
||||
break SEARCHPUBLIC;
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new ProgrammingError("Error inspecting method "+ functionName + ": " + e);
|
||||
} catch (SecurityException e) {
|
||||
throw new ProgrammingError("Acess error inspecting method "+ functionName + ": " + e);
|
||||
}
|
||||
} else {
|
||||
if (debug) {
|
||||
System.out.println("** Superlass " + theClass + " is not public");
|
||||
}
|
||||
}
|
||||
} // for
|
||||
} // if class not public
|
||||
// save it
|
||||
methodVector.addElement(method);
|
||||
}
|
||||
// If we have some, cache them
|
||||
Method [] methods = null;
|
||||
int nmbMethods = methodVector.size();
|
||||
if (nmbMethods>0) {
|
||||
if (debug) System.out.println("** " + nmbMethods + " methods named: '"
|
||||
+ functionName + "' + found, add to bean cache");
|
||||
methods = new Method[nmbMethods];
|
||||
methodVector.copyInto(methods);
|
||||
if (beanMethods==null) {
|
||||
beanMethods = new Hashtable();
|
||||
}
|
||||
beanMethods.put(functionName, methods);
|
||||
} else {
|
||||
if (debug) System.out.println("** No bean method named: '" +
|
||||
functionName + "' + found");
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
|
||||
}
|
1358
src/FESI/Interpreter/EcmaScriptEvaluateVisitor.java
Normal file
1358
src/FESI/Interpreter/EcmaScriptEvaluateVisitor.java
Normal file
File diff suppressed because it is too large
Load diff
298
src/FESI/Interpreter/EcmaScriptFunctionVisitor.java
Normal file
298
src/FESI/Interpreter/EcmaScriptFunctionVisitor.java
Normal file
|
@ -0,0 +1,298 @@
|
|||
// EcmaScriptFunctionVisitor.java
|
||||
// FESI Copyright (c) Jean-Marc Lugrin, 1999
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package FESI.Interpreter;
|
||||
|
||||
import FESI.Parser.*;
|
||||
import FESI.AST.*;
|
||||
import java.util.Vector;
|
||||
import FESI.Exceptions.*;
|
||||
import FESI.Data.*;
|
||||
|
||||
/**
|
||||
* The function visitor use the visitor pattern to iterate the
|
||||
* parsed code. It examine all function declarations and assign
|
||||
* them to their global variable.
|
||||
* <P>The function declarations will be ignored by the evaluation
|
||||
* visitor (the tree is not modified).
|
||||
*/
|
||||
public class EcmaScriptFunctionVisitor
|
||||
implements EcmaScriptVisitor,
|
||||
EcmaScriptConstants
|
||||
{
|
||||
|
||||
// The visitor work on behalf on an evaluator
|
||||
private Evaluator evaluator = null;
|
||||
private boolean debug = false;
|
||||
private EvaluationSource currentEvaluationSource = null;
|
||||
|
||||
private ESObject currentObject;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new visitor
|
||||
* @param evaluator On behalf of this evaluator
|
||||
*/
|
||||
public EcmaScriptFunctionVisitor(Evaluator evaluator) {
|
||||
super();
|
||||
this.evaluator = evaluator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process all function declarations in ths parse tree
|
||||
* @param node The parse tree
|
||||
* @evaluationSource A description of the source for error messages
|
||||
*/
|
||||
public void processFunctionDeclarations(ASTProgram node,
|
||||
EvaluationSource evaluationSource ) {
|
||||
if (debug) System.out.println("processFunctionDeclarations: " + node);
|
||||
if (currentEvaluationSource!=null) throw new ProgrammingError("illegal recursive function definition");
|
||||
this.currentEvaluationSource = evaluationSource;
|
||||
try {
|
||||
node.jjtAccept(this, null);
|
||||
} finally {
|
||||
this.currentEvaluationSource =null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void processFunctionDeclarations(ASTProgram node,
|
||||
EvaluationSource evaluationSource,
|
||||
ESObject currentObject ) {
|
||||
this.currentObject = currentObject;
|
||||
if (debug) System.out.println("processFunctionDeclarations: " + node);
|
||||
if (currentEvaluationSource!=null) throw new ProgrammingError("illegal recursive function definition");
|
||||
this.currentEvaluationSource = evaluationSource;
|
||||
try {
|
||||
node.jjtAccept(this, null);
|
||||
} finally {
|
||||
this.currentEvaluationSource =null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
* The following routines implement the walking process
|
||||
* Irrelevant parts of the tree are skipped
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
private void badAST() {
|
||||
throw new ProgrammingError("Bad AST walk in EcmaScriptFunctionVisitor");
|
||||
}
|
||||
|
||||
|
||||
// The dispatching is by node type - if the specific visitor
|
||||
// is not implemented, then this routine is called
|
||||
public Object visit(SimpleNode node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
public Object visit(ASTProgram node, Object data) {
|
||||
data = node.childrenAccept(this, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTStatementList node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTFunctionDeclaration node, Object data) {
|
||||
int nChildren = node.jjtGetNumChildren();
|
||||
if (nChildren!=3) {
|
||||
throw new ProgrammingError("Bad AST in function declaration");
|
||||
}
|
||||
|
||||
ASTIdentifier idNode = (ASTIdentifier) (node.jjtGetChild(0));
|
||||
FunctionEvaluationSource fes =
|
||||
new FunctionEvaluationSource(currentEvaluationSource,
|
||||
idNode.getName());
|
||||
|
||||
ASTFormalParameterList fpl = (ASTFormalParameterList) (node.jjtGetChild(1));
|
||||
ASTStatementList sl = (ASTStatementList) (node.jjtGetChild(2));
|
||||
EcmaScriptVariableVisitor varDeclarationVisitor = evaluator.getVarDeclarationVisitor();
|
||||
Vector variableNames = varDeclarationVisitor.processVariableDeclarations(sl, fes);
|
||||
|
||||
if (debug) System.out.println("FUNC DECL: " + idNode.getName());
|
||||
GlobalObject go = evaluator.getGlobalObject();
|
||||
try {
|
||||
ESReference newVar = new ESReference(go, idNode.getName(), idNode.hashCode());
|
||||
ConstructedFunctionObject func =
|
||||
ConstructedFunctionObject.makeNewConstructedFunction(
|
||||
evaluator,
|
||||
idNode.getName(),
|
||||
fes,
|
||||
node.getSourceString(),
|
||||
fpl.getArguments(),
|
||||
variableNames,
|
||||
sl);
|
||||
if (currentObject != null)
|
||||
currentObject.putHiddenProperty (idNode.getName(), func);
|
||||
else
|
||||
evaluator.putValue(newVar, func);
|
||||
} catch (EcmaScriptException e) {
|
||||
e.printStackTrace();
|
||||
throw new ProgrammingError("Unexpected error registering function" + e.getMessage());
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTFormalParameterList node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTStatement node, Object data) {
|
||||
// Ignore statements for function declaration visitors
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTVariableDeclaration node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTIfStatement node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTContinueStatement node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTWhileStatement node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTForStatement node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTForInStatement node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTForVarStatement node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTForVarInStatement node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTBreakStatement node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTReturnStatement node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTWithStatement node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTThisReference node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTCompositeReference node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTFunctionCallParameters node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTPropertyValueReference node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTPropertyIdentifierReference node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTAllocationExpression node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTOperator node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTPostfixExpression node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTUnaryExpression node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTBinaryExpressionSequence node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTAndExpressionSequence node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTOrExpressionSequence node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTConditionalExpression node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
|
||||
// Can we really have a cascade ?
|
||||
public Object visit(ASTAssignmentExpression node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTExpressionList node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTEmptyExpression node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTLiteral node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTIdentifier node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
274
src/FESI/Interpreter/EcmaScriptVariableVisitor.java
Normal file
274
src/FESI/Interpreter/EcmaScriptVariableVisitor.java
Normal file
|
@ -0,0 +1,274 @@
|
|||
// EcmaScriptVariableVisitor.java
|
||||
// FESI Copyright (c) Jean-Marc Lugrin, 1999
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package FESI.Interpreter;
|
||||
|
||||
import FESI.Parser.*;
|
||||
import FESI.AST.*;
|
||||
import FESI.Exceptions.*;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* The variable visitor use the visitor pattern to iterate the
|
||||
* parsed code. It examine all variable declaration at the current
|
||||
* level (it does not recurse in functions) and return the
|
||||
* list of variables as a vector.
|
||||
* <P>The variable declarations will be ignored by the evaluation
|
||||
* visitor (the tree is not modified).
|
||||
*/
|
||||
public class EcmaScriptVariableVisitor
|
||||
implements EcmaScriptVisitor,
|
||||
EcmaScriptConstants
|
||||
{
|
||||
|
||||
// The visitor work on behalf on an evaluator
|
||||
private Evaluator evaluator = null;
|
||||
private boolean debug = false;
|
||||
private Vector variableList = null;
|
||||
|
||||
/**
|
||||
* Create a new visitor
|
||||
* @param evaluator On behalf of this evaluator
|
||||
*/
|
||||
public EcmaScriptVariableVisitor(Evaluator evaluator) {
|
||||
super();
|
||||
this.evaluator = evaluator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process all variable declarations at the global level
|
||||
* @param node The parse tree
|
||||
* @evaluationSource A description of the source for error messages
|
||||
* @return A vector of variables
|
||||
*/
|
||||
public Vector processVariableDeclarations(ASTProgram node,
|
||||
EvaluationSource evaluationSource) {
|
||||
if (debug) System.out.println("processVariableDeclarations for program: " + node);
|
||||
variableList = new Vector();
|
||||
node.jjtAccept(this, evaluationSource);
|
||||
return variableList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process all variable declarations at the statement list level
|
||||
* @param node The parse tree
|
||||
* @evaluationSource A description of the source for error messages
|
||||
* @return A vector of variables
|
||||
*/
|
||||
public Vector processVariableDeclarations(ASTStatementList node,
|
||||
EvaluationSource evaluationSource) {
|
||||
if (debug) System.out.println("processVariableDeclarations for function body: " + node);
|
||||
variableList = new Vector();
|
||||
node.jjtAccept(this, evaluationSource);
|
||||
return variableList;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
* The following routines implement the walking process
|
||||
* Irrelevant parts of the tree are skipped
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
private void badAST() {
|
||||
throw new ProgrammingError("Bad AST walk in EcmaScriptVariableVisitor");
|
||||
}
|
||||
|
||||
|
||||
// The dispatching is by node type - if the specific visitor
|
||||
// is not implemented, then this routine is called
|
||||
public Object visit(SimpleNode node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTProgram node, Object data) {
|
||||
data = node.childrenAccept(this, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTStatementList node, Object data) {
|
||||
data = node.childrenAccept(this, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTFunctionDeclaration node, Object data) {
|
||||
; // ignore function declarations in this mode
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTFormalParameterList node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTStatement node, Object data) {
|
||||
data = node.childrenAccept(this, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTVariableDeclaration node, Object data) {
|
||||
int nChildren = node.jjtGetNumChildren();
|
||||
if (nChildren<1 || nChildren>2) {
|
||||
throw new ProgrammingError("Bad AST in variable declaration");
|
||||
}
|
||||
ASTIdentifier idNode = (ASTIdentifier) (node.jjtGetChild(0));
|
||||
if (debug) System.out.println("VAR DECL: " + idNode.getName());
|
||||
variableList.addElement(idNode.getName());
|
||||
//try {
|
||||
// evaluator.createVariable(idNode.getName());
|
||||
//} catch (EcmaScriptException e) {
|
||||
// e.printStackTrace();
|
||||
// throw new ProgrammingError(e.getMessage());
|
||||
//}
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTIfStatement node, Object data) {
|
||||
data = node.childrenAccept(this, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTContinueStatement node, Object data) {
|
||||
; // no internal variable declarations possible
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTWhileStatement node, Object data) {
|
||||
data = node.childrenAccept(this, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTForStatement node, Object data) {
|
||||
data = node.childrenAccept(this, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTForInStatement node, Object data) {
|
||||
data = node.childrenAccept(this, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTForVarStatement node, Object data) {
|
||||
data = node.childrenAccept(this, data);
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTForVarInStatement node, Object data) {
|
||||
data = node.childrenAccept(this, data);
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTBreakStatement node, Object data) {
|
||||
; // no internal variable declarations possible
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTReturnStatement node, Object data) {
|
||||
; // no internal variable declarations possible
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTWithStatement node, Object data) {
|
||||
node.setEvaluationSource(data);
|
||||
data = node.childrenAccept(this, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTThisReference node, Object data) {
|
||||
; // no internal variable declarations possible
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTCompositeReference node, Object data) {
|
||||
; // no internal variable declarations possible
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTFunctionCallParameters node, Object data) {
|
||||
; // no internal variable declarations possible
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTPropertyValueReference node, Object data) {
|
||||
; // no internal variable declarations possible
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTPropertyIdentifierReference node, Object data) {
|
||||
; // no internal variable declarations possible
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTAllocationExpression node, Object data) {
|
||||
; // no internal variable declarations possible
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTOperator node, Object data) {
|
||||
badAST();
|
||||
return data;
|
||||
}
|
||||
public Object visit(ASTPostfixExpression node, Object data) {
|
||||
; // no internal variable declarations possible
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTUnaryExpression node, Object data) {
|
||||
; // no internal variable declarations possible
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTBinaryExpressionSequence node, Object data) {
|
||||
; // no internal variable declarations possible
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTAndExpressionSequence node, Object data) {
|
||||
; // no internal variable declarations possible
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTOrExpressionSequence node, Object data) {
|
||||
; // no internal variable declarations possible
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTConditionalExpression node, Object data) {
|
||||
; // no internal variable declarations possible
|
||||
return data;
|
||||
}
|
||||
|
||||
// Can we really have a cascade ?
|
||||
public Object visit(ASTAssignmentExpression node, Object data) {
|
||||
; // no internal variable declarations possible
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTExpressionList node, Object data) {
|
||||
; // no internal variable declarations possible
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTEmptyExpression node, Object data) {
|
||||
; // no internal variable declarations possible
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTLiteral node, Object data) {
|
||||
; // no internal variable declarations possible
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTIdentifier node, Object data) {
|
||||
; // no internal variable declarations possible
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
69
src/FESI/Interpreter/EvaluationSource.java
Normal file
69
src/FESI/Interpreter/EvaluationSource.java
Normal file
|
@ -0,0 +1,69 @@
|
|||
// EvaluationSource.java
|
||||
// FESI Copyright (c) Jean-Marc Lugrin, 1999
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package FESI.Interpreter;
|
||||
|
||||
/**
|
||||
* Abstract declaration of an evaluation source. An evaluation
|
||||
* source is used to describe the source of a program for
|
||||
* error messages and debugging purpose.
|
||||
*/
|
||||
public abstract class EvaluationSource {
|
||||
|
||||
/**
|
||||
* The end of line string for this machine.
|
||||
*/
|
||||
protected String eol = System.getProperty("line.separator", "\n");
|
||||
|
||||
/**
|
||||
* The previous source in case of multi-level evaluation.
|
||||
*/
|
||||
protected EvaluationSource previousSource = null;
|
||||
|
||||
/**
|
||||
* Create a evaluation source linked to the previous source
|
||||
* (which can be null)
|
||||
*/
|
||||
public EvaluationSource(EvaluationSource previousSource) {
|
||||
super();
|
||||
this.previousSource = previousSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a description of the evaluation source. Must be
|
||||
* implemented for each specific evaluation source.
|
||||
*/
|
||||
abstract protected String getEvaluationSourceText();
|
||||
|
||||
/**
|
||||
* Display the description of the evaluation source
|
||||
*/
|
||||
public String toString() {
|
||||
return getEvaluationSourceText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an evaluation source number if defined
|
||||
* @return -1 if not defined
|
||||
*/
|
||||
public int getLineNumber() {
|
||||
if (previousSource != null) {
|
||||
return previousSource.getLineNumber();
|
||||
}
|
||||
return -1; // undefined
|
||||
}
|
||||
}
|
1060
src/FESI/Interpreter/Evaluator.java
Normal file
1060
src/FESI/Interpreter/Evaluator.java
Normal file
File diff suppressed because it is too large
Load diff
419
src/FESI/Interpreter/EventAdaptor.java
Normal file
419
src/FESI/Interpreter/EventAdaptor.java
Normal file
|
@ -0,0 +1,419 @@
|
|||
/*
|
||||
*
|
||||
* @(#) EncapsulatedEventAdaptor.java 1.4@(#)
|
||||
*
|
||||
* Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
|
||||
*
|
||||
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
|
||||
* SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
|
||||
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
|
||||
* THIS SOFTWARE OR ITS DERIVATIVES.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* FESI.Interpreter.EventAdaptor
|
||||
* </p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @author Laurence P. G. Cable.
|
||||
*/
|
||||
|
||||
package FESI.Interpreter;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import FESI.Data.ESWrapper;
|
||||
import FESI.Exceptions.*;
|
||||
|
||||
import java.util.EventObject;
|
||||
import java.util.EventListener;
|
||||
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.EventSetDescriptor;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.IntrospectionException;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* FESI.Interpreter.EventAdaptor is an abstract base
|
||||
* class designed to support the dynamic generation of java.util.EventListener
|
||||
* sub-interface adaptor classes.
|
||||
* </p>
|
||||
* <p>
|
||||
* These dynamically generated adaptor classes can be used to take arbitrary
|
||||
* events and call EcmaScript handlers.
|
||||
* </p>
|
||||
* <p>
|
||||
* Using this dynamic adaptor class, objects that emits events on arbitrary`
|
||||
* sub-interfaces of java.util.EventListener can be encapsulated and delivered
|
||||
* onto a single EventListener interface, this allowing late binding of event
|
||||
* behaviors and filtering applications.
|
||||
* </p>
|
||||
*
|
||||
* <P>see sunw.demo.encapsulatedEvents.EncapsulatedEvent</P>
|
||||
* <P>see sunw.demo.encapsulatedEvents.EncapsulatedEventListener</P>
|
||||
* <P>see sunw.demo.encapsulatedEvents.EncapsulatedEventException</P>
|
||||
* <P>see sunw.demo.encapsulatedEvents.EncapsulatedEventAdaptorGenerator</P>
|
||||
*/
|
||||
|
||||
public abstract class EventAdaptor {
|
||||
|
||||
/**
|
||||
* The Event Source
|
||||
*/
|
||||
|
||||
protected Object source;
|
||||
|
||||
/**
|
||||
* The target Wrapper object
|
||||
*/
|
||||
protected ESWrapper wrapper;
|
||||
|
||||
/**
|
||||
* The Event Source's add<T>Listener method
|
||||
*/
|
||||
|
||||
protected Method addAdaptorMethod;
|
||||
|
||||
/**
|
||||
* The Event Source's remove<T>Listener method
|
||||
*/
|
||||
|
||||
protected Method removeAdaptorMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Generate an Adaptor instance for the Listener Interface and Event Source
|
||||
*
|
||||
* @param lc The java.lang.Class object for the listener to adapt.
|
||||
* @param s The source object that the adaptor will listen to.
|
||||
* @param w The wrapper object that the adaptor will listen to.
|
||||
*
|
||||
* @return The newly instantiated dynamic adaptor
|
||||
*
|
||||
* @exception ClassNotFoundException If class cannot be loaded
|
||||
* @exception IntrospectionException If error in introspection
|
||||
* @exception InstantiationException If class found but cannot be instantiated
|
||||
* @exception IllegalAccessException If operationis not allowed
|
||||
*/
|
||||
|
||||
public static EventAdaptor getEventAdaptor(Class lc, Object s, ESWrapper w)
|
||||
throws ClassNotFoundException, InstantiationException,
|
||||
IllegalAccessException, IntrospectionException {
|
||||
Class eeac = null;
|
||||
EventAdaptor eea = null;
|
||||
BeanInfo sbi = Introspector.getBeanInfo(s.getClass());
|
||||
EventSetDescriptor[] sesd = sbi.getEventSetDescriptors();
|
||||
|
||||
/*
|
||||
* first validate that the event source emits event on the interface
|
||||
* specified.
|
||||
*/
|
||||
|
||||
if (validateEventSource(sesd, lc) == -1) {
|
||||
throw new IllegalArgumentException("Object: " +
|
||||
s +
|
||||
" does not source: " +
|
||||
lc.getName()
|
||||
);
|
||||
}
|
||||
|
||||
// generate the adaptor class
|
||||
|
||||
eeac = EventAdaptor.getEventAdaptorClass(lc);
|
||||
|
||||
// instantiate an instance of it ...
|
||||
|
||||
eea = (EventAdaptor)eeac.newInstance();
|
||||
|
||||
eea.setWrapper(w);
|
||||
|
||||
// and register the adaptor with the event source
|
||||
|
||||
try {
|
||||
eea.setSource(s);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to register with source");
|
||||
}
|
||||
|
||||
return eea;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param lc The java.lang.Class object for the listener to adapt.
|
||||
*
|
||||
* @exception ClassNotFoundException If class cannot be loaded
|
||||
*
|
||||
* @return The Class object for the newly generated dynamic adaptor class.
|
||||
*/
|
||||
|
||||
public static Class getEventAdaptorClass(Class lc) throws ClassNotFoundException {
|
||||
if (!java.util.EventListener.class.isAssignableFrom(lc)) {
|
||||
throw new IllegalArgumentException("Class is not a subinterface of java.util.EventListenerEventListener");
|
||||
}
|
||||
|
||||
return EventAdaptorGenerator.getAdaptorClassForListenerClass(lc);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> default constructor.
|
||||
*/
|
||||
|
||||
protected EventAdaptor() { super(); }
|
||||
|
||||
/**
|
||||
* @return the java.lang.Class object for the java.util.EventListener subinterface this instance adapts.
|
||||
*/
|
||||
|
||||
abstract public Class getListenerClass();
|
||||
|
||||
/**
|
||||
* @return the name of the java.util.EventListener subinterface this instance adapts.
|
||||
*/
|
||||
|
||||
public String getListenerClassName() {
|
||||
return getListenerClass().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the event source object that this instance is adapting.
|
||||
*/
|
||||
|
||||
public synchronized Object getSource() { return source; }
|
||||
|
||||
|
||||
public synchronized ESWrapper getWarepper() { return wrapper; }
|
||||
public synchronized void setWrapper(ESWrapper w) { wrapper = w; }
|
||||
|
||||
/**
|
||||
* @param sesd the EventSetDescriptor[] from the prospective event source.
|
||||
* @param lc the java.lang.Class for the EventListener we are adapting.
|
||||
* @return the index of the matching EventSetDescriptor or -1.
|
||||
*/
|
||||
|
||||
private static int validateEventSource(EventSetDescriptor[] sesd, Class lc) {
|
||||
for (int i = 0; i < sesd.length; i++)
|
||||
if (lc.equals(sesd[i].getListenerType())) return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* setSource sets the adaptor instance to listen to the specified
|
||||
* source. This operation will fail if the source does not emit events
|
||||
* on the EventListener subinterface this adaptor implements.
|
||||
* </p>
|
||||
*
|
||||
* @param s the prospective event source.
|
||||
*
|
||||
* @exception IntrospectionException If any error with introspection
|
||||
* @exception Exception If any other error
|
||||
*/
|
||||
|
||||
public synchronized void setSource(Object s)
|
||||
throws IntrospectionException, Exception {
|
||||
|
||||
if (source != null && s != null && source.equals(s)) return;
|
||||
|
||||
if (source != null) removeAdaptorFromSource(); // unregister current
|
||||
|
||||
if (s == null) {
|
||||
source = null;
|
||||
addAdaptorMethod = null;
|
||||
removeAdaptorMethod = null;
|
||||
return;
|
||||
}
|
||||
|
||||
BeanInfo sbi = Introspector.getBeanInfo(s.getClass());
|
||||
EventSetDescriptor[] sesd = sbi.getEventSetDescriptors();
|
||||
|
||||
int i;
|
||||
|
||||
if ((i = validateEventSource(sesd, getListenerClass())) == -1) {
|
||||
throw new IllegalArgumentException("Object: " +
|
||||
s +
|
||||
" does not source: " +
|
||||
getListenerClassName()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// update state
|
||||
|
||||
Object olds = source;
|
||||
Method oldam = addAdaptorMethod;
|
||||
Method oldrm = removeAdaptorMethod;
|
||||
|
||||
source = s;
|
||||
addAdaptorMethod = sesd[i].getAddListenerMethod();
|
||||
removeAdaptorMethod = sesd[i].getRemoveListenerMethod();
|
||||
|
||||
try {
|
||||
addAdaptorToSource(); // register with new source
|
||||
} catch (Exception e) {
|
||||
|
||||
// something went wrong ... restore previous state.
|
||||
|
||||
source = olds;
|
||||
addAdaptorMethod = oldam;
|
||||
removeAdaptorMethod = oldrm;
|
||||
|
||||
if (source != null) addAdaptorToSource();
|
||||
|
||||
throw e; // reraise problem
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>
|
||||
* This method is called from simple EventListener method stubs of
|
||||
* dynamic subclasses in order to create and EncapsulatedEvent and
|
||||
* deliver it to the registered listeners.
|
||||
* </p>
|
||||
*
|
||||
* @param e The EventObject being encapsulated
|
||||
* @param lm The jav.lang.reflect.Method describing the listener Method.
|
||||
*
|
||||
* @exception Exception If any error occured in dispatch event
|
||||
*/
|
||||
|
||||
protected void fire(EventObject e, Method lm) throws Exception {
|
||||
Object [] a = new Object [] {e};
|
||||
// System.out.println("FIRE: " + lm.getName() + " for " + getListenerClass().getName());
|
||||
wrapper.dispatchEvent(a, getListenerClass(), lm);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This method is called from cracked EventListener method stubs of
|
||||
* dynamic subclasses in order to create and EncapsulatedEvent and
|
||||
* deliver it to the registered listeners.
|
||||
* </p>
|
||||
*
|
||||
* @param a The cracked Event arguments being encapsulated
|
||||
* @param lm The jav.lang.reflect.Method describing the listener Method.
|
||||
*
|
||||
* @exception Exception If any error occured in dispatch event
|
||||
*/
|
||||
|
||||
protected void fire(Object[] a, Method lm) throws Exception {
|
||||
//System.out.println("FIRE: " + lm.getName() + " for " + getListenerClass().getName());
|
||||
wrapper.dispatchEvent(a, getListenerClass(), lm);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* EncapsulatedEventListener's may raise exceptions to the originating
|
||||
* event source through an adaptor by throwing the actual exception within
|
||||
* an instance of EncapsulatedEventException.
|
||||
* </p>
|
||||
* <p>
|
||||
* This method is called to verify that it is allowable to re-raise the
|
||||
* exception from the adaptor to the source, by checking that the listener
|
||||
* method on the adaptor that the source delivered the original event to
|
||||
* is permitted to throw such an exception, otherwise a RuntimeException
|
||||
* is raised.
|
||||
* </p>
|
||||
*
|
||||
* @param ex the exception thrown by the listener
|
||||
* @param eel the listener instance that threw it
|
||||
* @param lm the adaptors listener method that must re-raise it
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
protected final void handleEventException(EncapsulatedEventException ex, EncapsulatedEventListener eel, Method lm) throws Exception {
|
||||
Class ec = ex.getExceptionClass();
|
||||
Class[] ext = lm.getExceptionTypes();
|
||||
|
||||
// let's see if the Exception encapsulated is one the source is expecting
|
||||
// if it is then throw it.
|
||||
|
||||
for (int i = 0; i < ext.length; i++) {
|
||||
if (ext[i].isAssignableFrom(ec)) throw ex.getException();
|
||||
}
|
||||
|
||||
// if we get here then the Exception the listener is trying
|
||||
// to throw isnt one the source is expecting ... so throw it as a RTE
|
||||
|
||||
throw new RuntimeException("Event Source [" +
|
||||
source +
|
||||
"] is not prepared to catch [" +
|
||||
ex.getException() +
|
||||
"] from listener [" +
|
||||
eel
|
||||
);
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* Adds this Adaptor to the Event Source
|
||||
*/
|
||||
|
||||
protected void addAdaptorToSource() {
|
||||
try {
|
||||
Object[] args = new Object[1];
|
||||
|
||||
args[0] = this;
|
||||
|
||||
addAdaptorMethod.invoke(source, args);
|
||||
} catch (InvocationTargetException ite) {
|
||||
throw new ProgrammingError("cannot add adaptor [" +
|
||||
this +
|
||||
"] to source [" +
|
||||
source +
|
||||
"] InvocationTargetException: " +
|
||||
ite.getMessage()
|
||||
);
|
||||
} catch (IllegalAccessException iae) {
|
||||
throw new ProgrammingError("cannot add adaptor [" +
|
||||
this +
|
||||
"] to source [" +
|
||||
source +
|
||||
"] IllegalAccessException: " +
|
||||
iae.getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes this Adaptor instance from the Event Source
|
||||
*/
|
||||
|
||||
protected void removeAdaptorFromSource() {
|
||||
try {
|
||||
Object[] args = new Object[1];
|
||||
|
||||
args[0] = this;
|
||||
|
||||
removeAdaptorMethod.invoke(source, args);
|
||||
} catch (InvocationTargetException ite) {
|
||||
System.err.println("cannot remove adaptor [" +
|
||||
this +
|
||||
"] from source [" +
|
||||
source +
|
||||
"] InvocationTargetException: " +
|
||||
ite.getMessage()
|
||||
);
|
||||
} catch (IllegalAccessException iae) {
|
||||
System.err.println("cannot remove adaptor [" +
|
||||
this +
|
||||
"] from source [" +
|
||||
source +
|
||||
"] IllegalAccessException: " +
|
||||
iae.getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
262
src/FESI/Interpreter/EventAdaptorGenerator.java
Normal file
262
src/FESI/Interpreter/EventAdaptorGenerator.java
Normal file
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
*
|
||||
* @(#) EventAdaptorGenerator.java 1.3@(#)
|
||||
*
|
||||
* Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
|
||||
*
|
||||
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
|
||||
* SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
|
||||
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
|
||||
* THIS SOFTWARE OR ITS DERIVATIVES.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* FESI.Interpreter.EventAdaptorGenerator
|
||||
* </p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @author Laurence P. G. Cable
|
||||
*/
|
||||
|
||||
package FESI.Interpreter;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.ClassLoader;
|
||||
import java.lang.ClassFormatError;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
|
||||
import FESI.ClassFile.EventAdaptorClassFile;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The EventAdaptorGenerator is the class responsible for
|
||||
* dynamically generating classes that adapt arbitrary sub-interfaces of
|
||||
* java.util.EventListener to FESI.ClassFile.EventListener.
|
||||
* <p>
|
||||
* It is largely derived of EventAdaptorGenerator.java(1.3) of Sun,
|
||||
* delivered as an example of for the bean development tools.
|
||||
* </p>
|
||||
* <p>
|
||||
* The dynamically generated adaptor class is implemented as a subclass of
|
||||
* FESI.ClassFile and implements the specified
|
||||
* sub-interface of java.util.EventListener. Each listener method of the
|
||||
* sub-interface calls into FESI.ClassFile.EncapsulatedEvent to
|
||||
* deliver the event to the proper EcmaScript event processing routine.
|
||||
* </p>
|
||||
*/
|
||||
|
||||
public final class EventAdaptorGenerator extends ClassLoader {
|
||||
|
||||
private static String packagePrefix = "FESI.ClassFile";
|
||||
private static String adaptorInfix = ".DYN_EE_ADAPTOR.";
|
||||
|
||||
private static String adaptorClassNamePrefix = packagePrefix + adaptorInfix;
|
||||
|
||||
|
||||
/**
|
||||
* @return debug status
|
||||
*/
|
||||
|
||||
private boolean debug() { return false; }
|
||||
|
||||
/*
|
||||
* one instance of the Generator ...
|
||||
*/
|
||||
|
||||
private static EventAdaptorGenerator generator =
|
||||
new EventAdaptorGenerator();
|
||||
|
||||
/**
|
||||
* constructs a generator
|
||||
*/
|
||||
|
||||
private EventAdaptorGenerator() { super(); }
|
||||
|
||||
/**
|
||||
* invokes the ClassLoader to load the named class
|
||||
*
|
||||
* @param className the name of the class to load
|
||||
*
|
||||
* @return the newly loaded Class
|
||||
*
|
||||
* @exception ClassNotFoundException if the class cannnot be loaded
|
||||
*
|
||||
* @see java.lang.ClassLoader
|
||||
*/
|
||||
|
||||
private static Class loadClassNamed(String className)
|
||||
throws ClassNotFoundException {
|
||||
return generator.loadClass(className);
|
||||
}
|
||||
|
||||
/**
|
||||
* invoke the Class Loader to generate and load a dynamically generated
|
||||
* adaptor subclass of EventAdaptor to adapt to the specified
|
||||
* sub-interface of java.util.EventListener.
|
||||
*
|
||||
* @param lc The java.lang.Class object for the sub-interface to adapt
|
||||
*
|
||||
* @return the newly loaded dynamic adaptor class.
|
||||
*
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
|
||||
static Class getAdaptorClassForListenerClass(Class lc)
|
||||
throws ClassNotFoundException {
|
||||
|
||||
// we assume that lc is a subinterface of java.util.EventListener
|
||||
|
||||
return loadClassNamed(mapListenerNameToAdaptorName(lc.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* invoke the Class Loader to generate and load a dynamically generated
|
||||
* adaptor subclass of EncapsulatedEventAdaptor.
|
||||
*
|
||||
* @return the newly loaded class
|
||||
*
|
||||
* @throw ClassNotFoundException
|
||||
*/
|
||||
|
||||
static Class getAdaptorClassForListenerClass(String lcn)
|
||||
throws ClassNotFoundException {
|
||||
|
||||
// we assume that lc is a subinterface of java.util.EventListener
|
||||
|
||||
return loadClassNamed(mapListenerNameToAdaptorName(lcn));
|
||||
}
|
||||
|
||||
/**
|
||||
* loadClass will lookup classes with its Class Loader or via the
|
||||
* system, unless the class requested is a dynamic adaptor class,
|
||||
* then we invoke the code generator to create the adaptor class
|
||||
* on the fly.
|
||||
*
|
||||
* @return the newly loaded dynamic adaptor class.
|
||||
*
|
||||
* @exception ClassNotFoundException If the class cannot be found
|
||||
*/
|
||||
|
||||
protected Class loadClass(String className, boolean resolve)
|
||||
throws ClassNotFoundException {
|
||||
Class c = findLoadedClass(className); // check the cache
|
||||
|
||||
if (debug()) System.err.println("loadClass(" + className + ")");
|
||||
|
||||
if (c == null) { // not in cache
|
||||
if (isAdaptorClassName(className)) { // is this an adaptor?
|
||||
|
||||
// generate an adaptor to this sub-interface
|
||||
|
||||
c = generateAdaptorClass(className);
|
||||
} else try { // regular class
|
||||
ClassLoader mycl = this.getClass().getClassLoader();
|
||||
|
||||
// look for the requeseted class elsewhere ...
|
||||
|
||||
if (mycl != null) {
|
||||
c = mycl.loadClass(className); // try the CL that loaded me
|
||||
}
|
||||
|
||||
if (c == null) {
|
||||
c = findSystemClass(className); // try system for class
|
||||
}
|
||||
} catch (NoClassDefFoundError ncdfe) {
|
||||
throw new ClassNotFoundException(ncdfe.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (c != null) { // class found?
|
||||
if (resolve) resolveClass(c);
|
||||
} else
|
||||
throw new ClassNotFoundException(className);
|
||||
|
||||
if (debug()) System.err.println("loaded: " + c.getName());
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* map the EventListener subclass name to the synthetic name for a
|
||||
* dynamically generated adaptor class.
|
||||
*
|
||||
* @return the synthesised adaptor class name.
|
||||
*/
|
||||
|
||||
private static String mapListenerNameToAdaptorName(String listenerName) {
|
||||
return adaptorClassNamePrefix + listenerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to determine if class name is that of a dynamically generated
|
||||
* EncapsulatedEventAdaptor Class as created by this Generator.
|
||||
*
|
||||
* This is defined to be so, iff the class prefix matches the one
|
||||
* we generate.
|
||||
*
|
||||
* @return is this the "name" of an adaptor class?
|
||||
*/
|
||||
|
||||
private static boolean isAdaptorClassName(String className) {
|
||||
return className.startsWith(adaptorClassNamePrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the name of the sub-interface we are adapting from the name of the adaptor class.
|
||||
*/
|
||||
|
||||
public static String getBaseNameFromAdaptorName(String className) {
|
||||
return ((className != null && isAdaptorClassName(className))
|
||||
? className.substring(adaptorClassNamePrefix.length())
|
||||
: null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* generates the dynamic Adaptor class to bridge the EventListener
|
||||
* interface to the EncapsulatedListener interface.
|
||||
*
|
||||
* @return the newly generated adaptor class.
|
||||
*
|
||||
*/
|
||||
|
||||
private Class generateAdaptorClass(String className) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
|
||||
byte[] cimpl = null;
|
||||
Class clazz = null;
|
||||
|
||||
if (debug()) System.err.println("generateAdaptorClass(" + className + ")");
|
||||
|
||||
try { // generate the classfile into the stream
|
||||
new EventAdaptorClassFile(className, (OutputStream)baos);
|
||||
} catch (IOException ioe) {
|
||||
return null;
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
return null;
|
||||
}
|
||||
|
||||
cimpl = baos.toByteArray(); // get the classfile as an array of bytes
|
||||
|
||||
// now "try to" intern it into the runtime ...
|
||||
|
||||
try {
|
||||
clazz = defineClass(className, cimpl, 0, cimpl.length);
|
||||
} catch (ClassFormatError ex) {
|
||||
System.err.println("Failed to define adaptor for " + className);
|
||||
System.err.println("Caught: " + ex);
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
return clazz;
|
||||
}
|
||||
}
|
78
src/FESI/Interpreter/FESIResourceConnection.java
Normal file
78
src/FESI/Interpreter/FESIResourceConnection.java
Normal file
|
@ -0,0 +1,78 @@
|
|||
// FESIResourceConnection.java
|
||||
// FESI Copyright (c) Jean-Marc Lugrin, 1999
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package FESI.Interpreter;
|
||||
|
||||
import FESI.Data.ESLoader;
|
||||
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
|
||||
public class FESIResourceConnection extends URLConnection {
|
||||
private static boolean debug = false;
|
||||
|
||||
private Object resource; // the resource we are fetching
|
||||
private String cookie; // identification of the loader instance to use
|
||||
private String name; // name of the resource
|
||||
private final String prefix = LocalClassLoader.urlPrefix;
|
||||
private final int prefixLength = prefix.length();
|
||||
|
||||
public FESIResourceConnection (URL url)
|
||||
throws MalformedURLException, IOException
|
||||
{
|
||||
super(url);
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** new FESIResourceConnection('"+url+"')");
|
||||
String file = url.getFile();
|
||||
if (file.startsWith("/")) {
|
||||
file = file.substring(1);
|
||||
}
|
||||
if (! file.startsWith(prefix)) {
|
||||
throw new MalformedURLException("FESIResource file should start with /" + prefix);
|
||||
}
|
||||
cookie = file.substring(prefixLength, file.indexOf("/+/"));
|
||||
name = file.substring(file.indexOf("/+/")+3);
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** cookie: " + cookie + ", name: " + name);
|
||||
}
|
||||
|
||||
public void connect() throws IOException {
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** Connect: cookie: " + cookie + ", name: " + name);
|
||||
Object o = LocalClassLoader.getLocalResource(cookie, name);
|
||||
if (o == null) {
|
||||
resource = null;
|
||||
return;
|
||||
} else {
|
||||
resource = o;
|
||||
}
|
||||
}
|
||||
|
||||
public Object getContent() throws IOException {
|
||||
if (!connected) {
|
||||
connect();
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
if (!connected) {
|
||||
connect();
|
||||
}
|
||||
if (resource instanceof InputStream) {
|
||||
return (InputStream) resource;
|
||||
}
|
||||
return LocalClassLoader.getLocalResourceAsStream(cookie, name);
|
||||
}
|
||||
}
|
512
src/FESI/Interpreter/FesiHashtable.java
Normal file
512
src/FESI/Interpreter/FesiHashtable.java
Normal file
|
@ -0,0 +1,512 @@
|
|||
/*
|
||||
* FesiHashtable, adapted from:
|
||||
* @(#)Hashtable.java 1.41 97/01/28
|
||||
*
|
||||
* Copyright (c) 1995, 1996 Sun Microsystems, Inc. All Rights Reserved.
|
||||
*
|
||||
* This software is the confidential and proprietary information of Sun
|
||||
* Microsystems, Inc. ("Confidential Information"). You shall not
|
||||
* disclose such Confidential Information and shall use it only in
|
||||
* accordance with the terms of the license agreement you entered into
|
||||
* with Sun.
|
||||
*
|
||||
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
|
||||
* SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
|
||||
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
|
||||
* THIS SOFTWARE OR ITS DERIVATIVES.
|
||||
*
|
||||
* CopyrightVersion 1.1_beta
|
||||
*
|
||||
* Adapted by JM Lugrin for FESI
|
||||
* Synchronization removed (speed up, evaluator already synchronized)
|
||||
* Data type specific for object property list.
|
||||
* Externally provided hash code (to avoid recalculating the hash code.
|
||||
* Support the hidden and readonly properties (not enforced by this class).
|
||||
* Optimize compare for interned strings and poor hash function.
|
||||
*
|
||||
*/
|
||||
|
||||
package FESI.Interpreter;
|
||||
|
||||
import FESI.Data.ESValue;
|
||||
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* Hashtable collision list.
|
||||
*/
|
||||
class HashtableEntry {
|
||||
int hash;
|
||||
String key;
|
||||
ESValue value;
|
||||
HashtableEntry next;
|
||||
boolean hidden;
|
||||
boolean readonly;
|
||||
|
||||
protected Object clone() {
|
||||
HashtableEntry entry = new HashtableEntry();
|
||||
entry.hash = hash;
|
||||
entry.key = key;
|
||||
entry.value = value;
|
||||
entry.hidden = hidden;
|
||||
entry.readonly = readonly;
|
||||
entry.next = (next != null) ? (HashtableEntry)next.clone() : null;
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class implements a hashtable, which maps keys to values. Any
|
||||
* non-<code>null</code> object can be used as a key or as a value.
|
||||
* <p>
|
||||
* To successfully store and retrieve objects from a hashtable, the
|
||||
* objects used as keys must implement the <code>hashCode</code>
|
||||
* method and the <code>equals</code> method.
|
||||
* <p>
|
||||
* An instance of <code>Hashtable</code> has two parameters that
|
||||
* affect its efficiency: its <i>capacity</i> and its <i>load
|
||||
* factor</i>. The load factor should be between 0.0 and 1.0. When
|
||||
* the number of entries in the hashtable exceeds the product of the
|
||||
* load factor and the current capacity, the capacity is increased by
|
||||
* calling the <code>rehash</code> method. Larger load factors use
|
||||
* memory more efficiently, at the expense of larger expected time
|
||||
* per lookup.
|
||||
* <p>
|
||||
* If many entries are to be made into a <code>Hashtable</code>,
|
||||
* creating it with a sufficiently large capacity may allow the
|
||||
* entries to be inserted more efficiently than letting it perform
|
||||
* automatic rehashing as needed to grow the table.
|
||||
* <p>
|
||||
* This example creates a hashtable of numbers. It uses the names of
|
||||
* the numbers as keys:
|
||||
* <p><blockquote><pre>
|
||||
* Hashtable numbers = new Hashtable();
|
||||
* numbers.put("one", new Integer(1));
|
||||
* numbers.put("two", new Integer(2));
|
||||
* numbers.put("three", new Integer(3));
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* To retrieve a number, use the following code:
|
||||
* <p><blockquote><pre>
|
||||
* Integer n = (Integer)numbers.get("two");
|
||||
* if (n != null) {
|
||||
* System.out.println("two = " + n);
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @author Arthur van Hoff
|
||||
* @version 1.41, 01/28/97
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
* @see java.lang.Object#hashCode()
|
||||
* @see java.util.Hashtable#rehash()
|
||||
* @since JDK1.0
|
||||
*/
|
||||
public
|
||||
class FesiHashtable implements Cloneable {
|
||||
/**
|
||||
* The hash table data.
|
||||
*/
|
||||
private transient HashtableEntry table[];
|
||||
|
||||
/**
|
||||
* The total number of entries in the hash table.
|
||||
*/
|
||||
private transient int count;
|
||||
|
||||
/**
|
||||
* Rehashes the table when count exceeds this threshold.
|
||||
*/
|
||||
private int threshold;
|
||||
|
||||
/**
|
||||
* The load factor for the hashtable.
|
||||
*/
|
||||
private float loadFactor;
|
||||
|
||||
/**
|
||||
* Constructs a new, empty hashtable with the specified initial
|
||||
* capacity and the specified load factor.
|
||||
*
|
||||
* @param initialCapacity the initial capacity of the hashtable.
|
||||
* @param loadFactor a number between 0.0 and 1.0.
|
||||
* @exception IllegalArgumentException if the initial capacity is less
|
||||
* than or equal to zero, or if the load factor is less than
|
||||
* or equal to zero.
|
||||
* @since JDK1.0
|
||||
*/
|
||||
public FesiHashtable(int initialCapacity, float loadFactor) {
|
||||
if ((initialCapacity <= 0) || (loadFactor <= 0.0)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.loadFactor = loadFactor;
|
||||
table = new HashtableEntry[initialCapacity];
|
||||
threshold = (int)(initialCapacity * loadFactor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new, empty hashtable with the specified initial capacity
|
||||
* and default load factor.
|
||||
*
|
||||
* @param initialCapacity the initial capacity of the hashtable.
|
||||
* @since JDK1.0
|
||||
*/
|
||||
public FesiHashtable(int initialCapacity) {
|
||||
this(initialCapacity, 0.75f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new, empty hashtable with a default capacity and load
|
||||
* factor.
|
||||
*
|
||||
* @since JDK1.0
|
||||
*/
|
||||
public FesiHashtable() {
|
||||
this(27, 0.75f); // a smaller prime than the original 27
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of keys in this hashtable.
|
||||
*
|
||||
* @return the number of keys in this hashtable.
|
||||
* @since JDK1.0
|
||||
*/
|
||||
public int size() {
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if this hashtable maps no keys to values.
|
||||
*
|
||||
* @return <code>true</code> if this hashtable maps no keys to values;
|
||||
* <code>false</code> otherwise.
|
||||
* @since JDK1.0
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return count == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an enumeration of the keys in this hashtable.
|
||||
*
|
||||
* @return an enumeration of the keys in this hashtable.
|
||||
* @see java.util.Enumeration
|
||||
* @see java.util.Hashtable#elements()
|
||||
* @since JDK1.0
|
||||
*/
|
||||
public Enumeration keys() {
|
||||
return new HashtableEnumerator(table,true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an enumeration of the values in this hashtable.
|
||||
* Use the Enumeration methods on the returned object to fetch the elements
|
||||
* sequentially.
|
||||
*
|
||||
* @return an enumeration of the values in this hashtable.
|
||||
* @see java.util.Enumeration
|
||||
* @see java.util.Hashtable#keys()
|
||||
* @since JDK1.0
|
||||
*/
|
||||
public Enumeration elements() {
|
||||
return new HashtableEnumerator(table, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the specified object is a key in this hashtable.
|
||||
*
|
||||
* @param key possible key.
|
||||
* @return <code>true</code> if the specified object is a key in this
|
||||
* hashtable; <code>false</code> otherwise.
|
||||
*/
|
||||
|
||||
public boolean containsKey(String key, int hash) {
|
||||
HashtableEntry tab[] = table;
|
||||
int index = (hash & 0x7FFFFFFF) % tab.length;
|
||||
for (HashtableEntry e = tab[index] ; e != null ; e = e.next) {
|
||||
if ((e.key==key ) || ((e.hash == hash) && e.key.equals(key))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value to which the specified key is mapped in this hashtable.
|
||||
*
|
||||
* @param key a key in the hashtable.
|
||||
* @param hash the key hashtable.
|
||||
* @return the value to which the key is mapped in this hashtable;
|
||||
* <code>null</code> if the key is not mapped to any value in
|
||||
* this hashtable.
|
||||
*/
|
||||
|
||||
public ESValue get(String key, int hash) {
|
||||
HashtableEntry tab[] = table;
|
||||
int index = (hash & 0x7FFFFFFF) % tab.length;
|
||||
for (HashtableEntry e = tab[index] ; e != null ; e = e.next) {
|
||||
if ((e.key==key ) || ((e.hash == hash) && e.key.equals(key))) {
|
||||
return e.value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a property is hidden (return false if not present).
|
||||
*
|
||||
* @param key a key in the hashtable.
|
||||
* @param hash the key hashtable.
|
||||
* @return true if hidden.
|
||||
*/
|
||||
|
||||
public boolean isHidden(String key, int hash) {
|
||||
HashtableEntry tab[] = table;
|
||||
int index = (hash & 0x7FFFFFFF) % tab.length;
|
||||
for (HashtableEntry e = tab[index] ; e != null ; e = e.next) {
|
||||
if ((e.key==key ) || ((e.hash == hash) && e.key.equals(key))) {
|
||||
return e.hidden;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if a property is readonly (return false if not present).
|
||||
*
|
||||
* @param key a key in the hashtable.
|
||||
* @param hash the key hashtable.
|
||||
* @return true if hidden.
|
||||
*/
|
||||
|
||||
public boolean isReadonly(String key, int hash) {
|
||||
HashtableEntry tab[] = table;
|
||||
int index = (hash & 0x7FFFFFFF) % tab.length;
|
||||
for (HashtableEntry e = tab[index] ; e != null ; e = e.next) {
|
||||
if ((e.key==key ) || ((e.hash == hash) && e.key.equals(key))) {
|
||||
return e.readonly;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rehashes the contents of the hashtable into a hashtable with a
|
||||
* larger capacity. This method is called automatically when the
|
||||
* number of keys in the hashtable exceeds this hashtable's capacity
|
||||
* and load factor.
|
||||
*
|
||||
* @since JDK1.0
|
||||
*/
|
||||
protected void rehash() {
|
||||
int oldCapacity = table.length;
|
||||
HashtableEntry oldTable[] = table;
|
||||
|
||||
int newCapacity = oldCapacity * 2 + 1;
|
||||
if (newCapacity<101) newCapacity = 101; // Ensure a prime
|
||||
HashtableEntry newTable[] = new HashtableEntry[newCapacity];
|
||||
|
||||
threshold = (int)(newCapacity * loadFactor);
|
||||
table = newTable;
|
||||
|
||||
//System.out.println("rehash old=" + oldCapacity + ", new=" + newCapacity + ", thresh=" + threshold + ", count=" + count);
|
||||
|
||||
for (int i = oldCapacity ; i-- > 0 ;) {
|
||||
for (HashtableEntry old = oldTable[i] ; old != null ; ) {
|
||||
HashtableEntry e = old;
|
||||
old = old.next;
|
||||
|
||||
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
|
||||
e.next = newTable[index];
|
||||
newTable[index] = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the specified <code>key</code> to the specified
|
||||
* <code>value</code> in this hashtable. Neither the key nor the
|
||||
* value can be <code>null</code>.
|
||||
* <p>
|
||||
* The value can be retrieved by calling the <code>get</code> method
|
||||
* with a key that is equal to the original key.
|
||||
*
|
||||
* @param key the hashtable key.
|
||||
* @param hash the hash value.
|
||||
* @param hidden true if the entry must not be enumerated.
|
||||
* @param readonly true if the entry must not be deleted.
|
||||
* @param value the value.
|
||||
* @return the previous value of the specified key in this hashtable,
|
||||
* or <code>null</code> if it did not have one.
|
||||
* @exception NullPointerException if the key or value is
|
||||
* <code>null</code>.
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
* @see java.util.Hashtable#get(java.lang.Object)
|
||||
* @since JDK1.0
|
||||
*/
|
||||
public ESValue put(String key, int hash, boolean hidden, boolean readonly, ESValue value) {
|
||||
// Make sure the value is not null
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
// Makes sure the key is not already in the hashtable.
|
||||
HashtableEntry tab[] = table;
|
||||
int index = (hash & 0x7FFFFFFF) % tab.length;
|
||||
for (HashtableEntry e = tab[index] ; e != null ; e = e.next) {
|
||||
if ((e.hash == hash) && e.key.equals(key)) {
|
||||
ESValue old = e.value;
|
||||
e.value = value;
|
||||
return old;
|
||||
}
|
||||
}
|
||||
|
||||
if (count >= threshold) {
|
||||
// Rehash the table if the threshold is exceeded
|
||||
rehash();
|
||||
return put(key, hash, hidden, readonly, value);
|
||||
}
|
||||
|
||||
// Creates the new entry.
|
||||
HashtableEntry e = new HashtableEntry();
|
||||
e.hash = hash;
|
||||
e.key = key;
|
||||
e.value = value;
|
||||
e.hidden = hidden;
|
||||
e.readonly = readonly;
|
||||
e.next = tab[index];
|
||||
tab[index] = e;
|
||||
count++;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the key (and its corresponding value) from this
|
||||
* hashtable. This method does nothing if the key is not in the hashtable.
|
||||
*
|
||||
* @param key the key that needs to be removed.
|
||||
* @return the value to which the key had been mapped in this hashtable,
|
||||
* or <code>null</code> if the key did not have a mapping.
|
||||
* @since JDK1.0
|
||||
*/
|
||||
public ESValue remove(String key, int hash) {
|
||||
HashtableEntry tab[] = table;
|
||||
int index = (hash & 0x7FFFFFFF) % tab.length;
|
||||
for (HashtableEntry e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {
|
||||
if ((e.hash == hash) && e.key.equals(key)) {
|
||||
if (prev != null) {
|
||||
prev.next = e.next;
|
||||
} else {
|
||||
tab[index] = e.next;
|
||||
}
|
||||
count--;
|
||||
return e.value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears this hashtable so that it contains no keys.
|
||||
*
|
||||
* @since JDK1.0
|
||||
*/
|
||||
public void clear() {
|
||||
HashtableEntry tab[] = table;
|
||||
for (int index = tab.length; --index >= 0; )
|
||||
tab[index] = null;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a shallow copy of this hashtable. The keys and values
|
||||
* themselves are not cloned.
|
||||
* This is a relatively expensive operation.
|
||||
*
|
||||
* @return a clone of the hashtable.
|
||||
* @since JDK1.0
|
||||
*/
|
||||
public Object clone() {
|
||||
try {
|
||||
FesiHashtable t = (FesiHashtable)super.clone();
|
||||
t.table = new HashtableEntry[table.length];
|
||||
for (int i = table.length ; i-- > 0 ; ) {
|
||||
t.table[i] = (table[i] != null)
|
||||
? (HashtableEntry)table[i].clone() : null;
|
||||
}
|
||||
return t;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
// this shouldn't happen, since we are Cloneable
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a rather long string representation of this hashtable.
|
||||
*
|
||||
* @return a string representation of this hashtable.
|
||||
* @since JDK1.0
|
||||
*/
|
||||
public String toString() {
|
||||
int max = size() - 1;
|
||||
StringBuffer buf = new StringBuffer();
|
||||
Enumeration k = keys();
|
||||
Enumeration e = elements();
|
||||
buf.append("{");
|
||||
|
||||
for (int i = 0; i <= max; i++) {
|
||||
String s1 = k.nextElement().toString();
|
||||
String s2 = e.nextElement().toString();
|
||||
buf.append(s1 + "=" + s2);
|
||||
if (i < max) {
|
||||
buf.append(", ");
|
||||
}
|
||||
}
|
||||
buf.append("}");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A hashtable enumerator class. This class should remain opaque
|
||||
* to the client. It will use the Enumeration interface.
|
||||
*/
|
||||
class HashtableEnumerator implements Enumeration {
|
||||
boolean keys;
|
||||
int index;
|
||||
HashtableEntry table[];
|
||||
HashtableEntry entry;
|
||||
|
||||
HashtableEnumerator(HashtableEntry table[], boolean keys) {
|
||||
this.table = table;
|
||||
this.keys = keys;
|
||||
this.index = table.length;
|
||||
}
|
||||
|
||||
public boolean hasMoreElements() {
|
||||
if (entry != null) {
|
||||
return true;
|
||||
}
|
||||
while (index-- > 0) {
|
||||
if ((entry = table[index]) != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object nextElement() {
|
||||
if (entry == null) {
|
||||
while ((index-- > 0) && ((entry = table[index]) == null));
|
||||
}
|
||||
if (entry != null) {
|
||||
HashtableEntry e = entry;
|
||||
entry = e.next;
|
||||
return keys ? ((Object) e.key) : ((Object) e.value);
|
||||
}
|
||||
throw new java.util.NoSuchElementException("FesiHashtableEnumerator");
|
||||
}
|
||||
}
|
39
src/FESI/Interpreter/FileEvaluationSource.java
Normal file
39
src/FESI/Interpreter/FileEvaluationSource.java
Normal file
|
@ -0,0 +1,39 @@
|
|||
// FileEvaluationSource.java
|
||||
// FESI Copyright (c) Jean-Marc Lugrin, 1999
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package FESI.Interpreter;
|
||||
|
||||
/**
|
||||
* Describe a file used as a source.
|
||||
*/
|
||||
public class FileEvaluationSource extends EvaluationSource {
|
||||
|
||||
private String theFileName;
|
||||
|
||||
/**
|
||||
* Create a file source description
|
||||
* @param theFileName Describe the source file name
|
||||
* @param previousSource Describe the calling source
|
||||
*/
|
||||
public FileEvaluationSource(String theFileName, EvaluationSource previousSource) {
|
||||
super(previousSource);
|
||||
this.theFileName = theFileName;
|
||||
}
|
||||
protected String getEvaluationSourceText() {
|
||||
return "in file: '" + theFileName + "'";
|
||||
}
|
||||
}
|
43
src/FESI/Interpreter/FunctionEvaluationSource.java
Normal file
43
src/FESI/Interpreter/FunctionEvaluationSource.java
Normal file
|
@ -0,0 +1,43 @@
|
|||
// FunctionEvaluationSource.java
|
||||
// FESI Copyright (c) Jean-Marc Lugrin, 1999
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package FESI.Interpreter;
|
||||
|
||||
/**
|
||||
* Describe a function used as a source.
|
||||
*/
|
||||
|
||||
public class FunctionEvaluationSource extends EvaluationSource {
|
||||
|
||||
private String theFunctionName;
|
||||
|
||||
/**
|
||||
* Create a function source description
|
||||
* @param evaluationSource Describe the function name
|
||||
* @param previousSource Describe the calling source
|
||||
*/
|
||||
public FunctionEvaluationSource(
|
||||
EvaluationSource evaluationSource,
|
||||
String theFunctionName) {
|
||||
super(evaluationSource);
|
||||
this.theFunctionName = theFunctionName;
|
||||
}
|
||||
protected String getEvaluationSourceText() {
|
||||
return "of function '" + theFunctionName +"' " +
|
||||
previousSource.getEvaluationSourceText();
|
||||
}
|
||||
}
|
1526
src/FESI/Interpreter/Interpret.java
Normal file
1526
src/FESI/Interpreter/Interpret.java
Normal file
File diff suppressed because it is too large
Load diff
44
src/FESI/Interpreter/JarEvaluationSource.java
Normal file
44
src/FESI/Interpreter/JarEvaluationSource.java
Normal file
|
@ -0,0 +1,44 @@
|
|||
// JarEvaluationSource.java
|
||||
// FESI Copyright (c) Jean-Marc Lugrin, 1999
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package FESI.Interpreter;
|
||||
|
||||
|
||||
/**
|
||||
* Describe a jar entry used as a source.
|
||||
*/
|
||||
public class JarEvaluationSource extends EvaluationSource {
|
||||
|
||||
private String theJarName;
|
||||
private String theEntryName;
|
||||
|
||||
/**
|
||||
* Create a jar source description
|
||||
* @param theJarName Describe the source jar
|
||||
* @param theEntryName Describe the source entry in the jar
|
||||
* @param previousSource Describe the calling source
|
||||
*/
|
||||
public JarEvaluationSource(String theJarName, String theEntryName, EvaluationSource previousSource) {
|
||||
super(previousSource);
|
||||
this.theJarName = theJarName;
|
||||
this.theEntryName = theEntryName;
|
||||
}
|
||||
|
||||
protected String getEvaluationSourceText() {
|
||||
return "in entry: '" + theEntryName + "' of jar: '" + theJarName + "'";
|
||||
}
|
||||
}
|
52
src/FESI/Interpreter/LineEvaluationSource.java
Normal file
52
src/FESI/Interpreter/LineEvaluationSource.java
Normal file
|
@ -0,0 +1,52 @@
|
|||
// LineEvaluationSource.java
|
||||
// FESI Copyright (c) Jean-Marc Lugrin, 1999
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package FESI.Interpreter;
|
||||
|
||||
|
||||
/**
|
||||
* Add line number to an evaluation source
|
||||
*/
|
||||
|
||||
public class LineEvaluationSource extends EvaluationSource {
|
||||
|
||||
private int theLineNumber;
|
||||
|
||||
/**
|
||||
* Create a source description being the line number of a previous description
|
||||
* @param theLineNumber Describe the line number
|
||||
* @param previousSource Describe the calling source
|
||||
*/
|
||||
public LineEvaluationSource(int theLineNumber, EvaluationSource previousSource) {
|
||||
super(previousSource);
|
||||
this.theLineNumber = theLineNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the string describing the line number
|
||||
*/
|
||||
protected String getEvaluationSourceText() {
|
||||
return "at line " + theLineNumber + " " + previousSource.getEvaluationSourceText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the line number of the error if possible
|
||||
*/
|
||||
public int getLineNumber() {
|
||||
return theLineNumber;
|
||||
}
|
||||
}
|
375
src/FESI/Interpreter/LocalClassLoader.java
Normal file
375
src/FESI/Interpreter/LocalClassLoader.java
Normal file
|
@ -0,0 +1,375 @@
|
|||
// LocalClassLoaderjava
|
||||
// FESI Copyright (c) Jean-Marc Lugrin, 1999
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package FESI.Interpreter;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Properties;
|
||||
import java.util.zip.*;
|
||||
import java.net.*;
|
||||
import java.awt.*;
|
||||
|
||||
|
||||
// import sun.awt.image.*; // byte array image source
|
||||
|
||||
import FESI.Data.ESLoader;
|
||||
import FESI.Exceptions.*;
|
||||
|
||||
|
||||
/**
|
||||
* This code is largely inspired of Java Examples in a Nutshell.
|
||||
* The loaders are shared by all instance of the evaluator, they
|
||||
* are therefore synchronized when needed.
|
||||
**/
|
||||
|
||||
public class LocalClassLoader extends ClassLoader {
|
||||
|
||||
public final static String urlPrefix = "FESI";
|
||||
public final static String resourceName = "fesiresource"; // must match class name
|
||||
private static Hashtable loadersByCookie = new Hashtable(); // loaders by cookie
|
||||
private static Hashtable loadersByFilename = new Hashtable(); // loaders by file name
|
||||
private static final String protocolPathProp = "java.protocol.handler.pkgs";
|
||||
private static int cookieCount = 1;
|
||||
private static char fileSep = System.getProperty("file.separator", "/").charAt(0);
|
||||
|
||||
/** This is the directory from which the classes will be loaded */
|
||||
private boolean asJar;
|
||||
private String myCookie;
|
||||
private ZipFile zipFile = null;
|
||||
private File directoryFile = null;
|
||||
|
||||
/** The constructor. Just initialize the directory */
|
||||
private LocalClassLoader(File dir) {
|
||||
this.directoryFile = dir;
|
||||
this.asJar = false;
|
||||
myCookie = "fcl"+cookieCount++;
|
||||
loadersByCookie.put(myCookie, this);
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** New class loader: " + this);
|
||||
}
|
||||
|
||||
private LocalClassLoader(ZipFile zipFile) {
|
||||
this.asJar = true;
|
||||
this.zipFile = zipFile;
|
||||
myCookie = "fcl"+cookieCount++;
|
||||
loadersByCookie.put(myCookie, this);
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** New class loader: " + this);
|
||||
}
|
||||
|
||||
/** The factory. Make a loader if none exist for the same source */
|
||||
synchronized public static LocalClassLoader makeLocalClassLoader(String filename) throws EcmaScriptException{
|
||||
LocalClassLoader loader = null;
|
||||
boolean asJar = false;
|
||||
File file = new File(filename);
|
||||
String fullname = null;
|
||||
if (file.isFile()) {
|
||||
try {
|
||||
fullname = file.getCanonicalPath();
|
||||
} catch (IOException e) {
|
||||
fullname = file.getAbsolutePath(); // Hope this work
|
||||
}
|
||||
loader = (LocalClassLoader) loadersByFilename.get(fullname);
|
||||
if (loader == null) {
|
||||
ZipFile zipFile = null;
|
||||
try {
|
||||
zipFile = new ZipFile(fullname);
|
||||
} catch (IOException e) {
|
||||
throw new EcmaScriptException("IO Error opening zip file '" + fullname + "' : " + e);
|
||||
}
|
||||
loader = new LocalClassLoader(zipFile);
|
||||
loadersByFilename.put(fullname, loader);
|
||||
} else {
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** loader in cache: " + loader);
|
||||
}
|
||||
} else if (file.isDirectory()) {
|
||||
try {
|
||||
fullname = file.getCanonicalPath();
|
||||
} catch (IOException e) {
|
||||
fullname = file.getAbsolutePath(); // Hope this work
|
||||
}
|
||||
loader = (LocalClassLoader) loadersByFilename.get(fullname);
|
||||
if (loader == null) {
|
||||
loader = new LocalClassLoader(file);
|
||||
loadersByFilename.put(fullname, loader);
|
||||
} else {
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** loader in cache: " + loader);
|
||||
}
|
||||
} else {
|
||||
throw new EcmaScriptException("No file or directory '" + filename + "' found");
|
||||
}
|
||||
return loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method that calls the 2-argument form of this method
|
||||
*
|
||||
* @param name The name of the class
|
||||
* @return the loaded class
|
||||
* @exception ClassNotFoundException If class cannot be loaded
|
||||
*/
|
||||
public Class loadClass(String name) throws ClassNotFoundException {
|
||||
return loadClass(name, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is one abstract method of ClassLoader that all subclasses must
|
||||
* define. Its job is to load an array of bytes from somewhere and to
|
||||
* pass them to defineClass(). If the resolve argument is true, it must
|
||||
* also call resolveClass(), which will do things like verify the presence
|
||||
* of the superclass. Because of this second step, this method may be
|
||||
* called to load superclasses that are system classes, and it must take
|
||||
* this into account.
|
||||
* @param classname The name of the class to load
|
||||
* @param resolve True if class must be resolved
|
||||
* @return the loaded class
|
||||
* @exception ClassNotFoundException If class cannot be loaded
|
||||
**/
|
||||
public Class loadClass(String classname, boolean resolve)
|
||||
throws ClassNotFoundException {
|
||||
// if (ESLoader.isDebugLoader()) System.out.println(" ** loadClass: " + classname);
|
||||
try {
|
||||
// Our ClassLoader superclass has a built-in cache of classes it has
|
||||
// already loaded. So, first check the cache.
|
||||
Class c = findLoadedClass(classname);
|
||||
|
||||
// After this method loads a class, it will be called again to
|
||||
// load the superclasses. Since these may be system classes, we've
|
||||
// got to be able to load those too. So try to load the class as
|
||||
// a system class (i.e. from the CLASSPATH) and ignore any errors
|
||||
if (c == null) {
|
||||
try { c = findSystemClass(classname); }
|
||||
catch (Exception e) {}
|
||||
}
|
||||
|
||||
// If the class wasn't found by either of the above attempts, then
|
||||
// try to load it from a file in (or beneath) the directory
|
||||
// specified when this ClassLoader object was created. Form the
|
||||
// filename by replacing all dots in the class name with
|
||||
// (platform-independent) file separators and by adding the
|
||||
// ".class" extension.
|
||||
// Alternatively try to load it from the jar file which was
|
||||
// specified.
|
||||
if (c == null) {
|
||||
|
||||
//String asEntryName = (asJar ? (classname.replace('.','/') + ".class") : (classname + ".class"));
|
||||
String asEntryName = classname.replace('.',(asJar ? '/' : fileSep)) + ".class";
|
||||
byte classbytes[] = getResourceBuffer(asEntryName);
|
||||
if (classbytes == null) {
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** class '" + classname + "' not loaded");
|
||||
throw new ClassNotFoundException("Class '" + classname + "' not foud by " + this);
|
||||
} else {
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** class '" + classname + "' loaded");
|
||||
// Now call an inherited method to convert those bytes into a Class
|
||||
c = defineClass(classname, classbytes, 0, classbytes.length);
|
||||
}
|
||||
}
|
||||
|
||||
// If the resolve argument is true, call the inherited resolveClass
|
||||
// method.
|
||||
if (resolve) resolveClass(c);
|
||||
|
||||
// And we're done. Return the Class object we've loaded.
|
||||
return c;
|
||||
}
|
||||
// If anything goes wrong, throw a ClassNotFoundException error
|
||||
catch (Exception e) {
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** Error loading '" + classname + "' by loader: " + this + ", " + e);
|
||||
throw new ClassNotFoundException(e.toString()); }
|
||||
}
|
||||
|
||||
public URL getResource(String name) {
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** getResource: '" + name + "' asked to: " + this);
|
||||
URL url = getSystemResource(name);
|
||||
if (url != null) {
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** URL found in system as: " + url);
|
||||
return url;
|
||||
}
|
||||
try {
|
||||
url = new URL(resourceName, null, "/" + urlPrefix + myCookie + "/+/" + name);
|
||||
if (ESLoader.isDebugLoader())System.out.println(" ** URL found as " + url);
|
||||
} catch (MalformedURLException e) {
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** Bad URL " + url + " " + e);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
public InputStream getResourceAsStream(String name) {
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** getResourceAsStream: '" + name + "' asked to: " + this);
|
||||
InputStream back = getSystemResourceAsStream(name);
|
||||
if (back != null) {
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** getResourceAsStream("+name+") is a system resource");
|
||||
return back;
|
||||
}
|
||||
return getLocalResourceAsStream(name);
|
||||
}
|
||||
|
||||
private byte [] getResourceBuffer(String name) {
|
||||
byte buf[] = null;
|
||||
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** getResourceBuffer, resource '" + name + "'");
|
||||
|
||||
if (asJar) {
|
||||
ZipEntry zipEntry = zipFile.getEntry(name);
|
||||
if (zipEntry == null) {
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** Resource '" + name + "'not found in jar by: " + this);
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
InputStream inputStream = zipFile.getInputStream(zipEntry);
|
||||
int limit = (int)zipEntry.getSize();
|
||||
buf = new byte[limit];
|
||||
|
||||
int total = 0;
|
||||
while (total < limit)
|
||||
{
|
||||
int ct = inputStream.read(buf,total,limit-total);
|
||||
total = total + ct;
|
||||
if (ct == 0) {
|
||||
if (ESLoader.isDebugLoader()) {
|
||||
System.out.println(" ** Error read entry '" + name + "' in jar, loader: " + this);
|
||||
System.out.println("Only " +
|
||||
total + " bytes out of " + limit + " read from entry '" +
|
||||
name + "' in jar '" + zipFile.getName() +"'");
|
||||
}
|
||||
throw new IOException ("Only " +
|
||||
total + " bytes out of " + limit + " read from entry '" +
|
||||
name + "' in jar '" + zipFile.getName() +"'");
|
||||
}
|
||||
}
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** Error reading jar: " + e);
|
||||
return null;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
try {
|
||||
// Create a File object. Interpret the filename relative to the
|
||||
// directory specified for this ClassLoader.
|
||||
File f = new File(directoryFile, name);
|
||||
|
||||
// Read from file
|
||||
// Get the length of the class file, allocate an array of bytes for
|
||||
// it, and read it in all at once.
|
||||
int length = (int) f.length();
|
||||
buf = new byte[length];
|
||||
DataInputStream in = new DataInputStream(new FileInputStream(f));
|
||||
in.readFully(buf);
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** Error reading file: " + e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
private Object getLocalResource(String name) {
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** getLocalResource, resource '" + name + "' asked to: " + this);
|
||||
|
||||
byte buf[] = getResourceBuffer(name);
|
||||
if (buf==null) return null;
|
||||
|
||||
if (name.endsWith(".gif") || name.endsWith(".jpeg")) {
|
||||
Image image = Toolkit.getDefaultToolkit().createImage(buf);
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** Returning image resource: " + image);
|
||||
return image;
|
||||
// return new ByteArrayImageSource(buf); // SUN specific method
|
||||
} else {
|
||||
ByteArrayInputStream s = new ByteArrayInputStream(buf);
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** Returning stream resource: " + s);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream getLocalResourceAsStream(String name) {
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** getLocalResourceAsStream, resource '" + name + "' asked to: " + this);
|
||||
if (asJar) {
|
||||
int limit;
|
||||
|
||||
try
|
||||
{
|
||||
ZipEntry zipEntry = zipFile.getEntry(name);
|
||||
|
||||
if (zipEntry != null) {
|
||||
InputStream inputStream = zipFile.getInputStream(zipEntry);
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** Resource found, returned as stream: " + inputStream);
|
||||
return inputStream;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** Exception when loading resource: " + name +
|
||||
": " + e);
|
||||
}
|
||||
} else {
|
||||
|
||||
// Create a File object. Interpret the filename relative to the
|
||||
// directory specified for this ClassLoader.
|
||||
File f = new File(directoryFile, name);
|
||||
|
||||
// Get the stream of this file.
|
||||
try {
|
||||
InputStream inputStream = new FileInputStream(f);
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** Resource found, returned as stream: " + inputStream);
|
||||
return inputStream;
|
||||
} catch (IOException e) {
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** Exception when loading resource: " + name +
|
||||
": " + e);
|
||||
}
|
||||
}
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** Resource not found: " + name);
|
||||
return null;
|
||||
}
|
||||
|
||||
public static InputStream getLocalResourceAsStream(String cookie,
|
||||
String name) {
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** static getLocalResourceAsStream, cookie: " + cookie + ", resource: " + name);
|
||||
LocalClassLoader cl = (LocalClassLoader) loadersByCookie.get(cookie);
|
||||
if (cl == null) {
|
||||
if (ESLoader.isDebugLoader()) System.err.println(" @@ LocalClassLoader cookie: " + cookie + " NOT FOUND !");
|
||||
return null;
|
||||
}
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** Classloader found: " + cl);
|
||||
return cl.getLocalResourceAsStream(name);
|
||||
}
|
||||
|
||||
public static Object getLocalResource(String cookie,
|
||||
String name) {
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** static getLocalResource, cookie: " + cookie + ", resource: " + name);
|
||||
LocalClassLoader cl = (LocalClassLoader) loadersByCookie.get(cookie);
|
||||
if (cl == null) {
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" @@ LocalClassLoader cookie: " + cookie + " NOT FOUND !");
|
||||
return null;
|
||||
}
|
||||
if (ESLoader.isDebugLoader()) System.out.println(" ** Classloader found: " + cl);
|
||||
return cl.getLocalResource(name);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "LocalClassLoader["+myCookie+"]:" +
|
||||
(asJar ? ("JAR='" + zipFile.getName()): ("DIR='" + directoryFile)) +"'";
|
||||
}
|
||||
|
||||
static {
|
||||
// Add this protocol type to the http properties
|
||||
Properties newP = new Properties(System.getProperties());
|
||||
newP.put(protocolPathProp,
|
||||
newP.getProperty(protocolPathProp)+"|FESI.Interpreter");
|
||||
System.setProperties(newP);
|
||||
}
|
||||
}
|
74
src/FESI/Interpreter/ParsedProgram.java
Normal file
74
src/FESI/Interpreter/ParsedProgram.java
Normal file
|
@ -0,0 +1,74 @@
|
|||
// ParsedProgram.java
|
||||
// FESI Copyright (c) Jean-Marc Lugrin, 1999
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package FESI.Interpreter;
|
||||
|
||||
import FESI.Parser.*;
|
||||
import FESI.AST.*;
|
||||
import FESI.Data.*;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* Represent a parsed program or function
|
||||
*/
|
||||
public class ParsedProgram {
|
||||
// The parsed tree of the function
|
||||
private ASTProgram programNode = null;
|
||||
// The list of declared variables
|
||||
private Vector variableNames = null;
|
||||
// The source of the parsed program
|
||||
private EvaluationSource evaluationSource = null;
|
||||
|
||||
/**
|
||||
* Create a parsed program representation from the abstract tree and list of variables
|
||||
* @param programNode the parsed program
|
||||
* @param variableNames The variables declared by var
|
||||
* @param evaluationSource the source of the parsed tree
|
||||
*/
|
||||
protected ParsedProgram(ASTProgram programNode,
|
||||
Vector variableNames,
|
||||
EvaluationSource evaluationSource) {
|
||||
this.programNode = programNode;
|
||||
this.variableNames = variableNames;
|
||||
this.evaluationSource = evaluationSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the program node
|
||||
* @return the program node
|
||||
*/
|
||||
protected ASTProgram getProgramNode() {
|
||||
return programNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the variable list
|
||||
* @return the variable list
|
||||
*/
|
||||
protected Vector getVariableNames() {
|
||||
return variableNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the evaluation souce
|
||||
* @return the evaluation source
|
||||
*/
|
||||
protected EvaluationSource getEvaluationSource() {
|
||||
return evaluationSource;
|
||||
}
|
||||
}
|
147
src/FESI/Interpreter/ScopeChain.java
Normal file
147
src/FESI/Interpreter/ScopeChain.java
Normal file
|
@ -0,0 +1,147 @@
|
|||
// ScopeChain.java
|
||||
// FESI Copyright (c) Jean-Marc Lugrin, 1999
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package FESI.Interpreter;
|
||||
|
||||
import FESI.Exceptions.*;
|
||||
import FESI.Data.*;
|
||||
|
||||
/**
|
||||
* The scope chaine has established by WITH statements. Used to
|
||||
* lookup values by name.
|
||||
*/
|
||||
public class ScopeChain {
|
||||
private ScopeChain previousElement;
|
||||
private ESObject thisElement;
|
||||
|
||||
/**
|
||||
* CReate a new scope chain linked to a previous one (which
|
||||
* is null only for the topmost chain)
|
||||
* @param thisElement Object to look at at this level
|
||||
* @param previousElement previous object in scope chain
|
||||
*/
|
||||
ScopeChain(ESObject thisElement, ScopeChain previousElement) {
|
||||
this.previousElement = previousElement;
|
||||
this.thisElement = thisElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the previous element in scope chain
|
||||
* @return The previous element
|
||||
*/
|
||||
ScopeChain previousScope() {
|
||||
return previousElement;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a reference to an object in the scope chain, so that the value
|
||||
* can be accessed or modified.
|
||||
*
|
||||
* @param identifier The name of the object
|
||||
* @return an ESReference object
|
||||
* @exception EcmaScriptException Not thrown
|
||||
*/
|
||||
public ESReference getReference(String identifier) throws EcmaScriptException {
|
||||
return getReference(identifier, identifier.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a reference to an object in the scope chain, so that the value
|
||||
* can be accessed or modified.
|
||||
*
|
||||
* @param identifier The name of the object
|
||||
* @param hash The hashCode of this identifier
|
||||
* @return an ESReference object
|
||||
* @exception EcmaScriptException Not thrown
|
||||
*/
|
||||
public ESReference getReference(String identifier, int hash) throws EcmaScriptException {
|
||||
ScopeChain theChain = this;
|
||||
do {
|
||||
if (theChain.thisElement.hasProperty(identifier, hash)) {
|
||||
return new ESReference(theChain.thisElement, identifier, hash);
|
||||
}
|
||||
theChain = theChain.previousElement;
|
||||
} while (theChain != null);
|
||||
return new ESReference(null, identifier, hash);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a value for an object in the scope chain.
|
||||
* return ESUndefined for undefined properties of existing objects,
|
||||
* but generate an error for unexistent object.
|
||||
* <P>A variant of getProperty is used, which will call recursively
|
||||
* this routine and the getProperty in a previous scope until all
|
||||
* scopes have been examined or a value is returned. This avoid the
|
||||
* call of hasProperty followed by a second call to getProperty to
|
||||
* get the property value.
|
||||
*
|
||||
* @param identifier The name of the object
|
||||
* @return The value of the object, possibly ESUndefined
|
||||
* @exception EcmaScriptException if no global object has that value
|
||||
*/
|
||||
public ESValue getValue(String identifier) throws EcmaScriptException {
|
||||
return getValue(identifier, identifier.hashCode());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a value for an object in the scope chain.
|
||||
* return ESUndefined for undefined properties of existing objects,
|
||||
* but generate an error for unexistent object.
|
||||
* <P>A variant of getProperty is used, which will call recursively
|
||||
* this routine and the getProperty in a previous scope until all
|
||||
* scopes have been examined or a value is returned. This avoid the
|
||||
* call of hasProperty followed by a second call to getProperty to
|
||||
* get the property value.
|
||||
*
|
||||
* @param identifier The name of the object
|
||||
* @param hash The hash code of the element (for optimization)
|
||||
* @return The value of the object, possibly ESUndefined
|
||||
* @exception EcmaScriptException if no global object has that value
|
||||
*/
|
||||
public ESValue getValue(String identifier, int hash) throws EcmaScriptException {
|
||||
return thisElement.getPropertyInScope(identifier, previousElement, hash);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Call a function defined by name in the scope chain.
|
||||
* <P>A variant of doCall is used, which will call recursively
|
||||
* this routine and the doCall in a previous scope until all
|
||||
* scopes have been examined or a value is returned. This avoid the
|
||||
* call of hasProperty followed by a second call to getProperty to
|
||||
* get the property value.
|
||||
*
|
||||
* @param evaluator The evaluator doing the call
|
||||
* @param thisObject The 'this' object of the call
|
||||
* @param functionName The name of the function to call
|
||||
* @param hash The hash code of the function name (for optimization)
|
||||
* @param arguments The parameters of the call
|
||||
* @return The result of the call, possibly NULL
|
||||
* @exception EcmaScriptException for any error
|
||||
*/
|
||||
public ESValue doIndirectCall(Evaluator evaluator,
|
||||
ESObject thisObject,
|
||||
String functionName,
|
||||
int hash,
|
||||
ESValue[] arguments) throws EcmaScriptException {
|
||||
return thisElement.doIndirectCallInScope(evaluator, previousElement, thisObject, functionName, hash, arguments);
|
||||
}
|
||||
|
||||
}
|
66
src/FESI/Interpreter/StringEvaluationSource.java
Normal file
66
src/FESI/Interpreter/StringEvaluationSource.java
Normal file
|
@ -0,0 +1,66 @@
|
|||
// StringEvaluationSource.java
|
||||
// FESI Copyright (c) Jean-Marc Lugrin, 1999
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package FESI.Interpreter;
|
||||
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* Describe a string used as a source. The string is trimmed
|
||||
* to its first significant characters during display.
|
||||
*/
|
||||
public class StringEvaluationSource extends EvaluationSource {
|
||||
|
||||
private String theString;
|
||||
|
||||
|
||||
/**
|
||||
* Create a string source description
|
||||
* @param theString Describe the source
|
||||
* @param previousSource Describe the calling source
|
||||
*/
|
||||
public StringEvaluationSource(String theString, EvaluationSource previousSource) {
|
||||
super(previousSource);
|
||||
this.theString = theString;
|
||||
}
|
||||
|
||||
protected String getEvaluationSourceText() {
|
||||
String displayString = new String("");
|
||||
// All this to print just first line of string...
|
||||
boolean firstLineFound = false;
|
||||
boolean moreLinesFound = false;
|
||||
StringTokenizer t = new StringTokenizer(theString, "\n\r");
|
||||
while (t.hasMoreTokens()) {
|
||||
String theLine = t.nextToken();
|
||||
if (theLine.equals("\n") || theLine.equals("\r")) continue;
|
||||
if (theLine.trim().length()>0) { // Skip any leading empty lines
|
||||
if (!firstLineFound) {
|
||||
displayString = theLine;
|
||||
firstLineFound = true;
|
||||
continue;
|
||||
}
|
||||
moreLinesFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (moreLinesFound) {
|
||||
return "in string starting with: '" + displayString + "'...";
|
||||
} else {
|
||||
return "in string: '" + displayString + "'";
|
||||
}
|
||||
}
|
||||
}
|
40
src/FESI/Interpreter/UserEvaluationSource.java
Normal file
40
src/FESI/Interpreter/UserEvaluationSource.java
Normal file
|
@ -0,0 +1,40 @@
|
|||
// UserEvaluationSource.java
|
||||
// FESI Copyright (c) Jean-Marc Lugrin, 1999
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package FESI.Interpreter;
|
||||
|
||||
/**
|
||||
* Describe an arbitrary source provided by a user program.
|
||||
*/
|
||||
public class UserEvaluationSource extends EvaluationSource {
|
||||
|
||||
private String theDescription;
|
||||
|
||||
/**
|
||||
* Create a user described source
|
||||
* @param theDescription Describe the source
|
||||
* @param previousSource Describe the calling source
|
||||
*/
|
||||
public UserEvaluationSource(String theDescription, EvaluationSource previousSource) {
|
||||
super(previousSource);
|
||||
this.theDescription = theDescription;
|
||||
}
|
||||
|
||||
protected String getEvaluationSourceText() {
|
||||
return "in: '" + theDescription + "'";
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue