View Javadoc

1   /***************************************************************************************
2    * Copyright (c) Jonas Bonér, Alexandre Vasseur. All rights reserved.                 *
3    * http://aspectwerkz.codehaus.org                                                    *
4    * ---------------------------------------------------------------------------------- *
5    * The software in this package is published under the terms of the LGPL license      *
6    * a copy of which has been included with this distribution in the license.txt file.  *
7    **************************************************************************************/
8   package org.codehaus.aspectwerkz.expression;
9   
10  import org.codehaus.aspectwerkz.expression.ast.ASTAnd;
11  import org.codehaus.aspectwerkz.expression.ast.ASTAttribute;
12  import org.codehaus.aspectwerkz.expression.ast.ASTCall;
13  import org.codehaus.aspectwerkz.expression.ast.ASTCflow;
14  import org.codehaus.aspectwerkz.expression.ast.ASTCflowBelow;
15  import org.codehaus.aspectwerkz.expression.ast.ASTClassPattern;
16  import org.codehaus.aspectwerkz.expression.ast.ASTConstructorPattern;
17  import org.codehaus.aspectwerkz.expression.ast.ASTExecution;
18  import org.codehaus.aspectwerkz.expression.ast.ASTExpression;
19  import org.codehaus.aspectwerkz.expression.ast.ASTFieldPattern;
20  import org.codehaus.aspectwerkz.expression.ast.ASTGet;
21  import org.codehaus.aspectwerkz.expression.ast.ASTHandler;
22  import org.codehaus.aspectwerkz.expression.ast.ASTMethodPattern;
23  import org.codehaus.aspectwerkz.expression.ast.ASTModifier;
24  import org.codehaus.aspectwerkz.expression.ast.ASTNot;
25  import org.codehaus.aspectwerkz.expression.ast.ASTOr;
26  import org.codehaus.aspectwerkz.expression.ast.ASTParameter;
27  import org.codehaus.aspectwerkz.expression.ast.ASTPointcutReference;
28  import org.codehaus.aspectwerkz.expression.ast.ASTRoot;
29  import org.codehaus.aspectwerkz.expression.ast.ASTSet;
30  import org.codehaus.aspectwerkz.expression.ast.ASTStaticInitialization;
31  import org.codehaus.aspectwerkz.expression.ast.ASTWithin;
32  import org.codehaus.aspectwerkz.expression.ast.ASTWithinCode;
33  import org.codehaus.aspectwerkz.expression.ast.ExpressionParserVisitor;
34  import org.codehaus.aspectwerkz.expression.ast.Node;
35  import org.codehaus.aspectwerkz.expression.ast.SimpleNode;
36  import org.codehaus.aspectwerkz.expression.ast.ASTArgs;
37  import org.codehaus.aspectwerkz.expression.ast.ASTArgParameter;
38  import org.codehaus.aspectwerkz.expression.ast.ASTHasField;
39  import org.codehaus.aspectwerkz.expression.ast.ASTHasMethod;
40  import org.codehaus.aspectwerkz.expression.regexp.TypePattern;
41  import org.codehaus.aspectwerkz.reflect.ClassInfo;
42  import org.codehaus.aspectwerkz.reflect.ClassInfoHelper;
43  import org.codehaus.aspectwerkz.reflect.MemberInfo;
44  import org.codehaus.aspectwerkz.reflect.ReflectionInfo;
45  import org.codehaus.aspectwerkz.reflect.MethodInfo;
46  import org.codehaus.aspectwerkz.reflect.ConstructorInfo;
47  import org.codehaus.aspectwerkz.reflect.FieldInfo;
48  import org.codehaus.aspectwerkz.annotation.AnnotationInfo;
49  
50  import java.util.List;
51  import java.util.Iterator;
52  
53  /***
54   * The advised class filter visitor.
55   *
56   * Visit() methods are returning Boolean.TRUE/FALSE or null when decision cannot be taken.
57   * Using null allow composition of OR/AND with NOT in the best way.
58   *
59   * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
60   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
61   * @author Michael Nascimento
62   */
63  public class AdvisedClassFilterExpressionVisitor implements ExpressionParserVisitor {
64      protected final ASTRoot m_root;
65  
66      protected final String m_expression;
67  
68      protected final String m_namespace;
69  
70      /***
71       * Creates a new expression.
72       * 
73       * @param expression the expression as a string
74       * @param namespace the namespace
75       * @param root the AST root
76       */
77      public AdvisedClassFilterExpressionVisitor(final String expression, final String namespace, final ASTRoot root) {
78          m_root = root;
79          m_expression = expression;
80          m_namespace = namespace;
81      }
82  
83      /***
84       * Matches the expression context.
85       * 
86       * @param context
87       * @return
88       */
89      public boolean match(final ExpressionContext context) {
90          Boolean match = ((Boolean) visit(m_root, context));
91          // undeterministic is assumed to be "true" at this stage
92          // since it won't be composed anymore with a NOT (unless
93          // thru pointcut reference ie a new visitor)
94          return (match != null)?match.booleanValue():true;
95      }
96  
97      // ============ Boot strap =============
98      public Object visit(SimpleNode node, Object data) {
99          return node.jjtGetChild(0).jjtAccept(this, data);
100     }
101 
102     public Object visit(ASTRoot node, Object data) {
103         Node child = node.jjtGetChild(0);
104 
105 //        // if 'call' or 'handler' but no 'within*' then return true
106 //        if (child instanceof ASTCall || child instanceof ASTHandler) {
107 //            return Boolean.TRUE;
108 //        }
109         Boolean match = (Boolean) child.jjtAccept(this, data);
110         return match;
111     }
112 
113     public Object visit(ASTExpression node, Object data) {
114         Node child = node.jjtGetChild(0);
115 
116 //        // if 'call' or 'handler' but no 'within*' then return true
117 //        if (child instanceof ASTCall || child instanceof ASTHandler) {
118 //            return Boolean.TRUE;
119 //        }
120         Boolean match = (Boolean) child.jjtAccept(this, data);
121         return match;
122     }
123 
124     // ============ Logical operators =============
125     public Object visit(ASTOr node, Object data) {
126         Boolean matchL = (Boolean) node.jjtGetChild(0).jjtAccept(this, data);
127         Boolean matchR = (Boolean) node.jjtGetChild(1).jjtAccept(this, data);
128         Boolean intermediate = matchUndeterministicOr(matchL, matchR);
129         for (int i = 2; i < node.jjtGetNumChildren(); i++) {
130             Boolean matchNext = (Boolean) node.jjtGetChild(i).jjtAccept(this, data);
131             intermediate = matchUndeterministicOr(intermediate, matchNext);
132         }
133         return intermediate;
134     }
135 
136     public Object visit(ASTAnd node, Object data) {
137         // the AND and OR can have more than 2 nodes [see jjt grammar]
138         Boolean matchL = (Boolean) node.jjtGetChild(0).jjtAccept(this, data);
139         Boolean matchR = (Boolean) node.jjtGetChild(1).jjtAccept(this, data);
140         Boolean intermediate = matchUnderterministicAnd(matchL, matchR);
141         for (int i = 2; i < node.jjtGetNumChildren(); i++) {
142             Boolean matchNext = (Boolean) node.jjtGetChild(i).jjtAccept(this, data);
143             intermediate = matchUnderterministicAnd(intermediate, matchNext);
144         }
145         return intermediate;
146 
147 //        boolean hasCallOrHandlerPc = false;
148 //        boolean hasWithinPc = false;
149 //        boolean hasNotPc = false;
150 //        int notPcIndex = -1;
151 //
152 //        // handle 'call', with and without 'within*'
153 //        int nrOfChildren = node.jjtGetNumChildren();
154 //        for (int i = 0; i < nrOfChildren; i++) {
155 //            Node child = node.jjtGetChild(i);
156 //            if (child instanceof ASTNot) {
157 //                hasNotPc = true;
158 //                notPcIndex = i;
159 //            } else if (child instanceof ASTCall || child instanceof ASTHandler) {
160 //                hasCallOrHandlerPc = true;
161 //            } else if (child instanceof ASTWithin || child instanceof ASTWithinCode) {
162 //                hasWithinPc = true;
163 //            }
164 //        }
165 //
166 //        // check the child of the 'not' node
167 //        if (hasCallOrHandlerPc && hasNotPc) {
168 //            Node childChild = node.jjtGetChild(notPcIndex).jjtGetChild(0);
169 //            if (childChild instanceof ASTWithin || childChild instanceof ASTWithinCode) {
170 //                if (Boolean.TRUE.equals(childChild.jjtAccept(this, data))) {
171 //                    return Boolean.FALSE;
172 //                } else {
173 //                    return Boolean.TRUE;
174 //                }
175 //            }
176 //        } else if (hasCallOrHandlerPc && !hasWithinPc) {
177 //            return Boolean.TRUE;
178 //        }
179 //
180 //        // if not a 'call' or 'handler' pointcut
181 //        for (int i = 0; i < nrOfChildren; i++) {
182 //            Boolean match = (Boolean) node.jjtGetChild(i).jjtAccept(this, data);
183 //            if (match.equals(Boolean.TRUE)) {
184 //                return Boolean.TRUE;
185 //            }
186 //        }
187 //        return Boolean.FALSE;
188     }
189 
190     public Object visit(ASTNot node, Object data) {
191 //        // the NOT is not evaluated unless on within expressions
192 //        if (node.jjtGetChild(0) instanceof ASTWithin) {
193 //            Boolean match = (Boolean) node.jjtGetChild(0).jjtAccept(this, data);
194 //            if (match.equals(Boolean.TRUE)) {
195 //                return Boolean.FALSE;
196 //            } else {
197 //                return Boolean.TRUE;
198 //            }
199 //        }
200 //
201         Boolean match = (Boolean) node.jjtGetChild(0).jjtAccept(this, data);
202         if (match !=null) {
203             // regular NOT
204             if (match.equals(Boolean.TRUE)) {
205                 return Boolean.FALSE;
206             } else {
207                 return Boolean.TRUE;
208             }
209         } else {
210             return null;
211         }
212     }
213 
214     // ============ Pointcut types =============
215     public Object visit(ASTPointcutReference node, Object data) {
216         ExpressionContext context = (ExpressionContext) data;
217         ExpressionNamespace namespace = ExpressionNamespace.getNamespace(m_namespace);
218         AdvisedClassFilterExpressionVisitor expression = namespace.getAdvisedClassExpression(node.getName());
219         return new Boolean(expression.match(context));
220     }
221 
222     public Object visit(ASTExecution node, Object data) {
223         ExpressionContext context = (ExpressionContext) data;
224         // for execution evaluation, we always have the reflection info available
225         if (context.hasWithinPointcut() || context.hasExecutionPointcut()) {
226             return node.jjtGetChild(0).jjtAccept(this, context.getReflectionInfo());
227         } else {
228             return Boolean.FALSE;
229         }
230     }
231 
232     public Object visit(ASTCall node, Object data) {
233         ExpressionContext context = (ExpressionContext) data;
234         // for call evaluation, the reflection info may be null at the early matching phase
235         if (context.hasWithinPointcut() || context.hasCallPointcut()) {
236             if (context.hasReflectionInfo()) {
237                 return node.jjtGetChild(0).jjtAccept(this, context.getReflectionInfo());
238             } else {
239                 return null;
240             }
241         } else {
242             return Boolean.FALSE;
243         }
244     }
245 
246     public Object visit(ASTSet node, Object data) {
247         ExpressionContext context = (ExpressionContext) data;
248         // for set evaluation, the reflection info may be null at the early matching phase
249         // when we will allow for field interception within non declaring class
250         if (context.hasWithinPointcut() || context.hasSetPointcut()) {
251             if (context.hasReflectionInfo()) {
252                 return node.jjtGetChild(0).jjtAccept(this, context.getReflectionInfo());
253             } else {
254                 return null;
255             }
256         } else {
257             return Boolean.FALSE;
258         }
259     }
260 
261     public Object visit(ASTGet node, Object data) {
262         ExpressionContext context = (ExpressionContext) data;
263         // for get evaluation, the reflection info may be null at the early matching phase
264         // when we will allow for field interception within non declaring class
265         if (context.hasWithinPointcut() || context.hasGetPointcut()) {
266             if (context.hasReflectionInfo()) {
267                 return node.jjtGetChild(0).jjtAccept(this, context.getReflectionInfo());
268             } else {
269                 return null;
270             }
271         } else {
272             return Boolean.FALSE;
273         }
274     }
275 
276     public Object visit(ASTHandler node, Object data) {
277         //FIXME
278         return Boolean.TRUE;
279     }
280 
281     public Object visit(ASTStaticInitialization node, Object data) {
282         ExpressionContext context = (ExpressionContext) data;
283         if (context.hasWithinPointcut() || context.hasStaticInitializationPointcut()) {
284             return node.jjtGetChild(0).jjtAccept(this, context.getReflectionInfo());
285         } else {
286             return Boolean.FALSE;
287         }
288     }
289 
290     public Object visit(ASTWithin node, Object data) {
291         ExpressionContext context = (ExpressionContext) data;
292         ReflectionInfo reflectionInfo = context.getWithinReflectionInfo();
293         if (reflectionInfo instanceof MemberInfo) {
294             return node.jjtGetChild(0).jjtAccept(this, ((MemberInfo) reflectionInfo).getDeclaringType());
295         } else if (reflectionInfo instanceof ClassInfo) {
296             return node.jjtGetChild(0).jjtAccept(this, reflectionInfo);
297         } else {
298             return Boolean.FALSE;
299         }
300     }
301 
302     public Object visit(ASTWithinCode node, Object data) {
303         ExpressionContext context = (ExpressionContext) data;
304         return node.jjtGetChild(0).jjtAccept(this, context.getWithinReflectionInfo());
305     }
306 
307     public Object visit(ASTCflow node, Object data) {
308         return null;
309     }
310 
311     public Object visit(ASTCflowBelow node, Object data) {
312         return null;
313     }
314 
315     public Object visit(ASTArgs node, Object data) {
316         return null;
317     }
318 
319     public Object visit(ASTHasMethod node, Object data) {
320         ExpressionContext context = (ExpressionContext) data;
321         return node.jjtGetChild(0).jjtAccept(this, context.getWithinReflectionInfo());
322     }
323     
324     public Object visit(ASTHasField node, Object data) {
325         ExpressionContext context = (ExpressionContext) data;
326         return node.jjtGetChild(0).jjtAccept(this, context.getWithinReflectionInfo());
327     }
328  
329     // ============ Patterns =============
330     public Object visit(ASTClassPattern node, Object data) {
331         ClassInfo classInfo = (ClassInfo) data;
332         TypePattern typePattern = node.getTypePattern();
333         if (ClassInfoHelper.matchType(typePattern, classInfo) && visitAttributes(node, classInfo)) {
334             return Boolean.TRUE;
335         } else {
336             return Boolean.FALSE;
337         }
338     }
339 
340     public Object visit(ASTMethodPattern node, Object data) {
341         if (data instanceof ClassInfo) {
342             ClassInfo classInfo = (ClassInfo) data;
343             if (ClassInfoHelper.matchType(node.getDeclaringTypePattern(), classInfo)) {
344                 return Boolean.TRUE;
345             }
346             return Boolean.FALSE;
347         } else if (data instanceof MethodInfo) {
348             MethodInfo methodInfo = (MethodInfo) data;
349             if (ClassInfoHelper.matchType(node.getDeclaringTypePattern(), methodInfo.getDeclaringType())) {
350                 return null;// it might not match further because of modifiers etc
351             }
352             return Boolean.FALSE;
353         }
354         return Boolean.FALSE;
355     }
356 
357     public Object visit(ASTConstructorPattern node, Object data) {
358         if (data instanceof ClassInfo) {
359             ClassInfo classInfo = (ClassInfo) data;
360             if (ClassInfoHelper.matchType(node.getDeclaringTypePattern(), classInfo)) {
361                 // we matched but the actual match result may be false
362                 return Boolean.TRUE;
363             }
364         } else if (data instanceof ConstructorInfo) {
365             ConstructorInfo constructorInfo = (ConstructorInfo) data;
366             if (ClassInfoHelper.matchType(node.getDeclaringTypePattern(), constructorInfo.getDeclaringType())) {
367                 return null;// it might not match further because of modifiers etc
368             }
369             return Boolean.FALSE;
370         }
371         return Boolean.FALSE;
372     }
373 
374     public Object visit(ASTFieldPattern node, Object data) {
375         if (data instanceof ClassInfo) {
376             ClassInfo classInfo = (ClassInfo) data;
377             if (ClassInfoHelper.matchType(node.getDeclaringTypePattern(), classInfo)) {
378                 // we matched but the actual match result may be false
379                 return Boolean.TRUE;
380             }
381         } else if (data instanceof FieldInfo) {
382             FieldInfo fieldInfo = (FieldInfo) data;
383             if (ClassInfoHelper.matchType(node.getDeclaringTypePattern(), fieldInfo.getDeclaringType())) {
384                 return null;// it might not match further because of modifiers etc
385             }
386             return Boolean.FALSE;
387         }
388         return Boolean.FALSE;
389     }
390 
391     public Object visit(ASTParameter node, Object data) {
392         ClassInfo parameterType = (ClassInfo) data;
393         if (ClassInfoHelper.matchType(node.getDeclaringClassPattern(), parameterType)) {
394             // we matched but the actual match result may be false
395             return Boolean.TRUE;
396         } else {
397             return Boolean.FALSE;
398         }
399     }
400 
401     public Object visit(ASTArgParameter node, Object data) {
402         // never called
403         return Boolean.TRUE;
404     }
405 
406     public Object visit(ASTAttribute node, Object data) {
407         // called for class level annotation matching f.e. in a within context
408         List annotations = (List) data;
409         for (Iterator it = annotations.iterator(); it.hasNext();) {
410             AnnotationInfo annotation = (AnnotationInfo) it.next();
411             if (annotation.getName().equals(node.getName())) {
412                 return Boolean.TRUE;
413             }
414         }
415         return Boolean.FALSE;
416     }
417 
418     public Object visit(ASTModifier node, Object data) {
419         // ??
420         return null;
421     }
422 
423     /***
424      * Returns the string representation of the AST.
425      * 
426      * @return
427      */
428     public String toString() {
429         return m_expression;
430     }
431 
432     protected boolean visitAttributes(SimpleNode node, ReflectionInfo refInfo) {
433         int nrChildren = node.jjtGetNumChildren();
434         if (nrChildren != 0) {
435             for (int i = 0; i < nrChildren; i++) {
436                 Node child = node.jjtGetChild(i);
437                 if (child instanceof ASTAttribute) {
438                     List annotations = refInfo.getAnnotations();
439                     if (Boolean.TRUE.equals(child.jjtAccept(this, annotations))) {
440                         continue;
441                     } else {
442                         return false;
443                     }
444                 }
445             }
446         }
447         return true;
448     }
449 
450     private static Boolean matchUnderterministicAnd(Boolean lhs, Boolean rhs) {
451         if (lhs != null && rhs !=null) {
452             // regular AND
453             if (lhs.equals(Boolean.TRUE) && rhs.equals(Boolean.TRUE)) {
454                 return Boolean.TRUE;
455             } else {
456                 return Boolean.FALSE;
457             }
458         } else if (lhs != null && lhs.equals(Boolean.FALSE)) {
459             // one is undetermined and the other is false, so result is false
460             return Boolean.FALSE;
461         } else if (rhs != null && rhs.equals(Boolean.FALSE)) {
462             // one is undetermined and the other is false, so result is false
463             return Boolean.FALSE;
464         } else {
465             // both are undetermined, or one is true and the other undetermined
466             return null;
467         }
468     }
469 
470     private static Boolean matchUndeterministicOr(Boolean lhs, Boolean rhs) {
471         if (lhs != null && rhs !=null) {
472             // regular OR
473             if (lhs.equals(Boolean.TRUE) || rhs.equals(Boolean.TRUE)) {
474                 return Boolean.TRUE;
475             } else {
476                 return Boolean.FALSE;
477             }
478         } else {
479             // one or both is/are undetermined
480             // OR cannot be resolved
481             return null;
482         }
483     }
484 }