0N/A/*
1117N/A * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
553N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
553N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
553N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
553N/A * or visit www.oracle.com if you need additional information or have any
553N/A * questions.
0N/A */
0N/A
0N/Apackage com.sun.tools.javadoc;
0N/A
196N/Aimport java.io.File;
196N/Aimport java.io.IOException;
0N/Aimport java.util.Collection;
196N/Aimport java.util.EnumSet;
196N/Aimport java.util.HashMap;
196N/Aimport java.util.Map;
196N/Aimport java.util.Set;
196N/Aimport javax.tools.JavaFileManager.Location;
196N/Aimport javax.tools.JavaFileObject;
196N/Aimport javax.tools.StandardJavaFileManager;
196N/Aimport javax.tools.StandardLocation;
0N/A
196N/Aimport com.sun.tools.javac.code.Symbol.CompletionFailure;
196N/Aimport com.sun.tools.javac.comp.Annotate;
0N/Aimport com.sun.tools.javac.parser.DocCommentScanner;
196N/Aimport com.sun.tools.javac.tree.JCTree;
196N/Aimport com.sun.tools.javac.tree.JCTree.JCClassDecl;
196N/Aimport com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
196N/Aimport com.sun.tools.javac.util.Abort;
196N/Aimport com.sun.tools.javac.util.Context;
196N/Aimport com.sun.tools.javac.util.List;
196N/Aimport com.sun.tools.javac.util.ListBuffer;
196N/Aimport com.sun.tools.javac.util.Position;
0N/A
0N/A
0N/A/**
0N/A * This class could be the main entry point for Javadoc when Javadoc is used as a
0N/A * component in a larger software system. It provides operations to
0N/A * construct a new javadoc processor, and to run it on a set of source
0N/A * files.
0N/A * @author Neal Gafter
0N/A */
0N/Apublic class JavadocTool extends com.sun.tools.javac.main.JavaCompiler {
0N/A DocEnv docenv;
0N/A
0N/A final Context context;
0N/A final Messager messager;
0N/A final JavadocClassReader reader;
0N/A final JavadocEnter enter;
0N/A final Annotate annotate;
0N/A
0N/A /**
0N/A * Construct a new JavaCompiler processor, using appropriately
0N/A * extended phases of the underlying compiler.
0N/A */
0N/A protected JavadocTool(Context context) {
0N/A super(context);
0N/A this.context = context;
0N/A messager = Messager.instance0(context);
0N/A reader = JavadocClassReader.instance0(context);
0N/A enter = JavadocEnter.instance0(context);
0N/A annotate = Annotate.instance(context);
0N/A }
0N/A
0N/A /**
0N/A * For javadoc, the parser needs to keep comments. Overrides method from JavaCompiler.
0N/A */
0N/A protected boolean keepComments() {
0N/A return true;
0N/A }
0N/A
0N/A /**
0N/A * Construct a new javadoc tool.
0N/A */
0N/A public static JavadocTool make0(Context context) {
0N/A Messager messager = null;
0N/A try {
0N/A // force the use of Javadoc's class reader
0N/A JavadocClassReader.preRegister(context);
0N/A
0N/A // force the use of Javadoc's own enter phase
0N/A JavadocEnter.preRegister(context);
0N/A
0N/A // force the use of Javadoc's own member enter phase
0N/A JavadocMemberEnter.preRegister(context);
0N/A
0N/A // force the use of Javadoc's own todo phase
0N/A JavadocTodo.preRegister(context);
0N/A
0N/A // force the use of Messager as a Log
0N/A messager = Messager.instance0(context);
0N/A
0N/A return new JavadocTool(context);
0N/A } catch (CompletionFailure ex) {
0N/A messager.error(Position.NOPOS, ex.getMessage());
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A public RootDocImpl getRootDocImpl(String doclocale,
0N/A String encoding,
0N/A ModifierFilter filter,
0N/A List<String> javaNames,
0N/A List<String[]> options,
0N/A boolean breakiterator,
0N/A List<String> subPackages,
0N/A List<String> excludedPackages,
0N/A boolean docClasses,
0N/A boolean legacyDoclet,
0N/A boolean quiet) throws IOException {
0N/A docenv = DocEnv.instance(context);
0N/A docenv.showAccess = filter;
196N/A docenv.quiet = quiet;
0N/A docenv.breakiterator = breakiterator;
0N/A docenv.setLocale(doclocale);
0N/A docenv.setEncoding(encoding);
0N/A docenv.docClasses = docClasses;
0N/A docenv.legacyDoclet = legacyDoclet;
0N/A reader.sourceCompleter = docClasses ? null : this;
0N/A
0N/A ListBuffer<String> names = new ListBuffer<String>();
0N/A ListBuffer<JCCompilationUnit> classTrees = new ListBuffer<JCCompilationUnit>();
0N/A ListBuffer<JCCompilationUnit> packTrees = new ListBuffer<JCCompilationUnit>();
0N/A
0N/A try {
196N/A StandardJavaFileManager fm = (StandardJavaFileManager) docenv.fileManager;
0N/A for (List<String> it = javaNames; it.nonEmpty(); it = it.tail) {
0N/A String name = it.head;
0N/A if (!docClasses && name.endsWith(".java") && new File(name).exists()) {
196N/A JavaFileObject fo = fm.getJavaFileObjects(name).iterator().next();
0N/A docenv.notice("main.Loading_source_file", name);
196N/A JCCompilationUnit tree = parse(fo);
196N/A classTrees.append(tree);
0N/A } else if (isValidPackageName(name)) {
0N/A names = names.append(name);
0N/A } else if (name.endsWith(".java")) {
49N/A docenv.error(null, "main.file_not_found", name);
0N/A } else {
0N/A docenv.error(null, "main.illegal_package_name", name);
0N/A }
0N/A }
0N/A
0N/A if (!docClasses) {
0N/A // Recursively search given subpackages. If any packages
0N/A //are found, add them to the list.
196N/A Map<String,List<JavaFileObject>> packageFiles =
196N/A searchSubPackages(subPackages, names, excludedPackages);
0N/A
0N/A // Parse the packages
0N/A for (List<String> packs = names.toList(); packs.nonEmpty(); packs = packs.tail) {
0N/A // Parse sources ostensibly belonging to package.
196N/A String packageName = packs.head;
196N/A parsePackageClasses(packageName, packageFiles.get(packageName), packTrees, excludedPackages);
0N/A }
0N/A
0N/A if (messager.nerrors() != 0) return null;
0N/A
0N/A // Enter symbols for all files
0N/A docenv.notice("main.Building_tree");
0N/A enter.main(classTrees.toList().appendList(packTrees.toList()));
0N/A }
0N/A } catch (Abort ex) {}
0N/A
196N/A if (messager.nerrors() != 0)
196N/A return null;
0N/A
0N/A if (docClasses)
0N/A return new RootDocImpl(docenv, javaNames, options);
0N/A else
0N/A return new RootDocImpl(docenv, listClasses(classTrees.toList()), names.toList(), options);
0N/A }
0N/A
0N/A /** Is the given string a valid package name? */
0N/A boolean isValidPackageName(String s) {
0N/A int index;
0N/A while ((index = s.indexOf('.')) != -1) {
0N/A if (!isValidClassName(s.substring(0, index))) return false;
0N/A s = s.substring(index+1);
0N/A }
0N/A return isValidClassName(s);
0N/A }
0N/A
0N/A /**
0N/A * search all directories in path for subdirectory name. Add all
0N/A * .java files found in such a directory to args.
0N/A */
0N/A private void parsePackageClasses(String name,
196N/A Iterable<JavaFileObject> files,
196N/A ListBuffer<JCCompilationUnit> trees,
196N/A List<String> excludedPackages)
196N/A throws IOException {
0N/A if (excludedPackages.contains(name)) {
0N/A return;
0N/A }
196N/A
0N/A boolean hasFiles = false;
0N/A docenv.notice("main.Loading_source_files_for_package", name);
196N/A
196N/A if (files == null) {
196N/A Location location = docenv.fileManager.hasLocation(StandardLocation.SOURCE_PATH)
196N/A ? StandardLocation.SOURCE_PATH : StandardLocation.CLASS_PATH;
196N/A ListBuffer<JavaFileObject> lb = new ListBuffer<JavaFileObject>();
196N/A for (JavaFileObject fo: docenv.fileManager.list(
196N/A location, name, EnumSet.of(JavaFileObject.Kind.SOURCE), false)) {
196N/A String binaryName = docenv.fileManager.inferBinaryName(location, fo);
196N/A String simpleName = getSimpleName(binaryName);
196N/A if (isValidClassName(simpleName)) {
196N/A lb.append(fo);
0N/A }
0N/A }
196N/A files = lb.toList();
0N/A }
196N/A
196N/A for (JavaFileObject fo : files) {
196N/A // messager.notice("main.Loading_source_file", fn);
196N/A trees.append(parse(fo));
196N/A hasFiles = true;
196N/A }
196N/A
196N/A if (!hasFiles) {
0N/A messager.warning(null, "main.no_source_files_for_package",
196N/A name.replace(File.separatorChar, '.'));
196N/A }
0N/A }
0N/A
0N/A /**
0N/A * Recursively search all directories in path for subdirectory name.
0N/A * Add all packages found in such a directory to packages list.
0N/A */
196N/A private Map<String,List<JavaFileObject>> searchSubPackages(
196N/A List<String> subPackages,
196N/A ListBuffer<String> packages,
196N/A List<String> excludedPackages)
196N/A throws IOException {
196N/A Map<String,List<JavaFileObject>> packageFiles =
196N/A new HashMap<String,List<JavaFileObject>>();
196N/A
196N/A Map<String,Boolean> includedPackages = new HashMap<String,Boolean>();
196N/A includedPackages.put("", true);
196N/A for (String p: excludedPackages)
196N/A includedPackages.put(p, false);
196N/A
1117N/A StandardLocation path = docenv.fileManager.hasLocation(StandardLocation.SOURCE_PATH)
1117N/A ? StandardLocation.SOURCE_PATH : StandardLocation.CLASS_PATH;
1117N/A
1117N/A searchSubPackages(subPackages,
1117N/A includedPackages,
1117N/A packages, packageFiles,
1117N/A path,
1117N/A EnumSet.of(JavaFileObject.Kind.SOURCE));
1117N/A
196N/A return packageFiles;
196N/A }
196N/A
0N/A private void searchSubPackages(List<String> subPackages,
196N/A Map<String,Boolean> includedPackages,
196N/A ListBuffer<String> packages,
196N/A Map<String, List<JavaFileObject>> packageFiles,
196N/A StandardLocation location, Set<JavaFileObject.Kind> kinds)
196N/A throws IOException {
196N/A for (String subPackage: subPackages) {
196N/A if (!isIncluded(subPackage, includedPackages))
196N/A continue;
0N/A
196N/A for (JavaFileObject fo: docenv.fileManager.list(location, subPackage, kinds, true)) {
196N/A String binaryName = docenv.fileManager.inferBinaryName(location, fo);
196N/A String packageName = getPackageName(binaryName);
196N/A String simpleName = getSimpleName(binaryName);
196N/A if (isIncluded(packageName, includedPackages) && isValidClassName(simpleName)) {
196N/A List<JavaFileObject> list = packageFiles.get(packageName);
196N/A list = (list == null ? List.of(fo) : list.prepend(fo));
196N/A packageFiles.put(packageName, list);
196N/A if (!packages.contains(packageName))
196N/A packages.add(packageName);
196N/A }
196N/A }
196N/A }
196N/A }
196N/A
196N/A private String getPackageName(String name) {
196N/A int lastDot = name.lastIndexOf(".");
196N/A return (lastDot == -1 ? "" : name.substring(0, lastDot));
196N/A }
196N/A
196N/A private String getSimpleName(String name) {
196N/A int lastDot = name.lastIndexOf(".");
196N/A return (lastDot == -1 ? name : name.substring(lastDot + 1));
196N/A }
196N/A
196N/A private boolean isIncluded(String packageName, Map<String,Boolean> includedPackages) {
196N/A Boolean b = includedPackages.get(packageName);
196N/A if (b == null) {
196N/A b = isIncluded(getPackageName(packageName), includedPackages);
196N/A includedPackages.put(packageName, b);
196N/A }
196N/A return b;
0N/A }
0N/A
0N/A /**
0N/A * Recursively search all directories in path for subdirectory name.
0N/A * Add all packages found in such a directory to packages list.
0N/A */
0N/A private void searchSubPackage(String packageName,
0N/A ListBuffer<String> packages,
0N/A List<String> excludedPackages,
0N/A Collection<File> pathnames) {
0N/A if (excludedPackages.contains(packageName))
0N/A return;
0N/A
0N/A String packageFilename = packageName.replace('.', File.separatorChar);
0N/A boolean addedPackage = false;
0N/A for (File pathname : pathnames) {
0N/A File f = new File(pathname, packageFilename);
0N/A String filenames[] = f.list();
0N/A // if filenames not null, then found directory
0N/A if (filenames != null) {
0N/A for (String filename : filenames) {
0N/A if (!addedPackage
0N/A && (isValidJavaSourceFile(filename) ||
0N/A isValidJavaClassFile(filename))
0N/A && !packages.contains(packageName)) {
0N/A packages.append(packageName);
0N/A addedPackage = true;
0N/A } else if (isValidClassName(filename) &&
0N/A (new File(f, filename)).isDirectory()) {
0N/A searchSubPackage(packageName + "." + filename,
0N/A packages, excludedPackages, pathnames);
0N/A }
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Return true if given file name is a valid class file name.
0N/A * @param file the name of the file to check.
0N/A * @return true if given file name is a valid class file name
0N/A * and false otherwise.
0N/A */
0N/A private static boolean isValidJavaClassFile(String file) {
0N/A if (!file.endsWith(".class")) return false;
0N/A String clazzName = file.substring(0, file.length() - ".class".length());
0N/A return isValidClassName(clazzName);
0N/A }
0N/A
0N/A /**
0N/A * Return true if given file name is a valid Java source file name.
0N/A * @param file the name of the file to check.
0N/A * @return true if given file name is a valid Java source file name
0N/A * and false otherwise.
0N/A */
0N/A private static boolean isValidJavaSourceFile(String file) {
0N/A if (!file.endsWith(".java")) return false;
0N/A String clazzName = file.substring(0, file.length() - ".java".length());
0N/A return isValidClassName(clazzName);
0N/A }
0N/A
0N/A /** Are surrogates supported?
0N/A */
0N/A final static boolean surrogatesSupported = surrogatesSupported();
0N/A private static boolean surrogatesSupported() {
0N/A try {
0N/A boolean b = Character.isHighSurrogate('a');
0N/A return true;
0N/A } catch (NoSuchMethodError ex) {
0N/A return false;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Return true if given file name is a valid class name
0N/A * (including "package-info").
0N/A * @param clazzname the name of the class to check.
0N/A * @return true if given class name is a valid class name
0N/A * and false otherwise.
0N/A */
0N/A public static boolean isValidClassName(String s) {
0N/A if (s.length() < 1) return false;
0N/A if (s.equals("package-info")) return true;
0N/A if (surrogatesSupported) {
0N/A int cp = s.codePointAt(0);
0N/A if (!Character.isJavaIdentifierStart(cp))
0N/A return false;
0N/A for (int j=Character.charCount(cp); j<s.length(); j+=Character.charCount(cp)) {
0N/A cp = s.codePointAt(j);
0N/A if (!Character.isJavaIdentifierPart(cp))
0N/A return false;
0N/A }
0N/A } else {
0N/A if (!Character.isJavaIdentifierStart(s.charAt(0)))
0N/A return false;
0N/A for (int j=1; j<s.length(); j++)
0N/A if (!Character.isJavaIdentifierPart(s.charAt(j)))
0N/A return false;
0N/A }
0N/A return true;
0N/A }
0N/A
0N/A /**
0N/A * From a list of top level trees, return the list of contained class definitions
0N/A */
0N/A List<JCClassDecl> listClasses(List<JCCompilationUnit> trees) {
0N/A ListBuffer<JCClassDecl> result = new ListBuffer<JCClassDecl>();
0N/A for (JCCompilationUnit t : trees) {
0N/A for (JCTree def : t.defs) {
0N/A if (def.getTag() == JCTree.CLASSDEF)
0N/A result.append((JCClassDecl)def);
0N/A }
0N/A }
0N/A return result.toList();
0N/A }
0N/A
0N/A}