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