Panda.Core.EL
EL表达式是啥东东
EL (expression language) 简单来说就是一个表达式字符串。 Panda.EL就是执行这个EL并且返回结果的引擎。
最简单的用法
System.out.println(EL.eval("3+4*5")); // 将打印 23,够简单吧
它支持变量,比如
Map m = new HashMap();
m.put("a", 10);
System.out.println(EL.eval("a*10", m)); // 将打印 100
你可以为你的表达式随意设置变量的值。它支持如下类型的 Java 数据
- 整型 - int 或 Integer
- 浮点 - float 或 Float
- 长整 - long 或 Long
- 布尔 - boolean 或 Boolean
- 字符串 - String
- 数组 - T[]
- 列表 - List
- 集合 - Collection
- Map - Map<?, ?>
- 普通 Java 对象
支持什么样的操作符
符号 | 操作数 | 权重 | 说明 |
---|---|---|---|
() | * | 100 | 括号,优先计算 |
, | * | 90 | 逗号操作符,将左右两边的数据组织成一个数据 |
@ | 2 | 1 | 静态方法的调用 |
. | 2 | 1 | 访问对象的属性,或者Map的值,或者方法调用 |
{1,2} | * | 1 | Java数组 |
[‘abc’] | 2 | 1 | Java 对象 Map按键值获得值 |
[3] | 2 | 1 | 数字,列表,或者集合的下标访问符号 |
* | 2 | 3 | 乘 |
/ | 2 | 3 | 整除 |
% | 2 | 3 | 取模 |
+ | 2 | 4 | 加 |
- | 2 | 4 | 减 |
- | 2 | 2 | 负 |
>= | 2 | 6 | 大于等于 |
<= | 2 | 5 | 小于等于 |
== | 2 | 7 | 等于 |
!= | 2 | 6 | 不等于 |
! | 2 | 7 | 非 |
!! | 1 | 7 | 忽略異常,返回null |
> | 2 | 6 | 大于 |
< | 2 | 6 | 小于 |
&& | 2 | 11 | 逻辑与 |
|| | 2 | 12 | 逻辑或 |
A|||B | 2 | 12 | 若A为空或False返回B,返否則回A |
?: | 2 | 13 | 三元运算 |
& | 2 | 8 | 位运算,与 |
~ | 2 | 2 | 位运算,非 |
| | 2 | 10 | 位运算,或 |
^ | 2 | 9 | 位运算,异或 |
« | 2 | 5 | 位运算,左移 |
» | 2 | 5 | 位运算,右移 |
»> | 2 | 5 | 位运算,无符号右移 |
只要支持的操作符,它的优先级以及行为会和 Java 的表达式一致。如果你发现不一致 别犹豫,给我报 Issue 吧。
忠于 Java
Panda.EL 完全忠实于 JAVA 基本运算规则, 并没有做一些扩展, 比如最常见的数据类型转换。
在 JAVA 中进行数值运算的过程中, 是根据运算符两边的类型而最终决定运算结果的类型的。
比如:
7/3 // 将返回int型
而
(1.0 * 7)/3 // 返回double
(1.0f * 7)/3 // 则返回float
支持对象方法调用
比如:
Map map = new HashMap();
map.put("a", new BigDecimal("7"));
map.put("b", new BigDecimal("3"));
assertEquals(10, EL.eval(map, "a.add(b).intValue()"));
支持静态方法调用
比如:
assertFalse((Boolean)EL.eval("'java.lang.Boolean'@FALSE"));
assertEquals(Boolean.TRUE, EL.eval("'java.lang.Boolean'@parseBoolean('true')"));
一些表达式的例子
普通运算
System.out.println(EL.eval("3+2*5"));
// 输出为 13
字符串操作
System.out.println(EL.eval("' abc '.trim()"));
// 输出为 abc
Java 对象属性访问调用
Map map = new HashMap();
Pet pet = new Pet();
pet.setName("GFW");
map.put("pet", pet);
System.out.println(EL.eval("pet.name", map));
// 输出为 GFW
函数调用
Map map = new HashMap();
Pet pet = new Pet();
map.put("pet", pet);
EL.eval("pet.setName('XiaoBai')", map);
System.out.println(EL.eval("pet.getName()", map));
// 输出为 XiaoBai
数组访问
Map map = new HashMap();
map.put("x", new String[] { "A", "B", "C" });
System.out.println(EL.eval("x[0].toLowerCase()"), map);
// 输出为 a
列表访问
Map map = new HashMap();
map.put("x", Arrays.asList("A", "B", "C"));
System.out.println(EL.eval("x.get(0).toLowerCase()", map));
// 输出为 a
Map 访问
Map map = new HashMap();
map.put("map", Jsons.toJson("{x:10, y:5}"));
System.out.println(EL.eval("map['x'] * map['y']", map));
// 输出为 50
判断
Map map = new HashMap();
map.put("a",5);
System.out.println(EL.eval("a>10", map));
// 输出为 false
map.put("a",20);
System.out.println(EL.eval("a>10", map));
// 输出为 true
空值或异常的处理
例如obj.pet.name中的pet是null, 可以捕捉异常然后表达式的值为null
Map map = new HashMap();
map.set("obj", "pet");
ELContext ctx = new ELContext(map, true);
assertTrue((Boolean)EL.eval("!!(obj.pet.name) == null", ctx));
A或者B
有空值/异常处理后,有时候还需要填充一个默认值
Map map = new HashMap();
map.set("obj", "pet");
assertEquals("cat", EL.eval("!!(obj.pet.name) ||| 'cat'", map));
严格模式(strict)和非严格模式
缺省情况下,EL使用非严格模式(not strict),即空对象的函数调用不会抛出异常。 比如:
Map map = new HashMap();
map.set("obj", "pet");
assertEquals("cat", EL.eval("obj.pet.name ||| 'cat'", map));
如果是严格模式(strict),空对象的函数调用会抛出异常,比如:
Map map = new HashMap();
map.set("obj", "pet");
ELContext ctx = new ELContext(map, true);
assertEquals("cat", EL.eval("!!(obj.pet.name) ||| 'cat'", map));
它速度怎么样?
我觉得它速度不怎么样。它的工作的原理是这样的,每次解析都经过如果下三步:
- 解析成后缀表达式形式的一个队列
- 将后缀表达式解析成一棵运算树
- 对运算树的根结点进行运算
当然我也提供了一个提升效率的手段,因为如果每次计算都经过这三个步骤当然慢, 所以我们可以对它先预编译:
EL exp = new EL("a*10"); // 预编译结果为一个 EL 对象
Map m = new HashMap();
m.put("a", 10);
System.out.println(exp.eval(m)); // 将打印 100
EL在实例化时就会对表达式进行预编译,会直接编译成运算树,当调用eval方法时,就不用再耗时的编译动作了。它的 eval 函数是线程安全的。
静态函数EL.eval(“xxx”)会通过内部WeakHashMap缓存中查找EL(“xxx”)的对象,如果没找到,就会生成一个,并且把它保存至缓存里。