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.

AbstractRunSpec.java 41KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189
  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 Common Public License v1.0
  7. * which accompanies this distribution and is available at
  8. * http://www.eclipse.org/legal/cpl-v10.html
  9. *
  10. * Contributors:
  11. * Xerox/PARC initial implementation
  12. * ******************************************************************/
  13. package org.aspectj.testing.harness.bridge;
  14. import java.io.File;
  15. import java.io.PrintStream;
  16. import java.util.ArrayList;
  17. import java.util.Arrays;
  18. import java.util.BitSet;
  19. import java.util.Iterator;
  20. import java.util.List;
  21. import java.util.ListIterator;
  22. import java.util.StringTokenizer;
  23. import org.aspectj.bridge.*;
  24. import org.aspectj.bridge.IMessage;
  25. import org.aspectj.bridge.IMessageHandler;
  26. import org.aspectj.bridge.ISourceLocation;
  27. import org.aspectj.bridge.MessageHandler;
  28. import org.aspectj.bridge.MessageUtil;
  29. import org.aspectj.testing.run.IRunIterator;
  30. //import org.aspectj.testing.util.*;
  31. import org.aspectj.testing.util.BridgeUtil;
  32. import org.aspectj.testing.util.options.*;
  33. import org.aspectj.testing.util.options.Option.InvalidInputException;
  34. import org.aspectj.testing.xml.IXmlWritable;
  35. import org.aspectj.testing.xml.SoftMessage;
  36. import org.aspectj.testing.xml.XMLWriter;
  37. import org.aspectj.util.LangUtil;
  38. /**
  39. * Base class for initialization of components expecting messages,
  40. * options, files/paths, and source locations (resolved files),
  41. * and potentially containing child Spec.
  42. * <p>
  43. * <u>initialization</u>: This defines bean/xml setters for all.
  44. * This converts String to IMessage using
  45. * {@link MessageUtil#readMessage(String)}
  46. * and String to ISourceLocation using
  47. * {@link BridgeUtil#makeSourceLocation(input)}.
  48. * See those APIs for input form and limitations.
  49. * A Spec also accepts (or rejects) runtime configuration from a parent
  50. * in {@link adoptParentValues(RT, IMessageHandler)}.
  51. * Since some children Spec may balk but this parent Spec continue,
  52. * use {@link getChildren()} to get the full list of children Spec,
  53. * but {@link getWorkingChildren()} to get the list of children that
  54. * are not being skipped in accordance with runtime configuration.
  55. * <p>
  56. * <u>subclassing</u>: subclasses wishing other than
  57. * the default behavior for reading String input should override the
  58. * corresponding (bean) setter. They can also override the
  59. * add{foo} methods to get notice or modify objects constructed
  60. * from the input.
  61. * <p>
  62. * <u>bean properties</u>: because this is designed to work
  63. * by standard Java bean introspection, take care to follow
  64. * bean rules when adding methods. In particular, a property
  65. * is illegal if the setter takes a different type than the
  66. * getter returns. That means, e.g., that all List and array[] getters
  67. * should be named "get{property}[List|Array]". Otherwise
  68. * the XML readers will silently fail to set the property
  69. * (perhaps with trace information that the property had
  70. * no write method or was read-only).
  71. * <p>
  72. * <u>Coordination with writers</u>: because this reads the contents
  73. * of values written by IXmlWritable, they should ensure their
  74. * values are readable. When flattening and unflattening
  75. * lists, the convention is to use the {un}flattenList(..) methods
  76. * in XMLWriter.
  77. * @see XMLWriter@unflattenList(String)
  78. * @see XMLWriter@flattenList(List)
  79. */
  80. abstract public class AbstractRunSpec implements IRunSpec { // XXX use MessageHandler?
  81. private static final Fork FORK;
  82. static {
  83. String value =
  84. Globals.getSystemProperty(Globals.FORK_NAME, null);
  85. FORK = (LangUtil.isEmpty(value)
  86. ? Fork.NOFORK
  87. : new Fork(value));
  88. }
  89. /** true if we expect to use a staging directory */
  90. boolean isStaging;
  91. /** true if this spec permits bad input (e.g., to test error handling) */
  92. boolean badInput;
  93. protected String description;
  94. /** optional source location of the specification itself */
  95. protected ISourceLocation sourceLocation;
  96. private BitSet skipSet;
  97. private boolean skipAll;
  98. protected String xmlElementName; // nonfinal only for clone()
  99. protected final ArrayList /*String*/ keywords;
  100. protected final IMessageHolder /*IMessage*/ messages;
  101. protected final ArrayList /*String*/ options;
  102. protected final ArrayList /*String*/ paths;
  103. protected final ArrayList /*ISourceLocation*/ sourceLocations; // XXX remove?
  104. protected final ArrayList /*IRunSpec*/ children;
  105. protected final ArrayList /*DirChanges.Spec*/ dirChanges;
  106. protected XMLNames xmlNames;
  107. protected String comment;
  108. /** These options are 1:1 with spec, but set at runtime (not saved) */
  109. public final RT runtime;
  110. /** if true, then any child skip causes this to skip */
  111. protected boolean skipIfAnyChildSkipped; // nonfinal only for cloning
  112. public AbstractRunSpec(String xmlElementName) {
  113. this(xmlElementName, true);
  114. }
  115. public AbstractRunSpec(String xmlElementName, boolean skipIfAnyChildSkipped) {
  116. if (null == xmlElementName) {
  117. xmlElementName = "spec";
  118. }
  119. this.xmlElementName = xmlElementName;
  120. messages = new MessageHandler(true);
  121. options = new ArrayList();
  122. paths = new ArrayList();
  123. sourceLocations = new ArrayList();
  124. keywords = new ArrayList();
  125. children = new ArrayList();
  126. dirChanges = new ArrayList();
  127. xmlNames = XMLNames.DEFAULT;
  128. runtime = new RT();
  129. this.skipIfAnyChildSkipped = skipIfAnyChildSkipped;
  130. }
  131. /** @param comment ignored if null */
  132. public void setComment(String comment) {
  133. if (!LangUtil.isEmpty(comment)) {
  134. this.comment = comment;
  135. }
  136. }
  137. public void setStaging(boolean staging) {
  138. isStaging = staging;
  139. }
  140. public void setBadInput(boolean badInput) {
  141. this.badInput = badInput;
  142. }
  143. boolean isStaging() {
  144. return isStaging;
  145. }
  146. // ------- description (title, label...)
  147. public void setDescription(String description) {
  148. this.description = description;
  149. }
  150. public String getDescription() {
  151. return description;
  152. }
  153. // ------- source location of the spec
  154. public void setSourceLocation(ISourceLocation sourceLocation) {
  155. this.sourceLocation = sourceLocation;
  156. }
  157. public ISourceLocation getSourceLocation() {
  158. return sourceLocation;
  159. }
  160. // ------- keywords
  161. /** @param keyword added after trimming if not empty */
  162. public void setKeyword(String keyword) {
  163. addKeyword(keyword);
  164. }
  165. /** Add keyword if non-empty and not duplicate */
  166. public void addKeyword(String keyword) {
  167. if (!LangUtil.isEmptyTrimmed(keyword)) {
  168. keyword = keyword.trim();
  169. if (!keywords.contains(keyword)) {
  170. keywords.add(keyword);
  171. }
  172. }
  173. }
  174. public void setKeywords(String items) {
  175. addKeywords(items);
  176. }
  177. public void addKeywords(String items) {
  178. if (null != items) {
  179. addKeywords(XMLWriter.unflattenList(items));
  180. }
  181. }
  182. public void addKeywords(String[] ra) {
  183. if (null != ra) {
  184. for (int i = 0; i < ra.length; i++) {
  185. addKeyword(ra[i]);
  186. }
  187. }
  188. }
  189. public ArrayList getKeywordsList() {
  190. return makeList(keywords);
  191. }
  192. // ------- options - String args
  193. /** @return ArrayList of String options */
  194. public ArrayList getOptionsList() {
  195. return makeList(options);
  196. }
  197. /** @return String[] of options */
  198. public String[] getOptionsArray() {
  199. return (String[]) options.toArray(new String[0]);
  200. }
  201. public void setOption(String option) {
  202. addOption(option);
  203. }
  204. public void addOption(String option) {
  205. if ((null != option) && (0 < option.length())) {
  206. options.add(option);
  207. }
  208. }
  209. /** add options (from XML/bean) - removes any existing options */
  210. public void setOptions(String items) {
  211. this.options.clear();
  212. addOptions(items);
  213. }
  214. /**
  215. * Set options, removing any existing options.
  216. * @param options String[] options to use - may be null or empty
  217. */
  218. public void setOptionsArray(String[] options) {
  219. this.options.clear();
  220. if (!LangUtil.isEmpty(options)) {
  221. this.options.addAll(Arrays.asList(options));
  222. }
  223. }
  224. public void addOptions(String items) {
  225. if (null != items) {
  226. addOptions(XMLWriter.unflattenList(items));
  227. }
  228. }
  229. public void addOptions(String[] ra) {
  230. if (null != ra) {
  231. for (int i = 0; i < ra.length; i++) {
  232. addOption(ra[i]);
  233. }
  234. }
  235. }
  236. // -------------- source locations
  237. // /** @return ArrayList of ISourceLocation sourceLocs */
  238. // public ArrayList getSourceLocationsList() {
  239. // return makeList(sourceLocations);
  240. // }
  241. //
  242. // /** @return ISourceLocation[] sourceLocs */
  243. // public ISourceLocation[] getSourceLocationsArray() {
  244. // return (ISourceLocation[]) sourceLocations.toArray(new ISourceLocation[0]);
  245. // }
  246. //
  247. //
  248. // public void setSourceLocation(String input) {
  249. // if (null != input) {
  250. // ISourceLocation sl = BridgeUtil.makeSourceLocation(input);
  251. // if (null != sl) {
  252. // addSourceLocation(sl);
  253. // }
  254. // // XXX need error-handling for bad input
  255. // }
  256. // }
  257. //
  258. // public void addSourceLocation(ISourceLocation sourceLoc) {
  259. // if (null != sourceLoc) {
  260. // sourceLocations.add(sourceLoc);
  261. // }
  262. // }
  263. // --------------- (String) paths
  264. /** @return ArrayList of String paths */
  265. public ArrayList getPathsList() {
  266. return makeList(paths);
  267. }
  268. /** @return String[] of paths */
  269. public String[] getPathsArray() {
  270. return (String[]) paths.toArray(new String[0]);
  271. }
  272. // /** @return String[] of paths, removing any matching stripPrefix prefix */
  273. // public String[] getPathsArray(String stripPrefix) {
  274. // String[] result = getPathsArray();
  275. // if (!LangUtil.isEmpty(stripPrefix)) {
  276. // final int LEN = stripPrefix.length();
  277. // for (int i = 0; i < result.length; i++) {
  278. // if ((null != result[i]) && result[i].startsWith(stripPrefix)) {
  279. // result[i] = result[i].substring(LEN);
  280. // }
  281. // }
  282. // }
  283. // return result;
  284. // }
  285. //
  286. // /** @return ArrayList of File baseDir/{path} */
  287. // public ArrayList getPathsAsFile(File baseDir) {
  288. // if (null == baseDir) {
  289. // baseDir = new File(".");
  290. // }
  291. // ArrayList result = makeList(null);
  292. // for (Iterator iter = paths.iterator(); iter.hasNext();) {
  293. // String path = (String) iter.next();
  294. // result.add(new File(baseDir, path));
  295. // }
  296. // return result;
  297. // }
  298. public void setPath(String path) {
  299. addPath(path);
  300. }
  301. public void setPaths(String paths) {
  302. addPaths(paths);
  303. }
  304. public void addPath(String path) {
  305. if (null != path) {
  306. paths.add(path);
  307. }
  308. }
  309. public void addPaths(String items) {
  310. if (null != items) {
  311. addPaths(XMLWriter.unflattenList(items));
  312. }
  313. }
  314. public void addPaths(String[] ra) {
  315. if (null != ra) {
  316. for (int i = 0; i < ra.length; i++) {
  317. addPath(ra[i]);
  318. }
  319. }
  320. }
  321. public void addWrapFile(WrapFile file) {
  322. if (null != file) {
  323. paths.add(file.path);
  324. }
  325. }
  326. // --------------------- dir changes
  327. public void addDirChanges(DirChanges.Spec dirChangesSpec) {
  328. if (null != dirChangesSpec) {
  329. dirChanges.add(dirChangesSpec);
  330. }
  331. }
  332. // --------------------- messages
  333. public void setMessage(String message) {
  334. addMessage(message);
  335. }
  336. public void addMessage(IMessage message) {
  337. if (null != message) {
  338. if (!messages.handleMessage(message)) {
  339. String s = "invalid message: " + message;
  340. throw new IllegalArgumentException(s);
  341. }
  342. }
  343. }
  344. public void addMessage(String message) {
  345. if (null != message) {
  346. addMessage(BridgeUtil.readMessage(message));
  347. }
  348. }
  349. /** this can ONLY work if each item has no internal comma
  350. */
  351. public void addMessages(String items) {
  352. if (null != items) {
  353. String[] ra = XMLWriter.unflattenList(items);
  354. for (int i = 0; i < ra.length; i++) {
  355. addMessage(ra[i]);
  356. }
  357. }
  358. }
  359. public void addMessages(List messages) {
  360. if (null != messages) {
  361. for (Iterator iter = messages.iterator(); iter.hasNext();) {
  362. Object o = iter.next();
  363. if (o instanceof IMessage) {
  364. addMessage((IMessage) o);
  365. } else {
  366. // XXX warning?
  367. }
  368. }
  369. }
  370. }
  371. /** @return int number of message of this kind (optionally or greater */
  372. public int numMessages(IMessage.Kind kind, boolean orGreater) {
  373. return messages.numMessages(kind, orGreater);
  374. }
  375. public IMessageHolder getMessages() {
  376. return messages;
  377. }
  378. public void addChild(IRunSpec child) {
  379. // fyi, child is added when complete (depth-first), not when initialized,
  380. // so cannot affect initialization of child here
  381. if (null != child) {
  382. children.add(child);
  383. }
  384. }
  385. /** @return copy of children list */
  386. public ArrayList getChildren() {
  387. return makeList(children);
  388. }
  389. /** @return copy of children list without children to skip */
  390. public ArrayList getWorkingChildren() {
  391. if (skipAll) {
  392. return new ArrayList();
  393. }
  394. if (null == skipSet) {
  395. return getChildren();
  396. }
  397. ArrayList result = new ArrayList();
  398. int i = 0;
  399. for (Iterator iter = children.listIterator();
  400. iter.hasNext(); i++) {
  401. Object child = iter.next();
  402. if (!skipSet.get(i)) {
  403. result.add(child);
  404. }
  405. }
  406. return result;
  407. }
  408. /**
  409. * Recursively absorb parent values if different.
  410. * This implementation calls doAdoptParentValues(..)
  411. * and then calls this for any children.
  412. * This is when skipped children are determined.
  413. * Children may elect to balk at this point, reducing the
  414. * number of children or causing this spec to skip if
  415. * skipIfAnyChildrenSkipped. For each test skipped, either
  416. * this doAdoptParentValues(..) or the child's adoptParentValues(..)
  417. * should add one info message with the reason this is being skipped.
  418. * The only reason to override this would be to NOT
  419. * invoke the same for children, or to do something similar
  420. * for children which are not AbstractRunSpec.
  421. * @param parentRuntime the RT values to adopt - ignored if null
  422. * @param handler the IMessageHandler for info messages when skipping
  423. * @return false if this wants to be skipped, true otherwise
  424. */
  425. public boolean adoptParentValues(RT parentRuntime, IMessageHandler handler) {
  426. boolean skipped = false;
  427. skipAll = false;
  428. skipSet = new BitSet();
  429. if (null != parentRuntime) {
  430. skipped = !doAdoptParentValues(parentRuntime, handler);
  431. if (skipped && skipIfAnyChildSkipped) { // no need to continue checking
  432. skipAll = true;
  433. return false;
  434. }
  435. int i = 0;
  436. for (ListIterator iter = children.listIterator(); iter.hasNext(); i++) {
  437. IRunSpec child = (IRunSpec) iter.next();
  438. if (child instanceof AbstractRunSpec) { // XXX ugly instanceof
  439. AbstractRunSpec arsChild = (AbstractRunSpec) child;
  440. if (!arsChild.adoptParentValues(runtime, handler)) {
  441. skipSet.set(i);
  442. //iter.remove();
  443. if (!skipped) {
  444. skipped = true;
  445. if (skipIfAnyChildSkipped) { // no need to continue checking
  446. // String m = "skipping " + toString() + " because child "
  447. // + arsChild + " skipped";
  448. // MessageUtil.info(handler, m);
  449. skipAll = true;
  450. //children.clear();
  451. return false;
  452. }
  453. }
  454. }
  455. }
  456. }
  457. }
  458. return true;
  459. }
  460. /**
  461. * Adopt parent values.
  462. * This implementation makes a local copy.
  463. * If we interpret (and absorb) any options, they should be removed
  464. * from parentRuntime.
  465. * This sets verbose if different (override)
  466. * and directly adopts parentOptions if ours is null
  467. * and otherwise adds any non-null options we don't already have.
  468. * setting verbose and adding to parent options.
  469. * Implementors override this to affect how parent values are adopted.
  470. * Implementors should not recurse into children.
  471. * This method may be called multiple times, so implementors
  472. * should not destroy any spec information.
  473. * Always add an info message when returning false to skip
  474. * @param parentRuntime the RT values to adopt - never null
  475. * @return false if this wants to be skipped, true otherwise
  476. */
  477. protected boolean doAdoptParentValues(RT parentRuntime, IMessageHandler handler) {
  478. if (runtime.verbose != parentRuntime.verbose) {
  479. runtime.verbose = parentRuntime.verbose;
  480. }
  481. if (!LangUtil.isEmpty(runtime.parentOptions)) {
  482. runtime.parentOptions.clear();
  483. }
  484. if (!LangUtil.isEmpty(parentRuntime.parentOptions)) {
  485. runtime.parentOptions.addAll(parentRuntime.parentOptions);
  486. }
  487. return true;
  488. }
  489. /**
  490. * Implementations call this when signalling skips to ensure consistency
  491. * in message formatting
  492. * @param handler the IMessageHandler sink - not null
  493. * @param reason the String reason to skip - not null
  494. */
  495. protected void skipMessage(IMessageHandler handler, String reason) {
  496. LangUtil.throwIaxIfNull(handler, "handler");
  497. LangUtil.throwIaxIfNull(handler, "reason");
  498. // XXX for Runs, label does not identify the test
  499. String label = toString();
  500. MessageUtil.info(handler, "skipping \"" + label + "\" because " + reason);
  501. }
  502. // --------------------------- writing xml - would prefer castor..
  503. /**
  504. * Control XML output by renaming or suppressing output for
  505. * attributes and subelements.
  506. * Subelements are skipped by setting the XMLNames booleans to false.
  507. * Attributes are skipped by setting their name to null.
  508. * @param names XMLNames with new names and/or suppress flags.
  509. */
  510. protected void setXMLNames(XMLNames names) {
  511. if (null != names) {
  512. xmlNames = names;
  513. }
  514. }
  515. // /** @return null if value is null or name="{value}" otherwise */
  516. // private String makeAttr(XMLWriter out, String name, String value) {
  517. // if (null == value) {
  518. // return null;
  519. // }
  520. // return XMLWriter.makeAttribute(name, value);
  521. // }
  522. //
  523. // /** @return null if list is null or empty or name="{flattenedList}" otherwise */
  524. // private String makeAttr(XMLWriter out, String name, List list) {
  525. // if (LangUtil.isEmpty(list)) {
  526. // return null;
  527. // }
  528. // String flat = XMLWriter.flattenList(list);
  529. // return XMLWriter.makeAttribute(name, flat);
  530. // }
  531. //
  532. /** @return true if writeAttributes(..) will produce any output */
  533. protected boolean haveAttributes() {
  534. return ((!LangUtil.isEmpty(xmlNames.descriptionName)
  535. && !LangUtil.isEmpty(description))
  536. || (!LangUtil.isEmpty(xmlNames.keywordsName)
  537. && !LangUtil.isEmpty(keywords))
  538. || (!LangUtil.isEmpty(xmlNames.optionsName)
  539. && !LangUtil.isEmpty(options))
  540. || (!LangUtil.isEmpty(xmlNames.pathsName)
  541. && !LangUtil.isEmpty(paths)));
  542. }
  543. /**
  544. * Write attributes without opening or closing elements/attributes.
  545. * An attribute is written only if the value is not empty
  546. * and the name in xmlNames is not empty
  547. */
  548. protected void writeAttributes(XMLWriter out) {
  549. if (!LangUtil.isEmpty(xmlNames.descriptionName)
  550. && !LangUtil.isEmpty(description)) {
  551. out.printAttribute(xmlNames.descriptionName, description);
  552. }
  553. if (!LangUtil.isEmpty(xmlNames.keywordsName)
  554. && !LangUtil.isEmpty(keywords)) {
  555. out.printAttribute(xmlNames.keywordsName,
  556. XMLWriter.flattenList(keywords));
  557. }
  558. if (!LangUtil.isEmpty(xmlNames.optionsName)
  559. && !LangUtil.isEmpty(options)) {
  560. out.printAttribute(xmlNames.optionsName, XMLWriter.flattenList(options));
  561. }
  562. if (!LangUtil.isEmpty(xmlNames.pathsName)
  563. && !LangUtil.isEmpty(paths)) {
  564. out.printAttribute(xmlNames.pathsName, XMLWriter.flattenList(paths));
  565. }
  566. if (!LangUtil.isEmpty(xmlNames.commentName)
  567. && !LangUtil.isEmpty(comment)) {
  568. out.printAttribute(xmlNames.commentName, comment);
  569. }
  570. if (isStaging && !LangUtil.isEmpty(xmlNames.stagingName)) {
  571. out.printAttribute(xmlNames.stagingName, "true");
  572. }
  573. if (badInput && !LangUtil.isEmpty(xmlNames.badInputName)) {
  574. out.printAttribute(xmlNames.badInputName, "true");
  575. }
  576. }
  577. /**
  578. * The default implementation writes everything as attributes,
  579. * then subelements for dirChanges, messages, then subelements for children.
  580. * Subclasses that override may delegate back for any of these.
  581. * Subclasses may also set XMLNames to name or suppress any attribute
  582. * or subelement.
  583. * @see writeMessages(XMLWriter)
  584. * @see writeChildren(XMLWriter)
  585. * @see IXmlWritable#writeXml(XMLWriter)
  586. */
  587. public void writeXml(XMLWriter out) {
  588. out.startElement(xmlElementName,false);
  589. writeAttributes(out);
  590. out.endAttributes();
  591. if (!xmlNames.skipMessages) {
  592. writeMessages(out);
  593. }
  594. if (!xmlNames.skipChildren) {
  595. writeChildren(out);
  596. }
  597. out.endElement(xmlElementName);
  598. }
  599. /**
  600. * Write messages. Assumes attributes are closed,
  601. * can write child elements of current element.
  602. */
  603. protected void writeMessages(XMLWriter out) {
  604. if (0 < messages.numMessages(null, true)) {
  605. SoftMessage.writeXml(out, messages);
  606. }
  607. }
  608. /**
  609. * Write children. Assumes attributes are closed,
  610. * can write child elements of current element.
  611. */
  612. protected void writeChildren(XMLWriter out) {
  613. if (0 < children.size()) {
  614. for (Iterator iter = children.iterator(); iter.hasNext();) {
  615. IXmlWritable self = (IXmlWritable) iter.next();
  616. self.writeXml(out);
  617. }
  618. }
  619. }
  620. // --------------------------- logging
  621. public void printAll(PrintStream out, String prefix) {
  622. out.println(prefix + toString());
  623. for (Iterator iter = children.iterator(); iter.hasNext();) {
  624. AbstractRunSpec child = (AbstractRunSpec) iter.next(); // IRunSpec
  625. child.printAll(out, prefix + " ");
  626. }
  627. }
  628. /**
  629. * default implementation returns the description if not empty
  630. * or the unqualified class name otherwise.
  631. * Subclasses should not call toString from here unless they reimplement it.
  632. * @return name of this thing or type
  633. */
  634. protected String getPrintName() {
  635. if (!LangUtil.isEmpty(description)) {
  636. return description;
  637. } else {
  638. return LangUtil.unqualifiedClassName(this);
  639. }
  640. }
  641. /** @return summary count of spec elements */
  642. public String toString() {
  643. return getPrintName() + "(" + containedSummary() + ")";
  644. }
  645. /** @return String of the form (# [options|paths|locations|messages]).. */
  646. protected String containedSummary() {
  647. StringBuffer result = new StringBuffer();
  648. addListCount("options", options, result);
  649. addListCount("paths", paths, result);
  650. addListCount("sourceLocations", sourceLocations, result);
  651. List messagesList = messages.getUnmodifiableListView();
  652. addListCount("messages", messagesList, result);
  653. return result.toString().trim();
  654. }
  655. public String toLongString() {
  656. String mssg = "";
  657. if (0 < messages.numMessages(null, true)) {
  658. mssg = " expected messages (" + MessageUtil.renderCounts(messages) + ")";
  659. }
  660. return getPrintName() + containedToLongString() + mssg.trim();
  661. }
  662. /** @return String of the form (# [options|paths|locations|messages]).. */
  663. protected String containedToLongString() {
  664. StringBuffer result = new StringBuffer();
  665. addListEntries("options", options, result);
  666. addListEntries("paths", paths, result);
  667. addListEntries("sourceLocations", sourceLocations, result);
  668. List messagesList = messages.getUnmodifiableListView();
  669. addListEntries("messages", messagesList, result);
  670. return result.toString();
  671. }
  672. protected void initClone(AbstractRunSpec spec)
  673. throws CloneNotSupportedException {
  674. /*
  675. * clone associated objects only if not (used as?) read-only.
  676. */
  677. spec.badInput = badInput;
  678. spec.children.clear();
  679. for (Iterator iter = children.iterator(); iter.hasNext();) {
  680. // clone these...
  681. IRunSpec child = (IRunSpec) iter.next();
  682. // require all child classes to support clone?
  683. if (child instanceof AbstractRunSpec) {
  684. spec.addChild((AbstractRunSpec) ((AbstractRunSpec) child).clone());
  685. } else {
  686. throw new Error("unable to clone " + child);
  687. }
  688. }
  689. spec.comment = comment;
  690. spec.description = description;
  691. spec.dirChanges.clear();
  692. spec.dirChanges.addAll(dirChanges);
  693. spec.isStaging = spec.isStaging;
  694. spec.keywords.clear();
  695. spec.keywords.addAll(keywords);
  696. spec.messages.clearMessages();
  697. MessageUtil.handleAll(spec.messages, messages, false);
  698. spec.options.clear();
  699. spec.options.addAll(options);
  700. spec.paths.clear();
  701. spec.paths.addAll(paths);
  702. spec.runtime.copy(runtime);
  703. spec.skipAll = skipAll;
  704. spec.skipIfAnyChildSkipped = skipIfAnyChildSkipped;
  705. if (null != skipSet) {
  706. spec.skipSet = new BitSet();
  707. spec.skipSet.or(skipSet);
  708. }
  709. spec.sourceLocation = sourceLocation;
  710. spec.sourceLocations.clear();
  711. spec.sourceLocations.addAll(sourceLocations);
  712. spec.xmlElementName = xmlElementName;
  713. spec.xmlNames = ((AbstractRunSpec.XMLNames) xmlNames.clone());
  714. }
  715. protected final Fork getFork() {
  716. // spec ignored now, but perhaps not later...
  717. return FORK;
  718. }
  719. private static void addListCount(String name, List list, StringBuffer sink) {
  720. int size = list.size();
  721. if ((null != list) && (0 < size)) {
  722. sink.append(" " + size + " ");
  723. sink.append(name);
  724. }
  725. }
  726. private static void addListEntries(String name, List list, StringBuffer sink) {
  727. if ((null != list) && (0 < list.size())) {
  728. sink.append(" " + list.size() + " ");
  729. sink.append(name);
  730. sink.append(": ");
  731. sink.append(list.toString());
  732. }
  733. }
  734. private ArrayList makeList(List list) {
  735. ArrayList result = new ArrayList();
  736. if (null != list) {
  737. result.addAll(list);
  738. }
  739. return result;
  740. }
  741. /**
  742. * Subclasses use this to rename attributes or omit attributes or subelements.
  743. * To suppress output of an attribute, * pass "" as the name of the attribute.
  744. * To use default entries, pass null for that entry.
  745. */
  746. public static class XMLNames {
  747. public static final XMLNames DEFAULT =
  748. new XMLNames(null, "description", "sourceLocation",
  749. "keywords", "options", "paths", "comment",
  750. "staging", "badInput", false, false, false);
  751. final String descriptionName;
  752. final String sourceLocationName;
  753. final String keywordsName;
  754. final String optionsName;
  755. final String pathsName;
  756. final String commentName;
  757. final String stagingName;
  758. final String badInputName;
  759. final boolean skipDirChanges;
  760. final boolean skipMessages;
  761. final boolean skipChildren;
  762. protected Object clone() {
  763. return new XMLNames(
  764. null,
  765. descriptionName,
  766. sourceLocationName,
  767. keywordsName,
  768. optionsName,
  769. pathsName,
  770. commentName,
  771. stagingName,
  772. badInputName,
  773. skipDirChanges,
  774. skipMessages,
  775. skipChildren);
  776. }
  777. // not runtime, skipAll, skipIfAnyChildSkipped, skipSet
  778. // sourceLocations
  779. /** reset all names/behavior or pass defaultNames
  780. * as the defaults for any null elements
  781. */
  782. XMLNames(XMLNames defaultNames,
  783. String descriptionName,
  784. String sourceLocationName,
  785. String keywordsName,
  786. String optionsName,
  787. String pathsName,
  788. String commentName,
  789. String stagingName,
  790. String badInputName,
  791. boolean skipDirChanges,
  792. boolean skipMessages,
  793. boolean skipChildren) {
  794. this.skipDirChanges = skipDirChanges;
  795. this.skipMessages = skipMessages;
  796. this.skipChildren = skipChildren;
  797. if (null != defaultNames) {
  798. this.descriptionName = (null != descriptionName? descriptionName : defaultNames.descriptionName);
  799. this.sourceLocationName = (null != sourceLocationName ? sourceLocationName : defaultNames.sourceLocationName);
  800. this.keywordsName = (null != keywordsName ? keywordsName : defaultNames.keywordsName);
  801. this.optionsName = (null != optionsName ? optionsName : defaultNames.optionsName);
  802. this.pathsName = (null != pathsName ? pathsName : defaultNames.pathsName);
  803. this.commentName = (null != commentName ? commentName : defaultNames.commentName);
  804. this.stagingName = (null != stagingName ? stagingName : defaultNames.stagingName);
  805. this.badInputName = (null != badInputName ? badInputName : defaultNames.badInputName);
  806. } else {
  807. this.descriptionName = descriptionName;
  808. this.sourceLocationName = sourceLocationName;
  809. this.keywordsName = keywordsName;
  810. this.optionsName = optionsName;
  811. this.pathsName = pathsName;
  812. this.commentName = commentName;
  813. this.stagingName = stagingName;
  814. this.badInputName = badInputName;
  815. }
  816. }
  817. }
  818. /** subclasses implement this to create and set up a run */
  819. abstract public IRunIterator makeRunIterator(Sandbox sandbox, Validator validator);
  820. /** This is for separate file (sub-) elements with path attributes */
  821. public static class WrapFile {
  822. public String path;
  823. public void setPath(String path) {
  824. this.path = path;
  825. }
  826. }
  827. /** segregate runtime-only state in spec */
  828. public static class RT {
  829. /** true if we should emit verbose messages */
  830. private boolean verbose;
  831. /** null unless parent set options for children to consider */
  832. final private ArrayList /*String*/ parentOptions;
  833. public RT() {
  834. parentOptions = new ArrayList();
  835. }
  836. public boolean isVerbose() {
  837. return verbose;
  838. }
  839. /**
  840. * Set parent options - old options destroyed.
  841. * Will result in duplicates if duplicates added.
  842. * Null or empty entries are ignored
  843. * @param options ignored if null or empty
  844. */
  845. public void setOptions(String[] options) {
  846. parentOptions.clear();
  847. if (!LangUtil.isEmpty(options)) {
  848. for (int i = 0; i < options.length; i++) {
  849. if (!LangUtil.isEmpty(options[i])) {
  850. parentOptions.add(options[i]);
  851. }
  852. }
  853. }
  854. }
  855. /**
  856. * Copy values from another RT
  857. * @param toCopy the RT to copy from
  858. * @throws IllegalArgumentException if toCopy is null
  859. */
  860. public void copy(RT toCopy) {
  861. LangUtil.throwIaxIfNull(toCopy, "parent");
  862. parentOptions.clear();
  863. parentOptions.addAll(toCopy.parentOptions);
  864. verbose = toCopy.verbose;
  865. }
  866. /**
  867. * Return any parent option accepted by validOptions,
  868. * optionally removing the parent option.
  869. * @param validOptions String[] of options to extract
  870. * @param remove if true, then remove any parent option matched
  871. * @return String[] containing any validOptions[i] in parentOptions
  872. *
  873. */
  874. public Values extractOptions(
  875. Options validOptions,
  876. boolean remove,
  877. StringBuffer errors) {
  878. Values result = Values.EMPTY;
  879. if (null == errors) {
  880. errors = new StringBuffer();
  881. }
  882. if (null == validOptions) {
  883. errors.append("null options");
  884. return result;
  885. }
  886. if (LangUtil.isEmpty(parentOptions)) {
  887. return result;
  888. }
  889. // boolean haveOption = false;
  890. String[] parents = (String[]) parentOptions.toArray(new String[0]);
  891. try {
  892. result = validOptions.acceptInput(parents);
  893. } catch (InvalidInputException e) {
  894. errors.append(e.getFullMessage());
  895. return result;
  896. }
  897. if (remove) {
  898. Option.Value[] values = result.asArray();
  899. for (int i = 0; i < values.length; i++) {
  900. Option.Value value = values[i];
  901. if (null == value) {
  902. continue;
  903. }
  904. final int max = i + value.option.numArguments();
  905. if (max > i) {
  906. if (max >= parents.length) {
  907. errors.append("expecting more args for "
  908. + value.option
  909. + " at ["
  910. + i
  911. + "]: "
  912. + Arrays.asList(parents));
  913. return result;
  914. }
  915. // XXX verify
  916. for (int j = i; j < max ; j++) {
  917. parentOptions.remove(parents[j]);
  918. }
  919. i = max-1;
  920. }
  921. }
  922. }
  923. return result;
  924. }
  925. /**
  926. * Return any parent option which has one of validOptions as a prefix,
  927. * optionally absorbing (removing) the parent option.
  928. * @param validOptions String[] of options to extract
  929. * @param absorb if true, then remove any parent option matched
  930. * @return String[] containing any validOptions[i] in parentOptions
  931. * (at most once)
  932. */
  933. public String[] extractOptions(String[] validOptions, boolean absorb) {
  934. if (LangUtil.isEmpty(validOptions) || LangUtil.isEmpty(parentOptions)) {
  935. return new String[0];
  936. }
  937. ArrayList result = new ArrayList();
  938. // boolean haveOption = false;
  939. for (int i = 0; i < validOptions.length; i++) {
  940. String option = validOptions[i];
  941. if (LangUtil.isEmpty(option)) {
  942. continue;
  943. }
  944. for (ListIterator iter = parentOptions.listIterator(); iter.hasNext();) {
  945. String parentOption = (String) iter.next();
  946. if (parentOption.startsWith(option)) {
  947. result.add(parentOption);
  948. if (absorb) {
  949. iter.remove();
  950. }
  951. }
  952. }
  953. }
  954. return (String[]) result.toArray(new String[0]);
  955. }
  956. /** Get ListIterator that permits removals */
  957. ListIterator getListIterator() {
  958. return parentOptions.listIterator();
  959. }
  960. /**
  961. * Enable verbose logging
  962. * @param verbose if true, do verbose logging
  963. */
  964. public void setVerbose(boolean verbose) {
  965. if (this.verbose != verbose) {
  966. this.verbose = verbose;
  967. }
  968. }
  969. } // class RT
  970. protected static class Fork {
  971. public static final Fork NOFORK = new Fork();
  972. private static final String DELIMITER = ",";
  973. private final boolean fork;
  974. private final String version;
  975. private final String java;
  976. private final File javaHome;
  977. private final String bootclasspath;
  978. private final String errs;
  979. private Fork() {
  980. this(null);
  981. }
  982. /**
  983. * @param spec a String null (no) or of the form
  984. * <code>1.[12345],{javaHome},{java},{bootclasspath}</code>
  985. * where {javaHome} is a readable directory and
  986. * a prefix of {java}.
  987. */
  988. private Fork(String spec) {
  989. if (null == spec) {
  990. fork = false;
  991. version = null;
  992. java = null;
  993. javaHome = null;
  994. bootclasspath = null;
  995. errs = null;
  996. return;
  997. }
  998. String inputVersion = null;
  999. File inputJavaHome = null;
  1000. String inputJava = null;
  1001. String inputBootclasspath = null;
  1002. //
  1003. // paths should be in system-specific form
  1004. final String EXPECT =
  1005. "{version=1.[12345],{java.home},{java.command},{bootclasspath}";
  1006. final String EXAMPLE =
  1007. "1.1,d:/jdk11,d:/jdk11/bin/java,d:/jdk11/lib/classes.zip";
  1008. if (LangUtil.isEmpty(spec)) {
  1009. spec = "";
  1010. }
  1011. StringBuffer inputErrs = new StringBuffer();
  1012. StringTokenizer st = new StringTokenizer(spec, DELIMITER);
  1013. if (4 != st.countTokens()) {
  1014. inputErrs.append(" expecting 4 tokens.");
  1015. } else {
  1016. inputVersion = st.nextToken().trim();
  1017. String inputJavaHomePath = st.nextToken().trim();
  1018. inputJava = st.nextToken().trim();
  1019. inputBootclasspath = st.nextToken().trim();
  1020. if (!inputVersion.startsWith("1.")
  1021. || (3 != inputVersion.length())
  1022. || ('1' > inputVersion.charAt(2))
  1023. || ('6' < inputVersion.charAt(2)) ) {
  1024. inputErrs.append(" expecting version 1.[12345]");
  1025. }
  1026. inputJavaHome = new File(inputJavaHomePath);
  1027. if (!inputJavaHome.canRead()
  1028. || !inputJavaHome.isDirectory()) {
  1029. inputErrs.append(" expecting java.home dir: "
  1030. + inputJavaHomePath);
  1031. }
  1032. if (!inputJava.startsWith(inputJavaHomePath)) {
  1033. inputErrs.append(" expecting java in java.home.dir: "
  1034. + inputJava);
  1035. }
  1036. if (LangUtil.isEmpty(inputBootclasspath)) {
  1037. inputBootclasspath = null;
  1038. }
  1039. }
  1040. String inputErrString = inputErrs.toString();
  1041. fork = (0 == inputErrString.length());
  1042. if (fork) {
  1043. errs = null;
  1044. } else {
  1045. errs = "bad fork specification. Expecting "
  1046. + EXPECT
  1047. + " - for example, "
  1048. + EXAMPLE
  1049. + ". Problems: "
  1050. + inputErrString;
  1051. }
  1052. version = inputVersion;
  1053. java = inputJava;
  1054. javaHome = inputJavaHome;
  1055. bootclasspath = inputBootclasspath;
  1056. }
  1057. public boolean fork() {
  1058. return fork;
  1059. }
  1060. public String getJavaExecutablePath() {
  1061. return java;
  1062. }
  1063. public File getJavaHome() {
  1064. return javaHome;
  1065. }
  1066. public String getJavaBootclasspath() {
  1067. return bootclasspath;
  1068. }
  1069. public String getErrors() {
  1070. return errs;
  1071. }
  1072. public String toSpecString() {
  1073. if (!fork) {
  1074. return null;
  1075. }
  1076. return version
  1077. + DELIMITER
  1078. + javaHome
  1079. + DELIMITER
  1080. + java
  1081. + DELIMITER
  1082. + bootclasspath;
  1083. }
  1084. public String toString() {
  1085. return (fork ? "Fork [true]" : "Fork [false]"); // XXX upgrade
  1086. }
  1087. } // class Fork
  1088. }