/* * Copyright (C) 2011 4th Line GmbH, Switzerland * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.fourthline.lemma.pipeline.javadoc; import com.sun.javadoc.RootDoc; import org.fourthline.lemma.processor.ProcessorOptions; import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.CmdLineParser; import org.kohsuke.args4j.Option; import org.seamless.util.io.IO; import org.seamless.javadoc.EasyDoclet; import org.seamless.util.logging.LoggingUtil; import org.seamless.xhtml.XHTML; import org.seamless.xhtml.XHTMLParser; import org.seamless.xml.ParserException; import org.fourthline.lemma.pipeline.Pipeline; import org.fourthline.lemma.processor.Processor; import org.fourthline.lemma.processor.xhtml.JavadocCitationProcessor; import org.fourthline.lemma.processor.xhtml.TocProcessor; import org.fourthline.lemma.processor.xhtml.XRefProcessor; import org.fourthline.lemma.reader.javacode.JavacodeRawReader; import org.fourthline.lemma.reader.javadoc.AbstractJavadocReader; import org.fourthline.lemma.reader.text.PlaintextReader; import org.fourthline.lemma.reader.xml.XMLReader; import javax.xml.xpath.XPath; import java.io.File; import java.io.FileFilter; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.logging.Logger; /** * Reads an input XHTML document, processes it and returns an XHTML output document. * <p> * Call the {@link #main(String[])} method with your * {@link org.fourthline.lemma.pipeline.javadoc.XHTMLTemplateJavadocPipeline.Options} to start * Lemma. * </p> * <p> * The processors of this pipeline, in order, are: * </p> * <ol> * <li>{@link org.fourthline.lemma.processor.xhtml.JavadocCitationProcessor}</li> * <li>{@link org.fourthline.lemma.processor.xhtml.XRefProcessor}</li> * <li>{@link org.fourthline.lemma.processor.xhtml.TocProcessor}</li> * </ol> * * @author Christian Bauer */ public class XHTMLTemplateJavadocPipeline extends Pipeline<XHTML, XHTML> { static final private Logger log = Logger.getLogger(XHTMLTemplateJavadocPipeline.class.getName()); final private XHTMLParser parser = new XHTMLParser(); final private XPath xpath; final private RootDoc rootDoc; final private File[] sourceDirectories; final private boolean normalizeOutput; final private ProcessorOptions processorOptions; public XHTMLTemplateJavadocPipeline(SharedOptions options) { this(options.sourceDirectories, options.packageNames, true, options.processXRefs); } public XHTMLTemplateJavadocPipeline(List<File> sourceDirectories, List<String> packageNames, boolean normalizeOutput, boolean processXRefs) { log.info("Configuring pipeline..."); this.sourceDirectories = sourceDirectories.toArray(new File[sourceDirectories.size()]); // First sentence detection routine depends on locale in Javadoc // tool, so enforce it! Ridiculous! rootDoc = new EasyDoclet( "en_US", this.sourceDirectories, packageNames.toArray(new String[packageNames.size()]), new File[0] ).getRootDoc(); this.normalizeOutput = normalizeOutput; this.xpath = getParser().createXPath(); this.processorOptions = new ProcessorOptions(); processorOptions.processXRefs = processXRefs; } public XHTMLParser getParser() { return parser; } public XPath getXPath() { return xpath; } public File[] getSourceDirectories() { return sourceDirectories; } public RootDoc getRootDoc() { return rootDoc; } public boolean isNormalizeOutput() { return normalizeOutput; } public XHTML execute(File xhtmlTemplateFile) { XHTML template; try { log.info("Parsing initial XHTML template file: " + xhtmlTemplateFile); template = parser.parse(xhtmlTemplateFile); } catch (ParserException ex) { throw new RuntimeException(ex); } return execute(template); } @Override protected void resetContext() { super.resetContext(); getContext().put(AbstractJavadocReader.CONTEXT_ROOT_DOC, getRootDoc()); getContext().put(JavacodeRawReader.CONTEXT_SOURCE_DIRECTORIES, getSourceDirectories()); getContext().put(XMLReader.CONTEXT_SOURCE_DIRECTORIES, getSourceDirectories()); getContext().put(PlaintextReader.CONTEXT_SOURCE_DIRECTORIES, getSourceDirectories()); } @Override public XHTML execute(XHTML input) { XHTML output = super.execute(input); if (isNormalizeOutput()) output.getW3CDocument().normalizeDocument(); return output; } @Override public Processor<XHTML, XHTML>[] getProcessors() { return new Processor[]{ new JavadocCitationProcessor(getRootDoc()), new XRefProcessor(), new TocProcessor(), }; } @Override public ProcessorOptions getProcessorOptions() { return processorOptions; } public static void main(String[] args) throws Exception { LoggingUtil.loadDefaultConfiguration(); Options options = new Options(args); XHTMLTemplateJavadocPipeline pipeline = new XHTMLTemplateJavadocPipeline(options); XHTML result = pipeline.execute(options.xhtmlTemplateFile); pipeline.prepareOutputFile(options.xhtmlOutputFile, options.overwriteOutputFile); System.out.println("Writing output file: " + options.xhtmlOutputFile.getAbsolutePath()); IO.writeUTF8( options.xhtmlOutputFile, pipeline.getParser().print(result, 4, true) // TODO: Make configurable? ); } /** * Options which are shared between this bootstrap class and the Maven plugin. */ public static class SharedOptions { @Option(required = true, name = "-d", metaVar = "<path>", usage = "The base path(s) of all source and resource files.") public List<File> sourceDirectories = new ArrayList(); @Option(required = false, name = "-p", metaVar = "<package.name>", usage = "Included package, repeat option for multiple packages.") public List<String> packageNames = new ArrayList(); // TODO: Make this optional @Option(required = true, name = "-i", metaVar = "<template.xhtml>", usage = "XHTML template file.") public File xhtmlTemplateFile; @Option(name = "-xref", metaVar = "true|false", usage = "Process Javadoc {@link} tags with stable identifiers.") public boolean processXRefs = true; public SharedOptions() { } public SharedOptions(String[] args) { CmdLineParser cmdLineParser = new CmdLineParser(this); try { cmdLineParser.parseArgument(args); } catch (CmdLineException e) { System.err.println(e.getMessage()); System.err.println("USAGE: java -jar <JARFILE> [options]"); cmdLineParser.printUsage(System.err); System.exit(1); } if (!prepare()) { System.exit(1); } } /** * Called by the constructor to convert and validate the given option values. * * @return true if validation was successful. */ public boolean prepare() { for (File sourceDirectory : sourceDirectories) { if (!sourceDirectory.canRead()) { System.err.println("Source directory not found or not readable: " + sourceDirectory); return false; } if (!sourceDirectory.isDirectory()) { System.err.println("Source directory is not a directory: " + sourceDirectory); return false; } } if (!xhtmlTemplateFile.exists()) { System.err.println("XHTML template file not found: " + xhtmlTemplateFile); return false; } if (packageNames.size() == 0) { for (File sourceDirectory : sourceDirectories) { // Default to all sub-directories in source directory File[] subdirs = sourceDirectory.listFiles(new FileFilter() { public boolean accept(File file) { return file.isDirectory() && file.getName().matches("[a-zA-Z_]+"); } }); for (File subdir : subdirs) { packageNames.add(subdir.getName()); } } // Filter duplicates packageNames = new ArrayList(new LinkedHashSet(packageNames)); } return true; } } /** * Options which are specific to this bootstrap class. */ public static class Options extends SharedOptions { @Option(required = true, name = "-o", metaVar = "<result.xhtml>", usage = "XHTML output file.") public File xhtmlOutputFile; @Option(name = "-overwrite", metaVar = "true|false", usage = "Overwrite existing output file quietly.") public boolean overwriteOutputFile = false; public Options() { } public Options(String[] args) { super(args); } } }