duke@1: /*
xdono@404: * Copyright 2005-2009 Sun Microsystems, Inc. All Rights Reserved.
duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
duke@1: *
duke@1: * This code is free software; you can redistribute it and/or modify it
duke@1: * under the terms of the GNU General Public License version 2 only, as
duke@1: * published by the Free Software Foundation. Sun designates this
duke@1: * particular file as subject to the "Classpath" exception as provided
duke@1: * by Sun in the LICENSE file that accompanied this code.
duke@1: *
duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT
duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
duke@1: * version 2 for more details (a copy is included in the LICENSE file that
duke@1: * accompanied this code).
duke@1: *
duke@1: * You should have received a copy of the GNU General Public License version
duke@1: * 2 along with this work; if not, write to the Free Software Foundation,
duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
duke@1: *
duke@1: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
duke@1: * CA 95054 USA or visit www.sun.com if you need additional information or
duke@1: * have any questions.
duke@1: */
duke@1:
jjg@50: package com.sun.tools.javac.file;
duke@1:
duke@1: import java.io.ByteArrayOutputStream;
duke@1: import java.io.File;
duke@1: import java.io.FileNotFoundException;
duke@1: import java.io.IOException;
duke@1: import java.io.OutputStreamWriter;
duke@1: import java.net.MalformedURLException;
duke@1: import java.net.URI;
jjg@400: import java.net.URISyntaxException;
duke@1: import java.net.URL;
duke@1: import java.nio.CharBuffer;
duke@1: import java.nio.charset.Charset;
duke@1: import java.util.ArrayList;
duke@1: import java.util.Arrays;
duke@1: import java.util.Collection;
duke@1: import java.util.Collections;
duke@1: import java.util.EnumSet;
duke@1: import java.util.HashMap;
duke@1: import java.util.Iterator;
duke@1: import java.util.Map;
duke@1: import java.util.Set;
duke@1: import java.util.zip.ZipFile;
duke@1:
duke@1: import javax.lang.model.SourceVersion;
duke@1: import javax.tools.FileObject;
duke@1: import javax.tools.JavaFileManager;
duke@1: import javax.tools.JavaFileObject;
jjg@50: import javax.tools.StandardJavaFileManager;
duke@1:
jjg@103: import com.sun.tools.javac.file.RelativePath.RelativeFile;
jjg@103: import com.sun.tools.javac.file.RelativePath.RelativeDirectory;
jjg@50: import com.sun.tools.javac.main.OptionName;
jjg@450: import com.sun.tools.javac.util.BaseFileManager;
jjg@50: import com.sun.tools.javac.util.Context;
jjg@50: import com.sun.tools.javac.util.List;
jjg@50: import com.sun.tools.javac.util.ListBuffer;
duke@1:
jjg@103: import static javax.tools.StandardLocation.*;
duke@1: import static com.sun.tools.javac.main.OptionName.*;
duke@1:
duke@1: /**
duke@1: * This class provides access to the source, class and other files
duke@1: * used by the compiler and related tools.
jjg@333: *
jjg@333: *
This is NOT part of any API supported by Sun Microsystems.
jjg@333: * If you write code that depends on this, you do so at your own risk.
jjg@333: * This code and its internal interfaces are subject to change or
jjg@333: * deletion without notice.
duke@1: */
jjg@450: public class JavacFileManager extends BaseFileManager implements StandardJavaFileManager {
duke@1:
duke@1: boolean useZipFileIndex;
duke@1:
duke@1: public static char[] toArray(CharBuffer buffer) {
duke@1: if (buffer.hasArray())
duke@1: return ((CharBuffer)buffer.compact().flip()).array();
duke@1: else
duke@1: return buffer.toString().toCharArray();
duke@1: }
duke@1:
duke@1: /** Encapsulates knowledge of paths
duke@1: */
duke@1: private Paths paths;
duke@1:
jjg@106: private FSInfo fsInfo;
jjg@106:
duke@1: private final File uninited = new File("U N I N I T E D");
duke@1:
duke@1: private final Set sourceOrClass =
duke@1: EnumSet.of(JavaFileObject.Kind.SOURCE, JavaFileObject.Kind.CLASS);
duke@1:
duke@1: /** The standard output directory, primarily used for classes.
duke@1: * Initialized by the "-d" option.
duke@1: * If classOutDir = null, files are written into same directory as the sources
duke@1: * they were generated from.
duke@1: */
duke@1: private File classOutDir = uninited;
duke@1:
duke@1: /** The output directory, used when generating sources while processing annotations.
duke@1: * Initialized by the "-s" option.
duke@1: */
duke@1: private File sourceOutDir = uninited;
duke@1:
duke@1: protected boolean mmappedIO;
duke@1: protected boolean ignoreSymbolFile;
duke@1:
duke@1: /**
duke@1: * Register a Context.Factory to create a JavacFileManager.
duke@1: */
duke@1: public static void preRegister(final Context context) {
duke@1: context.put(JavaFileManager.class, new Context.Factory() {
duke@1: public JavaFileManager make() {
duke@1: return new JavacFileManager(context, true, null);
duke@1: }
duke@1: });
duke@1: }
duke@1:
duke@1: /**
duke@1: * Create a JavacFileManager using a given context, optionally registering
duke@1: * it as the JavaFileManager for that context.
duke@1: */
duke@1: public JavacFileManager(Context context, boolean register, Charset charset) {
jjg@450: super(charset);
duke@1: if (register)
duke@1: context.put(JavaFileManager.class, this);
duke@1: setContext(context);
duke@1: }
duke@1:
duke@1: /**
duke@1: * Set the context for JavacFileManager.
duke@1: */
jjg@450: @Override
duke@1: public void setContext(Context context) {
jjg@450: super.setContext(context);
duke@1: if (paths == null) {
duke@1: paths = Paths.instance(context);
duke@1: } else {
duke@1: // Reuse the Paths object as it stores the locations that
duke@1: // have been set with setLocation, etc.
duke@1: paths.setContext(context);
duke@1: }
duke@1:
jjg@106: fsInfo = FSInfo.instance(context);
duke@1:
duke@1: useZipFileIndex = System.getProperty("useJavaUtilZip") == null;// TODO: options.get("useJavaUtilZip") == null;
duke@1:
duke@1: mmappedIO = options.get("mmappedIO") != null;
duke@1: ignoreSymbolFile = options.get("ignore.symbol.file") != null;
duke@1: }
duke@1:
duke@1: public JavaFileObject getFileForInput(String name) {
duke@1: return getRegularFile(new File(name));
duke@1: }
duke@1:
duke@1: public JavaFileObject getRegularFile(File file) {
jjg@57: return new RegularFileObject(this, file);
duke@1: }
duke@1:
duke@1: public JavaFileObject getFileForOutput(String classname,
duke@1: JavaFileObject.Kind kind,
duke@1: JavaFileObject sibling)
duke@1: throws IOException
duke@1: {
duke@1: return getJavaFileForOutput(CLASS_OUTPUT, classname, kind, sibling);
duke@1: }
duke@1:
duke@1: public Iterable extends JavaFileObject> getJavaFileObjectsFromStrings(Iterable names) {
duke@1: ListBuffer files = new ListBuffer();
duke@1: for (String name : names)
duke@1: files.append(new File(nullCheck(name)));
duke@1: return getJavaFileObjectsFromFiles(files.toList());
duke@1: }
duke@1:
duke@1: public Iterable extends JavaFileObject> getJavaFileObjects(String... names) {
duke@1: return getJavaFileObjectsFromStrings(Arrays.asList(nullCheck(names)));
duke@1: }
duke@1:
duke@1: private static boolean isValidName(String name) {
duke@1: // Arguably, isValidName should reject keywords (such as in SourceVersion.isName() ),
duke@1: // but the set of keywords depends on the source level, and we don't want
duke@1: // impls of JavaFileManager to have to be dependent on the source level.
duke@1: // Therefore we simply check that the argument is a sequence of identifiers
duke@1: // separated by ".".
duke@1: for (String s : name.split("\\.", -1)) {
duke@1: if (!SourceVersion.isIdentifier(s))
duke@1: return false;
duke@1: }
duke@1: return true;
duke@1: }
duke@1:
duke@1: private static void validateClassName(String className) {
duke@1: if (!isValidName(className))
duke@1: throw new IllegalArgumentException("Invalid class name: " + className);
duke@1: }
duke@1:
duke@1: private static void validatePackageName(String packageName) {
duke@1: if (packageName.length() > 0 && !isValidName(packageName))
duke@1: throw new IllegalArgumentException("Invalid packageName name: " + packageName);
duke@1: }
duke@1:
duke@1: public static void testName(String name,
duke@1: boolean isValidPackageName,
duke@1: boolean isValidClassName)
duke@1: {
duke@1: try {
duke@1: validatePackageName(name);
duke@1: if (!isValidPackageName)
duke@1: throw new AssertionError("Invalid package name accepted: " + name);
duke@1: printAscii("Valid package name: \"%s\"", name);
duke@1: } catch (IllegalArgumentException e) {
duke@1: if (isValidPackageName)
duke@1: throw new AssertionError("Valid package name rejected: " + name);
duke@1: printAscii("Invalid package name: \"%s\"", name);
duke@1: }
duke@1: try {
duke@1: validateClassName(name);
duke@1: if (!isValidClassName)
duke@1: throw new AssertionError("Invalid class name accepted: " + name);
duke@1: printAscii("Valid class name: \"%s\"", name);
duke@1: } catch (IllegalArgumentException e) {
duke@1: if (isValidClassName)
duke@1: throw new AssertionError("Valid class name rejected: " + name);
duke@1: printAscii("Invalid class name: \"%s\"", name);
duke@1: }
duke@1: }
jjg@103:
duke@1: private static void printAscii(String format, Object... args) {
duke@1: String message;
duke@1: try {
duke@1: final String ascii = "US-ASCII";
duke@1: message = new String(String.format(null, format, args).getBytes(ascii), ascii);
duke@1: } catch (java.io.UnsupportedEncodingException ex) {
duke@1: throw new AssertionError(ex);
duke@1: }
duke@1: System.out.println(message);
duke@1: }
duke@1:
duke@1: /**
duke@1: * Insert all files in subdirectory `subdirectory' of `directory' which end
duke@1: * in one of the extensions in `extensions' into packageSym.
duke@1: */
duke@1: private void listDirectory(File directory,
jjg@103: RelativeDirectory subdirectory,
duke@1: Set fileKinds,
duke@1: boolean recurse,
duke@1: ListBuffer l) {
duke@1: Archive archive = archives.get(directory);
duke@1:
jjg@106: boolean isFile = fsInfo.isFile(directory);
duke@1:
duke@1: if (archive != null || isFile) {
duke@1: if (archive == null) {
duke@1: try {
duke@1: archive = openArchive(directory);
duke@1: } catch (IOException ex) {
duke@1: log.error("error.reading.file",
duke@1: directory, ex.getLocalizedMessage());
duke@1: return;
duke@1: }
duke@1: }
duke@1:
duke@1: List files = archive.getFiles(subdirectory);
duke@1: if (files != null) {
duke@1: for (String file; !files.isEmpty(); files = files.tail) {
duke@1: file = files.head;
duke@1: if (isValidFile(file, fileKinds)) {
duke@1: l.append(archive.getFileObject(subdirectory, file));
duke@1: }
duke@1: }
duke@1: }
duke@1: if (recurse) {
jjg@103: for (RelativeDirectory s: archive.getSubdirectories()) {
jjg@103: if (subdirectory.contains(s)) {
duke@1: // Because the archive map is a flat list of directories,
duke@1: // the enclosing loop will pick up all child subdirectories.
duke@1: // Therefore, there is no need to recurse deeper.
duke@1: listDirectory(directory, s, fileKinds, false, l);
duke@1: }
duke@1: }
duke@1: }
duke@1: } else {
jjg@103: File d = subdirectory.getFile(directory);
duke@1: if (!caseMapCheck(d, subdirectory))
duke@1: return;
duke@1:
duke@1: File[] files = d.listFiles();
duke@1: if (files == null)
duke@1: return;
duke@1:
duke@1: for (File f: files) {
duke@1: String fname = f.getName();
duke@1: if (f.isDirectory()) {
duke@1: if (recurse && SourceVersion.isIdentifier(fname)) {
duke@1: listDirectory(directory,
jjg@103: new RelativeDirectory(subdirectory, fname),
duke@1: fileKinds,
duke@1: recurse,
duke@1: l);
duke@1: }
duke@1: } else {
duke@1: if (isValidFile(fname, fileKinds)) {
duke@1: JavaFileObject fe =
jjg@57: new RegularFileObject(this, fname, new File(d, fname));
duke@1: l.append(fe);
duke@1: }
duke@1: }
duke@1: }
duke@1: }
duke@1: }
duke@1:
duke@1: private boolean isValidFile(String s, Set fileKinds) {
jjg@450: JavaFileObject.Kind kind = getKind(s);
duke@1: return fileKinds.contains(kind);
duke@1: }
duke@1:
duke@1: private static final boolean fileSystemIsCaseSensitive =
duke@1: File.separatorChar == '/';
duke@1:
duke@1: /** Hack to make Windows case sensitive. Test whether given path
duke@1: * ends in a string of characters with the same case as given name.
duke@1: * Ignore file separators in both path and name.
duke@1: */
jjg@103: private boolean caseMapCheck(File f, RelativePath name) {
duke@1: if (fileSystemIsCaseSensitive) return true;
duke@1: // Note that getCanonicalPath() returns the case-sensitive
duke@1: // spelled file name.
duke@1: String path;
duke@1: try {
duke@1: path = f.getCanonicalPath();
duke@1: } catch (IOException ex) {
duke@1: return false;
duke@1: }
duke@1: char[] pcs = path.toCharArray();
jjg@103: char[] ncs = name.path.toCharArray();
duke@1: int i = pcs.length - 1;
duke@1: int j = ncs.length - 1;
duke@1: while (i >= 0 && j >= 0) {
duke@1: while (i >= 0 && pcs[i] == File.separatorChar) i--;
jjg@103: while (j >= 0 && ncs[j] == '/') j--;
duke@1: if (i >= 0 && j >= 0) {
duke@1: if (pcs[i] != ncs[j]) return false;
duke@1: i--;
duke@1: j--;
duke@1: }
duke@1: }
duke@1: return j < 0;
duke@1: }
duke@1:
duke@1: /**
duke@1: * An archive provides a flat directory structure of a ZipFile by
duke@1: * mapping directory names to lists of files (basenames).
duke@1: */
duke@1: public interface Archive {
duke@1: void close() throws IOException;
duke@1:
jjg@103: boolean contains(RelativePath name);
duke@1:
jjg@103: JavaFileObject getFileObject(RelativeDirectory subdirectory, String file);
duke@1:
jjg@103: List getFiles(RelativeDirectory subdirectory);
duke@1:
jjg@103: Set getSubdirectories();
duke@1: }
duke@1:
duke@1: public class MissingArchive implements Archive {
duke@1: final File zipFileName;
duke@1: public MissingArchive(File name) {
duke@1: zipFileName = name;
duke@1: }
jjg@103: public boolean contains(RelativePath name) {
jjg@57: return false;
duke@1: }
duke@1:
duke@1: public void close() {
duke@1: }
duke@1:
jjg@103: public JavaFileObject getFileObject(RelativeDirectory subdirectory, String file) {
duke@1: return null;
duke@1: }
duke@1:
jjg@103: public List getFiles(RelativeDirectory subdirectory) {
duke@1: return List.nil();
duke@1: }
duke@1:
jjg@103: public Set getSubdirectories() {
duke@1: return Collections.emptySet();
duke@1: }
jjg@103:
jjg@400: @Override
jjg@103: public String toString() {
jjg@103: return "MissingArchive[" + zipFileName + "]";
jjg@103: }
duke@1: }
duke@1:
duke@1: /** A directory of zip files already opened.
duke@1: */
duke@1: Map archives = new HashMap();
duke@1:
jjg@103: private static final String[] symbolFileLocation = { "lib", "ct.sym" };
jjg@103: private static final RelativeDirectory symbolFilePrefix
jjg@103: = new RelativeDirectory("META-INF/sym/rt.jar/");
jjg@103:
duke@1: /** Open a new zip file directory.
duke@1: */
duke@1: protected Archive openArchive(File zipFileName) throws IOException {
duke@1: Archive archive = archives.get(zipFileName);
duke@1: if (archive == null) {
duke@1: File origZipFileName = zipFileName;
duke@1: if (!ignoreSymbolFile && paths.isBootClassPathRtJar(zipFileName)) {
duke@1: File file = zipFileName.getParentFile().getParentFile(); // ${java.home}
duke@1: if (new File(file.getName()).equals(new File("jre")))
duke@1: file = file.getParentFile();
duke@1: // file == ${jdk.home}
duke@1: for (String name : symbolFileLocation)
duke@1: file = new File(file, name);
duke@1: // file == ${jdk.home}/lib/ct.sym
duke@1: if (file.exists())
duke@1: zipFileName = file;
duke@1: }
duke@1:
duke@1: try {
duke@1:
duke@1: ZipFile zdir = null;
duke@1:
duke@1: boolean usePreindexedCache = false;
duke@1: String preindexCacheLocation = null;
duke@1:
duke@1: if (!useZipFileIndex) {
duke@1: zdir = new ZipFile(zipFileName);
duke@1: }
duke@1: else {
duke@1: usePreindexedCache = options.get("usezipindex") != null;
duke@1: preindexCacheLocation = options.get("java.io.tmpdir");
duke@1: String optCacheLoc = options.get("cachezipindexdir");
duke@1:
duke@1: if (optCacheLoc != null && optCacheLoc.length() != 0) {
duke@1: if (optCacheLoc.startsWith("\"")) {
duke@1: if (optCacheLoc.endsWith("\"")) {
duke@1: optCacheLoc = optCacheLoc.substring(1, optCacheLoc.length() - 1);
duke@1: }
duke@1: else {
duke@1: optCacheLoc = optCacheLoc.substring(1);
duke@1: }
duke@1: }
duke@1:
duke@1: File cacheDir = new File(optCacheLoc);
duke@1: if (cacheDir.exists() && cacheDir.canWrite()) {
duke@1: preindexCacheLocation = optCacheLoc;
duke@1: if (!preindexCacheLocation.endsWith("/") &&
duke@1: !preindexCacheLocation.endsWith(File.separator)) {
duke@1: preindexCacheLocation += File.separator;
duke@1: }
duke@1: }
duke@1: }
duke@1: }
duke@1:
duke@1: if (origZipFileName == zipFileName) {
duke@1: if (!useZipFileIndex) {
jjg@57: archive = new ZipArchive(this, zdir);
duke@1: } else {
jjg@103: archive = new ZipFileIndexArchive(this,
jjg@103: ZipFileIndex.getZipFileIndex(zipFileName,
jjg@103: null,
jjg@103: usePreindexedCache,
jjg@103: preindexCacheLocation,
jjg@103: options.get("writezipindexfiles") != null));
duke@1: }
duke@1: }
duke@1: else {
duke@1: if (!useZipFileIndex) {
jjg@57: archive = new SymbolArchive(this, origZipFileName, zdir, symbolFilePrefix);
duke@1: }
duke@1: else {
jjg@57: archive = new ZipFileIndexArchive(this,
jjg@57: ZipFileIndex.getZipFileIndex(zipFileName,
jjg@103: symbolFilePrefix,
jjg@103: usePreindexedCache,
jjg@103: preindexCacheLocation,
jjg@103: options.get("writezipindexfiles") != null));
duke@1: }
duke@1: }
duke@1: } catch (FileNotFoundException ex) {
duke@1: archive = new MissingArchive(zipFileName);
duke@1: } catch (IOException ex) {
jjg@56: if (zipFileName.exists())
jjg@56: log.error("error.reading.file", zipFileName, ex.getLocalizedMessage());
duke@1: archive = new MissingArchive(zipFileName);
duke@1: }
duke@1:
duke@1: archives.put(origZipFileName, archive);
duke@1: }
duke@1: return archive;
duke@1: }
duke@1:
duke@1: /** Flush any output resources.
duke@1: */
duke@1: public void flush() {
duke@1: contentCache.clear();
duke@1: }
duke@1:
duke@1: /**
duke@1: * Close the JavaFileManager, releasing resources.
duke@1: */
duke@1: public void close() {
duke@1: for (Iterator i = archives.values().iterator(); i.hasNext(); ) {
duke@1: Archive a = i.next();
duke@1: i.remove();
duke@1: try {
duke@1: a.close();
duke@1: } catch (IOException e) {
duke@1: }
duke@1: }
duke@1: }
duke@1:
duke@1: private String defaultEncodingName;
duke@1: private String getDefaultEncodingName() {
duke@1: if (defaultEncodingName == null) {
duke@1: defaultEncodingName =
duke@1: new OutputStreamWriter(new ByteArrayOutputStream()).getEncoding();
duke@1: }
duke@1: return defaultEncodingName;
duke@1: }
duke@1:
duke@1: public ClassLoader getClassLoader(Location location) {
duke@1: nullCheck(location);
duke@1: Iterable extends File> path = getLocation(location);
duke@1: if (path == null)
duke@1: return null;
duke@1: ListBuffer lb = new ListBuffer();
duke@1: for (File f: path) {
duke@1: try {
duke@1: lb.append(f.toURI().toURL());
duke@1: } catch (MalformedURLException e) {
duke@1: throw new AssertionError(e);
duke@1: }
duke@1: }
jjg@372:
jjg@450: return getClassLoader(lb.toArray(new URL[lb.size()]));
duke@1: }
duke@1:
duke@1: public Iterable list(Location location,
duke@1: String packageName,
duke@1: Set kinds,
duke@1: boolean recurse)
duke@1: throws IOException
duke@1: {
duke@1: // validatePackageName(packageName);
duke@1: nullCheck(packageName);
duke@1: nullCheck(kinds);
duke@1:
duke@1: Iterable extends File> path = getLocation(location);
duke@1: if (path == null)
duke@1: return List.nil();
jjg@103: RelativeDirectory subdirectory = RelativeDirectory.forPackage(packageName);
duke@1: ListBuffer results = new ListBuffer();
duke@1:
duke@1: for (File directory : path)
duke@1: listDirectory(directory, subdirectory, kinds, recurse, results);
duke@1:
duke@1: return results.toList();
duke@1: }
duke@1:
duke@1: public String inferBinaryName(Location location, JavaFileObject file) {
duke@1: file.getClass(); // null check
duke@1: location.getClass(); // null check
duke@1: // Need to match the path semantics of list(location, ...)
duke@1: Iterable extends File> path = getLocation(location);
duke@1: if (path == null) {
duke@1: return null;
duke@1: }
duke@1:
jjg@57: if (file instanceof BaseFileObject) {
jjg@57: return ((BaseFileObject) file).inferBinaryName(path);
duke@1: } else
duke@1: throw new IllegalArgumentException(file.getClass().getName());
duke@1: }
duke@1:
duke@1: public boolean isSameFile(FileObject a, FileObject b) {
duke@1: nullCheck(a);
duke@1: nullCheck(b);
duke@1: if (!(a instanceof BaseFileObject))
duke@1: throw new IllegalArgumentException("Not supported: " + a);
duke@1: if (!(b instanceof BaseFileObject))
duke@1: throw new IllegalArgumentException("Not supported: " + b);
duke@1: return a.equals(b);
duke@1: }
duke@1:
duke@1: public boolean hasLocation(Location location) {
duke@1: return getLocation(location) != null;
duke@1: }
duke@1:
duke@1: public JavaFileObject getJavaFileForInput(Location location,
duke@1: String className,
duke@1: JavaFileObject.Kind kind)
duke@1: throws IOException
duke@1: {
duke@1: nullCheck(location);
duke@1: // validateClassName(className);
duke@1: nullCheck(className);
duke@1: nullCheck(kind);
duke@1: if (!sourceOrClass.contains(kind))
duke@1: throw new IllegalArgumentException("Invalid kind " + kind);
jjg@103: return getFileForInput(location, RelativeFile.forClass(className, kind));
duke@1: }
duke@1:
duke@1: public FileObject getFileForInput(Location location,
duke@1: String packageName,
duke@1: String relativeName)
duke@1: throws IOException
duke@1: {
duke@1: nullCheck(location);
duke@1: // validatePackageName(packageName);
duke@1: nullCheck(packageName);
jjg@400: if (!isRelativeUri(relativeName))
duke@1: throw new IllegalArgumentException("Invalid relative name: " + relativeName);
jjg@103: RelativeFile name = packageName.length() == 0
jjg@103: ? new RelativeFile(relativeName)
jjg@103: : new RelativeFile(RelativeDirectory.forPackage(packageName), relativeName);
duke@1: return getFileForInput(location, name);
duke@1: }
duke@1:
jjg@103: private JavaFileObject getFileForInput(Location location, RelativeFile name) throws IOException {
duke@1: Iterable extends File> path = getLocation(location);
duke@1: if (path == null)
duke@1: return null;
duke@1:
duke@1: for (File dir: path) {
duke@1: if (dir.isDirectory()) {
jjg@103: File f = name.getFile(dir);
duke@1: if (f.exists())
jjg@57: return new RegularFileObject(this, f);
duke@1: } else {
duke@1: Archive a = openArchive(dir);
duke@1: if (a.contains(name)) {
jjg@103: return a.getFileObject(name.dirname(), name.basename());
duke@1: }
duke@1:
duke@1: }
duke@1: }
jjg@103:
duke@1: return null;
duke@1: }
duke@1:
duke@1: public JavaFileObject getJavaFileForOutput(Location location,
duke@1: String className,
duke@1: JavaFileObject.Kind kind,
duke@1: FileObject sibling)
duke@1: throws IOException
duke@1: {
duke@1: nullCheck(location);
duke@1: // validateClassName(className);
duke@1: nullCheck(className);
duke@1: nullCheck(kind);
duke@1: if (!sourceOrClass.contains(kind))
duke@1: throw new IllegalArgumentException("Invalid kind " + kind);
jjg@103: return getFileForOutput(location, RelativeFile.forClass(className, kind), sibling);
duke@1: }
duke@1:
duke@1: public FileObject getFileForOutput(Location location,
duke@1: String packageName,
duke@1: String relativeName,
duke@1: FileObject sibling)
duke@1: throws IOException
duke@1: {
duke@1: nullCheck(location);
duke@1: // validatePackageName(packageName);
duke@1: nullCheck(packageName);
jjg@400: if (!isRelativeUri(relativeName))
duke@1: throw new IllegalArgumentException("relativeName is invalid");
jjg@103: RelativeFile name = packageName.length() == 0
jjg@103: ? new RelativeFile(relativeName)
jjg@103: : new RelativeFile(RelativeDirectory.forPackage(packageName), relativeName);
duke@1: return getFileForOutput(location, name, sibling);
duke@1: }
duke@1:
duke@1: private JavaFileObject getFileForOutput(Location location,
jjg@103: RelativeFile fileName,
duke@1: FileObject sibling)
duke@1: throws IOException
duke@1: {
duke@1: File dir;
duke@1: if (location == CLASS_OUTPUT) {
duke@1: if (getClassOutDir() != null) {
duke@1: dir = getClassOutDir();
duke@1: } else {
duke@1: File siblingDir = null;
duke@1: if (sibling != null && sibling instanceof RegularFileObject) {
jjg@424: siblingDir = ((RegularFileObject)sibling).file.getParentFile();
duke@1: }
jjg@103: return new RegularFileObject(this, new File(siblingDir, fileName.basename()));
duke@1: }
duke@1: } else if (location == SOURCE_OUTPUT) {
duke@1: dir = (getSourceOutDir() != null ? getSourceOutDir() : getClassOutDir());
duke@1: } else {
duke@1: Iterable extends File> path = paths.getPathForLocation(location);
duke@1: dir = null;
duke@1: for (File f: path) {
duke@1: dir = f;
duke@1: break;
duke@1: }
duke@1: }
duke@1:
jjg@103: File file = fileName.getFile(dir); // null-safe
jjg@57: return new RegularFileObject(this, file);
duke@1:
duke@1: }
duke@1:
duke@1: public Iterable extends JavaFileObject> getJavaFileObjectsFromFiles(
duke@1: Iterable extends File> files)
duke@1: {
duke@1: ArrayList result;
mcimadamore@184: if (files instanceof Collection>)
mcimadamore@184: result = new ArrayList(((Collection>)files).size());
duke@1: else
duke@1: result = new ArrayList();
duke@1: for (File f: files)
jjg@57: result.add(new RegularFileObject(this, nullCheck(f)));
duke@1: return result;
duke@1: }
duke@1:
duke@1: public Iterable extends JavaFileObject> getJavaFileObjects(File... files) {
duke@1: return getJavaFileObjectsFromFiles(Arrays.asList(nullCheck(files)));
duke@1: }
duke@1:
duke@1: public void setLocation(Location location,
duke@1: Iterable extends File> path)
duke@1: throws IOException
duke@1: {
duke@1: nullCheck(location);
duke@1: paths.lazy();
duke@1:
duke@1: final File dir = location.isOutputLocation() ? getOutputDirectory(path) : null;
duke@1:
duke@1: if (location == CLASS_OUTPUT)
duke@1: classOutDir = getOutputLocation(dir, D);
duke@1: else if (location == SOURCE_OUTPUT)
duke@1: sourceOutDir = getOutputLocation(dir, S);
duke@1: else
duke@1: paths.setPathForLocation(location, path);
duke@1: }
duke@1: // where
duke@1: private File getOutputDirectory(Iterable extends File> path) throws IOException {
duke@1: if (path == null)
duke@1: return null;
duke@1: Iterator extends File> pathIter = path.iterator();
duke@1: if (!pathIter.hasNext())
duke@1: throw new IllegalArgumentException("empty path for directory");
duke@1: File dir = pathIter.next();
duke@1: if (pathIter.hasNext())
duke@1: throw new IllegalArgumentException("path too long for directory");
duke@1: if (!dir.exists())
duke@1: throw new FileNotFoundException(dir + ": does not exist");
duke@1: else if (!dir.isDirectory())
duke@1: throw new IOException(dir + ": not a directory");
duke@1: return dir;
duke@1: }
duke@1:
duke@1: private File getOutputLocation(File dir, OptionName defaultOptionName) {
duke@1: if (dir != null)
duke@1: return dir;
duke@1: String arg = options.get(defaultOptionName);
duke@1: if (arg == null)
duke@1: return null;
duke@1: return new File(arg);
duke@1: }
duke@1:
duke@1: public Iterable extends File> getLocation(Location location) {
duke@1: nullCheck(location);
duke@1: paths.lazy();
duke@1: if (location == CLASS_OUTPUT) {
duke@1: return (getClassOutDir() == null ? null : List.of(getClassOutDir()));
duke@1: } else if (location == SOURCE_OUTPUT) {
duke@1: return (getSourceOutDir() == null ? null : List.of(getSourceOutDir()));
duke@1: } else
duke@1: return paths.getPathForLocation(location);
duke@1: }
duke@1:
duke@1: private File getClassOutDir() {
duke@1: if (classOutDir == uninited)
duke@1: classOutDir = getOutputLocation(null, D);
duke@1: return classOutDir;
duke@1: }
duke@1:
duke@1: private File getSourceOutDir() {
duke@1: if (sourceOutDir == uninited)
duke@1: sourceOutDir = getOutputLocation(null, S);
duke@1: return sourceOutDir;
duke@1: }
duke@1:
duke@1: /**
duke@1: * Enforces the specification of a "relative" URI as used in
duke@1: * {@linkplain #getFileForInput(Location,String,URI)
duke@1: * getFileForInput}. This method must follow the rules defined in
duke@1: * that method, do not make any changes without consulting the
duke@1: * specification.
duke@1: */
duke@1: protected static boolean isRelativeUri(URI uri) {
duke@1: if (uri.isAbsolute())
duke@1: return false;
duke@1: String path = uri.normalize().getPath();
duke@1: if (path.length() == 0 /* isEmpty() is mustang API */)
duke@1: return false;
duke@1: char first = path.charAt(0);
duke@1: return first != '.' && first != '/';
duke@1: }
duke@1:
jjg@400: // Convenience method
jjg@400: protected static boolean isRelativeUri(String u) {
jjg@400: try {
jjg@400: return isRelativeUri(new URI(u));
jjg@400: } catch (URISyntaxException e) {
jjg@400: return false;
jjg@400: }
jjg@400: }
jjg@400:
duke@1: /**
duke@1: * Converts a relative file name to a relative URI. This is
duke@1: * different from File.toURI as this method does not canonicalize
duke@1: * the file before creating the URI. Furthermore, no schema is
duke@1: * used.
duke@1: * @param file a relative file name
duke@1: * @return a relative URI
duke@1: * @throws IllegalArgumentException if the file name is not
duke@1: * relative according to the definition given in {@link
duke@1: * javax.tools.JavaFileManager#getFileForInput}
duke@1: */
duke@1: public static String getRelativeName(File file) {
duke@1: if (!file.isAbsolute()) {
duke@1: String result = file.getPath().replace(File.separatorChar, '/');
jjg@400: if (isRelativeUri(result))
duke@1: return result;
duke@1: }
duke@1: throw new IllegalArgumentException("Invalid relative path: " + file);
duke@1: }
duke@1: }