aoqi@0: /* aoqi@0: * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.tools.javac.file; aoqi@0: aoqi@0: import java.io.ByteArrayOutputStream; aoqi@0: import java.io.File; aoqi@0: import java.io.FileNotFoundException; aoqi@0: import java.io.IOException; aoqi@0: import java.io.OutputStreamWriter; aoqi@0: import java.net.MalformedURLException; aoqi@0: import java.net.URI; aoqi@0: import java.net.URISyntaxException; aoqi@0: import java.net.URL; aoqi@0: import java.nio.CharBuffer; aoqi@0: import java.nio.charset.Charset; aoqi@0: import java.util.ArrayList; aoqi@0: import java.util.Arrays; aoqi@0: import java.util.Collection; aoqi@0: import java.util.Collections; aoqi@0: import java.util.Comparator; aoqi@0: import java.util.EnumSet; aoqi@0: import java.util.HashMap; aoqi@0: import java.util.Iterator; aoqi@0: import java.util.Map; aoqi@0: import java.util.Set; aoqi@0: import java.util.zip.ZipFile; aoqi@0: aoqi@0: import javax.lang.model.SourceVersion; aoqi@0: import javax.tools.FileObject; aoqi@0: import javax.tools.JavaFileManager; aoqi@0: import javax.tools.JavaFileObject; aoqi@0: import javax.tools.StandardJavaFileManager; aoqi@0: aoqi@0: import com.sun.tools.javac.file.RelativePath.RelativeFile; aoqi@0: import com.sun.tools.javac.file.RelativePath.RelativeDirectory; aoqi@0: import com.sun.tools.javac.util.BaseFileManager; aoqi@0: import com.sun.tools.javac.util.Context; aoqi@0: import com.sun.tools.javac.util.List; aoqi@0: import com.sun.tools.javac.util.ListBuffer; aoqi@0: aoqi@0: import static javax.tools.StandardLocation.*; aoqi@0: aoqi@0: /** aoqi@0: * This class provides access to the source, class and other files aoqi@0: * used by the compiler and related tools. aoqi@0: * aoqi@0: *

This is NOT part of any supported API. aoqi@0: * If you write code that depends on this, you do so at your own risk. aoqi@0: * This code and its internal interfaces are subject to change or aoqi@0: * deletion without notice. aoqi@0: */ aoqi@0: public class JavacFileManager extends BaseFileManager implements StandardJavaFileManager { aoqi@0: aoqi@0: public static char[] toArray(CharBuffer buffer) { aoqi@0: if (buffer.hasArray()) aoqi@0: return ((CharBuffer)buffer.compact().flip()).array(); aoqi@0: else aoqi@0: return buffer.toString().toCharArray(); aoqi@0: } aoqi@0: aoqi@0: private FSInfo fsInfo; aoqi@0: aoqi@0: private boolean contextUseOptimizedZip; aoqi@0: private ZipFileIndexCache zipFileIndexCache; aoqi@0: aoqi@0: private final Set sourceOrClass = aoqi@0: EnumSet.of(JavaFileObject.Kind.SOURCE, JavaFileObject.Kind.CLASS); aoqi@0: aoqi@0: protected boolean mmappedIO; aoqi@0: protected boolean symbolFileEnabled; aoqi@0: aoqi@0: protected enum SortFiles implements Comparator { aoqi@0: FORWARD { aoqi@0: public int compare(File f1, File f2) { aoqi@0: return f1.getName().compareTo(f2.getName()); aoqi@0: } aoqi@0: }, aoqi@0: REVERSE { aoqi@0: public int compare(File f1, File f2) { aoqi@0: return -f1.getName().compareTo(f2.getName()); aoqi@0: } aoqi@0: }; aoqi@0: }; aoqi@0: protected SortFiles sortFiles; aoqi@0: aoqi@0: /** aoqi@0: * Register a Context.Factory to create a JavacFileManager. aoqi@0: */ aoqi@0: public static void preRegister(Context context) { aoqi@0: context.put(JavaFileManager.class, new Context.Factory() { aoqi@0: public JavaFileManager make(Context c) { aoqi@0: return new JavacFileManager(c, true, null); aoqi@0: } aoqi@0: }); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Create a JavacFileManager using a given context, optionally registering aoqi@0: * it as the JavaFileManager for that context. aoqi@0: */ aoqi@0: public JavacFileManager(Context context, boolean register, Charset charset) { aoqi@0: super(charset); aoqi@0: if (register) aoqi@0: context.put(JavaFileManager.class, this); aoqi@0: setContext(context); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Set the context for JavacFileManager. aoqi@0: */ aoqi@0: @Override aoqi@0: public void setContext(Context context) { aoqi@0: super.setContext(context); aoqi@0: aoqi@0: fsInfo = FSInfo.instance(context); aoqi@0: aoqi@0: contextUseOptimizedZip = options.getBoolean("useOptimizedZip", true); aoqi@0: if (contextUseOptimizedZip) aoqi@0: zipFileIndexCache = ZipFileIndexCache.getSharedInstance(); aoqi@0: aoqi@0: mmappedIO = options.isSet("mmappedIO"); aoqi@0: symbolFileEnabled = !options.isSet("ignore.symbol.file"); aoqi@0: aoqi@0: String sf = options.get("sortFiles"); aoqi@0: if (sf != null) { aoqi@0: sortFiles = (sf.equals("reverse") ? SortFiles.REVERSE : SortFiles.FORWARD); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Set whether or not to use ct.sym as an alternate to rt.jar. aoqi@0: */ aoqi@0: public void setSymbolFileEnabled(boolean b) { aoqi@0: symbolFileEnabled = b; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public boolean isDefaultBootClassPath() { aoqi@0: return locations.isDefaultBootClassPath(); aoqi@0: } aoqi@0: aoqi@0: public JavaFileObject getFileForInput(String name) { aoqi@0: return getRegularFile(new File(name)); aoqi@0: } aoqi@0: aoqi@0: public JavaFileObject getRegularFile(File file) { aoqi@0: return new RegularFileObject(this, file); aoqi@0: } aoqi@0: aoqi@0: public JavaFileObject getFileForOutput(String classname, aoqi@0: JavaFileObject.Kind kind, aoqi@0: JavaFileObject sibling) aoqi@0: throws IOException aoqi@0: { aoqi@0: return getJavaFileForOutput(CLASS_OUTPUT, classname, kind, sibling); aoqi@0: } aoqi@0: aoqi@0: public Iterable getJavaFileObjectsFromStrings(Iterable names) { aoqi@0: ListBuffer files = new ListBuffer(); aoqi@0: for (String name : names) aoqi@0: files.append(new File(nullCheck(name))); aoqi@0: return getJavaFileObjectsFromFiles(files.toList()); aoqi@0: } aoqi@0: aoqi@0: public Iterable getJavaFileObjects(String... names) { aoqi@0: return getJavaFileObjectsFromStrings(Arrays.asList(nullCheck(names))); aoqi@0: } aoqi@0: aoqi@0: private static boolean isValidName(String name) { aoqi@0: // Arguably, isValidName should reject keywords (such as in SourceVersion.isName() ), aoqi@0: // but the set of keywords depends on the source level, and we don't want aoqi@0: // impls of JavaFileManager to have to be dependent on the source level. aoqi@0: // Therefore we simply check that the argument is a sequence of identifiers aoqi@0: // separated by ".". aoqi@0: for (String s : name.split("\\.", -1)) { aoqi@0: if (!SourceVersion.isIdentifier(s)) aoqi@0: return false; aoqi@0: } aoqi@0: return true; aoqi@0: } aoqi@0: aoqi@0: private static void validateClassName(String className) { aoqi@0: if (!isValidName(className)) aoqi@0: throw new IllegalArgumentException("Invalid class name: " + className); aoqi@0: } aoqi@0: aoqi@0: private static void validatePackageName(String packageName) { aoqi@0: if (packageName.length() > 0 && !isValidName(packageName)) aoqi@0: throw new IllegalArgumentException("Invalid packageName name: " + packageName); aoqi@0: } aoqi@0: aoqi@0: public static void testName(String name, aoqi@0: boolean isValidPackageName, aoqi@0: boolean isValidClassName) aoqi@0: { aoqi@0: try { aoqi@0: validatePackageName(name); aoqi@0: if (!isValidPackageName) aoqi@0: throw new AssertionError("Invalid package name accepted: " + name); aoqi@0: printAscii("Valid package name: \"%s\"", name); aoqi@0: } catch (IllegalArgumentException e) { aoqi@0: if (isValidPackageName) aoqi@0: throw new AssertionError("Valid package name rejected: " + name); aoqi@0: printAscii("Invalid package name: \"%s\"", name); aoqi@0: } aoqi@0: try { aoqi@0: validateClassName(name); aoqi@0: if (!isValidClassName) aoqi@0: throw new AssertionError("Invalid class name accepted: " + name); aoqi@0: printAscii("Valid class name: \"%s\"", name); aoqi@0: } catch (IllegalArgumentException e) { aoqi@0: if (isValidClassName) aoqi@0: throw new AssertionError("Valid class name rejected: " + name); aoqi@0: printAscii("Invalid class name: \"%s\"", name); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private static void printAscii(String format, Object... args) { aoqi@0: String message; aoqi@0: try { aoqi@0: final String ascii = "US-ASCII"; aoqi@0: message = new String(String.format(null, format, args).getBytes(ascii), ascii); aoqi@0: } catch (java.io.UnsupportedEncodingException ex) { aoqi@0: throw new AssertionError(ex); aoqi@0: } aoqi@0: System.out.println(message); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Insert all files in subdirectory subdirectory of directory directory aoqi@0: * which match fileKinds into resultList aoqi@0: */ aoqi@0: private void listDirectory(File directory, aoqi@0: RelativeDirectory subdirectory, aoqi@0: Set fileKinds, aoqi@0: boolean recurse, aoqi@0: ListBuffer resultList) { aoqi@0: File d = subdirectory.getFile(directory); aoqi@0: if (!caseMapCheck(d, subdirectory)) aoqi@0: return; aoqi@0: aoqi@0: File[] files = d.listFiles(); aoqi@0: if (files == null) aoqi@0: return; aoqi@0: aoqi@0: if (sortFiles != null) aoqi@0: Arrays.sort(files, sortFiles); aoqi@0: aoqi@0: for (File f: files) { aoqi@0: String fname = f.getName(); aoqi@0: if (f.isDirectory()) { aoqi@0: if (recurse && SourceVersion.isIdentifier(fname)) { aoqi@0: listDirectory(directory, aoqi@0: new RelativeDirectory(subdirectory, fname), aoqi@0: fileKinds, aoqi@0: recurse, aoqi@0: resultList); aoqi@0: } aoqi@0: } else { aoqi@0: if (isValidFile(fname, fileKinds)) { aoqi@0: JavaFileObject fe = aoqi@0: new RegularFileObject(this, fname, new File(d, fname)); aoqi@0: resultList.append(fe); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Insert all files in subdirectory subdirectory of archive archive aoqi@0: * which match fileKinds into resultList aoqi@0: */ aoqi@0: private void listArchive(Archive archive, aoqi@0: RelativeDirectory subdirectory, aoqi@0: Set fileKinds, aoqi@0: boolean recurse, aoqi@0: ListBuffer resultList) { aoqi@0: // Get the files directly in the subdir aoqi@0: List files = archive.getFiles(subdirectory); aoqi@0: if (files != null) { aoqi@0: for (; !files.isEmpty(); files = files.tail) { aoqi@0: String file = files.head; aoqi@0: if (isValidFile(file, fileKinds)) { aoqi@0: resultList.append(archive.getFileObject(subdirectory, file)); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: if (recurse) { aoqi@0: for (RelativeDirectory s: archive.getSubdirectories()) { aoqi@0: if (subdirectory.contains(s)) { aoqi@0: // Because the archive map is a flat list of directories, aoqi@0: // the enclosing loop will pick up all child subdirectories. aoqi@0: // Therefore, there is no need to recurse deeper. aoqi@0: listArchive(archive, s, fileKinds, false, resultList); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * container is a directory, a zip file, or a non-existant path. aoqi@0: * Insert all files in subdirectory subdirectory of container which aoqi@0: * match fileKinds into resultList aoqi@0: */ aoqi@0: private void listContainer(File container, aoqi@0: RelativeDirectory subdirectory, aoqi@0: Set fileKinds, aoqi@0: boolean recurse, aoqi@0: ListBuffer resultList) { aoqi@0: Archive archive = archives.get(container); aoqi@0: if (archive == null) { aoqi@0: // archives are not created for directories. aoqi@0: if (fsInfo.isDirectory(container)) { aoqi@0: listDirectory(container, aoqi@0: subdirectory, aoqi@0: fileKinds, aoqi@0: recurse, aoqi@0: resultList); aoqi@0: return; aoqi@0: } aoqi@0: aoqi@0: // Not a directory; either a file or non-existant, create the archive aoqi@0: try { aoqi@0: archive = openArchive(container); aoqi@0: } catch (IOException ex) { aoqi@0: log.error("error.reading.file", aoqi@0: container, getMessage(ex)); aoqi@0: return; aoqi@0: } aoqi@0: } aoqi@0: listArchive(archive, aoqi@0: subdirectory, aoqi@0: fileKinds, aoqi@0: recurse, aoqi@0: resultList); aoqi@0: } aoqi@0: aoqi@0: private boolean isValidFile(String s, Set fileKinds) { aoqi@0: JavaFileObject.Kind kind = getKind(s); aoqi@0: return fileKinds.contains(kind); aoqi@0: } aoqi@0: aoqi@0: private static final boolean fileSystemIsCaseSensitive = aoqi@0: File.separatorChar == '/'; aoqi@0: aoqi@0: /** Hack to make Windows case sensitive. Test whether given path aoqi@0: * ends in a string of characters with the same case as given name. aoqi@0: * Ignore file separators in both path and name. aoqi@0: */ aoqi@0: private boolean caseMapCheck(File f, RelativePath name) { aoqi@0: if (fileSystemIsCaseSensitive) return true; aoqi@0: // Note that getCanonicalPath() returns the case-sensitive aoqi@0: // spelled file name. aoqi@0: String path; aoqi@0: try { aoqi@0: path = f.getCanonicalPath(); aoqi@0: } catch (IOException ex) { aoqi@0: return false; aoqi@0: } aoqi@0: char[] pcs = path.toCharArray(); aoqi@0: char[] ncs = name.path.toCharArray(); aoqi@0: int i = pcs.length - 1; aoqi@0: int j = ncs.length - 1; aoqi@0: while (i >= 0 && j >= 0) { aoqi@0: while (i >= 0 && pcs[i] == File.separatorChar) i--; aoqi@0: while (j >= 0 && ncs[j] == '/') j--; aoqi@0: if (i >= 0 && j >= 0) { aoqi@0: if (pcs[i] != ncs[j]) return false; aoqi@0: i--; aoqi@0: j--; aoqi@0: } aoqi@0: } aoqi@0: return j < 0; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * An archive provides a flat directory structure of a ZipFile by aoqi@0: * mapping directory names to lists of files (basenames). aoqi@0: */ aoqi@0: public interface Archive { aoqi@0: void close() throws IOException; aoqi@0: aoqi@0: boolean contains(RelativePath name); aoqi@0: aoqi@0: JavaFileObject getFileObject(RelativeDirectory subdirectory, String file); aoqi@0: aoqi@0: List getFiles(RelativeDirectory subdirectory); aoqi@0: aoqi@0: Set getSubdirectories(); aoqi@0: } aoqi@0: aoqi@0: public class MissingArchive implements Archive { aoqi@0: final File zipFileName; aoqi@0: public MissingArchive(File name) { aoqi@0: zipFileName = name; aoqi@0: } aoqi@0: public boolean contains(RelativePath name) { aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: public void close() { aoqi@0: } aoqi@0: aoqi@0: public JavaFileObject getFileObject(RelativeDirectory subdirectory, String file) { aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: public List getFiles(RelativeDirectory subdirectory) { aoqi@0: return List.nil(); aoqi@0: } aoqi@0: aoqi@0: public Set getSubdirectories() { aoqi@0: return Collections.emptySet(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public String toString() { aoqi@0: return "MissingArchive[" + zipFileName + "]"; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** A directory of zip files already opened. aoqi@0: */ aoqi@0: Map archives = new HashMap(); aoqi@0: aoqi@0: private static final String[] symbolFileLocation = { "lib", "ct.sym" }; aoqi@0: private static final RelativeDirectory symbolFilePrefix aoqi@0: = new RelativeDirectory("META-INF/sym/rt.jar/"); aoqi@0: aoqi@0: /* aoqi@0: * This method looks for a ZipFormatException and takes appropriate aoqi@0: * evasive action. If there is a failure in the fast mode then we aoqi@0: * fail over to the platform zip, and allow it to deal with a potentially aoqi@0: * non compliant zip file. aoqi@0: */ aoqi@0: protected Archive openArchive(File zipFilename) throws IOException { aoqi@0: try { aoqi@0: return openArchive(zipFilename, contextUseOptimizedZip); aoqi@0: } catch (IOException ioe) { aoqi@0: if (ioe instanceof ZipFileIndex.ZipFormatException) { aoqi@0: return openArchive(zipFilename, false); aoqi@0: } else { aoqi@0: throw ioe; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** Open a new zip file directory, and cache it. aoqi@0: */ aoqi@0: private Archive openArchive(File zipFileName, boolean useOptimizedZip) throws IOException { aoqi@0: File origZipFileName = zipFileName; aoqi@0: if (symbolFileEnabled && locations.isDefaultBootClassPathRtJar(zipFileName)) { aoqi@0: File file = zipFileName.getParentFile().getParentFile(); // ${java.home} aoqi@0: if (new File(file.getName()).equals(new File("jre"))) aoqi@0: file = file.getParentFile(); aoqi@0: // file == ${jdk.home} aoqi@0: for (String name : symbolFileLocation) aoqi@0: file = new File(file, name); aoqi@0: // file == ${jdk.home}/lib/ct.sym aoqi@0: if (file.exists()) aoqi@0: zipFileName = file; aoqi@0: } aoqi@0: aoqi@0: Archive archive; aoqi@0: try { aoqi@0: aoqi@0: ZipFile zdir = null; aoqi@0: aoqi@0: boolean usePreindexedCache = false; aoqi@0: String preindexCacheLocation = null; aoqi@0: aoqi@0: if (!useOptimizedZip) { aoqi@0: zdir = new ZipFile(zipFileName); aoqi@0: } else { aoqi@0: usePreindexedCache = options.isSet("usezipindex"); aoqi@0: preindexCacheLocation = options.get("java.io.tmpdir"); aoqi@0: String optCacheLoc = options.get("cachezipindexdir"); aoqi@0: aoqi@0: if (optCacheLoc != null && optCacheLoc.length() != 0) { aoqi@0: if (optCacheLoc.startsWith("\"")) { aoqi@0: if (optCacheLoc.endsWith("\"")) { aoqi@0: optCacheLoc = optCacheLoc.substring(1, optCacheLoc.length() - 1); aoqi@0: } aoqi@0: else { aoqi@0: optCacheLoc = optCacheLoc.substring(1); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: File cacheDir = new File(optCacheLoc); aoqi@0: if (cacheDir.exists() && cacheDir.canWrite()) { aoqi@0: preindexCacheLocation = optCacheLoc; aoqi@0: if (!preindexCacheLocation.endsWith("/") && aoqi@0: !preindexCacheLocation.endsWith(File.separator)) { aoqi@0: preindexCacheLocation += File.separator; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: if (origZipFileName == zipFileName) { aoqi@0: if (!useOptimizedZip) { aoqi@0: archive = new ZipArchive(this, zdir); aoqi@0: } else { aoqi@0: archive = new ZipFileIndexArchive(this, aoqi@0: zipFileIndexCache.getZipFileIndex(zipFileName, aoqi@0: null, aoqi@0: usePreindexedCache, aoqi@0: preindexCacheLocation, aoqi@0: options.isSet("writezipindexfiles"))); aoqi@0: } aoqi@0: } else { aoqi@0: if (!useOptimizedZip) { aoqi@0: archive = new SymbolArchive(this, origZipFileName, zdir, symbolFilePrefix); aoqi@0: } else { aoqi@0: archive = new ZipFileIndexArchive(this, aoqi@0: zipFileIndexCache.getZipFileIndex(zipFileName, aoqi@0: symbolFilePrefix, aoqi@0: usePreindexedCache, aoqi@0: preindexCacheLocation, aoqi@0: options.isSet("writezipindexfiles"))); aoqi@0: } aoqi@0: } aoqi@0: } catch (FileNotFoundException ex) { aoqi@0: archive = new MissingArchive(zipFileName); aoqi@0: } catch (ZipFileIndex.ZipFormatException zfe) { aoqi@0: throw zfe; aoqi@0: } catch (IOException ex) { aoqi@0: if (zipFileName.exists()) aoqi@0: log.error("error.reading.file", zipFileName, getMessage(ex)); aoqi@0: archive = new MissingArchive(zipFileName); aoqi@0: } aoqi@0: aoqi@0: archives.put(origZipFileName, archive); aoqi@0: return archive; aoqi@0: } aoqi@0: aoqi@0: /** Flush any output resources. aoqi@0: */ aoqi@0: public void flush() { aoqi@0: contentCache.clear(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Close the JavaFileManager, releasing resources. aoqi@0: */ aoqi@0: public void close() { aoqi@0: for (Iterator i = archives.values().iterator(); i.hasNext(); ) { aoqi@0: Archive a = i.next(); aoqi@0: i.remove(); aoqi@0: try { aoqi@0: a.close(); aoqi@0: } catch (IOException e) { aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private String defaultEncodingName; aoqi@0: private String getDefaultEncodingName() { aoqi@0: if (defaultEncodingName == null) { aoqi@0: defaultEncodingName = aoqi@0: new OutputStreamWriter(new ByteArrayOutputStream()).getEncoding(); aoqi@0: } aoqi@0: return defaultEncodingName; aoqi@0: } aoqi@0: aoqi@0: public ClassLoader getClassLoader(Location location) { aoqi@0: nullCheck(location); aoqi@0: Iterable path = getLocation(location); aoqi@0: if (path == null) aoqi@0: return null; aoqi@0: ListBuffer lb = new ListBuffer(); aoqi@0: for (File f: path) { aoqi@0: try { aoqi@0: lb.append(f.toURI().toURL()); aoqi@0: } catch (MalformedURLException e) { aoqi@0: throw new AssertionError(e); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: return getClassLoader(lb.toArray(new URL[lb.size()])); aoqi@0: } aoqi@0: aoqi@0: public Iterable list(Location location, aoqi@0: String packageName, aoqi@0: Set kinds, aoqi@0: boolean recurse) aoqi@0: throws IOException aoqi@0: { aoqi@0: // validatePackageName(packageName); aoqi@0: nullCheck(packageName); aoqi@0: nullCheck(kinds); aoqi@0: aoqi@0: Iterable path = getLocation(location); aoqi@0: if (path == null) aoqi@0: return List.nil(); aoqi@0: RelativeDirectory subdirectory = RelativeDirectory.forPackage(packageName); aoqi@0: ListBuffer results = new ListBuffer(); aoqi@0: aoqi@0: for (File directory : path) aoqi@0: listContainer(directory, subdirectory, kinds, recurse, results); aoqi@0: return results.toList(); aoqi@0: } aoqi@0: aoqi@0: public String inferBinaryName(Location location, JavaFileObject file) { aoqi@0: file.getClass(); // null check aoqi@0: location.getClass(); // null check aoqi@0: // Need to match the path semantics of list(location, ...) aoqi@0: Iterable path = getLocation(location); aoqi@0: if (path == null) { aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: if (file instanceof BaseFileObject) { aoqi@0: return ((BaseFileObject) file).inferBinaryName(path); aoqi@0: } else aoqi@0: throw new IllegalArgumentException(file.getClass().getName()); aoqi@0: } aoqi@0: aoqi@0: public boolean isSameFile(FileObject a, FileObject b) { aoqi@0: nullCheck(a); aoqi@0: nullCheck(b); aoqi@0: if (!(a instanceof BaseFileObject)) aoqi@0: throw new IllegalArgumentException("Not supported: " + a); aoqi@0: if (!(b instanceof BaseFileObject)) aoqi@0: throw new IllegalArgumentException("Not supported: " + b); aoqi@0: return a.equals(b); aoqi@0: } aoqi@0: aoqi@0: public boolean hasLocation(Location location) { aoqi@0: return getLocation(location) != null; aoqi@0: } aoqi@0: aoqi@0: public JavaFileObject getJavaFileForInput(Location location, aoqi@0: String className, aoqi@0: JavaFileObject.Kind kind) aoqi@0: throws IOException aoqi@0: { aoqi@0: nullCheck(location); aoqi@0: // validateClassName(className); aoqi@0: nullCheck(className); aoqi@0: nullCheck(kind); aoqi@0: if (!sourceOrClass.contains(kind)) aoqi@0: throw new IllegalArgumentException("Invalid kind: " + kind); aoqi@0: return getFileForInput(location, RelativeFile.forClass(className, kind)); aoqi@0: } aoqi@0: aoqi@0: public FileObject getFileForInput(Location location, aoqi@0: String packageName, aoqi@0: String relativeName) aoqi@0: throws IOException aoqi@0: { aoqi@0: nullCheck(location); aoqi@0: // validatePackageName(packageName); aoqi@0: nullCheck(packageName); aoqi@0: if (!isRelativeUri(relativeName)) aoqi@0: throw new IllegalArgumentException("Invalid relative name: " + relativeName); aoqi@0: RelativeFile name = packageName.length() == 0 aoqi@0: ? new RelativeFile(relativeName) aoqi@0: : new RelativeFile(RelativeDirectory.forPackage(packageName), relativeName); aoqi@0: return getFileForInput(location, name); aoqi@0: } aoqi@0: aoqi@0: private JavaFileObject getFileForInput(Location location, RelativeFile name) throws IOException { aoqi@0: Iterable path = getLocation(location); aoqi@0: if (path == null) aoqi@0: return null; aoqi@0: aoqi@0: for (File dir: path) { aoqi@0: Archive a = archives.get(dir); aoqi@0: if (a == null) { aoqi@0: if (fsInfo.isDirectory(dir)) { aoqi@0: File f = name.getFile(dir); aoqi@0: if (f.exists()) aoqi@0: return new RegularFileObject(this, f); aoqi@0: continue; aoqi@0: } aoqi@0: // Not a directory, create the archive aoqi@0: a = openArchive(dir); aoqi@0: } aoqi@0: // Process the archive aoqi@0: if (a.contains(name)) { aoqi@0: return a.getFileObject(name.dirname(), name.basename()); aoqi@0: } aoqi@0: } aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: public JavaFileObject getJavaFileForOutput(Location location, aoqi@0: String className, aoqi@0: JavaFileObject.Kind kind, aoqi@0: FileObject sibling) aoqi@0: throws IOException aoqi@0: { aoqi@0: nullCheck(location); aoqi@0: // validateClassName(className); aoqi@0: nullCheck(className); aoqi@0: nullCheck(kind); aoqi@0: if (!sourceOrClass.contains(kind)) aoqi@0: throw new IllegalArgumentException("Invalid kind: " + kind); aoqi@0: return getFileForOutput(location, RelativeFile.forClass(className, kind), sibling); aoqi@0: } aoqi@0: aoqi@0: public FileObject getFileForOutput(Location location, aoqi@0: String packageName, aoqi@0: String relativeName, aoqi@0: FileObject sibling) aoqi@0: throws IOException aoqi@0: { aoqi@0: nullCheck(location); aoqi@0: // validatePackageName(packageName); aoqi@0: nullCheck(packageName); aoqi@0: if (!isRelativeUri(relativeName)) aoqi@0: throw new IllegalArgumentException("Invalid relative name: " + relativeName); aoqi@0: RelativeFile name = packageName.length() == 0 aoqi@0: ? new RelativeFile(relativeName) aoqi@0: : new RelativeFile(RelativeDirectory.forPackage(packageName), relativeName); aoqi@0: return getFileForOutput(location, name, sibling); aoqi@0: } aoqi@0: aoqi@0: private JavaFileObject getFileForOutput(Location location, aoqi@0: RelativeFile fileName, aoqi@0: FileObject sibling) aoqi@0: throws IOException aoqi@0: { aoqi@0: File dir; aoqi@0: if (location == CLASS_OUTPUT) { aoqi@0: if (getClassOutDir() != null) { aoqi@0: dir = getClassOutDir(); aoqi@0: } else { aoqi@0: File siblingDir = null; aoqi@0: if (sibling != null && sibling instanceof RegularFileObject) { aoqi@0: siblingDir = ((RegularFileObject)sibling).file.getParentFile(); aoqi@0: } aoqi@0: return new RegularFileObject(this, new File(siblingDir, fileName.basename())); aoqi@0: } aoqi@0: } else if (location == SOURCE_OUTPUT) { aoqi@0: dir = (getSourceOutDir() != null ? getSourceOutDir() : getClassOutDir()); aoqi@0: } else { aoqi@0: Iterable path = locations.getLocation(location); aoqi@0: dir = null; aoqi@0: for (File f: path) { aoqi@0: dir = f; aoqi@0: break; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: File file = fileName.getFile(dir); // null-safe aoqi@0: return new RegularFileObject(this, file); aoqi@0: aoqi@0: } aoqi@0: aoqi@0: public Iterable getJavaFileObjectsFromFiles( aoqi@0: Iterable files) aoqi@0: { aoqi@0: ArrayList result; aoqi@0: if (files instanceof Collection) aoqi@0: result = new ArrayList(((Collection)files).size()); aoqi@0: else aoqi@0: result = new ArrayList(); aoqi@0: for (File f: files) aoqi@0: result.add(new RegularFileObject(this, nullCheck(f))); aoqi@0: return result; aoqi@0: } aoqi@0: aoqi@0: public Iterable getJavaFileObjects(File... files) { aoqi@0: return getJavaFileObjectsFromFiles(Arrays.asList(nullCheck(files))); aoqi@0: } aoqi@0: aoqi@0: public void setLocation(Location location, aoqi@0: Iterable path) aoqi@0: throws IOException aoqi@0: { aoqi@0: nullCheck(location); aoqi@0: locations.setLocation(location, path); aoqi@0: } aoqi@0: aoqi@0: public Iterable getLocation(Location location) { aoqi@0: nullCheck(location); aoqi@0: return locations.getLocation(location); aoqi@0: } aoqi@0: aoqi@0: private File getClassOutDir() { aoqi@0: return locations.getOutputLocation(CLASS_OUTPUT); aoqi@0: } aoqi@0: aoqi@0: private File getSourceOutDir() { aoqi@0: return locations.getOutputLocation(SOURCE_OUTPUT); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Enforces the specification of a "relative" name as used in aoqi@0: * {@linkplain #getFileForInput(Location,String,String) aoqi@0: * getFileForInput}. This method must follow the rules defined in aoqi@0: * that method, do not make any changes without consulting the aoqi@0: * specification. aoqi@0: */ aoqi@0: protected static boolean isRelativeUri(URI uri) { aoqi@0: if (uri.isAbsolute()) aoqi@0: return false; aoqi@0: String path = uri.normalize().getPath(); aoqi@0: if (path.length() == 0 /* isEmpty() is mustang API */) aoqi@0: return false; aoqi@0: if (!path.equals(uri.getPath())) // implicitly checks for embedded . and .. aoqi@0: return false; aoqi@0: if (path.startsWith("/") || path.startsWith("./") || path.startsWith("../")) aoqi@0: return false; aoqi@0: return true; aoqi@0: } aoqi@0: aoqi@0: // Convenience method aoqi@0: protected static boolean isRelativeUri(String u) { aoqi@0: try { aoqi@0: return isRelativeUri(new URI(u)); aoqi@0: } catch (URISyntaxException e) { aoqi@0: return false; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Converts a relative file name to a relative URI. This is aoqi@0: * different from File.toURI as this method does not canonicalize aoqi@0: * the file before creating the URI. Furthermore, no schema is aoqi@0: * used. aoqi@0: * @param file a relative file name aoqi@0: * @return a relative URI aoqi@0: * @throws IllegalArgumentException if the file name is not aoqi@0: * relative according to the definition given in {@link aoqi@0: * javax.tools.JavaFileManager#getFileForInput} aoqi@0: */ aoqi@0: public static String getRelativeName(File file) { aoqi@0: if (!file.isAbsolute()) { aoqi@0: String result = file.getPath().replace(File.separatorChar, '/'); aoqi@0: if (isRelativeUri(result)) aoqi@0: return result; aoqi@0: } aoqi@0: throw new IllegalArgumentException("Invalid relative path: " + file); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Get a detail message from an IOException. aoqi@0: * Most, but not all, instances of IOException provide a non-null result aoqi@0: * for getLocalizedMessage(). But some instances return null: in these aoqi@0: * cases, fallover to getMessage(), and if even that is null, return the aoqi@0: * name of the exception itself. aoqi@0: * @param e an IOException aoqi@0: * @return a string to include in a compiler diagnostic aoqi@0: */ aoqi@0: public static String getMessage(IOException e) { aoqi@0: String s = e.getLocalizedMessage(); aoqi@0: if (s != null) aoqi@0: return s; aoqi@0: s = e.getMessage(); aoqi@0: if (s != null) aoqi@0: return s; aoqi@0: return e.toString(); aoqi@0: } aoqi@0: }