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

21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
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. }