Package org.aspectj.weaver.patterns

Source Code of org.aspectj.weaver.patterns.ExactAnnotationTypePattern

/* *******************************************************************
* Copyright (c) 2004 IBM Corporation.
* 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
* ******************************************************************/
package org.aspectj.weaver.patterns;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.util.FuzzyBoolean;
import org.aspectj.weaver.AnnotatedElement;
import org.aspectj.weaver.AnnotationAJ;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.CompressingDataOutputStream;
import org.aspectj.weaver.ISourceContext;
import org.aspectj.weaver.ReferenceType;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.TypeVariableReference;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.VersionedDataInputStream;
import org.aspectj.weaver.WeaverMessages;
import org.aspectj.weaver.World;
import org.aspectj.weaver.AjAttribute.WeaverVersionInfo;

/**
* Matches an annotation of a given type
*/
public class ExactAnnotationTypePattern extends AnnotationTypePattern {

  protected UnresolvedType annotationType;
  protected String formalName;
  protected boolean resolved = false;
  protected boolean bindingPattern = false;
  private Map<String, String> annotationValues;

  // OPTIMIZE is annotationtype really unresolved???? surely it is resolved by
  // now...
  public ExactAnnotationTypePattern(UnresolvedType annotationType, Map<String, String> annotationValues) {
    this.annotationType = annotationType;
    this.annotationValues = annotationValues;
    this.resolved = (annotationType instanceof ResolvedType);
  }

  // Used when deserializing, values will be added
  private ExactAnnotationTypePattern(UnresolvedType annotationType) {
    this.annotationType = annotationType;
    this.resolved = (annotationType instanceof ResolvedType);
  }

  protected ExactAnnotationTypePattern(String formalName) {
    this.formalName = formalName;
    this.resolved = false;
    this.bindingPattern = true;
    // will be turned into BindingAnnotationTypePattern during resolution
  }

  public ResolvedType getResolvedAnnotationType() {
    if (!resolved) {
      throw new IllegalStateException("I need to be resolved first!");
    }
    return (ResolvedType) annotationType;
  }

  public UnresolvedType getAnnotationType() {
    return annotationType;
  }

  public Map<String, String> getAnnotationValues() {
    return annotationValues;
  }

  @Override
  public FuzzyBoolean fastMatches(AnnotatedElement annotated) {
    if (annotated.hasAnnotation(annotationType) && annotationValues == null) {
      return FuzzyBoolean.YES;
    } else {
      // could be inherited, but we don't know that until we are
      // resolved, and we're not yet...
      return FuzzyBoolean.MAYBE;
    }
  }

  @Override
  public FuzzyBoolean matches(AnnotatedElement annotated) {
    return matches(annotated, null);
  }

  @Override
  public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) {
    if (!isForParameterAnnotationMatch()) {
      boolean checkSupers = false;
      if (getResolvedAnnotationType().isInheritedAnnotation()) {
        if (annotated instanceof ResolvedType) {
          checkSupers = true;
        }
      }

      if (annotated.hasAnnotation(annotationType)) {
        if (annotationType instanceof ReferenceType) {
          ReferenceType rt = (ReferenceType) annotationType;
          if (rt.getRetentionPolicy() != null && rt.getRetentionPolicy().equals("SOURCE")) {
            rt.getWorld()
                .getMessageHandler()
                .handleMessage(
                    MessageUtil.warn(WeaverMessages.format(WeaverMessages.NO_MATCH_BECAUSE_SOURCE_RETENTION,
                        annotationType, annotated), getSourceLocation()));
            return FuzzyBoolean.NO;
          }
        }

        // Are we also matching annotation values?
        if (annotationValues != null) {
          AnnotationAJ theAnnotation = annotated.getAnnotationOfType(annotationType);

          // Check each one
          Set<String> keys = annotationValues.keySet();
          for (String k : keys) {
            boolean notEqual = false;
            String v = annotationValues.get(k);
            // if the key has a trailing '!' then it means the source expressed k!=v - so we are looking for
            // something other than the value specified
            if (k.endsWith("!")) {
              notEqual = true;
              k = k.substring(0, k.length() - 1);
            }
            if (theAnnotation.hasNamedValue(k)) {
              // Simple case, value is 'name=value' and the
              // annotation specified the same thing
              if (notEqual) {
                if (theAnnotation.hasNameValuePair(k, v)) {
                  return FuzzyBoolean.NO;
                }
              } else {
                if (!theAnnotation.hasNameValuePair(k, v)) {
                  return FuzzyBoolean.NO;
                }
              }
            } else {
              // Complex case, look at the default value
              ResolvedMember[] ms = ((ResolvedType) annotationType).getDeclaredMethods();
              boolean foundMatch = false;
              for (int i = 0; i < ms.length && !foundMatch; i++) {
                if (ms[i].isAbstract() && ms[i].getParameterTypes().length == 0 && ms[i].getName().equals(k)) {
                  // we might be onto something
                  String s = ms[i].getAnnotationDefaultValue();
                  if (s != null && s.equals(v)) {
                    foundMatch = true;
                  }
                }
              }
              if (notEqual) {
                if (foundMatch) {
                  return FuzzyBoolean.NO;
                }
              } else {
                if (!foundMatch) {
                  return FuzzyBoolean.NO;
                }
              }
            }
          }
        }
        return FuzzyBoolean.YES;
      } else if (checkSupers) {
        ResolvedType toMatchAgainst = ((ResolvedType) annotated).getSuperclass();
        while (toMatchAgainst != null) {
          if (toMatchAgainst.hasAnnotation(annotationType)) {
            // Are we also matching annotation values?
            if (annotationValues != null) {
              AnnotationAJ theAnnotation = toMatchAgainst.getAnnotationOfType(annotationType);

              // Check each one
              Set<String> keys = annotationValues.keySet();
              for (String k : keys) {
                String v = annotationValues.get(k);
                if (theAnnotation.hasNamedValue(k)) {
                  // Simple case, value is 'name=value' and
                  // the annotation specified the same thing
                  if (!theAnnotation.hasNameValuePair(k, v)) {
                    return FuzzyBoolean.NO;
                  }
                } else {
                  // Complex case, look at the default value
                  ResolvedMember[] ms = ((ResolvedType) annotationType).getDeclaredMethods();
                  boolean foundMatch = false;
                  for (int i = 0; i < ms.length && !foundMatch; i++) {
                    if (ms[i].isAbstract() && ms[i].getParameterTypes().length == 0
                        && ms[i].getName().equals(k)) {
                      // we might be onto something
                      String s = ms[i].getAnnotationDefaultValue();
                      if (s != null && s.equals(v)) {
                        foundMatch = true;
                      }
                    }
                  }
                  if (!foundMatch) {
                    return FuzzyBoolean.NO;
                  }
                }
              }
            }
            return FuzzyBoolean.YES;
          }
          toMatchAgainst = toMatchAgainst.getSuperclass();
        }
      }
    } else {
      // check parameter annotations
      if (parameterAnnotations == null) {
        return FuzzyBoolean.NO;
      }
      for (int i = 0; i < parameterAnnotations.length; i++) {
        if (annotationType.equals(parameterAnnotations[i])) {
          // Are we also matching annotation values?
          if (annotationValues != null) {
            parameterAnnotations[i]
                .getWorld()
                .getMessageHandler()
                .handleMessage(
                    MessageUtil
                        .error("Compiler limitation: annotation value matching for parameter annotations not yet supported"));
            return FuzzyBoolean.NO;
          }
          return FuzzyBoolean.YES;
        }
      }
    }

    return FuzzyBoolean.NO;
  }

  // this version should be called for @this, @target, @args
  public FuzzyBoolean matchesRuntimeType(AnnotatedElement annotated) {
    if (getResolvedAnnotationType().isInheritedAnnotation()) {
      // a static match is good enough
      if (matches(annotated).alwaysTrue()) {
        return FuzzyBoolean.YES;
      }
    }
    // a subtype could match at runtime
    return FuzzyBoolean.MAYBE;
  }

  @Override
  public void resolve(World world) {
    if (!resolved) {
      annotationType = annotationType.resolve(world);
      resolved = true;
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see org.aspectj.weaver.patterns.AnnotationTypePattern#resolveBindings(org .aspectj.weaver.patterns.IScope,
   * org.aspectj.weaver.patterns.Bindings, boolean)
   */
  @Override
  public AnnotationTypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding) {
    if (resolved) {
      return this;
    }
    resolved = true;
    String simpleName = maybeGetSimpleName();
    if (simpleName != null) {
      FormalBinding formalBinding = scope.lookupFormal(simpleName);
      if (formalBinding != null) {
        if (bindings == null) {
          scope.message(IMessage.ERROR, this, "negation doesn't allow binding");
          return this;
        }
        if (!allowBinding) {
          scope.message(IMessage.ERROR, this, "name binding only allowed in @pcds, args, this, and target");
          return this;
        }
        formalName = simpleName;
        bindingPattern = true;
        verifyIsAnnotationType(formalBinding.getType().resolve(scope.getWorld()), scope);
        BindingAnnotationTypePattern binding = new BindingAnnotationTypePattern(formalBinding);
        binding.copyLocationFrom(this);
        bindings.register(binding, scope);
        binding.resolveBinding(scope.getWorld());
        if (isForParameterAnnotationMatch()) {
          binding.setForParameterAnnotationMatch();
        }

        return binding;
      }
    }

    // Non binding case
    String cleanname = annotationType.getName();
    annotationType = scope.getWorld().resolve(annotationType, true);

    // We may not have found it if it is in a package, lets look it up...
    if (ResolvedType.isMissing(annotationType)) {
      UnresolvedType type = null;
      while (ResolvedType.isMissing(type = scope.lookupType(cleanname, this))) {
        int lastDot = cleanname.lastIndexOf('.');
        if (lastDot == -1) {
          break;
        }
        cleanname = cleanname.substring(0, lastDot) + "$" + cleanname.substring(lastDot + 1);
      }
      annotationType = scope.getWorld().resolve(type, true);
    }

    verifyIsAnnotationType((ResolvedType) annotationType, scope);
    return this;
  }

  @Override
  public AnnotationTypePattern parameterizeWith(Map typeVariableMap, World w) {
    UnresolvedType newAnnotationType = annotationType;
    if (annotationType.isTypeVariableReference()) {
      TypeVariableReference t = (TypeVariableReference) annotationType;
      String key = t.getTypeVariable().getName();
      if (typeVariableMap.containsKey(key)) {
        newAnnotationType = (UnresolvedType) typeVariableMap.get(key);
      }
    } else if (annotationType.isParameterizedType()) {
      newAnnotationType = annotationType.parameterize(typeVariableMap);
    }
    ExactAnnotationTypePattern ret = new ExactAnnotationTypePattern(newAnnotationType, annotationValues);
    ret.formalName = formalName;
    ret.bindingPattern = bindingPattern;
    ret.copyLocationFrom(this);
    if (isForParameterAnnotationMatch()) {
      ret.setForParameterAnnotationMatch();
    }
    return ret;
  }

  protected String maybeGetSimpleName() {
    if (formalName != null) {
      return formalName;
    }
    String ret = annotationType.getName();
    return (ret.indexOf('.') == -1) ? ret : null;
  }

  protected void verifyIsAnnotationType(ResolvedType type, IScope scope) {
    if (!type.isAnnotation()) {
      IMessage m = MessageUtil.error(WeaverMessages.format(WeaverMessages.REFERENCE_TO_NON_ANNOTATION_TYPE, type.getName()),
          getSourceLocation());
      scope.getWorld().getMessageHandler().handleMessage(m);
      resolved = false;
    }
  }

  private static byte VERSION = 1; // rev if serialisation form changes

  /*
   * (non-Javadoc)
   *
   * @see org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream)
   */
  @Override
  public void write(CompressingDataOutputStream s) throws IOException {
    s.writeByte(AnnotationTypePattern.EXACT);
    s.writeByte(VERSION);
    s.writeBoolean(bindingPattern);
    if (bindingPattern) {
      s.writeUTF(formalName);
    } else {
      annotationType.write(s);
    }
    writeLocation(s);
    s.writeBoolean(isForParameterAnnotationMatch());
    if (annotationValues == null) {
      s.writeInt(0);
    } else {
      s.writeInt(annotationValues.size());
      Set<String> key = annotationValues.keySet();
      for (Iterator<String> keys = key.iterator(); keys.hasNext();) {
        String k = keys.next();
        s.writeUTF(k);
        s.writeUTF(annotationValues.get(k));
      }
    }
  }

  public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException {
    ExactAnnotationTypePattern ret;
    byte version = s.readByte();
    if (version > VERSION) {
      throw new BCException("ExactAnnotationTypePattern was written by a newer version of AspectJ");
    }
    boolean isBindingPattern = s.readBoolean();
    if (isBindingPattern) {
      ret = new ExactAnnotationTypePattern(s.readUTF());
    } else {
      ret = new ExactAnnotationTypePattern(UnresolvedType.read(s));
    }
    ret.readLocation(context, s);
    if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ160) {
      if (s.readBoolean()) {
        ret.setForParameterAnnotationMatch();
      }
    }
    if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ160M2) {
      int annotationValueCount = s.readInt();
      if (annotationValueCount > 0) {
        Map<String, String> aValues = new HashMap<String, String>();
        for (int i = 0; i < annotationValueCount; i++) {
          String key = s.readUTF();
          String val = s.readUTF();
          aValues.put(key, val);
        }
        ret.annotationValues = aValues;
      }
    }
    return ret;
  }

  /*
   * (non-Javadoc)
   *
   * @see java.lang.Object#equals(java.lang.Object)
   */
  @Override
  public boolean equals(Object obj) {
    if (!(obj instanceof ExactAnnotationTypePattern)) {
      return false;
    }
    ExactAnnotationTypePattern other = (ExactAnnotationTypePattern) obj;
    return (other.annotationType.equals(annotationType))
        && isForParameterAnnotationMatch() == other.isForParameterAnnotationMatch()
        && (annotationValues == null ? other.annotationValues == null : annotationValues.equals(other.annotationValues));
  }

  /*
   * (non-Javadoc)
   *
   * @see java.lang.Object#hashCode()
   */
  @Override
  public int hashCode() {
    return (((annotationType.hashCode()) * 37 + (isForParameterAnnotationMatch() ? 0 : 1)) * 37)
        + (annotationValues == null ? 0 : annotationValues.hashCode());
  }

  @Override
  public String toString() {
    if (!resolved && formalName != null) {
      return formalName;
    }
    String ret = "@" + annotationType.toString();
    if (formalName != null) {
      ret = ret + " " + formalName;
    }
    return ret;
  }

  @Override
  public Object accept(PatternNodeVisitor visitor, Object data) {
    return visitor.visit(this, data);
  }
}
TOP

Related Classes of org.aspectj.weaver.patterns.ExactAnnotationTypePattern

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.