Logo Search packages:      
Sourcecode: aspectj version File versions  Download package

ValidateAtAspectJAnnotationsVisitor.java

/* *******************************************************************
 * Copyright (c) 2005 IBM Corporation Ltd
 * All rights reserved. 
 * This program and the accompanying materials are made available 
 * under the terms of the Eclipse Public License v1.0 
 * which accompanies this distribution and is available at 
 * http://www.eclipse.org/legal/epl-v10.html 
 *  
 * Contributors: 
 *     Adrian Colyer  initial implementation 
 * ******************************************************************/
package org.aspectj.ajdt.internal.compiler.ast;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import org.aspectj.ajdt.internal.compiler.lookup.EclipseFactory;
import org.aspectj.ajdt.internal.compiler.lookup.EclipseScope;
import org.aspectj.ajdt.internal.core.builder.EclipseSourceContext;
import org.aspectj.bridge.context.CompilationAndWeavingContext;
import org.aspectj.bridge.context.ContextToken;
import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
import org.aspectj.org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Argument;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.NameReference;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.NormalAnnotation;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.StringLiteral;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.aspectj.org.eclipse.jdt.internal.compiler.impl.Constant;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.aspectj.weaver.AdviceKind;
import org.aspectj.weaver.AjAttribute;
import org.aspectj.weaver.ISourceContext;
import org.aspectj.weaver.ResolvedPointcutDefinition;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.patterns.AbstractPatternNodeVisitor;
import org.aspectj.weaver.patterns.FormalBinding;
import org.aspectj.weaver.patterns.IfPointcut;
import org.aspectj.weaver.patterns.ParserException;
import org.aspectj.weaver.patterns.PatternParser;
import org.aspectj.weaver.patterns.Pointcut;

public class ValidateAtAspectJAnnotationsVisitor extends ASTVisitor {

      private static final char[] beforeAdviceSig = "Lorg/aspectj/lang/annotation/Before;".toCharArray();
      private static final char[] afterAdviceSig = "Lorg/aspectj/lang/annotation/After;".toCharArray();
      private static final char[] afterReturningAdviceSig = "Lorg/aspectj/lang/annotation/AfterReturning;".toCharArray();
      private static final char[] afterThrowingAdviceSig = "Lorg/aspectj/lang/annotation/AfterThrowing;".toCharArray();
      private static final char[] aroundAdviceSig = "Lorg/aspectj/lang/annotation/Around;".toCharArray();
      private static final char[] pointcutSig = "Lorg/aspectj/lang/annotation/Pointcut;".toCharArray();
      private static final char[] aspectSig = "Lorg/aspectj/lang/annotation/Aspect;".toCharArray();
      private static final char[] declareParentsSig = "Lorg/aspectj/lang/annotation/DeclareParents;".toCharArray();
      private static final char[] adviceNameSig = "Lorg/aspectj/lang/annotation/AdviceName;".toCharArray();
      // private static final char[] orgAspectJLangAnnotation =
      // "org/aspectj/lang/annotation/".toCharArray();
      private static final char[] voidType = "void".toCharArray();
      private static final char[] booleanType = "boolean".toCharArray();
      private static final char[] joinPoint = "Lorg/aspectj/lang/JoinPoint;".toCharArray();
      private static final char[] joinPointStaticPart = "Lorg/aspectj/lang/JoinPoint$StaticPart;".toCharArray();
      private static final char[] joinPointEnclosingStaticPart = "Lorg/aspectj/lang/JoinPoint$EnclosingStaticPart;".toCharArray();
      private static final char[] proceedingJoinPoint = "Lorg/aspectj/lang/ProceedingJoinPoint;".toCharArray();
      // private static final char[][] adviceSigs = new char[][] {
      // beforeAdviceSig, afterAdviceSig, afterReturningAdviceSig,
      // afterThrowingAdviceSig, aroundAdviceSig };

      private final CompilationUnitDeclaration unit;
      private final Stack typeStack = new Stack();
      private AspectJAnnotations ajAnnotations;

      public ValidateAtAspectJAnnotationsVisitor(CompilationUnitDeclaration unit) {
            this.unit = unit;
      }

      public boolean visit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
            typeStack.push(localTypeDeclaration);
            ajAnnotations = new AspectJAnnotations(localTypeDeclaration.annotations);
            checkTypeDeclaration(localTypeDeclaration);
            return true;
      }

      public void endVisit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
            typeStack.pop();
      }

      public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
            typeStack.push(memberTypeDeclaration);
            ajAnnotations = new AspectJAnnotations(memberTypeDeclaration.annotations);
            checkTypeDeclaration(memberTypeDeclaration);
            return true;
      }

      public void endVisit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
            typeStack.pop();
      }

      public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
            typeStack.push(typeDeclaration);
            ajAnnotations = new AspectJAnnotations(typeDeclaration.annotations);
            checkTypeDeclaration(typeDeclaration);
            return true;
      }

      public void endVisit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
            typeStack.pop();
      }

      private void checkTypeDeclaration(TypeDeclaration typeDecl) {
            ContextToken tok = CompilationAndWeavingContext.enteringPhase(
                        CompilationAndWeavingContext.VALIDATING_AT_ASPECTJ_ANNOTATIONS, typeDecl.name);
            if (!(typeDecl instanceof AspectDeclaration)) {
                  if (ajAnnotations.hasAspectAnnotation) {
                        validateAspectDeclaration(typeDecl);
                  } else {
                        // check that class doesn't extend aspect
                        TypeReference parentRef = typeDecl.superclass;
                        if (parentRef != null) {
                              TypeBinding parentBinding = parentRef.resolvedType;
                              if (parentBinding instanceof SourceTypeBinding) {
                                    SourceTypeBinding parentSTB = (SourceTypeBinding) parentBinding;
                                    if (parentSTB.scope != null) {
                                          TypeDeclaration parentDecl = parentSTB.scope.referenceContext;
                                          if (isAspect(parentDecl)) {
                                                typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart, typeDecl.sourceEnd,
                                                            "a class cannot extend an aspect");
                                          }
                                    }
                              }
                        }
                  }
            } else {
                  // check that aspect doesn't have @Aspect annotation, we've already
                  // added on ourselves.
                  if (ajAnnotations.hasMultipleAspectAnnotations) {
                        typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart, typeDecl.sourceEnd,
                                    "aspects cannot have @Aspect annotation");
                  }
            }
            CompilationAndWeavingContext.leavingPhase(tok);
      }

      public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) {
            ajAnnotations = new AspectJAnnotations(fieldDeclaration.annotations);
            if (ajAnnotations.hasDeclareParents && !insideAspect()) {
                  scope.problemReporter().signalError(fieldDeclaration.sourceStart, fieldDeclaration.sourceEnd,
                              "DeclareParents can only be used inside an aspect type");
            }
            return true;
      }

      public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) {
            if (methodDeclaration.hasErrors()) {
                  return false;
            }
            ContextToken tok = CompilationAndWeavingContext.enteringPhase(
                        CompilationAndWeavingContext.VALIDATING_AT_ASPECTJ_ANNOTATIONS, methodDeclaration.selector);
            ajAnnotations = new AspectJAnnotations(methodDeclaration.annotations);
            if (!methodDeclaration.getClass().equals(AjMethodDeclaration.class)) {
                  // simply test for innapropriate use of annotations on code-style
                  // members
                  if (methodDeclaration instanceof PointcutDeclaration) {
                        if (ajAnnotations.hasMultiplePointcutAnnotations || ajAnnotations.hasAdviceAnnotation
                                    || ajAnnotations.hasAspectAnnotation || ajAnnotations.hasAdviceNameAnnotation) {
                              methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart,
                                          methodDeclaration.sourceEnd, "@AspectJ annotations cannot be declared on this aspect member");
                        }
                  } else if (methodDeclaration instanceof AdviceDeclaration) {
                        if (ajAnnotations.hasMultipleAdviceAnnotations || ajAnnotations.hasAspectAnnotation
                                    || ajAnnotations.hasPointcutAnnotation) {
                              methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart,
                                          methodDeclaration.sourceEnd, "Only @AdviceName AspectJ annotation allowed on advice");
                        }
                  } else {
                        if (ajAnnotations.hasAspectJAnnotations()) {
                              methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart,
                                          methodDeclaration.sourceEnd, "@AspectJ annotations cannot be declared on this aspect member");
                        }
                  }
                  CompilationAndWeavingContext.leavingPhase(tok);
                  return false;
            }

            if (ajAnnotations.hasAdviceAnnotation) {
                  validateAdvice(methodDeclaration);
            } else if (ajAnnotations.hasPointcutAnnotation) {
                  convertToPointcutDeclaration(methodDeclaration, scope);
            }
            CompilationAndWeavingContext.leavingPhase(tok);
            return false;
      }

      // private boolean isAspectJAnnotation(Annotation ann) {
      // if (ann.resolvedType == null) return false;
      // char[] sig = ann.resolvedType.signature();
      // return CharOperation.contains(orgAspectJLangAnnotation, sig);
      // }

      private boolean insideAspect() {
            if (typeStack.empty())
                  return false;
            TypeDeclaration typeDecl = (TypeDeclaration) typeStack.peek();
            return isAspect(typeDecl);
      }

      private boolean isAspect(TypeDeclaration typeDecl) {
            if (typeDecl instanceof AspectDeclaration)
                  return true;
            return new AspectJAnnotations(typeDecl.annotations).hasAspectAnnotation;
      }

      /**
       * aspect must be public nested aspect must be static cannot extend a concrete aspect pointcut in perclause must be good.
       */
      private void validateAspectDeclaration(TypeDeclaration typeDecl) {
            if (typeStack.size() > 1) {
                  // it's a nested aspect
                  if (!Modifier.isStatic(typeDecl.modifiers)) {
                        typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart, typeDecl.sourceEnd,
                                    "inner aspects must be static");
                        return;
                  }
            }

            SourceTypeBinding binding = typeDecl.binding;
            if (binding != null) {
                  if (binding.isEnum() || binding.isInterface() || binding.isAnnotationType()) {
                        typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart, typeDecl.sourceEnd,
                                    "only classes can have an @Aspect annotation");
                  }
            }

            // FIXME AV - do we really want that
            // if (!Modifier.isPublic(typeDecl.modifiers)) {
            // typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart,
            // typeDecl.sourceEnd,"@Aspect class must be public");
            // }

            TypeReference parentRef = typeDecl.superclass;
            if (parentRef != null) {
                  TypeBinding parentBinding = parentRef.resolvedType;
                  if (parentBinding instanceof SourceTypeBinding) {
                        SourceTypeBinding parentSTB = (SourceTypeBinding) parentBinding;
                        if (parentSTB.scope != null) { // scope is null if its a
                              // binarytypebinding (in AJ
                              // world, thats a subclass of
                              // SourceTypeBinding)
                              TypeDeclaration parentDecl = parentSTB.scope.referenceContext;
                              if (isAspect(parentDecl) && !Modifier.isAbstract(parentDecl.modifiers)) {
                                    typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart, typeDecl.sourceEnd,
                                                "cannot extend a concrete aspect");
                              }
                        }
                  }
            }

            Annotation aspectAnnotation = ajAnnotations.aspectAnnotation;

            int[] pcLoc = new int[2];
            String perClause = getStringLiteralFor("value", aspectAnnotation, pcLoc);
            // AspectDeclaration aspectDecl = new
            // AspectDeclaration(typeDecl.compilationResult);

            try {
                  if (perClause != null && !perClause.equals("")) {
                        ISourceContext context = new EclipseSourceContext(unit.compilationResult, pcLoc[0]);
                        Pointcut pc = new PatternParser(perClause, context).maybeParsePerClause();
                        FormalBinding[] bindings = new FormalBinding[0];
                        if (pc != null)
                              pc.resolve(new EclipseScope(bindings, typeDecl.scope));
                  }
            } catch (ParserException pEx) {
                  typeDecl.scope.problemReporter().parseError(pcLoc[0] + pEx.getLocation().getStart(),
                              pcLoc[0] + pEx.getLocation().getEnd(), -1, perClause.toCharArray(), perClause,
                              new String[] { pEx.getMessage() });
            }
      }

      /**
       * 1) Advice must be public 2) Advice must have a void return type if not around advice 3) Advice must not have any other @AspectJ
       * annotations 4) After throwing advice must declare the thrown formal 5) After returning advice must declare the returning
       * formal 6) Advice must not be static
       */
      private void validateAdvice(MethodDeclaration methodDeclaration) {

            if (!insideAspect()) {
                  methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart, methodDeclaration.sourceEnd,
                              "Advice must be declared inside an aspect type");
            }

            if (!Modifier.isPublic(methodDeclaration.modifiers)) {
                  methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart, methodDeclaration.sourceEnd,
                              "advice must be public");
            }

            if (Modifier.isStatic(methodDeclaration.modifiers)) {
                  methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart, methodDeclaration.sourceEnd,
                              "advice can not be declared static");
            }

            if (ajAnnotations.hasMultipleAdviceAnnotations) {
                  methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.duplicateAdviceAnnotation);
            }
            if (ajAnnotations.hasPointcutAnnotation) {
                  methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.pointcutAnnotation);
            }
            if (ajAnnotations.hasAspectAnnotation) {
                  methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.aspectAnnotation);
            }
            if (ajAnnotations.hasAdviceNameAnnotation) {
                  methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.adviceNameAnnotation);
            }

            if (ajAnnotations.adviceKind != AdviceKind.Around) {
                  ensureVoidReturnType(methodDeclaration);
            }

            if (ajAnnotations.adviceKind == AdviceKind.AfterThrowing) {
                  int[] throwingLocation = new int[2];
                  String thrownFormal = getStringLiteralFor("throwing", ajAnnotations.adviceAnnotation, throwingLocation);
                  if (thrownFormal != null) {
                        // Argument[] arguments = methodDeclaration.arguments;
                        if (!toArgumentNames(methodDeclaration.arguments).contains(thrownFormal)) {
                              methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart,
                                          methodDeclaration.sourceEnd,
                                          "throwing formal '" + thrownFormal + "' must be declared as a parameter in the advice signature");
                        }
                  }
            }

            if (ajAnnotations.adviceKind == AdviceKind.AfterReturning) {
                  int[] throwingLocation = new int[2];
                  String returningFormal = getStringLiteralFor("returning", ajAnnotations.adviceAnnotation, throwingLocation);
                  if (returningFormal != null) {
                        if (!toArgumentNames(methodDeclaration.arguments).contains(returningFormal)) {
                              methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart,
                                          methodDeclaration.sourceEnd,
                                          "returning formal '" + returningFormal + "' must be declared as a parameter in the advice signature");
                        }
                  }
            }

            resolveAndSetPointcut(methodDeclaration, ajAnnotations.adviceAnnotation);

      }

      /**
       * Get the argument names as a string list
       * 
       * @param arguments
       * @return argument names (possibly empty)
       */
      private List toArgumentNames(Argument[] arguments) {
            List names = new ArrayList();
            if (arguments == null) {
                  return names;
            } else {
                  for (int i = 0; i < arguments.length; i++) {
                        names.add(new String(arguments[i].name));
                  }
                  return names;
            }
      }

      private void resolveAndSetPointcut(MethodDeclaration methodDeclaration, Annotation adviceAnn) {
            int[] pcLocation = new int[2];
            String pointcutExpression = getStringLiteralFor("pointcut", adviceAnn, pcLocation);
            if (pointcutExpression == null)
                  pointcutExpression = getStringLiteralFor("value", adviceAnn, pcLocation);
            try {
                  // +1 to give first char of pointcut string
                  ISourceContext context = new EclipseSourceContext(unit.compilationResult, pcLocation[0] + 1);
                  PatternParser pp = new PatternParser(pointcutExpression, context);
                  Pointcut pc = pp.parsePointcut();
                  pp.checkEof();
                  FormalBinding[] bindings = buildFormalAdviceBindingsFrom(methodDeclaration);
                  pc.resolve(new EclipseScope(bindings, methodDeclaration.scope));
                  EclipseFactory factory = EclipseFactory.fromScopeLookupEnvironment(methodDeclaration.scope);
                  // now create a ResolvedPointcutDefinition,make an attribute out of
                  // it, and add it to the method
                  UnresolvedType[] paramTypes = new UnresolvedType[bindings.length];
                  for (int i = 0; i < paramTypes.length; i++)
                        paramTypes[i] = bindings[i].getType();
                  ResolvedPointcutDefinition resPcutDef = new ResolvedPointcutDefinition(factory.fromBinding(((TypeDeclaration) typeStack
                              .peek()).binding), methodDeclaration.modifiers, "anonymous", paramTypes, pc);
                  AjAttribute attr = new AjAttribute.PointcutDeclarationAttribute(resPcutDef);
                  ((AjMethodDeclaration) methodDeclaration).addAttribute(new EclipseAttributeAdapter(attr));
            } catch (ParserException pEx) {
                  methodDeclaration.scope.problemReporter().parseError(pcLocation[0] + pEx.getLocation().getStart(),
                              pcLocation[0] + pEx.getLocation().getEnd(), -1, pointcutExpression.toCharArray(), pointcutExpression,
                              new String[] { pEx.getMessage() });
            }
      }

      private void ensureVoidReturnType(MethodDeclaration methodDeclaration) {
            boolean returnsVoid = true;
            if ((methodDeclaration.returnType instanceof SingleTypeReference)) {
                  SingleTypeReference retType = (SingleTypeReference) methodDeclaration.returnType;
                  if (!CharOperation.equals(voidType, retType.token)) {
                        returnsVoid = false;
                  }
            } else {
                  returnsVoid = false;
            }
            if (!returnsVoid) {
                  methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
                              methodDeclaration.returnType.sourceEnd, "This advice must return void");
            }
      }

      private FormalBinding[] buildFormalAdviceBindingsFrom(MethodDeclaration mDecl) {
            if (mDecl.arguments == null)
                  return new FormalBinding[0];
            if (mDecl.binding == null)
                  return new FormalBinding[0];
            EclipseFactory factory = EclipseFactory.fromScopeLookupEnvironment(mDecl.scope);
            String extraArgName = maybeGetExtraArgName();
            if (extraArgName == null)
                  extraArgName = "";
            FormalBinding[] ret = new FormalBinding[mDecl.arguments.length];
            for (int i = 0; i < mDecl.arguments.length; i++) {
                  Argument arg = mDecl.arguments[i];
                  String name = new String(arg.name);
                  TypeBinding argTypeBinding = mDecl.binding.parameters[i];
                  UnresolvedType type = factory.fromBinding(argTypeBinding);
                  if (CharOperation.equals(joinPoint, argTypeBinding.signature())
                              || CharOperation.equals(joinPointStaticPart, argTypeBinding.signature())
                              || CharOperation.equals(joinPointEnclosingStaticPart, argTypeBinding.signature())
                              || CharOperation.equals(proceedingJoinPoint, argTypeBinding.signature()) || name.equals(extraArgName)) {
                        ret[i] = new FormalBinding.ImplicitFormalBinding(type, name, i);
                  } else {
                        ret[i] = new FormalBinding(type, name, i, arg.sourceStart, arg.sourceEnd);
                  }
            }
            return ret;
      }

      private String maybeGetExtraArgName() {
            String argName = null;
            if (ajAnnotations.adviceKind == AdviceKind.AfterReturning) {
                  argName = getStringLiteralFor("returning", ajAnnotations.adviceAnnotation, new int[2]);
            } else if (ajAnnotations.adviceKind == AdviceKind.AfterThrowing) {
                  argName = getStringLiteralFor("throwing", ajAnnotations.adviceAnnotation, new int[2]);
            }
            return argName;
      }

      private String getStringLiteralFor(String memberName, Annotation inAnnotation, int[] location) {
            if (inAnnotation instanceof SingleMemberAnnotation && memberName.equals("value")) {
                  SingleMemberAnnotation sma = (SingleMemberAnnotation) inAnnotation;
                  if (sma.memberValue instanceof StringLiteral) {
                        StringLiteral sv = (StringLiteral) sma.memberValue;
                        location[0] = sv.sourceStart;
                        location[1] = sv.sourceEnd;
                        return new String(sv.source());
                  } else if (sma.memberValue instanceof NameReference
                              && (((NameReference) sma.memberValue).binding instanceof FieldBinding)) {
                        Binding b = ((NameReference) sma.memberValue).binding;
                        Constant c = ((FieldBinding) b).constant;
                        return c.stringValue();
                  }
            }
            if (!(inAnnotation instanceof NormalAnnotation))
                  return null;
            NormalAnnotation ann = (NormalAnnotation) inAnnotation;
            MemberValuePair[] mvps = ann.memberValuePairs;
            if (mvps == null)
                  return null;
            for (int i = 0; i < mvps.length; i++) {
                  if (CharOperation.equals(memberName.toCharArray(), mvps[i].name)) {
                        if (mvps[i].value instanceof StringLiteral) {
                              StringLiteral sv = (StringLiteral) mvps[i].value;
                              location[0] = sv.sourceStart;
                              location[1] = sv.sourceEnd;
                              return new String(sv.source());
                        }
                  }
            }
            return null;
      }

      private void convertToPointcutDeclaration(MethodDeclaration methodDeclaration, ClassScope scope) {
            TypeDeclaration typeDecl = (TypeDeclaration) typeStack.peek();
            if (typeDecl.binding != null) {
                  if (!typeDecl.binding.isClass()) {
                        methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart, methodDeclaration.sourceEnd,
                                    "pointcuts can only be declared in a class or an aspect");
                  }
            }

            if (methodDeclaration.thrownExceptions != null && methodDeclaration.thrownExceptions.length > 0) {
                  methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart, methodDeclaration.sourceEnd,
                              "pointcuts cannot throw exceptions!");
            }

            PointcutDeclaration pcDecl = new PointcutDeclaration(unit.compilationResult);
            copyAllFields(methodDeclaration, pcDecl);

            if (ajAnnotations.hasAdviceAnnotation) {
                  methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.adviceAnnotation);
            }
            if (ajAnnotations.hasAspectAnnotation) {
                  methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.aspectAnnotation);
            }
            if (ajAnnotations.hasAdviceNameAnnotation) {
                  methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.adviceNameAnnotation);
            }

            boolean noValueSupplied = true;
            boolean containsIfPcd = false;
            int[] pcLocation = new int[2];
            String pointcutExpression = getStringLiteralFor("value", ajAnnotations.pointcutAnnotation, pcLocation);
            try {
                  ISourceContext context = new EclipseSourceContext(unit.compilationResult, pcLocation[0]);
                  Pointcut pc = null;// abstract
                  if (pointcutExpression == null || pointcutExpression.length() == 0) {
                        noValueSupplied = true; // matches nothing pointcut
                  } else {
                        noValueSupplied = false;
                        pc = new PatternParser(pointcutExpression, context).parsePointcut();
                  }
                  pcDecl.pointcutDesignator = (pc == null) ? null : new PointcutDesignator(pc);
                  pcDecl.setGenerateSyntheticPointcutMethod();
                  TypeDeclaration onType = (TypeDeclaration) typeStack.peek();
                  pcDecl.postParse(onType);
                  // EclipseFactory factory =
                  // EclipseFactory.fromScopeLookupEnvironment
                  // (methodDeclaration.scope);
                  // int argsLength = methodDeclaration.arguments == null ? 0 :
                  // methodDeclaration.arguments.length;
                  FormalBinding[] bindings = buildFormalAdviceBindingsFrom(methodDeclaration);
                  // FormalBinding[] bindings = new FormalBinding[argsLength];
                  // for (int i = 0, len = bindings.length; i < len; i++) {
                  // Argument arg = methodDeclaration.arguments[i];
                  // String name = new String(arg.name);
                  // UnresolvedType type =
                  // factory.fromBinding(methodDeclaration.binding.parameters[i]);
                  // bindings[i] = new FormalBinding(type, name, i, arg.sourceStart,
                  // arg.sourceEnd, "unknown");
                  // }
                  swap(onType, methodDeclaration, pcDecl);
                  if (pc != null) {
                        // has an expression
                        EclipseScope eScope = new EclipseScope(bindings, methodDeclaration.scope);
                        char[] packageName = null;
                        if (typeDecl.binding != null && typeDecl.binding.getPackage() != null) {
                              packageName = typeDecl.binding.getPackage().readableName();
                        }
                        eScope.setLimitedImports(packageName);
                        pc.resolve(eScope);
                        HasIfPCDVisitor ifFinder = new HasIfPCDVisitor();
                        pc.traverse(ifFinder, null);
                        containsIfPcd = ifFinder.containsIfPcd;
                  }
            } catch (ParserException pEx) {
                  methodDeclaration.scope.problemReporter().parseError(pcLocation[0] + pEx.getLocation().getStart(),
                              pcLocation[0] + pEx.getLocation().getEnd(), -1, pointcutExpression.toCharArray(), pointcutExpression,
                              new String[] { pEx.getMessage() });
            }

            boolean returnsVoid = false;
            boolean returnsBoolean = false;
            if ((methodDeclaration.returnType instanceof SingleTypeReference)) {
                  SingleTypeReference retType = (SingleTypeReference) methodDeclaration.returnType;
                  if (CharOperation.equals(voidType, retType.token))
                        returnsVoid = true;
                  if (CharOperation.equals(booleanType, retType.token))
                        returnsBoolean = true;
            }
            if (!returnsVoid && !containsIfPcd) {
                  methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
                              methodDeclaration.returnType.sourceEnd,
                              "Methods annotated with @Pointcut must return void unless the pointcut contains an if() expression");
            }
            if (!returnsBoolean && containsIfPcd) {
                  methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
                              methodDeclaration.returnType.sourceEnd,
                              "Methods annotated with @Pointcut must return boolean when the pointcut contains an if() expression");
            }

            if (methodDeclaration.statements != null && methodDeclaration.statements.length > 0 && !containsIfPcd) {
                  methodDeclaration.scope.problemReporter()
                              .signalError(methodDeclaration.returnType.sourceStart, methodDeclaration.returnType.sourceEnd,
                                          "Pointcuts without an if() expression should have an empty method body");
            }

            if (pcDecl.pointcutDesignator == null) {
                  if (Modifier.isAbstract(methodDeclaration.modifiers) || noValueSupplied // this
                  // is
                  // a
                  // matches
                  // nothing
                  // pointcut
                  // those 2 checks makes sense for aop.xml concretization but NOT for
                  // regular abstraction of pointcut
                  // && returnsVoid
                  // && (methodDeclaration.arguments == null ||
                  // methodDeclaration.arguments.length == 0)) {
                  ) {
                        // fine
                  } else {
                        methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
                                    methodDeclaration.returnType.sourceEnd,
                                    "Method annotated with @Pointcut() for abstract pointcut must be abstract");
                  }
            } else if (Modifier.isAbstract(methodDeclaration.modifiers)) {
                  methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
                              methodDeclaration.returnType.sourceEnd,
                              "Method annotated with non abstract @Pointcut(\"" + pointcutExpression + "\") is abstract");
            }
      }

      private void copyAllFields(MethodDeclaration from, MethodDeclaration to) {
            to.annotations = from.annotations;
            to.arguments = from.arguments;
            to.binding = from.binding;
            to.bits = from.bits;
            to.bodyEnd = from.bodyEnd;
            to.bodyStart = from.bodyStart;
            to.declarationSourceEnd = from.declarationSourceEnd;
            to.declarationSourceStart = from.declarationSourceStart;
            to.explicitDeclarations = from.explicitDeclarations;
            to.ignoreFurtherInvestigation = from.ignoreFurtherInvestigation;
            to.javadoc = from.javadoc;
            to.modifiers = from.modifiers;
            to.modifiersSourceStart = from.modifiersSourceStart;
            to.returnType = from.returnType;
            to.scope = from.scope;
            to.selector = from.selector;
            to.sourceEnd = from.sourceEnd;
            to.sourceStart = from.sourceStart;
            to.statements = from.statements;
            to.thrownExceptions = from.thrownExceptions;
            to.typeParameters = from.typeParameters;
      }

      private void swap(TypeDeclaration inType, MethodDeclaration thisDeclaration, MethodDeclaration forThatDeclaration) {
            for (int i = 0; i < inType.methods.length; i++) {
                  if (inType.methods[i] == thisDeclaration) {
                        inType.methods[i] = forThatDeclaration;
                        break;
                  }
            }
      }

      private static class AspectJAnnotations {
            boolean hasAdviceAnnotation = false;
            boolean hasPointcutAnnotation = false;
            boolean hasAspectAnnotation = false;
            boolean hasAdviceNameAnnotation = false;
            boolean hasDeclareParents = false;
            boolean hasMultipleAdviceAnnotations = false;
            boolean hasMultiplePointcutAnnotations = false;
            boolean hasMultipleAspectAnnotations = false;

            AdviceKind adviceKind = null;
            Annotation adviceAnnotation = null;
            Annotation pointcutAnnotation = null;
            Annotation aspectAnnotation = null;
            Annotation adviceNameAnnotation = null;

            Annotation duplicateAdviceAnnotation = null;
            Annotation duplicatePointcutAnnotation = null;
            Annotation duplicateAspectAnnotation = null;

            public AspectJAnnotations(Annotation[] annotations) {
                  if (annotations == null)
                        return;
                  for (int i = 0; i < annotations.length; i++) {
                        if (annotations[i].resolvedType == null)
                              continue; // user messed up annotation declaration
                        char[] sig = annotations[i].resolvedType.signature();
                        if (CharOperation.equals(afterAdviceSig, sig)) {
                              adviceKind = AdviceKind.After;
                              addAdviceAnnotation(annotations[i]);
                        } else if (CharOperation.equals(afterReturningAdviceSig, sig)) {
                              adviceKind = AdviceKind.AfterReturning;
                              addAdviceAnnotation(annotations[i]);
                        } else if (CharOperation.equals(afterThrowingAdviceSig, sig)) {
                              adviceKind = AdviceKind.AfterThrowing;
                              addAdviceAnnotation(annotations[i]);
                        } else if (CharOperation.equals(beforeAdviceSig, sig)) {
                              adviceKind = AdviceKind.Before;
                              addAdviceAnnotation(annotations[i]);
                        } else if (CharOperation.equals(aroundAdviceSig, sig)) {
                              adviceKind = AdviceKind.Around;
                              addAdviceAnnotation(annotations[i]);
                        } else if (CharOperation.equals(adviceNameSig, sig)) {
                              hasAdviceNameAnnotation = true;
                              adviceNameAnnotation = annotations[i];
                        } else if (CharOperation.equals(declareParentsSig, sig)) {
                              hasDeclareParents = true;
                        } else if (CharOperation.equals(aspectSig, sig)) {
                              if (hasAspectAnnotation) {
                                    hasMultipleAspectAnnotations = true;
                                    duplicateAspectAnnotation = annotations[i];
                              } else {
                                    hasAspectAnnotation = true;
                                    aspectAnnotation = annotations[i];
                              }
                        } else if (CharOperation.equals(pointcutSig, sig)) {
                              if (hasPointcutAnnotation) {
                                    hasMultiplePointcutAnnotations = true;
                                    duplicatePointcutAnnotation = annotations[i];
                              } else {
                                    hasPointcutAnnotation = true;
                                    pointcutAnnotation = annotations[i];
                              }

                        }
                  }
            }

            public boolean hasAspectJAnnotations() {
                  return hasAdviceAnnotation || hasPointcutAnnotation || hasAdviceNameAnnotation || hasAspectAnnotation;
            }

            private void addAdviceAnnotation(Annotation annotation) {
                  if (!hasAdviceAnnotation) {
                        hasAdviceAnnotation = true;
                        adviceAnnotation = annotation;
                  } else {
                        hasMultipleAdviceAnnotations = true;
                        duplicateAdviceAnnotation = annotation;
                  }
            }
      }

      private static class HasIfPCDVisitor extends AbstractPatternNodeVisitor {
            public boolean containsIfPcd = false;

            public Object visit(IfPointcut node, Object data) {
                  containsIfPcd = true;
                  return data;
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index