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

Wed, 03 Sep 2008 10:46:25 -0700

author
jjg
date
Wed, 03 Sep 2008 10:46:25 -0700
changeset 106
ceaa6549687a
parent 103
e571266ae14f
child 184
905e151a185a
permissions
-rw-r--r--

6743107: clean up use of static caches in file manager
Reviewed-by: mcimadamore

     1 /*
     2  * Copyright 2005-2008 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.file;
    28 import java.io.ByteArrayOutputStream;
    29 import java.io.File;
    30 import java.io.FileInputStream;
    31 import java.io.FileNotFoundException;
    32 import java.io.IOException;
    33 import java.io.InputStream;
    34 import java.io.OutputStreamWriter;
    35 import java.lang.ref.SoftReference;
    36 import java.net.MalformedURLException;
    37 import java.net.URI;
    38 import java.net.URL;
    39 import java.net.URLClassLoader;
    40 import java.nio.ByteBuffer;
    41 import java.nio.CharBuffer;
    42 import java.nio.channels.FileChannel;
    43 import java.nio.charset.Charset;
    44 import java.nio.charset.CharsetDecoder;
    45 import java.nio.charset.CoderResult;
    46 import java.nio.charset.CodingErrorAction;
    47 import java.nio.charset.IllegalCharsetNameException;
    48 import java.nio.charset.UnsupportedCharsetException;
    49 import java.util.ArrayList;
    50 import java.util.Arrays;
    51 import java.util.Collection;
    52 import java.util.Collections;
    53 import java.util.EnumSet;
    54 import java.util.HashMap;
    55 import java.util.Iterator;
    56 import java.util.Map;
    57 import java.util.Set;
    58 import java.util.zip.ZipFile;
    60 import javax.lang.model.SourceVersion;
    61 import javax.tools.FileObject;
    62 import javax.tools.JavaFileManager;
    63 import javax.tools.JavaFileObject;
    64 import javax.tools.StandardJavaFileManager;
    66 import com.sun.tools.javac.code.Source;
    67 import com.sun.tools.javac.file.RelativePath.RelativeFile;
    68 import com.sun.tools.javac.file.RelativePath.RelativeDirectory;
    69 import com.sun.tools.javac.main.JavacOption;
    70 import com.sun.tools.javac.main.OptionName;
    71 import com.sun.tools.javac.main.RecognizedOptions;
    72 import com.sun.tools.javac.util.Context;
    73 import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition;
    74 import com.sun.tools.javac.util.List;
    75 import com.sun.tools.javac.util.ListBuffer;
    76 import com.sun.tools.javac.util.Log;
    77 import com.sun.tools.javac.util.Options;
    79 import static javax.tools.StandardLocation.*;
    80 import static com.sun.tools.javac.main.OptionName.*;
    82 /**
    83  * This class provides access to the source, class and other files
    84  * used by the compiler and related tools.
    85  */
    86 public class JavacFileManager implements StandardJavaFileManager {
    88     boolean useZipFileIndex;
    90     public static char[] toArray(CharBuffer buffer) {
    91         if (buffer.hasArray())
    92             return ((CharBuffer)buffer.compact().flip()).array();
    93         else
    94             return buffer.toString().toCharArray();
    95     }
    97     /**
    98      * The log to be used for error reporting.
    99      */
   100     protected Log log;
   102     /** Encapsulates knowledge of paths
   103      */
   104     private Paths paths;
   106     private Options options;
   108     private FSInfo fsInfo;
   110     private final File uninited = new File("U N I N I T E D");
   112     private final Set<JavaFileObject.Kind> sourceOrClass =
   113         EnumSet.of(JavaFileObject.Kind.SOURCE, JavaFileObject.Kind.CLASS);
   115     /** The standard output directory, primarily used for classes.
   116      *  Initialized by the "-d" option.
   117      *  If classOutDir = null, files are written into same directory as the sources
   118      *  they were generated from.
   119      */
   120     private File classOutDir = uninited;
   122     /** The output directory, used when generating sources while processing annotations.
   123      *  Initialized by the "-s" option.
   124      */
   125     private File sourceOutDir = uninited;
   127     protected boolean mmappedIO;
   128     protected boolean ignoreSymbolFile;
   130     /**
   131      * User provided charset (through javax.tools).
   132      */
   133     protected Charset charset;
   135     /**
   136      * Register a Context.Factory to create a JavacFileManager.
   137      */
   138     public static void preRegister(final Context context) {
   139         context.put(JavaFileManager.class, new Context.Factory<JavaFileManager>() {
   140             public JavaFileManager make() {
   141                 return new JavacFileManager(context, true, null);
   142             }
   143         });
   144     }
   146     /**
   147      * Create a JavacFileManager using a given context, optionally registering
   148      * it as the JavaFileManager for that context.
   149      */
   150     public JavacFileManager(Context context, boolean register, Charset charset) {
   151         if (register)
   152             context.put(JavaFileManager.class, this);
   153         byteBufferCache = new ByteBufferCache();
   154         this.charset = charset;
   155         setContext(context);
   156     }
   158     /**
   159      * Set the context for JavacFileManager.
   160      */
   161     public void setContext(Context context) {
   162         log = Log.instance(context);
   163         if (paths == null) {
   164             paths = Paths.instance(context);
   165         } else {
   166             // Reuse the Paths object as it stores the locations that
   167             // have been set with setLocation, etc.
   168             paths.setContext(context);
   169         }
   171         options = Options.instance(context);
   172         fsInfo = FSInfo.instance(context);
   174         useZipFileIndex = System.getProperty("useJavaUtilZip") == null;// TODO: options.get("useJavaUtilZip") == null;
   176         mmappedIO = options.get("mmappedIO") != null;
   177         ignoreSymbolFile = options.get("ignore.symbol.file") != null;
   178     }
   180     public JavaFileObject getFileForInput(String name) {
   181         return getRegularFile(new File(name));
   182     }
   184     public JavaFileObject getRegularFile(File file) {
   185         return new RegularFileObject(this, file);
   186     }
   188     public JavaFileObject getFileForOutput(String classname,
   189                                            JavaFileObject.Kind kind,
   190                                            JavaFileObject sibling)
   191         throws IOException
   192     {
   193         return getJavaFileForOutput(CLASS_OUTPUT, classname, kind, sibling);
   194     }
   196     public Iterable<? extends JavaFileObject> getJavaFileObjectsFromStrings(Iterable<String> names) {
   197         ListBuffer<File> files = new ListBuffer<File>();
   198         for (String name : names)
   199             files.append(new File(nullCheck(name)));
   200         return getJavaFileObjectsFromFiles(files.toList());
   201     }
   203     public Iterable<? extends JavaFileObject> getJavaFileObjects(String... names) {
   204         return getJavaFileObjectsFromStrings(Arrays.asList(nullCheck(names)));
   205     }
   207     protected JavaFileObject.Kind getKind(String extension) {
   208         if (extension.equals(JavaFileObject.Kind.CLASS.extension))
   209             return JavaFileObject.Kind.CLASS;
   210         else if (extension.equals(JavaFileObject.Kind.SOURCE.extension))
   211             return JavaFileObject.Kind.SOURCE;
   212         else if (extension.equals(JavaFileObject.Kind.HTML.extension))
   213             return JavaFileObject.Kind.HTML;
   214         else
   215             return JavaFileObject.Kind.OTHER;
   216     }
   218     private static boolean isValidName(String name) {
   219         // Arguably, isValidName should reject keywords (such as in SourceVersion.isName() ),
   220         // but the set of keywords depends on the source level, and we don't want
   221         // impls of JavaFileManager to have to be dependent on the source level.
   222         // Therefore we simply check that the argument is a sequence of identifiers
   223         // separated by ".".
   224         for (String s : name.split("\\.", -1)) {
   225             if (!SourceVersion.isIdentifier(s))
   226                 return false;
   227         }
   228         return true;
   229     }
   231     private static void validateClassName(String className) {
   232         if (!isValidName(className))
   233             throw new IllegalArgumentException("Invalid class name: " + className);
   234     }
   236     private static void validatePackageName(String packageName) {
   237         if (packageName.length() > 0 && !isValidName(packageName))
   238             throw new IllegalArgumentException("Invalid packageName name: " + packageName);
   239     }
   241     public static void testName(String name,
   242                                 boolean isValidPackageName,
   243                                 boolean isValidClassName)
   244     {
   245         try {
   246             validatePackageName(name);
   247             if (!isValidPackageName)
   248                 throw new AssertionError("Invalid package name accepted: " + name);
   249             printAscii("Valid package name: \"%s\"", name);
   250         } catch (IllegalArgumentException e) {
   251             if (isValidPackageName)
   252                 throw new AssertionError("Valid package name rejected: " + name);
   253             printAscii("Invalid package name: \"%s\"", name);
   254         }
   255         try {
   256             validateClassName(name);
   257             if (!isValidClassName)
   258                 throw new AssertionError("Invalid class name accepted: " + name);
   259             printAscii("Valid class name: \"%s\"", name);
   260         } catch (IllegalArgumentException e) {
   261             if (isValidClassName)
   262                 throw new AssertionError("Valid class name rejected: " + name);
   263             printAscii("Invalid class name: \"%s\"", name);
   264         }
   265     }
   267     private static void printAscii(String format, Object... args) {
   268         String message;
   269         try {
   270             final String ascii = "US-ASCII";
   271             message = new String(String.format(null, format, args).getBytes(ascii), ascii);
   272         } catch (java.io.UnsupportedEncodingException ex) {
   273             throw new AssertionError(ex);
   274         }
   275         System.out.println(message);
   276     }
   278     /**
   279      * Insert all files in subdirectory `subdirectory' of `directory' which end
   280      * in one of the extensions in `extensions' into packageSym.
   281      */
   282     private void listDirectory(File directory,
   283                                RelativeDirectory subdirectory,
   284                                Set<JavaFileObject.Kind> fileKinds,
   285                                boolean recurse,
   286                                ListBuffer<JavaFileObject> l) {
   287         Archive archive = archives.get(directory);
   289         boolean isFile = fsInfo.isFile(directory);
   291         if (archive != null || isFile) {
   292             if (archive == null) {
   293                 try {
   294                     archive = openArchive(directory);
   295                 } catch (IOException ex) {
   296                     log.error("error.reading.file",
   297                        directory, ex.getLocalizedMessage());
   298                     return;
   299                 }
   300             }
   302             List<String> files = archive.getFiles(subdirectory);
   303             if (files != null) {
   304                 for (String file; !files.isEmpty(); files = files.tail) {
   305                     file = files.head;
   306                     if (isValidFile(file, fileKinds)) {
   307                         l.append(archive.getFileObject(subdirectory, file));
   308                     }
   309                 }
   310             }
   311             if (recurse) {
   312                 for (RelativeDirectory s: archive.getSubdirectories()) {
   313                     if (subdirectory.contains(s)) {
   314                         // Because the archive map is a flat list of directories,
   315                         // the enclosing loop will pick up all child subdirectories.
   316                         // Therefore, there is no need to recurse deeper.
   317                         listDirectory(directory, s, fileKinds, false, l);
   318                     }
   319                 }
   320             }
   321         } else {
   322             File d = subdirectory.getFile(directory);
   323             if (!caseMapCheck(d, subdirectory))
   324                 return;
   326             File[] files = d.listFiles();
   327             if (files == null)
   328                 return;
   330             for (File f: files) {
   331                 String fname = f.getName();
   332                 if (f.isDirectory()) {
   333                     if (recurse && SourceVersion.isIdentifier(fname)) {
   334                         listDirectory(directory,
   335                                       new RelativeDirectory(subdirectory, fname),
   336                                       fileKinds,
   337                                       recurse,
   338                                       l);
   339                     }
   340                 } else {
   341                     if (isValidFile(fname, fileKinds)) {
   342                         JavaFileObject fe =
   343                             new RegularFileObject(this, fname, new File(d, fname));
   344                         l.append(fe);
   345                     }
   346                 }
   347             }
   348         }
   349     }
   351     private boolean isValidFile(String s, Set<JavaFileObject.Kind> fileKinds) {
   352         int lastDot = s.lastIndexOf(".");
   353         String extn = (lastDot == -1 ? s : s.substring(lastDot));
   354         JavaFileObject.Kind kind = getKind(extn);
   355         return fileKinds.contains(kind);
   356     }
   358     private static final boolean fileSystemIsCaseSensitive =
   359         File.separatorChar == '/';
   361     /** Hack to make Windows case sensitive. Test whether given path
   362      *  ends in a string of characters with the same case as given name.
   363      *  Ignore file separators in both path and name.
   364      */
   365     private boolean caseMapCheck(File f, RelativePath name) {
   366         if (fileSystemIsCaseSensitive) return true;
   367         // Note that getCanonicalPath() returns the case-sensitive
   368         // spelled file name.
   369         String path;
   370         try {
   371             path = f.getCanonicalPath();
   372         } catch (IOException ex) {
   373             return false;
   374         }
   375         char[] pcs = path.toCharArray();
   376         char[] ncs = name.path.toCharArray();
   377         int i = pcs.length - 1;
   378         int j = ncs.length - 1;
   379         while (i >= 0 && j >= 0) {
   380             while (i >= 0 && pcs[i] == File.separatorChar) i--;
   381             while (j >= 0 && ncs[j] == '/') j--;
   382             if (i >= 0 && j >= 0) {
   383                 if (pcs[i] != ncs[j]) return false;
   384                 i--;
   385                 j--;
   386             }
   387         }
   388         return j < 0;
   389     }
   391     /**
   392      * An archive provides a flat directory structure of a ZipFile by
   393      * mapping directory names to lists of files (basenames).
   394      */
   395     public interface Archive {
   396         void close() throws IOException;
   398         boolean contains(RelativePath name);
   400         JavaFileObject getFileObject(RelativeDirectory subdirectory, String file);
   402         List<String> getFiles(RelativeDirectory subdirectory);
   404         Set<RelativeDirectory> getSubdirectories();
   405     }
   407     public class MissingArchive implements Archive {
   408         final File zipFileName;
   409         public MissingArchive(File name) {
   410             zipFileName = name;
   411         }
   412         public boolean contains(RelativePath name) {
   413             return false;
   414         }
   416         public void close() {
   417         }
   419         public JavaFileObject getFileObject(RelativeDirectory subdirectory, String file) {
   420             return null;
   421         }
   423         public List<String> getFiles(RelativeDirectory subdirectory) {
   424             return List.nil();
   425         }
   427         public Set<RelativeDirectory> getSubdirectories() {
   428             return Collections.emptySet();
   429         }
   431         public String toString() {
   432             return "MissingArchive[" + zipFileName + "]";
   433         }
   434     }
   436     /** A directory of zip files already opened.
   437      */
   438     Map<File, Archive> archives = new HashMap<File,Archive>();
   440     private static final String[] symbolFileLocation = { "lib", "ct.sym" };
   441     private static final RelativeDirectory symbolFilePrefix
   442             = new RelativeDirectory("META-INF/sym/rt.jar/");
   444     /** Open a new zip file directory.
   445      */
   446     protected Archive openArchive(File zipFileName) throws IOException {
   447         Archive archive = archives.get(zipFileName);
   448         if (archive == null) {
   449             File origZipFileName = zipFileName;
   450             if (!ignoreSymbolFile && paths.isBootClassPathRtJar(zipFileName)) {
   451                 File file = zipFileName.getParentFile().getParentFile(); // ${java.home}
   452                 if (new File(file.getName()).equals(new File("jre")))
   453                     file = file.getParentFile();
   454                 // file == ${jdk.home}
   455                 for (String name : symbolFileLocation)
   456                     file = new File(file, name);
   457                 // file == ${jdk.home}/lib/ct.sym
   458                 if (file.exists())
   459                     zipFileName = file;
   460             }
   462             try {
   464                 ZipFile zdir = null;
   466                 boolean usePreindexedCache = false;
   467                 String preindexCacheLocation = null;
   469                 if (!useZipFileIndex) {
   470                     zdir = new ZipFile(zipFileName);
   471                 }
   472                 else {
   473                     usePreindexedCache = options.get("usezipindex") != null;
   474                     preindexCacheLocation = options.get("java.io.tmpdir");
   475                     String optCacheLoc = options.get("cachezipindexdir");
   477                     if (optCacheLoc != null && optCacheLoc.length() != 0) {
   478                         if (optCacheLoc.startsWith("\"")) {
   479                             if (optCacheLoc.endsWith("\"")) {
   480                                 optCacheLoc = optCacheLoc.substring(1, optCacheLoc.length() - 1);
   481                             }
   482                            else {
   483                                 optCacheLoc = optCacheLoc.substring(1);
   484                             }
   485                         }
   487                         File cacheDir = new File(optCacheLoc);
   488                         if (cacheDir.exists() && cacheDir.canWrite()) {
   489                             preindexCacheLocation = optCacheLoc;
   490                             if (!preindexCacheLocation.endsWith("/") &&
   491                                 !preindexCacheLocation.endsWith(File.separator)) {
   492                                 preindexCacheLocation += File.separator;
   493                             }
   494                         }
   495                     }
   496                 }
   498                 if (origZipFileName == zipFileName) {
   499                     if (!useZipFileIndex) {
   500                         archive = new ZipArchive(this, zdir);
   501                     } else {
   502                         archive = new ZipFileIndexArchive(this,
   503                                 ZipFileIndex.getZipFileIndex(zipFileName,
   504                                     null,
   505                                     usePreindexedCache,
   506                                     preindexCacheLocation,
   507                                     options.get("writezipindexfiles") != null));
   508                     }
   509                 }
   510                 else {
   511                     if (!useZipFileIndex) {
   512                         archive = new SymbolArchive(this, origZipFileName, zdir, symbolFilePrefix);
   513                     }
   514                     else {
   515                         archive = new ZipFileIndexArchive(this,
   516                                 ZipFileIndex.getZipFileIndex(zipFileName,
   517                                     symbolFilePrefix,
   518                                     usePreindexedCache,
   519                                     preindexCacheLocation,
   520                                     options.get("writezipindexfiles") != null));
   521                     }
   522                 }
   523             } catch (FileNotFoundException ex) {
   524                 archive = new MissingArchive(zipFileName);
   525             } catch (IOException ex) {
   526                 if (zipFileName.exists())
   527                     log.error("error.reading.file", zipFileName, ex.getLocalizedMessage());
   528                 archive = new MissingArchive(zipFileName);
   529             }
   531             archives.put(origZipFileName, archive);
   532         }
   533         return archive;
   534     }
   536     /** Flush any output resources.
   537      */
   538     public void flush() {
   539         contentCache.clear();
   540     }
   542     /**
   543      * Close the JavaFileManager, releasing resources.
   544      */
   545     public void close() {
   546         for (Iterator<Archive> i = archives.values().iterator(); i.hasNext(); ) {
   547             Archive a = i.next();
   548             i.remove();
   549             try {
   550                 a.close();
   551             } catch (IOException e) {
   552             }
   553         }
   554     }
   556     CharBuffer getCachedContent(JavaFileObject file) {
   557         SoftReference<CharBuffer> r = contentCache.get(file);
   558         return (r == null ? null : r.get());
   559     }
   561     void cache(JavaFileObject file, CharBuffer cb) {
   562         contentCache.put(file, new SoftReference<CharBuffer>(cb));
   563     }
   565     private final Map<JavaFileObject, SoftReference<CharBuffer>> contentCache
   566             = new HashMap<JavaFileObject, SoftReference<CharBuffer>>();
   568     private String defaultEncodingName;
   569     private String getDefaultEncodingName() {
   570         if (defaultEncodingName == null) {
   571             defaultEncodingName =
   572                 new OutputStreamWriter(new ByteArrayOutputStream()).getEncoding();
   573         }
   574         return defaultEncodingName;
   575     }
   577     protected String getEncodingName() {
   578         String encName = options.get(OptionName.ENCODING);
   579         if (encName == null)
   580             return getDefaultEncodingName();
   581         else
   582             return encName;
   583     }
   585     protected Source getSource() {
   586         String sourceName = options.get(OptionName.SOURCE);
   587         Source source = null;
   588         if (sourceName != null)
   589             source = Source.lookup(sourceName);
   590         return (source != null ? source : Source.DEFAULT);
   591     }
   593     /**
   594      * Make a byte buffer from an input stream.
   595      */
   596     ByteBuffer makeByteBuffer(InputStream in)
   597         throws IOException {
   598         int limit = in.available();
   599         if (mmappedIO && in instanceof FileInputStream) {
   600             // Experimental memory mapped I/O
   601             FileInputStream fin = (FileInputStream)in;
   602             return fin.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, limit);
   603         }
   604         if (limit < 1024) limit = 1024;
   605         ByteBuffer result = byteBufferCache.get(limit);
   606         int position = 0;
   607         while (in.available() != 0) {
   608             if (position >= limit)
   609                 // expand buffer
   610                 result = ByteBuffer.
   611                     allocate(limit <<= 1).
   612                     put((ByteBuffer)result.flip());
   613             int count = in.read(result.array(),
   614                 position,
   615                 limit - position);
   616             if (count < 0) break;
   617             result.position(position += count);
   618         }
   619         return (ByteBuffer)result.flip();
   620     }
   622     void recycleByteBuffer(ByteBuffer bb) {
   623         byteBufferCache.put(bb);
   624     }
   626     /**
   627      * A single-element cache of direct byte buffers.
   628      */
   629     private static class ByteBufferCache {
   630         private ByteBuffer cached;
   631         ByteBuffer get(int capacity) {
   632             if (capacity < 20480) capacity = 20480;
   633             ByteBuffer result =
   634                 (cached != null && cached.capacity() >= capacity)
   635                 ? (ByteBuffer)cached.clear()
   636                 : ByteBuffer.allocate(capacity + capacity>>1);
   637             cached = null;
   638             return result;
   639         }
   640         void put(ByteBuffer x) {
   641             cached = x;
   642         }
   643     }
   645     private final ByteBufferCache byteBufferCache;
   647     CharsetDecoder getDecoder(String encodingName, boolean ignoreEncodingErrors) {
   648         Charset charset = (this.charset == null)
   649             ? Charset.forName(encodingName)
   650             : this.charset;
   651         CharsetDecoder decoder = charset.newDecoder();
   653         CodingErrorAction action;
   654         if (ignoreEncodingErrors)
   655             action = CodingErrorAction.REPLACE;
   656         else
   657             action = CodingErrorAction.REPORT;
   659         return decoder
   660             .onMalformedInput(action)
   661             .onUnmappableCharacter(action);
   662     }
   664     /**
   665      * Decode a ByteBuffer into a CharBuffer.
   666      */
   667     CharBuffer decode(ByteBuffer inbuf, boolean ignoreEncodingErrors) {
   668         String encodingName = getEncodingName();
   669         CharsetDecoder decoder;
   670         try {
   671             decoder = getDecoder(encodingName, ignoreEncodingErrors);
   672         } catch (IllegalCharsetNameException e) {
   673             log.error("unsupported.encoding", encodingName);
   674             return (CharBuffer)CharBuffer.allocate(1).flip();
   675         } catch (UnsupportedCharsetException e) {
   676             log.error("unsupported.encoding", encodingName);
   677             return (CharBuffer)CharBuffer.allocate(1).flip();
   678         }
   680         // slightly overestimate the buffer size to avoid reallocation.
   681         float factor =
   682             decoder.averageCharsPerByte() * 0.8f +
   683             decoder.maxCharsPerByte() * 0.2f;
   684         CharBuffer dest = CharBuffer.
   685             allocate(10 + (int)(inbuf.remaining()*factor));
   687         while (true) {
   688             CoderResult result = decoder.decode(inbuf, dest, true);
   689             dest.flip();
   691             if (result.isUnderflow()) { // done reading
   692                 // make sure there is at least one extra character
   693                 if (dest.limit() == dest.capacity()) {
   694                     dest = CharBuffer.allocate(dest.capacity()+1).put(dest);
   695                     dest.flip();
   696                 }
   697                 return dest;
   698             } else if (result.isOverflow()) { // buffer too small; expand
   699                 int newCapacity =
   700                     10 + dest.capacity() +
   701                     (int)(inbuf.remaining()*decoder.maxCharsPerByte());
   702                 dest = CharBuffer.allocate(newCapacity).put(dest);
   703             } else if (result.isMalformed() || result.isUnmappable()) {
   704                 // bad character in input
   706                 // report coding error (warn only pre 1.5)
   707                 if (!getSource().allowEncodingErrors()) {
   708                     log.error(new SimpleDiagnosticPosition(dest.limit()),
   709                               "illegal.char.for.encoding",
   710                               charset == null ? encodingName : charset.name());
   711                 } else {
   712                     log.warning(new SimpleDiagnosticPosition(dest.limit()),
   713                                 "illegal.char.for.encoding",
   714                                 charset == null ? encodingName : charset.name());
   715                 }
   717                 // skip past the coding error
   718                 inbuf.position(inbuf.position() + result.length());
   720                 // undo the flip() to prepare the output buffer
   721                 // for more translation
   722                 dest.position(dest.limit());
   723                 dest.limit(dest.capacity());
   724                 dest.put((char)0xfffd); // backward compatible
   725             } else {
   726                 throw new AssertionError(result);
   727             }
   728         }
   729         // unreached
   730     }
   732     public ClassLoader getClassLoader(Location location) {
   733         nullCheck(location);
   734         Iterable<? extends File> path = getLocation(location);
   735         if (path == null)
   736             return null;
   737         ListBuffer<URL> lb = new ListBuffer<URL>();
   738         for (File f: path) {
   739             try {
   740                 lb.append(f.toURI().toURL());
   741             } catch (MalformedURLException e) {
   742                 throw new AssertionError(e);
   743             }
   744         }
   745         return new URLClassLoader(lb.toArray(new URL[lb.size()]),
   746             getClass().getClassLoader());
   747     }
   749     public Iterable<JavaFileObject> list(Location location,
   750                                          String packageName,
   751                                          Set<JavaFileObject.Kind> kinds,
   752                                          boolean recurse)
   753         throws IOException
   754     {
   755         // validatePackageName(packageName);
   756         nullCheck(packageName);
   757         nullCheck(kinds);
   759         Iterable<? extends File> path = getLocation(location);
   760         if (path == null)
   761             return List.nil();
   762         RelativeDirectory subdirectory = RelativeDirectory.forPackage(packageName);
   763         ListBuffer<JavaFileObject> results = new ListBuffer<JavaFileObject>();
   765         for (File directory : path)
   766             listDirectory(directory, subdirectory, kinds, recurse, results);
   768         return results.toList();
   769     }
   771     public String inferBinaryName(Location location, JavaFileObject file) {
   772         file.getClass(); // null check
   773         location.getClass(); // null check
   774         // Need to match the path semantics of list(location, ...)
   775         Iterable<? extends File> path = getLocation(location);
   776         if (path == null) {
   777             return null;
   778         }
   780         if (file instanceof BaseFileObject) {
   781             return ((BaseFileObject) file).inferBinaryName(path);
   782         } else
   783             throw new IllegalArgumentException(file.getClass().getName());
   784     }
   786     public boolean isSameFile(FileObject a, FileObject b) {
   787         nullCheck(a);
   788         nullCheck(b);
   789         if (!(a instanceof BaseFileObject))
   790             throw new IllegalArgumentException("Not supported: " + a);
   791         if (!(b instanceof BaseFileObject))
   792             throw new IllegalArgumentException("Not supported: " + b);
   793         return a.equals(b);
   794     }
   796     public boolean handleOption(String current, Iterator<String> remaining) {
   797         for (JavacOption o: javacFileManagerOptions) {
   798             if (o.matches(current))  {
   799                 if (o.hasArg()) {
   800                     if (remaining.hasNext()) {
   801                         if (!o.process(options, current, remaining.next()))
   802                             return true;
   803                     }
   804                 } else {
   805                     if (!o.process(options, current))
   806                         return true;
   807                 }
   808                 // operand missing, or process returned false
   809                 throw new IllegalArgumentException(current);
   810             }
   811         }
   813         return false;
   814     }
   815     // where
   816         private static JavacOption[] javacFileManagerOptions =
   817             RecognizedOptions.getJavacFileManagerOptions(
   818             new RecognizedOptions.GrumpyHelper());
   820     public int isSupportedOption(String option) {
   821         for (JavacOption o : javacFileManagerOptions) {
   822             if (o.matches(option))
   823                 return o.hasArg() ? 1 : 0;
   824         }
   825         return -1;
   826     }
   828     public boolean hasLocation(Location location) {
   829         return getLocation(location) != null;
   830     }
   832     public JavaFileObject getJavaFileForInput(Location location,
   833                                               String className,
   834                                               JavaFileObject.Kind kind)
   835         throws IOException
   836     {
   837         nullCheck(location);
   838         // validateClassName(className);
   839         nullCheck(className);
   840         nullCheck(kind);
   841         if (!sourceOrClass.contains(kind))
   842             throw new IllegalArgumentException("Invalid kind " + kind);
   843         return getFileForInput(location, RelativeFile.forClass(className, kind));
   844     }
   846     public FileObject getFileForInput(Location location,
   847                                       String packageName,
   848                                       String relativeName)
   849         throws IOException
   850     {
   851         nullCheck(location);
   852         // validatePackageName(packageName);
   853         nullCheck(packageName);
   854         if (!isRelativeUri(URI.create(relativeName))) // FIXME 6419701
   855             throw new IllegalArgumentException("Invalid relative name: " + relativeName);
   856         RelativeFile name = packageName.length() == 0
   857             ? new RelativeFile(relativeName)
   858             : new RelativeFile(RelativeDirectory.forPackage(packageName), relativeName);
   859         return getFileForInput(location, name);
   860     }
   862     private JavaFileObject getFileForInput(Location location, RelativeFile name) throws IOException {
   863         Iterable<? extends File> path = getLocation(location);
   864         if (path == null)
   865             return null;
   867         for (File dir: path) {
   868             if (dir.isDirectory()) {
   869                 File f = name.getFile(dir);
   870                 if (f.exists())
   871                     return new RegularFileObject(this, f);
   872             } else {
   873                 Archive a = openArchive(dir);
   874                 if (a.contains(name)) {
   875                     return a.getFileObject(name.dirname(), name.basename());
   876                 }
   878             }
   879         }
   881         return null;
   882     }
   884     public JavaFileObject getJavaFileForOutput(Location location,
   885                                                String className,
   886                                                JavaFileObject.Kind kind,
   887                                                FileObject sibling)
   888         throws IOException
   889     {
   890         nullCheck(location);
   891         // validateClassName(className);
   892         nullCheck(className);
   893         nullCheck(kind);
   894         if (!sourceOrClass.contains(kind))
   895             throw new IllegalArgumentException("Invalid kind " + kind);
   896         return getFileForOutput(location, RelativeFile.forClass(className, kind), sibling);
   897     }
   899     public FileObject getFileForOutput(Location location,
   900                                        String packageName,
   901                                        String relativeName,
   902                                        FileObject sibling)
   903         throws IOException
   904     {
   905         nullCheck(location);
   906         // validatePackageName(packageName);
   907         nullCheck(packageName);
   908         if (!isRelativeUri(URI.create(relativeName))) // FIXME 6419701
   909             throw new IllegalArgumentException("relativeName is invalid");
   910         RelativeFile name = packageName.length() == 0
   911             ? new RelativeFile(relativeName)
   912             : new RelativeFile(RelativeDirectory.forPackage(packageName), relativeName);
   913         return getFileForOutput(location, name, sibling);
   914     }
   916     private JavaFileObject getFileForOutput(Location location,
   917                                             RelativeFile fileName,
   918                                             FileObject sibling)
   919         throws IOException
   920     {
   921         File dir;
   922         if (location == CLASS_OUTPUT) {
   923             if (getClassOutDir() != null) {
   924                 dir = getClassOutDir();
   925             } else {
   926                 File siblingDir = null;
   927                 if (sibling != null && sibling instanceof RegularFileObject) {
   928                     siblingDir = ((RegularFileObject)sibling).f.getParentFile();
   929                 }
   930                 return new RegularFileObject(this, new File(siblingDir, fileName.basename()));
   931             }
   932         } else if (location == SOURCE_OUTPUT) {
   933             dir = (getSourceOutDir() != null ? getSourceOutDir() : getClassOutDir());
   934         } else {
   935             Iterable<? extends File> path = paths.getPathForLocation(location);
   936             dir = null;
   937             for (File f: path) {
   938                 dir = f;
   939                 break;
   940             }
   941         }
   943         File file = fileName.getFile(dir); // null-safe
   944         return new RegularFileObject(this, file);
   946     }
   948     public Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles(
   949         Iterable<? extends File> files)
   950     {
   951         ArrayList<RegularFileObject> result;
   952         if (files instanceof Collection)
   953             result = new ArrayList<RegularFileObject>(((Collection)files).size());
   954         else
   955             result = new ArrayList<RegularFileObject>();
   956         for (File f: files)
   957             result.add(new RegularFileObject(this, nullCheck(f)));
   958         return result;
   959     }
   961     public Iterable<? extends JavaFileObject> getJavaFileObjects(File... files) {
   962         return getJavaFileObjectsFromFiles(Arrays.asList(nullCheck(files)));
   963     }
   965     public void setLocation(Location location,
   966                             Iterable<? extends File> path)
   967         throws IOException
   968     {
   969         nullCheck(location);
   970         paths.lazy();
   972         final File dir = location.isOutputLocation() ? getOutputDirectory(path) : null;
   974         if (location == CLASS_OUTPUT)
   975             classOutDir = getOutputLocation(dir, D);
   976         else if (location == SOURCE_OUTPUT)
   977             sourceOutDir = getOutputLocation(dir, S);
   978         else
   979             paths.setPathForLocation(location, path);
   980     }
   981     // where
   982         private File getOutputDirectory(Iterable<? extends File> path) throws IOException {
   983             if (path == null)
   984                 return null;
   985             Iterator<? extends File> pathIter = path.iterator();
   986             if (!pathIter.hasNext())
   987                 throw new IllegalArgumentException("empty path for directory");
   988             File dir = pathIter.next();
   989             if (pathIter.hasNext())
   990                 throw new IllegalArgumentException("path too long for directory");
   991             if (!dir.exists())
   992                 throw new FileNotFoundException(dir + ": does not exist");
   993             else if (!dir.isDirectory())
   994                 throw new IOException(dir + ": not a directory");
   995             return dir;
   996         }
   998     private File getOutputLocation(File dir, OptionName defaultOptionName) {
   999         if (dir != null)
  1000             return dir;
  1001         String arg = options.get(defaultOptionName);
  1002         if (arg == null)
  1003             return null;
  1004         return new File(arg);
  1007     public Iterable<? extends File> getLocation(Location location) {
  1008         nullCheck(location);
  1009         paths.lazy();
  1010         if (location == CLASS_OUTPUT) {
  1011             return (getClassOutDir() == null ? null : List.of(getClassOutDir()));
  1012         } else if (location == SOURCE_OUTPUT) {
  1013             return (getSourceOutDir() == null ? null : List.of(getSourceOutDir()));
  1014         } else
  1015             return paths.getPathForLocation(location);
  1018     private File getClassOutDir() {
  1019         if (classOutDir == uninited)
  1020             classOutDir = getOutputLocation(null, D);
  1021         return classOutDir;
  1024     private File getSourceOutDir() {
  1025         if (sourceOutDir == uninited)
  1026             sourceOutDir = getOutputLocation(null, S);
  1027         return sourceOutDir;
  1030     /**
  1031      * Enforces the specification of a "relative" URI as used in
  1032      * {@linkplain #getFileForInput(Location,String,URI)
  1033      * getFileForInput}.  This method must follow the rules defined in
  1034      * that method, do not make any changes without consulting the
  1035      * specification.
  1036      */
  1037     protected static boolean isRelativeUri(URI uri) {
  1038         if (uri.isAbsolute())
  1039             return false;
  1040         String path = uri.normalize().getPath();
  1041         if (path.length() == 0 /* isEmpty() is mustang API */)
  1042             return false;
  1043         char first = path.charAt(0);
  1044         return first != '.' && first != '/';
  1047     /**
  1048      * Converts a relative file name to a relative URI.  This is
  1049      * different from File.toURI as this method does not canonicalize
  1050      * the file before creating the URI.  Furthermore, no schema is
  1051      * used.
  1052      * @param file a relative file name
  1053      * @return a relative URI
  1054      * @throws IllegalArgumentException if the file name is not
  1055      * relative according to the definition given in {@link
  1056      * javax.tools.JavaFileManager#getFileForInput}
  1057      */
  1058     public static String getRelativeName(File file) {
  1059         if (!file.isAbsolute()) {
  1060             String result = file.getPath().replace(File.separatorChar, '/');
  1061             if (JavacFileManager.isRelativeUri(URI.create(result))) // FIXME 6419701
  1062                 return result;
  1064         throw new IllegalArgumentException("Invalid relative path: " + file);
  1067     @SuppressWarnings("deprecation") // bug 6410637
  1068     public static String getJavacFileName(FileObject file) {
  1069         if (file instanceof BaseFileObject)
  1070             return ((BaseFileObject)file).getPath();
  1071         URI uri = file.toUri();
  1072         String scheme = uri.getScheme();
  1073         if (scheme == null || scheme.equals("file") || scheme.equals("jar"))
  1074             return uri.getPath();
  1075         else
  1076             return uri.toString();
  1079     @SuppressWarnings("deprecation") // bug 6410637
  1080     public static String getJavacBaseFileName(FileObject file) {
  1081         if (file instanceof BaseFileObject)
  1082             return ((BaseFileObject)file).getName();
  1083         URI uri = file.toUri();
  1084         String scheme = uri.getScheme();
  1085         if (scheme == null || scheme.equals("file") || scheme.equals("jar")) {
  1086             String path = uri.getPath();
  1087             if (path == null)
  1088                 return null;
  1089             if (scheme != null && scheme.equals("jar"))
  1090                 path = path.substring(path.lastIndexOf('!') + 1);
  1091             return path.substring(path.lastIndexOf('/') + 1);
  1092         } else {
  1093             return uri.toString();
  1097     private static <T> T nullCheck(T o) {
  1098         o.getClass(); // null check
  1099         return o;
  1102     private static <T> Iterable<T> nullCheck(Iterable<T> it) {
  1103         for (T t : it)
  1104             t.getClass(); // null check
  1105         return it;

mercurial