/*
==============================================================================
This file is part of the MOA Lightweight Web Runner
Copyright 2008 by kRAkEn/gORe's Jucetice Application Development
------------------------------------------------------------------------------
MOA can be redistributed and/or modified under the terms of the
GNU Lesser General Public License, as published by the Free Software Foundation;
version 2 of the License only.
MOA is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with MOA; if not, visit www.gnu.org/licenses or write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
==============================================================================
*/
package org.qrone.r7.script.ext;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import org.mozilla.javascript.Callable;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.NativeArray;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Undefined;
/**
* A collection of Rhino utility methods.
*/
public class ScriptUtils {
//==============================================================================
/**
* Coerce/wrap a java object to a JS object, and mask Lists and Maps
* as native JS objects.
*
* @param obj the object to coerce/wrap
* @param scope the scope
* @return the wrapped/masked java object
*/
public static Object javaToJS(Scriptable scope, Object obj) {
if (obj instanceof List<?>) {
return new ListPrototype(scope, (List<?>) obj);
} else if (obj instanceof Map<?,?>) {
return new MapPrototype(scope, (Map<?, ?>) obj);
} else {
return Context.javaToJS(obj, scope);
}
}
//==============================================================================
/**
* Return a class prototype, or the object prototype if the class
* is not defined.
*
* @param scope the scope
* @param className the class name
* @return the class or object prototype
*/
public static Scriptable getClassOrObjectProto(Scriptable scope, String className) {
Scriptable proto = ScriptableObject.getClassPrototype(scope, className);
if (proto == null) {
proto = ScriptableObject.getObjectPrototype(scope);
}
return proto;
}
//==============================================================================
/**
* Make a scriptable object to be const and readonly in a scope
*
* @param scope the scope
* @param objectName the object name
*/
public static void makeScriptableObjectConst (ScriptableObject scope, String objectName) {
/*
scope.setAttributes (objectName, ScriptableObject.READONLY |
ScriptableObject.CONST |
ScriptableObject.PERMANENT);
*/
}
//==============================================================================
/**
*
* @param na
* @return
*/
public static List<Object> unwrapNativeArray (final NativeArray na) {
return new ArrayList<Object> () {{
for (int i = 0; i < na.getLength(); ++i) {
add(unwrapNative(na.get(i, null)));
}
}};
}
/**
*
* @param na
* @return
*/
public static List<Object> unwrapPrototypeArray (final ScriptableObject sObj) {
return new ArrayList<Object> () {{
final List<Object> sObjIds = Arrays.asList(sObj.getAllIds());
for (int i = 0; sObjIds.contains(i); ++i) {
add(unwrapNative(sObj.get(i, null)));
}
}};
}
/**
*
* @param sObj
* @return
*/
public static Map<String, Object> unwrapObject (final ScriptableObject sObj) {
return new HashMap<String, Object> () {{
for (Object id: sObj.getAllIds()) {
put(id.toString(), unwrapNative(sObj.get(id.toString(), null)));
}
}};
}
/**
*
* @param obj
* @return
*/
protected static Object unwrapNative (final Object obj)
{
if (obj instanceof NativeArray) {
return unwrapNativeArray ((NativeArray) obj);
}
else if (obj instanceof Callable) {
return obj;
}
else if (obj instanceof ScriptableObject) {
final ScriptableObject sObj = (ScriptableObject) obj;
final List<Object> sObjIds = Arrays.asList (sObj.getAllIds());
if (sObjIds.contains("keys")) { // a prototype enumerable/hash
return unwrapObject (sObj);
}
else if (sObjIds.contains("flatten")) { // a prototype enumerable/array
return unwrapPrototypeArray (sObj);
}
else {
return unwrapObject (sObj);
}
}
else
{
return obj;
}
}
//==============================================================================
/**
*
* @param scope
* @param en
* @return
*/
public static Object[] getArray (Scriptable scope, NativeArray array) {
Object[] v = new Object[(int) array.getLength ()];
for (int i = 0; i < v.length; i++)
v [i] = array.get (i, array);
return v;
}
/**
*
* @param scope
* @param v
* @return
*/
public static Scriptable getJsArray (Scriptable scope, Vector<?> v) {
List<Object> list = new ArrayList<Object>();
for (int i = 0; i < v.size (); i++) {
if (v.get (i) != null)
list.add (Context.toObject(v.get (i), scope));
}
Context cx = Context.getCurrentContext();
return cx.newArray (scope, list.toArray());
}
/**
*
* @param scope
* @param en
* @return
*/
public static Scriptable getJsArray (Scriptable scope, Enumeration<?> en) {
List<Object> list = new ArrayList<Object>();
while (en.hasMoreElements()) {
list.add(en.nextElement());
}
Context cx = Context.getCurrentContext();
return cx.newArray (scope, list.toArray());
}
/**
*
* @param scope
* @param arr
* @return
*/
public static Scriptable getJsArray (Scriptable scope, Object[] arr) {
Context cx = Context.getCurrentContext();
if (arr == null)
return cx.newArray(scope, 0);
int length = arr.length;
Scriptable array = cx.newArray(scope, length);
for (int i = 0; i < length; i++) {
if (arr[i] != null)
array.put(i, array, Context.toObject(arr[i], scope));
}
return array;
}
/**
*
* @param scope
* @param arr
* @return
*/
public static Scriptable getWrappedJsArray (Scriptable scope, Object[] arr) {
Context cx = Context.getCurrentContext();
if (arr == null)
return cx.newArray(scope, 0);
int length = arr.length;
Scriptable array = cx.newArray(scope, length);
for (int i = 0; i < length; i++) {
if (arr[i] != null)
array.put(i, array, Context.javaToJS(arr[i], scope));
}
return array;
}
//==============================================================================
/**
*
* @param scope
* @param v
* @return
*/
public static Scriptable getJsObject (Context cx, Scriptable scope, Map<?,?> m)
{
Scriptable object = cx.newObject(scope);
Object[] keys = m.keySet().toArray();
for (int i = 0; i < keys.length; i++)
{
if (m.get (keys[i]) != null)
{
object.put(Context.toString(keys[i]), object, Context.toObject(m.get (keys[i]), scope));
}
}
return object;
}
//==============================================================================
/**
* Initialize and normalize the global variables and arguments on a thread scope.
* @param args the arguments
*/
public static void initArguments (Object[] args, Scriptable scope)
{
if (args != null) {
for (int i = 0; i < args.length; i++)
args[i] = ScriptUtils.wrapArgument (args[i], scope);
}
}
/**
* Prepare a single property or argument value for use within rhino.
*
* @param value the property or argument value
* @param scope the scope
* @return the object wrapped and wired for rhino
*/
public static Object wrapArgument (Object value, Scriptable scope) {
if (value instanceof ScriptableObject) {
ScriptableObject scriptable = ((ScriptableObject) value);
scriptable.setPrototype (ScriptableObject.getClassPrototype (scope, scriptable.getClassName()));
scriptable.setParentScope (scope);
return scriptable;
} else {
return ScriptUtils.javaToJS(scope, value);
}
}
/**
* Make sure that number of arguments is valid.
*
* @param args the argument array
* @param min the minimum number of arguments
* @param max the maximum number of arguments
* @throws IllegalArgumentException if the number of arguments is not valid
*/
public static void checkArguments(Object[] args, int min, int max)
throws IllegalArgumentException {
if (min > -1 && args.length < min)
throw new IllegalArgumentException();
if (max > -1 && args.length > max)
throw new IllegalArgumentException();
}
/**
* Get an argument as ScriptableObject
*
* @param args the argument array
* @param pos the position of the requested argument
* @return the argument as ScriptableObject
* @throws IllegalArgumentException if the argument can't be converted to a map
*/
public static ScriptableObject getScriptableArgument(Object[] args, int pos)
throws IllegalArgumentException {
if (pos >= args.length || args[pos] == null || args[pos] == Undefined.instance)
return null;
if (args[pos] instanceof ScriptableObject)
return (ScriptableObject) args[pos];
throw new IllegalArgumentException("Can't convert to ScriptableObject: " + args[pos]);
}
/**
* Get an argument as string
*
* @param args the argument array
* @param pos the position of the requested argument
* @return the argument as string
*/
public static String getStringArgument(Object[] args, int pos, String defaultValue) {
if (pos >= args.length || args[pos] == null || args[pos] == Undefined.instance)
return defaultValue;
if (!(args[pos] instanceof String))
throw new IllegalArgumentException("Expected String as argument " + (pos + 1));
return (String) args[pos];
}
/**
* Get an argument as integer
*
* @param args the argument array
* @param pos the position of the requested argument
* @return the argument as string
*/
public static int getIntArgument(Object[] args, int pos, int defaultValue) {
if (pos >= args.length || args[pos] == null || args[pos] == Undefined.instance)
return defaultValue;
if (!(args[pos] instanceof Number))
throw new IllegalArgumentException("Expected Number as argument " + (pos + 1));
return ((Number) args[pos]).intValue();
}
/**
* Get an argument as Map
*
* @param args the argument array
* @param pos the position of the requested argument
* @return the argument as map
* @throws IllegalArgumentException if the argument can't be converted to a map
*/
public static Map<?,?> getMapArgument(Object[] args, int pos)
throws IllegalArgumentException {
if (pos >= args.length || args[pos] == null || args[pos] == Undefined.instance)
return null;
if (args[pos] instanceof Map<?,?>)
return (Map<?,?>) args[pos];
throw new IllegalArgumentException("Can't convert to java.util.Map: " + args[pos]);
}
/**
* Get an argument as object
* @param args the argument array
* @param pos the position of the requested argument
* @return the argument as object
*/
public static Object getObjectArgument(Object[] args, int pos, Object defaultValue) {
if (pos >= args.length || args[pos] == null || args[pos] == Undefined.instance)
return defaultValue;
return Context.jsToJava(args[pos], Object.class);
}
/**
* Get an argument as function
* @param args the argument array
* @param pos the position of the requested argument
* @return the argument as object
*/
public static Function getFunctionArgument(Object[] args, int pos, Object defaultValue) {
if (pos >= args.length || args[pos] == null || args[pos] == Undefined.instance)
return null;
if (args[pos] instanceof Function)
return (Function) args[pos];
throw new IllegalArgumentException("Can't convert to Function: " + args[pos]);
}
//==============================================================================
/**
* TODO
*/
/*
public static void dumpScriptable (final String name, final Scriptable src)
{
if (src != null)
{
Object[] ids = src.getIds();
for (int i = 0; i < ids.length; i++)
{
String key = (String) ids[i];
ScriptEngine.getLog().info (name + " (" + src.getClassName() + ") > " + key + " " + src.get (key, src));
}
}
}
*/
//==============================================================================
/**
*
*/
protected static void copyContextDataToScope (String name, Scriptable scope, Context cx)
{
scope.put (name, scope, ScriptUtils.wrapArgument (cx.getThreadLocal (name), scope));
}
}