duke@1: /* xdono@54: * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@1: * duke@1: * This code is free software; you can redistribute it and/or modify it duke@1: * under the terms of the GNU General Public License version 2 only, as duke@1: * published by the Free Software Foundation. Sun designates this duke@1: * particular file as subject to the "Classpath" exception as provided duke@1: * by Sun in the LICENSE file that accompanied this code. duke@1: * duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@1: * version 2 for more details (a copy is included in the LICENSE file that duke@1: * accompanied this code). duke@1: * duke@1: * You should have received a copy of the GNU General Public License version duke@1: * 2 along with this work; if not, write to the Free Software Foundation, duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@1: * duke@1: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, duke@1: * CA 95054 USA or visit www.sun.com if you need additional information or duke@1: * have any questions. duke@1: */ duke@1: duke@1: package com.sun.tools.javadoc; duke@1: duke@1: import java.io.*; duke@1: duke@1: import java.util.Collection; duke@1: duke@1: import com.sun.tools.javac.code.Symbol.*; duke@1: import com.sun.tools.javac.comp.*; jjg@50: import com.sun.tools.javac.file.Paths; duke@1: import com.sun.tools.javac.parser.DocCommentScanner; duke@1: import com.sun.tools.javac.tree.*; duke@1: import com.sun.tools.javac.tree.JCTree.*; duke@1: import com.sun.tools.javac.util.*; duke@1: duke@1: duke@1: /** duke@1: * This class could be the main entry point for Javadoc when Javadoc is used as a duke@1: * component in a larger software system. It provides operations to duke@1: * construct a new javadoc processor, and to run it on a set of source duke@1: * files. duke@1: * @author Neal Gafter duke@1: */ duke@1: public class JavadocTool extends com.sun.tools.javac.main.JavaCompiler { duke@1: DocEnv docenv; duke@1: duke@1: final Context context; duke@1: final Messager messager; duke@1: final JavadocClassReader reader; duke@1: final JavadocEnter enter; duke@1: final Annotate annotate; duke@1: private final Paths paths; duke@1: duke@1: /** duke@1: * Construct a new JavaCompiler processor, using appropriately duke@1: * extended phases of the underlying compiler. duke@1: */ duke@1: protected JavadocTool(Context context) { duke@1: super(context); duke@1: this.context = context; duke@1: messager = Messager.instance0(context); duke@1: reader = JavadocClassReader.instance0(context); duke@1: enter = JavadocEnter.instance0(context); duke@1: annotate = Annotate.instance(context); duke@1: paths = Paths.instance(context); duke@1: } duke@1: duke@1: /** duke@1: * For javadoc, the parser needs to keep comments. Overrides method from JavaCompiler. duke@1: */ duke@1: protected boolean keepComments() { duke@1: return true; duke@1: } duke@1: duke@1: /** duke@1: * Construct a new javadoc tool. duke@1: */ duke@1: public static JavadocTool make0(Context context) { duke@1: Messager messager = null; duke@1: try { duke@1: // force the use of Javadoc's class reader duke@1: JavadocClassReader.preRegister(context); duke@1: duke@1: // force the use of Javadoc's own enter phase duke@1: JavadocEnter.preRegister(context); duke@1: duke@1: // force the use of Javadoc's own member enter phase duke@1: JavadocMemberEnter.preRegister(context); duke@1: duke@1: // force the use of Javadoc's own todo phase duke@1: JavadocTodo.preRegister(context); duke@1: duke@1: // force the use of Messager as a Log duke@1: messager = Messager.instance0(context); duke@1: duke@1: // force the use of the scanner that captures Javadoc comments duke@1: DocCommentScanner.Factory.preRegister(context); duke@1: duke@1: return new JavadocTool(context); duke@1: } catch (CompletionFailure ex) { duke@1: messager.error(Position.NOPOS, ex.getMessage()); duke@1: return null; duke@1: } duke@1: } duke@1: duke@1: public RootDocImpl getRootDocImpl(String doclocale, duke@1: String encoding, duke@1: ModifierFilter filter, duke@1: List javaNames, duke@1: List options, duke@1: boolean breakiterator, duke@1: List subPackages, duke@1: List excludedPackages, duke@1: boolean docClasses, duke@1: boolean legacyDoclet, duke@1: boolean quiet) throws IOException { duke@1: docenv = DocEnv.instance(context); duke@1: docenv.showAccess = filter; duke@1: docenv.quiet = quiet; duke@1: docenv.breakiterator = breakiterator; duke@1: docenv.setLocale(doclocale); duke@1: docenv.setEncoding(encoding); duke@1: docenv.docClasses = docClasses; duke@1: docenv.legacyDoclet = legacyDoclet; duke@1: reader.sourceCompleter = docClasses ? null : this; duke@1: duke@1: ListBuffer names = new ListBuffer(); duke@1: ListBuffer classTrees = new ListBuffer(); duke@1: ListBuffer packTrees = new ListBuffer(); duke@1: duke@1: try { duke@1: for (List it = javaNames; it.nonEmpty(); it = it.tail) { duke@1: String name = it.head; duke@1: if (!docClasses && name.endsWith(".java") && new File(name).exists()) { duke@1: docenv.notice("main.Loading_source_file", name); duke@1: JCCompilationUnit tree = parse(name); duke@1: classTrees.append(tree); duke@1: } else if (isValidPackageName(name)) { duke@1: names = names.append(name); duke@1: } else if (name.endsWith(".java")) { jjg@50: docenv.error(null, "main.file_not_found", name); duke@1: } else { duke@1: docenv.error(null, "main.illegal_package_name", name); duke@1: } duke@1: } duke@1: duke@1: if (!docClasses) { duke@1: // Recursively search given subpackages. If any packages duke@1: //are found, add them to the list. duke@1: searchSubPackages(subPackages, names, excludedPackages); duke@1: duke@1: // Parse the packages duke@1: for (List packs = names.toList(); packs.nonEmpty(); packs = packs.tail) { duke@1: // Parse sources ostensibly belonging to package. duke@1: parsePackageClasses(packs.head, packTrees, excludedPackages); duke@1: } duke@1: duke@1: if (messager.nerrors() != 0) return null; duke@1: duke@1: // Enter symbols for all files duke@1: docenv.notice("main.Building_tree"); duke@1: enter.main(classTrees.toList().appendList(packTrees.toList())); duke@1: } duke@1: } catch (Abort ex) {} duke@1: duke@1: if (messager.nerrors() != 0) return null; duke@1: duke@1: if (docClasses) duke@1: return new RootDocImpl(docenv, javaNames, options); duke@1: else duke@1: return new RootDocImpl(docenv, listClasses(classTrees.toList()), names.toList(), options); duke@1: } duke@1: duke@1: /** Is the given string a valid package name? */ duke@1: boolean isValidPackageName(String s) { duke@1: int index; duke@1: while ((index = s.indexOf('.')) != -1) { duke@1: if (!isValidClassName(s.substring(0, index))) return false; duke@1: s = s.substring(index+1); duke@1: } duke@1: return isValidClassName(s); duke@1: } duke@1: duke@1: duke@1: private final static char pathSep = File.pathSeparatorChar; duke@1: duke@1: /** duke@1: * search all directories in path for subdirectory name. Add all duke@1: * .java files found in such a directory to args. duke@1: */ duke@1: private void parsePackageClasses(String name, duke@1: ListBuffer trees, duke@1: List excludedPackages) duke@1: throws IOException { duke@1: if (excludedPackages.contains(name)) { duke@1: return; duke@1: } duke@1: boolean hasFiles = false; duke@1: docenv.notice("main.Loading_source_files_for_package", name); duke@1: name = name.replace('.', File.separatorChar); duke@1: for (File pathname : paths.sourceSearchPath()) { duke@1: File f = new File(pathname, name); duke@1: String names[] = f.list(); duke@1: // if names not null, then found directory with source files duke@1: if (names != null) { duke@1: String dir = f.getAbsolutePath(); duke@1: if (!dir.endsWith(File.separator)) duke@1: dir = dir + File.separator; duke@1: for (int j = 0; j < names.length; j++) { duke@1: if (isValidJavaSourceFile(names[j])) { duke@1: String fn = dir + names[j]; duke@1: // messager.notice("main.Loading_source_file", fn); duke@1: trees.append(parse(fn)); duke@1: hasFiles = true; duke@1: } duke@1: } duke@1: } duke@1: } duke@1: if (!hasFiles) duke@1: messager.warning(null, "main.no_source_files_for_package", duke@1: name.replace(File.separatorChar, '.')); duke@1: } duke@1: duke@1: /** duke@1: * Recursively search all directories in path for subdirectory name. duke@1: * Add all packages found in such a directory to packages list. duke@1: */ duke@1: private void searchSubPackages(List subPackages, duke@1: ListBuffer packages, duke@1: List excludedPackages) { duke@1: // FIXME: This search path is bogus. duke@1: // Only the effective source path should be searched for sources. duke@1: // Only the effective class path should be searched for classes. duke@1: // Should the bootclasspath/extdirs also be searched for classes? duke@1: java.util.List pathnames = new java.util.ArrayList(); duke@1: if (paths.sourcePath() != null) duke@1: for (File elt : paths.sourcePath()) duke@1: pathnames.add(elt); duke@1: for (File elt : paths.userClassPath()) duke@1: pathnames.add(elt); duke@1: duke@1: for (String subPackage : subPackages) duke@1: searchSubPackage(subPackage, packages, excludedPackages, pathnames); duke@1: } duke@1: duke@1: /** duke@1: * Recursively search all directories in path for subdirectory name. duke@1: * Add all packages found in such a directory to packages list. duke@1: */ duke@1: private void searchSubPackage(String packageName, duke@1: ListBuffer packages, duke@1: List excludedPackages, duke@1: Collection pathnames) { duke@1: if (excludedPackages.contains(packageName)) duke@1: return; duke@1: duke@1: String packageFilename = packageName.replace('.', File.separatorChar); duke@1: boolean addedPackage = false; duke@1: for (File pathname : pathnames) { duke@1: File f = new File(pathname, packageFilename); duke@1: String filenames[] = f.list(); duke@1: // if filenames not null, then found directory duke@1: if (filenames != null) { duke@1: for (String filename : filenames) { duke@1: if (!addedPackage duke@1: && (isValidJavaSourceFile(filename) || duke@1: isValidJavaClassFile(filename)) duke@1: && !packages.contains(packageName)) { duke@1: packages.append(packageName); duke@1: addedPackage = true; duke@1: } else if (isValidClassName(filename) && duke@1: (new File(f, filename)).isDirectory()) { duke@1: searchSubPackage(packageName + "." + filename, duke@1: packages, excludedPackages, pathnames); duke@1: } duke@1: } duke@1: } duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * Return true if given file name is a valid class file name. duke@1: * @param file the name of the file to check. duke@1: * @return true if given file name is a valid class file name duke@1: * and false otherwise. duke@1: */ duke@1: private static boolean isValidJavaClassFile(String file) { duke@1: if (!file.endsWith(".class")) return false; duke@1: String clazzName = file.substring(0, file.length() - ".class".length()); duke@1: return isValidClassName(clazzName); duke@1: } duke@1: duke@1: /** duke@1: * Return true if given file name is a valid Java source file name. duke@1: * @param file the name of the file to check. duke@1: * @return true if given file name is a valid Java source file name duke@1: * and false otherwise. duke@1: */ duke@1: private static boolean isValidJavaSourceFile(String file) { duke@1: if (!file.endsWith(".java")) return false; duke@1: String clazzName = file.substring(0, file.length() - ".java".length()); duke@1: return isValidClassName(clazzName); duke@1: } duke@1: duke@1: /** Are surrogates supported? duke@1: */ duke@1: final static boolean surrogatesSupported = surrogatesSupported(); duke@1: private static boolean surrogatesSupported() { duke@1: try { duke@1: boolean b = Character.isHighSurrogate('a'); duke@1: return true; duke@1: } catch (NoSuchMethodError ex) { duke@1: return false; duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * Return true if given file name is a valid class name duke@1: * (including "package-info"). duke@1: * @param clazzname the name of the class to check. duke@1: * @return true if given class name is a valid class name duke@1: * and false otherwise. duke@1: */ duke@1: public static boolean isValidClassName(String s) { duke@1: if (s.length() < 1) return false; duke@1: if (s.equals("package-info")) return true; duke@1: if (surrogatesSupported) { duke@1: int cp = s.codePointAt(0); duke@1: if (!Character.isJavaIdentifierStart(cp)) duke@1: return false; duke@1: for (int j=Character.charCount(cp); j listClasses(List trees) { duke@1: ListBuffer result = new ListBuffer(); duke@1: for (JCCompilationUnit t : trees) { duke@1: for (JCTree def : t.defs) { duke@1: if (def.getTag() == JCTree.CLASSDEF) duke@1: result.append((JCClassDecl)def); duke@1: } duke@1: } duke@1: return result.toList(); duke@1: } duke@1: duke@1: }