]> source.dussan.org Git - aspectj.git/blob
313411f7ba742e5cae9c2acc5d616e2549141500
[aspectj.git] /
1 /* *******************************************************************
2  * Copyright (c) 2005 IBM Corporation Ltd
3  * All rights reserved. 
4  * This program and the accompanying materials are made available 
5  * under the terms of the Eclipse Public License v1.0 
6  * which accompanies this distribution and is available at 
7  * http://www.eclipse.org/legal/epl-v10.html 
8  *  
9  * Contributors: 
10  *     Adrian Colyer  initial implementation 
11  * ******************************************************************/
12 package org.aspectj.ajdt.internal.compiler.ast;
13
14 import java.lang.reflect.Modifier;
15 import java.util.Stack;
16 import java.util.ArrayList;
17 import java.util.List;
18
19 import org.aspectj.ajdt.internal.compiler.lookup.EclipseFactory;
20 import org.aspectj.ajdt.internal.compiler.lookup.EclipseScope;
21 import org.aspectj.ajdt.internal.core.builder.EclipseSourceContext;
22 import org.aspectj.bridge.context.CompilationAndWeavingContext;
23 import org.aspectj.bridge.context.ContextToken;
24 import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
25 import org.aspectj.org.eclipse.jdt.internal.compiler.ASTVisitor;
26 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation;
27 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Argument;
28 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
29 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
30 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
31 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.NormalAnnotation;
32 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
33 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
34 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.StringLiteral;
35 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
36 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeReference;
37 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.BlockScope;
38 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ClassScope;
39 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
40 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
41 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
42 import org.aspectj.weaver.AdviceKind;
43 import org.aspectj.weaver.AjAttribute;
44 import org.aspectj.weaver.ISourceContext;
45 import org.aspectj.weaver.ResolvedPointcutDefinition;
46 import org.aspectj.weaver.UnresolvedType;
47 import org.aspectj.weaver.patterns.AbstractPatternNodeVisitor;
48 import org.aspectj.weaver.patterns.FormalBinding;
49 import org.aspectj.weaver.patterns.IfPointcut;
50 import org.aspectj.weaver.patterns.ParserException;
51 import org.aspectj.weaver.patterns.PatternParser;
52 import org.aspectj.weaver.patterns.Pointcut;
53
54 public class ValidateAtAspectJAnnotationsVisitor extends ASTVisitor {
55
56         private static final char[] beforeAdviceSig = "Lorg/aspectj/lang/annotation/Before;".toCharArray();
57         private static final char[] afterAdviceSig = "Lorg/aspectj/lang/annotation/After;".toCharArray();
58         private static final char[] afterReturningAdviceSig = "Lorg/aspectj/lang/annotation/AfterReturning;".toCharArray();
59         private static final char[] afterThrowingAdviceSig = "Lorg/aspectj/lang/annotation/AfterThrowing;".toCharArray();
60         private static final char[] aroundAdviceSig = "Lorg/aspectj/lang/annotation/Around;".toCharArray();
61         private static final char[] pointcutSig = "Lorg/aspectj/lang/annotation/Pointcut;".toCharArray();
62         private static final char[] aspectSig = "Lorg/aspectj/lang/annotation/Aspect;".toCharArray();
63         private static final char[] adviceNameSig = "Lorg/aspectj/lang/annotation/AdviceName;".toCharArray();
64         private static final char[] orgAspectJLangAnnotation = "org/aspectj/lang/annotation/".toCharArray();
65         private static final char[] voidType = "void".toCharArray();
66         private static final char[] booleanType = "boolean".toCharArray();
67         private static final char[] joinPoint = "Lorg/aspectj/lang/JoinPoint;".toCharArray();
68         private static final char[] joinPointStaticPart = "Lorg/aspectj/lang/JoinPoint$StaticPart;".toCharArray();
69         private static final char[] joinPointEnclosingStaticPart = "Lorg/aspectj/lang/JoinPoint$EnclosingStaticPart;".toCharArray();
70         private static final char[] proceedingJoinPoint = "Lorg/aspectj/lang/ProceedingJoinPoint;".toCharArray();
71         private static final char[][] adviceSigs = new char[][] {beforeAdviceSig,afterAdviceSig,afterReturningAdviceSig,afterThrowingAdviceSig,aroundAdviceSig};
72         
73         
74         private CompilationUnitDeclaration unit;
75         private Stack typeStack = new Stack();
76         private AspectJAnnotations ajAnnotations;
77         
78         public ValidateAtAspectJAnnotationsVisitor(CompilationUnitDeclaration unit) {
79                 this.unit = unit;
80         }
81         
82         public boolean visit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
83                 typeStack.push(localTypeDeclaration);
84                 ajAnnotations = new AspectJAnnotations(localTypeDeclaration.annotations);
85                 checkTypeDeclaration(localTypeDeclaration);
86                 return true;
87         }
88         
89         public void endVisit(TypeDeclaration localTypeDeclaration,BlockScope scope) {
90                 typeStack.pop();
91         }
92         
93         public boolean visit(TypeDeclaration memberTypeDeclaration,ClassScope scope) {
94                 typeStack.push(memberTypeDeclaration);
95                 ajAnnotations = new AspectJAnnotations(memberTypeDeclaration.annotations);
96                 checkTypeDeclaration(memberTypeDeclaration);
97                 return true;
98         }
99         
100         public void endVisit(TypeDeclaration memberTypeDeclaration,ClassScope scope) {
101                 typeStack.pop();
102         }
103         
104         public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
105                 typeStack.push(typeDeclaration);
106                 ajAnnotations = new AspectJAnnotations(typeDeclaration.annotations);
107                 checkTypeDeclaration(typeDeclaration);
108                 return true;
109         }
110         
111         public void endVisit(TypeDeclaration typeDeclaration,CompilationUnitScope scope) {
112                 typeStack.pop();
113         }
114         
115         private void checkTypeDeclaration(TypeDeclaration typeDecl) {
116                 ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.VALIDATING_AT_ASPECTJ_ANNOTATIONS, typeDecl.name);
117                 if (!(typeDecl instanceof AspectDeclaration)) {
118                         if (ajAnnotations.hasAspectAnnotation) {
119                                 validateAspectDeclaration(typeDecl);
120                         } else {
121                                 // check that class doesn't extend aspect
122                                 TypeReference parentRef = typeDecl.superclass;
123                                 if (parentRef != null) {
124                                         TypeBinding parentBinding = parentRef.resolvedType;
125                                         if (parentBinding instanceof SourceTypeBinding) {
126                                                 SourceTypeBinding parentSTB = (SourceTypeBinding) parentBinding;
127                                                 if (parentSTB.scope != null) {
128                                                         TypeDeclaration parentDecl = parentSTB.scope.referenceContext;
129                                                         if (isAspect(parentDecl)) {
130                                                                 typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart,typeDecl.sourceEnd,"a class cannot extend an aspect");
131                                                         }
132                                                 }
133                                         }
134                                 }
135                         }
136                 } else {
137                         // check that aspect doesn't have @Aspect annotation, we've already added on ourselves.
138                         if (ajAnnotations.hasMultipleAspectAnnotations) {
139                                         typeDecl.scope.problemReporter().signalError(
140                                                         typeDecl.sourceStart,
141                                                         typeDecl.sourceEnd,
142                                                         "aspects cannot have @Aspect annotation"
143                                                         );
144                         }
145                 }
146                 CompilationAndWeavingContext.leavingPhase(tok);
147         }
148         
149         public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) {
150                 if (methodDeclaration.hasErrors()) {
151                         return false;
152                 }
153                 ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.VALIDATING_AT_ASPECTJ_ANNOTATIONS, methodDeclaration.selector);
154                 ajAnnotations = new AspectJAnnotations(methodDeclaration.annotations);
155                 if (!methodDeclaration.getClass().equals(AjMethodDeclaration.class)) {
156                         // simply test for innapropriate use of annotations on code-style members
157                         if (methodDeclaration instanceof PointcutDeclaration) {
158                                 if (ajAnnotations.hasMultiplePointcutAnnotations ||
159                                     ajAnnotations.hasAdviceAnnotation ||
160                                     ajAnnotations.hasAspectAnnotation ||
161                                     ajAnnotations.hasAdviceNameAnnotation) {
162                                         methodDeclaration.scope.problemReporter().signalError(
163                                                         methodDeclaration.sourceStart,
164                                                         methodDeclaration.sourceEnd,
165                                                         "@AspectJ annotations cannot be declared on this aspect member");
166                                 }
167                         } else if (methodDeclaration instanceof AdviceDeclaration) {
168                                 if (ajAnnotations.hasMultipleAdviceAnnotations ||
169                                         ajAnnotations.hasAspectAnnotation ||
170                                         ajAnnotations.hasPointcutAnnotation) {
171                                         methodDeclaration.scope.problemReporter().signalError(
172                                                                                 methodDeclaration.sourceStart,
173                                                                                 methodDeclaration.sourceEnd,
174                                                                                 "Only @AdviceName AspectJ annotation allowed on advice");
175                                 }                               
176                         } else {
177                                 if (ajAnnotations.hasAspectJAnnotations()) {
178                                         methodDeclaration.scope.problemReporter().signalError(
179                                                         methodDeclaration.sourceStart,
180                                                         methodDeclaration.sourceEnd,
181                                                         "@AspectJ annotations cannot be declared on this aspect member");                                       
182                                 }
183                         }
184                         CompilationAndWeavingContext.leavingPhase(tok);
185                         return false;
186                 }
187                 
188                 if (ajAnnotations.hasAdviceAnnotation) {
189                         validateAdvice(methodDeclaration);
190                 } else if (ajAnnotations.hasPointcutAnnotation) {
191                         convertToPointcutDeclaration(methodDeclaration,scope);
192                 }
193                 CompilationAndWeavingContext.leavingPhase(tok);
194                 return false;
195         }
196         
197         private boolean isAspectJAnnotation(Annotation ann) {
198                 if (ann.resolvedType == null) return false;
199                 char[] sig = ann.resolvedType.signature();
200                 return CharOperation.contains(orgAspectJLangAnnotation, sig);
201         }
202         
203         private boolean insideAspect() {
204                 if (typeStack.empty()) return false;
205                 TypeDeclaration typeDecl = (TypeDeclaration) typeStack.peek();
206                 return isAspect(typeDecl);
207         }
208         
209         private boolean isAspect(TypeDeclaration typeDecl) {
210                 if (typeDecl instanceof AspectDeclaration) return true;
211                 return new AspectJAnnotations(typeDecl.annotations).hasAspectAnnotation;
212         }
213         
214         /**
215      * aspect must be public
216          * nested aspect must be static
217          * cannot extend a concrete aspect
218          * pointcut in perclause must be good.
219          */
220         private void validateAspectDeclaration(TypeDeclaration typeDecl) {
221                 if (typeStack.size() > 1) {
222                         // it's a nested aspect
223                         if (!Modifier.isStatic(typeDecl.modifiers)) {
224                                 typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart, typeDecl.sourceEnd, "inner aspects must be static");
225                                 return;
226                         }
227                 }
228                 
229                 SourceTypeBinding binding = typeDecl.binding;
230                 if (binding != null) {
231                         if (binding.isEnum() || binding.isInterface() || binding.isAnnotationType()) {
232                                 typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart,typeDecl.sourceEnd,"only classes can have an @Aspect annotation");
233                         }
234                 }
235
236         //FIXME AV - do we really want that
237 //        if (!Modifier.isPublic(typeDecl.modifiers)) {
238 //            typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart,typeDecl.sourceEnd,"@Aspect class must be public");
239 //        }
240
241         TypeReference parentRef = typeDecl.superclass;
242                 if (parentRef != null) {
243                         TypeBinding parentBinding = parentRef.resolvedType;
244                         if (parentBinding instanceof SourceTypeBinding) {
245                                 SourceTypeBinding parentSTB = (SourceTypeBinding) parentBinding;
246                                 if (parentSTB.scope!=null) { // scope is null if its a binarytypebinding (in AJ world, thats a subclass of SourceTypeBinding)
247                                         TypeDeclaration parentDecl = parentSTB.scope.referenceContext; 
248                                         if (isAspect(parentDecl) && !Modifier.isAbstract(parentDecl.modifiers)) {
249                                                 typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart,typeDecl.sourceEnd,"cannot extend a concrete aspect");
250                                         }                       
251                                 }
252                         }
253                 }
254
255                 Annotation aspectAnnotation = ajAnnotations.aspectAnnotation;
256
257                 int[] pcLoc = new int[2];
258                 String perClause = getStringLiteralFor("value", aspectAnnotation, pcLoc);
259                 AspectDeclaration aspectDecl = new AspectDeclaration(typeDecl.compilationResult);
260
261                 try {
262                         if (perClause != null && !perClause.equals("")) {
263                                 ISourceContext context = new EclipseSourceContext(unit.compilationResult,pcLoc[0]);
264                                 Pointcut pc = new PatternParser(perClause,context).maybeParsePerClause();
265                             FormalBinding[] bindings = new FormalBinding[0];
266                                 if (pc != null) pc.resolve(new EclipseScope(bindings,typeDecl.scope));
267                         }
268                 } catch(ParserException pEx) {
269                         typeDecl.scope.problemReporter().parseError(
270                                         pcLoc[0] + pEx.getLocation().getStart(),
271                                         pcLoc[0] + pEx.getLocation().getEnd() ,
272                                         -1, 
273                                         perClause.toCharArray(), 
274                                         perClause, 
275                                         new String[] {pEx.getMessage()});
276                 }
277         }
278         
279         /**
280          * 1) Advice must be public
281          * 2) Advice must have a void return type if not around advice
282          * 3) Advice must not have any other @AspectJ annotations
283          * 4) After throwing advice must declare the thrown formal
284          * 5) After returning advice must declare the returning formal
285          */
286         private void validateAdvice(MethodDeclaration methodDeclaration) {
287                 
288                 if (!insideAspect()) {
289                         methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart,
290                                           methodDeclaration.sourceEnd, 
291                                           "Advice must be declared inside an aspect type");                     
292                 }
293                 
294                 if (!Modifier.isPublic(methodDeclaration.modifiers)) {
295                         methodDeclaration.scope.problemReporter()
296                                 .signalError(methodDeclaration.sourceStart,methodDeclaration.sourceEnd,"advice must be public");
297                 }
298                                 
299                 if (ajAnnotations.hasMultipleAdviceAnnotations) {
300                         methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.duplicateAdviceAnnotation);
301                 }
302                 if (ajAnnotations.hasPointcutAnnotation) {
303                         methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.pointcutAnnotation);                      
304                 }
305                 if (ajAnnotations.hasAspectAnnotation) {
306                         methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.aspectAnnotation);                        
307                 }
308                 if (ajAnnotations.hasAdviceNameAnnotation) {
309                         methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.adviceNameAnnotation);                    
310                 }
311
312                 if (ajAnnotations.adviceKind != AdviceKind.Around) {
313                         ensureVoidReturnType(methodDeclaration);
314                 }         
315                 
316                 if (ajAnnotations.adviceKind == AdviceKind.AfterThrowing) {
317                         int[] throwingLocation = new int[2];
318                         String thrownFormal = getStringLiteralFor("throwing",ajAnnotations.adviceAnnotation,throwingLocation);
319                         if (thrownFormal != null) {
320                                 Argument[] arguments = methodDeclaration.arguments;
321                                 if (!toArgumentNames(methodDeclaration.arguments).contains(thrownFormal)) {
322                                                 methodDeclaration.scope.problemReporter()
323                                                         .signalError(methodDeclaration.sourceStart,methodDeclaration.sourceEnd,"throwing formal '" + thrownFormal + "' must be declared as a parameter in the advice signature");
324                                 }
325                         }
326                 }
327                 
328                 if (ajAnnotations.adviceKind == AdviceKind.AfterReturning) {
329                         int[] throwingLocation = new int[2];
330                         String returningFormal = getStringLiteralFor("returning",ajAnnotations.adviceAnnotation,throwingLocation);
331                         if (returningFormal != null) {
332                                 if (!toArgumentNames(methodDeclaration.arguments).contains(returningFormal)) {
333                                                 methodDeclaration.scope.problemReporter()
334                                                         .signalError(methodDeclaration.sourceStart,methodDeclaration.sourceEnd,"returning formal '" + returningFormal + "' must be declared as a parameter in the advice signature");
335                                 }
336                         }
337                 }
338
339                 resolveAndSetPointcut(methodDeclaration, ajAnnotations.adviceAnnotation);
340                 
341         }
342
343     /**
344      * Get the argument names as a string list
345      * @param arguments
346      * @return argument names (possibly empty)
347      */
348     private List toArgumentNames(Argument[] arguments) {
349         List names = new ArrayList();
350         if (arguments == null) {
351             return names;
352         } else {
353             for (int i = 0; i < arguments.length; i++) {
354                 names.add(new String(arguments[i].name));
355             }
356             return names;
357         }
358     }
359
360     private void resolveAndSetPointcut(MethodDeclaration methodDeclaration, Annotation adviceAnn) {
361                 int[] pcLocation = new int[2];
362                 String pointcutExpression = getStringLiteralFor("pointcut",adviceAnn,pcLocation);
363                 if (pointcutExpression == null) pointcutExpression = getStringLiteralFor("value",adviceAnn,pcLocation);
364                 try {
365                         ISourceContext context = new EclipseSourceContext(unit.compilationResult,pcLocation[0]);
366                         Pointcut pc = new PatternParser(pointcutExpression,context).parsePointcut();
367                         FormalBinding[] bindings = buildFormalAdviceBindingsFrom(methodDeclaration);
368                         pc.resolve(new EclipseScope(bindings,methodDeclaration.scope));
369                         EclipseFactory factory = EclipseFactory.fromScopeLookupEnvironment(methodDeclaration.scope);
370                         // now create a ResolvedPointcutDefinition,make an attribute out of it, and add it to the method
371                         UnresolvedType[] paramTypes = new UnresolvedType[bindings.length];
372                         for (int i = 0; i < paramTypes.length; i++) paramTypes[i] = bindings[i].getType();
373                         ResolvedPointcutDefinition resPcutDef = 
374                                 new ResolvedPointcutDefinition(
375                                                 factory.fromBinding(((TypeDeclaration)typeStack.peek()).binding),
376                                                 methodDeclaration.modifiers,
377                                                 "anonymous",
378                                                 paramTypes,
379                                                 pc
380                                 );
381                         AjAttribute attr = new AjAttribute.PointcutDeclarationAttribute(resPcutDef);
382                         ((AjMethodDeclaration)methodDeclaration).addAttribute(new EclipseAttributeAdapter(attr));
383                 } catch(ParserException pEx) {
384                         methodDeclaration.scope.problemReporter().parseError(
385                                         pcLocation[0] + pEx.getLocation().getStart(),
386                                         pcLocation[0] + pEx.getLocation().getEnd() ,
387                                         -1, 
388                                         pointcutExpression.toCharArray(), 
389                                         pointcutExpression, 
390                                         new String[] {pEx.getMessage()});
391                 }
392         }
393
394         private void ensureVoidReturnType(MethodDeclaration methodDeclaration) {
395                 boolean returnsVoid = true;
396                 if ((methodDeclaration.returnType instanceof SingleTypeReference)) {
397                         SingleTypeReference retType = (SingleTypeReference) methodDeclaration.returnType;
398                         if (!CharOperation.equals(voidType,retType.token)) {
399                                 returnsVoid = false;
400                         }
401                 } else {
402                         returnsVoid = false;
403                 }
404                 if (!returnsVoid) {
405                         methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
406                                                                                                                                                                           methodDeclaration.returnType.sourceEnd, 
407                                                                                                                                                                           "This advice must return void");
408                 }
409         }
410         
411         private FormalBinding[] buildFormalAdviceBindingsFrom(MethodDeclaration mDecl) {
412                 if (mDecl.arguments == null) return new FormalBinding[0];
413                 EclipseFactory factory = EclipseFactory.fromScopeLookupEnvironment(mDecl.scope);
414                 String extraArgName = maybeGetExtraArgName();
415                 if (extraArgName == null) extraArgName = "";
416                 FormalBinding[] ret = new FormalBinding[mDecl.arguments.length];
417                 for (int i = 0; i < mDecl.arguments.length; i++) {
418             Argument arg = mDecl.arguments[i];
419             String name = new String(arg.name);
420                         TypeBinding argTypeBinding = mDecl.binding.parameters[i];
421             UnresolvedType type = factory.fromBinding(argTypeBinding);
422                         if  (CharOperation.equals(joinPoint,argTypeBinding.signature()) ||
423                                  CharOperation.equals(joinPointStaticPart,argTypeBinding.signature()) ||
424                                  CharOperation.equals(joinPointEnclosingStaticPart,argTypeBinding.signature()) ||
425                                  CharOperation.equals(proceedingJoinPoint,argTypeBinding.signature()) ||
426                                  name.equals(extraArgName)) {
427                                 ret[i] = new FormalBinding.ImplicitFormalBinding(type,name,i);
428                         } else {
429                     ret[i] = new FormalBinding(type, name, i, arg.sourceStart, arg.sourceEnd, "unknown");                                               
430                         }
431                 }
432                 return ret;
433         }
434         
435         private String maybeGetExtraArgName() {
436                 String argName = null;
437                 if (ajAnnotations.adviceKind == AdviceKind.AfterReturning) {
438                         argName = getStringLiteralFor("returning",ajAnnotations.adviceAnnotation,new int[2]);
439                 } else if (ajAnnotations.adviceKind == AdviceKind.AfterThrowing) {
440                         argName = getStringLiteralFor("throwing",ajAnnotations.adviceAnnotation,new int[2]);
441                 }
442                 return argName;
443         }
444
445         private String getStringLiteralFor(String memberName, Annotation inAnnotation, int[] location) {
446                 if (inAnnotation instanceof SingleMemberAnnotation && memberName.equals("value")) {
447                         SingleMemberAnnotation sma = (SingleMemberAnnotation) inAnnotation;
448                         if (sma.memberValue instanceof StringLiteral) {
449                                 StringLiteral sv = (StringLiteral) sma.memberValue;
450                                 location[0] = sv.sourceStart;
451                                 location[1] = sv.sourceEnd;
452                                 return new String(sv.source());
453                         }
454                 }
455                 if (! (inAnnotation instanceof NormalAnnotation)) return null;
456                 NormalAnnotation ann = (NormalAnnotation) inAnnotation;
457                 MemberValuePair[] mvps = ann.memberValuePairs;
458                 if (mvps == null) return null;
459                 for (int i = 0; i < mvps.length; i++) {
460                         if (CharOperation.equals(memberName.toCharArray(),mvps[i].name)) {
461                                 if (mvps[i].value instanceof StringLiteral) {
462                                         StringLiteral sv = (StringLiteral) mvps[i].value;
463                                         location[0] = sv.sourceStart;
464                                         location[1] = sv.sourceEnd;
465                                         return new String(sv.source());
466                                 }
467                         }
468                 }
469                 return null;
470         }
471         
472         private void convertToPointcutDeclaration(MethodDeclaration methodDeclaration, ClassScope scope) {
473                 TypeDeclaration typeDecl = (TypeDeclaration) typeStack.peek();
474                 if (typeDecl.binding != null) {
475                         if (!typeDecl.binding.isClass()) {
476                                 methodDeclaration.scope.problemReporter()
477                                         .signalError(methodDeclaration.sourceStart,methodDeclaration.sourceEnd,"pointcuts can only be declared in a class or an aspect");
478                         }
479                 }
480                 
481                 if (methodDeclaration.thrownExceptions != null && methodDeclaration.thrownExceptions.length > 0) {
482                         methodDeclaration.scope.problemReporter()
483                                 .signalError(methodDeclaration.sourceStart,methodDeclaration.sourceEnd,"pointcuts cannot throw exceptions!");
484                 }
485                 
486                 PointcutDeclaration pcDecl = new PointcutDeclaration(unit.compilationResult);
487                 copyAllFields(methodDeclaration,pcDecl);
488
489                 if (ajAnnotations.hasAdviceAnnotation) {
490                         methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.adviceAnnotation);                        
491                 }
492                 if (ajAnnotations.hasAspectAnnotation) {
493                         methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.aspectAnnotation);                        
494                 }
495                 if (ajAnnotations.hasAdviceNameAnnotation) {
496                         methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.adviceNameAnnotation);                    
497                 }
498         
499                 boolean noValueSupplied=true;
500                 boolean containsIfPcd = false;
501                 int[] pcLocation = new int[2];
502                 String pointcutExpression = getStringLiteralFor("value",ajAnnotations.pointcutAnnotation,pcLocation);
503                 try {
504                         ISourceContext context = new EclipseSourceContext(unit.compilationResult,pcLocation[0]);
505             Pointcut pc = null;//abstract
506             if (pointcutExpression == null  || pointcutExpression.length() == 0) {
507                 noValueSupplied=true; // matches nothing pointcut
508             } else {
509                 noValueSupplied=false;
510                 pc = new PatternParser(pointcutExpression,context).parsePointcut();
511             }
512             pcDecl.pointcutDesignator = (pc==null)?null:new PointcutDesignator(pc);
513                         pcDecl.setGenerateSyntheticPointcutMethod();
514                         TypeDeclaration onType = (TypeDeclaration) typeStack.peek();
515                         pcDecl.postParse(onType);
516 //                      EclipseFactory factory = EclipseFactory.fromScopeLookupEnvironment(methodDeclaration.scope);
517 //                      int argsLength = methodDeclaration.arguments == null ? 0 : methodDeclaration.arguments.length;
518                         FormalBinding[] bindings = buildFormalAdviceBindingsFrom(methodDeclaration);
519 //                  FormalBinding[] bindings = new FormalBinding[argsLength];
520 //              for (int i = 0, len = bindings.length; i < len; i++) {
521 //                  Argument arg = methodDeclaration.arguments[i];
522 //                  String name = new String(arg.name);
523 //                  UnresolvedType type = factory.fromBinding(methodDeclaration.binding.parameters[i]);
524 //                  bindings[i] = new FormalBinding(type, name, i, arg.sourceStart, arg.sourceEnd, "unknown");
525 //              }
526                         swap(onType,methodDeclaration,pcDecl);
527                         if (pc != null) {
528                 // has an expression
529                 pc.resolve(new EclipseScope(bindings,methodDeclaration.scope));
530                 HasIfPCDVisitor ifFinder = new HasIfPCDVisitor();
531                 pc.traverse(ifFinder, null);
532                 containsIfPcd = ifFinder.containsIfPcd;
533             }
534         } catch(ParserException pEx) {
535                         methodDeclaration.scope.problemReporter().parseError(
536                                         pcLocation[0] + pEx.getLocation().getStart(),
537                                         pcLocation[0] + pEx.getLocation().getEnd() ,
538                                         -1, 
539                                         pointcutExpression.toCharArray(), 
540                                         pointcutExpression, 
541                                         new String[] {pEx.getMessage()});
542                 }
543                 
544                 boolean returnsVoid = false;
545                 boolean returnsBoolean = false;
546                 if ((methodDeclaration.returnType instanceof SingleTypeReference)) {
547                         SingleTypeReference retType = (SingleTypeReference) methodDeclaration.returnType;
548                         if (CharOperation.equals(voidType,retType.token)) returnsVoid = true;
549                         if (CharOperation.equals(booleanType,retType.token)) returnsBoolean = true;
550                 } 
551                 if (!returnsVoid && !containsIfPcd) {
552                         methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
553                                                                                                                                                                           methodDeclaration.returnType.sourceEnd, 
554                                                                                                                                                                           "Methods annotated with @Pointcut must return void unless the pointcut contains an if() expression");
555                 }
556                 if (!returnsBoolean && containsIfPcd) {
557                         methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
558                                                                                                                                                                           methodDeclaration.returnType.sourceEnd, 
559                                                                                                                                                                           "Methods annotated with @Pointcut must return boolean when the pointcut contains an if() expression");
560                 }
561                 
562                 if (methodDeclaration.statements != null && methodDeclaration.statements.length > 0 && !containsIfPcd) {
563                         methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
564                                           methodDeclaration.returnType.sourceEnd, 
565                                           "Pointcuts without an if() expression should have an empty method body");                     
566                 }
567
568         if (pcDecl.pointcutDesignator == null) {
569             if (Modifier.isAbstract(methodDeclaration.modifiers) 
570                 || noValueSupplied // this is a matches nothing pointcut
571                 //those 2 checks makes sense for aop.xml concretization but NOT for regular abstraction of pointcut
572                 //&& returnsVoid
573                 //&& (methodDeclaration.arguments == null || methodDeclaration.arguments.length == 0)) {
574                 ) {
575                 ;//fine
576             } else {
577                         methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
578                                   methodDeclaration.returnType.sourceEnd,
579                                   "Method annotated with @Pointcut() for abstract pointcut must be abstract");
580             }
581         } else if (Modifier.isAbstract(methodDeclaration.modifiers)) {
582             methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
583                       methodDeclaration.returnType.sourceEnd,
584                       "Method annotated with non abstract @Pointcut(\""+pointcutExpression+"\") is abstract");
585         }
586     }
587         
588         private void copyAllFields(MethodDeclaration from, MethodDeclaration to) {
589                 to.annotations = from.annotations;
590                 to.arguments = from.arguments;
591                 to.binding = from.binding;
592                 to.bits = from.bits;
593                 to.bodyEnd = from.bodyEnd;
594                 to.bodyStart = from.bodyStart;
595                 to.declarationSourceEnd = from.declarationSourceEnd;
596                 to.declarationSourceStart = from.declarationSourceStart;
597                 to.errorInSignature = from.errorInSignature;
598                 to.explicitDeclarations = from.explicitDeclarations;
599                 to.ignoreFurtherInvestigation = from.ignoreFurtherInvestigation;
600                 to.javadoc = from.javadoc;
601                 to.modifiers = from.modifiers;
602                 to.modifiersSourceStart = from.modifiersSourceStart;
603                 to.needFreeReturn = from.needFreeReturn;
604                 to.returnType = from.returnType;
605                 to.scope = from.scope;
606                 to.selector = from.selector;
607                 to.sourceEnd = from.sourceEnd;
608                 to.sourceStart = from.sourceStart;
609                 to.statements = from.statements;
610                 to.thrownExceptions = from.thrownExceptions;
611                 to.typeParameters = from.typeParameters;
612         }
613         
614         private void swap(TypeDeclaration inType, MethodDeclaration thisDeclaration, MethodDeclaration forThatDeclaration) {
615                 for (int i = 0; i < inType.methods.length; i++) {
616                         if (inType.methods[i]  == thisDeclaration) {
617                                 inType.methods[i] = forThatDeclaration;
618                                 break;
619                         }
620                 }
621         }
622         
623         private static class AspectJAnnotations {
624                 boolean hasAdviceAnnotation = false;
625                 boolean hasPointcutAnnotation = false;
626                 boolean hasAspectAnnotation = false;
627                 boolean hasAdviceNameAnnotation = false;
628                 
629                 boolean hasMultipleAdviceAnnotations = false;
630                 boolean hasMultiplePointcutAnnotations = false;
631                 boolean hasMultipleAspectAnnotations = false;
632                 
633                 AdviceKind adviceKind = null;
634                 Annotation adviceAnnotation = null;
635                 Annotation pointcutAnnotation = null;
636                 Annotation aspectAnnotation = null;
637                 Annotation adviceNameAnnotation = null;
638
639                 Annotation duplicateAdviceAnnotation = null;
640                 Annotation duplicatePointcutAnnotation = null;
641                 Annotation duplicateAspectAnnotation = null;
642                 
643                 public AspectJAnnotations(Annotation[] annotations) {
644                         if (annotations == null) return;
645                         for (int i = 0; i < annotations.length; i++) {
646                                 if (annotations[i].resolvedType == null) continue; // user messed up annotation declaration
647                                 char[] sig = annotations[i].resolvedType.signature();
648                                 if (CharOperation.equals(afterAdviceSig,sig)) {
649                                         adviceKind = AdviceKind.After;
650                                         addAdviceAnnotation(annotations[i]);
651                                 } else if (CharOperation.equals(afterReturningAdviceSig,sig)) {
652                                         adviceKind = AdviceKind.AfterReturning;
653                                         addAdviceAnnotation(annotations[i]);
654                                 } else if (CharOperation.equals(afterThrowingAdviceSig,sig)) {
655                                         adviceKind = AdviceKind.AfterThrowing;
656                                         addAdviceAnnotation(annotations[i]);
657                                 } else if (CharOperation.equals(beforeAdviceSig,sig)) {
658                                         adviceKind = AdviceKind.Before;
659                                         addAdviceAnnotation(annotations[i]);
660                                 } else if (CharOperation.equals(aroundAdviceSig,sig)) {
661                                         adviceKind = AdviceKind.Around;
662                                         addAdviceAnnotation(annotations[i]);
663                                 } else if (CharOperation.equals(adviceNameSig,sig)) {
664                                         hasAdviceNameAnnotation = true;
665                                         adviceNameAnnotation = annotations[i];
666                                 } else if (CharOperation.equals(aspectSig,sig)) {
667                                         if (hasAspectAnnotation) {
668                                                 hasMultipleAspectAnnotations = true;
669                                                 duplicateAspectAnnotation = annotations[i];
670                                         } else {
671                                                 hasAspectAnnotation = true;
672                                                 aspectAnnotation = annotations[i];
673                                         }
674                                 } else if (CharOperation.equals(pointcutSig,sig)) {
675                                         if (hasPointcutAnnotation) {
676                                                 hasMultiplePointcutAnnotations = true;
677                                                 duplicatePointcutAnnotation = annotations[i];
678                                         } else {
679                                                 hasPointcutAnnotation = true;
680                                                 pointcutAnnotation = annotations[i];
681                                         }
682                                         
683                                 }
684                         }
685                 }
686                 
687                 public boolean hasAspectJAnnotations() {
688                         return hasAdviceAnnotation || hasPointcutAnnotation || hasAdviceNameAnnotation || hasAspectAnnotation;
689                 }
690
691                 private void addAdviceAnnotation(Annotation annotation) {
692                         if (!hasAdviceAnnotation) {
693                                 hasAdviceAnnotation = true;
694                                 adviceAnnotation = annotation;
695                         } else {
696                                 hasMultipleAdviceAnnotations = true;
697                                 duplicateAdviceAnnotation = annotation;
698                         }
699                 }
700         }
701         
702         private static class HasIfPCDVisitor extends AbstractPatternNodeVisitor {
703                 public boolean containsIfPcd = false;
704                 
705                 public Object visit(IfPointcut node, Object data) {
706                         containsIfPcd = true;
707                         return data;
708                 }
709         }
710 }