All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ajaxjs.jsonparser.JsEngineWrapper Maven / Gradle / Ivy

/**
 * 版权所有 2017 Sp42 [email protected]
 * 
 * 根据 2.0 版本 Apache 许可证("许可证")授权;
 * 根据本许可证,用户可以不使用此文件。
 * 用户可从下列网址获得许可证副本:
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 *    
 * 除非因适用法律需要或书面同意,根据许可证分发的软件是基于"按原样"基础提供,
 * 无任何明示的或暗示的保证或条件。详见根据许可证许可下,特定语言的管辖权限和限制。
 */
package com.ajaxjs.jsonparser;

import java.io.IOException;
import java.io.InputStream;
import java.util.Map;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import com.ajaxjs.util.CommonUtil;
import com.ajaxjs.util.io.FileHelper;
import com.ajaxjs.util.io.IoHelper;
import com.ajaxjs.util.logger.LogHelper;

/**
 * JS 引擎的包装器
 * 
 * @author sp42 [email protected]
 *
 */
public class JsEngineWrapper {
	private static final LogHelper LOGGER = LogHelper.getLog(JsEngineWrapper.class);

	/**
	 * 创建一个 JS 引擎的包装器
	 */
	public JsEngineWrapper() {
		this(engineFactory());
	}

	/**
	 * 必须依赖一个原生 engine 对象
	 * 
	 * @param engine JVM 脚本引擎
	 */
	public JsEngineWrapper(ScriptEngine engine) {
		this.engine = engine;
	}

	/**
	 * js 引擎
	 */
	private ScriptEngine engine;

	/**
	 * 返回 js 引擎
	 * 
	 * @return js 引擎
	 */
	public ScriptEngine getEngine() {
		return engine;
	}

	/**
	 * 保存 js 引擎
	 * 
	 * @param engine js 引擎
	 */
	public void setEngine(ScriptEngine engine) {
		this.engine = engine;
	}

	/**
	 * 创建 js 引擎工厂,支持 java 6/7 的 rhino 和 java 8 的 nashorn
	 * 
	 * @return js 引擎
	 */
	public static ScriptEngine engineFactory() {
		return new ScriptEngineManager()
				.getEngineByName(System.getProperty("java.version").contains("1.8.") ? "nashorn" : "rhino");
	}

	/**
	 * 加载 js 文件,可以链式调用这个方法加载多个 js 文件
	 * 
	 * @param path js 文件完整路徑
	 * @return 当前实例,可以链式调用这个方法加载多个 js 文件
	 */
	public JsEngineWrapper load(String path) {
		LOGGER.info("加载 js: {0} 文件", path);
		eval(FileHelper.openAsText(path));

		return this;
	}

	/**
	 * 加载某个类下面的 js 文件,可以链式调用这个方法加载多个 js 文件
	 * 
	 * @param clazz    该类目录下面必须有目标 js 文件
	 * @param fileName js 文件名
	 * @return 当前实例,可以链式调用这个方法加载多个 js 文件
	 */
	public JsEngineWrapper load(Class clazz, String fileName) {
		String code = null;

		try (InputStream in = clazz.getResourceAsStream(fileName)) {
			code = IoHelper.byteStream2string(in);
		} catch (IOException e) {
			LOGGER.warning(e);
		}

		if (code != null)
			eval(code);

		return this;
	}

	/**
	 * 调用脚本的方法
	 * 
	 * @param method  js 脚本代码
	 * @param clazz   目标类型
	 * @param binding 可以为 null,则表示调用全局方法
	 * @param args    参数列表
	 * @return JS 运算后的返回值,也可能是 null 没有返回
	 */
	@SuppressWarnings("unchecked")
	public  T call(String method, Class clazz, Object binding, Object... args) {
		Invocable inv = (Invocable) engine; // Invocable 接口是 ScriptEngine可选实现的接口。(多态)
		Object result = null;

		try {
			result = binding != null ? inv.invokeMethod(binding, method, args) : inv.invokeFunction(method, args);
		} catch (NoSuchMethodException e) {
			LOGGER.warning(e, "脚本引擎没有 {0}() 这个方法", method);
		} catch (ScriptException e) {
			LOGGER.warning(e, "向脚本引擎调用脚本方法异常!方法名称:" + method);
		}

		return result == null ? null : (T) result;
	}

	/**
	 * 在 Java 中向脚本引擎 (Script Engine) 传递变量,即脚本语言中可以得到来自 java 的变量。 当然,使用 eval()
	 * 也可达到同样之效果。另外亦可以直接传递 Java 对象。
	 * 
	 * @param varName 变量名
	 * @param obj     变量值
	 */
	public void put(String varName, Object obj) {
		engine.put(varName, obj);
	}

	/**
	 * 获取 js 的对象,如果最后一个不是对象,返回 Object,之前的为 NativeObject
	 * 
	 * @param namespace JS 对象的 key
	 * @return NativeObject 或 Object
	 */
	@SuppressWarnings("rawtypes")
	public Object get(String... namespace) {
		Map obj = (Map) engine.get(namespace[0]);

		for (int i = 1; i < namespace.length; i++) {
			try {
				obj = (Map) obj.get(namespace[i]);
			} catch (ClassCastException e) {
				return obj.get(namespace[i]);
			}
		}

		return obj;
	}

	/**
	 * 执行 js 代码
	 * 
	 * @param code  任意 js 代码
	 * @param clazz 返回的类型。当 clazz = null 时永远返回 null,表示只是执行,不要求返回结果。可理解为 return
	 *              void。如果想有返回值,至少有个 clazz = Object.class
	 * @return 执行结果
	 */
	@SuppressWarnings("unchecked")
	public  T eval(String code, Class clazz) {
		if (CommonUtil.isEmptyString(code))
			throw new UnsupportedOperationException("JS 代码不能为空!");

		Object obj = null;

		try {
			obj = engine.eval(code);
		} catch (ScriptException e) {
			LOGGER.warning(e, "脚本 eval() 运算发生异常!eval 代码:" + code);
		}

		if (obj != null && clazz != null) {
			// return Util.TypeConvert(js.eval(code), clazz); // 为什么要执行多次?
			return (T) obj;
		} else
			return null;
	}

	/**
	 * 执行 js 代码,不作类型转换(返回 Object)
	 * 
	 * @param code 任意 js 代码
	 * @return 执行结果,null 可能表示返回 null,又或者可能没有返回(即 void)
	 */
	public Object eval(String code) {
		return eval(code, Object.class);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy