/* Copyright (c) 2013-2017, Jesper Öqvist <jesper.oqvist@cs.lth.se> * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package org.extendj.ragdoc; import org.extendj.ast.ASTNode; import org.extendj.ast.BytecodeReader; import org.extendj.ast.CompilationUnit; import org.extendj.ast.Frontend; import org.extendj.ast.JavaParser; import org.extendj.ast.Problem; import org.extendj.ast.Program; import org.extendj.ast.TypeDecl; import se.llbit.json.JsonArray; import se.llbit.json.JsonObject; import se.llbit.json.JsonValue; import se.llbit.json.PrettyPrinter; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.Collection; import java.util.Map; /** * Generate API documentation for a JastAdd project. * * @author Jesper Öqvist <jesper.oqvist@cs.lth.se> */ public class RagDocBuilder extends Frontend { /** Generate pretty-printed JSON if in debug mode, otherwise minified JSON is generated. */ private static final boolean DEBUG = false; private JsonBuilder jsonBuilder; public static void main(String args[]) { RagDocBuilder rd = new RagDocBuilder(); int result = rd.compile(args); if (result != EXIT_SUCCESS) { System.exit(result); } } /** * @return {@code true} if documentation generation succeeds */ public int compile(String[] args) { return run(args, Program.defaultBytecodeReader(), Program.defaultJavaParser()); } @Override public int run(String[] args, BytecodeReader reader, JavaParser parser) { System.out.println("Analyzing source tree..."); long start = System.currentTimeMillis(); int result = super.run(args, reader, parser); if (result != 0) { return result; } if (program.options().hasOption("-version") || program.options().hasOption("-help") || program.options().files().isEmpty()) { // Nothing was analyzed. return result; } long time = System.currentTimeMillis() - start; System.out.format("Analysis took %.1fs.%n", time / 1000.0); try { File outputDir; if (program.options().hasValueForOption("-d")) { outputDir = new File(program.options().getValueForOption("-d")); } else { outputDir = new File("doc"); } if (!outputDir.isDirectory()) { outputDir.mkdir(); } if (!outputDir.isDirectory()) { System.err.format("Error: not a valid output directory: '%s'\n", outputDir.getName()); return EXIT_CONFIG_ERROR; } System.out.println("Writing ragdoc metadata to: " + outputDir.getPath()); System.out.println("Writing package summary."); outputJson(outputDir, "packages.json", jsonBuilder.packageIndex()); System.out.println("Writing classes..."); for (Map.Entry<TypeDecl, JsonObject> entry : jsonBuilder.typemap.entrySet()) { TypeDecl type = entry.getKey(); JsonObject typeJson = entry.getValue(); JsonArray subtypes = jsonBuilder.subtypesJson(type); if (!subtypes.isEmpty()) { typeJson.add("subtypes", jsonBuilder.subtypesJson(type)); } String fileName = type.name() + jsonBuilder.typeId(type).substring(1) + ".json"; outputJson(outputDir, fileName, typeJson); } System.out.println("Writing source files..."); for (Map.Entry<String, File> entry : jsonBuilder.sourceFiles.entrySet()) { File file = entry.getValue(); String path = entry.getKey(); path = path.replace('/', '_').replace('\\', '_').replace('.', '_'); copyFile(outputDir, path, file); } } catch (IOException e) { e.printStackTrace(); return EXIT_ERROR; } return result; } private void copyFile(File outputDir, String path, File file) throws IOException { Files.copy(file.toPath(), new File(outputDir, path).toPath(), StandardCopyOption.REPLACE_EXISTING); } private void outputJson(File dir, String fileName, JsonValue value) throws IOException { File outputFile = new File(dir, fileName); PrintStream out = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile))); JsonObject data = new JsonObject(); data.set("data", value); if (DEBUG) { PrettyPrinter pp = new PrettyPrinter(" ", out); data.prettyPrint(pp); } else { out.print(data.toCompactString()); } out.close(); } @Override protected int processCompilationUnit(CompilationUnit unit) { if (unit.fromSource()) { try { Collection<Problem> parseErrors = unit.parseErrors(); if (!parseErrors.isEmpty()) { processErrors(parseErrors, unit); return EXIT_ERROR; } else { jsonBuilder.addCompilationUnit(unit); } } catch (Throwable t) { System.err.println("Errors:"); System.err.println("Fatal exception while processing " + unit.pathName() + ":"); t.printStackTrace(System.err); return EXIT_ERROR; } } return EXIT_SUCCESS; } @Override protected String name() { return "rd-builder"; } @Override protected String version() { return "1.3.0"; } @Override protected void initOptions() { super.initOptions(); program.options().addKeyValueOption("-ragroot"); program.options().addKeyOption("-excludeGenerated"); } @Override protected int processArgs(String[] args) { int result = super.processArgs(args); if (result == 0) { // TODO add package exclude argument. File ragRoot; if (program.options().hasValueForOption("-ragroot")) { ragRoot = new File(program.options().getValueForOption("-ragroot")); } else { System.out.println("No ragroot directory specified. Using default."); File userDir = new File(System.getProperty("user.dir")); if (userDir.isDirectory()) { ragRoot = userDir; } else { ragRoot = new File("."); } } System.out.println("Using ragroot directory: " + ragRoot.getAbsolutePath()); boolean excludeGenerated = program.options().hasOption("-excludeGenerated"); jsonBuilder = new JsonBuilder(ragRoot, excludeGenerated); ASTNode.jsonBuilder = jsonBuilder; } return result; } }