src/share/classes/com/sun/tools/javac/util/JavacFileManager.java

Wed, 09 Apr 2008 13:41:45 +0100

author
mcimadamore
date
Wed, 09 Apr 2008 13:41:45 +0100
changeset 24
d032d5090fd5
parent 1
9a66ca7c79fa
child 38
65a447c75d4b
permissions
-rw-r--r--

5009937: hiding versus generics versus binary compatibility
Summary: missing implementation of JLS 8.4.8.3 (different arguments with same erasure not always triggering a compiler error)
Reviewed-by: jjg

     1 /*
     2  * Copyright 2005-2006 Sun Microsystems, Inc.  All Rights Reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Sun designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Sun in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
    23  * have any questions.
    24  */
    26 package com.sun.tools.javac.util;
    28 import com.sun.tools.javac.main.JavacOption;
    29 import com.sun.tools.javac.main.OptionName;
    30 import com.sun.tools.javac.main.RecognizedOptions;
    31 import java.io.ByteArrayOutputStream;
    32 import java.io.File;
    33 import java.io.FileInputStream;
    34 import java.io.FileNotFoundException;
    35 import java.io.FileOutputStream;
    36 import java.io.IOException;
    37 import java.io.InputStream;
    38 import java.io.OutputStream;
    39 import java.io.OutputStreamWriter;
    40 import java.io.Writer;
    41 import java.lang.ref.SoftReference;
    42 import java.net.MalformedURLException;
    43 import java.net.URI;
    44 import java.net.URISyntaxException;
    45 import java.net.URL;
    46 import java.net.URLClassLoader;
    47 import java.nio.ByteBuffer;
    48 import java.nio.CharBuffer;
    49 import java.nio.channels.FileChannel;
    50 import java.nio.charset.Charset;
    51 import java.nio.charset.CharsetDecoder;
    52 import java.nio.charset.CoderResult;
    53 import java.nio.charset.CodingErrorAction;
    54 import java.nio.charset.IllegalCharsetNameException;
    55 import java.nio.charset.UnsupportedCharsetException;
    56 import java.util.ArrayList;
    57 import java.util.Arrays;
    58 import java.util.Collection;
    59 import java.util.Collections;
    60 import java.util.EnumSet;
    61 import java.util.Enumeration;
    62 import java.util.HashMap;
    63 import java.util.Iterator;
    64 import java.util.Map;
    65 import java.util.Set;
    66 import java.util.zip.ZipEntry;
    67 import java.util.zip.ZipFile;
    69 import javax.lang.model.SourceVersion;
    70 import javax.tools.FileObject;
    71 import javax.tools.JavaFileManager;
    72 import javax.tools.JavaFileObject;
    74 import com.sun.tools.javac.code.Source;
    75 import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition;
    76 import java.util.concurrent.ConcurrentHashMap;
    77 import javax.tools.StandardJavaFileManager;
    79 import com.sun.tools.javac.zip.*;
    80 import java.io.ByteArrayInputStream;
    82 import static com.sun.tools.javac.main.OptionName.*;
    83 import static javax.tools.StandardLocation.*;
    85 /**
    86  * This class provides access to the source, class and other files
    87  * used by the compiler and related tools.
    88  */
    89 public class JavacFileManager implements StandardJavaFileManager {
    91     private static final String[] symbolFileLocation = { "lib", "ct.sym" };
    92     private static final String symbolFilePrefix = "META-INF/sym/rt.jar/";
    94     boolean useZipFileIndex;
    96     private static int symbolFilePrefixLength = 0;
    97     static {
    98         try {
    99             symbolFilePrefixLength = symbolFilePrefix.getBytes("UTF-8").length;
   100         } catch (java.io.UnsupportedEncodingException uee) {
   101             // Can't happen...UTF-8 is always supported.
   102         }
   103     }
   105     private static boolean CHECK_ZIP_TIMESTAMP = false;
   106     private static Map<File, Boolean> isDirectory = new ConcurrentHashMap<File, Boolean>();
   109     public static char[] toArray(CharBuffer buffer) {
   110         if (buffer.hasArray())
   111             return ((CharBuffer)buffer.compact().flip()).array();
   112         else
   113             return buffer.toString().toCharArray();
   114     }
   116     /**
   117      * The log to be used for error reporting.
   118      */
   119     protected Log log;
   121     /** Encapsulates knowledge of paths
   122      */
   123     private Paths paths;
   125     private Options options;
   127     private final File uninited = new File("U N I N I T E D");
   129     private final Set<JavaFileObject.Kind> sourceOrClass =
   130         EnumSet.of(JavaFileObject.Kind.SOURCE, JavaFileObject.Kind.CLASS);
   132     /** The standard output directory, primarily used for classes.
   133      *  Initialized by the "-d" option.
   134      *  If classOutDir = null, files are written into same directory as the sources
   135      *  they were generated from.
   136      */
   137     private File classOutDir = uninited;
   139     /** The output directory, used when generating sources while processing annotations.
   140      *  Initialized by the "-s" option.
   141      */
   142     private File sourceOutDir = uninited;
   144     protected boolean mmappedIO;
   145     protected boolean ignoreSymbolFile;
   147     /**
   148      * User provided charset (through javax.tools).
   149      */
   150     protected Charset charset;
   152     /**
   153      * Register a Context.Factory to create a JavacFileManager.
   154      */
   155     public static void preRegister(final Context context) {
   156         context.put(JavaFileManager.class, new Context.Factory<JavaFileManager>() {
   157             public JavaFileManager make() {
   158                 return new JavacFileManager(context, true, null);
   159             }
   160         });
   161     }
   163     /**
   164      * Create a JavacFileManager using a given context, optionally registering
   165      * it as the JavaFileManager for that context.
   166      */
   167     public JavacFileManager(Context context, boolean register, Charset charset) {
   168         if (register)
   169             context.put(JavaFileManager.class, this);
   170         byteBufferCache = new ByteBufferCache();
   171         this.charset = charset;
   172         setContext(context);
   173     }
   175     /**
   176      * Set the context for JavacFileManager.
   177      */
   178     public void setContext(Context context) {
   179         log = Log.instance(context);
   180         if (paths == null) {
   181             paths = Paths.instance(context);
   182         } else {
   183             // Reuse the Paths object as it stores the locations that
   184             // have been set with setLocation, etc.
   185             paths.setContext(context);
   186         }
   188         options = Options.instance(context);
   190         useZipFileIndex = System.getProperty("useJavaUtilZip") == null;// TODO: options.get("useJavaUtilZip") == null;
   191         CHECK_ZIP_TIMESTAMP = System.getProperty("checkZipIndexTimestamp") != null;// TODO: options.get("checkZipIndexTimestamp") != null;
   193         mmappedIO = options.get("mmappedIO") != null;
   194         ignoreSymbolFile = options.get("ignore.symbol.file") != null;
   195     }
   197     public JavaFileObject getFileForInput(String name) {
   198         return getRegularFile(new File(name));
   199     }
   201     public JavaFileObject getRegularFile(File file) {
   202         return new RegularFileObject(file);
   203     }
   205     public JavaFileObject getFileForOutput(String classname,
   206                                            JavaFileObject.Kind kind,
   207                                            JavaFileObject sibling)
   208         throws IOException
   209     {
   210         return getJavaFileForOutput(CLASS_OUTPUT, classname, kind, sibling);
   211     }
   213     public Iterable<? extends JavaFileObject> getJavaFileObjectsFromStrings(Iterable<String> names) {
   214         ListBuffer<File> files = new ListBuffer<File>();
   215         for (String name : names)
   216             files.append(new File(nullCheck(name)));
   217         return getJavaFileObjectsFromFiles(files.toList());
   218     }
   220     public Iterable<? extends JavaFileObject> getJavaFileObjects(String... names) {
   221         return getJavaFileObjectsFromStrings(Arrays.asList(nullCheck(names)));
   222     }
   224     protected JavaFileObject.Kind getKind(String extension) {
   225         if (extension.equals(JavaFileObject.Kind.CLASS.extension))
   226             return JavaFileObject.Kind.CLASS;
   227         else if (extension.equals(JavaFileObject.Kind.SOURCE.extension))
   228             return JavaFileObject.Kind.SOURCE;
   229         else if (extension.equals(JavaFileObject.Kind.HTML.extension))
   230             return JavaFileObject.Kind.HTML;
   231         else
   232             return JavaFileObject.Kind.OTHER;
   233     }
   235     private static boolean isValidName(String name) {
   236         // Arguably, isValidName should reject keywords (such as in SourceVersion.isName() ),
   237         // but the set of keywords depends on the source level, and we don't want
   238         // impls of JavaFileManager to have to be dependent on the source level.
   239         // Therefore we simply check that the argument is a sequence of identifiers
   240         // separated by ".".
   241         for (String s : name.split("\\.", -1)) {
   242             if (!SourceVersion.isIdentifier(s))
   243                 return false;
   244         }
   245         return true;
   246     }
   248     private static void validateClassName(String className) {
   249         if (!isValidName(className))
   250             throw new IllegalArgumentException("Invalid class name: " + className);
   251     }
   253     private static void validatePackageName(String packageName) {
   254         if (packageName.length() > 0 && !isValidName(packageName))
   255             throw new IllegalArgumentException("Invalid packageName name: " + packageName);
   256     }
   258     public static void testName(String name,
   259                                 boolean isValidPackageName,
   260                                 boolean isValidClassName)
   261     {
   262         try {
   263             validatePackageName(name);
   264             if (!isValidPackageName)
   265                 throw new AssertionError("Invalid package name accepted: " + name);
   266             printAscii("Valid package name: \"%s\"", name);
   267         } catch (IllegalArgumentException e) {
   268             if (isValidPackageName)
   269                 throw new AssertionError("Valid package name rejected: " + name);
   270             printAscii("Invalid package name: \"%s\"", name);
   271         }
   272         try {
   273             validateClassName(name);
   274             if (!isValidClassName)
   275                 throw new AssertionError("Invalid class name accepted: " + name);
   276             printAscii("Valid class name: \"%s\"", name);
   277         } catch (IllegalArgumentException e) {
   278             if (isValidClassName)
   279                 throw new AssertionError("Valid class name rejected: " + name);
   280             printAscii("Invalid class name: \"%s\"", name);
   281         }
   282     }
   283     private static void printAscii(String format, Object... args) {
   284         String message;
   285         try {
   286             final String ascii = "US-ASCII";
   287             message = new String(String.format(null, format, args).getBytes(ascii), ascii);
   288         } catch (java.io.UnsupportedEncodingException ex) {
   289             throw new AssertionError(ex);
   290         }
   291         System.out.println(message);
   292     }
   294     /** Return external representation of name,
   295      *  converting '.' to File.separatorChar.
   296      */
   297     private static String externalizeFileName(CharSequence name) {
   298         return name.toString().replace('.', File.separatorChar);
   299     }
   301     private static String externalizeFileName(CharSequence n, JavaFileObject.Kind kind) {
   302         return externalizeFileName(n) + kind.extension;
   303     }
   305     private static String baseName(String fileName) {
   306         return fileName.substring(fileName.lastIndexOf(File.separatorChar) + 1);
   307     }
   309     /**
   310      * Insert all files in subdirectory `subdirectory' of `directory' which end
   311      * in one of the extensions in `extensions' into packageSym.
   312      */
   313     private void listDirectory(File directory,
   314                                String subdirectory,
   315                                Set<JavaFileObject.Kind> fileKinds,
   316                                boolean recurse,
   317                                ListBuffer<JavaFileObject> l) {
   318         Archive archive = archives.get(directory);
   320         boolean isFile = false;
   321         if (CHECK_ZIP_TIMESTAMP) {
   322             Boolean isf = isDirectory.get(directory);
   323             if (isf == null) {
   324                 isFile = directory.isFile();
   325                 isDirectory.put(directory, isFile);
   326             }
   327             else {
   328                 isFile = directory.isFile();
   329             }
   330         }
   331         else {
   332             isFile = directory.isFile();
   333         }
   335         if (archive != null || isFile) {
   336             if (archive == null) {
   337                 try {
   338                     archive = openArchive(directory);
   339                 } catch (IOException ex) {
   340                     log.error("error.reading.file",
   341                        directory, ex.getLocalizedMessage());
   342                     return;
   343                 }
   344             }
   345             if (subdirectory.length() != 0) {
   346                 if (!useZipFileIndex) {
   347                     subdirectory = subdirectory.replace('\\', '/');
   348                     if (!subdirectory.endsWith("/")) subdirectory = subdirectory + "/";
   349                 }
   350                 else {
   351                     if (File.separatorChar == '/') {
   352                         subdirectory = subdirectory.replace('\\', '/');
   353                     }
   354                     else {
   355                         subdirectory = subdirectory.replace('/', '\\');
   356                     }
   358                     if (!subdirectory.endsWith(File.separator)) subdirectory = subdirectory + File.separator;
   359                 }
   360             }
   362             List<String> files = archive.getFiles(subdirectory);
   363             if (files != null) {
   364                 for (String file; !files.isEmpty(); files = files.tail) {
   365                     file = files.head;
   366                     if (isValidFile(file, fileKinds)) {
   367                         l.append(archive.getFileObject(subdirectory, file));
   368                     }
   369                 }
   370             }
   371             if (recurse) {
   372                 for (String s: archive.getSubdirectories()) {
   373                     if (s.startsWith(subdirectory) && !s.equals(subdirectory)) {
   374                         // Because the archive map is a flat list of directories,
   375                         // the enclosing loop will pick up all child subdirectories.
   376                         // Therefore, there is no need to recurse deeper.
   377                         listDirectory(directory, s, fileKinds, false, l);
   378                     }
   379                 }
   380             }
   381         } else {
   382             File d = subdirectory.length() != 0
   383                 ? new File(directory, subdirectory)
   384                 : directory;
   385             if (!caseMapCheck(d, subdirectory))
   386                 return;
   388             File[] files = d.listFiles();
   389             if (files == null)
   390                 return;
   392             for (File f: files) {
   393                 String fname = f.getName();
   394                 if (f.isDirectory()) {
   395                     if (recurse && SourceVersion.isIdentifier(fname)) {
   396                         listDirectory(directory,
   397                                       subdirectory + File.separator + fname,
   398                                       fileKinds,
   399                                       recurse,
   400                                       l);
   401                     }
   402                 } else {
   403                     if (isValidFile(fname, fileKinds)) {
   404                         JavaFileObject fe =
   405                         new RegularFileObject(fname, new File(d, fname));
   406                         l.append(fe);
   407                     }
   408                 }
   409             }
   410         }
   411     }
   413     private boolean isValidFile(String s, Set<JavaFileObject.Kind> fileKinds) {
   414         int lastDot = s.lastIndexOf(".");
   415         String extn = (lastDot == -1 ? s : s.substring(lastDot));
   416         JavaFileObject.Kind kind = getKind(extn);
   417         return fileKinds.contains(kind);
   418     }
   420     private static final boolean fileSystemIsCaseSensitive =
   421         File.separatorChar == '/';
   423     /** Hack to make Windows case sensitive. Test whether given path
   424      *  ends in a string of characters with the same case as given name.
   425      *  Ignore file separators in both path and name.
   426      */
   427     private boolean caseMapCheck(File f, String name) {
   428         if (fileSystemIsCaseSensitive) return true;
   429         // Note that getCanonicalPath() returns the case-sensitive
   430         // spelled file name.
   431         String path;
   432         try {
   433             path = f.getCanonicalPath();
   434         } catch (IOException ex) {
   435             return false;
   436         }
   437         char[] pcs = path.toCharArray();
   438         char[] ncs = name.toCharArray();
   439         int i = pcs.length - 1;
   440         int j = ncs.length - 1;
   441         while (i >= 0 && j >= 0) {
   442             while (i >= 0 && pcs[i] == File.separatorChar) i--;
   443             while (j >= 0 && ncs[j] == File.separatorChar) j--;
   444             if (i >= 0 && j >= 0) {
   445                 if (pcs[i] != ncs[j]) return false;
   446                 i--;
   447                 j--;
   448             }
   449         }
   450         return j < 0;
   451     }
   453     /**
   454      * An archive provides a flat directory structure of a ZipFile by
   455      * mapping directory names to lists of files (basenames).
   456      */
   457     public interface Archive {
   458         void close() throws IOException;
   460         boolean contains(String name);
   462         JavaFileObject getFileObject(String subdirectory, String file);
   464         List<String> getFiles(String subdirectory);
   466         Set<String> getSubdirectories();
   467     }
   469     public class ZipArchive implements Archive {
   470         protected final Map<String,List<String>> map;
   471         protected final ZipFile zdir;
   472         public ZipArchive(ZipFile zdir) throws IOException {
   473             this.zdir = zdir;
   474             this.map = new HashMap<String,List<String>>();
   475             for (Enumeration<? extends ZipEntry> e = zdir.entries(); e.hasMoreElements(); ) {
   476                 ZipEntry entry;
   477                 try {
   478                     entry = e.nextElement();
   479                 } catch (InternalError ex) {
   480                     IOException io = new IOException();
   481                     io.initCause(ex); // convenience constructors added in Mustang :-(
   482                     throw io;
   483                 }
   484                 addZipEntry(entry);
   485             }
   486         }
   488         void addZipEntry(ZipEntry entry) {
   489             String name = entry.getName();
   490             int i = name.lastIndexOf('/');
   491             String dirname = name.substring(0, i+1);
   492             String basename = name.substring(i+1);
   493             if (basename.length() == 0)
   494                 return;
   495             List<String> list = map.get(dirname);
   496             if (list == null)
   497                 list = List.nil();
   498             list = list.prepend(basename);
   499             map.put(dirname, list);
   500         }
   502         public boolean contains(String name) {
   503             int i = name.lastIndexOf('/');
   504             String dirname = name.substring(0, i+1);
   505             String basename = name.substring(i+1);
   506             if (basename.length() == 0)
   507                 return false;
   508             List<String> list = map.get(dirname);
   509             return (list != null && list.contains(basename));
   510         }
   512         public List<String> getFiles(String subdirectory) {
   513             return map.get(subdirectory);
   514         }
   516         public JavaFileObject getFileObject(String subdirectory, String file) {
   517             ZipEntry ze = zdir.getEntry(subdirectory + file);
   518             return new ZipFileObject(file, zdir, ze);
   519         }
   521         public Set<String> getSubdirectories() {
   522             return map.keySet();
   523         }
   525         public void close() throws IOException {
   526             zdir.close();
   527         }
   528     }
   530     public class SymbolArchive extends ZipArchive {
   531         final File origFile;
   532         public SymbolArchive(File orig, ZipFile zdir) throws IOException {
   533             super(zdir);
   534             this.origFile = orig;
   535         }
   537         @Override
   538         void addZipEntry(ZipEntry entry) {
   539             // called from super constructor, may not refer to origFile.
   540             String name = entry.getName();
   541             if (!name.startsWith(symbolFilePrefix))
   542                 return;
   543             name = name.substring(symbolFilePrefix.length());
   544             int i = name.lastIndexOf('/');
   545             String dirname = name.substring(0, i+1);
   546             String basename = name.substring(i+1);
   547             if (basename.length() == 0)
   548                 return;
   549             List<String> list = map.get(dirname);
   550             if (list == null)
   551                 list = List.nil();
   552             list = list.prepend(basename);
   553             map.put(dirname, list);
   554         }
   556         @Override
   557         public JavaFileObject getFileObject(String subdirectory, String file) {
   558             return super.getFileObject(symbolFilePrefix + subdirectory, file);
   559         }
   560     }
   562     public class MissingArchive implements Archive {
   563         final File zipFileName;
   564         public MissingArchive(File name) {
   565             zipFileName = name;
   566         }
   567         public boolean contains(String name) {
   568               return false;
   569         }
   571         public void close() {
   572         }
   574         public JavaFileObject getFileObject(String subdirectory, String file) {
   575             return null;
   576         }
   578         public List<String> getFiles(String subdirectory) {
   579             return List.nil();
   580         }
   582         public Set<String> getSubdirectories() {
   583             return Collections.emptySet();
   584         }
   585     }
   587     /** A directory of zip files already opened.
   588      */
   589     Map<File, Archive> archives = new HashMap<File,Archive>();
   591     /** Open a new zip file directory.
   592      */
   593     protected Archive openArchive(File zipFileName) throws IOException {
   594         Archive archive = archives.get(zipFileName);
   595         if (archive == null) {
   596             File origZipFileName = zipFileName;
   597             if (!ignoreSymbolFile && paths.isBootClassPathRtJar(zipFileName)) {
   598                 File file = zipFileName.getParentFile().getParentFile(); // ${java.home}
   599                 if (new File(file.getName()).equals(new File("jre")))
   600                     file = file.getParentFile();
   601                 // file == ${jdk.home}
   602                 for (String name : symbolFileLocation)
   603                     file = new File(file, name);
   604                 // file == ${jdk.home}/lib/ct.sym
   605                 if (file.exists())
   606                     zipFileName = file;
   607             }
   609             try {
   611                 ZipFile zdir = null;
   613                 boolean usePreindexedCache = false;
   614                 String preindexCacheLocation = null;
   616                 if (!useZipFileIndex) {
   617                     zdir = new ZipFile(zipFileName);
   618                 }
   619                 else {
   620                     usePreindexedCache = options.get("usezipindex") != null;
   621                     preindexCacheLocation = options.get("java.io.tmpdir");
   622                     String optCacheLoc = options.get("cachezipindexdir");
   624                     if (optCacheLoc != null && optCacheLoc.length() != 0) {
   625                         if (optCacheLoc.startsWith("\"")) {
   626                             if (optCacheLoc.endsWith("\"")) {
   627                                 optCacheLoc = optCacheLoc.substring(1, optCacheLoc.length() - 1);
   628                             }
   629                            else {
   630                                 optCacheLoc = optCacheLoc.substring(1);
   631                             }
   632                         }
   634                         File cacheDir = new File(optCacheLoc);
   635                         if (cacheDir.exists() && cacheDir.canWrite()) {
   636                             preindexCacheLocation = optCacheLoc;
   637                             if (!preindexCacheLocation.endsWith("/") &&
   638                                 !preindexCacheLocation.endsWith(File.separator)) {
   639                                 preindexCacheLocation += File.separator;
   640                             }
   641                         }
   642                     }
   643                 }
   645                 if (origZipFileName == zipFileName) {
   646                     if (!useZipFileIndex) {
   647                         archive = new ZipArchive(zdir);
   648                     } else {
   649                         archive = new ZipFileIndexArchive(this, ZipFileIndex.getZipFileIndex(zipFileName, 0,
   650                                 usePreindexedCache, preindexCacheLocation, options.get("writezipindexfiles") != null));
   651                     }
   652                 }
   653                 else {
   654                     if (!useZipFileIndex) {
   655                         archive = new SymbolArchive(origZipFileName, zdir);
   656                     }
   657                     else {
   658                         archive = new ZipFileIndexArchive(this, ZipFileIndex.getZipFileIndex(zipFileName, symbolFilePrefixLength,
   659                                 usePreindexedCache, preindexCacheLocation, options.get("writezipindexfiles") != null));
   660                     }
   661                 }
   662             } catch (FileNotFoundException ex) {
   663                 archive = new MissingArchive(zipFileName);
   664             } catch (IOException ex) {
   665                 log.error("error.reading.file", zipFileName, ex.getLocalizedMessage());
   666                 archive = new MissingArchive(zipFileName);
   667             }
   669             archives.put(origZipFileName, archive);
   670         }
   671         return archive;
   672     }
   674     /** Flush any output resources.
   675      */
   676     public void flush() {
   677         contentCache.clear();
   678     }
   680     /**
   681      * Close the JavaFileManager, releasing resources.
   682      */
   683     public void close() {
   684         for (Iterator<Archive> i = archives.values().iterator(); i.hasNext(); ) {
   685             Archive a = i.next();
   686             i.remove();
   687             try {
   688                 a.close();
   689             } catch (IOException e) {
   690             }
   691         }
   692     }
   694     private Map<JavaFileObject, SoftReference<CharBuffer>> contentCache = new HashMap<JavaFileObject, SoftReference<CharBuffer>>();
   696     private String defaultEncodingName;
   697     private String getDefaultEncodingName() {
   698         if (defaultEncodingName == null) {
   699             defaultEncodingName =
   700                 new OutputStreamWriter(new ByteArrayOutputStream()).getEncoding();
   701         }
   702         return defaultEncodingName;
   703     }
   705     protected String getEncodingName() {
   706         String encName = options.get(OptionName.ENCODING);
   707         if (encName == null)
   708             return getDefaultEncodingName();
   709         else
   710             return encName;
   711     }
   713     protected Source getSource() {
   714         String sourceName = options.get(OptionName.SOURCE);
   715         Source source = null;
   716         if (sourceName != null)
   717             source = Source.lookup(sourceName);
   718         return (source != null ? source : Source.DEFAULT);
   719     }
   721     /**
   722      * Make a byte buffer from an input stream.
   723      */
   724     private ByteBuffer makeByteBuffer(InputStream in)
   725         throws IOException {
   726         int limit = in.available();
   727         if (mmappedIO && in instanceof FileInputStream) {
   728             // Experimental memory mapped I/O
   729             FileInputStream fin = (FileInputStream)in;
   730             return fin.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, limit);
   731         }
   732         if (limit < 1024) limit = 1024;
   733         ByteBuffer result = byteBufferCache.get(limit);
   734         int position = 0;
   735         while (in.available() != 0) {
   736             if (position >= limit)
   737                 // expand buffer
   738                 result = ByteBuffer.
   739                     allocate(limit <<= 1).
   740                     put((ByteBuffer)result.flip());
   741             int count = in.read(result.array(),
   742                 position,
   743                 limit - position);
   744             if (count < 0) break;
   745             result.position(position += count);
   746         }
   747         return (ByteBuffer)result.flip();
   748     }
   750     /**
   751      * A single-element cache of direct byte buffers.
   752      */
   753     private static class ByteBufferCache {
   754         private ByteBuffer cached;
   755         ByteBuffer get(int capacity) {
   756             if (capacity < 20480) capacity = 20480;
   757             ByteBuffer result =
   758                 (cached != null && cached.capacity() >= capacity)
   759                 ? (ByteBuffer)cached.clear()
   760                 : ByteBuffer.allocate(capacity + capacity>>1);
   761             cached = null;
   762             return result;
   763         }
   764         void put(ByteBuffer x) {
   765             cached = x;
   766         }
   767     }
   768     private final ByteBufferCache byteBufferCache;
   770     private CharsetDecoder getDecoder(String encodingName, boolean ignoreEncodingErrors) {
   771         Charset charset = (this.charset == null)
   772             ? Charset.forName(encodingName)
   773             : this.charset;
   774         CharsetDecoder decoder = charset.newDecoder();
   776         CodingErrorAction action;
   777         if (ignoreEncodingErrors)
   778             action = CodingErrorAction.REPLACE;
   779         else
   780             action = CodingErrorAction.REPORT;
   782         return decoder
   783             .onMalformedInput(action)
   784             .onUnmappableCharacter(action);
   785     }
   787     /**
   788      * Decode a ByteBuffer into a CharBuffer.
   789      */
   790     private CharBuffer decode(ByteBuffer inbuf, boolean ignoreEncodingErrors) {
   791         String encodingName = getEncodingName();
   792         CharsetDecoder decoder;
   793         try {
   794             decoder = getDecoder(encodingName, ignoreEncodingErrors);
   795         } catch (IllegalCharsetNameException e) {
   796             log.error("unsupported.encoding", encodingName);
   797             return (CharBuffer)CharBuffer.allocate(1).flip();
   798         } catch (UnsupportedCharsetException e) {
   799             log.error("unsupported.encoding", encodingName);
   800             return (CharBuffer)CharBuffer.allocate(1).flip();
   801         }
   803         // slightly overestimate the buffer size to avoid reallocation.
   804         float factor =
   805             decoder.averageCharsPerByte() * 0.8f +
   806             decoder.maxCharsPerByte() * 0.2f;
   807         CharBuffer dest = CharBuffer.
   808             allocate(10 + (int)(inbuf.remaining()*factor));
   810         while (true) {
   811             CoderResult result = decoder.decode(inbuf, dest, true);
   812             dest.flip();
   814             if (result.isUnderflow()) { // done reading
   815                 // make sure there is at least one extra character
   816                 if (dest.limit() == dest.capacity()) {
   817                     dest = CharBuffer.allocate(dest.capacity()+1).put(dest);
   818                     dest.flip();
   819                 }
   820                 return dest;
   821             } else if (result.isOverflow()) { // buffer too small; expand
   822                 int newCapacity =
   823                     10 + dest.capacity() +
   824                     (int)(inbuf.remaining()*decoder.maxCharsPerByte());
   825                 dest = CharBuffer.allocate(newCapacity).put(dest);
   826             } else if (result.isMalformed() || result.isUnmappable()) {
   827                 // bad character in input
   829                 // report coding error (warn only pre 1.5)
   830                 if (!getSource().allowEncodingErrors()) {
   831                     log.error(new SimpleDiagnosticPosition(dest.limit()),
   832                               "illegal.char.for.encoding",
   833                               charset == null ? encodingName : charset.name());
   834                 } else {
   835                     log.warning(new SimpleDiagnosticPosition(dest.limit()),
   836                                 "illegal.char.for.encoding",
   837                                 charset == null ? encodingName : charset.name());
   838                 }
   840                 // skip past the coding error
   841                 inbuf.position(inbuf.position() + result.length());
   843                 // undo the flip() to prepare the output buffer
   844                 // for more translation
   845                 dest.position(dest.limit());
   846                 dest.limit(dest.capacity());
   847                 dest.put((char)0xfffd); // backward compatible
   848             } else {
   849                 throw new AssertionError(result);
   850             }
   851         }
   852         // unreached
   853     }
   855     public ClassLoader getClassLoader(Location location) {
   856         nullCheck(location);
   857         Iterable<? extends File> path = getLocation(location);
   858         if (path == null)
   859             return null;
   860         ListBuffer<URL> lb = new ListBuffer<URL>();
   861         for (File f: path) {
   862             try {
   863                 lb.append(f.toURI().toURL());
   864             } catch (MalformedURLException e) {
   865                 throw new AssertionError(e);
   866             }
   867         }
   868         return new URLClassLoader(lb.toArray(new URL[lb.size()]),
   869             getClass().getClassLoader());
   870     }
   872     public Iterable<JavaFileObject> list(Location location,
   873                                          String packageName,
   874                                          Set<JavaFileObject.Kind> kinds,
   875                                          boolean recurse)
   876         throws IOException
   877     {
   878         // validatePackageName(packageName);
   879         nullCheck(packageName);
   880         nullCheck(kinds);
   882         Iterable<? extends File> path = getLocation(location);
   883         if (path == null)
   884             return List.nil();
   885         String subdirectory = externalizeFileName(packageName);
   886         ListBuffer<JavaFileObject> results = new ListBuffer<JavaFileObject>();
   888         for (File directory : path)
   889             listDirectory(directory, subdirectory, kinds, recurse, results);
   891         return results.toList();
   892     }
   894     public String inferBinaryName(Location location, JavaFileObject file) {
   895         file.getClass(); // null check
   896         location.getClass(); // null check
   897         // Need to match the path semantics of list(location, ...)
   898         Iterable<? extends File> path = getLocation(location);
   899         if (path == null) {
   900             //System.err.println("Path for " + location + " is null");
   901             return null;
   902         }
   903         //System.err.println("Path for " + location + " is " + path);
   905         if (file instanceof RegularFileObject) {
   906             RegularFileObject r = (RegularFileObject) file;
   907             String rPath = r.getPath();
   908             //System.err.println("RegularFileObject " + file + " " +r.getPath());
   909             for (File dir: path) {
   910                 //System.err.println("dir: " + dir);
   911                 String dPath = dir.getPath();
   912                 if (!dPath.endsWith(File.separator))
   913                     dPath += File.separator;
   914                 if (rPath.regionMatches(true, 0, dPath, 0, dPath.length())
   915                     && new File(rPath.substring(0, dPath.length())).equals(new File(dPath))) {
   916                     String relativeName = rPath.substring(dPath.length());
   917                     return removeExtension(relativeName).replace(File.separatorChar, '.');
   918                 }
   919             }
   920         } else if (file instanceof ZipFileObject) {
   921             ZipFileObject z = (ZipFileObject) file;
   922             String entryName = z.getZipEntryName();
   923             if (entryName.startsWith(symbolFilePrefix))
   924                 entryName = entryName.substring(symbolFilePrefix.length());
   925             return removeExtension(entryName).replace('/', '.');
   926         } else if (file instanceof ZipFileIndexFileObject) {
   927             ZipFileIndexFileObject z = (ZipFileIndexFileObject) file;
   928             String entryName = z.getZipEntryName();
   929             if (entryName.startsWith(symbolFilePrefix))
   930                 entryName = entryName.substring(symbolFilePrefix.length());
   931             return removeExtension(entryName).replace(File.separatorChar, '.');
   932         } else
   933             throw new IllegalArgumentException(file.getClass().getName());
   934         // System.err.println("inferBinaryName failed for " + file);
   935         return null;
   936     }
   937     // where
   938         private static String removeExtension(String fileName) {
   939             int lastDot = fileName.lastIndexOf(".");
   940             return (lastDot == -1 ? fileName : fileName.substring(0, lastDot));
   941         }
   943     public boolean isSameFile(FileObject a, FileObject b) {
   944         nullCheck(a);
   945         nullCheck(b);
   946         if (!(a instanceof BaseFileObject))
   947             throw new IllegalArgumentException("Not supported: " + a);
   948         if (!(b instanceof BaseFileObject))
   949             throw new IllegalArgumentException("Not supported: " + b);
   950         return a.equals(b);
   951     }
   953     public boolean handleOption(String current, Iterator<String> remaining) {
   954         for (JavacOption o: javacFileManagerOptions) {
   955             if (o.matches(current))  {
   956                 if (o.hasArg()) {
   957                     if (remaining.hasNext()) {
   958                         if (!o.process(options, current, remaining.next()))
   959                             return true;
   960                     }
   961                 } else {
   962                     if (!o.process(options, current))
   963                         return true;
   964                 }
   965                 // operand missing, or process returned false
   966                 throw new IllegalArgumentException(current);
   967             }
   968         }
   970         return false;
   971     }
   972     // where
   973         private static JavacOption[] javacFileManagerOptions =
   974             RecognizedOptions.getJavacFileManagerOptions(
   975             new RecognizedOptions.GrumpyHelper());
   977     public int isSupportedOption(String option) {
   978         for (JavacOption o : javacFileManagerOptions) {
   979             if (o.matches(option))
   980                 return o.hasArg() ? 1 : 0;
   981         }
   982         return -1;
   983     }
   985     public boolean hasLocation(Location location) {
   986         return getLocation(location) != null;
   987     }
   989     public JavaFileObject getJavaFileForInput(Location location,
   990                                               String className,
   991                                               JavaFileObject.Kind kind)
   992         throws IOException
   993     {
   994         nullCheck(location);
   995         // validateClassName(className);
   996         nullCheck(className);
   997         nullCheck(kind);
   998         if (!sourceOrClass.contains(kind))
   999             throw new IllegalArgumentException("Invalid kind " + kind);
  1000         return getFileForInput(location, externalizeFileName(className, kind));
  1003     public FileObject getFileForInput(Location location,
  1004                                       String packageName,
  1005                                       String relativeName)
  1006         throws IOException
  1008         nullCheck(location);
  1009         // validatePackageName(packageName);
  1010         nullCheck(packageName);
  1011         if (!isRelativeUri(URI.create(relativeName))) // FIXME 6419701
  1012             throw new IllegalArgumentException("Invalid relative name: " + relativeName);
  1013         String name = packageName.length() == 0
  1014             ? relativeName
  1015             : new File(externalizeFileName(packageName), relativeName).getPath();
  1016         return getFileForInput(location, name);
  1019     private JavaFileObject getFileForInput(Location location, String name) throws IOException {
  1020         Iterable<? extends File> path = getLocation(location);
  1021         if (path == null)
  1022             return null;
  1024         for (File dir: path) {
  1025             if (dir.isDirectory()) {
  1026                 File f = new File(dir, name.replace('/', File.separatorChar));
  1027                 if (f.exists())
  1028                     return new RegularFileObject(f);
  1029             } else {
  1030                 Archive a = openArchive(dir);
  1031                 if (a.contains(name)) {
  1032                     int i = name.lastIndexOf('/');
  1033                     String dirname = name.substring(0, i+1);
  1034                     String basename = name.substring(i+1);
  1035                     return a.getFileObject(dirname, basename);
  1040         return null;
  1044     public JavaFileObject getJavaFileForOutput(Location location,
  1045                                                String className,
  1046                                                JavaFileObject.Kind kind,
  1047                                                FileObject sibling)
  1048         throws IOException
  1050         nullCheck(location);
  1051         // validateClassName(className);
  1052         nullCheck(className);
  1053         nullCheck(kind);
  1054         if (!sourceOrClass.contains(kind))
  1055             throw new IllegalArgumentException("Invalid kind " + kind);
  1056         return getFileForOutput(location, externalizeFileName(className, kind), sibling);
  1059     public FileObject getFileForOutput(Location location,
  1060                                        String packageName,
  1061                                        String relativeName,
  1062                                        FileObject sibling)
  1063         throws IOException
  1065         nullCheck(location);
  1066         // validatePackageName(packageName);
  1067         nullCheck(packageName);
  1068         if (!isRelativeUri(URI.create(relativeName))) // FIXME 6419701
  1069             throw new IllegalArgumentException("relativeName is invalid");
  1070         String name = packageName.length() == 0
  1071             ? relativeName
  1072             : new File(externalizeFileName(packageName), relativeName).getPath();
  1073         return getFileForOutput(location, name, sibling);
  1076     private JavaFileObject getFileForOutput(Location location,
  1077                                             String fileName,
  1078                                             FileObject sibling)
  1079         throws IOException
  1081         File dir;
  1082         if (location == CLASS_OUTPUT) {
  1083             if (getClassOutDir() != null) {
  1084                 dir = getClassOutDir();
  1085             } else {
  1086                 File siblingDir = null;
  1087                 if (sibling != null && sibling instanceof RegularFileObject) {
  1088                     siblingDir = ((RegularFileObject)sibling).f.getParentFile();
  1090                 return new RegularFileObject(new File(siblingDir, baseName(fileName)));
  1092         } else if (location == SOURCE_OUTPUT) {
  1093             dir = (getSourceOutDir() != null ? getSourceOutDir() : getClassOutDir());
  1094         } else {
  1095             Iterable<? extends File> path = paths.getPathForLocation(location);
  1096             dir = null;
  1097             for (File f: path) {
  1098                 dir = f;
  1099                 break;
  1103         File file = (dir == null ? new File(fileName) : new File(dir, fileName));
  1104         return new RegularFileObject(file);
  1108     public Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles(
  1109         Iterable<? extends File> files)
  1111         ArrayList<RegularFileObject> result;
  1112         if (files instanceof Collection)
  1113             result = new ArrayList<RegularFileObject>(((Collection)files).size());
  1114         else
  1115             result = new ArrayList<RegularFileObject>();
  1116         for (File f: files)
  1117             result.add(new RegularFileObject(nullCheck(f)));
  1118         return result;
  1121     public Iterable<? extends JavaFileObject> getJavaFileObjects(File... files) {
  1122         return getJavaFileObjectsFromFiles(Arrays.asList(nullCheck(files)));
  1125     public void setLocation(Location location,
  1126                             Iterable<? extends File> path)
  1127         throws IOException
  1129         nullCheck(location);
  1130         paths.lazy();
  1132         final File dir = location.isOutputLocation() ? getOutputDirectory(path) : null;
  1134         if (location == CLASS_OUTPUT)
  1135             classOutDir = getOutputLocation(dir, D);
  1136         else if (location == SOURCE_OUTPUT)
  1137             sourceOutDir = getOutputLocation(dir, S);
  1138         else
  1139             paths.setPathForLocation(location, path);
  1141     // where
  1142         private File getOutputDirectory(Iterable<? extends File> path) throws IOException {
  1143             if (path == null)
  1144                 return null;
  1145             Iterator<? extends File> pathIter = path.iterator();
  1146             if (!pathIter.hasNext())
  1147                 throw new IllegalArgumentException("empty path for directory");
  1148             File dir = pathIter.next();
  1149             if (pathIter.hasNext())
  1150                 throw new IllegalArgumentException("path too long for directory");
  1151             if (!dir.exists())
  1152                 throw new FileNotFoundException(dir + ": does not exist");
  1153             else if (!dir.isDirectory())
  1154                 throw new IOException(dir + ": not a directory");
  1155             return dir;
  1158     private File getOutputLocation(File dir, OptionName defaultOptionName) {
  1159         if (dir != null)
  1160             return dir;
  1161         String arg = options.get(defaultOptionName);
  1162         if (arg == null)
  1163             return null;
  1164         return new File(arg);
  1167     public Iterable<? extends File> getLocation(Location location) {
  1168         nullCheck(location);
  1169         paths.lazy();
  1170         if (location == CLASS_OUTPUT) {
  1171             return (getClassOutDir() == null ? null : List.of(getClassOutDir()));
  1172         } else if (location == SOURCE_OUTPUT) {
  1173             return (getSourceOutDir() == null ? null : List.of(getSourceOutDir()));
  1174         } else
  1175             return paths.getPathForLocation(location);
  1178     private File getClassOutDir() {
  1179         if (classOutDir == uninited)
  1180             classOutDir = getOutputLocation(null, D);
  1181         return classOutDir;
  1184     private File getSourceOutDir() {
  1185         if (sourceOutDir == uninited)
  1186             sourceOutDir = getOutputLocation(null, S);
  1187         return sourceOutDir;
  1190     /**
  1191      * Enforces the specification of a "relative" URI as used in
  1192      * {@linkplain #getFileForInput(Location,String,URI)
  1193      * getFileForInput}.  This method must follow the rules defined in
  1194      * that method, do not make any changes without consulting the
  1195      * specification.
  1196      */
  1197     protected static boolean isRelativeUri(URI uri) {
  1198         if (uri.isAbsolute())
  1199             return false;
  1200         String path = uri.normalize().getPath();
  1201         if (path.length() == 0 /* isEmpty() is mustang API */)
  1202             return false;
  1203         char first = path.charAt(0);
  1204         return first != '.' && first != '/';
  1207     /**
  1208      * Converts a relative file name to a relative URI.  This is
  1209      * different from File.toURI as this method does not canonicalize
  1210      * the file before creating the URI.  Furthermore, no schema is
  1211      * used.
  1212      * @param file a relative file name
  1213      * @return a relative URI
  1214      * @throws IllegalArgumentException if the file name is not
  1215      * relative according to the definition given in {@link
  1216      * javax.tools.JavaFileManager#getFileForInput}
  1217      */
  1218     public static String getRelativeName(File file) {
  1219         if (!file.isAbsolute()) {
  1220             String result = file.getPath().replace(File.separatorChar, '/');
  1221             if (JavacFileManager.isRelativeUri(URI.create(result))) // FIXME 6419701
  1222                 return result;
  1224         throw new IllegalArgumentException("Invalid relative path: " + file);
  1227     @SuppressWarnings("deprecation") // bug 6410637
  1228     protected static String getJavacFileName(FileObject file) {
  1229         if (file instanceof BaseFileObject)
  1230             return ((BaseFileObject)file).getPath();
  1231         URI uri = file.toUri();
  1232         String scheme = uri.getScheme();
  1233         if (scheme == null || scheme.equals("file") || scheme.equals("jar"))
  1234             return uri.getPath();
  1235         else
  1236             return uri.toString();
  1239     @SuppressWarnings("deprecation") // bug 6410637
  1240     protected static String getJavacBaseFileName(FileObject file) {
  1241         if (file instanceof BaseFileObject)
  1242             return ((BaseFileObject)file).getName();
  1243         URI uri = file.toUri();
  1244         String scheme = uri.getScheme();
  1245         if (scheme == null || scheme.equals("file") || scheme.equals("jar")) {
  1246             String path = uri.getPath();
  1247             if (path == null)
  1248                 return null;
  1249             if (scheme != null && scheme.equals("jar"))
  1250                 path = path.substring(path.lastIndexOf('!') + 1);
  1251             return path.substring(path.lastIndexOf('/') + 1);
  1252         } else {
  1253             return uri.toString();
  1257     private static <T> T nullCheck(T o) {
  1258         o.getClass(); // null check
  1259         return o;
  1262     private static <T> Iterable<T> nullCheck(Iterable<T> it) {
  1263         for (T t : it)
  1264             t.getClass(); // null check
  1265         return it;
  1268     /**
  1269      * A subclass of JavaFileObject representing regular files.
  1270      */
  1271     private class RegularFileObject extends BaseFileObject {
  1272         /** Have the parent directories been created?
  1273          */
  1274         private boolean hasParents=false;
  1276         /** The file's name.
  1277          */
  1278         private String name;
  1280         /** The underlying file.
  1281          */
  1282         final File f;
  1284         public RegularFileObject(File f) {
  1285             this(f.getName(), f);
  1288         public RegularFileObject(String name, File f) {
  1289             if (f.isDirectory())
  1290                 throw new IllegalArgumentException("directories not supported");
  1291             this.name = name;
  1292             this.f = f;
  1295         public InputStream openInputStream() throws IOException {
  1296             return new FileInputStream(f);
  1299         protected CharsetDecoder getDecoder(boolean ignoreEncodingErrors) {
  1300             return JavacFileManager.this.getDecoder(getEncodingName(), ignoreEncodingErrors);
  1303         public OutputStream openOutputStream() throws IOException {
  1304             ensureParentDirectoriesExist();
  1305             return new FileOutputStream(f);
  1308         public Writer openWriter() throws IOException {
  1309             ensureParentDirectoriesExist();
  1310             return new OutputStreamWriter(new FileOutputStream(f), getEncodingName());
  1313         private void ensureParentDirectoriesExist() throws IOException {
  1314             if (!hasParents) {
  1315                 File parent = f.getParentFile();
  1316                 if (parent != null && !parent.exists()) {
  1317                     if (!parent.mkdirs()) {
  1318                         // if the mkdirs failed, it may be because another process concurrently
  1319                         // created the directory, so check if the directory got created
  1320                         // anyway before throwing an exception
  1321                         if (!parent.exists() || !parent.isDirectory())
  1322                             throw new IOException("could not create parent directories");
  1325                 hasParents = true;
  1329         /** @deprecated see bug 6410637 */
  1330         @Deprecated
  1331         public String getName() {
  1332             return name;
  1335         public boolean isNameCompatible(String cn, JavaFileObject.Kind kind) {
  1336             cn.getClass(); // null check
  1337             if (kind == Kind.OTHER && getKind() != kind)
  1338                 return false;
  1339             String n = cn + kind.extension;
  1340             if (name.equals(n))
  1341                 return true;
  1342             if (name.equalsIgnoreCase(n)) {
  1343                 try {
  1344                     // allow for Windows
  1345                     return (f.getCanonicalFile().getName().equals(n));
  1346                 } catch (IOException e) {
  1349             return false;
  1352         /** @deprecated see bug 6410637 */
  1353         @Deprecated
  1354         public String getPath() {
  1355             return f.getPath();
  1358         public long getLastModified() {
  1359             return f.lastModified();
  1362         public boolean delete() {
  1363             return f.delete();
  1366         public CharBuffer getCharContent(boolean ignoreEncodingErrors) throws IOException {
  1367             SoftReference<CharBuffer> r = contentCache.get(this);
  1368             CharBuffer cb = (r == null ? null : r.get());
  1369             if (cb == null) {
  1370                 InputStream in = new FileInputStream(f);
  1371                 try {
  1372                     ByteBuffer bb = makeByteBuffer(in);
  1373                     JavaFileObject prev = log.useSource(this);
  1374                     try {
  1375                         cb = decode(bb, ignoreEncodingErrors);
  1376                     } finally {
  1377                         log.useSource(prev);
  1379                     byteBufferCache.put(bb); // save for next time
  1380                     if (!ignoreEncodingErrors)
  1381                         contentCache.put(this, new SoftReference<CharBuffer>(cb));
  1382                 } finally {
  1383                     in.close();
  1386             return cb;
  1389         @Override
  1390         public boolean equals(Object other) {
  1391             if (!(other instanceof RegularFileObject))
  1392                 return false;
  1393             RegularFileObject o = (RegularFileObject) other;
  1394             try {
  1395                 return f.equals(o.f)
  1396                     || f.getCanonicalFile().equals(o.f.getCanonicalFile());
  1397             } catch (IOException e) {
  1398                 return false;
  1402         @Override
  1403         public int hashCode() {
  1404             return f.hashCode();
  1407         public URI toUri() {
  1408             try {
  1409                 // Do no use File.toURI to avoid file system access
  1410                 String path = f.getAbsolutePath().replace(File.separatorChar, '/');
  1411                 return new URI("file://" + path).normalize();
  1412             } catch (URISyntaxException ex) {
  1413                 return f.toURI();
  1419     /**
  1420      * A subclass of JavaFileObject representing zip entries.
  1421      */
  1422     public class ZipFileObject extends BaseFileObject {
  1424         /** The entry's name.
  1425          */
  1426         private String name;
  1428         /** The zipfile containing the entry.
  1429          */
  1430         ZipFile zdir;
  1432         /** The underlying zip entry object.
  1433          */
  1434         ZipEntry entry;
  1436         public ZipFileObject(String name, ZipFile zdir, ZipEntry entry) {
  1437             this.name = name;
  1438             this.zdir = zdir;
  1439             this.entry = entry;
  1442         public InputStream openInputStream() throws IOException {
  1443             return zdir.getInputStream(entry);
  1446         public OutputStream openOutputStream() throws IOException {
  1447             throw new UnsupportedOperationException();
  1450         protected CharsetDecoder getDecoder(boolean ignoreEncodingErrors) {
  1451             return JavacFileManager.this.getDecoder(getEncodingName(), ignoreEncodingErrors);
  1454         public Writer openWriter() throws IOException {
  1455             throw new UnsupportedOperationException();
  1458         /** @deprecated see bug 6410637 */
  1459         @Deprecated
  1460         public String getName() {
  1461             return name;
  1464         public boolean isNameCompatible(String cn, JavaFileObject.Kind k) {
  1465             cn.getClass(); // null check
  1466             if (k == Kind.OTHER && getKind() != k)
  1467                 return false;
  1468             return name.equals(cn + k.extension);
  1471         /** @deprecated see bug 6410637 */
  1472         @Deprecated
  1473         public String getPath() {
  1474             return zdir.getName() + "(" + entry + ")";
  1477         public long getLastModified() {
  1478             return entry.getTime();
  1481         public boolean delete() {
  1482             throw new UnsupportedOperationException();
  1485         public CharBuffer getCharContent(boolean ignoreEncodingErrors) throws IOException {
  1486             SoftReference<CharBuffer> r = contentCache.get(this);
  1487             CharBuffer cb = (r == null ? null : r.get());
  1488             if (cb == null) {
  1489                 InputStream in = zdir.getInputStream(entry);
  1490                 try {
  1491                     ByteBuffer bb = makeByteBuffer(in);
  1492                     JavaFileObject prev = log.useSource(this);
  1493                     try {
  1494                         cb = decode(bb, ignoreEncodingErrors);
  1495                     } finally {
  1496                         log.useSource(prev);
  1498                     byteBufferCache.put(bb); // save for next time
  1499                     if (!ignoreEncodingErrors)
  1500                         contentCache.put(this, new SoftReference<CharBuffer>(cb));
  1501                 } finally {
  1502                     in.close();
  1505             return cb;
  1508         @Override
  1509         public boolean equals(Object other) {
  1510             if (!(other instanceof ZipFileObject))
  1511                 return false;
  1512             ZipFileObject o = (ZipFileObject) other;
  1513             return zdir.equals(o.zdir) || name.equals(o.name);
  1516         @Override
  1517         public int hashCode() {
  1518             return zdir.hashCode() + name.hashCode();
  1521         public String getZipName() {
  1522             return zdir.getName();
  1525         public String getZipEntryName() {
  1526             return entry.getName();
  1529         public URI toUri() {
  1530             String zipName = new File(getZipName()).toURI().normalize().getPath();
  1531             String entryName = getZipEntryName();
  1532             return URI.create("jar:" + zipName + "!" + entryName);
  1537     /**
  1538      * A subclass of JavaFileObject representing zip entries using the com.sun.tools.javac.zip.ZipFileIndex implementation.
  1539      */
  1540     public class ZipFileIndexFileObject extends BaseFileObject {
  1542             /** The entry's name.
  1543          */
  1544         private String name;
  1546         /** The zipfile containing the entry.
  1547          */
  1548         ZipFileIndex zfIndex;
  1550         /** The underlying zip entry object.
  1551          */
  1552         ZipFileIndexEntry entry;
  1554         /** The InputStream for this zip entry (file.)
  1555          */
  1556         InputStream inputStream = null;
  1558         /** The name of the zip file where this entry resides.
  1559          */
  1560         String zipName;
  1562         JavacFileManager defFileManager = null;
  1564         public ZipFileIndexFileObject(JavacFileManager fileManager, ZipFileIndex zfIndex, ZipFileIndexEntry entry, String zipFileName) {
  1565             super();
  1566             this.name = entry.getFileName();
  1567             this.zfIndex = zfIndex;
  1568             this.entry = entry;
  1569             this.zipName = zipFileName;
  1570             defFileManager = fileManager;
  1573         public InputStream openInputStream() throws IOException {
  1575             if (inputStream == null) {
  1576                 inputStream = new ByteArrayInputStream(read());
  1578             return inputStream;
  1581         protected CharsetDecoder getDecoder(boolean ignoreEncodingErrors) {
  1582             return JavacFileManager.this.getDecoder(getEncodingName(), ignoreEncodingErrors);
  1585         public OutputStream openOutputStream() throws IOException {
  1586             throw new UnsupportedOperationException();
  1589         public Writer openWriter() throws IOException {
  1590             throw new UnsupportedOperationException();
  1593         /** @deprecated see bug 6410637 */
  1594         @Deprecated
  1595         public String getName() {
  1596             return name;
  1599         public boolean isNameCompatible(String cn, JavaFileObject.Kind k) {
  1600             cn.getClass(); // null check
  1601             if (k == Kind.OTHER && getKind() != k)
  1602                 return false;
  1603             return name.equals(cn + k.extension);
  1606         /** @deprecated see bug 6410637 */
  1607         @Deprecated
  1608         public String getPath() {
  1609             return entry.getName() + "(" + entry + ")";
  1612         public long getLastModified() {
  1613             return entry.getLastModified();
  1616         public boolean delete() {
  1617             throw new UnsupportedOperationException();
  1620         @Override
  1621         public boolean equals(Object other) {
  1622             if (!(other instanceof ZipFileIndexFileObject))
  1623                 return false;
  1624             ZipFileIndexFileObject o = (ZipFileIndexFileObject) other;
  1625             return entry.equals(o.entry);
  1628         @Override
  1629         public int hashCode() {
  1630             return zipName.hashCode() + (name.hashCode() << 10);
  1633         public String getZipName() {
  1634             return zipName;
  1637         public String getZipEntryName() {
  1638             return entry.getName();
  1641         public URI toUri() {
  1642             String zipName = new File(getZipName()).toURI().normalize().getPath();
  1643             String entryName = getZipEntryName();
  1644             if (File.separatorChar != '/') {
  1645                 entryName = entryName.replace(File.separatorChar, '/');
  1647             return URI.create("jar:" + zipName + "!" + entryName);
  1650         private byte[] read() throws IOException {
  1651             if (entry == null) {
  1652                 entry = zfIndex.getZipIndexEntry(name);
  1653                 if (entry == null)
  1654                   throw new FileNotFoundException();
  1656             return zfIndex.read(entry);
  1659         public CharBuffer getCharContent(boolean ignoreEncodingErrors) throws IOException {
  1660             SoftReference<CharBuffer> r = defFileManager.contentCache.get(this);
  1661             CharBuffer cb = (r == null ? null : r.get());
  1662             if (cb == null) {
  1663                 InputStream in = new ByteArrayInputStream(zfIndex.read(entry));
  1664                 try {
  1665                     ByteBuffer bb = makeByteBuffer(in);
  1666                     JavaFileObject prev = log.useSource(this);
  1667                     try {
  1668                         cb = decode(bb, ignoreEncodingErrors);
  1669                     } finally {
  1670                         log.useSource(prev);
  1672                     byteBufferCache.put(bb); // save for next time
  1673                     if (!ignoreEncodingErrors)
  1674                         defFileManager.contentCache.put(this, new SoftReference<CharBuffer>(cb));
  1675                 } finally {
  1676                     in.close();
  1679             return cb;
  1683     public class ZipFileIndexArchive implements Archive {
  1684         private final ZipFileIndex zfIndex;
  1685         private JavacFileManager fileManager;
  1687         public ZipFileIndexArchive(JavacFileManager fileManager, ZipFileIndex zdir) throws IOException {
  1688             this.fileManager = fileManager;
  1689             this.zfIndex = zdir;
  1692         public boolean contains(String name) {
  1693             return zfIndex.contains(name);
  1696         public com.sun.tools.javac.util.List<String> getFiles(String subdirectory) {
  1697               return zfIndex.getFiles(((subdirectory.endsWith("/") || subdirectory.endsWith("\\"))? subdirectory.substring(0, subdirectory.length() - 1) : subdirectory));
  1700         public JavaFileObject getFileObject(String subdirectory, String file) {
  1701             String fullZipFileName = subdirectory + file;
  1702             ZipFileIndexEntry entry = zfIndex.getZipIndexEntry(fullZipFileName);
  1703             JavaFileObject ret = new ZipFileIndexFileObject(fileManager, zfIndex, entry, zfIndex.getZipFile().getPath());
  1704             return ret;
  1707         public Set<String> getSubdirectories() {
  1708             return zfIndex.getAllDirectories();
  1711         public void close() throws IOException {
  1712             zfIndex.close();

mercurial