org.aspectj.weaver.patterns.DeclareAnnotation Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.apache.servicemix.bundles.aspectj
Show all versions of org.apache.servicemix.bundles.aspectj
This OSGi bundle wraps aspectjrt and aspectjweaver ${pkgVersion} jar files.
/* *******************************************************************
* 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 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);
}
@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 (!onType.hasAnnotation(annotation.getType())) {
onType.addAnnotation(annotation);
}
}
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
*/
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)) {
AnnotationAJ[] annos = member.getAnnotations();
if (annos == null) {
// if weaving broken code, this can happen
return;
}
int idx = 0;
if (annos.length > 0
&& annos[0].getType().getSignature().equals("Lorg/aspectj/internal/lang/annotation/ajcDeclareAnnotation;")) {
if (annos.length < 2)
continue;
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
*/
public ResolvedType getAnnotationType() {
if (annotationType == null) {
String annotationMethod = annotationMethods.get(0);
for (Iterator iter = containingAspect.getMethods(true, true); iter.hasNext();) {
ResolvedMember member = iter.next();
if (member.getName().equals(annotationMethod)) {
ResolvedType[] annoTypes = member.getAnnotationTypes();
if (annoTypes == null) {
// if weaving broken code, this can happen
return null;
}
int idx = 0;
if (annoTypes[0].getSignature().equals("Lorg/aspectj/internal/lang/annotation/ajcDeclareAnnotation;")) {
idx = 1;
if (annoTypes.length < 2)
continue;
}
annotationType = annoTypes[idx];
break;
}
}
}
return annotationType;
}
/**
* @return true if the annotation specified is allowed on a field
*/
public boolean isAnnotationAllowedOnField() {
ensureAnnotationDiscovered();
return 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;
}
}