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.

SampleGatherer.java 37KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049
  1. /* *******************************************************************
  2. * Copyright (c) 2003 Contributors.
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Eclipse Public License v1.0
  6. * which accompanies this distribution and is available at
  7. * http://www.eclipse.org/legal/epl-v10.html
  8. *
  9. * Contributors:
  10. * Wes Isberg initial implementation
  11. * ******************************************************************/
  12. /*
  13. * A quickie hack to extract sample code from testable sources.
  14. * This could reuse a lot of code from elsewhere,
  15. * but currently doesn't,
  16. * to keep it in the build module which avoids dependencies.
  17. * (Too bad we can't use scripting languages...)
  18. */
  19. package org.aspectj.internal.tools.build;
  20. import java.io.*;
  21. import java.text.DateFormat;
  22. import java.util.*;
  23. /**
  24. * This gathers sample code delimited with [START..END]-SAMPLE
  25. * from source files under a base directory,
  26. * along with any <code>@author</code> info.
  27. * <pre>// START-SAMPLE {anchorName} {anchorText}
  28. * ... sample code ...
  29. * // END-SAMPLE {anchorName}
  30. * </pre>
  31. * where {anchorName} need not be unique and might be
  32. * hierarchical wrt "-", e.g., "genus-species-individual".
  33. */
  34. public class SampleGatherer {
  35. /** EOL String for gathered lines */
  36. public static final String EOL = "\n"; // XXX
  37. static final String START = "START-SAMPLE";
  38. static final String END = "END-SAMPLE";
  39. static final String AUTHOR = "@author";
  40. static final String FLAG = "XXX";
  41. // private static void test(String[] args){
  42. // String[] from = new String[] { "<pre>", "</pre>" };
  43. // String[] to = new String[] { "&lt;pre>", "&lt;/pre>" };
  44. // String source = "in this <pre> day and </pre> age of <pre and /pre>";
  45. // System.err.println("from " + source);
  46. // System.err.println(" to " + SampleUtil.replace(source, from, to));
  47. // source = "<pre> day and </pre>";
  48. // System.err.println("from " + source);
  49. // System.err.println(" to " + SampleUtil.replace(source, from, to));
  50. // source = "<pre day and </pre";
  51. // System.err.println("from " + source);
  52. // System.err.println(" to " + SampleUtil.replace(source, from, to));
  53. // source = "<pre> day and </pre> age";
  54. // System.err.println("from " + source);
  55. // System.err.println(" to " + SampleUtil.replace(source, from, to));
  56. // source = "in this <pre> day and </pre> age";
  57. // System.err.println("from " + source);
  58. // System.err.println(" to " + SampleUtil.replace(source, from, to));
  59. //
  60. // }
  61. /**
  62. * Emit samples gathered from any input args.
  63. * @param args the String[] of paths to files or directories to search
  64. * @throws IOException if unable to read a source file
  65. */
  66. public static void main(String[] args) throws IOException {
  67. if ((null == args) || (0 == args.length)) {
  68. String cname = SampleGatherer.class.getName();
  69. System.err.println("java " + cname + " [dir|file]");
  70. return;
  71. }
  72. Samples result = new Samples();
  73. for (int i = 0; i < args.length; i++) {
  74. result = gather(new File(args[i]), result);
  75. }
  76. StringBuffer sb = HTMLSamplesRenderer.ME.render(result, null);
  77. File out = new File("../docs/dist/doc/sample-code.html");
  78. FileOutputStream fos = new FileOutputStream(out);
  79. fos.write(sb.toString().getBytes());
  80. fos.close();
  81. System.out.println("see file:///" + out);
  82. }
  83. /**
  84. * Gather samples from a source file or directory
  85. * @param source the File file or directory to start with
  86. * @param sink the Samples collection to add to
  87. * @return sink or a new Samples collection with any samples found
  88. * @throws IOException if unable to read a source file
  89. */
  90. public static Samples gather(File source, Samples sink)
  91. throws IOException {
  92. if (null == sink) {
  93. sink = new Samples();
  94. }
  95. if (null == source) {
  96. source = new File(".");
  97. }
  98. doGather(source, sink);
  99. return sink;
  100. }
  101. private static String trimCommentEnd(String line, int start) {
  102. if (null == line) {
  103. return "";
  104. }
  105. if ((start > 0) && (start < line.length())) {
  106. line = line.substring(start);
  107. }
  108. line = line.trim();
  109. if (line.endsWith("*/")) {
  110. line = line.substring(0, line.length()-2).trim();
  111. } else if (line.endsWith("-->")) {
  112. line = line.substring(0, line.length()-3).trim();
  113. }
  114. return line;
  115. }
  116. private static void doGather(File source, Samples sink)
  117. throws IOException {
  118. if (source.isFile()) {
  119. if (isSource(source)) {
  120. gatherFromFile(source, sink);
  121. }
  122. } else if (source.isDirectory() && source.canRead()) {
  123. File[] files = source.listFiles();
  124. for (int i = 0; i < files.length; i++) {
  125. doGather(files[i], sink);
  126. }
  127. }
  128. }
  129. private static boolean isSource(File file) {
  130. if ((null == file) || !file.isFile() || !file.canRead()) {
  131. return false;
  132. }
  133. String path = file.getName().toLowerCase();
  134. String[] suffixes = Sample.Kind.SOURCE_SUFFIXES;
  135. for (int i = 0; i < suffixes.length; i++) {
  136. if (path.endsWith(suffixes[i])) {
  137. return true;
  138. }
  139. }
  140. return false;
  141. }
  142. private static void gatherFromFile(final File source, final Samples sink)
  143. throws IOException {
  144. Reader reader = null;
  145. try {
  146. String author = null;
  147. StringBuffer sampleCode = new StringBuffer();
  148. String anchorName = null;
  149. String anchorTitle = null;
  150. ArrayList flags = new ArrayList();
  151. int startLine = -1; // seeking
  152. int endLine = Integer.MAX_VALUE; // not seeking
  153. reader = new FileReader(source);
  154. LineNumberReader lineReader = new LineNumberReader(reader);
  155. String line;
  156. while (null != (line = lineReader.readLine())) { // XXX naive
  157. // found start?
  158. int loc = line.indexOf(START);
  159. if (-1 != loc) {
  160. int lineNumber = lineReader.getLineNumber();
  161. if (-1 != startLine) {
  162. abort("unexpected " + START, source, line, lineNumber);
  163. }
  164. startLine = lineNumber;
  165. endLine = -1;
  166. anchorName = trimCommentEnd(line, loc + START.length());
  167. loc = anchorName.indexOf(" ");
  168. if (-1 == loc) {
  169. anchorTitle = null;
  170. } else {
  171. anchorTitle = anchorName.substring(1+loc).trim();
  172. anchorName = anchorName.substring(0, loc);
  173. }
  174. continue;
  175. }
  176. // found end?
  177. loc = line.indexOf(END);
  178. if (-1 != loc) {
  179. int lineNumber = lineReader.getLineNumber();
  180. if (Integer.MAX_VALUE == endLine) {
  181. abort("unexpected " + END, source, line, lineNumber);
  182. }
  183. String newtag = trimCommentEnd(line, loc + END.length());
  184. if ((newtag.length() > 0) && !newtag.equals(anchorName)) {
  185. String m = "expected " + anchorName
  186. + " got " + newtag;
  187. abort(m, source, line, lineNumber);
  188. }
  189. endLine = lineNumber;
  190. Sample sample = new Sample(anchorName,
  191. anchorTitle,
  192. author,
  193. sampleCode.toString(),
  194. source,
  195. startLine,
  196. endLine,
  197. (String[]) flags.toArray(new String[flags.size()]));
  198. sink.addSample(sample);
  199. // back to seeking start
  200. sampleCode.setLength(0);
  201. startLine = -1;
  202. endLine = Integer.MAX_VALUE;
  203. continue;
  204. }
  205. // found author?
  206. loc = line.indexOf(AUTHOR);
  207. if (-1 != loc) {
  208. author = trimCommentEnd(line, loc + AUTHOR.length());
  209. }
  210. // found flag comment?
  211. loc = line.indexOf(FLAG);
  212. if (-1 != loc) {
  213. flags.add(trimCommentEnd(line, loc + FLAG.length()));
  214. }
  215. // reading?
  216. if ((-1 != startLine) && (-1 == endLine)) {
  217. sampleCode.append(line);
  218. sampleCode.append(EOL);
  219. }
  220. }
  221. if (-1 == endLine) {
  222. abort("incomplete sample", source, "", lineReader.getLineNumber());
  223. }
  224. } finally {
  225. if (null != reader) {
  226. reader.close();
  227. }
  228. }
  229. }
  230. private static void abort(String why, File file, String line, int lineNumber)
  231. throws Abort {
  232. throw new Abort(why + " at " + file + ":" + lineNumber + ": " + line);
  233. }
  234. // private static void delay(Object toDelay) {
  235. // synchronized (toDelay) { // XXX sleep instead?
  236. // toDelay.notifyAll();
  237. // }
  238. // }
  239. static class Abort extends IOException {
  240. private static final long serialVersionUID = -1l;
  241. Abort(String s) {
  242. super(s);
  243. }
  244. }
  245. }
  246. /**
  247. * Data associated with sample code - struct class.
  248. */
  249. class Sample {
  250. public static final String ASPECTJ_TEAM = "The AspectJ Team";
  251. /** sort by anchorName, file path, and start/end location */
  252. static Comparator<Sample> NAME_SOURCE_COMPARER = new Comparator<Sample>() {
  253. public int compare(Sample left, Sample right) {
  254. if (null == left) {
  255. return (null == right ? 0 : -1);
  256. }
  257. if (null == right) {
  258. return 1;
  259. }
  260. int result = left.anchorName.compareTo(right.anchorName);
  261. if (0 != result) {
  262. return result;
  263. }
  264. result = left.sourcePath.compareTo(right.sourcePath);
  265. if (0 != result) {
  266. return result;
  267. }
  268. result = right.startLine - left.startLine;
  269. if (0 != result) {
  270. return result;
  271. }
  272. return right.endLine - left.endLine;
  273. }
  274. };
  275. /** sort by author, then NAME_SOURCE_COMPARER */
  276. static Comparator<Sample> AUTHOR_NAME_SOURCE_COMPARER = new Comparator<Sample>() {
  277. public int compare(Sample left, Sample right) {
  278. if (null == left) {
  279. return (null == right ? 0 : -1);
  280. }
  281. if (null == right) {
  282. return 1;
  283. }
  284. int result = left.author.compareTo(right.author);
  285. if (0 != result) {
  286. return result;
  287. }
  288. return NAME_SOURCE_COMPARER.compare(left,right);
  289. }
  290. };
  291. final String anchorName;
  292. final String anchorTitle;
  293. final String author;
  294. final String sampleCode;
  295. final File sourcePath;
  296. final int startLine;
  297. final int endLine;
  298. final Kind kind;
  299. /** List of String flags found in the sample */
  300. final List flags;
  301. public Sample(
  302. String anchorName,
  303. String anchorTitle,
  304. String author,
  305. String sampleCode,
  306. File sourcePath,
  307. int startLine,
  308. int endLine,
  309. String[] flags) {
  310. this.anchorName = anchorName;
  311. this.anchorTitle = anchorTitle;
  312. this.author = (null != author ? author : ASPECTJ_TEAM);
  313. this.sampleCode = sampleCode;
  314. this.sourcePath = sourcePath;
  315. this.startLine = startLine;
  316. this.endLine = endLine;
  317. this.kind = Kind.getKind(sourcePath);
  318. // List theFlags;
  319. if ((null == flags) || (0 == flags.length)) {
  320. this.flags = Collections.EMPTY_LIST;
  321. } else {
  322. this.flags = Collections.unmodifiableList(Arrays.asList(flags));
  323. }
  324. }
  325. public String toString() {
  326. return sampleCode;
  327. }
  328. public static class Kind {
  329. /** lowercase source suffixes identify files to gather samples from */
  330. public static final String[] SOURCE_SUFFIXES = new String[]
  331. { ".java", ".aj", ".sh", ".ksh",
  332. ".txt", ".text", ".html", ".htm", ".xml" };
  333. static final Kind XML = new Kind();
  334. static final Kind HTML = new Kind();
  335. static final Kind PROGRAM = new Kind();
  336. static final Kind SCRIPT = new Kind();
  337. static final Kind TEXT = new Kind();
  338. static final Kind OTHER = new Kind();
  339. public static Kind getKind(File file) {
  340. if (null == file) {
  341. return OTHER;
  342. }
  343. String name = file.getName().toLowerCase();
  344. if ((name.endsWith(".java") || name.endsWith(".aj"))) {
  345. return PROGRAM;
  346. }
  347. if ((name.endsWith(".html") || name.endsWith(".htm"))) {
  348. return HTML;
  349. }
  350. if ((name.endsWith(".sh") || name.endsWith(".ksh"))) {
  351. return SCRIPT;
  352. }
  353. if ((name.endsWith(".txt") || name.endsWith(".text"))) {
  354. return TEXT;
  355. }
  356. if (name.endsWith(".xml")) {
  357. return XML;
  358. }
  359. return OTHER;
  360. }
  361. private Kind() {
  362. }
  363. }
  364. }
  365. /**
  366. * type-safe Collection of samples.
  367. */
  368. class Samples {
  369. private ArrayList<Sample> samples = new ArrayList<Sample>();
  370. int size() {
  371. return samples.size();
  372. }
  373. void addSample(Sample sample) {
  374. samples.add(sample);
  375. }
  376. /**
  377. * @return List copy, sorted by Sample.NAME_SOURCE_COMPARER
  378. */
  379. List<Sample> getSortedSamples() {
  380. return getSortedSamples(Sample.NAME_SOURCE_COMPARER);
  381. }
  382. List<Sample> getSortedSamples(Comparator<Sample> comparer) {
  383. ArrayList<Sample> result = new ArrayList<Sample>();
  384. result.addAll(samples);
  385. Collections.sort(result, comparer);
  386. return result;
  387. }
  388. }
  389. /**
  390. * Render samples by using method visitors.
  391. */
  392. class SamplesRenderer {
  393. public static SamplesRenderer ME = new SamplesRenderer();
  394. protected SamplesRenderer() {
  395. }
  396. public static final String EOL = "\n"; // XXX
  397. public static final String INFO =
  398. "<p>This contains contributions from the AspectJ community of "
  399. + "<ul><li>sample code for AspectJ programs,</li>"
  400. + "<li>sample code for extensions to AspectJ tools using the public API's,</li>"
  401. + "<li>sample scripts for invoking AspectJ tools, and </li> "
  402. + "<li>documentation trails showing how to do given tasks"
  403. + " using AspectJ, AJDT, or various IDE or deployment"
  404. + " environments.</li></ul></p>"
  405. + "<p>Find complete source files in the AspectJ CVS repository at "
  406. + "<code>org.aspectj/modules/docs/sandbox</code>. "
  407. + "For instructions on downloading code from the CVS repository, "
  408. + "see the <a href=\"doc/faq.html#q:buildingsource\">FAQ entry "
  409. + "\"buildingsource\"</a>.</p>";
  410. public static final String COPYRIGHT =
  411. "<p><small>Copyright 2003 Contributors. All Rights Reserved. "
  412. + "This sample code is made available under the Common Public " + "License version 1.0 available at "
  413. + "<a href=\"http://www.eclipse.org/legal/epl-v10.html\">"
  414. + "http://www.eclipse.org/legal/epl-v10.html</a>."
  415. + "Contributors are listed in this document as authors. "
  416. + "Permission to republish portions of this sample code "
  417. + "is hereby granted if the publication acknowledges "
  418. + "the author by name and "
  419. + "the source by reference to the AspectJ project home page "
  420. + " at http://eclipse.org/aspectj.</small></p>"
  421. + EOL;
  422. /** template algorithm to render */
  423. public final StringBuffer render(Samples samples, StringBuffer sink) {
  424. if (null == sink) {
  425. sink = new StringBuffer();
  426. }
  427. if ((null == samples) || (0 == samples.size())) {
  428. return sink;
  429. }
  430. startList(samples, sink);
  431. List list = samples.getSortedSamples();
  432. String anchorName = null;
  433. for (ListIterator iter = list.listIterator();
  434. iter.hasNext();) {
  435. Sample sample = (Sample) iter.next();
  436. String newAnchorName = sample.anchorName;
  437. if ((null == anchorName)
  438. || (!anchorName.equals(newAnchorName))) {
  439. endAnchorName(anchorName, sink);
  440. startAnchorName(newAnchorName, sample.anchorTitle, sink);
  441. anchorName = newAnchorName;
  442. }
  443. render(sample, sink);
  444. }
  445. endAnchorName(anchorName, sink);
  446. endList(samples, sink);
  447. return sink;
  448. }
  449. protected void startList(Samples samples, StringBuffer sink) {
  450. sink.append("Printing " + samples.size() + " samples");
  451. sink.append(EOL);
  452. }
  453. protected void startAnchorName(String name, String title, StringBuffer sink) {
  454. sink.append("anchor " + name);
  455. sink.append(EOL);
  456. }
  457. protected void render(Sample sample, StringBuffer sink) {
  458. SampleUtil.render(sample, "=", ", ",sink);
  459. sink.setLength(sink.length()-2);
  460. sink.append(EOL);
  461. }
  462. /**
  463. * @param name the String name being ended - ignore if null
  464. * @param sink
  465. */
  466. protected void endAnchorName(String name, StringBuffer sink) {
  467. if (null == name) {
  468. return;
  469. }
  470. }
  471. protected void endList(Samples samples, StringBuffer sink) {
  472. sink.append("Printed " + samples.size() + " samples");
  473. sink.append(EOL);
  474. }
  475. }
  476. // XXX need DocBookSamplesRenderer
  477. /**
  478. * Output the samples as a single HTML file, with a table of contents
  479. * and sorting the samples by their anchor tags.
  480. */
  481. class HTMLSamplesRenderer extends SamplesRenderer {
  482. public static SamplesRenderer ME = new HTMLSamplesRenderer();
  483. // XXX move these
  484. public static boolean doHierarchical = true;
  485. public static boolean doFlags = false;
  486. final StringBuffer tableOfContents;
  487. final StringBuffer sampleSection;
  488. String[] lastAnchor = new String[0];
  489. String currentAnchor;
  490. String currentAuthor;
  491. protected HTMLSamplesRenderer() {
  492. sampleSection = new StringBuffer();
  493. tableOfContents = new StringBuffer();
  494. }
  495. protected void startAnchorName(String name, String title, StringBuffer sink) {
  496. if (doHierarchical) {
  497. doContentTree(name);
  498. }
  499. // ---- now do anchor
  500. tableOfContents.append(" <li><a href=\"#" + name);
  501. if ((null == title) || (0 == title.length())) {
  502. title = name;
  503. }
  504. tableOfContents.append("\">" + title + "</a></li>");
  505. tableOfContents.append(EOL);
  506. currentAnchor = name;
  507. }
  508. protected void startList(Samples samples, StringBuffer sink) {
  509. }
  510. protected void render(Sample sample, StringBuffer sink) {
  511. if (null != currentAnchor) {
  512. if (!currentAnchor.equals(sample.anchorName)) {
  513. String m = "expected " + currentAnchor
  514. + " got " + sample.anchorName;
  515. throw new Error(m);
  516. }
  517. currentAnchor = null;
  518. }
  519. // do heading then code
  520. renderHeading(sample.anchorName, sample.anchorTitle, sampleSection);
  521. if (sample.kind == Sample.Kind.HTML) {
  522. renderHTML(sample);
  523. } else if (sample.kind == Sample.Kind.XML) {
  524. renderXML(sample);
  525. } else {
  526. renderPre(sample);
  527. }
  528. }
  529. protected boolean doRenderAuthor(Sample sample) {
  530. return (null != sample.author);
  531. // && !sample.author.equals(currentAuthor)
  532. }
  533. protected void renderStandardHeader(Sample sample) {
  534. // XXX starting same as pre
  535. if (doRenderAuthor(sample)) {
  536. currentAuthor = sample.author;
  537. sampleSection.append(" <p>| &nbsp; " + currentAuthor);
  538. sampleSection.append(EOL);
  539. }
  540. sampleSection.append(" &nbsp;|&nbsp; ");
  541. sampleSection.append(SampleUtil.renderCodePath(sample.sourcePath));
  542. sampleSection.append(":" + sample.startLine);
  543. sampleSection.append(" &nbsp;|");
  544. sampleSection.append(EOL);
  545. sampleSection.append("<p>");
  546. sampleSection.append(EOL);
  547. if (doFlags) {
  548. boolean flagHeaderDone = false;
  549. for (Iterator iter = sample.flags.iterator(); iter.hasNext();) {
  550. String flag = (String) iter.next();
  551. if (!flagHeaderDone) {
  552. sampleSection.append("<p>Comments flagged:<ul>");
  553. sampleSection.append(EOL);
  554. flagHeaderDone = true;
  555. }
  556. sampleSection.append("<li>");
  557. sampleSection.append(flag);
  558. sampleSection.append("</li>");
  559. }
  560. if (flagHeaderDone) {
  561. sampleSection.append("</ul>");
  562. sampleSection.append(EOL);
  563. }
  564. }
  565. }
  566. protected void renderXML(Sample sample) {
  567. renderStandardHeader(sample);
  568. sampleSection.append(" <pre>");
  569. sampleSection.append(EOL);
  570. sampleSection.append(prepareXMLSample(sample.sampleCode));
  571. sampleSection.append(EOL);
  572. sampleSection.append(" </pre>");
  573. sampleSection.append(EOL);
  574. }
  575. protected void renderHTML(Sample sample) {
  576. renderStandardHeader(sample);
  577. sampleSection.append(EOL);
  578. sampleSection.append(prepareHTMLSample(sample.sampleCode));
  579. sampleSection.append(EOL);
  580. }
  581. protected void renderPre(Sample sample) {
  582. renderStandardHeader(sample);
  583. sampleSection.append(" <pre>");
  584. sampleSection.append(EOL);
  585. sampleSection.append(prepareCodeSample(sample.sampleCode));
  586. sampleSection.append(" </pre>");
  587. sampleSection.append(EOL);
  588. }
  589. protected void endAnchorName(String name, StringBuffer sink) {
  590. if (null == name) {
  591. return;
  592. }
  593. currentAnchor = null;
  594. currentAuthor = null; // authors don't span anchors
  595. }
  596. protected void endList(Samples samples, StringBuffer sink) {
  597. sink.append("<html>");
  598. sink.append(EOL);
  599. sink.append("<title>AspectJ sample code</title>");
  600. sink.append(EOL);
  601. sink.append("<body>");
  602. sink.append(EOL);
  603. sink.append(" <a name=\"top\"></a>");
  604. sink.append(EOL);
  605. sink.append(" <h1>AspectJ sample code</h1>");
  606. sink.append(INFO);
  607. sink.append(EOL);
  608. sink.append(COPYRIGHT);
  609. sink.append(EOL);
  610. sink.append("<p><small>Generated on ");
  611. sink.append(DateFormat.getDateInstance().format(new Date()));
  612. sink.append(" by SamplesGatherer</small>");
  613. sink.append(EOL);
  614. sink.append(" <h2>Contents</h2>");
  615. sink.append(EOL);
  616. sink.append(" <ul>");
  617. sink.append(EOL);
  618. sink.append(tableOfContents.toString());
  619. // unwind to common prefix, if necessary
  620. for (int i = 0; i < lastAnchor.length ; i++) {
  621. sink.append(" </ul>");
  622. }
  623. sink.append(" <li><a href=\"#authorIndex\">Author Index</a></li>");
  624. sink.append(" </ul>");
  625. sink.append(" <h2>Listings</h2>");
  626. sink.append(EOL);
  627. sink.append(sampleSection.toString());
  628. renderAuthorIndex(samples, sink);
  629. sink.append("</body></html>");
  630. sink.append(EOL);
  631. }
  632. protected String prepareXMLSample(String sampleCode) {
  633. String[] from = new String[] {"\t", "<"};
  634. String[] to = new String[] {" ", "&lt;"};
  635. return (SampleUtil.replace(sampleCode, from, to));
  636. }
  637. protected String prepareHTMLSample(String sampleCode) {
  638. String[] from = new String[20];
  639. String[] to = new String[20];
  640. for (int i = 0; i < to.length; i++) {
  641. String h = "h" + i + ">";
  642. from[i] = "<" + h;
  643. to[i] = "<p><b>";
  644. from[++i] = "</" + h;
  645. to[i] = "</b></p><p>";
  646. }
  647. return (SampleUtil.replace(sampleCode, from, to));
  648. }
  649. protected String prepareCodeSample(String sampleCode) {
  650. String[] from = new String[] { "<pre>", "</pre>" };
  651. String[] to = new String[] { "&lt;pre>", "&lt;/pre>" };
  652. return (SampleUtil.replace(sampleCode, from, to));
  653. }
  654. protected void renderHeading(String anchor, String title, StringBuffer sink) {
  655. sink.append(" <a name=\"" + anchor + "\"></a>");
  656. sink.append(EOL);
  657. if ((null == title) || (0 == title.length())) {
  658. title = anchor;
  659. }
  660. sink.append(" <h3>" + title + "</h3>");
  661. sink.append(EOL);
  662. sink.append("<a href=\"#top\">back to top</a>");
  663. sink.append(EOL);
  664. }
  665. /**
  666. * Manage headings in both table of contents and listings.
  667. * @param name the String anchor
  668. */
  669. protected void doContentTree(String name) {
  670. if (name.equals(lastAnchor)) {
  671. return;
  672. }
  673. // ---- handle trees
  674. String[] parts = SampleUtil.splitAnchorName(name);
  675. //String[] lastAnchor = (String[]) lastAnchors.peek();
  676. int firstDiff = SampleUtil.commonPrefix(parts, lastAnchor);
  677. // unwind to common prefix, if necessary
  678. if (firstDiff+1 < lastAnchor.length) {
  679. for (int i = 1; i < lastAnchor.length-firstDiff ; i++) {
  680. tableOfContents.append(" </ul>");
  681. tableOfContents.append(EOL);
  682. }
  683. }
  684. // build up prefix
  685. StringBuffer branchAnchor = new StringBuffer();
  686. for (int i = 0; i < firstDiff;) {
  687. branchAnchor.append(parts[i]);
  688. i++;
  689. branchAnchor.append("-");
  690. }
  691. // emit leading headers, but not anchor itself
  692. for (int i = firstDiff; i < (parts.length-1); i++) {
  693. branchAnchor.append(parts[i]);
  694. String prefixName = branchAnchor.toString();
  695. branchAnchor.append("-");
  696. tableOfContents.append(" <li><a href=\"#");
  697. tableOfContents.append(prefixName);
  698. tableOfContents.append("\">" + prefixName + "</a></li>");
  699. tableOfContents.append(EOL);
  700. tableOfContents.append(" <ul>");
  701. tableOfContents.append(EOL);
  702. renderHeading(prefixName, prefixName, sampleSection);
  703. }
  704. lastAnchor = parts;
  705. }
  706. protected void renderAuthorIndex(Samples samples, StringBuffer sink) {
  707. sink.append("<h2><a name=\"authorIndex\"></a>Author Index</h2>");
  708. List list = samples.getSortedSamples(Sample.AUTHOR_NAME_SOURCE_COMPARER);
  709. String lastAuthor = null;
  710. for (ListIterator iter = list.listIterator(); iter.hasNext();) {
  711. Sample sample = (Sample)iter.next();
  712. String author = sample.author;
  713. if (!author.equals(lastAuthor)) {
  714. if (null != lastAuthor) {
  715. sink.append("</li></ul>");
  716. }
  717. sink.append("<li>");
  718. sink.append(author);
  719. sink.append(EOL);
  720. sink.append("<ul>");
  721. sink.append(EOL);
  722. lastAuthor = author;
  723. }
  724. sink.append(" <li><a href=\"#");
  725. sink.append(sample.anchorName);
  726. sink.append("\">");
  727. if (null == sample.anchorTitle) {
  728. sink.append(sample.anchorName);
  729. } else {
  730. sink.append(sample.anchorTitle);
  731. }
  732. sink.append("</a></li>");
  733. }
  734. }
  735. }
  736. class SampleUtil {
  737. public static final String SAMPLE_BASE_DIR_NAME = "sandbox";
  738. public static void simpleRender(Samples result, StringBuffer sink) {
  739. List sortedSamples = result.getSortedSamples();
  740. int i = 0;
  741. for (ListIterator iter = sortedSamples.listIterator();
  742. iter.hasNext();) {
  743. Sample sample = (Sample) iter.next();
  744. sink.append(i++ + ": " + sample);
  745. }
  746. }
  747. /** result struct for getPackagePath */
  748. static class JavaFile {
  749. /** input File possibly signifying a java file */
  750. final File path;
  751. /** String java path suffix in form "com/company/Bar.java"
  752. * null if this is not a java file
  753. */
  754. final String javaPath;
  755. /** any prefix before java path suffix in the original path */
  756. final String prefix;
  757. /** error handling */
  758. final Throwable thrown;
  759. JavaFile(File path, String javaPath, String prefix, Throwable thrown) {
  760. this.path = path;
  761. this.javaPath = javaPath;
  762. this.prefix = prefix;
  763. this.thrown = thrown;
  764. }
  765. }
  766. /**
  767. * Read any package statement in the file to determine
  768. * the package path of the file
  769. * @param path the File to seek the package in
  770. * @return the JavaFile with the components of the path
  771. */
  772. public static JavaFile getJavaFile(File path) {
  773. if (null == path) {
  774. throw new IllegalArgumentException("null path");
  775. }
  776. String result = path.getPath().replace('\\', '/');
  777. String packag = "";
  778. String javaPath = null;
  779. String prefix = null;
  780. Throwable thrown = null;
  781. if (result.endsWith(".java") || result.endsWith(".aj")) {
  782. FileReader reader = null;
  783. try {
  784. reader = new FileReader(path);
  785. BufferedReader br = new BufferedReader(reader);
  786. String line;
  787. while (null != (line = br.readLine())) {
  788. int loc = line.indexOf("package");
  789. if (-1 != loc) {
  790. int end = line.indexOf(";");
  791. if (-1 == loc) {
  792. String m = "unterminated package statement \"";
  793. throw new Error(m + line + "\" in " + path);
  794. }
  795. packag = (line.substring(loc + 7, end) + ".")
  796. .trim()
  797. .replace('.', '/');
  798. break;
  799. }
  800. loc = line.indexOf("import");
  801. if (-1 != loc) {
  802. break;
  803. }
  804. }
  805. } catch (IOException e) {
  806. thrown = e;
  807. } finally {
  808. if (null != reader) {
  809. try {
  810. reader.close();
  811. } catch (IOException e1) {
  812. // ignore
  813. }
  814. }
  815. }
  816. if (null == thrown) {
  817. javaPath = packag + path.getName();
  818. int loc = result.indexOf(javaPath);
  819. if (-1 == loc) {
  820. String m = "expected suffix " + javaPath + " in ";
  821. throw new Error(m + result);
  822. }
  823. prefix = result.substring(0, loc);
  824. }
  825. }
  826. return new JavaFile(path, javaPath, prefix, thrown);
  827. }
  828. /**
  829. * Extract file path relative to base of package directory
  830. * and directory in SAMPLE_BASE_DIR_NAME for this file.
  831. * @param path the File to render from SAMPLE_BASE_DIR_NAME
  832. * @return String "baseDir {path}"
  833. */
  834. public static String renderCodePath(File path) {
  835. JavaFile javaFile = getJavaFile(path);
  836. if (javaFile.thrown != null) {
  837. throw new Error(javaFile.thrown.getClass()
  838. + ": " + javaFile.thrown.getMessage());
  839. }
  840. String file = javaFile.javaPath; // can be null...
  841. String prefix = javaFile.prefix;
  842. if (prefix == null) {
  843. prefix = path.getPath().replace('\\', '/');
  844. }
  845. int loc = prefix.lastIndexOf(SAMPLE_BASE_DIR_NAME);
  846. if (-1 == loc) {
  847. String m = "not after " + SAMPLE_BASE_DIR_NAME;
  848. throw new IllegalArgumentException(m + "?: " + path);
  849. }
  850. prefix = prefix.substring(loc + 1 + SAMPLE_BASE_DIR_NAME.length());
  851. if (file == null) {
  852. int slash = prefix.lastIndexOf('/');
  853. if (-1 == slash) {
  854. file = prefix;
  855. prefix = "";
  856. } else {
  857. file = prefix.substring(slash+1);
  858. prefix = prefix.substring(0, slash);
  859. }
  860. }
  861. if (prefix.endsWith("/")) {
  862. prefix = prefix.substring(0, prefix.length()-1);
  863. }
  864. return (prefix + " " + file).trim();
  865. }
  866. public static int commonPrefix(String[] lhs, String[] rhs) {
  867. final int max = smallerSize(lhs, rhs);
  868. int firstDiff = 0;
  869. while (firstDiff < max) {
  870. if (!lhs[firstDiff].equals(rhs[firstDiff])) {
  871. break;
  872. }
  873. firstDiff++;
  874. }
  875. return firstDiff;
  876. }
  877. private static int smallerSize(Object[] one, Object[] two) {
  878. if ((null == one) || (null == two)) {
  879. return 0;
  880. }
  881. return (one.length > two.length ? two.length : one.length);
  882. }
  883. public static String[] splitAnchorName(Sample sample) {
  884. return splitAnchorName(sample.anchorName);
  885. }
  886. public static String[] splitAnchorName(String anchorName) {
  887. ArrayList<String> result = new ArrayList<String>();
  888. int start = 0;
  889. int loc = anchorName.indexOf("-", start);
  890. String next;
  891. while (loc != -1) {
  892. next = anchorName.substring(start, loc);
  893. result.add(next);
  894. start = loc+1;
  895. loc = anchorName.indexOf("-", start);
  896. }
  897. next = anchorName.substring(start);
  898. result.add(next);
  899. return (String[]) result.toArray(new String[result.size()]);
  900. }
  901. /**
  902. * Replace literals with literals in source string
  903. * @param source the String to modify
  904. * @param from the String[] of literals to replace
  905. * @param to the String[] of literals to use when replacing
  906. * @return the String source as modified by the replaces
  907. */
  908. public static String replace(String source, String[] from, String[] to) {
  909. if ((null == source) || (0 == source.length())) {
  910. return source;
  911. }
  912. if (from.length != to.length) {
  913. throw new IllegalArgumentException("unmatched from/to");
  914. }
  915. StringBuffer result = new StringBuffer();
  916. int LEN = source.length();
  917. int start = 0;
  918. for (int i = 0; i < LEN; i++) {
  919. String suffix = source.substring(i);
  920. for (int j = 0; j < from.length; j++) {
  921. if (suffix.startsWith(from[j])) {
  922. result.append(source.substring(start, i));
  923. result.append(to[j]);
  924. start = i + from[j].length();
  925. i = start-1;
  926. break;
  927. }
  928. }
  929. }
  930. if (start < source.length()) {
  931. result.append(source.substring(start));
  932. }
  933. return result.toString();
  934. }
  935. public static void render(
  936. Sample sample,
  937. String fieldDelim,
  938. String valueDelim,
  939. StringBuffer sink) {
  940. if ((null == sink) || (null == sample)) {
  941. return;
  942. }
  943. if (null == fieldDelim) {
  944. fieldDelim = "";
  945. }
  946. if (null == valueDelim) {
  947. valueDelim = "";
  948. }
  949. sink.append("anchorName");
  950. sink.append(valueDelim);
  951. sink.append(sample.anchorName);
  952. sink.append(fieldDelim);
  953. sink.append("author");
  954. sink.append(valueDelim);
  955. sink.append(sample.author);
  956. sink.append(fieldDelim);
  957. sink.append("sourcePath");
  958. sink.append(valueDelim);
  959. sink.append(sample.sourcePath.toString());
  960. sink.append(fieldDelim);
  961. sink.append("startLine");
  962. sink.append(valueDelim);
  963. sink.append(sample.startLine);
  964. sink.append(fieldDelim);
  965. sink.append("endLine");
  966. sink.append(valueDelim);
  967. sink.append(sample.endLine);
  968. sink.append(fieldDelim);
  969. sink.append("sampleCode");
  970. sink.append(valueDelim);
  971. sink.append(sample.sampleCode.toString());
  972. sink.append(fieldDelim);
  973. }
  974. private SampleUtil(){}
  975. }