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 36KB

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