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

Tue, 04 Mar 2008 15:45:20 +0000

author
mcimadamore
date
Tue, 04 Mar 2008 15:45:20 +0000
changeset 8
38bd6375f37d
parent 1
9a66ca7c79fa
child 50
b9bcea8bbe24
permissions
-rw-r--r--

6663588: Compiler goes into infinite loop for Cyclic Inheritance test case
Summary: interplay between cyclic inheritance and tvar bounds hangs javac
Reviewed-by: jjg

     1 /*
     2  * Copyright 2001-2006 Sun Microsystems, Inc.  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.  Sun designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Sun in the LICENSE file that accompanied this code.
    10  *
    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  *
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
    23  * have any questions.
    24  */
    26 package com.sun.tools.javadoc;
    28 import java.io.*;
    30 import java.util.Collection;
    32 import com.sun.tools.javac.code.*;
    33 import com.sun.tools.javac.code.Symbol.*;
    34 import com.sun.tools.javac.comp.*;
    35 import com.sun.tools.javac.jvm.ClassReader;
    36 import com.sun.tools.javac.jvm.ClassWriter;
    37 import com.sun.tools.javac.parser.DocCommentScanner;
    38 import com.sun.tools.javac.util.Paths;
    39 import com.sun.tools.javac.tree.*;
    40 import com.sun.tools.javac.tree.JCTree.*;
    41 import com.sun.tools.javac.util.*;
    43 import com.sun.javadoc.LanguageVersion;
    44 import static com.sun.javadoc.LanguageVersion.*;
    46 /**
    47  *  This class could be the main entry point for Javadoc when Javadoc is used as a
    48  *  component in a larger software system. It provides operations to
    49  *  construct a new javadoc processor, and to run it on a set of source
    50  *  files.
    51  *  @author Neal Gafter
    52  */
    53 public class JavadocTool extends com.sun.tools.javac.main.JavaCompiler {
    54     DocEnv docenv;
    56     final Context context;
    57     final Messager messager;
    58     final JavadocClassReader reader;
    59     final JavadocEnter enter;
    60     final Annotate annotate;
    61     private final Paths paths;
    63     /**
    64      * Construct a new JavaCompiler processor, using appropriately
    65      * extended phases of the underlying compiler.
    66      */
    67     protected JavadocTool(Context context) {
    68         super(context);
    69         this.context = context;
    70         messager = Messager.instance0(context);
    71         reader = JavadocClassReader.instance0(context);
    72         enter = JavadocEnter.instance0(context);
    73         annotate = Annotate.instance(context);
    74         paths = Paths.instance(context);
    75     }
    77     /**
    78      * For javadoc, the parser needs to keep comments. Overrides method from JavaCompiler.
    79      */
    80     protected boolean keepComments() {
    81         return true;
    82     }
    84     /**
    85      *  Construct a new javadoc tool.
    86      */
    87     public static JavadocTool make0(Context context) {
    88         Messager messager = null;
    89         try {
    90             // force the use of Javadoc's class reader
    91             JavadocClassReader.preRegister(context);
    93             // force the use of Javadoc's own enter phase
    94             JavadocEnter.preRegister(context);
    96             // force the use of Javadoc's own member enter phase
    97             JavadocMemberEnter.preRegister(context);
    99             // force the use of Javadoc's own todo phase
   100             JavadocTodo.preRegister(context);
   102             // force the use of Messager as a Log
   103             messager = Messager.instance0(context);
   105             // force the use of the scanner that captures Javadoc comments
   106             DocCommentScanner.Factory.preRegister(context);
   108             return new JavadocTool(context);
   109         } catch (CompletionFailure ex) {
   110             messager.error(Position.NOPOS, ex.getMessage());
   111             return null;
   112         }
   113     }
   115     public RootDocImpl getRootDocImpl(String doclocale,
   116                                       String encoding,
   117                                       ModifierFilter filter,
   118                                       List<String> javaNames,
   119                                       List<String[]> options,
   120                                       boolean breakiterator,
   121                                       List<String> subPackages,
   122                                       List<String> excludedPackages,
   123                                       boolean docClasses,
   124                                       boolean legacyDoclet,
   125                       boolean quiet) throws IOException {
   126         docenv = DocEnv.instance(context);
   127         docenv.showAccess = filter;
   128     docenv.quiet = quiet;
   129         docenv.breakiterator = breakiterator;
   130         docenv.setLocale(doclocale);
   131         docenv.setEncoding(encoding);
   132         docenv.docClasses = docClasses;
   133         docenv.legacyDoclet = legacyDoclet;
   134         reader.sourceCompleter = docClasses ? null : this;
   136         ListBuffer<String> names = new ListBuffer<String>();
   137         ListBuffer<JCCompilationUnit> classTrees = new ListBuffer<JCCompilationUnit>();
   138         ListBuffer<JCCompilationUnit> packTrees = new ListBuffer<JCCompilationUnit>();
   140         try {
   141             for (List<String> it = javaNames; it.nonEmpty(); it = it.tail) {
   142                 String name = it.head;
   143                 if (!docClasses && name.endsWith(".java") && new File(name).exists()) {
   144                     docenv.notice("main.Loading_source_file", name);
   145                         JCCompilationUnit tree = parse(name);
   146                         classTrees.append(tree);
   147                 } else if (isValidPackageName(name)) {
   148                     names = names.append(name);
   149                 } else if (name.endsWith(".java")) {
   150                     docenv.error(null, "main.file_not_found", name);;
   151                 } else {
   152                     docenv.error(null, "main.illegal_package_name", name);
   153                 }
   154             }
   156             if (!docClasses) {
   157                 // Recursively search given subpackages.  If any packages
   158                 //are found, add them to the list.
   159                 searchSubPackages(subPackages, names, excludedPackages);
   161                 // Parse the packages
   162                 for (List<String> packs = names.toList(); packs.nonEmpty(); packs = packs.tail) {
   163                     // Parse sources ostensibly belonging to package.
   164                     parsePackageClasses(packs.head, packTrees, excludedPackages);
   165                 }
   167                 if (messager.nerrors() != 0) return null;
   169                 // Enter symbols for all files
   170                 docenv.notice("main.Building_tree");
   171                 enter.main(classTrees.toList().appendList(packTrees.toList()));
   172             }
   173         } catch (Abort ex) {}
   175         if (messager.nerrors() != 0) return null;
   177         if (docClasses)
   178             return new RootDocImpl(docenv, javaNames, options);
   179         else
   180             return new RootDocImpl(docenv, listClasses(classTrees.toList()), names.toList(), options);
   181     }
   183     /** Is the given string a valid package name? */
   184     boolean isValidPackageName(String s) {
   185         int index;
   186         while ((index = s.indexOf('.')) != -1) {
   187             if (!isValidClassName(s.substring(0, index))) return false;
   188             s = s.substring(index+1);
   189         }
   190         return isValidClassName(s);
   191     }
   194     private final static char pathSep = File.pathSeparatorChar;
   196     /**
   197      * search all directories in path for subdirectory name. Add all
   198      * .java files found in such a directory to args.
   199      */
   200     private void parsePackageClasses(String name,
   201                                      ListBuffer<JCCompilationUnit> trees,
   202                                      List<String> excludedPackages)
   203         throws IOException {
   204         if (excludedPackages.contains(name)) {
   205             return;
   206         }
   207         boolean hasFiles = false;
   208         docenv.notice("main.Loading_source_files_for_package", name);
   209         name = name.replace('.', File.separatorChar);
   210         for (File pathname : paths.sourceSearchPath()) {
   211             File f = new File(pathname, name);
   212             String names[] = f.list();
   213             // if names not null, then found directory with source files
   214             if (names != null) {
   215                 String dir = f.getAbsolutePath();
   216                 if (!dir.endsWith(File.separator))
   217                     dir = dir + File.separator;
   218                 for (int j = 0; j < names.length; j++) {
   219                     if (isValidJavaSourceFile(names[j])) {
   220                         String fn = dir + names[j];
   221                         // messager.notice("main.Loading_source_file", fn);
   222                             trees.append(parse(fn));
   223                         hasFiles = true;
   224                     }
   225                 }
   226             }
   227         }
   228         if (!hasFiles)
   229             messager.warning(null, "main.no_source_files_for_package",
   230                              name.replace(File.separatorChar, '.'));
   231     }
   233     /**
   234      * Recursively search all directories in path for subdirectory name.
   235      * Add all packages found in such a directory to packages list.
   236      */
   237     private void searchSubPackages(List<String> subPackages,
   238                                    ListBuffer<String> packages,
   239                                    List<String> excludedPackages) {
   240         // FIXME: This search path is bogus.
   241         // Only the effective source path should be searched for sources.
   242         // Only the effective class path should be searched for classes.
   243         // Should the bootclasspath/extdirs also be searched for classes?
   244         java.util.List<File> pathnames = new java.util.ArrayList<File>();
   245         if (paths.sourcePath() != null)
   246             for (File elt : paths.sourcePath())
   247                 pathnames.add(elt);
   248         for (File elt : paths.userClassPath())
   249             pathnames.add(elt);
   251         for (String subPackage : subPackages)
   252             searchSubPackage(subPackage, packages, excludedPackages, pathnames);
   253     }
   255     /**
   256      * Recursively search all directories in path for subdirectory name.
   257      * Add all packages found in such a directory to packages list.
   258      */
   259     private void searchSubPackage(String packageName,
   260                                   ListBuffer<String> packages,
   261                                   List<String> excludedPackages,
   262                                   Collection<File> pathnames) {
   263         if (excludedPackages.contains(packageName))
   264             return;
   266         String packageFilename = packageName.replace('.', File.separatorChar);
   267         boolean addedPackage = false;
   268         for (File pathname : pathnames) {
   269             File f = new File(pathname, packageFilename);
   270             String filenames[] = f.list();
   271             // if filenames not null, then found directory
   272             if (filenames != null) {
   273                 for (String filename : filenames) {
   274                     if (!addedPackage
   275                             && (isValidJavaSourceFile(filename) ||
   276                                 isValidJavaClassFile(filename))
   277                             && !packages.contains(packageName)) {
   278                         packages.append(packageName);
   279                         addedPackage = true;
   280                     } else if (isValidClassName(filename) &&
   281                                (new File(f, filename)).isDirectory()) {
   282                         searchSubPackage(packageName + "." + filename,
   283                                          packages, excludedPackages, pathnames);
   284                     }
   285                 }
   286             }
   287         }
   288     }
   290     /**
   291      * Return true if given file name is a valid class file name.
   292      * @param file the name of the file to check.
   293      * @return true if given file name is a valid class file name
   294      * and false otherwise.
   295      */
   296     private static boolean isValidJavaClassFile(String file) {
   297         if (!file.endsWith(".class")) return false;
   298         String clazzName = file.substring(0, file.length() - ".class".length());
   299         return isValidClassName(clazzName);
   300     }
   302     /**
   303      * Return true if given file name is a valid Java source file name.
   304      * @param file the name of the file to check.
   305      * @return true if given file name is a valid Java source file name
   306      * and false otherwise.
   307      */
   308     private static boolean isValidJavaSourceFile(String file) {
   309         if (!file.endsWith(".java")) return false;
   310         String clazzName = file.substring(0, file.length() - ".java".length());
   311         return isValidClassName(clazzName);
   312     }
   314     /** Are surrogates supported?
   315      */
   316     final static boolean surrogatesSupported = surrogatesSupported();
   317     private static boolean surrogatesSupported() {
   318         try {
   319             boolean b = Character.isHighSurrogate('a');
   320             return true;
   321         } catch (NoSuchMethodError ex) {
   322             return false;
   323         }
   324     }
   326     /**
   327      * Return true if given file name is a valid class name
   328      * (including "package-info").
   329      * @param clazzname the name of the class to check.
   330      * @return true if given class name is a valid class name
   331      * and false otherwise.
   332      */
   333     public static boolean isValidClassName(String s) {
   334         if (s.length() < 1) return false;
   335         if (s.equals("package-info")) return true;
   336         if (surrogatesSupported) {
   337             int cp = s.codePointAt(0);
   338             if (!Character.isJavaIdentifierStart(cp))
   339                 return false;
   340             for (int j=Character.charCount(cp); j<s.length(); j+=Character.charCount(cp)) {
   341                 cp = s.codePointAt(j);
   342                 if (!Character.isJavaIdentifierPart(cp))
   343                     return false;
   344             }
   345         } else {
   346             if (!Character.isJavaIdentifierStart(s.charAt(0)))
   347                 return false;
   348             for (int j=1; j<s.length(); j++)
   349                 if (!Character.isJavaIdentifierPart(s.charAt(j)))
   350                     return false;
   351         }
   352         return true;
   353     }
   355     /**
   356      * From a list of top level trees, return the list of contained class definitions
   357      */
   358     List<JCClassDecl> listClasses(List<JCCompilationUnit> trees) {
   359         ListBuffer<JCClassDecl> result = new ListBuffer<JCClassDecl>();
   360         for (JCCompilationUnit t : trees) {
   361             for (JCTree def : t.defs) {
   362                 if (def.getTag() == JCTree.CLASSDEF)
   363                     result.append((JCClassDecl)def);
   364             }
   365         }
   366         return result.toList();
   367     }
   369 }

mercurial