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

org.aspectj.weaver.patterns.DeclareAnnotation 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) 2005 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
 *
 * Contributors:
 *     Adrian Colyer     initial implementation
 *      Andy Clement     got it working
 * ******************************************************************/
package org.aspectj.weaver.patterns;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.aspectj.bridge.MessageUtil;
import org.aspectj.weaver.AjAttribute.WeaverVersionInfo;
import org.aspectj.weaver.AnnotationAJ;
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;

/**
 * Represents a declare annotation statement, one of atField, atMethod, atConstructor or atType.
 *
 * @author Andy Clement
 */
public class DeclareAnnotation extends Declare {

	public static final Kind AT_TYPE = new Kind(1, "type");
	public static final Kind AT_FIELD = new Kind(2, "field");
	public static final Kind AT_METHOD = new Kind(3, "method");
	public static final Kind AT_CONSTRUCTOR = new Kind(4, "constructor");
	public static final Kind AT_REMOVE_FROM_FIELD = new Kind(5, "removeFromField");
	private static final String AJC_DECLARE_ANNOTATION = "Lorg/aspectj/internal/lang/annotation/ajcDeclareAnnotation;";

	private Kind kind;
	// for declare @type
	private TypePattern typePattern;
	// for declare @field,@method,@constructor
	private ISignaturePattern signaturePattern;
	private ResolvedType containingAspect;
	private List annotationMethods;
	private List annotationStrings;
	private AnnotationAJ annotation; // discovered when required
	private ResolvedType annotationType; // discovered when required

	// not serialized:
	private int annotationStart;
	private int annotationEnd;

	/**
	 * Constructor for declare atType.
	 */
	public DeclareAnnotation(Kind kind, TypePattern typePattern) {
		this.typePattern = typePattern;
		this.kind = kind;
		init();
	}

	/**
	 * Constructor for declare atMethod/atField/atConstructor.
	 */
	public DeclareAnnotation(Kind kind, ISignaturePattern sigPattern) {
		this.signaturePattern = sigPattern;
		this.kind = kind;
		init();
	}

	private void init() {
		this.annotationMethods = new ArrayList<>();
		annotationMethods.add("unknown");
		this.annotationStrings = new ArrayList<>();
		annotationStrings.add("@");
	}

	/**
	 * Returns the string, useful before the real annotation has been resolved
	 */
	public String getAnnotationString() {
		return annotationStrings.get(0);
	}

	public boolean isExactPattern() {
		return typePattern instanceof ExactTypePattern;
	}

	public String getAnnotationMethod() {
		return annotationMethods.get(0);
	}

	@Override
	public String toString() {
		StringBuilder ret = new StringBuilder();
		ret.append("declare @");
		ret.append(kind);
		ret.append(" : ");
		ret.append(typePattern != null ? typePattern.toString() : signaturePattern.toString());
		ret.append(" : ");
		ret.append(annotationStrings.get(0));
		return ret.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.signaturePattern != null && this.signaturePattern instanceof SignaturePattern)
			((SignaturePattern) this.signaturePattern).traverse(visitor, ret);
		if (this.typePattern != null)
			this.typePattern.traverse(visitor, ret);
		return ret;
	}

	@Override
	public void resolve(IScope scope) {
		if (!scope.getWorld().isInJava5Mode()) {
			String msg = null;
			if (kind == AT_TYPE) {
				msg = WeaverMessages.DECLARE_ATTYPE_ONLY_SUPPORTED_AT_JAVA5_LEVEL;
			} else if (kind == AT_METHOD) {
				msg = WeaverMessages.DECLARE_ATMETHOD_ONLY_SUPPORTED_AT_JAVA5_LEVEL;
			} else if (kind == AT_FIELD) {
				msg = WeaverMessages.DECLARE_ATFIELD_ONLY_SUPPORTED_AT_JAVA5_LEVEL;
			} else if (kind == AT_CONSTRUCTOR) {
				msg = WeaverMessages.DECLARE_ATCONS_ONLY_SUPPORTED_AT_JAVA5_LEVEL;
			}
			scope.message(MessageUtil.error(WeaverMessages.format(msg), getSourceLocation()));
			return;
		}
		if (typePattern != null) {
			typePattern = typePattern.resolveBindings(scope, Bindings.NONE, false, false);
		}
		if (signaturePattern != null) {
			signaturePattern = signaturePattern.resolveBindings(scope, Bindings.NONE);
		}
		this.containingAspect = scope.getEnclosingType();
	}

	@Override
	public Declare parameterizeWith(Map typeVariableBindingMap, World w) {
		DeclareAnnotation ret;
		if (this.kind == AT_TYPE) {
			ret = new DeclareAnnotation(kind, this.typePattern.parameterizeWith(typeVariableBindingMap, w));
		} else {
			ret = new DeclareAnnotation(kind, this.signaturePattern.parameterizeWith(typeVariableBindingMap, w));
		}
		ret.annotationMethods = this.annotationMethods;
		ret.annotationStrings = this.annotationStrings;
		ret.annotation = this.annotation;
		ret.containingAspect = this.containingAspect;
		ret.copyLocationFrom(this);
		return ret;
	}

	@Override
	public boolean isAdviceLike() {
		return false;
	}

	public void setAnnotationString(String annotationString) {
		this.annotationStrings.set(0, annotationString);
	}

	public void setAnnotationLocation(int start, int end) {
		this.annotationStart = start;
		this.annotationEnd = end;
	}

	public int getAnnotationSourceStart() {
		return annotationStart;
	}

	public int getAnnotationSourceEnd() {
		return annotationEnd;
	}

	public void setAnnotationMethod(String methodName) {
		this.annotationMethods.set(0, methodName);
	}

	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof DeclareAnnotation)) {
			return false;
		}
		DeclareAnnotation other = (DeclareAnnotation) obj;
		if (!this.kind.equals(other.kind)) {
			return false;
		}
		if (!this.annotationStrings.get(0).equals(other.annotationStrings.get(0))) {
			return false;
		}
		if (!this.annotationMethods.get(0).equals(other.annotationMethods.get(0))) {
			return false;
		}
		if (this.typePattern != null) {
			if (!typePattern.equals(other.typePattern)) {
				return false;
			}
		}
		if (this.signaturePattern != null) {
			if (!signaturePattern.equals(other.signaturePattern)) {
				return false;
			}
		}
		return true;
	}

	@Override
	public int hashCode() {
		int result = 19;
		result = 37 * result + kind.hashCode();
		result = 37 * result + annotationStrings.get(0).hashCode();
		result = 37 * result + annotationMethods.get(0).hashCode();
		if (typePattern != null) {
			result = 37 * result + typePattern.hashCode();
		}
		if (signaturePattern != null) {
			result = 37 * result + signaturePattern.hashCode();
		}
		return result;
	}

	@Override
	public void write(CompressingDataOutputStream s) throws IOException {
		s.writeByte(Declare.ANNOTATION);
		if (kind.id == AT_FIELD.id && isRemover) {
			s.writeInt(AT_REMOVE_FROM_FIELD.id);
		} else {
			s.writeInt(kind.id);
		}
		int max = 0;
		s.writeByte(max = annotationStrings.size());
		for (int i = 0; i < max; i++) {
			s.writeUTF(annotationStrings.get(i));
		}
		s.writeByte(max = annotationMethods.size());
		for (int i = 0; i < max; i++) {
			s.writeUTF(annotationMethods.get(i));
		}
		if (typePattern != null) {
			typePattern.write(s);
		}
		if (signaturePattern != null) {
			AbstractSignaturePattern.writeCompoundSignaturePattern(s, signaturePattern);
		}
		writeLocation(s);
	}

	public static Declare read(VersionedDataInputStream s, ISourceContext context) throws IOException {
		DeclareAnnotation ret = null;
		boolean isRemover = false;
		int kind = s.readInt();
		if (kind == AT_REMOVE_FROM_FIELD.id) {
			kind = AT_FIELD.id;
			isRemover = true;
		}
		// old format was just a single string and method
		if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_AJ169) {
			// int numAnnotationStrings =
			s.readByte();
		}
		String annotationString = s.readUTF();
		if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_AJ169) {
			// int numAnnotationMethods =
			s.readByte();
		}
		String annotationMethod = s.readUTF();
		TypePattern tp = null;
		SignaturePattern sp = null;
		switch (kind) {
		case 1:
			tp = TypePattern.read(s, context);
			ret = new DeclareAnnotation(AT_TYPE, tp);
			break;
		case 2:
			if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_AJ169) {
				ret = new DeclareAnnotation(AT_FIELD, AbstractSignaturePattern.readCompoundSignaturePattern(s, context));
			} else {
				sp = SignaturePattern.read(s, context);
				ret = new DeclareAnnotation(AT_FIELD, sp);
			}
			if (isRemover) {
				ret.setRemover(true);
			}
			break;
		case 3:
			if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_AJ169) {
				ret = new DeclareAnnotation(AT_METHOD, AbstractSignaturePattern.readCompoundSignaturePattern(s, context));
			} else {
				sp = SignaturePattern.read(s, context);
				ret = new DeclareAnnotation(AT_METHOD, sp);
			}
			break;
		case 4:
			if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_AJ169) {
				ret = new DeclareAnnotation(AT_CONSTRUCTOR, AbstractSignaturePattern.readCompoundSignaturePattern(s, context));
			} else {
				sp = SignaturePattern.read(s, context);
				ret = new DeclareAnnotation(AT_CONSTRUCTOR, sp);
			}
			break;

		}
		ret.setAnnotationString(annotationString);
		ret.setAnnotationMethod(annotationMethod);
		ret.readLocation(context, s);
		return ret;
	}

	/**
	 * For declare atConstructor, atMethod, atField
	 */
	public boolean matches(ResolvedMember resolvedmember, World world) {
		if (kind == AT_METHOD || kind == AT_CONSTRUCTOR) {
			if (resolvedmember != null && resolvedmember.getName().charAt(0) == '<') {
				//  or 
				if (kind == AT_METHOD) {
					return false;
				}
			}
		}
		return signaturePattern.matches(resolvedmember, world, false);
	}

	/**
	 * For declare atType.
	 */
	public boolean matches(ResolvedType type) {
		if (!typePattern.matchesStatically(type)) {
			return false;
		}
		if (type.getWorld().getLint().typeNotExposedToWeaver.isEnabled() && !type.isExposedToWeaver()) {
			type.getWorld().getLint().typeNotExposedToWeaver.signal(type.getName(), getSourceLocation());
		}
		return true;
	}

	public void setAspect(ResolvedType typeX) {
		containingAspect = typeX;
	}

	public UnresolvedType getAspect() {
		return containingAspect;
	}

	public void copyAnnotationTo(ResolvedType onType) {
		ensureAnnotationDiscovered();
		if (annotation == null)
			return;
		if (!onType.hasAnnotation(annotation.getType())) {
			onType.addAnnotation(annotation);
		}
	}

	/**
	 * @return declared annotation; can be null for annotations with SOURCE retention, which of course are not present in
	 * the byte code
	 */
	public AnnotationAJ getAnnotation() {
		ensureAnnotationDiscovered();
		return annotation;
	}

	/**
	 * The annotation specified in the declare @type is stored against a simple method of the form "ajc$declare_",
	 * this method finds that method and retrieves the annotation.
	 * 

* Caveat: Nothing will be found for annotations with SOURCE retention, which of course are not present in the byte * code. */ private void ensureAnnotationDiscovered() { if (annotation != null) return; String annotationMethod = annotationMethods.get(0); for (Iterator iter = containingAspect.getMethods(true, true); iter.hasNext();) { ResolvedMember member = iter.next(); if (!member.getName().equals(annotationMethod)) continue; AnnotationAJ[] annos = member.getAnnotations(); // If weaving broken code, annos can be null or empty if (annos == null || annos.length == 0) return; int idx = 0; if (annos[0].getType().getSignature().equals(AJC_DECLARE_ANNOTATION)) { // @ajcDeclareAnnotation marker annotations should always come in pairs with the actual declared annotations. // If the second annotation is not found, it means that it has SOURCE retention and the annotation declaration // is to be ignored. if (annos.length < 2) break; idx = 1; } annotation = annos[idx]; break; } } public TypePattern getTypePattern() { return typePattern; } public ISignaturePattern getSignaturePattern() { return signaturePattern; } public boolean isStarredAnnotationPattern() { if (typePattern != null) { return typePattern.isStarAnnotation(); } else { return signaturePattern.isStarAnnotation(); } } public Kind getKind() { return kind; } public boolean isDeclareAtConstuctor() { return kind.equals(AT_CONSTRUCTOR); } public boolean isDeclareAtMethod() { return kind.equals(AT_METHOD); } public boolean isDeclareAtType() { return kind.equals(AT_TYPE); } public boolean isDeclareAtField() { return kind.equals(AT_FIELD); } /** * @return the type of the annotation; can be null for annotations with SOURCE retention, which of course are not * present in the byte code */ public ResolvedType getAnnotationType() { if (annotationType != null) return annotationType; String annotationMethod = annotationMethods.get(0); for (Iterator iter = containingAspect.getMethods(true, true); iter.hasNext(); ) { ResolvedMember member = iter.next(); if (!member.getName().equals(annotationMethod)) continue; ResolvedType[] annoTypes = member.getAnnotationTypes(); // If weaving broken code, annoTypes can be null or empty if (annoTypes == null || annoTypes.length == 0) return null; int idx = 0; if (annoTypes[0].getSignature().equals(AJC_DECLARE_ANNOTATION)) { // @ajcDeclareAnnotation marker annotations should always come in pairs with the actual declared annotations. // If the second annotation is not found, it means that it has SOURCE retention and the annotation declaration // is to be ignored. if (annoTypes.length < 2) break; idx = 1; } annotationType = annoTypes[idx]; break; } return annotationType; } /** * @return true if the annotation specified is allowed on a field */ public boolean isAnnotationAllowedOnField() { ensureAnnotationDiscovered(); return annotation != null && annotation.allowedOnField(); } public String getPatternAsString() { if (signaturePattern != null) { return signaturePattern.toString(); } if (typePattern != null) { return typePattern.toString(); } return "DONT KNOW"; } /** * Return true if this declare annotation could ever match something in the specified type - only really able to make * intelligent decision if a type was specified in the sig/type pattern signature. */ public boolean couldEverMatch(ResolvedType type) { // Haven't implemented variant for typePattern (doesn't seem worth it!) // BUGWARNING This test might not be sufficient for funny cases relating // to interfaces and the use of '+' - but it seems really important to // do something here so we don't iterate over all fields and all methods // in all types exposed to the weaver! So look out for bugs here and // we can update the test as appropriate. if (signaturePattern != null) { return signaturePattern.couldEverMatch(type); } return true; } /** * Provide a name suffix so that we can tell the different declare annotations forms apart in the AjProblemReporter */ @Override public String getNameSuffix() { return getKind().toString(); } /** * Captures type of declare annotation (method/type/field/constructor) */ public static class Kind { private final int id; private String s; private Kind(int n, String name) { id = n; s = name; } @Override public int hashCode() { return (19 + 37 * id); } @Override public boolean equals(Object obj) { if (!(obj instanceof Kind)) { return false; } Kind other = (Kind) obj; return other.id == id; } @Override public String toString() { return "at_" + s; } } boolean isRemover = false; public void setRemover(boolean b) { isRemover = b; } public boolean isRemover() { return isRemover; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy