Fri, 16 Jul 2010 19:35:24 -0700
6911256: Project Coin: Support Automatic Resource Management (ARM) blocks in the compiler
6964740: Project Coin: More tests for ARM compiler changes
6965277: Project Coin: Correctness issues in ARM implementation
6967065: add -Xlint warning category for Automatic Resource Management (ARM)
Reviewed-by: jjb, darcy, mcimadamore, jjg, briangoetz
Contributed-by: tball@google.com
1 /*
2 * Copyright (c) 2009, 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.javac.nio;
29 import java.io.File;
30 import java.io.FileNotFoundException;
31 import java.io.IOException;
32 import java.net.MalformedURLException;
33 import java.net.URL;
34 import java.nio.charset.Charset;
35 import java.nio.file.Files;
36 import java.nio.file.FileSystem;
37 import java.nio.file.FileSystems;
38 import java.nio.file.FileVisitOption;
39 import java.nio.file.FileVisitResult;
40 import java.nio.file.Path;
41 import java.nio.file.SimpleFileVisitor;
42 import java.nio.file.attribute.Attributes;
43 import java.nio.file.attribute.BasicFileAttributes;
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.Collection;
47 import java.util.Collections;
48 import java.util.EnumSet;
49 import java.util.HashMap;
50 import java.util.Iterator;
51 import java.util.LinkedHashSet;
52 import java.util.Map;
53 import java.util.Set;
54 import javax.lang.model.SourceVersion;
55 import javax.tools.FileObject;
56 import javax.tools.JavaFileManager;
57 import javax.tools.JavaFileObject;
58 import javax.tools.JavaFileObject.Kind;
59 import javax.tools.StandardLocation;
61 import static java.nio.file.FileVisitOption.*;
62 import static javax.tools.StandardLocation.*;
64 import com.sun.tools.javac.file.Paths;
65 import com.sun.tools.javac.util.BaseFileManager;
66 import com.sun.tools.javac.util.Context;
67 import com.sun.tools.javac.util.List;
68 import com.sun.tools.javac.util.ListBuffer;
70 import static com.sun.tools.javac.main.OptionName.*;
73 // NOTE the imports carefully for this compilation unit.
74 //
75 // Path: java.nio.file.Path -- the new NIO type for which this file manager exists
76 //
77 // Paths: com.sun.tools.javac.file.Paths -- legacy javac type for handling path options
78 // The other Paths (java.nio.file.Paths) is not used
80 // NOTE this and related classes depend on new API in JDK 7.
81 // This requires special handling while bootstrapping the JDK build,
82 // when these classes might not yet have been compiled. To workaround
83 // this, the build arranges to make stubs of these classes available
84 // when compiling this and related classes. The set of stub files
85 // is specified in make/build.properties.
87 /**
88 * Implementation of PathFileManager: a JavaFileManager based on the use
89 * of java.nio.file.Path.
90 *
91 * <p>Just as a Path is somewhat analagous to a File, so too is this
92 * JavacPathFileManager analogous to JavacFileManager, as it relates to the
93 * support of FileObjects based on File objects (i.e. just RegularFileObject,
94 * not ZipFileObject and its variants.)
95 *
96 * <p>The default values for the standard locations supported by this file
97 * manager are the same as the default values provided by JavacFileManager --
98 * i.e. as determined by the javac.file.Paths class. To override these values,
99 * call {@link #setLocation}.
100 *
101 * <p>To reduce confusion with Path objects, the locations such as "class path",
102 * "source path", etc, are generically referred to here as "search paths".
103 *
104 * <p><b>This is NOT part of any supported API.
105 * If you write code that depends on this, you do so at your own risk.
106 * This code and its internal interfaces are subject to change or
107 * deletion without notice.</b>
108 */
109 public class JavacPathFileManager extends BaseFileManager implements PathFileManager {
110 protected FileSystem defaultFileSystem;
112 /**
113 * Create a JavacPathFileManager using a given context, optionally registering
114 * it as the JavaFileManager for that context.
115 */
116 public JavacPathFileManager(Context context, boolean register, Charset charset) {
117 super(charset);
118 if (register)
119 context.put(JavaFileManager.class, this);
120 pathsForLocation = new HashMap<Location, PathsForLocation>();
121 fileSystems = new HashMap<Path,FileSystem>();
122 setContext(context);
123 }
125 /**
126 * Set the context for JavacPathFileManager.
127 */
128 @Override
129 protected void setContext(Context context) {
130 super.setContext(context);
131 searchPaths = Paths.instance(context);
132 }
134 @Override
135 public FileSystem getDefaultFileSystem() {
136 if (defaultFileSystem == null)
137 defaultFileSystem = FileSystems.getDefault();
138 return defaultFileSystem;
139 }
141 @Override
142 public void setDefaultFileSystem(FileSystem fs) {
143 defaultFileSystem = fs;
144 }
146 @Override
147 public void flush() throws IOException {
148 contentCache.clear();
149 }
151 @Override
152 public void close() throws IOException {
153 for (FileSystem fs: fileSystems.values())
154 fs.close();
155 }
157 @Override
158 public ClassLoader getClassLoader(Location location) {
159 nullCheck(location);
160 Iterable<? extends Path> path = getLocation(location);
161 if (path == null)
162 return null;
163 ListBuffer<URL> lb = new ListBuffer<URL>();
164 for (Path p: path) {
165 try {
166 lb.append(p.toUri().toURL());
167 } catch (MalformedURLException e) {
168 throw new AssertionError(e);
169 }
170 }
172 return getClassLoader(lb.toArray(new URL[lb.size()]));
173 }
175 // <editor-fold defaultstate="collapsed" desc="Location handling">
177 public boolean hasLocation(Location location) {
178 return (getLocation(location) != null);
179 }
181 public Iterable<? extends Path> getLocation(Location location) {
182 nullCheck(location);
183 lazyInitSearchPaths();
184 PathsForLocation path = pathsForLocation.get(location);
185 if (path == null && !pathsForLocation.containsKey(location)) {
186 setDefaultForLocation(location);
187 path = pathsForLocation.get(location);
188 }
189 return path;
190 }
192 private Path getOutputLocation(Location location) {
193 Iterable<? extends Path> paths = getLocation(location);
194 return (paths == null ? null : paths.iterator().next());
195 }
197 public void setLocation(Location location, Iterable<? extends Path> searchPath)
198 throws IOException
199 {
200 nullCheck(location);
201 lazyInitSearchPaths();
202 if (searchPath == null) {
203 setDefaultForLocation(location);
204 } else {
205 if (location.isOutputLocation())
206 checkOutputPath(searchPath);
207 PathsForLocation pl = new PathsForLocation();
208 for (Path p: searchPath)
209 pl.add(p); // TODO -Xlint:path warn if path not found
210 pathsForLocation.put(location, pl);
211 }
212 }
214 private void checkOutputPath(Iterable<? extends Path> searchPath) throws IOException {
215 Iterator<? extends Path> pathIter = searchPath.iterator();
216 if (!pathIter.hasNext())
217 throw new IllegalArgumentException("empty path for directory");
218 Path path = pathIter.next();
219 if (pathIter.hasNext())
220 throw new IllegalArgumentException("path too long for directory");
221 if (!path.exists())
222 throw new FileNotFoundException(path + ": does not exist");
223 else if (!isDirectory(path))
224 throw new IOException(path + ": not a directory");
225 }
227 private void setDefaultForLocation(Location locn) {
228 Collection<File> files = null;
229 if (locn instanceof StandardLocation) {
230 switch ((StandardLocation) locn) {
231 case CLASS_PATH:
232 files = searchPaths.userClassPath();
233 break;
234 case PLATFORM_CLASS_PATH:
235 files = searchPaths.bootClassPath();
236 break;
237 case SOURCE_PATH:
238 files = searchPaths.sourcePath();
239 break;
240 case CLASS_OUTPUT: {
241 String arg = options.get(D);
242 files = (arg == null ? null : Collections.singleton(new File(arg)));
243 break;
244 }
245 case SOURCE_OUTPUT: {
246 String arg = options.get(S);
247 files = (arg == null ? null : Collections.singleton(new File(arg)));
248 break;
249 }
250 }
251 }
253 PathsForLocation pl = new PathsForLocation();
254 if (files != null) {
255 for (File f: files)
256 pl.add(f.toPath());
257 }
258 pathsForLocation.put(locn, pl);
259 }
261 private void lazyInitSearchPaths() {
262 if (!inited) {
263 setDefaultForLocation(PLATFORM_CLASS_PATH);
264 setDefaultForLocation(CLASS_PATH);
265 setDefaultForLocation(SOURCE_PATH);
266 inited = true;
267 }
268 }
269 // where
270 private boolean inited = false;
272 private Map<Location, PathsForLocation> pathsForLocation;
273 private Paths searchPaths;
275 private static class PathsForLocation extends LinkedHashSet<Path> {
276 private static final long serialVersionUID = 6788510222394486733L;
277 }
279 // </editor-fold>
281 // <editor-fold defaultstate="collapsed" desc="FileObject handling">
283 @Override
284 public Path getPath(FileObject fo) {
285 nullCheck(fo);
286 if (!(fo instanceof PathFileObject))
287 throw new IllegalArgumentException();
288 return ((PathFileObject) fo).getPath();
289 }
291 @Override
292 public boolean isSameFile(FileObject a, FileObject b) {
293 nullCheck(a);
294 nullCheck(b);
295 if (!(a instanceof PathFileObject))
296 throw new IllegalArgumentException("Not supported: " + a);
297 if (!(b instanceof PathFileObject))
298 throw new IllegalArgumentException("Not supported: " + b);
299 return ((PathFileObject) a).isSameFile((PathFileObject) b);
300 }
302 @Override
303 public Iterable<JavaFileObject> list(Location location,
304 String packageName, Set<Kind> kinds, boolean recurse)
305 throws IOException {
306 // validatePackageName(packageName);
307 nullCheck(packageName);
308 nullCheck(kinds);
310 Iterable<? extends Path> paths = getLocation(location);
311 if (paths == null)
312 return List.nil();
313 ListBuffer<JavaFileObject> results = new ListBuffer<JavaFileObject>();
315 for (Path path : paths)
316 list(path, packageName, kinds, recurse, results);
318 return results.toList();
319 }
321 private void list(Path path, String packageName, final Set<Kind> kinds,
322 boolean recurse, final ListBuffer<JavaFileObject> results)
323 throws IOException {
324 if (!path.exists())
325 return;
327 final Path pathDir;
328 if (isDirectory(path))
329 pathDir = path;
330 else {
331 FileSystem fs = getFileSystem(path);
332 if (fs == null)
333 return;
334 pathDir = fs.getRootDirectories().iterator().next();
335 }
336 String sep = path.getFileSystem().getSeparator();
337 Path packageDir = packageName.isEmpty() ? pathDir
338 : pathDir.resolve(packageName.replace(".", sep));
339 if (!packageDir.exists())
340 return;
342 /* Alternate impl of list, superceded by use of Files.walkFileTree */
343 // Deque<Path> queue = new LinkedList<Path>();
344 // queue.add(packageDir);
345 //
346 // Path dir;
347 // while ((dir = queue.poll()) != null) {
348 // DirectoryStream<Path> ds = dir.newDirectoryStream();
349 // try {
350 // for (Path p: ds) {
351 // String name = p.getName().toString();
352 // if (isDirectory(p)) {
353 // if (recurse && SourceVersion.isIdentifier(name)) {
354 // queue.add(p);
355 // }
356 // } else {
357 // if (kinds.contains(getKind(name))) {
358 // JavaFileObject fe =
359 // PathFileObject.createDirectoryPathFileObject(this, p, pathDir);
360 // results.append(fe);
361 // }
362 // }
363 // }
364 // } finally {
365 // ds.close();
366 // }
367 // }
368 int maxDepth = (recurse ? Integer.MAX_VALUE : 1);
369 Set<FileVisitOption> opts = EnumSet.of(DETECT_CYCLES, FOLLOW_LINKS);
370 Files.walkFileTree(packageDir, opts, maxDepth,
371 new SimpleFileVisitor<Path>() {
372 @Override
373 public FileVisitResult preVisitDirectory(Path dir) {
374 if (SourceVersion.isIdentifier(dir.getName().toString())) // JSR 292?
375 return FileVisitResult.CONTINUE;
376 else
377 return FileVisitResult.SKIP_SUBTREE;
378 }
380 @Override
381 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
382 if (attrs.isRegularFile() && kinds.contains(getKind(file.getName().toString()))) {
383 JavaFileObject fe =
384 PathFileObject.createDirectoryPathFileObject(
385 JavacPathFileManager.this, file, pathDir);
386 results.append(fe);
387 }
388 return FileVisitResult.CONTINUE;
389 }
390 });
391 }
393 @Override
394 public Iterable<? extends JavaFileObject> getJavaFileObjectsFromPaths(
395 Iterable<? extends Path> paths) {
396 ArrayList<PathFileObject> result;
397 if (paths instanceof Collection<?>)
398 result = new ArrayList<PathFileObject>(((Collection<?>)paths).size());
399 else
400 result = new ArrayList<PathFileObject>();
401 for (Path p: paths)
402 result.add(PathFileObject.createSimplePathFileObject(this, nullCheck(p)));
403 return result;
404 }
406 @Override
407 public Iterable<? extends JavaFileObject> getJavaFileObjects(Path... paths) {
408 return getJavaFileObjectsFromPaths(Arrays.asList(nullCheck(paths)));
409 }
411 @Override
412 public JavaFileObject getJavaFileForInput(Location location,
413 String className, Kind kind) throws IOException {
414 return getFileForInput(location, getRelativePath(className, kind));
415 }
417 @Override
418 public FileObject getFileForInput(Location location,
419 String packageName, String relativeName) throws IOException {
420 return getFileForInput(location, getRelativePath(packageName, relativeName));
421 }
423 private JavaFileObject getFileForInput(Location location, String relativePath)
424 throws IOException {
425 for (Path p: getLocation(location)) {
426 if (isDirectory(p)) {
427 Path f = resolve(p, relativePath);
428 if (f.exists())
429 return PathFileObject.createDirectoryPathFileObject(this, f, p);
430 } else {
431 FileSystem fs = getFileSystem(p);
432 if (fs != null) {
433 Path file = getPath(fs, relativePath);
434 if (file.exists())
435 return PathFileObject.createJarPathFileObject(this, file);
436 }
437 }
438 }
439 return null;
440 }
442 @Override
443 public JavaFileObject getJavaFileForOutput(Location location,
444 String className, Kind kind, FileObject sibling) throws IOException {
445 return getFileForOutput(location, getRelativePath(className, kind), sibling);
446 }
448 @Override
449 public FileObject getFileForOutput(Location location, String packageName,
450 String relativeName, FileObject sibling)
451 throws IOException {
452 return getFileForOutput(location, getRelativePath(packageName, relativeName), sibling);
453 }
455 private JavaFileObject getFileForOutput(Location location,
456 String relativePath, FileObject sibling) {
457 Path dir = getOutputLocation(location);
458 if (dir == null) {
459 if (location == CLASS_OUTPUT) {
460 Path siblingDir = null;
461 if (sibling != null && sibling instanceof PathFileObject) {
462 siblingDir = ((PathFileObject) sibling).getPath().getParent();
463 }
464 return PathFileObject.createSiblingPathFileObject(this,
465 siblingDir.resolve(getBaseName(relativePath)),
466 relativePath);
467 } else if (location == SOURCE_OUTPUT) {
468 dir = getOutputLocation(CLASS_OUTPUT);
469 }
470 }
472 Path file;
473 if (dir != null) {
474 file = resolve(dir, relativePath);
475 return PathFileObject.createDirectoryPathFileObject(this, file, dir);
476 } else {
477 file = getPath(getDefaultFileSystem(), relativePath);
478 return PathFileObject.createSimplePathFileObject(this, file);
479 }
481 }
483 @Override
484 public String inferBinaryName(Location location, JavaFileObject fo) {
485 nullCheck(fo);
486 // Need to match the path semantics of list(location, ...)
487 Iterable<? extends Path> paths = getLocation(location);
488 if (paths == null) {
489 return null;
490 }
492 if (!(fo instanceof PathFileObject))
493 throw new IllegalArgumentException(fo.getClass().getName());
495 return ((PathFileObject) fo).inferBinaryName(paths);
496 }
498 private FileSystem getFileSystem(Path p) throws IOException {
499 FileSystem fs = fileSystems.get(p);
500 if (fs == null) {
501 fs = FileSystems.newFileSystem(p, Collections.<String,Void>emptyMap(), null);
502 fileSystems.put(p, fs);
503 }
504 return fs;
505 }
507 private Map<Path,FileSystem> fileSystems;
509 // </editor-fold>
511 // <editor-fold defaultstate="collapsed" desc="Utility methods">
513 private static String getRelativePath(String className, Kind kind) {
514 return className.replace(".", "/") + kind.extension;
515 }
517 private static String getRelativePath(String packageName, String relativeName) {
518 return packageName.replace(".", "/") + relativeName;
519 }
521 private static String getBaseName(String relativePath) {
522 int lastSep = relativePath.lastIndexOf("/");
523 return relativePath.substring(lastSep + 1); // safe if "/" not found
524 }
526 private static boolean isDirectory(Path path) throws IOException {
527 BasicFileAttributes attrs = Attributes.readBasicFileAttributes(path);
528 return attrs.isDirectory();
529 }
531 private static Path getPath(FileSystem fs, String relativePath) {
532 return fs.getPath(relativePath.replace("/", fs.getSeparator()));
533 }
535 private static Path resolve(Path base, String relativePath) {
536 FileSystem fs = base.getFileSystem();
537 Path rp = fs.getPath(relativePath.replace("/", fs.getSeparator()));
538 return base.resolve(rp);
539 }
541 // </editor-fold>
543 }