diff --git a/src/helma/scripting/rhino/GlobalObject.java b/src/helma/scripting/rhino/GlobalObject.java new file mode 100644 index 00000000..a8cf6171 --- /dev/null +++ b/src/helma/scripting/rhino/GlobalObject.java @@ -0,0 +1,283 @@ +/* + * 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 helma.framework.*; +import helma.framework.core.*; +import helma.objectmodel.*; +import helma.objectmodel.db.*; +import helma.util.HtmlEncoder; +import org.mozilla.javascript.*; +import java.util.HashMap; +import java.util.Map; + +/** + * + */ +public class GlobalObject extends ScriptableObject { + Application app; + RhinoCore core; + + /** + * Creates a new GlobalObject object. + * + * @param core ... + * @param app ... + * + * @throws PropertyException ... + */ + public GlobalObject(RhinoCore core, Application app) + throws PropertyException { + this.core = core; + this.app = app; + + String[] globalFuncs = { + "renderSkin", "renderSkinAsString", "getProperty", + "authenticate", "createSkin", "format", "encode", + "encodeXml", "encodeForm", "stripTags" + }; + + defineFunctionProperties(globalFuncs, GlobalObject.class, 0); + put("app", this, new ApplicationBean(app)); + put("Xml", this, new XmlObject(core)); + } + + /** + * + * + * @return ... + */ + public String getClassName() { + return "GlobalObject"; + } + + /** + * + * + * @param skin ... + * @param param ... + * + * @return ... + */ + public boolean renderSkin(Object skin, Object param) { + // System.err.println ("RENDERSKIN CALLED WITH PARAM "+param); + Context cx = Context.getCurrentContext(); + RequestEvaluator reval = (RequestEvaluator) cx.getThreadLocal("reval"); + Skin s; + + if (skin instanceof Skin) { + s = (Skin) skin; + } else { + s = core.app.getSkin(null, skin.toString(), null); + } + + Map p = null; + + if ((param != null) && (param != Undefined.instance)) { + p = new HashMap(); + + if (param instanceof Scriptable) { + Scriptable sp = (Scriptable) param; + Object[] ids = sp.getIds(); + + for (int i = 0; i < ids.length; i++) { + Object obj = sp.get(ids[i].toString(), sp); + if (obj instanceof NativeJavaObject) { + p.put(ids[i], ((NativeJavaObject) obj).unwrap()); + } else { + p.put(ids[i], obj); + } + } + } + } + + if (s != null) { + s.render(reval, null, p); + } + + return true; + } + + /** + * + * + * @param skin ... + * @param param ... + * + * @return ... + */ + public String renderSkinAsString(Object skin, Object param) { + Context cx = Context.getCurrentContext(); + RequestEvaluator reval = (RequestEvaluator) cx.getThreadLocal("reval"); + Skin s; + + if (skin instanceof Skin) { + s = (Skin) skin; + } else { + s = core.app.getSkin(null, skin.toString(), null); + } + + Map p = null; + + if ((param != null) && (param != Undefined.instance)) { + p = new HashMap(); + + if (param instanceof Scriptable) { + Scriptable sp = (Scriptable) param; + Object[] ids = sp.getIds(); + + for (int i = 0; i < ids.length; i++) { + Object obj = sp.get(ids[i].toString(), sp); + if (obj instanceof NativeJavaObject) { + p.put(ids[i], ((NativeJavaObject) obj).unwrap()); + } else { + p.put(ids[i], obj); + } + } + } + } + + if (s != null) { + reval.res.pushStringBuffer(); + s.render(reval, null, p); + + return reval.res.popStringBuffer(); + } + + return ""; + } + + /** + * + * + * @param propname ... + * + * @return ... + */ + public String getProperty(String propname) { + return app.getProperty(propname); + } + + /** + * + * + * @param user ... + * @param pwd ... + * + * @return ... + */ + public boolean authenticate(String user, String pwd) { + return app.authenticate(user, pwd); + } + + /** + * + * + * @param str ... + * + * @return ... + */ + public Skin createSkin(String str) { + return new Skin(str, app); + } + + /** + * + * + * @param str ... + * + * @return ... + */ + public String encode(String str) { + return HtmlEncoder.encodeAll(str); + } + + /** + * + * + * @param str ... + * + * @return ... + */ + public String encodeXml(String str) { + return HtmlEncoder.encodeXml(str); + } + + /** + * + * + * @param str ... + * + * @return ... + */ + public String encodeForm(String str) { + return HtmlEncoder.encodeFormValue(str); + } + + /** + * + * + * @param str ... + * + * @return ... + */ + public String format(String str) { + return HtmlEncoder.encode(str); + } + + /** + * + * + * @param str ... + * + * @return ... + */ + public String stripTags(String str) { + if (str == null) { + return null; + } + + char[] c = str.toCharArray(); + boolean inTag = false; + int i; + int j = 0; + + for (i = 0; i < c.length; i++) { + if (c[i] == '<') { + inTag = true; + } + + if (!inTag) { + if (i > j) { + c[j] = c[i]; + } + + j++; + } + + if (c[i] == '>') { + inTag = false; + } + } + + if (i > j) { + return new String(c, 0, j); + } + + return str; + } +} diff --git a/src/helma/scripting/rhino/HopObject.java b/src/helma/scripting/rhino/HopObject.java new file mode 100644 index 00000000..f828cb5d --- /dev/null +++ b/src/helma/scripting/rhino/HopObject.java @@ -0,0 +1,738 @@ +/* + * 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 helma.framework.*; +import helma.framework.core.*; +import helma.objectmodel.*; +import helma.objectmodel.db.*; +import org.mozilla.javascript.*; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +/** + * + */ +public class HopObject extends ScriptableObject { + static Method hopObjCtor; + + static { + Method[] methods = HopObject.class.getMethods(); + + for (int i = 0; i < methods.length; i++) { + if ("hopObjectConstructor".equals(methods[i].getName())) { + hopObjCtor = methods[i]; + + break; + } + } + } + + String className; + INode node; + RhinoCore core; + + /** + * Creates a new HopObject object. + */ + public HopObject() { + className = "HopObject"; + } + + /** + * Creates a new HopObject object. + * + * @param cname ... + */ + public HopObject(String cname) { + className = cname; + } + + // public void jsConstructor () { + + /* Context cx = Context.getCurrentContext (); + RhinoEngine engine = (RhinoEngine) cx.getThreadLocal ("engine"); + core = engine.core; + node = new helma.objectmodel.db.Node (null, null, core.app.getWrappedNodeManager ()); */ + + // } + public static Object hopObjectConstructor(Context cx, Object[] args, + Function ctorObj, boolean inNewExpr) { + RhinoEngine engine = (RhinoEngine) cx.getThreadLocal("engine"); + RhinoCore c = engine.core; + String prototype = ((FunctionObject) ctorObj).getFunctionName(); + INode n = new helma.objectmodel.db.Node(prototype, prototype, + c.app.getWrappedNodeManager()); + HopObject hobj = new HopObject(prototype); + + hobj.init(c, n); + + return hobj; + } + + /** + * + * + * @return ... + */ + public String getClassName() { + return className; + } + + /** + * + * + * @param c ... + * @param n ... + */ + public void init(RhinoCore c, INode n) { + core = c; + node = n; + } + + /** + * + * + * @return ... + */ + public Object jsGet_cache() { + if (node == null) { + return null; + } + + return node.getCacheNode(); + } + + /** + * + * + * @param skin ... + * @param param ... + * + * @return ... + */ + public boolean jsFunction_renderSkin(Object skin, Object param) { + // System.err.println ("RENDERSKIN CALLED WITH PARAM "+param); + Context cx = Context.getCurrentContext(); + RequestEvaluator reval = (RequestEvaluator) cx.getThreadLocal("reval"); + Skin s; + + if (skin instanceof Skin) { + s = (Skin) skin; + } else { + s = core.app.getSkin(node, skin.toString(), null); + } + + Map p = null; + + if ((param != null) && (param != Undefined.instance)) { + p = new HashMap(); + + if (param instanceof Scriptable) { + Scriptable sp = (Scriptable) param; + Object[] ids = sp.getIds(); + + for (int i = 0; i < ids.length; i++) { + Object obj = sp.get(ids[i].toString(), sp); + if (obj instanceof NativeJavaObject) { + p.put(ids[i], ((NativeJavaObject) obj).unwrap()); + } else { + p.put(ids[i], obj); + } + } + } + } + + if (s != null) { + s.render(reval, node, p); + } + + return true; + } + + /** + * + * + * @param skin ... + * @param param ... + * + * @return ... + */ + public String jsFunction_renderSkinAsString(Object skin, Object param) { + // System.err.println ("RENDERSKIN CALLED WITH PARAM "+param); + Context cx = Context.getCurrentContext(); + RequestEvaluator reval = (RequestEvaluator) cx.getThreadLocal("reval"); + Skin s; + + if (skin instanceof Skin) { + s = (Skin) skin; + } else { + s = core.app.getSkin(node, skin.toString(), null); + } + + Map p = null; + + if ((param != null) && (param != Undefined.instance)) { + p = new HashMap(); + + if (param instanceof Scriptable) { + Scriptable sp = (Scriptable) param; + Object[] ids = sp.getIds(); + + for (int i = 0; i < ids.length; i++) { + Object obj = sp.get(ids[i].toString(), sp); + if (obj instanceof NativeJavaObject) { + p.put(ids[i], ((NativeJavaObject) obj).unwrap()); + } else { + p.put(ids[i], obj); + } + } + } + } + + if (s != null) { + reval.res.pushStringBuffer(); + s.render(reval, node, p); + + return reval.res.popStringBuffer(); + } + + return ""; + } + + /** + * + * + * @param action ... + * + * @return ... + */ + public Object jsFunction_href(Object action) { + if (node == null) { + return null; + } + + String act = null; + + if (action != null) { + if (action instanceof NativeJavaObject) { + act = ((NativeJavaObject) action).unwrap().toString(); + } else if (!(action instanceof Undefined)) { + act = action.toString(); + } + } + + return core.app.getNodeHref(node, act); + } + + /** + * + * + * @param id ... + * + * @return ... + */ + public Object jsFunction_get(Object id) { + if ((node == null) || (id == null)) { + return null; + } + + Context cx = Context.getCurrentContext(); + Object n = null; + + if (id instanceof Number) { + n = node.getSubnodeAt(((Number) id).intValue()); + } else { + n = node.getChildElement(id.toString()); + } + + if (n == null) { + return null; + } else { + return cx.toObject(n, core.global); + } + } + + /** + * + * + * @return ... + */ + public int jsFunction_count() { + if (node == null) { + return 0; + } + + return node.numberOfNodes(); + } + + /** + * + * + * @return ... + */ + public int jsFunction_size() { + return jsFunction_count(); + } + + /** + * Prefetch child objects from (relational) database. + */ + public void jsFunction_prefetchChildren() { + jsFunction_prefetchChildren(0, 1000); + } + + /** + * Prefetch child objects from (relational) database. + */ + public void jsFunction_prefetchChildren(int start, int length) { + if (!(node instanceof helma.objectmodel.db.Node)) { + return; + } + + try { + ((helma.objectmodel.db.Node) node).prefetchChildren(start, length); + } catch (Exception x) { + } + } + + /** + * + * + * @return ... + */ + public Object[] jsFunction_list() { + int l = node.numberOfNodes(); + Enumeration e = node.getSubnodes(); + ArrayList a = new ArrayList(); + + while ((e != null) && e.hasMoreElements()) { + a.add(e.nextElement()); + } + + return a.toArray(); + } + + /** + * + * + * @param child ... + * + * @return ... + */ + public boolean jsFunction_add(Object child) { + if ((node == null) || (child == null)) { + return false; + } + + if (child instanceof HopObject) { + INode added = node.addNode(((HopObject) child).node); + + return true; + } else if (child instanceof INode) { + INode added = node.addNode((INode) child); + + return true; + } + + return false; + } + + /** + * + * + * @param index ... + * @param child ... + * + * @return ... + */ + public boolean jsFunction_addAt(int index, Object child) { + if (child == null) { + return false; + } + + if (child instanceof HopObject) { + INode added = node.addNode(((HopObject) child).node, index); + + return true; + } else if (child instanceof INode) { + INode added = node.addNode((INode) child, index); + + return true; + } + + return false; + } + + /** + * Remove node itself or one or more subnodes. + */ + public boolean jsFunction_remove(Object child) { + // semantics: if called without arguments, remove self. + // otherwise, remove given subnodes. + if (child == null) { + return node.remove(); + } else if (node != null) { + try { + if (child instanceof HopObject) { + HopObject hobj = (HopObject) child; + + if (hobj.node != null) { + node.removeNode(hobj.node); + + return true; + } + } + } catch (Exception x) { + System.err.println("XXX: " + x); + x.printStackTrace(); + } + } + + return false; + } + + /** + * Invalidate the node itself or a subnode + */ + public boolean jsFunction_invalidate(String childId) { + if (node instanceof helma.objectmodel.db.Node) { + if (childId == null) { + ((helma.objectmodel.db.Node) node).invalidate(); + } else { + ((helma.objectmodel.db.Node) node).invalidateNode(childId); + } + } + + return true; + } + + /** + * Check if node is contained in subnodes + */ + public int jsFunction_contains(Object obj) { + if ((node != null) && (obj != null) && obj instanceof HopObject) { + return node.contains(((HopObject) obj).node); + } + + return -1; + } + + /** + * + * + * @param name ... + * @param start ... + * @param value ... + */ + public void put(String name, Scriptable start, Object value) { + if (node == null) { + super.put(name, start, value); + } else { + if (value instanceof NativeJavaObject) { + value = ((NativeJavaObject) value).unwrap(); + } + + if ((value == null) || (value == Undefined.instance)) { + node.unset(name); + } else if (value instanceof Scriptable) { + Scriptable s = (Scriptable) value; + + if ("Date".equals(s.getClassName())) { + node.setDate(name, new Date((long) ScriptRuntime.toNumber(s))); + } else { + node.setString(name, s.toString()); + } + } else if (value instanceof String) { + node.setString(name, value.toString()); + } else if (value instanceof Boolean) { + node.setBoolean(name, ((Boolean) value).booleanValue()); + } else if (value instanceof Number) { + node.setFloat(name, ((Number) value).doubleValue()); + } else if (value instanceof Date) { + node.setDate(name, (Date) value); + } else if (value instanceof INode) { + node.setNode(name, (INode) value); + } else if (value instanceof HopObject) { + node.setNode(name, ((HopObject) value).node); + } else { + node.setJavaObject(name, value); + } + } + } + + /** + * + * + * @param name ... + * @param start ... + * + * @return ... + */ + public boolean has(String name, Scriptable start) { + if (super.has(name, start)) { + return true; + } + + if ((prototype != null) && prototype.has(name, start)) { + return true; + } + + if ((node != null) && (node.get(name) != null)) { + return true; + } + + return false; + } + + /** + * + * + * @param name ... + */ + public void delete(String name) { + if ((node != null)) { + node.unset(name); + } + } + + /** + * + * + * @param name ... + * @param start ... + * + * @return ... + */ + public Object get(String name, Scriptable start) { + // System.err.println ("GET from "+node+": "+name); + Object retval = super.get(name, start); + + if (retval != ScriptableObject.NOT_FOUND) { + return retval; + } + + if (prototype != null) { + retval = prototype.get(name, start); + } + + if (retval != ScriptableObject.NOT_FOUND) { + return retval; + } + + if (node != null) { + // Everything starting with an underscore is interpreted as internal property + if (name.startsWith("_")) { + return getInternalProperty(name); + } + + IProperty p = node.get(name); + + if (p != null) { + if (p.getType() == IProperty.STRING) { + return p.getStringValue(); + } + + if (p.getType() == IProperty.BOOLEAN) { + return p.getBooleanValue() ? Boolean.TRUE : Boolean.FALSE; + } + + if (p.getType() == IProperty.INTEGER) { + return new Long(p.getIntegerValue()); + } + + if (p.getType() == IProperty.FLOAT) { + return new Double(p.getFloatValue()); + } + + Context cx = Context.getCurrentContext(); + + if (p.getType() == IProperty.DATE) { + Date d = p.getDateValue(); + + if (d == null) { + return NOT_FOUND; + } else { + Object[] args = { new Long(d.getTime()) }; + try { + return cx.newObject(core.global, "Date", args); + } catch (PropertyException px) { + return NOT_FOUND; + } catch (NotAFunctionException nafx) { + return NOT_FOUND; + } catch (JavaScriptException nafx) { + return NOT_FOUND; + } + } + } + + if (p.getType() == IProperty.NODE) { + INode n = p.getNodeValue(); + + if (n == null) { + return NOT_FOUND; + } else { + return cx.toObject(n, core.global); + } + } + + if (p.getType() == IProperty.JAVAOBJECT) { + Object obj = p.getJavaObjectValue(); + + if (obj == null) { + return NOT_FOUND; + } else { + return cx.toObject(obj, core.global); + } + } + } + + // as last resort, try to get property as anonymous subnode + Object anon = node.getChildElement(name); + + if (anon != null) { + return anon; + } + } + + return NOT_FOUND; + } + + private Object getInternalProperty(String name) { + if ("__id__".equals(name) || "_id".equals(name)) { + return node.getID(); + } + + if ("__prototype__".equals(name) || "_prototype".equals(name)) { + return node.getPrototype(); + } + + if ("__parent__".equals(name) || "_parent".equals(name)) { + return core.getNodeWrapper(node.getParent()); + } + + // some more internal properties + if ("__name__".equals(name)) { + return node.getName(); + } + + if ("__fullname__".equals(name)) { + return node.getFullName(); + } + + if ("__hash__".equals(name)) { + return Integer.toString(node.hashCode()); + } + + if ("__node__".equals(name)) { + return node; + } + + if ("__created__".equalsIgnoreCase(name)) { + return new Date(node.created()); + } + + if ("__lastmodified__".equalsIgnoreCase(name)) { + return new Date(node.lastModified()); + } + + return NOT_FOUND; + } + + /** + * + * + * @return ... + */ + public Object[] getIds() { + if (node == null) { + return new Object[0]; + } + + Enumeration enum = node.properties(); + ArrayList list = new ArrayList(); + + while (enum.hasMoreElements()) + list.add(enum.nextElement()); + + return list.toArray(); + } + + /** + * + * + * @param name ... + * @param start ... + * + * @return ... + */ + public boolean has(int idx, Scriptable start) { + if (node != null) { + return (0 <= idx && idx < node.numberOfNodes()); + } + + return false; + } + + /** + * + * + * @param name ... + * @param start ... + * + * @return ... + */ + public Object get(int idx, Scriptable start) { + if (node != null) { + INode n = node.getSubnodeAt(idx); + + if (n == null) { + return NOT_FOUND; + } else { + Context cx = Context.getCurrentContext(); + return cx.toObject(n, core.global); + } + } + + return NOT_FOUND; + } + + + /** + * + * + * @param hint ... + * + * @return ... + */ + public Object getDefaultValue(Class hint) { + return (className != null) ? ("[HopObject " + className + "]") : "[HopObject]"; + } + + /** + * + * + * @return ... + */ + public String toString() { + return (className != null) ? ("[HopObject " + className + "]") : "[HopObject]"; + } +} diff --git a/src/helma/scripting/rhino/JavaObject.java b/src/helma/scripting/rhino/JavaObject.java new file mode 100644 index 00000000..0615cf6e --- /dev/null +++ b/src/helma/scripting/rhino/JavaObject.java @@ -0,0 +1,176 @@ +/* + * 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 helma.framework.*; +import helma.framework.core.*; +import helma.objectmodel.*; +import helma.objectmodel.db.*; +import org.mozilla.javascript.*; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +/** + * + */ +public class JavaObject extends NativeJavaObject { + + RhinoCore core; + Scriptable prototype; + + /** + * Creates a new JavaObject wrapper. + */ + public JavaObject(Scriptable scope, Object obj, Scriptable prototype) { + this.parent = scope; + this.javaObject = obj; + this.prototype = prototype; + staticType = obj.getClass(); + initMembers(); + } + + + /** + * + * + * @param skin ... + * @param param ... + * + * @return ... + */ + public boolean jsFunction_renderSkin(Object skin, Object param) { + // System.err.println ("RENDERSKIN CALLED WITH PARAM "+param); + Context cx = Context.getCurrentContext(); + RequestEvaluator reval = (RequestEvaluator) cx.getThreadLocal("reval"); + Skin s; + + if (skin instanceof Skin) { + s = (Skin) skin; + } else { + s = core.app.getSkin(javaObject, skin.toString(), null); + } + + Map p = null; + + if ((param != null) && (param != Undefined.instance)) { + p = new HashMap(); + + if (param instanceof Scriptable) { + Scriptable sp = (Scriptable) param; + Object[] ids = sp.getIds(); + + for (int i = 0; i < ids.length; i++) + p.put(ids[i], sp.get(ids[i].toString(), sp)); + } + } + + if (s != null) { + s.render(reval, javaObject, p); + } + + return true; + } + + /** + * + * + * @param skin ... + * @param param ... + * + * @return ... + */ + public String jsFunction_renderSkinAsString(Object skin, Object param) { + // System.err.println ("RENDERSKIN CALLED WITH PARAM "+param); + Context cx = Context.getCurrentContext(); + RequestEvaluator reval = (RequestEvaluator) cx.getThreadLocal("reval"); + Skin s; + + if (skin instanceof Skin) { + s = (Skin) skin; + } else { + s = core.app.getSkin(javaObject, skin.toString(), null); + } + + Map p = null; + + if ((param != null) && (param != Undefined.instance)) { + p = new HashMap(); + + if (param instanceof Scriptable) { + Scriptable sp = (Scriptable) param; + Object[] ids = sp.getIds(); + + for (int i = 0; i < ids.length; i++) + p.put(ids[i], sp.get(ids[i].toString(), sp)); + } + } + + if (s != null) { + reval.res.pushStringBuffer(); + s.render(reval, javaObject, p); + + return reval.res.popStringBuffer(); + } + + return ""; + } + + /** + * + * + * @param action ... + * + * @return ... + */ + public Object jsFunction_href(Object action) { + if (javaObject == null) { + return null; + } + + String act = null; + + if (action != null) { + if (action instanceof NativeJavaObject) { + act = ((NativeJavaObject) action).unwrap().toString(); + } else if (!(action instanceof Undefined)) { + act = action.toString(); + } + } + + return core.app.getNodeHref(javaObject, act); + } + + public boolean has(String name, Scriptable start) { + System.err.println ("HAS: "+name); + if (prototype.has(name, start)) + return true; + return super.has(name, start); + } + + public Object get(String name, Scriptable start) { + System.err.println ("GET: "+name); + Object obj = prototype.get(name, start); + if (obj != null && obj != Undefined.instance) + return obj; + return super.get(name, start); + } + +} diff --git a/src/helma/scripting/rhino/MapWrapper.java b/src/helma/scripting/rhino/MapWrapper.java new file mode 100644 index 00000000..23ad267c --- /dev/null +++ b/src/helma/scripting/rhino/MapWrapper.java @@ -0,0 +1,139 @@ +/* + * 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.Context; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.ScriptableObject; +import org.mozilla.javascript.NativeJavaObject; +import java.util.HashMap; +import java.util.Map; + +/** + * + */ +public class MapWrapper extends ScriptableObject { + Map map; + RhinoCore core; + + /** + * Creates a new MapWrapper object. + */ + public MapWrapper() { + map = null; + } + + /** + * Creates a new MapWrapper object. + * + * @param map ... + * @param core ... + */ + public MapWrapper(Map map, RhinoCore core) { + this.map = map; + this.core = core; + } + + /** + * + * + * @param name ... + * @param start ... + * @param value ... + */ + public void put(String name, Scriptable start, Object value) { + if (map == null) { + map = new HashMap(); + } + + if (value instanceof NativeJavaObject) { + map.put(name, ((NativeJavaObject) value).unwrap()); + } else { + map.put(name, value); + } + } + + /** + * + * + * @param name ... + * @param start ... + * + * @return ... + */ + public Object get(String name, Scriptable start) { + if (map == null) { + return null; + } + + Object obj = map.get(name); + + if (obj != null) { + Context cx = Context.getCurrentContext(); + + return cx.toObject(obj, core.global); + } + + return null; + } + + /** + * + * + * @param name ... + * @param start ... + * + * @return ... + */ + public boolean has(String name, Scriptable start) { + return (map != null) && map.containsKey(name); + } + + /** + * + * + * @param name ... + */ + public void delete(String name) { + if (map != null) { + map.remove(name); + } + } + + + /** + * + * + * @return ... + */ + public Object[] getIds() { + if (map == null) { + return new Object[0]; + } + + return map.keySet().toArray(); + } + + /** + * + * + * @return ... + */ + public String getClassName() { + return "[MapWrapper]"; + } +} diff --git a/src/helma/scripting/rhino/RhinoActionAdapter.java b/src/helma/scripting/rhino/RhinoActionAdapter.java new file mode 100644 index 00000000..7ccd0f5f --- /dev/null +++ b/src/helma/scripting/rhino/RhinoActionAdapter.java @@ -0,0 +1,84 @@ +/* + * 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 helma.scripting.*; + +// import java.util.Vector; +// import java.util.Iterator; +import java.io.*; + +// import helma.framework.*; +// import helma.framework.core.*; +// import helma.util.Updatable; + +/** + * An class that updates fesi interpreters with actionfiles and templates. + */ +public class RhinoActionAdapter { + String sourceName; + String function; + String functionAsString; + + /** + * Creates a new RhinoActionAdapter object. + * + * @param action ... + */ + public RhinoActionAdapter(ActionFile action) { + String content = action.getContent(); + String functionName = action.getFunctionName(); + + sourceName = action.toString(); + function = composeFunction(functionName, + "arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10", + content); + + // check if this is a template and we need to generate an "_as_string" variant + if (action instanceof Template) { + functionAsString = composeFunction(functionName + "_as_string", + "arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10", + "res.pushStringBuffer(); " + content + + "\r\nreturn res.popStringBuffer();\r\n"); + } else { + functionAsString = null; + } + } + + protected String composeFunction(String funcname, String params, String body) { + if ((body == null) || "".equals(body.trim())) { + body = ";\r\n"; + } else { + body = body + "\r\n"; + } + + if (params == null) { + params = ""; + } + + StringBuffer f = new StringBuffer("function "); + + f.append(funcname); + f.append(" ("); + f.append(params); + f.append(") {\n"); + f.append(body); + f.append("\n}"); + + return f.toString(); + } +} diff --git a/src/helma/scripting/rhino/RhinoCore.java b/src/helma/scripting/rhino/RhinoCore.java new file mode 100644 index 00000000..e50e35f4 --- /dev/null +++ b/src/helma/scripting/rhino/RhinoCore.java @@ -0,0 +1,686 @@ +/* + * 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 helma.framework.*; +import helma.framework.core.*; +import helma.main.Server; +import helma.objectmodel.*; +import helma.objectmodel.db.DbMapping; +import helma.objectmodel.db.Relation; +import helma.scripting.*; +import helma.util.CacheMap; +import helma.util.Updatable; +import org.mozilla.javascript.*; +import java.io.*; +import java.util.*; + +/** + * This is the implementation of ScriptingEnvironment for the Mozilla Rhino EcmaScript interpreter. + */ +public final class RhinoCore implements WrapHandler { + // the application we're running in + public final Application app; + + // the global object + Scriptable global; + + // caching table for JavaScript object wrappers + CacheMap wrappercache; + + // table containing JavaScript prototypes + Hashtable prototypes; + long lastUpdate = 0; + + /** + * Create a Rhino evaluator for the given application and request evaluator. + */ + public RhinoCore(Application app) { + this.app = app; + wrappercache = new CacheMap(500, .75f); + prototypes = new Hashtable(); + + Context context = Context.enter(); + + context.setCompileFunctionsWithDynamicScope(true); + context.setWrapHandler(this); + + int optLevel = 0; + + try { + optLevel = Integer.parseInt(app.getProperty("rhino.optlevel")); + } catch (Exception ignore) { + } + + // System.err.println("Setting Rhino optlevel to " + optLevel); + context.setOptimizationLevel(optLevel); + + try { + GlobalObject g = new GlobalObject(this, app); + + global = context.initStandardObjects(g); + ScriptableObject.defineClass(global, HopObject.class); + putPrototype("hopobject", + ScriptableObject.getClassPrototype(global, "HopObject")); + putPrototype("global", global); + initialize(); + } catch (Exception e) { + System.err.println("Cannot initialize interpreter"); + System.err.println("Error: " + e); + e.printStackTrace(); + throw new RuntimeException(e.getMessage()); + } finally { + context.exit(); + } + } + + /** + * Initialize the evaluator, making sure the minimum type information + * necessary to bootstrap the rest is parsed. + */ + private synchronized void initialize() { + Collection protos = app.getPrototypes(); + + for (Iterator i = protos.iterator(); i.hasNext();) { + Prototype proto = (Prototype) i.next(); + + initPrototype(proto); + } + + // always fully initialize global prototype, because + // we always need it and there's no chance to trigger + // creation on demand. + getPrototype("global"); + } + + /** + * Initialize a prototype without fully parsing its script files. + */ + synchronized void initPrototype(Prototype prototype) { + // System.err.println ("FESI INIT PROTO "+prototype); + Scriptable op = null; + String name = prototype.getName(); + + // get the prototype's prototype if possible and necessary + Scriptable opp = null; + Prototype parent = prototype.getParentPrototype(); + + if (parent != null) { + // see if parent prototype is already registered. if not, register it + opp = getRawPrototype(parent.getName()); + + if (opp == null) { + initPrototype(parent); + opp = getRawPrototype(parent.getName()); + } + } + + if (!"global".equalsIgnoreCase(name) && !"hopobject".equalsIgnoreCase(name) && + (opp == null)) { + if (app.isJavaPrototype(name)) { + opp = getRawPrototype("__javaobject__"); + } else { + opp = getRawPrototype("hopobject"); + } + } + + // if prototype doesn't exist (i.e. is a standard prototype built by HopExtension), create it. + op = getRawPrototype(name); + + if (op == null) { + try { + Context context = Context.getCurrentContext(); + + op = new HopObject(name); // context.newObject (global /*, "HopObject" */); + op.setPrototype(opp); + op.setParentScope(global); + op.put("prototypename", op, name); + } catch (Exception ignore) { + System.err.println("Error creating prototype: " + ignore); + ignore.printStackTrace(); + } + + putPrototype(name, op); + } else { + // set parent prototype just in case it has been changed + op.setPrototype(opp); + } + + // Register a constructor for all types except global. + // This will first create a new prototyped hopobject and then calls + // the actual (scripted) constructor on it. + if (!"global".equalsIgnoreCase(name) && !"root".equalsIgnoreCase(name)) { + try { + FunctionObject fp = new FunctionObject(name, HopObject.hopObjCtor, global); + + fp.addAsConstructor(global, op); + } catch (Exception ignore) { + System.err.println("Error adding ctor for " + name + ": " + ignore); + ignore.printStackTrace(); + } + } + } + + /** + * Set up a prototype, parsing and compiling all its script files. + */ + synchronized void evaluatePrototype(Prototype prototype) { + // System.err.println ("FESI EVALUATE PROTO "+prototype+" FOR "+this); + Scriptable op = null; + + // get the prototype's prototype if possible and necessary + Scriptable opp = null; + Prototype parent = prototype.getParentPrototype(); + + if (parent != null) { + // see if parent prototype is already registered. if not, register it + opp = getPrototype(parent.getName()); + + if (opp == null) { + evaluatePrototype(parent); + opp = getPrototype(parent.getName()); + } + } + + String name = prototype.getName(); + + if (!"global".equalsIgnoreCase(name) && !"hopobject".equalsIgnoreCase(name) && + (opp == null)) { + if (app.isJavaPrototype(name)) { + opp = getPrototype("__javaobject__"); + } else { + opp = getPrototype("hopobject"); + } + } + + // if prototype doesn't exist (i.e. is a standard prototype built by HopExtension), create it. + op = getPrototype(name); + + if (op == null) { + try { + Context context = Context.getCurrentContext(); + + op = new HopObject(name); // context.newObject (global /*, "HopObject" */); + op.setPrototype(opp); + op.setParentScope(global); + op.put("prototypename", op, name); + } catch (Exception ignore) { + System.err.println("Error creating prototype: " + ignore); + ignore.printStackTrace(); + } + + putPrototype(name, op); + } else { + // reset prototype to original state + resetPrototype(op); + + // set parent prototype just in case it has been changed + op.setPrototype(opp); + } + + // Register a constructor for all types except global. + // This will first create a new prototyped hopobject and then calls + // the actual (scripted) constructor on it. + if (!"global".equalsIgnoreCase(name) && !"root".equalsIgnoreCase(name)) { + try { + FunctionObject fp = new FunctionObject(name, HopObject.hopObjCtor, global); + + fp.addAsConstructor(global, op); + } catch (Exception ignore) { + System.err.println("Error adding ctor for " + name + ": " + ignore); + ignore.printStackTrace(); + } + } + + for (Iterator it = prototype.getZippedCode().values().iterator(); it.hasNext();) { + Object code = it.next(); + + evaluate(prototype, code); + } + + for (Iterator it = prototype.getCode().values().iterator(); it.hasNext();) { + Object code = it.next(); + + evaluate(prototype, code); + } + } + + /** + * Return an object prototype to its initial state, removing all application specific + * functions. + */ + synchronized void resetPrototype(Scriptable op) { + Object[] ids = op.getIds(); + + for (int i = 0; i < ids.length; i++) { + /* String prop = en.nextElement ().toString (); + try { + ESValue esv = op.getProperty (prop, prop.hashCode ()); + if (esv instanceof ConstructedFunctionObject || esv instanceof FesiActionAdapter.ThrowException) + op.deleteProperty (prop, prop.hashCode()); + } catch (Exception x) {} */ + } + } + + /** + * This method is called before an execution context is entered to let the + * engine know it should update its prototype information. + */ + public synchronized void updatePrototypes() { + if ((System.currentTimeMillis() - lastUpdate) < 1000L) { + return; + } + + Collection protos = app.getPrototypes(); + + for (Iterator i = protos.iterator(); i.hasNext();) { + Prototype proto = (Prototype) i.next(); + TypeInfo info = (TypeInfo) prototypes.get(proto.getName()); + + if (info == null) { + // a prototype we don't know anything about yet. Init local update info. + initPrototype(proto); + info = (TypeInfo) prototypes.get(proto.getName()); + } + + // only update prototype if it has already been initialized. + // otherwise, this will be done on demand + // System.err.println ("CHECKING PROTO "+proto+": "+info); + if (info.lastUpdate > 0) { + Prototype p = app.typemgr.getPrototype(info.protoName); + + if (p != null) { + // System.err.println ("UPDATING PROTO: "+p); + app.typemgr.updatePrototype(p); + + if (p.getLastUpdate() > info.lastUpdate) { + evaluatePrototype(p); + info.lastUpdate = p.getLastUpdate(); + } + } + } + } + + lastUpdate = System.currentTimeMillis(); + } + + /** + * Check if an object has a function property (public method if it + * is a java object) with that name. + */ + public boolean hasFunction(String protoname, String fname) { + // System.err.println ("HAS_FUNC: "+fname); + try { + Scriptable op = getPrototype(protoname); + + // if this is an untyped object return false + if (op == null) { + return false; + } + + Object func = ScriptableObject.getProperty(op, fname); + + if ((func != null) && func instanceof Function) { + return true; + } + } catch (Exception esx) { + // System.err.println ("Error in hasFunction: "+esx); + return false; + } + + return false; + } + + /** + * Convert an input argument from Java to the scripting runtime + * representation. + */ + + /* public static ESValue processXmlRpcArgument (Object what, Evaluator evaluator) throws Exception { + if (what == null) + return ESNull.theNull; + if (what instanceof Vector) { + Vector v = (Vector) what; + ArrayPrototype retval = new ArrayPrototype (evaluator.getArrayPrototype (), evaluator); + int l = v.size (); + for (int i=0; i info.lastUpdate) { + info.lastUpdate = p.getLastUpdate(); + evaluatePrototype(p); + } + + // set info.lastUpdate to 1 if it is 0 so we know we + // have initialized this prototype already, even if + // it is empty (i.e. doesn't contain any scripts/skins/actoins + if (info.lastUpdate == 0) { + info.lastUpdate = 1; + } + } + } + + return (info == null) ? null : info.objectPrototype; + } + + /** + * Register an object prototype for a certain prototype name. + */ + public void putPrototype(String protoName, Scriptable op) { + if ((protoName != null) && (op != null)) { + prototypes.put(protoName, new TypeInfo(op, protoName)); + } + } + + /** + * + * + * @param scope ... + * @param obj ... + * @param staticType ... + * + * @return ... + */ + public Object wrap(Scriptable scope, Object obj, Class staticType) { + if (obj instanceof INode) { + return getNodeWrapper((INode) obj); + } + + if (obj instanceof IPathElement) { + return getElementWrapper(obj); + } + + if (obj instanceof Map) { + return new MapWrapper((Map) obj, this); + } + + return null; + } + + /** + * Get a Script wrapper for an object. In contrast to getElementWrapper, this is called for + * any Java object, not just the ones in the request path which we know are scripted. + * So what we do is check if the object belongs to a scripted class. If so, we call getElementWrapper() + * with the object, otherwise we return a generic unscripted object wrapper. + */ + /* public Scriptable getObjectWrapper(Object e) { + if (app.getPrototypeName(e) != null) { + return getElementWrapper(e); + } + / else if (e instanceof INode) + return new ESNode ((INode) e, this); / + else { + return Context.getCurrentContext().toObject(e, global); + } + } */ + + /** + * Get a Script wrapper for any given object. If the object implements the IPathElement + * interface, the getPrototype method will be used to retrieve the name of the prototype + * to use. Otherwise, a Java-Class-to-Script-Prototype mapping is consulted. + */ + public Scriptable getElementWrapper(Object e) { + // Gotta find out the prototype name to use for this object... + String prototypeName = app.getPrototypeName(e); + + Scriptable op = getPrototype(prototypeName); + + if (op == null) { + op = getPrototype("hopobject"); + } + + return new JavaObject(global, e, op); + } + + /** + * Get a script wrapper for an instance of helma.objectmodel.INode + */ + public Scriptable getNodeWrapper(INode n) { + // FIXME: should this return ESNull.theNull? + if (n == null) { + return null; + } + + HopObject esn = (HopObject) wrappercache.get(n); + + if (esn == null) { + try { + String protoname = n.getPrototype(); + + Scriptable op = null; + + // set the DbMapping of the node according to its prototype. + // this *should* be done on the objectmodel level, but isn't currently + // for embedded nodes since there's not enough type info at the objectmodel level + // for those nodes. + if ((protoname != null) && (protoname.length() > 0) && + (n.getDbMapping() == null)) { + n.setDbMapping(app.getDbMapping(protoname)); + } + + op = getPrototype(protoname); + + // no prototype found for this node? + if (op == null) { + op = getPrototype("hopobject"); + } + + esn = new HopObject(); + esn.init(this, n); + esn.setPrototype(op); + + wrappercache.put(n, esn); + + // app.logEvent ("Wrapper for "+n+" created"); + } catch (Exception x) { + System.err.println("Error creating node wrapper: " + x); + throw new RuntimeException(x.toString()); + } + } + + return esn; + } + + /** + * Register a new Node wrapper with the wrapper cache. This is used by the + * Node constructor. + */ + public void putNodeWrapper(INode n, Scriptable esn) { + wrappercache.put(n, esn); + } + + private synchronized void evaluate(Prototype prototype, Object code) { + if (code instanceof FunctionFile) { + FunctionFile funcfile = (FunctionFile) code; + File file = funcfile.getFile(); + + if (file != null) { + try { + FileReader fr = new FileReader(file); + + updateEvaluator(prototype, fr, funcfile.getSourceName()); + } catch (IOException iox) { + app.logEvent("Error updating function file: " + iox); + } + } else { + StringReader reader = new StringReader(funcfile.getContent()); + + updateEvaluator(prototype, reader, funcfile.getSourceName()); + } + } else if (code instanceof ActionFile) { + ActionFile action = (ActionFile) code; + RhinoActionAdapter fa = new RhinoActionAdapter(action); + + try { + updateEvaluator(prototype, new StringReader(fa.function), + action.getSourceName()); + } catch (Exception esx) { + app.logEvent("Error parsing " + action + ": " + esx); + } + } + } + + private synchronized void updateEvaluator(Prototype prototype, Reader reader, + String sourceName) { + // context = Context.enter(context); + try { + Scriptable op = getPrototype(prototype.getName()); + + // get the current context + Context cx = Context.getCurrentContext(); + + // do the update, evaluating the file + cx.evaluateReader(op, reader, sourceName, 1, null); + } catch (Throwable e) { + app.logEvent("Error parsing function file " + sourceName + ": " + e); + e.printStackTrace(); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException ignore) { + } + } + } + } + + class TypeInfo { + Scriptable objectPrototype; + long lastUpdate = 0; + String protoName; + + public TypeInfo(Scriptable op, String name) { + objectPrototype = op; + protoName = name; + } + + public String toString() { + return ("TypeInfo[" + protoName + "," + new Date(lastUpdate) + "]"); + } + } +} diff --git a/src/helma/scripting/rhino/RhinoEngine.java b/src/helma/scripting/rhino/RhinoEngine.java new file mode 100644 index 00000000..85edc869 --- /dev/null +++ b/src/helma/scripting/rhino/RhinoEngine.java @@ -0,0 +1,442 @@ +/* + * 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 helma.extensions.ConfigurationException; +import helma.extensions.HelmaExtension; +import helma.framework.*; +import helma.framework.core.*; +import helma.main.Server; +import helma.objectmodel.*; +import helma.objectmodel.db.DbMapping; +import helma.objectmodel.db.Relation; +import helma.scripting.*; +import helma.scripting.fesi.extensions.*; +import helma.util.CacheMap; +import helma.util.Updatable; +import org.mozilla.javascript.*; +import java.io.*; +import java.util.*; + +/** + * This is the implementation of ScriptingEnvironment for the Mozilla Rhino EcmaScript interpreter. + */ +public final class RhinoEngine implements ScriptingEngine { + static Map coreMap; + + // the application we're running in + public Application app; + + // The Rhino context + Context context; + + // the global object + Scriptable global; + + // the request evaluator instance owning this fesi evaluator + RequestEvaluator reval; + RhinoCore core; + + // remember global variables from last invokation to be able to + // do lazy cleanup + Map lastGlobals = null; + + // the global vars set by extensions + HashMap extensionGlobals; + + /** + * Zero argument constructor. + */ + public RhinoEngine() { + } + + /** + * Init the scripting engine with an application and a request evaluator + */ + public void init(Application app, RequestEvaluator reval) { + this.app = app; + this.reval = reval; + core = getRhinoCore(app); + context = Context.enter(); + context.setCompileFunctionsWithDynamicScope(true); + + try { + global = context.newObject(core.global); + global.setPrototype(core.global); + global.setParentScope(null); + + // context.putThreadLocal ("reval", reval); + // context.putThreadLocal ("engine", this); + extensionGlobals = new HashMap(); + + Vector extVec = Server.getServer().getExtensions(); + + for (int i = 0; i < extVec.size(); i++) { + HelmaExtension ext = (HelmaExtension) extVec.get(i); + + try { + HashMap tmpGlobals = ext.initScripting(app, this); + + if (tmpGlobals != null) { + extensionGlobals.putAll(tmpGlobals); + } + } catch (ConfigurationException e) { + app.logEvent("Couldn't initialize extension " + ext.getName() + ": " + + e.getMessage()); + } + } + + // context.removeThreadLocal ("reval"); + // context.removeThreadLocal ("engine"); + } catch (Exception e) { + System.err.println("Cannot initialize interpreter"); + System.err.println("Error: " + e); + e.printStackTrace(); + throw new RuntimeException(e.getMessage()); + } finally { + // Context.exit (); + } + } + + static synchronized RhinoCore getRhinoCore(Application app) { + RhinoCore core = null; + + if (coreMap == null) { + coreMap = new HashMap(); + } else { + core = (RhinoCore) coreMap.get(app); + } + + if (core == null) { + core = new RhinoCore(app); + coreMap.put(app, core); + } + + return core; + } + + /** + * This method is called before an execution context is entered to let the + * engine know it should update its prototype information. + */ + public void updatePrototypes() { + context = Context.enter(context); + context.setCompileFunctionsWithDynamicScope(true); + context.setWrapHandler(core); + + int optLevel = 0; + + try { + optLevel = Integer.parseInt(app.getProperty("rhino.optlevel")); + } catch (Exception ignore) { + } + + context.setOptimizationLevel(optLevel); + core.updatePrototypes(); + context.putThreadLocal("reval", reval); + context.putThreadLocal("engine", this); + } + + /** + * This method is called when an execution context for a request + * evaluation is entered. The globals parameter contains the global values + * to be applied during this execution context. + */ + public void enterContext(HashMap globals) throws ScriptingException { + // set the thread filed in the FESI evaluator + // evaluator.thread = Thread.currentThread (); + // set globals on the global object + // context = Context.enter (context); + globals.putAll(extensionGlobals); + + if ((globals != null) && (globals != lastGlobals)) { + // loop through global vars and set them + for (Iterator i = globals.keySet().iterator(); i.hasNext();) { + String k = (String) i.next(); + Object v = globals.get(k); + Scriptable scriptable = null; + + try { + // we do a lot of extra work to make access to global variables + // comfortable to EcmaScript coders, i.e. we use a lot of custom wrappers + // that expose properties and functions in a special way instead of just going + // with the standard java object wrappers. + if ("path".equals(k)) { + Scriptable arr = context.newObject(global, "Array"); + List path = (List) v; + + // register path elements with their prototype + for (int j = 0; j < path.size(); j++) { + Object pathElem = path.get(j); + Scriptable wrappedElement = context.toObject(pathElem, global); + + arr.put(j, arr, wrappedElement); + + String protoname = app.getPrototypeName(pathElem); + + if (protoname != null) { + arr.put(protoname, arr, wrappedElement); + } + } + + v = arr; + } + + /* if (v instanceof Map) { + sv = new ESMapWrapper (this, (Map) v); + } else if ("path".equals (k)) { + ArrayPrototype parr = new ArrayPrototype (evaluator.getArrayPrototype(), evaluator); + List path = (List) v; + // register path elements with their prototype + for (int j=0; j 1 && arguments[1] instanceof ESNumber) + // writer.setMaxLevels(arguments[1].toInt32()); + writer.setDatabaseMode(false); + + boolean result = writer.write(node); + + writer.flush(); + + return out.toString("UTF-8"); + } + + /** + * + * + * @param file ... + * + * @return ... + * + * @throws RuntimeException ... + */ + public Object read(String file) throws RuntimeException { + return read(file, null); + } + + /** + * + * + * @param file ... + * @param hopObject ... + * + * @return ... + * + * @throws RuntimeException ... + */ + public Object read(String file, Object hopObject) throws RuntimeException { + if (file == null) { + throw new RuntimeException("Missing arguments in Xml.read()"); + } + + INode node = null; + + if (hopObject instanceof HopObject) { + node = ((HopObject) hopObject).node; + } + + if (node == null) { + // make sure we have a node, even if 2nd arg doesn't exist or is not a node + node = new Node((String) null, (String) null, + core.getApplication().getWrappedNodeManager()); + } + + try { + XmlReader reader = new XmlReader(); + INode result = reader.read(new File(file), node); + + return core.getNodeWrapper(result); + } catch (NoClassDefFoundError e) { + throw new RuntimeException("Can't load XML parser:" + e); + } catch (Exception f) { + throw new RuntimeException(f.toString()); + } + } + + /** + * + * + * @param str ... + * + * @return ... + * + * @throws RuntimeException ... + */ + public Object readFromString(String str) throws RuntimeException { + return readFromString(str, null); + } + + /** + * + * + * @param str ... + * @param hopObject ... + * + * @return ... + * + * @throws RuntimeException ... + */ + public Object readFromString(String str, Object hopObject) + throws RuntimeException { + if (str == null) { + throw new RuntimeException("Missing arguments in Xml.read()"); + } + + INode node = null; + + if (hopObject instanceof HopObject) { + node = ((HopObject) hopObject).node; + } + + if (node == null) { + // make sure we have a node, even if 2nd arg doesn't exist or is not a node + node = new Node((String) null, (String) null, + core.getApplication().getWrappedNodeManager()); + } + + try { + XmlReader reader = new XmlReader(); + INode result = reader.read(new StringReader(str), node); + + return core.getNodeWrapper(result); + } catch (NoClassDefFoundError e) { + throw new RuntimeException("Can't load XML parser:" + e); + } catch (Exception f) { + throw new RuntimeException(f.toString()); + } + } + + /* class XmlGet extends BuiltinFunctionObject { + XmlGet(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); + } + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { + if ( arguments==null || arguments.length==0 ) + throw new EcmaScriptException("Xml.get() needs a location as an argument"); + try { + XmlConverter converter; + if ( arguments.length>1 ) { + converter = new XmlConverter (arguments[1].toString()); + } else { + converter = new XmlConverter (); + } + INode node = new helma.objectmodel.db.Node ( (String)null, (String)null, this.evaluator.engine.getApplication().getWrappedNodeManager() ); + INode result = converter.convert (arguments[0].toString(),node); + return this.evaluator.engine.getNodeWrapper(result); + } catch ( NoClassDefFoundError e ) { + throw new EcmaScriptException("Can't load dom-capable xml parser."); + } catch ( RuntimeException f ) { + throw new EcmaScriptException(f.toString()); + } + } + } + class XmlGetFromString extends BuiltinFunctionObject { + XmlGetFromString(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); + } + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { + if ( arguments==null || arguments.length==0 ) + throw new EcmaScriptException("Xml.getFromString() needs an XML string as parameter"); + try { + XmlConverter converter; + if ( arguments.length>1 ) { + converter = new XmlConverter (arguments[1].toString()); + } else { + converter = new XmlConverter (); + } + INode node = new helma.objectmodel.db.Node ( (String)null, (String)null, this.evaluator.engine.getApplication().getWrappedNodeManager() ); + INode result = converter.convertFromString (arguments[0].toString(),node); + return this.evaluator.engine.getNodeWrapper(result); + } catch ( NoClassDefFoundError e ) { + throw new EcmaScriptException("Can't load dom-capable xml parser."); + } catch ( RuntimeException f ) { + throw new EcmaScriptException(f.toString()); + } + } + } */ +}