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

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