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

org.aspectj.weaver.patterns.WildAnnotationTypePattern 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.BCException;
import org.aspectj.weaver.CompressingDataOutputStream;
import org.aspectj.weaver.ISourceContext;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.VersionedDataInputStream;
import org.aspectj.weaver.WeaverMessages;
import org.aspectj.weaver.World;

/**
 * @author colyer
 * @author Andy Clement
 */
public class WildAnnotationTypePattern extends AnnotationTypePattern {

	private TypePattern typePattern;
	private boolean resolved = false;
	Map annotationValues;

	public WildAnnotationTypePattern(TypePattern typePattern) {
		super();
		this.typePattern = typePattern;
		this.setLocation(typePattern.getSourceContext(), typePattern.start, typePattern.end);
	}

	public WildAnnotationTypePattern(TypePattern typePattern, Map annotationValues) {
		super();
		this.typePattern = typePattern;
		this.annotationValues = annotationValues;
		// PVAL make the location be from start of type pattern to end of values
		this.setLocation(typePattern.getSourceContext(), typePattern.start, typePattern.end);
	}

	public TypePattern getTypePattern() {
		return typePattern;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.aspectj.weaver.patterns.AnnotationTypePattern#matches(org.aspectj.weaver.AnnotatedElement)
	 */
	@Override
	public FuzzyBoolean matches(AnnotatedElement annotated) {
		return matches(annotated, null);
	}

	/**
	 * Resolve any annotation values specified, checking they are all well formed (valid names, valid values)
	 *
	 * @param annotationType the annotation type for which the values have been specified
	 * @param scope the scope within which to resolve type references (eg. Color.GREEN)
	 */
	protected void resolveAnnotationValues(ResolvedType annotationType, IScope scope) {
		if (annotationValues == null) {
			return;
		}
		// Check any values specified are OK:
		// - the value names are for valid annotation fields
		// - the specified values are of the correct type
		// - for enums, check the specified values can be resolved in the specified scope
		Map replacementValues = new HashMap<>();
		Set keys = annotationValues.keySet();
		ResolvedMember[] ms = annotationType.getDeclaredMethods();
		for (String k: keys) {
			String key = k;
			// a trailing ! indicates the the user expressed key!=value rather than key=value as a match constraint
			if (k.endsWith("!")) {
				key = key.substring(0, k.length() - 1);
			}
			String v = annotationValues.get(k);
			boolean validKey = false;
			for (ResolvedMember resolvedMember : ms) {
				if (resolvedMember.getName().equals(key) && resolvedMember.isAbstract()) {
					validKey = true;
					ResolvedType t = resolvedMember.getReturnType().resolve(scope.getWorld());
					if (t.isEnum()) {
						// value must be an enum reference X.Y
						int pos = v.lastIndexOf(".");
						if (pos == -1) {
							IMessage m = MessageUtil.error(
									WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "enum"), getSourceLocation());
							scope.getWorld().getMessageHandler().handleMessage(m);
						} else {
							String typename = v.substring(0, pos);
							ResolvedType rt = scope.lookupType(typename, this).resolve(scope.getWorld());
							v = rt.getSignature() + v.substring(pos + 1); // from 'Color.RED' to 'Lp/Color;RED'
							replacementValues.put(k, v);
							break;
						}
					} else if (t.isPrimitiveType()) {
						if (t.getSignature().equals("I")) {
							try {
								int value = Integer.parseInt(v);
								replacementValues.put(k, Integer.toString(value));
								break;
							} catch (NumberFormatException nfe) {
								IMessage m = MessageUtil.error(
										WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "int"),
										getSourceLocation());
								scope.getWorld().getMessageHandler().handleMessage(m);
							}
						} else if (t.getSignature().equals("F")) {
							try {
								float value = Float.parseFloat(v);
								replacementValues.put(k, Float.toString(value));
								break;
							} catch (NumberFormatException nfe) {
								IMessage m = MessageUtil.error(
										WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "float"),
										getSourceLocation());
								scope.getWorld().getMessageHandler().handleMessage(m);
							}

						} else if (t.getSignature().equals("Z")) {
							if (v.equalsIgnoreCase("true") || v.equalsIgnoreCase("false")) {
								// is it ok !
							} else {
								IMessage m = MessageUtil.error(
										WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "boolean"),
										getSourceLocation());
								scope.getWorld().getMessageHandler().handleMessage(m);
							}
						} else if (t.getSignature().equals("S")) {
							try {
								short value = Short.parseShort(v);
								replacementValues.put(k, Short.toString(value));
								break;
							} catch (NumberFormatException nfe) {
								IMessage m = MessageUtil.error(
										WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "short"),
										getSourceLocation());
								scope.getWorld().getMessageHandler().handleMessage(m);
							}
						} else if (t.getSignature().equals("J")) {
							try {
								replacementValues.put(k, Long.toString(Long.parseLong(v)));
								break;
							} catch (NumberFormatException nfe) {
								IMessage m = MessageUtil.error(
										WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "long"),
										getSourceLocation());
								scope.getWorld().getMessageHandler().handleMessage(m);
							}
						} else if (t.getSignature().equals("D")) {
							try {
								replacementValues.put(k, Double.toString(Double.parseDouble(v)));
								break;
							} catch (NumberFormatException nfe) {
								IMessage m = MessageUtil.error(
										WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "double"),
										getSourceLocation());
								scope.getWorld().getMessageHandler().handleMessage(m);
							}
						} else if (t.getSignature().equals("B")) {
							try {
								replacementValues.put(k, Byte.toString(Byte.parseByte(v)));
								break;
							} catch (NumberFormatException nfe) {
								IMessage m = MessageUtil.error(
										WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "byte"),
										getSourceLocation());
								scope.getWorld().getMessageHandler().handleMessage(m);
							}
						} else if (t.getSignature().equals("C")) {
							if (v.length() != 3) { // '?'
								IMessage m = MessageUtil.error(
										WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "char"),
										getSourceLocation());
								scope.getWorld().getMessageHandler().handleMessage(m);
							} else {
								replacementValues.put(k, v.substring(1, 2));
								break;
							}
						} else {
							throw new RuntimeException("Not implemented for " + t);
						}
					} else if (t.equals(ResolvedType.JL_STRING)) {
						// nothing to do, it will be OK
					} else if (t.equals(ResolvedType.JL_CLASS) || (t.isParameterizedOrGenericType() && t.getRawType().equals(ResolvedType.JL_CLASS))) {
						String typename = v.substring(0, v.lastIndexOf('.')); // strip off '.class'
						ResolvedType rt = scope.lookupType(typename, this).resolve(scope.getWorld());
						if (rt.isMissing()) {
							IMessage m = MessageUtil.error("Unable to resolve type '" + v + "' specified for value '" + k + "'",
									getSourceLocation());
							scope.getWorld().getMessageHandler().handleMessage(m);
						}
						replacementValues.put(k, rt.getSignature());
						break;
					} else {
						if (t.isAnnotation()) {
							if (v.contains("(")) {
								throw new RuntimeException(
										"Compiler limitation: annotation values can only currently be marker annotations (no values): "
												+ v);
							}
							String typename = v.substring(1);
							ResolvedType rt = scope.lookupType(typename, this).resolve(scope.getWorld());
							if (rt.isMissing()) {
								IMessage m = MessageUtil.error(
										"Unable to resolve type '" + v + "' specified for value '" + k + "'", getSourceLocation());
								scope.getWorld().getMessageHandler().handleMessage(m);
							}
							replacementValues.put(k, rt.getSignature());
							break;
//						} else if (t.isArray()) {
							// Looks like {} aren't pseudotokens in the parser so they don't get through for our pointcut parser
//							// @Foo(value=[Foo.class])
//							String typename = v.substring(0, v.lastIndexOf('.')); // strip off '.class'
//							ResolvedType rt = scope.lookupType(typename, this).resolve(scope.getWorld());
//							if (rt.isMissing()) {
//								IMessage m = MessageUtil.error("Unable to resolve type '" + v + "' specified for value '" + k + "'",
//										getSourceLocation());
//								scope.getWorld().getMessageHandler().handleMessage(m);
//							}
//							replacementValues.put(k, rt.getSignature());
						} else {
							scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.UNSUPPORTED_ANNOTATION_VALUE_TYPE, t), getSourceLocation()));
							replacementValues.put(k, "");
						}
					}
				}
			}
			if (!validKey) {
				IMessage m = MessageUtil.error(WeaverMessages.format(WeaverMessages.UNKNOWN_ANNOTATION_VALUE, annotationType, k),
						getSourceLocation());
				scope.getWorld().getMessageHandler().handleMessage(m);
			}
		}
		annotationValues.putAll(replacementValues);
	}

	@Override
	public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) {
		if (!resolved) {
			throw new IllegalStateException("Can't match on an unresolved annotation type pattern");
		}
		if (annotationValues != null && !typePattern.hasFailedResolution()) {
			// PVAL improve this restriction, would allow '*(value=Color.RED)'
			throw new IllegalStateException("Cannot use annotationvalues with a wild annotation pattern");
		}
		if (isForParameterAnnotationMatch()) {
			if (parameterAnnotations != null && parameterAnnotations.length != 0) {
				for (ResolvedType parameterAnnotation : parameterAnnotations) {
					if (typePattern.matches(parameterAnnotation, TypePattern.STATIC).alwaysTrue()) {
						return FuzzyBoolean.YES;
					}
				}
			}
		} else {
			// matches if the type of any of the annotations on the AnnotatedElement is
			// matched by the typePattern.
			ResolvedType[] annTypes = annotated.getAnnotationTypes();
			if (annTypes != null && annTypes.length != 0) {
				for (ResolvedType annType : annTypes) {
					if (typePattern.matches(annType, TypePattern.STATIC).alwaysTrue()) {
						return FuzzyBoolean.YES;
					}
				}
			}
		}
		return FuzzyBoolean.NO;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.aspectj.weaver.patterns.AnnotationTypePattern#resolve(org.aspectj.weaver.World)
	 */
	@Override
	public void resolve(World world) {
		if (!resolved) {
			// attempt resolution - this helps with the Spring bug where they resolve() the pointcut in no scope (SPR-5307)
			if (typePattern instanceof WildTypePattern && (annotationValues == null || annotationValues.isEmpty())) {
				WildTypePattern wildTypePattern = (WildTypePattern) typePattern;
				String fullyQualifiedName = wildTypePattern.maybeGetCleanName();
				if (fullyQualifiedName != null && fullyQualifiedName.contains(".")) {
					ResolvedType resolvedType = world.resolve(UnresolvedType.forName(fullyQualifiedName));
					if (resolvedType != null && !resolvedType.isMissing()) {
						typePattern = new ExactTypePattern(resolvedType, false, false);
					}
				}
			}
			resolved = true;
		}
	}

	/**
	 * This can modify in place, or return a new TypePattern if the type changes.
	 */
	@Override
	public AnnotationTypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding) {
		if (!scope.getWorld().isInJava5Mode()) {
			scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.ANNOTATIONS_NEED_JAVA5), getSourceLocation()));
			return this;
		}
		if (resolved) {
			return this;
		}
		this.typePattern = typePattern.resolveBindings(scope, bindings, false, false);
		resolved = true;
		if (typePattern instanceof ExactTypePattern) {
			ExactTypePattern et = (ExactTypePattern) typePattern;
			if (!et.getExactType().resolve(scope.getWorld()).isAnnotation()) {
				IMessage m = MessageUtil.error(
						WeaverMessages.format(WeaverMessages.REFERENCE_TO_NON_ANNOTATION_TYPE, et.getExactType().getName()),
						getSourceLocation());
				scope.getWorld().getMessageHandler().handleMessage(m);
				resolved = false;
			}
			ResolvedType annotationType = et.getExactType().resolve(scope.getWorld());
			resolveAnnotationValues(annotationType, scope);
			ExactAnnotationTypePattern eatp = new ExactAnnotationTypePattern(annotationType, annotationValues);
			eatp.copyLocationFrom(this);
			if (isForParameterAnnotationMatch()) {
				eatp.setForParameterAnnotationMatch();
			}
			return eatp;
		} else {
			return this;
		}
	}

	@Override
	public AnnotationTypePattern parameterizeWith(Map typeVariableMap, World w) {
		WildAnnotationTypePattern ret = new WildAnnotationTypePattern(typePattern.parameterizeWith(typeVariableMap, w));
		ret.copyLocationFrom(this);
		ret.resolved = resolved;
		return ret;
	}

	private static final byte VERSION = 1; // rev if ser. form changes

	@Override
	public void write(CompressingDataOutputStream s) throws IOException {
		s.writeByte(AnnotationTypePattern.WILD);
		s.writeByte(VERSION);
		typePattern.write(s);
		writeLocation(s);
		s.writeBoolean(isForParameterAnnotationMatch());
		// PVAL
		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 {
		WildAnnotationTypePattern ret;
		byte version = s.readByte();
		if (version > VERSION) {
			throw new BCException("ExactAnnotationTypePattern was written by a newer version of AspectJ");
		}
		TypePattern t = TypePattern.read(s, context);
		ret = new WildAnnotationTypePattern(t);
		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;
	}

	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof WildAnnotationTypePattern)) {
			return false;
		}
		WildAnnotationTypePattern other = (WildAnnotationTypePattern) obj;
		return other.typePattern.equals(typePattern)
				&& this.isForParameterAnnotationMatch() == other.isForParameterAnnotationMatch()
				&& (annotationValues == null ? other.annotationValues == null : annotationValues.equals(other.annotationValues));
	}

	@Override
	public int hashCode() {
		return (((17 + 37 * typePattern.hashCode()) * 37 + (isForParameterAnnotationMatch() ? 0 : 1)) * 37)
				+ (annotationValues == null ? 0 : annotationValues.hashCode());
	}

	@Override
	public String toString() {
		return "@(" + typePattern.toString() + ")";
	}

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

	public Object traverse(PatternNodeVisitor visitor, Object data) {
		Object ret = accept(visitor, data);
		if (this.typePattern != null)
			this.typePattern.traverse(visitor, ret);
		return ret;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy