* Clean up HopObject wrapper, move constructor code into separate HopObjectCtor class

* Implement HopObject compilation trigger on HopObject constructor property access
* Fix race condition in RhinoCore.updatePrototypes() that could result in failed requests
  at application startup time; tighten up synchronization.
* Fix ListViewWrapper to perform static JS function setup rather than setting up functions for
  each wrapper.
* Implement getOrderedView() in SubnodeList (pulled out of OrderedSubnodeList).
* Cleaned up and simplified OrderedSubnodeList.
* Change ordering for null properties: add at the end of the list instead of the beginning.
* Cache nodes fetched in NodeManager.updateSubnodeList()
This commit is contained in:
hns 2006-03-21 16:52:46 +00:00
parent 91a92b6072
commit 80e0d4e012
10 changed files with 494 additions and 412 deletions

View file

@ -1244,7 +1244,7 @@ public final class Node implements INode, Serializable {
loadNodes();
if (subnodes == null) {
subnodes = new SubnodeList();
subnodes = new SubnodeList(nmgr, dbmap.getSubnodeRelation());
}
if (create || subnodes.contains(new NodeHandle(new SyntheticKey(getKey(), sid)))) {
@ -1582,11 +1582,11 @@ public final class Node implements INode, Serializable {
public SubnodeList createSubnodeList() {
Relation rel = this.dbmap == null ? null : this.dbmap.getSubnodeRelation();
if (rel != null && rel.updateCriteria != null) {
subnodes = new UpdateableSubnodeList(rel);
subnodes = new UpdateableSubnodeList(nmgr, rel);
} else if (rel != null && rel.autoSorted) {
subnodes = new OrderedSubnodeList(rel);
subnodes = new OrderedSubnodeList(nmgr, rel);
} else {
subnodes = new SubnodeList();
subnodes = new SubnodeList(nmgr, rel);
}
return subnodes;
}
@ -1661,11 +1661,11 @@ public final class Node implements INode, Serializable {
}
/**
* Return this Node's subnode list
*
*
* @return ...
* @return the subnode list
*/
public List getSubnodeList() {
public SubnodeList getSubnodeList() {
return subnodes;
}

View file

@ -1186,6 +1186,12 @@ public final class NodeManager {
continue;
}
key = node.getKey();
synchronized (cache) {
Node oldnode = (Node) cache.put(key, node);
if ((oldnode != null) && (oldnode.getState() != INode.INVALID)) {
cache.put(key, oldnode);
}
}
} else {
key = new DbKey(rel.otherType, kstr);
}
@ -1320,7 +1326,7 @@ public final class NodeManager {
SubnodeList sn = (SubnodeList) groupbySubnodes.get(groupName);
if (sn == null) {
sn = new SubnodeList();
sn = new SubnodeList(safe, rel);
groupbySubnodes.put(groupName, sn);
}

View file

@ -29,49 +29,24 @@ import java.util.List;
* or remove-methods are called.
*/
public class OrderedSubnodeList extends SubnodeList {
HashMap views = null;
private final OrderedSubnodeList origin;
// the base subnode list, in case this is an ordered view
private SubnodeList origin;
// an array containing the order-fields
private final String orderProperties[];
private String orderProperties[];
// an array containing the direction for ordering
private final boolean orderIsDesc[];
// the relation which is the basis for this collection
final Relation rel;
private boolean orderIsDesc[];
/**
* Construct a new OrderedSubnodeList. The Relation is needed
* to get the information about the ORDERING
*/
public OrderedSubnodeList (Relation rel) {
this.rel = rel;
public OrderedSubnodeList (WrappedNodeManager nmgr, Relation rel) {
super(nmgr, rel);
this.origin = null;
// check the order of this collection for automatically sorting
// in the values in the correct order
if (rel.order == null) {
orderProperties=null;
orderIsDesc=null;
} else {
String singleOrders[] = rel.order.split(",");
orderProperties = new String[singleOrders.length];
orderIsDesc = new boolean[singleOrders.length];
DbMapping dbm = rel.otherType;
for (int i = 0; i < singleOrders.length; i++) {
String currOrder[] = singleOrders[i].trim().split(" ");
if (currOrder[0].equalsIgnoreCase(rel.otherType.getIDField())) {
orderProperties[i]=null;
} else {
orderProperties[i] = dbm.columnNameToProperty(currOrder[0]);
}
System.err.println("ORDER PROP " + i + " IS " + orderProperties[i] + " FROM " +currOrder[0]);
if (currOrder.length < 2
|| "ASC".equalsIgnoreCase(currOrder[1]))
orderIsDesc[i]=false;
else
orderIsDesc[i]=true;
}
}
initOrder(rel.order);
}
/**
@ -80,36 +55,31 @@ public class OrderedSubnodeList extends SubnodeList {
* @param expr the new order for this view
* @param rel the relation given for the origin-list
*/
public OrderedSubnodeList (OrderedSubnodeList origin, String expr, Relation rel) {
public OrderedSubnodeList (WrappedNodeManager nmgr, Relation rel, SubnodeList origin, String expr) {
super(nmgr, rel);
this.origin = origin;
this.rel = rel;
if (expr==null) {
initOrder(expr);
if (origin != null) {
sortIn(origin, false);
}
}
private void initOrder(String order) {
if (order == null) {
orderProperties=null;
orderIsDesc=null;
} else {
String singleOrders[] = expr.split(",");
orderProperties = new String[singleOrders.length];
orderIsDesc = new boolean[singleOrders.length];
DbMapping dbm = rel.otherType;
for (int i = 0; i<singleOrders.length; i++) {
String currOrder[] = singleOrders[i].trim().split(" ");
if (currOrder[0].equalsIgnoreCase("_id")) {
orderProperties[i]=null;
} else {
if (dbm.propertyToColumnName(currOrder[0])==null)
throw new RuntimeException ("Properties must be mapped to get an ordered collection for these properties.");
orderProperties[i]=currOrder[0];
}
if (currOrder.length < 2
|| "ASC".equalsIgnoreCase(currOrder[1]))
orderIsDesc[i]=false;
else
orderIsDesc[i]=true;
String orderParts[] = order.split(",");
orderProperties = new String[orderParts.length];
orderIsDesc = new boolean[orderParts.length];
for (int i = 0; i < orderParts.length; i++) {
String part[] = orderParts[i].trim().split(" ");
orderProperties[i] = part[0].equals("_id") ?
null : part[0];
orderIsDesc[i] = part.length == 2 &&
"DESC".equalsIgnoreCase(part[1]);
}
}
if (origin == null)
return;
this.sortIn(origin, false);
}
/**
@ -119,8 +89,19 @@ public class OrderedSubnodeList extends SubnodeList {
* @param obj element to be inserted.
*/
public boolean add(Object obj) {
System.err.println("******** SORT-ADDING " + obj);
return add(obj, false);
return add(obj, true);
}
/**
* Adds the specified object to the list at the given position
* @param idx the index to insert the element at
* @param obj the object t add
*/
public void add(int idx, Object obj) {
if (this.orderProperties!=null)
throw new RuntimeException ("Indexed add isn't alowed for ordered subnodes");
super.add(idx, obj);
addToViews(obj);
}
/**
@ -130,20 +111,23 @@ public class OrderedSubnodeList extends SubnodeList {
* @param obj element to be inserted.
*/
public boolean addSorted(Object obj) {
return add(obj, true);
return add(obj, false);
}
private boolean add(Object obj, boolean sorted) {
if (origin != null)
boolean add(Object obj, boolean sort) {
if (origin != null) {
return origin.add(obj);
vAdd(obj);
while (rel.maxSize>0 && this.size() >= rel.maxSize)
super.remove(0);
}
addToViews(obj);
int maxSize = rel == null ? 0 : rel.maxSize;
while (maxSize > 0 && this.size() >= maxSize) {
remove(size() - 1);
}
// escape sorting for presorted adds and grouped nodes
if (sorted || rel.groupby != null) {
super.add(obj);
} else {
if (sort && (rel == null || rel.groupby == null)) {
sortIn(obj);
} else {
super.add(obj);
}
return true;
}
@ -156,36 +140,23 @@ public class OrderedSubnodeList extends SubnodeList {
// no order, just add
if (this.orderProperties==null)
return super.add(obj);
vAdd(obj);
long start = System.currentTimeMillis();
try {
int idx = this.determineNodePosition((NodeHandle) obj, 0);
System.err.println("Position: " + idx);
if (idx<0)
return super.add(obj);
else
super.add(idx, obj);
return true;
} finally {
System.out.println("Sortmillis: " + (System.currentTimeMillis() - start));
}
}
private void vAdd (Object obj) {
if (views==null || origin!=null || views.size()<1)
return;
for (Iterator i = views.values().iterator(); i.hasNext(); ) {
OrderedSubnodeList osl = (OrderedSubnodeList) i.next();
osl.sortIn(obj);
}
addToViews(obj);
Node node = ((NodeHandle) obj).getNode(nmgr);
int idx = this.determineNodePosition(node, 0);
// System.err.println("Position: " + idx);
if (idx<0)
return super.add(obj);
else
super.add(idx, obj);
return true;
}
public boolean addAll(Collection col) {
return sortIn(col, true) > 0;
}
private void vAddAll (Collection col) {
if (views==null || origin!=null || views.size()<1)
private void addAllToViews (Collection col) {
if (views == null || origin != null || views.isEmpty())
return;
for (Iterator i = views.values().iterator(); i.hasNext(); ) {
OrderedSubnodeList osl = (OrderedSubnodeList) i.next();
@ -193,37 +164,32 @@ public class OrderedSubnodeList extends SubnodeList {
}
}
public void add(int idx, Object obj) {
if (this.orderProperties!=null)
throw new RuntimeException ("Indexed add isn't alowed for ordered subnodes");
super.add(idx, obj);
vAdd(obj);
}
/**
* Add all nodes contained inside the specified Collection to this
* UpdateableSubnodeList. The order of the added Nodes is asumed to
* be ordered according to the SQL-Order-Clausel given for this
* Subnodecollection but doesn't prevent adding of unordered Collections.
* Ordered Collections will be sorted in more efficient than unordered ones.
* UpdateableSubnodeList. The order of the added Nodes is assumed to
* be ordered according to the SQL-Order-Clause given for this
* Subnode collection but doesn't prevent adding of unordered Collections.
* Ordered Collections will be sorted in more efficiently than unordered ones.
*
* @param col the collection containing all elements to add in the order returned by the select-statement
* @param colHasDefaultOrder true if the given collection does have the default-order defined by the relation
*/
public int sortIn (Collection col, boolean colHasDefaultOrder) {
vAddAll(col);
addAllToViews(col);
int cntr=0;
// there is no order specified, add on top
if (orderProperties==null) {
if (orderProperties == null) {
for (Iterator i = col.iterator(); i.hasNext(); ) {
super.add(cntr, i.next());
cntr++;
}
if (rel.maxSize > 0) {
int diff = this.size() - rel.maxSize;
int maxSize = rel == null ? 0 : rel.maxSize;
if (maxSize > 0) {
int diff = this.size() - maxSize;
if (diff > 0)
super.removeRange(this.size()-1-diff, this.size()-1);
}
} else if (!colHasDefaultOrder || origin!=null) {
} else if (!colHasDefaultOrder || origin != null) {
// this collection is a view or the given collection doesn't have the
// default order
for (Iterator i = col.iterator(); i.hasNext(); ) {
@ -235,21 +201,21 @@ public class OrderedSubnodeList extends SubnodeList {
}
} else {
NodeHandle[] nhArr = (NodeHandle[]) col.toArray (new NodeHandle[0]);
int locIdx=determineNodePosition(nhArr[0], 0); // determine start-point
if (locIdx==-1)
locIdx=this.size();
Node node = nhArr[0].getNode(nmgr);
int locIdx = determineNodePosition(node, 0); // determine start-point
if (locIdx == -1)
locIdx = this.size();
// int interval=Math.max(1, this.size()/2);
int addIdx=0;
for (; addIdx < nhArr.length; addIdx++) {
while (locIdx < this.size() && compareNodes(nhArr[addIdx], (NodeHandle) this.get(locIdx)) >= 0)
for (int addIdx=0; addIdx < nhArr.length; addIdx++) {
node = nhArr[addIdx].getNode(nmgr);
while (locIdx < this.size() &&
compareNodes(node, (NodeHandle) this.get(locIdx)) >= 0)
locIdx++;
if (locIdx >= this.size())
break;
this.add(locIdx, nhArr[addIdx]);
cntr++;
}
for (; addIdx < nhArr.length; addIdx++) {
this.add(nhArr[addIdx]);
if (locIdx < this.size()) {
this.add(locIdx, nhArr[addIdx]);
} else {
this.add(nhArr[addIdx]);
}
cntr++;
}
}
@ -257,54 +223,18 @@ public class OrderedSubnodeList extends SubnodeList {
}
/**
* remove the object specified by the given index-position
* @param idx the index-position of the NodeHandle to remove
*/
public Object remove (int idx) {
vRemove(idx);
return super.remove(idx);
}
private void vRemove(int idx) {
if (views==null || origin!=null || views.size()<1)
return;
for (Iterator i = views.values().iterator(); i.hasNext(); ) {
OrderedSubnodeList osl = (OrderedSubnodeList) i.next();
osl.remove(idx);
}
}
/**
* remove the given Object from this List
* @param obj the NodeHandle to remove
*/
public boolean remove (Object obj) {
vRemove(obj);
return super.remove(obj);
}
private void vRemove(Object obj) {
if (views==null || origin!=null || views.size()<1)
return;
for (Iterator i = views.values().iterator(); i.hasNext(); ) {
OrderedSubnodeList osl = (OrderedSubnodeList) i.next();
osl.remove(obj);
}
}
/**
* remove all elements conteined inside the specified collection
* remove all elements contained inside the specified collection
* from this List
* @param c the Collection containing all Objects to remove from this List
* @return true if the List has been modified
*/
public boolean removeAll(Collection c) {
vRemoveAll(c);
removeAllFromViews(c);
return super.removeAll(c);
}
private void vRemoveAll(Collection c) {
if (views==null || origin!=null || views.size()<1)
private void removeAllFromViews(Collection c) {
if (views == null || origin != null || views.isEmpty())
return;
for (Iterator i = views.values().iterator(); i.hasNext(); ) {
OrderedSubnodeList osl = (OrderedSubnodeList) i.next();
@ -319,12 +249,12 @@ public class OrderedSubnodeList extends SubnodeList {
* @return true if the List has been modified
*/
public boolean retainAll (Collection c) {
vRetainAll(c);
retainAllInViews(c);
return super.retainAll(c);
}
private void vRetainAll(Collection c) {
if (views==null || origin!=null || views.size()<1)
private void retainAllInViews(Collection c) {
if (views == null || origin != null || views.isEmpty())
return;
for (Iterator i = views.values().iterator(); i.hasNext(); ) {
OrderedSubnodeList osl = (OrderedSubnodeList) i.next();
@ -332,21 +262,22 @@ public class OrderedSubnodeList extends SubnodeList {
}
}
private int determineNodePosition (NodeHandle nh, int startIdx) {
private int determineNodePosition (Node node, int startIdx) {
int size = this.size();
int interval = Math.max(1, (size-startIdx)/2);
int interval = Math.max(1, (size - startIdx) / 2);
boolean dirUp=true;
int cntr = 0;
int maxSize = rel == null ? 0 : rel.maxSize;
for (int i = 0; i < size
&& (i < rel.maxSize || rel.maxSize <= 0)
&& cntr<(size*2); cntr++) { // cntr is used to avoid endless-loops which shouldn't happen
&& (i < maxSize || maxSize <= 0)
&& cntr < (size * 2); cntr++) { // cntr is used to avoid endless-loops which shouldn't happen
NodeHandle curr = (NodeHandle) this.get(i);
int comp = compareNodes(nh, curr);
int comp = compareNodes(node, curr);
// current NodeHandle is below the given NodeHandle
// interval has to be 1 and
// idx must be zero or the node before the current node must be higher or equal
// all conditions must be met to determine the correct position of a node
if (comp < 0 && interval==1 && (i==0 || compareNodes(nh, (NodeHandle) this.get(i-1)) >= 0)) {
if (comp < 0 && interval==1 && (i==0 || compareNodes(node, (NodeHandle) this.get(i-1)) >= 0)) {
return i;
} else if (comp < 0) {
dirUp=false;
@ -359,7 +290,7 @@ public class OrderedSubnodeList extends SubnodeList {
if (dirUp) {
i=i+interval;
if (i >= this.size()) {
if (compareNodes(nh, (NodeHandle) this.get(size-1)) >= 0)
if (compareNodes(node, (NodeHandle) this.get(size-1)) >= 0)
break;
interval = Math.max(1, (i - size-1)/2);
i = this.size()-1;
@ -367,58 +298,58 @@ public class OrderedSubnodeList extends SubnodeList {
interval = Math.max(1,interval/2);
}
} else {
i=i-interval;
i = i-interval;
if (i < 0) { // shouldn't happen i think
interval=Math.max(1,(interval+i)/2);
interval=Math.max(1, (interval+i)/2);
i=0;
}
}
}
if (cntr >= size*2 && size>1) {
System.err.println("determineNodePosition needed more than the allowed iterations" + this.rel.prototype);
if (cntr >= size * 2 && size > 1) {
System.err.println("determineNodePosition needed more than the allowed iterations");
}
return -1;
}
/**
* Compare two nodes depending on the specified ORDER for this collection.
* @param nh1 the first NodeHandle
* @param nh2 the second NodeHandle
* @param node the first Node
* @param nodeHandle the second Node
* @return an integer lesser than zero if nh1 is less than, zero if nh1 is equal to and a value greater than zero if nh1 is bigger than nh2.
*/
private int compareNodes(NodeHandle nh1, NodeHandle nh2) {
WrappedNodeManager wnmgr=null;
private int compareNodes(Node node, NodeHandle nodeHandle) {
for (int i = 0; i < orderProperties.length; i++) {
if (orderProperties[i]==null) {
// we have the id as order-criteria-> avoid loading node
// and compare numerically instead of lexicographically
String s1 = nh1.getID();
String s2 = nh2.getID();
String s1 = node.getID();
String s2 = nodeHandle.getID();
int j = compareNumericString (s1, s2);
if (j==0)
if (j == 0)
continue;
if (orderIsDesc[i])
j=j*-1;
j = j * -1;
return j;
}
System.err.println("CHECKING PROPERTY: " + orderProperties[i] + " / " + orderIsDesc[i]);
if (wnmgr == null)
wnmgr = rel.otherType.getWrappedNodeManager();
Property p1 = nh1.getNode(wnmgr).getProperty(orderProperties[i]);
Property p2 = nh2.getNode(wnmgr).getProperty(orderProperties[i]);
System.out.println ("*** Comparing " + p1 + " - " + p2);
Property p1 = node.getProperty(orderProperties[i]);
Property p2 = nodeHandle.getNode(nmgr).getProperty(orderProperties[i]);
int j;
if (p1==null && p2==null)
if (p1 == null && p2 == null) {
continue;
else if (p1==null)
} else if (p1 == null) {
j = 1;
} else if (p2 == null) {
j = -1;
else
} else {
j = p1.compareTo(p2);
if (j == 0)
}
if (j == 0) {
continue;
if (orderIsDesc[i])
}
if (orderIsDesc[i]) {
j = j * -1;
}
return j;
}
return -1;
@ -451,21 +382,10 @@ public class OrderedSubnodeList extends SubnodeList {
}
public List getOrderedView (String order) {
if (origin != null)
if (origin != null) {
return origin.getOrderedView(order);
String key = order.trim().toLowerCase();
if (key.equalsIgnoreCase(rel.order))
return this;
long start = System.currentTimeMillis();
if (views == null)
views = new HashMap();
OrderedSubnodeList osl = (OrderedSubnodeList) views.get(key);
if (osl == null) {
osl = new OrderedSubnodeList (this, order, rel);
views.put(key, osl);
System.out.println("getting view cost me " + (System.currentTimeMillis()-start) + " millis");
} else
System.out.println("getting cached view cost me " + (System.currentTimeMillis()-start) + " millis");
return osl;
} else {
return super.getOrderedView(order);
}
}
}

View file

@ -508,9 +508,9 @@ public final class Property implements IProperty, Serializable, Cloneable, Compa
if (value==null && pvalue == null) {
return 0;
} else if (value == null) {
return -1;
} if (pvalue == null) {
return 1;
} if (pvalue == null) {
return -1;
}
if (type != ptype) {
throw new ClassCastException("uncomparable values " + this + "(" + type + ") : " + p + "(" + ptype + ")");

View file

@ -17,12 +17,34 @@
package helma.objectmodel.db;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Iterator;
/**
* A subclass of ArrayList that adds an addSorted(Object) method to
*/
public class SubnodeList extends ArrayList {
WrappedNodeManager nmgr;
HashMap views = null;
Relation rel;
/**
* Hide/disable zero argument constructor for subclasses
*/
private SubnodeList() {}
/**
* Creates a new subnode list
* @param nmgr
*/
public SubnodeList(WrappedNodeManager nmgr, Relation rel) {
this.nmgr = nmgr;
this.rel = rel;
}
/**
* Inserts the specified element at the specified position in this
* list without performing custom ordering
@ -33,4 +55,77 @@ public class SubnodeList extends ArrayList {
return add(obj);
}
/**
* Adds the specified object to this list performing
* custom ordering
*
* @param obj element to be inserted.
*/
public boolean add(Object obj) {
addToViews(obj);
return super.add(obj);
}
/**
* Adds the specified object to the list at the given position
* @param idx the index to insert the element at
* @param obj the object t add
*/
public void add(int idx, Object obj) {
addToViews(obj);
super.add(idx, obj);
}
/**
* remove the object specified by the given index-position
* @param idx the index-position of the NodeHandle to remove
*/
public Object remove (int idx) {
Object obj = get(idx);
if (obj != null) {
removeFromViews(obj);
}
return super.remove(idx);
}
/**
* remove the given Object from this List
* @param obj the NodeHandle to remove
*/
public boolean remove (Object obj) {
removeFromViews(obj);
return super.remove(obj);
}
protected void removeFromViews(Object obj) {
if (views == null || views.isEmpty())
return;
for (Iterator i = views.values().iterator(); i.hasNext(); ) {
OrderedSubnodeList osl = (OrderedSubnodeList) i.next();
osl.remove(obj);
}
}
public List getOrderedView (String order) {
String key = order.trim().toLowerCase();
// long start = System.currentTimeMillis();
if (views == null) {
views = new HashMap();
}
OrderedSubnodeList osl = (OrderedSubnodeList) views.get(key);
if (osl == null) {
osl = new OrderedSubnodeList (nmgr, rel, this, order);
views.put(key, osl);
}
return osl;
}
protected void addToViews (Object obj) {
if (views == null || views.isEmpty())
return;
for (Iterator i = views.values().iterator(); i.hasNext(); ) {
OrderedSubnodeList osl = (OrderedSubnodeList) i.next();
osl.sortIn(obj);
}
}
}

View file

@ -38,8 +38,8 @@ public class UpdateableSubnodeList extends OrderedSubnodeList {
* Construct a new UpdateableSubnodeList. The Relation is needed
* to get the information about the ORDERING and the UPDATECriteriaS
*/
public UpdateableSubnodeList (Relation rel) {
super(rel);
public UpdateableSubnodeList (WrappedNodeManager nmgr, Relation rel) {
super(nmgr, rel);
// check the update-criterias for updating this collection
if (rel.updateCriteria == null) {
// criteria-field muss vom criteria-operant getrennt werden
@ -391,7 +391,7 @@ public class UpdateableSubnodeList extends OrderedSubnodeList {
/**
* if the wrapped List is an instance of OrderedSubnodeList,
* the sortIn-method will be used.
* the sortIn() method will be used.
*/
public boolean addAll(Collection col) {
return sortIn(col, true) > 0;

View file

@ -35,16 +35,6 @@ import java.io.IOException;
*
*/
public class HopObject extends ScriptableObject implements Wrapper, PropertyRecorder {
static Method hopObjCtor;
static {
try {
hopObjCtor = HopObject.class.getMethod("jsConstructor", new Class[] {
Context.class, Object[].class, Function.class, Boolean.TYPE });
} catch (NoSuchMethodException e) {
throw new RuntimeException("Error getting HopObject.jsConstructor()");
}
}
String className;
INode node;
@ -54,40 +44,46 @@ public class HopObject extends ScriptableObject implements Wrapper, PropertyReco
private boolean isRecording = false;
private HashSet changedProperties;
/**
* Creates a new HopObject object.
*/
public HopObject() {
className = "HopObject";
}
/**
* Creates a new HopObject prototype.
*
* @param cname ...
* @param className the prototype name
* @param core the RhinoCore
*/
protected HopObject(String cname) {
className = cname;
protected HopObject(String className, RhinoCore core) {
this.className = className;
this.core = core;
setParentScope(core.global);
}
/**
* Creates a new HopObject prototype.
* Creates a new HopObject.
*
* @param cname ...
* @param className the className
* @param proto the object's prototype
*/
protected HopObject(String cname, Scriptable proto) {
className = cname;
protected HopObject(String className, RhinoCore core,
INode node, Scriptable proto) {
this(className, core);
this.node = node;
setPrototype(proto);
}
public static HopObject init(Scriptable scope)
/**
* Initialize HopObject prototype for Rhino scope.
*
* @param core the RhinoCore
* @return the HopObject prototype
* @throws PropertyException
*/
public static HopObject init(RhinoCore core)
throws PropertyException {
int attributes = READONLY | DONTENUM | PERMANENT;
// create prototype object
HopObject proto = new HopObject();
proto.setPrototype(getObjectPrototype(scope));
HopObject proto = new HopObject("HopObject", core);
proto.setPrototype(getObjectPrototype(core.global));
// install JavaScript methods and properties
Method[] methods = HopObject.class.getDeclaredMethods();
@ -109,54 +105,6 @@ public class HopObject extends ScriptableObject implements Wrapper, PropertyReco
return proto;
}
/**
* This method is used as HopObject constructor from JavaScript.
*/
public static Object jsConstructor(Context cx, Object[] args,
Function ctorObj, boolean inNewExpr)
throws EvaluatorException, ScriptingException {
RhinoEngine engine = (RhinoEngine) cx.getThreadLocal("engine");
RhinoCore core = engine.core;
String protoname = ((FunctionObject) ctorObj).getFunctionName();
// if this is a java object prototype, create a new java object
// of the given class instead of a HopObject.
if (core.app.isJavaPrototype(protoname)) {
String classname = core.app.getJavaClassForPrototype(protoname);
try {
Class clazz = Class.forName(classname);
// try to get the constructor matching our arguments
Class[] argsTypes = new Class[args.length];
for (int i=0; i<argsTypes.length; i++) {
argsTypes[i] = args[i] == null ? null : args[i].getClass();
}
Constructor cnst = clazz.getConstructor(argsTypes);
// crate a new instance using the constructor
Object obj = cnst.newInstance(args);
return Context.toObject(obj, engine.global);
} catch (Exception x) {
System.err.println("Error in Java constructor: "+x);
throw new EvaluatorException(x.toString());
}
} else {
INode node = new helma.objectmodel.db.Node(protoname, protoname,
core.app.getWrappedNodeManager());
Scriptable proto = core.getPrototype(protoname);
HopObject hobj = new HopObject(protoname, proto);
hobj.init(core, node);
if (proto != null) {
engine.invoke(hobj,
"__constructor__",
args, ScriptingEngine.ARGS_WRAP_NONE,
false);
}
return hobj;
}
}
/**
*
*
@ -177,17 +125,6 @@ public class HopObject extends ScriptableObject implements Wrapper, PropertyReco
return toString();
}
/**
*
*
* @param c ...
* @param n ...
*/
public void init(RhinoCore c, INode n) {
core = c;
node = n;
}
/**
* Return the INode wrapped by this HopObject.
*
@ -217,7 +154,7 @@ public class HopObject extends ScriptableObject implements Wrapper, PropertyReco
* Check if the node has been invalidated. If so, it has to be re-fetched
* from the db via the app's node manager.
*/
private final void checkNode() {
private void checkNode() {
if (node != null && node.getState() == INode.INVALID) {
if (node instanceof helma.objectmodel.db.Node) {
NodeHandle handle = ((helma.objectmodel.db.Node) node).getHandle();
@ -425,7 +362,7 @@ public class HopObject extends ScriptableObject implements Wrapper, PropertyReco
if (id instanceof Number) {
n = node.getSubnodeAt(((Number) id).intValue());
} else if (id != null) {
} else {
n = node.getChildElement(id.toString());
}
@ -1132,16 +1069,12 @@ public class HopObject extends ScriptableObject implements Wrapper, PropertyReco
}
helma.objectmodel.db.Node n = (helma.objectmodel.db.Node) node;
n.loadNodes();
List subnodes = n.getSubnodeList();
SubnodeList subnodes = n.getSubnodeList();
if (subnodes == null) {
throw new RuntimeException (
"getOrderedView only callable on already existing subnode-collections");
}
if (subnodes instanceof OrderedSubnodeList) {
return new ListViewWrapper ((((OrderedSubnodeList) subnodes).getOrderedView(expr)),
return new ListViewWrapper (subnodes.getOrderedView(expr),
core, n.getDbMapping().getWrappedNodeManager(), this);
}
throw new RuntimeException (
"getOrderedView only callable on OrderedSubnodeList");
}
}

View file

@ -0,0 +1,149 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.scripting.rhino;
import org.mozilla.javascript.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import helma.objectmodel.INode;
import helma.objectmodel.db.DbMapping;
import helma.objectmodel.db.DbKey;
public class HopObjectCtor extends FunctionObject {
// init flag to trigger prototype compilation on
// static constructor property access
boolean initialized;
RhinoCore core;
static Method hopObjCtor;
static {
try {
hopObjCtor = HopObjectCtor.class.getMethod("jsConstructor", new Class[] {
Context.class, Object[].class, Function.class, Boolean.TYPE });
} catch (NoSuchMethodException e) {
throw new RuntimeException("Error getting HopObjectCtor.jsConstructor()");
}
}
static final int attr = ScriptableObject.DONTENUM |
ScriptableObject.PERMANENT |
ScriptableObject.READONLY;
/**
* Create and install a HopObject constructor.
* Part of this is copied from o.m.j.FunctionObject.addAsConstructor().
*
* @param prototype
*/
public HopObjectCtor(String protoName, RhinoCore core, Scriptable prototype) {
super(protoName, hopObjCtor, core.global);
this.core = core;
// Scriptable ps = prototype.getParentScope();
addAsConstructor(core.global, prototype);
// prototype.setParentScope(ps);
defineProperty("getById", new GetById(), attr);
}
/**
* This method is used as HopObject constructor from JavaScript.
*/
public static Object jsConstructor(Context cx, Object[] args,
Function ctorObj, boolean inNewExpr)
throws JavaScriptException {
HopObjectCtor ctor = (HopObjectCtor) ctorObj;
RhinoCore core = ctor.core;
String protoname = ctor.getFunctionName();
// if this is a java object prototype, create a new java object
// of the given class instead of a HopObject.
if (core.app.isJavaPrototype(protoname)) {
String classname = core.app.getJavaClassForPrototype(protoname);
try {
Class clazz = Class.forName(classname);
// try to get the constructor matching our arguments
Class[] argsTypes = new Class[args.length];
for (int i=0; i<argsTypes.length; i++) {
argsTypes[i] = args[i] == null ? null : args[i].getClass();
}
Constructor cnst = clazz.getConstructor(argsTypes);
// crate a new instance using the constructor
Object obj = cnst.newInstance(args);
return Context.toObject(obj, core.global);
} catch (Exception x) {
System.err.println("Error in Java constructor: "+x);
throw new EvaluatorException(x.toString());
}
} else {
INode node = new helma.objectmodel.db.Node(protoname, protoname,
core.app.getWrappedNodeManager());
Scriptable proto = core.getPrototype(protoname);
HopObject hobj = new HopObject(protoname, core, node, proto);
if (proto != null) {
Object f = ScriptableObject.getProperty(proto, "__constructor__");
if (f instanceof Function) {
((Function) f).call(cx, core.global, hobj, args);
}
}
return hobj;
}
}
public Object get(String name, Scriptable start) {
if (!initialized && !"prototype".equals(name)) {
// trigger prototype compilation on static
// constructor property access
initialized = true;
core.getPrototype(functionName);
}
return super.get(name, start);
}
class GetById extends BaseFunction {
/**
* Retrieve any persistent HopObject by type name and id.
*
* @return the HopObject or null if it doesn't exist
*/
public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
if (args.length < 1 || args.length > 2)
throw new IllegalArgumentException("Wrong number of arguments in getById()");
// If second argument is provided, use it as type name.
// Otherwise, use our own type name.
String type = args.length == 1 ?
HopObjectCtor.this.getFunctionName() :
Context.toString(args[1]);
DbMapping dbmap = core.app.getDbMapping(type);
if (dbmap == null)
return null;
Object node = null;
try {
DbKey key = new DbKey(dbmap, Context.toString(args[0]));
node = core.app.getNodeManager().getNode(key);
} catch (Exception x) {
return null;
}
return node == null ? null : Context.toObject(node, this);
}
}
}

View file

@ -46,6 +46,26 @@ public class ListViewWrapper extends ScriptableObject implements Wrapper, Script
final HopObject hObj;
INode node;
static ListViewWrapper listViewProto;
/**
* Private constructor used to create the object prototype.
*/
private ListViewWrapper() {
list = null;
core = null;
wnm = null;
node = null;
hObj = null;
}
/**
* Create a JS wrapper around a subnode list.
* @param list
* @param core
* @param wnm
* @param hObj
*/
ListViewWrapper (List list, RhinoCore core, WrappedNodeManager wnm, HopObject hObj) {
if (list == null) {
throw new IllegalArgumentException ("ListWrapper unable to wrap null list.");
@ -55,13 +75,20 @@ public class ListViewWrapper extends ScriptableObject implements Wrapper, Script
this.wnm = wnm;
this.hObj = hObj;
this.node = hObj.node;
init();
if (listViewProto == null) {
listViewProto = new ListViewWrapper();
listViewProto.init();
}
setPrototype(listViewProto);
}
private void init() {
/**
* Init JS functions from methods.
*/
void init() {
int attributes = READONLY | DONTENUM | PERMANENT;
Method[] methods = this.getClass().getDeclaredMethods();
Method[] methods = getClass().getDeclaredMethods();
for (int i=0; i<methods.length; i++) {
String methodName = methods[i].getName();
@ -70,7 +97,6 @@ public class ListViewWrapper extends ScriptableObject implements Wrapper, Script
FunctionObject func = new FunctionObject(methodName,
methods[i], this);
this.defineProperty(methodName, func, attributes);
}
}
}
@ -98,9 +124,7 @@ public class ListViewWrapper extends ScriptableObject implements Wrapper, Script
// return HopObject in case of a NodeHandle
if (obj instanceof NodeHandle) {
HopObject hObj = new HopObject();
hObj.init(core, ((NodeHandle) obj).getNode(wnm));
return hObj;
return Context.toObject(((NodeHandle) obj).getNode(wnm), core.global);
} else if (!(obj instanceof Scriptable)) {
// do NOT wrap primitives - otherwise they'll be wrapped as Objects,
// which makes them unusable for many purposes (e.g. ==)

View file

@ -113,7 +113,7 @@ public final class RhinoCore implements ScopeProvider {
pathProto = new PathWrapper(this);
hopObjectProto = HopObject.init(global);
hopObjectProto = HopObject.init(this);
// use lazy loaded constructors for all extension objects that
// adhere to the ScriptableObject.defineClass() protocol
new LazilyLoadedCtor(global, "File",
@ -207,10 +207,9 @@ public final class RhinoCore implements ScopeProvider {
} else if ("hopobject".equals(lowerCaseName)) {
op = hopObjectProto;
} else {
op = new HopObject(name);
op.setParentScope(global);
op = new HopObject(name, this);
}
type = registerPrototype(prototype, op);
registerPrototype(prototype, op);
}
// Register a constructor for all types except global.
@ -218,13 +217,10 @@ public final class RhinoCore implements ScopeProvider {
// the actual (scripted) constructor on it.
if (!"global".equals(lowerCaseName)) {
try {
FunctionObject fo = new FunctionObject(name, HopObject.hopObjCtor, global);
fo.addAsConstructor(global, op);
// add static getById() function
fo.defineProperty("getById", new GetById(name), GetById.ATTRIBUTES);
} catch (Exception ignore) {
System.err.println("Error adding ctor for " + name + ": " + ignore);
ignore.printStackTrace();
new HopObjectCtor(name, this, op);
op.setParentScope(global);
} catch (Exception x) {
app.logError("Error adding ctor for " + name, x);
}
}
}
@ -294,58 +290,52 @@ public final class RhinoCore implements ScopeProvider {
* here is to check for update those prototypes which already have been compiled
* before. Others will be updated/compiled on demand.
*/
public void updatePrototypes() throws IOException {
public synchronized void updatePrototypes() throws IOException {
if ((System.currentTimeMillis() - lastUpdate) < 1000L + updateSnooze) {
return;
}
synchronized(this) {
if ((System.currentTimeMillis() - lastUpdate) < 1000L + updateSnooze) {
return;
// init prototypes and/or update prototype checksums
app.typemgr.checkPrototypes();
// get a collection of all prototypes (code directories)
Collection protos = app.getPrototypes();
// in order to respect inter-prototype dependencies, we try to update
// the global prototype before all other prototypes, and parent
// prototypes before their descendants.
HashSet checked = new HashSet(protos.size() * 2);
TypeInfo type = (TypeInfo) prototypes.get("global");
if (type != null) {
updatePrototype(type, checked);
}
for (Iterator i = protos.iterator(); i.hasNext();) {
Prototype proto = (Prototype) i.next();
if (checked.contains(proto)) {
continue;
}
// init prototypes and/or update prototype checksums
app.typemgr.checkPrototypes();
type = (TypeInfo) prototypes.get(proto.getLowerCaseName());
// get a collection of all prototypes (code directories)
Collection protos = app.getPrototypes();
// in order to respect inter-prototype dependencies, we try to update
// the global prototype before all other prototypes, and parent
// prototypes before their descendants.
HashSet checked = new HashSet(protos.size() * 2);
TypeInfo type = (TypeInfo) prototypes.get("global");
if (type != null) {
if (type == null) {
// a prototype we don't know anything about yet. Init local update info.
initPrototype(proto);
} else if (type.lastUpdate > -1) {
// only need to update prototype if it has already been initialized.
// otherwise, this will be done on demand.
updatePrototype(type, checked);
}
for (Iterator i = protos.iterator(); i.hasNext();) {
Prototype proto = (Prototype) i.next();
if (checked.contains(proto)) {
continue;
}
type = (TypeInfo) prototypes.get(proto.getLowerCaseName());
if (type == null) {
// a prototype we don't know anything about yet. Init local update info.
initPrototype(proto);
} else if (type.lastUpdate > -1) {
// only need to update prototype if it has already been initialized.
// otherwise, this will be done on demand.
updatePrototype(type, checked);
}
}
lastUpdate = System.currentTimeMillis();
// max updateSnooze is 4 seconds, reached after 66.6 idle minutes
long newSnooze = (lastUpdate - app.typemgr.getLastCodeUpdate()) / 1000;
updateSnooze = Math.min(4000, Math.max(0, newSnooze));
}
lastUpdate = System.currentTimeMillis();
// max updateSnooze is 4 seconds, reached after 66.6 idle minutes
long newSnooze = (lastUpdate - app.typemgr.getLastCodeUpdate()) / 1000;
updateSnooze = Math.min(4000, Math.max(0, newSnooze));
}
/**
@ -638,8 +628,7 @@ public final class RhinoCore implements ScopeProvider {
}
}
esn = new HopObject(protoname, op);
esn.init(this, n);
esn = new HopObject(protoname, this, n, op);
wrappercache.put(n, esn);
}
@ -1077,41 +1066,7 @@ public final class RhinoCore implements ScopeProvider {
} else {
df = new DecimalFormat("#,##0.00");
}
return df.format(ScriptRuntime.toNumber(thisObj)).toString();
}
}
class GetById extends BaseFunction {
static final int ATTRIBUTES = DONTENUM | PERMANENT | READONLY;
String typeName;
public GetById(String typeName) {
this.typeName = typeName;
}
/**
* Retrieve any persistent HopObject by type name and id.
*
* @return the HopObject or null if it doesn't exist
*/
public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
if (args.length < 1 || args.length > 2)
throw new IllegalArgumentException("Wrong number of arguments in getById()");
// If second argument is provided, use it as type name.
// Otherwise, use our own type name.
String type = args.length == 1 ? typeName: Context.toString(args[1]);
DbMapping dbmap = app.getDbMapping(type);
if (dbmap == null)
return null;
Object node = null;
try {
DbKey key = new DbKey(dbmap, Context.toString(args[0]));
node = app.getNodeManager().getNode(key);
} catch (Exception x) {
return null;
}
return node == null ? null : Context.toObject(node, this);
return df.format(ScriptRuntime.toNumber(thisObj));
}
}