src/share/classes/com/sun/tools/javadoc/JavadocTool.java

Fri, 14 Feb 2014 17:28:07 -0800

author
ksrini
date
Fri, 14 Feb 2014 17:28:07 -0800
changeset 2281
b06e33ab7f61
parent 2007
a76c663a9cac
child 2367
1737ad9ac984
permissions
-rw-r--r--

8029145: javadoc fails with java.lang.IllegalStateException: endPosTable already set
Reviewed-by: jjg

duke@1 1 /*
ksrini@2281 2 * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved.
duke@1 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
duke@1 4 *
duke@1 5 * This code is free software; you can redistribute it and/or modify it
duke@1 6 * under the terms of the GNU General Public License version 2 only, as
ohair@554 7 * published by the Free Software Foundation. Oracle designates this
duke@1 8 * particular file as subject to the "Classpath" exception as provided
ohair@554 9 * by Oracle in the LICENSE file that accompanied this code.
duke@1 10 *
duke@1 11 * This code is distributed in the hope that it will be useful, but WITHOUT
duke@1 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
duke@1 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
duke@1 14 * version 2 for more details (a copy is included in the LICENSE file that
duke@1 15 * accompanied this code).
duke@1 16 *
duke@1 17 * You should have received a copy of the GNU General Public License version
duke@1 18 * 2 along with this work; if not, write to the Free Software Foundation,
duke@1 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
duke@1 20 *
ohair@554 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
ohair@554 22 * or visit www.oracle.com if you need additional information or have any
ohair@554 23 * questions.
duke@1 24 */
duke@1 25
duke@1 26 package com.sun.tools.javadoc;
duke@1 27
jjg@197 28 import java.io.File;
jjg@197 29 import java.io.IOException;
jjg@197 30 import java.util.Collection;
jjg@197 31 import java.util.EnumSet;
jjg@197 32 import java.util.HashMap;
ksrini@2281 33 import java.util.HashSet;
jjg@197 34 import java.util.Map;
jjg@197 35 import java.util.Set;
jjg@197 36 import javax.tools.JavaFileManager.Location;
jjg@197 37 import javax.tools.JavaFileObject;
jjg@197 38 import javax.tools.StandardJavaFileManager;
jjg@197 39 import javax.tools.StandardLocation;
duke@1 40
jjg@197 41 import com.sun.tools.javac.code.Symbol.CompletionFailure;
jjg@197 42 import com.sun.tools.javac.tree.JCTree;
jjg@197 43 import com.sun.tools.javac.tree.JCTree.JCClassDecl;
jjg@197 44 import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
jjg@197 45 import com.sun.tools.javac.util.Abort;
jjg@197 46 import com.sun.tools.javac.util.Context;
jjg@197 47 import com.sun.tools.javac.util.List;
jjg@197 48 import com.sun.tools.javac.util.ListBuffer;
jjg@197 49 import com.sun.tools.javac.util.Position;
duke@1 50
duke@1 51
duke@1 52 /**
duke@1 53 * This class could be the main entry point for Javadoc when Javadoc is used as a
duke@1 54 * component in a larger software system. It provides operations to
duke@1 55 * construct a new javadoc processor, and to run it on a set of source
duke@1 56 * files.
jjg@1359 57 *
jjg@1359 58 * <p><b>This is NOT part of any supported API.
jjg@1359 59 * If you write code that depends on this, you do so at your own risk.
jjg@1359 60 * This code and its internal interfaces are subject to change or
jjg@1359 61 * deletion without notice.</b>
jjg@1359 62 *
duke@1 63 * @author Neal Gafter
duke@1 64 */
duke@1 65 public class JavadocTool extends com.sun.tools.javac.main.JavaCompiler {
duke@1 66 DocEnv docenv;
duke@1 67
duke@1 68 final Messager messager;
jjg@1411 69 final JavadocClassReader javadocReader;
jjg@1411 70 final JavadocEnter javadocEnter;
duke@1 71
duke@1 72 /**
duke@1 73 * Construct a new JavaCompiler processor, using appropriately
duke@1 74 * extended phases of the underlying compiler.
duke@1 75 */
duke@1 76 protected JavadocTool(Context context) {
duke@1 77 super(context);
duke@1 78 messager = Messager.instance0(context);
jjg@1411 79 javadocReader = JavadocClassReader.instance0(context);
jjg@1411 80 javadocEnter = JavadocEnter.instance0(context);
duke@1 81 }
duke@1 82
duke@1 83 /**
duke@1 84 * For javadoc, the parser needs to keep comments. Overrides method from JavaCompiler.
duke@1 85 */
duke@1 86 protected boolean keepComments() {
duke@1 87 return true;
duke@1 88 }
duke@1 89
duke@1 90 /**
duke@1 91 * Construct a new javadoc tool.
duke@1 92 */
duke@1 93 public static JavadocTool make0(Context context) {
duke@1 94 Messager messager = null;
duke@1 95 try {
duke@1 96 // force the use of Javadoc's class reader
duke@1 97 JavadocClassReader.preRegister(context);
duke@1 98
duke@1 99 // force the use of Javadoc's own enter phase
duke@1 100 JavadocEnter.preRegister(context);
duke@1 101
duke@1 102 // force the use of Javadoc's own member enter phase
duke@1 103 JavadocMemberEnter.preRegister(context);
duke@1 104
duke@1 105 // force the use of Javadoc's own todo phase
duke@1 106 JavadocTodo.preRegister(context);
duke@1 107
duke@1 108 // force the use of Messager as a Log
duke@1 109 messager = Messager.instance0(context);
duke@1 110
duke@1 111 return new JavadocTool(context);
duke@1 112 } catch (CompletionFailure ex) {
duke@1 113 messager.error(Position.NOPOS, ex.getMessage());
duke@1 114 return null;
duke@1 115 }
duke@1 116 }
duke@1 117
duke@1 118 public RootDocImpl getRootDocImpl(String doclocale,
duke@1 119 String encoding,
duke@1 120 ModifierFilter filter,
duke@1 121 List<String> javaNames,
duke@1 122 List<String[]> options,
jjg@1413 123 Iterable<? extends JavaFileObject> fileObjects,
duke@1 124 boolean breakiterator,
duke@1 125 List<String> subPackages,
duke@1 126 List<String> excludedPackages,
duke@1 127 boolean docClasses,
duke@1 128 boolean legacyDoclet,
duke@1 129 boolean quiet) throws IOException {
duke@1 130 docenv = DocEnv.instance(context);
duke@1 131 docenv.showAccess = filter;
jjg@197 132 docenv.quiet = quiet;
duke@1 133 docenv.breakiterator = breakiterator;
duke@1 134 docenv.setLocale(doclocale);
duke@1 135 docenv.setEncoding(encoding);
duke@1 136 docenv.docClasses = docClasses;
duke@1 137 docenv.legacyDoclet = legacyDoclet;
jfranck@2007 138 javadocReader.sourceCompleter = docClasses ? null : thisCompleter;
duke@1 139
duke@1 140 ListBuffer<String> names = new ListBuffer<String>();
duke@1 141 ListBuffer<JCCompilationUnit> classTrees = new ListBuffer<JCCompilationUnit>();
duke@1 142 ListBuffer<JCCompilationUnit> packTrees = new ListBuffer<JCCompilationUnit>();
duke@1 143
duke@1 144 try {
jjg@1413 145 StandardJavaFileManager fm = docenv.fileManager instanceof StandardJavaFileManager
jjg@1413 146 ? (StandardJavaFileManager) docenv.fileManager : null;
duke@1 147 for (List<String> it = javaNames; it.nonEmpty(); it = it.tail) {
duke@1 148 String name = it.head;
jjg@1413 149 if (!docClasses && fm != null && name.endsWith(".java") && new File(name).exists()) {
jjg@197 150 JavaFileObject fo = fm.getJavaFileObjects(name).iterator().next();
duke@1 151 docenv.notice("main.Loading_source_file", name);
jjg@197 152 JCCompilationUnit tree = parse(fo);
jjg@197 153 classTrees.append(tree);
duke@1 154 } else if (isValidPackageName(name)) {
duke@1 155 names = names.append(name);
duke@1 156 } else if (name.endsWith(".java")) {
jjg@1413 157 if (fm == null)
jjg@1413 158 throw new IllegalArgumentException();
jjg@1413 159 else
jjg@1411 160 docenv.error(null, "main.file_not_found", name);
duke@1 161 } else {
duke@1 162 docenv.error(null, "main.illegal_package_name", name);
duke@1 163 }
duke@1 164 }
jjg@1413 165 for (JavaFileObject fo: fileObjects) {
jjg@1413 166 docenv.notice("main.Loading_source_file", fo.getName());
jjg@1413 167 JCCompilationUnit tree = parse(fo);
jjg@1413 168 classTrees.append(tree);
jjg@1413 169 }
duke@1 170
duke@1 171 if (!docClasses) {
duke@1 172 // Recursively search given subpackages. If any packages
duke@1 173 //are found, add them to the list.
jjg@197 174 Map<String,List<JavaFileObject>> packageFiles =
jjg@197 175 searchSubPackages(subPackages, names, excludedPackages);
duke@1 176
duke@1 177 // Parse the packages
duke@1 178 for (List<String> packs = names.toList(); packs.nonEmpty(); packs = packs.tail) {
duke@1 179 // Parse sources ostensibly belonging to package.
jjg@197 180 String packageName = packs.head;
jjg@197 181 parsePackageClasses(packageName, packageFiles.get(packageName), packTrees, excludedPackages);
duke@1 182 }
duke@1 183
duke@1 184 if (messager.nerrors() != 0) return null;
duke@1 185
duke@1 186 // Enter symbols for all files
duke@1 187 docenv.notice("main.Building_tree");
jjg@1411 188 javadocEnter.main(classTrees.toList().appendList(packTrees.toList()));
duke@1 189 }
duke@1 190 } catch (Abort ex) {}
duke@1 191
jjg@197 192 if (messager.nerrors() != 0)
jjg@197 193 return null;
duke@1 194
duke@1 195 if (docClasses)
duke@1 196 return new RootDocImpl(docenv, javaNames, options);
duke@1 197 else
duke@1 198 return new RootDocImpl(docenv, listClasses(classTrees.toList()), names.toList(), options);
duke@1 199 }
duke@1 200
duke@1 201 /** Is the given string a valid package name? */
duke@1 202 boolean isValidPackageName(String s) {
duke@1 203 int index;
duke@1 204 while ((index = s.indexOf('.')) != -1) {
duke@1 205 if (!isValidClassName(s.substring(0, index))) return false;
duke@1 206 s = s.substring(index+1);
duke@1 207 }
duke@1 208 return isValidClassName(s);
duke@1 209 }
duke@1 210
duke@1 211 /**
duke@1 212 * search all directories in path for subdirectory name. Add all
duke@1 213 * .java files found in such a directory to args.
duke@1 214 */
duke@1 215 private void parsePackageClasses(String name,
jjg@197 216 Iterable<JavaFileObject> files,
jjg@197 217 ListBuffer<JCCompilationUnit> trees,
jjg@197 218 List<String> excludedPackages)
jjg@197 219 throws IOException {
duke@1 220 if (excludedPackages.contains(name)) {
duke@1 221 return;
duke@1 222 }
jjg@197 223
duke@1 224 boolean hasFiles = false;
duke@1 225 docenv.notice("main.Loading_source_files_for_package", name);
jjg@197 226
jjg@197 227 if (files == null) {
jjg@197 228 Location location = docenv.fileManager.hasLocation(StandardLocation.SOURCE_PATH)
jjg@197 229 ? StandardLocation.SOURCE_PATH : StandardLocation.CLASS_PATH;
jjg@197 230 ListBuffer<JavaFileObject> lb = new ListBuffer<JavaFileObject>();
jjg@197 231 for (JavaFileObject fo: docenv.fileManager.list(
jjg@197 232 location, name, EnumSet.of(JavaFileObject.Kind.SOURCE), false)) {
jjg@197 233 String binaryName = docenv.fileManager.inferBinaryName(location, fo);
jjg@197 234 String simpleName = getSimpleName(binaryName);
jjg@197 235 if (isValidClassName(simpleName)) {
jjg@197 236 lb.append(fo);
duke@1 237 }
duke@1 238 }
jjg@197 239 files = lb.toList();
duke@1 240 }
jjg@197 241
ksrini@2281 242 Set<JavaFileObject> ufiles = new HashSet<>();
jjg@197 243 for (JavaFileObject fo : files) {
ksrini@2281 244 if (ufiles.add(fo)) { // ignore duplicates
ksrini@2281 245 // messager.notice("main.Loading_source_file", fn);
ksrini@2281 246 trees.append(parse(fo));
ksrini@2281 247 hasFiles = true;
ksrini@2281 248 }
jjg@197 249 }
jjg@197 250
jjg@197 251 if (!hasFiles) {
jjg@1411 252 messager.warning(Messager.NOPOS, "main.no_source_files_for_package",
jjg@197 253 name.replace(File.separatorChar, '.'));
jjg@197 254 }
duke@1 255 }
duke@1 256
duke@1 257 /**
duke@1 258 * Recursively search all directories in path for subdirectory name.
duke@1 259 * Add all packages found in such a directory to packages list.
duke@1 260 */
jjg@197 261 private Map<String,List<JavaFileObject>> searchSubPackages(
jjg@197 262 List<String> subPackages,
jjg@197 263 ListBuffer<String> packages,
jjg@197 264 List<String> excludedPackages)
jjg@197 265 throws IOException {
jjg@197 266 Map<String,List<JavaFileObject>> packageFiles =
jjg@197 267 new HashMap<String,List<JavaFileObject>>();
jjg@197 268
jjg@197 269 Map<String,Boolean> includedPackages = new HashMap<String,Boolean>();
jjg@197 270 includedPackages.put("", true);
jjg@197 271 for (String p: excludedPackages)
jjg@197 272 includedPackages.put(p, false);
jjg@197 273
jjg@1094 274 StandardLocation path = docenv.fileManager.hasLocation(StandardLocation.SOURCE_PATH)
jjg@1094 275 ? StandardLocation.SOURCE_PATH : StandardLocation.CLASS_PATH;
jjg@1094 276
jjg@1094 277 searchSubPackages(subPackages,
jjg@1094 278 includedPackages,
jjg@1094 279 packages, packageFiles,
jjg@1094 280 path,
jjg@1094 281 EnumSet.of(JavaFileObject.Kind.SOURCE));
jjg@1094 282
jjg@197 283 return packageFiles;
jjg@197 284 }
jjg@197 285
duke@1 286 private void searchSubPackages(List<String> subPackages,
jjg@197 287 Map<String,Boolean> includedPackages,
jjg@197 288 ListBuffer<String> packages,
jjg@197 289 Map<String, List<JavaFileObject>> packageFiles,
jjg@197 290 StandardLocation location, Set<JavaFileObject.Kind> kinds)
jjg@197 291 throws IOException {
jjg@197 292 for (String subPackage: subPackages) {
jjg@197 293 if (!isIncluded(subPackage, includedPackages))
jjg@197 294 continue;
duke@1 295
jjg@197 296 for (JavaFileObject fo: docenv.fileManager.list(location, subPackage, kinds, true)) {
jjg@197 297 String binaryName = docenv.fileManager.inferBinaryName(location, fo);
jjg@197 298 String packageName = getPackageName(binaryName);
jjg@197 299 String simpleName = getSimpleName(binaryName);
jjg@197 300 if (isIncluded(packageName, includedPackages) && isValidClassName(simpleName)) {
jjg@197 301 List<JavaFileObject> list = packageFiles.get(packageName);
jjg@197 302 list = (list == null ? List.of(fo) : list.prepend(fo));
jjg@197 303 packageFiles.put(packageName, list);
jjg@197 304 if (!packages.contains(packageName))
jjg@197 305 packages.add(packageName);
jjg@197 306 }
jjg@197 307 }
jjg@197 308 }
jjg@197 309 }
jjg@197 310
jjg@197 311 private String getPackageName(String name) {
jjg@197 312 int lastDot = name.lastIndexOf(".");
jjg@197 313 return (lastDot == -1 ? "" : name.substring(0, lastDot));
jjg@197 314 }
jjg@197 315
jjg@197 316 private String getSimpleName(String name) {
jjg@197 317 int lastDot = name.lastIndexOf(".");
jjg@197 318 return (lastDot == -1 ? name : name.substring(lastDot + 1));
jjg@197 319 }
jjg@197 320
jjg@197 321 private boolean isIncluded(String packageName, Map<String,Boolean> includedPackages) {
jjg@197 322 Boolean b = includedPackages.get(packageName);
jjg@197 323 if (b == null) {
jjg@197 324 b = isIncluded(getPackageName(packageName), includedPackages);
jjg@197 325 includedPackages.put(packageName, b);
jjg@197 326 }
jjg@197 327 return b;
duke@1 328 }
duke@1 329
duke@1 330 /**
duke@1 331 * Recursively search all directories in path for subdirectory name.
duke@1 332 * Add all packages found in such a directory to packages list.
duke@1 333 */
duke@1 334 private void searchSubPackage(String packageName,
duke@1 335 ListBuffer<String> packages,
duke@1 336 List<String> excludedPackages,
duke@1 337 Collection<File> pathnames) {
duke@1 338 if (excludedPackages.contains(packageName))
duke@1 339 return;
duke@1 340
duke@1 341 String packageFilename = packageName.replace('.', File.separatorChar);
duke@1 342 boolean addedPackage = false;
duke@1 343 for (File pathname : pathnames) {
duke@1 344 File f = new File(pathname, packageFilename);
duke@1 345 String filenames[] = f.list();
duke@1 346 // if filenames not null, then found directory
duke@1 347 if (filenames != null) {
duke@1 348 for (String filename : filenames) {
duke@1 349 if (!addedPackage
duke@1 350 && (isValidJavaSourceFile(filename) ||
duke@1 351 isValidJavaClassFile(filename))
duke@1 352 && !packages.contains(packageName)) {
duke@1 353 packages.append(packageName);
duke@1 354 addedPackage = true;
duke@1 355 } else if (isValidClassName(filename) &&
duke@1 356 (new File(f, filename)).isDirectory()) {
duke@1 357 searchSubPackage(packageName + "." + filename,
duke@1 358 packages, excludedPackages, pathnames);
duke@1 359 }
duke@1 360 }
duke@1 361 }
duke@1 362 }
duke@1 363 }
duke@1 364
duke@1 365 /**
duke@1 366 * Return true if given file name is a valid class file name.
duke@1 367 * @param file the name of the file to check.
duke@1 368 * @return true if given file name is a valid class file name
duke@1 369 * and false otherwise.
duke@1 370 */
duke@1 371 private static boolean isValidJavaClassFile(String file) {
duke@1 372 if (!file.endsWith(".class")) return false;
duke@1 373 String clazzName = file.substring(0, file.length() - ".class".length());
duke@1 374 return isValidClassName(clazzName);
duke@1 375 }
duke@1 376
duke@1 377 /**
duke@1 378 * Return true if given file name is a valid Java source file name.
duke@1 379 * @param file the name of the file to check.
duke@1 380 * @return true if given file name is a valid Java source file name
duke@1 381 * and false otherwise.
duke@1 382 */
duke@1 383 private static boolean isValidJavaSourceFile(String file) {
duke@1 384 if (!file.endsWith(".java")) return false;
duke@1 385 String clazzName = file.substring(0, file.length() - ".java".length());
duke@1 386 return isValidClassName(clazzName);
duke@1 387 }
duke@1 388
duke@1 389 /** Are surrogates supported?
duke@1 390 */
duke@1 391 final static boolean surrogatesSupported = surrogatesSupported();
duke@1 392 private static boolean surrogatesSupported() {
duke@1 393 try {
duke@1 394 boolean b = Character.isHighSurrogate('a');
duke@1 395 return true;
duke@1 396 } catch (NoSuchMethodError ex) {
duke@1 397 return false;
duke@1 398 }
duke@1 399 }
duke@1 400
duke@1 401 /**
duke@1 402 * Return true if given file name is a valid class name
duke@1 403 * (including "package-info").
jjg@1358 404 * @param s the name of the class to check.
duke@1 405 * @return true if given class name is a valid class name
duke@1 406 * and false otherwise.
duke@1 407 */
duke@1 408 public static boolean isValidClassName(String s) {
duke@1 409 if (s.length() < 1) return false;
duke@1 410 if (s.equals("package-info")) return true;
duke@1 411 if (surrogatesSupported) {
duke@1 412 int cp = s.codePointAt(0);
duke@1 413 if (!Character.isJavaIdentifierStart(cp))
duke@1 414 return false;
duke@1 415 for (int j=Character.charCount(cp); j<s.length(); j+=Character.charCount(cp)) {
duke@1 416 cp = s.codePointAt(j);
duke@1 417 if (!Character.isJavaIdentifierPart(cp))
duke@1 418 return false;
duke@1 419 }
duke@1 420 } else {
duke@1 421 if (!Character.isJavaIdentifierStart(s.charAt(0)))
duke@1 422 return false;
duke@1 423 for (int j=1; j<s.length(); j++)
duke@1 424 if (!Character.isJavaIdentifierPart(s.charAt(j)))
duke@1 425 return false;
duke@1 426 }
duke@1 427 return true;
duke@1 428 }
duke@1 429
duke@1 430 /**
duke@1 431 * From a list of top level trees, return the list of contained class definitions
duke@1 432 */
duke@1 433 List<JCClassDecl> listClasses(List<JCCompilationUnit> trees) {
duke@1 434 ListBuffer<JCClassDecl> result = new ListBuffer<JCClassDecl>();
duke@1 435 for (JCCompilationUnit t : trees) {
duke@1 436 for (JCTree def : t.defs) {
jjg@1127 437 if (def.hasTag(JCTree.Tag.CLASSDEF))
duke@1 438 result.append((JCClassDecl)def);
duke@1 439 }
duke@1 440 }
duke@1 441 return result.toList();
duke@1 442 }
duke@1 443
duke@1 444 }

mercurial