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