You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

WildAnnotationTypePattern.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. /* *******************************************************************
  2. * Copyright (c) 2004 IBM Corporation.
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Eclipse Public License v1.0
  6. * which accompanies this distribution and is available at
  7. * http://www.eclipse.org/legal/epl-v10.html
  8. *
  9. * ******************************************************************/
  10. package org.aspectj.weaver.patterns;
  11. import java.io.IOException;
  12. import java.util.HashMap;
  13. import java.util.Iterator;
  14. import java.util.Map;
  15. import java.util.Set;
  16. import org.aspectj.bridge.IMessage;
  17. import org.aspectj.bridge.MessageUtil;
  18. import org.aspectj.util.FuzzyBoolean;
  19. import org.aspectj.weaver.AjAttribute.WeaverVersionInfo;
  20. import org.aspectj.weaver.AnnotatedElement;
  21. import org.aspectj.weaver.BCException;
  22. import org.aspectj.weaver.CompressingDataOutputStream;
  23. import org.aspectj.weaver.ISourceContext;
  24. import org.aspectj.weaver.ResolvedMember;
  25. import org.aspectj.weaver.ResolvedType;
  26. import org.aspectj.weaver.UnresolvedType;
  27. import org.aspectj.weaver.VersionedDataInputStream;
  28. import org.aspectj.weaver.WeaverMessages;
  29. import org.aspectj.weaver.World;
  30. /**
  31. * @author colyer
  32. * @author Andy Clement
  33. */
  34. public class WildAnnotationTypePattern extends AnnotationTypePattern {
  35. private TypePattern typePattern;
  36. private boolean resolved = false;
  37. Map<String, String> annotationValues;
  38. public WildAnnotationTypePattern(TypePattern typePattern) {
  39. super();
  40. this.typePattern = typePattern;
  41. this.setLocation(typePattern.getSourceContext(), typePattern.start, typePattern.end);
  42. }
  43. public WildAnnotationTypePattern(TypePattern typePattern, Map<String, String> annotationValues) {
  44. super();
  45. this.typePattern = typePattern;
  46. this.annotationValues = annotationValues;
  47. // PVAL make the location be from start of type pattern to end of values
  48. this.setLocation(typePattern.getSourceContext(), typePattern.start, typePattern.end);
  49. }
  50. public TypePattern getTypePattern() {
  51. return typePattern;
  52. }
  53. /*
  54. * (non-Javadoc)
  55. *
  56. * @see org.aspectj.weaver.patterns.AnnotationTypePattern#matches(org.aspectj.weaver.AnnotatedElement)
  57. */
  58. @Override
  59. public FuzzyBoolean matches(AnnotatedElement annotated) {
  60. return matches(annotated, null);
  61. }
  62. /**
  63. * Resolve any annotation values specified, checking they are all well formed (valid names, valid values)
  64. *
  65. * @param annotationType the annotation type for which the values have been specified
  66. * @param scope the scope within which to resolve type references (eg. Color.GREEN)
  67. */
  68. protected void resolveAnnotationValues(ResolvedType annotationType, IScope scope) {
  69. if (annotationValues == null) {
  70. return;
  71. }
  72. // Check any values specified are OK:
  73. // - the value names are for valid annotation fields
  74. // - the specified values are of the correct type
  75. // - for enums, check the specified values can be resolved in the specified scope
  76. Map<String,String> replacementValues = new HashMap<String,String>();
  77. Set<String> keys = annotationValues.keySet();
  78. ResolvedMember[] ms = annotationType.getDeclaredMethods();
  79. for (String k: keys) {
  80. String key = k;
  81. // a trailing ! indicates the the user expressed key!=value rather than key=value as a match constraint
  82. if (k.endsWith("!")) {
  83. key = key.substring(0, k.length() - 1);
  84. }
  85. String v = annotationValues.get(k);
  86. boolean validKey = false;
  87. for (ResolvedMember resolvedMember : ms) {
  88. if (resolvedMember.getName().equals(key) && resolvedMember.isAbstract()) {
  89. validKey = true;
  90. ResolvedType t = resolvedMember.getReturnType().resolve(scope.getWorld());
  91. if (t.isEnum()) {
  92. // value must be an enum reference X.Y
  93. int pos = v.lastIndexOf(".");
  94. if (pos == -1) {
  95. IMessage m = MessageUtil.error(
  96. WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "enum"), getSourceLocation());
  97. scope.getWorld().getMessageHandler().handleMessage(m);
  98. } else {
  99. String typename = v.substring(0, pos);
  100. ResolvedType rt = scope.lookupType(typename, this).resolve(scope.getWorld());
  101. v = rt.getSignature() + v.substring(pos + 1); // from 'Color.RED' to 'Lp/Color;RED'
  102. replacementValues.put(k, v);
  103. break;
  104. }
  105. } else if (t.isPrimitiveType()) {
  106. if (t.getSignature().equals("I")) {
  107. try {
  108. int value = Integer.parseInt(v);
  109. replacementValues.put(k, Integer.toString(value));
  110. break;
  111. } catch (NumberFormatException nfe) {
  112. IMessage m = MessageUtil.error(
  113. WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "int"),
  114. getSourceLocation());
  115. scope.getWorld().getMessageHandler().handleMessage(m);
  116. }
  117. } else if (t.getSignature().equals("F")) {
  118. try {
  119. float value = Float.parseFloat(v);
  120. replacementValues.put(k, Float.toString(value));
  121. break;
  122. } catch (NumberFormatException nfe) {
  123. IMessage m = MessageUtil.error(
  124. WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "float"),
  125. getSourceLocation());
  126. scope.getWorld().getMessageHandler().handleMessage(m);
  127. }
  128. } else if (t.getSignature().equals("Z")) {
  129. if (v.equalsIgnoreCase("true") || v.equalsIgnoreCase("false")) {
  130. // is it ok !
  131. } else {
  132. IMessage m = MessageUtil.error(
  133. WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "boolean"),
  134. getSourceLocation());
  135. scope.getWorld().getMessageHandler().handleMessage(m);
  136. }
  137. } else if (t.getSignature().equals("S")) {
  138. try {
  139. short value = Short.parseShort(v);
  140. replacementValues.put(k, Short.toString(value));
  141. break;
  142. } catch (NumberFormatException nfe) {
  143. IMessage m = MessageUtil.error(
  144. WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "short"),
  145. getSourceLocation());
  146. scope.getWorld().getMessageHandler().handleMessage(m);
  147. }
  148. } else if (t.getSignature().equals("J")) {
  149. try {
  150. replacementValues.put(k, Long.toString(Long.parseLong(v)));
  151. break;
  152. } catch (NumberFormatException nfe) {
  153. IMessage m = MessageUtil.error(
  154. WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "long"),
  155. getSourceLocation());
  156. scope.getWorld().getMessageHandler().handleMessage(m);
  157. }
  158. } else if (t.getSignature().equals("D")) {
  159. try {
  160. replacementValues.put(k, Double.toString(Double.parseDouble(v)));
  161. break;
  162. } catch (NumberFormatException nfe) {
  163. IMessage m = MessageUtil.error(
  164. WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "double"),
  165. getSourceLocation());
  166. scope.getWorld().getMessageHandler().handleMessage(m);
  167. }
  168. } else if (t.getSignature().equals("B")) {
  169. try {
  170. replacementValues.put(k, Byte.toString(Byte.parseByte(v)));
  171. break;
  172. } catch (NumberFormatException nfe) {
  173. IMessage m = MessageUtil.error(
  174. WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "byte"),
  175. getSourceLocation());
  176. scope.getWorld().getMessageHandler().handleMessage(m);
  177. }
  178. } else if (t.getSignature().equals("C")) {
  179. if (v.length() != 3) { // '?'
  180. IMessage m = MessageUtil.error(
  181. WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "char"),
  182. getSourceLocation());
  183. scope.getWorld().getMessageHandler().handleMessage(m);
  184. } else {
  185. replacementValues.put(k, v.substring(1, 2));
  186. break;
  187. }
  188. } else {
  189. throw new RuntimeException("Not implemented for " + t);
  190. }
  191. } else if (t.equals(ResolvedType.JL_STRING)) {
  192. // nothing to do, it will be OK
  193. } else if (t.equals(ResolvedType.JL_CLASS) || (t.isParameterizedOrGenericType() && t.getRawType().equals(ResolvedType.JL_CLASS))) {
  194. String typename = v.substring(0, v.lastIndexOf('.')); // strip off '.class'
  195. ResolvedType rt = scope.lookupType(typename, this).resolve(scope.getWorld());
  196. if (rt.isMissing()) {
  197. IMessage m = MessageUtil.error("Unable to resolve type '" + v + "' specified for value '" + k + "'",
  198. getSourceLocation());
  199. scope.getWorld().getMessageHandler().handleMessage(m);
  200. }
  201. replacementValues.put(k, rt.getSignature());
  202. break;
  203. } else {
  204. if (t.isAnnotation()) {
  205. if (v.contains("(")) {
  206. throw new RuntimeException(
  207. "Compiler limitation: annotation values can only currently be marker annotations (no values): "
  208. + v);
  209. }
  210. String typename = v.substring(1);
  211. ResolvedType rt = scope.lookupType(typename, this).resolve(scope.getWorld());
  212. if (rt.isMissing()) {
  213. IMessage m = MessageUtil.error(
  214. "Unable to resolve type '" + v + "' specified for value '" + k + "'", getSourceLocation());
  215. scope.getWorld().getMessageHandler().handleMessage(m);
  216. }
  217. replacementValues.put(k, rt.getSignature());
  218. break;
  219. // } else if (t.isArray()) {
  220. // Looks like {} aren't pseudotokens in the parser so they don't get through for our pointcut parser
  221. // // @Foo(value=[Foo.class])
  222. // String typename = v.substring(0, v.lastIndexOf('.')); // strip off '.class'
  223. // ResolvedType rt = scope.lookupType(typename, this).resolve(scope.getWorld());
  224. // if (rt.isMissing()) {
  225. // IMessage m = MessageUtil.error("Unable to resolve type '" + v + "' specified for value '" + k + "'",
  226. // getSourceLocation());
  227. // scope.getWorld().getMessageHandler().handleMessage(m);
  228. // }
  229. // replacementValues.put(k, rt.getSignature());
  230. } else {
  231. scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.UNSUPPORTED_ANNOTATION_VALUE_TYPE, t), getSourceLocation()));
  232. replacementValues.put(k, "");
  233. }
  234. }
  235. }
  236. }
  237. if (!validKey) {
  238. IMessage m = MessageUtil.error(WeaverMessages.format(WeaverMessages.UNKNOWN_ANNOTATION_VALUE, annotationType, k),
  239. getSourceLocation());
  240. scope.getWorld().getMessageHandler().handleMessage(m);
  241. }
  242. }
  243. annotationValues.putAll(replacementValues);
  244. }
  245. @Override
  246. public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) {
  247. if (!resolved) {
  248. throw new IllegalStateException("Can't match on an unresolved annotation type pattern");
  249. }
  250. if (annotationValues != null && !typePattern.hasFailedResolution()) {
  251. // PVAL improve this restriction, would allow '*(value=Color.RED)'
  252. throw new IllegalStateException("Cannot use annotationvalues with a wild annotation pattern");
  253. }
  254. if (isForParameterAnnotationMatch()) {
  255. if (parameterAnnotations != null && parameterAnnotations.length != 0) {
  256. for (ResolvedType parameterAnnotation : parameterAnnotations) {
  257. if (typePattern.matches(parameterAnnotation, TypePattern.STATIC).alwaysTrue()) {
  258. return FuzzyBoolean.YES;
  259. }
  260. }
  261. }
  262. } else {
  263. // matches if the type of any of the annotations on the AnnotatedElement is
  264. // matched by the typePattern.
  265. ResolvedType[] annTypes = annotated.getAnnotationTypes();
  266. if (annTypes != null && annTypes.length != 0) {
  267. for (ResolvedType annType : annTypes) {
  268. if (typePattern.matches(annType, TypePattern.STATIC).alwaysTrue()) {
  269. return FuzzyBoolean.YES;
  270. }
  271. }
  272. }
  273. }
  274. return FuzzyBoolean.NO;
  275. }
  276. /*
  277. * (non-Javadoc)
  278. *
  279. * @see org.aspectj.weaver.patterns.AnnotationTypePattern#resolve(org.aspectj.weaver.World)
  280. */
  281. @Override
  282. public void resolve(World world) {
  283. if (!resolved) {
  284. // attempt resolution - this helps with the Spring bug where they resolve() the pointcut in no scope (SPR-5307)
  285. if (typePattern instanceof WildTypePattern && (annotationValues == null || annotationValues.isEmpty())) {
  286. WildTypePattern wildTypePattern = (WildTypePattern) typePattern;
  287. String fullyQualifiedName = wildTypePattern.maybeGetCleanName();
  288. if (fullyQualifiedName != null && fullyQualifiedName.contains(".")) {
  289. ResolvedType resolvedType = world.resolve(UnresolvedType.forName(fullyQualifiedName));
  290. if (resolvedType != null && !resolvedType.isMissing()) {
  291. typePattern = new ExactTypePattern(resolvedType, false, false);
  292. }
  293. }
  294. }
  295. resolved = true;
  296. }
  297. }
  298. /**
  299. * This can modify in place, or return a new TypePattern if the type changes.
  300. */
  301. @Override
  302. public AnnotationTypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding) {
  303. if (!scope.getWorld().isInJava5Mode()) {
  304. scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.ANNOTATIONS_NEED_JAVA5), getSourceLocation()));
  305. return this;
  306. }
  307. if (resolved) {
  308. return this;
  309. }
  310. this.typePattern = typePattern.resolveBindings(scope, bindings, false, false);
  311. resolved = true;
  312. if (typePattern instanceof ExactTypePattern) {
  313. ExactTypePattern et = (ExactTypePattern) typePattern;
  314. if (!et.getExactType().resolve(scope.getWorld()).isAnnotation()) {
  315. IMessage m = MessageUtil.error(
  316. WeaverMessages.format(WeaverMessages.REFERENCE_TO_NON_ANNOTATION_TYPE, et.getExactType().getName()),
  317. getSourceLocation());
  318. scope.getWorld().getMessageHandler().handleMessage(m);
  319. resolved = false;
  320. }
  321. ResolvedType annotationType = et.getExactType().resolve(scope.getWorld());
  322. resolveAnnotationValues(annotationType, scope);
  323. ExactAnnotationTypePattern eatp = new ExactAnnotationTypePattern(annotationType, annotationValues);
  324. eatp.copyLocationFrom(this);
  325. if (isForParameterAnnotationMatch()) {
  326. eatp.setForParameterAnnotationMatch();
  327. }
  328. return eatp;
  329. } else {
  330. return this;
  331. }
  332. }
  333. @Override
  334. public AnnotationTypePattern parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) {
  335. WildAnnotationTypePattern ret = new WildAnnotationTypePattern(typePattern.parameterizeWith(typeVariableMap, w));
  336. ret.copyLocationFrom(this);
  337. ret.resolved = resolved;
  338. return ret;
  339. }
  340. private static final byte VERSION = 1; // rev if ser. form changes
  341. @Override
  342. public void write(CompressingDataOutputStream s) throws IOException {
  343. s.writeByte(AnnotationTypePattern.WILD);
  344. s.writeByte(VERSION);
  345. typePattern.write(s);
  346. writeLocation(s);
  347. s.writeBoolean(isForParameterAnnotationMatch());
  348. // PVAL
  349. if (annotationValues == null) {
  350. s.writeInt(0);
  351. } else {
  352. s.writeInt(annotationValues.size());
  353. Set<String> key = annotationValues.keySet();
  354. for (String k : key) {
  355. s.writeUTF(k);
  356. s.writeUTF(annotationValues.get(k));
  357. }
  358. }
  359. }
  360. public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException {
  361. WildAnnotationTypePattern ret;
  362. byte version = s.readByte();
  363. if (version > VERSION) {
  364. throw new BCException("ExactAnnotationTypePattern was written by a newer version of AspectJ");
  365. }
  366. TypePattern t = TypePattern.read(s, context);
  367. ret = new WildAnnotationTypePattern(t);
  368. ret.readLocation(context, s);
  369. if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ160) {
  370. if (s.readBoolean()) {
  371. ret.setForParameterAnnotationMatch();
  372. }
  373. }
  374. if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ160M2) {
  375. int annotationValueCount = s.readInt();
  376. if (annotationValueCount > 0) {
  377. Map<String, String> aValues = new HashMap<String, String>();
  378. for (int i = 0; i < annotationValueCount; i++) {
  379. String key = s.readUTF();
  380. String val = s.readUTF();
  381. aValues.put(key, val);
  382. }
  383. ret.annotationValues = aValues;
  384. }
  385. }
  386. return ret;
  387. }
  388. @Override
  389. public boolean equals(Object obj) {
  390. if (!(obj instanceof WildAnnotationTypePattern)) {
  391. return false;
  392. }
  393. WildAnnotationTypePattern other = (WildAnnotationTypePattern) obj;
  394. return other.typePattern.equals(typePattern)
  395. && this.isForParameterAnnotationMatch() == other.isForParameterAnnotationMatch()
  396. && (annotationValues == null ? other.annotationValues == null : annotationValues.equals(other.annotationValues));
  397. }
  398. @Override
  399. public int hashCode() {
  400. return (((17 + 37 * typePattern.hashCode()) * 37 + (isForParameterAnnotationMatch() ? 0 : 1)) * 37)
  401. + (annotationValues == null ? 0 : annotationValues.hashCode());
  402. }
  403. @Override
  404. public String toString() {
  405. return "@(" + typePattern.toString() + ")";
  406. }
  407. @Override
  408. public Object accept(PatternNodeVisitor visitor, Object data) {
  409. return visitor.visit(this, data);
  410. }
  411. }