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

Fri, 11 Dec 2009 14:26:27 -0800

author
jjg
date
Fri, 11 Dec 2009 14:26:27 -0800
changeset 450
4011f49b4af8
parent 424
86b773b7cb40
child 510
72833a8a6086
permissions
-rw-r--r--

6906175: bridge JSR199 and JSR 203 APIs
Reviewed-by: darcy, alanb

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

mercurial