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.

ExactAnnotationFieldTypePattern.java 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. /* *******************************************************************
  2. * Copyright (c) 2008 Contributors
  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. * Contributors:
  10. * Andy Clement initial implementation
  11. * ******************************************************************/
  12. package org.aspectj.weaver.patterns;
  13. import java.io.IOException;
  14. import java.util.Map;
  15. import org.aspectj.bridge.IMessage;
  16. import org.aspectj.util.FuzzyBoolean;
  17. import org.aspectj.weaver.AnnotatedElement;
  18. import org.aspectj.weaver.BCException;
  19. import org.aspectj.weaver.CompressingDataOutputStream;
  20. import org.aspectj.weaver.ISourceContext;
  21. import org.aspectj.weaver.ReferenceType;
  22. import org.aspectj.weaver.ResolvedMember;
  23. import org.aspectj.weaver.ResolvedType;
  24. import org.aspectj.weaver.UnresolvedType;
  25. import org.aspectj.weaver.VersionedDataInputStream;
  26. import org.aspectj.weaver.World;
  27. /**
  28. * Represents an attempt to bind the field of an annotation within a pointcut. For example:
  29. * <pre><code>
  30. * before(Level lev): execution(* *(..)) &amp;&amp; @annotation(TraceAnnotation(lev))
  31. * </code></pre>
  32. * <p>This binding annotation type pattern will be for 'lev'.</p>
  33. */
  34. public class ExactAnnotationFieldTypePattern extends ExactAnnotationTypePattern {
  35. UnresolvedType annotationType;
  36. private ResolvedMember field;
  37. public ExactAnnotationFieldTypePattern(ExactAnnotationTypePattern p, String formalName) {
  38. super(formalName);
  39. this.annotationType = p.annotationType;
  40. this.copyLocationFrom(p);
  41. }
  42. public ExactAnnotationFieldTypePattern(UnresolvedType annotationType, String formalName) {
  43. super(formalName);
  44. this.annotationType = annotationType;
  45. }
  46. /**
  47. * resolve one of these funky things. Need to: <br>
  48. * (a) Check the formal is bound <br>
  49. * (b) Check the annotation type is valid
  50. */
  51. @Override
  52. public AnnotationTypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding) {
  53. if (resolved) {
  54. return this;
  55. }
  56. resolved = true;
  57. FormalBinding formalBinding = scope.lookupFormal(formalName);
  58. if (formalBinding == null) {
  59. scope.message(IMessage.ERROR, this,
  60. "When using @annotation(<annotationType>(<annotationField>)), <annotationField> must be bound");
  61. return this;
  62. }
  63. annotationType = scope.getWorld().resolve(annotationType, true);
  64. // May not be directly found if in a package, so go looking if that is the case:
  65. if (ResolvedType.isMissing(annotationType)) {
  66. String cleanname = annotationType.getName();
  67. UnresolvedType type = null;
  68. while (ResolvedType.isMissing(type = scope.lookupType(cleanname, this))) {
  69. int lastDot = cleanname.lastIndexOf('.');
  70. if (lastDot == -1) {
  71. break;
  72. }
  73. cleanname = cleanname.substring(0, lastDot) + "$" + cleanname.substring(lastDot + 1);
  74. }
  75. annotationType = scope.getWorld().resolve(type, true);
  76. if (ResolvedType.isMissing(annotationType)) {
  77. // there are likely to be other errors around that have led to us being unable to
  78. // resolve the annotation type, let's quit now
  79. return this;
  80. }
  81. }
  82. verifyIsAnnotationType((ResolvedType) annotationType, scope);
  83. ResolvedType formalBindingType = formalBinding.getType().resolve(scope.getWorld());
  84. String bindingTypeSignature = formalBindingType.getSignature();
  85. if (!(formalBindingType.isEnum() || bindingTypeSignature.equals("Ljava/lang/String;") || bindingTypeSignature.equals("I"))) {
  86. scope.message(IMessage.ERROR, this,
  87. "The field within the annotation must be an enum, string or int. '" + formalBinding.getType()
  88. + "' is not (compiler limitation)");
  89. }
  90. bindingPattern = true;
  91. // Check that the formal is bound to a type that is represented by one field in the annotation type
  92. ReferenceType theAnnotationType = (ReferenceType) annotationType;
  93. ResolvedMember[] annotationFields = theAnnotationType.getDeclaredMethods();
  94. field = null;
  95. boolean looksAmbiguous = false;
  96. for (ResolvedMember resolvedMember : annotationFields) {
  97. if (resolvedMember.getReturnType().equals(formalBinding.getType())) {
  98. if (field != null) {
  99. boolean haveProblem = true;
  100. // use the name to differentiate
  101. if (field.getName().equals(formalName)) {
  102. // don't use this new field
  103. haveProblem = false;
  104. } else if (resolvedMember.getName().equals(formalName)) {
  105. // ok, let's use this one
  106. field = resolvedMember;
  107. haveProblem = false;
  108. }
  109. if (haveProblem) {
  110. looksAmbiguous = true;
  111. }
  112. } else {
  113. field = resolvedMember;
  114. }
  115. }
  116. }
  117. if (looksAmbiguous) {
  118. // did we find something that does match by name?
  119. if (field == null || !field.getName().equals(formalName)) {
  120. scope.message(IMessage.ERROR, this, "The field type '" + formalBinding.getType()
  121. + "' is ambiguous for annotation type '" + theAnnotationType.getName() + "'");
  122. }
  123. }
  124. if (field == null) {
  125. scope.message(IMessage.ERROR, this, "No field of type '" + formalBinding.getType() + "' exists on annotation type '"
  126. + theAnnotationType.getName() + "'");
  127. }
  128. BindingAnnotationFieldTypePattern binding = new BindingAnnotationFieldTypePattern(formalBinding.getType(),
  129. formalBinding.getIndex(), theAnnotationType);
  130. binding.copyLocationFrom(this);
  131. binding.formalName = this.formalName;
  132. bindings.register(binding, scope);
  133. binding.resolveBinding(scope.getWorld());
  134. return binding;
  135. }
  136. @Override
  137. public void write(CompressingDataOutputStream s) throws IOException {
  138. s.writeByte(AnnotationTypePattern.EXACTFIELD);
  139. s.writeUTF(formalName);
  140. annotationType.write(s);
  141. writeLocation(s);
  142. }
  143. public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException {
  144. ExactAnnotationFieldTypePattern ret;
  145. String formalName = s.readUTF();
  146. UnresolvedType annotationType = UnresolvedType.read(s);
  147. ret = new ExactAnnotationFieldTypePattern(annotationType, formalName);
  148. ret.readLocation(context, s);
  149. return ret;
  150. }
  151. // ---
  152. @Override
  153. public Object accept(PatternNodeVisitor visitor, Object data) {
  154. return visitor.visit(this, data);
  155. }
  156. @Override
  157. public boolean equals(Object obj) {
  158. if (!(obj instanceof ExactAnnotationFieldTypePattern)) {
  159. return false;
  160. }
  161. ExactAnnotationFieldTypePattern other = (ExactAnnotationFieldTypePattern) obj;
  162. return (other.annotationType.equals(annotationType)) && (other.field.equals(field))
  163. && (other.formalName.equals(this.formalName));
  164. }
  165. @Override
  166. public int hashCode() {
  167. int hashcode = annotationType.hashCode();
  168. hashcode = hashcode * 37 + field.hashCode();
  169. hashcode = hashcode * 37 + formalName.hashCode();
  170. return hashcode;
  171. }
  172. // TODO these are currently unimplemented as I believe it resolves to a Binding form *always* and so they don't get
  173. // called
  174. @Override
  175. public FuzzyBoolean fastMatches(AnnotatedElement annotated) {
  176. throw new BCException("unimplemented");
  177. }
  178. @Override
  179. public UnresolvedType getAnnotationType() {
  180. throw new BCException("unimplemented");
  181. }
  182. @Override
  183. public Map getAnnotationValues() {
  184. throw new BCException("unimplemented");
  185. }
  186. @Override
  187. public ResolvedType getResolvedAnnotationType() {
  188. throw new BCException("unimplemented");
  189. }
  190. @Override
  191. public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) {
  192. throw new BCException("unimplemented");
  193. }
  194. @Override
  195. public FuzzyBoolean matches(AnnotatedElement annotated) {
  196. throw new BCException("unimplemented");
  197. }
  198. @Override
  199. public FuzzyBoolean matchesRuntimeType(AnnotatedElement annotated) {
  200. throw new BCException("unimplemented");
  201. }
  202. @Override
  203. public AnnotationTypePattern parameterizeWith(Map typeVariableMap, World w) {
  204. throw new BCException("unimplemented");
  205. }
  206. @Override
  207. public void resolve(World world) {
  208. throw new BCException("unimplemented");
  209. }
  210. @Override
  211. public String toString() {
  212. if (!resolved && formalName != null) {
  213. return formalName;
  214. }
  215. StringBuilder ret = new StringBuilder();
  216. ret.append("@").append(annotationType.toString());
  217. ret.append("(").append(formalName).append(")");
  218. return ret.toString();
  219. }
  220. }