123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468 |
- /* *******************************************************************
- * 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 v 2.0
- * which accompanies this distribution and is available at
- * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
- *
- * ******************************************************************/
- package org.aspectj.weaver.patterns;
-
- import java.io.IOException;
- import java.util.HashMap;
- 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.AjAttribute.WeaverVersionInfo;
- 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;
-
- /**
- * 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 (ResolvedType parameterAnnotation : parameterAnnotations) {
- if (annotationType.equals(parameterAnnotation)) {
- // Are we also matching annotation values?
- if (annotationValues != null) {
- parameterAnnotation
- .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<String,UnresolvedType> typeVariableMap, World w) {
- UnresolvedType newAnnotationType = annotationType;
- if (annotationType.isTypeVariableReference()) {
- TypeVariableReference t = (TypeVariableReference) annotationType;
- String key = t.getTypeVariable().getName();
- if (typeVariableMap.containsKey(key)) {
- newAnnotationType = 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 (String k : key) {
- 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<>();
- 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);
- }
- }
|