您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

SampleGatherer.java 37KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053
  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 NAME_SOURCE_COMPARER = new Comparator() {
  253. public int compare(Object lhs, Object rhs) {
  254. Sample left = (Sample) lhs;
  255. Sample right = (Sample) rhs;
  256. if (null == left) {
  257. return (null == right ? 0 : -1);
  258. }
  259. if (null == right) {
  260. return 1;
  261. }
  262. int result = left.anchorName.compareTo(right.anchorName);
  263. if (0 != result) {
  264. return result;
  265. }
  266. result = left.sourcePath.compareTo(right.sourcePath);
  267. if (0 != result) {
  268. return result;
  269. }
  270. result = right.startLine - left.startLine;
  271. if (0 != result) {
  272. return result;
  273. }
  274. return right.endLine - left.endLine;
  275. }
  276. };
  277. /** sort by author, then NAME_SOURCE_COMPARER */
  278. static Comparator AUTHOR_NAME_SOURCE_COMPARER = new Comparator() {
  279. public int compare(Object lhs, Object rhs) {
  280. Sample left = (Sample) lhs;
  281. Sample right = (Sample) rhs;
  282. if (null == left) {
  283. return (null == right ? 0 : -1);
  284. }
  285. if (null == right) {
  286. return 1;
  287. }
  288. int result = left.author.compareTo(right.author);
  289. if (0 != result) {
  290. return result;
  291. }
  292. return NAME_SOURCE_COMPARER.compare(lhs, rhs);
  293. }
  294. };
  295. final String anchorName;
  296. final String anchorTitle;
  297. final String author;
  298. final String sampleCode;
  299. final File sourcePath;
  300. final int startLine;
  301. final int endLine;
  302. final Kind kind;
  303. /** List of String flags found in the sample */
  304. final List flags;
  305. public Sample(
  306. String anchorName,
  307. String anchorTitle,
  308. String author,
  309. String sampleCode,
  310. File sourcePath,
  311. int startLine,
  312. int endLine,
  313. String[] flags) {
  314. this.anchorName = anchorName;
  315. this.anchorTitle = anchorTitle;
  316. this.author = (null != author ? author : ASPECTJ_TEAM);
  317. this.sampleCode = sampleCode;
  318. this.sourcePath = sourcePath;
  319. this.startLine = startLine;
  320. this.endLine = endLine;
  321. this.kind = Kind.getKind(sourcePath);
  322. // List theFlags;
  323. if ((null == flags) || (0 == flags.length)) {
  324. this.flags = Collections.EMPTY_LIST;
  325. } else {
  326. this.flags = Collections.unmodifiableList(Arrays.asList(flags));
  327. }
  328. }
  329. public String toString() {
  330. return sampleCode;
  331. }
  332. public static class Kind {
  333. /** lowercase source suffixes identify files to gather samples from */
  334. public static final String[] SOURCE_SUFFIXES = new String[]
  335. { ".java", ".aj", ".sh", ".ksh",
  336. ".txt", ".text", ".html", ".htm", ".xml" };
  337. static final Kind XML = new Kind();
  338. static final Kind HTML = new Kind();
  339. static final Kind PROGRAM = new Kind();
  340. static final Kind SCRIPT = new Kind();
  341. static final Kind TEXT = new Kind();
  342. static final Kind OTHER = new Kind();
  343. public static Kind getKind(File file) {
  344. if (null == file) {
  345. return OTHER;
  346. }
  347. String name = file.getName().toLowerCase();
  348. if ((name.endsWith(".java") || name.endsWith(".aj"))) {
  349. return PROGRAM;
  350. }
  351. if ((name.endsWith(".html") || name.endsWith(".htm"))) {
  352. return HTML;
  353. }
  354. if ((name.endsWith(".sh") || name.endsWith(".ksh"))) {
  355. return SCRIPT;
  356. }
  357. if ((name.endsWith(".txt") || name.endsWith(".text"))) {
  358. return TEXT;
  359. }
  360. if (name.endsWith(".xml")) {
  361. return XML;
  362. }
  363. return OTHER;
  364. }
  365. private Kind() {
  366. }
  367. }
  368. }
  369. /**
  370. * type-safe Collection of samples.
  371. */
  372. class Samples {
  373. private ArrayList samples = new ArrayList();
  374. int size() {
  375. return samples.size();
  376. }
  377. void addSample(Sample sample) {
  378. samples.add(sample);
  379. }
  380. /**
  381. * @return List copy, sorted by Sample.NAME_SOURCE_COMPARER
  382. */
  383. List getSortedSamples() {
  384. return getSortedSamples(Sample.NAME_SOURCE_COMPARER);
  385. }
  386. List getSortedSamples(Comparator comparer) {
  387. ArrayList result = new ArrayList();
  388. result.addAll(samples);
  389. Collections.sort(result, comparer);
  390. return result;
  391. }
  392. }
  393. /**
  394. * Render samples by using method visitors.
  395. */
  396. class SamplesRenderer {
  397. public static SamplesRenderer ME = new SamplesRenderer();
  398. protected SamplesRenderer() {
  399. }
  400. public static final String EOL = "\n"; // XXX
  401. public static final String INFO =
  402. "<p>This contains contributions from the AspectJ community of "
  403. + "<ul><li>sample code for AspectJ programs,</li>"
  404. + "<li>sample code for extensions to AspectJ tools using the public API's,</li>"
  405. + "<li>sample scripts for invoking AspectJ tools, and </li> "
  406. + "<li>documentation trails showing how to do given tasks"
  407. + " using AspectJ, AJDT, or various IDE or deployment"
  408. + " environments.</li></ul></p>"
  409. + "<p>Find complete source files in the AspectJ CVS repository at "
  410. + "<code>org.aspectj/modules/docs/sandbox</code>. "
  411. + "For instructions on downloading code from the CVS repository, "
  412. + "see the <a href=\"doc/faq.html#q:buildingsource\">FAQ entry "
  413. + "\"buildingsource\"</a>.</p>";
  414. public static final String COPYRIGHT =
  415. "<p><small>Copyright 2003 Contributors. All Rights Reserved. "
  416. + "This sample code is made available under the Common Public " + "License version 1.0 available at "
  417. + "<a href=\"http://www.eclipse.org/legal/epl-v10.html\">"
  418. + "http://www.eclipse.org/legal/epl-v10.html</a>."
  419. + "Contributors are listed in this document as authors. "
  420. + "Permission to republish portions of this sample code "
  421. + "is hereby granted if the publication acknowledges "
  422. + "the author by name and "
  423. + "the source by reference to the AspectJ project home page "
  424. + " at http://eclipse.org/aspectj.</small></p>"
  425. + EOL;
  426. /** template algorithm to render */
  427. public final StringBuffer render(Samples samples, StringBuffer sink) {
  428. if (null == sink) {
  429. sink = new StringBuffer();
  430. }
  431. if ((null == samples) || (0 == samples.size())) {
  432. return sink;
  433. }
  434. startList(samples, sink);
  435. List list = samples.getSortedSamples();
  436. String anchorName = null;
  437. for (ListIterator iter = list.listIterator();
  438. iter.hasNext();) {
  439. Sample sample = (Sample) iter.next();
  440. String newAnchorName = sample.anchorName;
  441. if ((null == anchorName)
  442. || (!anchorName.equals(newAnchorName))) {
  443. endAnchorName(anchorName, sink);
  444. startAnchorName(newAnchorName, sample.anchorTitle, sink);
  445. anchorName = newAnchorName;
  446. }
  447. render(sample, sink);
  448. }
  449. endAnchorName(anchorName, sink);
  450. endList(samples, sink);
  451. return sink;
  452. }
  453. protected void startList(Samples samples, StringBuffer sink) {
  454. sink.append("Printing " + samples.size() + " samples");
  455. sink.append(EOL);
  456. }
  457. protected void startAnchorName(String name, String title, StringBuffer sink) {
  458. sink.append("anchor " + name);
  459. sink.append(EOL);
  460. }
  461. protected void render(Sample sample, StringBuffer sink) {
  462. SampleUtil.render(sample, "=", ", ",sink);
  463. sink.setLength(sink.length()-2);
  464. sink.append(EOL);
  465. }
  466. /**
  467. * @param name the String name being ended - ignore if null
  468. * @param sink
  469. */
  470. protected void endAnchorName(String name, StringBuffer sink) {
  471. if (null == name) {
  472. return;
  473. }
  474. }
  475. protected void endList(Samples samples, StringBuffer sink) {
  476. sink.append("Printed " + samples.size() + " samples");
  477. sink.append(EOL);
  478. }
  479. }
  480. // XXX need DocBookSamplesRenderer
  481. /**
  482. * Output the samples as a single HTML file, with a table of contents
  483. * and sorting the samples by their anchor tags.
  484. */
  485. class HTMLSamplesRenderer extends SamplesRenderer {
  486. public static SamplesRenderer ME = new HTMLSamplesRenderer();
  487. // XXX move these
  488. public static boolean doHierarchical = true;
  489. public static boolean doFlags = false;
  490. final StringBuffer tableOfContents;
  491. final StringBuffer sampleSection;
  492. String[] lastAnchor = new String[0];
  493. String currentAnchor;
  494. String currentAuthor;
  495. protected HTMLSamplesRenderer() {
  496. sampleSection = new StringBuffer();
  497. tableOfContents = new StringBuffer();
  498. }
  499. protected void startAnchorName(String name, String title, StringBuffer sink) {
  500. if (doHierarchical) {
  501. doContentTree(name);
  502. }
  503. // ---- now do anchor
  504. tableOfContents.append(" <li><a href=\"#" + name);
  505. if ((null == title) || (0 == title.length())) {
  506. title = name;
  507. }
  508. tableOfContents.append("\">" + title + "</a></li>");
  509. tableOfContents.append(EOL);
  510. currentAnchor = name;
  511. }
  512. protected void startList(Samples samples, StringBuffer sink) {
  513. }
  514. protected void render(Sample sample, StringBuffer sink) {
  515. if (null != currentAnchor) {
  516. if (!currentAnchor.equals(sample.anchorName)) {
  517. String m = "expected " + currentAnchor
  518. + " got " + sample.anchorName;
  519. throw new Error(m);
  520. }
  521. currentAnchor = null;
  522. }
  523. // do heading then code
  524. renderHeading(sample.anchorName, sample.anchorTitle, sampleSection);
  525. if (sample.kind == Sample.Kind.HTML) {
  526. renderHTML(sample);
  527. } else if (sample.kind == Sample.Kind.XML) {
  528. renderXML(sample);
  529. } else {
  530. renderPre(sample);
  531. }
  532. }
  533. protected boolean doRenderAuthor(Sample sample) {
  534. return (null != sample.author);
  535. // && !sample.author.equals(currentAuthor)
  536. }
  537. protected void renderStandardHeader(Sample sample) {
  538. // XXX starting same as pre
  539. if (doRenderAuthor(sample)) {
  540. currentAuthor = sample.author;
  541. sampleSection.append(" <p>| &nbsp; " + currentAuthor);
  542. sampleSection.append(EOL);
  543. }
  544. sampleSection.append(" &nbsp;|&nbsp; ");
  545. sampleSection.append(SampleUtil.renderCodePath(sample.sourcePath));
  546. sampleSection.append(":" + sample.startLine);
  547. sampleSection.append(" &nbsp;|");
  548. sampleSection.append(EOL);
  549. sampleSection.append("<p>");
  550. sampleSection.append(EOL);
  551. if (doFlags) {
  552. boolean flagHeaderDone = false;
  553. for (Iterator iter = sample.flags.iterator(); iter.hasNext();) {
  554. String flag = (String) iter.next();
  555. if (!flagHeaderDone) {
  556. sampleSection.append("<p>Comments flagged:<ul>");
  557. sampleSection.append(EOL);
  558. flagHeaderDone = true;
  559. }
  560. sampleSection.append("<li>");
  561. sampleSection.append(flag);
  562. sampleSection.append("</li>");
  563. }
  564. if (flagHeaderDone) {
  565. sampleSection.append("</ul>");
  566. sampleSection.append(EOL);
  567. }
  568. }
  569. }
  570. protected void renderXML(Sample sample) {
  571. renderStandardHeader(sample);
  572. sampleSection.append(" <pre>");
  573. sampleSection.append(EOL);
  574. sampleSection.append(prepareXMLSample(sample.sampleCode));
  575. sampleSection.append(EOL);
  576. sampleSection.append(" </pre>");
  577. sampleSection.append(EOL);
  578. }
  579. protected void renderHTML(Sample sample) {
  580. renderStandardHeader(sample);
  581. sampleSection.append(EOL);
  582. sampleSection.append(prepareHTMLSample(sample.sampleCode));
  583. sampleSection.append(EOL);
  584. }
  585. protected void renderPre(Sample sample) {
  586. renderStandardHeader(sample);
  587. sampleSection.append(" <pre>");
  588. sampleSection.append(EOL);
  589. sampleSection.append(prepareCodeSample(sample.sampleCode));
  590. sampleSection.append(" </pre>");
  591. sampleSection.append(EOL);
  592. }
  593. protected void endAnchorName(String name, StringBuffer sink) {
  594. if (null == name) {
  595. return;
  596. }
  597. currentAnchor = null;
  598. currentAuthor = null; // authors don't span anchors
  599. }
  600. protected void endList(Samples samples, StringBuffer sink) {
  601. sink.append("<html>");
  602. sink.append(EOL);
  603. sink.append("<title>AspectJ sample code</title>");
  604. sink.append(EOL);
  605. sink.append("<body>");
  606. sink.append(EOL);
  607. sink.append(" <a name=\"top\"></a>");
  608. sink.append(EOL);
  609. sink.append(" <h1>AspectJ sample code</h1>");
  610. sink.append(INFO);
  611. sink.append(EOL);
  612. sink.append(COPYRIGHT);
  613. sink.append(EOL);
  614. sink.append("<p><small>Generated on ");
  615. sink.append(DateFormat.getDateInstance().format(new Date()));
  616. sink.append(" by SamplesGatherer</small>");
  617. sink.append(EOL);
  618. sink.append(" <h2>Contents</h2>");
  619. sink.append(EOL);
  620. sink.append(" <ul>");
  621. sink.append(EOL);
  622. sink.append(tableOfContents.toString());
  623. // unwind to common prefix, if necessary
  624. for (int i = 0; i < lastAnchor.length ; i++) {
  625. sink.append(" </ul>");
  626. }
  627. sink.append(" <li><a href=\"#authorIndex\">Author Index</a></li>");
  628. sink.append(" </ul>");
  629. sink.append(" <h2>Listings</h2>");
  630. sink.append(EOL);
  631. sink.append(sampleSection.toString());
  632. renderAuthorIndex(samples, sink);
  633. sink.append("</body></html>");
  634. sink.append(EOL);
  635. }
  636. protected String prepareXMLSample(String sampleCode) {
  637. String[] from = new String[] {"\t", "<"};
  638. String[] to = new String[] {" ", "&lt;"};
  639. return (SampleUtil.replace(sampleCode, from, to));
  640. }
  641. protected String prepareHTMLSample(String sampleCode) {
  642. String[] from = new String[20];
  643. String[] to = new String[20];
  644. for (int i = 0; i < to.length; i++) {
  645. String h = "h" + i + ">";
  646. from[i] = "<" + h;
  647. to[i] = "<p><b>";
  648. from[++i] = "</" + h;
  649. to[i] = "</b></p><p>";
  650. }
  651. return (SampleUtil.replace(sampleCode, from, to));
  652. }
  653. protected String prepareCodeSample(String sampleCode) {
  654. String[] from = new String[] { "<pre>", "</pre>" };
  655. String[] to = new String[] { "&lt;pre>", "&lt;/pre>" };
  656. return (SampleUtil.replace(sampleCode, from, to));
  657. }
  658. protected void renderHeading(String anchor, String title, StringBuffer sink) {
  659. sink.append(" <a name=\"" + anchor + "\"></a>");
  660. sink.append(EOL);
  661. if ((null == title) || (0 == title.length())) {
  662. title = anchor;
  663. }
  664. sink.append(" <h3>" + title + "</h3>");
  665. sink.append(EOL);
  666. sink.append("<a href=\"#top\">back to top</a>");
  667. sink.append(EOL);
  668. }
  669. /**
  670. * Manage headings in both table of contents and listings.
  671. * @param name the String anchor
  672. */
  673. protected void doContentTree(String name) {
  674. if (name.equals(lastAnchor)) {
  675. return;
  676. }
  677. // ---- handle trees
  678. String[] parts = SampleUtil.splitAnchorName(name);
  679. //String[] lastAnchor = (String[]) lastAnchors.peek();
  680. int firstDiff = SampleUtil.commonPrefix(parts, lastAnchor);
  681. // unwind to common prefix, if necessary
  682. if (firstDiff+1 < lastAnchor.length) {
  683. for (int i = 1; i < lastAnchor.length-firstDiff ; i++) {
  684. tableOfContents.append(" </ul>");
  685. tableOfContents.append(EOL);
  686. }
  687. }
  688. // build up prefix
  689. StringBuffer branchAnchor = new StringBuffer();
  690. for (int i = 0; i < firstDiff;) {
  691. branchAnchor.append(parts[i]);
  692. i++;
  693. branchAnchor.append("-");
  694. }
  695. // emit leading headers, but not anchor itself
  696. for (int i = firstDiff; i < (parts.length-1); i++) {
  697. branchAnchor.append(parts[i]);
  698. String prefixName = branchAnchor.toString();
  699. branchAnchor.append("-");
  700. tableOfContents.append(" <li><a href=\"#");
  701. tableOfContents.append(prefixName);
  702. tableOfContents.append("\">" + prefixName + "</a></li>");
  703. tableOfContents.append(EOL);
  704. tableOfContents.append(" <ul>");
  705. tableOfContents.append(EOL);
  706. renderHeading(prefixName, prefixName, sampleSection);
  707. }
  708. lastAnchor = parts;
  709. }
  710. protected void renderAuthorIndex(Samples samples, StringBuffer sink) {
  711. sink.append("<h2><a name=\"authorIndex\"></a>Author Index</h2>");
  712. List list = samples.getSortedSamples(Sample.AUTHOR_NAME_SOURCE_COMPARER);
  713. String lastAuthor = null;
  714. for (ListIterator iter = list.listIterator(); iter.hasNext();) {
  715. Sample sample = (Sample)iter.next();
  716. String author = sample.author;
  717. if (!author.equals(lastAuthor)) {
  718. if (null != lastAuthor) {
  719. sink.append("</li></ul>");
  720. }
  721. sink.append("<li>");
  722. sink.append(author);
  723. sink.append(EOL);
  724. sink.append("<ul>");
  725. sink.append(EOL);
  726. lastAuthor = author;
  727. }
  728. sink.append(" <li><a href=\"#");
  729. sink.append(sample.anchorName);
  730. sink.append("\">");
  731. if (null == sample.anchorTitle) {
  732. sink.append(sample.anchorName);
  733. } else {
  734. sink.append(sample.anchorTitle);
  735. }
  736. sink.append("</a></li>");
  737. }
  738. }
  739. }
  740. class SampleUtil {
  741. public static final String SAMPLE_BASE_DIR_NAME = "sandbox";
  742. public static void simpleRender(Samples result, StringBuffer sink) {
  743. List sortedSamples = result.getSortedSamples();
  744. int i = 0;
  745. for (ListIterator iter = sortedSamples.listIterator();
  746. iter.hasNext();) {
  747. Sample sample = (Sample) iter.next();
  748. sink.append(i++ + ": " + sample);
  749. }
  750. }
  751. /** result struct for getPackagePath */
  752. static class JavaFile {
  753. /** input File possibly signifying a java file */
  754. final File path;
  755. /** String java path suffix in form "com/company/Bar.java"
  756. * null if this is not a java file
  757. */
  758. final String javaPath;
  759. /** any prefix before java path suffix in the original path */
  760. final String prefix;
  761. /** error handling */
  762. final Throwable thrown;
  763. JavaFile(File path, String javaPath, String prefix, Throwable thrown) {
  764. this.path = path;
  765. this.javaPath = javaPath;
  766. this.prefix = prefix;
  767. this.thrown = thrown;
  768. }
  769. }
  770. /**
  771. * Read any package statement in the file to determine
  772. * the package path of the file
  773. * @param path the File to seek the package in
  774. * @return the JavaFile with the components of the path
  775. */
  776. public static JavaFile getJavaFile(File path) {
  777. if (null == path) {
  778. throw new IllegalArgumentException("null path");
  779. }
  780. String result = path.getPath().replace('\\', '/');
  781. String packag = "";
  782. String javaPath = null;
  783. String prefix = null;
  784. Throwable thrown = null;
  785. if (result.endsWith(".java") || result.endsWith(".aj")) {
  786. FileReader reader = null;
  787. try {
  788. reader = new FileReader(path);
  789. BufferedReader br = new BufferedReader(reader);
  790. String line;
  791. while (null != (line = br.readLine())) {
  792. int loc = line.indexOf("package");
  793. if (-1 != loc) {
  794. int end = line.indexOf(";");
  795. if (-1 == loc) {
  796. String m = "unterminated package statement \"";
  797. throw new Error(m + line + "\" in " + path);
  798. }
  799. packag = (line.substring(loc + 7, end) + ".")
  800. .trim()
  801. .replace('.', '/');
  802. break;
  803. }
  804. loc = line.indexOf("import");
  805. if (-1 != loc) {
  806. break;
  807. }
  808. }
  809. } catch (IOException e) {
  810. thrown = e;
  811. } finally {
  812. if (null != reader) {
  813. try {
  814. reader.close();
  815. } catch (IOException e1) {
  816. // ignore
  817. }
  818. }
  819. }
  820. if (null == thrown) {
  821. javaPath = packag + path.getName();
  822. int loc = result.indexOf(javaPath);
  823. if (-1 == loc) {
  824. String m = "expected suffix " + javaPath + " in ";
  825. throw new Error(m + result);
  826. }
  827. prefix = result.substring(0, loc);
  828. }
  829. }
  830. return new JavaFile(path, javaPath, prefix, thrown);
  831. }
  832. /**
  833. * Extract file path relative to base of package directory
  834. * and directory in SAMPLE_BASE_DIR_NAME for this file.
  835. * @param path the File to render from SAMPLE_BASE_DIR_NAME
  836. * @return String "baseDir {path}"
  837. */
  838. public static String renderCodePath(File path) {
  839. JavaFile javaFile = getJavaFile(path);
  840. if (javaFile.thrown != null) {
  841. throw new Error(javaFile.thrown.getClass()
  842. + ": " + javaFile.thrown.getMessage());
  843. }
  844. String file = javaFile.javaPath; // can be null...
  845. String prefix = javaFile.prefix;
  846. if (prefix == null) {
  847. prefix = path.getPath().replace('\\', '/');
  848. }
  849. int loc = prefix.lastIndexOf(SAMPLE_BASE_DIR_NAME);
  850. if (-1 == loc) {
  851. String m = "not after " + SAMPLE_BASE_DIR_NAME;
  852. throw new IllegalArgumentException(m + "?: " + path);
  853. }
  854. prefix = prefix.substring(loc + 1 + SAMPLE_BASE_DIR_NAME.length());
  855. if (file == null) {
  856. int slash = prefix.lastIndexOf('/');
  857. if (-1 == slash) {
  858. file = prefix;
  859. prefix = "";
  860. } else {
  861. file = prefix.substring(slash+1);
  862. prefix = prefix.substring(0, slash);
  863. }
  864. }
  865. if (prefix.endsWith("/")) {
  866. prefix = prefix.substring(0, prefix.length()-1);
  867. }
  868. return (prefix + " " + file).trim();
  869. }
  870. public static int commonPrefix(String[] lhs, String[] rhs) {
  871. final int max = smallerSize(lhs, rhs);
  872. int firstDiff = 0;
  873. while (firstDiff < max) {
  874. if (!lhs[firstDiff].equals(rhs[firstDiff])) {
  875. break;
  876. }
  877. firstDiff++;
  878. }
  879. return firstDiff;
  880. }
  881. private static int smallerSize(Object[] one, Object[] two) {
  882. if ((null == one) || (null == two)) {
  883. return 0;
  884. }
  885. return (one.length > two.length ? two.length : one.length);
  886. }
  887. public static String[] splitAnchorName(Sample sample) {
  888. return splitAnchorName(sample.anchorName);
  889. }
  890. public static String[] splitAnchorName(String anchorName) {
  891. ArrayList result = new ArrayList();
  892. int start = 0;
  893. int loc = anchorName.indexOf("-", start);
  894. String next;
  895. while (loc != -1) {
  896. next = anchorName.substring(start, loc);
  897. result.add(next);
  898. start = loc+1;
  899. loc = anchorName.indexOf("-", start);
  900. }
  901. next = anchorName.substring(start);
  902. result.add(next);
  903. return (String[]) result.toArray(new String[result.size()]);
  904. }
  905. /**
  906. * Replace literals with literals in source string
  907. * @param source the String to modify
  908. * @param from the String[] of literals to replace
  909. * @param to the String[] of literals to use when replacing
  910. * @return the String source as modified by the replaces
  911. */
  912. public static String replace(String source, String[] from, String[] to) {
  913. if ((null == source) || (0 == source.length())) {
  914. return source;
  915. }
  916. if (from.length != to.length) {
  917. throw new IllegalArgumentException("unmatched from/to");
  918. }
  919. StringBuffer result = new StringBuffer();
  920. int LEN = source.length();
  921. int start = 0;
  922. for (int i = 0; i < LEN; i++) {
  923. String suffix = source.substring(i);
  924. for (int j = 0; j < from.length; j++) {
  925. if (suffix.startsWith(from[j])) {
  926. result.append(source.substring(start, i));
  927. result.append(to[j]);
  928. start = i + from[j].length();
  929. i = start-1;
  930. break;
  931. }
  932. }
  933. }
  934. if (start < source.length()) {
  935. result.append(source.substring(start));
  936. }
  937. return result.toString();
  938. }
  939. public static void render(
  940. Sample sample,
  941. String fieldDelim,
  942. String valueDelim,
  943. StringBuffer sink) {
  944. if ((null == sink) || (null == sample)) {
  945. return;
  946. }
  947. if (null == fieldDelim) {
  948. fieldDelim = "";
  949. }
  950. if (null == valueDelim) {
  951. valueDelim = "";
  952. }
  953. sink.append("anchorName");
  954. sink.append(valueDelim);
  955. sink.append(sample.anchorName);
  956. sink.append(fieldDelim);
  957. sink.append("author");
  958. sink.append(valueDelim);
  959. sink.append(sample.author);
  960. sink.append(fieldDelim);
  961. sink.append("sourcePath");
  962. sink.append(valueDelim);
  963. sink.append(sample.sourcePath.toString());
  964. sink.append(fieldDelim);
  965. sink.append("startLine");
  966. sink.append(valueDelim);
  967. sink.append(sample.startLine);
  968. sink.append(fieldDelim);
  969. sink.append("endLine");
  970. sink.append(valueDelim);
  971. sink.append(sample.endLine);
  972. sink.append(fieldDelim);
  973. sink.append("sampleCode");
  974. sink.append(valueDelim);
  975. sink.append(sample.sampleCode.toString());
  976. sink.append(fieldDelim);
  977. }
  978. private SampleUtil(){}
  979. }