1 | package com.hammurapi.extract.java; |
2 | |
3 | import java.io.StringReader; |
4 | import java.lang.reflect.InvocationTargetException; |
5 | import java.util.ArrayList; |
6 | import java.util.Collection; |
7 | import java.util.concurrent.TimeUnit; |
8 | import java.util.concurrent.atomic.AtomicReference; |
9 | |
10 | import org.codehaus.commons.compiler.CompileException; |
11 | import org.codehaus.janino.ExpressionEvaluator; |
12 | import org.codehaus.janino.Java; |
13 | import org.codehaus.janino.Java.Atom; |
14 | import org.codehaus.janino.Java.BinaryOperation; |
15 | import org.codehaus.janino.Java.Instanceof; |
16 | import org.codehaus.janino.Java.Literal; |
17 | import org.codehaus.janino.Java.MethodInvocation; |
18 | import org.codehaus.janino.Java.ParenthesizedExpression; |
19 | import org.codehaus.janino.Java.Rvalue; |
20 | import org.codehaus.janino.Java.Type; |
21 | import org.codehaus.janino.Java.UnaryOperation; |
22 | import org.codehaus.janino.Parser; |
23 | import org.codehaus.janino.Scanner; |
24 | |
25 | import antlr.RecognitionException; |
26 | import antlr.TokenStreamException; |
27 | |
28 | import com.hammurapi.extract.Add; |
29 | import com.hammurapi.extract.And; |
30 | import com.hammurapi.extract.CommutativeAnd; |
31 | import com.hammurapi.extract.CommutativeOr; |
32 | import com.hammurapi.extract.CompositePredicate; |
33 | import com.hammurapi.extract.Constant; |
34 | import com.hammurapi.extract.Divide; |
35 | import com.hammurapi.extract.Equal; |
36 | import com.hammurapi.extract.Extractor; |
37 | import com.hammurapi.extract.ExtractorException; |
38 | import com.hammurapi.extract.ExtractorFactory; |
39 | import com.hammurapi.extract.ExtractorUtil; |
40 | import com.hammurapi.extract.False; |
41 | import com.hammurapi.extract.GreaterEqual; |
42 | import com.hammurapi.extract.GreaterThan; |
43 | import com.hammurapi.extract.InstanceOfPredicate; |
44 | import com.hammurapi.extract.LessEqual; |
45 | import com.hammurapi.extract.LessThan; |
46 | import com.hammurapi.extract.Multiply; |
47 | import com.hammurapi.extract.Not; |
48 | import com.hammurapi.extract.NotEqual; |
49 | import com.hammurapi.extract.Or; |
50 | import com.hammurapi.extract.Predicate; |
51 | import com.hammurapi.extract.Subtract; |
52 | import com.hammurapi.extract.True; |
53 | |
54 | public class JavaExtractorFactory implements ExtractorFactory { |
55 | |
56 | private static final String LOGICAL_OR = "||"; |
57 | private static final String LOGICAL_AND = "&&"; |
58 | private static final String COMMUTATIVE_AND_MACRO = "AND"; |
59 | private static final String COMMUTATIVE_OR_MACRO = "OR"; |
60 | private static final String COST_MACRO = "COST"; |
61 | |
62 | @SuppressWarnings("unchecked") |
63 | @Override |
64 | public <T, V, C> Extractor<T, V, C> createExtractor( |
65 | String language, |
66 | String code, |
67 | String[] parameterNames, |
68 | Class<T>[] parameterTypes, |
69 | Class<V> valueType, |
70 | Class<C> contextType, |
71 | ClassLoader classLoader) { |
72 | |
73 | if ("java".equals(language)) { |
74 | try { |
75 | Parser jnp = new Parser(new Scanner("Expression "+code, new StringReader(code))); |
76 | Atom expr = jnp.parseExpression(); |
77 | Extractor<T, V, C> ret = createExtractor(0, null, expr, parameterNames, parameterTypes, valueType, contextType, classLoader, null, null); |
78 | if (ret instanceof CompositePredicate) { |
79 | return ((CompositePredicate) ret).normalize(); |
80 | } |
81 | return ret; |
82 | } catch (ExtractorException e) { |
83 | throw e; |
84 | } catch (Exception e) { |
85 | throw new ExtractorException("Error creating Java extractor for code '"+code+"': "+e, e); |
86 | } |
87 | } |
88 | return null; |
89 | } |
90 | |
91 | @SuppressWarnings("unchecked") |
92 | @Override |
93 | public <T, C> Predicate<T, C> createPredicate( |
94 | String language, |
95 | String code, |
96 | String[] parameterNames, |
97 | Class<T>[] parameterTypes, |
98 | Class<C> contextType, |
99 | 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 | } |