org.aspectj.weaver.patterns.WildAnnotationTypePattern Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aspectjweaver Show documentation
Show all versions of aspectjweaver Show documentation
The AspectJ weaver introduces advices to java classes
/* *******************************************************************
* Copyright (c) 2004 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
*
* ******************************************************************/
package org.aspectj.weaver.patterns;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.util.FuzzyBoolean;
import org.aspectj.weaver.AjAttribute.WeaverVersionInfo;
import org.aspectj.weaver.AnnotatedElement;
import org.aspectj.weaver.BCException;
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;
/**
* @author colyer
* @author Andy Clement
*/
public class WildAnnotationTypePattern extends AnnotationTypePattern {
private TypePattern typePattern;
private boolean resolved = false;
Map annotationValues;
public WildAnnotationTypePattern(TypePattern typePattern) {
super();
this.typePattern = typePattern;
this.setLocation(typePattern.getSourceContext(), typePattern.start, typePattern.end);
}
public WildAnnotationTypePattern(TypePattern typePattern, Map annotationValues) {
super();
this.typePattern = typePattern;
this.annotationValues = annotationValues;
// PVAL make the location be from start of type pattern to end of values
this.setLocation(typePattern.getSourceContext(), typePattern.start, typePattern.end);
}
public TypePattern getTypePattern() {
return typePattern;
}
/*
* (non-Javadoc)
*
* @see org.aspectj.weaver.patterns.AnnotationTypePattern#matches(org.aspectj.weaver.AnnotatedElement)
*/
@Override
public FuzzyBoolean matches(AnnotatedElement annotated) {
return matches(annotated, null);
}
/**
* Resolve any annotation values specified, checking they are all well formed (valid names, valid values)
*
* @param annotationType the annotation type for which the values have been specified
* @param scope the scope within which to resolve type references (eg. Color.GREEN)
*/
protected void resolveAnnotationValues(ResolvedType annotationType, IScope scope) {
if (annotationValues == null) {
return;
}
// Check any values specified are OK:
// - the value names are for valid annotation fields
// - the specified values are of the correct type
// - for enums, check the specified values can be resolved in the specified scope
Map replacementValues = new HashMap();
Set keys = annotationValues.keySet();
ResolvedMember[] ms = annotationType.getDeclaredMethods();
for (String k: keys) {
String key = k;
// a trailing ! indicates the the user expressed key!=value rather than key=value as a match constraint
if (k.endsWith("!")) {
key = key.substring(0, k.length() - 1);
}
String v = annotationValues.get(k);
boolean validKey = false;
for (int i = 0; i < ms.length; i++) {
ResolvedMember resolvedMember = ms[i];
if (resolvedMember.getName().equals(key) && resolvedMember.isAbstract()) {
validKey = true;
ResolvedType t = resolvedMember.getReturnType().resolve(scope.getWorld());
if (t.isEnum()) {
// value must be an enum reference X.Y
int pos = v.lastIndexOf(".");
if (pos == -1) {
IMessage m = MessageUtil.error(
WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "enum"), getSourceLocation());
scope.getWorld().getMessageHandler().handleMessage(m);
} else {
String typename = v.substring(0, pos);
ResolvedType rt = scope.lookupType(typename, this).resolve(scope.getWorld());
v = rt.getSignature() + v.substring(pos + 1); // from 'Color.RED' to 'Lp/Color;RED'
replacementValues.put(k, v);
break;
}
} else if (t.isPrimitiveType()) {
if (t.getSignature().equals("I")) {
try {
int value = Integer.parseInt(v);
replacementValues.put(k, Integer.toString(value));
break;
} catch (NumberFormatException nfe) {
IMessage m = MessageUtil.error(
WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "int"),
getSourceLocation());
scope.getWorld().getMessageHandler().handleMessage(m);
}
} else if (t.getSignature().equals("F")) {
try {
float value = Float.parseFloat(v);
replacementValues.put(k, Float.toString(value));
break;
} catch (NumberFormatException nfe) {
IMessage m = MessageUtil.error(
WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "float"),
getSourceLocation());
scope.getWorld().getMessageHandler().handleMessage(m);
}
} else if (t.getSignature().equals("Z")) {
if (v.equalsIgnoreCase("true") || v.equalsIgnoreCase("false")) {
// is it ok !
} else {
IMessage m = MessageUtil.error(
WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "boolean"),
getSourceLocation());
scope.getWorld().getMessageHandler().handleMessage(m);
}
} else if (t.getSignature().equals("S")) {
try {
short value = Short.parseShort(v);
replacementValues.put(k, Short.toString(value));
break;
} catch (NumberFormatException nfe) {
IMessage m = MessageUtil.error(
WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "short"),
getSourceLocation());
scope.getWorld().getMessageHandler().handleMessage(m);
}
} else if (t.getSignature().equals("J")) {
try {
replacementValues.put(k, Long.toString(Long.parseLong(v)));
break;
} catch (NumberFormatException nfe) {
IMessage m = MessageUtil.error(
WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "long"),
getSourceLocation());
scope.getWorld().getMessageHandler().handleMessage(m);
}
} else if (t.getSignature().equals("D")) {
try {
replacementValues.put(k, Double.toString(Double.parseDouble(v)));
break;
} catch (NumberFormatException nfe) {
IMessage m = MessageUtil.error(
WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "double"),
getSourceLocation());
scope.getWorld().getMessageHandler().handleMessage(m);
}
} else if (t.getSignature().equals("B")) {
try {
replacementValues.put(k, Byte.toString(Byte.parseByte(v)));
break;
} catch (NumberFormatException nfe) {
IMessage m = MessageUtil.error(
WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "byte"),
getSourceLocation());
scope.getWorld().getMessageHandler().handleMessage(m);
}
} else if (t.getSignature().equals("C")) {
if (v.length() != 3) { // '?'
IMessage m = MessageUtil.error(
WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "char"),
getSourceLocation());
scope.getWorld().getMessageHandler().handleMessage(m);
} else {
replacementValues.put(k, v.substring(1, 2));
break;
}
} else {
throw new RuntimeException("Not implemented for " + t);
}
} else if (t.equals(ResolvedType.JL_STRING)) {
// nothing to do, it will be OK
} else if (t.equals(ResolvedType.JL_CLASS) || (t.isParameterizedOrGenericType() && t.getRawType().equals(ResolvedType.JL_CLASS))) {
String typename = v.substring(0, v.lastIndexOf('.')); // strip off '.class'
ResolvedType rt = scope.lookupType(typename, this).resolve(scope.getWorld());
if (rt.isMissing()) {
IMessage m = MessageUtil.error("Unable to resolve type '" + v + "' specified for value '" + k + "'",
getSourceLocation());
scope.getWorld().getMessageHandler().handleMessage(m);
}
replacementValues.put(k, rt.getSignature());
break;
} else {
if (t.isAnnotation()) {
if (v.indexOf("(") != -1) {
throw new RuntimeException(
"Compiler limitation: annotation values can only currently be marker annotations (no values): "
+ v);
}
String typename = v.substring(1);
ResolvedType rt = scope.lookupType(typename, this).resolve(scope.getWorld());
if (rt.isMissing()) {
IMessage m = MessageUtil.error(
"Unable to resolve type '" + v + "' specified for value '" + k + "'", getSourceLocation());
scope.getWorld().getMessageHandler().handleMessage(m);
}
replacementValues.put(k, rt.getSignature());
break;
// } else if (t.isArray()) {
// Looks like {} aren't pseudotokens in the parser so they don't get through for our pointcut parser
// // @Foo(value=[Foo.class])
// String typename = v.substring(0, v.lastIndexOf('.')); // strip off '.class'
// ResolvedType rt = scope.lookupType(typename, this).resolve(scope.getWorld());
// if (rt.isMissing()) {
// IMessage m = MessageUtil.error("Unable to resolve type '" + v + "' specified for value '" + k + "'",
// getSourceLocation());
// scope.getWorld().getMessageHandler().handleMessage(m);
// }
// replacementValues.put(k, rt.getSignature());
} else {
scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.UNSUPPORTED_ANNOTATION_VALUE_TYPE,t), getSourceLocation()));
replacementValues.put(k,"");
}
}
}
}
if (!validKey) {
IMessage m = MessageUtil.error(WeaverMessages.format(WeaverMessages.UNKNOWN_ANNOTATION_VALUE, annotationType, k),
getSourceLocation());
scope.getWorld().getMessageHandler().handleMessage(m);
}
}
annotationValues.putAll(replacementValues);
}
@Override
public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) {
if (!resolved) {
throw new IllegalStateException("Can't match on an unresolved annotation type pattern");
}
if (annotationValues != null && !typePattern.hasFailedResolution()) {
// PVAL improve this restriction, would allow '*(value=Color.RED)'
throw new IllegalStateException("Cannot use annotationvalues with a wild annotation pattern");
}
if (isForParameterAnnotationMatch()) {
if (parameterAnnotations != null && parameterAnnotations.length != 0) {
for (int i = 0; i < parameterAnnotations.length; i++) {
if (typePattern.matches(parameterAnnotations[i], TypePattern.STATIC).alwaysTrue()) {
return FuzzyBoolean.YES;
}
}
}
} else {
// matches if the type of any of the annotations on the AnnotatedElement is
// matched by the typePattern.
ResolvedType[] annTypes = annotated.getAnnotationTypes();
if (annTypes != null && annTypes.length != 0) {
for (int i = 0; i < annTypes.length; i++) {
if (typePattern.matches(annTypes[i], TypePattern.STATIC).alwaysTrue()) {
return FuzzyBoolean.YES;
}
}
}
}
return FuzzyBoolean.NO;
}
/*
* (non-Javadoc)
*
* @see org.aspectj.weaver.patterns.AnnotationTypePattern#resolve(org.aspectj.weaver.World)
*/
@Override
public void resolve(World world) {
if (!resolved) {
// attempt resolution - this helps with the Spring bug where they resolve() the pointcut in no scope (SPR-5307)
if (typePattern instanceof WildTypePattern && (annotationValues == null || annotationValues.isEmpty())) {
WildTypePattern wildTypePattern = (WildTypePattern) typePattern;
String fullyQualifiedName = wildTypePattern.maybeGetCleanName();
if (fullyQualifiedName != null && fullyQualifiedName.indexOf(".") != -1) {
ResolvedType resolvedType = world.resolve(UnresolvedType.forName(fullyQualifiedName));
if (resolvedType != null && !resolvedType.isMissing()) {
typePattern = new ExactTypePattern(resolvedType, false, false);
}
}
}
resolved = true;
}
}
/**
* This can modify in place, or return a new TypePattern if the type changes.
*/
@Override
public AnnotationTypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding) {
if (!scope.getWorld().isInJava5Mode()) {
scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.ANNOTATIONS_NEED_JAVA5), getSourceLocation()));
return this;
}
if (resolved) {
return this;
}
this.typePattern = typePattern.resolveBindings(scope, bindings, false, false);
resolved = true;
if (typePattern instanceof ExactTypePattern) {
ExactTypePattern et = (ExactTypePattern) typePattern;
if (!et.getExactType().resolve(scope.getWorld()).isAnnotation()) {
IMessage m = MessageUtil.error(
WeaverMessages.format(WeaverMessages.REFERENCE_TO_NON_ANNOTATION_TYPE, et.getExactType().getName()),
getSourceLocation());
scope.getWorld().getMessageHandler().handleMessage(m);
resolved = false;
}
ResolvedType annotationType = et.getExactType().resolve(scope.getWorld());
resolveAnnotationValues(annotationType, scope);
ExactAnnotationTypePattern eatp = new ExactAnnotationTypePattern(annotationType, annotationValues);
eatp.copyLocationFrom(this);
if (isForParameterAnnotationMatch()) {
eatp.setForParameterAnnotationMatch();
}
return eatp;
} else {
return this;
}
}
@Override
public AnnotationTypePattern parameterizeWith(Map typeVariableMap, World w) {
WildAnnotationTypePattern ret = new WildAnnotationTypePattern(typePattern.parameterizeWith(typeVariableMap, w));
ret.copyLocationFrom(this);
ret.resolved = resolved;
return ret;
}
private static final byte VERSION = 1; // rev if ser. form changes
@Override
public void write(CompressingDataOutputStream s) throws IOException {
s.writeByte(AnnotationTypePattern.WILD);
s.writeByte(VERSION);
typePattern.write(s);
writeLocation(s);
s.writeBoolean(isForParameterAnnotationMatch());
// PVAL
if (annotationValues == null) {
s.writeInt(0);
} else {
s.writeInt(annotationValues.size());
Set key = annotationValues.keySet();
for (Iterator keys = key.iterator(); keys.hasNext();) {
String k = keys.next();
s.writeUTF(k);
s.writeUTF(annotationValues.get(k));
}
}
}
public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException {
WildAnnotationTypePattern ret;
byte version = s.readByte();
if (version > VERSION) {
throw new BCException("ExactAnnotationTypePattern was written by a newer version of AspectJ");
}
TypePattern t = TypePattern.read(s, context);
ret = new WildAnnotationTypePattern(t);
ret.readLocation(context, s);
if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ160) {
if (s.readBoolean()) {
ret.setForParameterAnnotationMatch();
}
}
if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ160M2) {
int annotationValueCount = s.readInt();
if (annotationValueCount > 0) {
Map aValues = new HashMap();
for (int i = 0; i < annotationValueCount; i++) {
String key = s.readUTF();
String val = s.readUTF();
aValues.put(key, val);
}
ret.annotationValues = aValues;
}
}
return ret;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof WildAnnotationTypePattern)) {
return false;
}
WildAnnotationTypePattern other = (WildAnnotationTypePattern) obj;
return other.typePattern.equals(typePattern)
&& this.isForParameterAnnotationMatch() == other.isForParameterAnnotationMatch()
&& (annotationValues == null ? other.annotationValues == null : annotationValues.equals(other.annotationValues));
}
@Override
public int hashCode() {
return (((17 + 37 * typePattern.hashCode()) * 37 + (isForParameterAnnotationMatch() ? 0 : 1)) * 37)
+ (annotationValues == null ? 0 : annotationValues.hashCode());
}
@Override
public String toString() {
return "@(" + typePattern.toString() + ")";
}
@Override
public Object accept(PatternNodeVisitor visitor, Object data) {
return visitor.visit(this, data);
}
}