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

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

/* *******************************************************************
 * 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