一个简单的el表达式实现
实现还在完善种,现在只能处理引用类型的的属性字段和函数,不支持容器类数组!不支持子类型
线来个测试用例
package com.isnowfox.oel; import java.util.Date; import com.isnowfox.core.junit.BaseTest; import com.isnowfox.util.RandomUtils; /** * el 测试用例 * @author zuoge85 * */ public class OelTest extends BaseTest{ public void testBasObject() throws UnknownKeyException{ OelEngine oel = OelEngine.getInstance(); TestObject obj = new TestObject(); Date date = new Date(); int number = RandomUtils.randInt(); String string = "测试用例"; obj.dateField =date; obj.setDateProperty(date); obj.intField =number; obj.setIntProperty(number); obj.stringField = string; obj.setStringProperty(string); obj.dateMethod(date); assertEquals(date, oel.el(obj, "dateProperty")); assertEquals(date.getTime(), oel.el(obj, "dateProperty.time")); assertEquals(date, oel.el(obj, "dateField")); assertEquals(date.getTime(), oel.el(obj, "dateField.time")); assertEquals(date, oel.el(obj, "dateMethod()")); assertEquals(date.getTime(), oel.el(obj, "dateMethod().time")); assertEquals(number, oel.el(obj, "intField")); assertEquals(number, oel.el(obj, "intProperty")); assertEquals(string, oel.el(obj, "stringField")); assertEquals(string, oel.el(obj, "stringProperty")); } }
package com.isnowfox.oel; import java.util.Date; public class TestObject { public int intField; public String stringField; public Date dateField; private int intProperty; private String StringProperty; private Date dateProperty; private Date dateMethod; public int getIntProperty() { return intProperty; } public void setIntProperty(int intProperty) { this.intProperty = intProperty; } public String getStringProperty() { return StringProperty; } public void setStringProperty(String stringProperty) { StringProperty = stringProperty; } public Date getDateProperty() { return dateProperty; } public void setDateProperty(Date dateProperty) { this.dateProperty = dateProperty; } public Date dateMethod(){ return dateMethod; } public void dateMethod(Date dateMethod){ this.dateMethod = dateMethod; } @Override public String toString() { return "TestObject [intField=" + intField + ", StringField=" + stringField + ", dataField=" + dateField + ", intProperty=" + intProperty + ", StringProperty=" + StringProperty + ", dataProperty=" + dateProperty + "]"; } }
下面是实现
package com.isnowfox.oel; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import javassist.CtNewMethod; import javassist.NotFoundException; import com.isnowfox.oel.KeyInfo.Type; /** * oel引擎 * @author zuoge85 * */ public class OelEngine { private static final boolean DEBUG = true; private static final String PACKAGE_NAME = OelEngine.class.getPackage().getName(); private static Map, Class>> primitivesWrapMap = new HashMap , Class>>(16); static{ primitivesWrapMap.put(boolean.class, Boolean.class); primitivesWrapMap.put(byte.class, Byte.class); primitivesWrapMap.put(char.class, Character.class); primitivesWrapMap.put(double.class, Double.class); primitivesWrapMap.put(float.class, Float.class); primitivesWrapMap.put(int.class, Integer.class); primitivesWrapMap.put(long.class, Long.class); primitivesWrapMap.put(short.class, Short.class); primitivesWrapMap.put( void.class, Void.class); } public static final OelEngine getInstance(){ return Inner.instance; } private Map cacheMap = new HashMap<>(); private AtomicInteger itemSeed = new AtomicInteger(); public Object el(Object obj,String el) throws UnknownKeyException{ if(obj == null){ throw new NullPointerException("null"); } try { Class> cls = obj.getClass(); String cacheKey = cls.getName() + el; OelProxy p =cacheMap.get(cacheKey); if(p==null){ ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.makeClass(PACKAGE_NAME + ".MyOelProxy" + itemSeed.incrementAndGet()); cc.addInterface(pool.get(PACKAGE_NAME +".OelProxy")); CtMethod method = CtNewMethod.make("public Object el(Object obj);", cc); method.setBody(makeMethod(cls, obj, el)); cc.addMethod(method); if(DEBUG){ cc.debugWriteFile("debug"); } p = (OelProxy) cc.toClass().newInstance(); cacheMap.put(cacheKey, p); } return p.el(obj); } catch (NotFoundException|CannotCompileException|InstantiationException|IllegalAccessException e) { throw new RuntimeException(e); } } private String makeMethod(Class> cls, Object obj, String el) throws UnknownKeyException{ int varSeed = 0; StringBuilder sb = new StringBuilder("{\n"); String[] keys = el.split("\\."); sb.append(cls.getCanonicalName() + " var_" + (++varSeed) + " = (" +cls.getCanonicalName() + ")$1;\n" ); for(String key:keys){ KeyInfo info =analyse(cls,key); int nextVar = varSeed++ ; switch (info.getType()) { case FIELD: sb.append(info.getCls().getCanonicalName() +" var_" + varSeed + " = var_"+ nextVar +"." + key + ";\n"); break; case PROPERTY: sb.append(info.getCls().getCanonicalName() +" var_" + varSeed + " = var_"+ nextVar +"." + info.getMethodName() + "();\n"); break; case METHOD: sb.append(info.getCls().getCanonicalName() +" var_" + varSeed + " = var_"+ nextVar +"." + key + ";\n"); break; default: break; } cls = info.getCls(); } if(cls.isPrimitive()){ cls = wrap(cls); sb.append("return "+cls.getCanonicalName()+".valueOf(var_" + varSeed + ");}"); }else{ sb.append("return var_" + varSeed + ";}"); } return sb.toString(); } private KeyInfo analyse(Class> cls,String key) throws UnknownKeyException{ try { if(key.endsWith("()")){ try { Method method = cls.getMethod(key.substring(0, key.length()-2)); return new KeyInfo(Type.METHOD, method.getReturnType()); } catch (Exception e2) { } } PropertyDescriptor prop = new PropertyDescriptor(key, cls); return new KeyInfo(Type.PROPERTY, prop.getPropertyType(), prop.getReadMethod().getName()); } catch (IntrospectionException e) { //属性没找到那么可能是field Field field; try { field = cls.getField(key); return new KeyInfo(Type.FIELD, field.getType()); } catch (Exception e1) { } } throw new UnknownKeyException("未知的 key:"+key +",cls:"+ cls); } private Class> wrap(Class> cls){ return primitivesWrapMap.get(cls); } public Object part(Object Obj,String name){ return null; } private static class Inner{ private static final OelEngine instance = new OelEngine(); } }