blob: 41ee2c4a57732b51e86d457779e6029e172d80f2 [file] [log] [blame]
J. Dukef57b87e2007-12-01 00:00:00 +00001/*
Jan Lahodaec398152017-01-20 09:26:49 +01002 * Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved.
J. Dukef57b87e2007-12-01 00:00:00 +00003 * 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
Kelly O'Hairbd4f4be2010-05-25 15:54:51 -07007 * published by the Free Software Foundation. Oracle designates this
J. Dukef57b87e2007-12-01 00:00:00 +00008 * particular file as subject to the "Classpath" exception as provided
Kelly O'Hairbd4f4be2010-05-25 15:54:51 -07009 * by Oracle in the LICENSE file that accompanied this code.
J. Dukef57b87e2007-12-01 00:00:00 +000010 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
Kelly O'Hairbd4f4be2010-05-25 15:54:51 -070021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
J. Dukef57b87e2007-12-01 00:00:00 +000024 */
25
Jonathan Gibbonsdaca0042016-05-09 16:52:15 -070026package com.sun.tools.javadoc.main;
J. Dukef57b87e2007-12-01 00:00:00 +000027
Jonathan Gibbons981f0252009-01-20 15:17:45 -080028import java.io.File;
29import java.io.IOException;
J. Dukef57b87e2007-12-01 00:00:00 +000030import java.util.Collection;
Kumar Srinivasan28143872016-07-20 12:49:32 -070031import java.util.Collections;
Jonathan Gibbons981f0252009-01-20 15:17:45 -080032import java.util.EnumSet;
Kumar Srinivasanc7480b62014-02-14 17:28:07 -080033import java.util.HashSet;
Jonathan Gibbons966fc672015-08-10 12:27:29 -070034import java.util.LinkedHashMap;
35import java.util.LinkedHashSet;
Jonathan Gibbons981f0252009-01-20 15:17:45 -080036import java.util.Map;
37import java.util.Set;
Jonathan Gibbons31e63402014-05-18 19:59:10 -070038
Jonathan Gibbons966fc672015-08-10 12:27:29 -070039import javax.tools.JavaFileManager;
Jonathan Gibbons981f0252009-01-20 15:17:45 -080040import javax.tools.JavaFileManager.Location;
41import javax.tools.JavaFileObject;
42import javax.tools.StandardJavaFileManager;
43import javax.tools.StandardLocation;
J. Dukef57b87e2007-12-01 00:00:00 +000044
Jonathan Gibbons31e63402014-05-18 19:59:10 -070045import com.sun.tools.javac.code.ClassFinder;
Andreas Lundblad4c307782015-04-28 22:25:36 +020046import com.sun.tools.javac.code.Symbol.Completer;
Alan Bateman001ebb32016-03-17 19:04:28 +000047import com.sun.tools.javac.code.Symbol.ModuleSymbol;
Kumar Srinivasan28143872016-07-20 12:49:32 -070048import com.sun.tools.javac.code.Symbol.PackageSymbol;
Jonathan Gibbons31e63402014-05-18 19:59:10 -070049import com.sun.tools.javac.comp.Enter;
Jonathan Gibbons981f0252009-01-20 15:17:45 -080050import com.sun.tools.javac.tree.JCTree;
51import com.sun.tools.javac.tree.JCTree.JCClassDecl;
52import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
53import com.sun.tools.javac.util.Abort;
54import com.sun.tools.javac.util.Context;
55import com.sun.tools.javac.util.List;
56import com.sun.tools.javac.util.ListBuffer;
Kumar Srinivasan28143872016-07-20 12:49:32 -070057import com.sun.tools.javac.util.Name;
J. Dukef57b87e2007-12-01 00:00:00 +000058
J. Dukef57b87e2007-12-01 00:00:00 +000059
60/**
61 * This class could be the main entry point for Javadoc when Javadoc is used as a
62 * component in a larger software system. It provides operations to
63 * construct a new javadoc processor, and to run it on a set of source
64 * files.
Jonathan Gibbons680ffeb2012-10-10 16:48:21 -070065 *
66 * <p><b>This is NOT part of any supported API.
67 * If you write code that depends on this, you do so at your own risk.
68 * This code and its internal interfaces are subject to change or
69 * deletion without notice.</b>
70 *
J. Dukef57b87e2007-12-01 00:00:00 +000071 * @author Neal Gafter
72 */
Jonathan Gibbonscfc5ca52016-05-27 13:06:58 -070073@Deprecated
J. Dukef57b87e2007-12-01 00:00:00 +000074public class JavadocTool extends com.sun.tools.javac.main.JavaCompiler {
75 DocEnv docenv;
76
J. Dukef57b87e2007-12-01 00:00:00 +000077 final Messager messager;
Jonathan Gibbons31e63402014-05-18 19:59:10 -070078 final ClassFinder javadocFinder;
79 final Enter javadocEnter;
Kumar Srinivasan7a7a57b2014-04-16 18:15:48 -070080 final Set<JavaFileObject> uniquefiles;
J. Dukef57b87e2007-12-01 00:00:00 +000081
82 /**
83 * Construct a new JavaCompiler processor, using appropriately
84 * extended phases of the underlying compiler.
85 */
86 protected JavadocTool(Context context) {
87 super(context);
J. Dukef57b87e2007-12-01 00:00:00 +000088 messager = Messager.instance0(context);
Jonathan Gibbons31e63402014-05-18 19:59:10 -070089 javadocFinder = JavadocClassFinder.instance(context);
90 javadocEnter = JavadocEnter.instance(context);
Kumar Srinivasan7a7a57b2014-04-16 18:15:48 -070091 uniquefiles = new HashSet<>();
J. Dukef57b87e2007-12-01 00:00:00 +000092 }
93
94 /**
95 * For javadoc, the parser needs to keep comments. Overrides method from JavaCompiler.
96 */
Alan Bateman001ebb32016-03-17 19:04:28 +000097 @Override
J. Dukef57b87e2007-12-01 00:00:00 +000098 protected boolean keepComments() {
99 return true;
100 }
101
102 /**
103 * Construct a new javadoc tool.
104 */
105 public static JavadocTool make0(Context context) {
Alan Bateman001ebb32016-03-17 19:04:28 +0000106 // force the use of Javadoc's class finder
107 JavadocClassFinder.preRegister(context);
J. Dukef57b87e2007-12-01 00:00:00 +0000108
Alan Bateman001ebb32016-03-17 19:04:28 +0000109 // force the use of Javadoc's own enter phase
110 JavadocEnter.preRegister(context);
J. Dukef57b87e2007-12-01 00:00:00 +0000111
Alan Bateman001ebb32016-03-17 19:04:28 +0000112 // force the use of Javadoc's own member enter phase
113 JavadocMemberEnter.preRegister(context);
J. Dukef57b87e2007-12-01 00:00:00 +0000114
Alan Bateman001ebb32016-03-17 19:04:28 +0000115 // force the use of Javadoc's own todo phase
116 JavadocTodo.preRegister(context);
J. Dukef57b87e2007-12-01 00:00:00 +0000117
Alan Bateman001ebb32016-03-17 19:04:28 +0000118 // force the use of Messager as a Log
119 Messager.instance0(context);
J. Dukef57b87e2007-12-01 00:00:00 +0000120
Alan Bateman001ebb32016-03-17 19:04:28 +0000121 return new JavadocTool(context);
J. Dukef57b87e2007-12-01 00:00:00 +0000122 }
123
124 public RootDocImpl getRootDocImpl(String doclocale,
125 String encoding,
126 ModifierFilter filter,
Jonathan Gibbons966fc672015-08-10 12:27:29 -0700127 List<String> args,
J. Dukef57b87e2007-12-01 00:00:00 +0000128 List<String[]> options,
Jonathan Gibbonsb3a3f7c2012-11-15 23:07:24 -0800129 Iterable<? extends JavaFileObject> fileObjects,
J. Dukef57b87e2007-12-01 00:00:00 +0000130 boolean breakiterator,
131 List<String> subPackages,
132 List<String> excludedPackages,
133 boolean docClasses,
134 boolean legacyDoclet,
135 boolean quiet) throws IOException {
136 docenv = DocEnv.instance(context);
137 docenv.showAccess = filter;
Jonathan Gibbons981f0252009-01-20 15:17:45 -0800138 docenv.quiet = quiet;
J. Dukef57b87e2007-12-01 00:00:00 +0000139 docenv.breakiterator = breakiterator;
140 docenv.setLocale(doclocale);
141 docenv.setEncoding(encoding);
142 docenv.docClasses = docClasses;
143 docenv.legacyDoclet = legacyDoclet;
Jonathan Gibbons31e63402014-05-18 19:59:10 -0700144
Andreas Lundblad4c307782015-04-28 22:25:36 +0200145 javadocFinder.sourceCompleter = docClasses ? Completer.NULL_COMPLETER : sourceCompleter;
J. Dukef57b87e2007-12-01 00:00:00 +0000146
Jonathan Gibbons966fc672015-08-10 12:27:29 -0700147 if (docClasses) {
148 // If -Xclasses is set, the args should be a series of class names
149 for (String arg: args) {
150 if (!isValidPackageName(arg)) // checks
151 docenv.error(null, "main.illegal_class_name", arg);
152 }
153 if (messager.nerrors() != 0) {
154 return null;
155 }
156 return new RootDocImpl(docenv, args, options);
157 }
158
Brian Goetz6b6b4882013-12-18 16:05:18 -0500159 ListBuffer<JCCompilationUnit> classTrees = new ListBuffer<>();
Jonathan Gibbons966fc672015-08-10 12:27:29 -0700160 Set<String> includedPackages = new LinkedHashSet<>();
J. Dukef57b87e2007-12-01 00:00:00 +0000161
162 try {
Jonathan Gibbonsb3a3f7c2012-11-15 23:07:24 -0800163 StandardJavaFileManager fm = docenv.fileManager instanceof StandardJavaFileManager
164 ? (StandardJavaFileManager) docenv.fileManager : null;
Jonathan Gibbons966fc672015-08-10 12:27:29 -0700165 Set<String> packageNames = new LinkedHashSet<>();
166 // Normally, the args should be a series of package names or file names.
167 // Parse the files and collect the package names.
168 for (String arg: args) {
169 if (fm != null && arg.endsWith(".java") && new File(arg).exists()) {
Alan Bateman001ebb32016-03-17 19:04:28 +0000170 if (new File(arg).getName().equals("module-info.java")) {
171 docenv.warning(null, "main.file_ignored", arg);
172 } else {
173 parse(fm.getJavaFileObjects(arg), classTrees, true);
174 }
Jonathan Gibbons966fc672015-08-10 12:27:29 -0700175 } else if (isValidPackageName(arg)) {
176 packageNames.add(arg);
177 } else if (arg.endsWith(".java")) {
Jonathan Gibbonsb3a3f7c2012-11-15 23:07:24 -0800178 if (fm == null)
179 throw new IllegalArgumentException();
180 else
Jonathan Gibbons966fc672015-08-10 12:27:29 -0700181 docenv.error(null, "main.file_not_found", arg);
J. Dukef57b87e2007-12-01 00:00:00 +0000182 } else {
Jonathan Gibbons966fc672015-08-10 12:27:29 -0700183 docenv.error(null, "main.illegal_package_name", arg);
J. Dukef57b87e2007-12-01 00:00:00 +0000184 }
185 }
Jonathan Gibbons966fc672015-08-10 12:27:29 -0700186
187 // Parse file objects provide via the DocumentationTool API
188 parse(fileObjects, classTrees, true);
Alan Bateman001ebb32016-03-17 19:04:28 +0000189
Jan Lahodaec9ca292016-08-11 17:26:12 +0200190 modules.initModules(classTrees.toList());
Jonathan Gibbons966fc672015-08-10 12:27:29 -0700191
192 // Build up the complete list of any packages to be documented
Vicente Romero58de9402016-05-27 19:45:57 -0400193 Location location = modules.multiModuleMode ? StandardLocation.MODULE_SOURCE_PATH
Alan Bateman001ebb32016-03-17 19:04:28 +0000194 : docenv.fileManager.hasLocation(StandardLocation.SOURCE_PATH) ? StandardLocation.SOURCE_PATH
195 : StandardLocation.CLASS_PATH;
Jonathan Gibbons966fc672015-08-10 12:27:29 -0700196
197 PackageTable t = new PackageTable(docenv.fileManager, location)
198 .packages(packageNames)
199 .subpackages(subPackages, excludedPackages);
200
201 includedPackages = t.getIncludedPackages();
202
203 // Parse the files in the packages to be documented
204 ListBuffer<JCCompilationUnit> packageTrees = new ListBuffer<>();
205 for (String packageName: includedPackages) {
206 List<JavaFileObject> files = t.getFiles(packageName);
207 docenv.notice("main.Loading_source_files_for_package", packageName);
208
209 if (files.isEmpty())
210 messager.warning(Messager.NOPOS, "main.no_source_files_for_package", packageName);
211 parse(files, packageTrees, false);
Jonathan Gibbonsb3a3f7c2012-11-15 23:07:24 -0800212 }
Alan Bateman001ebb32016-03-17 19:04:28 +0000213 modules.enter(packageTrees.toList(), null);
J. Dukef57b87e2007-12-01 00:00:00 +0000214
Jonathan Gibbons966fc672015-08-10 12:27:29 -0700215 if (messager.nerrors() != 0) {
216 return null;
J. Dukef57b87e2007-12-01 00:00:00 +0000217 }
Jonathan Gibbons966fc672015-08-10 12:27:29 -0700218
219 // Enter symbols for all files
220 docenv.notice("main.Building_tree");
221 javadocEnter.main(classTrees.toList().appendList(packageTrees.toList()));
J. Dukef57b87e2007-12-01 00:00:00 +0000222 } catch (Abort ex) {}
223
Jonathan Gibbons981f0252009-01-20 15:17:45 -0800224 if (messager.nerrors() != 0)
225 return null;
J. Dukef57b87e2007-12-01 00:00:00 +0000226
Jonathan Gibbons966fc672015-08-10 12:27:29 -0700227 return new RootDocImpl(docenv, listClasses(classTrees.toList()), List.from(includedPackages), options);
J. Dukef57b87e2007-12-01 00:00:00 +0000228 }
229
230 /** Is the given string a valid package name? */
231 boolean isValidPackageName(String s) {
232 int index;
233 while ((index = s.indexOf('.')) != -1) {
234 if (!isValidClassName(s.substring(0, index))) return false;
235 s = s.substring(index+1);
236 }
237 return isValidClassName(s);
238 }
239
Jonathan Gibbons966fc672015-08-10 12:27:29 -0700240 private void parse(Iterable<? extends JavaFileObject> files, ListBuffer<JCCompilationUnit> trees,
Kumar Srinivasan7a7a57b2014-04-16 18:15:48 -0700241 boolean trace) {
Jonathan Gibbons966fc672015-08-10 12:27:29 -0700242 for (JavaFileObject fo: files) {
243 if (uniquefiles.add(fo)) { // ignore duplicates
244 if (trace)
245 docenv.notice("main.Loading_source_file", fo.getName());
246 trees.append(parse(fo));
Jonathan Gibbons981f0252009-01-20 15:17:45 -0800247 }
248 }
249 }
250
J. Dukef57b87e2007-12-01 00:00:00 +0000251 /** Are surrogates supported?
252 */
253 final static boolean surrogatesSupported = surrogatesSupported();
254 private static boolean surrogatesSupported() {
255 try {
256 boolean b = Character.isHighSurrogate('a');
257 return true;
258 } catch (NoSuchMethodError ex) {
259 return false;
260 }
261 }
262
263 /**
264 * Return true if given file name is a valid class name
265 * (including "package-info").
Jonathan Gibbonsb155b542012-10-09 19:31:58 -0700266 * @param s the name of the class to check.
J. Dukef57b87e2007-12-01 00:00:00 +0000267 * @return true if given class name is a valid class name
268 * and false otherwise.
269 */
270 public static boolean isValidClassName(String s) {
271 if (s.length() < 1) return false;
272 if (s.equals("package-info")) return true;
273 if (surrogatesSupported) {
274 int cp = s.codePointAt(0);
275 if (!Character.isJavaIdentifierStart(cp))
276 return false;
277 for (int j=Character.charCount(cp); j<s.length(); j+=Character.charCount(cp)) {
278 cp = s.codePointAt(j);
279 if (!Character.isJavaIdentifierPart(cp))
280 return false;
281 }
282 } else {
283 if (!Character.isJavaIdentifierStart(s.charAt(0)))
284 return false;
285 for (int j=1; j<s.length(); j++)
286 if (!Character.isJavaIdentifierPart(s.charAt(j)))
287 return false;
288 }
289 return true;
290 }
291
292 /**
293 * From a list of top level trees, return the list of contained class definitions
294 */
295 List<JCClassDecl> listClasses(List<JCCompilationUnit> trees) {
Brian Goetz6b6b4882013-12-18 16:05:18 -0500296 ListBuffer<JCClassDecl> result = new ListBuffer<>();
J. Dukef57b87e2007-12-01 00:00:00 +0000297 for (JCCompilationUnit t : trees) {
298 for (JCTree def : t.defs) {
Vicente Romero399b71b2011-11-08 11:51:05 -0800299 if (def.hasTag(JCTree.Tag.CLASSDEF))
J. Dukef57b87e2007-12-01 00:00:00 +0000300 result.append((JCClassDecl)def);
301 }
302 }
303 return result.toList();
304 }
305
Jonathan Gibbons966fc672015-08-10 12:27:29 -0700306 /**
307 * A table to manage included and excluded packages.
308 */
Alan Bateman001ebb32016-03-17 19:04:28 +0000309 class PackageTable {
Jonathan Gibbons966fc672015-08-10 12:27:29 -0700310 private final Map<String, Entry> entries = new LinkedHashMap<>();
311 private final Set<String> includedPackages = new LinkedHashSet<>();
312 private final JavaFileManager fm;
313 private final Location location;
314 private final Set<JavaFileObject.Kind> sourceKinds = EnumSet.of(JavaFileObject.Kind.SOURCE);
315
316 /**
317 * Creates a table to manage included and excluded packages.
318 * @param fm The file manager used to locate source files
319 * @param locn the location used to locate source files
320 */
321 PackageTable(JavaFileManager fm, Location locn) {
322 this.fm = fm;
323 this.location = locn;
324 getEntry("").excluded = false;
325 }
326
327 PackageTable packages(Collection<String> packageNames) {
328 includedPackages.addAll(packageNames);
329 return this;
330 }
331
332 PackageTable subpackages(Collection<String> packageNames, Collection<String> excludePackageNames)
333 throws IOException {
334 for (String p: excludePackageNames) {
335 getEntry(p).excluded = true;
336 }
337
338 for (String packageName: packageNames) {
Alan Bateman001ebb32016-03-17 19:04:28 +0000339 Location packageLocn = getLocation(packageName);
340 for (JavaFileObject fo: fm.list(packageLocn, packageName, sourceKinds, true)) {
341 String binaryName = fm.inferBinaryName(packageLocn, fo);
Jonathan Gibbons966fc672015-08-10 12:27:29 -0700342 String pn = getPackageName(binaryName);
343 String simpleName = getSimpleName(binaryName);
344 Entry e = getEntry(pn);
345 if (!e.isExcluded() && isValidClassName(simpleName)) {
346 includedPackages.add(pn);
347 e.files = (e.files == null ? List.of(fo) : e.files.prepend(fo));
348 }
349 }
350 }
351 return this;
352 }
353
354 /**
355 * Returns the aggregate set of included packages.
356 * @return the aggregate set of included packages
357 */
358 Set<String> getIncludedPackages() {
359 return includedPackages;
360 }
361
362 /**
363 * Returns the set of source files for a package.
364 * @param packageName the specified package
365 * @return the set of file objects for the specified package
366 * @throws IOException if an error occurs while accessing the files
367 */
368 List<JavaFileObject> getFiles(String packageName) throws IOException {
369 Entry e = getEntry(packageName);
370 // The files may have been found as a side effect of searching for subpackages
371 if (e.files != null)
372 return e.files;
373
374 ListBuffer<JavaFileObject> lb = new ListBuffer<>();
Alan Bateman001ebb32016-03-17 19:04:28 +0000375 Location packageLocn = getLocation(packageName);
376 for (JavaFileObject fo: fm.list(packageLocn, packageName, sourceKinds, false)) {
377 String binaryName = fm.inferBinaryName(packageLocn, fo);
Jonathan Gibbons966fc672015-08-10 12:27:29 -0700378 String simpleName = getSimpleName(binaryName);
379 if (isValidClassName(simpleName)) {
380 lb.append(fo);
381 }
382 }
383
384 return lb.toList();
385 }
386
Alan Bateman001ebb32016-03-17 19:04:28 +0000387 private Location getLocation(String packageName) throws IOException {
388 if (location == StandardLocation.MODULE_SOURCE_PATH) {
389 // TODO: handle invalid results
Kumar Srinivasan28143872016-07-20 12:49:32 -0700390 Name pack = names.fromString(packageName);
391
392 for (ModuleSymbol msym : modules.allModules()) {
393 PackageSymbol p = syms.getPackage(msym, pack);
394 if (p != null && !p.members().isEmpty()) {
Jonathan Gibbonsc7374cd2016-11-16 12:12:02 -0800395 return fm.getLocationForModule(location, msym.name.toString());
Kumar Srinivasan28143872016-07-20 12:49:32 -0700396 }
397 }
398
399 return null;
Alan Bateman001ebb32016-03-17 19:04:28 +0000400 } else {
401 return location;
402 }
403 }
Jonathan Gibbons966fc672015-08-10 12:27:29 -0700404
405 private Entry getEntry(String name) {
406 Entry e = entries.get(name);
407 if (e == null)
408 entries.put(name, e = new Entry(name));
409 return e;
410 }
411
412 private String getPackageName(String name) {
413 int lastDot = name.lastIndexOf(".");
414 return (lastDot == -1 ? "" : name.substring(0, lastDot));
415 }
416
417 private String getSimpleName(String name) {
418 int lastDot = name.lastIndexOf(".");
419 return (lastDot == -1 ? name : name.substring(lastDot + 1));
420 }
421
422 class Entry {
423 final String name;
424 Boolean excluded;
425 List<JavaFileObject> files;
426
427 Entry(String name) {
428 this.name = name;
429 }
430
431 boolean isExcluded() {
432 if (excluded == null)
433 excluded = getEntry(getPackageName(name)).isExcluded();
434 return excluded;
435 }
436 }
437 }
438
J. Dukef57b87e2007-12-01 00:00:00 +0000439}