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.ASTParameter;
25  import org.codehaus.aspectwerkz.expression.ast.ASTPointcutReference;
26  import org.codehaus.aspectwerkz.expression.ast.ASTRoot;
27  import org.codehaus.aspectwerkz.expression.ast.ASTSet;
28  import org.codehaus.aspectwerkz.expression.ast.ASTStaticInitialization;
29  import org.codehaus.aspectwerkz.expression.ast.ASTWithin;
30  import org.codehaus.aspectwerkz.expression.ast.ASTWithinCode;
31  import org.codehaus.aspectwerkz.expression.ast.ExpressionParserVisitor;
32  import org.codehaus.aspectwerkz.expression.ast.Node;
33  import org.codehaus.aspectwerkz.expression.ast.SimpleNode;
34  import org.codehaus.aspectwerkz.expression.ast.ASTArgs;
35  import org.codehaus.aspectwerkz.expression.ast.ASTArgParameter;
36  import org.codehaus.aspectwerkz.expression.ast.ASTHasField;
37  import org.codehaus.aspectwerkz.expression.ast.ASTHasMethod;
38  import org.codehaus.aspectwerkz.expression.ast.ASTTarget;
39  import org.codehaus.aspectwerkz.expression.ast.ASTThis;
40  import org.codehaus.aspectwerkz.expression.ast.ASTNot;
41  import org.codehaus.aspectwerkz.expression.regexp.TypePattern;
42  import org.codehaus.aspectwerkz.reflect.ClassInfo;
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.reflect.ClassInfoHelper;
49  import org.codehaus.aspectwerkz.reflect.StaticInitializationInfo;
50  import org.codehaus.aspectwerkz.annotation.AnnotationInfo;
51  import org.codehaus.aspectwerkz.util.Util;
52  
53  import java.util.List;
54  import java.util.Iterator;
55  
56  /***
57   * The advised class filter visitor.
58   * <p/>
59   * Visit() methods are returning Boolean.TRUE/FALSE or null when decision cannot be taken.
60   * Using null allow composition of OR/AND with NOT in the best way.
61   *
62   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
63   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
64   * @author Michael Nascimento
65   * @author <a href="mailto:the_mindstorm@evolva.ro">Alex Popescu</a>
66   */
67  public class AdvisedClassFilterExpressionVisitor extends ExpressionVisitor implements ExpressionParserVisitor {
68  
69      /***
70       * Creates a new expression.
71       *
72       * @param expression the expression as a string
73       * @param namespace  the namespace
74       * @param root       the AST root
75       */
76      public AdvisedClassFilterExpressionVisitor(final ExpressionInfo expressionInfo, final String expression,
77                                                 final String namespace, final Node root) {
78          super(expressionInfo, expression, namespace, root);
79      }
80  
81      // ============ Boot strap =============
82      public Object visit(SimpleNode node, Object data) {
83          return node.jjtGetChild(0).jjtAccept(this, data);
84      }
85  
86      public Object visit(ASTRoot node, Object data) {
87          Node child = node.jjtGetChild(0);
88          Boolean match = (Boolean) child.jjtAccept(this, data);
89          return match;
90      }
91  
92      public Object visit(ASTExpression node, Object data) {
93          Node child = node.jjtGetChild(0);
94          Boolean match = (Boolean) child.jjtAccept(this, data);
95          return match;
96      }
97  
98      public Object visit(ASTNot node, Object data) {
99          return super.visit(node,data);
100     }
101 
102     // ============ Pointcut types =============
103     public Object visit(ASTPointcutReference node, Object data) {
104         ExpressionContext context = (ExpressionContext) data;
105         ExpressionNamespace namespace = ExpressionNamespace.getNamespace(m_namespace);
106         AdvisedClassFilterExpressionVisitor expression = namespace.getAdvisedClassExpression(node.getName());
107         return expression.matchUndeterministic(context);
108     }
109 
110     public Object visit(ASTExecution node, Object data) {
111         ExpressionContext context = (ExpressionContext) data;
112 
113         // only the last node might be the pattern, others are annotations
114         Node patternNode = node.jjtGetChild(node.jjtGetNumChildren() - 1);
115         boolean checkPattern = !(patternNode instanceof ASTAttribute);
116         
117         if(checkPattern) {
118             if (context.hasWithinPointcut() || context.hasExecutionPointcut()) {
119                 if (context.hasExecutionPointcut()) {
120                     // reflectionInfo was given
121                     return patternNode.jjtAccept(this, context.getReflectionInfo());
122                 } else {
123                     // only withinInfo was given
124                     return patternNode.jjtAccept(this, context.getWithinReflectionInfo()); 
125                 }
126             } else {
127                 return Boolean.FALSE;
128             }
129         } else {
130         	return null;
131         }
132     }
133 
134     public Object visit(ASTCall node, Object data) {
135         ExpressionContext context = (ExpressionContext) data;
136         
137         // only the last node might be the pattern, others are annotations
138         Node patternNode = node.jjtGetChild(node.jjtGetNumChildren() - 1);
139         boolean checkPattern = !(patternNode instanceof ASTAttribute);
140         
141         if(checkPattern) {
142             if (context.hasWithinPointcut() || context.hasCallPointcut()) {
143                 if (context.hasReflectionInfo()) {
144                         return patternNode.jjtAccept(this, context.getReflectionInfo());
145                 } else {
146                     return null;
147                 }
148             } else {
149                 return Boolean.FALSE;
150             }
151         } else {
152         	return null;
153         }
154     }
155 
156     public Object visit(ASTSet node, Object data) {
157         ExpressionContext context = (ExpressionContext) data;
158         
159         // only the last node might be the pattern, others are annotations
160         Node patternNode = node.jjtGetChild(node.jjtGetNumChildren() - 1);
161         boolean checkPattern = !(patternNode instanceof ASTAttribute);
162         
163         // for set evaluation, the reflection info may be null at the early matching phase
164         // when we will allow for field interception within non declaring class
165         if(checkPattern) {
166             if (context.hasWithinPointcut() || context.hasSetPointcut()) {
167                 if (context.hasReflectionInfo()) {
168                         return patternNode.jjtAccept(this, context.getReflectionInfo());
169                 } else {
170                     return null;
171                 }
172             } else {
173                 return Boolean.FALSE;
174             }
175         } else {
176         	return null;
177         }
178     }
179 
180     public Object visit(ASTGet node, Object data) {
181         ExpressionContext context = (ExpressionContext) data;
182 
183         // only the last node might be the pattern, others are annotations
184         Node patternNode = node.jjtGetChild(node.jjtGetNumChildren() - 1);
185         boolean checkPattern = !(patternNode instanceof ASTAttribute);
186         
187         // for get evaluation, the reflection info may be null at the early matching phase
188         // since we allow for field interception within non declaring class
189         if(checkPattern) {
190             if (context.hasWithinPointcut() || context.hasGetPointcut()) {
191                 if (context.hasReflectionInfo()) {
192                 	return patternNode.jjtAccept(this, context.getReflectionInfo());
193                 } else {
194                     return null;
195                 }
196             } else {
197                 return Boolean.FALSE;
198             }
199         } else {
200         	return null;
201         }
202     }
203 
204     public Object visit(ASTHandler node, Object data) {
205         return null;
206     }
207 
208     public Object visit(ASTStaticInitialization node, Object data) {
209         ExpressionContext context = (ExpressionContext) data;
210 
211         if (context.hasStaticInitializationPointcut() && context.hasWithinReflectionInfo()) {
212         	ReflectionInfo reflectInfo = context.getWithinReflectionInfo();
213             if (reflectInfo instanceof StaticInitializationInfo) {
214                 reflectInfo = ((StaticInitializationInfo) reflectInfo).getDeclaringType();
215             }
216         	if (reflectInfo instanceof ClassInfo) {
217                 // In an annotated subtree, the last child node represents the pattern
218                 Node patternNode = node.jjtGetChild(node.jjtGetNumChildren() - 1);
219                 if (!(patternNode instanceof ASTAttribute)) {
220                     Boolean matchPattern = (Boolean) patternNode.jjtAccept(this, reflectInfo);
221                     if (Boolean.FALSE.equals(matchPattern)) {
222                         return Boolean.FALSE;
223                     }
224                 }
225 
226                 // match on the annotations since the pattern was not there or matched
227             	boolean matchedAnnotations = visitAttributes(node, reflectInfo);
228             	if (!matchedAnnotations) {
229             		return Boolean.FALSE;
230             	} else {
231                     return null;//match but early phase
232                 }
233         	} else {
234         		return Boolean.FALSE;
235         	}
236         } else {
237             return Boolean.FALSE;
238         }
239     }
240 
241     public Object visit(ASTWithinCode node, Object data) {
242         ExpressionContext context = (ExpressionContext) data;
243         ReflectionInfo withinInfo = context.getWithinReflectionInfo();
244 
245         if (node.isStaticInitializer()) {
246             // transform the node in a within node to do an exact match on the within info
247             //TODO would be worth to do the fastNode creation only once somewhere
248             ASTWithin fastNode = new ASTWithin(0);
249             for (int i = 0; i < node.jjtGetChild(0).jjtGetNumChildren(); i++) {
250                   fastNode.jjtAddChild(node.jjtGetChild(0).jjtGetChild(i), i);
251             }
252             return super.visit(fastNode, data);
253         } else {
254 	        Node patternNode = node.jjtGetChild(node.jjtGetNumChildren() - 1);
255 	        boolean checkPattern = !(patternNode instanceof ASTAttribute);
256 	
257 	        if (checkPattern) {
258 	            if (withinInfo instanceof MemberInfo) {
259 	                return patternNode.jjtAccept(this, withinInfo);
260 	            } else if (withinInfo instanceof ClassInfo) {
261 	                Boolean matchDeclaringType = (Boolean) patternNode.jjtAccept(this, withinInfo);
262 	                if (Boolean.FALSE.equals(matchDeclaringType)) {
263 	                    return Boolean.FALSE;
264 	                } else {
265 	                    // may be we match now but not later?
266 	                    return null;
267 	                }
268 	            } else {
269 	                return null;
270 	            }
271 	        } else {
272 	            return null;
273 	        }
274         }
275     }
276 
277     public Object visit(ASTCflow node, Object data) {
278         return null;
279     }
280 
281     public Object visit(ASTCflowBelow node, Object data) {
282         return null;
283     }
284 
285     public Object visit(ASTArgs node, Object data) {
286         return null;
287     }
288 
289     public Object visit(ASTTarget node, Object data) {
290         return null;// is that good enough ? For execution PC we would optimize some
291     }
292 
293     public Object visit(ASTThis node, Object data) {
294         ExpressionContext context = (ExpressionContext) data;
295         if (context.hasWithinReflectionInfo()) {
296             ReflectionInfo withinInfo = context.getWithinReflectionInfo();
297             if (withinInfo instanceof MemberInfo) {
298                 return Util.booleanValueOf(
299                         ClassInfoHelper.instanceOf(
300                                 ((MemberInfo) withinInfo).getDeclaringType(),
301                                 node.getBoundedType(m_expressionInfo)
302                         )
303                 );
304             } else if (withinInfo instanceof ClassInfo) {
305                 return Util.booleanValueOf(
306                         ClassInfoHelper.instanceOf((ClassInfo) withinInfo, node.getBoundedType(m_expressionInfo))
307                 );
308             }
309         }
310         return Boolean.FALSE;
311     }
312 
313     // ============ Patterns =============
314 //    public Object visit(ASTClassPattern node, Object data) {
315 //        if(null == data) {
316 //            return null;
317 //        } else if( !(data instanceof ClassInfo) ) {
318 //        	return Boolean.FALSE;
319 //        }
320 //
321 //        ClassInfo classInfo = (ClassInfo) data;
322 //        if (node.getTypePattern().matchType(classInfo) && visitAttributes(node, classInfo)) {
323 //            return Boolean.TRUE;
324 //        } else {
325 //            return Boolean.FALSE;
326 //        }
327 //    }
328 
329     public Object visit(ASTMethodPattern node, Object data) {
330         if (data instanceof ClassInfo) {
331             ClassInfo classInfo = (ClassInfo) data;
332             if (node.getDeclaringTypePattern().matchType(classInfo)) {
333                 return Boolean.TRUE;
334             }
335             return Boolean.FALSE;
336         } else if (data instanceof MethodInfo) {
337             MethodInfo methodInfo = (MethodInfo) data;
338             if (node.getDeclaringTypePattern().matchType(methodInfo.getDeclaringType())) {
339                 return null;// it might not match further because of modifiers etc
340             }
341             return Boolean.FALSE;
342         }
343         return Boolean.FALSE;
344     }
345 
346     public Object visit(ASTConstructorPattern node, Object data) {
347         if (data instanceof ClassInfo) {
348             ClassInfo classInfo = (ClassInfo) data;
349             if (node.getDeclaringTypePattern().matchType(classInfo)) {
350                 // we matched but the actual match result may be false
351                 return Boolean.TRUE;
352             }
353         } else if (data instanceof ConstructorInfo) {
354             ConstructorInfo constructorInfo = (ConstructorInfo) data;
355             if (node.getDeclaringTypePattern().matchType(constructorInfo.getDeclaringType())) {
356                 return null;// it might not match further because of modifiers etc
357             }
358             return Boolean.FALSE;
359         }
360         return Boolean.FALSE;
361     }
362 
363     public Object visit(ASTFieldPattern node, Object data) {
364         if (data instanceof ClassInfo) {
365             ClassInfo classInfo = (ClassInfo) data;
366             if (node.getDeclaringTypePattern().matchType(classInfo)) {
367                 // we matched but the actual match result may be false
368                 return Boolean.TRUE;
369             }
370         } else if (data instanceof FieldInfo) {
371             FieldInfo fieldInfo = (FieldInfo) data;
372             if (node.getDeclaringTypePattern().matchType(fieldInfo.getDeclaringType())) {
373                 return null;// it might not match further because of modifiers etc
374             }
375             return Boolean.FALSE;
376         }
377         return Boolean.FALSE;
378     }
379 
380     public Object visit(ASTParameter node, Object data) {
381         ClassInfo parameterType = (ClassInfo) data;
382         if (node.getDeclaringClassPattern().matchType(parameterType)) {
383             // we matched but the actual match result may be false
384             return Boolean.TRUE;
385         } else {
386             return Boolean.FALSE;
387         }
388     }
389 
390     public Object visit(ASTArgParameter node, Object data) {
391         // never called
392         return Boolean.TRUE;
393     }
394 
395     public Object visit(ASTAttribute node, Object data) {
396         // called for class level annotation matching f.e. in a within context
397         boolean matchAnnotation = false;
398         List annotations = (List) data;
399         for (Iterator it = annotations.iterator(); it.hasNext();) {
400             AnnotationInfo annotation = (AnnotationInfo) it.next();
401             if (annotation.getName().equals(node.getName())) {
402                 matchAnnotation = true;
403             }
404         }
405         if (node.isNot()) {
406             return Util.booleanValueOf(!matchAnnotation);
407         } else {
408             return Util.booleanValueOf(matchAnnotation);
409         }
410     }
411 
412     public Object visit(ASTModifier node, Object data) {
413         // TODO
414         return null;
415     }
416 
417     /***
418      * Returns the string representation of the AST.
419      *
420      * @return
421      */
422     public String toString() {
423         return m_expression;
424     }
425 }