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