--- old/src/share/classes/com/sun/tools/javac/file/CloseableURLClassLoader.java 2009-09-24 17:46:28.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/file/CloseableURLClassLoader.java 2009-09-24 17:46:28.000000000 -0700 @@ -45,9 +45,9 @@ * This code and its internal interfaces are subject to change or * deletion without notice. */ -class CloseableURLClassLoader +public class CloseableURLClassLoader extends URLClassLoader implements Closeable { - CloseableURLClassLoader(URL[] urls, ClassLoader parent) throws Error { + public CloseableURLClassLoader(URL[] urls, ClassLoader parent) throws Error { super(urls, parent); try { getLoaders(); //proactive check that URLClassLoader is as expected @@ -63,6 +63,7 @@ * @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()) { --- old/src/share/classes/com/sun/tools/javac/file/Paths.java 2009-09-24 17:46:28.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/file/Paths.java 2009-09-24 17:46:28.000000000 -0700 @@ -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); --- old/src/share/classes/javax/tools/StandardJavaFileManager.java 2009-09-24 17:46:29.000000000 -0700 +++ new/src/share/classes/javax/tools/StandardJavaFileManager.java 2009-09-24 17:46:28.000000000 -0700 @@ -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 2009-07-15 19:05:43.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/nio/JavacPathFileManager.java 2009-09-24 17:46:29.000000000 -0700 @@ -0,0 +1,797 @@ +/* + * 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.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.File; +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.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.nio.file.DirectoryStream; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Path; +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.Deque; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; +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 javax.tools.StandardLocation.*; + +import com.sun.tools.javac.code.Source; +import com.sun.tools.javac.file.CloseableURLClassLoader; +import com.sun.tools.javac.file.Paths; +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.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 com.sun.tools.javac.main.OptionName.*; + +/** + *

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. + */ +public class JavacPathFileManager 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) { + if (register) + context.put(JavaFileManager.class, this); + this.charset = charset; + byteBufferCache = new ByteBufferCache(); + pathsForLocation = new HashMap(); + fileSystems = new HashMap(); + setContext(context); + } + + /** + * Set the context for JavacPathFileManager. + */ + void setContext(Context context) { + log = Log.instance(context); + options = Options.instance(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 path = getLocation(location); + if (path == null) + return null; + ListBuffer lb = new ListBuffer(); + 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()])); + } + + // + + public boolean hasLocation(Location location) { + return (getLocation(location) != null); + } + + public Iterable 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 paths = getLocation(location); + return (paths == null ? null : paths.iterator().next()); + } + + public void setLocation(Location location, Iterable 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 searchPath) throws IOException { + Iterator 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 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 pathsForLocation; + private Paths searchPaths; + + private static class PathsForLocation extends LinkedHashSet { + private static final long serialVersionUID = 6788510222394486733L; + } + + // + + // + + @Override + public Path getPath(FileObject fo) { + fo.getClass(); // null check + 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); + } + + public Iterable list(Location location, + String packageName, Set kinds, boolean recurse) + throws IOException { + // validatePackageName(packageName); + nullCheck(packageName); + nullCheck(kinds); + + Iterable paths = getLocation(location); + if (paths == null) + return List.nil(); + ListBuffer results = new ListBuffer(); + + for (Path path : paths) + list(path, packageName, kinds, recurse, results); + + return results.toList(); + } + + private void list(Path path, String packageName, Set kinds, + boolean recurse, ListBuffer results) + throws IOException { + if (!path.exists()) + return; + + 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; + +// if (!caseMapCheck(d, subdirectory)) +// return; + + Deque queue = new LinkedList(); + queue.add(packageDir); + + Path dir; + while ((dir = queue.poll()) != null) { + DirectoryStream 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(); + } + } + } + + @Override + public Iterable getJavaFileObjectsFromPaths( + Iterable paths) { + ArrayList result; + if (paths instanceof Collection) + result = new ArrayList(((Collection)paths).size()); + else + result = new ArrayList(); + for (Path p: paths) + result.add(PathFileObject.createSimplePathFileObject(this, nullCheck(p))); + return result; + } + + @Override + public Iterable getJavaFileObjects(Path... paths) { + return getJavaFileObjectsFromPaths(Arrays.asList(nullCheck(paths))); + } + + public JavaFileObject getJavaFileForInput(Location location, + String className, Kind kind) throws IOException { + return getFileForInput(location, getRelativePath(className, kind)); + } + + 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; + } + + public JavaFileObject getJavaFileForOutput(Location location, + String className, Kind kind, FileObject sibling) throws IOException { + return getFileForOutput(location, getRelativePath(className, kind), sibling); + } + + 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); + } + + } + + public String inferBinaryName(Location location, JavaFileObject fo) { + nullCheck(fo); + // Need to match the path semantics of list(location, ...) + Iterable 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.emptyMap(), null); + fileSystems.put(p, fs); + } + return fs; + } + + private Map fileSystems; + + // + + // + + 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 String getExtension(String fileName) { + int lastDot = fileName.lastIndexOf("."); + return (lastDot == -1 ? "" : fileName.substring(lastDot)); + } + + protected Kind getKind(String fileName) { + String extn = getExtension(fileName); + if (extn.equals(Kind.CLASS.extension)) + return JavaFileObject.Kind.CLASS; + else if (extn.equals(Kind.SOURCE.extension)) + return JavaFileObject.Kind.SOURCE; + else if (extn.equals(Kind.HTML.extension)) + return JavaFileObject.Kind.HTML; + else + return JavaFileObject.Kind.OTHER; + } + + 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); + } + + // + + // + + // The following code is mostly all cut n paste from JavacFileManager and + // could/should be shared with it, perhaps by moving to a new class + // BaseFileManager in com.sun.tools.javac.util. + // There are no references to java.io.File or java.nio.file.Path here. + + /** + * The log to be used for error reporting. + */ + protected Log log; + + /** + * User provided charset (through javax.tools). + */ + protected Charset charset; + + private 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 loader = + Class.forName(classLoaderClass).asSubclass(ClassLoader.class); + Class[] constrArgTypes = { URL[].class, ClassLoader.class }; + Constructor 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); + } + + + // + public boolean handleOption(String current, Iterator 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; + } + // + + // + private String defaultEncodingName; + private String getDefaultEncodingName() { + if (defaultEncodingName == null) { + defaultEncodingName = + new OutputStreamWriter(new ByteArrayOutputStream()).getEncoding(); + } + return defaultEncodingName; + } + + protected String getEncodingName() { + String encName = options.get(OptionName.ENCODING); + if (encName == null) + return getDefaultEncodingName(); + else + return encName; + } + + 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 + } + + 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); + } + // + + // + /** + * Make a byte buffer from an input stream. + */ + 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(); + } + + 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; + // + + // + CharBuffer getCachedContent(JavaFileObject file) { + SoftReference r = contentCache.get(file); + return (r == null ? null : r.get()); + } + + void cache(JavaFileObject file, CharBuffer cb) { + contentCache.put(file, new SoftReference(cb)); + } + + private final Map> contentCache + = new HashMap>(); + // + + private static T nullCheck(T o) { + o.getClass(); // null check + return o; + } + + private static Collection nullCheck(Collection it) { + for (T t : it) + t.getClass(); // null check + return it; + } + // +} --- /dev/null 2009-07-15 19:05:43.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/nio/PathFileManager.java 2009-09-24 17:46:29.000000000 -0700 @@ -0,0 +1,124 @@ +/* + * 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, JavaCompipler might reasonably provide a method getPathFileManager, + * similar to {@link javax.tools.JavaCompiler#getStandardFileManager + * getStandardFileManager}. + * + *

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. + */ +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 getJavaFileObjectsFromPaths( + Iterable paths); + + /** + * Get file objects representing the given paths. + * Convenience method equivalent to: + * + *

+     *     getJavaFileObjectsFromPaths({@linkplain java.util.Arrays#asList Arrays.asList}(paths))
+     * 
+ * + * @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 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 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 searchPath) throws IOException; +} --- /dev/null 2009-07-15 19:05:43.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/nio/PathFileObject.java 2009-09-24 17:46:30.000000000 -0700 @@ -0,0 +1,313 @@ +/* + * 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.FilterInputStream; +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; + + +/** + * Implementation of JavaFileObject using java.nio.file API. + * + *

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. + */ +abstract class PathFileObject implements JavaFileObject { + private JavacPathFileManager fileManager; + private Path path; + + static PathFileObject createDirectoryPathFileObject(JavacPathFileManager fileManager, + final Path path, final Path dir) { + return new PathFileObject(fileManager, path) { + @Override + String inferBinaryName(Iterable paths) { + return toBinaryName(dir.relativize(path)); + } + }; + } + + static PathFileObject createJarPathFileObject(JavacPathFileManager fileManager, + final Path path) { + return new PathFileObject(fileManager, path) { + @Override + String inferBinaryName(Iterable paths) { + return toBinaryName(path.relativize(path)); + } + }; + } + + static PathFileObject createSiblingPathFileObject(JavacPathFileManager fileManager, + final Path path, final String relativePath) { + return new PathFileObject(fileManager, path) { + @Override + String inferBinaryName(Iterable paths) { + return toBinaryName(relativePath, "/"); + } + }; + } + + static PathFileObject createSimplePathFileObject(JavacPathFileManager fileManager, + final Path path) { + return new PathFileObject(fileManager, path) { + @Override + String inferBinaryName(Iterable 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 paths); + + /** + * Return the Path for this object. + * @return the Path for this object. + */ + Path getPath() { + return path; + } + + @Override + public Kind getKind() { + String name = path.getName().toString(); + 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; + } + + @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 { + InputStream in = path.newInputStream(); + //return path.newInputStream(); + // Currently, the stream obtained from path.newInputStream returns 0 + // for available(), which causes downstream problems for javac. + return new FilterInputStream(in) { + @Override + public int available() throws IOException { + return ((int) size()); + } + }; + } + + @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 2009-07-15 19:05:43.000000000 -0700 +++ new/test/tools/javac/nio/compileTest/CompileTest.java 2009-09-24 17:46:30.000000000 -0700 @@ -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 options = new ArrayList(); + options.addAll(Arrays.asList(opts)); + options.addAll(Arrays.asList( + "-verbose", "-XDverboseCompilePolicy", + "-d", classes.toString() + )); + Iterable 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 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 2009-07-15 19:05:43.000000000 -0700 +++ new/test/tools/javac/nio/compileTest/HelloPathWorld.java 2009-09-24 17:46:30.000000000 -0700 @@ -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!"); + } +}