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

Fri, 09 May 2014 20:33:21 -0700

author
mfang
date
Fri, 09 May 2014 20:33:21 -0700
changeset 2388
0add97444be9
parent 1377
e6cb81683ffe
child 2525
2eb010b6cb22
permissions
-rw-r--r--

8041424: 8u20 l10n resource file translation update 1
Reviewed-by: naoto, yhuang

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

mercurial