/* ******************************************************************* * 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 v1.0 * which accompanies this distribution and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Adrian Colyer initial implementation * Andy Clement got it working * ******************************************************************/ package org.aspectj.weaver.patterns; import java.io.DataOutputStream; import java.io.IOException; import java.util.Iterator; import java.util.Map; import org.aspectj.bridge.MessageUtil; import org.aspectj.weaver.AnnotationAJ; 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; 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"); private Kind kind; private TypePattern typePattern; // for declare @type private SignaturePattern sigPattern; // for declare // @field,@method,@constructor private String annotationMethod = "unknown"; private String annotationString = "@<annotation>"; private ResolvedType containingAspect; private AnnotationAJ annotation; private ResolvedType annotationType; /** * Captures type of declare annotation (method/type/field/constructor) */ 00050 public static class Kind { private final int id; private String s; private Kind(int n, String name) { id = n; s = name; } public int hashCode() { return (19 + 37 * id); } public boolean equals(Object obj) { if (!(obj instanceof Kind)) return false; Kind other = (Kind) obj; return other.id == id; } public String toString() { return "at_" + s; } } public DeclareAnnotation(Kind kind, TypePattern typePattern) { this.typePattern = typePattern; this.kind = kind; } /** * Returns the string, useful before the real annotation has been resolved */ public String getAnnotationString() { return annotationString; } public DeclareAnnotation(Kind kind, SignaturePattern sigPattern) { this.sigPattern = sigPattern; this.kind = kind; } public boolean isExactPattern() { return typePattern instanceof ExactTypePattern; } public String getAnnotationMethod() { return annotationMethod; } public String toString() { StringBuffer ret = new StringBuffer(); ret.append("declare @"); ret.append(kind); ret.append(" : "); ret.append(typePattern != null ? typePattern.toString() : sigPattern.toString()); ret.append(" : "); ret.append(annotationString); return ret.toString(); } public Object accept(PatternNodeVisitor visitor, Object data) { return visitor.visit(this, data); } 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 (sigPattern != null) { sigPattern = sigPattern.resolveBindings(scope, Bindings.NONE); } this.containingAspect = scope.getEnclosingType(); } 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.sigPattern.parameterizeWith(typeVariableBindingMap, w)); } ret.annotationMethod = this.annotationMethod; ret.annotationString = this.annotationString; ret.containingAspect = this.containingAspect; ret.annotation = this.annotation; ret.copyLocationFrom(this); return ret; } public boolean isAdviceLike() { return false; } public void setAnnotationString(String as) { this.annotationString = as; } public void setAnnotationMethod(String methName) { this.annotationMethod = methName; } 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.annotationString.equals(other.annotationString)) return false; if (!this.annotationMethod.equals(other.annotationMethod)) return false; if (this.typePattern != null) { if (!typePattern.equals(other.typePattern)) return false; } if (this.sigPattern != null) { if (!sigPattern.equals(other.sigPattern)) return false; } return true; } public int hashCode() { int result = 19; result = 37 * result + kind.hashCode(); result = 37 * result + annotationString.hashCode(); result = 37 * result + annotationMethod.hashCode(); if (typePattern != null) result = 37 * result + typePattern.hashCode(); if (sigPattern != null) result = 37 * result + sigPattern.hashCode(); return result; } /* * (non-Javadoc) * * @see org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream) */ public void write(DataOutputStream s) throws IOException { s.writeByte(Declare.ANNOTATION); s.writeInt(kind.id); s.writeUTF(annotationString); s.writeUTF(annotationMethod); if (typePattern != null) typePattern.write(s); if (sigPattern != null) sigPattern.write(s); writeLocation(s); } public static Declare read(VersionedDataInputStream s, ISourceContext context) throws IOException { DeclareAnnotation ret = null; int kind = s.readInt(); String annotationString = s.readUTF(); 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: sp = SignaturePattern.read(s, context); ret = new DeclareAnnotation(AT_FIELD, sp); break; case 3: sp = SignaturePattern.read(s, context); ret = new DeclareAnnotation(AT_METHOD, sp); break; case 4: sp = SignaturePattern.read(s, context); ret = new DeclareAnnotation(AT_CONSTRUCTOR, sp); break; } // if (kind==AT_TYPE.id) { // tp = TypePattern.read(s,context); // ret = new DeclareAnnotation(AT_TYPE,tp); // } else { // sp = SignaturePattern.read(s,context); // ret = new DeclareAnnotation(kind,sp); // } ret.setAnnotationString(annotationString); ret.setAnnotationMethod(annotationMethod); ret.readLocation(context, s); return ret; } // public boolean getAnnotationIfMatches(ResolvedType onType) { // return (match(onType)); // } /** * For @constructor, @method, @field */ public boolean matches(ResolvedMember rm, World world) { return sigPattern.matches(rm, world, false); } /** * For @type */ public boolean matches(ResolvedType typeX) { if (!typePattern.matchesStatically(typeX)) return false; if (typeX.getWorld().getLint().typeNotExposedToWeaver.isEnabled() && !typeX.isExposedToWeaver()) { typeX.getWorld().getLint().typeNotExposedToWeaver.signal(typeX.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_<NN>", this method * finds that method and retrieves the annotation */ private void ensureAnnotationDiscovered() { if (annotation != null) return; for (Iterator iter = containingAspect.getMethods(); iter.hasNext();) { ResolvedMember member = (ResolvedMember) 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;")) { idx = 1; } annotation = annos[idx]; break; } } } public TypePattern getTypePattern() { return typePattern; } public SignaturePattern getSignaturePattern() { return sigPattern; } public boolean isStarredAnnotationPattern() { if (typePattern != null) return typePattern.isStarAnnotation(); if (sigPattern != null) return sigPattern.isStarAnnotation(); throw new RuntimeException("Impossible! what kind of deca is this: " + this); } 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) { for (Iterator iter = containingAspect.getMethods(); iter.hasNext();) { ResolvedMember member = (ResolvedMember) 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; } 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 (sigPattern != null) return sigPattern.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 (sigPattern != null) return sigPattern.getDeclaringType().matches(type, TypePattern.STATIC).maybeTrue(); return true; } /** * Provide a name suffix so that we can tell the different declare annotations forms apart in the AjProblemReporter */ public String getNameSuffix() { return getKind().toString(); } }