/* * Scriptographer * * This file is part of Scriptographer, a Scripting Plugin for Adobe Illustrator * http://scriptographer.org/ * * Copyright (c) 2002-2010, Juerg Lehni * http://scratchdisk.com/ * * All rights reserved. See LICENSE file for details. * * File created on 06.03.2005. */ package com.scratchdisk.script.rhino; import java.lang.reflect.Method; import java.util.Date; import org.mozilla.javascript.Context; import org.mozilla.javascript.EvaluatorException; import org.mozilla.javascript.Function; import org.mozilla.javascript.ImporterTopLevel; import org.mozilla.javascript.LazilyLoadedCtor; import org.mozilla.javascript.NativeJavaObject; import org.mozilla.javascript.ScriptRuntime; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.Undefined; import org.mozilla.javascript.Wrapper; import com.scratchdisk.script.ScriptEngine; /** * @author lehni */ public class TopLevel extends ImporterTopLevel { public TopLevel() { } public TopLevel(Context context, boolean sealed) { super(context, sealed); } public TopLevel(Context context) { super(context, false); } protected static final String[] topPackages = { "Packages", "com.scratchdisk.script.rhino.ExtendedJavaTopPackage", "java", "com.scratchdisk.script.rhino.ExtendedJavaTopPackage", "javax", "com.scratchdisk.script.rhino.ExtendedJavaTopPackage", "org", "com.scratchdisk.script.rhino.ExtendedJavaTopPackage", "com", "com.scratchdisk.script.rhino.ExtendedJavaTopPackage", "edu", "com.scratchdisk.script.rhino.ExtendedJavaTopPackage", "net", "com.scratchdisk.script.rhino.ExtendedJavaTopPackage", // "getClass", "com.scratchdisk.script.rhino.ExtendedJavaTopPackage" }; public void initStandardObjects(Context cx, boolean sealed) { super.initStandardObjects(cx, sealed); // Override the class loading objects with our own extended classes for (int i = 0; i != topPackages.length; i += 2) new LazilyLoadedCtor(this, topPackages[i], topPackages[i + 1], false); // define some global functions and objects: String[] names = { "print", "evaluate" }; defineFunctionProperties(names, TopLevel.class, ScriptableObject.DONTENUM); ScriptableObject objProto = (ScriptableObject) getObjectPrototype(this); objProto.defineFunctionProperties(new String[] { "dontEnum", "toJava", "print", "evaluate" }, TopLevel.class, DONTENUM); } public static void defineProperty(ScriptableObject obj, String name, String getter, String setter) throws SecurityException, NoSuchMethodException { Class<? extends ScriptableObject> cls = obj.getClass(); Method getterMethod = getter != null ? cls.getDeclaredMethod(getter, new Class[] { Scriptable.class }) : null; Method setterMethod = setter != null ? cls.getDeclaredMethod(setter, new Class[] { Scriptable.class, Object.class }) : null; obj.defineProperty(name, null, getterMethod, setterMethod, ScriptableObject.DONTENUM); } /** * Set DONTENUM attributes on the given properties in this object. * This is set on the JavaScript Object prototype. */ public static Object dontEnum(Context cx, Scriptable thisObj, Object[] args, Function funObj) { // Don't throw error for now if dontEnum cannot do anything on this object. // e.g. if it is a NativeJavaObject. // TODO: This needs to change in the future. // But since dontEnum will go away in favor of something the standard // Object.defineProperty() method described in the EcmaScript 3.1, // this is fine for now. // if (!(thisObj instanceof ScriptableObject)) { // throw new EvaluatorException( // "dontEnum() called on non-ScriptableObject"); // } if (thisObj instanceof ScriptableObject) { ScriptableObject obj = (ScriptableObject) thisObj; for (int i = 0; i < args.length; i++) { if (!(args[i] instanceof String)) { throw new EvaluatorException( "dontEnum() called with non-String argument"); } String str = (String) args[i]; if (obj.has(str, obj)) { int attr = obj.getAttributes(str); if ((attr & PERMANENT) == 0) obj.setAttributes(str, attr | DONTENUM); } } } return null; } /** * Convert an object into a wrapper that exposes the java methods of the * object to JavaScript. This is useful for treating native numbers, * strings, etc as their java counterpart such as java.lang.Double, * java.lang.String etc. * * @param thisObj a java object that is wrapped in a special way Rhino * @return the object wrapped as NativeJavaObject, exposing the public * methods of the underlying class. */ public static Object toJava(Context cx, Scriptable thisObj, Object[] args, Function funObj) { if (thisObj == null || thisObj instanceof NativeJavaObject || thisObj == Undefined.instance) { return thisObj; } Scriptable topLevel = ScriptRuntime.getTopCallScope(cx); Object obj = thisObj; if (thisObj instanceof Wrapper) { obj = ((Wrapper) thisObj).unwrap(); } else { if ("Date".equals(thisObj.getClassName())) { return new NativeJavaObject(topLevel, new Date((long) ScriptRuntime.toNumber(thisObj)), null); } } return new NativeJavaObject(topLevel, obj, null); } public String getClassName() { return "global"; } /** * Print the string values of its arguments. * * This method is defined as a JavaScript function. Note that its arguments * are of the "varargs" form, which allows it to handle an arbitrary number * of arguments supplied to the JavaScript function. * */ public static void print(Context cx, Scriptable thisObj, Object[] args, Function funObj) { for (int i = 0; i < args.length; i++) { if (i > 0) System.out.print(" "); // Convert the arbitrary JavaScript value into a string form. String s = Context.toString(args[i]); System.out.print(s); } System.out.println(); } /** * Evaluates the given JavaScript string in the current scope. Similar to * eval(), but it allows the use of another object than the global scope: * e.g.: * <code> * var obj = { * eval: evaluate * }; * obj.eval("print(this);"); * </code> */ public static void evaluate(Context cx, Scriptable thisObj, Object[] args, Function funObj) { ScriptEngine engine = ScriptEngine.getEngineByName("JavaScript"); engine.evaluate(Context.toString(args[0]), "evaluate", engine.getScope(thisObj)); } }