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.

BridgeUtil.java 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. /* *******************************************************************
  2. * Copyright (c) 1999-2001 Xerox Corporation,
  3. * 2002 Palo Alto Research Center, Incorporated (PARC).
  4. * All rights reserved.
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Public License v1.0
  7. * which accompanies this distribution and is available at
  8. * http://www.eclipse.org/legal/epl-v10.html
  9. *
  10. * Contributors:
  11. * Xerox/PARC initial implementation
  12. * ******************************************************************/
  13. package org.aspectj.testing.util;
  14. import java.io.File;
  15. import java.util.Comparator;
  16. import org.aspectj.bridge.IMessage;
  17. import org.aspectj.bridge.ISourceLocation;
  18. import org.aspectj.bridge.Message;
  19. import org.aspectj.bridge.MessageUtil;
  20. import org.aspectj.bridge.SourceLocation;
  21. import org.aspectj.testing.run.IRunStatus;
  22. import org.aspectj.testing.run.RunValidator;
  23. import org.aspectj.util.FileUtil;
  24. /**
  25. *
  26. */
  27. public class BridgeUtil {
  28. // private static final String INDENT = " ";
  29. /** result value when writeMessage is passed null */
  30. private static final String NULL_MESSAGE_OUTPUT = "<null message output>";
  31. /** result value when readMessage is passed null */
  32. private static final IMessage NULL_MESSAGE_INPUT = null;
  33. private static final String KIND_DELIM = ": \"";
  34. private static final String MESSAGE_DELIM = "\" - ";
  35. public static ISourceLocation makeSourceLocation(UtilLineReader reader) {
  36. LangUtil.throwIaxIfNull(reader, "reader");
  37. int line = reader.getLineNumber();
  38. return new SourceLocation(reader.getFile(), line, line, 0);
  39. }
  40. /**
  41. * Method readSourceLocation.
  42. * @param sourceLocStr
  43. * @return ISourceLocation
  44. */
  45. private static ISourceLocation readSourceLocation(String sourceLocStr) {
  46. return BridgeUtil.makeSourceLocation(sourceLocStr);
  47. }
  48. // public static IMessage makeMessage(String message, IMessage.Kind kind,
  49. // Throwable thrown, LineReader reader) {
  50. // ISourceLocation sl = (null == reader ? null : MessageUtil.makeSourceLocation(reader));
  51. // if (null == kind) kind = IMessage.INFO;
  52. // return new Message(message, kind, thrown, sl);
  53. // }
  54. /**
  55. * Read a message from a string written by writeMessage(IMessage).
  56. * Does not handle exceptions at all or source location well. XXX
  57. * @param message the String representation of a message
  58. * @return IMessage
  59. */
  60. public static IMessage readMessage(String message) {
  61. if (null == message) {
  62. return NULL_MESSAGE_INPUT;
  63. }
  64. if (NULL_MESSAGE_OUTPUT.equals(message)) {
  65. return null;
  66. }
  67. int kindEnd = message.indexOf(KIND_DELIM);
  68. int messageEnd = message.indexOf(MESSAGE_DELIM);
  69. int messageStart = kindEnd+KIND_DELIM.length();
  70. int sourceLocStart = messageEnd+MESSAGE_DELIM.length();
  71. String kindStr = message.substring(0, kindEnd);
  72. String text = message.substring(messageStart, messageEnd);
  73. String sourceLocStr = message.substring(sourceLocStart);
  74. IMessage.Kind kind = MessageUtil.getKind(kindStr);
  75. ISourceLocation loc = readSourceLocation(sourceLocStr);
  76. return new Message(text, kind, null, loc);
  77. }
  78. /**
  79. * Write a message to a string to be read by readMessage(String)
  80. * @param message the String representation of a message
  81. * @return IMessage
  82. */
  83. public static String writeMessage(IMessage message) {
  84. if (null == message) {
  85. return NULL_MESSAGE_OUTPUT;
  86. }
  87. return message.getKind()
  88. + KIND_DELIM
  89. + message.getMessage()
  90. + MESSAGE_DELIM
  91. + message.getSourceLocation(); // XXX implement
  92. }
  93. public static class Comparators {
  94. /**
  95. * Compare based solely on null-inequality:
  96. * -1 if one is not null and two is null,
  97. * 1 if one is null and two is not null,
  98. * 0 otherwise.
  99. */
  100. static int compareNull(Object one, Object two) {
  101. return (null == one
  102. ? (null == two ? 0 : 1)
  103. : (null == two ? -1 : 0));
  104. }
  105. /**
  106. * Soft comparison of String returns 0 if either is empty
  107. * or a substring of the other, and the case-insensitive
  108. * ordering otherwise.
  109. * @param lhs_s
  110. * @param rhs_s
  111. * @return
  112. */
  113. static int compareStringsSoftly(String lhs_s, String rhs_s) {
  114. if (LangUtil.isEmpty(lhs_s)
  115. || LangUtil.isEmpty(rhs_s)) {
  116. return 0;
  117. }
  118. if ((lhs_s.contains(rhs_s))
  119. || (rhs_s.contains(lhs_s))) {
  120. return 0;
  121. }
  122. return String.CASE_INSENSITIVE_ORDER.compare(lhs_s, rhs_s);
  123. }
  124. /**
  125. * This returns 0 if one file path is a suffix of the other
  126. * or a case-insensitive string comparison otherwise.
  127. * WARNING: it returns 0 if either file is
  128. * ISourceLocation.NO_FILE to permit tests to
  129. * not specify file paths.
  130. *
  131. * Use only for sorts, not to maintain maps.
  132. */
  133. public static final Comparator<File> WEAK_File = new Comparator<File>() {
  134. public int compare(File o1, File o2) {
  135. if ((o1 == o2)
  136. || (o1 == ISourceLocation.NO_FILE)
  137. || (o2 == ISourceLocation.NO_FILE) ) {
  138. return 0;
  139. }
  140. int result = compareNull(o1, o2);
  141. if (0 != result) {
  142. return result;
  143. }
  144. File one = (File) o1;
  145. File two = (File) o2;
  146. String s1 = one.getPath();
  147. String s2 = two.getPath();
  148. // check if normalize needed
  149. if (s1.endsWith(s2) || s2.endsWith(s1)) {
  150. return 0;
  151. }
  152. s1 = FileUtil.weakNormalize(s1);
  153. s2 = FileUtil.weakNormalize(s2);
  154. if (s1.endsWith(s2) || s2.endsWith(s1)) {
  155. return 0;
  156. }
  157. return String.CASE_INSENSITIVE_ORDER.compare(s1, s2);
  158. }
  159. };
  160. /**
  161. * Ordering only uses line number.
  162. * Use only for sorts, not to maintain maps.
  163. */
  164. public static final Comparator<ISourceLocation> WEAK_ISourceLocation = new Comparator<ISourceLocation>() {
  165. public int compare(ISourceLocation o1, ISourceLocation o2) {
  166. if (o1 == o2) {
  167. return 0;
  168. }
  169. int result = compareNull(o1, o2);
  170. if (0 != result) {
  171. return result;
  172. }
  173. ISourceLocation one = (ISourceLocation) o1;
  174. ISourceLocation two = (ISourceLocation) o2;
  175. int i1 = one.getLine();
  176. int i2 = two.getLine();
  177. return i1 - i2;
  178. }
  179. };
  180. /**
  181. * Like WEAK_ISourceLocation, except it also
  182. * uses WEAK_FILE on the sourceFile.
  183. * Use only for sorts, not to maintain maps.
  184. */
  185. public static final Comparator<ISourceLocation> MEDIUM_ISourceLocation = new Comparator<ISourceLocation>() {
  186. public int compare(ISourceLocation o1, ISourceLocation o2) {
  187. int result = WEAK_ISourceLocation.compare(o1, o2);
  188. if (0 != result) {
  189. return result;
  190. }
  191. ISourceLocation one = (ISourceLocation) o1;
  192. ISourceLocation two = (ISourceLocation) o2;
  193. result = compareNull(one, two);
  194. if (0 != result) { // one but not other is null
  195. return result;
  196. }
  197. if (null == one) { // both null
  198. return 0;
  199. }
  200. // neither null
  201. return WEAK_File.compare(one.getSourceFile(), two.getSourceFile());
  202. }
  203. };
  204. /**
  205. * Ordering uses kind and weak source location,
  206. * and ignores message
  207. * so use only for sorts, not to maintain maps
  208. */
  209. public static final Comparator<IMessage> WEAK_IMessage = new Comparator<IMessage>() {
  210. public int compare(IMessage o1, IMessage o2) {
  211. if (o1 == o2) {
  212. return 0;
  213. }
  214. int result = compareNull(o1, o2);
  215. if (0 != result) {
  216. return result;
  217. }
  218. IMessage one = (IMessage) o1;
  219. IMessage two = (IMessage) o2;
  220. IMessage.Kind kind1 = one.getKind();
  221. IMessage.Kind kind2= two.getKind();
  222. result = IMessage.Kind.COMPARATOR.compare(kind1, kind2);
  223. if (0 != result) {
  224. return result;
  225. }
  226. ISourceLocation sl1 = one.getSourceLocation();
  227. ISourceLocation sl2 = two.getSourceLocation();
  228. return WEAK_ISourceLocation.compare(sl1, sl2);
  229. }
  230. };
  231. /**
  232. * Ordering uses line and weak filename and message
  233. * (message matches if either is a substring of the other,
  234. * or if either is empty, i.e., none specified).
  235. * so use only for sorts, not to maintain maps
  236. */
  237. public static final Comparator<IMessage> MEDIUM_IMessage = new Comparator<IMessage>() {
  238. public int compare(IMessage o1, IMessage o2) {
  239. int result = WEAK_IMessage.compare(o1, o2);
  240. if (0 != result) {
  241. return result;
  242. }
  243. IMessage rhs_m= (IMessage) o1;
  244. IMessage lhs_m = (IMessage) o2;
  245. ISourceLocation rhs_sl = rhs_m.getSourceLocation();
  246. ISourceLocation lhs_sl = lhs_m.getSourceLocation();
  247. result = MEDIUM_ISourceLocation.compare(lhs_sl, rhs_sl);
  248. if (0 != result) {
  249. return result;
  250. }
  251. String lhs_s =lhs_m.getMessage();
  252. String rhs_s = rhs_m.getMessage();
  253. return compareStringsSoftly(lhs_s, rhs_s);
  254. }
  255. };
  256. }
  257. public static SourceLocation makeSourceLocation(String input) { // XXX only for testing, not production
  258. return makeSourceLocation(input, (File) null);
  259. }
  260. public static SourceLocation makeSourceLocation(String input, String path) {
  261. return makeSourceLocation(input, (null == path ? null : new File(path)));
  262. }
  263. /** attempt to create a source location from the input */
  264. public static SourceLocation makeSourceLocation(String input, File defaultFile) {
  265. /*
  266. * Forms interpreted:
  267. * # - line
  268. * file - file
  269. * file:# - file, line
  270. * #:# - if defaultFile is not null, then file, line, column
  271. * file:#:# - file, line, column
  272. * file:#:#:? - file, line, column, message
  273. */
  274. // SourceLocation result = null;
  275. if ((null == input) || (0 == input.length())) {
  276. if (null == defaultFile) {
  277. return null;
  278. } else {
  279. return new SourceLocation(defaultFile, 0, 0, 0);
  280. }
  281. }
  282. input = input.trim();
  283. String path = null;
  284. int line = 0;
  285. int endLine = 0;
  286. int column = 0;
  287. // String message = null;
  288. // first try line only
  289. line = convert(input);
  290. if (-1 != line) {
  291. return new SourceLocation(defaultFile, line, line, 0);
  292. }
  293. // if not a line - must be > 2 characters
  294. if (3 > input.length()) {
  295. return null; // throw new IllegalArgumentException("too short: " + input);
  296. }
  297. final String fixTag = "FIXFIX";
  298. if (input.charAt(1) == ':') { // windows drive ambiguates ":" file:line:col separator
  299. input = fixTag + input.substring(0,1) + input.substring(2);
  300. }
  301. // expecting max: path:line:column:message
  302. // if 1 colon, delimits line (to second colon or end of string)
  303. // if 2 colon, delimits column (to third colon or end of string)
  304. // if 3 colon, delimits column (to fourth colon or end of string)
  305. // todo: use this instead??
  306. final int colon1 = input.indexOf(":",2); // 2 to get past windows drives...
  307. final int colon2 = (-1 == colon1?-1:input.indexOf(":", colon1+1));
  308. final int colon3 = (-1 == colon2?-1:input.indexOf(":", colon2+1));
  309. String s;
  310. if (-1 == colon1) { // no colon; only path (number handled above)
  311. path = input;
  312. } else { // 1+ colon => file:line // XXX later or line:column
  313. path = input.substring(0, colon1);
  314. s = input.substring(colon1+1,(-1!=colon2?colon2:input.length())).trim();
  315. line = convert(s);
  316. if (-1 == line) {
  317. return null;
  318. //line = "expecting line(number) at \"" + line + "\" in " + input;
  319. //throw new IllegalArgumentException(line);
  320. } else if (-1 != colon2) { // 2+ colon => col
  321. s = input.substring(colon2+1,(-1!=colon3?colon3:input.length())).trim();
  322. column = convert(s);
  323. if (-1 == column) {
  324. return null;
  325. //col = "expecting col(number) at \"" + col + "\" in " + input;
  326. //throw new IllegalArgumentException(col);
  327. } else if (-1 != colon3) { // 3 colon => message
  328. input.substring(colon3+1); // do not trim message
  329. }
  330. }
  331. }
  332. if (path.startsWith(fixTag)) {
  333. int len = fixTag.length();
  334. path = path.substring(len, 1+len) + ":" +
  335. path.substring(1+len);
  336. }
  337. if ((endLine == 0) && (line != 0)) {
  338. endLine = line;
  339. }
  340. // XXX removed message/comment
  341. return new SourceLocation(new File(path), line, endLine, column);
  342. }
  343. // XXX reconsider convert if used in production code
  344. /**
  345. * Convert String to int using ascii and optionally
  346. * tolerating text
  347. * @param s the String to convert
  348. * @param permitText if true, pick a sequence of numbers
  349. * within a possibly non-numeric String
  350. * @param last if permitText, then if this is true the
  351. * last sequence is used - otherwise the first is used
  352. * XXX only default u.s. encodings..
  353. * @return -1 or value if a valid, totally-numeric positive string 0..MAX_WIDTH
  354. */
  355. private static int convert(String s) {
  356. return convert(s, false, false);
  357. }
  358. // XXX reconsider convert if used in production code
  359. /**
  360. * Convert String to int using ascii and optionally
  361. * tolerating text
  362. * @param s the String to convert
  363. * @param permitText if true, pick a sequence of numbers
  364. * within a possibly non-numeric String
  365. * @param last if permitText, then if this is true the
  366. * last sequence is used - otherwise the first is used
  367. * XXX only default u.s. encodings..
  368. * @return -1 or value if a valid, positive string 0..MAX_WIDTH
  369. */
  370. private static int convert(String s, boolean permitText,
  371. boolean first) {
  372. int result = -1;
  373. int last = -1;
  374. int max = s.length();
  375. boolean reading = false;
  376. for (int i = 0; i < max; i++) {
  377. char c = s.charAt(i);
  378. if ((c >= '0') && (c <= '9')) {
  379. if (-1 == result) { // prefix loop
  380. result = 0;
  381. reading = true;
  382. }
  383. result = ((result * 10) + (c - '0'));
  384. } else if (!permitText) {
  385. return -1;
  386. } else if (reading) { // from numeric -> non-numeric
  387. if (first) {
  388. return result;
  389. } else {
  390. last = result;
  391. }
  392. reading = false;
  393. }
  394. }
  395. if (permitText && !first && (-1 != last) && (-1 == result)) {
  396. result = last;
  397. }
  398. return ((0 < result) && (result < ISourceLocation.MAX_LINE) ? result : -1);
  399. }
  400. private BridgeUtil() {}
  401. /** @return String for status header, counting children passed/failed */
  402. public static String childString(IRunStatus runStatus, int numSkips, int numIncomplete) {
  403. if (null == runStatus) {
  404. return "((RunStatus) null)";
  405. }
  406. if (0 > numSkips) {
  407. numSkips = 0;
  408. }
  409. if (0 > numIncomplete) {
  410. numIncomplete = 0;
  411. }
  412. StringBuffer sb = new StringBuffer();
  413. if (RunValidator.NORMAL.runPassed(runStatus)) {
  414. sb.append("PASS ");
  415. } else {
  416. sb.append("FAIL ");
  417. }
  418. Object id = runStatus.getIdentifier();
  419. if (null != id) {
  420. sb.append(id.toString() + " ");
  421. }
  422. IRunStatus[] children = runStatus.getChildren();
  423. final int numChildren = (null == children ? 0 : children.length);
  424. final int numTests = numIncomplete + numChildren + numSkips;
  425. int numFails = 0;
  426. if (!LangUtil.isEmpty(children)) {
  427. for (IRunStatus child : children) {
  428. if (!RunValidator.NORMAL.runPassed(child)) {
  429. numFails++;
  430. }
  431. }
  432. }
  433. final int numPass = children.length - numFails;
  434. sb.append(numTests + " tests");
  435. if (0 < numTests) {
  436. sb.append(" (");
  437. }
  438. if (0 < numSkips) {
  439. sb.append(numSkips + " skipped");
  440. if (0 < (numFails + numPass + numIncomplete)) {
  441. sb.append(", ");
  442. }
  443. }
  444. if (0 < numIncomplete) {
  445. sb.append(numIncomplete + " incomplete");
  446. if (0 < (numFails + numPass)) {
  447. sb.append(", ");
  448. }
  449. }
  450. if (0 < numFails) {
  451. sb.append(numFails + " failed");
  452. if (0 < numPass) {
  453. sb.append(", ");
  454. }
  455. }
  456. if (0 < numPass) {
  457. sb.append(numPass + " passed)");
  458. } else if (0 < numTests) {
  459. sb.append(")");
  460. }
  461. return sb.toString().trim();
  462. }
  463. /** @return String for status header */
  464. public static String toShortString(IRunStatus runStatus) {
  465. if (null == runStatus) {
  466. return "((RunStatus) null)";
  467. }
  468. StringBuffer sb = new StringBuffer();
  469. if (RunValidator.NORMAL.runPassed(runStatus)) {
  470. sb.append("PASS ");
  471. } else {
  472. sb.append("FAIL ");
  473. }
  474. Object id = runStatus.getIdentifier();
  475. if (null != id) {
  476. sb.append(id.toString() + " ");
  477. }
  478. sb.append(MessageUtil.renderCounts(runStatus));
  479. return sb.toString().trim();
  480. }
  481. }