Make type handling case-insensitive, change default prototypes to first-letter-upper-case,

do some heavily needed refactoring around helma.scripting.rhino.HopObject and
helma.framework.core.TypeManager.
This commit is contained in:
hns 2003-12-18 17:34:13 +00:00
parent 3f284fa899
commit e57a939fa0
10 changed files with 220 additions and 155 deletions

View file

@ -283,7 +283,7 @@ public final class Application implements IPathElement, Runnable {
// set the context classloader. Note that this must be done before // set the context classloader. Note that this must be done before
// using the logging framework so that a new LogFactory gets created // using the logging framework so that a new LogFactory gets created
// for this app. // for this app.
Thread.currentThread().setContextClassLoader(typemgr.loader); Thread.currentThread().setContextClassLoader(typemgr.getClassLoader());
if (Server.getServer() != null) { if (Server.getServer() != null) {
Vector extensions = Server.getServer().getExtensions(); Vector extensions = Server.getServer().getExtensions();
@ -356,7 +356,7 @@ public final class Application implements IPathElement, Runnable {
nmgr = new NodeManager(this, dbDir.getAbsolutePath(), props); nmgr = new NodeManager(this, dbDir.getAbsolutePath(), props);
// reset the classloader to the parent/system/server classloader. // reset the classloader to the parent/system/server classloader.
Thread.currentThread().setContextClassLoader(typemgr.loader.getParent()); Thread.currentThread().setContextClassLoader(typemgr.getClassLoader().getParent());
} }
@ -695,13 +695,15 @@ public final class Application implements IPathElement, Runnable {
try { try {
if (classMapping.containsKey("root.factory.class") && if (classMapping.containsKey("root.factory.class") &&
classMapping.containsKey("root.factory.method")) { classMapping.containsKey("root.factory.method")) {
Class c = typemgr.loader.loadClass(classMapping.getProperty("root.factory.class")); String rootFactory = classMapping.getProperty("root.factory.class");
Class c = typemgr.getClassLoader().loadClass(rootFactory);
Method m = c.getMethod(classMapping.getProperty("root.factory.method"), Method m = c.getMethod(classMapping.getProperty("root.factory.method"),
null); null);
rootObject = m.invoke(c, null); rootObject = m.invoke(c, null);
} else { } else {
Class c = typemgr.loader.loadClass(classMapping.getProperty("root")); String rootClass = classMapping.getProperty("root");
Class c = typemgr.getClassLoader().loadClass(rootClass);
rootObject = c.newInstance(); rootObject = c.newInstance();
} }
@ -795,14 +797,14 @@ public final class Application implements IPathElement, Runnable {
* Return the prototype with the given name, if it exists * Return the prototype with the given name, if it exists
*/ */
public Prototype getPrototypeByName(String name) { public Prototype getPrototypeByName(String name) {
return (Prototype) typemgr.prototypes.get(name); return typemgr.getPrototype(name);
} }
/** /**
* Return a collection containing all prototypes defined for this application * Return a collection containing all prototypes defined for this application
*/ */
public Collection getPrototypes() { public Collection getPrototypes() {
return typemgr.prototypes.values(); return typemgr.getPrototypes();
} }
/** /**
@ -1193,7 +1195,7 @@ public final class Application implements IPathElement, Runnable {
* Return the application's classloader * Return the application's classloader
*/ */
public ClassLoader getClassLoader() { public ClassLoader getClassLoader() {
return typemgr.loader; return typemgr.getClassLoader();
} }
////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////

View file

@ -19,6 +19,7 @@ package helma.framework.core;
import helma.objectmodel.db.DbMapping; import helma.objectmodel.db.DbMapping;
import helma.scripting.*; import helma.scripting.*;
import helma.util.SystemMap; import helma.util.SystemMap;
import helma.util.SystemProperties;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
@ -30,6 +31,7 @@ import java.util.*;
*/ */
public final class Prototype { public final class Prototype {
String name; String name;
String lowerCaseName;
Application app; Application app;
File directory; File directory;
File[] files; File[] files;
@ -61,15 +63,37 @@ public final class Prototype {
/** /**
* Creates a new Prototype object. * Creates a new Prototype object.
* *
* @param name ... * @param name the prototype's name
* @param dir ... * @param dir the prototype directory, if known
* @param app ... * @param app the application this prototype is a part of
*/ */
public Prototype(String name, File dir, Application app) { public Prototype(String name, File dir, Application app) {
// app.logEvent ("Constructing Prototype "+app.getName()+"/"+name); // app.logEvent ("Constructing Prototype "+app.getName()+"/"+name);
this.app = app; this.app = app;
this.name = name; this.name = name;
this.directory = dir; lowerCaseName = name.toLowerCase();
if (dir != null) {
directory = dir;
} else {
directory = new File(app.appDir, name);
// a little bit of overkill to maintain backwards compatibility
// with lower case type names...
if (!directory.isDirectory()) {
File lowerDir = new File(app.appDir, lowerCaseName);
if (lowerDir.isDirectory()) {
directory = lowerDir;
}
}
}
// Create and register type properties file
File propfile = new File(directory, "type.properties");
SystemProperties props = new SystemProperties(propfile.getAbsolutePath());
dbmap = new DbMapping(app, name, props);
// we don't need to put the DbMapping into proto.updatables, because
// dbmappings are checked separately in TypeManager.checkFiles() for
// each request
code = new HashMap(); code = new HashMap();
zippedCode = new HashMap(); zippedCode = new HashMap();
@ -145,7 +169,7 @@ public final class Prototype {
*/ */
public void setParentPrototype(Prototype parent) { public void setParentPrototype(Prototype parent) {
// this is not allowed for the hopobject and global prototypes // this is not allowed for the hopobject and global prototypes
if ("hopobject".equalsIgnoreCase(name) || "global".equalsIgnoreCase(name)) { if ("HopObject".equals(name) || "global".equals(name)) {
return; return;
} }
@ -164,11 +188,11 @@ public final class Prototype {
* Check if the given prototype is within this prototype's parent chain. * Check if the given prototype is within this prototype's parent chain.
*/ */
public final boolean isInstanceOf(String pname) { public final boolean isInstanceOf(String pname) {
if (name.equals(pname)) { if (name.equals(pname) || lowerCaseName.equals(pname)) {
return true; return true;
} }
if ((parent != null) && !"hopobject".equalsIgnoreCase(parent.getName())) { if ((parent != null) && !"HopObject".equals(parent.getName())) {
return parent.isInstanceOf(pname); return parent.isInstanceOf(pname);
} }
@ -185,12 +209,17 @@ public final class Prototype {
Prototype p = parent; Prototype p = parent;
while ((p != null) && !"hopobject".equalsIgnoreCase(p.getName())) { while ((p != null) && !"hopobject".equals(p.getLowerCaseName())) {
Object old = handlers.put(p.name, obj); Object old = handlers.put(p.name, obj);
// if an object was already registered by this name, put it back in again. // if an object was already registered by this name, put it back in again.
if (old != null) { if (old != null) {
handlers.put(p.name, old); handlers.put(p.name, old);
} }
// same with lower case name
old = handlers.put(p.lowerCaseName, obj);
if (old != null) {
handlers.put(p.lowerCaseName, old);
}
p = p.parent; p = p.parent;
} }
@ -201,7 +230,7 @@ public final class Prototype {
* *
* @param dbmap ... * @param dbmap ...
*/ */
public void setDbMapping(DbMapping dbmap) { protected void setDbMapping(DbMapping dbmap) {
this.dbmap = dbmap; this.dbmap = dbmap;
} }
@ -245,7 +274,7 @@ public final class Prototype {
} }
/** /**
* * Return this prototype's name
* *
* @return ... * @return ...
*/ */
@ -253,6 +282,15 @@ public final class Prototype {
return name; return name;
} }
/**
* Return this prototype's name in lower case letters
*
* @return ...
*/
public String getLowerCaseName() {
return lowerCaseName;
}
/** /**
* Get the last time any script has been re-read for this prototype. * Get the last time any script has been re-read for this prototype.
*/ */

View file

@ -294,6 +294,7 @@ public final class RequestEvaluator implements Runnable {
// immediately register objects with their direct prototype name // immediately register objects with their direct prototype name
if (protos[i] != null) { if (protos[i] != null) {
macroHandlers.put(protos[i].getName(), obj); macroHandlers.put(protos[i].getName(), obj);
macroHandlers.put(protos[i].getLowerCaseName(), obj);
} }
} }

View file

@ -61,6 +61,7 @@ public class RequestPath {
if (proto != null) { if (proto != null) {
primaryProtos.put(proto.getName(), obj); primaryProtos.put(proto.getName(), obj);
primaryProtos.put(proto.getLowerCaseName(), obj);
proto.registerParents(secondaryProtos, obj); proto.registerParents(secondaryProtos, obj);
} }
} }

View file

@ -412,7 +412,7 @@ public final class Skin {
// not a global macro - need to find handler object // not a global macro - need to find handler object
// was called with this object - check it or its parents for matching prototype // was called with this object - check it or its parents for matching prototype
if (!handler.equals("this") && if (!handler.equals("this") &&
!handler.equals(app.getPrototypeName(thisObject))) { !handler.equalsIgnoreCase(app.getPrototypeName(thisObject))) {
// the handler object is not what we want // the handler object is not what we want
Object n = thisObject; Object n = thisObject;

View file

@ -30,30 +30,34 @@ import java.util.*;
* applications and updates the evaluators if anything has changed. * applications and updates the evaluators if anything has changed.
*/ */
public final class TypeManager { public final class TypeManager {
final static String[] standardTypes = { "user", "global", "root", "hopobject" }; final static String[] standardTypes = { "User", "Global", "Root", "HopObject" };
final static String templateExtension = ".hsp"; final static String templateExtension = ".hsp";
final static String scriptExtension = ".js"; final static String scriptExtension = ".js";
final static String actionExtension = ".hac"; final static String actionExtension = ".hac";
final static String skinExtension = ".skin"; final static String skinExtension = ".skin";
Application app;
File appDir; private Application app;
HashMap prototypes; // map of prototypes private File appDir;
HashMap zipfiles; // map of zipped script files // map of prototypes
HashSet jarfiles; // set of Java archives private HashMap prototypes;
long lastCheck = 0; // map of zipped script files
long appDirMod = 0; private HashMap zipfiles;
// set of Java archives
private HashSet jarfiles;
private long lastCheck = 0;
private long appDirMod = 0;
// a checksum that changes whenever something in the application files changes. // a checksum that changes whenever something in the application files changes.
long checksum; private long checksum;
// the hopobject prototype // the hopobject prototype
Prototype hopobjectProto; // private Prototype hopobjectProto;
// the global prototype // the global prototype
Prototype globalProto; // private Prototype globalProto;
// app specific class loader, includes jar files in the app directory // app specific class loader, includes jar files in the app directory
AppClassLoader loader; private AppClassLoader loader;
/** /**
* Creates a new TypeManager object. * Creates a new TypeManager object.
@ -114,13 +118,9 @@ public final class TypeManager {
*/ */
public void createPrototypes() { public void createPrototypes() {
// create standard prototypes. // create standard prototypes.
createPrototype("root"); for (int i = 0; i < standardTypes.length; i++) {
createPrototype("user"); createPrototype(standardTypes[i], null);
}
// get references to hopobject and global protos,
// since we need it regularly when setting parent prototypes.
hopobjectProto = createPrototype("hopobject");
globalProto = createPrototype("global");
// loop through directories and create prototypes // loop through directories and create prototypes
checkFiles(); checkFiles();
@ -236,19 +236,12 @@ public final class TypeManager {
DbMapping dbmap = proto.getDbMapping(); DbMapping dbmap = proto.getDbMapping();
if ((dbmap != null) && dbmap.needsUpdate()) { if ((dbmap != null) && dbmap.needsUpdate()) {
// call dbmap.update(). This also checks the
// parent prototype for prototypes other than
// global and HopObject, which is a bit awkward...
// I mean we're the type manager, so this should
// be part of our job, right?
dbmap.update(); dbmap.update();
// this is now done in dbmap.update()!!!
/*if ((proto != hopobjectProto) && (proto != globalProto)) {
// set parent prototype, in case it has changed.
String parentName = dbmap.getExtends();
if (parentName != null) {
proto.setParentPrototype(getPrototype(parentName));
} else if (!app.isJavaPrototype(proto.getName())) {
proto.setParentPrototype(hopobjectProto);
}
} */
} }
} }
@ -292,40 +285,48 @@ public final class TypeManager {
return checksum; return checksum;
} }
/**
* Return the class loader used by this application.
*
* @return the ClassLoader
*/
public ClassLoader getClassLoader() {
return loader;
}
/**
* Return a collection containing the prototypes defined for this type
* manager.
*
* @return a collection containing the prototypes
*/
public Collection getPrototypes() {
return prototypes.values();
}
/** /**
* Get a prototype defined for this application * Get a prototype defined for this application
*/ */
public Prototype getPrototype(String typename) { public Prototype getPrototype(String typename) {
return (Prototype) prototypes.get(typename); if (typename == null) {
return null;
}
return (Prototype) prototypes.get(typename.toLowerCase());
} }
/** /**
* Get a prototype, creating it if it doesn't already exist. Note * Create and register a new Prototype.
* that it doesn't create a DbMapping - this is left to the *
* caller (e.g. ZippedAppFile). * @param typename the name of the prototype
*/ * @param dir the prototype directory if it is know, or null if we
public Prototype createPrototype(String typename) { * ought to find out by ourselves
return createPrototype(typename, new File(appDir, typename)); * @return the newly created prototype
}
/**
* Create a prototype from a directory containing scripts and other stuff
*/ */
public Prototype createPrototype(String typename, File dir) { public Prototype createPrototype(String typename, File dir) {
Prototype proto = new Prototype(typename, dir, app); Prototype proto = new Prototype(typename, dir, app);
// Create and register type properties file
File propfile = new File(dir, "type.properties");
SystemProperties props = new SystemProperties(propfile.getAbsolutePath());
DbMapping dbmap = new DbMapping(app, typename, props);
// we don't need to put the DbMapping into proto.updatables, because
// dbmappings are checked separately in checkFiles for each request
// proto.updatables.put ("type.properties", dbmap);
proto.setDbMapping(dbmap);
// put the prototype into our map // put the prototype into our map
prototypes.put(typename, proto); prototypes.put(proto.getLowerCaseName(), proto);
return proto; return proto;
} }

View file

@ -97,7 +97,7 @@ public class ZippedAppFile implements Updatable {
Prototype proto = app.typemgr.getPrototype(dir); Prototype proto = app.typemgr.getPrototype(dir);
if (proto == null) { if (proto == null) {
proto = app.typemgr.createPrototype(dir); proto = app.typemgr.createPrototype(dir, null);
newPrototypes.add(proto); newPrototypes.add(proto);
} }

View file

@ -505,7 +505,7 @@ public final class Node implements INode, Serializable {
public String getPrototype() { public String getPrototype() {
// if prototype is null, it's a vanilla HopObject. // if prototype is null, it's a vanilla HopObject.
if (prototype == null) { if (prototype == null) {
return "hopobject"; return "HopObject";
} }
return prototype; return prototype;

View file

@ -78,6 +78,35 @@ public class HopObject extends ScriptableObject implements Wrapper {
setPrototype(proto); setPrototype(proto);
} }
public static HopObject init(Scriptable scope)
throws PropertyException {
int attributes = READONLY | DONTENUM | PERMANENT;
// create prototype object
HopObject proto = new HopObject();
proto.setPrototype(getObjectPrototype(scope));
// install JavaScript methods and properties
Method[] methods = HopObject.class.getDeclaredMethods();
for (int i=0; i<methods.length; i++) {
String methodName = methods[i].getName();
if (methodName.startsWith("jsFunction_")) {
methodName = methodName.substring(11);
FunctionObject func = new FunctionObject(methodName,
methods[i], proto);
proto.defineProperty(methodName, func, attributes);
} else if (methodName.startsWith("jsGet_")) {
methodName = methodName.substring(6);
proto.defineProperty(methodName, null, methods[i],
null, attributes);
}
}
return proto;
}
/** /**
* This method is used as HopObject constructor from JavaScript. * This method is used as HopObject constructor from JavaScript.
*/ */
@ -408,14 +437,18 @@ public class HopObject extends ScriptableObject implements Wrapper {
/** /**
* Prefetch child objects from (relational) database. * Prefetch child objects from (relational) database.
*/ */
public void jsFunction_prefetchChildren() { public void jsFunction_prefetchChildren(Object startArg, Object lengthArg) {
jsFunction_prefetchChildren(0, 1000); // check if we were called with no arguments
if (startArg == Undefined.instance && lengthArg == Undefined.instance) {
prefetchChildren(0, 1000);
} else {
int start = (int) ScriptRuntime.toNumber(startArg);
int length = (int) ScriptRuntime.toNumber(lengthArg);
prefetchChildren(start, length);
}
} }
/** private void prefetchChildren(int start, int length) {
* Prefetch child objects from (relational) database.
*/
public void jsFunction_prefetchChildren(int start, int length) {
if (!(node instanceof helma.objectmodel.db.Node)) { if (!(node instanceof helma.objectmodel.db.Node)) {
return; return;
} }
@ -424,7 +457,8 @@ public class HopObject extends ScriptableObject implements Wrapper {
try { try {
((helma.objectmodel.db.Node) node).prefetchChildren(start, length); ((helma.objectmodel.db.Node) node).prefetchChildren(start, length);
} catch (Exception x) { } catch (Exception ignore) {
System.err.println("Error in HopObject.prefetchChildren(): "+ignore);
} }
} }
@ -446,6 +480,7 @@ public class HopObject extends ScriptableObject implements Wrapper {
private Scriptable list() { private Scriptable list() {
checkNode(); checkNode();
prefetchChildren(0, 1000);
Enumeration e = node.getSubnodes(); Enumeration e = node.getSubnodes();
ArrayList a = new ArrayList(); ArrayList a = new ArrayList();
@ -475,7 +510,7 @@ public class HopObject extends ScriptableObject implements Wrapper {
checkNode(); checkNode();
jsFunction_prefetchChildren(start, length); prefetchChildren(start, length);
ArrayList a = new ArrayList(); ArrayList a = new ArrayList();
for (int i=start; i<start+length; i++) { for (int i=start; i<start+length; i++) {
@ -706,7 +741,7 @@ public class HopObject extends ScriptableObject implements Wrapper {
* @return ... * @return ...
*/ */
public Object get(String name, Scriptable start) { public Object get(String name, Scriptable start) {
// System.err.println ("GET from "+node+": "+name); // System.err.println("GET from "+this+": "+name+" ->"+super.get(name, start));
Object retval = null; Object retval = null;
if (node == null) { if (node == null) {

View file

@ -51,6 +51,9 @@ public final class RhinoCore {
// the wrap factory // the wrap factory
WrapFactory wrapper; WrapFactory wrapper;
// the prototype for HopObject
ScriptableObject hopObjectProto;
// the prototype for path objects // the prototype for path objects
PathWrapper pathProto; PathWrapper pathProto;
@ -90,21 +93,15 @@ public final class RhinoCore {
global = (GlobalObject) context.initStandardObjects(g); global = (GlobalObject) context.initStandardObjects(g);
ScriptableObject.defineClass(global, HopObject.class);
ScriptableObject.defineClass(global, FileObject.class);
ScriptableObject.defineClass(global, FtpObject.class);
pathProto = new PathWrapper(this); pathProto = new PathWrapper(this);
hopObjectProto = HopObject.init(global);
FileObject.init(global);
FtpObject.init(global);
ImageObject.init(global); ImageObject.init(global);
XmlRpcObject.init(global); XmlRpcObject.init(global);
MailObject.init(global, app.getProperties()); MailObject.init(global, app.getProperties());
registerPrototype("hopobject",
(ScriptableObject) ScriptableObject
.getClassPrototype(global, "HopObject"));
registerPrototype("global", global);
// add some convenience functions to string, date and number prototypes // add some convenience functions to string, date and number prototypes
Scriptable stringProto = ScriptableObject.getClassPrototype(global, "String"); Scriptable stringProto = ScriptableObject.getClassPrototype(global, "String");
stringProto.put("trim", stringProto, new StringTrim()); stringProto.put("trim", stringProto, new StringTrim());
@ -150,33 +147,34 @@ public final class RhinoCore {
* *
* @param prototype the prototype to be created * @param prototype the prototype to be created
*/ */
synchronized void initPrototype(Prototype prototype) { synchronized TypeInfo initPrototype(Prototype prototype) {
String name = prototype.getName(); String name = prototype.getName();
String lowerCaseName = prototype.getLowerCaseName();
TypeInfo type = (TypeInfo) prototypes.get(lowerCaseName);
// check if the prototype info exists already // check if the prototype info exists already
ScriptableObject op = getRawPrototype(name); ScriptableObject op = (type == null) ? null : type.objectPrototype;
// if prototype info doesn't exist (i.e. is a standard prototype // if prototype info doesn't exist (i.e. is a standard prototype
// built by HopExtension), create it. // built by HopExtension), create it.
if (op == null) { if (op == null) {
try { if ("global".equals(lowerCaseName)) {
op = global;
} else if ("hopobject".equals(lowerCaseName)) {
op = hopObjectProto;
} else {
op = new HopObject(name); op = new HopObject(name);
op.setParentScope(global); op.setParentScope(global);
// op.put("prototypename", op, name);
} catch (Exception ignore) {
System.err.println("Error creating prototype: " + ignore);
ignore.printStackTrace();
} }
registerPrototype(name, op); type = registerPrototype(prototype, op);
} }
// Register a constructor for all types except global. // Register a constructor for all types except global.
// This will first create a new prototyped hopobject and then calls // This will first create a new prototyped HopObject and then calls
// the actual (scripted) constructor on it. // the actual (scripted) constructor on it.
if (!"global".equalsIgnoreCase(name) && if (!"global".equals(lowerCaseName)) {
!"root".equalsIgnoreCase(name) &&
!"hopobject".equalsIgnoreCase(name)) {
try { try {
installConstructor(name, op); installConstructor(name, op);
} catch (Exception ignore) { } catch (Exception ignore) {
@ -184,18 +182,20 @@ public final class RhinoCore {
ignore.printStackTrace(); ignore.printStackTrace();
} }
} }
return type;
} }
/** /**
* Set up a prototype, parsing and compiling all its script files. * Set up a prototype, parsing and compiling all its script files.
* *
* @param prototype the prototype to update/evaluate/compile
* @param type the info, containing the object proto, last update time and * @param type the info, containing the object proto, last update time and
* the set of compiled functions properties * the set of compiled functions properties
*/ */
synchronized void evaluatePrototype(Prototype prototype, TypeInfo type) { synchronized void evaluatePrototype(TypeInfo type) {
// System.err.println("EVALUATING PROTO: "+prototype);
Scriptable op = type.objectPrototype; Scriptable op = type.objectPrototype;
Prototype prototype = type.frameworkPrototype;
// set the parent prototype in case it hasn't been done before // set the parent prototype in case it hasn't been done before
// or it has changed... // or it has changed...
@ -258,8 +258,9 @@ public final class RhinoCore {
*/ */
private void setParentPrototype(Prototype prototype, Scriptable op) { private void setParentPrototype(Prototype prototype, Scriptable op) {
String name = prototype.getName(); String name = prototype.getName();
String lowerCaseName = prototype.getLowerCaseName();
if (!"global".equalsIgnoreCase(name) && !"hopobject".equalsIgnoreCase(name)) { if (!"global".equals(lowerCaseName) && !"hopobject".equals(lowerCaseName)) {
// get the prototype's prototype if possible and necessary // get the prototype's prototype if possible and necessary
Scriptable opp = null; Scriptable opp = null;
@ -282,12 +283,12 @@ public final class RhinoCore {
/** /**
* This is a version of org.mozilla.javascript.FunctionObject.addAsConstructor() * This is a version of org.mozilla.javascript.FunctionObject.addAsConstructor()
* that does not set the constructor property in the prototype. This is because * that does not set the constructor property in the prototype. This is because
* we want our own scripted constructor function to prevail, if it is defined. * we want our own scripted constructor function to be visible, if it is defined.
* *
* @param name the name of the constructor * @param name the name of the constructor
* @param op the object prototype * @param op the object prototype
*/ */
private void installConstructor(String name, Scriptable op) { void installConstructor(String name, Scriptable op) {
FunctionObject fo = new FunctionObject(name, HopObject.hopObjCtor, global); FunctionObject fo = new FunctionObject(name, HopObject.hopObjCtor, global);
ScriptRuntime.setFunctionProtoAndParent(global, fo); ScriptRuntime.setFunctionProtoAndParent(global, fo);
@ -315,27 +316,23 @@ public final class RhinoCore {
for (Iterator i = protos.iterator(); i.hasNext();) { for (Iterator i = protos.iterator(); i.hasNext();) {
Prototype proto = (Prototype) i.next(); Prototype proto = (Prototype) i.next();
TypeInfo type = (TypeInfo) prototypes.get(proto.getName()); TypeInfo type = (TypeInfo) prototypes.get(proto.getLowerCaseName());
if (type == null) { if (type == null) {
// a prototype we don't know anything about yet. Init local update info. // a prototype we don't know anything about yet. Init local update info.
initPrototype(proto); type = initPrototype(proto);
type = (TypeInfo) prototypes.get(proto.getName());
} }
// only update prototype if it has already been initialized. // only update prototype if it has already been initialized.
// otherwise, this will be done on demand // otherwise, this will be done on demand
// System.err.println ("CHECKING PROTO "+proto+": "+type); // System.err.println ("CHECKING PROTO "+proto+": "+type);
if (type.lastUpdate > -1) { if (type.lastUpdate > -1) {
Prototype p = app.typemgr.getPrototype(type.protoName); // let the type manager scan the prototype's directory
app.typemgr.updatePrototype(proto);
if (p != null) { // and re-evaluate if necessary
// System.err.println ("UPDATING PROTO: "+p); if (type.needsUpdate()) {
app.typemgr.updatePrototype(p); evaluatePrototype(type);
if (p.getLastUpdate() > type.lastUpdate) {
evaluatePrototype(p, type);
}
} }
} }
} }
@ -343,19 +340,6 @@ public final class RhinoCore {
lastUpdate = System.currentTimeMillis(); lastUpdate = System.currentTimeMillis();
} }
/**
* Get a raw prototype, i.e. in potentially unfinished state
* without checking if it needs to be updated.
*/
private ScriptableObject getRawPrototype(String protoName) {
if (protoName == null) {
return null;
}
TypeInfo type = (TypeInfo) prototypes.get(protoName);
return (type == null) ? null : type.objectPrototype;
}
/** /**
* A version of getPrototype() that retrieves a prototype and checks * A version of getPrototype() that retrieves a prototype and checks
@ -394,20 +378,16 @@ public final class RhinoCore {
return null; return null;
} }
TypeInfo type = (TypeInfo) prototypes.get(protoName); TypeInfo type = (TypeInfo) prototypes.get(protoName.toLowerCase());
// if type exists and hasn't been evaluated (used) yet, evaluate it now. // if type exists and hasn't been evaluated (used) yet, evaluate it now.
// otherwise, it has already been evaluated for this request by updatePrototypes(), // otherwise, it has already been evaluated for this request by updatePrototypes(),
// which is called before a request is handled. // which is called before a request is handled.
if ((type != null) && (type.lastUpdate == -1)) { if ((type != null) && (type.lastUpdate == -1)) {
Prototype p = app.typemgr.getPrototype(protoName); app.typemgr.updatePrototype(type.frameworkPrototype);
if (p != null) { if (type.needsUpdate()) {
app.typemgr.updatePrototype(p); evaluatePrototype(type);
if (p.getLastUpdate() > type.lastUpdate) {
evaluatePrototype(p, type);
}
} }
} }
@ -417,10 +397,10 @@ public final class RhinoCore {
/** /**
* Register an object prototype for a prototype name. * Register an object prototype for a prototype name.
*/ */
private void registerPrototype(String protoName, ScriptableObject op) { private TypeInfo registerPrototype(Prototype proto, ScriptableObject op) {
if ((protoName != null) && (op != null)) { TypeInfo type = new TypeInfo(proto, op);
prototypes.put(protoName, new TypeInfo(op, protoName)); prototypes.put(proto.getLowerCaseName(), type);
} return type;
} }
/** /**
@ -561,8 +541,8 @@ public final class RhinoCore {
Scriptable op = getPrototype(prototypeName); Scriptable op = getPrototype(prototypeName);
if (op == null) { if (op == null) {
prototypeName = "hopobject"; prototypeName = "HopObject";
op = getPrototype("hopobject"); op = getPrototype("HopObject");
} }
w = new JavaObject(global, e, prototypeName, op, this); w = new JavaObject(global, e, prototypeName, op, this);
@ -602,8 +582,8 @@ public final class RhinoCore {
// no prototype found for this node? // no prototype found for this node?
if (op == null) { if (op == null) {
op = getValidPrototype("hopobject"); op = getValidPrototype("HopObject");
protoname = "hopobject"; protoname = "HopObject";
} }
esn = new HopObject(protoname, op); esn = new HopObject(protoname, op);
@ -797,7 +777,7 @@ public final class RhinoCore {
} }
// mark prototype as broken // mark prototype as broken
if (type.error == null && e instanceof EcmaError) { if (type.error == null && e instanceof EcmaError) {
if ("global".equals(type.protoName)) { if ("global".equals(type.frameworkPrototype.getLowerCaseName())) {
globalError = (EcmaError) e; globalError = (EcmaError) e;
} else { } else {
type.error = (EcmaError) e; type.error = (EcmaError) e;
@ -828,7 +808,10 @@ public final class RhinoCore {
*/ */
class TypeInfo { class TypeInfo {
// the object prototype for this type // the framework prototype object
Prototype frameworkPrototype;
// the JavaScript prototype for this type
ScriptableObject objectPrototype; ScriptableObject objectPrototype;
// timestamp of last update. This is -1 so even an empty prototype directory // timestamp of last update. This is -1 so even an empty prototype directory
@ -837,7 +820,7 @@ public final class RhinoCore {
long lastUpdate = -1; long lastUpdate = -1;
// the prototype name // the prototype name
String protoName; // String protoName;
// a set of property values that were defined in last script compliation // a set of property values that were defined in last script compliation
Set compiledFunctions; Set compiledFunctions;
@ -847,9 +830,9 @@ public final class RhinoCore {
EcmaError error; EcmaError error;
public TypeInfo(ScriptableObject op, String name) { public TypeInfo(Prototype proto, ScriptableObject op) {
frameworkPrototype = proto;
objectPrototype = op; objectPrototype = op;
protoName = name;
compiledFunctions = new HashSet(0); compiledFunctions = new HashSet(0);
// remember properties already defined on this object prototype // remember properties already defined on this object prototype
predefinedProperties = new HashSet(); predefinedProperties = new HashSet();
@ -859,8 +842,12 @@ public final class RhinoCore {
} }
} }
public boolean needsUpdate() {
return frameworkPrototype.getLastUpdate() > lastUpdate;
}
public String toString() { public String toString() {
return ("TypeInfo[" + protoName + "," + new Date(lastUpdate) + "]"); return ("TypeInfo[" + frameworkPrototype + "," + new Date(lastUpdate) + "]");
} }
} }