SampleGatherer.java
/* *******************************************************************
* Copyright (c) 2003 Contributors.
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License v 2.0
* which accompanies this distribution and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
*
* 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.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.Reader;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
/**
* 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
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[] { "<pre>", "</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 {
if ((null == args) || (0 == args.length)) {
String cname = SampleGatherer.class.getName();
System.err.println("java " + cname + " [dir|file]");
return;
}
Samples result = new Samples();
for (String arg : args) {
result = gather(new File(arg), 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 (File file : files) {
doGather(file, sink);
}
}
}
private static boolean isSource(File file) {
if ((null == file) || !file.isFile() || !file.canRead()) {
return false;
}
String path = file.getName().toLowerCase();
String[] suffixes = Sample.Kind.SOURCE_SUFFIXES;
for (String suffix : suffixes) {
if (path.endsWith(suffix)) {
return true;
}
}
return false;
}
private static void gatherFromFile(final File source, final Samples sink)
throws IOException {
Reader reader = null;
try {
String author = null;
StringBuilder sampleCode = new StringBuilder();
String anchorName = null;
String anchorTitle = null;
ArrayList<String> 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,
flags.toArray(new String[0]));
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 {
private static final long serialVersionUID = -1l;
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<Sample> NAME_SOURCE_COMPARER = new Comparator<Sample>() {
public int compare(Sample left, Sample right) {
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<Sample> AUTHOR_NAME_SOURCE_COMPARER = new Comparator<Sample>() {
public int compare(Sample left, Sample right) {
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(left,right);
}
};
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<String> 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.emptyList();
} else {
this.flags = Collections.unmodifiableList(Arrays.asList(flags));
}
}
public String toString() {
return sampleCode;
}
public static class Kind {
/** lowercase source suffixes identify files to gather samples from */
public static final String[] SOURCE_SUFFIXES = new String[]
{ ".java", ".aj", ".sh", ".ksh",
".txt", ".text", ".html", ".htm", ".xml" };
static final Kind XML = new 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;
}
if (name.endsWith(".xml")) {
return XML;
}
return OTHER;
}
private Kind() {
}
}
}
/**
* type-safe Collection of samples.
*/
class Samples {
private ArrayList<Sample> 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<Sample> getSortedSamples() {
return getSortedSamples(Sample.NAME_SOURCE_COMPARER);
}
List<Sample> getSortedSamples(Comparator<Sample> comparer) {
List<Sample> result = new ArrayList<>(samples);
result.sort(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 =
"<p>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></p>"
+ "<p>Find complete source files in the AspectJ CVS repository at "
+ "<code>org.aspectj/modules/docs/sandbox</code>. "
+ "For instructions on downloading code from the CVS repository, "
+ "see the <a href=\"doc/faq.html#q:buildingsource\">FAQ entry "
+ "\"buildingsource\"</a>.</p>";
public static final String COPYRIGHT =
"<p><small>Copyright 2003 Contributors. All Rights Reserved. "
+ "This sample code is made available under the Eclipse Public "
+ "License v 2.0 available at "
+ "<a href=\"https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt\">"
+ "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt</a>. "
+ "Contributors are listed in this document as authors. "
+ "Permission to republish portions of this sample code "
+ "is hereby granted if the publication acknowledges "
+ "the author by name and "
+ "the source by reference to the AspectJ project home page "
+ "at https://eclipse.org/aspectj.</small></p>"
+ 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<Sample> list = samples.getSortedSamples();
String anchorName = null;
for (Sample sample : list) {
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 if (sample.kind == Sample.Kind.XML) {
renderXML(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>| " + currentAuthor);
sampleSection.append(EOL);
}
sampleSection.append(" | ");
sampleSection.append(SampleUtil.renderCodePath(sample.sourcePath));
sampleSection.append(":" + sample.startLine);
sampleSection.append(" |");
sampleSection.append(EOL);
sampleSection.append("<p>");
sampleSection.append(EOL);
if (doFlags) {
boolean flagHeaderDone = false;
for (String flag : sample.flags) {
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 renderXML(Sample sample) {
renderStandardHeader(sample);
sampleSection.append(" <pre>");
sampleSection.append(EOL);
sampleSection.append(prepareXMLSample(sample.sampleCode));
sampleSection.append(EOL);
sampleSection.append(" </pre>");
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("<p><small>Generated on ");
sink.append(DateFormat.getDateInstance().format(new Date()));
sink.append(" by SamplesGatherer</small>");
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 prepareXMLSample(String sampleCode) {
String[] from = new String[] {"\t", "<"};
String[] to = new String[] {" ", "<"};
return (SampleUtil.replace(sampleCode, from, to));
}
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[] { "<pre>", "</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
StringBuilder branchAnchor = new StringBuilder();
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<Sample> list = samples.getSortedSamples(Sample.AUTHOR_NAME_SOURCE_COMPARER);
String lastAuthor = null;
for (Sample sample : list) {
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<Sample> sortedSamples = result.getSortedSamples();
int i = 0;
for (Sample sample : sortedSamples) {
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<String> 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 result.toArray(new String[0]);
}
/**
* 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");
}
StringBuilder result = new StringBuilder();
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(){}
}