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.

WildTypePattern.java 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. /* *******************************************************************
  2. * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Common Public License v1.0
  6. * which accompanies this distribution and is available at
  7. * http://www.eclipse.org/legal/cpl-v10.html
  8. *
  9. * Contributors:
  10. * PARC initial implementation
  11. * ******************************************************************/
  12. package org.aspectj.weaver.patterns;
  13. import java.io.DataInputStream;
  14. import java.io.DataOutputStream;
  15. import java.io.IOException;
  16. import java.util.ArrayList;
  17. import java.util.List;
  18. import org.aspectj.bridge.IMessage;
  19. import org.aspectj.bridge.Message;
  20. import org.aspectj.bridge.MessageUtil;
  21. import org.aspectj.util.FileUtil;
  22. import org.aspectj.util.FuzzyBoolean;
  23. import org.aspectj.weaver.ISourceContext;
  24. import org.aspectj.weaver.ResolvedTypeX;
  25. import org.aspectj.weaver.TypeX;
  26. import org.aspectj.weaver.WeaverMessages;
  27. //XXX need to use dim in matching
  28. public class WildTypePattern extends TypePattern {
  29. NamePattern[] namePatterns;
  30. int ellipsisCount;
  31. String[] importedPrefixes;
  32. String[] knownMatches;
  33. int dim;
  34. WildTypePattern(NamePattern[] namePatterns, boolean includeSubtypes, int dim) {
  35. super(includeSubtypes);
  36. this.namePatterns = namePatterns;
  37. this.dim = dim;
  38. ellipsisCount = 0;
  39. for (int i=0; i<namePatterns.length; i++) {
  40. if (namePatterns[i] == NamePattern.ELLIPSIS) ellipsisCount++;
  41. }
  42. setLocation(namePatterns[0].getSourceContext(), namePatterns[0].getStart(), namePatterns[namePatterns.length-1].getEnd());
  43. }
  44. public WildTypePattern(List names, boolean includeSubtypes, int dim) {
  45. this((NamePattern[])names.toArray(new NamePattern[names.size()]), includeSubtypes, dim);
  46. }
  47. public WildTypePattern(List names, boolean includeSubtypes, int dim, int endPos) {
  48. this(names, includeSubtypes, dim);
  49. this.end = endPos;
  50. }
  51. //XXX inefficient implementation
  52. public static char[][] splitNames(String s) {
  53. List ret = new ArrayList();
  54. int startIndex = 0;
  55. while (true) {
  56. int breakIndex = s.indexOf('.', startIndex); // what about /
  57. if (breakIndex == -1) breakIndex = s.indexOf('$', startIndex); // we treat $ like . here
  58. if (breakIndex == -1) break;
  59. char[] name = s.substring(startIndex, breakIndex).toCharArray();
  60. ret.add(name);
  61. startIndex = breakIndex+1;
  62. }
  63. ret.add(s.substring(startIndex).toCharArray());
  64. return (char[][])ret.toArray(new char[ret.size()][]);
  65. }
  66. /**
  67. * @see org.aspectj.weaver.TypePattern#matchesExactly(IType)
  68. */
  69. protected boolean matchesExactly(ResolvedTypeX type) {
  70. String targetTypeName = type.getName();
  71. //System.err.println("match: " + targetTypeName + ", " + knownMatches); //Arrays.asList(importedPrefixes));
  72. return matchesExactlyByName(targetTypeName);
  73. }
  74. /**
  75. * Used in conjunction with checks on 'isStar()' to tell you if this pattern represents '*' or '*[]' which are
  76. * different !
  77. */
  78. public int getDimensions() {
  79. return dim;
  80. }
  81. /**
  82. * @param targetTypeName
  83. * @return
  84. */
  85. private boolean matchesExactlyByName(String targetTypeName) {
  86. //XXX hack
  87. if (knownMatches == null && importedPrefixes == null) {
  88. return innerMatchesExactly(targetTypeName);
  89. }
  90. if (isStar()) {
  91. // we match if the dimensions match
  92. int numDimensionsInTargetType = 0;
  93. if (dim > 0) {
  94. int index;
  95. while((index = targetTypeName.indexOf('[')) != -1) {
  96. numDimensionsInTargetType++;
  97. targetTypeName = targetTypeName.substring(index+1);
  98. }
  99. if (numDimensionsInTargetType == dim) {
  100. return true;
  101. } else {
  102. return false;
  103. }
  104. }
  105. }
  106. // if our pattern is length 1, then known matches are exact matches
  107. // if it's longer than that, then known matches are prefixes of a sort
  108. if (namePatterns.length == 1) {
  109. for (int i=0, len=knownMatches.length; i < len; i++) {
  110. if (knownMatches[i].equals(targetTypeName)) return true;
  111. }
  112. } else {
  113. for (int i=0, len=knownMatches.length; i < len; i++) {
  114. String knownPrefix = knownMatches[i] + "$";
  115. if (targetTypeName.startsWith(knownPrefix)) {
  116. int pos = lastIndexOfDotOrDollar(knownMatches[i]);
  117. if (innerMatchesExactly(targetTypeName.substring(pos+1))) {
  118. return true;
  119. }
  120. }
  121. }
  122. }
  123. // if any prefixes match, strip the prefix and check that the rest matches
  124. // assumes that prefixes have a dot at the end
  125. for (int i=0, len=importedPrefixes.length; i < len; i++) {
  126. String prefix = importedPrefixes[i];
  127. //System.err.println("prefix match? " + prefix + " to " + targetTypeName);
  128. if (targetTypeName.startsWith(prefix)) {
  129. if (innerMatchesExactly(targetTypeName.substring(prefix.length()))) {
  130. return true;
  131. }
  132. }
  133. }
  134. return innerMatchesExactly(targetTypeName);
  135. }
  136. private int lastIndexOfDotOrDollar(String string) {
  137. int dot = string.lastIndexOf('.');
  138. int dollar = string.lastIndexOf('$');
  139. return Math.max(dot, dollar);
  140. }
  141. private boolean innerMatchesExactly(String targetTypeName) {
  142. //??? doing this everytime is not very efficient
  143. char[][] names = splitNames(targetTypeName);
  144. return innerMatchesExactly(names);
  145. }
  146. private boolean innerMatchesExactly(char[][] names) {
  147. int namesLength = names.length;
  148. int patternsLength = namePatterns.length;
  149. int namesIndex = 0;
  150. int patternsIndex = 0;
  151. if (ellipsisCount == 0) {
  152. if (namesLength != patternsLength) return false;
  153. while (patternsIndex < patternsLength) {
  154. if (!namePatterns[patternsIndex++].matches(names[namesIndex++])) {
  155. return false;
  156. }
  157. }
  158. return true;
  159. } else if (ellipsisCount == 1) {
  160. if (namesLength < patternsLength-1) return false;
  161. while (patternsIndex < patternsLength) {
  162. NamePattern p = namePatterns[patternsIndex++];
  163. if (p == NamePattern.ELLIPSIS) {
  164. namesIndex = namesLength - (patternsLength-patternsIndex);
  165. } else {
  166. if (!p.matches(names[namesIndex++])) {
  167. return false;
  168. }
  169. }
  170. }
  171. return true;
  172. } else {
  173. // System.err.print("match(\"" + Arrays.asList(namePatterns) + "\", \"" + Arrays.asList(names) + "\") -> ");
  174. boolean b = outOfStar(namePatterns, names, 0, 0, patternsLength - ellipsisCount, namesLength, ellipsisCount);
  175. // System.err.println(b);
  176. return b;
  177. }
  178. }
  179. private static boolean outOfStar(final NamePattern[] pattern, final char[][] target,
  180. int pi, int ti,
  181. int pLeft, int tLeft,
  182. final int starsLeft) {
  183. if (pLeft > tLeft) return false;
  184. while (true) {
  185. // invariant: if (tLeft > 0) then (ti < target.length && pi < pattern.length)
  186. if (tLeft == 0) return true;
  187. if (pLeft == 0) {
  188. return (starsLeft > 0);
  189. }
  190. if (pattern[pi] == NamePattern.ELLIPSIS) {
  191. return inStar(pattern, target, pi+1, ti, pLeft, tLeft, starsLeft-1);
  192. }
  193. if (! pattern[pi].matches(target[ti])) {
  194. return false;
  195. }
  196. pi++; ti++; pLeft--; tLeft--;
  197. }
  198. }
  199. private static boolean inStar(final NamePattern[] pattern, final char[][] target,
  200. int pi, int ti,
  201. final int pLeft, int tLeft,
  202. int starsLeft) {
  203. // invariant: pLeft > 0, so we know we'll run out of stars and find a real char in pattern
  204. // of course, we probably can't parse multiple ..'s in a row, but this keeps the algorithm
  205. // exactly parallel with that in NamePattern
  206. NamePattern patternChar = pattern[pi];
  207. while (patternChar == NamePattern.ELLIPSIS) {
  208. starsLeft--;
  209. patternChar = pattern[++pi];
  210. }
  211. while (true) {
  212. // invariant: if (tLeft > 0) then (ti < target.length)
  213. if (pLeft > tLeft) return false;
  214. if (patternChar.matches(target[ti])) {
  215. if (outOfStar(pattern, target, pi+1, ti+1, pLeft-1, tLeft-1, starsLeft)) return true;
  216. }
  217. ti++; tLeft--;
  218. }
  219. }
  220. /**
  221. * @see org.aspectj.weaver.TypePattern#matchesInstanceof(IType)
  222. */
  223. public FuzzyBoolean matchesInstanceof(ResolvedTypeX type) {
  224. //XXX hack to let unmatched types just silently remain so
  225. if (maybeGetSimpleName() != null) return FuzzyBoolean.NO;
  226. type.getWorld().getMessageHandler().handleMessage(
  227. new Message("can't do instanceof matching on patterns with wildcards",
  228. IMessage.ERROR, null, getSourceLocation()));
  229. return FuzzyBoolean.NO;
  230. }
  231. public NamePattern extractName() {
  232. //System.err.println("extract from : " + Arrays.asList(namePatterns));
  233. int len = namePatterns.length;
  234. NamePattern ret = namePatterns[len-1];
  235. NamePattern[] newNames = new NamePattern[len-1];
  236. System.arraycopy(namePatterns, 0, newNames, 0, len-1);
  237. namePatterns = newNames;
  238. //System.err.println(" left : " + Arrays.asList(namePatterns));
  239. return ret;
  240. }
  241. /**
  242. * Method maybeExtractName.
  243. * @param string
  244. * @return boolean
  245. */
  246. public boolean maybeExtractName(String string) {
  247. int len = namePatterns.length;
  248. NamePattern ret = namePatterns[len-1];
  249. String simple = ret.maybeGetSimpleName();
  250. if (simple != null && simple.equals(string)) {
  251. extractName();
  252. return true;
  253. }
  254. return false;
  255. }
  256. /**
  257. * If this type pattern has no '.' or '*' in it, then
  258. * return a simple string
  259. *
  260. * otherwise, this will return null;
  261. */
  262. public String maybeGetSimpleName() {
  263. if (namePatterns.length == 1) {
  264. return namePatterns[0].maybeGetSimpleName();
  265. }
  266. return null;
  267. }
  268. /**
  269. * If this type pattern has no '*' or '..' in it
  270. */
  271. public String maybeGetCleanName() {
  272. if (namePatterns.length == 0) {
  273. throw new RuntimeException("bad name: " + namePatterns);
  274. }
  275. //System.out.println("get clean: " + this);
  276. StringBuffer buf = new StringBuffer();
  277. for (int i=0, len=namePatterns.length; i < len; i++) {
  278. NamePattern p = namePatterns[i];
  279. String simpleName = p.maybeGetSimpleName();
  280. if (simpleName == null) return null;
  281. if (i > 0) buf.append(".");
  282. buf.append(simpleName);
  283. }
  284. //System.out.println(buf);
  285. return buf.toString();
  286. }
  287. /**
  288. * Need to determine if I'm really a pattern or a reference to a formal
  289. *
  290. * We may wish to further optimize the case of pattern vs. non-pattern
  291. *
  292. * We will be replaced by what we return
  293. */
  294. public TypePattern resolveBindings(IScope scope, Bindings bindings,
  295. boolean allowBinding, boolean requireExactType)
  296. {
  297. if (isStar()) {
  298. if (dim == 0) { // pr72531
  299. return TypePattern.ANY; //??? loses source location
  300. }
  301. }
  302. String simpleName = maybeGetSimpleName();
  303. if (simpleName != null) {
  304. FormalBinding formalBinding = scope.lookupFormal(simpleName);
  305. if (formalBinding != null) {
  306. if (bindings == null) {
  307. scope.message(IMessage.ERROR, this, "negation doesn't allow binding");
  308. return this;
  309. }
  310. if (!allowBinding) {
  311. scope.message(IMessage.ERROR, this,
  312. "name binding only allowed in target, this, and args pcds");
  313. return this;
  314. }
  315. BindingTypePattern binding = new BindingTypePattern(formalBinding);
  316. binding.copyLocationFrom(this);
  317. bindings.register(binding, scope);
  318. return binding;
  319. }
  320. }
  321. String cleanName = maybeGetCleanName();
  322. String originalName = cleanName;
  323. // if we discover it is 'MISSING' when searching via the scope, this next local var will
  324. // tell us if it is really missing or if it does exist in the world and we just can't
  325. // see it from the current scope.
  326. ResolvedTypeX resolvedTypeInTheWorld = null;
  327. if (cleanName != null) {
  328. TypeX type;
  329. //System.out.println("resolve: " + cleanName);
  330. //??? this loop has too many inefficiencies to count
  331. resolvedTypeInTheWorld = scope.getWorld().resolve(TypeX.forName(cleanName),true);
  332. while ((type = scope.lookupType(cleanName, this)) == ResolvedTypeX.MISSING) {
  333. int lastDot = cleanName.lastIndexOf('.');
  334. if (lastDot == -1) break;
  335. cleanName = cleanName.substring(0, lastDot) + '$' + cleanName.substring(lastDot+1);
  336. if (resolvedTypeInTheWorld == ResolvedTypeX.MISSING)
  337. resolvedTypeInTheWorld = scope.getWorld().resolve(TypeX.forName(cleanName),true);
  338. }
  339. if (type == ResolvedTypeX.MISSING) {
  340. if (requireExactType) {
  341. if (!allowBinding) {
  342. scope.getWorld().getMessageHandler().handleMessage(
  343. MessageUtil.error(WeaverMessages.format(WeaverMessages.CANT_BIND_TYPE,originalName),
  344. getSourceLocation()));
  345. } else if (scope.getWorld().getLint().invalidAbsoluteTypeName.isEnabled()) {
  346. scope.getWorld().getLint().invalidAbsoluteTypeName.signal(originalName, getSourceLocation());
  347. }
  348. return NO;
  349. } else if (scope.getWorld().getLint().invalidAbsoluteTypeName.isEnabled()) {
  350. // Only put the lint warning out if we can't find it in the world
  351. if (resolvedTypeInTheWorld == ResolvedTypeX.MISSING)
  352. scope.getWorld().getLint().invalidAbsoluteTypeName.signal(originalName, getSourceLocation());
  353. }
  354. } else {
  355. if (dim != 0) type = TypeX.makeArray(type, dim);
  356. TypePattern ret = new ExactTypePattern(type, includeSubtypes);
  357. ret.copyLocationFrom(this);
  358. return ret;
  359. }
  360. } else {
  361. if (requireExactType) {
  362. scope.getWorld().getMessageHandler().handleMessage(
  363. MessageUtil.error(WeaverMessages.format(WeaverMessages.WILDCARD_NOT_ALLOWED),
  364. getSourceLocation()));
  365. return NO;
  366. }
  367. //XXX need to implement behavior for Lint.invalidWildcardTypeName
  368. }
  369. importedPrefixes = scope.getImportedPrefixes();
  370. knownMatches = preMatch(scope.getImportedNames());
  371. return this;
  372. }
  373. public TypePattern resolveBindingsFromRTTI(boolean allowBinding, boolean requireExactType) {
  374. if (isStar()) {
  375. return TypePattern.ANY; //??? loses source location
  376. }
  377. String cleanName = maybeGetCleanName();
  378. if (cleanName != null) {
  379. Class clazz = null;
  380. clazz = maybeGetPrimitiveClass(cleanName);
  381. while (clazz == null) {
  382. try {
  383. clazz = Class.forName(cleanName);
  384. } catch (ClassNotFoundException cnf) {
  385. int lastDotIndex = cleanName.lastIndexOf('.');
  386. if (lastDotIndex == -1) break;
  387. cleanName = cleanName.substring(0, lastDotIndex) + '$' + cleanName.substring(lastDotIndex+1);
  388. }
  389. }
  390. if (clazz == null) {
  391. try {
  392. clazz = Class.forName("java.lang." + cleanName);
  393. } catch (ClassNotFoundException cnf) {
  394. }
  395. }
  396. if (clazz == null) {
  397. if (requireExactType) {
  398. return NO;
  399. }
  400. } else {
  401. TypeX type = TypeX.forName(clazz.getName());
  402. if (dim != 0) type = TypeX.makeArray(type,dim);
  403. TypePattern ret = new ExactTypePattern(type, includeSubtypes);
  404. ret.copyLocationFrom(this);
  405. return ret;
  406. }
  407. } else if (requireExactType) {
  408. return NO;
  409. }
  410. importedPrefixes = SimpleScope.javaLangPrefixArray;
  411. knownMatches = new String[0];
  412. return this;
  413. }
  414. private Class maybeGetPrimitiveClass(String typeName) {
  415. return (Class) ExactTypePattern.primitiveTypesMap.get(typeName);
  416. }
  417. public boolean isStar() {
  418. return namePatterns.length == 1 && namePatterns[0].isAny();
  419. }
  420. /**
  421. * returns those possible matches which I match exactly the last element of
  422. */
  423. private String[] preMatch(String[] possibleMatches) {
  424. //if (namePatterns.length != 1) return CollectionUtil.NO_STRINGS;
  425. List ret = new ArrayList();
  426. for (int i=0, len=possibleMatches.length; i < len; i++) {
  427. char[][] names = splitNames(possibleMatches[i]); //??? not most efficient
  428. if (namePatterns[0].matches(names[names.length-1])) {
  429. ret.add(possibleMatches[i]);
  430. }
  431. }
  432. return (String[])ret.toArray(new String[ret.size()]);
  433. }
  434. // public void postRead(ResolvedTypeX enclosingType) {
  435. // this.importedPrefixes = enclosingType.getImportedPrefixes();
  436. // this.knownNames = prematch(enclosingType.getImportedNames());
  437. // }
  438. public String toString() {
  439. StringBuffer buf = new StringBuffer();
  440. for (int i=0, len=namePatterns.length; i < len; i++) {
  441. NamePattern name = namePatterns[i];
  442. if (name == null) {
  443. buf.append(".");
  444. } else {
  445. if (i > 0) buf.append(".");
  446. buf.append(name.toString());
  447. }
  448. }
  449. return buf.toString();
  450. }
  451. public boolean equals(Object other) {
  452. if (!(other instanceof WildTypePattern)) return false;
  453. WildTypePattern o = (WildTypePattern)other;
  454. int len = o.namePatterns.length;
  455. if (len != this.namePatterns.length) return false;
  456. for (int i=0; i < len; i++) {
  457. if (!o.namePatterns[i].equals(this.namePatterns[i])) return false;
  458. }
  459. return true;
  460. }
  461. public int hashCode() {
  462. int result = 17;
  463. for (int i = 0, len = namePatterns.length; i < len; i++) {
  464. result = 37*result + namePatterns[i].hashCode();
  465. }
  466. return result;
  467. }
  468. public FuzzyBoolean matchesInstanceof(Class type) {
  469. return FuzzyBoolean.NO;
  470. }
  471. public boolean matchesExactly(Class type) {
  472. return matchesExactlyByName(type.getName());
  473. }
  474. /**
  475. * @see org.aspectj.weaver.patterns.PatternNode#write(DataOutputStream)
  476. */
  477. public void write(DataOutputStream s) throws IOException {
  478. s.writeByte(TypePattern.WILD);
  479. s.writeShort(namePatterns.length);
  480. for (int i = 0; i < namePatterns.length; i++) {
  481. namePatterns[i].write(s);
  482. }
  483. s.writeBoolean(includeSubtypes);
  484. s.writeInt(dim);
  485. //??? storing this information with every type pattern is wasteful of .class
  486. // file size. Storing it on enclosing types would be more efficient
  487. FileUtil.writeStringArray(knownMatches, s);
  488. FileUtil.writeStringArray(importedPrefixes, s);
  489. writeLocation(s);
  490. }
  491. public static TypePattern read(DataInputStream s, ISourceContext context) throws IOException {
  492. int len = s.readShort();
  493. NamePattern[] namePatterns = new NamePattern[len];
  494. for (int i=0; i < len; i++) {
  495. namePatterns[i] = NamePattern.read(s);
  496. }
  497. boolean includeSubtypes = s.readBoolean();
  498. int dim = s.readInt();
  499. WildTypePattern ret = new WildTypePattern(namePatterns, includeSubtypes, dim);
  500. ret.knownMatches = FileUtil.readStringArray(s);
  501. ret.importedPrefixes = FileUtil.readStringArray(s);
  502. ret.readLocation(context, s);
  503. return ret;
  504. }
  505. }