mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
add ExpressionUtil
This commit is contained in:
parent
7817599cb6
commit
09d00f9fdf
@ -13,6 +13,7 @@
|
||||
* 【core 】 HexUtil增加format方法(issue#I245NF@Gitee)
|
||||
* 【poi 】 ExcelWriter增加setCurrentRowToEnd方法(issue#I24A2R@Gitee)
|
||||
* 【core 】 ExcelWriter增加setCurrentRowToEnd方法(issue#I24A2R@Gitee)
|
||||
* 【extra 】 增加表达式引擎封装(ExpressionUtil)(pr#1203@Github)
|
||||
|
||||
### Bug修复
|
||||
* 【core 】 修复DateUtil.current使用System.nanoTime的问题(issue#1198@Github)
|
||||
|
@ -277,5 +277,40 @@
|
||||
<version>1.2.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.googlecode.aviator</groupId>
|
||||
<artifactId>aviator</artifactId>
|
||||
<version>5.1.4</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-jexl3</artifactId>
|
||||
<version>3.1</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mvel</groupId>
|
||||
<artifactId>mvel2</artifactId>
|
||||
<version>2.4.10.Final</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jfirer</groupId>
|
||||
<artifactId>jfireEl</artifactId>
|
||||
<version>1.0</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-expression</artifactId>
|
||||
<version>5.3.0</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@ -0,0 +1,20 @@
|
||||
package cn.hutool.extra.expression;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 表达式引擎API接口,通过实现此接口,完成表达式的解析和执行
|
||||
*
|
||||
* @author looll,independenter
|
||||
* @since 5.5.0
|
||||
*/
|
||||
public interface ExpressionEngine {
|
||||
|
||||
/**
|
||||
* 执行表达式
|
||||
* @param expression 表达式
|
||||
* @param context 表达式上下文,用于存储表达式中所需的变量值等
|
||||
* @return 执行结果
|
||||
*/
|
||||
Object eval(String expression, Map<String, Object> context);
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package cn.hutool.extra.expression;
|
||||
|
||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
/**
|
||||
* 表达式语言异常
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class ExpressionException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ExpressionException(Throwable e) {
|
||||
super(ExceptionUtil.getMessage(e), e);
|
||||
}
|
||||
|
||||
public ExpressionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ExpressionException(String messageTemplate, Object... params) {
|
||||
super(StrUtil.format(messageTemplate, params));
|
||||
}
|
||||
|
||||
public ExpressionException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
public ExpressionException(Throwable throwable, String messageTemplate, Object... params) {
|
||||
super(StrUtil.format(messageTemplate, params), throwable);
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package cn.hutool.extra.expression;
|
||||
|
||||
import cn.hutool.extra.expression.engine.ExpressionFactory;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 表达式引擎工具类
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.5.0
|
||||
*/
|
||||
public class ExpressionUtil {
|
||||
|
||||
/**
|
||||
* 获得全局单例的表达式引擎
|
||||
*
|
||||
* @return 全局单例的表达式引擎
|
||||
*/
|
||||
public static ExpressionEngine getEngine() {
|
||||
return ExpressionFactory.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行表达式
|
||||
*
|
||||
* @param expression 表达式
|
||||
* @param context 表达式上下文,用于存储表达式中所需的变量值等
|
||||
* @return 执行结果
|
||||
*/
|
||||
public static Object eval(String expression, Map<String, Object> context) {
|
||||
return getEngine().eval(expression, context);
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package cn.hutool.extra.expression.engine;
|
||||
|
||||
import cn.hutool.core.lang.Singleton;
|
||||
import cn.hutool.core.util.ServiceLoaderUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.log.StaticLog;
|
||||
|
||||
import cn.hutool.extra.expression.ExpressionEngine;
|
||||
import cn.hutool.extra.expression.ExpressionException;
|
||||
|
||||
/**
|
||||
* 表达式语言引擎工厂类,,用于根据用户引入的表达式jar,自动创建对应的引擎对象
|
||||
*
|
||||
* @since 5.5.0
|
||||
* @author looly
|
||||
*/
|
||||
public class ExpressionFactory {
|
||||
|
||||
/**
|
||||
* 获得单例的{@link ExpressionEngine}
|
||||
*
|
||||
* @return 单例的{@link ExpressionEngine}
|
||||
*/
|
||||
public static ExpressionEngine get(){
|
||||
return Singleton.get(ExpressionEngine.class.getName(), ExpressionFactory::create);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户引入的表达式引擎jar,自动创建对应的拼音引擎对象<br>
|
||||
* 推荐创建的引擎单例使用,此方法每次调用会返回新的引擎
|
||||
*
|
||||
* @return {@link ExpressionEngine}
|
||||
*/
|
||||
public static ExpressionEngine create() {
|
||||
final ExpressionEngine engine = doCreate();
|
||||
StaticLog.debug("Use [{}] Engine As Default.", StrUtil.removeSuffix(engine.getClass().getSimpleName(), "Engine"));
|
||||
return engine;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户引入的拼音引擎jar,自动创建对应的拼音引擎对象<br>
|
||||
* 推荐创建的引擎单例使用,此方法每次调用会返回新的引擎
|
||||
*
|
||||
* @return {@link ExpressionEngine}
|
||||
*/
|
||||
private static ExpressionEngine doCreate() {
|
||||
final ExpressionEngine engine = ServiceLoaderUtil.loadFirstAvailable(ExpressionEngine.class);
|
||||
if(null != engine){
|
||||
return engine;
|
||||
}
|
||||
|
||||
throw new ExpressionException("No expression jar found ! Please add one of it to your project !");
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package cn.hutool.extra.expression.engine.aviator;
|
||||
|
||||
import cn.hutool.extra.expression.ExpressionEngine;
|
||||
import com.googlecode.aviator.AviatorEvaluator;
|
||||
import com.googlecode.aviator.AviatorEvaluatorInstance;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Aviator引擎封装<br>
|
||||
* 见:https://github.com/killme2008/aviatorscript
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.5.0
|
||||
*/
|
||||
public class AviatorEngine implements ExpressionEngine {
|
||||
|
||||
private final AviatorEvaluatorInstance engine;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public AviatorEngine() {
|
||||
engine = AviatorEvaluator.getInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(String expression, Map<String, Object> context) {
|
||||
return engine.execute(expression, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取{@link AviatorEvaluatorInstance}
|
||||
*
|
||||
* @return {@link AviatorEvaluatorInstance}
|
||||
*/
|
||||
public AviatorEvaluatorInstance getEngine() {
|
||||
return this.engine;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Aviator引擎封装,见:https://github.com/killme2008/aviatorscript
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
package cn.hutool.extra.expression.engine.aviator;
|
@ -0,0 +1,37 @@
|
||||
package cn.hutool.extra.expression.engine.jexl;
|
||||
|
||||
import cn.hutool.extra.expression.ExpressionEngine;
|
||||
import org.apache.commons.jexl3.JexlBuilder;
|
||||
import org.apache.commons.jexl3.MapContext;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Jexl3引擎封装<br>
|
||||
* 见:https://github.com/apache/commons-jexl
|
||||
*
|
||||
* @since 5.5.0
|
||||
* @author looly
|
||||
*/
|
||||
public class JexlEngine implements ExpressionEngine {
|
||||
|
||||
private final org.apache.commons.jexl3.JexlEngine engine;
|
||||
|
||||
public JexlEngine(){
|
||||
engine = (new JexlBuilder()).cache(512).strict(true).silent(false).create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(String expression, Map<String, Object> context) {
|
||||
return engine.createExpression(expression).evaluate(new MapContext(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取{@link org.apache.commons.jexl3.JexlEngine}
|
||||
*
|
||||
* @return {@link org.apache.commons.jexl3.JexlEngine}
|
||||
*/
|
||||
public org.apache.commons.jexl3.JexlEngine getEngine() {
|
||||
return this.engine;
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Jexl3引擎封装,见:https://github.com/apache/commons-jexl
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
package cn.hutool.extra.expression.engine.jexl;
|
@ -0,0 +1,27 @@
|
||||
package cn.hutool.extra.expression.engine.jfireel;
|
||||
|
||||
import cn.hutool.extra.expression.ExpressionEngine;
|
||||
import com.jfirer.jfireel.expression.Expression;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JfireEL引擎封装<br>
|
||||
* 见:https://gitee.com/eric_ds/jfireEL
|
||||
*
|
||||
* @since 5.5.0
|
||||
* @author looly
|
||||
*/
|
||||
public class JfireELEngine implements ExpressionEngine {
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public JfireELEngine(){
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(String expression, Map<String, Object> context) {
|
||||
return Expression.parse(expression).calculate(context);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* JfireEL引擎封装<br>
|
||||
* 见:https://gitee.com/eric_ds/jfireEL
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
package cn.hutool.extra.expression.engine.jfireel;
|
@ -0,0 +1,27 @@
|
||||
package cn.hutool.extra.expression.engine.mvel;
|
||||
|
||||
import cn.hutool.extra.expression.ExpressionEngine;
|
||||
import org.mvel2.MVEL;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* MVEL (MVFLEX Expression Language)引擎封装<br>
|
||||
* 见:https://github.com/mvel/mvel
|
||||
*
|
||||
* @since 5.5.0
|
||||
* @author looly
|
||||
*/
|
||||
public class MvelEngine implements ExpressionEngine {
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public MvelEngine(){
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(String expression, Map<String, Object> context) {
|
||||
return MVEL.eval(expression, context);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* MVEL (MVFLEX Expression Language)引擎封装<br>
|
||||
* 见:https://github.com/mvel/mvel
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
package cn.hutool.extra.expression.engine.mvel;
|
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* 表达式语言引擎封装
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
package cn.hutool.extra.expression.engine;
|
@ -0,0 +1,35 @@
|
||||
package cn.hutool.extra.expression.engine.spel;
|
||||
|
||||
import cn.hutool.extra.expression.ExpressionEngine;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Spring-Expression引擎封装<br>
|
||||
* 见:https://github.com/spring-projects/spring-framework/tree/master/spring-expression
|
||||
*
|
||||
* @since 5.5.0
|
||||
* @author looly
|
||||
*/
|
||||
public class SpELEngine implements ExpressionEngine {
|
||||
|
||||
private final ExpressionParser parser;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public SpELEngine(){
|
||||
parser = new SpelExpressionParser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(String expression, Map<String, Object> context) {
|
||||
final EvaluationContext evaluationContext = new StandardEvaluationContext();
|
||||
context.forEach(evaluationContext::setVariable);
|
||||
return parser.parseExpression(expression).getValue(evaluationContext);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Spring-Expression引擎封装<br>
|
||||
* 见:https://github.com/spring-projects/spring-framework/tree/master/spring-expression
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
package cn.hutool.extra.expression.engine.spel;
|
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* 表达式语言引擎封装
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
package cn.hutool.extra.expression;
|
@ -3,10 +3,11 @@ package cn.hutool.extra.pinyin.engine;
|
||||
import cn.hutool.core.lang.Singleton;
|
||||
import cn.hutool.core.util.ServiceLoaderUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.pinyin.PinyinEngine;
|
||||
import cn.hutool.extra.template.TemplateException;
|
||||
import cn.hutool.log.StaticLog;
|
||||
|
||||
import cn.hutool.extra.pinyin.PinyinEngine;
|
||||
import cn.hutool.extra.pinyin.PinyinException;
|
||||
|
||||
/**
|
||||
* 简单拼音引擎工厂,用于根据用户引入的拼音库jar,自动创建对应的拼音引擎对象
|
||||
*
|
||||
@ -47,6 +48,6 @@ public class PinyinFactory {
|
||||
return engine;
|
||||
}
|
||||
|
||||
throw new TemplateException("No pinyin jar found ! Please add one of it to your project !");
|
||||
throw new PinyinException("No pinyin jar found ! Please add one of it to your project !");
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
cn.hutool.extra.expression.engine.aviator.AviatorEngine
|
||||
cn.hutool.extra.expression.engine.jexl.JexlEngine
|
||||
cn.hutool.extra.expression.engine.mvel.MvelEngine
|
||||
cn.hutool.extra.expression.engine.jfireel.JfireELEngine
|
||||
cn.hutool.extra.expression.engine.spel.SpELEngine
|
@ -0,0 +1,62 @@
|
||||
package cn.hutool.extra.expression;
|
||||
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.extra.expression.engine.aviator.AviatorEngine;
|
||||
import lombok.Data;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Aviator引擎单元测试,来自https://github.com/looly/hutool/pull/1203
|
||||
*/
|
||||
public class AviatorTest {
|
||||
|
||||
@Test
|
||||
public void simpleTest(){
|
||||
Foo foo = new Foo(100, 3.14f, new Date());
|
||||
ExpressionEngine engine = new AviatorEngine();
|
||||
String exp =
|
||||
"\"[foo i=\"+ foo.i + \", f=\" + foo.f + \", date.year=\" + (foo.date.year+1900) + \", date.month=\" + foo.date.month + \", bars[0].name=\" + #foo.bars[0].name + \"]\"";
|
||||
String result = (String) engine.eval(exp, Dict.create().set("foo", foo));
|
||||
Assert.assertEquals("[foo i=100, f=3.14, date.year=2020, date.month=10, bars[0].name=bar]", result);
|
||||
|
||||
// Assignment.
|
||||
exp = "#foo.bars[0].name='hello aviator' ; #foo.bars[0].name";
|
||||
result = (String) engine.eval(exp, Dict.create().set("foo", foo));
|
||||
Assert.assertEquals("hello aviator", result);
|
||||
Assert.assertEquals("hello aviator", foo.bars[0].getName());
|
||||
|
||||
exp = "foo.bars[0] = nil ; foo.bars[0]";
|
||||
result = (String) engine.eval(exp, Dict.create().set("foo", foo));
|
||||
Console.log("Execute expression: " + exp);
|
||||
Assert.assertNull(result);
|
||||
Assert.assertNull(foo.bars[0]);
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Bar {
|
||||
public Bar() {
|
||||
this.name = "bar";
|
||||
}
|
||||
private String name;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Foo {
|
||||
int i;
|
||||
float f;
|
||||
Date date;
|
||||
Bar[] bars = new Bar[1];
|
||||
|
||||
public Foo(final int i, final float f, final Date date) {
|
||||
super();
|
||||
this.i = i;
|
||||
this.f = f;
|
||||
this.date = date;
|
||||
this.bars[0] = new Bar();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package cn.hutool.extra.expression;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.extra.expression.engine.jexl.JexlEngine;
|
||||
import cn.hutool.extra.expression.engine.jfireel.JfireELEngine;
|
||||
import cn.hutool.extra.expression.engine.mvel.MvelEngine;
|
||||
import cn.hutool.extra.expression.engine.spel.SpELEngine;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ExpressionUtilTest {
|
||||
|
||||
@Test
|
||||
public void evalTest(){
|
||||
final Dict dict = Dict.create()
|
||||
.set("a", 100.3)
|
||||
.set("b", 45)
|
||||
.set("c", -199.100);
|
||||
final Object eval = ExpressionUtil.eval("a-(b-c)", dict);
|
||||
Assert.assertEquals(-143.8, (double)eval, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jexlTest(){
|
||||
ExpressionEngine engine = new JexlEngine();
|
||||
|
||||
final Dict dict = Dict.create()
|
||||
.set("a", 100.3)
|
||||
.set("b", 45)
|
||||
.set("c", -199.100);
|
||||
final Object eval = engine.eval("a-(b-c)", dict);
|
||||
Assert.assertEquals(-143.8, (double)eval, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mvelTest(){
|
||||
ExpressionEngine engine = new MvelEngine();
|
||||
|
||||
final Dict dict = Dict.create()
|
||||
.set("a", 100.3)
|
||||
.set("b", 45)
|
||||
.set("c", -199.100);
|
||||
final Object eval = engine.eval("a-(b-c)", dict);
|
||||
Assert.assertEquals(-143.8, (double)eval, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jfireELTest(){
|
||||
ExpressionEngine engine = new JfireELEngine();
|
||||
|
||||
final Dict dict = Dict.create()
|
||||
.set("a", 100.3)
|
||||
.set("b", 45)
|
||||
.set("c", -199.100);
|
||||
final Object eval = engine.eval("a-(b-c)", dict);
|
||||
Assert.assertEquals(-143.8, (double)eval, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void spELTest(){
|
||||
ExpressionEngine engine = new SpELEngine();
|
||||
|
||||
final Dict dict = Dict.create()
|
||||
.set("a", 100.3)
|
||||
.set("b", 45)
|
||||
.set("c", -199.100);
|
||||
final Object eval = engine.eval("#a-(#b-#c)", dict);
|
||||
Assert.assertEquals(-143.8, (double)eval, 2);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user