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