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

Mon, 24 Jan 2011 16:38:56 -0800

author
jjg
date
Mon, 24 Jan 2011 16:38:56 -0800
changeset 839
a8437c34fdc7
parent 818
d33d8c381aa1
child 882
3d45cc94ee0f
permissions
-rw-r--r--

6988106: javac report 'java.lang.IllegalMonitorStateException'
Reviewed-by: ksrini

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

mercurial