summaryrefslogtreecommitdiffstats
path: root/build/src
diff options
context:
space:
mode:
authorwisberg <wisberg>2003-08-06 02:08:40 +0000
committerwisberg <wisberg>2003-08-06 02:08:40 +0000
commita63bc04fb1cb6e8d6d0bc2a509ab9658e3d78c43 (patch)
treecd4e827c60136df7bf9850591b0c64baff549d20 /build/src
parentb0d37c4b51a344bee94bb7f7cc1ecef1a233e3ab (diff)
downloadaspectj-a63bc04fb1cb6e8d6d0bc2a509ab9658e3d78c43.tar.gz
aspectj-a63bc04fb1cb6e8d6d0bc2a509ab9658e3d78c43.zip
initial checkin of the sandbox.
The basic structure and examples of each type are there, but I have more examples and the ones there are not altogether validated. I'll make a few more changes before emailing dev and users about usage, etc.
Diffstat (limited to 'build/src')
-rw-r--r--build/src/org/aspectj/internal/tools/build/SampleGatherer.java1011
1 files changed, 1011 insertions, 0 deletions
diff --git a/build/src/org/aspectj/internal/tools/build/SampleGatherer.java b/build/src/org/aspectj/internal/tools/build/SampleGatherer.java
new file mode 100644
index 000000000..299053b8e
--- /dev/null
+++ b/build/src/org/aspectj/internal/tools/build/SampleGatherer.java
@@ -0,0 +1,1011 @@
+/* *******************************************************************
+ * Copyright (c) 2003 Contributors.
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * Wes Isberg initial implementation
+ * ******************************************************************/
+
+/*
+ * A quickie hack to extract sample code from testable sources.
+ * This could reuse a lot of code from elsewhere,
+ * but currently doesn't,
+ * to keep it in the build module which avoids dependencies.
+ * (Too bad we can't use scripting languages...)
+ */
+package org.aspectj.internal.tools.build;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * This gathers sample code delimited with [START..END]-SAMPLE
+ * from source files under a base directory,
+ * along with any <code>@author</code> info.
+ * <pre>// START-SAMPLE {anchorName} {anchorText}
+ * ... sample code ...
+ * // END-SAMPLE {anchorName}
+ * </pre>
+ * where {anchorName} need not be unique and might be
+ * hierarchical wrt "-", e.g., "genus-species-individual".
+ */
+public class SampleGatherer {
+
+ /** EOL String for gathered lines */
+ public static final String EOL = "\n"; // XXX
+
+ /** lowercase source suffixes identify files to gather samples from */
+ public static final String[] SOURCE_SUFFIXES = new String[]
+ { ".java", ".aj", ".sh", ".txt", ".html", ".htm" };
+
+ static final String START = "START-SAMPLE";
+ static final String END = "END-SAMPLE";
+ static final String AUTHOR = "@author";
+ static final String FLAG = "XXX";
+
+ private static void test(String[] args){
+ String[] from = new String[] { "<pre>", "</pre>" };
+ String[] to = new String[] { "&lt;pre>", "&lt;/pre>" };
+ String source = "in this <pre> day and </pre> age of <pre and /pre>";
+ System.err.println("from " + source);
+ System.err.println(" to " + SampleUtil.replace(source, from, to));
+ source = "<pre> day and </pre>";
+ System.err.println("from " + source);
+ System.err.println(" to " + SampleUtil.replace(source, from, to));
+ source = "<pre day and </pre";
+ System.err.println("from " + source);
+ System.err.println(" to " + SampleUtil.replace(source, from, to));
+ source = "<pre> day and </pre> age";
+ System.err.println("from " + source);
+ System.err.println(" to " + SampleUtil.replace(source, from, to));
+ source = "in this <pre> day and </pre> age";
+ System.err.println("from " + source);
+ System.err.println(" to " + SampleUtil.replace(source, from, to));
+
+ }
+ /**
+ * Emit samples gathered from any input args.
+ * @param args the String[] of paths to files or directories to search
+ * @throws IOException if unable to read a source file
+ */
+ public static void main(String[] args) throws IOException {
+ Samples result = new Samples();
+ for (int i = 0; i < args.length; i++) {
+ result = gather(new File(args[i]), result);
+ }
+
+ StringBuffer sb = HTMLSamplesRenderer.ME.render(result, null);
+
+ File out = new File("../docs/dist/doc/sample-code.html");
+ FileOutputStream fos = new FileOutputStream(out);
+ fos.write(sb.toString().getBytes());
+ fos.close();
+ System.out.println("see file:///" + out);
+ }
+
+ /**
+ * Gather samples from a source file or directory
+ * @param source the File file or directory to start with
+ * @param sink the Samples collection to add to
+ * @return sink or a new Samples collection with any samples found
+ * @throws IOException if unable to read a source file
+ */
+ public static Samples gather(File source, Samples sink)
+ throws IOException {
+ if (null == sink) {
+ sink = new Samples();
+ }
+ if (null == source) {
+ source = new File(".");
+ }
+ doGather(source, sink);
+ return sink;
+ }
+ private static String trimCommentEnd(String line, int start) {
+ if (null == line) {
+ return "";
+ }
+ if ((start > 0) && (start < line.length())) {
+ line = line.substring(start);
+ }
+ line = line.trim();
+ if (line.endsWith("*/")) {
+ line = line.substring(0, line.length()-2).trim();
+ } else if (line.endsWith("-->")) {
+ line = line.substring(0, line.length()-3).trim();
+ }
+ return line;
+ }
+
+ private static void doGather(File source, Samples sink)
+ throws IOException {
+ if (source.isFile()) {
+ if (isSource(source)) {
+ gatherFromFile(source, sink);
+ }
+ } else if (source.isDirectory() && source.canRead()) {
+ File[] files = source.listFiles();
+ for (int i = 0; i < files.length; i++) {
+ doGather(files[i], sink);
+ }
+ }
+ }
+
+ private static boolean isSource(File file) {
+ if ((null == file) || !file.isFile() || !file.canRead()) {
+ return false;
+ }
+ String path = file.getName().toLowerCase();
+ for (int i = 0; i < SOURCE_SUFFIXES.length; i++) {
+ if (path.endsWith(SOURCE_SUFFIXES[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static void gatherFromFile(final File source, final Samples sink)
+ throws IOException {
+ Reader reader = null;
+ try {
+ String author = null;
+ StringBuffer sampleCode = new StringBuffer();
+ String anchorName = null;
+ String anchorTitle = null;
+ ArrayList flags = new ArrayList();
+ int startLine = -1; // seeking
+ int endLine = Integer.MAX_VALUE; // not seeking
+ reader = new FileReader(source);
+ LineNumberReader lineReader = new LineNumberReader(reader);
+ String line;
+
+ while (null != (line = lineReader.readLine())) { // XXX naive
+ // found start?
+ int loc = line.indexOf(START);
+ if (-1 != loc) {
+ int lineNumber = lineReader.getLineNumber();
+ if (-1 != startLine) {
+ abort("unexpected " + START, source, line, lineNumber);
+ }
+ startLine = lineNumber;
+ endLine = -1;
+ anchorName = trimCommentEnd(line, loc + START.length());
+ loc = anchorName.indexOf(" ");
+ if (-1 == loc) {
+ anchorTitle = null;
+ } else {
+ anchorTitle = anchorName.substring(1+loc).trim();
+ anchorName = anchorName.substring(0, loc);
+ }
+ continue;
+ }
+
+ // found end?
+ loc = line.indexOf(END);
+ if (-1 != loc) {
+ int lineNumber = lineReader.getLineNumber();
+ if (Integer.MAX_VALUE == endLine) {
+ abort("unexpected " + END, source, line, lineNumber);
+ }
+ String newtag = trimCommentEnd(line, loc + END.length());
+ if ((newtag.length() > 0) && !newtag.equals(anchorName)) {
+ String m = "expected " + anchorName
+ + " got " + newtag;
+ abort(m, source, line, lineNumber);
+ }
+ endLine = lineNumber;
+ Sample sample = new Sample(anchorName,
+ anchorTitle,
+ author,
+ sampleCode.toString(),
+ source,
+ startLine,
+ endLine,
+ (String[]) flags.toArray(new String[flags.size()]));
+ sink.addSample(sample);
+
+ // back to seeking start
+ sampleCode.setLength(0);
+ startLine = -1;
+ endLine = Integer.MAX_VALUE;
+ continue;
+ }
+
+ // found author?
+ loc = line.indexOf(AUTHOR);
+ if (-1 != loc) {
+ author = trimCommentEnd(line, loc + AUTHOR.length());
+ }
+ // found flag comment?
+ loc = line.indexOf(FLAG);
+ if (-1 != loc) {
+ flags.add(trimCommentEnd(line, loc + FLAG.length()));
+ }
+
+ // reading?
+ if ((-1 != startLine) && (-1 == endLine)) {
+ sampleCode.append(line);
+ sampleCode.append(EOL);
+ }
+ }
+ if (-1 == endLine) {
+ abort("incomplete sample", source, "", lineReader.getLineNumber());
+ }
+ } finally {
+ if (null != reader) {
+ reader.close();
+ }
+ }
+ }
+ private static void abort(String why, File file, String line, int lineNumber)
+ throws Abort {
+ throw new Abort(why + " at " + file + ":" + lineNumber + ": " + line);
+ }
+ private static void delay(Object toDelay) {
+ synchronized (toDelay) { // XXX sleep instead?
+ toDelay.notifyAll();
+ }
+ }
+ static class Abort extends IOException {
+ Abort(String s) {
+ super(s);
+ }
+ }
+}
+
+/**
+ * Data associated with sample code - struct class.
+ */
+class Sample {
+ public static final String ASPECTJ_TEAM = "The AspectJ Team";
+
+ /** sort by anchorName, file path, and start/end location */
+ static Comparator NAME_SOURCE_COMPARER = new Comparator() {
+ public int compare(Object lhs, Object rhs) {
+ Sample left = (Sample) lhs;
+ Sample right = (Sample) rhs;
+ if (null == left) {
+ return (null == right ? 0 : -1);
+ }
+ if (null == right) {
+ return 1;
+ }
+ int result = left.anchorName.compareTo(right.anchorName);
+ if (0 != result) {
+ return result;
+ }
+ result = left.sourcePath.compareTo(right.sourcePath);
+ if (0 != result) {
+ return result;
+ }
+ result = right.startLine - left.startLine;
+ if (0 != result) {
+ return result;
+ }
+ return right.endLine - left.endLine;
+ }
+ };
+
+ /** sort by author, then NAME_SOURCE_COMPARER */
+ static Comparator AUTHOR_NAME_SOURCE_COMPARER = new Comparator() {
+ public int compare(Object lhs, Object rhs) {
+ Sample left = (Sample) lhs;
+ Sample right = (Sample) rhs;
+ if (null == left) {
+ return (null == right ? 0 : -1);
+ }
+ if (null == right) {
+ return 1;
+ }
+ int result = left.author.compareTo(right.author);
+ if (0 != result) {
+ return result;
+ }
+ return NAME_SOURCE_COMPARER.compare(lhs, rhs);
+ }
+ };
+
+ final String anchorName;
+ final String anchorTitle;
+ final String author;
+ final String sampleCode;
+ final File sourcePath;
+ final int startLine;
+ final int endLine;
+ final Kind kind;
+ /** List of String flags found in the sample */
+ final List flags;
+ public Sample(
+ String anchorName,
+ String anchorTitle,
+ String author,
+ String sampleCode,
+ File sourcePath,
+ int startLine,
+ int endLine,
+ String[] flags) {
+ this.anchorName = anchorName;
+ this.anchorTitle = anchorTitle;
+ this.author = (null != author ? author : ASPECTJ_TEAM);
+ this.sampleCode = sampleCode;
+ this.sourcePath = sourcePath;
+ this.startLine = startLine;
+ this.endLine = endLine;
+ this.kind = Kind.getKind(sourcePath);
+ List theFlags;
+ if ((null == flags) || (0 == flags.length)) {
+ this.flags = Collections.EMPTY_LIST;
+ } else {
+ this.flags = Collections.unmodifiableList(Arrays.asList(flags));
+ }
+ }
+
+ public String toString() {
+ return sampleCode;
+ }
+
+ public static class Kind {
+ static final Kind HTML = new Kind();
+ static final Kind PROGRAM = new Kind();
+ static final Kind SCRIPT = new Kind();
+ static final Kind TEXT = new Kind();
+ static final Kind OTHER = new Kind();
+ public static Kind getKind(File file) {
+ if (null == file) {
+ return OTHER;
+ }
+ String name = file.getName().toLowerCase();
+ if ((name.endsWith(".java") || name.endsWith(".aj"))) {
+ return PROGRAM;
+ }
+ if ((name.endsWith(".html") || name.endsWith(".htm"))) {
+ return HTML;
+ }
+ if ((name.endsWith(".sh") || name.endsWith(".ksh"))) {
+ return SCRIPT;
+ }
+ if ((name.endsWith(".txt") || name.endsWith(".text"))) {
+ return TEXT;
+ }
+ return OTHER;
+ }
+ private Kind() {
+ }
+ }
+}
+
+/**
+ * type-safe Collection of samples.
+ */
+class Samples {
+ private ArrayList samples = new ArrayList();
+ int size() {
+ return samples.size();
+ }
+ void addSample(Sample sample) {
+ samples.add(sample);
+ }
+ /**
+ * @return List copy, sorted by Sample.NAME_SOURCE_COMPARER
+ */
+ List getSortedSamples() {
+ return getSortedSamples(Sample.NAME_SOURCE_COMPARER);
+ }
+
+ List getSortedSamples(Comparator comparer) {
+ ArrayList result = new ArrayList();
+ result.addAll(samples);
+ Collections.sort(result, comparer);
+ return result;
+ }
+}
+
+
+/**
+ * Render samples by using method visitors.
+ */
+class SamplesRenderer {
+ public static SamplesRenderer ME = new SamplesRenderer();
+ protected SamplesRenderer() {
+ }
+ public static final String EOL = "\n"; // XXX
+ public static final String INFO =
+ "This contains contributions from the AspectJ community of "
+ + "<ul><li>sample code for AspectJ programs,</li>"
+ + "<li>sample code for extensions to AspectJ tools using the public API's,</li>"
+ + "<li>sample scripts for invoking AspectJ tools, and </li> "
+ + "<li>documentation trails showing how to do given tasks"
+ + " using AspectJ, AJDT, or various IDE or deployment"
+ + " environments.</li></ul>";
+
+ public static final String COPYRIGHT =
+ "This documentation is made available under the Common Public " + "License version 1.0 available at "
+ + "<a href=\"http://www.eclipse.org/legal/cpl-v10.html\">"
+ + "http://www.eclipse.org/legal/cpl-v10.html</a>."
+ + "<p>Copyright 2003 Contributors. All Rights Reserved. "
+ + "Contributors are listed in this document as authors. "
+ + "Permission to republish portions of this documentation "
+ + "is hereby granted if the publication acknowledges "
+ + "the author by name and "
+ + "the source by reference to the AspectJ project home page "
+ + " at http://eclipse.org/aspectj."
+ + EOL
+ ;
+
+ /** template algorithm to render */
+ public final StringBuffer render(Samples samples, StringBuffer sink) {
+ if (null == sink) {
+ sink = new StringBuffer();
+ }
+ if ((null == samples) || (0 == samples.size())) {
+ return sink;
+ }
+ startList(samples, sink);
+ List list = samples.getSortedSamples();
+ String anchorName = null;
+ for (ListIterator iter = list.listIterator();
+ iter.hasNext();) {
+ Sample sample = (Sample) iter.next();
+ String newAnchorName = sample.anchorName;
+ if ((null == anchorName)
+ || (!anchorName.equals(newAnchorName))) {
+ endAnchorName(anchorName, sink);
+ startAnchorName(newAnchorName, sample.anchorTitle, sink);
+ anchorName = newAnchorName;
+ }
+ render(sample, sink);
+ }
+ endAnchorName(anchorName, sink);
+ endList(samples, sink);
+ return sink;
+ }
+ protected void startList(Samples samples, StringBuffer sink) {
+ sink.append("Printing " + samples.size() + " samples");
+ sink.append(EOL);
+ }
+
+ protected void startAnchorName(String name, String title, StringBuffer sink) {
+ sink.append("anchor " + name);
+ sink.append(EOL);
+ }
+
+ protected void render(Sample sample, StringBuffer sink) {
+ SampleUtil.render(sample, "=", ", ",sink);
+ sink.setLength(sink.length()-2);
+ sink.append(EOL);
+ }
+
+ /**
+ * @param name the String name being ended - ignore if null
+ * @param sink
+ */
+ protected void endAnchorName(String name, StringBuffer sink) {
+ if (null == name) {
+ return;
+ }
+ }
+
+ protected void endList(Samples samples, StringBuffer sink) {
+ sink.append("Printed " + samples.size() + " samples");
+ sink.append(EOL);
+ }
+
+}
+
+// XXX need DocBookSamplesRenderer
+
+/**
+ * Output the samples as a single HTML file, with a table of contents
+ * and sorting the samples by their anchor tags.
+ */
+class HTMLSamplesRenderer extends SamplesRenderer {
+ public static SamplesRenderer ME = new HTMLSamplesRenderer();
+ // XXX move these
+ public static boolean doHierarchical = true;
+ public static boolean doFlags = false;
+
+
+ final StringBuffer tableOfContents;
+ final StringBuffer sampleSection;
+ String[] lastAnchor = new String[0];
+ String currentAnchor;
+ String currentAuthor;
+
+ protected HTMLSamplesRenderer() {
+ sampleSection = new StringBuffer();
+ tableOfContents = new StringBuffer();
+ }
+
+ protected void startAnchorName(String name, String title, StringBuffer sink) {
+ if (doHierarchical) {
+ doContentTree(name);
+ }
+ // ---- now do anchor
+ tableOfContents.append(" <li><a href=\"#" + name);
+ if ((null == title) || (0 == title.length())) {
+ title = name;
+ }
+ tableOfContents.append("\">" + title + "</a></li>");
+ tableOfContents.append(EOL);
+ currentAnchor = name;
+ }
+
+ protected void startList(Samples samples, StringBuffer sink) {
+ }
+
+ protected void render(Sample sample, StringBuffer sink) {
+ if (null != currentAnchor) {
+ if (!currentAnchor.equals(sample.anchorName)) {
+ String m = "expected " + currentAnchor
+ + " got " + sample.anchorName;
+ throw new Error(m);
+ }
+ currentAnchor = null;
+ }
+
+ // do heading then code
+ renderHeading(sample.anchorName, sample.anchorTitle, sampleSection);
+ if (sample.kind == Sample.Kind.HTML) {
+ renderHTML(sample);
+ } else {
+ renderPre(sample);
+ }
+ }
+
+ protected boolean doRenderAuthor(Sample sample) {
+ return (null != sample.author);
+ // && !sample.author.equals(currentAuthor)
+ }
+
+ protected void renderStandardHeader(Sample sample) {
+ // XXX starting same as pre
+ if (doRenderAuthor(sample)) {
+ currentAuthor = sample.author;
+ sampleSection.append(" <p>author: " + currentAuthor + "</p>");
+ sampleSection.append(EOL);
+ }
+ sampleSection.append(" <p>from: ");
+ sampleSection.append(SampleUtil.renderCodePath(sample.sourcePath));
+ sampleSection.append(":" + sample.startLine);
+ sampleSection.append(EOL);
+ if (doFlags) {
+ boolean flagHeaderDone = false;
+ for (Iterator iter = sample.flags.iterator(); iter.hasNext();) {
+ String flag = (String) iter.next();
+ if (!flagHeaderDone) {
+ sampleSection.append("<p>Comments flagged:<ul>");
+ sampleSection.append(EOL);
+ flagHeaderDone = true;
+ }
+ sampleSection.append("<li>");
+ sampleSection.append(flag);
+ sampleSection.append("</li>");
+ }
+ if (flagHeaderDone) {
+ sampleSection.append("</ul>");
+ sampleSection.append(EOL);
+ }
+ }
+ }
+
+ protected void renderHTML(Sample sample) {
+ renderStandardHeader(sample);
+ sampleSection.append(EOL);
+ sampleSection.append(prepareHTMLSample(sample.sampleCode));
+ sampleSection.append(EOL);
+ }
+
+ protected void renderPre(Sample sample) {
+ renderStandardHeader(sample);
+ sampleSection.append(" <pre>");
+ sampleSection.append(EOL);
+ sampleSection.append(prepareCodeSample(sample.sampleCode));
+ sampleSection.append(" </pre>");
+ sampleSection.append(EOL);
+ }
+
+ protected void endAnchorName(String name, StringBuffer sink) {
+ if (null == name) {
+ return;
+ }
+ currentAnchor = null;
+ currentAuthor = null; // authors don't span anchors
+ }
+
+ protected void endList(Samples samples, StringBuffer sink) {
+ sink.append("<html>");
+ sink.append(EOL);
+ sink.append("<title>AspectJ sample code</title>");
+ sink.append(EOL);
+ sink.append("<body>");
+ sink.append(EOL);
+ sink.append(" <a name=\"top\"></a>");
+ sink.append(EOL);
+ sink.append(" <h1>AspectJ sample code</h1>");
+ sink.append(INFO);
+ sink.append(EOL);
+ sink.append(COPYRIGHT);
+ sink.append(EOL);
+ sink.append(" <h2>Contents</h2>");
+ sink.append(EOL);
+ sink.append(" <ul>");
+ sink.append(EOL);
+ sink.append(tableOfContents.toString());
+ // unwind to common prefix, if necessary
+ for (int i = 0; i < lastAnchor.length ; i++) {
+ sink.append(" </ul>");
+ }
+
+ sink.append(" <li><a href=\"#authorIndex\">Author Index</a></li>");
+ sink.append(" </ul>");
+ sink.append(" <h2>Listings</h2>");
+ sink.append(EOL);
+ sink.append(sampleSection.toString());
+ renderAuthorIndex(samples, sink);
+ sink.append("</body></html>");
+ sink.append(EOL);
+ }
+
+ protected String prepareHTMLSample(String sampleCode) {
+ String[] from = new String[20];
+ String[] to = new String[20];
+ for (int i = 0; i < to.length; i++) {
+ String h = "h" + i + ">";
+ from[i] = "<" + h;
+ to[i] = "<p><b>";
+ from[++i] = "</" + h;
+ to[i] = "</b></p><p>";
+ }
+ return (SampleUtil.replace(sampleCode, from, to));
+ }
+
+ protected String prepareCodeSample(String sampleCode) {
+ String[] from = new String[] { "<pre>", "</pre>" };
+ String[] to = new String[] { "&lt;pre>", "&lt;/pre>" };
+ return (SampleUtil.replace(sampleCode, from, to));
+ }
+
+ protected void renderHeading(String anchor, String title, StringBuffer sink) {
+ sink.append(" <a name=\"" + anchor + "\"></a>");
+ sink.append(EOL);
+ if ((null == title) || (0 == title.length())) {
+ title = anchor;
+ }
+ sink.append(" <h3>" + title + "</h3>");
+ sink.append(EOL);
+ sink.append("<a href=\"#top\">back to top</a>");
+ sink.append(EOL);
+ }
+
+ /**
+ * Manage headings in both table of contents and listings.
+ * @param name the String anchor
+ */
+ protected void doContentTree(String name) {
+ if (name.equals(lastAnchor)) {
+ return;
+ }
+ // ---- handle trees
+ String[] parts = SampleUtil.splitAnchorName(name);
+ //String[] lastAnchor = (String[]) lastAnchors.peek();
+ int firstDiff = SampleUtil.commonPrefix(parts, lastAnchor);
+ // unwind to common prefix, if necessary
+ if (firstDiff+1 < lastAnchor.length) {
+ for (int i = 1; i < lastAnchor.length-firstDiff ; i++) {
+ tableOfContents.append(" </ul>");
+ tableOfContents.append(EOL);
+ }
+ }
+ // build up prefix
+ StringBuffer branchAnchor = new StringBuffer();
+ for (int i = 0; i < firstDiff;) {
+ branchAnchor.append(parts[i]);
+ i++;
+ branchAnchor.append("-");
+ }
+ // emit leading headers, but not anchor itself
+ for (int i = firstDiff; i < (parts.length-1); i++) {
+ branchAnchor.append(parts[i]);
+ String prefixName = branchAnchor.toString();
+ branchAnchor.append("-");
+ tableOfContents.append(" <li><a href=\"#");
+ tableOfContents.append(prefixName);
+ tableOfContents.append("\">" + prefixName + "</a></li>");
+ tableOfContents.append(EOL);
+ tableOfContents.append(" <ul>");
+ tableOfContents.append(EOL);
+
+ renderHeading(prefixName, prefixName, sampleSection);
+ }
+ lastAnchor = parts;
+ }
+
+ protected void renderAuthorIndex(Samples samples, StringBuffer sink) {
+ sink.append("<h2><a name=\"authorIndex\"></a>Author Index</h2>");
+ List list = samples.getSortedSamples(Sample.AUTHOR_NAME_SOURCE_COMPARER);
+ String lastAuthor = null;
+ for (ListIterator iter = list.listIterator(); iter.hasNext();) {
+ Sample sample = (Sample)iter.next();
+ String author = sample.author;
+ if (!author.equals(lastAuthor)) {
+ if (null != lastAuthor) {
+ sink.append("</li></ul>");
+ }
+ sink.append("<li>");
+ sink.append(author);
+ sink.append(EOL);
+ sink.append("<ul>");
+ sink.append(EOL);
+ lastAuthor = author;
+ }
+ sink.append(" <li><a href=\"#");
+ sink.append(sample.anchorName);
+ sink.append("\">");
+ if (null == sample.anchorTitle) {
+ sink.append(sample.anchorName);
+ } else {
+ sink.append(sample.anchorTitle);
+ }
+ sink.append("</a></li>");
+ }
+ }
+}
+
+class SampleUtil {
+ public static final String SAMPLE_BASE_DIR_NAME = "sandbox";
+
+ public static void simpleRender(Samples result, StringBuffer sink) {
+ List sortedSamples = result.getSortedSamples();
+ int i = 0;
+ for (ListIterator iter = sortedSamples.listIterator();
+ iter.hasNext();) {
+ Sample sample = (Sample) iter.next();
+ sink.append(i++ + ": " + sample);
+ }
+ }
+
+ /** result struct for getPackagePath */
+ static class JavaFile {
+ /** input File possibly signifying a java file */
+ final File path;
+
+ /** String java path suffix in form "com/company/Bar.java"
+ * null if this is not a java file
+ */
+ final String javaPath;
+
+ /** any prefix before java path suffix in the original path */
+ final String prefix;
+
+ /** error handling */
+ final Throwable thrown;
+ JavaFile(File path, String javaPath, String prefix, Throwable thrown) {
+ this.path = path;
+ this.javaPath = javaPath;
+ this.prefix = prefix;
+ this.thrown = thrown;
+ }
+ }
+
+ /**
+ * Read any package statement in the file to determine
+ * the package path of the file
+ * @param path the File to seek the package in
+ * @return the JavaFile with the components of the path
+ */
+ public static JavaFile getJavaFile(File path) {
+ if (null == path) {
+ throw new IllegalArgumentException("null path");
+ }
+ String result = path.getPath().replace('\\', '/');
+ String packag = "";
+ String javaPath = null;
+ String prefix = null;
+ Throwable thrown = null;
+ if (result.endsWith(".java") || result.endsWith(".aj")) {
+ FileReader reader = null;
+ try {
+ reader = new FileReader(path);
+ BufferedReader br = new BufferedReader(reader);
+ String line;
+ while (null != (line = br.readLine())) {
+ int loc = line.indexOf("package");
+ if (-1 != loc) {
+ int end = line.indexOf(";");
+ if (-1 == loc) {
+ String m = "unterminated package statement \"";
+ throw new Error(m + line + "\" in " + path);
+ }
+ packag = (line.substring(loc + 7, end) + ".")
+ .trim()
+ .replace('.', '/');
+ break;
+ }
+ loc = line.indexOf("import");
+ if (-1 != loc) {
+ break;
+ }
+ }
+ } catch (IOException e) {
+ thrown = e;
+ } finally {
+ if (null != reader) {
+ try {
+ reader.close();
+ } catch (IOException e1) {
+ // ignore
+ }
+ }
+ }
+ if (null == thrown) {
+ javaPath = packag + path.getName();
+ int loc = result.indexOf(javaPath);
+ if (-1 == loc) {
+ String m = "expected suffix " + javaPath + " in ";
+ throw new Error(m + result);
+ }
+ prefix = result.substring(0, loc);
+ }
+ }
+ return new JavaFile(path, javaPath, prefix, thrown);
+ }
+
+ /**
+ * Extract file path relative to base of package directory
+ * and directory in SAMPLE_BASE_DIR_NAME for this file.
+ * @param path the File to render from SAMPLE_BASE_DIR_NAME
+ * @return String "baseDir {path}"
+ */
+ public static String renderCodePath(File path) {
+ JavaFile javaFile = getJavaFile(path);
+ if (javaFile.thrown != null) {
+ throw new Error(javaFile.thrown.getClass()
+ + ": " + javaFile.thrown.getMessage());
+ }
+
+ String file = javaFile.javaPath; // can be null...
+ String prefix = javaFile.prefix;
+ if (prefix == null) {
+ prefix = path.getPath().replace('\\', '/');
+ }
+ int loc = prefix.lastIndexOf(SAMPLE_BASE_DIR_NAME);
+ if (-1 == loc) {
+ String m = "not after " + SAMPLE_BASE_DIR_NAME;
+ throw new IllegalArgumentException(m + "?: " + path);
+ }
+ prefix = prefix.substring(loc + 1 + SAMPLE_BASE_DIR_NAME.length());
+
+ if (file == null) {
+ int slash = prefix.lastIndexOf('/');
+ if (-1 == slash) {
+ file = prefix;
+ prefix = "";
+ } else {
+ file = prefix.substring(slash+1);
+ prefix = prefix.substring(0, slash);
+ }
+ }
+ if (prefix.endsWith("/")) {
+ prefix = prefix.substring(0, prefix.length()-1);
+ }
+ return (prefix + " " + file).trim();
+ }
+
+ public static int commonPrefix(String[] lhs, String[] rhs) {
+ final int max = smallerSize(lhs, rhs);
+ int firstDiff = 0;
+ while (firstDiff < max) {
+ if (!lhs[firstDiff].equals(rhs[firstDiff])) {
+ break;
+ }
+ firstDiff++;
+ }
+ return firstDiff;
+ }
+
+ private static int smallerSize(Object[] one, Object[] two) {
+ if ((null == one) || (null == two)) {
+ return 0;
+ }
+ return (one.length > two.length ? two.length : one.length);
+ }
+
+ public static String[] splitAnchorName(Sample sample) {
+ return splitAnchorName(sample.anchorName);
+ }
+
+ public static String[] splitAnchorName(String anchorName) {
+ ArrayList result = new ArrayList();
+ int start = 0;
+ int loc = anchorName.indexOf("-", start);
+ String next;
+ while (loc != -1) {
+ next = anchorName.substring(start, loc);
+ result.add(next);
+ start = loc+1;
+ loc = anchorName.indexOf("-", start);
+ }
+ next = anchorName.substring(start);
+ result.add(next);
+ return (String[]) result.toArray(new String[result.size()]);
+ }
+ /**
+ * Replace literals with literals in source string
+ * @param source the String to modify
+ * @param from the String[] of literals to replace
+ * @param to the String[] of literals to use when replacing
+ * @return the String source as modified by the replaces
+ */
+ public static String replace(String source, String[] from, String[] to) {
+ if ((null == source) || (0 == source.length())) {
+ return source;
+ }
+ if (from.length != to.length) {
+ throw new IllegalArgumentException("unmatched from/to");
+ }
+ StringBuffer result = new StringBuffer();
+ int LEN = source.length();
+ int start = 0;
+ for (int i = 0; i < LEN; i++) {
+ String suffix = source.substring(i);
+ for (int j = 0; j < from.length; j++) {
+ if (suffix.startsWith(from[j])) {
+ result.append(source.substring(start, i));
+ result.append(to[j]);
+ start = i + from[j].length();
+ i = start-1;
+ break;
+ }
+ }
+ }
+ if (start < source.length()) {
+ result.append(source.substring(start));
+ }
+ return result.toString();
+ }
+
+ public static void render(
+ Sample sample,
+ String fieldDelim,
+ String valueDelim,
+ StringBuffer sink) {
+ if ((null == sink) || (null == sample)) {
+ return;
+ }
+ if (null == fieldDelim) {
+ fieldDelim = "";
+ }
+ if (null == valueDelim) {
+ valueDelim = "";
+ }
+ sink.append("anchorName");
+ sink.append(valueDelim);
+ sink.append(sample.anchorName);
+ sink.append(fieldDelim);
+ sink.append("author");
+ sink.append(valueDelim);
+ sink.append(sample.author);
+ sink.append(fieldDelim);
+ sink.append("sourcePath");
+ sink.append(valueDelim);
+ sink.append(sample.sourcePath.toString());
+ sink.append(fieldDelim);
+ sink.append("startLine");
+ sink.append(valueDelim);
+ sink.append(sample.startLine);
+ sink.append(fieldDelim);
+ sink.append("endLine");
+ sink.append(valueDelim);
+ sink.append(sample.endLine);
+ sink.append(fieldDelim);
+ sink.append("sampleCode");
+ sink.append(valueDelim);
+ sink.append(sample.sampleCode.toString());
+ sink.append(fieldDelim);
+ }
+ private SampleUtil(){}
+}