JavacFileManager.java revision 3814:cea064fe9c1d
1189251Ssam/* 2214734Srpaulo * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. 3252726Srpaulo * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4189251Ssam * 5252726Srpaulo * This code is free software; you can redistribute it and/or modify it 6252726Srpaulo * under the terms of the GNU General Public License version 2 only, as 7189251Ssam * published by the Free Software Foundation. Oracle designates this 8189251Ssam * particular file as subject to the "Classpath" exception as provided 9189251Ssam * by Oracle in the LICENSE file that accompanied this code. 10189251Ssam * 11189251Ssam * This code is distributed in the hope that it will be useful, but WITHOUT 12189251Ssam * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13189251Ssam * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14189251Ssam * version 2 for more details (a copy is included in the LICENSE file that 15189251Ssam * accompanied this code). 16189251Ssam * 17189251Ssam * You should have received a copy of the GNU General Public License version 18189251Ssam * 2 along with this work; if not, write to the Free Software Foundation, 19189251Ssam * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20189251Ssam * 21189251Ssam * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22189251Ssam * or visit www.oracle.com if you need additional information or have any 23189251Ssam * questions. 24189251Ssam */ 25252726Srpaulo 26252726Srpaulopackage com.sun.tools.javac.file; 27252726Srpaulo 28252726Srpauloimport java.io.File; 29252726Srpauloimport java.io.IOException; 30189251Ssamimport java.net.MalformedURLException; 31214734Srpauloimport java.net.URI; 32189251Ssamimport java.net.URISyntaxException; 33189251Ssamimport java.net.URL; 34189251Ssamimport java.nio.CharBuffer; 35189251Ssamimport java.nio.charset.Charset; 36189251Ssamimport java.nio.file.FileSystem; 37189251Ssamimport java.nio.file.FileSystems; 38189251Ssamimport java.nio.file.FileVisitOption; 39189251Ssamimport java.nio.file.FileVisitResult; 40189251Ssamimport java.nio.file.Files; 41189251Ssamimport java.nio.file.InvalidPathException; 42189251Ssamimport java.nio.file.LinkOption; 43189251Ssamimport java.nio.file.Path; 44189251Ssamimport java.nio.file.Paths; 45189251Ssamimport java.nio.file.ProviderNotFoundException; 46189251Ssamimport java.nio.file.SimpleFileVisitor; 47189251Ssamimport java.nio.file.attribute.BasicFileAttributes; 48189251Ssamimport java.nio.file.spi.FileSystemProvider; 49189251Ssamimport java.util.ArrayList; 50189251Ssamimport java.util.Arrays; 51189251Ssamimport java.util.Collection; 52214734Srpauloimport java.util.Collections; 53214734Srpauloimport java.util.Comparator; 54214734Srpauloimport java.util.EnumSet; 55214734Srpauloimport java.util.HashMap; 56252726Srpauloimport java.util.Iterator; 57214734Srpauloimport java.util.Map; 58214734Srpauloimport java.util.Objects; 59214734Srpauloimport java.util.ServiceLoader; 60214734Srpauloimport java.util.Set; 61214734Srpauloimport java.util.stream.Collectors; 62189251Ssamimport java.util.stream.Stream; 63189251Ssam 64189251Ssamimport javax.lang.model.SourceVersion; 65189251Ssamimport javax.tools.FileObject; 66189251Ssamimport javax.tools.JavaFileManager; 67189251Ssamimport javax.tools.JavaFileObject; 68189251Ssamimport javax.tools.StandardJavaFileManager; 69189251Ssam 70189251Ssamimport com.sun.tools.javac.file.RelativePath.RelativeDirectory; 71189251Ssamimport com.sun.tools.javac.file.RelativePath.RelativeFile; 72189251Ssamimport com.sun.tools.javac.util.Assert; 73189251Ssamimport com.sun.tools.javac.util.Context; 74189251Ssamimport com.sun.tools.javac.util.DefinedBy; 75189251Ssamimport com.sun.tools.javac.util.DefinedBy.Api; 76189251Ssamimport com.sun.tools.javac.util.List; 77189251Ssamimport com.sun.tools.javac.util.ListBuffer; 78214734Srpauloimport com.sun.tools.javac.util.JDK9Wrappers.Configuration; 79214734Srpauloimport com.sun.tools.javac.util.JDK9Wrappers.Layer; 80214734Srpauloimport com.sun.tools.javac.util.JDK9Wrappers.ModuleFinder; 81214734Srpauloimport com.sun.tools.javac.util.JDK9Wrappers.Module; 82214734Srpauloimport com.sun.tools.javac.util.JDK9Wrappers.ServiceLoaderHelper; 83214734Srpaulo 84252726Srpauloimport static java.nio.file.FileVisitOption.FOLLOW_LINKS; 85252726Srpaulo 86189251Ssamimport static javax.tools.StandardLocation.*; 87189251Ssam 88189251Ssam/** 89189251Ssam * This class provides access to the source, class and other files 90189251Ssam * used by the compiler and related tools. 91189251Ssam * 92189251Ssam * <p><b>This is NOT part of any supported API. 93189251Ssam * If you write code that depends on this, you do so at your own risk. 94189251Ssam * This code and its internal interfaces are subject to change or 95189251Ssam * deletion without notice.</b> 96189251Ssam */ 97189251Ssampublic class JavacFileManager extends BaseFileManager implements StandardJavaFileManager { 98189251Ssam 99189251Ssam @SuppressWarnings("cast") 100189251Ssam public static char[] toArray(CharBuffer buffer) { 101189251Ssam if (buffer.hasArray()) 102189251Ssam return ((CharBuffer)buffer.compact().flip()).array(); 103189251Ssam else 104189251Ssam return buffer.toString().toCharArray(); 105189251Ssam } 106189251Ssam 107189251Ssam private FSInfo fsInfo; 108189251Ssam 109189251Ssam private final Set<JavaFileObject.Kind> sourceOrClass = 110189251Ssam EnumSet.of(JavaFileObject.Kind.SOURCE, JavaFileObject.Kind.CLASS); 111189251Ssam 112189251Ssam protected boolean symbolFileEnabled; 113189251Ssam 114189251Ssam private PathFactory pathFactory = Paths::get; 115189251Ssam 116189251Ssam protected enum SortFiles implements Comparator<Path> { 117189251Ssam FORWARD { 118189251Ssam @Override 119189251Ssam public int compare(Path f1, Path f2) { 120189251Ssam return f1.getFileName().compareTo(f2.getFileName()); 121189251Ssam } 122189251Ssam }, 123189251Ssam REVERSE { 124189251Ssam @Override 125189251Ssam public int compare(Path f1, Path f2) { 126189251Ssam return -f1.getFileName().compareTo(f2.getFileName()); 127189251Ssam } 128189251Ssam } 129189251Ssam } 130189251Ssam 131189251Ssam protected SortFiles sortFiles; 132189251Ssam 133189251Ssam /** 134189251Ssam * Register a Context.Factory to create a JavacFileManager. 135189251Ssam */ 136189251Ssam public static void preRegister(Context context) { 137189251Ssam context.put(JavaFileManager.class, new Context.Factory<JavaFileManager>() { 138189251Ssam @Override 139189251Ssam public JavaFileManager make(Context c) { 140189251Ssam return new JavacFileManager(c, true, null); 141189251Ssam } 142189251Ssam }); 143189251Ssam } 144189251Ssam 145189251Ssam /** 146189251Ssam * Create a JavacFileManager using a given context, optionally registering 147189251Ssam * it as the JavaFileManager for that context. 148189251Ssam */ 149189251Ssam public JavacFileManager(Context context, boolean register, Charset charset) { 150189251Ssam super(charset); 151189251Ssam if (register) 152189251Ssam context.put(JavaFileManager.class, this); 153189251Ssam setContext(context); 154189251Ssam } 155189251Ssam 156189251Ssam /** 157189251Ssam * Set the context for JavacFileManager. 158189251Ssam */ 159189251Ssam @Override 160189251Ssam public void setContext(Context context) { 161189251Ssam super.setContext(context); 162189251Ssam 163189251Ssam fsInfo = FSInfo.instance(context); 164189251Ssam 165189251Ssam symbolFileEnabled = !options.isSet("ignore.symbol.file"); 166189251Ssam 167189251Ssam String sf = options.get("sortFiles"); 168189251Ssam if (sf != null) { 169189251Ssam sortFiles = (sf.equals("reverse") ? SortFiles.REVERSE : SortFiles.FORWARD); 170189251Ssam } 171189251Ssam } 172189251Ssam 173189251Ssam @Override @DefinedBy(DefinedBy.Api.COMPILER) 174189251Ssam public void setPathFactory(PathFactory f) { 175189251Ssam pathFactory = Objects.requireNonNull(f); 176189251Ssam locations.setPathFactory(f); 177189251Ssam } 178189251Ssam 179189251Ssam private Path getPath(String first, String... more) { 180189251Ssam return pathFactory.getPath(first, more); 181189251Ssam } 182189251Ssam 183189251Ssam /** 184189251Ssam * Set whether or not to use ct.sym as an alternate to rt.jar. 185189251Ssam */ 186189251Ssam public void setSymbolFileEnabled(boolean b) { 187189251Ssam symbolFileEnabled = b; 188189251Ssam } 189189251Ssam 190189251Ssam public boolean isSymbolFileEnabled() { 191189251Ssam return symbolFileEnabled; 192189251Ssam } 193189251Ssam 194189251Ssam // used by tests 195189251Ssam public JavaFileObject getJavaFileObject(String name) { 196189251Ssam return getJavaFileObjects(name).iterator().next(); 197189251Ssam } 198189251Ssam 199189251Ssam // used by tests 200189251Ssam public JavaFileObject getJavaFileObject(Path file) { 201189251Ssam return getJavaFileObjects(file).iterator().next(); 202189251Ssam } 203189251Ssam 204189251Ssam public JavaFileObject getFileForOutput(String classname, 205189251Ssam JavaFileObject.Kind kind, 206189251Ssam JavaFileObject sibling) 207189251Ssam throws IOException 208189251Ssam { 209189251Ssam return getJavaFileForOutput(CLASS_OUTPUT, classname, kind, sibling); 210189251Ssam } 211189251Ssam 212189251Ssam @Override @DefinedBy(Api.COMPILER) 213189251Ssam public Iterable<? extends JavaFileObject> getJavaFileObjectsFromStrings(Iterable<String> names) { 214189251Ssam ListBuffer<Path> paths = new ListBuffer<>(); 215189251Ssam for (String name : names) 216189251Ssam paths.append(getPath(nullCheck(name))); 217189251Ssam return getJavaFileObjectsFromPaths(paths.toList()); 218189251Ssam } 219189251Ssam 220189251Ssam @Override @DefinedBy(Api.COMPILER) 221189251Ssam public Iterable<? extends JavaFileObject> getJavaFileObjects(String... names) { 222189251Ssam return getJavaFileObjectsFromStrings(Arrays.asList(nullCheck(names))); 223189251Ssam } 224189251Ssam 225189251Ssam private static boolean isValidName(String name) { 226189251Ssam // Arguably, isValidName should reject keywords (such as in SourceVersion.isName() ), 227189251Ssam // but the set of keywords depends on the source level, and we don't want 228189251Ssam // impls of JavaFileManager to have to be dependent on the source level. 229189251Ssam // Therefore we simply check that the argument is a sequence of identifiers 230189251Ssam // separated by ".". 231189251Ssam for (String s : name.split("\\.", -1)) { 232189251Ssam if (!SourceVersion.isIdentifier(s)) 233189251Ssam return false; 234189251Ssam } 235189251Ssam return true; 236189251Ssam } 237189251Ssam 238189251Ssam private static void validateClassName(String className) { 239189251Ssam if (!isValidName(className)) 240189251Ssam throw new IllegalArgumentException("Invalid class name: " + className); 241189251Ssam } 242189251Ssam 243189251Ssam private static void validatePackageName(String packageName) { 244189251Ssam if (packageName.length() > 0 && !isValidName(packageName)) 245189251Ssam throw new IllegalArgumentException("Invalid packageName name: " + packageName); 246189251Ssam } 247189251Ssam 248189251Ssam public static void testName(String name, 249189251Ssam boolean isValidPackageName, 250189251Ssam boolean isValidClassName) 251189251Ssam { 252189251Ssam try { 253189251Ssam validatePackageName(name); 254189251Ssam if (!isValidPackageName) 255189251Ssam throw new AssertionError("Invalid package name accepted: " + name); 256189251Ssam printAscii("Valid package name: \"%s\"", name); 257189251Ssam } catch (IllegalArgumentException e) { 258189251Ssam if (isValidPackageName) 259189251Ssam throw new AssertionError("Valid package name rejected: " + name); 260189251Ssam printAscii("Invalid package name: \"%s\"", name); 261189251Ssam } 262189251Ssam try { 263189251Ssam validateClassName(name); 264189251Ssam if (!isValidClassName) 265189251Ssam throw new AssertionError("Invalid class name accepted: " + name); 266189251Ssam printAscii("Valid class name: \"%s\"", name); 267189251Ssam } catch (IllegalArgumentException e) { 268189251Ssam if (isValidClassName) 269189251Ssam throw new AssertionError("Valid class name rejected: " + name); 270189251Ssam printAscii("Invalid class name: \"%s\"", name); 271189251Ssam } 272189251Ssam } 273189251Ssam 274189251Ssam private static void printAscii(String format, Object... args) { 275189251Ssam String message; 276189251Ssam try { 277189251Ssam final String ascii = "US-ASCII"; 278189251Ssam message = new String(String.format(null, format, args).getBytes(ascii), ascii); 279189251Ssam } catch (java.io.UnsupportedEncodingException ex) { 280189251Ssam throw new AssertionError(ex); 281189251Ssam } 282189251Ssam System.out.println(message); 283189251Ssam } 284189251Ssam 285189251Ssam private final Map<Path, Container> containers = new HashMap<>(); 286189251Ssam 287189251Ssam synchronized Container getContainer(Path path) throws IOException { 288189251Ssam Container fs = containers.get(path); 289189251Ssam 290189251Ssam if (fs != null) { 291189251Ssam return fs; 292189251Ssam } 293189251Ssam 294189251Ssam if (fsInfo.isFile(path) && path.equals(Locations.thisSystemModules)) { 295189251Ssam containers.put(path, fs = new JRTImageContainer()); 296189251Ssam return fs; 297189251Ssam } 298189251Ssam 299189251Ssam Path realPath = fsInfo.getCanonicalFile(path); 300189251Ssam 301189251Ssam fs = containers.get(realPath); 302189251Ssam 303189251Ssam if (fs != null) { 304189251Ssam containers.put(path, fs); 305189251Ssam return fs; 306189251Ssam } 307189251Ssam 308189251Ssam BasicFileAttributes attr = null; 309189251Ssam 310189251Ssam try { 311189251Ssam attr = Files.readAttributes(realPath, BasicFileAttributes.class); 312189251Ssam } catch (IOException ex) { 313189251Ssam //non-existing 314189251Ssam fs = MISSING_CONTAINER; 315189251Ssam } 316189251Ssam 317189251Ssam if (attr != null) { 318189251Ssam if (attr.isDirectory()) { 319189251Ssam fs = new DirectoryContainer(realPath); 320189251Ssam } else { 321189251Ssam try { 322189251Ssam fs = new ArchiveContainer(realPath); 323189251Ssam } catch (ProviderNotFoundException | SecurityException ex) { 324189251Ssam throw new IOException(ex); 325189251Ssam } 326189251Ssam } 327189251Ssam } 328189251Ssam 329189251Ssam containers.put(realPath, fs); 330189251Ssam containers.put(path, fs); 331189251Ssam 332189251Ssam return fs; 333189251Ssam } 334189251Ssam 335189251Ssam private interface Container { 336189251Ssam /** 337189251Ssam * Insert all files in subdirectory subdirectory of container which 338189251Ssam * match fileKinds into resultList 339189251Ssam */ 340189251Ssam public abstract void list(Path userPath, 341189251Ssam RelativeDirectory subdirectory, 342189251Ssam Set<JavaFileObject.Kind> fileKinds, 343189251Ssam boolean recurse, 344189251Ssam ListBuffer<JavaFileObject> resultList) throws IOException; 345189251Ssam public abstract JavaFileObject getFileObject(Path userPath, RelativeFile name) throws IOException; 346189251Ssam public abstract void close() throws IOException; 347189251Ssam } 348189251Ssam 349189251Ssam private static final Container MISSING_CONTAINER = new Container() { 350189251Ssam @Override 351189251Ssam public void list(Path userPath, 352189251Ssam RelativeDirectory subdirectory, 353189251Ssam Set<JavaFileObject.Kind> fileKinds, 354189251Ssam boolean recurse, 355189251Ssam ListBuffer<JavaFileObject> resultList) throws IOException { 356189251Ssam } 357189251Ssam @Override 358189251Ssam public JavaFileObject getFileObject(Path userPath, RelativeFile name) throws IOException { 359189251Ssam return null; 360189251Ssam } 361189251Ssam @Override 362189251Ssam public void close() throws IOException {} 363189251Ssam }; 364189251Ssam 365189251Ssam private final class JRTImageContainer implements Container { 366189251Ssam 367189251Ssam /** 368189251Ssam * Insert all files in a subdirectory of the platform image 369189251Ssam * which match fileKinds into resultList. 370189251Ssam */ 371189251Ssam @Override 372189251Ssam public void list(Path userPath, 373189251Ssam RelativeDirectory subdirectory, 374189251Ssam Set<JavaFileObject.Kind> fileKinds, 375189251Ssam boolean recurse, 376189251Ssam ListBuffer<JavaFileObject> resultList) throws IOException { 377189251Ssam try { 378189251Ssam JRTIndex.Entry e = getJRTIndex().getEntry(subdirectory); 379189251Ssam if (symbolFileEnabled && e.ctSym.hidden) 380189251Ssam return; 381189251Ssam for (Path file: e.files.values()) { 382189251Ssam if (fileKinds.contains(getKind(file))) { 383189251Ssam JavaFileObject fe 384189251Ssam = PathFileObject.forJRTPath(JavacFileManager.this, file); 385189251Ssam resultList.append(fe); 386189251Ssam } 387189251Ssam } 388189251Ssam 389189251Ssam if (recurse) { 390189251Ssam for (RelativeDirectory rd: e.subdirs) { 391189251Ssam list(userPath, rd, fileKinds, recurse, resultList); 392189251Ssam } 393189251Ssam } 394189251Ssam } catch (IOException ex) { 395189251Ssam ex.printStackTrace(System.err); 396189251Ssam log.error("error.reading.file", userPath, getMessage(ex)); 397189251Ssam } 398189251Ssam } 399189251Ssam 400189251Ssam @Override 401189251Ssam public JavaFileObject getFileObject(Path userPath, RelativeFile name) throws IOException { 402189251Ssam JRTIndex.Entry e = getJRTIndex().getEntry(name.dirname()); 403189251Ssam if (symbolFileEnabled && e.ctSym.hidden) 404189251Ssam return null; 405189251Ssam Path p = e.files.get(name.basename()); 406189251Ssam if (p != null) { 407189251Ssam return PathFileObject.forJRTPath(JavacFileManager.this, p); 408189251Ssam } else { 409189251Ssam return null; 410189251Ssam } 411189251Ssam } 412189251Ssam 413189251Ssam @Override 414189251Ssam public void close() throws IOException { 415189251Ssam } 416189251Ssam } 417189251Ssam 418189251Ssam private synchronized JRTIndex getJRTIndex() { 419189251Ssam if (jrtIndex == null) 420189251Ssam jrtIndex = JRTIndex.getSharedInstance(); 421189251Ssam return jrtIndex; 422189251Ssam } 423189251Ssam 424189251Ssam private JRTIndex jrtIndex; 425189251Ssam 426189251Ssam private final class DirectoryContainer implements Container { 427189251Ssam private final Path directory; 428189251Ssam 429189251Ssam public DirectoryContainer(Path directory) { 430189251Ssam this.directory = directory; 431189251Ssam } 432189251Ssam 433189251Ssam /** 434189251Ssam * Insert all files in subdirectory subdirectory of directory userPath 435189251Ssam * which match fileKinds into resultList 436189251Ssam */ 437189251Ssam @Override 438189251Ssam public void list(Path userPath, 439189251Ssam RelativeDirectory subdirectory, 440189251Ssam Set<JavaFileObject.Kind> fileKinds, 441189251Ssam boolean recurse, 442189251Ssam ListBuffer<JavaFileObject> resultList) throws IOException { 443189251Ssam Path d; 444189251Ssam try { 445189251Ssam d = subdirectory.resolveAgainst(userPath); 446189251Ssam } catch (InvalidPathException ignore) { 447189251Ssam return ; 448189251Ssam } 449189251Ssam 450189251Ssam if (!Files.exists(d)) { 451189251Ssam return; 452189251Ssam } 453189251Ssam 454189251Ssam if (!caseMapCheck(d, subdirectory)) { 455189251Ssam return; 456189251Ssam } 457189251Ssam 458189251Ssam java.util.List<Path> files; 459189251Ssam try (Stream<Path> s = Files.list(d)) { 460189251Ssam files = (sortFiles == null ? s : s.sorted(sortFiles)).collect(Collectors.toList()); 461189251Ssam } catch (IOException ignore) { 462189251Ssam return; 463189251Ssam } 464189251Ssam 465189251Ssam for (Path f: files) { 466189251Ssam String fname = f.getFileName().toString(); 467189251Ssam if (fname.endsWith("/")) 468189251Ssam fname = fname.substring(0, fname.length() - 1); 469189251Ssam if (Files.isDirectory(f)) { 470189251Ssam if (recurse && SourceVersion.isIdentifier(fname)) { 471189251Ssam list(userPath, 472189251Ssam new RelativeDirectory(subdirectory, fname), 473189251Ssam fileKinds, 474189251Ssam recurse, 475189251Ssam resultList); 476189251Ssam } 477189251Ssam } else { 478189251Ssam if (isValidFile(fname, fileKinds)) { 479189251Ssam RelativeFile file = new RelativeFile(subdirectory, fname); 480189251Ssam JavaFileObject fe = PathFileObject.forDirectoryPath(JavacFileManager.this, 481189251Ssam file.resolveAgainst(directory), userPath, file); 482189251Ssam resultList.append(fe); 483189251Ssam } 484189251Ssam } 485189251Ssam } 486189251Ssam } 487189251Ssam 488189251Ssam @Override 489189251Ssam public JavaFileObject getFileObject(Path userPath, RelativeFile name) throws IOException { 490189251Ssam try { 491189251Ssam Path f = name.resolveAgainst(userPath); 492189251Ssam if (Files.exists(f)) 493189251Ssam return PathFileObject.forSimplePath(JavacFileManager.this, 494189251Ssam fsInfo.getCanonicalFile(f), f); 495189251Ssam } catch (InvalidPathException ignore) { 496189251Ssam } 497189251Ssam return null; 498189251Ssam } 499189251Ssam 500189251Ssam @Override 501189251Ssam public void close() throws IOException { 502189251Ssam } 503189251Ssam } 504189251Ssam 505189251Ssam private final class ArchiveContainer implements Container { 506189251Ssam private final Path archivePath; 507189251Ssam private final FileSystem fileSystem; 508189251Ssam private final Map<RelativePath, Path> pathCache = new HashMap<>(); 509189251Ssam 510189251Ssam public ArchiveContainer(Path archivePath) throws IOException, ProviderNotFoundException, SecurityException { 511189251Ssam this.archivePath = archivePath; 512189251Ssam if (multiReleaseValue != null && archivePath.toString().endsWith(".jar")) { 513189251Ssam Map<String,String> env = Collections.singletonMap("multi-release", multiReleaseValue); 514189251Ssam FileSystemProvider jarFSProvider = fsInfo.getJarFSProvider(); 515189251Ssam Assert.checkNonNull(jarFSProvider, "should have been caught before!"); 516189251Ssam this.fileSystem = jarFSProvider.newFileSystem(archivePath, env); 517189251Ssam } else { 518189251Ssam this.fileSystem = FileSystems.newFileSystem(archivePath, null); 519189251Ssam } 520189251Ssam } 521189251Ssam 522189251Ssam /** 523189251Ssam * Insert all files in subdirectory subdirectory of this archive 524189251Ssam * which match fileKinds into resultList 525189251Ssam */ 526189251Ssam @Override 527189251Ssam public void list(Path userPath, 528252726Srpaulo RelativeDirectory subdirectory, 529252726Srpaulo Set<JavaFileObject.Kind> fileKinds, 530252726Srpaulo boolean recurse, 531252726Srpaulo ListBuffer<JavaFileObject> resultList) throws IOException { 532252726Srpaulo Path resolvedSubdirectory = resolvePath(subdirectory); 533252726Srpaulo 534252726Srpaulo if (resolvedSubdirectory == null) 535252726Srpaulo return ; 536252726Srpaulo 537189251Ssam int maxDepth = (recurse ? Integer.MAX_VALUE : 1); 538189251Ssam Set<FileVisitOption> opts = EnumSet.of(FOLLOW_LINKS); 539189251Ssam Files.walkFileTree(resolvedSubdirectory, opts, maxDepth, 540189251Ssam new SimpleFileVisitor<Path>() { 541189251Ssam @Override 542189251Ssam public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { 543189251Ssam if (isValid(dir.getFileName())) { 544189251Ssam return FileVisitResult.CONTINUE; 545189251Ssam } else { 546189251Ssam return FileVisitResult.SKIP_SUBTREE; 547189251Ssam } 548189251Ssam } 549189251Ssam 550189251Ssam boolean isValid(Path fileName) { 551189251Ssam if (fileName == null) { 552189251Ssam return true; 553189251Ssam } else { 554189251Ssam String name = fileName.toString(); 555189251Ssam if (name.endsWith("/")) { 556189251Ssam name = name.substring(0, name.length() - 1); 557189251Ssam } 558189251Ssam return SourceVersion.isIdentifier(name); 559189251Ssam } 560189251Ssam } 561189251Ssam 562189251Ssam @Override 563189251Ssam public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { 564189251Ssam if (attrs.isRegularFile() && fileKinds.contains(getKind(file.getFileName().toString()))) { 565189251Ssam JavaFileObject fe = PathFileObject.forJarPath( 566189251Ssam JavacFileManager.this, file, archivePath); 567189251Ssam resultList.append(fe); 568189251Ssam } 569189251Ssam return FileVisitResult.CONTINUE; 570189251Ssam } 571189251Ssam }); 572189251Ssam 573189251Ssam } 574189251Ssam 575189251Ssam @Override 576189251Ssam public JavaFileObject getFileObject(Path userPath, RelativeFile name) throws IOException { 577189251Ssam Path p = resolvePath(name); 578189251Ssam if (p != null) 579189251Ssam return PathFileObject.forJarPath(JavacFileManager.this, p, userPath); 580189251Ssam 581189251Ssam return null; 582189251Ssam } 583189251Ssam 584189251Ssam private synchronized Path resolvePath(RelativePath path) { 585189251Ssam if (!pathCache.containsKey(path)) { 586189251Ssam Path relativePath = path.resolveAgainst(fileSystem); 587189251Ssam 588189251Ssam if (!Files.exists(relativePath)) { 589189251Ssam relativePath = null; 590189251Ssam } 591189251Ssam 592189251Ssam pathCache.put(path, relativePath); 593189251Ssam return relativePath; 594189251Ssam } 595189251Ssam return pathCache.get(path); 596189251Ssam } 597189251Ssam 598189251Ssam @Override 599189251Ssam public void close() throws IOException { 600189251Ssam fileSystem.close(); 601189251Ssam } 602189251Ssam } 603189251Ssam 604189251Ssam /** 605189251Ssam * container is a directory, a zip file, or a non-existent path. 606189251Ssam */ 607189251Ssam private boolean isValidFile(String s, Set<JavaFileObject.Kind> fileKinds) { 608189251Ssam JavaFileObject.Kind kind = getKind(s); 609189251Ssam return fileKinds.contains(kind); 610189251Ssam } 611189251Ssam 612189251Ssam private static final boolean fileSystemIsCaseSensitive = 613189251Ssam File.separatorChar == '/'; 614189251Ssam 615189251Ssam /** Hack to make Windows case sensitive. Test whether given path 616189251Ssam * ends in a string of characters with the same case as given name. 617189251Ssam * Ignore file separators in both path and name. 618189251Ssam */ 619189251Ssam private boolean caseMapCheck(Path f, RelativePath name) { 620189251Ssam if (fileSystemIsCaseSensitive) return true; 621189251Ssam // Note that toRealPath() returns the case-sensitive 622189251Ssam // spelled file name. 623189251Ssam String path; 624189251Ssam char sep; 625189251Ssam try { 626189251Ssam path = f.toRealPath(LinkOption.NOFOLLOW_LINKS).toString(); 627189251Ssam sep = f.getFileSystem().getSeparator().charAt(0); 628189251Ssam } catch (IOException ex) { 629189251Ssam return false; 630189251Ssam } 631189251Ssam char[] pcs = path.toCharArray(); 632189251Ssam char[] ncs = name.path.toCharArray(); 633189251Ssam int i = pcs.length - 1; 634189251Ssam int j = ncs.length - 1; 635189251Ssam while (i >= 0 && j >= 0) { 636189251Ssam while (i >= 0 && pcs[i] == sep) i--; 637189251Ssam while (j >= 0 && ncs[j] == '/') j--; 638189251Ssam if (i >= 0 && j >= 0) { 639189251Ssam if (pcs[i] != ncs[j]) return false; 640189251Ssam i--; 641189251Ssam j--; 642189251Ssam } 643189251Ssam } 644189251Ssam return j < 0; 645189251Ssam } 646189251Ssam 647189251Ssam /** Flush any output resources. 648189251Ssam */ 649189251Ssam @Override @DefinedBy(Api.COMPILER) 650189251Ssam public void flush() { 651189251Ssam contentCache.clear(); 652189251Ssam } 653189251Ssam 654189251Ssam /** 655189251Ssam * Close the JavaFileManager, releasing resources. 656189251Ssam */ 657189251Ssam @Override @DefinedBy(Api.COMPILER) 658189251Ssam public void close() throws IOException { 659189251Ssam if (deferredCloseTimeout > 0) { 660189251Ssam deferredClose(); 661189251Ssam return; 662189251Ssam } 663189251Ssam 664189251Ssam locations.close(); 665189251Ssam for (Container container: containers.values()) { 666189251Ssam container.close(); 667189251Ssam } 668189251Ssam containers.clear(); 669189251Ssam contentCache.clear(); 670189251Ssam } 671189251Ssam 672189251Ssam @Override @DefinedBy(Api.COMPILER) 673189251Ssam public ClassLoader getClassLoader(Location location) { 674189251Ssam checkNotModuleOrientedLocation(location); 675189251Ssam Iterable<? extends File> path = getLocation(location); 676189251Ssam if (path == null) 677189251Ssam return null; 678189251Ssam ListBuffer<URL> lb = new ListBuffer<>(); 679189251Ssam for (File f: path) { 680189251Ssam try { 681189251Ssam lb.append(f.toURI().toURL()); 682189251Ssam } catch (MalformedURLException e) { 683189251Ssam throw new AssertionError(e); 684189251Ssam } 685189251Ssam } 686189251Ssam 687189251Ssam return getClassLoader(lb.toArray(new URL[lb.size()])); 688189251Ssam } 689189251Ssam 690189251Ssam @Override @DefinedBy(Api.COMPILER) 691189251Ssam public Iterable<JavaFileObject> list(Location location, 692189251Ssam String packageName, 693189251Ssam Set<JavaFileObject.Kind> kinds, 694189251Ssam boolean recurse) 695214734Srpaulo throws IOException 696214734Srpaulo { 697214734Srpaulo checkNotModuleOrientedLocation(location); 698214734Srpaulo // validatePackageName(packageName); 699214734Srpaulo nullCheck(packageName); 700214734Srpaulo nullCheck(kinds); 701252726Srpaulo 702214734Srpaulo Iterable<? extends Path> path = getLocationAsPaths(location); 703214734Srpaulo if (path == null) 704214734Srpaulo return List.nil(); 705214734Srpaulo RelativeDirectory subdirectory = RelativeDirectory.forPackage(packageName); 706214734Srpaulo ListBuffer<JavaFileObject> results = new ListBuffer<>(); 707214734Srpaulo 708214734Srpaulo for (Path directory : path) { 709214734Srpaulo Container container = getContainer(directory); 710214734Srpaulo 711214734Srpaulo container.list(directory, subdirectory, kinds, recurse, results); 712252726Srpaulo } 713252726Srpaulo 714214734Srpaulo return results.toList(); 715214734Srpaulo } 716214734Srpaulo 717214734Srpaulo @Override @DefinedBy(Api.COMPILER) 718214734Srpaulo public String inferBinaryName(Location location, JavaFileObject file) { 719214734Srpaulo checkNotModuleOrientedLocation(location); 720214734Srpaulo Objects.requireNonNull(file); 721214734Srpaulo // Need to match the path semantics of list(location, ...) 722252726Srpaulo Iterable<? extends Path> path = getLocationAsPaths(location); 723252726Srpaulo if (path == null) { 724214734Srpaulo return null; 725214734Srpaulo } 726214734Srpaulo 727214734Srpaulo if (file instanceof PathFileObject) { 728189251Ssam return ((PathFileObject) file).inferBinaryName(path); 729189251Ssam } else 730252726Srpaulo throw new IllegalArgumentException(file.getClass().getName()); 731209158Srpaulo } 732209158Srpaulo 733189251Ssam @Override @DefinedBy(Api.COMPILER) 734189251Ssam public boolean isSameFile(FileObject a, FileObject b) { 735189251Ssam nullCheck(a); 736189251Ssam nullCheck(b); 737189251Ssam if (a instanceof PathFileObject && b instanceof PathFileObject) 738209158Srpaulo return ((PathFileObject) a).isSameFile((PathFileObject) b); 739209158Srpaulo return a.equals(b); 740209158Srpaulo } 741209158Srpaulo 742209158Srpaulo @Override @DefinedBy(Api.COMPILER) 743209158Srpaulo public boolean hasLocation(Location location) { 744209158Srpaulo nullCheck(location); 745209158Srpaulo return locations.hasLocation(location); 746209158Srpaulo } 747189251Ssam 748189251Ssam @Override @DefinedBy(Api.COMPILER) 749189251Ssam public JavaFileObject getJavaFileForInput(Location location, 750189251Ssam String className, 751189251Ssam JavaFileObject.Kind kind) 752189251Ssam throws IOException 753189251Ssam { 754189251Ssam checkNotModuleOrientedLocation(location); 755189251Ssam // validateClassName(className); 756189251Ssam nullCheck(className); 757189251Ssam nullCheck(kind); 758189251Ssam if (!sourceOrClass.contains(kind)) 759189251Ssam throw new IllegalArgumentException("Invalid kind: " + kind); 760189251Ssam return getFileForInput(location, RelativeFile.forClass(className, kind)); 761189251Ssam } 762189251Ssam 763189251Ssam @Override @DefinedBy(Api.COMPILER) 764189251Ssam public FileObject getFileForInput(Location location, 765189251Ssam String packageName, 766189251Ssam String relativeName) 767189251Ssam throws IOException 768189251Ssam { 769189251Ssam checkNotModuleOrientedLocation(location); 770189251Ssam // validatePackageName(packageName); 771189251Ssam nullCheck(packageName); 772189251Ssam if (!isRelativeUri(relativeName)) 773189251Ssam throw new IllegalArgumentException("Invalid relative name: " + relativeName); 774189251Ssam RelativeFile name = packageName.length() == 0 775189251Ssam ? new RelativeFile(relativeName) 776189251Ssam : new RelativeFile(RelativeDirectory.forPackage(packageName), relativeName); 777189251Ssam return getFileForInput(location, name); 778189251Ssam } 779189251Ssam 780189251Ssam private JavaFileObject getFileForInput(Location location, RelativeFile name) throws IOException { 781189251Ssam Iterable<? extends Path> path = getLocationAsPaths(location); 782189251Ssam if (path == null) 783189251Ssam return null; 784189251Ssam 785189251Ssam for (Path file: path) { 786189251Ssam JavaFileObject fo = getContainer(file).getFileObject(file, name); 787189251Ssam 788189251Ssam if (fo != null) { 789189251Ssam return fo; 790189251Ssam } 791189251Ssam } 792189251Ssam return null; 793214734Srpaulo } 794214734Srpaulo 795189251Ssam @Override @DefinedBy(Api.COMPILER) 796189251Ssam public JavaFileObject getJavaFileForOutput(Location location, 797189251Ssam String className, 798189251Ssam JavaFileObject.Kind kind, 799189251Ssam FileObject sibling) 800189251Ssam throws IOException 801189251Ssam { 802189251Ssam checkOutputLocation(location); 803189251Ssam // validateClassName(className); 804189251Ssam nullCheck(className); 805189251Ssam nullCheck(kind); 806189251Ssam if (!sourceOrClass.contains(kind)) 807189251Ssam throw new IllegalArgumentException("Invalid kind: " + kind); 808189251Ssam return getFileForOutput(location, RelativeFile.forClass(className, kind), sibling); 809189251Ssam } 810189251Ssam 811189251Ssam @Override @DefinedBy(Api.COMPILER) 812189251Ssam public FileObject getFileForOutput(Location location, 813189251Ssam String packageName, 814189251Ssam String relativeName, 815189251Ssam FileObject sibling) 816189251Ssam throws IOException 817189251Ssam { 818189251Ssam checkOutputLocation(location); 819189251Ssam // validatePackageName(packageName); 820189251Ssam nullCheck(packageName); 821189251Ssam if (!isRelativeUri(relativeName)) 822189251Ssam throw new IllegalArgumentException("Invalid relative name: " + relativeName); 823189251Ssam RelativeFile name = packageName.length() == 0 824189251Ssam ? new RelativeFile(relativeName) 825189251Ssam : new RelativeFile(RelativeDirectory.forPackage(packageName), relativeName); 826189251Ssam return getFileForOutput(location, name, sibling); 827189251Ssam } 828189251Ssam 829189251Ssam private JavaFileObject getFileForOutput(Location location, 830189251Ssam RelativeFile fileName, 831189251Ssam FileObject sibling) 832189251Ssam throws IOException 833189251Ssam { 834189251Ssam Path dir; 835189251Ssam if (location == CLASS_OUTPUT) { 836189251Ssam if (getClassOutDir() != null) { 837189251Ssam dir = getClassOutDir(); 838189251Ssam } else { 839189251Ssam String baseName = fileName.basename(); 840189251Ssam if (sibling != null && sibling instanceof PathFileObject) { 841189251Ssam return ((PathFileObject) sibling).getSibling(baseName); 842189251Ssam } else { 843189251Ssam Path p = getPath(baseName); 844189251Ssam Path real = fsInfo.getCanonicalFile(p); 845189251Ssam return PathFileObject.forSimplePath(this, real, p); 846189251Ssam } 847189251Ssam } 848189251Ssam } else if (location == SOURCE_OUTPUT) { 849189251Ssam dir = (getSourceOutDir() != null ? getSourceOutDir() : getClassOutDir()); 850189251Ssam } else { 851189251Ssam Iterable<? extends Path> path = locations.getLocation(location); 852189251Ssam dir = null; 853189251Ssam for (Path f: path) { 854189251Ssam dir = f; 855189251Ssam break; 856189251Ssam } 857189251Ssam } 858189251Ssam 859189251Ssam try { 860189251Ssam if (dir == null) { 861189251Ssam dir = getPath(System.getProperty("user.dir")); 862189251Ssam } 863189251Ssam Path path = fileName.resolveAgainst(fsInfo.getCanonicalFile(dir)); 864189251Ssam return PathFileObject.forDirectoryPath(this, path, dir, fileName); 865189251Ssam } catch (InvalidPathException e) { 866189251Ssam throw new IOException("bad filename " + fileName, e); 867189251Ssam } 868189251Ssam } 869189251Ssam 870189251Ssam @Override @DefinedBy(Api.COMPILER) 871189251Ssam public Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles( 872189251Ssam Iterable<? extends File> files) 873189251Ssam { 874189251Ssam ArrayList<PathFileObject> result; 875189251Ssam if (files instanceof Collection<?>) 876189251Ssam result = new ArrayList<>(((Collection<?>)files).size()); 877189251Ssam else 878189251Ssam result = new ArrayList<>(); 879189251Ssam for (File f: files) { 880189251Ssam Objects.requireNonNull(f); 881189251Ssam Path p = f.toPath(); 882189251Ssam result.add(PathFileObject.forSimplePath(this, 883189251Ssam fsInfo.getCanonicalFile(p), p)); 884189251Ssam } 885189251Ssam return result; 886189251Ssam } 887189251Ssam 888189251Ssam @Override @DefinedBy(Api.COMPILER) 889189251Ssam public Iterable<? extends JavaFileObject> getJavaFileObjectsFromPaths( 890189251Ssam Iterable<? extends Path> paths) 891189251Ssam { 892189251Ssam ArrayList<PathFileObject> result; 893189251Ssam if (paths instanceof Collection<?>) 894189251Ssam result = new ArrayList<>(((Collection<?>)paths).size()); 895189251Ssam else 896189251Ssam result = new ArrayList<>(); 897189251Ssam for (Path p: paths) 898189251Ssam result.add(PathFileObject.forSimplePath(this, 899189251Ssam fsInfo.getCanonicalFile(p), p)); 900189251Ssam return result; 901189251Ssam } 902189251Ssam 903189251Ssam @Override @DefinedBy(Api.COMPILER) 904189251Ssam public Iterable<? extends JavaFileObject> getJavaFileObjects(File... files) { 905189251Ssam return getJavaFileObjectsFromFiles(Arrays.asList(nullCheck(files))); 906189251Ssam } 907189251Ssam 908189251Ssam @Override @DefinedBy(Api.COMPILER) 909189251Ssam public Iterable<? extends JavaFileObject> getJavaFileObjects(Path... paths) { 910189251Ssam return getJavaFileObjectsFromPaths(Arrays.asList(nullCheck(paths))); 911189251Ssam } 912189251Ssam 913189251Ssam @Override @DefinedBy(Api.COMPILER) 914189251Ssam public void setLocation(Location location, 915189251Ssam Iterable<? extends File> searchpath) 916189251Ssam throws IOException 917189251Ssam { 918189251Ssam nullCheck(location); 919189251Ssam locations.setLocation(location, asPaths(searchpath)); 920189251Ssam } 921189251Ssam 922189251Ssam @Override @DefinedBy(Api.COMPILER) 923189251Ssam public void setLocationFromPaths(Location location, 924189251Ssam Collection<? extends Path> searchpath) 925189251Ssam throws IOException 926189251Ssam { 927189251Ssam nullCheck(location); 928189251Ssam locations.setLocation(location, nullCheck(searchpath)); 929189251Ssam } 930189251Ssam 931189251Ssam @Override @DefinedBy(Api.COMPILER) 932189251Ssam public Iterable<? extends File> getLocation(Location location) { 933189251Ssam nullCheck(location); 934189251Ssam return asFiles(locations.getLocation(location)); 935189251Ssam } 936189251Ssam 937189251Ssam @Override @DefinedBy(Api.COMPILER) 938189251Ssam public Iterable<? extends Path> getLocationAsPaths(Location location) { 939189251Ssam nullCheck(location); 940189251Ssam return locations.getLocation(location); 941189251Ssam } 942189251Ssam 943189251Ssam private Path getClassOutDir() { 944189251Ssam return locations.getOutputLocation(CLASS_OUTPUT); 945189251Ssam } 946189251Ssam 947189251Ssam private Path getSourceOutDir() { 948189251Ssam return locations.getOutputLocation(SOURCE_OUTPUT); 949189251Ssam } 950189251Ssam 951189251Ssam @Override @DefinedBy(Api.COMPILER) 952189251Ssam public Location getLocationForModule(Location location, String moduleName) throws IOException { 953189251Ssam checkModuleOrientedOrOutputLocation(location); 954189251Ssam nullCheck(moduleName); 955189251Ssam return locations.getLocationForModule(location, moduleName); 956189251Ssam } 957189251Ssam 958189251Ssam @Override @DefinedBy(Api.COMPILER) 959189251Ssam public <S> ServiceLoader<S> getServiceLoader(Location location, Class<S> service) throws IOException { 960189251Ssam nullCheck(location); 961189251Ssam nullCheck(service); 962189251Ssam Module.getModule(getClass()).addUses(service); 963189251Ssam if (location.isModuleOrientedLocation()) { 964189251Ssam Collection<Path> paths = locations.getLocation(location); 965189251Ssam ModuleFinder finder = ModuleFinder.of(paths.toArray(new Path[paths.size()])); 966189251Ssam Layer bootLayer = Layer.boot(); 967189251Ssam Configuration cf = bootLayer.configuration().resolveRequiresAndUses(ModuleFinder.of(), finder, Collections.emptySet()); 968189251Ssam Layer layer = bootLayer.defineModulesWithOneLoader(cf, ClassLoader.getSystemClassLoader()); 969189251Ssam return ServiceLoaderHelper.load(layer, service); 970189251Ssam } else { 971189251Ssam return ServiceLoader.load(service, getClassLoader(location)); 972189251Ssam } 973189251Ssam } 974189251Ssam 975189251Ssam @Override @DefinedBy(Api.COMPILER) 976189251Ssam public Location getLocationForModule(Location location, JavaFileObject fo, String pkgName) throws IOException { 977189251Ssam checkModuleOrientedOrOutputLocation(location); 978189251Ssam if (!(fo instanceof PathFileObject)) 979189251Ssam throw new IllegalArgumentException(fo.getName()); 980189251Ssam int depth = 1; // allow 1 for filename 981189251Ssam if (pkgName != null && !pkgName.isEmpty()) { 982189251Ssam depth += 1; 983189251Ssam for (int i = 0; i < pkgName.length(); i++) { 984189251Ssam switch (pkgName.charAt(i)) { 985189251Ssam case '/': case '.': 986189251Ssam depth++; 987189251Ssam } 988189251Ssam } 989189251Ssam } 990189251Ssam Path p = Locations.normalize(((PathFileObject) fo).path); 991189251Ssam int fc = p.getNameCount(); 992189251Ssam if (depth < fc) { 993189251Ssam Path root = p.getRoot(); 994189251Ssam Path subpath = p.subpath(0, fc - depth); 995189251Ssam Path dir = (root == null) ? subpath : root.resolve(subpath); 996189251Ssam // need to find dir in location 997189251Ssam return locations.getLocationForModule(location, dir); 998189251Ssam } else { 999189251Ssam return null; 1000189251Ssam } 1001189251Ssam } 1002189251Ssam 1003189251Ssam @Override @DefinedBy(Api.COMPILER) 1004189251Ssam public String inferModuleName(Location location) { 1005189251Ssam checkNotModuleOrientedLocation(location); 1006189251Ssam return locations.inferModuleName(location); 1007189251Ssam } 1008189251Ssam 1009189251Ssam @Override @DefinedBy(Api.COMPILER) 1010189251Ssam public Iterable<Set<Location>> listLocationsForModules(Location location) throws IOException { 1011189251Ssam checkModuleOrientedOrOutputLocation(location); 1012189251Ssam return locations.listLocationsForModules(location); 1013189251Ssam } 1014189251Ssam 1015189251Ssam @Override @DefinedBy(Api.COMPILER) 1016189251Ssam public Path asPath(FileObject file) { 1017189251Ssam if (file instanceof PathFileObject) { 1018189251Ssam return ((PathFileObject) file).path; 1019189251Ssam } else 1020189251Ssam throw new IllegalArgumentException(file.getName()); 1021189251Ssam } 1022189251Ssam 1023189251Ssam /** 1024189251Ssam * Enforces the specification of a "relative" name as used in 1025189251Ssam * {@linkplain #getFileForInput(Location,String,String) 1026189251Ssam * getFileForInput}. This method must follow the rules defined in 1027189251Ssam * that method, do not make any changes without consulting the 1028189251Ssam * specification. 1029189251Ssam */ 1030189251Ssam protected static boolean isRelativeUri(URI uri) { 1031189251Ssam if (uri.isAbsolute()) 1032189251Ssam return false; 1033189251Ssam String path = uri.normalize().getPath(); 1034189251Ssam if (path.length() == 0 /* isEmpty() is mustang API */) 1035189251Ssam return false; 1036189251Ssam if (!path.equals(uri.getPath())) // implicitly checks for embedded . and .. 1037189251Ssam return false; 1038189251Ssam if (path.startsWith("/") || path.startsWith("./") || path.startsWith("../")) 1039189251Ssam return false; 1040189251Ssam return true; 1041189251Ssam } 1042189251Ssam 1043189251Ssam // Convenience method 1044189251Ssam protected static boolean isRelativeUri(String u) { 1045189251Ssam try { 1046189251Ssam return isRelativeUri(new URI(u)); 1047189251Ssam } catch (URISyntaxException e) { 1048189251Ssam return false; 1049189251Ssam } 1050189251Ssam } 1051189251Ssam 1052189251Ssam /** 1053189251Ssam * Converts a relative file name to a relative URI. This is 1054189251Ssam * different from File.toURI as this method does not canonicalize 1055189251Ssam * the file before creating the URI. Furthermore, no schema is 1056189251Ssam * used. 1057189251Ssam * @param file a relative file name 1058189251Ssam * @return a relative URI 1059189251Ssam * @throws IllegalArgumentException if the file name is not 1060189251Ssam * relative according to the definition given in {@link 1061214734Srpaulo * javax.tools.JavaFileManager#getFileForInput} 1062214734Srpaulo */ 1063214734Srpaulo public static String getRelativeName(File file) { 1064214734Srpaulo if (!file.isAbsolute()) { 1065214734Srpaulo String result = file.getPath().replace(File.separatorChar, '/'); 1066214734Srpaulo if (isRelativeUri(result)) 1067214734Srpaulo return result; 1068214734Srpaulo } 1069214734Srpaulo throw new IllegalArgumentException("Invalid relative path: " + file); 1070214734Srpaulo } 1071214734Srpaulo 1072214734Srpaulo /** 1073214734Srpaulo * Get a detail message from an IOException. 1074214734Srpaulo * Most, but not all, instances of IOException provide a non-null result 1075214734Srpaulo * for getLocalizedMessage(). But some instances return null: in these 1076214734Srpaulo * cases, fallover to getMessage(), and if even that is null, return the 1077214734Srpaulo * name of the exception itself. 1078214734Srpaulo * @param e an IOException 1079214734Srpaulo * @return a string to include in a compiler diagnostic 1080214734Srpaulo */ 1081214734Srpaulo public static String getMessage(IOException e) { 1082214734Srpaulo String s = e.getLocalizedMessage(); 1083214734Srpaulo if (s != null) 1084214734Srpaulo return s; 1085214734Srpaulo s = e.getMessage(); 1086214734Srpaulo if (s != null) 1087214734Srpaulo return s; 1088214734Srpaulo return e.toString(); 1089214734Srpaulo } 1090214734Srpaulo 1091214734Srpaulo private void checkOutputLocation(Location location) { 1092214734Srpaulo Objects.requireNonNull(location); 1093214734Srpaulo if (!location.isOutputLocation()) 1094214734Srpaulo throw new IllegalArgumentException("location is not an output location: " + location.getName()); 1095214734Srpaulo } 1096214734Srpaulo 1097214734Srpaulo private void checkModuleOrientedOrOutputLocation(Location location) { 1098214734Srpaulo Objects.requireNonNull(location); 1099214734Srpaulo if (!location.isModuleOrientedLocation() && !location.isOutputLocation()) 1100214734Srpaulo throw new IllegalArgumentException( 1101214734Srpaulo "location is not an output location or a module-oriented location: " 1102214734Srpaulo + location.getName()); 1103214734Srpaulo } 1104214734Srpaulo 1105214734Srpaulo private void checkNotModuleOrientedLocation(Location location) { 1106214734Srpaulo Objects.requireNonNull(location); 1107214734Srpaulo if (location.isModuleOrientedLocation()) 1108214734Srpaulo throw new IllegalArgumentException("location is module-oriented: " + location.getName()); 1109214734Srpaulo } 1110214734Srpaulo 1111214734Srpaulo /* Converters between files and paths. 1112214734Srpaulo * These are temporary until we can update the StandardJavaFileManager API. 1113214734Srpaulo */ 1114214734Srpaulo 1115214734Srpaulo private static Iterable<Path> asPaths(final Iterable<? extends File> files) { 1116214734Srpaulo if (files == null) 1117214734Srpaulo return null; 1118214734Srpaulo 1119214734Srpaulo return () -> new Iterator<Path>() { 1120214734Srpaulo Iterator<? extends File> iter = files.iterator(); 1121214734Srpaulo 1122214734Srpaulo @Override 1123214734Srpaulo public boolean hasNext() { 1124214734Srpaulo return iter.hasNext(); 1125214734Srpaulo } 1126214734Srpaulo 1127214734Srpaulo @Override 1128214734Srpaulo public Path next() { 1129214734Srpaulo return iter.next().toPath(); 1130214734Srpaulo } 1131214734Srpaulo }; 1132214734Srpaulo } 1133214734Srpaulo 1134214734Srpaulo private static Iterable<File> asFiles(final Iterable<? extends Path> paths) { 1135214734Srpaulo if (paths == null) 1136214734Srpaulo return null; 1137214734Srpaulo 1138214734Srpaulo return () -> new Iterator<File>() { 1139214734Srpaulo Iterator<? extends Path> iter = paths.iterator(); 1140214734Srpaulo 1141214734Srpaulo @Override 1142214734Srpaulo public boolean hasNext() { 1143214734Srpaulo return iter.hasNext(); 1144214734Srpaulo } 1145214734Srpaulo 1146214734Srpaulo @Override 1147214734Srpaulo public File next() { 1148214734Srpaulo try { 1149214734Srpaulo return iter.next().toFile(); 1150214734Srpaulo } catch (UnsupportedOperationException e) { 1151214734Srpaulo throw new IllegalStateException(e); 1152214734Srpaulo } 1153214734Srpaulo } 1154214734Srpaulo }; 1155214734Srpaulo } 1156252726Srpaulo} 1157214734Srpaulo