63 import javax.tools.JavaFileManager; |
63 import javax.tools.JavaFileManager; |
64 import javax.tools.JavaFileObject; |
64 import javax.tools.JavaFileObject; |
65 import javax.tools.StandardJavaFileManager; |
65 import javax.tools.StandardJavaFileManager; |
66 |
66 |
67 import com.sun.tools.javac.code.Source; |
67 import com.sun.tools.javac.code.Source; |
|
68 import com.sun.tools.javac.file.RelativePath.RelativeFile; |
|
69 import com.sun.tools.javac.file.RelativePath.RelativeDirectory; |
68 import com.sun.tools.javac.main.JavacOption; |
70 import com.sun.tools.javac.main.JavacOption; |
69 import com.sun.tools.javac.main.OptionName; |
71 import com.sun.tools.javac.main.OptionName; |
70 import com.sun.tools.javac.main.RecognizedOptions; |
72 import com.sun.tools.javac.main.RecognizedOptions; |
71 import com.sun.tools.javac.util.Context; |
73 import com.sun.tools.javac.util.Context; |
72 import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition; |
74 import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition; |
73 import com.sun.tools.javac.util.List; |
75 import com.sun.tools.javac.util.List; |
74 import com.sun.tools.javac.util.ListBuffer; |
76 import com.sun.tools.javac.util.ListBuffer; |
75 import com.sun.tools.javac.util.Log; |
77 import com.sun.tools.javac.util.Log; |
76 import com.sun.tools.javac.util.Options; |
78 import com.sun.tools.javac.util.Options; |
77 |
79 |
|
80 import static javax.tools.StandardLocation.*; |
78 import static com.sun.tools.javac.main.OptionName.*; |
81 import static com.sun.tools.javac.main.OptionName.*; |
79 import static javax.tools.StandardLocation.*; |
|
80 |
82 |
81 /** |
83 /** |
82 * This class provides access to the source, class and other files |
84 * This class provides access to the source, class and other files |
83 * used by the compiler and related tools. |
85 * used by the compiler and related tools. |
84 */ |
86 */ |
85 public class JavacFileManager implements StandardJavaFileManager { |
87 public class JavacFileManager implements StandardJavaFileManager { |
86 |
|
87 private static final String[] symbolFileLocation = { "lib", "ct.sym" }; |
|
88 private static final String symbolFilePrefix = "META-INF/sym/rt.jar/"; |
|
89 |
88 |
90 boolean useZipFileIndex; |
89 boolean useZipFileIndex; |
91 |
90 |
92 private static boolean CHECK_ZIP_TIMESTAMP = false; |
91 private static boolean CHECK_ZIP_TIMESTAMP = false; |
93 private static Map<File, Boolean> isDirectory = new ConcurrentHashMap<File, Boolean>(); |
92 private static Map<File, Boolean> isDirectory = new ConcurrentHashMap<File, Boolean>(); |
265 if (isValidClassName) |
264 if (isValidClassName) |
266 throw new AssertionError("Valid class name rejected: " + name); |
265 throw new AssertionError("Valid class name rejected: " + name); |
267 printAscii("Invalid class name: \"%s\"", name); |
266 printAscii("Invalid class name: \"%s\"", name); |
268 } |
267 } |
269 } |
268 } |
|
269 |
270 private static void printAscii(String format, Object... args) { |
270 private static void printAscii(String format, Object... args) { |
271 String message; |
271 String message; |
272 try { |
272 try { |
273 final String ascii = "US-ASCII"; |
273 final String ascii = "US-ASCII"; |
274 message = new String(String.format(null, format, args).getBytes(ascii), ascii); |
274 message = new String(String.format(null, format, args).getBytes(ascii), ascii); |
276 throw new AssertionError(ex); |
276 throw new AssertionError(ex); |
277 } |
277 } |
278 System.out.println(message); |
278 System.out.println(message); |
279 } |
279 } |
280 |
280 |
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 } |
|
287 |
|
288 private static String externalizeFileName(CharSequence n, JavaFileObject.Kind kind) { |
|
289 return externalizeFileName(n) + kind.extension; |
|
290 } |
|
291 |
|
292 private static String baseName(String fileName) { |
|
293 return fileName.substring(fileName.lastIndexOf(File.separatorChar) + 1); |
|
294 } |
|
295 |
|
296 /** |
281 /** |
297 * Insert all files in subdirectory `subdirectory' of `directory' which end |
282 * Insert all files in subdirectory `subdirectory' of `directory' which end |
298 * in one of the extensions in `extensions' into packageSym. |
283 * in one of the extensions in `extensions' into packageSym. |
299 */ |
284 */ |
300 private void listDirectory(File directory, |
285 private void listDirectory(File directory, |
301 String subdirectory, |
286 RelativeDirectory subdirectory, |
302 Set<JavaFileObject.Kind> fileKinds, |
287 Set<JavaFileObject.Kind> fileKinds, |
303 boolean recurse, |
288 boolean recurse, |
304 ListBuffer<JavaFileObject> l) { |
289 ListBuffer<JavaFileObject> l) { |
305 Archive archive = archives.get(directory); |
290 Archive archive = archives.get(directory); |
306 |
291 |
327 log.error("error.reading.file", |
312 log.error("error.reading.file", |
328 directory, ex.getLocalizedMessage()); |
313 directory, ex.getLocalizedMessage()); |
329 return; |
314 return; |
330 } |
315 } |
331 } |
316 } |
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 } |
|
344 |
|
345 if (!subdirectory.endsWith(File.separator)) subdirectory = subdirectory + File.separator; |
|
346 } |
|
347 } |
|
348 |
317 |
349 List<String> files = archive.getFiles(subdirectory); |
318 List<String> files = archive.getFiles(subdirectory); |
350 if (files != null) { |
319 if (files != null) { |
351 for (String file; !files.isEmpty(); files = files.tail) { |
320 for (String file; !files.isEmpty(); files = files.tail) { |
352 file = files.head; |
321 file = files.head; |
354 l.append(archive.getFileObject(subdirectory, file)); |
323 l.append(archive.getFileObject(subdirectory, file)); |
355 } |
324 } |
356 } |
325 } |
357 } |
326 } |
358 if (recurse) { |
327 if (recurse) { |
359 for (String s: archive.getSubdirectories()) { |
328 for (RelativeDirectory s: archive.getSubdirectories()) { |
360 if (s.startsWith(subdirectory) && !s.equals(subdirectory)) { |
329 if (subdirectory.contains(s)) { |
361 // Because the archive map is a flat list of directories, |
330 // Because the archive map is a flat list of directories, |
362 // the enclosing loop will pick up all child subdirectories. |
331 // the enclosing loop will pick up all child subdirectories. |
363 // Therefore, there is no need to recurse deeper. |
332 // Therefore, there is no need to recurse deeper. |
364 listDirectory(directory, s, fileKinds, false, l); |
333 listDirectory(directory, s, fileKinds, false, l); |
365 } |
334 } |
366 } |
335 } |
367 } |
336 } |
368 } else { |
337 } else { |
369 File d = subdirectory.length() != 0 |
338 File d = subdirectory.getFile(directory); |
370 ? new File(directory, subdirectory) |
|
371 : directory; |
|
372 if (!caseMapCheck(d, subdirectory)) |
339 if (!caseMapCheck(d, subdirectory)) |
373 return; |
340 return; |
374 |
341 |
375 File[] files = d.listFiles(); |
342 File[] files = d.listFiles(); |
376 if (files == null) |
343 if (files == null) |
379 for (File f: files) { |
346 for (File f: files) { |
380 String fname = f.getName(); |
347 String fname = f.getName(); |
381 if (f.isDirectory()) { |
348 if (f.isDirectory()) { |
382 if (recurse && SourceVersion.isIdentifier(fname)) { |
349 if (recurse && SourceVersion.isIdentifier(fname)) { |
383 listDirectory(directory, |
350 listDirectory(directory, |
384 subdirectory + File.separator + fname, |
351 new RelativeDirectory(subdirectory, fname), |
385 fileKinds, |
352 fileKinds, |
386 recurse, |
353 recurse, |
387 l); |
354 l); |
388 } |
355 } |
389 } else { |
356 } else { |
409 |
376 |
410 /** Hack to make Windows case sensitive. Test whether given path |
377 /** Hack to make Windows case sensitive. Test whether given path |
411 * ends in a string of characters with the same case as given name. |
378 * ends in a string of characters with the same case as given name. |
412 * Ignore file separators in both path and name. |
379 * Ignore file separators in both path and name. |
413 */ |
380 */ |
414 private boolean caseMapCheck(File f, String name) { |
381 private boolean caseMapCheck(File f, RelativePath name) { |
415 if (fileSystemIsCaseSensitive) return true; |
382 if (fileSystemIsCaseSensitive) return true; |
416 // Note that getCanonicalPath() returns the case-sensitive |
383 // Note that getCanonicalPath() returns the case-sensitive |
417 // spelled file name. |
384 // spelled file name. |
418 String path; |
385 String path; |
419 try { |
386 try { |
420 path = f.getCanonicalPath(); |
387 path = f.getCanonicalPath(); |
421 } catch (IOException ex) { |
388 } catch (IOException ex) { |
422 return false; |
389 return false; |
423 } |
390 } |
424 char[] pcs = path.toCharArray(); |
391 char[] pcs = path.toCharArray(); |
425 char[] ncs = name.toCharArray(); |
392 char[] ncs = name.path.toCharArray(); |
426 int i = pcs.length - 1; |
393 int i = pcs.length - 1; |
427 int j = ncs.length - 1; |
394 int j = ncs.length - 1; |
428 while (i >= 0 && j >= 0) { |
395 while (i >= 0 && j >= 0) { |
429 while (i >= 0 && pcs[i] == File.separatorChar) i--; |
396 while (i >= 0 && pcs[i] == File.separatorChar) i--; |
430 while (j >= 0 && ncs[j] == File.separatorChar) j--; |
397 while (j >= 0 && ncs[j] == '/') j--; |
431 if (i >= 0 && j >= 0) { |
398 if (i >= 0 && j >= 0) { |
432 if (pcs[i] != ncs[j]) return false; |
399 if (pcs[i] != ncs[j]) return false; |
433 i--; |
400 i--; |
434 j--; |
401 j--; |
435 } |
402 } |
442 * mapping directory names to lists of files (basenames). |
409 * mapping directory names to lists of files (basenames). |
443 */ |
410 */ |
444 public interface Archive { |
411 public interface Archive { |
445 void close() throws IOException; |
412 void close() throws IOException; |
446 |
413 |
447 boolean contains(String name); |
414 boolean contains(RelativePath name); |
448 |
415 |
449 JavaFileObject getFileObject(String subdirectory, String file); |
416 JavaFileObject getFileObject(RelativeDirectory subdirectory, String file); |
450 |
417 |
451 List<String> getFiles(String subdirectory); |
418 List<String> getFiles(RelativeDirectory subdirectory); |
452 |
419 |
453 Set<String> getSubdirectories(); |
420 Set<RelativeDirectory> getSubdirectories(); |
454 } |
421 } |
455 |
422 |
456 public class MissingArchive implements Archive { |
423 public class MissingArchive implements Archive { |
457 final File zipFileName; |
424 final File zipFileName; |
458 public MissingArchive(File name) { |
425 public MissingArchive(File name) { |
459 zipFileName = name; |
426 zipFileName = name; |
460 } |
427 } |
461 public boolean contains(String name) { |
428 public boolean contains(RelativePath name) { |
462 return false; |
429 return false; |
463 } |
430 } |
464 |
431 |
465 public void close() { |
432 public void close() { |
466 } |
433 } |
467 |
434 |
468 public JavaFileObject getFileObject(String subdirectory, String file) { |
435 public JavaFileObject getFileObject(RelativeDirectory subdirectory, String file) { |
469 return null; |
436 return null; |
470 } |
437 } |
471 |
438 |
472 public List<String> getFiles(String subdirectory) { |
439 public List<String> getFiles(RelativeDirectory subdirectory) { |
473 return List.nil(); |
440 return List.nil(); |
474 } |
441 } |
475 |
442 |
476 public Set<String> getSubdirectories() { |
443 public Set<RelativeDirectory> getSubdirectories() { |
477 return Collections.emptySet(); |
444 return Collections.emptySet(); |
478 } |
445 } |
|
446 |
|
447 public String toString() { |
|
448 return "MissingArchive[" + zipFileName + "]"; |
|
449 } |
479 } |
450 } |
480 |
451 |
481 /** A directory of zip files already opened. |
452 /** A directory of zip files already opened. |
482 */ |
453 */ |
483 Map<File, Archive> archives = new HashMap<File,Archive>(); |
454 Map<File, Archive> archives = new HashMap<File,Archive>(); |
|
455 |
|
456 private static final String[] symbolFileLocation = { "lib", "ct.sym" }; |
|
457 private static final RelativeDirectory symbolFilePrefix |
|
458 = new RelativeDirectory("META-INF/sym/rt.jar/"); |
484 |
459 |
485 /** Open a new zip file directory. |
460 /** Open a new zip file directory. |
486 */ |
461 */ |
487 protected Archive openArchive(File zipFileName) throws IOException { |
462 protected Archive openArchive(File zipFileName) throws IOException { |
488 Archive archive = archives.get(zipFileName); |
463 Archive archive = archives.get(zipFileName); |
538 |
513 |
539 if (origZipFileName == zipFileName) { |
514 if (origZipFileName == zipFileName) { |
540 if (!useZipFileIndex) { |
515 if (!useZipFileIndex) { |
541 archive = new ZipArchive(this, zdir); |
516 archive = new ZipArchive(this, zdir); |
542 } else { |
517 } else { |
543 archive = new ZipFileIndexArchive(this, ZipFileIndex.getZipFileIndex(zipFileName, null, |
518 archive = new ZipFileIndexArchive(this, |
544 usePreindexedCache, preindexCacheLocation, options.get("writezipindexfiles") != null)); |
519 ZipFileIndex.getZipFileIndex(zipFileName, |
|
520 null, |
|
521 usePreindexedCache, |
|
522 preindexCacheLocation, |
|
523 options.get("writezipindexfiles") != null)); |
545 } |
524 } |
546 } |
525 } |
547 else { |
526 else { |
548 if (!useZipFileIndex) { |
527 if (!useZipFileIndex) { |
549 archive = new SymbolArchive(this, origZipFileName, zdir, symbolFilePrefix); |
528 archive = new SymbolArchive(this, origZipFileName, zdir, symbolFilePrefix); |
550 } |
529 } |
551 else { |
530 else { |
552 archive = new ZipFileIndexArchive(this, |
531 archive = new ZipFileIndexArchive(this, |
553 ZipFileIndex.getZipFileIndex(zipFileName, |
532 ZipFileIndex.getZipFileIndex(zipFileName, |
554 symbolFilePrefix, |
533 symbolFilePrefix, |
555 usePreindexedCache, |
534 usePreindexedCache, |
556 preindexCacheLocation, |
535 preindexCacheLocation, |
557 options.get("writezipindexfiles") != null)); |
536 options.get("writezipindexfiles") != null)); |
558 } |
537 } |
559 } |
538 } |
560 } catch (FileNotFoundException ex) { |
539 } catch (FileNotFoundException ex) { |
561 archive = new MissingArchive(zipFileName); |
540 archive = new MissingArchive(zipFileName); |
562 } catch (IOException ex) { |
541 } catch (IOException ex) { |
794 nullCheck(kinds); |
773 nullCheck(kinds); |
795 |
774 |
796 Iterable<? extends File> path = getLocation(location); |
775 Iterable<? extends File> path = getLocation(location); |
797 if (path == null) |
776 if (path == null) |
798 return List.nil(); |
777 return List.nil(); |
799 String subdirectory = externalizeFileName(packageName); |
778 RelativeDirectory subdirectory = RelativeDirectory.forPackage(packageName); |
800 ListBuffer<JavaFileObject> results = new ListBuffer<JavaFileObject>(); |
779 ListBuffer<JavaFileObject> results = new ListBuffer<JavaFileObject>(); |
801 |
780 |
802 for (File directory : path) |
781 for (File directory : path) |
803 listDirectory(directory, subdirectory, kinds, recurse, results); |
782 listDirectory(directory, subdirectory, kinds, recurse, results); |
804 |
783 |
875 // validateClassName(className); |
854 // validateClassName(className); |
876 nullCheck(className); |
855 nullCheck(className); |
877 nullCheck(kind); |
856 nullCheck(kind); |
878 if (!sourceOrClass.contains(kind)) |
857 if (!sourceOrClass.contains(kind)) |
879 throw new IllegalArgumentException("Invalid kind " + kind); |
858 throw new IllegalArgumentException("Invalid kind " + kind); |
880 return getFileForInput(location, externalizeFileName(className, kind)); |
859 return getFileForInput(location, RelativeFile.forClass(className, kind)); |
881 } |
860 } |
882 |
861 |
883 public FileObject getFileForInput(Location location, |
862 public FileObject getFileForInput(Location location, |
884 String packageName, |
863 String packageName, |
885 String relativeName) |
864 String relativeName) |
888 nullCheck(location); |
867 nullCheck(location); |
889 // validatePackageName(packageName); |
868 // validatePackageName(packageName); |
890 nullCheck(packageName); |
869 nullCheck(packageName); |
891 if (!isRelativeUri(URI.create(relativeName))) // FIXME 6419701 |
870 if (!isRelativeUri(URI.create(relativeName))) // FIXME 6419701 |
892 throw new IllegalArgumentException("Invalid relative name: " + relativeName); |
871 throw new IllegalArgumentException("Invalid relative name: " + relativeName); |
893 String name = packageName.length() == 0 |
872 RelativeFile name = packageName.length() == 0 |
894 ? relativeName |
873 ? new RelativeFile(relativeName) |
895 : new File(externalizeFileName(packageName), relativeName).getPath(); |
874 : new RelativeFile(RelativeDirectory.forPackage(packageName), relativeName); |
896 return getFileForInput(location, name); |
875 return getFileForInput(location, name); |
897 } |
876 } |
898 |
877 |
899 private JavaFileObject getFileForInput(Location location, String name) throws IOException { |
878 private JavaFileObject getFileForInput(Location location, RelativeFile name) throws IOException { |
900 Iterable<? extends File> path = getLocation(location); |
879 Iterable<? extends File> path = getLocation(location); |
901 if (path == null) |
880 if (path == null) |
902 return null; |
881 return null; |
903 |
882 |
904 for (File dir: path) { |
883 for (File dir: path) { |
905 if (dir.isDirectory()) { |
884 if (dir.isDirectory()) { |
906 File f = new File(dir, name.replace('/', File.separatorChar)); |
885 File f = name.getFile(dir); |
907 if (f.exists()) |
886 if (f.exists()) |
908 return new RegularFileObject(this, f); |
887 return new RegularFileObject(this, f); |
909 } else { |
888 } else { |
910 Archive a = openArchive(dir); |
889 Archive a = openArchive(dir); |
911 if (a.contains(name)) { |
890 if (a.contains(name)) { |
912 int i = name.lastIndexOf('/'); |
891 return a.getFileObject(name.dirname(), name.basename()); |
913 String dirname = name.substring(0, i+1); |
892 } |
914 String basename = name.substring(i+1); |
893 |
915 return a.getFileObject(dirname, basename); |
894 } |
916 } |
895 } |
917 |
896 |
918 } |
|
919 } |
|
920 return null; |
897 return null; |
921 |
|
922 } |
898 } |
923 |
899 |
924 public JavaFileObject getJavaFileForOutput(Location location, |
900 public JavaFileObject getJavaFileForOutput(Location location, |
925 String className, |
901 String className, |
926 JavaFileObject.Kind kind, |
902 JavaFileObject.Kind kind, |
931 // validateClassName(className); |
907 // validateClassName(className); |
932 nullCheck(className); |
908 nullCheck(className); |
933 nullCheck(kind); |
909 nullCheck(kind); |
934 if (!sourceOrClass.contains(kind)) |
910 if (!sourceOrClass.contains(kind)) |
935 throw new IllegalArgumentException("Invalid kind " + kind); |
911 throw new IllegalArgumentException("Invalid kind " + kind); |
936 return getFileForOutput(location, externalizeFileName(className, kind), sibling); |
912 return getFileForOutput(location, RelativeFile.forClass(className, kind), sibling); |
937 } |
913 } |
938 |
914 |
939 public FileObject getFileForOutput(Location location, |
915 public FileObject getFileForOutput(Location location, |
940 String packageName, |
916 String packageName, |
941 String relativeName, |
917 String relativeName, |
945 nullCheck(location); |
921 nullCheck(location); |
946 // validatePackageName(packageName); |
922 // validatePackageName(packageName); |
947 nullCheck(packageName); |
923 nullCheck(packageName); |
948 if (!isRelativeUri(URI.create(relativeName))) // FIXME 6419701 |
924 if (!isRelativeUri(URI.create(relativeName))) // FIXME 6419701 |
949 throw new IllegalArgumentException("relativeName is invalid"); |
925 throw new IllegalArgumentException("relativeName is invalid"); |
950 String name = packageName.length() == 0 |
926 RelativeFile name = packageName.length() == 0 |
951 ? relativeName |
927 ? new RelativeFile(relativeName) |
952 : new File(externalizeFileName(packageName), relativeName).getPath(); |
928 : new RelativeFile(RelativeDirectory.forPackage(packageName), relativeName); |
953 return getFileForOutput(location, name, sibling); |
929 return getFileForOutput(location, name, sibling); |
954 } |
930 } |
955 |
931 |
956 private JavaFileObject getFileForOutput(Location location, |
932 private JavaFileObject getFileForOutput(Location location, |
957 String fileName, |
933 RelativeFile fileName, |
958 FileObject sibling) |
934 FileObject sibling) |
959 throws IOException |
935 throws IOException |
960 { |
936 { |
961 File dir; |
937 File dir; |
962 if (location == CLASS_OUTPUT) { |
938 if (location == CLASS_OUTPUT) { |
965 } else { |
941 } else { |
966 File siblingDir = null; |
942 File siblingDir = null; |
967 if (sibling != null && sibling instanceof RegularFileObject) { |
943 if (sibling != null && sibling instanceof RegularFileObject) { |
968 siblingDir = ((RegularFileObject)sibling).f.getParentFile(); |
944 siblingDir = ((RegularFileObject)sibling).f.getParentFile(); |
969 } |
945 } |
970 return new RegularFileObject(this, new File(siblingDir, baseName(fileName))); |
946 return new RegularFileObject(this, new File(siblingDir, fileName.basename())); |
971 } |
947 } |
972 } else if (location == SOURCE_OUTPUT) { |
948 } else if (location == SOURCE_OUTPUT) { |
973 dir = (getSourceOutDir() != null ? getSourceOutDir() : getClassOutDir()); |
949 dir = (getSourceOutDir() != null ? getSourceOutDir() : getClassOutDir()); |
974 } else { |
950 } else { |
975 Iterable<? extends File> path = paths.getPathForLocation(location); |
951 Iterable<? extends File> path = paths.getPathForLocation(location); |