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

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