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

Fri, 04 Jul 2008 15:06:27 -0700

author
tbell
date
Fri, 04 Jul 2008 15:06:27 -0700
changeset 62
07c916ecfc71
parent 57
aa67a5da66e3
parent 54
eaf608c64fec
child 103
e571266ae14f
permissions
-rw-r--r--

Merge

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

mercurial