All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aspectj.weaver.patterns.ExactAnnotationTypePattern Maven / Gradle / Ivy

Go to download

AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based @AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step. This library is a superset of AspectJ weaver and hence also of AspectJ runtime.

There is a newer version: 1.9.22.1
Show newest version
/* *******************************************************************
 * 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 annotationValues;

	// OPTIMIZE is annotationtype really unresolved???? surely it is resolved by
	// now...
	public ExactAnnotationTypePattern(UnresolvedType annotationType, Map 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 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 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 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 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 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 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);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy