1 /* *******************************************************************
2 * Copyright (c) 2005 IBM Corporation Ltd
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
10 * Adrian Colyer initial implementation
11 * ******************************************************************/
12 package org.aspectj.ajdt.internal.compiler.ast;
14 import java.lang.reflect.Modifier;
15 import java.util.Stack;
16 import java.util.ArrayList;
17 import java.util.List;
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.NameReference;
33 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.NormalAnnotation;
34 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
35 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
36 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.StringLiteral;
37 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
38 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeReference;
39 import org.aspectj.org.eclipse.jdt.internal.compiler.impl.Constant;
40 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.Binding;
41 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.BlockScope;
42 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ClassScope;
43 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
44 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
45 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.MethodScope;
46 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
47 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
48 import org.aspectj.weaver.AdviceKind;
49 import org.aspectj.weaver.AjAttribute;
50 import org.aspectj.weaver.BCException;
51 import org.aspectj.weaver.ISourceContext;
52 import org.aspectj.weaver.ResolvedPointcutDefinition;
53 import org.aspectj.weaver.UnresolvedType;
54 import org.aspectj.weaver.patterns.AbstractPatternNodeVisitor;
55 import org.aspectj.weaver.patterns.FormalBinding;
56 import org.aspectj.weaver.patterns.IfPointcut;
57 import org.aspectj.weaver.patterns.ParserException;
58 import org.aspectj.weaver.patterns.PatternParser;
59 import org.aspectj.weaver.patterns.Pointcut;
61 public class ValidateAtAspectJAnnotationsVisitor extends ASTVisitor {
63 private static final char[] beforeAdviceSig = "Lorg/aspectj/lang/annotation/Before;".toCharArray();
64 private static final char[] afterAdviceSig = "Lorg/aspectj/lang/annotation/After;".toCharArray();
65 private static final char[] afterReturningAdviceSig = "Lorg/aspectj/lang/annotation/AfterReturning;".toCharArray();
66 private static final char[] afterThrowingAdviceSig = "Lorg/aspectj/lang/annotation/AfterThrowing;".toCharArray();
67 private static final char[] aroundAdviceSig = "Lorg/aspectj/lang/annotation/Around;".toCharArray();
68 private static final char[] pointcutSig = "Lorg/aspectj/lang/annotation/Pointcut;".toCharArray();
69 private static final char[] aspectSig = "Lorg/aspectj/lang/annotation/Aspect;".toCharArray();
70 private static final char[] declareParentsSig = "Lorg/aspectj/lang/annotation/DeclareParents;".toCharArray();
71 private static final char[] adviceNameSig = "Lorg/aspectj/lang/annotation/AdviceName;".toCharArray();
72 private static final char[] orgAspectJLangAnnotation = "org/aspectj/lang/annotation/".toCharArray();
73 private static final char[] voidType = "void".toCharArray();
74 private static final char[] booleanType = "boolean".toCharArray();
75 private static final char[] joinPoint = "Lorg/aspectj/lang/JoinPoint;".toCharArray();
76 private static final char[] joinPointStaticPart = "Lorg/aspectj/lang/JoinPoint$StaticPart;".toCharArray();
77 private static final char[] joinPointEnclosingStaticPart = "Lorg/aspectj/lang/JoinPoint$EnclosingStaticPart;".toCharArray();
78 private static final char[] proceedingJoinPoint = "Lorg/aspectj/lang/ProceedingJoinPoint;".toCharArray();
79 private static final char[][] adviceSigs = new char[][] {beforeAdviceSig,afterAdviceSig,afterReturningAdviceSig,afterThrowingAdviceSig,aroundAdviceSig};
82 private CompilationUnitDeclaration unit;
83 private Stack typeStack = new Stack();
84 private AspectJAnnotations ajAnnotations;
86 public ValidateAtAspectJAnnotationsVisitor(CompilationUnitDeclaration unit) {
90 public boolean visit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
91 typeStack.push(localTypeDeclaration);
92 ajAnnotations = new AspectJAnnotations(localTypeDeclaration.annotations);
93 checkTypeDeclaration(localTypeDeclaration);
97 public void endVisit(TypeDeclaration localTypeDeclaration,BlockScope scope) {
101 public boolean visit(TypeDeclaration memberTypeDeclaration,ClassScope scope) {
102 typeStack.push(memberTypeDeclaration);
103 ajAnnotations = new AspectJAnnotations(memberTypeDeclaration.annotations);
104 checkTypeDeclaration(memberTypeDeclaration);
108 public void endVisit(TypeDeclaration memberTypeDeclaration,ClassScope scope) {
112 public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
113 typeStack.push(typeDeclaration);
114 ajAnnotations = new AspectJAnnotations(typeDeclaration.annotations);
115 checkTypeDeclaration(typeDeclaration);
119 public void endVisit(TypeDeclaration typeDeclaration,CompilationUnitScope scope) {
123 private void checkTypeDeclaration(TypeDeclaration typeDecl) {
124 ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.VALIDATING_AT_ASPECTJ_ANNOTATIONS, typeDecl.name);
125 if (!(typeDecl instanceof AspectDeclaration)) {
126 if (ajAnnotations.hasAspectAnnotation) {
127 validateAspectDeclaration(typeDecl);
129 // check that class doesn't extend aspect
130 TypeReference parentRef = typeDecl.superclass;
131 if (parentRef != null) {
132 TypeBinding parentBinding = parentRef.resolvedType;
133 if (parentBinding instanceof SourceTypeBinding) {
134 SourceTypeBinding parentSTB = (SourceTypeBinding) parentBinding;
135 if (parentSTB.scope != null) {
136 TypeDeclaration parentDecl = parentSTB.scope.referenceContext;
137 if (isAspect(parentDecl)) {
138 typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart,typeDecl.sourceEnd,"a class cannot extend an aspect");
145 // check that aspect doesn't have @Aspect annotation, we've already added on ourselves.
146 if (ajAnnotations.hasMultipleAspectAnnotations) {
147 typeDecl.scope.problemReporter().signalError(
148 typeDecl.sourceStart,
150 "aspects cannot have @Aspect annotation"
154 CompilationAndWeavingContext.leavingPhase(tok);
157 public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) {
158 ajAnnotations = new AspectJAnnotations(fieldDeclaration.annotations);
159 if (ajAnnotations.hasDeclareParents && !insideAspect()) {
160 scope.problemReporter().signalError(fieldDeclaration.sourceStart,
161 fieldDeclaration.sourceEnd,
162 "DeclareParents can only be used inside an aspect type");
166 public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) {
167 if (methodDeclaration.hasErrors()) {
170 ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.VALIDATING_AT_ASPECTJ_ANNOTATIONS, methodDeclaration.selector);
171 ajAnnotations = new AspectJAnnotations(methodDeclaration.annotations);
172 if (!methodDeclaration.getClass().equals(AjMethodDeclaration.class)) {
173 // simply test for innapropriate use of annotations on code-style members
174 if (methodDeclaration instanceof PointcutDeclaration) {
175 if (ajAnnotations.hasMultiplePointcutAnnotations ||
176 ajAnnotations.hasAdviceAnnotation ||
177 ajAnnotations.hasAspectAnnotation ||
178 ajAnnotations.hasAdviceNameAnnotation) {
179 methodDeclaration.scope.problemReporter().signalError(
180 methodDeclaration.sourceStart,
181 methodDeclaration.sourceEnd,
182 "@AspectJ annotations cannot be declared on this aspect member");
184 } else if (methodDeclaration instanceof AdviceDeclaration) {
185 if (ajAnnotations.hasMultipleAdviceAnnotations ||
186 ajAnnotations.hasAspectAnnotation ||
187 ajAnnotations.hasPointcutAnnotation) {
188 methodDeclaration.scope.problemReporter().signalError(
189 methodDeclaration.sourceStart,
190 methodDeclaration.sourceEnd,
191 "Only @AdviceName AspectJ annotation allowed on advice");
194 if (ajAnnotations.hasAspectJAnnotations()) {
195 methodDeclaration.scope.problemReporter().signalError(
196 methodDeclaration.sourceStart,
197 methodDeclaration.sourceEnd,
198 "@AspectJ annotations cannot be declared on this aspect member");
201 CompilationAndWeavingContext.leavingPhase(tok);
205 if (ajAnnotations.hasAdviceAnnotation) {
206 validateAdvice(methodDeclaration);
207 } else if (ajAnnotations.hasPointcutAnnotation) {
208 convertToPointcutDeclaration(methodDeclaration,scope);
210 CompilationAndWeavingContext.leavingPhase(tok);
214 private boolean isAspectJAnnotation(Annotation ann) {
215 if (ann.resolvedType == null) return false;
216 char[] sig = ann.resolvedType.signature();
217 return CharOperation.contains(orgAspectJLangAnnotation, sig);
220 private boolean insideAspect() {
221 if (typeStack.empty()) return false;
222 TypeDeclaration typeDecl = (TypeDeclaration) typeStack.peek();
223 return isAspect(typeDecl);
226 private boolean isAspect(TypeDeclaration typeDecl) {
227 if (typeDecl instanceof AspectDeclaration) return true;
228 return new AspectJAnnotations(typeDecl.annotations).hasAspectAnnotation;
232 * aspect must be public
233 * nested aspect must be static
234 * cannot extend a concrete aspect
235 * pointcut in perclause must be good.
237 private void validateAspectDeclaration(TypeDeclaration typeDecl) {
238 if (typeStack.size() > 1) {
239 // it's a nested aspect
240 if (!Modifier.isStatic(typeDecl.modifiers)) {
241 typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart, typeDecl.sourceEnd, "inner aspects must be static");
246 SourceTypeBinding binding = typeDecl.binding;
247 if (binding != null) {
248 if (binding.isEnum() || binding.isInterface() || binding.isAnnotationType()) {
249 typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart,typeDecl.sourceEnd,"only classes can have an @Aspect annotation");
253 //FIXME AV - do we really want that
254 // if (!Modifier.isPublic(typeDecl.modifiers)) {
255 // typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart,typeDecl.sourceEnd,"@Aspect class must be public");
258 TypeReference parentRef = typeDecl.superclass;
259 if (parentRef != null) {
260 TypeBinding parentBinding = parentRef.resolvedType;
261 if (parentBinding instanceof SourceTypeBinding) {
262 SourceTypeBinding parentSTB = (SourceTypeBinding) parentBinding;
263 if (parentSTB.scope!=null) { // scope is null if its a binarytypebinding (in AJ world, thats a subclass of SourceTypeBinding)
264 TypeDeclaration parentDecl = parentSTB.scope.referenceContext;
265 if (isAspect(parentDecl) && !Modifier.isAbstract(parentDecl.modifiers)) {
266 typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart,typeDecl.sourceEnd,"cannot extend a concrete aspect");
272 Annotation aspectAnnotation = ajAnnotations.aspectAnnotation;
274 int[] pcLoc = new int[2];
275 String perClause = getStringLiteralFor("value", aspectAnnotation, pcLoc);
276 AspectDeclaration aspectDecl = new AspectDeclaration(typeDecl.compilationResult);
279 if (perClause != null && !perClause.equals("")) {
280 ISourceContext context = new EclipseSourceContext(unit.compilationResult,pcLoc[0]);
281 Pointcut pc = new PatternParser(perClause,context).maybeParsePerClause();
282 FormalBinding[] bindings = new FormalBinding[0];
283 if (pc != null) pc.resolve(new EclipseScope(bindings,typeDecl.scope));
285 } catch(ParserException pEx) {
286 typeDecl.scope.problemReporter().parseError(
287 pcLoc[0] + pEx.getLocation().getStart(),
288 pcLoc[0] + pEx.getLocation().getEnd() ,
290 perClause.toCharArray(),
292 new String[] {pEx.getMessage()});
297 * 1) Advice must be public
298 * 2) Advice must have a void return type if not around advice
299 * 3) Advice must not have any other @AspectJ annotations
300 * 4) After throwing advice must declare the thrown formal
301 * 5) After returning advice must declare the returning formal
302 * 6) Advice must not be static
304 private void validateAdvice(MethodDeclaration methodDeclaration) {
306 if (!insideAspect()) {
307 methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart,
308 methodDeclaration.sourceEnd,
309 "Advice must be declared inside an aspect type");
312 if (!Modifier.isPublic(methodDeclaration.modifiers)) {
313 methodDeclaration.scope.problemReporter()
314 .signalError(methodDeclaration.sourceStart,methodDeclaration.sourceEnd,"advice must be public");
317 if (Modifier.isStatic(methodDeclaration.modifiers)) {
318 methodDeclaration.scope.problemReporter()
319 .signalError(methodDeclaration.sourceStart,methodDeclaration.sourceEnd,"advice can not be declared static");
322 if (ajAnnotations.hasMultipleAdviceAnnotations) {
323 methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.duplicateAdviceAnnotation);
325 if (ajAnnotations.hasPointcutAnnotation) {
326 methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.pointcutAnnotation);
328 if (ajAnnotations.hasAspectAnnotation) {
329 methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.aspectAnnotation);
331 if (ajAnnotations.hasAdviceNameAnnotation) {
332 methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.adviceNameAnnotation);
335 if (ajAnnotations.adviceKind != AdviceKind.Around) {
336 ensureVoidReturnType(methodDeclaration);
339 if (ajAnnotations.adviceKind == AdviceKind.AfterThrowing) {
340 int[] throwingLocation = new int[2];
341 String thrownFormal = getStringLiteralFor("throwing",ajAnnotations.adviceAnnotation,throwingLocation);
342 if (thrownFormal != null) {
343 Argument[] arguments = methodDeclaration.arguments;
344 if (!toArgumentNames(methodDeclaration.arguments).contains(thrownFormal)) {
345 methodDeclaration.scope.problemReporter()
346 .signalError(methodDeclaration.sourceStart,methodDeclaration.sourceEnd,"throwing formal '" + thrownFormal + "' must be declared as a parameter in the advice signature");
351 if (ajAnnotations.adviceKind == AdviceKind.AfterReturning) {
352 int[] throwingLocation = new int[2];
353 String returningFormal = getStringLiteralFor("returning",ajAnnotations.adviceAnnotation,throwingLocation);
354 if (returningFormal != null) {
355 if (!toArgumentNames(methodDeclaration.arguments).contains(returningFormal)) {
356 methodDeclaration.scope.problemReporter()
357 .signalError(methodDeclaration.sourceStart,methodDeclaration.sourceEnd,"returning formal '" + returningFormal + "' must be declared as a parameter in the advice signature");
362 resolveAndSetPointcut(methodDeclaration, ajAnnotations.adviceAnnotation);
367 * Get the argument names as a string list
369 * @return argument names (possibly empty)
371 private List toArgumentNames(Argument[] arguments) {
372 List names = new ArrayList();
373 if (arguments == null) {
376 for (int i = 0; i < arguments.length; i++) {
377 names.add(new String(arguments[i].name));
383 private void resolveAndSetPointcut(MethodDeclaration methodDeclaration, Annotation adviceAnn) {
384 int[] pcLocation = new int[2];
385 String pointcutExpression = getStringLiteralFor("pointcut",adviceAnn,pcLocation);
386 if (pointcutExpression == null) pointcutExpression = getStringLiteralFor("value",adviceAnn,pcLocation);
388 ISourceContext context = new EclipseSourceContext(unit.compilationResult,pcLocation[0]);
389 Pointcut pc = new PatternParser(pointcutExpression,context).parsePointcut();
390 FormalBinding[] bindings = buildFormalAdviceBindingsFrom(methodDeclaration);
391 pc.resolve(new EclipseScope(bindings,methodDeclaration.scope));
392 EclipseFactory factory = EclipseFactory.fromScopeLookupEnvironment(methodDeclaration.scope);
393 // now create a ResolvedPointcutDefinition,make an attribute out of it, and add it to the method
394 UnresolvedType[] paramTypes = new UnresolvedType[bindings.length];
395 for (int i = 0; i < paramTypes.length; i++) paramTypes[i] = bindings[i].getType();
396 ResolvedPointcutDefinition resPcutDef =
397 new ResolvedPointcutDefinition(
398 factory.fromBinding(((TypeDeclaration)typeStack.peek()).binding),
399 methodDeclaration.modifiers,
404 AjAttribute attr = new AjAttribute.PointcutDeclarationAttribute(resPcutDef);
405 ((AjMethodDeclaration)methodDeclaration).addAttribute(new EclipseAttributeAdapter(attr));
406 } catch(ParserException pEx) {
407 methodDeclaration.scope.problemReporter().parseError(
408 pcLocation[0] + pEx.getLocation().getStart(),
409 pcLocation[0] + pEx.getLocation().getEnd() ,
411 pointcutExpression.toCharArray(),
413 new String[] {pEx.getMessage()});
417 private void ensureVoidReturnType(MethodDeclaration methodDeclaration) {
418 boolean returnsVoid = true;
419 if ((methodDeclaration.returnType instanceof SingleTypeReference)) {
420 SingleTypeReference retType = (SingleTypeReference) methodDeclaration.returnType;
421 if (!CharOperation.equals(voidType,retType.token)) {
428 methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
429 methodDeclaration.returnType.sourceEnd,
430 "This advice must return void");
434 private FormalBinding[] buildFormalAdviceBindingsFrom(MethodDeclaration mDecl) {
435 if (mDecl.arguments == null) return new FormalBinding[0];
436 if (mDecl.binding == null) return new FormalBinding[0];
437 EclipseFactory factory = EclipseFactory.fromScopeLookupEnvironment(mDecl.scope);
438 String extraArgName = maybeGetExtraArgName();
439 if (extraArgName == null) extraArgName = "";
440 FormalBinding[] ret = new FormalBinding[mDecl.arguments.length];
441 for (int i = 0; i < mDecl.arguments.length; i++) {
442 Argument arg = mDecl.arguments[i];
443 String name = new String(arg.name);
444 TypeBinding argTypeBinding = mDecl.binding.parameters[i];
445 UnresolvedType type = factory.fromBinding(argTypeBinding);
446 if (CharOperation.equals(joinPoint,argTypeBinding.signature()) ||
447 CharOperation.equals(joinPointStaticPart,argTypeBinding.signature()) ||
448 CharOperation.equals(joinPointEnclosingStaticPart,argTypeBinding.signature()) ||
449 CharOperation.equals(proceedingJoinPoint,argTypeBinding.signature()) ||
450 name.equals(extraArgName)) {
451 ret[i] = new FormalBinding.ImplicitFormalBinding(type,name,i);
453 ret[i] = new FormalBinding(type, name, i, arg.sourceStart, arg.sourceEnd, "unknown");
459 private String maybeGetExtraArgName() {
460 String argName = null;
461 if (ajAnnotations.adviceKind == AdviceKind.AfterReturning) {
462 argName = getStringLiteralFor("returning",ajAnnotations.adviceAnnotation,new int[2]);
463 } else if (ajAnnotations.adviceKind == AdviceKind.AfterThrowing) {
464 argName = getStringLiteralFor("throwing",ajAnnotations.adviceAnnotation,new int[2]);
469 private String getStringLiteralFor(String memberName, Annotation inAnnotation, int[] location) {
470 if (inAnnotation instanceof SingleMemberAnnotation && memberName.equals("value")) {
471 SingleMemberAnnotation sma = (SingleMemberAnnotation) inAnnotation;
472 if (sma.memberValue instanceof StringLiteral) {
473 StringLiteral sv = (StringLiteral) sma.memberValue;
474 location[0] = sv.sourceStart;
475 location[1] = sv.sourceEnd;
476 return new String(sv.source());
477 } else if (sma.memberValue instanceof NameReference && (((NameReference)sma.memberValue).binding instanceof FieldBinding)) {
478 Binding b = ((NameReference)sma.memberValue).binding;
479 Constant c = ((FieldBinding)b).constant;
480 return c.stringValue();
483 if (! (inAnnotation instanceof NormalAnnotation)) return null;
484 NormalAnnotation ann = (NormalAnnotation) inAnnotation;
485 MemberValuePair[] mvps = ann.memberValuePairs;
486 if (mvps == null) return null;
487 for (int i = 0; i < mvps.length; i++) {
488 if (CharOperation.equals(memberName.toCharArray(),mvps[i].name)) {
489 if (mvps[i].value instanceof StringLiteral) {
490 StringLiteral sv = (StringLiteral) mvps[i].value;
491 location[0] = sv.sourceStart;
492 location[1] = sv.sourceEnd;
493 return new String(sv.source());
500 private void convertToPointcutDeclaration(MethodDeclaration methodDeclaration, ClassScope scope) {
501 TypeDeclaration typeDecl = (TypeDeclaration) typeStack.peek();
502 if (typeDecl.binding != null) {
503 if (!typeDecl.binding.isClass()) {
504 methodDeclaration.scope.problemReporter()
505 .signalError(methodDeclaration.sourceStart,methodDeclaration.sourceEnd,"pointcuts can only be declared in a class or an aspect");
509 if (methodDeclaration.thrownExceptions != null && methodDeclaration.thrownExceptions.length > 0) {
510 methodDeclaration.scope.problemReporter()
511 .signalError(methodDeclaration.sourceStart,methodDeclaration.sourceEnd,"pointcuts cannot throw exceptions!");
514 PointcutDeclaration pcDecl = new PointcutDeclaration(unit.compilationResult);
515 copyAllFields(methodDeclaration,pcDecl);
517 if (ajAnnotations.hasAdviceAnnotation) {
518 methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.adviceAnnotation);
520 if (ajAnnotations.hasAspectAnnotation) {
521 methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.aspectAnnotation);
523 if (ajAnnotations.hasAdviceNameAnnotation) {
524 methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.adviceNameAnnotation);
527 boolean noValueSupplied=true;
528 boolean containsIfPcd = false;
529 int[] pcLocation = new int[2];
530 String pointcutExpression = getStringLiteralFor("value",ajAnnotations.pointcutAnnotation,pcLocation);
532 ISourceContext context = new EclipseSourceContext(unit.compilationResult,pcLocation[0]);
533 Pointcut pc = null;//abstract
534 if (pointcutExpression == null || pointcutExpression.length() == 0) {
535 noValueSupplied=true; // matches nothing pointcut
537 noValueSupplied=false;
538 pc = new PatternParser(pointcutExpression,context).parsePointcut();
540 pcDecl.pointcutDesignator = (pc==null)?null:new PointcutDesignator(pc);
541 pcDecl.setGenerateSyntheticPointcutMethod();
542 TypeDeclaration onType = (TypeDeclaration) typeStack.peek();
543 pcDecl.postParse(onType);
544 // EclipseFactory factory = EclipseFactory.fromScopeLookupEnvironment(methodDeclaration.scope);
545 // int argsLength = methodDeclaration.arguments == null ? 0 : methodDeclaration.arguments.length;
546 FormalBinding[] bindings = buildFormalAdviceBindingsFrom(methodDeclaration);
547 // FormalBinding[] bindings = new FormalBinding[argsLength];
548 // for (int i = 0, len = bindings.length; i < len; i++) {
549 // Argument arg = methodDeclaration.arguments[i];
550 // String name = new String(arg.name);
551 // UnresolvedType type = factory.fromBinding(methodDeclaration.binding.parameters[i]);
552 // bindings[i] = new FormalBinding(type, name, i, arg.sourceStart, arg.sourceEnd, "unknown");
554 swap(onType,methodDeclaration,pcDecl);
557 pc.resolve(new EclipseScope(bindings,methodDeclaration.scope));
558 HasIfPCDVisitor ifFinder = new HasIfPCDVisitor();
559 pc.traverse(ifFinder, null);
560 containsIfPcd = ifFinder.containsIfPcd;
562 } catch(ParserException pEx) {
563 methodDeclaration.scope.problemReporter().parseError(
564 pcLocation[0] + pEx.getLocation().getStart(),
565 pcLocation[0] + pEx.getLocation().getEnd() ,
567 pointcutExpression.toCharArray(),
569 new String[] {pEx.getMessage()});
572 boolean returnsVoid = false;
573 boolean returnsBoolean = false;
574 if ((methodDeclaration.returnType instanceof SingleTypeReference)) {
575 SingleTypeReference retType = (SingleTypeReference) methodDeclaration.returnType;
576 if (CharOperation.equals(voidType,retType.token)) returnsVoid = true;
577 if (CharOperation.equals(booleanType,retType.token)) returnsBoolean = true;
579 if (!returnsVoid && !containsIfPcd) {
580 methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
581 methodDeclaration.returnType.sourceEnd,
582 "Methods annotated with @Pointcut must return void unless the pointcut contains an if() expression");
584 if (!returnsBoolean && containsIfPcd) {
585 methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
586 methodDeclaration.returnType.sourceEnd,
587 "Methods annotated with @Pointcut must return boolean when the pointcut contains an if() expression");
590 if (methodDeclaration.statements != null && methodDeclaration.statements.length > 0 && !containsIfPcd) {
591 methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
592 methodDeclaration.returnType.sourceEnd,
593 "Pointcuts without an if() expression should have an empty method body");
596 if (pcDecl.pointcutDesignator == null) {
597 if (Modifier.isAbstract(methodDeclaration.modifiers)
598 || noValueSupplied // this is a matches nothing pointcut
599 //those 2 checks makes sense for aop.xml concretization but NOT for regular abstraction of pointcut
601 //&& (methodDeclaration.arguments == null || methodDeclaration.arguments.length == 0)) {
605 methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
606 methodDeclaration.returnType.sourceEnd,
607 "Method annotated with @Pointcut() for abstract pointcut must be abstract");
609 } else if (Modifier.isAbstract(methodDeclaration.modifiers)) {
610 methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
611 methodDeclaration.returnType.sourceEnd,
612 "Method annotated with non abstract @Pointcut(\""+pointcutExpression+"\") is abstract");
616 private void copyAllFields(MethodDeclaration from, MethodDeclaration to) {
617 to.annotations = from.annotations;
618 to.arguments = from.arguments;
619 to.binding = from.binding;
621 to.bodyEnd = from.bodyEnd;
622 to.bodyStart = from.bodyStart;
623 to.declarationSourceEnd = from.declarationSourceEnd;
624 to.declarationSourceStart = from.declarationSourceStart;
625 to.explicitDeclarations = from.explicitDeclarations;
626 to.ignoreFurtherInvestigation = from.ignoreFurtherInvestigation;
627 to.javadoc = from.javadoc;
628 to.modifiers = from.modifiers;
629 to.modifiersSourceStart = from.modifiersSourceStart;
630 to.returnType = from.returnType;
631 to.scope = from.scope;
632 to.selector = from.selector;
633 to.sourceEnd = from.sourceEnd;
634 to.sourceStart = from.sourceStart;
635 to.statements = from.statements;
636 to.thrownExceptions = from.thrownExceptions;
637 to.typeParameters = from.typeParameters;
640 private void swap(TypeDeclaration inType, MethodDeclaration thisDeclaration, MethodDeclaration forThatDeclaration) {
641 for (int i = 0; i < inType.methods.length; i++) {
642 if (inType.methods[i] == thisDeclaration) {
643 inType.methods[i] = forThatDeclaration;
649 private static class AspectJAnnotations {
650 boolean hasAdviceAnnotation = false;
651 boolean hasPointcutAnnotation = false;
652 boolean hasAspectAnnotation = false;
653 boolean hasAdviceNameAnnotation = false;
654 boolean hasDeclareParents = false;
655 boolean hasMultipleAdviceAnnotations = false;
656 boolean hasMultiplePointcutAnnotations = false;
657 boolean hasMultipleAspectAnnotations = false;
659 AdviceKind adviceKind = null;
660 Annotation adviceAnnotation = null;
661 Annotation pointcutAnnotation = null;
662 Annotation aspectAnnotation = null;
663 Annotation adviceNameAnnotation = null;
665 Annotation duplicateAdviceAnnotation = null;
666 Annotation duplicatePointcutAnnotation = null;
667 Annotation duplicateAspectAnnotation = null;
669 public AspectJAnnotations(Annotation[] annotations) {
670 if (annotations == null) return;
671 for (int i = 0; i < annotations.length; i++) {
672 if (annotations[i].resolvedType == null) continue; // user messed up annotation declaration
673 char[] sig = annotations[i].resolvedType.signature();
674 if (CharOperation.equals(afterAdviceSig,sig)) {
675 adviceKind = AdviceKind.After;
676 addAdviceAnnotation(annotations[i]);
677 } else if (CharOperation.equals(afterReturningAdviceSig,sig)) {
678 adviceKind = AdviceKind.AfterReturning;
679 addAdviceAnnotation(annotations[i]);
680 } else if (CharOperation.equals(afterThrowingAdviceSig,sig)) {
681 adviceKind = AdviceKind.AfterThrowing;
682 addAdviceAnnotation(annotations[i]);
683 } else if (CharOperation.equals(beforeAdviceSig,sig)) {
684 adviceKind = AdviceKind.Before;
685 addAdviceAnnotation(annotations[i]);
686 } else if (CharOperation.equals(aroundAdviceSig,sig)) {
687 adviceKind = AdviceKind.Around;
688 addAdviceAnnotation(annotations[i]);
689 } else if (CharOperation.equals(adviceNameSig,sig)) {
690 hasAdviceNameAnnotation = true;
691 adviceNameAnnotation = annotations[i];
692 } else if (CharOperation.equals(declareParentsSig,sig)) {
693 hasDeclareParents = true;
694 } else if (CharOperation.equals(aspectSig,sig)) {
695 if (hasAspectAnnotation) {
696 hasMultipleAspectAnnotations = true;
697 duplicateAspectAnnotation = annotations[i];
699 hasAspectAnnotation = true;
700 aspectAnnotation = annotations[i];
702 } else if (CharOperation.equals(pointcutSig,sig)) {
703 if (hasPointcutAnnotation) {
704 hasMultiplePointcutAnnotations = true;
705 duplicatePointcutAnnotation = annotations[i];
707 hasPointcutAnnotation = true;
708 pointcutAnnotation = annotations[i];
715 public boolean hasAspectJAnnotations() {
716 return hasAdviceAnnotation || hasPointcutAnnotation || hasAdviceNameAnnotation || hasAspectAnnotation;
719 private void addAdviceAnnotation(Annotation annotation) {
720 if (!hasAdviceAnnotation) {
721 hasAdviceAnnotation = true;
722 adviceAnnotation = annotation;
724 hasMultipleAdviceAnnotations = true;
725 duplicateAdviceAnnotation = annotation;
730 private static class HasIfPCDVisitor extends AbstractPatternNodeVisitor {
731 public boolean containsIfPcd = false;
733 public Object visit(IfPointcut node, Object data) {
734 containsIfPcd = true;