/* *******************************************************************
 * 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, Andy Clement
/** * @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)
Params:
  • annotationType – the annotation type for which the values have been specified
  • scope – the scope within which to resolve type references (eg. Color.GREEN)
/** * 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.
/** * 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); } }