1/* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24package toolbox; 25 26import java.io.File; 27import java.io.IOException; 28import java.io.PrintWriter; 29import java.nio.file.Path; 30import java.nio.file.Paths; 31import java.util.ArrayList; 32import java.util.Arrays; 33import java.util.Collections; 34import java.util.HashMap; 35import java.util.List; 36import java.util.Map; 37import java.util.stream.Collectors; 38import java.util.stream.Stream; 39 40import javax.tools.DocumentationTool.DocumentationTask; 41import javax.tools.DocumentationTool; 42import javax.tools.JavaFileManager; 43import javax.tools.JavaFileManager.Location; 44import javax.tools.JavaFileObject; 45import javax.tools.StandardJavaFileManager; 46import javax.tools.StandardLocation; 47import javax.tools.ToolProvider; 48 49import jdk.javadoc.internal.api.JavadocTool; 50 51/** 52 * A task to configure and run the documentation tool, javadoc. 53 */ 54public class JavadocTask extends AbstractTask<JavadocTask> { 55 private boolean includeStandardOptions; 56 private List<Path> classpath; 57 private List<Path> sourcepath; 58 private Path outdir; 59 private List<String> options; 60 private List<String> classes; 61 private List<String> files; 62 private List<JavaFileObject> fileObjects; 63 private JavaFileManager fileManager; 64 65 private JavadocTool jdtool; 66 private StandardJavaFileManager internalFileManager; 67 private Class<?> docletClass = null; // use the standard doclet by default 68 69 /** 70 * Creates a task to execute {@code javadoc} using API mode. 71 * @param toolBox the {@code ToolBox} to use 72 */ 73 public JavadocTask(ToolBox toolBox) { 74 super(toolBox, Task.Mode.API); 75 } 76 77 /** 78 * Creates a task to execute {@code javadoc} in a specified mode. 79 * @param toolBox the {@code ToolBox} to use 80 * @param mode the mode to be used 81 */ 82 public JavadocTask(ToolBox toolBox, Task.Mode mode) { 83 super(toolBox, mode); 84 } 85 86 /** 87 * Sets the classpath. 88 * @param classpath the classpath 89 * @return this task object 90 */ 91 public JavadocTask classpath(String classpath) { 92 this.classpath = Stream.of(classpath.split(File.pathSeparator)) 93 .filter(s -> !s.isEmpty()) 94 .map(s -> Paths.get(s)) 95 .collect(Collectors.toList()); 96 return this; 97 } 98 99 /** 100 * Sets the classpath. 101 * @param classpath the classpath 102 * @return this task object 103 */ 104 public JavadocTask classpath(Path... classpath) { 105 this.classpath = Arrays.asList(classpath); 106 return this; 107 } 108 109 /** 110 * Sets the classpath. 111 * @param classpath the classpath 112 * @return this task object 113 */ 114 public JavadocTask classpath(List<Path> classpath) { 115 this.classpath = classpath; 116 return this; 117 } 118 119 /** 120 * Sets the sourcepath. 121 * @param sourcepath the sourcepath 122 * @return this task object 123 */ 124 public JavadocTask sourcepath(String sourcepath) { 125 this.sourcepath = Stream.of(sourcepath.split(File.pathSeparator)) 126 .filter(s -> !s.isEmpty()) 127 .map(s -> Paths.get(s)) 128 .collect(Collectors.toList()); 129 return this; 130 } 131 132 /** 133 * Sets the sourcepath. 134 * @param sourcepath the sourcepath 135 * @return this task object 136 */ 137 public JavadocTask sourcepath(Path... sourcepath) { 138 this.sourcepath = Arrays.asList(sourcepath); 139 return this; 140 } 141 142 /** 143 * Sets the sourcepath. 144 * @param sourcepath the sourcepath 145 * @return this task object 146 */ 147 public JavadocTask sourcepath(List<Path> sourcepath) { 148 this.sourcepath = sourcepath; 149 return this; 150 } 151 152 /** 153 * Sets the output directory. 154 * @param outdir the output directory 155 * @return this task object 156 */ 157 public JavadocTask outdir(String outdir) { 158 this.outdir = Paths.get(outdir); 159 return this; 160 } 161 162 /** 163 * Sets the output directory. 164 * @param outdir the output directory 165 * @return this task object 166 */ 167 public JavadocTask outdir(Path outdir) { 168 this.outdir = outdir; 169 return this; 170 } 171 172 /** 173 * Sets the options. 174 * @param options the options 175 * @return this task object 176 */ 177 public JavadocTask options(String... options) { 178 this.options = Arrays.asList(options); 179 return this; 180 } 181 182 /** 183 * Sets the options. 184 * @param options the options 185 * @return this task object 186 */ 187 public JavadocTask options(List<String> options) { 188 this.options = options; 189 return this; 190 } 191 192 /** 193 * Sets the files to be documented. 194 * @param files the files 195 * @return this task object 196 */ 197 public JavadocTask files(String... files) { 198 this.files = Arrays.asList(files); 199 return this; 200 } 201 202 /** 203 * Sets the files to be documented. 204 * @param files the files 205 * @return this task object 206 */ 207 public JavadocTask files(Path... files) { 208 this.files = Stream.of(files) 209 .map(Path::toString) 210 .collect(Collectors.toList()); 211 return this; 212 } 213 214 /** 215 * Sets the files to be documented. 216 * @param files the files 217 * @return this task object 218 */ 219 public JavadocTask files(List<Path> files) { 220 this.files = files.stream() 221 .map(Path::toString) 222 .collect(Collectors.toList()); 223 return this; 224 } 225 226 /** 227 * Sets the sources to be documented. 228 * Each source string is converted into an in-memory object that 229 * can be passed directly to the tool. 230 * @param sources the sources 231 * @return this task object 232 */ 233 public JavadocTask sources(String... sources) { 234 fileObjects = Stream.of(sources) 235 .map(s -> new ToolBox.JavaSource(s)) 236 .collect(Collectors.toList()); 237 return this; 238 } 239 240 /** 241 * Sets the file manager to be used by this task. 242 * @param fileManager the file manager 243 * @return this task object 244 */ 245 public JavadocTask fileManager(JavaFileManager fileManager) { 246 this.fileManager = fileManager; 247 return this; 248 } 249 250 /** 251 * Sets the doclet class to be invoked by javadoc. 252 * Note: this is applicable only in API mode. 253 * @param docletClass the user specified doclet 254 * @return this task object 255 */ 256 public JavadocTask docletClass(Class<?> docletClass) { 257 this.docletClass = docletClass; 258 return this; 259 } 260 261 /** 262 * {@inheritDoc} 263 * @return the name "javadoc" 264 */ 265 @Override 266 public String name() { 267 return "javadoc"; 268 } 269 270 /** 271 * Calls the javadoc tool with the arguments as currently configured. 272 * @return a Result object indicating the outcome of the execution 273 * and the content of any output written to stdout, stderr, or the 274 * main stream by the tool. 275 */ 276 @Override 277 public Task.Result run() { 278 if (mode == Task.Mode.EXEC) 279 return runExec(); 280 281 AbstractTask.WriterOutput direct = new AbstractTask.WriterOutput(); 282 // The following are to catch output to System.out and System.err, 283 // in case these are used instead of the primary (main) stream 284 AbstractTask.StreamOutput sysOut = new AbstractTask.StreamOutput(System.out, System::setOut); 285 AbstractTask.StreamOutput sysErr = new AbstractTask.StreamOutput(System.err, System::setErr); 286 int rc; 287 Map<Task.OutputKind, String> outputMap = new HashMap<>(); 288 try { 289 switch (mode == null ? Task.Mode.API : mode) { 290 case API: 291 rc = runAPI(direct.pw); 292 break; 293 case CMDLINE: 294 rc = runCommand(direct.pw); 295 break; 296 default: 297 throw new IllegalStateException(); 298 } 299 } catch (IOException e) { 300 toolBox.out.println("Exception occurred: " + e); 301 rc = 99; 302 } finally { 303 outputMap.put(Task.OutputKind.STDOUT, sysOut.close()); 304 outputMap.put(Task.OutputKind.STDERR, sysErr.close()); 305 outputMap.put(Task.OutputKind.DIRECT, direct.close()); 306 } 307 return checkExit(new Task.Result(toolBox, this, rc, outputMap)); 308 } 309 310 private int runAPI(PrintWriter pw) throws IOException { 311 try { 312 jdtool = (JavadocTool) ToolProvider.getSystemDocumentationTool(); 313 jdtool = new JavadocTool(); 314 315 if (fileManager == null) 316 fileManager = internalFileManager = jdtool.getStandardFileManager(null, null, null); 317 if (outdir != null) 318 setLocationFromPaths(DocumentationTool.Location.DOCUMENTATION_OUTPUT, 319 Collections.singletonList(outdir)); 320 if (classpath != null) 321 setLocationFromPaths(StandardLocation.CLASS_PATH, classpath); 322 if (sourcepath != null) 323 setLocationFromPaths(StandardLocation.SOURCE_PATH, sourcepath); 324 List<String> allOpts = new ArrayList<>(); 325 if (options != null) 326 allOpts.addAll(options); 327 328 Iterable<? extends JavaFileObject> allFiles = joinFiles(files, fileObjects); 329 DocumentationTask task = jdtool.getTask(pw, 330 fileManager, 331 null, // diagnostic listener; should optionally collect diags 332 docletClass, 333 allOpts, 334 allFiles); 335 return ((DocumentationTask) task).call() ? 0 : 1; 336 } finally { 337 if (internalFileManager != null) 338 internalFileManager.close(); 339 } 340 } 341 342 private void setLocationFromPaths(Location location, List<Path> files) throws IOException { 343 if (!(fileManager instanceof StandardJavaFileManager)) 344 throw new IllegalStateException("not a StandardJavaFileManager"); 345 ((StandardJavaFileManager) fileManager).setLocationFromPaths(location, files); 346 } 347 348 private int runCommand(PrintWriter pw) { 349 List<String> args = getAllArgs(); 350 String[] argsArray = args.toArray(new String[args.size()]); 351 return jdk.javadoc.internal.tool.Main.execute(argsArray, pw); 352 } 353 354 private Task.Result runExec() { 355 List<String> args = new ArrayList<>(); 356 Path javadoc = toolBox.getJDKTool("javadoc"); 357 args.add(javadoc.toString()); 358 if (includeStandardOptions) { 359 args.addAll(toolBox.split(System.getProperty("test.tool.vm.opts"), " +")); 360 } 361 args.addAll(getAllArgs()); 362 363 String[] argsArray = args.toArray(new String[args.size()]); 364 ProcessBuilder pb = getProcessBuilder(); 365 pb.command(argsArray); 366 try { 367 return runProcess(toolBox, this, pb.start()); 368 } catch (IOException | InterruptedException e) { 369 throw new Error(e); 370 } 371 } 372 373 private List<String> getAllArgs() { 374 List<String> args = new ArrayList<>(); 375 if (options != null) 376 args.addAll(options); 377 if (outdir != null) { 378 args.add("-d"); 379 args.add(outdir.toString()); 380 } 381 if (classpath != null) { 382 args.add("-classpath"); 383 args.add(toSearchPath(classpath)); 384 } 385 if (sourcepath != null) { 386 args.add("-sourcepath"); 387 args.add(toSearchPath(sourcepath)); 388 } 389 if (classes != null) 390 args.addAll(classes); 391 if (files != null) 392 args.addAll(files); 393 394 return args; 395 } 396 397 private String toSearchPath(List<Path> files) { 398 return files.stream() 399 .map(Path::toString) 400 .collect(Collectors.joining(File.pathSeparator)); 401 } 402 403 private Iterable<? extends JavaFileObject> joinFiles( 404 List<String> files, List<JavaFileObject> fileObjects) { 405 if (files == null) 406 return fileObjects; 407 if (internalFileManager == null) 408 internalFileManager = jdtool.getStandardFileManager(null, null, null); 409 Iterable<? extends JavaFileObject> filesAsFileObjects = 410 internalFileManager.getJavaFileObjectsFromStrings(files); 411 if (fileObjects == null) 412 return filesAsFileObjects; 413 List<JavaFileObject> combinedList = new ArrayList<>(); 414 for (JavaFileObject o : filesAsFileObjects) 415 combinedList.add(o); 416 combinedList.addAll(fileObjects); 417 return combinedList; 418 } 419} 420