Package org.aspectj.ajdt.internal.compiler.ast

Source Code of org.aspectj.ajdt.internal.compiler.ast.ValidateAtAspectJAnnotationsVisitor$HasIfPCDVisitor

/* *******************************************************************
* 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;
    }
  }
}
TOP

Related Classes of org.aspectj.ajdt.internal.compiler.ast.ValidateAtAspectJAnnotationsVisitor$HasIfPCDVisitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.