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}