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

changeset 0
959103a6100f
child 2525
2eb010b6cb22
equal deleted inserted replaced
-1:000000000000 0:959103a6100f
1 /*
2 * Copyright (c) 2005, 2012, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package com.sun.tools.javac.file;
27
28 import java.io.ByteArrayOutputStream;
29 import java.io.File;
30 import java.io.FileNotFoundException;
31 import java.io.IOException;
32 import java.io.OutputStreamWriter;
33 import java.net.MalformedURLException;
34 import java.net.URI;
35 import java.net.URISyntaxException;
36 import java.net.URL;
37 import java.nio.CharBuffer;
38 import java.nio.charset.Charset;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.Collection;
42 import java.util.Collections;
43 import java.util.Comparator;
44 import java.util.EnumSet;
45 import java.util.HashMap;
46 import java.util.Iterator;
47 import java.util.Map;
48 import java.util.Set;
49 import java.util.zip.ZipFile;
50
51 import javax.lang.model.SourceVersion;
52 import javax.tools.FileObject;
53 import javax.tools.JavaFileManager;
54 import javax.tools.JavaFileObject;
55 import javax.tools.StandardJavaFileManager;
56
57 import com.sun.tools.javac.file.RelativePath.RelativeFile;
58 import com.sun.tools.javac.file.RelativePath.RelativeDirectory;
59 import com.sun.tools.javac.util.BaseFileManager;
60 import com.sun.tools.javac.util.Context;
61 import com.sun.tools.javac.util.List;
62 import com.sun.tools.javac.util.ListBuffer;
63
64 import static javax.tools.StandardLocation.*;
65
66 /**
67 * This class provides access to the source, class and other files
68 * used by the compiler and related tools.
69 *
70 * <p><b>This is NOT part of any supported API.
71 * If you write code that depends on this, you do so at your own risk.
72 * This code and its internal interfaces are subject to change or
73 * deletion without notice.</b>
74 */
75 public class JavacFileManager extends BaseFileManager implements StandardJavaFileManager {
76
77 public static char[] toArray(CharBuffer buffer) {
78 if (buffer.hasArray())
79 return ((CharBuffer)buffer.compact().flip()).array();
80 else
81 return buffer.toString().toCharArray();
82 }
83
84 private FSInfo fsInfo;
85
86 private boolean contextUseOptimizedZip;
87 private ZipFileIndexCache zipFileIndexCache;
88
89 private final Set<JavaFileObject.Kind> sourceOrClass =
90 EnumSet.of(JavaFileObject.Kind.SOURCE, JavaFileObject.Kind.CLASS);
91
92 protected boolean mmappedIO;
93 protected boolean symbolFileEnabled;
94
95 protected enum SortFiles implements Comparator<File> {
96 FORWARD {
97 public int compare(File f1, File f2) {
98 return f1.getName().compareTo(f2.getName());
99 }
100 },
101 REVERSE {
102 public int compare(File f1, File f2) {
103 return -f1.getName().compareTo(f2.getName());
104 }
105 };
106 };
107 protected SortFiles sortFiles;
108
109 /**
110 * Register a Context.Factory to create a JavacFileManager.
111 */
112 public static void preRegister(Context context) {
113 context.put(JavaFileManager.class, new Context.Factory<JavaFileManager>() {
114 public JavaFileManager make(Context c) {
115 return new JavacFileManager(c, true, null);
116 }
117 });
118 }
119
120 /**
121 * Create a JavacFileManager using a given context, optionally registering
122 * it as the JavaFileManager for that context.
123 */
124 public JavacFileManager(Context context, boolean register, Charset charset) {
125 super(charset);
126 if (register)
127 context.put(JavaFileManager.class, this);
128 setContext(context);
129 }
130
131 /**
132 * Set the context for JavacFileManager.
133 */
134 @Override
135 public void setContext(Context context) {
136 super.setContext(context);
137
138 fsInfo = FSInfo.instance(context);
139
140 contextUseOptimizedZip = options.getBoolean("useOptimizedZip", true);
141 if (contextUseOptimizedZip)
142 zipFileIndexCache = ZipFileIndexCache.getSharedInstance();
143
144 mmappedIO = options.isSet("mmappedIO");
145 symbolFileEnabled = !options.isSet("ignore.symbol.file");
146
147 String sf = options.get("sortFiles");
148 if (sf != null) {
149 sortFiles = (sf.equals("reverse") ? SortFiles.REVERSE : SortFiles.FORWARD);
150 }
151 }
152
153 /**
154 * Set whether or not to use ct.sym as an alternate to rt.jar.
155 */
156 public void setSymbolFileEnabled(boolean b) {
157 symbolFileEnabled = b;
158 }
159
160 @Override
161 public boolean isDefaultBootClassPath() {
162 return locations.isDefaultBootClassPath();
163 }
164
165 public JavaFileObject getFileForInput(String name) {
166 return getRegularFile(new File(name));
167 }
168
169 public JavaFileObject getRegularFile(File file) {
170 return new RegularFileObject(this, file);
171 }
172
173 public JavaFileObject getFileForOutput(String classname,
174 JavaFileObject.Kind kind,
175 JavaFileObject sibling)
176 throws IOException
177 {
178 return getJavaFileForOutput(CLASS_OUTPUT, classname, kind, sibling);
179 }
180
181 public Iterable<? extends JavaFileObject> getJavaFileObjectsFromStrings(Iterable<String> names) {
182 ListBuffer<File> files = new ListBuffer<File>();
183 for (String name : names)
184 files.append(new File(nullCheck(name)));
185 return getJavaFileObjectsFromFiles(files.toList());
186 }
187
188 public Iterable<? extends JavaFileObject> getJavaFileObjects(String... names) {
189 return getJavaFileObjectsFromStrings(Arrays.asList(nullCheck(names)));
190 }
191
192 private static boolean isValidName(String name) {
193 // Arguably, isValidName should reject keywords (such as in SourceVersion.isName() ),
194 // but the set of keywords depends on the source level, and we don't want
195 // impls of JavaFileManager to have to be dependent on the source level.
196 // Therefore we simply check that the argument is a sequence of identifiers
197 // separated by ".".
198 for (String s : name.split("\\.", -1)) {
199 if (!SourceVersion.isIdentifier(s))
200 return false;
201 }
202 return true;
203 }
204
205 private static void validateClassName(String className) {
206 if (!isValidName(className))
207 throw new IllegalArgumentException("Invalid class name: " + className);
208 }
209
210 private static void validatePackageName(String packageName) {
211 if (packageName.length() > 0 && !isValidName(packageName))
212 throw new IllegalArgumentException("Invalid packageName name: " + packageName);
213 }
214
215 public static void testName(String name,
216 boolean isValidPackageName,
217 boolean isValidClassName)
218 {
219 try {
220 validatePackageName(name);
221 if (!isValidPackageName)
222 throw new AssertionError("Invalid package name accepted: " + name);
223 printAscii("Valid package name: \"%s\"", name);
224 } catch (IllegalArgumentException e) {
225 if (isValidPackageName)
226 throw new AssertionError("Valid package name rejected: " + name);
227 printAscii("Invalid package name: \"%s\"", name);
228 }
229 try {
230 validateClassName(name);
231 if (!isValidClassName)
232 throw new AssertionError("Invalid class name accepted: " + name);
233 printAscii("Valid class name: \"%s\"", name);
234 } catch (IllegalArgumentException e) {
235 if (isValidClassName)
236 throw new AssertionError("Valid class name rejected: " + name);
237 printAscii("Invalid class name: \"%s\"", name);
238 }
239 }
240
241 private static void printAscii(String format, Object... args) {
242 String message;
243 try {
244 final String ascii = "US-ASCII";
245 message = new String(String.format(null, format, args).getBytes(ascii), ascii);
246 } catch (java.io.UnsupportedEncodingException ex) {
247 throw new AssertionError(ex);
248 }
249 System.out.println(message);
250 }
251
252
253 /**
254 * Insert all files in subdirectory subdirectory of directory directory
255 * which match fileKinds into resultList
256 */
257 private void listDirectory(File directory,
258 RelativeDirectory subdirectory,
259 Set<JavaFileObject.Kind> fileKinds,
260 boolean recurse,
261 ListBuffer<JavaFileObject> resultList) {
262 File d = subdirectory.getFile(directory);
263 if (!caseMapCheck(d, subdirectory))
264 return;
265
266 File[] files = d.listFiles();
267 if (files == null)
268 return;
269
270 if (sortFiles != null)
271 Arrays.sort(files, sortFiles);
272
273 for (File f: files) {
274 String fname = f.getName();
275 if (f.isDirectory()) {
276 if (recurse && SourceVersion.isIdentifier(fname)) {
277 listDirectory(directory,
278 new RelativeDirectory(subdirectory, fname),
279 fileKinds,
280 recurse,
281 resultList);
282 }
283 } else {
284 if (isValidFile(fname, fileKinds)) {
285 JavaFileObject fe =
286 new RegularFileObject(this, fname, new File(d, fname));
287 resultList.append(fe);
288 }
289 }
290 }
291 }
292
293 /**
294 * Insert all files in subdirectory subdirectory of archive archive
295 * which match fileKinds into resultList
296 */
297 private void listArchive(Archive archive,
298 RelativeDirectory subdirectory,
299 Set<JavaFileObject.Kind> fileKinds,
300 boolean recurse,
301 ListBuffer<JavaFileObject> resultList) {
302 // Get the files directly in the subdir
303 List<String> files = archive.getFiles(subdirectory);
304 if (files != null) {
305 for (; !files.isEmpty(); files = files.tail) {
306 String file = files.head;
307 if (isValidFile(file, fileKinds)) {
308 resultList.append(archive.getFileObject(subdirectory, file));
309 }
310 }
311 }
312 if (recurse) {
313 for (RelativeDirectory s: archive.getSubdirectories()) {
314 if (subdirectory.contains(s)) {
315 // Because the archive map is a flat list of directories,
316 // the enclosing loop will pick up all child subdirectories.
317 // Therefore, there is no need to recurse deeper.
318 listArchive(archive, s, fileKinds, false, resultList);
319 }
320 }
321 }
322 }
323
324 /**
325 * container is a directory, a zip file, or a non-existant path.
326 * Insert all files in subdirectory subdirectory of container which
327 * match fileKinds into resultList
328 */
329 private void listContainer(File container,
330 RelativeDirectory subdirectory,
331 Set<JavaFileObject.Kind> fileKinds,
332 boolean recurse,
333 ListBuffer<JavaFileObject> resultList) {
334 Archive archive = archives.get(container);
335 if (archive == null) {
336 // archives are not created for directories.
337 if (fsInfo.isDirectory(container)) {
338 listDirectory(container,
339 subdirectory,
340 fileKinds,
341 recurse,
342 resultList);
343 return;
344 }
345
346 // Not a directory; either a file or non-existant, create the archive
347 try {
348 archive = openArchive(container);
349 } catch (IOException ex) {
350 log.error("error.reading.file",
351 container, getMessage(ex));
352 return;
353 }
354 }
355 listArchive(archive,
356 subdirectory,
357 fileKinds,
358 recurse,
359 resultList);
360 }
361
362 private boolean isValidFile(String s, Set<JavaFileObject.Kind> fileKinds) {
363 JavaFileObject.Kind kind = getKind(s);
364 return fileKinds.contains(kind);
365 }
366
367 private static final boolean fileSystemIsCaseSensitive =
368 File.separatorChar == '/';
369
370 /** Hack to make Windows case sensitive. Test whether given path
371 * ends in a string of characters with the same case as given name.
372 * Ignore file separators in both path and name.
373 */
374 private boolean caseMapCheck(File f, RelativePath name) {
375 if (fileSystemIsCaseSensitive) return true;
376 // Note that getCanonicalPath() returns the case-sensitive
377 // spelled file name.
378 String path;
379 try {
380 path = f.getCanonicalPath();
381 } catch (IOException ex) {
382 return false;
383 }
384 char[] pcs = path.toCharArray();
385 char[] ncs = name.path.toCharArray();
386 int i = pcs.length - 1;
387 int j = ncs.length - 1;
388 while (i >= 0 && j >= 0) {
389 while (i >= 0 && pcs[i] == File.separatorChar) i--;
390 while (j >= 0 && ncs[j] == '/') j--;
391 if (i >= 0 && j >= 0) {
392 if (pcs[i] != ncs[j]) return false;
393 i--;
394 j--;
395 }
396 }
397 return j < 0;
398 }
399
400 /**
401 * An archive provides a flat directory structure of a ZipFile by
402 * mapping directory names to lists of files (basenames).
403 */
404 public interface Archive {
405 void close() throws IOException;
406
407 boolean contains(RelativePath name);
408
409 JavaFileObject getFileObject(RelativeDirectory subdirectory, String file);
410
411 List<String> getFiles(RelativeDirectory subdirectory);
412
413 Set<RelativeDirectory> getSubdirectories();
414 }
415
416 public class MissingArchive implements Archive {
417 final File zipFileName;
418 public MissingArchive(File name) {
419 zipFileName = name;
420 }
421 public boolean contains(RelativePath name) {
422 return false;
423 }
424
425 public void close() {
426 }
427
428 public JavaFileObject getFileObject(RelativeDirectory subdirectory, String file) {
429 return null;
430 }
431
432 public List<String> getFiles(RelativeDirectory subdirectory) {
433 return List.nil();
434 }
435
436 public Set<RelativeDirectory> getSubdirectories() {
437 return Collections.emptySet();
438 }
439
440 @Override
441 public String toString() {
442 return "MissingArchive[" + zipFileName + "]";
443 }
444 }
445
446 /** A directory of zip files already opened.
447 */
448 Map<File, Archive> archives = new HashMap<File,Archive>();
449
450 private static final String[] symbolFileLocation = { "lib", "ct.sym" };
451 private static final RelativeDirectory symbolFilePrefix
452 = new RelativeDirectory("META-INF/sym/rt.jar/");
453
454 /*
455 * This method looks for a ZipFormatException and takes appropriate
456 * evasive action. If there is a failure in the fast mode then we
457 * fail over to the platform zip, and allow it to deal with a potentially
458 * non compliant zip file.
459 */
460 protected Archive openArchive(File zipFilename) throws IOException {
461 try {
462 return openArchive(zipFilename, contextUseOptimizedZip);
463 } catch (IOException ioe) {
464 if (ioe instanceof ZipFileIndex.ZipFormatException) {
465 return openArchive(zipFilename, false);
466 } else {
467 throw ioe;
468 }
469 }
470 }
471
472 /** Open a new zip file directory, and cache it.
473 */
474 private Archive openArchive(File zipFileName, boolean useOptimizedZip) throws IOException {
475 File origZipFileName = zipFileName;
476 if (symbolFileEnabled && locations.isDefaultBootClassPathRtJar(zipFileName)) {
477 File file = zipFileName.getParentFile().getParentFile(); // ${java.home}
478 if (new File(file.getName()).equals(new File("jre")))
479 file = file.getParentFile();
480 // file == ${jdk.home}
481 for (String name : symbolFileLocation)
482 file = new File(file, name);
483 // file == ${jdk.home}/lib/ct.sym
484 if (file.exists())
485 zipFileName = file;
486 }
487
488 Archive archive;
489 try {
490
491 ZipFile zdir = null;
492
493 boolean usePreindexedCache = false;
494 String preindexCacheLocation = null;
495
496 if (!useOptimizedZip) {
497 zdir = new ZipFile(zipFileName);
498 } else {
499 usePreindexedCache = options.isSet("usezipindex");
500 preindexCacheLocation = options.get("java.io.tmpdir");
501 String optCacheLoc = options.get("cachezipindexdir");
502
503 if (optCacheLoc != null && optCacheLoc.length() != 0) {
504 if (optCacheLoc.startsWith("\"")) {
505 if (optCacheLoc.endsWith("\"")) {
506 optCacheLoc = optCacheLoc.substring(1, optCacheLoc.length() - 1);
507 }
508 else {
509 optCacheLoc = optCacheLoc.substring(1);
510 }
511 }
512
513 File cacheDir = new File(optCacheLoc);
514 if (cacheDir.exists() && cacheDir.canWrite()) {
515 preindexCacheLocation = optCacheLoc;
516 if (!preindexCacheLocation.endsWith("/") &&
517 !preindexCacheLocation.endsWith(File.separator)) {
518 preindexCacheLocation += File.separator;
519 }
520 }
521 }
522 }
523
524 if (origZipFileName == zipFileName) {
525 if (!useOptimizedZip) {
526 archive = new ZipArchive(this, zdir);
527 } else {
528 archive = new ZipFileIndexArchive(this,
529 zipFileIndexCache.getZipFileIndex(zipFileName,
530 null,
531 usePreindexedCache,
532 preindexCacheLocation,
533 options.isSet("writezipindexfiles")));
534 }
535 } else {
536 if (!useOptimizedZip) {
537 archive = new SymbolArchive(this, origZipFileName, zdir, symbolFilePrefix);
538 } else {
539 archive = new ZipFileIndexArchive(this,
540 zipFileIndexCache.getZipFileIndex(zipFileName,
541 symbolFilePrefix,
542 usePreindexedCache,
543 preindexCacheLocation,
544 options.isSet("writezipindexfiles")));
545 }
546 }
547 } catch (FileNotFoundException ex) {
548 archive = new MissingArchive(zipFileName);
549 } catch (ZipFileIndex.ZipFormatException zfe) {
550 throw zfe;
551 } catch (IOException ex) {
552 if (zipFileName.exists())
553 log.error("error.reading.file", zipFileName, getMessage(ex));
554 archive = new MissingArchive(zipFileName);
555 }
556
557 archives.put(origZipFileName, archive);
558 return archive;
559 }
560
561 /** Flush any output resources.
562 */
563 public void flush() {
564 contentCache.clear();
565 }
566
567 /**
568 * Close the JavaFileManager, releasing resources.
569 */
570 public void close() {
571 for (Iterator<Archive> i = archives.values().iterator(); i.hasNext(); ) {
572 Archive a = i.next();
573 i.remove();
574 try {
575 a.close();
576 } catch (IOException e) {
577 }
578 }
579 }
580
581 private String defaultEncodingName;
582 private String getDefaultEncodingName() {
583 if (defaultEncodingName == null) {
584 defaultEncodingName =
585 new OutputStreamWriter(new ByteArrayOutputStream()).getEncoding();
586 }
587 return defaultEncodingName;
588 }
589
590 public ClassLoader getClassLoader(Location location) {
591 nullCheck(location);
592 Iterable<? extends File> path = getLocation(location);
593 if (path == null)
594 return null;
595 ListBuffer<URL> lb = new ListBuffer<URL>();
596 for (File f: path) {
597 try {
598 lb.append(f.toURI().toURL());
599 } catch (MalformedURLException e) {
600 throw new AssertionError(e);
601 }
602 }
603
604 return getClassLoader(lb.toArray(new URL[lb.size()]));
605 }
606
607 public Iterable<JavaFileObject> list(Location location,
608 String packageName,
609 Set<JavaFileObject.Kind> kinds,
610 boolean recurse)
611 throws IOException
612 {
613 // validatePackageName(packageName);
614 nullCheck(packageName);
615 nullCheck(kinds);
616
617 Iterable<? extends File> path = getLocation(location);
618 if (path == null)
619 return List.nil();
620 RelativeDirectory subdirectory = RelativeDirectory.forPackage(packageName);
621 ListBuffer<JavaFileObject> results = new ListBuffer<JavaFileObject>();
622
623 for (File directory : path)
624 listContainer(directory, subdirectory, kinds, recurse, results);
625 return results.toList();
626 }
627
628 public String inferBinaryName(Location location, JavaFileObject file) {
629 file.getClass(); // null check
630 location.getClass(); // null check
631 // Need to match the path semantics of list(location, ...)
632 Iterable<? extends File> path = getLocation(location);
633 if (path == null) {
634 return null;
635 }
636
637 if (file instanceof BaseFileObject) {
638 return ((BaseFileObject) file).inferBinaryName(path);
639 } else
640 throw new IllegalArgumentException(file.getClass().getName());
641 }
642
643 public boolean isSameFile(FileObject a, FileObject b) {
644 nullCheck(a);
645 nullCheck(b);
646 if (!(a instanceof BaseFileObject))
647 throw new IllegalArgumentException("Not supported: " + a);
648 if (!(b instanceof BaseFileObject))
649 throw new IllegalArgumentException("Not supported: " + b);
650 return a.equals(b);
651 }
652
653 public boolean hasLocation(Location location) {
654 return getLocation(location) != null;
655 }
656
657 public JavaFileObject getJavaFileForInput(Location location,
658 String className,
659 JavaFileObject.Kind kind)
660 throws IOException
661 {
662 nullCheck(location);
663 // validateClassName(className);
664 nullCheck(className);
665 nullCheck(kind);
666 if (!sourceOrClass.contains(kind))
667 throw new IllegalArgumentException("Invalid kind: " + kind);
668 return getFileForInput(location, RelativeFile.forClass(className, kind));
669 }
670
671 public FileObject getFileForInput(Location location,
672 String packageName,
673 String relativeName)
674 throws IOException
675 {
676 nullCheck(location);
677 // validatePackageName(packageName);
678 nullCheck(packageName);
679 if (!isRelativeUri(relativeName))
680 throw new IllegalArgumentException("Invalid relative name: " + relativeName);
681 RelativeFile name = packageName.length() == 0
682 ? new RelativeFile(relativeName)
683 : new RelativeFile(RelativeDirectory.forPackage(packageName), relativeName);
684 return getFileForInput(location, name);
685 }
686
687 private JavaFileObject getFileForInput(Location location, RelativeFile name) throws IOException {
688 Iterable<? extends File> path = getLocation(location);
689 if (path == null)
690 return null;
691
692 for (File dir: path) {
693 Archive a = archives.get(dir);
694 if (a == null) {
695 if (fsInfo.isDirectory(dir)) {
696 File f = name.getFile(dir);
697 if (f.exists())
698 return new RegularFileObject(this, f);
699 continue;
700 }
701 // Not a directory, create the archive
702 a = openArchive(dir);
703 }
704 // Process the archive
705 if (a.contains(name)) {
706 return a.getFileObject(name.dirname(), name.basename());
707 }
708 }
709 return null;
710 }
711
712 public JavaFileObject getJavaFileForOutput(Location location,
713 String className,
714 JavaFileObject.Kind kind,
715 FileObject sibling)
716 throws IOException
717 {
718 nullCheck(location);
719 // validateClassName(className);
720 nullCheck(className);
721 nullCheck(kind);
722 if (!sourceOrClass.contains(kind))
723 throw new IllegalArgumentException("Invalid kind: " + kind);
724 return getFileForOutput(location, RelativeFile.forClass(className, kind), sibling);
725 }
726
727 public FileObject getFileForOutput(Location location,
728 String packageName,
729 String relativeName,
730 FileObject sibling)
731 throws IOException
732 {
733 nullCheck(location);
734 // validatePackageName(packageName);
735 nullCheck(packageName);
736 if (!isRelativeUri(relativeName))
737 throw new IllegalArgumentException("Invalid relative name: " + relativeName);
738 RelativeFile name = packageName.length() == 0
739 ? new RelativeFile(relativeName)
740 : new RelativeFile(RelativeDirectory.forPackage(packageName), relativeName);
741 return getFileForOutput(location, name, sibling);
742 }
743
744 private JavaFileObject getFileForOutput(Location location,
745 RelativeFile fileName,
746 FileObject sibling)
747 throws IOException
748 {
749 File dir;
750 if (location == CLASS_OUTPUT) {
751 if (getClassOutDir() != null) {
752 dir = getClassOutDir();
753 } else {
754 File siblingDir = null;
755 if (sibling != null && sibling instanceof RegularFileObject) {
756 siblingDir = ((RegularFileObject)sibling).file.getParentFile();
757 }
758 return new RegularFileObject(this, new File(siblingDir, fileName.basename()));
759 }
760 } else if (location == SOURCE_OUTPUT) {
761 dir = (getSourceOutDir() != null ? getSourceOutDir() : getClassOutDir());
762 } else {
763 Iterable<? extends File> path = locations.getLocation(location);
764 dir = null;
765 for (File f: path) {
766 dir = f;
767 break;
768 }
769 }
770
771 File file = fileName.getFile(dir); // null-safe
772 return new RegularFileObject(this, file);
773
774 }
775
776 public Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles(
777 Iterable<? extends File> files)
778 {
779 ArrayList<RegularFileObject> result;
780 if (files instanceof Collection<?>)
781 result = new ArrayList<RegularFileObject>(((Collection<?>)files).size());
782 else
783 result = new ArrayList<RegularFileObject>();
784 for (File f: files)
785 result.add(new RegularFileObject(this, nullCheck(f)));
786 return result;
787 }
788
789 public Iterable<? extends JavaFileObject> getJavaFileObjects(File... files) {
790 return getJavaFileObjectsFromFiles(Arrays.asList(nullCheck(files)));
791 }
792
793 public void setLocation(Location location,
794 Iterable<? extends File> path)
795 throws IOException
796 {
797 nullCheck(location);
798 locations.setLocation(location, path);
799 }
800
801 public Iterable<? extends File> getLocation(Location location) {
802 nullCheck(location);
803 return locations.getLocation(location);
804 }
805
806 private File getClassOutDir() {
807 return locations.getOutputLocation(CLASS_OUTPUT);
808 }
809
810 private File getSourceOutDir() {
811 return locations.getOutputLocation(SOURCE_OUTPUT);
812 }
813
814 /**
815 * Enforces the specification of a "relative" name as used in
816 * {@linkplain #getFileForInput(Location,String,String)
817 * getFileForInput}. This method must follow the rules defined in
818 * that method, do not make any changes without consulting the
819 * specification.
820 */
821 protected static boolean isRelativeUri(URI uri) {
822 if (uri.isAbsolute())
823 return false;
824 String path = uri.normalize().getPath();
825 if (path.length() == 0 /* isEmpty() is mustang API */)
826 return false;
827 if (!path.equals(uri.getPath())) // implicitly checks for embedded . and ..
828 return false;
829 if (path.startsWith("/") || path.startsWith("./") || path.startsWith("../"))
830 return false;
831 return true;
832 }
833
834 // Convenience method
835 protected static boolean isRelativeUri(String u) {
836 try {
837 return isRelativeUri(new URI(u));
838 } catch (URISyntaxException e) {
839 return false;
840 }
841 }
842
843 /**
844 * Converts a relative file name to a relative URI. This is
845 * different from File.toURI as this method does not canonicalize
846 * the file before creating the URI. Furthermore, no schema is
847 * used.
848 * @param file a relative file name
849 * @return a relative URI
850 * @throws IllegalArgumentException if the file name is not
851 * relative according to the definition given in {@link
852 * javax.tools.JavaFileManager#getFileForInput}
853 */
854 public static String getRelativeName(File file) {
855 if (!file.isAbsolute()) {
856 String result = file.getPath().replace(File.separatorChar, '/');
857 if (isRelativeUri(result))
858 return result;
859 }
860 throw new IllegalArgumentException("Invalid relative path: " + file);
861 }
862
863 /**
864 * Get a detail message from an IOException.
865 * Most, but not all, instances of IOException provide a non-null result
866 * for getLocalizedMessage(). But some instances return null: in these
867 * cases, fallover to getMessage(), and if even that is null, return the
868 * name of the exception itself.
869 * @param e an IOException
870 * @return a string to include in a compiler diagnostic
871 */
872 public static String getMessage(IOException e) {
873 String s = e.getLocalizedMessage();
874 if (s != null)
875 return s;
876 s = e.getMessage();
877 if (s != null)
878 return s;
879 return e.toString();
880 }
881 }

mercurial