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