ExactAnnotationFieldTypePattern.java
/* *******************************************************************
* Copyright (c) 2008 Contributors
* 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:
* Andy Clement initial implementation
* ******************************************************************/
package org.aspectj.weaver.patterns;
import java.io.IOException;
import java.util.Map;
import org.aspectj.bridge.IMessage;
import org.aspectj.util.FuzzyBoolean;
import org.aspectj.weaver.AnnotatedElement;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.CompressingDataOutputStream;
import org.aspectj.weaver.ISourceContext;
import org.aspectj.weaver.ReferenceType;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.VersionedDataInputStream;
import org.aspectj.weaver.World;
/**
* Represents an attempt to bind the field of an annotation within a pointcut. For example:
* <pre><code>
* before(Level lev): execution(* *(..)) && @annotation(TraceAnnotation(lev))
* </code></pre>
* <p>This binding annotation type pattern will be for 'lev'.</p>
*/
public class ExactAnnotationFieldTypePattern extends ExactAnnotationTypePattern {
UnresolvedType annotationType;
private ResolvedMember field;
public ExactAnnotationFieldTypePattern(ExactAnnotationTypePattern p, String formalName) {
super(formalName);
this.annotationType = p.annotationType;
this.copyLocationFrom(p);
}
public ExactAnnotationFieldTypePattern(UnresolvedType annotationType, String formalName) {
super(formalName);
this.annotationType = annotationType;
}
/**
* resolve one of these funky things. Need to: <br>
* (a) Check the formal is bound <br>
* (b) Check the annotation type is valid
*/
@Override
public AnnotationTypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding) {
if (resolved) {
return this;
}
resolved = true;
FormalBinding formalBinding = scope.lookupFormal(formalName);
if (formalBinding == null) {
scope.message(IMessage.ERROR, this,
"When using @annotation(<annotationType>(<annotationField>)), <annotationField> must be bound");
return this;
}
annotationType = scope.getWorld().resolve(annotationType, true);
// May not be directly found if in a package, so go looking if that is the case:
if (ResolvedType.isMissing(annotationType)) {
String cleanname = annotationType.getName();
UnresolvedType type = null;
while (ResolvedType.isMissing(type = scope.lookupType(cleanname, this))) {
int lastDot = cleanname.lastIndexOf('.');
if (lastDot == -1) {
break;
}
cleanname = cleanname.substring(0, lastDot) + "$" + cleanname.substring(lastDot + 1);
}
annotationType = scope.getWorld().resolve(type, true);
if (ResolvedType.isMissing(annotationType)) {
// there are likely to be other errors around that have led to us being unable to
// resolve the annotation type, let's quit now
return this;
}
}
verifyIsAnnotationType((ResolvedType) annotationType, scope);
ResolvedType formalBindingType = formalBinding.getType().resolve(scope.getWorld());
String bindingTypeSignature = formalBindingType.getSignature();
if (!(formalBindingType.isEnum() || bindingTypeSignature.equals("Ljava/lang/String;") || bindingTypeSignature.equals("I"))) {
scope.message(IMessage.ERROR, this,
"The field within the annotation must be an enum, string or int. '" + formalBinding.getType()
+ "' is not (compiler limitation)");
}
bindingPattern = true;
// Check that the formal is bound to a type that is represented by one field in the annotation type
ReferenceType theAnnotationType = (ReferenceType) annotationType;
ResolvedMember[] annotationFields = theAnnotationType.getDeclaredMethods();
field = null;
boolean looksAmbiguous = false;
for (ResolvedMember resolvedMember : annotationFields) {
if (resolvedMember.getReturnType().equals(formalBinding.getType())) {
if (field != null) {
boolean haveProblem = true;
// use the name to differentiate
if (field.getName().equals(formalName)) {
// don't use this new field
haveProblem = false;
} else if (resolvedMember.getName().equals(formalName)) {
// ok, let's use this one
field = resolvedMember;
haveProblem = false;
}
if (haveProblem) {
looksAmbiguous = true;
}
} else {
field = resolvedMember;
}
}
}
if (looksAmbiguous) {
// did we find something that does match by name?
if (field == null || !field.getName().equals(formalName)) {
scope.message(IMessage.ERROR, this, "The field type '" + formalBinding.getType()
+ "' is ambiguous for annotation type '" + theAnnotationType.getName() + "'");
}
}
if (field == null) {
scope.message(IMessage.ERROR, this, "No field of type '" + formalBinding.getType() + "' exists on annotation type '"
+ theAnnotationType.getName() + "'");
}
BindingAnnotationFieldTypePattern binding = new BindingAnnotationFieldTypePattern(formalBinding.getType(),
formalBinding.getIndex(), theAnnotationType);
binding.copyLocationFrom(this);
binding.formalName = this.formalName;
bindings.register(binding, scope);
binding.resolveBinding(scope.getWorld());
return binding;
}
@Override
public void write(CompressingDataOutputStream s) throws IOException {
s.writeByte(AnnotationTypePattern.EXACTFIELD);
s.writeUTF(formalName);
annotationType.write(s);
writeLocation(s);
}
public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException {
ExactAnnotationFieldTypePattern ret;
String formalName = s.readUTF();
UnresolvedType annotationType = UnresolvedType.read(s);
ret = new ExactAnnotationFieldTypePattern(annotationType, formalName);
ret.readLocation(context, s);
return ret;
}
// ---
@Override
public Object accept(PatternNodeVisitor visitor, Object data) {
return visitor.visit(this, data);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ExactAnnotationFieldTypePattern)) {
return false;
}
ExactAnnotationFieldTypePattern other = (ExactAnnotationFieldTypePattern) obj;
return (other.annotationType.equals(annotationType)) && (other.field.equals(field))
&& (other.formalName.equals(this.formalName));
}
@Override
public int hashCode() {
int hashcode = annotationType.hashCode();
hashcode = hashcode * 37 + field.hashCode();
hashcode = hashcode * 37 + formalName.hashCode();
return hashcode;
}
// TODO these are currently unimplemented as I believe it resolves to a Binding form *always* and so they don't get
// called
@Override
public FuzzyBoolean fastMatches(AnnotatedElement annotated) {
throw new BCException("unimplemented");
}
@Override
public UnresolvedType getAnnotationType() {
throw new BCException("unimplemented");
}
@Override
public Map getAnnotationValues() {
throw new BCException("unimplemented");
}
@Override
public ResolvedType getResolvedAnnotationType() {
throw new BCException("unimplemented");
}
@Override
public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) {
throw new BCException("unimplemented");
}
@Override
public FuzzyBoolean matches(AnnotatedElement annotated) {
throw new BCException("unimplemented");
}
@Override
public FuzzyBoolean matchesRuntimeType(AnnotatedElement annotated) {
throw new BCException("unimplemented");
}
@Override
public AnnotationTypePattern parameterizeWith(Map typeVariableMap, World w) {
throw new BCException("unimplemented");
}
@Override
public void resolve(World world) {
throw new BCException("unimplemented");
}
@Override
public String toString() {
if (!resolved && formalName != null) {
return formalName;
}
StringBuilder ret = new StringBuilder();
ret.append("@").append(annotationType.toString());
ret.append("(").append(formalName).append(")");
return ret.toString();
}
}