001package com.hammurapi.extract.java; 002 003import java.io.StringReader; 004import java.lang.reflect.InvocationTargetException; 005import java.util.ArrayList; 006import java.util.Collection; 007import java.util.concurrent.TimeUnit; 008import java.util.concurrent.atomic.AtomicReference; 009 010import org.codehaus.commons.compiler.CompileException; 011import org.codehaus.janino.ExpressionEvaluator; 012import org.codehaus.janino.Java; 013import org.codehaus.janino.Java.Atom; 014import org.codehaus.janino.Java.BinaryOperation; 015import org.codehaus.janino.Java.Instanceof; 016import org.codehaus.janino.Java.Literal; 017import org.codehaus.janino.Java.MethodInvocation; 018import org.codehaus.janino.Java.ParenthesizedExpression; 019import org.codehaus.janino.Java.Rvalue; 020import org.codehaus.janino.Java.Type; 021import org.codehaus.janino.Java.UnaryOperation; 022import org.codehaus.janino.Parser; 023import org.codehaus.janino.Scanner; 024 025import antlr.RecognitionException; 026import antlr.TokenStreamException; 027 028import com.hammurapi.extract.Add; 029import com.hammurapi.extract.And; 030import com.hammurapi.extract.CommutativeAnd; 031import com.hammurapi.extract.CommutativeOr; 032import com.hammurapi.extract.CompositePredicate; 033import com.hammurapi.extract.Constant; 034import com.hammurapi.extract.Divide; 035import com.hammurapi.extract.Equal; 036import com.hammurapi.extract.Extractor; 037import com.hammurapi.extract.ExtractorException; 038import com.hammurapi.extract.ExtractorFactory; 039import com.hammurapi.extract.ExtractorUtil; 040import com.hammurapi.extract.False; 041import com.hammurapi.extract.GreaterEqual; 042import com.hammurapi.extract.GreaterThan; 043import com.hammurapi.extract.InstanceOfPredicate; 044import com.hammurapi.extract.LessEqual; 045import com.hammurapi.extract.LessThan; 046import com.hammurapi.extract.Multiply; 047import com.hammurapi.extract.Not; 048import com.hammurapi.extract.NotEqual; 049import com.hammurapi.extract.Or; 050import com.hammurapi.extract.Predicate; 051import com.hammurapi.extract.Subtract; 052import com.hammurapi.extract.True; 053 054public class JavaExtractorFactory implements ExtractorFactory { 055 056 private static final String LOGICAL_OR = "||"; 057 private static final String LOGICAL_AND = "&&"; 058 private static final String COMMUTATIVE_AND_MACRO = "AND"; 059 private static final String COMMUTATIVE_OR_MACRO = "OR"; 060 private static final String COST_MACRO = "COST"; 061 062 @SuppressWarnings("unchecked") 063 @Override 064 public <T, V, C> Extractor<T, V, C> createExtractor( 065 String language, 066 String code, 067 String[] parameterNames, 068 Class<T>[] parameterTypes, 069 Class<V> valueType, 070 Class<C> contextType, 071 ClassLoader classLoader) { 072 073 if ("java".equals(language)) { 074 try { 075 Parser jnp = new Parser(new Scanner("Expression "+code, new StringReader(code))); 076 Atom expr = jnp.parseExpression(); 077 Extractor<T, V, C> ret = createExtractor(0, null, expr, parameterNames, parameterTypes, valueType, contextType, classLoader, null, null); 078 if (ret instanceof CompositePredicate) { 079 return ((CompositePredicate) ret).normalize(); 080 } 081 return ret; 082 } catch (ExtractorException e) { 083 throw e; 084 } catch (Exception e) { 085 throw new ExtractorException("Error creating Java extractor for code '"+code+"': "+e, e); 086 } 087 } 088 return null; 089 } 090 091 @SuppressWarnings("unchecked") 092 @Override 093 public <T, C> Predicate<T, C> createPredicate( 094 String language, 095 String code, 096 String[] parameterNames, 097 Class<T>[] parameterTypes, 098 Class<C> contextType, 099 ClassLoader classLoader) { 100 101 if ("java".equals(language)) { 102 try { 103 Parser jnp = new Parser(new Scanner("Expression "+code, new StringReader(code))); 104 Atom expr = jnp.parseExpression(); 105 Extractor<T, Boolean, C> ret = createExtractor(0, null, expr, parameterNames, parameterTypes, Boolean.class, contextType, classLoader, null, null); 106 if (ret instanceof CompositePredicate) { 107 return ((CompositePredicate) ret).normalize(); 108 } 109 return ExtractorUtil.wrap(ret); 110 } catch (ExtractorException e) { 111 throw e; 112 } catch (Exception e) { 113 throw new ExtractorException("Error creating Java extractor for code '"+code+"': "+e, e); 114 } 115 } 116 return null; 117 } 118 119 @SuppressWarnings("unchecked") 120 private <T, V, C> Extractor<T, V, C> createExtractor( 121 double initialCost, 122 TimeUnit costUnit, 123 Atom expr, 124 String[] parameterNames, 125 Class<T>[] parameterTypes, 126 Class<V> valueType, 127 Class<C> contextType, 128 ClassLoader classLoader, 129 Collection<Predicate<T,C>> collector, 130 Class<?> collectorType) throws RecognitionException, TokenStreamException, ClassNotFoundException, CompileException, InvocationTargetException { 131 132 Params<T, C> params = new Params<T, C>(expr, parameterNames, parameterTypes, contextType); 133 134 final Class<V> objectType = (Class<V>) Object.class; 135 final Class<V> numberType = (Class<V>) Number.class; 136 137 138 // Try to evaluate expression which doesn't depend on arguments. 139 if (params.topLevelNames.isEmpty()) { 140 try { 141 ExpressionEvaluator eval = new ExpressionEvaluator(expr.toString(), Object.class, new String[] {}, new Class[] {}); 142 Object val = eval.evaluate(new Object[] {}); 143 144 if (Boolean.TRUE.equals(val)) { 145 return compose((Predicate<T, C>) True.getInstance(), collector); 146 } 147 148 if (Boolean.FALSE.equals(val)) { 149 return compose((Predicate<T, C>) False.getInstance(), collector); 150 } 151 152 return new Constant<T, V, C>((V) val); 153 } catch (Exception e) { 154 // Nothing. 155 } 156 } 157 158 // Do parsing 159 160 if (expr instanceof ParenthesizedExpression) { 161 return createExtractor(initialCost, costUnit, ((ParenthesizedExpression) expr).value, parameterNames, parameterTypes, valueType, contextType, classLoader, collector, collectorType); 162 } 163 164 if (expr instanceof BinaryOperation) { 165 BinaryOperation bo = (BinaryOperation) expr; 166 // * 167 if ("*".equals(bo.op)) { 168 // TODO - in the future handle multiplication chains like 2*3*10*55 in a single multiply. 169 Extractor<T, Number, C> leftExtractor = (Extractor<T, Number, C>) createExtractor(0, null, bo.lhs, parameterNames, parameterTypes, valueType, contextType, classLoader, null, null); 170 Extractor<T, Number, C> rightExtractor = (Extractor<T, Number, C>) createExtractor(0, null, bo.rhs, parameterNames, parameterTypes, valueType, contextType, classLoader, null, null); 171 return (Extractor<T, V, C>) new Multiply<T, C>(initialCost, costUnit, leftExtractor, rightExtractor); 172 } 173 // / 174 if ("/".equals(bo.op)) { 175 // TODO - in the future handle multiplication chains like 2*3*10*55 in a single multiply. 176 Extractor<T, Number, C> leftExtractor = (Extractor<T, Number, C>) createExtractor(0, null, bo.lhs, parameterNames, parameterTypes, valueType, contextType, classLoader, null, null); 177 Extractor<T, Number, C> rightExtractor = (Extractor<T, Number, C>) createExtractor(0, null, bo.rhs, parameterNames, parameterTypes, valueType, contextType, classLoader, null, null); 178 return (Extractor<T, V, C>) new Divide<T, C>(initialCost, costUnit, leftExtractor, rightExtractor); 179 } 180 // * 181 if ("-".equals(bo.op)) { 182 // TODO - in the future handle multiplication chains like 2*3*10*55 in a single multiply. 183 Extractor<T, Number, C> leftExtractor = (Extractor<T, Number, C>) createExtractor(0, null, bo.lhs, parameterNames, parameterTypes, valueType, contextType, classLoader, null, null); 184 Extractor<T, Number, C> rightExtractor = (Extractor<T, Number, C>) createExtractor(0, null, bo.rhs, parameterNames, parameterTypes, valueType, contextType, classLoader, null, null); 185 return (Extractor<T, V, C>) new Subtract<T, C>(initialCost, costUnit, leftExtractor, rightExtractor); 186 } 187 // + 188 if ("+".equals(bo.op)) { 189 // TODO - in the future handle addition chains like 2+3+10+55 in a single add. 190 // Do only if both left and right 191 Extractor<T, Object, C> leftExtractor = (Extractor<T, Object, C>) createExtractor(0, null, bo.lhs, parameterNames, parameterTypes, valueType, contextType, classLoader, null, null); 192 Extractor<T, Object, C> rightExtractor = (Extractor<T, Object, C>) createExtractor(0, null, bo.rhs, parameterNames, parameterTypes, valueType, contextType, classLoader, null, null); 193 return (Extractor<T, V, C>) new Add<T, C>(initialCost, costUnit, leftExtractor, rightExtractor); 194 } 195 // < 196 if (LOGICAL_OR.equals(bo.op)) { 197 Collection<Predicate<T,C>> ret = Or.class.equals(collectorType) ? collector : new ArrayList<Predicate<T,C>>(); 198 createExtractor(0, null, bo.lhs, parameterNames, parameterTypes, valueType, contextType, classLoader, ret, Or.class); 199 createExtractor(0, null, bo.rhs, parameterNames, parameterTypes, valueType, contextType, classLoader, ret, Or.class); 200 return (Extractor<T, V, C>) new Or<T,C>(initialCost, costUnit, ret); 201 } 202 203 if (LOGICAL_AND.equals(bo.op)) { 204 Collection<Predicate<T,C>> ret = And.class.equals(collectorType) ? collector : new ArrayList<Predicate<T,C>>(); 205 createExtractor(0, null, bo.lhs, parameterNames, parameterTypes, valueType, contextType, classLoader, ret, And.class); 206 createExtractor(0, null, bo.rhs, parameterNames, parameterTypes, valueType, contextType, classLoader, ret, And.class); 207 return (Extractor<T, V, C>) new And<T,C>(initialCost, costUnit, ret); 208 } 209 210 if ("<".equals(bo.op)) { 211 Extractor<T, V, C> leftExtractor = createExtractor(0, null, bo.lhs, parameterNames, parameterTypes, numberType, contextType, classLoader, null, null); 212 Extractor<T, V, C> rightExtractor = createExtractor(0, null, bo.rhs, parameterNames, parameterTypes, numberType, contextType, classLoader, null, null); 213 return compose(new LessThan(initialCost, costUnit, leftExtractor, rightExtractor), collector); 214 } 215 // > 216 if (">".equals(bo.op)) { 217 Extractor<T, V, C> leftExtractor = createExtractor(0, null, bo.lhs, parameterNames, parameterTypes, numberType, contextType, classLoader, null, null); 218 Extractor<T, V, C> rightExtractor = createExtractor(0, null, bo.rhs, parameterNames, parameterTypes, numberType, contextType, classLoader, null, null); 219 return compose(new GreaterThan(initialCost, costUnit, leftExtractor, rightExtractor), collector); 220 } 221 // <= 222 if ("<=".equals(bo.op)) { 223 Extractor<T, V, C> leftExtractor = createExtractor(0, null, bo.lhs, parameterNames, parameterTypes, numberType, contextType, classLoader, null, null); 224 Extractor<T, V, C> rightExtractor = createExtractor(0, null, bo.rhs, parameterNames, parameterTypes, numberType, contextType, classLoader, null, null); 225 return compose(new LessEqual(initialCost, costUnit, leftExtractor, rightExtractor), collector); 226 } 227 // >= 228 if (">=".equals(bo.op)) { 229 Extractor<T, V, C> leftExtractor = createExtractor(0, null, bo.lhs, parameterNames, parameterTypes, numberType, contextType, classLoader, null, null); 230 Extractor<T, V, C> rightExtractor = createExtractor(0, null, bo.rhs, parameterNames, parameterTypes, numberType, contextType, classLoader, null, null); 231 return compose(new GreaterEqual(initialCost, costUnit, leftExtractor, rightExtractor), collector); 232 } 233 // == 234 if ("==".equals(bo.op)) { 235 Extractor<T, V, C> leftExtractor = createExtractor(0, null, bo.lhs, parameterNames, parameterTypes, objectType, contextType, classLoader, null, null); 236 Extractor<T, V, C> rightExtractor = createExtractor(0, null, bo.rhs, parameterNames, parameterTypes, objectType, contextType, classLoader, null, null); 237 return compose(new Equal(initialCost, costUnit, leftExtractor, rightExtractor, true), collector); 238 } 239 // != 240 if ("!=".equals(bo.op)) { 241 Extractor<T, V, C> leftExtractor = createExtractor(0, null, bo.lhs, parameterNames, parameterTypes, objectType, contextType, classLoader, null, null); 242 Extractor<T, V, C> rightExtractor = createExtractor(0, null, bo.rhs, parameterNames, parameterTypes, objectType, contextType, classLoader, null, null); 243 return compose(new NotEqual(initialCost, costUnit, leftExtractor, rightExtractor, true), collector); 244 } 245 } else if (expr instanceof Instanceof) { 246 ClassLoader cl = classLoader==null ? getClass().getClassLoader() : classLoader; 247 Type rhs = ((Instanceof) expr).rhs; 248 Rvalue lhs = ((Instanceof) expr).lhs; 249 Extractor<T, V, C> extractor = createExtractor(initialCost, costUnit, lhs, parameterNames, parameterTypes, objectType, contextType, classLoader, null, null); 250 return compose(new InstanceOfPredicate(extractor, cl.loadClass(rhs.toString())), collector); 251 } else if (expr instanceof MethodInvocation) { 252 MethodInvocation mi = (MethodInvocation) expr; 253 if (mi.methodName.equals("equals") && mi.optionalTarget!=null) { 254 Extractor<T, V, C> le = createExtractor(0, null, mi.optionalTarget, parameterNames, parameterTypes, objectType, contextType, classLoader, null, null); 255 Extractor<T, V, C> re = createExtractor(0, null, mi.arguments[0], parameterNames, parameterTypes, objectType, contextType, classLoader, null, null); 256 return compose(new Equal<T, V, C>(initialCost, costUnit, le, re, false), collector); 257 } else if (COMMUTATIVE_OR_MACRO.equals(mi.methodName)) { 258 Collection<Predicate<T,C>> ret = CommutativeOr.class.equals(collectorType) ? collector : new ArrayList<Predicate<T,C>>(); 259 for (Java.Rvalue arg: mi.arguments) { 260 createExtractor(0, null, arg, parameterNames, parameterTypes, valueType, contextType, classLoader, ret, CommutativeOr.class); 261 } 262 return (Extractor<T, V, C>) new CommutativeOr<T,C>(initialCost, costUnit, ret); 263 } else if (COMMUTATIVE_AND_MACRO.equals(mi.methodName)) { 264 Collection<Predicate<T,C>> ret = CommutativeAnd.class.equals(collectorType) ? collector : new ArrayList<Predicate<T,C>>(); 265 for (Java.Rvalue arg: mi.arguments) { 266 createExtractor(0, null, arg, parameterNames, parameterTypes, valueType, contextType, classLoader, ret, CommutativeAnd.class); 267 } 268 return (Extractor<T, V, C>) new CommutativeAnd<T,C>(initialCost, costUnit, ret); 269 } else if (COST_MACRO.equals(mi.methodName)) { 270 String costValue = mi.arguments[0].toString(); 271 for (TimeUnit cu: TimeUnit.values()) { 272 if (cu.name().equals(costValue)) { 273 return createExtractor(0, cu, mi.arguments[1], parameterNames, parameterTypes, valueType, contextType, classLoader, collector, collectorType); 274 } 275 } 276 ExpressionEvaluator eval = new ExpressionEvaluator(costValue, Object.class, new String[] {}, new Class[] {}); 277 Object val = eval.evaluate(new Object[] {}); 278 return createExtractor(((Number) val).doubleValue(), null, mi.arguments[1], parameterNames, parameterTypes, valueType, contextType, classLoader, collector, collectorType); 279 } 280 } else if (expr instanceof UnaryOperation) { 281 UnaryOperation uo = (UnaryOperation) expr; 282 if ("!".equals(uo.operator)) { 283 Extractor<T, V, C> extractor = createExtractor(initialCost, costUnit, uo.operand, parameterNames, parameterTypes, valueType, contextType, classLoader, null, null); 284 return compose(new Not((Predicate) extractor), collector); 285 } 286 } else if (expr instanceof Literal) { 287 Literal lit = (Literal) expr; 288 if (Boolean.TRUE.equals(lit.value)) { 289 return compose((Predicate<T, C>) True.getInstance(), collector); 290 } 291 292 if (Boolean.FALSE.equals(lit.value)) { 293 return compose((Predicate<T, C>) False.getInstance(), collector); 294 } 295 296 return new Constant<T, V, C>((V) lit.value); 297 } 298 299 if (Boolean.class.equals(valueType)) { 300 return compose(new JavaPredicate<T, C>(initialCost, costUnit, params, classLoader), collector); 301 } 302 return new JavaExtractor<T, V, C>(initialCost, costUnit, params, valueType, classLoader); 303 } 304 305 @SuppressWarnings("unchecked") 306 private <T, V, C> Extractor<T, V, C> compose(Predicate<T, C> ret, Collection<Predicate<T,C>> collector) { 307 if (collector==null) { 308 return (Extractor<T, V, C>) ret; 309 } 310 collector.add(ret); 311 return null; 312 } 313 314// private <T, V, C> Predicate<T, C> createPredicate( 315// Atom expr, 316// String[] parameterNames, 317// Class<T>[] parameterTypes, 318// Class<V> valueType, 319// Class<C> contextType, 320// ClassLoader classLoader, 321// Predicate<T, C> parentPredicate) throws RecognitionException, TokenStreamException, ClassNotFoundException { 322// 323// if (expr instanceof Instanceof) { 324// ClassLoader cl = classLoader==null ? getClass().getClassLoader() : classLoader; 325// Type rhs = ((Instanceof) expr).rhs; 326// Rvalue lhs = ((Instanceof) expr).lhs; 327// Extractor<T, V, C> extractor = createExtractor(lhs, parameterNames, parameterTypes, valueType, contextType, classLoader); 328// return new InstanceOfPredicate(extractor, cl.loadClass(rhs.toString())); 329// } else if (expr instanceof UnaryOperation) { 330// UnaryOperation uo = (UnaryOperation) expr; 331// if ("!".equals(uo.operator)) { 332// return new Not(createPredicate(uo.operand, parameterNames, parameterTypes, valueType, contextType, classLoader, null)); 333// } 334// } else if (expr instanceof MethodInvocation) { 335// MethodInvocation mi = (MethodInvocation) expr; 336// if (mi.methodName.equals("equals") && mi.optionalTarget!=null) { 337// Extractor<T, V, C> le = createExtractor(mi.optionalTarget, parameterNames, parameterTypes, valueType, contextType, classLoader); 338// Extractor<T, V, C> re = createExtractor(mi.arguments[0], parameterNames, parameterTypes, valueType, contextType, classLoader); 339// return new Equal<T, V, C>(le, re, false); 340// } else if (COMMUTATIVE_OR_MACROS.equals(mi.methodName)) { 341// CommutativeOr<T, C> ret = parentPredicate instanceof CommutativeOr ? (CommutativeOr<T, C>) parentPredicate : new CommutativeOr<T, C>(); 342// for (Java.Rvalue arg: mi.arguments) { 343// createPredicate(arg, parameterNames, parameterTypes, valueType, contextType, classLoader, ret); 344// } 345// return ret; 346// } else if (COMMUTATIVE_AND_MACROS.equals(mi.methodName)) { 347// CommutativeAnd<T, C> ret = parentPredicate instanceof CommutativeAnd ? (CommutativeAnd<T, C>) parentPredicate : new CommutativeAnd<T, C>(); 348// for (Java.Rvalue arg: mi.arguments) { 349// createPredicate(arg, parameterNames, parameterTypes, valueType, contextType, classLoader, ret); 350// } 351// return ret; 352// } 353// } else if (expr instanceof BinaryOperation) { 354// BinaryOperation bo = (BinaryOperation) expr; 355// } 356// 357// // TODO parse, 358// Params<T, C> params = new Params<T, C>(expr, parameterNames, parameterTypes, contextType); 359// return new JavaPredicate<T, C>(params, classLoader); 360// } 361 362}