changeset 476:4011f49b4af8

6906175: bridge JSR199 and JSR 203 APIs Reviewed-by: darcy, alanb
author jjg
date Fri, 11 Dec 2009 14:26:27 -0800
parents ff823a039e16
children fbeb560f39e7
files make/build.properties make/build.xml src/share/classes/com/sun/tools/javac/file/BaseFileObject.java src/share/classes/com/sun/tools/javac/file/CloseableURLClassLoader.java src/share/classes/com/sun/tools/javac/file/JavacFileManager.java src/share/classes/com/sun/tools/javac/file/Paths.java src/share/classes/com/sun/tools/javac/nio/JavacPathFileManager.java src/share/classes/com/sun/tools/javac/nio/PathFileManager.java src/share/classes/com/sun/tools/javac/nio/PathFileObject.java src/share/classes/com/sun/tools/javac/util/BaseFileManager.java src/share/classes/com/sun/tools/javac/util/CloseableURLClassLoader.java src/share/classes/javax/tools/StandardJavaFileManager.java test/tools/javac/nio/compileTest/CompileTest.java test/tools/javac/nio/compileTest/HelloPathWorld.java
diffstat 14 files changed, 1674 insertions(+), 417 deletions(-) [+]
line wrap: on
line diff
--- a/make/build.properties	Thu Dec 10 20:35:31 2009 -0800
+++ b/make/build.properties	Fri Dec 11 14:26:27 2009 -0800
@@ -149,11 +149,26 @@
 #
 
 # The following files require the import JDK to be available
-require.import.jdk.files =
+require.import.jdk.files = \
+    com/sun/tools/javac/nio/*.java
 
 # The following files in the import jdk source directory are required
 # in order to compile the files defined in ${require.import.jdk.files}
-import.jdk.stub.files =
+#
+# For NIO, the list of stub files is defined by the contents of the primary
+# API packages, together with such types that may be required in order to
+# compile the stubs. Some of these dependencies would go away if the stub
+# generator were to be improved -- e.g. by removing unnecessary imports.
+#
+import.jdk.stub.files = \
+    java/io/File.java \
+    java/nio/file/**.java \
+    java/nio/file/attribute/**.java \
+    java/nio/file/spi/**.java \
+    java/nio/channels/AsynchronousChannel.java \
+    java/nio/channels/AsynchronousFileChannel.java \
+    java/nio/channels/CompletionHandler.java \
+    java/nio/channels/SeekableByteChannel.java
 
 # The following value is used by the main jtreg target.
 # An empty value means all tests
--- a/make/build.xml	Thu Dec 10 20:35:31 2009 -0800
+++ b/make/build.xml	Fri Dec 11 14:26:27 2009 -0800
@@ -98,7 +98,7 @@
         import.jdk should be unset, or set to jdk home (to use rt.jar)
         or to jdk repo (to use src/share/classes).
         Based on the value, if any, set up default values for javac's sourcepath,
-        classpath and bootclasspath. Note: the default values are overridden 
+        classpath and bootclasspath. Note: the default values are overridden
         in the build-bootstrap-classes macro. -->
 
     <available property="import.jdk.src.dir" value="${import.jdk}/src/share/classes"
@@ -552,8 +552,8 @@
                     <compilerarg line="${javac.version.opt}"/>
                     <compilerarg line="${javac.lint.opts}"/>
                 </javac>
-                <copy todir="@{classes.dir}">
-                    <fileset dir="${src.classes.dir}" includes="@{includes}">
+                <copy todir="@{classes.dir}" includeemptydirs="false">
+                    <fileset dir="${src.classes.dir}" includes="@{includes}" excludes="@{excludes}">
                         <exclude name="**/*.java"/>
                         <exclude name="**/*.properties"/>
                         <exclude name="**/*-template"/>
--- a/src/share/classes/com/sun/tools/javac/file/BaseFileObject.java	Thu Dec 10 20:35:31 2009 -0800
+++ b/src/share/classes/com/sun/tools/javac/file/BaseFileObject.java	Fri Dec 11 14:26:27 2009 -0800
@@ -39,6 +39,8 @@
 
 import static javax.tools.JavaFileObject.Kind.*;
 
+import com.sun.tools.javac.util.BaseFileManager;
+
 /**
  * <p><b>This is NOT part of any API supported by Sun Microsystems.
  * If you write code that depends on this, you do so at your own risk.
@@ -74,14 +76,7 @@
     protected abstract String inferBinaryName(Iterable<? extends File> path);
 
     protected static JavaFileObject.Kind getKind(String filename) {
-        if (filename.endsWith(CLASS.extension))
-            return CLASS;
-        else if (filename.endsWith(SOURCE.extension))
-            return SOURCE;
-        else if (filename.endsWith(HTML.extension))
-            return HTML;
-        else
-            return OTHER;
+        return BaseFileManager.getKind(filename);
     }
 
     protected static String removeExtension(String fileName) {
--- a/src/share/classes/com/sun/tools/javac/file/CloseableURLClassLoader.java	Thu Dec 10 20:35:31 2009 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,107 +0,0 @@
-/*
- * Copyright 2007 Sun Microsystems, Inc.  All Rights Reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Sun designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Sun in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
- */
-
-package com.sun.tools.javac.file;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.lang.reflect.Field;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.ArrayList;
-import java.util.jar.JarFile;
-
-/**
- * A URLClassLoader that also implements Closeable.
- * Reflection is used to access internal data structures in the URLClassLoader,
- * since no public API exists for this purpose. Therefore this code is somewhat
- * fragile. Caveat emptor.
- * @throws Error if the internal data structures are not as expected.
- *
- *  <p><b>This is NOT part of any API supported by Sun Microsystems.  If
- *  you write code that depends on this, you do so at your own risk.
- *  This code and its internal interfaces are subject to change or
- *  deletion without notice.</b>
- */
-class CloseableURLClassLoader
-        extends URLClassLoader implements Closeable {
-    CloseableURLClassLoader(URL[] urls, ClassLoader parent) throws Error {
-        super(urls, parent);
-        try {
-            getLoaders(); //proactive check that URLClassLoader is as expected
-        } catch (Throwable t) {
-            throw new Error("cannot create CloseableURLClassLoader", t);
-        }
-    }
-
-    /**
-     * Close any jar files that may have been opened by the class loader.
-     * Reflection is used to access the jar files in the URLClassLoader's
-     * internal data structures.
-     * @throws java.io.IOException if the jar files cannot be found for any
-     * reson, or if closing the jar file itself causes an IOException.
-     */
-    public void close() throws IOException {
-        try {
-            for (Object l: getLoaders()) {
-                if (l.getClass().getName().equals("sun.misc.URLClassPath$JarLoader")) {
-                    Field jarField = l.getClass().getDeclaredField("jar");
-                    JarFile jar = (JarFile) getField(l, jarField);
-                    if (jar != null) {
-                        //System.err.println("CloseableURLClassLoader: closing " + jar);
-                        jar.close();
-                    }
-                }
-            }
-        } catch (Throwable t) {
-            IOException e = new IOException("cannot close class loader");
-            e.initCause(t);
-            throw e;
-        }
-    }
-
-    private ArrayList<?> getLoaders()
-            throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException
-    {
-        Field ucpField = URLClassLoader.class.getDeclaredField("ucp");
-        Object urlClassPath = getField(this, ucpField);
-        if (urlClassPath == null)
-            throw new AssertionError("urlClassPath not set in URLClassLoader");
-        Field loadersField = urlClassPath.getClass().getDeclaredField("loaders");
-        return (ArrayList<?>) getField(urlClassPath, loadersField);
-    }
-
-    private Object getField(Object o, Field f)
-            throws IllegalArgumentException, IllegalAccessException {
-        boolean prev = f.isAccessible();
-        try {
-            f.setAccessible(true);
-            return f.get(o);
-        } finally {
-            f.setAccessible(prev);
-        }
-    }
-
-}
--- a/src/share/classes/com/sun/tools/javac/file/JavacFileManager.java	Thu Dec 10 20:35:31 2009 -0800
+++ b/src/share/classes/com/sun/tools/javac/file/JavacFileManager.java	Fri Dec 11 14:26:27 2009 -0800
@@ -26,29 +26,16 @@
 package com.sun.tools.javac.file;
 
 import java.io.ByteArrayOutputStream;
-import java.io.Closeable;
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.OutputStreamWriter;
-import java.lang.ref.SoftReference;
-import java.lang.reflect.Constructor;
 import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
-import java.net.URLClassLoader;
-import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
-import java.nio.channels.FileChannel;
 import java.nio.charset.Charset;
-import java.nio.charset.CharsetDecoder;
-import java.nio.charset.CoderResult;
-import java.nio.charset.CodingErrorAction;
-import java.nio.charset.IllegalCharsetNameException;
-import java.nio.charset.UnsupportedCharsetException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -66,18 +53,13 @@
 import javax.tools.JavaFileObject;
 import javax.tools.StandardJavaFileManager;
 
-import com.sun.tools.javac.code.Source;
 import com.sun.tools.javac.file.RelativePath.RelativeFile;
 import com.sun.tools.javac.file.RelativePath.RelativeDirectory;
-import com.sun.tools.javac.main.JavacOption;
 import com.sun.tools.javac.main.OptionName;
-import com.sun.tools.javac.main.RecognizedOptions;
+import com.sun.tools.javac.util.BaseFileManager;
 import com.sun.tools.javac.util.Context;
-import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition;
 import com.sun.tools.javac.util.List;
 import com.sun.tools.javac.util.ListBuffer;
-import com.sun.tools.javac.util.Log;
-import com.sun.tools.javac.util.Options;
 
 import static javax.tools.StandardLocation.*;
 import static com.sun.tools.javac.main.OptionName.*;
@@ -91,7 +73,7 @@
  * This code and its internal interfaces are subject to change or
  * deletion without notice.</b>
  */
-public class JavacFileManager implements StandardJavaFileManager {
+public class JavacFileManager extends BaseFileManager implements StandardJavaFileManager {
 
     boolean useZipFileIndex;
 
@@ -102,17 +84,10 @@
             return buffer.toString().toCharArray();
     }
 
-    /**
-     * The log to be used for error reporting.
-     */
-    protected Log log;
-
     /** Encapsulates knowledge of paths
      */
     private Paths paths;
 
-    private Options options;
-
     private FSInfo fsInfo;
 
     private final File uninited = new File("U N I N I T E D");
@@ -134,12 +109,6 @@
 
     protected boolean mmappedIO;
     protected boolean ignoreSymbolFile;
-    protected String classLoaderClass;
-
-    /**
-     * User provided charset (through javax.tools).
-     */
-    protected Charset charset;
 
     /**
      * Register a Context.Factory to create a JavacFileManager.
@@ -157,18 +126,18 @@
      * it as the JavaFileManager for that context.
      */
     public JavacFileManager(Context context, boolean register, Charset charset) {
+        super(charset);
         if (register)
             context.put(JavaFileManager.class, this);
-        byteBufferCache = new ByteBufferCache();
-        this.charset = charset;
         setContext(context);
     }
 
     /**
      * Set the context for JavacFileManager.
      */
+    @Override
     public void setContext(Context context) {
-        log = Log.instance(context);
+        super.setContext(context);
         if (paths == null) {
             paths = Paths.instance(context);
         } else {
@@ -177,14 +146,12 @@
             paths.setContext(context);
         }
 
-        options = Options.instance(context);
         fsInfo = FSInfo.instance(context);
 
         useZipFileIndex = System.getProperty("useJavaUtilZip") == null;// TODO: options.get("useJavaUtilZip") == null;
 
         mmappedIO = options.get("mmappedIO") != null;
         ignoreSymbolFile = options.get("ignore.symbol.file") != null;
-        classLoaderClass = options.get("procloader");
     }
 
     public JavaFileObject getFileForInput(String name) {
@@ -214,17 +181,6 @@
         return getJavaFileObjectsFromStrings(Arrays.asList(nullCheck(names)));
     }
 
-    protected JavaFileObject.Kind getKind(String extension) {
-        if (extension.equals(JavaFileObject.Kind.CLASS.extension))
-            return JavaFileObject.Kind.CLASS;
-        else if (extension.equals(JavaFileObject.Kind.SOURCE.extension))
-            return JavaFileObject.Kind.SOURCE;
-        else if (extension.equals(JavaFileObject.Kind.HTML.extension))
-            return JavaFileObject.Kind.HTML;
-        else
-            return JavaFileObject.Kind.OTHER;
-    }
-
     private static boolean isValidName(String name) {
         // Arguably, isValidName should reject keywords (such as in SourceVersion.isName() ),
         // but the set of keywords depends on the source level, and we don't want
@@ -359,9 +315,7 @@
     }
 
     private boolean isValidFile(String s, Set<JavaFileObject.Kind> fileKinds) {
-        int lastDot = s.lastIndexOf(".");
-        String extn = (lastDot == -1 ? s : s.substring(lastDot));
-        JavaFileObject.Kind kind = getKind(extn);
+        JavaFileObject.Kind kind = getKind(s);
         return fileKinds.contains(kind);
     }
 
@@ -564,18 +518,6 @@
         }
     }
 
-    CharBuffer getCachedContent(JavaFileObject file) {
-        SoftReference<CharBuffer> r = contentCache.get(file);
-        return (r == null ? null : r.get());
-    }
-
-    void cache(JavaFileObject file, CharBuffer cb) {
-        contentCache.put(file, new SoftReference<CharBuffer>(cb));
-    }
-
-    private final Map<JavaFileObject, SoftReference<CharBuffer>> contentCache
-            = new HashMap<JavaFileObject, SoftReference<CharBuffer>>();
-
     private String defaultEncodingName;
     private String getDefaultEncodingName() {
         if (defaultEncodingName == null) {
@@ -585,161 +527,6 @@
         return defaultEncodingName;
     }
 
-    protected String getEncodingName() {
-        String encName = options.get(OptionName.ENCODING);
-        if (encName == null)
-            return getDefaultEncodingName();
-        else
-            return encName;
-    }
-
-    protected Source getSource() {
-        String sourceName = options.get(OptionName.SOURCE);
-        Source source = null;
-        if (sourceName != null)
-            source = Source.lookup(sourceName);
-        return (source != null ? source : Source.DEFAULT);
-    }
-
-    /**
-     * Make a byte buffer from an input stream.
-     */
-    ByteBuffer makeByteBuffer(InputStream in)
-        throws IOException {
-        int limit = in.available();
-        if (mmappedIO && in instanceof FileInputStream) {
-            // Experimental memory mapped I/O
-            FileInputStream fin = (FileInputStream)in;
-            return fin.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, limit);
-        }
-        if (limit < 1024) limit = 1024;
-        ByteBuffer result = byteBufferCache.get(limit);
-        int position = 0;
-        while (in.available() != 0) {
-            if (position >= limit)
-                // expand buffer
-                result = ByteBuffer.
-                    allocate(limit <<= 1).
-                    put((ByteBuffer)result.flip());
-            int count = in.read(result.array(),
-                position,
-                limit - position);
-            if (count < 0) break;
-            result.position(position += count);
-        }
-        return (ByteBuffer)result.flip();
-    }
-
-    void recycleByteBuffer(ByteBuffer bb) {
-        byteBufferCache.put(bb);
-    }
-
-    /**
-     * A single-element cache of direct byte buffers.
-     */
-    private static class ByteBufferCache {
-        private ByteBuffer cached;
-        ByteBuffer get(int capacity) {
-            if (capacity < 20480) capacity = 20480;
-            ByteBuffer result =
-                (cached != null && cached.capacity() >= capacity)
-                ? (ByteBuffer)cached.clear()
-                : ByteBuffer.allocate(capacity + capacity>>1);
-            cached = null;
-            return result;
-        }
-        void put(ByteBuffer x) {
-            cached = x;
-        }
-    }
-
-    private final ByteBufferCache byteBufferCache;
-
-    CharsetDecoder getDecoder(String encodingName, boolean ignoreEncodingErrors) {
-        Charset cs = (this.charset == null)
-            ? Charset.forName(encodingName)
-            : this.charset;
-        CharsetDecoder decoder = cs.newDecoder();
-
-        CodingErrorAction action;
-        if (ignoreEncodingErrors)
-            action = CodingErrorAction.REPLACE;
-        else
-            action = CodingErrorAction.REPORT;
-
-        return decoder
-            .onMalformedInput(action)
-            .onUnmappableCharacter(action);
-    }
-
-    /**
-     * Decode a ByteBuffer into a CharBuffer.
-     */
-    CharBuffer decode(ByteBuffer inbuf, boolean ignoreEncodingErrors) {
-        String encodingName = getEncodingName();
-        CharsetDecoder decoder;
-        try {
-            decoder = getDecoder(encodingName, ignoreEncodingErrors);
-        } catch (IllegalCharsetNameException e) {
-            log.error("unsupported.encoding", encodingName);
-            return (CharBuffer)CharBuffer.allocate(1).flip();
-        } catch (UnsupportedCharsetException e) {
-            log.error("unsupported.encoding", encodingName);
-            return (CharBuffer)CharBuffer.allocate(1).flip();
-        }
-
-        // slightly overestimate the buffer size to avoid reallocation.
-        float factor =
-            decoder.averageCharsPerByte() * 0.8f +
-            decoder.maxCharsPerByte() * 0.2f;
-        CharBuffer dest = CharBuffer.
-            allocate(10 + (int)(inbuf.remaining()*factor));
-
-        while (true) {
-            CoderResult result = decoder.decode(inbuf, dest, true);
-            dest.flip();
-
-            if (result.isUnderflow()) { // done reading
-                // make sure there is at least one extra character
-                if (dest.limit() == dest.capacity()) {
-                    dest = CharBuffer.allocate(dest.capacity()+1).put(dest);
-                    dest.flip();
-                }
-                return dest;
-            } else if (result.isOverflow()) { // buffer too small; expand
-                int newCapacity =
-                    10 + dest.capacity() +
-                    (int)(inbuf.remaining()*decoder.maxCharsPerByte());
-                dest = CharBuffer.allocate(newCapacity).put(dest);
-            } else if (result.isMalformed() || result.isUnmappable()) {
-                // bad character in input
-
-                // report coding error (warn only pre 1.5)
-                if (!getSource().allowEncodingErrors()) {
-                    log.error(new SimpleDiagnosticPosition(dest.limit()),
-                              "illegal.char.for.encoding",
-                              charset == null ? encodingName : charset.name());
-                } else {
-                    log.warning(new SimpleDiagnosticPosition(dest.limit()),
-                                "illegal.char.for.encoding",
-                                charset == null ? encodingName : charset.name());
-                }
-
-                // skip past the coding error
-                inbuf.position(inbuf.position() + result.length());
-
-                // undo the flip() to prepare the output buffer
-                // for more translation
-                dest.position(dest.limit());
-                dest.limit(dest.capacity());
-                dest.put((char)0xfffd); // backward compatible
-            } else {
-                throw new AssertionError(result);
-            }
-        }
-        // unreached
-    }
-
     public ClassLoader getClassLoader(Location location) {
         nullCheck(location);
         Iterable<? extends File> path = getLocation(location);
@@ -754,39 +541,7 @@
             }
         }
 
-        URL[] urls = lb.toArray(new URL[lb.size()]);
-        ClassLoader thisClassLoader = getClass().getClassLoader();
-
-        // Bug: 6558476
-        // Ideally, ClassLoader should be Closeable, but before JDK7 it is not.
-        // On older versions, try the following, to get a closeable classloader.
-
-        // 1: Allow client to specify the class to use via hidden option
-        if (classLoaderClass != null) {
-            try {
-                Class<? extends ClassLoader> loader =
-                        Class.forName(classLoaderClass).asSubclass(ClassLoader.class);
-                Class<?>[] constrArgTypes = { URL[].class, ClassLoader.class };
-                Constructor<? extends ClassLoader> constr = loader.getConstructor(constrArgTypes);
-                return constr.newInstance(new Object[] { urls, thisClassLoader });
-            } catch (Throwable t) {
-                // ignore errors loading user-provided class loader, fall through
-            }
-        }
-
-        // 2: If URLClassLoader implements Closeable, use that.
-        if (Closeable.class.isAssignableFrom(URLClassLoader.class))
-            return new URLClassLoader(urls, thisClassLoader);
-
-        // 3: Try using private reflection-based CloseableURLClassLoader
-        try {
-            return new CloseableURLClassLoader(urls, thisClassLoader);
-        } catch (Throwable t) {
-            // ignore errors loading workaround class loader, fall through
-        }
-
-        // 4: If all else fails, use plain old standard URLClassLoader
-        return new URLClassLoader(urls, thisClassLoader);
+        return getClassLoader(lb.toArray(new URL[lb.size()]));
     }
 
     public Iterable<JavaFileObject> list(Location location,
@@ -836,38 +591,6 @@
         return a.equals(b);
     }
 
-    public boolean handleOption(String current, Iterator<String> remaining) {
-        for (JavacOption o: javacFileManagerOptions) {
-            if (o.matches(current))  {
-                if (o.hasArg()) {
-                    if (remaining.hasNext()) {
-                        if (!o.process(options, current, remaining.next()))
-                            return true;
-                    }
-                } else {
-                    if (!o.process(options, current))
-                        return true;
-                }
-                // operand missing, or process returned false
-                throw new IllegalArgumentException(current);
-            }
-        }
-
-        return false;
-    }
-    // where
-        private static JavacOption[] javacFileManagerOptions =
-            RecognizedOptions.getJavacFileManagerOptions(
-            new RecognizedOptions.GrumpyHelper());
-
-    public int isSupportedOption(String option) {
-        for (JavacOption o : javacFileManagerOptions) {
-            if (o.matches(option))
-                return o.hasArg() ? 1 : 0;
-        }
-        return -1;
-    }
-
     public boolean hasLocation(Location location) {
         return getLocation(location) != null;
     }
@@ -1115,15 +838,4 @@
         }
         throw new IllegalArgumentException("Invalid relative path: " + file);
     }
-
-    private static <T> T nullCheck(T o) {
-        o.getClass(); // null check
-        return o;
-    }
-
-    private static <T> Iterable<T> nullCheck(Iterable<T> it) {
-        for (T t : it)
-            t.getClass(); // null check
-        return it;
-    }
 }
--- a/src/share/classes/com/sun/tools/javac/file/Paths.java	Thu Dec 10 20:35:31 2009 -0800
+++ b/src/share/classes/com/sun/tools/javac/file/Paths.java	Fri Dec 11 14:26:27 2009 -0800
@@ -66,7 +66,7 @@
      *  @param context the context
      *  @return the Paths instance for this context
      */
-    static Paths instance(Context context) {
+    public static Paths instance(Context context) {
         Paths instance = context.get(pathsKey);
         if (instance == null)
             instance = new Paths(context);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/javac/nio/JavacPathFileManager.java	Fri Dec 11 14:26:27 2009 -0800
@@ -0,0 +1,543 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.tools.javac.nio;
+
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.FileVisitOption;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.Attributes;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import javax.lang.model.SourceVersion;
+import javax.tools.FileObject;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.JavaFileObject.Kind;
+import javax.tools.StandardLocation;
+
+import static java.nio.file.FileVisitOption.*;
+import static javax.tools.StandardLocation.*;
+
+import com.sun.tools.javac.file.Paths;
+import com.sun.tools.javac.util.BaseFileManager;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+
+import static com.sun.tools.javac.main.OptionName.*;
+
+
+// NOTE the imports carefully for this compilation unit.
+//
+// Path:  java.nio.file.Path -- the new NIO type for which this file manager exists
+//
+// Paths: com.sun.tools.javac.file.Paths -- legacy javac type for handling path options
+//      The other Paths (java.nio.file.Paths) is not used
+
+// NOTE this and related classes depend on new API in JDK 7.
+// This requires special handling while bootstrapping the JDK build,
+// when these classes might not yet have been compiled. To workaround
+// this, the build arranges to make stubs of these classes available
+// when compiling this and related classes. The set of stub files
+// is specified in make/build.properties.
+
+/**
+ *  Implementation of PathFileManager: a JavaFileManager based on the use
+ *  of java.nio.file.Path.
+ *
+ *  <p>Just as a Path is somewhat analagous to a File, so too is this
+ *  JavacPathFileManager analogous to JavacFileManager, as it relates to the
+ *  support of FileObjects based on File objects (i.e. just RegularFileObject,
+ *  not ZipFileObject and its variants.)
+ *
+ *  <p>The default values for the standard locations supported by this file
+ *  manager are the same as the default values provided by JavacFileManager --
+ *  i.e. as determined by the javac.file.Paths class. To override these values,
+ *  call {@link #setLocation}.
+ *
+ *  <p>To reduce confusion with Path objects, the locations such as "class path",
+ *  "source path", etc, are generically referred to here as "search paths".
+ *
+ *  <p><b>This is NOT part of any API supported by Sun Microsystems.  If
+ *  you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ */
+public class JavacPathFileManager extends BaseFileManager implements PathFileManager {
+    protected FileSystem defaultFileSystem;
+
+    /**
+     * Create a JavacPathFileManager using a given context, optionally registering
+     * it as the JavaFileManager for that context.
+     */
+    public JavacPathFileManager(Context context, boolean register, Charset charset) {
+        super(charset);
+        if (register)
+            context.put(JavaFileManager.class, this);
+        pathsForLocation = new HashMap<Location, PathsForLocation>();
+        fileSystems = new HashMap<Path,FileSystem>();
+        setContext(context);
+    }
+
+    /**
+     * Set the context for JavacPathFileManager.
+     */
+    @Override
+    protected void setContext(Context context) {
+        super.setContext(context);
+        searchPaths = Paths.instance(context);
+    }
+
+    @Override
+    public FileSystem getDefaultFileSystem() {
+        if (defaultFileSystem == null)
+            defaultFileSystem = FileSystems.getDefault();
+        return defaultFileSystem;
+    }
+
+    @Override
+    public void setDefaultFileSystem(FileSystem fs) {
+        defaultFileSystem = fs;
+    }
+
+    @Override
+    public void flush() throws IOException {
+        contentCache.clear();
+    }
+
+    @Override
+    public void close() throws IOException {
+        for (FileSystem fs: fileSystems.values())
+            fs.close();
+    }
+
+    @Override
+    public ClassLoader getClassLoader(Location location) {
+        nullCheck(location);
+        Iterable<? extends Path> path = getLocation(location);
+        if (path == null)
+            return null;
+        ListBuffer<URL> lb = new ListBuffer<URL>();
+        for (Path p: path) {
+            try {
+                lb.append(p.toUri().toURL());
+            } catch (MalformedURLException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        return getClassLoader(lb.toArray(new URL[lb.size()]));
+    }
+
+    // <editor-fold defaultstate="collapsed" desc="Location handling">
+
+    public boolean hasLocation(Location location) {
+        return (getLocation(location) != null);
+    }
+
+    public Iterable<? extends Path> getLocation(Location location) {
+        nullCheck(location);
+        lazyInitSearchPaths();
+        PathsForLocation path = pathsForLocation.get(location);
+        if (path == null && !pathsForLocation.containsKey(location)) {
+            setDefaultForLocation(location);
+            path = pathsForLocation.get(location);
+        }
+        return path;
+    }
+
+    private Path getOutputLocation(Location location) {
+        Iterable<? extends Path> paths = getLocation(location);
+        return (paths == null ? null : paths.iterator().next());
+    }
+
+    public void setLocation(Location location, Iterable<? extends Path> searchPath)
+            throws IOException
+    {
+        nullCheck(location);
+        lazyInitSearchPaths();
+        if (searchPath == null) {
+            setDefaultForLocation(location);
+        } else {
+            if (location.isOutputLocation())
+                checkOutputPath(searchPath);
+            PathsForLocation pl = new PathsForLocation();
+            for (Path p: searchPath)
+                pl.add(p);  // TODO -Xlint:path warn if path not found
+            pathsForLocation.put(location, pl);
+        }
+    }
+
+    private void checkOutputPath(Iterable<? extends Path> searchPath) throws IOException {
+        Iterator<? extends Path> pathIter = searchPath.iterator();
+        if (!pathIter.hasNext())
+            throw new IllegalArgumentException("empty path for directory");
+        Path path = pathIter.next();
+        if (pathIter.hasNext())
+            throw new IllegalArgumentException("path too long for directory");
+        if (!path.exists())
+            throw new FileNotFoundException(path + ": does not exist");
+        else if (!isDirectory(path))
+            throw new IOException(path + ": not a directory");
+    }
+
+    private void setDefaultForLocation(Location locn) {
+        Collection<File> files = null;
+        if (locn instanceof StandardLocation) {
+            switch ((StandardLocation) locn) {
+                case CLASS_PATH:
+                    files = searchPaths.userClassPath();
+                    break;
+                case PLATFORM_CLASS_PATH:
+                    files = searchPaths.bootClassPath();
+                    break;
+                case SOURCE_PATH:
+                    files = searchPaths.sourcePath();
+                    break;
+                case CLASS_OUTPUT: {
+                    String arg = options.get(D);
+                    files = (arg == null ? null : Collections.singleton(new File(arg)));
+                    break;
+                }
+                case SOURCE_OUTPUT: {
+                    String arg = options.get(S);
+                    files = (arg == null ? null : Collections.singleton(new File(arg)));
+                    break;
+                }
+            }
+        }
+
+        PathsForLocation pl = new PathsForLocation();
+        if (files != null) {
+            for (File f: files)
+                pl.add(f.toPath());
+        }
+        pathsForLocation.put(locn, pl);
+    }
+
+    private void lazyInitSearchPaths() {
+        if (!inited) {
+            setDefaultForLocation(PLATFORM_CLASS_PATH);
+            setDefaultForLocation(CLASS_PATH);
+            setDefaultForLocation(SOURCE_PATH);
+            inited = true;
+        }
+    }
+    // where
+        private boolean inited = false;
+
+    private Map<Location, PathsForLocation> pathsForLocation;
+    private Paths searchPaths;
+
+    private static class PathsForLocation extends LinkedHashSet<Path> {
+        private static final long serialVersionUID = 6788510222394486733L;
+    }
+
+    // </editor-fold>
+
+    // <editor-fold defaultstate="collapsed" desc="FileObject handling">
+
+    @Override
+    public Path getPath(FileObject fo) {
+        nullCheck(fo);
+        if (!(fo instanceof PathFileObject))
+            throw new IllegalArgumentException();
+        return ((PathFileObject) fo).getPath();
+    }
+
+    @Override
+    public boolean isSameFile(FileObject a, FileObject b) {
+        nullCheck(a);
+        nullCheck(b);
+        if (!(a instanceof PathFileObject))
+            throw new IllegalArgumentException("Not supported: " + a);
+        if (!(b instanceof PathFileObject))
+            throw new IllegalArgumentException("Not supported: " + b);
+        return ((PathFileObject) a).isSameFile((PathFileObject) b);
+    }
+
+    @Override
+    public Iterable<JavaFileObject> list(Location location,
+            String packageName, Set<Kind> kinds, boolean recurse)
+            throws IOException {
+        // validatePackageName(packageName);
+        nullCheck(packageName);
+        nullCheck(kinds);
+
+        Iterable<? extends Path> paths = getLocation(location);
+        if (paths == null)
+            return List.nil();
+        ListBuffer<JavaFileObject> results = new ListBuffer<JavaFileObject>();
+
+        for (Path path : paths)
+            list(path, packageName, kinds, recurse, results);
+
+        return results.toList();
+    }
+
+    private void list(Path path, String packageName, final Set<Kind> kinds,
+            boolean recurse, final ListBuffer<JavaFileObject> results)
+            throws IOException {
+        if (!path.exists())
+            return;
+
+        final Path pathDir;
+        if (isDirectory(path))
+            pathDir = path;
+        else {
+            FileSystem fs = getFileSystem(path);
+            if (fs == null)
+                return;
+            pathDir = fs.getRootDirectories().iterator().next();
+        }
+        String sep = path.getFileSystem().getSeparator();
+        Path packageDir = packageName.isEmpty() ? pathDir
+                : pathDir.resolve(packageName.replace(".", sep));
+        if (!packageDir.exists())
+            return;
+
+/* Alternate impl of list, superceded by use of Files.walkFileTree */
+//        Deque<Path> queue = new LinkedList<Path>();
+//        queue.add(packageDir);
+//
+//        Path dir;
+//        while ((dir = queue.poll()) != null) {
+//            DirectoryStream<Path> ds = dir.newDirectoryStream();
+//            try {
+//                for (Path p: ds) {
+//                    String name = p.getName().toString();
+//                    if (isDirectory(p)) {
+//                        if (recurse && SourceVersion.isIdentifier(name)) {
+//                            queue.add(p);
+//                        }
+//                    } else {
+//                        if (kinds.contains(getKind(name))) {
+//                            JavaFileObject fe =
+//                                PathFileObject.createDirectoryPathFileObject(this, p, pathDir);
+//                            results.append(fe);
+//                        }
+//                    }
+//                }
+//            } finally {
+//                ds.close();
+//            }
+//        }
+        int maxDepth = (recurse ? Integer.MAX_VALUE : 1);
+        Set<FileVisitOption> opts = EnumSet.of(DETECT_CYCLES, FOLLOW_LINKS);
+        Files.walkFileTree(packageDir, opts, maxDepth,
+                new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult preVisitDirectory(Path dir) {
+                if (SourceVersion.isIdentifier(dir.getName().toString())) // JSR 292?
+                    return FileVisitResult.CONTINUE;
+                else
+                    return FileVisitResult.SKIP_SUBTREE;
+            }
+
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+                if (attrs.isRegularFile() && kinds.contains(getKind(file.getName().toString()))) {
+                    JavaFileObject fe =
+                        PathFileObject.createDirectoryPathFileObject(
+                            JavacPathFileManager.this, file, pathDir);
+                    results.append(fe);
+                }
+                return FileVisitResult.CONTINUE;
+            }
+        });
+    }
+
+    @Override
+    public Iterable<? extends JavaFileObject> getJavaFileObjectsFromPaths(
+        Iterable<? extends Path> paths) {
+        ArrayList<PathFileObject> result;
+        if (paths instanceof Collection<?>)
+            result = new ArrayList<PathFileObject>(((Collection<?>)paths).size());
+        else
+            result = new ArrayList<PathFileObject>();
+        for (Path p: paths)
+            result.add(PathFileObject.createSimplePathFileObject(this, nullCheck(p)));
+        return result;
+    }
+
+    @Override
+    public Iterable<? extends JavaFileObject> getJavaFileObjects(Path... paths) {
+        return getJavaFileObjectsFromPaths(Arrays.asList(nullCheck(paths)));
+    }
+
+    @Override
+    public JavaFileObject getJavaFileForInput(Location location,
+            String className, Kind kind) throws IOException {
+        return getFileForInput(location, getRelativePath(className, kind));
+    }
+
+    @Override
+    public FileObject getFileForInput(Location location,
+            String packageName, String relativeName) throws IOException {
+        return getFileForInput(location, getRelativePath(packageName, relativeName));
+    }
+
+    private JavaFileObject getFileForInput(Location location, String relativePath)
+            throws IOException {
+        for (Path p: getLocation(location)) {
+            if (isDirectory(p)) {
+                Path f = resolve(p, relativePath);
+                if (f.exists())
+                    return PathFileObject.createDirectoryPathFileObject(this, f, p);
+            } else {
+                FileSystem fs = getFileSystem(p);
+                if (fs != null) {
+                    Path file = getPath(fs, relativePath);
+                    if (file.exists())
+                        return PathFileObject.createJarPathFileObject(this, file);
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public JavaFileObject getJavaFileForOutput(Location location,
+            String className, Kind kind, FileObject sibling) throws IOException {
+        return getFileForOutput(location, getRelativePath(className, kind), sibling);
+    }
+
+    @Override
+    public FileObject getFileForOutput(Location location, String packageName,
+            String relativeName, FileObject sibling)
+            throws IOException {
+        return getFileForOutput(location, getRelativePath(packageName, relativeName), sibling);
+    }
+
+    private JavaFileObject getFileForOutput(Location location,
+            String relativePath, FileObject sibling) {
+        Path dir = getOutputLocation(location);
+        if (dir == null) {
+            if (location == CLASS_OUTPUT) {
+                Path siblingDir = null;
+                if (sibling != null && sibling instanceof PathFileObject) {
+                    siblingDir = ((PathFileObject) sibling).getPath().getParent();
+                }
+                return PathFileObject.createSiblingPathFileObject(this,
+                        siblingDir.resolve(getBaseName(relativePath)),
+                        relativePath);
+            } else if (location == SOURCE_OUTPUT) {
+                dir = getOutputLocation(CLASS_OUTPUT);
+            }
+        }
+
+        Path file;
+        if (dir != null) {
+            file = resolve(dir, relativePath);
+            return PathFileObject.createDirectoryPathFileObject(this, file, dir);
+        } else {
+            file = getPath(getDefaultFileSystem(), relativePath);
+            return PathFileObject.createSimplePathFileObject(this, file);
+        }
+
+    }
+
+    @Override
+    public String inferBinaryName(Location location, JavaFileObject fo) {
+        nullCheck(fo);
+        // Need to match the path semantics of list(location, ...)
+        Iterable<? extends Path> paths = getLocation(location);
+        if (paths == null) {
+            return null;
+        }
+
+        if (!(fo instanceof PathFileObject))
+            throw new IllegalArgumentException(fo.getClass().getName());
+
+        return ((PathFileObject) fo).inferBinaryName(paths);
+    }
+
+    private FileSystem getFileSystem(Path p) throws IOException {
+        FileSystem fs = fileSystems.get(p);
+        if (fs == null) {
+            fs = FileSystems.newFileSystem(p, Collections.<String,Void>emptyMap(), null);
+            fileSystems.put(p, fs);
+        }
+        return fs;
+    }
+
+    private Map<Path,FileSystem> fileSystems;
+
+    // </editor-fold>
+
+    // <editor-fold defaultstate="collapsed" desc="Utility methods">
+
+    private static String getRelativePath(String className, Kind kind) {
+        return className.replace(".", "/") + kind.extension;
+    }
+
+    private static String getRelativePath(String packageName, String relativeName) {
+        return packageName.replace(".", "/") + relativeName;
+    }
+
+    private static String getBaseName(String relativePath) {
+        int lastSep = relativePath.lastIndexOf("/");
+        return relativePath.substring(lastSep + 1); // safe if "/" not found
+    }
+
+    private static boolean isDirectory(Path path) throws IOException {
+        BasicFileAttributes attrs = Attributes.readBasicFileAttributes(path);
+        return attrs.isDirectory();
+    }
+
+    private static Path getPath(FileSystem fs, String relativePath) {
+        return fs.getPath(relativePath.replace("/", fs.getSeparator()));
+    }
+
+    private static Path resolve(Path base, String relativePath) {
+        FileSystem fs = base.getFileSystem();
+        Path rp = fs.getPath(relativePath.replace("/", fs.getSeparator()));
+        return base.resolve(rp);
+    }
+
+    // </editor-fold>
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/javac/nio/PathFileManager.java	Fri Dec 11 14:26:27 2009 -0800
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.tools.javac.nio;
+
+import java.io.IOException;
+import java.nio.file.FileSystem;
+import java.nio.file.Path;
+import javax.tools.FileObject;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+
+/**
+ *  File manager based on {@linkplain File java.nio.file.Path}.
+ *
+ *  Eventually, this should be moved to javax.tools.
+ *  Also, JavaCompiler might reasonably provide a method getPathFileManager,
+ *  similar to {@link javax.tools.JavaCompiler#getStandardFileManager
+ *  getStandardFileManager}. However, would need to be handled carefully
+ *  as another forward reference from langtools to jdk.
+ *
+ *  <p><b>This is NOT part of any API supported by Sun Microsystems.  If
+ *  you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ */
+public interface PathFileManager extends JavaFileManager {
+    /**
+     * Get the default file system used to create paths. If no value has been
+     * set, the default file system is {@link FileSystems#getDefault}.
+     */
+    FileSystem getDefaultFileSystem();
+
+    /**
+     * Set the default file system used to create paths.
+     * @param fs the default file system used to create any new paths.
+     */
+    void setDefaultFileSystem(FileSystem fs);
+
+    /**
+     * Get file objects representing the given files.
+     *
+     * @param paths a list of paths
+     * @return a list of file objects
+     * @throws IllegalArgumentException if the list of paths includes
+     * a directory
+     */
+    Iterable<? extends JavaFileObject> getJavaFileObjectsFromPaths(
+        Iterable<? extends Path> paths);
+
+    /**
+     * Get file objects representing the given paths.
+     * Convenience method equivalent to:
+     *
+     * <pre>
+     *     getJavaFileObjectsFromPaths({@linkplain java.util.Arrays#asList Arrays.asList}(paths))
+     * </pre>
+     *
+     * @param paths an array of paths
+     * @return a list of file objects
+     * @throws IllegalArgumentException if the array of files includes
+     * a directory
+     * @throws NullPointerException if the given array contains null
+     * elements
+     */
+    Iterable<? extends JavaFileObject> getJavaFileObjects(Path... paths);
+
+    /**
+     * Return the Path for a file object that has been obtained from this
+     * file manager.
+     *
+     * @param fo A file object that has been obtained from this file manager.
+     * @return The underlying Path object.
+     * @throws IllegalArgumentException is the file object was not obtained from
+     * from this file manager.
+     */
+    Path getPath(FileObject fo);
+
+    /**
+     * Get the search path associated with the given location.
+     *
+     * @param location a location
+     * @return a list of paths or {@code null} if this location has no
+     * associated search path
+     * @see #setLocation
+     */
+    Iterable<? extends Path> getLocation(Location location);
+
+    /**
+     * Associate the given search path with the given location.  Any
+     * previous value will be discarded.
+     *
+     * @param location a location
+     * @param searchPath a list of files, if {@code null} use the default
+     * search path for this location
+     * @see #getLocation
+     * @throws IllegalArgumentException if location is an output
+     * location and searchpath does not contain exactly one element
+     * @throws IOException if location is an output location and searchpath
+     * does not represent an existing directory
+     */
+    void setLocation(Location location, Iterable<? extends Path> searchPath) throws IOException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/javac/nio/PathFileObject.java	Fri Dec 11 14:26:27 2009 -0800
@@ -0,0 +1,319 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.tools.javac.nio;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharsetDecoder;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.Attributes;
+import java.nio.file.attribute.BasicFileAttributes;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.NestingKind;
+import javax.tools.JavaFileObject;
+
+import com.sun.tools.javac.util.BaseFileManager;
+
+
+/**
+ *  Implementation of JavaFileObject using java.nio.file API.
+ *
+ *  <p>PathFileObjects are, for the most part, straightforward wrappers around
+ *  Path objects. The primary complexity is the support for "inferBinaryName".
+ *  This is left as an abstract method, implemented by each of a number of
+ *  different factory methods, which compute the binary name based on
+ *  information available at the time the file object is created.
+ *
+ *  <p><b>This is NOT part of any API supported by Sun Microsystems.  If
+ *  you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ */
+abstract class PathFileObject implements JavaFileObject {
+    private JavacPathFileManager fileManager;
+    private Path path;
+
+    /**
+     * Create a PathFileObject within a directory, such that the binary name
+     * can be inferred from the relationship to the parent directory.
+     */
+    static PathFileObject createDirectoryPathFileObject(JavacPathFileManager fileManager,
+            final Path path, final Path dir) {
+        return new PathFileObject(fileManager, path) {
+            @Override
+            String inferBinaryName(Iterable<? extends Path> paths) {
+                return toBinaryName(dir.relativize(path));
+            }
+        };
+    }
+
+    /**
+     * Create a PathFileObject in a file system such as a jar file, such that
+     * the binary name can be inferred from its position within the filesystem.
+     */
+    static PathFileObject createJarPathFileObject(JavacPathFileManager fileManager,
+            final Path path) {
+        return new PathFileObject(fileManager, path) {
+            @Override
+            String inferBinaryName(Iterable<? extends Path> paths) {
+                return toBinaryName(path);
+            }
+        };
+    }
+
+    /**
+     * Create a PathFileObject whose binary name can be inferred from the
+     * relative path to a sibling.
+     */
+    static PathFileObject createSiblingPathFileObject(JavacPathFileManager fileManager,
+            final Path path, final String relativePath) {
+        return new PathFileObject(fileManager, path) {
+            @Override
+            String inferBinaryName(Iterable<? extends Path> paths) {
+                return toBinaryName(relativePath, "/");
+            }
+        };
+    }
+
+    /**
+     * Create a PathFileObject whose binary name might be inferred from its
+     * position on a search path.
+     */
+    static PathFileObject createSimplePathFileObject(JavacPathFileManager fileManager,
+            final Path path) {
+        return new PathFileObject(fileManager, path) {
+            @Override
+            String inferBinaryName(Iterable<? extends Path> paths) {
+                Path absPath = path.toAbsolutePath();
+                for (Path p: paths) {
+                    Path ap = p.toAbsolutePath();
+                    if (absPath.startsWith(ap)) {
+                        try {
+                            Path rp = ap.relativize(absPath);
+                            if (rp != null) // maybe null if absPath same as ap
+                                return toBinaryName(rp);
+                        } catch (IllegalArgumentException e) {
+                            // ignore this p if cannot relativize path to p
+                        }
+                    }
+                }
+                return null;
+            }
+        };
+    }
+
+    protected PathFileObject(JavacPathFileManager fileManager, Path path) {
+        fileManager.getClass(); // null check
+        path.getClass();        // null check
+        this.fileManager = fileManager;
+        this.path = path;
+    }
+
+    abstract String inferBinaryName(Iterable<? extends Path> paths);
+
+    /**
+     * Return the Path for this object.
+     * @return the Path for this object.
+     */
+    Path getPath() {
+        return path;
+    }
+
+    @Override
+    public Kind getKind() {
+        return BaseFileManager.getKind(path.getName().toString());
+    }
+
+    @Override
+    public boolean isNameCompatible(String simpleName, Kind kind) {
+        simpleName.getClass();
+        // null check
+        if (kind == Kind.OTHER && getKind() != kind) {
+            return false;
+        }
+        String sn = simpleName + kind.extension;
+        String pn = path.getName().toString();
+        if (pn.equals(sn)) {
+            return true;
+        }
+        if (pn.equalsIgnoreCase(sn)) {
+            try {
+                // allow for Windows
+                return path.toRealPath(false).getName().toString().equals(sn);
+            } catch (IOException e) {
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public NestingKind getNestingKind() {
+        return null;
+    }
+
+    @Override
+    public Modifier getAccessLevel() {
+        return null;
+    }
+
+    @Override
+    public URI toUri() {
+        return path.toUri();
+    }
+
+    @Override
+    public String getName() {
+        return path.toString();
+    }
+
+    @Override
+    public InputStream openInputStream() throws IOException {
+        return path.newInputStream();
+    }
+
+    @Override
+    public OutputStream openOutputStream() throws IOException {
+        ensureParentDirectoriesExist();
+        return path.newOutputStream();
+    }
+
+    @Override
+    public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
+        CharsetDecoder decoder = fileManager.getDecoder(fileManager.getEncodingName(), ignoreEncodingErrors);
+        return new InputStreamReader(openInputStream(), decoder);
+    }
+
+    @Override
+    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+        CharBuffer cb = fileManager.getCachedContent(this);
+        if (cb == null) {
+            InputStream in = openInputStream();
+            try {
+                ByteBuffer bb = fileManager.makeByteBuffer(in);
+                JavaFileObject prev = fileManager.log.useSource(this);
+                try {
+                    cb = fileManager.decode(bb, ignoreEncodingErrors);
+                } finally {
+                    fileManager.log.useSource(prev);
+                }
+                fileManager.recycleByteBuffer(bb);
+                if (!ignoreEncodingErrors) {
+                    fileManager.cache(this, cb);
+                }
+            } finally {
+                in.close();
+            }
+        }
+        return cb;
+    }
+
+    @Override
+    public Writer openWriter() throws IOException {
+        ensureParentDirectoriesExist();
+        return new OutputStreamWriter(path.newOutputStream(), fileManager.getEncodingName());
+    }
+
+    @Override
+    public long getLastModified() {
+        try {
+            BasicFileAttributes attrs = Attributes.readBasicFileAttributes(path);
+            return attrs.lastModifiedTime().toMillis();
+        } catch (IOException e) {
+            return -1;
+        }
+    }
+
+    @Override
+    public boolean delete() {
+        try {
+            path.delete();
+            return true;
+        } catch (IOException e) {
+            return false;
+        }
+    }
+
+    public boolean isSameFile(PathFileObject other) {
+        try {
+            return path.isSameFile(other.path);
+        } catch (IOException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        return (other instanceof PathFileObject && path.equals(((PathFileObject) other).path));
+    }
+
+    @Override
+    public int hashCode() {
+        return path.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "[" + path + "]";
+    }
+
+    private void ensureParentDirectoriesExist() throws IOException {
+        Path parent = path.getParent();
+        if (parent != null)
+            Files.createDirectories(parent);
+    }
+
+    private long size() {
+        try {
+            BasicFileAttributes attrs = Attributes.readBasicFileAttributes(path);
+            return attrs.size();
+        } catch (IOException e) {
+            return -1;
+        }
+    }
+
+    protected static String toBinaryName(Path relativePath) {
+        return toBinaryName(relativePath.toString(),
+                relativePath.getFileSystem().getSeparator());
+    }
+
+    protected static String toBinaryName(String relativePath, String sep) {
+        return removeExtension(relativePath).replaceAll(sep, ".");
+    }
+
+    protected static String removeExtension(String fileName) {
+        int lastDot = fileName.lastIndexOf(".");
+        return (lastDot == -1 ? fileName : fileName.substring(0, lastDot));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/javac/util/BaseFileManager.java	Fri Dec 11 14:26:27 2009 -0800
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.tools.javac.util;
+
+import com.sun.tools.javac.code.Source;
+import com.sun.tools.javac.main.JavacOption;
+import com.sun.tools.javac.main.OptionName;
+import com.sun.tools.javac.main.RecognizedOptions;
+import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition;
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.lang.ref.SoftReference;
+import java.lang.reflect.Constructor;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.UnsupportedCharsetException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import javax.tools.JavaFileObject;
+import javax.tools.JavaFileObject.Kind;
+
+/**
+ * Utility methods for building a filemanager.
+ * There are no references here to file-system specific objects such as
+ * java.io.File or java.nio.file.Path.
+ */
+public class BaseFileManager {
+    protected BaseFileManager(Charset charset) {
+        this.charset = charset;
+        byteBufferCache = new ByteBufferCache();
+    }
+
+    /**
+     * Set the context for JavacPathFileManager.
+     */
+    protected void setContext(Context context) {
+        log = Log.instance(context);
+        options = Options.instance(context);
+        classLoaderClass = options.get("procloader");
+    }
+
+    /**
+     * The log to be used for error reporting.
+     */
+    public Log log;
+
+    /**
+     * User provided charset (through javax.tools).
+     */
+    protected Charset charset;
+
+    protected Options options;
+
+    protected String classLoaderClass;
+
+    protected Source getSource() {
+        String sourceName = options.get(OptionName.SOURCE);
+        Source source = null;
+        if (sourceName != null)
+            source = Source.lookup(sourceName);
+        return (source != null ? source : Source.DEFAULT);
+    }
+
+    protected ClassLoader getClassLoader(URL[] urls) {
+        ClassLoader thisClassLoader = getClass().getClassLoader();
+
+        // Bug: 6558476
+        // Ideally, ClassLoader should be Closeable, but before JDK7 it is not.
+        // On older versions, try the following, to get a closeable classloader.
+
+        // 1: Allow client to specify the class to use via hidden option
+        if (classLoaderClass != null) {
+            try {
+                Class<? extends ClassLoader> loader =
+                        Class.forName(classLoaderClass).asSubclass(ClassLoader.class);
+                Class<?>[] constrArgTypes = { URL[].class, ClassLoader.class };
+                Constructor<? extends ClassLoader> constr = loader.getConstructor(constrArgTypes);
+                return constr.newInstance(new Object[] { urls, thisClassLoader });
+            } catch (Throwable t) {
+                // ignore errors loading user-provided class loader, fall through
+            }
+        }
+
+        // 2: If URLClassLoader implements Closeable, use that.
+        if (Closeable.class.isAssignableFrom(URLClassLoader.class))
+            return new URLClassLoader(urls, thisClassLoader);
+
+        // 3: Try using private reflection-based CloseableURLClassLoader
+        try {
+            return new CloseableURLClassLoader(urls, thisClassLoader);
+        } catch (Throwable t) {
+            // ignore errors loading workaround class loader, fall through
+        }
+
+        // 4: If all else fails, use plain old standard URLClassLoader
+        return new URLClassLoader(urls, thisClassLoader);
+    }
+
+    // <editor-fold defaultstate="collapsed" desc="Option handling">
+    public boolean handleOption(String current, Iterator<String> remaining) {
+        for (JavacOption o: javacFileManagerOptions) {
+            if (o.matches(current))  {
+                if (o.hasArg()) {
+                    if (remaining.hasNext()) {
+                        if (!o.process(options, current, remaining.next()))
+                            return true;
+                    }
+                } else {
+                    if (!o.process(options, current))
+                        return true;
+                }
+                // operand missing, or process returned false
+                throw new IllegalArgumentException(current);
+            }
+        }
+
+        return false;
+    }
+    // where
+        private static JavacOption[] javacFileManagerOptions =
+            RecognizedOptions.getJavacFileManagerOptions(
+            new RecognizedOptions.GrumpyHelper());
+
+    public int isSupportedOption(String option) {
+        for (JavacOption o : javacFileManagerOptions) {
+            if (o.matches(option))
+                return o.hasArg() ? 1 : 0;
+        }
+        return -1;
+    }
+    // </editor-fold>
+
+    // <editor-fold defaultstate="collapsed" desc="Encoding">
+    private String defaultEncodingName;
+    private String getDefaultEncodingName() {
+        if (defaultEncodingName == null) {
+            defaultEncodingName =
+                new OutputStreamWriter(new ByteArrayOutputStream()).getEncoding();
+        }
+        return defaultEncodingName;
+    }
+
+    public String getEncodingName() {
+        String encName = options.get(OptionName.ENCODING);
+        if (encName == null)
+            return getDefaultEncodingName();
+        else
+            return encName;
+    }
+
+    public CharBuffer decode(ByteBuffer inbuf, boolean ignoreEncodingErrors) {
+        String encodingName = getEncodingName();
+        CharsetDecoder decoder;
+        try {
+            decoder = getDecoder(encodingName, ignoreEncodingErrors);
+        } catch (IllegalCharsetNameException e) {
+            log.error("unsupported.encoding", encodingName);
+            return (CharBuffer)CharBuffer.allocate(1).flip();
+        } catch (UnsupportedCharsetException e) {
+            log.error("unsupported.encoding", encodingName);
+            return (CharBuffer)CharBuffer.allocate(1).flip();
+        }
+
+        // slightly overestimate the buffer size to avoid reallocation.
+        float factor =
+            decoder.averageCharsPerByte() * 0.8f +
+            decoder.maxCharsPerByte() * 0.2f;
+        CharBuffer dest = CharBuffer.
+            allocate(10 + (int)(inbuf.remaining()*factor));
+
+        while (true) {
+            CoderResult result = decoder.decode(inbuf, dest, true);
+            dest.flip();
+
+            if (result.isUnderflow()) { // done reading
+                // make sure there is at least one extra character
+                if (dest.limit() == dest.capacity()) {
+                    dest = CharBuffer.allocate(dest.capacity()+1).put(dest);
+                    dest.flip();
+                }
+                return dest;
+            } else if (result.isOverflow()) { // buffer too small; expand
+                int newCapacity =
+                    10 + dest.capacity() +
+                    (int)(inbuf.remaining()*decoder.maxCharsPerByte());
+                dest = CharBuffer.allocate(newCapacity).put(dest);
+            } else if (result.isMalformed() || result.isUnmappable()) {
+                // bad character in input
+
+                // report coding error (warn only pre 1.5)
+                if (!getSource().allowEncodingErrors()) {
+                    log.error(new SimpleDiagnosticPosition(dest.limit()),
+                              "illegal.char.for.encoding",
+                              charset == null ? encodingName : charset.name());
+                } else {
+                    log.warning(new SimpleDiagnosticPosition(dest.limit()),
+                                "illegal.char.for.encoding",
+                                charset == null ? encodingName : charset.name());
+                }
+
+                // skip past the coding error
+                inbuf.position(inbuf.position() + result.length());
+
+                // undo the flip() to prepare the output buffer
+                // for more translation
+                dest.position(dest.limit());
+                dest.limit(dest.capacity());
+                dest.put((char)0xfffd); // backward compatible
+            } else {
+                throw new AssertionError(result);
+            }
+        }
+        // unreached
+    }
+
+    public CharsetDecoder getDecoder(String encodingName, boolean ignoreEncodingErrors) {
+        Charset cs = (this.charset == null)
+            ? Charset.forName(encodingName)
+            : this.charset;
+        CharsetDecoder decoder = cs.newDecoder();
+
+        CodingErrorAction action;
+        if (ignoreEncodingErrors)
+            action = CodingErrorAction.REPLACE;
+        else
+            action = CodingErrorAction.REPORT;
+
+        return decoder
+            .onMalformedInput(action)
+            .onUnmappableCharacter(action);
+    }
+    // </editor-fold>
+
+    // <editor-fold defaultstate="collapsed" desc="ByteBuffers">
+    /**
+     * Make a byte buffer from an input stream.
+     */
+    public ByteBuffer makeByteBuffer(InputStream in)
+        throws IOException {
+        int limit = in.available();
+        if (limit < 1024) limit = 1024;
+        ByteBuffer result = byteBufferCache.get(limit);
+        int position = 0;
+        while (in.available() != 0) {
+            if (position >= limit)
+                // expand buffer
+                result = ByteBuffer.
+                    allocate(limit <<= 1).
+                    put((ByteBuffer)result.flip());
+            int count = in.read(result.array(),
+                position,
+                limit - position);
+            if (count < 0) break;
+            result.position(position += count);
+        }
+        return (ByteBuffer)result.flip();
+    }
+
+    public void recycleByteBuffer(ByteBuffer bb) {
+        byteBufferCache.put(bb);
+    }
+
+    /**
+     * A single-element cache of direct byte buffers.
+     */
+    private static class ByteBufferCache {
+        private ByteBuffer cached;
+        ByteBuffer get(int capacity) {
+            if (capacity < 20480) capacity = 20480;
+            ByteBuffer result =
+                (cached != null && cached.capacity() >= capacity)
+                ? (ByteBuffer)cached.clear()
+                : ByteBuffer.allocate(capacity + capacity>>1);
+            cached = null;
+            return result;
+        }
+        void put(ByteBuffer x) {
+            cached = x;
+        }
+    }
+
+    private final ByteBufferCache byteBufferCache;
+    // </editor-fold>
+
+    // <editor-fold defaultstate="collapsed" desc="Content cache">
+    public CharBuffer getCachedContent(JavaFileObject file) {
+        SoftReference<CharBuffer> r = contentCache.get(file);
+        return (r == null ? null : r.get());
+    }
+
+    public void cache(JavaFileObject file, CharBuffer cb) {
+        contentCache.put(file, new SoftReference<CharBuffer>(cb));
+    }
+
+    protected final Map<JavaFileObject, SoftReference<CharBuffer>> contentCache
+            = new HashMap<JavaFileObject, SoftReference<CharBuffer>>();
+    // </editor-fold>
+
+    public static Kind getKind(String name) {
+        if (name.endsWith(Kind.CLASS.extension))
+            return Kind.CLASS;
+        else if (name.endsWith(Kind.SOURCE.extension))
+            return Kind.SOURCE;
+        else if (name.endsWith(Kind.HTML.extension))
+            return Kind.HTML;
+        else
+            return Kind.OTHER;
+    }
+
+    protected static <T> T nullCheck(T o) {
+        o.getClass(); // null check
+        return o;
+    }
+
+    protected static <T> Collection<T> nullCheck(Collection<T> it) {
+        for (T t : it)
+            t.getClass(); // null check
+        return it;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/javac/util/CloseableURLClassLoader.java	Fri Dec 11 14:26:27 2009 -0800
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.tools.javac.util;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.jar.JarFile;
+
+/**
+ * A URLClassLoader that also implements Closeable.
+ * Reflection is used to access internal data structures in the URLClassLoader,
+ * since no public API exists for this purpose. Therefore this code is somewhat
+ * fragile. Caveat emptor.
+ * @throws Error if the internal data structures are not as expected.
+ *
+ *  <p><b>This is NOT part of any API supported by Sun Microsystems.  If
+ *  you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ */
+public class CloseableURLClassLoader
+        extends URLClassLoader implements Closeable {
+    public CloseableURLClassLoader(URL[] urls, ClassLoader parent) throws Error {
+        super(urls, parent);
+        try {
+            getLoaders(); //proactive check that URLClassLoader is as expected
+        } catch (Throwable t) {
+            throw new Error("cannot create CloseableURLClassLoader", t);
+        }
+    }
+
+    /**
+     * Close any jar files that may have been opened by the class loader.
+     * Reflection is used to access the jar files in the URLClassLoader's
+     * internal data structures.
+     * @throws java.io.IOException if the jar files cannot be found for any
+     * reson, or if closing the jar file itself causes an IOException.
+     */
+    @Override
+    public void close() throws IOException {
+        try {
+            for (Object l: getLoaders()) {
+                if (l.getClass().getName().equals("sun.misc.URLClassPath$JarLoader")) {
+                    Field jarField = l.getClass().getDeclaredField("jar");
+                    JarFile jar = (JarFile) getField(l, jarField);
+                    if (jar != null) {
+                        //System.err.println("CloseableURLClassLoader: closing " + jar);
+                        jar.close();
+                    }
+                }
+            }
+        } catch (Throwable t) {
+            IOException e = new IOException("cannot close class loader");
+            e.initCause(t);
+            throw e;
+        }
+    }
+
+    private ArrayList<?> getLoaders()
+            throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException
+    {
+        Field ucpField = URLClassLoader.class.getDeclaredField("ucp");
+        Object urlClassPath = getField(this, ucpField);
+        if (urlClassPath == null)
+            throw new AssertionError("urlClassPath not set in URLClassLoader");
+        Field loadersField = urlClassPath.getClass().getDeclaredField("loaders");
+        return (ArrayList<?>) getField(urlClassPath, loadersField);
+    }
+
+    private Object getField(Object o, Field f)
+            throws IllegalArgumentException, IllegalAccessException {
+        boolean prev = f.isAccessible();
+        try {
+            f.setAccessible(true);
+            return f.get(o);
+        } finally {
+            f.setAccessible(prev);
+        }
+    }
+
+}
--- a/src/share/classes/javax/tools/StandardJavaFileManager.java	Thu Dec 10 20:35:31 2009 -0800
+++ b/src/share/classes/javax/tools/StandardJavaFileManager.java	Fri Dec 11 14:26:27 2009 -0800
@@ -28,7 +28,6 @@
 import java.io.File;
 import java.io.IOException;
 import java.util.*;
-import java.util.concurrent.*;
 
 /**
  * File manager based on {@linkplain File java.io.File}.  A common way
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/nio/compileTest/CompileTest.java	Fri Dec 11 14:26:27 2009 -0800
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/**
+ * @test
+ * @compile HelloPathWorld.java
+ * @run main CompileTest
+ */
+
+import java.io.*;
+import java.nio.file.*;
+import java.util.*;
+import java.util.jar.*;
+import javax.tools.*;
+
+import com.sun.tools.javac.nio.*;
+import com.sun.tools.javac.util.Context;
+import java.nio.file.spi.FileSystemProvider;
+
+
+public class CompileTest {
+    public static void main(String[] args) throws Exception {
+        new CompileTest().run();
+    }
+
+    public void run() throws Exception {
+        File rtDir = new File("rt.dir");
+        File javaHome = new File(System.getProperty("java.home"));
+        if (javaHome.getName().equals("jre"))
+            javaHome = javaHome.getParentFile();
+        File rtJar = new File(new File(new File(javaHome, "jre"), "lib"), "rt.jar");
+        expand(rtJar, rtDir);
+
+        String[] rtDir_opts = {
+            "-bootclasspath", rtDir.toString(),
+            "-classpath", "",
+            "-sourcepath", "",
+            "-extdirs", ""
+        };
+        test(rtDir_opts, "HelloPathWorld");
+
+        if (isJarFileSystemAvailable()) {
+            String[] rtJar_opts = {
+                "-bootclasspath", rtJar.toString(),
+                "-classpath", "",
+                "-sourcepath", "",
+                "-extdirs", ""
+            };
+            test(rtJar_opts, "HelloPathWorld");
+
+            String[] default_opts = { };
+            test(default_opts, "HelloPathWorld");
+
+            // finally, a non-trivial program
+            test(default_opts, "CompileTest");
+        } else
+            System.err.println("jar file system not available: test skipped");
+    }
+
+    void test(String[] opts, String className) throws Exception {
+        count++;
+        System.err.println("Test " + count + " " + Arrays.asList(opts) + " " + className);
+        Path testSrcDir = Paths.get(System.getProperty("test.src"));
+        Path testClassesDir = Paths.get(System.getProperty("test.classes"));
+        Path classes = Paths.get("classes." + count);
+        classes.createDirectory();
+
+        Context ctx = new Context();
+        PathFileManager fm = new JavacPathFileManager(ctx, true, null);
+        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+        List<String> options = new ArrayList<String>();
+        options.addAll(Arrays.asList(opts));
+        options.addAll(Arrays.asList(
+                "-verbose", "-XDverboseCompilePolicy",
+                "-d", classes.toString()
+        ));
+        Iterable<? extends JavaFileObject> compilationUnits =
+                fm.getJavaFileObjects(testSrcDir.resolve(className + ".java"));
+        StringWriter sw = new StringWriter();
+        PrintWriter out = new PrintWriter(sw);
+        JavaCompiler.CompilationTask t =
+                compiler.getTask(out, fm, null, options, null, compilationUnits);
+        boolean ok = t.call();
+        System.err.println(sw.toString());
+        if (!ok) {
+            throw new Exception("compilation failed");
+        }
+
+        File expect = new File("classes." + count + "/" + className + ".class");
+        if (!expect.exists())
+            throw new Exception("expected file not found: " + expect);
+        long expectedSize = new File(testClassesDir.toString(), className + ".class").length();
+        long actualSize = expect.length();
+        if (expectedSize != actualSize)
+            throw new Exception("wrong size found: " + actualSize + "; expected: " + expectedSize);
+    }
+
+    boolean isJarFileSystemAvailable() {
+        boolean result = false;
+        for (FileSystemProvider fsp: FileSystemProvider.installedProviders()) {
+            String scheme = fsp.getScheme();
+            System.err.println("Provider: " + scheme + " " + fsp);
+            if (scheme.equalsIgnoreCase("jar") || scheme.equalsIgnoreCase("zip"))
+                result = true;
+        }
+        return result;
+    }
+
+    void expand(File jar, File dir) throws IOException {
+        JarFile jarFile = new JarFile(jar);
+        try {
+            Enumeration<JarEntry> entries = jarFile.entries();
+            while (entries.hasMoreElements()) {
+                JarEntry je = entries.nextElement();
+                if (!je.isDirectory()) {
+                    copy(jarFile.getInputStream(je), new File(dir, je.getName()));
+                }
+            }
+        } finally {
+            jarFile.close();
+        }
+    }
+
+    void copy(InputStream in, File dest) throws IOException {
+        dest.getParentFile().mkdirs();
+        OutputStream out = new BufferedOutputStream(new FileOutputStream(dest));
+        try {
+            byte[] data = new byte[8192];
+            int n;
+            while ((n = in.read(data, 0, data.length)) > 0)
+                out.write(data, 0, n);
+        } finally {
+            out.close();
+            in.close();
+        }
+    }
+
+    void error(String message) {
+        System.err.println("Error: " + message);
+        errors++;
+    }
+
+    int errors;
+    int count;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/nio/compileTest/HelloPathWorld.java	Fri Dec 11 14:26:27 2009 -0800
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+class HelloPathWorld {
+    public static void main(String... args) {
+        System.out.println("Hello World!");
+    }
+}