JavacFileManager.java revision 2694:fd59a2d43134
1146773Ssam/* 2146773Ssam * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. 3146773Ssam * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4146773Ssam * 5251158Sdelphij * This code is free software; you can redistribute it and/or modify it 6146773Ssam * under the terms of the GNU General Public License version 2 only, as 7146773Ssam * published by the Free Software Foundation. Oracle designates this 8146773Ssam * particular file as subject to the "Classpath" exception as provided 9146773Ssam * by Oracle in the LICENSE file that accompanied this code. 10146773Ssam * 11146773Ssam * This code is distributed in the hope that it will be useful, but WITHOUT 12146773Ssam * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13146773Ssam * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14146773Ssam * version 2 for more details (a copy is included in the LICENSE file that 15146773Ssam * accompanied this code). 16146773Ssam * 17146773Ssam * You should have received a copy of the GNU General Public License version 18146773Ssam * 2 along with this work; if not, write to the Free Software Foundation, 19146773Ssam * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20146773Ssam * 21146773Ssam * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22146773Ssam * or visit www.oracle.com if you need additional information or have any 23146773Ssam * questions. 24214478Srpaulo */ 25146773Ssam 26146773Ssampackage com.sun.tools.javac.file; 27146773Ssam 28146773Ssamimport java.io.ByteArrayOutputStream; 29146773Ssamimport java.io.File; 30146773Ssamimport java.io.FileNotFoundException; 31146773Ssamimport java.io.IOException; 32146773Ssamimport java.io.OutputStreamWriter; 33146773Ssamimport java.net.MalformedURLException; 34146773Ssamimport java.net.URI; 35146773Ssamimport java.net.URISyntaxException; 36146773Ssamimport java.net.URL; 37146773Ssamimport java.nio.CharBuffer; 38146773Ssamimport java.nio.charset.Charset; 39146773Ssamimport java.util.ArrayList; 40146773Ssamimport java.util.Arrays; 41146773Ssamimport java.util.Collection; 42146773Ssamimport java.util.Collections; 43146773Ssamimport java.util.Comparator; 44146773Ssamimport java.util.EnumSet; 45146773Ssamimport java.util.HashMap; 46146773Ssamimport java.util.Iterator; 47146773Ssamimport java.util.Map; 48146773Ssamimport java.util.Set; 49146773Ssamimport java.util.zip.ZipFile; 50146773Ssam 51146773Ssamimport javax.lang.model.SourceVersion; 52146773Ssamimport javax.tools.FileObject; 53146773Ssamimport javax.tools.JavaFileManager; 54146773Ssamimport javax.tools.JavaFileObject; 55146773Ssamimport javax.tools.StandardJavaFileManager; 56146773Ssam 57146773Ssamimport com.sun.tools.javac.file.RelativePath.RelativeFile; 58146773Ssamimport com.sun.tools.javac.file.RelativePath.RelativeDirectory; 59146773Ssamimport com.sun.tools.javac.util.BaseFileManager; 60146773Ssamimport com.sun.tools.javac.util.Context; 61146773Ssamimport com.sun.tools.javac.util.DefinedBy; 62146773Ssamimport com.sun.tools.javac.util.DefinedBy.Api; 63146773Ssamimport com.sun.tools.javac.util.List; 64146773Ssamimport com.sun.tools.javac.util.ListBuffer; 65146773Ssam 66146773Ssamimport static javax.tools.StandardLocation.*; 67146773Ssam 68146773Ssam/** 69146773Ssam * This class provides access to the source, class and other files 70146773Ssam * used by the compiler and related tools. 71146773Ssam * 72146773Ssam * <p><b>This is NOT part of any supported API. 73146773Ssam * If you write code that depends on this, you do so at your own risk. 74146773Ssam * This code and its internal interfaces are subject to change or 75146773Ssam * deletion without notice.</b> 76146773Ssam */ 77146773Ssampublic class JavacFileManager extends BaseFileManager implements StandardJavaFileManager { 78146773Ssam 79146773Ssam @SuppressWarnings("cast") 80190207Srpaulo public static char[] toArray(CharBuffer buffer) { 81146773Ssam if (buffer.hasArray()) 82146773Ssam return ((CharBuffer)buffer.compact().flip()).array(); 83146773Ssam else 84146773Ssam return buffer.toString().toCharArray(); 85146773Ssam } 86146773Ssam 87214478Srpaulo private FSInfo fsInfo; 88146773Ssam 89146773Ssam private boolean contextUseOptimizedZip; 90190207Srpaulo private ZipFileIndexCache zipFileIndexCache; 91146773Ssam 92146773Ssam private final Set<JavaFileObject.Kind> sourceOrClass = 93146773Ssam EnumSet.of(JavaFileObject.Kind.SOURCE, JavaFileObject.Kind.CLASS); 94146773Ssam 95146773Ssam protected boolean mmappedIO; 96146773Ssam protected boolean symbolFileEnabled; 97146773Ssam 98146773Ssam protected enum SortFiles implements Comparator<File> { 99146773Ssam FORWARD { 100146773Ssam public int compare(File f1, File f2) { 101146773Ssam return f1.getName().compareTo(f2.getName()); 102146773Ssam } 103146773Ssam }, 104146773Ssam REVERSE { 105146773Ssam public int compare(File f1, File f2) { 106190207Srpaulo return -f1.getName().compareTo(f2.getName()); 107190207Srpaulo } 108146773Ssam } 109146773Ssam } 110235530Sdelphij 111146773Ssam protected SortFiles sortFiles; 112146773Ssam 113251158Sdelphij /** 114146773Ssam * Register a Context.Factory to create a JavacFileManager. 115190207Srpaulo */ 116190207Srpaulo public static void preRegister(Context context) { 117190207Srpaulo context.put(JavaFileManager.class, new Context.Factory<JavaFileManager>() { 118146773Ssam public JavaFileManager make(Context c) { 119162017Ssam return new JavacFileManager(c, true, null); 120235530Sdelphij } 121162017Ssam }); 122146773Ssam } 123146773Ssam 124146773Ssam /** 125146773Ssam * Create a JavacFileManager using a given context, optionally registering 126146773Ssam * it as the JavaFileManager for that context. 127146773Ssam */ 128214478Srpaulo public JavacFileManager(Context context, boolean register, Charset charset) { 129146773Ssam super(charset); 130146773Ssam if (register) 131146773Ssam context.put(JavaFileManager.class, this); 132146773Ssam setContext(context); 133146773Ssam } 134146773Ssam 135146773Ssam /** 136146773Ssam * Set the context for JavacFileManager. 137146773Ssam */ 138146773Ssam @Override 139146773Ssam public void setContext(Context context) { 140146773Ssam super.setContext(context); 141146773Ssam 142146773Ssam fsInfo = FSInfo.instance(context); 143146773Ssam 144146773Ssam contextUseOptimizedZip = options.getBoolean("useOptimizedZip", true); 145146773Ssam if (contextUseOptimizedZip) 146146773Ssam zipFileIndexCache = ZipFileIndexCache.getSharedInstance(); 147146773Ssam 148146773Ssam mmappedIO = options.isSet("mmappedIO"); 149146773Ssam symbolFileEnabled = !options.isSet("ignore.symbol.file"); 150146773Ssam 151146773Ssam String sf = options.get("sortFiles"); 152146773Ssam if (sf != null) { 153146773Ssam sortFiles = (sf.equals("reverse") ? SortFiles.REVERSE : SortFiles.FORWARD); 154214478Srpaulo } 155214478Srpaulo } 156214478Srpaulo 157214478Srpaulo /** 158214478Srpaulo * Set whether or not to use ct.sym as an alternate to rt.jar. 159146773Ssam */ 160214478Srpaulo public void setSymbolFileEnabled(boolean b) { 161214478Srpaulo symbolFileEnabled = b; 162235530Sdelphij } 163214478Srpaulo 164214478Srpaulo public JavaFileObject getFileForInput(String name) { 165146773Ssam return getRegularFile(new File(name)); 166214478Srpaulo } 167214478Srpaulo 168214478Srpaulo public JavaFileObject getRegularFile(File file) { 169214478Srpaulo return new RegularFileObject(this, file); 170214478Srpaulo } 171146773Ssam 172146773Ssam public JavaFileObject getFileForOutput(String classname, 173146773Ssam JavaFileObject.Kind kind, 174146773Ssam JavaFileObject sibling) 175146773Ssam throws IOException 176146773Ssam { 177146773Ssam return getJavaFileForOutput(CLASS_OUTPUT, classname, kind, sibling); 178146773Ssam } 179146773Ssam 180251158Sdelphij @DefinedBy(Api.COMPILER) 181251158Sdelphij public Iterable<? extends JavaFileObject> getJavaFileObjectsFromStrings(Iterable<String> names) { 182251158Sdelphij ListBuffer<File> files = new ListBuffer<>(); 183251158Sdelphij for (String name : names) 184251158Sdelphij files.append(new File(nullCheck(name))); 185251158Sdelphij return getJavaFileObjectsFromFiles(files.toList()); 186146773Ssam } 187146773Ssam 188146773Ssam @DefinedBy(Api.COMPILER) 189146773Ssam public Iterable<? extends JavaFileObject> getJavaFileObjects(String... names) { 190146773Ssam return getJavaFileObjectsFromStrings(Arrays.asList(nullCheck(names))); 191146773Ssam } 192146773Ssam 193146773Ssam private static boolean isValidName(String name) { 194146773Ssam // Arguably, isValidName should reject keywords (such as in SourceVersion.isName() ), 195214478Srpaulo // but the set of keywords depends on the source level, and we don't want 196214478Srpaulo // impls of JavaFileManager to have to be dependent on the source level. 197214478Srpaulo // Therefore we simply check that the argument is a sequence of identifiers 198214478Srpaulo // separated by ".". 199214478Srpaulo for (String s : name.split("\\.", -1)) { 200214478Srpaulo if (!SourceVersion.isIdentifier(s)) 201214478Srpaulo return false; 202146773Ssam } 203214478Srpaulo return true; 204146773Ssam } 205214478Srpaulo 206214478Srpaulo private static void validateClassName(String className) { 207214478Srpaulo if (!isValidName(className)) 208214478Srpaulo throw new IllegalArgumentException("Invalid class name: " + className); 209146773Ssam } 210146773Ssam 211146773Ssam private static void validatePackageName(String packageName) { 212146773Ssam if (packageName.length() > 0 && !isValidName(packageName)) 213146773Ssam throw new IllegalArgumentException("Invalid packageName name: " + packageName); 214146773Ssam } 215146773Ssam 216146773Ssam public static void testName(String name, 217146773Ssam boolean isValidPackageName, 218146773Ssam boolean isValidClassName) 219146773Ssam { 220146773Ssam try { 221146773Ssam validatePackageName(name); 222146773Ssam if (!isValidPackageName) 223146773Ssam throw new AssertionError("Invalid package name accepted: " + name); 224146773Ssam printAscii("Valid package name: \"%s\"", name); 225146773Ssam } catch (IllegalArgumentException e) { 226146773Ssam if (isValidPackageName) 227146773Ssam throw new AssertionError("Valid package name rejected: " + name); 228146773Ssam printAscii("Invalid package name: \"%s\"", name); 229146773Ssam } 230146773Ssam try { 231146773Ssam validateClassName(name); 232146773Ssam if (!isValidClassName) 233146773Ssam throw new AssertionError("Invalid class name accepted: " + name); 234146773Ssam printAscii("Valid class name: \"%s\"", name); 235146773Ssam } catch (IllegalArgumentException e) { 236146773Ssam if (isValidClassName) 237146773Ssam throw new AssertionError("Valid class name rejected: " + name); 238146773Ssam printAscii("Invalid class name: \"%s\"", name); 239146773Ssam } 240146773Ssam } 241146773Ssam 242146773Ssam private static void printAscii(String format, Object... args) { 243251158Sdelphij String message; 244146773Ssam try { 245146773Ssam final String ascii = "US-ASCII"; 246146773Ssam message = new String(String.format(null, format, args).getBytes(ascii), ascii); 247146773Ssam } catch (java.io.UnsupportedEncodingException ex) { 248146773Ssam throw new AssertionError(ex); 249146773Ssam } 250146773Ssam System.out.println(message); 251146773Ssam } 252146773Ssam 253146773Ssam 254146773Ssam /** 255146773Ssam * Insert all files in subdirectory subdirectory of directory directory 256146773Ssam * which match fileKinds into resultList 257146773Ssam */ 258146773Ssam private void listDirectory(File directory, 259146773Ssam RelativeDirectory subdirectory, 260146773Ssam Set<JavaFileObject.Kind> fileKinds, 261172683Smlaier boolean recurse, 262146773Ssam ListBuffer<JavaFileObject> resultList) { 263235530Sdelphij File d = subdirectory.getFile(directory); 264235530Sdelphij if (!caseMapCheck(d, subdirectory)) 265235530Sdelphij return; 266146773Ssam 267146773Ssam File[] files = d.listFiles(); 268146773Ssam if (files == null) 269146773Ssam return; 270146773Ssam 271146773Ssam if (sortFiles != null) 272146773Ssam Arrays.sort(files, sortFiles); 273146773Ssam 274146773Ssam for (File f: files) { 275146773Ssam String fname = f.getName(); 276146773Ssam if (f.isDirectory()) { 277235530Sdelphij if (recurse && SourceVersion.isIdentifier(fname)) { 278235530Sdelphij listDirectory(directory, 279235530Sdelphij new RelativeDirectory(subdirectory, fname), 280146773Ssam fileKinds, 281235530Sdelphij recurse, 282235530Sdelphij resultList); 283235530Sdelphij } 284190207Srpaulo } else { 285146773Ssam if (isValidFile(fname, fileKinds)) { 286146773Ssam JavaFileObject fe = 287146773Ssam new RegularFileObject(this, fname, new File(d, fname)); 288146773Ssam resultList.append(fe); 289241235Sdelphij } 290251158Sdelphij } 291214478Srpaulo } 292214478Srpaulo } 293146773Ssam 294146773Ssam /** 295146773Ssam * Insert all files in subdirectory subdirectory of archive archive 296146773Ssam * which match fileKinds into resultList 297146773Ssam */ 298146773Ssam private void listArchive(Archive archive, 299146773Ssam RelativeDirectory subdirectory, 300146773Ssam Set<JavaFileObject.Kind> fileKinds, 301190207Srpaulo boolean recurse, 302146773Ssam ListBuffer<JavaFileObject> resultList) { 303235530Sdelphij // Get the files directly in the subdir 304235530Sdelphij List<String> files = archive.getFiles(subdirectory); 305235530Sdelphij if (files != null) { 306235530Sdelphij for (; !files.isEmpty(); files = files.tail) { 307235530Sdelphij String file = files.head; 308251158Sdelphij if (isValidFile(file, fileKinds)) { 309235530Sdelphij resultList.append(archive.getFileObject(subdirectory, file)); 310251158Sdelphij } 311235530Sdelphij } 312251158Sdelphij } 313235530Sdelphij if (recurse) { 314235530Sdelphij for (RelativeDirectory s: archive.getSubdirectories()) { 315235530Sdelphij if (subdirectory.contains(s)) { 316235530Sdelphij // Because the archive map is a flat list of directories, 317235530Sdelphij // the enclosing loop will pick up all child subdirectories. 318235530Sdelphij // Therefore, there is no need to recurse deeper. 319146773Ssam listArchive(archive, s, fileKinds, false, resultList); 320146773Ssam } 321162017Ssam } 322162017Ssam } 323162017Ssam } 324162017Ssam 325146773Ssam /** 326162017Ssam * container is a directory, a zip file, or a non-existant path. 327162017Ssam * Insert all files in subdirectory subdirectory of container which 328162017Ssam * match fileKinds into resultList 329146773Ssam */ 330146773Ssam private void listContainer(File container, 331146773Ssam RelativeDirectory subdirectory, 332146773Ssam Set<JavaFileObject.Kind> fileKinds, 333146773Ssam boolean recurse, 334146773Ssam ListBuffer<JavaFileObject> resultList) { 335146773Ssam Archive archive = archives.get(container); 336146773Ssam if (archive == null) { 337146773Ssam // archives are not created for directories. 338146773Ssam if (fsInfo.isDirectory(container)) { 339146773Ssam listDirectory(container, 340146773Ssam subdirectory, 341146773Ssam fileKinds, 342146773Ssam recurse, 343146773Ssam resultList); 344146773Ssam return; 345146773Ssam } 346146773Ssam 347146773Ssam // Not a directory; either a file or non-existant, create the archive 348146773Ssam try { 349146773Ssam archive = openArchive(container); 350146773Ssam } catch (IOException ex) { 351146773Ssam log.error("error.reading.file", 352146773Ssam container, getMessage(ex)); 353146773Ssam return; 354146773Ssam } 355146773Ssam } 356146773Ssam listArchive(archive, 357146773Ssam subdirectory, 358146773Ssam fileKinds, 359146773Ssam recurse, 360146773Ssam resultList); 361146773Ssam } 362146773Ssam 363146773Ssam private boolean isValidFile(String s, Set<JavaFileObject.Kind> fileKinds) { 364146773Ssam JavaFileObject.Kind kind = getKind(s); 365146773Ssam return fileKinds.contains(kind); 366146773Ssam } 367146773Ssam 368235530Sdelphij private static final boolean fileSystemIsCaseSensitive = 369146773Ssam File.separatorChar == '/'; 370146773Ssam 371146773Ssam /** Hack to make Windows case sensitive. Test whether given path 372146773Ssam * ends in a string of characters with the same case as given name. 373146773Ssam * Ignore file separators in both path and name. 374146773Ssam */ 375146773Ssam private boolean caseMapCheck(File f, RelativePath name) { 376146773Ssam if (fileSystemIsCaseSensitive) return true; 377146773Ssam // Note that getCanonicalPath() returns the case-sensitive 378146773Ssam // spelled file name. 379146773Ssam String path; 380146773Ssam try { 381146773Ssam path = f.getCanonicalPath(); 382146773Ssam } catch (IOException ex) { 383146773Ssam return false; 384146773Ssam } 385146773Ssam char[] pcs = path.toCharArray(); 386146773Ssam char[] ncs = name.path.toCharArray(); 387146773Ssam int i = pcs.length - 1; 388146773Ssam int j = ncs.length - 1; 389146773Ssam while (i >= 0 && j >= 0) { 390146773Ssam while (i >= 0 && pcs[i] == File.separatorChar) i--; 391146773Ssam while (j >= 0 && ncs[j] == '/') j--; 392146773Ssam if (i >= 0 && j >= 0) { 393146773Ssam if (pcs[i] != ncs[j]) return false; 394146773Ssam i--; 395214478Srpaulo j--; 396146773Ssam } 397146773Ssam } 398146773Ssam return j < 0; 399146773Ssam } 400146773Ssam 401146773Ssam /** 402146773Ssam * An archive provides a flat directory structure of a ZipFile by 403146773Ssam * mapping directory names to lists of files (basenames). 404146773Ssam */ 405146773Ssam public interface Archive { 406146773Ssam void close() throws IOException; 407146773Ssam 408146773Ssam boolean contains(RelativePath name); 409146773Ssam 410146773Ssam JavaFileObject getFileObject(RelativeDirectory subdirectory, String file); 411146773Ssam 412146773Ssam List<String> getFiles(RelativeDirectory subdirectory); 413146773Ssam 414235530Sdelphij Set<RelativeDirectory> getSubdirectories(); 415146773Ssam } 416146773Ssam 417251158Sdelphij public class MissingArchive implements Archive { 418146773Ssam final File zipFileName; 419146773Ssam public MissingArchive(File name) { 420146773Ssam zipFileName = name; 421146773Ssam } 422146773Ssam public boolean contains(RelativePath name) { 423146773Ssam return false; 424146773Ssam } 425146773Ssam 426146773Ssam public void close() { 427146773Ssam } 428146773Ssam 429146773Ssam public JavaFileObject getFileObject(RelativeDirectory subdirectory, String file) { 430146773Ssam return null; 431146773Ssam } 432146773Ssam 433146773Ssam public List<String> getFiles(RelativeDirectory subdirectory) { 434146773Ssam return List.nil(); 435146773Ssam } 436146773Ssam 437146773Ssam public Set<RelativeDirectory> getSubdirectories() { 438146773Ssam return Collections.emptySet(); 439146773Ssam } 440146773Ssam 441146773Ssam @Override 442146773Ssam public String toString() { 443146773Ssam return "MissingArchive[" + zipFileName + "]"; 444146773Ssam } 445146773Ssam } 446146773Ssam 447146773Ssam /** A directory of zip files already opened. 448146773Ssam */ 449146773Ssam Map<File, Archive> archives = new HashMap<>(); 450146773Ssam 451146773Ssam private static final String[] symbolFileLocation = { "lib", "ct.sym" }; 452146773Ssam private static final RelativeDirectory symbolFilePrefix 453146773Ssam = new RelativeDirectory("META-INF/sym/rt.jar/"); 454146773Ssam 455146773Ssam /* 456146773Ssam * This method looks for a ZipFormatException and takes appropriate 457146773Ssam * evasive action. If there is a failure in the fast mode then we 458146773Ssam * fail over to the platform zip, and allow it to deal with a potentially 459146773Ssam * non compliant zip file. 460146773Ssam */ 461235530Sdelphij protected Archive openArchive(File zipFilename) throws IOException { 462235530Sdelphij try { 463146773Ssam return openArchive(zipFilename, contextUseOptimizedZip); 464146773Ssam } catch (IOException ioe) { 465146773Ssam if (ioe instanceof ZipFileIndex.ZipFormatException) { 466146773Ssam return openArchive(zipFilename, false); 467146773Ssam } else { 468146773Ssam throw ioe; 469214478Srpaulo } 470146773Ssam } 471214478Srpaulo } 472235530Sdelphij 473214478Srpaulo /** Open a new zip file directory, and cache it. 474235530Sdelphij */ 475235530Sdelphij private Archive openArchive(File zipFileName, boolean useOptimizedZip) throws IOException { 476146773Ssam File origZipFileName = zipFileName; 477146773Ssam if (symbolFileEnabled && locations.isDefaultBootClassPathRtJar(zipFileName)) { 478235530Sdelphij File file = zipFileName.getParentFile().getParentFile(); // ${java.home} 479146773Ssam if (new File(file.getName()).equals(new File("jre"))) 480235530Sdelphij file = file.getParentFile(); 481146773Ssam // file == ${jdk.home} 482146773Ssam for (String name : symbolFileLocation) 483146773Ssam file = new File(file, name); 484146773Ssam // file == ${jdk.home}/lib/ct.sym 485146773Ssam if (file.exists()) 486146773Ssam zipFileName = file; 487146773Ssam } 488146773Ssam 489146773Ssam Archive archive; 490146773Ssam try { 491146773Ssam 492146773Ssam ZipFile zdir = null; 493146773Ssam 494146773Ssam boolean usePreindexedCache = false; 495235530Sdelphij String preindexCacheLocation = null; 496146773Ssam 497146773Ssam if (!useOptimizedZip) { 498146773Ssam zdir = new ZipFile(zipFileName); 499235530Sdelphij } else { 500235530Sdelphij usePreindexedCache = options.isSet("usezipindex"); 501235530Sdelphij preindexCacheLocation = options.get("java.io.tmpdir"); 502235530Sdelphij String optCacheLoc = options.get("cachezipindexdir"); 503235530Sdelphij 504235530Sdelphij if (optCacheLoc != null && optCacheLoc.length() != 0) { 505235530Sdelphij if (optCacheLoc.startsWith("\"")) { 506146773Ssam if (optCacheLoc.endsWith("\"")) { 507146773Ssam optCacheLoc = optCacheLoc.substring(1, optCacheLoc.length() - 1); 508214478Srpaulo } 509214478Srpaulo else { 510214478Srpaulo optCacheLoc = optCacheLoc.substring(1); 511214478Srpaulo } 512214478Srpaulo } 513214478Srpaulo 514214478Srpaulo File cacheDir = new File(optCacheLoc); 515146773Ssam if (cacheDir.exists() && cacheDir.canWrite()) { 516 preindexCacheLocation = optCacheLoc; 517 if (!preindexCacheLocation.endsWith("/") && 518 !preindexCacheLocation.endsWith(File.separator)) { 519 preindexCacheLocation += File.separator; 520 } 521 } 522 } 523 } 524 525 if (origZipFileName == zipFileName) { 526 if (!useOptimizedZip) { 527 archive = new ZipArchive(this, zdir); 528 } else { 529 archive = new ZipFileIndexArchive(this, 530 zipFileIndexCache.getZipFileIndex(zipFileName, 531 null, 532 usePreindexedCache, 533 preindexCacheLocation, 534 options.isSet("writezipindexfiles"))); 535 } 536 } else { 537 if (!useOptimizedZip) { 538 archive = new SymbolArchive(this, origZipFileName, zdir, symbolFilePrefix); 539 } else { 540 archive = new ZipFileIndexArchive(this, 541 zipFileIndexCache.getZipFileIndex(zipFileName, 542 symbolFilePrefix, 543 usePreindexedCache, 544 preindexCacheLocation, 545 options.isSet("writezipindexfiles"))); 546 } 547 } 548 } catch (FileNotFoundException ex) { 549 archive = new MissingArchive(zipFileName); 550 } catch (ZipFileIndex.ZipFormatException zfe) { 551 throw zfe; 552 } catch (IOException ex) { 553 if (zipFileName.exists()) 554 log.error("error.reading.file", zipFileName, getMessage(ex)); 555 archive = new MissingArchive(zipFileName); 556 } 557 558 archives.put(origZipFileName, archive); 559 return archive; 560 } 561 562 /** Flush any output resources. 563 */ 564 @DefinedBy(Api.COMPILER) 565 public void flush() { 566 contentCache.clear(); 567 } 568 569 /** 570 * Close the JavaFileManager, releasing resources. 571 */ 572 @DefinedBy(Api.COMPILER) 573 public void close() { 574 for (Iterator<Archive> i = archives.values().iterator(); i.hasNext(); ) { 575 Archive a = i.next(); 576 i.remove(); 577 try { 578 a.close(); 579 } catch (IOException e) { 580 } 581 } 582 } 583 584 @DefinedBy(Api.COMPILER) 585 public ClassLoader getClassLoader(Location location) { 586 nullCheck(location); 587 Iterable<? extends File> path = getLocation(location); 588 if (path == null) 589 return null; 590 ListBuffer<URL> lb = new ListBuffer<>(); 591 for (File f: path) { 592 try { 593 lb.append(f.toURI().toURL()); 594 } catch (MalformedURLException e) { 595 throw new AssertionError(e); 596 } 597 } 598 599 return getClassLoader(lb.toArray(new URL[lb.size()])); 600 } 601 602 @DefinedBy(Api.COMPILER) 603 public Iterable<JavaFileObject> list(Location location, 604 String packageName, 605 Set<JavaFileObject.Kind> kinds, 606 boolean recurse) 607 throws IOException 608 { 609 // validatePackageName(packageName); 610 nullCheck(packageName); 611 nullCheck(kinds); 612 613 Iterable<? extends File> path = getLocation(location); 614 if (path == null) 615 return List.nil(); 616 RelativeDirectory subdirectory = RelativeDirectory.forPackage(packageName); 617 ListBuffer<JavaFileObject> results = new ListBuffer<>(); 618 619 for (File directory : path) 620 listContainer(directory, subdirectory, kinds, recurse, results); 621 return results.toList(); 622 } 623 624 @DefinedBy(Api.COMPILER) 625 public String inferBinaryName(Location location, JavaFileObject file) { 626 file.getClass(); // null check 627 location.getClass(); // null check 628 // Need to match the path semantics of list(location, ...) 629 Iterable<? extends File> path = getLocation(location); 630 if (path == null) { 631 return null; 632 } 633 634 if (file instanceof BaseFileObject) { 635 return ((BaseFileObject) file).inferBinaryName(path); 636 } else 637 throw new IllegalArgumentException(file.getClass().getName()); 638 } 639 640 @DefinedBy(Api.COMPILER) 641 public boolean isSameFile(FileObject a, FileObject b) { 642 nullCheck(a); 643 nullCheck(b); 644 if (!(a instanceof BaseFileObject)) 645 throw new IllegalArgumentException("Not supported: " + a); 646 if (!(b instanceof BaseFileObject)) 647 throw new IllegalArgumentException("Not supported: " + b); 648 return a.equals(b); 649 } 650 651 @DefinedBy(Api.COMPILER) 652 public boolean hasLocation(Location location) { 653 return getLocation(location) != null; 654 } 655 656 @DefinedBy(Api.COMPILER) 657 public JavaFileObject getJavaFileForInput(Location location, 658 String className, 659 JavaFileObject.Kind kind) 660 throws IOException 661 { 662 nullCheck(location); 663 // validateClassName(className); 664 nullCheck(className); 665 nullCheck(kind); 666 if (!sourceOrClass.contains(kind)) 667 throw new IllegalArgumentException("Invalid kind: " + kind); 668 return getFileForInput(location, RelativeFile.forClass(className, kind)); 669 } 670 671 @DefinedBy(Api.COMPILER) 672 public FileObject getFileForInput(Location location, 673 String packageName, 674 String relativeName) 675 throws IOException 676 { 677 nullCheck(location); 678 // validatePackageName(packageName); 679 nullCheck(packageName); 680 if (!isRelativeUri(relativeName)) 681 throw new IllegalArgumentException("Invalid relative name: " + relativeName); 682 RelativeFile name = packageName.length() == 0 683 ? new RelativeFile(relativeName) 684 : new RelativeFile(RelativeDirectory.forPackage(packageName), relativeName); 685 return getFileForInput(location, name); 686 } 687 688 private JavaFileObject getFileForInput(Location location, RelativeFile name) throws IOException { 689 Iterable<? extends File> path = getLocation(location); 690 if (path == null) 691 return null; 692 693 for (File dir: path) { 694 Archive a = archives.get(dir); 695 if (a == null) { 696 if (fsInfo.isDirectory(dir)) { 697 File f = name.getFile(dir); 698 if (f.exists()) 699 return new RegularFileObject(this, f); 700 continue; 701 } 702 // Not a directory, create the archive 703 a = openArchive(dir); 704 } 705 // Process the archive 706 if (a.contains(name)) { 707 return a.getFileObject(name.dirname(), name.basename()); 708 } 709 } 710 return null; 711 } 712 713 @DefinedBy(Api.COMPILER) 714 public JavaFileObject getJavaFileForOutput(Location location, 715 String className, 716 JavaFileObject.Kind kind, 717 FileObject sibling) 718 throws IOException 719 { 720 nullCheck(location); 721 // validateClassName(className); 722 nullCheck(className); 723 nullCheck(kind); 724 if (!sourceOrClass.contains(kind)) 725 throw new IllegalArgumentException("Invalid kind: " + kind); 726 return getFileForOutput(location, RelativeFile.forClass(className, kind), sibling); 727 } 728 729 @DefinedBy(Api.COMPILER) 730 public FileObject getFileForOutput(Location location, 731 String packageName, 732 String relativeName, 733 FileObject sibling) 734 throws IOException 735 { 736 nullCheck(location); 737 // validatePackageName(packageName); 738 nullCheck(packageName); 739 if (!isRelativeUri(relativeName)) 740 throw new IllegalArgumentException("Invalid relative name: " + relativeName); 741 RelativeFile name = packageName.length() == 0 742 ? new RelativeFile(relativeName) 743 : new RelativeFile(RelativeDirectory.forPackage(packageName), relativeName); 744 return getFileForOutput(location, name, sibling); 745 } 746 747 private JavaFileObject getFileForOutput(Location location, 748 RelativeFile fileName, 749 FileObject sibling) 750 throws IOException 751 { 752 File dir; 753 if (location == CLASS_OUTPUT) { 754 if (getClassOutDir() != null) { 755 dir = getClassOutDir(); 756 } else { 757 File siblingDir = null; 758 if (sibling != null && sibling instanceof RegularFileObject) { 759 siblingDir = ((RegularFileObject)sibling).file.getParentFile(); 760 } 761 return new RegularFileObject(this, new File(siblingDir, fileName.basename())); 762 } 763 } else if (location == SOURCE_OUTPUT) { 764 dir = (getSourceOutDir() != null ? getSourceOutDir() : getClassOutDir()); 765 } else { 766 Iterable<? extends File> path = locations.getLocation(location); 767 dir = null; 768 for (File f: path) { 769 dir = f; 770 break; 771 } 772 } 773 774 File file = fileName.getFile(dir); // null-safe 775 return new RegularFileObject(this, file); 776 777 } 778 779 @DefinedBy(Api.COMPILER) 780 public Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles( 781 Iterable<? extends File> files) 782 { 783 ArrayList<RegularFileObject> result; 784 if (files instanceof Collection<?>) 785 result = new ArrayList<>(((Collection<?>)files).size()); 786 else 787 result = new ArrayList<>(); 788 for (File f: files) 789 result.add(new RegularFileObject(this, nullCheck(f))); 790 return result; 791 } 792 793 @DefinedBy(Api.COMPILER) 794 public Iterable<? extends JavaFileObject> getJavaFileObjects(File... files) { 795 return getJavaFileObjectsFromFiles(Arrays.asList(nullCheck(files))); 796 } 797 798 @DefinedBy(Api.COMPILER) 799 public void setLocation(Location location, 800 Iterable<? extends File> path) 801 throws IOException 802 { 803 nullCheck(location); 804 locations.setLocation(location, path); 805 } 806 807 @DefinedBy(Api.COMPILER) 808 public Iterable<? extends File> getLocation(Location location) { 809 nullCheck(location); 810 return locations.getLocation(location); 811 } 812 813 private File getClassOutDir() { 814 return locations.getOutputLocation(CLASS_OUTPUT); 815 } 816 817 private File getSourceOutDir() { 818 return locations.getOutputLocation(SOURCE_OUTPUT); 819 } 820 821 /** 822 * Enforces the specification of a "relative" name as used in 823 * {@linkplain #getFileForInput(Location,String,String) 824 * getFileForInput}. This method must follow the rules defined in 825 * that method, do not make any changes without consulting the 826 * specification. 827 */ 828 protected static boolean isRelativeUri(URI uri) { 829 if (uri.isAbsolute()) 830 return false; 831 String path = uri.normalize().getPath(); 832 if (path.length() == 0 /* isEmpty() is mustang API */) 833 return false; 834 if (!path.equals(uri.getPath())) // implicitly checks for embedded . and .. 835 return false; 836 if (path.startsWith("/") || path.startsWith("./") || path.startsWith("../")) 837 return false; 838 return true; 839 } 840 841 // Convenience method 842 protected static boolean isRelativeUri(String u) { 843 try { 844 return isRelativeUri(new URI(u)); 845 } catch (URISyntaxException e) { 846 return false; 847 } 848 } 849 850 /** 851 * Converts a relative file name to a relative URI. This is 852 * different from File.toURI as this method does not canonicalize 853 * the file before creating the URI. Furthermore, no schema is 854 * used. 855 * @param file a relative file name 856 * @return a relative URI 857 * @throws IllegalArgumentException if the file name is not 858 * relative according to the definition given in {@link 859 * javax.tools.JavaFileManager#getFileForInput} 860 */ 861 public static String getRelativeName(File file) { 862 if (!file.isAbsolute()) { 863 String result = file.getPath().replace(File.separatorChar, '/'); 864 if (isRelativeUri(result)) 865 return result; 866 } 867 throw new IllegalArgumentException("Invalid relative path: " + file); 868 } 869 870 /** 871 * Get a detail message from an IOException. 872 * Most, but not all, instances of IOException provide a non-null result 873 * for getLocalizedMessage(). But some instances return null: in these 874 * cases, fallover to getMessage(), and if even that is null, return the 875 * name of the exception itself. 876 * @param e an IOException 877 * @return a string to include in a compiler diagnostic 878 */ 879 public static String getMessage(IOException e) { 880 String s = e.getLocalizedMessage(); 881 if (s != null) 882 return s; 883 s = e.getMessage(); 884 if (s != null) 885 return s; 886 return e.toString(); 887 } 888} 889