This commit is contained in:
Looly
2022-06-16 19:19:56 +08:00
parent e491349b81
commit 40ff9f051e
26 changed files with 160 additions and 119 deletions

View File

@@ -372,7 +372,7 @@
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>3.0.1</version>
<version>3.0.2</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
@@ -464,7 +464,7 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.3.19</version>
<version>5.3.20</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
@@ -490,5 +490,23 @@
<version>6.1.6</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.6</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.openjdk.nashorn</groupId>
<artifactId>nashorn-core</artifactId>
<version>15.4</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,273 @@
package cn.hutool.extra.script;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.map.WeakConcurrentMap;
import cn.hutool.core.text.StrUtil;
import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.Invocable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
/**
* 脚本工具类
*
* @author Looly
*/
public class ScriptUtil {
private static final ScriptEngineManager MANAGER = new ScriptEngineManager();
private static final WeakConcurrentMap<String, ScriptEngine> CACHE = new WeakConcurrentMap<>();
/**
* 获得单例的{@link ScriptEngine} 实例
*
* @param nameOrExtOrMime 脚本名称
* @return {@link ScriptEngine} 实例
*/
public static ScriptEngine getScript(final String nameOrExtOrMime) {
return CACHE.computeIfAbsent(nameOrExtOrMime, (key) -> createScript(nameOrExtOrMime));
}
/**
* 创建 {@link ScriptEngine} 实例
*
* @param nameOrExtOrMime 脚本名称
* @return {@link ScriptEngine} 实例
* @since 5.2.6
*/
public static ScriptEngine createScript(final String nameOrExtOrMime) {
ScriptEngine engine = MANAGER.getEngineByName(nameOrExtOrMime);
if (null == engine) {
engine = MANAGER.getEngineByExtension(nameOrExtOrMime);
}
if (null == engine) {
engine = MANAGER.getEngineByMimeType(nameOrExtOrMime);
}
if (null == engine) {
throw new NullPointerException(StrUtil.format("Script for [{}] not support !", nameOrExtOrMime));
}
return engine;
}
/**
* 获得单例的JavaScript引擎
*
* @return Javascript引擎
* @since 5.2.5
*/
public static ScriptEngine getJsEngine() {
return getScript("js");
}
/**
* 创建新的JavaScript引擎
*
* @return Javascript引擎
* @since 5.2.6
*/
public static ScriptEngine createJsEngine() {
return createScript("js");
}
/**
* 获得单例的Python引擎<br>
* 需要引入org.python:jython
*
* @return Python引擎
* @since 5.2.5
*/
public static ScriptEngine getPythonEngine() {
System.setProperty("python.import.site", "false");
return getScript("python");
}
/**
* 创建Python引擎<br>
* 需要引入org.python:jython
*
* @return Python引擎
* @since 5.2.6
*/
public static ScriptEngine createPythonEngine() {
System.setProperty("python.import.site", "false");
return createScript("python");
}
/**
* 获得单例的Lua引擎<br>
* 需要引入org.luaj:luaj-jse
*
* @return Lua引擎
* @since 5.2.5
*/
public static ScriptEngine getLuaEngine() {
return getScript("lua");
}
/**
* 创建Lua引擎<br>
* 需要引入org.luaj:luaj-jse
*
* @return Lua引擎
* @since 5.2.6
*/
public static ScriptEngine createLuaEngine() {
return createScript("lua");
}
/**
* 获得单例的Groovy引擎<br>
* 需要引入org.codehaus.groovy:groovy-all
*
* @return Groovy引擎
* @since 5.2.5
*/
public static ScriptEngine getGroovyEngine() {
return getScript("groovy");
}
/**
* 创建Groovy引擎<br>
* 需要引入org.codehaus.groovy:groovy-all
*
* @return Groovy引擎
* @since 5.2.6
*/
public static ScriptEngine createGroovyEngine() {
return createScript("groovy");
}
/**
* 执行Javascript脚本返回Invocable此方法分为两种情况
*
* <ol>
* <li>执行的脚本返回值是可执行的脚本方法</li>
* <li>脚本为函数库则ScriptEngine本身为可执行方法</li>
* </ol>
*
* @param script 脚本内容
* @return 执行结果
* @throws UtilException 脚本异常
* @since 5.3.6
*/
public static Invocable evalInvocable(final String script) throws UtilException {
final ScriptEngine jsEngine = getJsEngine();
final Object eval;
try {
eval = jsEngine.eval(script);
} catch (final ScriptException e) {
throw new UtilException(e);
}
if(eval instanceof Invocable){
return (Invocable)eval;
} else if(jsEngine instanceof Invocable){
return (Invocable)jsEngine;
}
throw new UtilException("Script is not invocable !");
}
/**
* 执行有返回值的Javascript脚本
*
* @param script 脚本内容
* @return 执行结果
* @throws UtilException 脚本异常
* @since 3.2.0
*/
public static Object eval(final String script) throws UtilException {
try {
return getJsEngine().eval(script);
} catch (final ScriptException e) {
throw new UtilException(e);
}
}
/**
* 执行有返回值的脚本
*
* @param script 脚本内容
* @param context 脚本上下文
* @return 执行结果
* @throws UtilException 脚本异常
* @since 3.2.0
*/
public static Object eval(final String script, final ScriptContext context) throws UtilException {
try {
return getJsEngine().eval(script, context);
} catch (final ScriptException e) {
throw new UtilException(e);
}
}
/**
* 执行有返回值的脚本
*
* @param script 脚本内容
* @param bindings 绑定的参数
* @return 执行结果
* @throws UtilException 脚本异常
* @since 3.2.0
*/
public static Object eval(final String script, final Bindings bindings) throws UtilException {
try {
return getJsEngine().eval(script, bindings);
} catch (final ScriptException e) {
throw new UtilException(e);
}
}
/**
* 执行JS脚本中的指定方法
*
* @param script js脚本
* @param func 方法名
* @param args 方法参数
* @return 结果
* @since 5.3.6
*/
public static Object invoke(final String script, final String func, final Object... args) {
final Invocable eval = evalInvocable(script);
try {
return eval.invokeFunction(func, args);
} catch (final ScriptException | NoSuchMethodException e) {
throw new UtilException(e);
}
}
/**
* 编译Javascript脚本
*
* @param script 脚本内容
* @return {@link CompiledScript}
* @throws UtilException 脚本异常
* @since 3.2.0
*/
public static CompiledScript compile(final String script) throws UtilException {
try {
return compile(getJsEngine(), script);
} catch (final ScriptException e) {
throw new UtilException(e);
}
}
/**
* 编译Javascript脚本
*
* @param engine 引擎
* @param script 脚本内容
* @return {@link CompiledScript}
* @throws ScriptException 脚本异常
*/
public static CompiledScript compile(final ScriptEngine engine, final String script) throws ScriptException {
if (engine instanceof Compilable) {
final Compilable compEngine = (Compilable) engine;
return compEngine.compile(script);
}
return null;
}
}

View File

@@ -0,0 +1,6 @@
/**
* 使用Java对脚本引擎的工具封装
*
* @author looly
*/
package cn.hutool.extra.script;

View File

@@ -0,0 +1,121 @@
package cn.hutool.extra.xml;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.XmlUtil;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import java.io.File;
import java.io.Reader;
import java.io.StringWriter;
import java.nio.charset.Charset;
/**
* JAXBJava Architecture for XML Binding根据XML Schema产生Java对象即实现xml和Bean互转。
* <p>
* 相关介绍:
* <ul>
* <li><a href="https://www.cnblogs.com/yanghaolie/p/11110991.html">https://www.cnblogs.com/yanghaolie/p/11110991.html</a></li>
* <li><a href="https://my.oschina.net/u/4266515/blog/3330113">https://my.oschina.net/u/4266515/blog/3330113</a></li>
* </ul>
*
* @author dazer
* @see XmlUtil
* @since 5.7.3
*/
public class JAXBUtil {
/**
* JavaBean转换成xml
* <p>
* bean上面用的常用注解
*
* @param bean Bean对象
* @return 输出的XML字符串
* @see XmlRootElement {@code @XmlRootElement(name = "school")}
* @see XmlElement {@code @XmlElement(name = "school_name", required = true)}
* @see XmlElementWrapper {@code @XmlElementWrapper(name="schools")}
* @see XmlTransient JAXB "有两个名为 "**" 的属性,类的两个属性具有相同名称 "**""解决方案
*/
public static String beanToXml(final Object bean) {
return beanToXml(bean, CharsetUtil.UTF_8, true);
}
/**
* JavaBean转换成xml
*
* @param bean Bean对象
* @param charset 编码 eg: utf-8
* @param format 是否格式化输出eg: true
* @return 输出的XML字符串
*/
public static String beanToXml(final Object bean, final Charset charset, final boolean format) {
final StringWriter writer;
try {
final JAXBContext context = JAXBContext.newInstance(bean.getClass());
final Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, format);
marshaller.setProperty(Marshaller.JAXB_ENCODING, charset.name());
writer = new StringWriter();
marshaller.marshal(bean, writer);
} catch (final Exception e) {
throw new UtilException("convertToXml 错误:" + e.getMessage(), e);
}
return writer.toString();
}
/**
* xml转换成JavaBean
*
* @param <T> Bean类型
* @param xml XML字符串
* @param c Bean类型
* @return bean
*/
public static <T> T xmlToBean(final String xml, final Class<T> c) {
return xmlToBean(StrUtil.getReader(xml), c);
}
/**
* XML文件转Bean
*
* @param file 文件
* @param charset 编码
* @param c Bean类
* @param <T> Bean类型
* @return Bean
*/
public static <T> T xmlToBean(final File file, final Charset charset, final Class<T> c) {
return xmlToBean(FileUtil.getReader(file, charset), c);
}
/**
* 从{@link Reader}中读取XML字符串并转换为Bean
*
* @param reader {@link Reader}
* @param c Bean类
* @param <T> Bean类型
* @return Bean
*/
@SuppressWarnings("unchecked")
public static <T> T xmlToBean(final Reader reader, final Class<T> c) {
try {
final JAXBContext context = JAXBContext.newInstance(c);
final Unmarshaller unmarshaller = context.createUnmarshaller();
return (T) unmarshaller.unmarshal(reader);
} catch (final Exception e) {
throw new RuntimeException("convertToJava2 错误:" + e.getMessage(), e);
} finally {
IoUtil.close(reader);
}
}
}

View File

@@ -0,0 +1,8 @@
/**
* XML和JAXBJava Architecture for XML Binding相关封装<br>
* 由于JDK11+移除了"javax.xml.bind"相关类型因此封装于extra模块
*
* @author looly
*
*/
package cn.hutool.extra.xml;

View File

@@ -0,0 +1,39 @@
package cn.hutool.extra.script;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.io.resource.ResourceUtil;
import org.junit.Assert;
import org.junit.Test;
import javax.script.CompiledScript;
import javax.script.ScriptException;
/**
* 脚本单元测试类
*
* @author looly
*
*/
public class ScriptUtilTest {
@Test
public void compileTest() {
final CompiledScript script = ScriptUtil.compile("print('Script test!');");
try {
script.eval();
} catch (final ScriptException e) {
throw new UtilException(e);
}
}
@Test
public void evalTest() {
ScriptUtil.eval("print('Script test!');");
}
@Test
public void invokeTest() {
final Object result = ScriptUtil.invoke(ResourceUtil.readUtf8Str("filter1.js"), "filter1", 2, 1);
Assert.assertTrue((Boolean) result);
}
}

View File

@@ -0,0 +1,116 @@
package cn.hutool.extra.xml;
import org.junit.Assert;
import org.junit.Test;
import javax.xml.bind.annotation.*;
/**
* {@link JAXBUtil} 工具类
* 测试 xml 和 bean 互转工具类
*
* @author dazer
*/
public class JAXBUtilTest {
private final String xmlStr = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
"<school>\n" +
" <school_name>西安市第一中学</school_name>\n" +
" <school_address>西安市雁塔区长安堡一号</school_address>\n" +
" <room>\n" +
" <room_no>101</room_no>\n" +
" <room_name>101教室</room_name>\n" +
" </room>\n" +
"</school>\n";
@Test
public void beanToXmlTest() {
final SchoolVo schoolVo = new SchoolVo();
schoolVo.setSchoolName("西安市第一中学");
schoolVo.setSchoolAddress("西安市雁塔区长安堡一号");
final SchoolVo.RoomVo roomVo = new SchoolVo.RoomVo();
roomVo.setRoomName("101教室");
roomVo.setRoomNo("101");
schoolVo.setRoom(roomVo);
Assert.assertEquals(xmlStr, JAXBUtil.beanToXml(schoolVo));
}
@Test
public void xmlToBeanTest() {
final SchoolVo schoolVo = JAXBUtil.xmlToBean(xmlStr, SchoolVo.class);
Assert.assertNotNull(schoolVo);
Assert.assertEquals("西安市第一中学", schoolVo.getSchoolName());
Assert.assertEquals("西安市雁塔区长安堡一号", schoolVo.getSchoolAddress());
Assert.assertEquals("101教室", schoolVo.getRoom().getRoomName());
Assert.assertEquals("101", schoolVo.getRoom().getRoomNo());
}
@XmlRootElement(name = "school")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(propOrder={"schoolName", "schoolAddress", "room"})
public static class SchoolVo {
@XmlElement(name = "school_name", required = true)
private String schoolName;
@XmlElement(name = "school_address", required = true)
private String schoolAddress;
@XmlElement(name = "room", required = true)
private RoomVo room;
@XmlTransient
public String getSchoolName() {
return schoolName;
}
public void setSchoolName(final String schoolName) {
this.schoolName = schoolName;
}
@XmlTransient
public String getSchoolAddress() {
return schoolAddress;
}
public void setSchoolAddress(final String schoolAddress) {
this.schoolAddress = schoolAddress;
}
@XmlTransient
public RoomVo getRoom() {
return room;
}
public void setRoom(final RoomVo room) {
this.room = room;
}
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(propOrder={"roomNo", "roomName"})
public static final class RoomVo {
@XmlElement(name = "room_no", required = true)
private String roomNo;
@XmlElement(name = "room_name", required = true)
private String roomName;
@XmlTransient
public String getRoomNo() {
return roomNo;
}
public void setRoomNo(final String roomNo) {
this.roomNo = roomNo;
}
@XmlTransient
public String getRoomName() {
return roomName;
}
public void setRoomName(final String roomName) {
this.roomName = roomName;
}
}
}
}

View File

@@ -0,0 +1,6 @@
function filter1(a, b) {
if (a > b) {
return a > b;
}
return false;
}