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