Package org.aspectj.ajdt.internal.compiler.lookup

Source Code of org.aspectj.ajdt.internal.compiler.lookup.EclipseSourceType

/* *******************************************************************
* Copyright (c) 2002,2010 Contributors
* 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:
*     PARC                 initial implementation
*     Alexandre Vasseur    support for @AJ perClause
* ******************************************************************/

package org.aspectj.ajdt.internal.compiler.lookup;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.aspectj.ajdt.internal.compiler.ast.AdviceDeclaration;
import org.aspectj.ajdt.internal.compiler.ast.AspectDeclaration;
import org.aspectj.ajdt.internal.compiler.ast.DeclareAnnotationDeclaration;
import org.aspectj.ajdt.internal.compiler.ast.DeclareDeclaration;
import org.aspectj.ajdt.internal.compiler.ast.InterTypeDeclaration;
import org.aspectj.ajdt.internal.compiler.ast.PointcutDeclaration;
import org.aspectj.ajdt.internal.core.builder.EclipseSourceContext;
import org.aspectj.bridge.IMessage;
import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
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.ArrayInitializer;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Expression;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MarkerAnnotation;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
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.QualifiedNameReference;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
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.TypeParameter;
import org.aspectj.org.eclipse.jdt.internal.compiler.impl.Constant;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.aspectj.weaver.AbstractReferenceTypeDelegate;
import org.aspectj.weaver.AnnotationAJ;
import org.aspectj.weaver.AnnotationNameValuePair;
import org.aspectj.weaver.AnnotationTargetKind;
import org.aspectj.weaver.AnnotationValue;
import org.aspectj.weaver.ArrayAnnotationValue;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.EnumAnnotationValue;
import org.aspectj.weaver.ReferenceType;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedPointcutDefinition;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.StandardAnnotation;
import org.aspectj.weaver.TypeVariable;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.WeaverStateInfo;
import org.aspectj.weaver.World;
import org.aspectj.weaver.bcel.AtAjAttributes.LazyResolvedPointcutDefinition;
import org.aspectj.weaver.patterns.Declare;
import org.aspectj.weaver.patterns.FormalBinding;
import org.aspectj.weaver.patterns.ParserException;
import org.aspectj.weaver.patterns.PatternParser;
import org.aspectj.weaver.patterns.PerClause;
import org.aspectj.weaver.patterns.PerFromSuper;
import org.aspectj.weaver.patterns.PerSingleton;
import org.aspectj.weaver.patterns.Pointcut;

/**
* Supports viewing eclipse TypeDeclarations/SourceTypeBindings as a ResolvedType
*
* @author Jim Hugunin
* @author Andy Clement
*/
public class EclipseSourceType extends AbstractReferenceTypeDelegate {
  private static final char[] pointcutSig = "Lorg/aspectj/lang/annotation/Pointcut;".toCharArray();
  private static final char[] aspectSig = "Lorg/aspectj/lang/annotation/Aspect;".toCharArray();
  protected ResolvedPointcutDefinition[] declaredPointcuts = null;
  protected ResolvedMember[] declaredMethods = null;
  protected ResolvedMember[] declaredFields = null;

  public List<Declare> declares = new ArrayList<Declare>();
  public List<EclipseTypeMunger> typeMungers = new ArrayList<EclipseTypeMunger>();

  private final EclipseFactory factory;

  private final SourceTypeBinding binding;
  private final TypeDeclaration declaration;
  private final CompilationUnitDeclaration unit;
  private boolean annotationsFullyResolved = false;
  private boolean annotationTypesAreResolved = false;
  private ResolvedType[] annotationTypes = null;

  private boolean discoveredAnnotationTargetKinds = false;
  private AnnotationTargetKind[] annotationTargetKinds;
  private AnnotationAJ[] annotations = null;

  protected EclipseFactory eclipseWorld() {
    return factory;
  }

  public EclipseSourceType(ReferenceType resolvedTypeX, EclipseFactory factory, SourceTypeBinding binding,
      TypeDeclaration declaration, CompilationUnitDeclaration unit) {
    super(resolvedTypeX, true);
    this.factory = factory;
    this.binding = binding;
    this.declaration = declaration;
    this.unit = unit;

    setSourceContext(new EclipseSourceContext(declaration.compilationResult));
    resolvedTypeX.setStartPos(declaration.sourceStart);
    resolvedTypeX.setEndPos(declaration.sourceEnd);
  }

  public boolean isAspect() {
    final boolean isCodeStyle = declaration instanceof AspectDeclaration;
    return isCodeStyle ? isCodeStyle : isAnnotationStyleAspect();
  }

  public boolean isAnonymous() {
    if (declaration.binding != null) {
      return declaration.binding.isAnonymousType();
    }
    return ((declaration.modifiers & (ASTNode.IsAnonymousType | ASTNode.IsLocalType)) != 0);
  }

  public boolean isNested() {
    if (declaration.binding != null) {
      return (declaration.binding.isMemberType());
    }
    return ((declaration.modifiers & ASTNode.IsMemberType) != 0);
  }

  public ResolvedType getOuterClass() {
    if (declaration.enclosingType == null) {
      return null;
    }
    return eclipseWorld().fromEclipse(declaration.enclosingType.binding);
  }

  public boolean isAnnotationStyleAspect() {
    if (declaration.annotations == null) {
      return false;
    }
    ResolvedType[] annotations = getAnnotationTypes();
    for (int i = 0; i < annotations.length; i++) {
      if ("org.aspectj.lang.annotation.Aspect".equals(annotations[i].getName())) {
        return true;
      }
    }
    return false;
  }

  /** Returns "" if there is a problem */
  private String getPointcutStringFromAnnotationStylePointcut(AbstractMethodDeclaration amd) {
    Annotation[] ans = amd.annotations;
    if (ans == null) {
      return "";
    }
    for (int i = 0; i < ans.length; i++) {
      if (ans[i].resolvedType == null) {
        continue; // XXX happens if we do this very early from
      }
      // buildInterTypeandPerClause
      // may prevent us from resolving references made in @Pointcuts to
      // an @Pointcut in a code-style aspect
      char[] sig = ans[i].resolvedType.signature();
      if (CharOperation.equals(pointcutSig, sig)) {
        if (ans[i].memberValuePairs().length == 0) {
          return ""; // empty pointcut expression
        }
        Expression expr = ans[i].memberValuePairs()[0].value;
        if (expr instanceof StringLiteral) {
          StringLiteral sLit = ((StringLiteral) expr);
          return new String(sLit.source());
        } else if (expr instanceof NameReference && (((NameReference) expr).binding instanceof FieldBinding)) {
          Binding b = ((NameReference) expr).binding;
          Constant c = ((FieldBinding) b).constant;
          return c.stringValue();
        } else {
          throw new BCException("Do not know how to recover pointcut definition from " + expr + " (type "
              + expr.getClass().getName() + ")");
        }
      }
    }
    return "";
  }

  private boolean isAnnotationStylePointcut(Annotation[] annotations) {
    if (annotations == null) {
      return false;
    }
    for (int i = 0; i < annotations.length; i++) {
      if (annotations[i].resolvedType == null) {
        continue; // XXX happens if we do this very early from
      }
      // buildInterTypeandPerClause
      // may prevent us from resolving references made in @Pointcuts to
      // an @Pointcut in a code-style aspect
      char[] sig = annotations[i].resolvedType.signature();
      if (CharOperation.equals(pointcutSig, sig)) {
        return true;
      }
    }
    return false;
  }

  public WeaverStateInfo getWeaverState() {
    return null;
  }

  public ResolvedType getSuperclass() {
    if (binding.isInterface()) {
      return getResolvedTypeX().getWorld().getCoreType(UnresolvedType.OBJECT);
    }
    // XXX what about java.lang.Object
    return eclipseWorld().fromEclipse(binding.superclass());
  }

  public ResolvedType[] getDeclaredInterfaces() {
    return eclipseWorld().fromEclipse(binding.superInterfaces());
  }

  protected void fillDeclaredMembers() {
    List<ResolvedMember> declaredPointcuts = new ArrayList<ResolvedMember>();
    List<ResolvedMember> declaredMethods = new ArrayList<ResolvedMember>();
    List<ResolvedMember> declaredFields = new ArrayList<ResolvedMember>();

    binding.methods(); // the important side-effect of this call is to make
    // sure bindings are completed
    AbstractMethodDeclaration[] methods = declaration.methods;
    if (methods != null) {
      for (int i = 0, len = methods.length; i < len; i++) {
        AbstractMethodDeclaration amd = methods[i];
        if (amd == null || amd.ignoreFurtherInvestigation) {
          continue;
        }
        if (amd instanceof PointcutDeclaration) {
          PointcutDeclaration d = (PointcutDeclaration) amd;
          ResolvedPointcutDefinition df = d.makeResolvedPointcutDefinition(factory);
          if (df != null) {
            declaredPointcuts.add(df);
          }
        } else if (amd instanceof InterTypeDeclaration) {
          // these are handled in a separate pass
          continue;
        } else if (amd instanceof DeclareDeclaration && !(amd instanceof DeclareAnnotationDeclaration)) { // surfaces
          // the
          // annotated
          // ajc$ method
          // these are handled in a separate pass
          continue;
        } else if (amd instanceof AdviceDeclaration) {
          // these are ignored during compilation and only used during
          // weaving
          continue;
        } else if ((amd.annotations != null) && isAnnotationStylePointcut(amd.annotations)) {
          // consider pointcuts defined via annotations
          ResolvedPointcutDefinition df = makeResolvedPointcutDefinition(amd);
          if (df != null) {
            declaredPointcuts.add(df);
          }
        } else {
          if (amd.binding == null || !amd.binding.isValidBinding()) {
            continue;
          }
          ResolvedMember member = factory.makeResolvedMember(amd.binding);
          if (unit != null) {
            boolean positionKnown = true;
            if (amd.binding.sourceMethod() == null) {
              if (amd.binding.declaringClass instanceof SourceTypeBinding) {
                SourceTypeBinding stb = ((SourceTypeBinding) amd.binding.declaringClass);
                if (stb.scope == null || stb.scope.referenceContext == null) {
                  positionKnown = false;
                }
              }
            }
            if (positionKnown) { // pr229829
              member.setSourceContext(new EclipseSourceContext(unit.compilationResult, amd.binding.sourceStart()));
              member.setPosition(amd.binding.sourceStart(), amd.binding.sourceEnd());
            } else {
              member.setSourceContext(new EclipseSourceContext(unit.compilationResult, 0));
              member.setPosition(0, 0);
            }
          }
          declaredMethods.add(member);
        }
      }
    }

    FieldBinding[] fields = binding.fields();
    for (int i = 0, len = fields.length; i < len; i++) {
      FieldBinding f = fields[i];
      declaredFields.add(factory.makeResolvedMember(f));
    }

    this.declaredPointcuts = declaredPointcuts.toArray(new ResolvedPointcutDefinition[declaredPointcuts.size()]);
    this.declaredMethods = declaredMethods.toArray(new ResolvedMember[declaredMethods.size()]);
    this.declaredFields = declaredFields.toArray(new ResolvedMember[declaredFields.size()]);
  }

  private ResolvedPointcutDefinition makeResolvedPointcutDefinition(AbstractMethodDeclaration md) {
    if (md.binding == null) {
      return null; // there is another error that has caused this...
      // pr138143
    }

    EclipseSourceContext eSourceContext = new EclipseSourceContext(md.compilationResult);
    Pointcut pc = null;
    if (!md.isAbstract()) {
      String expression = getPointcutStringFromAnnotationStylePointcut(md);
      try {
        pc = new PatternParser(expression, eSourceContext).parsePointcut();
      } catch (ParserException pe) { // error will be reported by other
        // means...
        pc = Pointcut.makeMatchesNothing(Pointcut.SYMBOLIC);
      }
    }

    FormalBinding[] bindings = buildFormalAdviceBindingsFrom(md);

    ResolvedPointcutDefinition rpd = new LazyResolvedPointcutDefinition(factory.fromBinding(md.binding.declaringClass),
        md.modifiers, new String(md.selector), factory.fromBindings(md.binding.parameters), factory
            .fromBinding(md.binding.returnType), pc, new EclipseScope(bindings, md.scope));

    rpd.setPosition(md.sourceStart, md.sourceEnd);
    rpd.setSourceContext(eSourceContext);
    return rpd;
  }

  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 FormalBinding[] buildFormalAdviceBindingsFrom(AbstractMethodDeclaration 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();
    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;
  }

  /**
   * This method may not return all fields, for example it may not include the ajc$initFailureCause or ajc$perSingletonInstance
   * fields - see bug 129613
   */
  public ResolvedMember[] getDeclaredFields() {
    if (declaredFields == null) {
      fillDeclaredMembers();
    }
    return declaredFields;
  }

  /**
   * This method may not return all methods, for example it may not include clinit, aspectOf, hasAspect or ajc$postClinit methods
   * - see bug 129613
   */
  public ResolvedMember[] getDeclaredMethods() {
    if (declaredMethods == null) {
      fillDeclaredMembers();
    }
    return declaredMethods;
  }

  public ResolvedMember[] getDeclaredPointcuts() {
    if (declaredPointcuts == null) {
      fillDeclaredMembers();
    }
    return declaredPointcuts;
  }

  public int getModifiers() {
    // only return the real Java modifiers, not the extra eclipse ones
    return binding.modifiers & ExtraCompilerModifiers.AccJustFlag;
  }

  public String toString() {
    return "EclipseSourceType(" + new String(binding.sourceName()) + ")";
  }

  // XXX make sure this is applied to classes and interfaces
  public void checkPointcutDeclarations() {
    ResolvedMember[] pointcuts = getDeclaredPointcuts();
    boolean sawError = false;
    for (int i = 0, len = pointcuts.length; i < len; i++) {
      if (pointcuts[i] == null) {
        // Something else is broken in this file and will be reported separately
        continue;
      }
      if (pointcuts[i].isAbstract()) {
        if (!this.isAspect()) {
          eclipseWorld().showMessage(IMessage.ERROR, "abstract pointcut only allowed in aspect" + pointcuts[i].getName(),
              pointcuts[i].getSourceLocation(), null);
          sawError = true;
        } else if (!binding.isAbstract()) {
          eclipseWorld().showMessage(IMessage.ERROR, "abstract pointcut in concrete aspect" + pointcuts[i],
              pointcuts[i].getSourceLocation(), null);
          sawError = true;
        }
      }

      for (int j = i + 1; j < len; j++) {
        if (pointcuts[j] == null) {
          // Something else is broken in this file and will be reported separately
          continue;
        }
        if (pointcuts[i].getName().equals(pointcuts[j].getName())) {
          eclipseWorld().showMessage(IMessage.ERROR, "duplicate pointcut name: " + pointcuts[j].getName(),
              pointcuts[i].getSourceLocation(), pointcuts[j].getSourceLocation());
          sawError = true;
        }
      }
    }

    // now check all inherited pointcuts to be sure that they're handled
    // reasonably
    if (sawError || !isAspect()) {
      return;
    }

    // find all pointcuts that override ones from super and check override
    // is legal
    // i.e. same signatures and greater or equal visibility
    // find all inherited abstract pointcuts and make sure they're
    // concretized if I'm concrete
    // find all inherited pointcuts and make sure they don't conflict
    getResolvedTypeX().getExposedPointcuts(); // ??? this is an odd
    // construction

  }

  // ???
  // public CrosscuttingMembers collectCrosscuttingMembers() {
  // return crosscuttingMembers;
  // }

  // public ISourceLocation getSourceLocation() {
  // TypeDeclaration dec = binding.scope.referenceContext;
  // return new EclipseSourceLocation(dec.compilationResult, dec.sourceStart,
  // dec.sourceEnd);
  // }

  public boolean isInterface() {
    return binding.isInterface();
  }

  // XXXAJ5: Should be constants in the eclipse compiler somewhere, once it
  // supports 1.5
  public final static short ACC_ANNOTATION = 0x2000;
  public final static short ACC_ENUM = 0x4000;

  public boolean isEnum() {
    return (binding.getAccessFlags() & ACC_ENUM) != 0;
  }

  public boolean isAnnotation() {
    return (binding.getAccessFlags() & ACC_ANNOTATION) != 0;
  }

  public boolean isAnnotationWithRuntimeRetention() {
    if (!isAnnotation()) {
      return false;
    } else {
      return (binding.getAnnotationTagBits() & TagBits.AnnotationRetentionMASK) == TagBits.AnnotationRuntimeRetention;
    }
  }

  public String getRetentionPolicy() {
    if (isAnnotation()) {
      if ((binding.getAnnotationTagBits() & TagBits.AnnotationRetentionMASK) == TagBits.AnnotationRuntimeRetention) {
        return "RUNTIME";
      }
      if ((binding.getAnnotationTagBits() & TagBits.AnnotationRetentionMASK) == TagBits.AnnotationSourceRetention) {
        return "SOURCE";
      }
      if ((binding.getAnnotationTagBits() & TagBits.AnnotationRetentionMASK) == TagBits.AnnotationClassRetention) {
        return "CLASS";
      }
    }
    return null;
  }

  public boolean canAnnotationTargetType() {
    if (isAnnotation()) {
      return ((binding.getAnnotationTagBits() & TagBits.AnnotationForType) != 0);
    }
    return false;
  }

  public AnnotationTargetKind[] getAnnotationTargetKinds() {
    if (discoveredAnnotationTargetKinds) {
      return annotationTargetKinds;
    }
    discoveredAnnotationTargetKinds = true;
    annotationTargetKinds = null; // null means we have no idea or the
    // @Target annotation hasn't been used
    // if (isAnnotation()) {
    // Annotation[] annotationsOnThisType = declaration.annotations;
    // if (annotationsOnThisType != null) {
    // for (int i = 0; i < annotationsOnThisType.length; i++) {
    // Annotation a = annotationsOnThisType[i];
    // if (a.resolvedType != null) {
    // String packageName = new
    // String(a.resolvedType.qualifiedPackageName()).concat(".");
    // String sourceName = new String(a.resolvedType.qualifiedSourceName());
    // if ((packageName +
    // sourceName).equals(UnresolvedType.AT_TARGET.getName())) {
    // MemberValuePair[] pairs = a.memberValuePairs();
    // for (int j = 0; j < pairs.length; j++) {
    // MemberValuePair pair = pairs[j];
    // targetKind = pair.value.toString();
    // return targetKind;
    // }
    // }
    // }
    // }
    // }
    // }
    // return targetKind;
    if (isAnnotation()) {
      List<AnnotationTargetKind> targetKinds = new ArrayList<AnnotationTargetKind>();

      if ((binding.getAnnotationTagBits() & TagBits.AnnotationForAnnotationType) != 0) {
        targetKinds.add(AnnotationTargetKind.ANNOTATION_TYPE);
      }
      if ((binding.getAnnotationTagBits() & TagBits.AnnotationForConstructor) != 0) {
        targetKinds.add(AnnotationTargetKind.CONSTRUCTOR);
      }
      if ((binding.getAnnotationTagBits() & TagBits.AnnotationForField) != 0) {
        targetKinds.add(AnnotationTargetKind.FIELD);
      }
      if ((binding.getAnnotationTagBits() & TagBits.AnnotationForLocalVariable) != 0) {
        targetKinds.add(AnnotationTargetKind.LOCAL_VARIABLE);
      }
      if ((binding.getAnnotationTagBits() & TagBits.AnnotationForMethod) != 0) {
        targetKinds.add(AnnotationTargetKind.METHOD);
      }
      if ((binding.getAnnotationTagBits() & TagBits.AnnotationForPackage) != 0) {
        targetKinds.add(AnnotationTargetKind.PACKAGE);
      }
      if ((binding.getAnnotationTagBits() & TagBits.AnnotationForParameter) != 0) {
        targetKinds.add(AnnotationTargetKind.PARAMETER);
      }
      if ((binding.getAnnotationTagBits() & TagBits.AnnotationForType) != 0) {
        targetKinds.add(AnnotationTargetKind.TYPE);
      }

      if (!targetKinds.isEmpty()) {
        annotationTargetKinds = new AnnotationTargetKind[targetKinds.size()];
        return targetKinds.toArray(annotationTargetKinds);
      }
    }
    return annotationTargetKinds;
  }

  /**
   * Ensure the annotation types have been resolved, where resolved means the eclipse type bindings have been converted to their
   * ResolvedType representations. This does not deeply resolve the annotations, it only does the type names.
   */
  private void ensureAnnotationTypesResolved() {
    // may need to re-resolve if new annotations have been added
    int declarationAnnoCount = (declaration.annotations == null ? 0 : declaration.annotations.length);
    if (!annotationTypesAreResolved || declarationAnnoCount != annotationTypes.length) {
      Annotation[] as = declaration.annotations;
      if (as == null) {
        annotationTypes = ResolvedType.NONE;
      } else {
        annotationTypes = new ResolvedType[as.length];
        for (int a = 0; a < as.length; a++) {
          TypeBinding tb = as[a].type.resolveType(declaration.staticInitializerScope);
          if (tb == null) {
            annotationTypes[a] = ResolvedType.MISSING;
          } else {
            annotationTypes[a] = factory.fromTypeBindingToRTX(tb);
          }
        }
      }
      annotationTypesAreResolved = true;
    }
  }

  public boolean hasAnnotation(UnresolvedType ofType) {
    ensureAnnotationTypesResolved();
    for (int a = 0, max = annotationTypes.length; a < max; a++) {
      if (ofType.equals(annotationTypes[a])) {
        return true;
      }
    }
    return false;
  }

  /**
   * WARNING: This method does not have a complete implementation.
   *
   * The aim is that it converts Eclipse annotation objects to the AspectJ form of annotations (the type AnnotationAJ). The
   * AnnotationX objects returned are wrappers over either a Bcel annotation type or the AspectJ AnnotationAJ type. The minimal
   * implementation provided here is for processing the RetentionPolicy and Target annotation types - these are the only ones
   * which the weaver will attempt to process from an EclipseSourceType.
   *
   * More notes: The pipeline has required us to implement this. With the pipeline we can be weaving a type and asking questions
   * of annotations before they have been turned into Bcel objects - ie. when they are still in EclipseSourceType form. Without
   * the pipeline we would have converted everything to Bcel objects before proceeding with weaving. Because the pipeline won't
   * start weaving until all aspects have been compiled and the fact that no AspectJ constructs match on the values within
   * annotations, this code only needs to deal with converting system annotations that the weaver needs to process
   * (RetentionPolicy, Target).
   */
  public AnnotationAJ[] getAnnotations() {
    if (annotations != null) {
      return annotations; // only do this once
    }
    if (!annotationsFullyResolved) {
      TypeDeclaration.resolveAnnotations(declaration.staticInitializerScope, declaration.annotations, binding);
      annotationsFullyResolved = true;
    }
    Annotation[] as = declaration.annotations;
    if (as == null || as.length == 0) {
      annotations = AnnotationAJ.EMPTY_ARRAY;
    } else {
      annotations = new AnnotationAJ[as.length];
      for (int i = 0; i < as.length; i++) {
        annotations[i] = convertEclipseAnnotation(as[i], factory.getWorld());
      }
    }
    return annotations;
  }

  /**
   * Convert one eclipse annotation into an AnnotationX object containing an AnnotationAJ object.
   *
   * This code and the helper methods used by it will go *BANG* if they encounter anything not currently supported - this is safer
   * than limping along with a malformed annotation. When the *BANG* is encountered the bug reporter should indicate the kind of
   * annotation they were working with and this code can be enhanced to support it.
   */
  public AnnotationAJ convertEclipseAnnotation(Annotation eclipseAnnotation, World w) {
    // TODO if it is sourcevisible, we shouldn't let it through!!!!!!!!!
    // testcase!
    ResolvedType annotationType = factory.fromTypeBindingToRTX(eclipseAnnotation.type.resolvedType);
    // long bs = (eclipseAnnotation.bits & TagBits.AnnotationRetentionMASK);
    boolean isRuntimeVisible = (eclipseAnnotation.bits & TagBits.AnnotationRetentionMASK) == TagBits.AnnotationRuntimeRetention;
    StandardAnnotation annotationAJ = new StandardAnnotation(annotationType, isRuntimeVisible);
    generateAnnotation(eclipseAnnotation, annotationAJ);
    return annotationAJ;
  }

  static class MissingImplementationException extends RuntimeException {
    MissingImplementationException(String reason) {
      super(reason);
    }
  }

  /**
   * Use the information in the supplied eclipse based annotation to fill in the standard annotation.
   *
   * @param annotation eclipse based annotation representation
   * @param annotationAJ AspectJ based annotation representation
   */
  private void generateAnnotation(Annotation annotation, StandardAnnotation annotationAJ) {
    if (annotation instanceof NormalAnnotation) {
      NormalAnnotation normalAnnotation = (NormalAnnotation) annotation;
      MemberValuePair[] memberValuePairs = normalAnnotation.memberValuePairs;
      if (memberValuePairs != null) {
        int memberValuePairsLength = memberValuePairs.length;
        for (int i = 0; i < memberValuePairsLength; i++) {
          MemberValuePair memberValuePair = memberValuePairs[i];
          MethodBinding methodBinding = memberValuePair.binding;
          if (methodBinding == null) {
            // is this just a marker annotation?
            throw new MissingImplementationException(
                "Please raise an AspectJ bug.  AspectJ does not know how to convert this annotation [" + annotation
                    + "]");
          } else {
            AnnotationValue av = generateElementValue(memberValuePair.value, methodBinding.returnType);
            AnnotationNameValuePair anvp = new AnnotationNameValuePair(new String(memberValuePair.name), av);
            annotationAJ.addNameValuePair(anvp);
          }
        }
      }
    } else if (annotation instanceof SingleMemberAnnotation) {
      // this is a single member annotation (one member value)
      SingleMemberAnnotation singleMemberAnnotation = (SingleMemberAnnotation) annotation;
      MethodBinding methodBinding = singleMemberAnnotation.memberValuePairs()[0].binding;
      if (methodBinding == null) {
        throw new MissingImplementationException(
            "Please raise an AspectJ bug.  AspectJ does not know how to convert this annotation [" + annotation + "]");
      } else {
        AnnotationValue av = generateElementValue(singleMemberAnnotation.memberValue, methodBinding.returnType);
        annotationAJ.addNameValuePair(new AnnotationNameValuePair(new String(
            singleMemberAnnotation.memberValuePairs()[0].name), av));
      }
    } else if (annotation instanceof MarkerAnnotation) {
      return;
    } else {
      // this is something else...
      throw new MissingImplementationException(
          "Please raise an AspectJ bug.  AspectJ does not know how to convert this annotation [" + annotation + "]");
    }
  }

  private AnnotationValue generateElementValue(Expression defaultValue, TypeBinding memberValuePairReturnType) {
    Constant constant = defaultValue.constant;
    TypeBinding defaultValueBinding = defaultValue.resolvedType;
    if (defaultValueBinding == null) {
      throw new MissingImplementationException(
          "Please raise an AspectJ bug.  AspectJ does not know how to convert this annotation value [" + defaultValue
              + "]");
    } else {
      if (memberValuePairReturnType.isArrayType() && !defaultValueBinding.isArrayType()) {
        if (constant != null && constant != Constant.NotAConstant) {
          // Testcase for this clause is MultiProjectIncrementalTests.testAnnotations_pr262154()
          AnnotationValue av = EclipseAnnotationConvertor.generateElementValueForConstantExpression(defaultValue,
              defaultValueBinding);
          return new ArrayAnnotationValue(new AnnotationValue[] { av });
        } else {
          AnnotationValue av = generateElementValueForNonConstantExpression(defaultValue, defaultValueBinding);
          return new ArrayAnnotationValue(new AnnotationValue[] { av });
        }
      } else {
        if (constant != null && constant != Constant.NotAConstant) {
          AnnotationValue av = EclipseAnnotationConvertor.generateElementValueForConstantExpression(defaultValue,
              defaultValueBinding);
          if (av == null) {
            throw new MissingImplementationException(
                "Please raise an AspectJ bug.  AspectJ does not know how to convert this annotation value ["
                    + defaultValue + "]");
          }
          return av;
          // generateElementValue(attributeOffset, defaultValue,
          // constant, memberValuePairReturnType.leafComponentType());
        } else {
          AnnotationValue av = generateElementValueForNonConstantExpression(defaultValue, defaultValueBinding);
          return av;
        }
      }
    }
  }

  private AnnotationValue generateElementValueForNonConstantExpression(Expression defaultValue, TypeBinding defaultValueBinding) {
    if (defaultValueBinding != null) {
      if (defaultValueBinding.isEnum()) {
        FieldBinding fieldBinding = null;
        if (defaultValue instanceof QualifiedNameReference) {
          QualifiedNameReference nameReference = (QualifiedNameReference) defaultValue;
          fieldBinding = (FieldBinding) nameReference.binding;
        } else if (defaultValue instanceof SingleNameReference) {
          SingleNameReference nameReference = (SingleNameReference) defaultValue;
          fieldBinding = (FieldBinding) nameReference.binding;
        } else {
          throw new MissingImplementationException(
              "Please raise an AspectJ bug.  AspectJ does not know how to convert this annotation value ["
                  + defaultValue + "]");
        }
        if (fieldBinding != null) {
          String sig = new String(fieldBinding.type.signature());
          AnnotationValue enumValue = new EnumAnnotationValue(sig, new String(fieldBinding.name));
          return enumValue;
        }
        throw new MissingImplementationException(
            "Please raise an AspectJ bug.  AspectJ does not know how to convert this annotation value [" + defaultValue
                + "]");
      } else if (defaultValueBinding.isAnnotationType()) {
        throw new MissingImplementationException(
            "Please raise an AspectJ bug.  AspectJ does not know how to convert this annotation value [" + defaultValue
                + "]");
        // contents[contentsOffset++] = (byte) '@';
        // generateAnnotation((Annotation) defaultValue,
        // attributeOffset);
      } else if (defaultValueBinding.isArrayType()) {
        // array type
        if (defaultValue instanceof ArrayInitializer) {
          ArrayInitializer arrayInitializer = (ArrayInitializer) defaultValue;
          int arrayLength = arrayInitializer.expressions != null ? arrayInitializer.expressions.length : 0;
          AnnotationValue[] values = new AnnotationValue[arrayLength];
          for (int i = 0; i < arrayLength; i++) {
            values[i] = generateElementValue(arrayInitializer.expressions[i], defaultValueBinding.leafComponentType());// ,
            // attributeOffset
            // )
            // ;
          }
          ArrayAnnotationValue aav = new ArrayAnnotationValue(values);
          return aav;
        } else {
          throw new MissingImplementationException(
              "Please raise an AspectJ bug.  AspectJ does not know how to convert this annotation value ["
                  + defaultValue + "]");
        }
        // } else if (defaultValue instanceof MagicLiteral) {
        // if (defaultValue instanceof FalseLiteral) {
        // new AnnotationValue
        // } else if (defaultValue instanceof TrueLiteral) {
        //         
        // } else {
        // throw new MissingImplementationException(
        // "Please raise an AspectJ bug.  AspectJ does not know how to convert this annotation value ["
        // +defaultValue+"]");
        // }
      } else {
        // class type
        throw new MissingImplementationException(
            "Please raise an AspectJ bug.  AspectJ does not know how to convert this annotation value [" + defaultValue
                + "]");
        // if (contentsOffset + 3 >= this.contents.length) {
        // resizeContents(3);
        // }
        // contents[contentsOffset++] = (byte) 'c';
        // if (defaultValue instanceof ClassLiteralAccess) {
        // ClassLiteralAccess classLiteralAccess = (ClassLiteralAccess)
        // defaultValue;
        // final int classInfoIndex =
        // constantPool.literalIndex(classLiteralAccess
        // .targetType.signature());
        // contents[contentsOffset++] = (byte) (classInfoIndex >> 8);
        // contents[contentsOffset++] = (byte) classInfoIndex;
        // } else {
        // contentsOffset = attributeOffset;
        // }
      }
    } else {
      throw new MissingImplementationException(
          "Please raise an AspectJ bug.  AspectJ does not know how to convert this annotation value [" + defaultValue
              + "]");
      // contentsOffset = attributeOffset;
    }
  }

  public ResolvedType[] getAnnotationTypes() {
    ensureAnnotationTypesResolved();
    return annotationTypes;
  }

  public PerClause getPerClause() {
    // should probably be: ((AspectDeclaration)declaration).perClause;
    // but we don't need this level of detail, and working with real per
    // clauses
    // at this stage of compilation is not worth the trouble
    if (!isAnnotationStyleAspect()) {
      if (declaration instanceof AspectDeclaration) {
        PerClause pc = ((AspectDeclaration) declaration).perClause;
        if (pc != null) {
          return pc;
        }
      }
      return new PerSingleton();
    } else {
      // for @Aspect, we do need the real kind though we don't need the
      // real perClause
      // at least try to get the right perclause
      PerClause pc = null;
      if (declaration instanceof AspectDeclaration) {
        pc = ((AspectDeclaration) declaration).perClause;
      }
      if (pc == null) {
        PerClause.Kind kind = getPerClauseForTypeDeclaration(declaration);
        // returning a perFromSuper is enough to get the correct kind..
        // (that's really a hack - AV)
        return new PerFromSuper(kind);
      }
      return pc;
    }
  }

  PerClause.Kind getPerClauseForTypeDeclaration(TypeDeclaration typeDeclaration) {
    Annotation[] annotations = typeDeclaration.annotations;
    for (int i = 0; i < annotations.length; i++) {
      Annotation annotation = annotations[i];
      if (CharOperation.equals(aspectSig, annotation.resolvedType.signature())) {
        // found @Aspect(...)
        if (annotation.memberValuePairs() == null || annotation.memberValuePairs().length == 0) {
          // it is an @Aspect or @Aspect()
          // needs to use PerFromSuper if declaration extends a super
          // aspect
          PerClause.Kind kind = lookupPerClauseKind(typeDeclaration.binding.superclass);
          // if no super aspect, we have a @Aspect() means singleton
          if (kind == null) {
            return PerClause.SINGLETON;
          } else {
            return kind;
          }
        } else if (annotation instanceof SingleMemberAnnotation) {
          // it is an @Aspect(...something...)
          SingleMemberAnnotation theAnnotation = (SingleMemberAnnotation) annotation;
          String clause = new String(((StringLiteral) theAnnotation.memberValue).source());// TODO
          // cast
          // safe
          // ?
          return determinePerClause(typeDeclaration, clause);
        } else if (annotation instanceof NormalAnnotation) { // this
          // kind
          // if it
          // was
          // added
          // by
          // the
          // visitor
          // !
          // it is an @Aspect(...something...)
          NormalAnnotation theAnnotation = (NormalAnnotation) annotation;
          if (theAnnotation.memberValuePairs == null || theAnnotation.memberValuePairs.length < 1) {
            return PerClause.SINGLETON;
          }
          String clause = new String(((StringLiteral) theAnnotation.memberValuePairs[0].value).source());// TODO
          // cast
          // safe
          // ?
          return determinePerClause(typeDeclaration, clause);
        } else {
          eclipseWorld().showMessage(
              IMessage.ABORT,
              "@Aspect annotation is expected to be SingleMemberAnnotation with 'String value()' as unique element",
              new EclipseSourceLocation(typeDeclaration.compilationResult, typeDeclaration.sourceStart,
                  typeDeclaration.sourceEnd), null);
          return PerClause.SINGLETON;// fallback strategy just to
          // avoid NPE
        }
      }
    }
    return null;// no @Aspect annotation at all (not as aspect)
  }

  private PerClause.Kind determinePerClause(TypeDeclaration typeDeclaration, String clause) {
    if (clause.startsWith("perthis(")) {
      return PerClause.PEROBJECT;
    } else if (clause.startsWith("pertarget(")) {
      return PerClause.PEROBJECT;
    } else if (clause.startsWith("percflow(")) {
      return PerClause.PERCFLOW;
    } else if (clause.startsWith("percflowbelow(")) {
      return PerClause.PERCFLOW;
    } else if (clause.startsWith("pertypewithin(")) {
      return PerClause.PERTYPEWITHIN;
    } else if (clause.startsWith("issingleton(")) {
      return PerClause.SINGLETON;
    } else {
      eclipseWorld().showMessage(
          IMessage.ABORT,
          "cannot determine perClause '" + clause + "'",
          new EclipseSourceLocation(typeDeclaration.compilationResult, typeDeclaration.sourceStart,
              typeDeclaration.sourceEnd), null);
      return PerClause.SINGLETON;// fallback strategy just to avoid NPE
    }
  }

  // adapted from AspectDeclaration
  private PerClause.Kind lookupPerClauseKind(ReferenceBinding binding) {
    final PerClause.Kind kind;
    if (binding instanceof BinaryTypeBinding) {
      ResolvedType superTypeX = factory.fromEclipse(binding);
      PerClause perClause = superTypeX.getPerClause();
      // clause is null for non aspect classes since coming from BCEL
      // attributes
      if (perClause != null) {
        kind = superTypeX.getPerClause().getKind();
      } else {
        kind = null;
      }
    } else if (binding instanceof SourceTypeBinding) {
      SourceTypeBinding sourceSc = (SourceTypeBinding) binding;
      if (sourceSc.scope.referenceContext instanceof AspectDeclaration) {
        // code style
        kind = ((AspectDeclaration) sourceSc.scope.referenceContext).perClause.getKind();
      } else { // if (sourceSc.scope.referenceContext instanceof
        // TypeDeclaration) {
        // if @Aspect: perFromSuper, else if @Aspect(..) get from anno
        // value, else null
        kind = getPerClauseForTypeDeclaration((sourceSc.scope.referenceContext));
      }
    } else {
      // XXX need to handle this too
      kind = null;
    }
    return kind;
  }

  public Collection getDeclares() {
    return declares;
  }

  public Collection getPrivilegedAccesses() {
    return Collections.EMPTY_LIST;
  }

  public Collection getTypeMungers() {
    return typeMungers;
  }

  public boolean doesNotExposeShadowMungers() {
    return true;
  }

  public String getDeclaredGenericSignature() {
    return CharOperation.charToString(binding.genericSignature());
  }

  public boolean isGeneric() {
    return binding.isGenericType();
  }

  public TypeVariable[] getTypeVariables() {
    if (declaration.typeParameters == null) {
      return new TypeVariable[0];
    }
    TypeVariable[] typeVariables = new TypeVariable[declaration.typeParameters.length];
    for (int i = 0; i < typeVariables.length; i++) {
      typeVariables[i] = typeParameter2TypeVariable(declaration.typeParameters[i]);
    }
    return typeVariables;
  }

  private TypeVariable typeParameter2TypeVariable(TypeParameter typeParameter) {
    String name = new String(typeParameter.name);
    ReferenceBinding superclassBinding = typeParameter.binding.superclass;
    UnresolvedType superclass = UnresolvedType.forSignature(new String(superclassBinding.signature()));
    UnresolvedType[] superinterfaces = null;
    ReferenceBinding[] superInterfaceBindings = typeParameter.binding.superInterfaces;
    if (superInterfaceBindings != null) {
      superinterfaces = new UnresolvedType[superInterfaceBindings.length];
      for (int i = 0; i < superInterfaceBindings.length; i++) {
        superinterfaces[i] = UnresolvedType.forSignature(new String(superInterfaceBindings[i].signature()));
      }
    }
    // XXX what about lower binding?
    TypeVariable tv = new TypeVariable(name, superclass, superinterfaces);
    tv.setDeclaringElement(factory.fromBinding(typeParameter.binding.declaringElement));
    tv.setRank(typeParameter.binding.rank);
    return tv;
  }

}
TOP

Related Classes of org.aspectj.ajdt.internal.compiler.lookup.EclipseSourceType

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.