0001: /*
0002: * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package com.sun.tools.javac.util;
0027:
0028: import com.sun.tools.javac.main.JavacOption;
0029: import com.sun.tools.javac.main.OptionName;
0030: import com.sun.tools.javac.main.RecognizedOptions;
0031: import java.io.ByteArrayOutputStream;
0032: import java.io.File;
0033: import java.io.FileInputStream;
0034: import java.io.FileNotFoundException;
0035: import java.io.FileOutputStream;
0036: import java.io.IOException;
0037: import java.io.InputStream;
0038: import java.io.OutputStream;
0039: import java.io.OutputStreamWriter;
0040: import java.io.Writer;
0041: import java.lang.ref.SoftReference;
0042: import java.net.MalformedURLException;
0043: import java.net.URI;
0044: import java.net.URISyntaxException;
0045: import java.net.URL;
0046: import java.net.URLClassLoader;
0047: import java.nio.ByteBuffer;
0048: import java.nio.CharBuffer;
0049: import java.nio.channels.FileChannel;
0050: import java.nio.charset.Charset;
0051: import java.nio.charset.CharsetDecoder;
0052: import java.nio.charset.CoderResult;
0053: import java.nio.charset.CodingErrorAction;
0054: import java.nio.charset.IllegalCharsetNameException;
0055: import java.nio.charset.UnsupportedCharsetException;
0056: import java.util.ArrayList;
0057: import java.util.Arrays;
0058: import java.util.Collection;
0059: import java.util.Collections;
0060: import java.util.EnumSet;
0061: import java.util.Enumeration;
0062: import java.util.HashMap;
0063: import java.util.Iterator;
0064: import java.util.Map;
0065: import java.util.Set;
0066: import java.util.zip.ZipEntry;
0067: import java.util.zip.ZipFile;
0068:
0069: import javax.lang.model.SourceVersion;
0070: import javax.tools.FileObject;
0071: import javax.tools.JavaFileManager;
0072: import javax.tools.JavaFileObject;
0073:
0074: import com.sun.tools.javac.code.Source;
0075: import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition;
0076: import java.util.concurrent.ConcurrentHashMap;
0077: import javax.tools.StandardJavaFileManager;
0078:
0079: import com.sun.tools.javac.zip.*;
0080: import java.io.ByteArrayInputStream;
0081:
0082: import static com.sun.tools.javac.main.OptionName.*;
0083: import static javax.tools.StandardLocation.*;
0084:
0085: /**
0086: * This class provides access to the source, class and other files
0087: * used by the compiler and related tools.
0088: */
0089: @Version("@(#)JavacFileManager.java 1.48 07/05/05")
0090: public class JavacFileManager implements StandardJavaFileManager {
0091:
0092: private static final String[] symbolFileLocation = { "lib",
0093: "ct.sym" };
0094: private static final String symbolFilePrefix = "META-INF/sym/rt.jar/";
0095:
0096: boolean useZipFileIndex;
0097:
0098: private static int symbolFilePrefixLength = 0;
0099: static {
0100: try {
0101: symbolFilePrefixLength = symbolFilePrefix.getBytes("UTF-8").length;
0102: } catch (java.io.UnsupportedEncodingException uee) {
0103: // Can't happen...UTF-8 is always supported.
0104: }
0105: }
0106:
0107: private static boolean CHECK_ZIP_TIMESTAMP = false;
0108: private static Map<File, Boolean> isDirectory = new ConcurrentHashMap<File, Boolean>();
0109:
0110: public static char[] toArray(CharBuffer buffer) {
0111: if (buffer.hasArray())
0112: return ((CharBuffer) buffer.compact().flip()).array();
0113: else
0114: return buffer.toString().toCharArray();
0115: }
0116:
0117: /**
0118: * The log to be used for error reporting.
0119: */
0120: protected Log log;
0121:
0122: /** Encapsulates knowledge of paths
0123: */
0124: private Paths paths;
0125:
0126: private Options options;
0127:
0128: private final File uninited = new File("U N I N I T E D");
0129:
0130: private final Set<JavaFileObject.Kind> sourceOrClass = EnumSet.of(
0131: JavaFileObject.Kind.SOURCE, JavaFileObject.Kind.CLASS);
0132:
0133: /** The standard output directory, primarily used for classes.
0134: * Initialized by the "-d" option.
0135: * If classOutDir = null, files are written into same directory as the sources
0136: * they were generated from.
0137: */
0138: private File classOutDir = uninited;
0139:
0140: /** The output directory, used when generating sources while processing annotations.
0141: * Initialized by the "-s" option.
0142: */
0143: private File sourceOutDir = uninited;
0144:
0145: protected boolean mmappedIO;
0146: protected boolean ignoreSymbolFile;
0147:
0148: /**
0149: * User provided charset (through javax.tools).
0150: */
0151: protected Charset charset;
0152:
0153: /**
0154: * Register a Context.Factory to create a JavacFileManager.
0155: */
0156: public static void preRegister(final Context context) {
0157: context.put(JavaFileManager.class,
0158: new Context.Factory<JavaFileManager>() {
0159: public JavaFileManager make() {
0160: return new JavacFileManager(context, true, null);
0161: }
0162: });
0163: }
0164:
0165: /**
0166: * Create a JavacFileManager using a given context, optionally registering
0167: * it as the JavaFileManager for that context.
0168: */
0169: public JavacFileManager(Context context, boolean register,
0170: Charset charset) {
0171: if (register)
0172: context.put(JavaFileManager.class, this );
0173: byteBufferCache = new ByteBufferCache();
0174: this .charset = charset;
0175: setContext(context);
0176: }
0177:
0178: /**
0179: * Set the context for JavacFileManager.
0180: */
0181: public void setContext(Context context) {
0182: log = Log.instance(context);
0183: if (paths == null) {
0184: paths = Paths.instance(context);
0185: } else {
0186: // Reuse the Paths object as it stores the locations that
0187: // have been set with setLocation, etc.
0188: paths.setContext(context);
0189: }
0190:
0191: options = Options.instance(context);
0192:
0193: useZipFileIndex = System.getProperty("useJavaUtilZip") == null;// TODO: options.get("useJavaUtilZip") == null;
0194: CHECK_ZIP_TIMESTAMP = System
0195: .getProperty("checkZipIndexTimestamp") != null;// TODO: options.get("checkZipIndexTimestamp") != null;
0196:
0197: mmappedIO = options.get("mmappedIO") != null;
0198: ignoreSymbolFile = options.get("ignore.symbol.file") != null;
0199: }
0200:
0201: public JavaFileObject getFileForInput(String name) {
0202: return getRegularFile(new File(name));
0203: }
0204:
0205: public JavaFileObject getRegularFile(File file) {
0206: return new RegularFileObject(file);
0207: }
0208:
0209: public JavaFileObject getFileForOutput(String classname,
0210: JavaFileObject.Kind kind, JavaFileObject sibling)
0211: throws IOException {
0212: return getJavaFileForOutput(CLASS_OUTPUT, classname, kind,
0213: sibling);
0214: }
0215:
0216: public Iterable<? extends JavaFileObject> getJavaFileObjectsFromStrings(
0217: Iterable<String> names) {
0218: ListBuffer<File> files = new ListBuffer<File>();
0219: for (String name : names)
0220: files.append(new File(nullCheck(name)));
0221: return getJavaFileObjectsFromFiles(files.toList());
0222: }
0223:
0224: public Iterable<? extends JavaFileObject> getJavaFileObjects(
0225: String... names) {
0226: return getJavaFileObjectsFromStrings(Arrays
0227: .asList(nullCheck(names)));
0228: }
0229:
0230: protected JavaFileObject.Kind getKind(String extension) {
0231: if (extension.equals(JavaFileObject.Kind.CLASS.extension))
0232: return JavaFileObject.Kind.CLASS;
0233: else if (extension.equals(JavaFileObject.Kind.SOURCE.extension))
0234: return JavaFileObject.Kind.SOURCE;
0235: else if (extension.equals(JavaFileObject.Kind.HTML.extension))
0236: return JavaFileObject.Kind.HTML;
0237: else
0238: return JavaFileObject.Kind.OTHER;
0239: }
0240:
0241: private static boolean isValidName(String name) {
0242: // Arguably, isValidName should reject keywords (such as in SourceVersion.isName() ),
0243: // but the set of keywords depends on the source level, and we don't want
0244: // impls of JavaFileManager to have to be dependent on the source level.
0245: // Therefore we simply check that the argument is a sequence of identifiers
0246: // separated by ".".
0247: for (String s : name.split("\\.", -1)) {
0248: if (!SourceVersion.isIdentifier(s))
0249: return false;
0250: }
0251: return true;
0252: }
0253:
0254: private static void validateClassName(String className) {
0255: if (!isValidName(className))
0256: throw new IllegalArgumentException("Invalid class name: "
0257: + className);
0258: }
0259:
0260: private static void validatePackageName(String packageName) {
0261: if (packageName.length() > 0 && !isValidName(packageName))
0262: throw new IllegalArgumentException(
0263: "Invalid packageName name: " + packageName);
0264: }
0265:
0266: public static void testName(String name,
0267: boolean isValidPackageName, boolean isValidClassName) {
0268: try {
0269: validatePackageName(name);
0270: if (!isValidPackageName)
0271: throw new AssertionError(
0272: "Invalid package name accepted: " + name);
0273: printAscii("Valid package name: \"%s\"", name);
0274: } catch (IllegalArgumentException e) {
0275: if (isValidPackageName)
0276: throw new AssertionError(
0277: "Valid package name rejected: " + name);
0278: printAscii("Invalid package name: \"%s\"", name);
0279: }
0280: try {
0281: validateClassName(name);
0282: if (!isValidClassName)
0283: throw new AssertionError(
0284: "Invalid class name accepted: " + name);
0285: printAscii("Valid class name: \"%s\"", name);
0286: } catch (IllegalArgumentException e) {
0287: if (isValidClassName)
0288: throw new AssertionError("Valid class name rejected: "
0289: + name);
0290: printAscii("Invalid class name: \"%s\"", name);
0291: }
0292: }
0293:
0294: private static void printAscii(String format, Object... args) {
0295: String message;
0296: try {
0297: final String ascii = "US-ASCII";
0298: message = new String(String.format(null, format, args)
0299: .getBytes(ascii), ascii);
0300: } catch (java.io.UnsupportedEncodingException ex) {
0301: throw new AssertionError(ex);
0302: }
0303: System.out.println(message);
0304: }
0305:
0306: /** Return external representation of name,
0307: * converting '.' to File.separatorChar.
0308: */
0309: private static String externalizeFileName(CharSequence name) {
0310: return name.toString().replace('.', File.separatorChar);
0311: }
0312:
0313: private static String externalizeFileName(CharSequence n,
0314: JavaFileObject.Kind kind) {
0315: return externalizeFileName(n) + kind.extension;
0316: }
0317:
0318: private static String baseName(String fileName) {
0319: return fileName.substring(fileName
0320: .lastIndexOf(File.separatorChar) + 1);
0321: }
0322:
0323: /**
0324: * Insert all files in subdirectory `subdirectory' of `directory' which end
0325: * in one of the extensions in `extensions' into packageSym.
0326: */
0327: private void listDirectory(File directory, String subdirectory,
0328: Set<JavaFileObject.Kind> fileKinds, boolean recurse,
0329: ListBuffer<JavaFileObject> l) {
0330: Archive archive = archives.get(directory);
0331:
0332: boolean isFile = false;
0333: if (CHECK_ZIP_TIMESTAMP) {
0334: Boolean isf = isDirectory.get(directory);
0335: if (isf == null) {
0336: isFile = directory.isFile();
0337: isDirectory.put(directory, isFile);
0338: } else {
0339: isFile = directory.isFile();
0340: }
0341: } else {
0342: isFile = directory.isFile();
0343: }
0344:
0345: if (archive != null || isFile) {
0346: if (archive == null) {
0347: try {
0348: archive = openArchive(directory);
0349: } catch (IOException ex) {
0350: log.error("error.reading.file", directory, ex
0351: .getLocalizedMessage());
0352: return;
0353: }
0354: }
0355: if (subdirectory.length() != 0) {
0356: if (!useZipFileIndex) {
0357: subdirectory = subdirectory.replace('\\', '/');
0358: if (!subdirectory.endsWith("/"))
0359: subdirectory = subdirectory + "/";
0360: } else {
0361: if (File.separatorChar == '/') {
0362: subdirectory = subdirectory.replace('\\', '/');
0363: } else {
0364: subdirectory = subdirectory.replace('/', '\\');
0365: }
0366:
0367: if (!subdirectory.endsWith(File.separator))
0368: subdirectory = subdirectory + File.separator;
0369: }
0370: }
0371:
0372: List<String> files = archive.getFiles(subdirectory);
0373: if (files != null) {
0374: for (String file; !files.isEmpty(); files = files.tail) {
0375: file = files.head;
0376: if (isValidFile(file, fileKinds)) {
0377: l.append(archive.getFileObject(subdirectory,
0378: file));
0379: }
0380: }
0381: }
0382: if (recurse) {
0383: for (String s : archive.getSubdirectories()) {
0384: if (s.startsWith(subdirectory)
0385: && !s.equals(subdirectory)) {
0386: // Because the archive map is a flat list of directories,
0387: // the enclosing loop will pick up all child subdirectories.
0388: // Therefore, there is no need to recurse deeper.
0389: listDirectory(directory, s, fileKinds, false, l);
0390: }
0391: }
0392: }
0393: } else {
0394: File d = subdirectory.length() != 0 ? new File(directory,
0395: subdirectory) : directory;
0396: if (!caseMapCheck(d, subdirectory))
0397: return;
0398:
0399: File[] files = d.listFiles();
0400: if (files == null)
0401: return;
0402:
0403: for (File f : files) {
0404: String fname = f.getName();
0405: if (f.isDirectory()) {
0406: if (recurse && SourceVersion.isIdentifier(fname)) {
0407: listDirectory(directory, subdirectory
0408: + File.separator + fname, fileKinds,
0409: recurse, l);
0410: }
0411: } else {
0412: if (isValidFile(fname, fileKinds)) {
0413: JavaFileObject fe = new RegularFileObject(
0414: fname, new File(d, fname));
0415: l.append(fe);
0416: }
0417: }
0418: }
0419: }
0420: }
0421:
0422: private boolean isValidFile(String s,
0423: Set<JavaFileObject.Kind> fileKinds) {
0424: int lastDot = s.lastIndexOf(".");
0425: String extn = (lastDot == -1 ? s : s.substring(lastDot));
0426: JavaFileObject.Kind kind = getKind(extn);
0427: return fileKinds.contains(kind);
0428: }
0429:
0430: private static final boolean fileSystemIsCaseSensitive = File.separatorChar == '/';
0431:
0432: /** Hack to make Windows case sensitive. Test whether given path
0433: * ends in a string of characters with the same case as given name.
0434: * Ignore file separators in both path and name.
0435: */
0436: private boolean caseMapCheck(File f, String name) {
0437: if (fileSystemIsCaseSensitive)
0438: return true;
0439: // Note that getCanonicalPath() returns the case-sensitive
0440: // spelled file name.
0441: String path;
0442: try {
0443: path = f.getCanonicalPath();
0444: } catch (IOException ex) {
0445: return false;
0446: }
0447: char[] pcs = path.toCharArray();
0448: char[] ncs = name.toCharArray();
0449: int i = pcs.length - 1;
0450: int j = ncs.length - 1;
0451: while (i >= 0 && j >= 0) {
0452: while (i >= 0 && pcs[i] == File.separatorChar)
0453: i--;
0454: while (j >= 0 && ncs[j] == File.separatorChar)
0455: j--;
0456: if (i >= 0 && j >= 0) {
0457: if (pcs[i] != ncs[j])
0458: return false;
0459: i--;
0460: j--;
0461: }
0462: }
0463: return j < 0;
0464: }
0465:
0466: /**
0467: * An archive provides a flat directory structure of a ZipFile by
0468: * mapping directory names to lists of files (basenames).
0469: */
0470: public interface Archive {
0471: void close() throws IOException;
0472:
0473: boolean contains(String name);
0474:
0475: JavaFileObject getFileObject(String subdirectory, String file);
0476:
0477: List<String> getFiles(String subdirectory);
0478:
0479: Set<String> getSubdirectories();
0480: }
0481:
0482: public class ZipArchive implements Archive {
0483: protected final Map<String, List<String>> map;
0484: protected final ZipFile zdir;
0485:
0486: public ZipArchive(ZipFile zdir) throws IOException {
0487: this .zdir = zdir;
0488: this .map = new HashMap<String, List<String>>();
0489: for (Enumeration<? extends ZipEntry> e = zdir.entries(); e
0490: .hasMoreElements();) {
0491: ZipEntry entry;
0492: try {
0493: entry = e.nextElement();
0494: } catch (InternalError ex) {
0495: IOException io = new IOException();
0496: io.initCause(ex); // convenience constructors added in Mustang :-(
0497: throw io;
0498: }
0499: addZipEntry(entry);
0500: }
0501: }
0502:
0503: void addZipEntry(ZipEntry entry) {
0504: String name = entry.getName();
0505: int i = name.lastIndexOf('/');
0506: String dirname = name.substring(0, i + 1);
0507: String basename = name.substring(i + 1);
0508: if (basename.length() == 0)
0509: return;
0510: List<String> list = map.get(dirname);
0511: if (list == null)
0512: list = List.nil();
0513: list = list.prepend(basename);
0514: map.put(dirname, list);
0515: }
0516:
0517: public boolean contains(String name) {
0518: int i = name.lastIndexOf('/');
0519: String dirname = name.substring(0, i + 1);
0520: String basename = name.substring(i + 1);
0521: if (basename.length() == 0)
0522: return false;
0523: List<String> list = map.get(dirname);
0524: return (list != null && list.contains(basename));
0525: }
0526:
0527: public List<String> getFiles(String subdirectory) {
0528: return map.get(subdirectory);
0529: }
0530:
0531: public JavaFileObject getFileObject(String subdirectory,
0532: String file) {
0533: ZipEntry ze = zdir.getEntry(subdirectory + file);
0534: return new ZipFileObject(file, zdir, ze);
0535: }
0536:
0537: public Set<String> getSubdirectories() {
0538: return map.keySet();
0539: }
0540:
0541: public void close() throws IOException {
0542: zdir.close();
0543: }
0544: }
0545:
0546: public class SymbolArchive extends ZipArchive {
0547: final File origFile;
0548:
0549: public SymbolArchive(File orig, ZipFile zdir)
0550: throws IOException {
0551: super (zdir);
0552: this .origFile = orig;
0553: }
0554:
0555: @Override
0556: void addZipEntry(ZipEntry entry) {
0557: // called from super constructor, may not refer to origFile.
0558: String name = entry.getName();
0559: if (!name.startsWith(symbolFilePrefix))
0560: return;
0561: name = name.substring(symbolFilePrefix.length());
0562: int i = name.lastIndexOf('/');
0563: String dirname = name.substring(0, i + 1);
0564: String basename = name.substring(i + 1);
0565: if (basename.length() == 0)
0566: return;
0567: List<String> list = map.get(dirname);
0568: if (list == null)
0569: list = List.nil();
0570: list = list.prepend(basename);
0571: map.put(dirname, list);
0572: }
0573:
0574: @Override
0575: public JavaFileObject getFileObject(String subdirectory,
0576: String file) {
0577: return super .getFileObject(symbolFilePrefix + subdirectory,
0578: file);
0579: }
0580: }
0581:
0582: public class MissingArchive implements Archive {
0583: final File zipFileName;
0584:
0585: public MissingArchive(File name) {
0586: zipFileName = name;
0587: }
0588:
0589: public boolean contains(String name) {
0590: return false;
0591: }
0592:
0593: public void close() {
0594: }
0595:
0596: public JavaFileObject getFileObject(String subdirectory,
0597: String file) {
0598: return null;
0599: }
0600:
0601: public List<String> getFiles(String subdirectory) {
0602: return List.nil();
0603: }
0604:
0605: public Set<String> getSubdirectories() {
0606: return Collections.emptySet();
0607: }
0608: }
0609:
0610: /** A directory of zip files already opened.
0611: */
0612: Map<File, Archive> archives = new HashMap<File, Archive>();
0613:
0614: /** Open a new zip file directory.
0615: */
0616: protected Archive openArchive(File zipFileName) throws IOException {
0617: Archive archive = archives.get(zipFileName);
0618: if (archive == null) {
0619: File origZipFileName = zipFileName;
0620: if (!ignoreSymbolFile
0621: && paths.isBootClassPathRtJar(zipFileName)) {
0622: File file = zipFileName.getParentFile().getParentFile(); // ${java.home}
0623: if (new File(file.getName()).equals(new File("jre")))
0624: file = file.getParentFile();
0625: // file == ${jdk.home}
0626: for (String name : symbolFileLocation)
0627: file = new File(file, name);
0628: // file == ${jdk.home}/lib/ct.sym
0629: if (file.exists())
0630: zipFileName = file;
0631: }
0632:
0633: try {
0634:
0635: ZipFile zdir = null;
0636:
0637: boolean usePreindexedCache = false;
0638: String preindexCacheLocation = null;
0639:
0640: if (!useZipFileIndex) {
0641: zdir = new ZipFile(zipFileName);
0642: } else {
0643: usePreindexedCache = options.get("usezipindex") != null;
0644: preindexCacheLocation = options
0645: .get("java.io.tmpdir");
0646: String optCacheLoc = options
0647: .get("cachezipindexdir");
0648:
0649: if (optCacheLoc != null
0650: && optCacheLoc.length() != 0) {
0651: if (optCacheLoc.startsWith("\"")) {
0652: if (optCacheLoc.endsWith("\"")) {
0653: optCacheLoc = optCacheLoc.substring(1,
0654: optCacheLoc.length() - 1);
0655: } else {
0656: optCacheLoc = optCacheLoc.substring(1);
0657: }
0658: }
0659:
0660: File cacheDir = new File(optCacheLoc);
0661: if (cacheDir.exists() && cacheDir.canWrite()) {
0662: preindexCacheLocation = optCacheLoc;
0663: if (!preindexCacheLocation.endsWith("/")
0664: && !preindexCacheLocation
0665: .endsWith(File.separator)) {
0666: preindexCacheLocation += File.separator;
0667: }
0668: }
0669: }
0670: }
0671:
0672: if (origZipFileName == zipFileName) {
0673: if (!useZipFileIndex) {
0674: archive = new ZipArchive(zdir);
0675: } else {
0676: archive = new ZipFileIndexArchive(
0677: this ,
0678: ZipFileIndex
0679: .getZipFileIndex(
0680: zipFileName,
0681: 0,
0682: usePreindexedCache,
0683: preindexCacheLocation,
0684: options
0685: .get("writezipindexfiles") != null));
0686: }
0687: } else {
0688: if (!useZipFileIndex) {
0689: archive = new SymbolArchive(origZipFileName,
0690: zdir);
0691: } else {
0692: archive = new ZipFileIndexArchive(
0693: this ,
0694: ZipFileIndex
0695: .getZipFileIndex(
0696: zipFileName,
0697: symbolFilePrefixLength,
0698: usePreindexedCache,
0699: preindexCacheLocation,
0700: options
0701: .get("writezipindexfiles") != null));
0702: }
0703: }
0704: } catch (FileNotFoundException ex) {
0705: archive = new MissingArchive(zipFileName);
0706: } catch (IOException ex) {
0707: log.error("error.reading.file", zipFileName, ex
0708: .getLocalizedMessage());
0709: archive = new MissingArchive(zipFileName);
0710: }
0711:
0712: archives.put(origZipFileName, archive);
0713: }
0714: return archive;
0715: }
0716:
0717: /** Flush any output resources.
0718: */
0719: public void flush() {
0720: contentCache.clear();
0721: }
0722:
0723: /**
0724: * Close the JavaFileManager, releasing resources.
0725: */
0726: public void close() {
0727: for (Iterator<Archive> i = archives.values().iterator(); i
0728: .hasNext();) {
0729: Archive a = i.next();
0730: i.remove();
0731: try {
0732: a.close();
0733: } catch (IOException e) {
0734: }
0735: }
0736: }
0737:
0738: private Map<JavaFileObject, SoftReference<CharBuffer>> contentCache = new HashMap<JavaFileObject, SoftReference<CharBuffer>>();
0739:
0740: private String defaultEncodingName;
0741:
0742: private String getDefaultEncodingName() {
0743: if (defaultEncodingName == null) {
0744: defaultEncodingName = new OutputStreamWriter(
0745: new ByteArrayOutputStream()).getEncoding();
0746: }
0747: return defaultEncodingName;
0748: }
0749:
0750: protected String getEncodingName() {
0751: String encName = options.get(OptionName.ENCODING);
0752: if (encName == null)
0753: return getDefaultEncodingName();
0754: else
0755: return encName;
0756: }
0757:
0758: protected Source getSource() {
0759: String sourceName = options.get(OptionName.SOURCE);
0760: Source source = null;
0761: if (sourceName != null)
0762: source = Source.lookup(sourceName);
0763: return (source != null ? source : Source.DEFAULT);
0764: }
0765:
0766: /**
0767: * Make a byte buffer from an input stream.
0768: */
0769: private ByteBuffer makeByteBuffer(InputStream in)
0770: throws IOException {
0771: int limit = in.available();
0772: if (mmappedIO && in instanceof FileInputStream) {
0773: // Experimental memory mapped I/O
0774: FileInputStream fin = (FileInputStream) in;
0775: return fin.getChannel().map(FileChannel.MapMode.READ_ONLY,
0776: 0, limit);
0777: }
0778: if (limit < 1024)
0779: limit = 1024;
0780: ByteBuffer result = byteBufferCache.get(limit);
0781: int position = 0;
0782: while (in.available() != 0) {
0783: if (position >= limit)
0784: // expand buffer
0785: result = ByteBuffer.allocate(limit <<= 1).put(
0786: (ByteBuffer) result.flip());
0787: int count = in.read(result.array(), position, limit
0788: - position);
0789: if (count < 0)
0790: break;
0791: result.position(position += count);
0792: }
0793: return (ByteBuffer) result.flip();
0794: }
0795:
0796: /**
0797: * A single-element cache of direct byte buffers.
0798: */
0799: private static class ByteBufferCache {
0800: private ByteBuffer cached;
0801:
0802: ByteBuffer get(int capacity) {
0803: if (capacity < 20480)
0804: capacity = 20480;
0805: ByteBuffer result = (cached != null && cached.capacity() >= capacity) ? (ByteBuffer) cached
0806: .clear()
0807: : ByteBuffer.allocate(capacity + capacity >> 1);
0808: cached = null;
0809: return result;
0810: }
0811:
0812: void put(ByteBuffer x) {
0813: cached = x;
0814: }
0815: }
0816:
0817: private final ByteBufferCache byteBufferCache;
0818:
0819: private CharsetDecoder getDecoder(String encodingName,
0820: boolean ignoreEncodingErrors) {
0821: Charset charset = (this .charset == null) ? Charset
0822: .forName(encodingName) : this .charset;
0823: CharsetDecoder decoder = charset.newDecoder();
0824:
0825: CodingErrorAction action;
0826: if (ignoreEncodingErrors)
0827: action = CodingErrorAction.REPLACE;
0828: else
0829: action = CodingErrorAction.REPORT;
0830:
0831: return decoder.onMalformedInput(action).onUnmappableCharacter(
0832: action);
0833: }
0834:
0835: /**
0836: * Decode a ByteBuffer into a CharBuffer.
0837: */
0838: private CharBuffer decode(ByteBuffer inbuf,
0839: boolean ignoreEncodingErrors) {
0840: String encodingName = getEncodingName();
0841: CharsetDecoder decoder;
0842: try {
0843: decoder = getDecoder(encodingName, ignoreEncodingErrors);
0844: } catch (IllegalCharsetNameException e) {
0845: log.error("unsupported.encoding", encodingName);
0846: return (CharBuffer) CharBuffer.allocate(1).flip();
0847: } catch (UnsupportedCharsetException e) {
0848: log.error("unsupported.encoding", encodingName);
0849: return (CharBuffer) CharBuffer.allocate(1).flip();
0850: }
0851:
0852: // slightly overestimate the buffer size to avoid reallocation.
0853: float factor = decoder.averageCharsPerByte() * 0.8f
0854: + decoder.maxCharsPerByte() * 0.2f;
0855: CharBuffer dest = CharBuffer.allocate(10 + (int) (inbuf
0856: .remaining() * factor));
0857:
0858: while (true) {
0859: CoderResult result = decoder.decode(inbuf, dest, true);
0860: dest.flip();
0861:
0862: if (result.isUnderflow()) { // done reading
0863: // make sure there is at least one extra character
0864: if (dest.limit() == dest.capacity()) {
0865: dest = CharBuffer.allocate(dest.capacity() + 1)
0866: .put(dest);
0867: dest.flip();
0868: }
0869: return dest;
0870: } else if (result.isOverflow()) { // buffer too small; expand
0871: int newCapacity = 10
0872: + dest.capacity()
0873: + (int) (inbuf.remaining() * decoder
0874: .maxCharsPerByte());
0875: dest = CharBuffer.allocate(newCapacity).put(dest);
0876: } else if (result.isMalformed() || result.isUnmappable()) {
0877: // bad character in input
0878:
0879: // report coding error (warn only pre 1.5)
0880: if (!getSource().allowEncodingErrors()) {
0881: log.error(
0882: new SimpleDiagnosticPosition(dest.limit()),
0883: "illegal.char.for.encoding",
0884: charset == null ? encodingName : charset
0885: .name());
0886: } else {
0887: log.warning(new SimpleDiagnosticPosition(dest
0888: .limit()), "illegal.char.for.encoding",
0889: charset == null ? encodingName : charset
0890: .name());
0891: }
0892:
0893: // skip past the coding error
0894: inbuf.position(inbuf.position() + result.length());
0895:
0896: // undo the flip() to prepare the output buffer
0897: // for more translation
0898: dest.position(dest.limit());
0899: dest.limit(dest.capacity());
0900: dest.put((char) 0xfffd); // backward compatible
0901: } else {
0902: throw new AssertionError(result);
0903: }
0904: }
0905: // unreached
0906: }
0907:
0908: public ClassLoader getClassLoader(Location location) {
0909: nullCheck(location);
0910: Iterable<? extends File> path = getLocation(location);
0911: if (path == null)
0912: return null;
0913: ListBuffer<URL> lb = new ListBuffer<URL>();
0914: for (File f : path) {
0915: try {
0916: lb.append(f.toURI().toURL());
0917: } catch (MalformedURLException e) {
0918: throw new AssertionError(e);
0919: }
0920: }
0921: return new URLClassLoader(lb.toArray(new URL[lb.size()]),
0922: getClass().getClassLoader());
0923: }
0924:
0925: public Iterable<JavaFileObject> list(Location location,
0926: String packageName, Set<JavaFileObject.Kind> kinds,
0927: boolean recurse) throws IOException {
0928: // validatePackageName(packageName);
0929: nullCheck(packageName);
0930: nullCheck(kinds);
0931:
0932: Iterable<? extends File> path = getLocation(location);
0933: if (path == null)
0934: return List.nil();
0935: String subdirectory = externalizeFileName(packageName);
0936: ListBuffer<JavaFileObject> results = new ListBuffer<JavaFileObject>();
0937:
0938: for (File directory : path)
0939: listDirectory(directory, subdirectory, kinds, recurse,
0940: results);
0941:
0942: return results.toList();
0943: }
0944:
0945: public String inferBinaryName(Location location, JavaFileObject file) {
0946: file.getClass(); // null check
0947: location.getClass(); // null check
0948: // Need to match the path semantics of list(location, ...)
0949: Iterable<? extends File> path = getLocation(location);
0950: if (path == null) {
0951: //System.err.println("Path for " + location + " is null");
0952: return null;
0953: }
0954: //System.err.println("Path for " + location + " is " + path);
0955:
0956: if (file instanceof RegularFileObject) {
0957: RegularFileObject r = (RegularFileObject) file;
0958: String rPath = r.getPath();
0959: //System.err.println("RegularFileObject " + file + " " +r.getPath());
0960: for (File dir : path) {
0961: //System.err.println("dir: " + dir);
0962: String dPath = dir.getPath();
0963: if (!dPath.endsWith(File.separator))
0964: dPath += File.separator;
0965: if (rPath.regionMatches(true, 0, dPath, 0, dPath
0966: .length())
0967: && new File(rPath.substring(0, dPath.length()))
0968: .equals(new File(dPath))) {
0969: String relativeName = rPath.substring(dPath
0970: .length());
0971: return removeExtension(relativeName).replace(
0972: File.separatorChar, '.');
0973: }
0974: }
0975: } else if (file instanceof ZipFileObject) {
0976: ZipFileObject z = (ZipFileObject) file;
0977: String entryName = z.getZipEntryName();
0978: if (entryName.startsWith(symbolFilePrefix))
0979: entryName = entryName.substring(symbolFilePrefix
0980: .length());
0981: return removeExtension(entryName).replace('/', '.');
0982: } else if (file instanceof ZipFileIndexFileObject) {
0983: ZipFileIndexFileObject z = (ZipFileIndexFileObject) file;
0984: String entryName = z.getZipEntryName();
0985: if (entryName.startsWith(symbolFilePrefix))
0986: entryName = entryName.substring(symbolFilePrefix
0987: .length());
0988: return removeExtension(entryName).replace(
0989: File.separatorChar, '.');
0990: } else
0991: throw new IllegalArgumentException(file.getClass()
0992: .getName());
0993: // System.err.println("inferBinaryName failed for " + file);
0994: return null;
0995: }
0996:
0997: // where
0998: private static String removeExtension(String fileName) {
0999: int lastDot = fileName.lastIndexOf(".");
1000: return (lastDot == -1 ? fileName : fileName.substring(0,
1001: lastDot));
1002: }
1003:
1004: public boolean isSameFile(FileObject a, FileObject b) {
1005: nullCheck(a);
1006: nullCheck(b);
1007: if (!(a instanceof BaseFileObject))
1008: throw new IllegalArgumentException("Not supported: " + a);
1009: if (!(b instanceof BaseFileObject))
1010: throw new IllegalArgumentException("Not supported: " + b);
1011: return a.equals(b);
1012: }
1013:
1014: public boolean handleOption(String current,
1015: Iterator<String> remaining) {
1016: for (JavacOption o : javacFileManagerOptions) {
1017: if (o.matches(current)) {
1018: if (o.hasArg()) {
1019: if (remaining.hasNext()) {
1020: if (!o.process(options, current, remaining
1021: .next()))
1022: return true;
1023: }
1024: } else {
1025: if (!o.process(options, current))
1026: return true;
1027: }
1028: // operand missing, or process returned false
1029: throw new IllegalArgumentException(current);
1030: }
1031: }
1032:
1033: return false;
1034: }
1035:
1036: // where
1037: private static JavacOption[] javacFileManagerOptions = RecognizedOptions
1038: .getJavacFileManagerOptions(new RecognizedOptions.GrumpyHelper());
1039:
1040: public int isSupportedOption(String option) {
1041: for (JavacOption o : javacFileManagerOptions) {
1042: if (o.matches(option))
1043: return o.hasArg() ? 1 : 0;
1044: }
1045: return -1;
1046: }
1047:
1048: public boolean hasLocation(Location location) {
1049: return getLocation(location) != null;
1050: }
1051:
1052: public JavaFileObject getJavaFileForInput(Location location,
1053: String className, JavaFileObject.Kind kind)
1054: throws IOException {
1055: nullCheck(location);
1056: // validateClassName(className);
1057: nullCheck(className);
1058: nullCheck(kind);
1059: if (!sourceOrClass.contains(kind))
1060: throw new IllegalArgumentException("Invalid kind " + kind);
1061: return getFileForInput(location, externalizeFileName(className,
1062: kind));
1063: }
1064:
1065: public FileObject getFileForInput(Location location,
1066: String packageName, String relativeName) throws IOException {
1067: nullCheck(location);
1068: // validatePackageName(packageName);
1069: nullCheck(packageName);
1070: if (!isRelativeUri(URI.create(relativeName))) // FIXME 6419701
1071: throw new IllegalArgumentException(
1072: "Invalid relative name: " + relativeName);
1073: String name = packageName.length() == 0 ? relativeName
1074: : new File(externalizeFileName(packageName),
1075: relativeName).getPath();
1076: return getFileForInput(location, name);
1077: }
1078:
1079: private JavaFileObject getFileForInput(Location location,
1080: String name) throws IOException {
1081: Iterable<? extends File> path = getLocation(location);
1082: if (path == null)
1083: return null;
1084:
1085: for (File dir : path) {
1086: if (dir.isDirectory()) {
1087: File f = new File(dir, name.replace('/',
1088: File.separatorChar));
1089: if (f.exists())
1090: return new RegularFileObject(f);
1091: } else {
1092: Archive a = openArchive(dir);
1093: if (a.contains(name)) {
1094: int i = name.lastIndexOf('/');
1095: String dirname = name.substring(0, i + 1);
1096: String basename = name.substring(i + 1);
1097: return a.getFileObject(dirname, basename);
1098: }
1099:
1100: }
1101: }
1102: return null;
1103:
1104: }
1105:
1106: public JavaFileObject getJavaFileForOutput(Location location,
1107: String className, JavaFileObject.Kind kind,
1108: FileObject sibling) throws IOException {
1109: nullCheck(location);
1110: // validateClassName(className);
1111: nullCheck(className);
1112: nullCheck(kind);
1113: if (!sourceOrClass.contains(kind))
1114: throw new IllegalArgumentException("Invalid kind " + kind);
1115: return getFileForOutput(location, externalizeFileName(
1116: className, kind), sibling);
1117: }
1118:
1119: public FileObject getFileForOutput(Location location,
1120: String packageName, String relativeName, FileObject sibling)
1121: throws IOException {
1122: nullCheck(location);
1123: // validatePackageName(packageName);
1124: nullCheck(packageName);
1125: if (!isRelativeUri(URI.create(relativeName))) // FIXME 6419701
1126: throw new IllegalArgumentException(
1127: "relativeName is invalid");
1128: String name = packageName.length() == 0 ? relativeName
1129: : new File(externalizeFileName(packageName),
1130: relativeName).getPath();
1131: return getFileForOutput(location, name, sibling);
1132: }
1133:
1134: private JavaFileObject getFileForOutput(Location location,
1135: String fileName, FileObject sibling) throws IOException {
1136: File dir;
1137: if (location == CLASS_OUTPUT) {
1138: if (getClassOutDir() != null) {
1139: dir = getClassOutDir();
1140: } else {
1141: File siblingDir = null;
1142: if (sibling != null
1143: && sibling instanceof RegularFileObject) {
1144: siblingDir = ((RegularFileObject) sibling).f
1145: .getParentFile();
1146: }
1147: return new RegularFileObject(new File(siblingDir,
1148: baseName(fileName)));
1149: }
1150: } else if (location == SOURCE_OUTPUT) {
1151: dir = (getSourceOutDir() != null ? getSourceOutDir()
1152: : getClassOutDir());
1153: } else {
1154: Iterable<? extends File> path = paths
1155: .getPathForLocation(location);
1156: dir = null;
1157: for (File f : path) {
1158: dir = f;
1159: break;
1160: }
1161: }
1162:
1163: File file = (dir == null ? new File(fileName) : new File(dir,
1164: fileName));
1165: return new RegularFileObject(file);
1166:
1167: }
1168:
1169: public Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles(
1170: Iterable<? extends File> files) {
1171: ArrayList<RegularFileObject> result;
1172: if (files instanceof Collection)
1173: result = new ArrayList<RegularFileObject>(
1174: ((Collection) files).size());
1175: else
1176: result = new ArrayList<RegularFileObject>();
1177: for (File f : files)
1178: result.add(new RegularFileObject(nullCheck(f)));
1179: return result;
1180: }
1181:
1182: public Iterable<? extends JavaFileObject> getJavaFileObjects(
1183: File... files) {
1184: return getJavaFileObjectsFromFiles(Arrays
1185: .asList(nullCheck(files)));
1186: }
1187:
1188: public void setLocation(Location location,
1189: Iterable<? extends File> path) throws IOException {
1190: nullCheck(location);
1191: paths.lazy();
1192:
1193: final File dir = location.isOutputLocation() ? getOutputDirectory(path)
1194: : null;
1195:
1196: if (location == CLASS_OUTPUT)
1197: classOutDir = getOutputLocation(dir, D);
1198: else if (location == SOURCE_OUTPUT)
1199: sourceOutDir = getOutputLocation(dir, S);
1200: else
1201: paths.setPathForLocation(location, path);
1202: }
1203:
1204: // where
1205: private File getOutputDirectory(Iterable<? extends File> path)
1206: throws IOException {
1207: if (path == null)
1208: return null;
1209: Iterator<? extends File> pathIter = path.iterator();
1210: if (!pathIter.hasNext())
1211: throw new IllegalArgumentException(
1212: "empty path for directory");
1213: File dir = pathIter.next();
1214: if (pathIter.hasNext())
1215: throw new IllegalArgumentException(
1216: "path too long for directory");
1217: if (!dir.exists())
1218: throw new FileNotFoundException(dir + ": does not exist");
1219: else if (!dir.isDirectory())
1220: throw new IOException(dir + ": not a directory");
1221: return dir;
1222: }
1223:
1224: private File getOutputLocation(File dir,
1225: OptionName defaultOptionName) {
1226: if (dir != null)
1227: return dir;
1228: String arg = options.get(defaultOptionName);
1229: if (arg == null)
1230: return null;
1231: return new File(arg);
1232: }
1233:
1234: public Iterable<? extends File> getLocation(Location location) {
1235: nullCheck(location);
1236: paths.lazy();
1237: if (location == CLASS_OUTPUT) {
1238: return (getClassOutDir() == null ? null : List
1239: .of(getClassOutDir()));
1240: } else if (location == SOURCE_OUTPUT) {
1241: return (getSourceOutDir() == null ? null : List
1242: .of(getSourceOutDir()));
1243: } else
1244: return paths.getPathForLocation(location);
1245: }
1246:
1247: private File getClassOutDir() {
1248: if (classOutDir == uninited)
1249: classOutDir = getOutputLocation(null, D);
1250: return classOutDir;
1251: }
1252:
1253: private File getSourceOutDir() {
1254: if (sourceOutDir == uninited)
1255: sourceOutDir = getOutputLocation(null, S);
1256: return sourceOutDir;
1257: }
1258:
1259: /**
1260: * Enforces the specification of a "relative" URI as used in
1261: * {@linkplain #getFileForInput(Location,String,URI)
1262: * getFileForInput}. This method must follow the rules defined in
1263: * that method, do not make any changes without consulting the
1264: * specification.
1265: */
1266: protected static boolean isRelativeUri(URI uri) {
1267: if (uri.isAbsolute())
1268: return false;
1269: String path = uri.normalize().getPath();
1270: if (path.length() == 0 /* isEmpty() is mustang API */)
1271: return false;
1272: char first = path.charAt(0);
1273: return first != '.' && first != '/';
1274: }
1275:
1276: /**
1277: * Converts a relative file name to a relative URI. This is
1278: * different from File.toURI as this method does not canonicalize
1279: * the file before creating the URI. Furthermore, no schema is
1280: * used.
1281: * @param file a relative file name
1282: * @return a relative URI
1283: * @throws IllegalArgumentException if the file name is not
1284: * relative according to the definition given in {@link
1285: * javax.tools.JavaFileManager#getFileForInput}
1286: */
1287: public static String getRelativeName(File file) {
1288: if (!file.isAbsolute()) {
1289: String result = file.getPath().replace(File.separatorChar,
1290: '/');
1291: if (JavacFileManager.isRelativeUri(URI.create(result))) // FIXME 6419701
1292: return result;
1293: }
1294: throw new IllegalArgumentException("Invalid relative path: "
1295: + file);
1296: }
1297:
1298: @SuppressWarnings("deprecation")
1299: // bug 6410637
1300: protected static String getJavacFileName(FileObject file) {
1301: if (file instanceof BaseFileObject)
1302: return ((BaseFileObject) file).getPath();
1303: URI uri = file.toUri();
1304: String scheme = uri.getScheme();
1305: if (scheme == null || scheme.equals("file")
1306: || scheme.equals("jar"))
1307: return uri.getPath();
1308: else
1309: return uri.toString();
1310: }
1311:
1312: @SuppressWarnings("deprecation")
1313: // bug 6410637
1314: protected static String getJavacBaseFileName(FileObject file) {
1315: if (file instanceof BaseFileObject)
1316: return ((BaseFileObject) file).getName();
1317: URI uri = file.toUri();
1318: String scheme = uri.getScheme();
1319: if (scheme == null || scheme.equals("file")
1320: || scheme.equals("jar")) {
1321: String path = uri.getPath();
1322: if (path == null)
1323: return null;
1324: if (scheme != null && scheme.equals("jar"))
1325: path = path.substring(path.lastIndexOf('!') + 1);
1326: return path.substring(path.lastIndexOf('/') + 1);
1327: } else {
1328: return uri.toString();
1329: }
1330: }
1331:
1332: private static <T> T nullCheck(T o) {
1333: o.getClass(); // null check
1334: return o;
1335: }
1336:
1337: private static <T> Iterable<T> nullCheck(Iterable<T> it) {
1338: for (T t : it)
1339: t.getClass(); // null check
1340: return it;
1341: }
1342:
1343: /**
1344: * A subclass of JavaFileObject representing regular files.
1345: */
1346: private class RegularFileObject extends BaseFileObject {
1347: /** Have the parent directories been created?
1348: */
1349: private boolean hasParents = false;
1350:
1351: /** The file's name.
1352: */
1353: private String name;
1354:
1355: /** The underlying file.
1356: */
1357: final File f;
1358:
1359: public RegularFileObject(File f) {
1360: this (f.getName(), f);
1361: }
1362:
1363: public RegularFileObject(String name, File f) {
1364: if (f.isDirectory())
1365: throw new IllegalArgumentException(
1366: "directories not supported");
1367: this .name = name;
1368: this .f = f;
1369: }
1370:
1371: public InputStream openInputStream() throws IOException {
1372: return new FileInputStream(f);
1373: }
1374:
1375: protected CharsetDecoder getDecoder(boolean ignoreEncodingErrors) {
1376: return JavacFileManager.this .getDecoder(getEncodingName(),
1377: ignoreEncodingErrors);
1378: }
1379:
1380: public OutputStream openOutputStream() throws IOException {
1381: ensureParentDirectoriesExist();
1382: return new FileOutputStream(f);
1383: }
1384:
1385: public Writer openWriter() throws IOException {
1386: ensureParentDirectoriesExist();
1387: return new OutputStreamWriter(new FileOutputStream(f),
1388: getEncodingName());
1389: }
1390:
1391: private void ensureParentDirectoriesExist() throws IOException {
1392: if (!hasParents) {
1393: File parent = f.getParentFile();
1394: if (parent != null && !parent.exists()) {
1395: if (!parent.mkdirs()) {
1396: // if the mkdirs failed, it may be because another process concurrently
1397: // created the directory, so check if the directory got created
1398: // anyway before throwing an exception
1399: if (!parent.exists() || !parent.isDirectory())
1400: throw new IOException(
1401: "could not create parent directories");
1402: }
1403: }
1404: hasParents = true;
1405: }
1406: }
1407:
1408: /** @deprecated see bug 6410637 */
1409: @Deprecated
1410: public String getName() {
1411: return name;
1412: }
1413:
1414: public boolean isNameCompatible(String cn,
1415: JavaFileObject.Kind kind) {
1416: cn.getClass(); // null check
1417: if (kind == Kind.OTHER && getKind() != kind)
1418: return false;
1419: String n = cn + kind.extension;
1420: if (name.equals(n))
1421: return true;
1422: if (name.equalsIgnoreCase(n)) {
1423: try {
1424: // allow for Windows
1425: return (f.getCanonicalFile().getName().equals(n));
1426: } catch (IOException e) {
1427: }
1428: }
1429: return false;
1430: }
1431:
1432: /** @deprecated see bug 6410637 */
1433: @Deprecated
1434: public String getPath() {
1435: return f.getPath();
1436: }
1437:
1438: public long getLastModified() {
1439: return f.lastModified();
1440: }
1441:
1442: public boolean delete() {
1443: return f.delete();
1444: }
1445:
1446: public CharBuffer getCharContent(boolean ignoreEncodingErrors)
1447: throws IOException {
1448: SoftReference<CharBuffer> r = contentCache.get(this );
1449: CharBuffer cb = (r == null ? null : r.get());
1450: if (cb == null) {
1451: InputStream in = new FileInputStream(f);
1452: try {
1453: ByteBuffer bb = makeByteBuffer(in);
1454: JavaFileObject prev = log.useSource(this );
1455: try {
1456: cb = decode(bb, ignoreEncodingErrors);
1457: } finally {
1458: log.useSource(prev);
1459: }
1460: byteBufferCache.put(bb); // save for next time
1461: if (!ignoreEncodingErrors)
1462: contentCache.put(this ,
1463: new SoftReference<CharBuffer>(cb));
1464: } finally {
1465: in.close();
1466: }
1467: }
1468: return cb;
1469: }
1470:
1471: @Override
1472: public boolean equals(Object other) {
1473: if (!(other instanceof RegularFileObject))
1474: return false;
1475: RegularFileObject o = (RegularFileObject) other;
1476: try {
1477: return f.equals(o.f)
1478: || f.getCanonicalFile().equals(
1479: o.f.getCanonicalFile());
1480: } catch (IOException e) {
1481: return false;
1482: }
1483: }
1484:
1485: @Override
1486: public int hashCode() {
1487: return f.hashCode();
1488: }
1489:
1490: public URI toUri() {
1491: try {
1492: // Do no use File.toURI to avoid file system access
1493: String path = f.getAbsolutePath().replace(
1494: File.separatorChar, '/');
1495: return new URI("file://" + path).normalize();
1496: } catch (URISyntaxException ex) {
1497: return f.toURI();
1498: }
1499: }
1500:
1501: }
1502:
1503: /**
1504: * A subclass of JavaFileObject representing zip entries.
1505: */
1506: public class ZipFileObject extends BaseFileObject {
1507:
1508: /** The entry's name.
1509: */
1510: private String name;
1511:
1512: /** The zipfile containing the entry.
1513: */
1514: ZipFile zdir;
1515:
1516: /** The underlying zip entry object.
1517: */
1518: ZipEntry entry;
1519:
1520: public ZipFileObject(String name, ZipFile zdir, ZipEntry entry) {
1521: this .name = name;
1522: this .zdir = zdir;
1523: this .entry = entry;
1524: }
1525:
1526: public InputStream openInputStream() throws IOException {
1527: return zdir.getInputStream(entry);
1528: }
1529:
1530: public OutputStream openOutputStream() throws IOException {
1531: throw new UnsupportedOperationException();
1532: }
1533:
1534: protected CharsetDecoder getDecoder(boolean ignoreEncodingErrors) {
1535: return JavacFileManager.this .getDecoder(getEncodingName(),
1536: ignoreEncodingErrors);
1537: }
1538:
1539: public Writer openWriter() throws IOException {
1540: throw new UnsupportedOperationException();
1541: }
1542:
1543: /** @deprecated see bug 6410637 */
1544: @Deprecated
1545: public String getName() {
1546: return name;
1547: }
1548:
1549: public boolean isNameCompatible(String cn, JavaFileObject.Kind k) {
1550: cn.getClass(); // null check
1551: if (k == Kind.OTHER && getKind() != k)
1552: return false;
1553: return name.equals(cn + k.extension);
1554: }
1555:
1556: /** @deprecated see bug 6410637 */
1557: @Deprecated
1558: public String getPath() {
1559: return zdir.getName() + "(" + entry + ")";
1560: }
1561:
1562: public long getLastModified() {
1563: return entry.getTime();
1564: }
1565:
1566: public boolean delete() {
1567: throw new UnsupportedOperationException();
1568: }
1569:
1570: public CharBuffer getCharContent(boolean ignoreEncodingErrors)
1571: throws IOException {
1572: SoftReference<CharBuffer> r = contentCache.get(this );
1573: CharBuffer cb = (r == null ? null : r.get());
1574: if (cb == null) {
1575: InputStream in = zdir.getInputStream(entry);
1576: try {
1577: ByteBuffer bb = makeByteBuffer(in);
1578: JavaFileObject prev = log.useSource(this );
1579: try {
1580: cb = decode(bb, ignoreEncodingErrors);
1581: } finally {
1582: log.useSource(prev);
1583: }
1584: byteBufferCache.put(bb); // save for next time
1585: if (!ignoreEncodingErrors)
1586: contentCache.put(this ,
1587: new SoftReference<CharBuffer>(cb));
1588: } finally {
1589: in.close();
1590: }
1591: }
1592: return cb;
1593: }
1594:
1595: @Override
1596: public boolean equals(Object other) {
1597: if (!(other instanceof ZipFileObject))
1598: return false;
1599: ZipFileObject o = (ZipFileObject) other;
1600: return zdir.equals(o.zdir) || name.equals(o.name);
1601: }
1602:
1603: @Override
1604: public int hashCode() {
1605: return zdir.hashCode() + name.hashCode();
1606: }
1607:
1608: public String getZipName() {
1609: return zdir.getName();
1610: }
1611:
1612: public String getZipEntryName() {
1613: return entry.getName();
1614: }
1615:
1616: public URI toUri() {
1617: String zipName = new File(getZipName()).toURI().normalize()
1618: .getPath();
1619: String entryName = getZipEntryName();
1620: return URI.create("jar:" + zipName + "!" + entryName);
1621: }
1622:
1623: }
1624:
1625: /**
1626: * A subclass of JavaFileObject representing zip entries using the com.sun.tools.javac.zip.ZipFileIndex implementation.
1627: */
1628: public class ZipFileIndexFileObject extends BaseFileObject {
1629:
1630: /** The entry's name.
1631: */
1632: private String name;
1633:
1634: /** The zipfile containing the entry.
1635: */
1636: ZipFileIndex zfIndex;
1637:
1638: /** The underlying zip entry object.
1639: */
1640: ZipFileIndexEntry entry;
1641:
1642: /** The InputStream for this zip entry (file.)
1643: */
1644: InputStream inputStream = null;
1645:
1646: /** The name of the zip file where this entry resides.
1647: */
1648: String zipName;
1649:
1650: JavacFileManager defFileManager = null;
1651:
1652: public ZipFileIndexFileObject(JavacFileManager fileManager,
1653: ZipFileIndex zfIndex, ZipFileIndexEntry entry,
1654: String zipFileName) {
1655: super ();
1656: this .name = entry.getFileName();
1657: this .zfIndex = zfIndex;
1658: this .entry = entry;
1659: this .zipName = zipFileName;
1660: defFileManager = fileManager;
1661: }
1662:
1663: public InputStream openInputStream() throws IOException {
1664:
1665: if (inputStream == null) {
1666: inputStream = new ByteArrayInputStream(read());
1667: }
1668: return inputStream;
1669: }
1670:
1671: protected CharsetDecoder getDecoder(boolean ignoreEncodingErrors) {
1672: return JavacFileManager.this .getDecoder(getEncodingName(),
1673: ignoreEncodingErrors);
1674: }
1675:
1676: public OutputStream openOutputStream() throws IOException {
1677: throw new UnsupportedOperationException();
1678: }
1679:
1680: public Writer openWriter() throws IOException {
1681: throw new UnsupportedOperationException();
1682: }
1683:
1684: /** @deprecated see bug 6410637 */
1685: @Deprecated
1686: public String getName() {
1687: return name;
1688: }
1689:
1690: public boolean isNameCompatible(String cn, JavaFileObject.Kind k) {
1691: cn.getClass(); // null check
1692: if (k == Kind.OTHER && getKind() != k)
1693: return false;
1694: return name.equals(cn + k.extension);
1695: }
1696:
1697: /** @deprecated see bug 6410637 */
1698: @Deprecated
1699: public String getPath() {
1700: return entry.getName() + "(" + entry + ")";
1701: }
1702:
1703: public long getLastModified() {
1704: return entry.getLastModified();
1705: }
1706:
1707: public boolean delete() {
1708: throw new UnsupportedOperationException();
1709: }
1710:
1711: @Override
1712: public boolean equals(Object other) {
1713: if (!(other instanceof ZipFileIndexFileObject))
1714: return false;
1715: ZipFileIndexFileObject o = (ZipFileIndexFileObject) other;
1716: return entry.equals(o.entry);
1717: }
1718:
1719: @Override
1720: public int hashCode() {
1721: return zipName.hashCode() + (name.hashCode() << 10);
1722: }
1723:
1724: public String getZipName() {
1725: return zipName;
1726: }
1727:
1728: public String getZipEntryName() {
1729: return entry.getName();
1730: }
1731:
1732: public URI toUri() {
1733: String zipName = new File(getZipName()).toURI().normalize()
1734: .getPath();
1735: String entryName = getZipEntryName();
1736: if (File.separatorChar != '/') {
1737: entryName = entryName.replace(File.separatorChar, '/');
1738: }
1739: return URI.create("jar:" + zipName + "!" + entryName);
1740: }
1741:
1742: private byte[] read() throws IOException {
1743: if (entry == null) {
1744: entry = zfIndex.getZipIndexEntry(name);
1745: if (entry == null)
1746: throw new FileNotFoundException();
1747: }
1748: return zfIndex.read(entry);
1749: }
1750:
1751: public CharBuffer getCharContent(boolean ignoreEncodingErrors)
1752: throws IOException {
1753: SoftReference<CharBuffer> r = defFileManager.contentCache
1754: .get(this );
1755: CharBuffer cb = (r == null ? null : r.get());
1756: if (cb == null) {
1757: InputStream in = new ByteArrayInputStream(zfIndex
1758: .read(entry));
1759: try {
1760: ByteBuffer bb = makeByteBuffer(in);
1761: JavaFileObject prev = log.useSource(this );
1762: try {
1763: cb = decode(bb, ignoreEncodingErrors);
1764: } finally {
1765: log.useSource(prev);
1766: }
1767: byteBufferCache.put(bb); // save for next time
1768: if (!ignoreEncodingErrors)
1769: defFileManager.contentCache.put(this ,
1770: new SoftReference<CharBuffer>(cb));
1771: } finally {
1772: in.close();
1773: }
1774: }
1775: return cb;
1776: }
1777: }
1778:
1779: public class ZipFileIndexArchive implements Archive {
1780: private final ZipFileIndex zfIndex;
1781: private JavacFileManager fileManager;
1782:
1783: public ZipFileIndexArchive(JavacFileManager fileManager,
1784: ZipFileIndex zdir) throws IOException {
1785: this .fileManager = fileManager;
1786: this .zfIndex = zdir;
1787: }
1788:
1789: public boolean contains(String name) {
1790: return zfIndex.contains(name);
1791: }
1792:
1793: public com.sun.tools.javac.util.List<String> getFiles(
1794: String subdirectory) {
1795: return zfIndex
1796: .getFiles(((subdirectory.endsWith("/") || subdirectory
1797: .endsWith("\\")) ? subdirectory.substring(
1798: 0, subdirectory.length() - 1)
1799: : subdirectory));
1800: }
1801:
1802: public JavaFileObject getFileObject(String subdirectory,
1803: String file) {
1804: String fullZipFileName = subdirectory + file;
1805: ZipFileIndexEntry entry = zfIndex
1806: .getZipIndexEntry(fullZipFileName);
1807: JavaFileObject ret = new ZipFileIndexFileObject(
1808: fileManager, zfIndex, entry, zfIndex.getZipFile()
1809: .getPath());
1810: return ret;
1811: }
1812:
1813: public Set<String> getSubdirectories() {
1814: return zfIndex.getAllDirectories();
1815: }
1816:
1817: public void close() throws IOException {
1818: zfIndex.close();
1819: }
1820: }
1821: }
|