/* * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code 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 General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package toolbox; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.tools.DocumentationTool.DocumentationTask; import javax.tools.DocumentationTool; import javax.tools.JavaFileManager; import javax.tools.JavaFileManager.Location; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.StandardLocation; import javax.tools.ToolProvider; import jdk.javadoc.internal.api.JavadocTool; /** * A task to configure and run the documentation tool, javadoc. */ public class JavadocTask extends AbstractTask<JavadocTask> { private boolean includeStandardOptions; private List<Path> classpath; private List<Path> sourcepath; private Path outdir; private List<String> options; private List<String> classes; private List<String> files; private List<JavaFileObject> fileObjects; private JavaFileManager fileManager; private JavadocTool jdtool; private StandardJavaFileManager internalFileManager; private Class<?> docletClass = null; // use the standard doclet by default /** * Creates a task to execute {@code javadoc} using API mode. * @param toolBox the {@code ToolBox} to use */ public JavadocTask(ToolBox toolBox) { super(toolBox, Task.Mode.API); } /** * Creates a task to execute {@code javadoc} in a specified mode. * @param toolBox the {@code ToolBox} to use * @param mode the mode to be used */ public JavadocTask(ToolBox toolBox, Task.Mode mode) { super(toolBox, mode); } /** * Sets the classpath. * @param classpath the classpath * @return this task object */ public JavadocTask classpath(String classpath) { this.classpath = Stream.of(classpath.split(File.pathSeparator)) .filter(s -> !s.isEmpty()) .map(s -> Paths.get(s)) .collect(Collectors.toList()); return this; } /** * Sets the classpath. * @param classpath the classpath * @return this task object */ public JavadocTask classpath(Path... classpath) { this.classpath = Arrays.asList(classpath); return this; } /** * Sets the classpath. * @param classpath the classpath * @return this task object */ public JavadocTask classpath(List<Path> classpath) { this.classpath = classpath; return this; } /** * Sets the sourcepath. * @param sourcepath the sourcepath * @return this task object */ public JavadocTask sourcepath(String sourcepath) { this.sourcepath = Stream.of(sourcepath.split(File.pathSeparator)) .filter(s -> !s.isEmpty()) .map(s -> Paths.get(s)) .collect(Collectors.toList()); return this; } /** * Sets the sourcepath. * @param sourcepath the sourcepath * @return this task object */ public JavadocTask sourcepath(Path... sourcepath) { this.sourcepath = Arrays.asList(sourcepath); return this; } /** * Sets the sourcepath. * @param sourcepath the sourcepath * @return this task object */ public JavadocTask sourcepath(List<Path> sourcepath) { this.sourcepath = sourcepath; return this; } /** * Sets the output directory. * @param outdir the output directory * @return this task object */ public JavadocTask outdir(String outdir) { this.outdir = Paths.get(outdir); return this; } /** * Sets the output directory. * @param outdir the output directory * @return this task object */ public JavadocTask outdir(Path outdir) { this.outdir = outdir; return this; } /** * Sets the options. * @param options the options * @return this task object */ public JavadocTask options(String... options) { this.options = Arrays.asList(options); return this; } /** * Sets the options. * @param options the options * @return this task object */ public JavadocTask options(List<String> options) { this.options = options; return this; } /** * Sets the files to be documented. * @param files the files * @return this task object */ public JavadocTask files(String... files) { this.files = Arrays.asList(files); return this; } /** * Sets the files to be documented. * @param files the files * @return this task object */ public JavadocTask files(Path... files) { this.files = Stream.of(files) .map(Path::toString) .collect(Collectors.toList()); return this; } /** * Sets the files to be documented. * @param files the files * @return this task object */ public JavadocTask files(List<Path> files) { this.files = files.stream() .map(Path::toString) .collect(Collectors.toList()); return this; } /** * Sets the sources to be documented. * Each source string is converted into an in-memory object that * can be passed directly to the tool. * @param sources the sources * @return this task object */ public JavadocTask sources(String... sources) { fileObjects = Stream.of(sources) .map(s -> new ToolBox.JavaSource(s)) .collect(Collectors.toList()); return this; } /** * Sets the file manager to be used by this task. * @param fileManager the file manager * @return this task object */ public JavadocTask fileManager(JavaFileManager fileManager) { this.fileManager = fileManager; return this; } /** * Sets the doclet class to be invoked by javadoc. * Note: this is applicable only in API mode. * @param docletClass the user specified doclet * @return this task object */ public JavadocTask docletClass(Class<?> docletClass) { this.docletClass = docletClass; return this; } /** * {@inheritDoc} * @return the name "javadoc" */ @Override public String name() { return "javadoc"; } /** * Calls the javadoc tool with the arguments as currently configured. * @return a Result object indicating the outcome of the execution * and the content of any output written to stdout, stderr, or the * main stream by the tool. */ @Override public Task.Result run() { if (mode == Task.Mode.EXEC) return runExec(); AbstractTask.WriterOutput direct = new AbstractTask.WriterOutput(); // The following are to catch output to System.out and System.err, // in case these are used instead of the primary (main) stream AbstractTask.StreamOutput sysOut = new AbstractTask.StreamOutput(System.out, System::setOut); AbstractTask.StreamOutput sysErr = new AbstractTask.StreamOutput(System.err, System::setErr); int rc; Map<Task.OutputKind, String> outputMap = new HashMap<>(); try { switch (mode == null ? Task.Mode.API : mode) { case API: rc = runAPI(direct.pw); break; case CMDLINE: rc = runCommand(direct.pw); break; default: throw new IllegalStateException(); } } catch (IOException e) { toolBox.out.println("Exception occurred: " + e); rc = 99; } finally { outputMap.put(Task.OutputKind.STDOUT, sysOut.close()); outputMap.put(Task.OutputKind.STDERR, sysErr.close()); outputMap.put(Task.OutputKind.DIRECT, direct.close()); } return checkExit(new Task.Result(toolBox, this, rc, outputMap)); } private int runAPI(PrintWriter pw) throws IOException { try { jdtool = (JavadocTool) ToolProvider.getSystemDocumentationTool(); jdtool = new JavadocTool(); if (fileManager == null) fileManager = internalFileManager = jdtool.getStandardFileManager(null, null, null); if (outdir != null) setLocationFromPaths(DocumentationTool.Location.DOCUMENTATION_OUTPUT, Collections.singletonList(outdir)); if (classpath != null) setLocationFromPaths(StandardLocation.CLASS_PATH, classpath); if (sourcepath != null) setLocationFromPaths(StandardLocation.SOURCE_PATH, sourcepath); List<String> allOpts = new ArrayList<>(); if (options != null) allOpts.addAll(options); Iterable<? extends JavaFileObject> allFiles = joinFiles(files, fileObjects); DocumentationTask task = jdtool.getTask(pw, fileManager, null, // diagnostic listener; should optionally collect diags docletClass, allOpts, allFiles); return ((DocumentationTask) task).call() ? 0 : 1; } finally { if (internalFileManager != null) internalFileManager.close(); } } private void setLocationFromPaths(Location location, List<Path> files) throws IOException { if (!(fileManager instanceof StandardJavaFileManager)) throw new IllegalStateException("not a StandardJavaFileManager"); ((StandardJavaFileManager) fileManager).setLocationFromPaths(location, files); } private int runCommand(PrintWriter pw) { List<String> args = getAllArgs(); String[] argsArray = args.toArray(new String[args.size()]); return jdk.javadoc.internal.tool.Main.execute(argsArray, pw); } private Task.Result runExec() { List<String> args = new ArrayList<>(); Path javadoc = toolBox.getJDKTool("javadoc"); args.add(javadoc.toString()); if (includeStandardOptions) { args.addAll(toolBox.split(System.getProperty("test.tool.vm.opts"), " +")); } args.addAll(getAllArgs()); String[] argsArray = args.toArray(new String[args.size()]); ProcessBuilder pb = getProcessBuilder(); pb.command(argsArray); try { return runProcess(toolBox, this, pb.start()); } catch (IOException | InterruptedException e) { throw new Error(e); } } private List<String> getAllArgs() { List<String> args = new ArrayList<>(); if (options != null) args.addAll(options); if (outdir != null) { args.add("-d"); args.add(outdir.toString()); } if (classpath != null) { args.add("-classpath"); args.add(toSearchPath(classpath)); } if (sourcepath != null) { args.add("-sourcepath"); args.add(toSearchPath(sourcepath)); } if (classes != null) args.addAll(classes); if (files != null) args.addAll(files); return args; } private String toSearchPath(List<Path> files) { return files.stream() .map(Path::toString) .collect(Collectors.joining(File.pathSeparator)); } private Iterable<? extends JavaFileObject> joinFiles( List<String> files, List<JavaFileObject> fileObjects) { if (files == null) return fileObjects; if (internalFileManager == null) internalFileManager = jdtool.getStandardFileManager(null, null, null); Iterable<? extends JavaFileObject> filesAsFileObjects = internalFileManager.getJavaFileObjectsFromStrings(files); if (fileObjects == null) return filesAsFileObjects; List<JavaFileObject> combinedList = new ArrayList<>(); for (JavaFileObject o : filesAsFileObjects) combinedList.add(o); combinedList.addAll(fileObjects); return combinedList; } }