blob: 0941462d18ac2071e44e868e31778fd2a5e125ef [file] [log] [blame]
/* *******************************************************************
* 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<String, String> annotationValues;
public WildAnnotationTypePattern(TypePattern typePattern) {
super();
this.typePattern = typePattern;
this.setLocation(typePattern.getSourceContext(), typePattern.start, typePattern.end);
}
public WildAnnotationTypePattern(TypePattern typePattern, Map<String, String> 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<String,String> replacementValues = new HashMap<String,String>();
Set<String> 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<String,UnresolvedType> 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<String> key = annotationValues.keySet();
for (Iterator<String> 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<String, String> aValues = new HashMap<String, String>();
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);
}
}