JavacFileManager.java revision 3294:9adfb22ff08f
1/* 2 * Copyright (c) 2005, 2016, 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 26package com.sun.tools.javac.file; 27 28import java.io.File; 29import java.io.IOException; 30import java.net.MalformedURLException; 31import java.net.URI; 32import java.net.URISyntaxException; 33import java.net.URL; 34import java.nio.CharBuffer; 35import java.nio.charset.Charset; 36import java.nio.file.FileSystem; 37import java.nio.file.FileSystems; 38import java.nio.file.FileVisitOption; 39import java.nio.file.FileVisitResult; 40import java.nio.file.Files; 41import java.nio.file.InvalidPathException; 42import java.nio.file.LinkOption; 43import java.nio.file.Path; 44import java.nio.file.Paths; 45import java.nio.file.SimpleFileVisitor; 46import java.nio.file.attribute.BasicFileAttributes; 47import java.util.ArrayList; 48import java.util.Arrays; 49import java.util.Collection; 50import java.util.Collections; 51import java.util.Comparator; 52import java.util.EnumSet; 53import java.util.HashMap; 54import java.util.Iterator; 55import java.util.Map; 56import java.util.Objects; 57import java.util.ServiceLoader; 58import java.util.Set; 59import java.util.stream.Collectors; 60import java.util.stream.Stream; 61 62import javax.lang.model.SourceVersion; 63import javax.tools.FileObject; 64import javax.tools.JavaFileManager; 65import javax.tools.JavaFileObject; 66import javax.tools.StandardJavaFileManager; 67 68import com.sun.tools.javac.file.RelativePath.RelativeDirectory; 69import com.sun.tools.javac.file.RelativePath.RelativeFile; 70import com.sun.tools.javac.util.Context; 71import com.sun.tools.javac.util.DefinedBy; 72import com.sun.tools.javac.util.DefinedBy.Api; 73import com.sun.tools.javac.util.List; 74import com.sun.tools.javac.util.ListBuffer; 75import com.sun.tools.javac.util.ModuleWrappers.Configuration; 76import com.sun.tools.javac.util.ModuleWrappers.Layer; 77import com.sun.tools.javac.util.ModuleWrappers.ModuleFinder; 78import com.sun.tools.javac.util.ModuleWrappers.ServiceLoaderHelper; 79 80import static java.nio.file.FileVisitOption.FOLLOW_LINKS; 81 82import static javax.tools.StandardLocation.*; 83 84/** 85 * This class provides access to the source, class and other files 86 * used by the compiler and related tools. 87 * 88 * <p><b>This is NOT part of any supported API. 89 * If you write code that depends on this, you do so at your own risk. 90 * This code and its internal interfaces are subject to change or 91 * deletion without notice.</b> 92 */ 93public class JavacFileManager extends BaseFileManager implements StandardJavaFileManager { 94 95 @SuppressWarnings("cast") 96 public static char[] toArray(CharBuffer buffer) { 97 if (buffer.hasArray()) 98 return ((CharBuffer)buffer.compact().flip()).array(); 99 else 100 return buffer.toString().toCharArray(); 101 } 102 103 private FSInfo fsInfo; 104 105 private final Set<JavaFileObject.Kind> sourceOrClass = 106 EnumSet.of(JavaFileObject.Kind.SOURCE, JavaFileObject.Kind.CLASS); 107 108 protected boolean symbolFileEnabled; 109 110 protected enum SortFiles implements Comparator<Path> { 111 FORWARD { 112 @Override 113 public int compare(Path f1, Path f2) { 114 return f1.getFileName().compareTo(f2.getFileName()); 115 } 116 }, 117 REVERSE { 118 @Override 119 public int compare(Path f1, Path f2) { 120 return -f1.getFileName().compareTo(f2.getFileName()); 121 } 122 } 123 } 124 125 protected SortFiles sortFiles; 126 127 /** 128 * Register a Context.Factory to create a JavacFileManager. 129 */ 130 public static void preRegister(Context context) { 131 context.put(JavaFileManager.class, new Context.Factory<JavaFileManager>() { 132 @Override 133 public JavaFileManager make(Context c) { 134 return new JavacFileManager(c, true, null); 135 } 136 }); 137 } 138 139 /** 140 * Create a JavacFileManager using a given context, optionally registering 141 * it as the JavaFileManager for that context. 142 */ 143 public JavacFileManager(Context context, boolean register, Charset charset) { 144 super(charset); 145 if (register) 146 context.put(JavaFileManager.class, this); 147 setContext(context); 148 } 149 150 /** 151 * Set the context for JavacFileManager. 152 */ 153 @Override 154 public void setContext(Context context) { 155 super.setContext(context); 156 157 fsInfo = FSInfo.instance(context); 158 159 symbolFileEnabled = !options.isSet("ignore.symbol.file"); 160 161 String sf = options.get("sortFiles"); 162 if (sf != null) { 163 sortFiles = (sf.equals("reverse") ? SortFiles.REVERSE : SortFiles.FORWARD); 164 } 165 } 166 167 /** 168 * Set whether or not to use ct.sym as an alternate to rt.jar. 169 */ 170 public void setSymbolFileEnabled(boolean b) { 171 symbolFileEnabled = b; 172 } 173 174 public boolean isSymbolFileEnabled() { 175 return symbolFileEnabled; 176 } 177 178 // used by tests 179 public JavaFileObject getJavaFileObject(String name) { 180 return getJavaFileObjects(name).iterator().next(); 181 } 182 183 // used by tests 184 public JavaFileObject getJavaFileObject(Path file) { 185 return getJavaFileObjects(file).iterator().next(); 186 } 187 188 public JavaFileObject getFileForOutput(String classname, 189 JavaFileObject.Kind kind, 190 JavaFileObject sibling) 191 throws IOException 192 { 193 return getJavaFileForOutput(CLASS_OUTPUT, classname, kind, sibling); 194 } 195 196 @Override @DefinedBy(Api.COMPILER) 197 public Iterable<? extends JavaFileObject> getJavaFileObjectsFromStrings(Iterable<String> names) { 198 ListBuffer<Path> paths = new ListBuffer<>(); 199 for (String name : names) 200 paths.append(Paths.get(nullCheck(name))); 201 return getJavaFileObjectsFromPaths(paths.toList()); 202 } 203 204 @Override @DefinedBy(Api.COMPILER) 205 public Iterable<? extends JavaFileObject> getJavaFileObjects(String... names) { 206 return getJavaFileObjectsFromStrings(Arrays.asList(nullCheck(names))); 207 } 208 209 private static boolean isValidName(String name) { 210 // Arguably, isValidName should reject keywords (such as in SourceVersion.isName() ), 211 // but the set of keywords depends on the source level, and we don't want 212 // impls of JavaFileManager to have to be dependent on the source level. 213 // Therefore we simply check that the argument is a sequence of identifiers 214 // separated by ".". 215 for (String s : name.split("\\.", -1)) { 216 if (!SourceVersion.isIdentifier(s)) 217 return false; 218 } 219 return true; 220 } 221 222 private static void validateClassName(String className) { 223 if (!isValidName(className)) 224 throw new IllegalArgumentException("Invalid class name: " + className); 225 } 226 227 private static void validatePackageName(String packageName) { 228 if (packageName.length() > 0 && !isValidName(packageName)) 229 throw new IllegalArgumentException("Invalid packageName name: " + packageName); 230 } 231 232 public static void testName(String name, 233 boolean isValidPackageName, 234 boolean isValidClassName) 235 { 236 try { 237 validatePackageName(name); 238 if (!isValidPackageName) 239 throw new AssertionError("Invalid package name accepted: " + name); 240 printAscii("Valid package name: \"%s\"", name); 241 } catch (IllegalArgumentException e) { 242 if (isValidPackageName) 243 throw new AssertionError("Valid package name rejected: " + name); 244 printAscii("Invalid package name: \"%s\"", name); 245 } 246 try { 247 validateClassName(name); 248 if (!isValidClassName) 249 throw new AssertionError("Invalid class name accepted: " + name); 250 printAscii("Valid class name: \"%s\"", name); 251 } catch (IllegalArgumentException e) { 252 if (isValidClassName) 253 throw new AssertionError("Valid class name rejected: " + name); 254 printAscii("Invalid class name: \"%s\"", name); 255 } 256 } 257 258 private static void printAscii(String format, Object... args) { 259 String message; 260 try { 261 final String ascii = "US-ASCII"; 262 message = new String(String.format(null, format, args).getBytes(ascii), ascii); 263 } catch (java.io.UnsupportedEncodingException ex) { 264 throw new AssertionError(ex); 265 } 266 System.out.println(message); 267 } 268 269 /** 270 * Insert all files in a subdirectory of the platform image 271 * which match fileKinds into resultList. 272 */ 273 private void listJRTImage(RelativeDirectory subdirectory, 274 Set<JavaFileObject.Kind> fileKinds, 275 boolean recurse, 276 ListBuffer<JavaFileObject> resultList) throws IOException { 277 JRTIndex.Entry e = getJRTIndex().getEntry(subdirectory); 278 if (symbolFileEnabled && e.ctSym.hidden) 279 return; 280 for (Path file: e.files.values()) { 281 if (fileKinds.contains(getKind(file))) { 282 JavaFileObject fe 283 = PathFileObject.forJRTPath(JavacFileManager.this, file); 284 resultList.append(fe); 285 } 286 } 287 288 if (recurse) { 289 for (RelativeDirectory rd: e.subdirs) { 290 listJRTImage(rd, fileKinds, recurse, resultList); 291 } 292 } 293 } 294 295 private synchronized JRTIndex getJRTIndex() { 296 if (jrtIndex == null) 297 jrtIndex = JRTIndex.getSharedInstance(); 298 return jrtIndex; 299 } 300 301 private JRTIndex jrtIndex; 302 303 304 /** 305 * Insert all files in subdirectory subdirectory of directory directory 306 * which match fileKinds into resultList 307 */ 308 private void listDirectory(Path directory, Path realDirectory, 309 RelativeDirectory subdirectory, 310 Set<JavaFileObject.Kind> fileKinds, 311 boolean recurse, 312 ListBuffer<JavaFileObject> resultList) { 313 Path d; 314 try { 315 d = subdirectory.resolveAgainst(directory); 316 } catch (InvalidPathException ignore) { 317 return; 318 } 319 320 if (!Files.exists(d)) { 321 return; 322 } 323 324 if (!caseMapCheck(d, subdirectory)) { 325 return; 326 } 327 328 java.util.List<Path> files; 329 try (Stream<Path> s = Files.list(d)) { 330 files = (sortFiles == null ? s : s.sorted(sortFiles)).collect(Collectors.toList()); 331 } catch (IOException ignore) { 332 return; 333 } 334 335 if (realDirectory == null) 336 realDirectory = fsInfo.getCanonicalFile(directory); 337 338 for (Path f: files) { 339 String fname = f.getFileName().toString(); 340 if (fname.endsWith("/")) 341 fname = fname.substring(0, fname.length() - 1); 342 if (Files.isDirectory(f)) { 343 if (recurse && SourceVersion.isIdentifier(fname)) { 344 listDirectory(directory, realDirectory, 345 new RelativeDirectory(subdirectory, fname), 346 fileKinds, 347 recurse, 348 resultList); 349 } 350 } else { 351 if (isValidFile(fname, fileKinds)) { 352 RelativeFile file = new RelativeFile(subdirectory, fname); 353 JavaFileObject fe = PathFileObject.forDirectoryPath(this, 354 file.resolveAgainst(realDirectory), directory, file); 355 resultList.append(fe); 356 } 357 } 358 } 359 } 360 361 /** 362 * Insert all files in subdirectory subdirectory of archive archivePath 363 * which match fileKinds into resultList 364 */ 365 private void listArchive(Path archivePath, 366 RelativeDirectory subdirectory, 367 Set<JavaFileObject.Kind> fileKinds, 368 boolean recurse, 369 ListBuffer<JavaFileObject> resultList) 370 throws IOException { 371 FileSystem fs = getFileSystem(archivePath); 372 if (fs == null) { 373 return; 374 } 375 376 Path containerSubdir = subdirectory.resolveAgainst(fs); 377 if (!Files.exists(containerSubdir)) { 378 return; 379 } 380 381 int maxDepth = (recurse ? Integer.MAX_VALUE : 1); 382 Set<FileVisitOption> opts = EnumSet.of(FOLLOW_LINKS); 383 Files.walkFileTree(containerSubdir, opts, maxDepth, 384 new SimpleFileVisitor<Path>() { 385 @Override 386 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { 387 if (isValid(dir.getFileName())) { 388 return FileVisitResult.CONTINUE; 389 } else { 390 return FileVisitResult.SKIP_SUBTREE; 391 } 392 } 393 394 boolean isValid(Path fileName) { 395 if (fileName == null) { 396 return true; 397 } else { 398 String name = fileName.toString(); 399 if (name.endsWith("/")) { 400 name = name.substring(0, name.length() - 1); 401 } 402 return SourceVersion.isIdentifier(name); 403 } 404 } 405 406 @Override 407 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { 408 if (attrs.isRegularFile() && fileKinds.contains(getKind(file.getFileName().toString()))) { 409 JavaFileObject fe = PathFileObject.forJarPath( 410 JavacFileManager.this, file, archivePath); 411 resultList.append(fe); 412 } 413 return FileVisitResult.CONTINUE; 414 } 415 }); 416 417 } 418 419 /** 420 * container is a directory, a zip file, or a non-existant path. 421 * Insert all files in subdirectory subdirectory of container which 422 * match fileKinds into resultList 423 */ 424 private void listContainer(Path container, 425 RelativeDirectory subdirectory, 426 Set<JavaFileObject.Kind> fileKinds, 427 boolean recurse, 428 ListBuffer<JavaFileObject> resultList) 429 throws IOException { 430 if (Files.isRegularFile(container) && container.equals(Locations.thisSystemModules)) { 431 try { 432 listJRTImage(subdirectory, 433 fileKinds, 434 recurse, 435 resultList); 436 } catch (IOException ex) { 437 ex.printStackTrace(System.err); 438 log.error("error.reading.file", container, getMessage(ex)); 439 } 440 return; 441 } 442 443 if (Files.isDirectory(container)) { 444 listDirectory(container, null, 445 subdirectory, 446 fileKinds, 447 recurse, 448 resultList); 449 return; 450 } 451 452 if (Files.isRegularFile(container)) { 453 listArchive(container, 454 subdirectory, 455 fileKinds, 456 recurse, 457 resultList); 458 } 459 } 460 461 private boolean isValidFile(String s, Set<JavaFileObject.Kind> fileKinds) { 462 JavaFileObject.Kind kind = getKind(s); 463 return fileKinds.contains(kind); 464 } 465 466 private static final boolean fileSystemIsCaseSensitive = 467 File.separatorChar == '/'; 468 469 /** Hack to make Windows case sensitive. Test whether given path 470 * ends in a string of characters with the same case as given name. 471 * Ignore file separators in both path and name. 472 */ 473 private boolean caseMapCheck(Path f, RelativePath name) { 474 if (fileSystemIsCaseSensitive) return true; 475 // Note that toRealPath() returns the case-sensitive 476 // spelled file name. 477 String path; 478 char sep; 479 try { 480 path = f.toRealPath(LinkOption.NOFOLLOW_LINKS).toString(); 481 sep = f.getFileSystem().getSeparator().charAt(0); 482 } catch (IOException ex) { 483 return false; 484 } 485 char[] pcs = path.toCharArray(); 486 char[] ncs = name.path.toCharArray(); 487 int i = pcs.length - 1; 488 int j = ncs.length - 1; 489 while (i >= 0 && j >= 0) { 490 while (i >= 0 && pcs[i] == sep) i--; 491 while (j >= 0 && ncs[j] == '/') j--; 492 if (i >= 0 && j >= 0) { 493 if (pcs[i] != ncs[j]) return false; 494 i--; 495 j--; 496 } 497 } 498 return j < 0; 499 } 500 501 private FileSystem getFileSystem(Path path) throws IOException { 502 Path realPath = fsInfo.getCanonicalFile(path); 503 FileSystem fs = fileSystems.get(realPath); 504 if (fs == null) { 505 fileSystems.put(realPath, fs = FileSystems.newFileSystem(realPath, null)); 506 } 507 return fs; 508 } 509 510 private final Map<Path,FileSystem> fileSystems = new HashMap<>(); 511 512 513 /** Flush any output resources. 514 */ 515 @Override @DefinedBy(Api.COMPILER) 516 public void flush() { 517 contentCache.clear(); 518 } 519 520 /** 521 * Close the JavaFileManager, releasing resources. 522 */ 523 @Override @DefinedBy(Api.COMPILER) 524 public void close() throws IOException { 525 if (deferredCloseTimeout > 0) { 526 deferredClose(); 527 return; 528 } 529 530 locations.close(); 531 for (FileSystem fs: fileSystems.values()) { 532 fs.close(); 533 } 534 fileSystems.clear(); 535 contentCache.clear(); 536 } 537 538 @Override @DefinedBy(Api.COMPILER) 539 public ClassLoader getClassLoader(Location location) { 540 nullCheck(location); 541 Iterable<? extends File> path = getLocation(location); 542 if (path == null) 543 return null; 544 ListBuffer<URL> lb = new ListBuffer<>(); 545 for (File f: path) { 546 try { 547 lb.append(f.toURI().toURL()); 548 } catch (MalformedURLException e) { 549 throw new AssertionError(e); 550 } 551 } 552 553 return getClassLoader(lb.toArray(new URL[lb.size()])); 554 } 555 556 @Override @DefinedBy(Api.COMPILER) 557 public Iterable<JavaFileObject> list(Location location, 558 String packageName, 559 Set<JavaFileObject.Kind> kinds, 560 boolean recurse) 561 throws IOException 562 { 563 // validatePackageName(packageName); 564 nullCheck(packageName); 565 nullCheck(kinds); 566 567 Iterable<? extends Path> path = getLocationAsPaths(location); 568 if (path == null) 569 return List.nil(); 570 RelativeDirectory subdirectory = RelativeDirectory.forPackage(packageName); 571 ListBuffer<JavaFileObject> results = new ListBuffer<>(); 572 573 for (Path directory : path) 574 listContainer(directory, subdirectory, kinds, recurse, results); 575 return results.toList(); 576 } 577 578 @Override @DefinedBy(Api.COMPILER) 579 public String inferBinaryName(Location location, JavaFileObject file) { 580 Objects.requireNonNull(file); 581 Objects.requireNonNull(location); 582 // Need to match the path semantics of list(location, ...) 583 Iterable<? extends Path> path = getLocationAsPaths(location); 584 if (path == null) { 585 return null; 586 } 587 588 if (file instanceof PathFileObject) { 589 return ((PathFileObject) file).inferBinaryName(path); 590 } else 591 throw new IllegalArgumentException(file.getClass().getName()); 592 } 593 594 @Override @DefinedBy(Api.COMPILER) 595 public boolean isSameFile(FileObject a, FileObject b) { 596 nullCheck(a); 597 nullCheck(b); 598 if (a instanceof PathFileObject && b instanceof PathFileObject) 599 return ((PathFileObject) a).isSameFile((PathFileObject) b); 600 return a.equals(b); 601 } 602 603 @Override @DefinedBy(Api.COMPILER) 604 public boolean hasLocation(Location location) { 605 nullCheck(location); 606 return locations.hasLocation(location); 607 } 608 609 @Override @DefinedBy(Api.COMPILER) 610 public JavaFileObject getJavaFileForInput(Location location, 611 String className, 612 JavaFileObject.Kind kind) 613 throws IOException 614 { 615 nullCheck(location); 616 // validateClassName(className); 617 nullCheck(className); 618 nullCheck(kind); 619 if (!sourceOrClass.contains(kind)) 620 throw new IllegalArgumentException("Invalid kind: " + kind); 621 return getFileForInput(location, RelativeFile.forClass(className, kind)); 622 } 623 624 @Override @DefinedBy(Api.COMPILER) 625 public FileObject getFileForInput(Location location, 626 String packageName, 627 String relativeName) 628 throws IOException 629 { 630 nullCheck(location); 631 // validatePackageName(packageName); 632 nullCheck(packageName); 633 if (!isRelativeUri(relativeName)) 634 throw new IllegalArgumentException("Invalid relative name: " + relativeName); 635 RelativeFile name = packageName.length() == 0 636 ? new RelativeFile(relativeName) 637 : new RelativeFile(RelativeDirectory.forPackage(packageName), relativeName); 638 return getFileForInput(location, name); 639 } 640 641 private JavaFileObject getFileForInput(Location location, RelativeFile name) throws IOException { 642 Iterable<? extends Path> path = getLocationAsPaths(location); 643 if (path == null) 644 return null; 645 646 for (Path file: path) { 647 if (file.equals(Locations.thisSystemModules)) { 648 JRTIndex.Entry e = getJRTIndex().getEntry(name.dirname()); 649 if (symbolFileEnabled && e.ctSym.hidden) 650 continue; 651 Path p = e.files.get(name.basename()); 652 if (p != null) 653 return PathFileObject.forJRTPath(this, p); 654 } else if (Files.isDirectory(file)) { 655 try { 656 Path f = name.resolveAgainst(file); 657 if (Files.exists(f)) 658 return PathFileObject.forSimplePath(this, 659 fsInfo.getCanonicalFile(f), f); 660 } catch (InvalidPathException ignore) { 661 } 662 } else if (Files.isRegularFile(file)) { 663 FileSystem fs = getFileSystem(file); 664 if (fs != null) { 665 Path fsRoot = fs.getRootDirectories().iterator().next(); 666 Path f = name.resolveAgainst(fsRoot); 667 if (Files.exists(f)) 668 return PathFileObject.forJarPath(this, f, file); 669 } 670 } 671 } 672 return null; 673 } 674 675 @Override @DefinedBy(Api.COMPILER) 676 public JavaFileObject getJavaFileForOutput(Location location, 677 String className, 678 JavaFileObject.Kind kind, 679 FileObject sibling) 680 throws IOException 681 { 682 nullCheck(location); 683 // validateClassName(className); 684 nullCheck(className); 685 nullCheck(kind); 686 if (!sourceOrClass.contains(kind)) 687 throw new IllegalArgumentException("Invalid kind: " + kind); 688 return getFileForOutput(location, RelativeFile.forClass(className, kind), sibling); 689 } 690 691 @Override @DefinedBy(Api.COMPILER) 692 public FileObject getFileForOutput(Location location, 693 String packageName, 694 String relativeName, 695 FileObject sibling) 696 throws IOException 697 { 698 nullCheck(location); 699 // validatePackageName(packageName); 700 nullCheck(packageName); 701 if (!isRelativeUri(relativeName)) 702 throw new IllegalArgumentException("Invalid relative name: " + relativeName); 703 RelativeFile name = packageName.length() == 0 704 ? new RelativeFile(relativeName) 705 : new RelativeFile(RelativeDirectory.forPackage(packageName), relativeName); 706 return getFileForOutput(location, name, sibling); 707 } 708 709 private JavaFileObject getFileForOutput(Location location, 710 RelativeFile fileName, 711 FileObject sibling) 712 throws IOException 713 { 714 Path dir; 715 if (location == CLASS_OUTPUT) { 716 if (getClassOutDir() != null) { 717 dir = getClassOutDir(); 718 } else { 719 String baseName = fileName.basename(); 720 if (sibling != null && sibling instanceof PathFileObject) { 721 return ((PathFileObject) sibling).getSibling(baseName); 722 } else { 723 Path p = Paths.get(baseName); 724 Path real = fsInfo.getCanonicalFile(p); 725 return PathFileObject.forSimplePath(this, real, p); 726 } 727 } 728 } else if (location == SOURCE_OUTPUT) { 729 dir = (getSourceOutDir() != null ? getSourceOutDir() : getClassOutDir()); 730 } else { 731 Iterable<? extends Path> path = locations.getLocation(location); 732 dir = null; 733 for (Path f: path) { 734 dir = f; 735 break; 736 } 737 } 738 739 try { 740 if (dir == null) { 741 dir = Paths.get(System.getProperty("user.dir")); 742 } 743 Path path = fileName.resolveAgainst(fsInfo.getCanonicalFile(dir)); 744 return PathFileObject.forDirectoryPath(this, path, dir, fileName); 745 } catch (InvalidPathException e) { 746 throw new IOException("bad filename " + fileName, e); 747 } 748 } 749 750 @Override @DefinedBy(Api.COMPILER) 751 public Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles( 752 Iterable<? extends File> files) 753 { 754 ArrayList<PathFileObject> result; 755 if (files instanceof Collection<?>) 756 result = new ArrayList<>(((Collection<?>)files).size()); 757 else 758 result = new ArrayList<>(); 759 for (File f: files) { 760 Objects.requireNonNull(f); 761 Path p = f.toPath(); 762 result.add(PathFileObject.forSimplePath(this, 763 fsInfo.getCanonicalFile(p), p)); 764 } 765 return result; 766 } 767 768 @Override @DefinedBy(Api.COMPILER) 769 public Iterable<? extends JavaFileObject> getJavaFileObjectsFromPaths( 770 Iterable<? extends Path> paths) 771 { 772 ArrayList<PathFileObject> result; 773 if (paths instanceof Collection<?>) 774 result = new ArrayList<>(((Collection<?>)paths).size()); 775 else 776 result = new ArrayList<>(); 777 for (Path p: paths) 778 result.add(PathFileObject.forSimplePath(this, 779 fsInfo.getCanonicalFile(p), p)); 780 return result; 781 } 782 783 @Override @DefinedBy(Api.COMPILER) 784 public Iterable<? extends JavaFileObject> getJavaFileObjects(File... files) { 785 return getJavaFileObjectsFromFiles(Arrays.asList(nullCheck(files))); 786 } 787 788 @Override @DefinedBy(Api.COMPILER) 789 public Iterable<? extends JavaFileObject> getJavaFileObjects(Path... paths) { 790 return getJavaFileObjectsFromPaths(Arrays.asList(nullCheck(paths))); 791 } 792 793 @Override @DefinedBy(Api.COMPILER) 794 public void setLocation(Location location, 795 Iterable<? extends File> searchpath) 796 throws IOException 797 { 798 nullCheck(location); 799 locations.setLocation(location, asPaths(searchpath)); 800 } 801 802 @Override @DefinedBy(Api.COMPILER) 803 public void setLocationFromPaths(Location location, 804 Iterable<? extends Path> searchpath) 805 throws IOException 806 { 807 nullCheck(location); 808 locations.setLocation(location, nullCheck(searchpath)); 809 } 810 811 @Override @DefinedBy(Api.COMPILER) 812 public Iterable<? extends File> getLocation(Location location) { 813 nullCheck(location); 814 return asFiles(locations.getLocation(location)); 815 } 816 817 @Override @DefinedBy(Api.COMPILER) 818 public Iterable<? extends Path> getLocationAsPaths(Location location) { 819 nullCheck(location); 820 return locations.getLocation(location); 821 } 822 823 private Path getClassOutDir() { 824 return locations.getOutputLocation(CLASS_OUTPUT); 825 } 826 827 private Path getSourceOutDir() { 828 return locations.getOutputLocation(SOURCE_OUTPUT); 829 } 830 831 @Override @DefinedBy(Api.COMPILER) 832 public Location getModuleLocation(Location location, String moduleName) throws IOException { 833 nullCheck(location); 834 nullCheck(moduleName); 835 return locations.getModuleLocation(location, moduleName); 836 } 837 838 @Override @DefinedBy(Api.COMPILER) 839 public <S> ServiceLoader<S> getServiceLoader(Location location, Class<S> service) throws IOException { 840 nullCheck(location); 841 nullCheck(service); 842 if (location.isModuleLocation()) { 843 Collection<Path> paths = locations.getLocation(location); 844 ModuleFinder finder = ModuleFinder.of(paths.toArray(new Path[paths.size()])); 845 Layer bootLayer = Layer.boot(); 846 Configuration cf = bootLayer.configuration().resolveRequiresAndUses(ModuleFinder.empty(), finder, Collections.emptySet()); 847 Layer layer = bootLayer.defineModulesWithOneLoader(cf, ClassLoader.getSystemClassLoader()); 848 return ServiceLoaderHelper.load(layer, service); 849 } else { 850 return ServiceLoader.load(service, getClassLoader(location)); 851 } 852 } 853 854 @Override @DefinedBy(Api.COMPILER) 855 public Location getModuleLocation(Location location, JavaFileObject fo, String pkgName) throws IOException { 856 nullCheck(location); 857 if (!(fo instanceof PathFileObject)) 858 throw new IllegalArgumentException(fo.getName()); 859 int depth = 1; // allow 1 for filename 860 if (pkgName != null && !pkgName.isEmpty()) { 861 depth += 1; 862 for (int i = 0; i < pkgName.length(); i++) { 863 switch (pkgName.charAt(i)) { 864 case '/': case '.': 865 depth++; 866 } 867 } 868 } 869 Path p = Locations.normalize(((PathFileObject) fo).path); 870 int fc = p.getNameCount(); 871 if (depth < fc) { 872 Path root = p.getRoot(); 873 Path subpath = p.subpath(0, fc - depth); 874 Path dir = (root == null) ? subpath : root.resolve(subpath); 875 // need to find dir in location 876 return locations.getModuleLocation(location, dir); 877 } else { 878 return null; 879 } 880 } 881 882 @Override @DefinedBy(Api.COMPILER) 883 public String inferModuleName(Location location) { 884 nullCheck(location); 885 return locations.inferModuleName(location); 886 } 887 888 @Override @DefinedBy(Api.COMPILER) 889 public Iterable<Set<Location>> listModuleLocations(Location location) throws IOException { 890 nullCheck(location); 891 return locations.listModuleLocations(location); 892 } 893 894 @Override @DefinedBy(Api.COMPILER) 895 public Path asPath(FileObject file) { 896 if (file instanceof PathFileObject) { 897 return ((PathFileObject) file).path; 898 } else 899 throw new IllegalArgumentException(file.getName()); 900 } 901 902 /** 903 * Enforces the specification of a "relative" name as used in 904 * {@linkplain #getFileForInput(Location,String,String) 905 * getFileForInput}. This method must follow the rules defined in 906 * that method, do not make any changes without consulting the 907 * specification. 908 */ 909 protected static boolean isRelativeUri(URI uri) { 910 if (uri.isAbsolute()) 911 return false; 912 String path = uri.normalize().getPath(); 913 if (path.length() == 0 /* isEmpty() is mustang API */) 914 return false; 915 if (!path.equals(uri.getPath())) // implicitly checks for embedded . and .. 916 return false; 917 if (path.startsWith("/") || path.startsWith("./") || path.startsWith("../")) 918 return false; 919 return true; 920 } 921 922 // Convenience method 923 protected static boolean isRelativeUri(String u) { 924 try { 925 return isRelativeUri(new URI(u)); 926 } catch (URISyntaxException e) { 927 return false; 928 } 929 } 930 931 /** 932 * Converts a relative file name to a relative URI. This is 933 * different from File.toURI as this method does not canonicalize 934 * the file before creating the URI. Furthermore, no schema is 935 * used. 936 * @param file a relative file name 937 * @return a relative URI 938 * @throws IllegalArgumentException if the file name is not 939 * relative according to the definition given in {@link 940 * javax.tools.JavaFileManager#getFileForInput} 941 */ 942 public static String getRelativeName(File file) { 943 if (!file.isAbsolute()) { 944 String result = file.getPath().replace(File.separatorChar, '/'); 945 if (isRelativeUri(result)) 946 return result; 947 } 948 throw new IllegalArgumentException("Invalid relative path: " + file); 949 } 950 951 /** 952 * Get a detail message from an IOException. 953 * Most, but not all, instances of IOException provide a non-null result 954 * for getLocalizedMessage(). But some instances return null: in these 955 * cases, fallover to getMessage(), and if even that is null, return the 956 * name of the exception itself. 957 * @param e an IOException 958 * @return a string to include in a compiler diagnostic 959 */ 960 public static String getMessage(IOException e) { 961 String s = e.getLocalizedMessage(); 962 if (s != null) 963 return s; 964 s = e.getMessage(); 965 if (s != null) 966 return s; 967 return e.toString(); 968 } 969 970 /* Converters between files and paths. 971 * These are temporary until we can update the StandardJavaFileManager API. 972 */ 973 974 private static Iterable<Path> asPaths(final Iterable<? extends File> files) { 975 if (files == null) 976 return null; 977 978 return () -> new Iterator<Path>() { 979 Iterator<? extends File> iter = files.iterator(); 980 981 @Override 982 public boolean hasNext() { 983 return iter.hasNext(); 984 } 985 986 @Override 987 public Path next() { 988 return iter.next().toPath(); 989 } 990 }; 991 } 992 993 private static Iterable<File> asFiles(final Iterable<? extends Path> paths) { 994 if (paths == null) 995 return null; 996 997 return () -> new Iterator<File>() { 998 Iterator<? extends Path> iter = paths.iterator(); 999 1000 @Override 1001 public boolean hasNext() { 1002 return iter.hasNext(); 1003 } 1004 1005 @Override 1006 public File next() { 1007 try { 1008 return iter.next().toFile(); 1009 } catch (UnsupportedOperationException e) { 1010 throw new IllegalStateException(e); 1011 } 1012 } 1013 }; 1014 } 1015} 1016