public boolean isAnnotation() {
return (binding.getAccessFlags() & ACC_ANNOTATION)!=0;
}
+
+ public boolean hasAnnotation(ResolvedTypeX ofType) {
+ throw new RuntimeException("How to implement this? Needs to ask eclipse!");
+ }
public PerClause getPerClause() {
//should probably be: ((AspectDeclaration)declaration).perClause;
* Represents any element that may have annotations
*/
public interface AnnotatedElement {
- boolean hasAnnotation(TypeX ofType);
+ boolean hasAnnotation(ResolvedTypeX ofType);
// SomeType getAnnotation(TypeX ofType);
}
else return name;
}
- /* (non-Javadoc)
- * @see org.aspectj.weaver.AnnotatedElement#hasAnnotation(org.aspectj.weaver.TypeX)
- */
- public boolean hasAnnotation(TypeX ofType) {
- // TODO complete this method
- return false;
+ /**
+ * If you want a sensible answer, resolve the member and call
+ * hasAnnotation() on the ResolvedMember.
+ */
+ public boolean hasAnnotation(ResolvedTypeX ofType) {
+ throw new UnsupportedOperationException("You should resolve this member and call hasAnnotation() on the result...");
}
- // ---- fields 'n' stuff
+
+ // ---- fields 'n' stuff
public static final Member[] NONE = new Member[0];
* This is the declared member, i.e. it will always correspond to an
* actual method/... declaration
*/
-public class ResolvedMember extends Member implements IHasPosition {
+public class ResolvedMember extends Member implements IHasPosition, AnnotatedElement {
public String[] parameterNames = null;
protected TypeX[] checkedExceptions = TypeX.NONE;
public boolean isAjSynthetic() {
return true;
}
+
+ public boolean hasAnnotation(ResolvedTypeX ofType) {
+ // The ctors don't allow annotations to be specified ... yet - but
+ // that doesn't mean it is an error to call this method.
+ // Normally the weaver will be working with subtypes of
+ // this type - BcelField/BcelMethod
+ return false;
+ }
public boolean isBridgeMethod() {
return (modifiers & Constants.ACC_BRIDGE)!=0;
import org.aspectj.weaver.patterns.Declare;
import org.aspectj.weaver.patterns.PerClause;
-public abstract class ResolvedTypeX extends TypeX {
+public abstract class ResolvedTypeX extends TypeX implements AnnotatedElement {
protected World world;
public final boolean isClass() {
return delegate.isClass();
}
+
+ public boolean hasAnnotation(ResolvedTypeX ofType) {
+ return delegate.hasAnnotation(ofType);
+ }
public boolean isAspect() {
return delegate.isAspect();
public abstract boolean isInterface();
public abstract boolean isEnum();
public abstract boolean isAnnotation();
+
+ public abstract boolean hasAnnotation(ResolvedTypeX ofType);
public abstract ResolvedMember[] getDeclaredFields();
return ResolvedMember.NONE;
}
+ public boolean hasAnnotation(ResolvedTypeX ofType) {
+ return false;
+ }
+
public final ResolvedTypeX getSuperclass() {
return world.getCoreType(OBJECT);
}
public final boolean isPrimitive() {
return true;
}
+ public boolean hasAnnotation(ResolvedTypeX ofType) {
+ return false;
+ }
public final boolean isAssignableFrom(TypeX other) {
if (! other.isPrimitive()) return false;
return assignTable[((Primitive)other).index][index];
public final String getName() {
return MISSING_NAME;
}
+ public boolean hasAnnotation(ResolvedTypeX ofType) {
+ return false;
+ }
public final ResolvedMember[] getDeclaredFields() {
return ResolvedMember.NONE;
}
return world.resolve(this);
}
- public boolean hasAnnotation(TypeX ofType) {
- //TODO implement TypeX.hasAnnotation
- return false;
+ public boolean hasAnnotation(ResolvedTypeX ofType) {
+ throw new UnsupportedOperationException("You should resolve this member and call hasAnnotation() on the result...");
}
// ---- fields
public static final String ITDC_ON_ANNOTATION_NOT_ALLOWED = "itdcOnAnnotationNotAllowed";
public static final String ITDM_ON_ANNOTATION_NOT_ALLOWED = "itdmOnAnnotationNotAllowed";
public static final String ITDF_ON_ANNOTATION_NOT_ALLOWED = "itdfOnAnnotationNotAllowed";
+ public static final String REFERENCE_TO_NON_ANNOTATION_TYPE = "referenceToNonAnnotationType";
public static String format(String key) {
return bundle.getString(key);
import org.aspectj.apache.bcel.classfile.Attribute;
import org.aspectj.apache.bcel.classfile.Field;
import org.aspectj.apache.bcel.classfile.Synthetic;
+import org.aspectj.apache.bcel.classfile.annotation.Annotation;
import org.aspectj.weaver.AjAttribute;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.ResolvedMember;
+import org.aspectj.weaver.ResolvedTypeX;
import org.aspectj.weaver.TypeX;
import org.aspectj.weaver.World;
public boolean isSynthetic() {
return isSynthetic;
}
+
+ public boolean hasAnnotation(ResolvedTypeX ofType) {
+ Annotation[] anns = field.getAnnotations();
+ for (int i = 0; i < anns.length; i++) {
+ Annotation annotation = anns[i];
+ if (annotation.getTypeName().equals(ofType.getName())) return true;
+ }
+ return false;
+ }
}
import org.aspectj.apache.bcel.classfile.LocalVariable;
import org.aspectj.apache.bcel.classfile.LocalVariableTable;
import org.aspectj.apache.bcel.classfile.Method;
+import org.aspectj.apache.bcel.classfile.annotation.Annotation;
import org.aspectj.weaver.AjAttribute;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.ResolvedMember;
+import org.aspectj.weaver.ResolvedTypeX;
import org.aspectj.weaver.ShadowMunger;
import org.aspectj.weaver.TypeX;
import org.aspectj.weaver.World;
return super.getKind();
}
}
+
+ public boolean hasAnnotation(ResolvedTypeX ofType) {
+ Annotation[] anns = method.getAnnotations();
+ for (int i = 0; i < anns.length; i++) {
+ Annotation annotation = anns[i];
+ if (annotation.getTypeName().equals(ofType.getName())) return true;
+ }
+ return false;
+ }
}
import org.aspectj.apache.bcel.classfile.Field;
import org.aspectj.apache.bcel.classfile.JavaClass;
import org.aspectj.apache.bcel.classfile.Method;
+import org.aspectj.apache.bcel.classfile.annotation.Annotation;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.weaver.AjAttribute;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.ResolvedTypeX;
import org.aspectj.weaver.TypeX;
import org.aspectj.weaver.WeaverStateInfo;
+import org.aspectj.weaver.AjAttribute.WeaverVersionInfo;
import org.aspectj.weaver.patterns.PerClause;
// ??? exposed for testing
}
} else if (a instanceof AjAttribute.WeaverVersionInfo) {
wvInfo = (AjAttribute.WeaverVersionInfo)a;
- if (wvInfo.getMajorVersion() > wvInfo.getCurrentWeaverMajorVersion()) {
+ if (wvInfo.getMajorVersion() > WeaverVersionInfo.getCurrentWeaverMajorVersion()) {
// The class file containing this attribute was created by a version of AspectJ that
// added some behavior that 'this' version of AspectJ doesn't understand. And the
// class file contains changes that mean 'this' version of AspectJ cannot continue.
throw new BCException("Unable to continue, this version of AspectJ supports classes built with weaver version "+
- wvInfo.toCurrentVersionString()+" but the class "+ javaClass.getClassName()+" is version "+wvInfo.toString());
+ WeaverVersionInfo.toCurrentVersionString()+" but the class "+ javaClass.getClassName()+" is version "+wvInfo.toString());
}
} else {
throw new BCException("bad attribute " + a);
//System.err.println("javaClass: " + Arrays.asList(javaClass.getInterfaceNames()) + " super " + javaClass.getSuperclassName());
//if (lazyClassGen != null) lazyClassGen.print();
}
+
+// public Annotation[] getAnnotations() {
+// if (annotations == null) {
+// annotations = javaClass.getAnnotations();
+//// This would be how to resolve annotations ...
+//// annotationsTypeXs = new ResolvedTypeX[annotations.length];
+//// for (int i = 0; i < annotations.length; i++) {
+//// Annotation annotation = annotations[i];
+//// getResolvedTypeX().getWorld().resolve(TypeX.forName(annotation.getTypeName()));
+//// }
+// }
+// return annotations;
+// }
+
+ public boolean hasAnnotation(ResolvedTypeX ofType) {
+ Annotation[] annotationsOnThisType = javaClass.getAnnotations();
+ for (int i = 0; i < annotationsOnThisType.length; i++) {
+ Annotation a = annotationsOnThisType[i];
+ if (a.getTypeName().equals(ofType.getName())) return true;
+ }
+ return false;
+ }
}
import java.io.DataOutputStream;
import java.io.IOException;
+import org.aspectj.bridge.IMessage;
+import org.aspectj.bridge.MessageUtil;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.ISourceContext;
import org.aspectj.weaver.IntMap;
import org.aspectj.weaver.TypeX;
+import org.aspectj.weaver.WeaverMessages;
+import org.aspectj.weaver.World;
public class BindingAnnotationTypePattern extends ExactAnnotationTypePattern implements BindingPattern {
this(binding.getType(),binding.getIndex());
}
+ public AnnotationTypePattern resolve(World world) {
+ if (resolved) return this;
+ resolved = true;
+ annotationType = annotationType.resolve(world);
+ if (!annotationType.isAnnotation(world)) {
+ IMessage m = MessageUtil.error(
+ WeaverMessages.format(WeaverMessages.REFERENCE_TO_NON_ANNOTATION_TYPE,annotationType.getName()),
+ getSourceLocation());
+ world.getMessageHandler().handleMessage(m);
+ resolved = false;
+ }
+ return this;
+ }
+
+
public int getFormalIndex() {
return formalIndex;
}
import java.io.IOException;
import org.aspectj.bridge.IMessage;
+import org.aspectj.bridge.MessageUtil;
import org.aspectj.util.FuzzyBoolean;
import org.aspectj.weaver.AnnotatedElement;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.ISourceContext;
+import org.aspectj.weaver.ResolvedTypeX;
import org.aspectj.weaver.TypeX;
+import org.aspectj.weaver.WeaverMessages;
/**
* Matches an annotation of a given type
protected TypeX annotationType;
protected String formalName;
- private boolean resolved = false;
+ protected boolean resolved = false;
private boolean bindingPattern = false;
/**
// will be turned into BindingAnnotationTypePattern during resolution
}
+
public FuzzyBoolean matches(AnnotatedElement annotated) {
- return (annotated.hasAnnotation(annotationType) ?
+ if (!resolved) {
+ throw new IllegalStateException("Can't match on an unresolved annotation type pattern");
+ }
+ return (annotated.hasAnnotation((ResolvedTypeX)annotationType) ?
FuzzyBoolean.YES : FuzzyBoolean.NO);
}
BindingAnnotationTypePattern binding = new BindingAnnotationTypePattern(formalBinding);
binding.copyLocationFrom(this);
bindings.register(binding, scope);
+ binding.resolve(scope.getWorld());
return binding;
} else {
}
} else {
annotationType = annotationType.resolve(scope.getWorld());
+ if (!annotationType.isAnnotation(scope.getWorld())) {
+ IMessage m = MessageUtil.error(
+ WeaverMessages.format(WeaverMessages.REFERENCE_TO_NON_ANNOTATION_TYPE,annotationType.getName()),
+ getSourceLocation());
+ scope.getWorld().getMessageHandler().handleMessage(m);
+ resolved = false;
+ }
return this;
}
}
import org.aspectj.util.FuzzyBoolean;
import org.aspectj.weaver.ISourceContext;
import org.aspectj.weaver.IntMap;
+import org.aspectj.weaver.Member;
+import org.aspectj.weaver.NameMangler;
+import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedTypeX;
import org.aspectj.weaver.Shadow;
import org.aspectj.weaver.ShadowMunger;
*/
public FuzzyBoolean match(Shadow shadow) {
if (!couldMatch(shadow)) return FuzzyBoolean.NO;
- return type.matches(shadow.getSignature());
+ Member member = shadow.getSignature();
+ ResolvedMember rMember = member.resolve(shadow.getIWorld());
+ if (rMember == null) {
+ if (member.getName().startsWith(NameMangler.PREFIX)) {
+ return FuzzyBoolean.NO;
+ }
+ shadow.getIWorld().getLint().unresolvableMember.signal(member.toString(), getSourceLocation());
+ return FuzzyBoolean.NO;
+ }
+ return type.matches(rMember);
}
private boolean couldMatch(Shadow shadow) {
public boolean matches(Member member, World world) {
return (matchesIgnoringAnnotations(member,world) &&
- annotationPattern.matches(member).alwaysTrue());
+ matchesAnnotations(member,world));
+ }
+
+ public boolean matchesAnnotations(Member member,World world) {
+ ResolvedMember rMember = member.resolve(world);
+ if (rMember == null) {
+ if (member.getName().startsWith(NameMangler.PREFIX)) {
+ return false;
+ }
+ world.getLint().unresolvableMember.signal(member.toString(), getSourceLocation());
+ return false;
+ }
+ return annotationPattern.matches(rMember).alwaysTrue();
}
public boolean matchesIgnoringAnnotations(Member member, World world) {
itdmOnAnnotationNotAllowed=can''t make inter-type method declarations on annotation types
itdfOnAnnotationNotAllowed=can''t make inter-type field declarations on annotation types
+referenceToNonAnnotationType=Type referred to is not an annotation type: {0}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2004 IBM Corporation.
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * Andy Clement initial implementation
+ * ******************************************************************/
+package org.aspectj.weaver.patterns;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import junit.framework.TestCase;
+
+import org.aspectj.bridge.AbortException;
+import org.aspectj.bridge.IMessage;
+import org.aspectj.bridge.IMessageHandler;
+import org.aspectj.bridge.MessageHandler;
+import org.aspectj.bridge.IMessage.Kind;
+import org.aspectj.weaver.BcweaverTests;
+import org.aspectj.weaver.ResolvedMember;
+import org.aspectj.weaver.ResolvedTypeX;
+import org.aspectj.weaver.bcel.BcelWorld;
+
+/*
+ * Sample types that this program uses are:
+
+import p.SimpleAnnotation;
+
+@SimpleAnnotation(id=2)
+public class AnnotatedClass {
+
+ @SimpleAnnotation(id=3)
+ public void m1() { }
+
+ @SimpleAnnotation(id=4)
+ int i;
+}
+
+ * with SimpleAnnotation defined as:
+
+package p;
+import java.lang.annotation.*;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SimpleAnnotation {
+ int id();
+ String fruit() default "bananas";
+}
+
+*NOTE NOTE NOTE NOTE NOTE NOTE NOTE*
+If you need to rebuild the test data code, run 'ant -f build-15.xml' in the
+testdata directory.
+
+ */
+public class AnnotationPatternMatchingTestCase extends TestCase {
+
+ private BcelWorld world;
+ private AnnotationTypePattern fooTP,simpleAnnotationTP;
+
+ private ResolvedTypeX loadType(String name) {
+ if (world == null) {
+ world = new BcelWorld(BcweaverTests.TESTDATA_PATH + "/testcode.jar");
+ }
+ return world.resolve(name);
+ }
+
+ private void initAnnotationTypePatterns() {
+ PatternParser p = new PatternParser("@Foo");
+ fooTP = p.parseAnnotationTypePattern();
+ fooTP = fooTP.resolveBindings(makeSimpleScope(),new Bindings(3),true);
+
+ p = new PatternParser("@p.SimpleAnnotation");
+ simpleAnnotationTP = p.parseAnnotationTypePattern();
+ simpleAnnotationTP = simpleAnnotationTP.resolveBindings(makeSimpleScope(),new Bindings(3),true);
+ }
+
+
+ public void testAnnotationPatternMatchingOnTypes() {
+
+ ResolvedTypeX rtx = loadType("AnnotatedClass");
+ initAnnotationTypePatterns();
+
+ // One should match
+ assertTrue("@Foo should not match on the AnnotatedClass",
+ fooTP.matches(rtx).alwaysFalse());
+ assertTrue("@SimpleAnnotation should match on the AnnotatedClass",
+ simpleAnnotationTP.matches(rtx).alwaysTrue());
+
+ }
+
+ static class MyMessageHandler implements IMessageHandler {
+ public List messages = new ArrayList();
+ public boolean handleMessage(IMessage message) throws AbortException {
+ messages.add(message);
+ return false;
+ }
+ public boolean isIgnoring(Kind kind) {return false;}
+ }
+
+ public void testReferenceToNonAnnotationType() {
+ ResolvedTypeX rtx = loadType("AnnotatedClass"); // inits the world
+ PatternParser p = new PatternParser("@java.lang.String");
+
+ MyMessageHandler mh = new MyMessageHandler();
+ world.setMessageHandler(mh);
+ AnnotationTypePattern atp = p.parseAnnotationTypePattern();
+ atp = atp.resolveBindings(makeSimpleScope(),new Bindings(3),true);
+
+ assertTrue("Expected 1 error message but got "+mh.messages.size(),mh.messages.size()==1);
+
+ String expected = "Type referred to is not an annotation type";
+ String msg = ((IMessage)mh.messages.get(0)).toString();
+ assertTrue("Expected: "+expected+" but got "+msg,msg.indexOf(expected)!=-1);
+ }
+
+ public void testReferenceViaFormalToNonAnnotationType() {
+ ResolvedTypeX rtx = loadType("AnnotatedClass"); // inits the world
+ PatternParser p = new PatternParser("a");
+
+ MyMessageHandler mh = new MyMessageHandler();
+ world.setMessageHandler(mh);
+ AnnotationTypePattern atp = p.parseAnnotationNameOrVarTypePattern();
+ atp = atp.resolveBindings(makeSimpleScope(),new Bindings(3),true);
+
+ assertTrue("Expected 1 error message but got "+mh.messages.size(),mh.messages.size()==1);
+
+ String expected = "Type referred to is not an annotation type";
+ String msg = ((IMessage)mh.messages.get(0)).toString();
+ assertTrue("Expected: "+expected+" but got "+msg,msg.indexOf(expected)!=-1);
+ }
+
+ public TestScope makeSimpleScope() {
+ return new TestScope(new String[] {"int", "java.lang.String"}, new String[] {"a", "b"}, world);
+ }
+
+ public void testUnresolvedAnnotationTypes() {
+ ResolvedTypeX rtx = loadType("AnnotatedClass");
+
+ PatternParser p = new PatternParser("@Foo");
+ AnnotationTypePattern fooTP = p.parseAnnotationTypePattern();
+ try {
+ fooTP.matches(rtx);
+ fail("Should have failed with illegal state exception, fooTP is not resolved");
+ } catch (IllegalStateException ise) {
+ // Correct!
+ }
+ }
+
+ public void testAnnotationPatternMatchingOnMethods() {
+
+ ResolvedTypeX rtx = loadType("AnnotatedClass");
+ ResolvedMember aMethod = rtx.getDeclaredMethods()[1];
+
+ assertTrue("Haven't got the right method, I'm looking for 'm1()': "+aMethod.getName(),
+ aMethod.getName().equals("m1"));
+
+ initAnnotationTypePatterns();
+
+ // One should match
+ assertTrue("@Foo should not match on the AnnotatedClass.m1() method",
+ fooTP.matches(aMethod).alwaysFalse());
+ assertTrue("@SimpleAnnotation should match on the AnnotatedClass.m1() method",
+ simpleAnnotationTP.matches(aMethod).alwaysTrue());
+
+ }
+
+ public void testAnnotationPatternMatchingOnFields() {
+
+ ResolvedTypeX rtx = loadType("AnnotatedClass");
+ ResolvedMember aField = rtx.getDeclaredFields()[0];
+
+ assertTrue("Haven't got the right field, I'm looking for 'i'"+aField.getName(),
+ aField.getName().equals("i"));
+
+ initAnnotationTypePatterns();
+
+ // One should match
+ assertTrue("@Foo should not match on the AnnotatedClass.i field",
+ fooTP.matches(aField).alwaysFalse());
+ assertTrue("@SimpleAnnotation should match on the AnnotatedClass.i field",
+ simpleAnnotationTP.matches(aField).alwaysTrue());
+
+ }
+
+}
package org.aspectj.weaver.patterns;
import org.aspectj.weaver.AnnotatedElement;
+import org.aspectj.weaver.BcweaverTests;
+import org.aspectj.weaver.ResolvedTypeX;
import org.aspectj.weaver.TypeX;
import org.aspectj.weaver.bcel.BcelWorld;
public void testExactAnnotationPatternMatching() {
PatternParser p = new PatternParser("@Foo");
AnnotationTypePattern ap = p.parseAnnotationTypePattern();
+ ap = ap.resolveBindings(makeSimpleScope(),new Bindings(3),true);
AnnotatedElementImpl ae = new AnnotatedElementImpl(new String[]{"Foo"});
assertTrue("matches element with Foo",ap.matches(ae).alwaysTrue());
AnnotatedElementImpl ae2 = new AnnotatedElementImpl(new String[]{"Boo"});
public void testAndAnnotationPatternMatching() {
PatternParser p = new PatternParser("@Foo && @Boo");
AnnotationTypePattern ap = p.parseAnnotationTypePattern();
+ ap = ap.resolveBindings(makeSimpleScope(),new Bindings(3),true);
AnnotatedElementImpl ae = new AnnotatedElementImpl(new String[] {"Foo","Boo"});
assertTrue("matches foo and boo",ap.matches(ae).alwaysTrue());
ae = new AnnotatedElementImpl(new String[] {"Foo"});
public void testOrAnnotationPatternMatching() {
PatternParser p = new PatternParser("@Foo || @Boo");
AnnotationTypePattern ap = p.parseAnnotationTypePattern();
+ ap = ap.resolveBindings(makeSimpleScope(),new Bindings(3),true);
AnnotatedElementImpl ae = new AnnotatedElementImpl(new String[] {"Foo","Boo"});
assertTrue("matches foo and boo",ap.matches(ae).alwaysTrue());
ae = new AnnotatedElementImpl(new String[] {"Foo"});
public void testNotAnnotationPatternMatching() {
PatternParser p = new PatternParser("!@Foo");
AnnotationTypePattern ap = p.parseAnnotationTypePattern();
+ ap = ap.resolveBindings(makeSimpleScope(),new Bindings(3),true);
AnnotatedElementImpl ae = new AnnotatedElementImpl(new String[] {"Foo","Boo"});
assertTrue("does not match foo and boo",ap.matches(ae).alwaysFalse());
ae = new AnnotatedElementImpl(new String[] {"Boo"});
public TestScope makeSimpleScope() {
- return new TestScope(new String[] {"int", "java.lang.String","Foo"}, new String[] {"a", "b","foo"}, new BcelWorld());
+ BcelWorld bWorld = new BcelWorld(BcweaverTests.TESTDATA_PATH + "/testcode.jar"); // testcode contains Foo/Boo/Goo/etc
+ return new TestScope(new String[] {"int", "java.lang.String","Foo"},
+ new String[] {"a", "b","foo"},
+ bWorld);
}
// put test cases for AnnotationPatternList matching in separate test class...
this.annotationTypes = annotationTypes;
}
- public boolean hasAnnotation(TypeX ofType) {
+ public boolean hasAnnotation(ResolvedTypeX ofType) {
for (int i = 0; i < annotationTypes.length; i++) {
if (annotationTypes[i].equals(ofType.getName())) return true;
}
suite.addTestSuite(KindedTestCase.class);
suite.addTestSuite(WithinCodeTestCase.class);
suite.addTestSuite(AnnotationPatternTestCase.class);
+ suite.addTestSuite(AnnotationPatternMatchingTestCase.class);
//$JUnit-END$
return suite;
}