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.

AjcSpecXmlReader.java 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. /* *******************************************************************
  2. * Copyright (c) 1999-2001 Xerox Corporation,
  3. * 2002 Palo Alto Research Center, Incorporated (PARC),
  4. * 2003 Contributors.
  5. * All rights reserved.
  6. * This program and the accompanying materials are made available
  7. * under the terms of the Eclipse Public License v1.0
  8. * which accompanies this distribution and is available at
  9. * http://www.eclipse.org/legal/epl-v10.html
  10. *
  11. * Contributors:
  12. * Xerox/PARC initial implementation
  13. * Wes Isberg resolver
  14. * ******************************************************************/
  15. package org.aspectj.testing.xml;
  16. //import java.util.Vector;
  17. import java.io.File;
  18. import java.io.FileInputStream;
  19. import java.io.FileOutputStream;
  20. import java.io.FileWriter;
  21. import java.io.IOException;
  22. import java.io.PrintWriter;
  23. import org.apache.commons.digester.Digester;
  24. import org.aspectj.bridge.AbortException;
  25. import org.aspectj.bridge.IMessage;
  26. import org.aspectj.bridge.ISourceLocation;
  27. import org.aspectj.bridge.MessageUtil;
  28. import org.aspectj.bridge.SourceLocation;
  29. import org.aspectj.testing.harness.bridge.AjcTest;
  30. import org.aspectj.testing.harness.bridge.CompilerRun;
  31. import org.aspectj.testing.harness.bridge.DirChanges;
  32. import org.aspectj.testing.harness.bridge.IncCompilerRun;
  33. import org.aspectj.testing.harness.bridge.JavaRun;
  34. import org.aspectj.testing.harness.bridge.Sandbox;
  35. import org.aspectj.testing.harness.bridge.Validator;
  36. import org.aspectj.testing.util.RunUtils;
  37. import org.aspectj.util.LangUtil;
  38. import org.xml.sax.EntityResolver;
  39. import org.xml.sax.InputSource;
  40. import org.xml.sax.SAXException;
  41. /**
  42. * Read an ajc test specification in xml form.
  43. * Input files should comply with DOCTYPE
  44. */
  45. public class AjcSpecXmlReader {
  46. /*
  47. * To add new elements or attributes:
  48. * - update the DOCTYPE
  49. * - update setupDigester(..)
  50. * - new sub-elements should be created
  51. * - new attributes should have values set as bean properties
  52. * (possibly by mapping names)
  53. * - new sub-elements should be added to parents
  54. * => the parents need the add method - e.g., add{foo}({foo} toadd)
  55. * - add tests
  56. * - add compile-time checks for mapping APIs in
  57. * setupDigesterComipileTimeCheck
  58. * - when adding an attribute set by bean introspection,
  59. * add to the list returned by expectedProperties()
  60. * - update any client writers referring to the DOCTYPE, as necessary.
  61. * - the parent IXmlWriter should delegate to the child component
  62. * as IXmlWriter (or write the subelement itself)
  63. *
  64. * Debugging
  65. * - use logLevel = 2 for tracing
  66. * - common mistakes
  67. * - dtd has to match input
  68. * - no rule defined (or misdefined) so element ignored
  69. * - property read-only (?)
  70. */
  71. // private static final String EOL = "\n";
  72. /** presumed relative-path to dtd file for any XML files written by writeSuiteToXmlFile */
  73. public static final String DTD_PATH = "../tests/ajcTestSuite.dtd";
  74. /** expected doc type of AjcSpec XML files */
  75. public static final String DOCTYPE = "<!DOCTYPE "
  76. + AjcTest.Suite.Spec.XMLNAME + " SYSTEM \"" + DTD_PATH + "\">";
  77. private static final AjcSpecXmlReader ME
  78. = new AjcSpecXmlReader();
  79. /** @return shared instance */
  80. public static final AjcSpecXmlReader getReader() {
  81. return ME;
  82. }
  83. public static void main(String[] a) throws IOException {
  84. writeDTD(new File("../tests/ajcTestSuite2.dtd"));
  85. }
  86. /**
  87. * Write a DTD to dtdFile.
  88. * @deprecated
  89. * @param dtdFile the File to write to
  90. */
  91. public static void writeDTD(File dtdFile) throws IOException {
  92. LangUtil.throwIaxIfNull(dtdFile, "dtdFile");
  93. PrintWriter out = new PrintWriter(new FileWriter(dtdFile));
  94. try {
  95. out.println("<!-- document type for ajc test suite - see "
  96. + AjcSpecXmlReader.class.getName() + " -->");
  97. //out.println(getDocType());
  98. } finally {
  99. out.close();
  100. }
  101. }
  102. private static final String[] LOG = new String[] {"info", "debug", "trace" };
  103. // XXX logLevel n>0 causes JUnit tests to fail!
  104. private int logLevel = 0; // use 2 for tracing
  105. private AjcSpecXmlReader() {}
  106. /** @param level 0..2, info..trace */
  107. public void setLogLevel(int level) {
  108. if (level < 0) {
  109. level = 0;
  110. }
  111. if (level > 2) {
  112. level = 2;
  113. }
  114. logLevel = level;
  115. }
  116. /**
  117. * Print an IXmlWritable to the output file
  118. * with our leader and DOCTYPE.
  119. * @param output the File to write to - overwritten
  120. * @param tests the List of IXmlWritable to write
  121. * @return null if no warnings detected, warnings otherwise
  122. */
  123. public String writeSuiteToXmlFile(File output, IXmlWritable topNode) throws IOException {
  124. PrintWriter writer = new PrintWriter(new FileOutputStream(output));
  125. XMLWriter printSink = new XMLWriter(writer);
  126. writer.println("");
  127. writer.println(AjcSpecXmlReader.DOCTYPE);
  128. writer.println("");
  129. topNode.writeXml(printSink);
  130. writer.close();
  131. String parent = output.getParent();
  132. if (null == parent) {
  133. parent = ".";
  134. }
  135. String dtdPath = parent + "/" + DTD_PATH;
  136. File dtdFile = new File(dtdPath);
  137. if (!dtdFile.canRead()) {
  138. return "expecting dtd file: " + dtdFile.getPath();
  139. }
  140. return null;
  141. }
  142. /**
  143. * Read the specifications for a suite of AjcTest from an XML file.
  144. * This also sets the suite dir in the specification.
  145. * @param file the File must be readable, comply with DOCTYPE.
  146. * @return AjcTest.Suite.Spec read from file
  147. * @see setLogLevel(int)
  148. */
  149. public AjcTest.Suite.Spec readAjcSuite(File file) throws IOException, AbortException {
  150. // setup loggers for digester and beanutils...
  151. System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog"); // XXX
  152. System.setProperty("org.apache.commons.logging.simplelog.defaultlog", LOG[logLevel]); // trace debug XXX
  153. final Digester digester = makeDigester(file);
  154. SuiteHolder holder = new SuiteHolder();
  155. digester.push(holder);
  156. FileInputStream input = new FileInputStream(file);
  157. try {
  158. digester.parse(input);
  159. } catch (SAXException e) {
  160. MessageUtil.fail("parsing " + file, e);
  161. } finally {
  162. if (null != input) {
  163. input.close();
  164. input = null;
  165. }
  166. }
  167. AjcTest.Suite.Spec result = holder.spec;
  168. if (null != result) {
  169. file = file.getAbsoluteFile();
  170. result.setSourceLocation(new SourceLocation(file, 1));
  171. File suiteDir = file.getParentFile();
  172. if (null == suiteDir) {
  173. // should not be the case if absolute
  174. suiteDir = new File("."); // user.dir?
  175. }
  176. result.setSuiteDirFile(suiteDir);
  177. if (result.runtime.isVerbose()) { // XXX hack fixup
  178. RunUtils.enableVerbose(result);
  179. }
  180. }
  181. return result;
  182. }
  183. private Digester makeDigester(final File suiteFile) {
  184. // implement EntityResolver directly; set is failing
  185. Digester result = new Digester() {
  186. final SuiteResolver resolver = new SuiteResolver(suiteFile);
  187. public InputSource resolveEntity(
  188. String publicId,
  189. String systemId)
  190. throws SAXException {
  191. return resolver.resolveEntity(publicId, systemId);
  192. }
  193. };
  194. setupDigester(result);
  195. return result;
  196. }
  197. /** set up the mapping between the xml and Java. */
  198. private void setupDigester(Digester digester) {
  199. // XXX supply sax parser to ignore white space?
  200. digester.setValidating(true);
  201. // try {
  202. // // this is the correct approach, but the commons parser
  203. // // fails to accept a second, overriding registration - see
  204. // // http://lists.xml.org/archives/xml-dev/200111/msg00959.html
  205. // digester.getXMLReader().setEntityResolver(new SuiteResolver(suiteFile));
  206. // } catch (SAXException e) {
  207. // System.err.println("unable to set entity resolver");
  208. // e.printStackTrace(System.err);
  209. // }
  210. // element names come from the element components
  211. final String suiteX = AjcTest.Suite.Spec.XMLNAME;
  212. final String ajctestX = suiteX + "/" + AjcTest.Spec.XMLNAME;
  213. final String compileX = ajctestX + "/" + CompilerRun.Spec.XMLNAME;
  214. final String inccompileX = ajctestX + "/" + IncCompilerRun.Spec.XMLNAME;
  215. final String runX = ajctestX + "/" + JavaRun.Spec.XMLNAME;
  216. final String dirchangesX = "*/" + DirChanges.Spec.XMLNAME;
  217. final String messageX = "*/" + SoftMessage.XMLNAME;
  218. final String messageSrcLocX = messageX + "/" +SoftSourceLocation.XMLNAME;
  219. // ---- each sub-element needs to be created
  220. // handle messages the same at any level
  221. digester.addObjectCreate(suiteX, AjcTest.Suite.Spec.class.getName());
  222. digester.addObjectCreate(ajctestX, AjcTest.Spec.class.getName());
  223. digester.addObjectCreate(compileX, CompilerRun.Spec.class.getName());
  224. //digester.addObjectCreate(compileX + "/file", AbstractRunSpec.WrapFile.class.getName());
  225. digester.addObjectCreate(inccompileX, IncCompilerRun.Spec.class.getName());
  226. digester.addObjectCreate(runX, JavaRun.Spec.class.getName());
  227. digester.addObjectCreate(messageX, SoftMessage.class.getName());
  228. digester.addObjectCreate(messageSrcLocX, SoftSourceLocation.class.getName());
  229. digester.addObjectCreate(dirchangesX, DirChanges.Spec.class.getName());
  230. // ---- set bean properties for sub-elements created automatically
  231. // -- some remapped - warnings
  232. // - if property exists, map will not be used
  233. digester.addSetProperties(suiteX); // ok to have suite messages and global suite options, etc.
  234. digester.addSetProperties(ajctestX,
  235. new String[] { "title", "dir", "pr"},
  236. new String[] { "description", "testDirOffset", "bugId"});
  237. digester.addSetProperties(compileX,
  238. new String[] { "files", "argfiles"},
  239. new String[] { "paths", "argfiles"});
  240. digester.addSetProperties(compileX + "/file");
  241. digester.addSetProperties(inccompileX, "classes", "paths");
  242. digester.addSetProperties(runX,
  243. new String[] { "class", "vm", "skipTester", "fork", "vmargs", "aspectpath", "module"},
  244. new String[] { "className", "javaVersion", "skipTester", "fork", "vmArgs", "aspectpath", "module"});
  245. digester.addSetProperties(dirchangesX);
  246. digester.addSetProperties(messageX);
  247. digester.addSetProperties(messageSrcLocX, "line", "lineAsString");
  248. digester.addSetProperties(messageX, "kind", "kindAsString");
  249. digester.addSetProperties(messageX, "line", "lineAsString");
  250. //digester.addSetProperties(messageX, "details", "details");
  251. // only file subelement of compile uses text as path... XXX vestigial
  252. digester.addCallMethod(compileX + "/file", "setFile", 0);
  253. // ---- when subelements are created, add to parent
  254. // add ajctest to suite, runs to ajctest, files to compile, messages to any parent...
  255. // the method name (e.g., "addSuite") is in the parent (SuiteHolder)
  256. // the class (e.g., AjcTest.Suite.Spec) refers to the type of the object created
  257. digester.addSetNext(suiteX, "addSuite", AjcTest.Suite.Spec.class.getName());
  258. digester.addSetNext(ajctestX, "addChild", AjcTest.Spec.class.getName());
  259. digester.addSetNext(compileX, "addChild", CompilerRun.Spec.class.getName());
  260. digester.addSetNext(inccompileX, "addChild", IncCompilerRun.Spec.class.getName());
  261. digester.addSetNext(runX, "addChild", JavaRun.Spec.class.getName());
  262. //digester.addSetNext(compileX + "/file", "addWrapFile", AbstractRunSpec.WrapFile.class.getName());
  263. digester.addSetNext(messageX, "addMessage", IMessage.class.getName());
  264. // setSourceLocation is for the inline variant
  265. // addSourceLocation is for the extra
  266. digester.addSetNext(messageSrcLocX, "addSourceLocation", ISourceLocation.class.getName());
  267. digester.addSetNext(dirchangesX, "addDirChanges", DirChanges.Spec.class.getName());
  268. // can set parent, but prefer to have "knows-about" flow down only...
  269. }
  270. // ------------------------------------------------------------ testing code
  271. /**
  272. * Get expected bean properties for introspection tests.
  273. * This should return an expected property for every attribute in DOCTYPE,
  274. * using any mapped-to names from setupDigester.
  275. */
  276. static BProps[] expectedProperties() {
  277. return new BProps[]
  278. {
  279. new BProps(AjcTest.Suite.Spec.class,
  280. new String[] { "suiteDir"}), // verbose removed
  281. new BProps(AjcTest.Spec.class,
  282. new String[] { "description", "testDirOffset", "bugId"}),
  283. // mapped from { "title", "dir", "pr"}
  284. new BProps(CompilerRun.Spec.class,
  285. new String[] { "files", "options",
  286. "staging", "badInput", "reuseCompiler", "includeClassesDir",
  287. "argfiles", "aspectpath", "classpath", "extdirs",
  288. "sourceroots", "xlintfile", "outjar"}),
  289. new BProps(IncCompilerRun.Spec.class,
  290. new String[] { "tag" }),
  291. new BProps(JavaRun.Spec.class,
  292. new String[] { "className", "skipTester", "options",
  293. "javaVersion", "errStreamIsError", "outStreamIsError",
  294. "fork", "vmArgs", "aspectpath"}),
  295. // mapped from { "class", ...}
  296. new BProps(DirChanges.Spec.class,
  297. new String[] { "added", "removed", "updated", "unchanged", "dirToken", "defaultSuffix"}),
  298. // new BProps(AbstractRunSpec.WrapFile.class,
  299. // new String[] { "path"}),
  300. new BProps(SoftMessage.class,
  301. new String[] { "kindAsString", "lineAsString", "text", "details", "file"})
  302. // mapped from { "kind", "line", ...}
  303. };
  304. }
  305. /**
  306. * This is only to do compile-time checking for the APIs impliedly
  307. * used in setupDigester(..).
  308. * The property setter checks are redundant with tests based on
  309. * expectedProperties().
  310. */
  311. private static void setupDigesterCompileTimeCheck() {
  312. if (true) { throw new Error("never invoked"); }
  313. AjcTest.Suite.Spec suite = new AjcTest.Suite.Spec();
  314. AjcTest.Spec test = new AjcTest.Spec();
  315. Sandbox sandbox = null;
  316. Validator validator = null;
  317. // AjcTest ajctest = new AjcTest(test, sandbox, validator);
  318. // ajctest.addRunSpec((AbstractRunSpec) null);
  319. //// test.makeIncCompilerRun((IncCompilerRun.Spec) null);
  320. //// test.makeJavaRun((JavaRun.Spec) null);
  321. // ajctest.setDescription((String) null);
  322. // ajctest.setTestBaseDirOffset((String) null);
  323. // ajctest.setBugId((String) null);
  324. // ajctest.setTestSourceLocation((ISourceLocation) null);
  325. CompilerRun.Spec crunSpec = new CompilerRun.Spec();
  326. crunSpec.addMessage((IMessage) null);
  327. // XXX crunSpec.addSourceLocation((ISourceLocation) null);
  328. // crunSpec.addWrapFile((AbstractRunSpec.WrapFile) null);
  329. crunSpec.setOptions((String) null);
  330. crunSpec.setPaths((String) null);
  331. crunSpec.setIncludeClassesDir(false);
  332. crunSpec.setReuseCompiler(false);
  333. crunSpec.setXlintfile((String) null);
  334. crunSpec.setOutjar((String)null);
  335. IncCompilerRun.Spec icrunSpec = new IncCompilerRun.Spec();
  336. icrunSpec.addMessage((IMessage) null);
  337. icrunSpec.setTag((String) null);
  338. icrunSpec.setFresh(false);
  339. JavaRun.Spec jrunspec = new JavaRun.Spec();
  340. jrunspec.addMessage((IMessage) null);
  341. jrunspec.setClassName((String) null);
  342. jrunspec.addMessage((IMessage) null);
  343. // input s.b. interpretable by Boolean.valueOf(String)
  344. jrunspec.setSkipTester(true);
  345. jrunspec.setErrStreamIsError("false");
  346. jrunspec.setOutStreamIsError("false");
  347. jrunspec.setAspectpath("");
  348. jrunspec.setClasspath("");
  349. jrunspec.setFork(false);
  350. jrunspec.setLTW("false");
  351. jrunspec.setException("Error");
  352. DirChanges.Spec dcspec = new DirChanges.Spec();
  353. dcspec.setAdded((String) null);
  354. dcspec.setRemoved((String) null);
  355. dcspec.setUpdated((String) null);
  356. dcspec.setDefaultSuffix((String) null);
  357. dcspec.setDirToken((String) null);
  358. SoftMessage m = new SoftMessage();
  359. m.setSourceLocation((ISourceLocation) null);
  360. m.setText((String) null);
  361. m.setKindAsString((String) null);
  362. m.setDetails((String) null);
  363. SoftSourceLocation sl = new SoftSourceLocation();
  364. sl.setFile((String) null);
  365. sl.setLine((String) null);
  366. sl.setColumn((String) null);
  367. sl.setEndLine((String) null);
  368. // add attribute setters to validate?
  369. }
  370. /** top element on Digester stack holds the test suite */
  371. public static class SuiteHolder {
  372. AjcTest.Suite.Spec spec;
  373. public void addSuite(AjcTest.Suite.Spec spec) {
  374. this.spec = spec;
  375. }
  376. }
  377. /** hold class/properties association for testing */
  378. static class BProps {
  379. final Class cl;
  380. final String[] props;
  381. BProps(Class cl, String[] props) {
  382. this.cl = cl;
  383. this.props = props;
  384. }
  385. }
  386. /**
  387. * Find file NAME=="ajcTestSuite.dtd" from some reasonably-local
  388. * relative directories.
  389. * XXX bug: commons parser doesn't accept second registration,
  390. * so we override Digester's implementation instead.
  391. * XXX cannot JUnit test SuiteResolver since they run from
  392. * local directory with valid reference
  393. * XXX does not fix JDK 1.4 parser message "unable to resolve without base URI"
  394. * XXX should be able to just set BaseURI instead...
  395. */
  396. static class SuiteResolver implements EntityResolver {
  397. public static final String NAME = "ajcTestSuite.dtd";
  398. private static boolean isDir(File dir) {
  399. return ((null != dir) && dir.canRead() && dir.isDirectory());
  400. }
  401. private final File suiteFile;
  402. public SuiteResolver(File suiteFile) {
  403. this.suiteFile = suiteFile;
  404. }
  405. private String getPath(String id) {
  406. // first, try id relative to suite file
  407. final File suiteFileDir = suiteFile.getParentFile();
  408. if (isDir(suiteFileDir)) {
  409. String path = suiteFileDir.getPath()
  410. + "/" + id;
  411. File result = new File(path);
  412. if (result.canRead() && result.isFile()) {
  413. return result.getPath();
  414. }
  415. }
  416. // then try misc paths relative to suite file or current dir
  417. final File[] baseDirs = new File[]
  418. { suiteFileDir, new File(".")
  419. };
  420. final String[] locations = new String[]
  421. { ".", "..", "../tests", "../../tests",
  422. "../../../tests", "tests", "modules/tests"
  423. };
  424. File baseDir;
  425. for (int j = 0; j < baseDirs.length; j++) {
  426. baseDir = baseDirs[j];
  427. if (!isDir(baseDir)) {
  428. continue;
  429. }
  430. for (int i = 0; i < locations.length; i++) {
  431. File dir = new File(baseDir, locations[i]);
  432. if (isDir(dir)) {
  433. File temp = new File(dir, NAME);
  434. if (temp.isFile() && temp.canRead()) {
  435. return temp.getPath();
  436. }
  437. }
  438. }
  439. }
  440. return null;
  441. }
  442. public InputSource resolveEntity(
  443. String publicId,
  444. String systemId)
  445. throws SAXException {
  446. InputSource result = null;
  447. if ((null != systemId) &&
  448. systemId.endsWith(NAME)) {
  449. String path = getPath(systemId);
  450. if (null != path) {
  451. result = new InputSource(path);
  452. result.setSystemId(path);
  453. result.setPublicId(path);
  454. }
  455. }
  456. return result;
  457. }
  458. }
  459. }
  460. //private String getDocTypePath() {
  461. // String result = null;
  462. // if (null != suiteFile) {
  463. // FileReader fr = null;
  464. // try {
  465. // fr = new FileReader(suiteFile);
  466. // BufferedReader reader = new BufferedReader(fr);
  467. // String line;
  468. // while (null != (line = reader.readLine())) {
  469. // String upper = line.trim();
  470. // line = upper.toLowerCase();
  471. // if (line.startsWith("<")) {
  472. // if (line.startsWith("<!doctype ")) {
  473. // int start = line.indexOf("\"");
  474. // int end = line.lastIndexOf(NAME + "\"");
  475. // if ((0 < start) && (start < end)) {
  476. // return upper.substring(start+1,
  477. // end + NAME.length());
  478. // }
  479. // } else if (!line.startsWith("<?xml")) {
  480. // break; // something else...
  481. // }
  482. // }
  483. // }
  484. // } catch (IOException e) {
  485. // // ignore
  486. // } finally {
  487. // if (null != fr) {
  488. // try {
  489. // fr.close();
  490. // } catch (IOException e1) { // ignore
  491. // }
  492. // }
  493. // }
  494. // }
  495. // return null;
  496. //}