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.

LangUtil.java 43KB

21 years ago
21 years ago
21 years ago
16 years ago
21 years ago
21 years ago
13 years ago
13 years ago
13 years ago
13 years ago
15 years ago
15 years ago
13 years ago
21 years ago
21 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
21 years ago
13 years ago
13 years ago
13 years ago
21 years ago
13 years ago
15 years ago
21 years ago
21 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
21 years ago
13 years ago
13 years ago
13 years ago
21 years ago
21 years ago
13 years ago
15 years ago
13 years ago
15 years ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436
  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.util;
  14. import java.io.ByteArrayOutputStream;
  15. import java.io.File;
  16. import java.io.IOException;
  17. import java.io.PrintWriter;
  18. import java.io.StringWriter;
  19. import java.lang.reflect.Array;
  20. import java.lang.reflect.InvocationTargetException;
  21. import java.security.PrivilegedActionException;
  22. import java.sql.SQLException;
  23. import java.util.ArrayList;
  24. import java.util.Arrays;
  25. import java.util.BitSet;
  26. import java.util.Collection;
  27. import java.util.Collections;
  28. import java.util.LinkedList;
  29. import java.util.List;
  30. import java.util.StringTokenizer;
  31. /**
  32. *
  33. */
  34. public class LangUtil {
  35. public static final String EOL;
  36. private static double vmVersion;
  37. static {
  38. StringWriter buf = new StringWriter();
  39. PrintWriter writer = new PrintWriter(buf);
  40. writer.println("");
  41. String eol = "\n";
  42. try {
  43. buf.close();
  44. StringBuffer sb = buf.getBuffer();
  45. if (sb != null) {
  46. eol = buf.toString();
  47. }
  48. } catch (Throwable t) {
  49. }
  50. EOL = eol;
  51. }
  52. static {
  53. try {
  54. String vm = System.getProperty("java.version"); // JLS 20.18.7
  55. if (vm == null) {
  56. vm = System.getProperty("java.runtime.version");
  57. }
  58. if (vm == null) {
  59. vm = System.getProperty("java.vm.version");
  60. }
  61. if (vm == null) {
  62. new RuntimeException(
  63. "System properties appear damaged, cannot find: java.version/java.runtime.version/java.vm.version")
  64. .printStackTrace(System.err);
  65. vmVersion = 1.5;
  66. } else {
  67. try {
  68. String versionString = vm.substring(0, 3);
  69. Double temp = new Double(Double.parseDouble(versionString));
  70. vmVersion = temp.floatValue();
  71. } catch (Exception e) {
  72. vmVersion = 1.4;
  73. }
  74. }
  75. } catch (Throwable t) {
  76. new RuntimeException(
  77. "System properties appear damaged, cannot find: java.version/java.runtime.version/java.vm.version", t)
  78. .printStackTrace(System.err);
  79. vmVersion = 1.5;
  80. }
  81. }
  82. public static boolean is13VMOrGreater() {
  83. return 1.3 <= vmVersion;
  84. }
  85. public static boolean is14VMOrGreater() {
  86. return 1.4 <= vmVersion;
  87. }
  88. public static boolean is15VMOrGreater() {
  89. return 1.5 <= vmVersion;
  90. }
  91. public static boolean is16VMOrGreater() {
  92. return 1.6 <= vmVersion;
  93. }
  94. public static boolean is17VMOrGreater() {
  95. return 1.7 <= vmVersion;
  96. }
  97. /**
  98. * Shorthand for "if null, throw IllegalArgumentException"
  99. *
  100. * @throws IllegalArgumentException "null {name}" if o is null
  101. */
  102. public static final void throwIaxIfNull(final Object o, final String name) {
  103. if (null == o) {
  104. String message = "null " + (null == name ? "input" : name);
  105. throw new IllegalArgumentException(message);
  106. }
  107. }
  108. /**
  109. * Shorthand for "if not null or not assignable, throw IllegalArgumentException"
  110. *
  111. * @param c the Class to check - use null to ignore type check
  112. * @throws IllegalArgumentException "null {name}" if o is null
  113. */
  114. public static final void throwIaxIfNotAssignable(final Object ra[], final Class<?> c, final String name) {
  115. throwIaxIfNull(ra, name);
  116. String label = (null == name ? "input" : name);
  117. for (int i = 0; i < ra.length; i++) {
  118. if (null == ra[i]) {
  119. String m = " null " + label + "[" + i + "]";
  120. throw new IllegalArgumentException(m);
  121. } else if (null != c) {
  122. Class<?> actualClass = ra[i].getClass();
  123. if (!c.isAssignableFrom(actualClass)) {
  124. String message = label + " not assignable to " + c.getName();
  125. throw new IllegalArgumentException(message);
  126. }
  127. }
  128. }
  129. }
  130. /**
  131. * Shorthand for "if not null or not assignable, throw IllegalArgumentException"
  132. *
  133. * @throws IllegalArgumentException "null {name}" if o is null
  134. */
  135. public static final void throwIaxIfNotAssignable(final Object o, final Class<?> c, final String name) {
  136. throwIaxIfNull(o, name);
  137. if (null != c) {
  138. Class<?> actualClass = o.getClass();
  139. if (!c.isAssignableFrom(actualClass)) {
  140. String message = name + " not assignable to " + c.getName();
  141. throw new IllegalArgumentException(message);
  142. }
  143. }
  144. }
  145. // /**
  146. // * Shorthand for
  147. // "if any not null or not assignable, throw IllegalArgumentException"
  148. // * @throws IllegalArgumentException "{name} is not assignable to {c}"
  149. // */
  150. // public static final void throwIaxIfNotAllAssignable(final Collection
  151. // collection,
  152. // final Class c, final String name) {
  153. // throwIaxIfNull(collection, name);
  154. // if (null != c) {
  155. // for (Iterator iter = collection.iterator(); iter.hasNext();) {
  156. // throwIaxIfNotAssignable(iter.next(), c, name);
  157. //
  158. // }
  159. // }
  160. // }
  161. /**
  162. * Shorthand for "if false, throw IllegalArgumentException"
  163. *
  164. * @throws IllegalArgumentException "{message}" if test is false
  165. */
  166. public static final void throwIaxIfFalse(final boolean test, final String message) {
  167. if (!test) {
  168. throw new IllegalArgumentException(message);
  169. }
  170. }
  171. // /** @return ((null == s) || (0 == s.trim().length())); */
  172. // public static boolean isEmptyTrimmed(String s) {
  173. // return ((null == s) || (0 == s.length())
  174. // || (0 == s.trim().length()));
  175. // }
  176. /** @return ((null == s) || (0 == s.length())); */
  177. public static boolean isEmpty(String s) {
  178. return ((null == s) || (0 == s.length()));
  179. }
  180. /** @return ((null == ra) || (0 == ra.length)) */
  181. public static boolean isEmpty(Object[] ra) {
  182. return ((null == ra) || (0 == ra.length));
  183. }
  184. /** @return ((null == collection) || (0 == collection.size())) */
  185. public static boolean isEmpty(Collection<?> collection) {
  186. return ((null == collection) || (0 == collection.size()));
  187. }
  188. /**
  189. * Splits <code>text</code> at whitespace.
  190. *
  191. * @param text <code>String</code> to split.
  192. */
  193. public static String[] split(String text) {
  194. return (String[]) strings(text).toArray(new String[0]);
  195. }
  196. /**
  197. * Splits <code>input</code> at commas, trimming any white space.
  198. *
  199. * @param input <code>String</code> to split.
  200. * @return List of String of elements.
  201. */
  202. public static List<String> commaSplit(String input) {
  203. return anySplit(input, ",");
  204. }
  205. /**
  206. * Split string as classpath, delimited at File.pathSeparator. Entries are not trimmed, but empty entries are ignored.
  207. *
  208. * @param classpath the String to split - may be null or empty
  209. * @return String[] of classpath entries
  210. */
  211. public static String[] splitClasspath(String classpath) {
  212. if (LangUtil.isEmpty(classpath)) {
  213. return new String[0];
  214. }
  215. StringTokenizer st = new StringTokenizer(classpath, File.pathSeparator);
  216. ArrayList<String> result = new ArrayList<String>(st.countTokens());
  217. while (st.hasMoreTokens()) {
  218. String entry = st.nextToken();
  219. if (!LangUtil.isEmpty(entry)) {
  220. result.add(entry);
  221. }
  222. }
  223. return (String[]) result.toArray(new String[0]);
  224. }
  225. /**
  226. * Get System property as boolean, but use default value where the system property is not set.
  227. *
  228. * @return true if value is set to true, false otherwise
  229. */
  230. public static boolean getBoolean(String propertyName, boolean defaultValue) {
  231. if (null != propertyName) {
  232. try {
  233. String value = System.getProperty(propertyName);
  234. if (null != value) {
  235. return Boolean.valueOf(value).booleanValue();
  236. }
  237. } catch (Throwable t) {
  238. // default below
  239. }
  240. }
  241. return defaultValue;
  242. }
  243. /**
  244. * Splits <code>input</code>, removing delimiter and trimming any white space. Returns an empty collection if the input is null.
  245. * If delimiter is null or empty or if the input contains no delimiters, the input itself is returned after trimming white
  246. * space.
  247. *
  248. * @param input <code>String</code> to split.
  249. * @param delim <code>String</code> separators for input.
  250. * @return List of String of elements.
  251. */
  252. public static List<String> anySplit(String input, String delim) {
  253. if (null == input) {
  254. return Collections.emptyList();
  255. }
  256. ArrayList<String> result = new ArrayList<String>();
  257. if (LangUtil.isEmpty(delim) || (-1 == input.indexOf(delim))) {
  258. result.add(input.trim());
  259. } else {
  260. StringTokenizer st = new StringTokenizer(input, delim);
  261. while (st.hasMoreTokens()) {
  262. result.add(st.nextToken().trim());
  263. }
  264. }
  265. return result;
  266. }
  267. /**
  268. * Splits strings into a <code>List</code> using a <code>StringTokenizer</code>.
  269. *
  270. * @param text <code>String</code> to split.
  271. */
  272. public static List<String> strings(String text) {
  273. if (LangUtil.isEmpty(text)) {
  274. return Collections.emptyList();
  275. }
  276. List<String> strings = new ArrayList<String>();
  277. StringTokenizer tok = new StringTokenizer(text);
  278. while (tok.hasMoreTokens()) {
  279. strings.add(tok.nextToken());
  280. }
  281. return strings;
  282. }
  283. /** @return a non-null unmodifiable List */
  284. public static <T> List<T> safeList(List<T> list) {
  285. return (null == list ? Collections.<T>emptyList() : Collections.unmodifiableList(list));
  286. }
  287. // /**
  288. // * Select from input String[] based on suffix-matching
  289. // * @param inputs String[] of input - null ignored
  290. // * @param suffixes String[] of suffix selectors - null ignored
  291. // * @param ignoreCase if true, ignore case
  292. // * @return String[] of input that end with any input
  293. // */
  294. // public static String[] endsWith(String[] inputs, String[] suffixes,
  295. // boolean ignoreCase) {
  296. // if (LangUtil.isEmpty(inputs) || LangUtil.isEmpty(suffixes)) {
  297. // return new String[0];
  298. // }
  299. // if (ignoreCase) {
  300. // String[] temp = new String[suffixes.length];
  301. // for (int i = 0; i < temp.length; i++) {
  302. // String suff = suffixes[i];
  303. // temp[i] = (null == suff ? null : suff.toLowerCase());
  304. // }
  305. // suffixes = temp;
  306. // }
  307. // ArrayList result = new ArrayList();
  308. // for (int i = 0; i < inputs.length; i++) {
  309. // String input = inputs[i];
  310. // if (null == input) {
  311. // continue;
  312. // }
  313. // if (!ignoreCase) {
  314. // input = input.toLowerCase();
  315. // }
  316. // for (int j = 0; j < suffixes.length; j++) {
  317. // String suffix = suffixes[j];
  318. // if (null == suffix) {
  319. // continue;
  320. // }
  321. // if (input.endsWith(suffix)) {
  322. // result.add(input);
  323. // break;
  324. // }
  325. // }
  326. // }
  327. // return (String[]) result.toArray(new String[0]);
  328. // }
  329. //
  330. // /**
  331. // * Select from input String[] if readable directories
  332. // * @param inputs String[] of input - null ignored
  333. // * @param baseDir the base directory of the input
  334. // * @return String[] of input that end with any input
  335. // */
  336. // public static String[] selectDirectories(String[] inputs, File baseDir) {
  337. // if (LangUtil.isEmpty(inputs)) {
  338. // return new String[0];
  339. // }
  340. // ArrayList result = new ArrayList();
  341. // for (int i = 0; i < inputs.length; i++) {
  342. // String input = inputs[i];
  343. // if (null == input) {
  344. // continue;
  345. // }
  346. // File inputFile = new File(baseDir, input);
  347. // if (inputFile.canRead() && inputFile.isDirectory()) {
  348. // result.add(input);
  349. // }
  350. // }
  351. // return (String[]) result.toArray(new String[0]);
  352. // }
  353. /**
  354. * copy non-null two-dimensional String[][]
  355. *
  356. * @see extractOptions(String[], String[][])
  357. */
  358. public static String[][] copyStrings(String[][] in) {
  359. String[][] out = new String[in.length][];
  360. for (int i = 0; i < out.length; i++) {
  361. out[i] = new String[in[i].length];
  362. System.arraycopy(in[i], 0, out[i], 0, out[i].length);
  363. }
  364. return out;
  365. }
  366. /**
  367. * Extract options and arguments to input option list, returning remainder. The input options will be nullified if not found.
  368. * e.g.,
  369. *
  370. * <pre>
  371. * String[] options = new String[][] { new String[] { &quot;-verbose&quot; }, new String[] { &quot;-classpath&quot;, null } };
  372. * String[] args = extractOptions(args, options);
  373. * boolean verbose = null != options[0][0];
  374. * boolean classpath = options[1][1];
  375. * </pre>
  376. *
  377. * @param args the String[] input options
  378. * @param options the String[][]options to find in the input args - not null for each String[] component the first subcomponent
  379. * is the option itself, and there is one String subcomponent for each additional argument.
  380. * @return String[] of args remaining after extracting options to extracted
  381. */
  382. public static String[] extractOptions(String[] args, String[][] options) {
  383. if (LangUtil.isEmpty(args) || LangUtil.isEmpty(options)) {
  384. return args;
  385. }
  386. BitSet foundSet = new BitSet();
  387. String[] result = new String[args.length];
  388. int resultIndex = 0;
  389. for (int j = 0; j < args.length; j++) {
  390. boolean found = false;
  391. for (int i = 0; !found && (i < options.length); i++) {
  392. String[] option = options[i];
  393. LangUtil.throwIaxIfFalse(!LangUtil.isEmpty(option), "options");
  394. String sought = option[0];
  395. found = sought.equals(args[j]);
  396. if (found) {
  397. foundSet.set(i);
  398. int doMore = option.length - 1;
  399. if (0 < doMore) {
  400. final int MAX = j + doMore;
  401. if (MAX >= args.length) {
  402. String s = "expecting " + doMore + " args after ";
  403. throw new IllegalArgumentException(s + args[j]);
  404. }
  405. for (int k = 1; k < option.length; k++) {
  406. option[k] = args[++j];
  407. }
  408. }
  409. }
  410. }
  411. if (!found) {
  412. result[resultIndex++] = args[j];
  413. }
  414. }
  415. // unset any not found
  416. for (int i = 0; i < options.length; i++) {
  417. if (!foundSet.get(i)) {
  418. options[i][0] = null;
  419. }
  420. }
  421. // fixup remainder
  422. if (resultIndex < args.length) {
  423. String[] temp = new String[resultIndex];
  424. System.arraycopy(result, 0, temp, 0, resultIndex);
  425. args = temp;
  426. }
  427. return args;
  428. }
  429. //
  430. // /**
  431. // * Extract options and arguments to input parameter list, returning
  432. // remainder.
  433. // * @param args the String[] input options
  434. // * @param validOptions the String[] options to find in the input args -
  435. // not null
  436. // * @param optionArgs the int[] number of arguments for each option in
  437. // validOptions
  438. // * (if null, then no arguments for any option)
  439. // * @param extracted the List for the matched options
  440. // * @return String[] of args remaining after extracting options to
  441. // extracted
  442. // */
  443. // public static String[] extractOptions(String[] args, String[]
  444. // validOptions,
  445. // int[] optionArgs, List extracted) {
  446. // if (LangUtil.isEmpty(args)
  447. // || LangUtil.isEmpty(validOptions) ) {
  448. // return args;
  449. // }
  450. // if (null != optionArgs) {
  451. // if (optionArgs.length != validOptions.length) {
  452. // throw new IllegalArgumentException("args must match options");
  453. // }
  454. // }
  455. // String[] result = new String[args.length];
  456. // int resultIndex = 0;
  457. // for (int j = 0; j < args.length; j++) {
  458. // boolean found = false;
  459. // for (int i = 0; !found && (i < validOptions.length); i++) {
  460. // String sought = validOptions[i];
  461. // int doMore = (null == optionArgs ? 0 : optionArgs[i]);
  462. // if (LangUtil.isEmpty(sought)) {
  463. // continue;
  464. // }
  465. // found = sought.equals(args[j]);
  466. // if (found) {
  467. // if (null != extracted) {
  468. // extracted.add(sought);
  469. // }
  470. // if (0 < doMore) {
  471. // final int MAX = j + doMore;
  472. // if (MAX >= args.length) {
  473. // String s = "expecting " + doMore + " args after ";
  474. // throw new IllegalArgumentException(s + args[j]);
  475. // }
  476. // if (null != extracted) {
  477. // while (j < MAX) {
  478. // extracted.add(args[++j]);
  479. // }
  480. // } else {
  481. // j = MAX;
  482. // }
  483. // }
  484. // break;
  485. // }
  486. // }
  487. // if (!found) {
  488. // result[resultIndex++] = args[j];
  489. // }
  490. // }
  491. // if (resultIndex < args.length) {
  492. // String[] temp = new String[resultIndex];
  493. // System.arraycopy(result, 0, temp, 0, resultIndex);
  494. // args = temp;
  495. // }
  496. // return args;
  497. // }
  498. // /** @return String[] of entries in validOptions found in args */
  499. // public static String[] selectOptions(String[] args, String[]
  500. // validOptions) {
  501. // if (LangUtil.isEmpty(args) || LangUtil.isEmpty(validOptions)) {
  502. // return new String[0];
  503. // }
  504. // ArrayList result = new ArrayList();
  505. // for (int i = 0; i < validOptions.length; i++) {
  506. // String sought = validOptions[i];
  507. // if (LangUtil.isEmpty(sought)) {
  508. // continue;
  509. // }
  510. // for (int j = 0; j < args.length; j++) {
  511. // if (sought.equals(args[j])) {
  512. // result.add(sought);
  513. // break;
  514. // }
  515. // }
  516. // }
  517. // return (String[]) result.toArray(new String[0]);
  518. // }
  519. // /** @return String[] of entries in validOptions found in args */
  520. // public static String[] selectOptions(List args, String[] validOptions) {
  521. // if (LangUtil.isEmpty(args) || LangUtil.isEmpty(validOptions)) {
  522. // return new String[0];
  523. // }
  524. // ArrayList result = new ArrayList();
  525. // for (int i = 0; i < validOptions.length; i++) {
  526. // String sought = validOptions[i];
  527. // if (LangUtil.isEmpty(sought)) {
  528. // continue;
  529. // }
  530. // for (Iterator iter = args.iterator(); iter.hasNext();) {
  531. // String arg = (String) iter.next();
  532. // if (sought.equals(arg)) {
  533. // result.add(sought);
  534. // break;
  535. // }
  536. // }
  537. // }
  538. // return (String[]) result.toArray(new String[0]);
  539. // }
  540. // /**
  541. // * Generate variants of String[] options by creating an extra set for
  542. // * each option that ends with "-". If none end with "-", then an
  543. // * array equal to <code>new String[][] { options }</code> is returned;
  544. // * if one ends with "-", then two sets are returned,
  545. // * three causes eight sets, etc.
  546. // * @return String[][] with each option set.
  547. // * @throws IllegalArgumentException if any option is null or empty.
  548. // */
  549. // public static String[][] optionVariants(String[] options) {
  550. // if ((null == options) || (0 == options.length)) {
  551. // return new String[][] { new String[0]};
  552. // }
  553. // // be nice, don't stomp input
  554. // String[] temp = new String[options.length];
  555. // System.arraycopy(options, 0, temp, 0, temp.length);
  556. // options = temp;
  557. // boolean[] dup = new boolean[options.length];
  558. // int numDups = 0;
  559. //
  560. // for (int i = 0; i < options.length; i++) {
  561. // String option = options[i];
  562. // if (LangUtil.isEmpty(option)) {
  563. // throw new IllegalArgumentException("empty option at " + i);
  564. // }
  565. // if (option.endsWith("-")) {
  566. // options[i] = option.substring(0, option.length()-1);
  567. // dup[i] = true;
  568. // numDups++;
  569. // }
  570. // }
  571. // final String[] NONE = new String[0];
  572. // final int variants = exp(2, numDups);
  573. // final String[][] result = new String[variants][];
  574. // // variant is a bitmap wrt doing extra value when dup[k]=true
  575. // for (int variant = 0; variant < variants; variant++) {
  576. // ArrayList next = new ArrayList();
  577. // int nextOption = 0;
  578. // for (int k = 0; k < options.length; k++) {
  579. // if (!dup[k] || (0 != (variant & (1 << (nextOption++))))) {
  580. // next.add(options[k]);
  581. // }
  582. // }
  583. // result[variant] = (String[]) next.toArray(NONE);
  584. // }
  585. // return result;
  586. // }
  587. //
  588. // private static int exp(int base, int power) { // not in Math?
  589. // if (0 > power) {
  590. // throw new IllegalArgumentException("negative power: " + power);
  591. // }
  592. // int result = 1;
  593. // while (0 < power--) {
  594. // result *= base;
  595. // }
  596. // return result;
  597. // }
  598. // /**
  599. // * Make a copy of the array.
  600. // * @return an array with the same component type as source
  601. // * containing same elements, even if null.
  602. // * @throws IllegalArgumentException if source is null
  603. // */
  604. // public static final Object[] copy(Object[] source) {
  605. // LangUtil.throwIaxIfNull(source, "source");
  606. // final Class c = source.getClass().getComponentType();
  607. // Object[] result = (Object[]) Array.newInstance(c, source.length);
  608. // System.arraycopy(source, 0, result, 0, result.length);
  609. // return result;
  610. // }
  611. /**
  612. * Convert arrays safely. The number of elements in the result will be 1 smaller for each element that is null or not
  613. * assignable. This will use sink if it has exactly the right size. The result will always have the same component type as sink.
  614. *
  615. * @return an array with the same component type as sink containing any assignable elements in source (in the same order).
  616. * @throws IllegalArgumentException if either is null
  617. */
  618. public static Object[] safeCopy(Object[] source, Object[] sink) {
  619. final Class<?> sinkType = (null == sink ? Object.class : sink.getClass().getComponentType());
  620. final int sourceLength = (null == source ? 0 : source.length);
  621. final int sinkLength = (null == sink ? 0 : sink.length);
  622. final int resultSize;
  623. ArrayList<Object> result = null;
  624. if (0 == sourceLength) {
  625. resultSize = 0;
  626. } else {
  627. result = new ArrayList<Object>(sourceLength);
  628. for (int i = 0; i < sourceLength; i++) {
  629. if ((null != source[i]) && (sinkType.isAssignableFrom(source[i].getClass()))) {
  630. result.add(source[i]);
  631. }
  632. }
  633. resultSize = result.size();
  634. }
  635. if (resultSize != sinkLength) {
  636. sink = (Object[]) Array.newInstance(sinkType, result.size());
  637. }
  638. if (0 < resultSize) {
  639. sink = result.toArray(sink);
  640. }
  641. return sink;
  642. }
  643. /**
  644. * @return a String with the unqualified class name of the class (or "null")
  645. */
  646. public static String unqualifiedClassName(Class<?> c) {
  647. if (null == c) {
  648. return "null";
  649. }
  650. String name = c.getName();
  651. int loc = name.lastIndexOf(".");
  652. if (-1 != loc) {
  653. name = name.substring(1 + loc);
  654. }
  655. return name;
  656. }
  657. /**
  658. * @return a String with the unqualified class name of the object (or "null")
  659. */
  660. public static String unqualifiedClassName(Object o) {
  661. return LangUtil.unqualifiedClassName(null == o ? null : o.getClass());
  662. }
  663. /** inefficient way to replace all instances of sought with replace */
  664. public static String replace(String in, String sought, String replace) {
  665. if (LangUtil.isEmpty(in) || LangUtil.isEmpty(sought)) {
  666. return in;
  667. }
  668. StringBuffer result = new StringBuffer();
  669. final int len = sought.length();
  670. int start = 0;
  671. int loc;
  672. while (-1 != (loc = in.indexOf(sought, start))) {
  673. result.append(in.substring(start, loc));
  674. if (!LangUtil.isEmpty(replace)) {
  675. result.append(replace);
  676. }
  677. start = loc + len;
  678. }
  679. result.append(in.substring(start));
  680. return result.toString();
  681. }
  682. /** render i right-justified with a given width less than about 40 */
  683. public static String toSizedString(long i, int width) {
  684. String result = "" + i;
  685. int size = result.length();
  686. if (width > size) {
  687. final String pad = " ";
  688. final int padLength = pad.length();
  689. if (width > padLength) {
  690. width = padLength;
  691. }
  692. int topad = width - size;
  693. result = pad.substring(0, topad) + result;
  694. }
  695. return result;
  696. }
  697. // /** clip StringBuffer to maximum number of lines */
  698. // static String clipBuffer(StringBuffer buffer, int maxLines) {
  699. // if ((null == buffer) || (1 > buffer.length())) return "";
  700. // StringBuffer result = new StringBuffer();
  701. // int j = 0;
  702. // final int MAX = maxLines;
  703. // final int N = buffer.length();
  704. // for (int i = 0, srcBegin = 0; i < MAX; srcBegin += j) {
  705. // // todo: replace with String variant if/since getting char?
  706. // char[] chars = new char[128];
  707. // int srcEnd = srcBegin+chars.length;
  708. // if (srcEnd >= N) {
  709. // srcEnd = N-1;
  710. // }
  711. // if (srcBegin == srcEnd) break;
  712. // //log("srcBegin:" + srcBegin + ":srcEnd:" + srcEnd);
  713. // buffer.getChars(srcBegin, srcEnd, chars, 0);
  714. // for (j = 0; j < srcEnd-srcBegin/*chars.length*/; j++) {
  715. // char c = chars[j];
  716. // if (c == '\n') {
  717. // i++;
  718. // j++;
  719. // break;
  720. // }
  721. // }
  722. // try { result.append(chars, 0, j); }
  723. // catch (Throwable t) { }
  724. // }
  725. // return result.toString();
  726. // }
  727. /**
  728. * @return "({UnqualifiedExceptionClass}) {message}"
  729. */
  730. public static String renderExceptionShort(Throwable e) {
  731. if (null == e) {
  732. return "(Throwable) null";
  733. }
  734. return "(" + LangUtil.unqualifiedClassName(e) + ") " + e.getMessage();
  735. }
  736. /**
  737. * Renders exception <code>t</code> after unwrapping and eliding any test packages.
  738. *
  739. * @param t <code>Throwable</code> to print.
  740. * @see #maxStackTrace
  741. */
  742. public static String renderException(Throwable t) {
  743. return renderException(t, true);
  744. }
  745. /**
  746. * Renders exception <code>t</code>, unwrapping, optionally eliding and limiting total number of lines.
  747. *
  748. * @param t <code>Throwable</code> to print.
  749. * @param elide true to limit to 100 lines and elide test packages
  750. * @see StringChecker#TEST_PACKAGES
  751. */
  752. public static String renderException(Throwable t, boolean elide) {
  753. if (null == t) {
  754. return "null throwable";
  755. }
  756. t = unwrapException(t);
  757. StringBuffer stack = stackToString(t, false);
  758. if (elide) {
  759. elideEndingLines(StringChecker.TEST_PACKAGES, stack, 100);
  760. }
  761. return stack.toString();
  762. }
  763. /**
  764. * Trim ending lines from a StringBuffer, clipping to maxLines and further removing any number of trailing lines accepted by
  765. * checker.
  766. *
  767. * @param checker returns true if trailing line should be elided.
  768. * @param stack StringBuffer with lines to elide
  769. * @param maxLines int for maximum number of resulting lines
  770. */
  771. static void elideEndingLines(StringChecker checker, StringBuffer stack, int maxLines) {
  772. if (null == checker || (null == stack) || (0 == stack.length())) {
  773. return;
  774. }
  775. final LinkedList<String> lines = new LinkedList<String>();
  776. StringTokenizer st = new StringTokenizer(stack.toString(), "\n\r");
  777. while (st.hasMoreTokens() && (0 < --maxLines)) {
  778. lines.add(st.nextToken());
  779. }
  780. st = null;
  781. String line;
  782. int elided = 0;
  783. while (!lines.isEmpty()) {
  784. line = (String) lines.getLast();
  785. if (!checker.acceptString(line)) {
  786. break;
  787. } else {
  788. elided++;
  789. lines.removeLast();
  790. }
  791. }
  792. if ((elided > 0) || (maxLines < 1)) {
  793. final int EOL_LEN = EOL.length();
  794. int totalLength = 0;
  795. while (!lines.isEmpty()) {
  796. totalLength += EOL_LEN + ((String) lines.getFirst()).length();
  797. lines.removeFirst();
  798. }
  799. if (stack.length() > totalLength) {
  800. stack.setLength(totalLength);
  801. if (elided > 0) {
  802. stack.append(" (... " + elided + " lines...)");
  803. }
  804. }
  805. }
  806. }
  807. /** Dump message and stack to StringBuffer. */
  808. public static StringBuffer stackToString(Throwable throwable, boolean skipMessage) {
  809. if (null == throwable) {
  810. return new StringBuffer();
  811. }
  812. StringWriter buf = new StringWriter();
  813. PrintWriter writer = new PrintWriter(buf);
  814. if (!skipMessage) {
  815. writer.println(throwable.getMessage());
  816. }
  817. throwable.printStackTrace(writer);
  818. try {
  819. buf.close();
  820. } catch (IOException ioe) {
  821. } // ignored
  822. return buf.getBuffer();
  823. }
  824. /** @return Throwable input or tail of any wrapped exception chain */
  825. public static Throwable unwrapException(Throwable t) {
  826. Throwable current = t;
  827. Throwable next = null;
  828. while (current != null) {
  829. // Java 1.2 exceptions that carry exceptions
  830. if (current instanceof InvocationTargetException) {
  831. next = ((InvocationTargetException) current).getTargetException();
  832. } else if (current instanceof ClassNotFoundException) {
  833. next = ((ClassNotFoundException) current).getException();
  834. } else if (current instanceof ExceptionInInitializerError) {
  835. next = ((ExceptionInInitializerError) current).getException();
  836. } else if (current instanceof PrivilegedActionException) {
  837. next = ((PrivilegedActionException) current).getException();
  838. } else if (current instanceof SQLException) {
  839. next = ((SQLException) current).getNextException();
  840. }
  841. // ...getException():
  842. // javax.naming.event.NamingExceptionEvent
  843. // javax.naming.ldap.UnsolicitedNotification
  844. // javax.xml.parsers.FactoryConfigurationError
  845. // javax.xml.transform.TransformerFactoryConfigurationError
  846. // javax.xml.transform.TransformerException
  847. // org.xml.sax.SAXException
  848. // 1.4: Throwable.getCause
  849. // java.util.logging.LogRecord.getThrown()
  850. if (null == next) {
  851. break;
  852. } else {
  853. current = next;
  854. next = null;
  855. }
  856. }
  857. return current;
  858. }
  859. /**
  860. * Replacement for Arrays.asList(..) which gacks on null and returns a List in which remove is an unsupported operation.
  861. *
  862. * @param array the Object[] to convert (may be null)
  863. * @return the List corresponding to array (never null)
  864. */
  865. public static List<Object> arrayAsList(Object[] array) {
  866. if ((null == array) || (1 > array.length)) {
  867. return Collections.emptyList();
  868. }
  869. ArrayList<Object> list = new ArrayList<Object>();
  870. list.addAll(Arrays.asList(array));
  871. return list;
  872. }
  873. /** check if input contains any packages to elide. */
  874. public static class StringChecker {
  875. static StringChecker TEST_PACKAGES = new StringChecker(new String[] { "org.aspectj.testing",
  876. "org.eclipse.jdt.internal.junit", "junit.framework.",
  877. "org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" });
  878. String[] infixes;
  879. /** @param infixes adopted */
  880. StringChecker(String[] infixes) {
  881. this.infixes = infixes;
  882. }
  883. /** @return true if input contains infixes */
  884. public boolean acceptString(String input) {
  885. boolean result = false;
  886. if (!LangUtil.isEmpty(input)) {
  887. for (int i = 0; !result && (i < infixes.length); i++) {
  888. result = (-1 != input.indexOf(infixes[i]));
  889. }
  890. }
  891. return result;
  892. }
  893. }
  894. /**
  895. * Gen classpath.
  896. *
  897. * @param bootclasspath
  898. * @param classpath
  899. * @param classesDir
  900. * @param outputJar
  901. * @return String combining classpath elements
  902. */
  903. public static String makeClasspath( // XXX dumb implementation
  904. String bootclasspath, String classpath, String classesDir, String outputJar) {
  905. StringBuffer sb = new StringBuffer();
  906. addIfNotEmpty(bootclasspath, sb, File.pathSeparator);
  907. addIfNotEmpty(classpath, sb, File.pathSeparator);
  908. if (!addIfNotEmpty(classesDir, sb, File.pathSeparator)) {
  909. addIfNotEmpty(outputJar, sb, File.pathSeparator);
  910. }
  911. return sb.toString();
  912. }
  913. /**
  914. * @param input ignored if null
  915. * @param sink the StringBuffer to add input to - return false if null
  916. * @param delimiter the String to append to input when added - ignored if empty
  917. * @return true if input + delimiter added to sink
  918. */
  919. private static boolean addIfNotEmpty(String input, StringBuffer sink, String delimiter) {
  920. if (LangUtil.isEmpty(input) || (null == sink)) {
  921. return false;
  922. }
  923. sink.append(input);
  924. if (!LangUtil.isEmpty(delimiter)) {
  925. sink.append(delimiter);
  926. }
  927. return true;
  928. }
  929. /**
  930. * Create or initialize a process controller to run a process in another VM asynchronously.
  931. *
  932. * @param controller the ProcessController to initialize, if not null
  933. * @param classpath
  934. * @param mainClass
  935. * @param args
  936. * @return initialized ProcessController
  937. */
  938. public static ProcessController makeProcess(ProcessController controller, String classpath, String mainClass, String[] args) {
  939. File java = LangUtil.getJavaExecutable();
  940. ArrayList<String> cmd = new ArrayList<String>();
  941. cmd.add(java.getAbsolutePath());
  942. cmd.add("-classpath");
  943. cmd.add(classpath);
  944. cmd.add(mainClass);
  945. if (!LangUtil.isEmpty(args)) {
  946. cmd.addAll(Arrays.asList(args));
  947. }
  948. String[] command = (String[]) cmd.toArray(new String[0]);
  949. if (null == controller) {
  950. controller = new ProcessController();
  951. }
  952. controller.init(command, mainClass);
  953. return controller;
  954. }
  955. // /**
  956. // * Create a process to run asynchronously.
  957. // * @param controller if not null, initialize this one
  958. // * @param command the String[] command to run
  959. // * @param controller the ProcessControl for streams and results
  960. // */
  961. // public static ProcessController makeProcess( // not needed?
  962. // ProcessController controller,
  963. // String[] command,
  964. // String label) {
  965. // if (null == controller) {
  966. // controller = new ProcessController();
  967. // }
  968. // controller.init(command, label);
  969. // return controller;
  970. // }
  971. /**
  972. * Find java executable File path from java.home system property.
  973. *
  974. * @return File associated with the java command, or null if not found.
  975. */
  976. public static File getJavaExecutable() {
  977. String javaHome = null;
  978. File result = null;
  979. // java.home
  980. // java.class.path
  981. // java.ext.dirs
  982. try {
  983. javaHome = System.getProperty("java.home");
  984. } catch (Throwable t) {
  985. // ignore
  986. }
  987. if (null != javaHome) {
  988. File binDir = new File(javaHome, "bin");
  989. if (binDir.isDirectory() && binDir.canRead()) {
  990. String[] execs = new String[] { "java", "java.exe" };
  991. for (int i = 0; i < execs.length; i++) {
  992. result = new File(binDir, execs[i]);
  993. if (result.canRead()) {
  994. break;
  995. }
  996. }
  997. }
  998. }
  999. return result;
  1000. }
  1001. // /**
  1002. // * Sleep for a particular period (in milliseconds).
  1003. // *
  1004. // * @param time the long time in milliseconds to sleep
  1005. // * @return true if delay succeeded, false if interrupted 100 times
  1006. // */
  1007. // public static boolean sleep(long milliseconds) {
  1008. // if (milliseconds == 0) {
  1009. // return true;
  1010. // } else if (milliseconds < 0) {
  1011. // throw new IllegalArgumentException("negative: " + milliseconds);
  1012. // }
  1013. // return sleepUntil(milliseconds + System.currentTimeMillis());
  1014. // }
  1015. /**
  1016. * Sleep until a particular time.
  1017. *
  1018. * @param time the long time in milliseconds to sleep until
  1019. * @return true if delay succeeded, false if interrupted 100 times
  1020. */
  1021. public static boolean sleepUntil(long time) {
  1022. if (time == 0) {
  1023. return true;
  1024. } else if (time < 0) {
  1025. throw new IllegalArgumentException("negative: " + time);
  1026. }
  1027. // final Thread thread = Thread.currentThread();
  1028. long curTime = System.currentTimeMillis();
  1029. for (int i = 0; (i < 100) && (curTime < time); i++) {
  1030. try {
  1031. Thread.sleep(time - curTime);
  1032. } catch (InterruptedException e) {
  1033. // ignore
  1034. }
  1035. curTime = System.currentTimeMillis();
  1036. }
  1037. return (curTime >= time);
  1038. }
  1039. /**
  1040. * Handle an external process asynchrously. <code>start()</code> launches a main thread to wait for the process and pipes
  1041. * streams (in child threads) through to the corresponding streams (e.g., the process System.err to this System.err). This can
  1042. * complete normally, by exception, or on demand by a client. Clients can implement <code>doCompleting(..)</code> to get notice
  1043. * when the process completes.
  1044. * <p>
  1045. * The following sample code creates a process with a completion callback starts it, and some time later retries the process.
  1046. *
  1047. * <pre>
  1048. * LangUtil.ProcessController controller = new LangUtil.ProcessController() {
  1049. * protected void doCompleting(LangUtil.ProcessController.Thrown thrown, int result) {
  1050. * // signal result
  1051. * }
  1052. * };
  1053. * controller.init(new String[] { &quot;java&quot;, &quot;-version&quot; }, &quot;java version&quot;);
  1054. * controller.start();
  1055. * // some time later...
  1056. * // retry...
  1057. * if (!controller.completed()) {
  1058. * controller.stop();
  1059. * controller.reinit();
  1060. * controller.start();
  1061. * }
  1062. * </pre>
  1063. *
  1064. * <u>warning</u>: Currently this does not close the input or output streams, since doing so prevents their use later.
  1065. */
  1066. public static class ProcessController {
  1067. /*
  1068. * XXX not verified thread-safe, but should be. Known problems: - user stops (completed = true) then exception thrown from
  1069. * destroying process (stop() expects !completed) ...
  1070. */
  1071. private String[] command;
  1072. private String[] envp;
  1073. private String label;
  1074. private boolean init;
  1075. private boolean started;
  1076. private boolean completed;
  1077. /** if true, stopped by user when not completed */
  1078. private boolean userStopped;
  1079. private Process process;
  1080. private FileUtil.Pipe errStream;
  1081. private FileUtil.Pipe outStream;
  1082. private FileUtil.Pipe inStream;
  1083. private ByteArrayOutputStream errSnoop;
  1084. private ByteArrayOutputStream outSnoop;
  1085. private int result;
  1086. private Thrown thrown;
  1087. public ProcessController() {
  1088. }
  1089. /**
  1090. * Permit re-running using the same command if this is not started or if completed. Can also call this when done with
  1091. * results to release references associated with results (e.g., stack traces).
  1092. */
  1093. public final void reinit() {
  1094. if (!init) {
  1095. throw new IllegalStateException("must init(..) before reinit()");
  1096. }
  1097. if (started && !completed) {
  1098. throw new IllegalStateException("not completed - do stop()");
  1099. }
  1100. // init everything but command and label
  1101. started = false;
  1102. completed = false;
  1103. result = Integer.MIN_VALUE;
  1104. thrown = null;
  1105. process = null;
  1106. errStream = null;
  1107. outStream = null;
  1108. inStream = null;
  1109. }
  1110. public final void init(String classpath, String mainClass, String[] args) {
  1111. init(LangUtil.getJavaExecutable(), classpath, mainClass, args);
  1112. }
  1113. public final void init(File java, String classpath, String mainClass, String[] args) {
  1114. LangUtil.throwIaxIfNull(java, "java");
  1115. LangUtil.throwIaxIfNull(mainClass, "mainClass");
  1116. LangUtil.throwIaxIfNull(args, "args");
  1117. ArrayList<String> cmd = new ArrayList<String>();
  1118. cmd.add(java.getAbsolutePath());
  1119. cmd.add("-classpath");
  1120. cmd.add(classpath);
  1121. cmd.add(mainClass);
  1122. if (!LangUtil.isEmpty(args)) {
  1123. cmd.addAll(Arrays.asList(args));
  1124. }
  1125. init((String[]) cmd.toArray(new String[0]), mainClass);
  1126. }
  1127. public final void init(String[] command, String label) {
  1128. this.command = (String[]) LangUtil.safeCopy(command, new String[0]);
  1129. if (1 > this.command.length) {
  1130. throw new IllegalArgumentException("empty command");
  1131. }
  1132. this.label = LangUtil.isEmpty(label) ? command[0] : label;
  1133. init = true;
  1134. reinit();
  1135. }
  1136. public final void setEnvp(String[] envp) {
  1137. this.envp = (String[]) LangUtil.safeCopy(envp, new String[0]);
  1138. if (1 > this.envp.length) {
  1139. throw new IllegalArgumentException("empty envp");
  1140. }
  1141. }
  1142. public final void setErrSnoop(ByteArrayOutputStream snoop) {
  1143. errSnoop = snoop;
  1144. if (null != errStream) {
  1145. errStream.setSnoop(errSnoop);
  1146. }
  1147. }
  1148. public final void setOutSnoop(ByteArrayOutputStream snoop) {
  1149. outSnoop = snoop;
  1150. if (null != outStream) {
  1151. outStream.setSnoop(outSnoop);
  1152. }
  1153. }
  1154. /**
  1155. * Start running the process and pipes asynchronously.
  1156. *
  1157. * @return Thread started or null if unable to start thread (results available via <code>getThrown()</code>, etc.)
  1158. */
  1159. public final Thread start() {
  1160. if (!init) {
  1161. throw new IllegalStateException("not initialized");
  1162. }
  1163. synchronized (this) {
  1164. if (started) {
  1165. throw new IllegalStateException("already started");
  1166. }
  1167. started = true;
  1168. }
  1169. try {
  1170. process = Runtime.getRuntime().exec(command);
  1171. } catch (IOException e) {
  1172. stop(e, Integer.MIN_VALUE);
  1173. return null;
  1174. }
  1175. errStream = new FileUtil.Pipe(process.getErrorStream(), System.err);
  1176. if (null != errSnoop) {
  1177. errStream.setSnoop(errSnoop);
  1178. }
  1179. outStream = new FileUtil.Pipe(process.getInputStream(), System.out);
  1180. if (null != outSnoop) {
  1181. outStream.setSnoop(outSnoop);
  1182. }
  1183. inStream = new FileUtil.Pipe(System.in, process.getOutputStream());
  1184. // start 4 threads, process & pipes for in, err, out
  1185. Runnable processRunner = new Runnable() {
  1186. public void run() {
  1187. Throwable thrown = null;
  1188. int result = Integer.MIN_VALUE;
  1189. try {
  1190. // pipe threads are children
  1191. new Thread(errStream).start();
  1192. new Thread(outStream).start();
  1193. new Thread(inStream).start();
  1194. process.waitFor();
  1195. result = process.exitValue();
  1196. } catch (Throwable e) {
  1197. thrown = e;
  1198. } finally {
  1199. stop(thrown, result);
  1200. }
  1201. }
  1202. };
  1203. Thread result = new Thread(processRunner, label);
  1204. result.start();
  1205. return result;
  1206. }
  1207. /**
  1208. * Destroy any process, stop any pipes. This waits for the pipes to clear (reading until no more input is available), but
  1209. * does not wait for the input stream for the pipe to close (i.e., not waiting for end-of-file on input stream).
  1210. */
  1211. public final synchronized void stop() {
  1212. if (completed) {
  1213. return;
  1214. }
  1215. userStopped = true;
  1216. stop(null, Integer.MIN_VALUE);
  1217. }
  1218. public final String[] getCommand() {
  1219. String[] toCopy = command;
  1220. if (LangUtil.isEmpty(toCopy)) {
  1221. return new String[0];
  1222. }
  1223. String[] result = new String[toCopy.length];
  1224. System.arraycopy(toCopy, 0, result, 0, result.length);
  1225. return result;
  1226. }
  1227. public final boolean completed() {
  1228. return completed;
  1229. }
  1230. public final boolean started() {
  1231. return started;
  1232. }
  1233. public final boolean userStopped() {
  1234. return userStopped;
  1235. }
  1236. /**
  1237. * Get any Throwable thrown. Note that the process can complete normally (with a valid return value), at the same time the
  1238. * pipes throw exceptions, and that this may return some exceptions even if the process is not complete.
  1239. *
  1240. * @return null if not complete or Thrown containing exceptions thrown by the process and streams.
  1241. */
  1242. public final Thrown getThrown() { // cache this
  1243. return makeThrown(null);
  1244. }
  1245. public final int getResult() {
  1246. return result;
  1247. }
  1248. /**
  1249. * Subclasses implement this to get synchronous notice of completion. All pipes and processes should be complete at this
  1250. * time. To get the exceptions thrown for the pipes, use <code>getThrown()</code>. If there is an exception, the process
  1251. * completed abruptly (including side-effects of the user halting the process). If <code>userStopped()</code> is true, then
  1252. * some client asked that the process be destroyed using <code>stop()</code>. Otherwise, the result code should be the
  1253. * result value returned by the process.
  1254. *
  1255. * @param thrown same as <code>getThrown().fromProcess</code>.
  1256. * @param result same as <code>getResult()</code>
  1257. * @see getThrown()
  1258. * @see getResult()
  1259. * @see stop()
  1260. */
  1261. protected void doCompleting(Thrown thrown, int result) {
  1262. }
  1263. /**
  1264. * Handle termination (on-demand, abrupt, or normal) by destroying and/or halting process and pipes.
  1265. *
  1266. * @param thrown ignored if null
  1267. * @param result ignored if Integer.MIN_VALUE
  1268. */
  1269. private final synchronized void stop(Throwable thrown, int result) {
  1270. if (completed) {
  1271. throw new IllegalStateException("already completed");
  1272. } else if (null != this.thrown) {
  1273. throw new IllegalStateException("already set thrown: " + thrown);
  1274. }
  1275. // assert null == this.thrown
  1276. this.thrown = makeThrown(thrown);
  1277. if (null != process) {
  1278. process.destroy();
  1279. }
  1280. if (null != inStream) {
  1281. inStream.halt(false, true); // this will block if waiting
  1282. inStream = null;
  1283. }
  1284. if (null != outStream) {
  1285. outStream.halt(true, true);
  1286. outStream = null;
  1287. }
  1288. if (null != errStream) {
  1289. errStream.halt(true, true);
  1290. errStream = null;
  1291. }
  1292. if (Integer.MIN_VALUE != result) {
  1293. this.result = result;
  1294. }
  1295. completed = true;
  1296. doCompleting(this.thrown, result);
  1297. }
  1298. /**
  1299. * Create snapshot of Throwable's thrown.
  1300. *
  1301. * @param thrown ignored if null or if this.thrown is not null
  1302. */
  1303. private final synchronized Thrown makeThrown(Throwable processThrown) {
  1304. if (null != thrown) {
  1305. return thrown;
  1306. }
  1307. return new Thrown(processThrown, (null == outStream ? null : outStream.getThrown()), (null == errStream ? null
  1308. : errStream.getThrown()), (null == inStream ? null : inStream.getThrown()));
  1309. }
  1310. public static class Thrown {
  1311. public final Throwable fromProcess;
  1312. public final Throwable fromErrPipe;
  1313. public final Throwable fromOutPipe;
  1314. public final Throwable fromInPipe;
  1315. /** true only if some Throwable is not null */
  1316. public final boolean thrown;
  1317. private Thrown(Throwable fromProcess, Throwable fromOutPipe, Throwable fromErrPipe, Throwable fromInPipe) {
  1318. this.fromProcess = fromProcess;
  1319. this.fromErrPipe = fromErrPipe;
  1320. this.fromOutPipe = fromOutPipe;
  1321. this.fromInPipe = fromInPipe;
  1322. thrown = ((null != fromProcess) || (null != fromInPipe) || (null != fromOutPipe) || (null != fromErrPipe));
  1323. }
  1324. public String toString() {
  1325. StringBuffer sb = new StringBuffer();
  1326. append(sb, fromProcess, "process");
  1327. append(sb, fromOutPipe, " stdout");
  1328. append(sb, fromErrPipe, " stderr");
  1329. append(sb, fromInPipe, " stdin");
  1330. if (0 == sb.length()) {
  1331. return "Thrown (none)";
  1332. } else {
  1333. return sb.toString();
  1334. }
  1335. }
  1336. private void append(StringBuffer sb, Throwable thrown, String label) {
  1337. if (null != thrown) {
  1338. sb.append("from " + label + ": ");
  1339. sb.append(LangUtil.renderExceptionShort(thrown));
  1340. sb.append(LangUtil.EOL);
  1341. }
  1342. }
  1343. } // class Thrown
  1344. }
  1345. }