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