/*
* JEF - Copyright 2009-2010 Jiyi (mr.jiyi@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jef.jre5support.script;
import java.util.HashMap;
import javax.script.Bindings;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import jef.common.log.LogUtil;
import jef.script.javascript.RhinoScriptEngineFactory;
import jef.tools.ArrayUtils;
import jef.tools.IOUtils;
import jef.tools.StringUtils;
import jef.tools.XMLUtils;
import jef.tools.ZipUtils;
import org.apache.commons.lang.ObjectUtils;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Decompiler;
import org.mozilla.javascript.NativeArray;
import org.mozilla.javascript.NativeFunction;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.UintMap;
import org.mozilla.javascript.Undefined;
import org.mozilla.javascript.UniqueTag;
import org.mozilla.javascript.Wrapper;
public class JavaScriptUtil {
private static RhinoScriptEngineFactory fac = new RhinoScriptEngineFactory();
public synchronized static ScriptEngine newEngine() {
return fac.getScriptEngine();
}
private static final String printSource = "function echo(str) { \n"
+ " if (typeof(str) == 'undefined') { \n"
+ " str = 'undefined'; \n"
+ " } else if (str == null) { \n"
+ " str = 'null'; \n"
+ " } \n"
+ " LogUtil.show(str) \n"
+ "}";
public static void initEngine(ScriptEngine e, Bindings... b) {
importClass(e, LogUtil.class, b);
importClass(e, StringUtils.class, b);
importClass(e, ArrayUtils.class, b);
importClass(e, XMLUtils.class, b);
importClass(e, IOUtils.class, b);
importClass(e, ZipUtils.class, b);
try {
if (b.length > 0) {
e.eval(printSource, b[0]);
} else {
e.eval(printSource);
}
} catch (ScriptException ex) {
LogUtil.exception(ex);
}
}
public static void importPackage(ScriptEngine e, Package pkg, Bindings... b) {
try {
if (b.length == 0) {
e.eval("importPackage(Packages." + pkg.getName() + ")");
} else {
e.eval("importPackage(Packages." + pkg.getName() + ")", b[0]);
}
} catch (ScriptException e1) {
throw new RuntimeException(e1);
}
}
public static void importClass(ScriptEngine e, Class<?> pkg, Bindings... b) {
try {
if (b.length == 0) {
e.eval("importClass(Packages." + pkg.getName() + ")");
} else {
e.eval("importClass(Packages." + pkg.getName() + ")", b[0]);
}
} catch (ScriptException e1) {
throw new RuntimeException(e1);
}
}
/**
* 将Native对象转换为String数组
*
* @Title: toStringArray
* @Description: TODO(这里用一句话描述这个方法的作用)
* @param 参数
* @return String[] 返回类型
* @throws
*/
public static String[] toStringArray(NativeArray nv) {
String[] result = new String[(int) nv.getLength()];
for (int i = 0; i < result.length; i++) {
result[i] = StringUtils.toString(nv.get(i, null));
}
return result;
}
/**
* 将NativeArray返回结果转换为int[]对象
*
* @Title: toIntArray
* @Description: TODO(这里用一句话描述这个方法的作用)
* @param 参数
* @return int[] 返回类型
* @throws
*/
public static int[] toIntArray(NativeArray nv) {
int[] result = new int[(int) nv.getLength()];
for (int i = 0; i < result.length; i++) {
Object obj = nv.get(i, null);
if (obj instanceof Number) {
result[i] = ((Number) obj).intValue();
} else {
result[i] = StringUtils.toInt(StringUtils.toString(obj), 0);
}
}
return result;
}
/**
* 将Rhino中的JavaScript对象转换成相应的Java对象
* <p>
* 如果对象类型是wraped java object,那么 unwrap ; 如果对象类型是 scriptable object, 那么转换为相应的
* java object。
* </p>
*
* @param jsObj
* 要转换为Java对象的返回值
* @param context
* 上下文
* @return return 转换后的Java对象,当产生异常的时候返回null
*/
public static Object jsToJava(Object jsObj) {
return jsToJava(jsObj, Context.enter());
}
private static Object jsToJava(Object jsObj, Context context) {
try {
if (jsObj == null || jsObj == Undefined.instance
|| jsObj == ScriptableObject.NOT_FOUND)
return null;
if (jsObj instanceof Wrapper)
return ((Wrapper) jsObj).unwrap();
// Rhino 内的 JavaScript 对象一般都继承 ScriptableObject
if (jsObj instanceof ScriptableObject) {
final ScriptableObject scriptObj = (ScriptableObject) jsObj;
return scriptableToJava(scriptObj, context);
} else {
// 为Java Object,直接返回
return jsObj;
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
/**
* 对Scriptable对象转换成相应的Java对象
* <p>
* 如果对象类型是数组,按下标数组和关联数组两种情况分别转换为Array和Map; 否则转换为对应的Java对象,或者是一个包含此对象所有属性的Map
* </p>
*
* @param scriptObj
* 需要转换的Scriptable对象
* @param context
* 上下文
* @return return 转换后的Java对象
*/
@SuppressWarnings( { "rawtypes", "unchecked" })
private static Object scriptableToJava(ScriptableObject scriptObj,
Context context) throws IllegalAccessException,
NoSuchFieldException {
// Array & Arguments
if (ScriptRuntime.isArrayObject(scriptObj)) {
final Object[] arrayElements = (Object[]) context
.getElements(scriptObj);
// If scriptObj is a associative arry, arrayElements.length will
// always 0
// So if scriptObj is empty or index array, return true, else return
// false
if (scriptObj.getIds().length == 0
|| arrayElements.length == scriptObj.getIds().length) {
for (int i = 0; i < arrayElements.length; i++) {
arrayElements[i] = jsToJava(arrayElements[i], context);
}
return arrayElements;
} else {
final Object[] ids = scriptObj.getIds();
final HashMap map = new HashMap();
for (int i = 0; i < ids.length; i++) {
final String key = ids[i].toString();
Object value = scriptObj.get(key, scriptObj);
// if value is UniqueTag, means index is numeric,
// should get its value by index
if (value.getClass().equals(UniqueTag.class)) {
value = scriptObj.get(Integer.parseInt(key), scriptObj);
}
map.put(ids[i], jsToJava(value, context));
}
return map;
}
} else {
final String jsClassName = scriptObj.getClassName();
// If jsClassName is 'Object', means scriptObj could't directly
// convert to a normal java object, in this case we
// return a map contains all properties in this scriptable object.
if ("Object".equals(jsClassName)) {
final Object[] ids = scriptObj.getIds();
final HashMap map = new HashMap();
for (int i = 0; i < ids.length; i++) {
final String key = ids[i].toString();
final Object value = scriptObj.get(key, scriptObj);
map.put(key, jsToJava(value, context));
}
return map;
}
// If jsClassName is 'Funtion' & instanceof NativeFunction,
// means scriptObj is a function defined in script,
// in this case we return a String present source of this function
else if ("Function".equals(jsClassName)
&& scriptObj instanceof NativeFunction) {
final NativeFunction func = (NativeFunction) scriptObj;
return Decompiler.decompile(func.getEncodedSource(),
Decompiler.TO_SOURCE_FLAG, new UintMap());
}
// Else, we can covert it to a desired java object by
// Context.jsToJava()
else {
final Class clazz = (Class) ScriptRuntime.class
.getDeclaredField(jsClassName + "Class").get(scriptObj);
return Context.jsToJava(scriptObj, clazz);
}
}
}
}