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

Wed, 26 Jan 2011 11:20:19 -0800

author
jjg
date
Wed, 26 Jan 2011 11:20:19 -0800
changeset 841
df371fd16386
parent 818
d33d8c381aa1
child 874
e0c16199b2e0
permissions
-rw-r--r--

6554097: "final" confuses @SuppressWarnings
Reviewed-by: mcimadamore

duke@1 1 /*
jjg@818 2 * Copyright (c) 2003, 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;
jjg@50 27
duke@1 28 import java.io.File;
duke@1 29 import java.io.IOException;
darcy@497 30 import java.net.MalformedURLException;
darcy@497 31 import java.net.URL;
duke@1 32 import java.util.HashMap;
duke@1 33 import java.util.HashSet;
duke@1 34 import java.util.Map;
duke@1 35 import java.util.Set;
duke@1 36 import java.util.Collection;
duke@1 37 import java.util.Collections;
duke@1 38 import java.util.LinkedHashSet;
darcy@497 39 import java.util.StringTokenizer;
duke@1 40 import java.util.zip.ZipFile;
duke@1 41 import javax.tools.JavaFileManager.Location;
duke@1 42
jjg@50 43 import com.sun.tools.javac.code.Lint;
jjg@50 44 import com.sun.tools.javac.util.Context;
jjg@151 45 import com.sun.tools.javac.util.ListBuffer;
jjg@50 46 import com.sun.tools.javac.util.Log;
jjg@50 47 import com.sun.tools.javac.util.Options;
jjg@50 48
jjg@50 49 import static javax.tools.StandardLocation.*;
duke@1 50 import static com.sun.tools.javac.main.OptionName.*;
duke@1 51
duke@1 52 /** This class converts command line arguments, environment variables
duke@1 53 * and system properties (in File.pathSeparator-separated String form)
duke@1 54 * into a boot class path, user class path, and source path (in
duke@1 55 * Collection<String> form).
duke@1 56 *
jjg@581 57 * <p><b>This is NOT part of any supported API.
jjg@581 58 * If you write code that depends on this, you do so at your own risk.
duke@1 59 * This code and its internal interfaces are subject to change or
duke@1 60 * deletion without notice.</b>
duke@1 61 */
duke@1 62 public class Paths {
duke@1 63
duke@1 64 /** The context key for the todo list */
duke@1 65 protected static final Context.Key<Paths> pathsKey =
duke@1 66 new Context.Key<Paths>();
duke@1 67
jjg@14 68 /** Get the Paths instance for this context.
jjg@14 69 * @param context the context
jjg@14 70 * @return the Paths instance for this context
jjg@14 71 */
jjg@450 72 public static Paths instance(Context context) {
duke@1 73 Paths instance = context.get(pathsKey);
duke@1 74 if (instance == null)
duke@1 75 instance = new Paths(context);
duke@1 76 return instance;
duke@1 77 }
duke@1 78
duke@1 79 /** The log to use for warning output */
duke@1 80 private Log log;
duke@1 81
duke@1 82 /** Collection of command-line options */
duke@1 83 private Options options;
duke@1 84
duke@1 85 /** Handler for -Xlint options */
duke@1 86 private Lint lint;
duke@1 87
jjg@106 88 /** Access to (possibly cached) file info */
jjg@106 89 private FSInfo fsInfo;
duke@1 90
duke@1 91 protected Paths(Context context) {
duke@1 92 context.put(pathsKey, this);
duke@1 93 pathsForLocation = new HashMap<Location,Path>(16);
duke@1 94 setContext(context);
duke@1 95 }
duke@1 96
duke@1 97 void setContext(Context context) {
duke@1 98 log = Log.instance(context);
duke@1 99 options = Options.instance(context);
duke@1 100 lint = Lint.instance(context);
jjg@106 101 fsInfo = FSInfo.instance(context);
duke@1 102 }
duke@1 103
duke@1 104 /** Whether to warn about non-existent path elements */
duke@1 105 private boolean warn;
duke@1 106
duke@1 107 private Map<Location, Path> pathsForLocation;
duke@1 108
duke@1 109 private boolean inited = false; // TODO? caching bad?
duke@1 110
duke@1 111 /**
duke@1 112 * rt.jar as found on the default bootclass path. If the user specified a
duke@1 113 * bootclasspath, null is used.
duke@1 114 */
jjg@818 115 private File defaultBootClassPathRtJar = null;
duke@1 116
jjg@757 117 /**
jjg@757 118 * Is bootclasspath the default?
jjg@757 119 */
jjg@757 120 private boolean isDefaultBootClassPath;
jjg@757 121
duke@1 122 Path getPathForLocation(Location location) {
duke@1 123 Path path = pathsForLocation.get(location);
duke@1 124 if (path == null)
duke@1 125 setPathForLocation(location, null);
duke@1 126 return pathsForLocation.get(location);
duke@1 127 }
duke@1 128
duke@1 129 void setPathForLocation(Location location, Iterable<? extends File> path) {
duke@1 130 // TODO? if (inited) throw new IllegalStateException
duke@1 131 // TODO: otherwise reset sourceSearchPath, classSearchPath as needed
duke@1 132 Path p;
duke@1 133 if (path == null) {
duke@1 134 if (location == CLASS_PATH)
duke@1 135 p = computeUserClassPath();
duke@1 136 else if (location == PLATFORM_CLASS_PATH)
jjg@757 137 p = computeBootClassPath(); // sets isDefaultBootClassPath
duke@1 138 else if (location == ANNOTATION_PROCESSOR_PATH)
duke@1 139 p = computeAnnotationProcessorPath();
duke@1 140 else if (location == SOURCE_PATH)
duke@1 141 p = computeSourcePath();
duke@1 142 else
duke@1 143 // no defaults for other paths
duke@1 144 p = null;
duke@1 145 } else {
jjg@818 146 if (location == PLATFORM_CLASS_PATH) {
jjg@818 147 defaultBootClassPathRtJar = null;
jjg@757 148 isDefaultBootClassPath = false;
jjg@818 149 }
duke@1 150 p = new Path();
duke@1 151 for (File f: path)
duke@1 152 p.addFile(f, warn); // TODO: is use of warn appropriate?
duke@1 153 }
duke@1 154 pathsForLocation.put(location, p);
duke@1 155 }
duke@1 156
jjg@758 157 public boolean isDefaultBootClassPath() {
jjg@757 158 lazy();
jjg@757 159 return isDefaultBootClassPath;
jjg@757 160 }
jjg@757 161
duke@1 162 protected void lazy() {
duke@1 163 if (!inited) {
duke@1 164 warn = lint.isEnabled(Lint.LintCategory.PATH);
duke@1 165
duke@1 166 pathsForLocation.put(PLATFORM_CLASS_PATH, computeBootClassPath());
duke@1 167 pathsForLocation.put(CLASS_PATH, computeUserClassPath());
duke@1 168 pathsForLocation.put(SOURCE_PATH, computeSourcePath());
duke@1 169
duke@1 170 inited = true;
duke@1 171 }
duke@1 172 }
duke@1 173
duke@1 174 public Collection<File> bootClassPath() {
duke@1 175 lazy();
duke@1 176 return Collections.unmodifiableCollection(getPathForLocation(PLATFORM_CLASS_PATH));
duke@1 177 }
duke@1 178 public Collection<File> userClassPath() {
duke@1 179 lazy();
duke@1 180 return Collections.unmodifiableCollection(getPathForLocation(CLASS_PATH));
duke@1 181 }
duke@1 182 public Collection<File> sourcePath() {
duke@1 183 lazy();
duke@1 184 Path p = getPathForLocation(SOURCE_PATH);
duke@1 185 return p == null || p.size() == 0
duke@1 186 ? null
duke@1 187 : Collections.unmodifiableCollection(p);
duke@1 188 }
duke@1 189
jjg@818 190 boolean isDefaultBootClassPathRtJar(File file) {
jjg@818 191 return file.equals(defaultBootClassPathRtJar);
duke@1 192 }
duke@1 193
jjg@151 194 /**
jjg@151 195 * Split a path into its elements. Empty path elements will be ignored.
jjg@151 196 * @param path The path to be split
jjg@151 197 * @return The elements of the path
jjg@151 198 */
jjg@151 199 private static Iterable<File> getPathEntries(String path) {
jjg@151 200 return getPathEntries(path, null);
jjg@151 201 }
duke@1 202
jjg@151 203 /**
jjg@151 204 * Split a path into its elements. If emptyPathDefault is not null, all
jjg@151 205 * empty elements in the path, including empty elements at either end of
jjg@151 206 * the path, will be replaced with the value of emptyPathDefault.
jjg@151 207 * @param path The path to be split
jjg@151 208 * @param emptyPathDefault The value to substitute for empty path elements,
jjg@151 209 * or null, to ignore empty path elements
jjg@151 210 * @return The elements of the path
jjg@151 211 */
jjg@151 212 private static Iterable<File> getPathEntries(String path, File emptyPathDefault) {
jjg@151 213 ListBuffer<File> entries = new ListBuffer<File>();
jjg@151 214 int start = 0;
jjg@151 215 while (start <= path.length()) {
jjg@151 216 int sep = path.indexOf(File.pathSeparatorChar, start);
jjg@151 217 if (sep == -1)
jjg@151 218 sep = path.length();
jjg@151 219 if (start < sep)
jjg@151 220 entries.add(new File(path.substring(start, sep)));
jjg@151 221 else if (emptyPathDefault != null)
jjg@151 222 entries.add(emptyPathDefault);
jjg@151 223 start = sep + 1;
duke@1 224 }
jjg@151 225 return entries;
duke@1 226 }
duke@1 227
duke@1 228 private class Path extends LinkedHashSet<File> {
duke@1 229 private static final long serialVersionUID = 0;
duke@1 230
duke@1 231 private boolean expandJarClassPaths = false;
duke@1 232 private Set<File> canonicalValues = new HashSet<File>();
duke@1 233
duke@1 234 public Path expandJarClassPaths(boolean x) {
duke@1 235 expandJarClassPaths = x;
duke@1 236 return this;
duke@1 237 }
duke@1 238
duke@1 239 /** What to use when path element is the empty string */
jjg@151 240 private File emptyPathDefault = null;
duke@1 241
jjg@151 242 public Path emptyPathDefault(File x) {
duke@1 243 emptyPathDefault = x;
duke@1 244 return this;
duke@1 245 }
duke@1 246
duke@1 247 public Path() { super(); }
duke@1 248
duke@1 249 public Path addDirectories(String dirs, boolean warn) {
duke@1 250 if (dirs != null)
jjg@151 251 for (File dir : getPathEntries(dirs))
duke@1 252 addDirectory(dir, warn);
duke@1 253 return this;
duke@1 254 }
duke@1 255
duke@1 256 public Path addDirectories(String dirs) {
duke@1 257 return addDirectories(dirs, warn);
duke@1 258 }
duke@1 259
jjg@151 260 private void addDirectory(File dir, boolean warn) {
jjg@151 261 if (!dir.isDirectory()) {
duke@1 262 if (warn)
jjg@612 263 log.warning(Lint.LintCategory.PATH,
jjg@612 264 "dir.path.element.not.found", dir);
duke@1 265 return;
duke@1 266 }
duke@1 267
jjg@151 268 File[] files = dir.listFiles();
duke@1 269 if (files == null)
duke@1 270 return;
duke@1 271
duke@1 272 for (File direntry : files) {
duke@1 273 if (isArchive(direntry))
duke@1 274 addFile(direntry, warn);
duke@1 275 }
duke@1 276 }
duke@1 277
duke@1 278 public Path addFiles(String files, boolean warn) {
jjg@757 279 if (files != null) {
jjg@151 280 for (File file : getPathEntries(files, emptyPathDefault))
duke@1 281 addFile(file, warn);
jjg@757 282 }
duke@1 283 return this;
duke@1 284 }
duke@1 285
duke@1 286 public Path addFiles(String files) {
duke@1 287 return addFiles(files, warn);
duke@1 288 }
duke@1 289
duke@1 290 public void addFile(File file, boolean warn) {
jjh@801 291 if (contains(file)) {
jjh@801 292 // discard duplicates
duke@1 293 return;
duke@1 294 }
duke@1 295
jjg@106 296 if (! fsInfo.exists(file)) {
duke@1 297 /* No such file or directory exists */
jjg@612 298 if (warn) {
jjg@612 299 log.warning(Lint.LintCategory.PATH,
jjg@612 300 "path.element.not.found", file);
jjg@612 301 }
jjh@801 302 super.add(file);
jjh@801 303 return;
jjh@801 304 }
jjh@801 305
jjh@801 306 File canonFile = fsInfo.getCanonicalFile(file);
jjh@801 307 if (canonicalValues.contains(canonFile)) {
jjh@801 308 /* Discard duplicates and avoid infinite recursion */
jjh@801 309 return;
jjh@801 310 }
jjh@801 311
jjh@801 312 if (fsInfo.isFile(file)) {
duke@1 313 /* File is an ordinary file. */
duke@1 314 if (!isArchive(file)) {
duke@1 315 /* Not a recognized extension; open it to see if
duke@1 316 it looks like a valid zip file. */
duke@1 317 try {
duke@1 318 ZipFile z = new ZipFile(file);
duke@1 319 z.close();
jjg@612 320 if (warn) {
jjg@612 321 log.warning(Lint.LintCategory.PATH,
jjg@612 322 "unexpected.archive.file", file);
jjg@612 323 }
duke@1 324 } catch (IOException e) {
duke@1 325 // FIXME: include e.getLocalizedMessage in warning
jjg@612 326 if (warn) {
jjg@612 327 log.warning(Lint.LintCategory.PATH,
jjg@612 328 "invalid.archive.file", file);
jjg@612 329 }
duke@1 330 return;
duke@1 331 }
duke@1 332 }
duke@1 333 }
duke@1 334
duke@1 335 /* Now what we have left is either a directory or a file name
jjh@801 336 conforming to archive naming convention */
duke@1 337 super.add(file);
jjg@106 338 canonicalValues.add(canonFile);
duke@1 339
jjh@801 340 if (expandJarClassPaths && fsInfo.isFile(file))
duke@1 341 addJarClassPath(file, warn);
duke@1 342 }
duke@1 343
duke@1 344 // Adds referenced classpath elements from a jar's Class-Path
duke@1 345 // Manifest entry. In some future release, we may want to
duke@1 346 // update this code to recognize URLs rather than simple
duke@1 347 // filenames, but if we do, we should redo all path-related code.
duke@1 348 private void addJarClassPath(File jarFile, boolean warn) {
duke@1 349 try {
jjg@106 350 for (File f: fsInfo.getJarClassPath(jarFile)) {
jjg@106 351 addFile(f, warn);
duke@1 352 }
duke@1 353 } catch (IOException e) {
jjg@510 354 log.error("error.reading.file", jarFile, JavacFileManager.getMessage(e));
duke@1 355 }
duke@1 356 }
duke@1 357 }
duke@1 358
duke@1 359 private Path computeBootClassPath() {
jjg@818 360 defaultBootClassPathRtJar = null;
duke@1 361 Path path = new Path();
duke@1 362
jjg@757 363 String bootclasspathOpt = options.get(BOOTCLASSPATH);
jjg@757 364 String endorseddirsOpt = options.get(ENDORSEDDIRS);
jjg@757 365 String extdirsOpt = options.get(EXTDIRS);
jjg@757 366 String xbootclasspathPrependOpt = options.get(XBOOTCLASSPATH_PREPEND);
jjg@757 367 String xbootclasspathAppendOpt = options.get(XBOOTCLASSPATH_APPEND);
duke@1 368
jjg@757 369 path.addFiles(xbootclasspathPrependOpt);
jjg@757 370
jjg@757 371 if (endorseddirsOpt != null)
jjg@757 372 path.addDirectories(endorseddirsOpt);
duke@1 373 else
duke@1 374 path.addDirectories(System.getProperty("java.endorsed.dirs"), false);
duke@1 375
jjg@757 376 if (bootclasspathOpt != null) {
jjg@757 377 path.addFiles(bootclasspathOpt);
duke@1 378 } else {
duke@1 379 // Standard system classes for this compiler's release.
duke@1 380 String files = System.getProperty("sun.boot.class.path");
duke@1 381 path.addFiles(files, false);
duke@1 382 File rt_jar = new File("rt.jar");
jjg@151 383 for (File file : getPathEntries(files)) {
jjg@151 384 if (new File(file.getName()).equals(rt_jar))
jjg@818 385 defaultBootClassPathRtJar = file;
duke@1 386 }
duke@1 387 }
duke@1 388
jjg@757 389 path.addFiles(xbootclasspathAppendOpt);
duke@1 390
duke@1 391 // Strictly speaking, standard extensions are not bootstrap
duke@1 392 // classes, but we treat them identically, so we'll pretend
duke@1 393 // that they are.
jjg@757 394 if (extdirsOpt != null)
jjg@757 395 path.addDirectories(extdirsOpt);
duke@1 396 else
duke@1 397 path.addDirectories(System.getProperty("java.ext.dirs"), false);
duke@1 398
jjg@757 399 isDefaultBootClassPath =
jjg@757 400 (xbootclasspathPrependOpt == null) &&
jjg@757 401 (bootclasspathOpt == null) &&
jjg@757 402 (xbootclasspathAppendOpt == null);
jjg@757 403
duke@1 404 return path;
duke@1 405 }
duke@1 406
duke@1 407 private Path computeUserClassPath() {
duke@1 408 String cp = options.get(CLASSPATH);
duke@1 409
duke@1 410 // CLASSPATH environment variable when run from `javac'.
duke@1 411 if (cp == null) cp = System.getProperty("env.class.path");
duke@1 412
duke@1 413 // If invoked via a java VM (not the javac launcher), use the
duke@1 414 // platform class path
duke@1 415 if (cp == null && System.getProperty("application.home") == null)
duke@1 416 cp = System.getProperty("java.class.path");
duke@1 417
duke@1 418 // Default to current working directory.
duke@1 419 if (cp == null) cp = ".";
duke@1 420
duke@1 421 return new Path()
jjg@151 422 .expandJarClassPaths(true) // Only search user jars for Class-Paths
jjg@151 423 .emptyPathDefault(new File(".")) // Empty path elt ==> current directory
duke@1 424 .addFiles(cp);
duke@1 425 }
duke@1 426
duke@1 427 private Path computeSourcePath() {
duke@1 428 String sourcePathArg = options.get(SOURCEPATH);
duke@1 429 if (sourcePathArg == null)
duke@1 430 return null;
duke@1 431
duke@1 432 return new Path().addFiles(sourcePathArg);
duke@1 433 }
duke@1 434
duke@1 435 private Path computeAnnotationProcessorPath() {
duke@1 436 String processorPathArg = options.get(PROCESSORPATH);
duke@1 437 if (processorPathArg == null)
duke@1 438 return null;
duke@1 439
duke@1 440 return new Path().addFiles(processorPathArg);
duke@1 441 }
duke@1 442
duke@1 443 /** The actual effective locations searched for sources */
duke@1 444 private Path sourceSearchPath;
duke@1 445
duke@1 446 public Collection<File> sourceSearchPath() {
duke@1 447 if (sourceSearchPath == null) {
duke@1 448 lazy();
duke@1 449 Path sourcePath = getPathForLocation(SOURCE_PATH);
duke@1 450 Path userClassPath = getPathForLocation(CLASS_PATH);
duke@1 451 sourceSearchPath = sourcePath != null ? sourcePath : userClassPath;
duke@1 452 }
duke@1 453 return Collections.unmodifiableCollection(sourceSearchPath);
duke@1 454 }
duke@1 455
duke@1 456 /** The actual effective locations searched for classes */
duke@1 457 private Path classSearchPath;
duke@1 458
duke@1 459 public Collection<File> classSearchPath() {
duke@1 460 if (classSearchPath == null) {
duke@1 461 lazy();
duke@1 462 Path bootClassPath = getPathForLocation(PLATFORM_CLASS_PATH);
duke@1 463 Path userClassPath = getPathForLocation(CLASS_PATH);
duke@1 464 classSearchPath = new Path();
duke@1 465 classSearchPath.addAll(bootClassPath);
duke@1 466 classSearchPath.addAll(userClassPath);
duke@1 467 }
duke@1 468 return Collections.unmodifiableCollection(classSearchPath);
duke@1 469 }
duke@1 470
duke@1 471 /** The actual effective locations for non-source, non-class files */
duke@1 472 private Path otherSearchPath;
duke@1 473
duke@1 474 Collection<File> otherSearchPath() {
duke@1 475 if (otherSearchPath == null) {
duke@1 476 lazy();
duke@1 477 Path userClassPath = getPathForLocation(CLASS_PATH);
duke@1 478 Path sourcePath = getPathForLocation(SOURCE_PATH);
duke@1 479 if (sourcePath == null)
duke@1 480 otherSearchPath = userClassPath;
duke@1 481 else {
duke@1 482 otherSearchPath = new Path();
duke@1 483 otherSearchPath.addAll(userClassPath);
duke@1 484 otherSearchPath.addAll(sourcePath);
duke@1 485 }
duke@1 486 }
duke@1 487 return Collections.unmodifiableCollection(otherSearchPath);
duke@1 488 }
duke@1 489
duke@1 490 /** Is this the name of an archive file? */
jjg@106 491 private boolean isArchive(File file) {
duke@1 492 String n = file.getName().toLowerCase();
jjg@106 493 return fsInfo.isFile(file)
duke@1 494 && (n.endsWith(".jar") || n.endsWith(".zip"));
duke@1 495 }
darcy@497 496
darcy@497 497 /**
darcy@497 498 * Utility method for converting a search path string to an array
darcy@497 499 * of directory and JAR file URLs.
darcy@497 500 *
darcy@497 501 * Note that this method is called by apt and the DocletInvoker.
darcy@497 502 *
darcy@497 503 * @param path the search path string
darcy@497 504 * @return the resulting array of directory and JAR file URLs
darcy@497 505 */
darcy@497 506 public static URL[] pathToURLs(String path) {
darcy@497 507 StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
darcy@497 508 URL[] urls = new URL[st.countTokens()];
darcy@497 509 int count = 0;
darcy@497 510 while (st.hasMoreTokens()) {
darcy@497 511 URL url = fileToURL(new File(st.nextToken()));
darcy@497 512 if (url != null) {
darcy@497 513 urls[count++] = url;
darcy@497 514 }
darcy@497 515 }
darcy@497 516 if (urls.length != count) {
darcy@497 517 URL[] tmp = new URL[count];
darcy@497 518 System.arraycopy(urls, 0, tmp, 0, count);
darcy@497 519 urls = tmp;
darcy@497 520 }
darcy@497 521 return urls;
darcy@497 522 }
darcy@497 523
darcy@497 524 /**
darcy@497 525 * Returns the directory or JAR file URL corresponding to the specified
darcy@497 526 * local file name.
darcy@497 527 *
darcy@497 528 * @param file the File object
darcy@497 529 * @return the resulting directory or JAR file URL, or null if unknown
darcy@497 530 */
darcy@497 531 private static URL fileToURL(File file) {
darcy@497 532 String name;
darcy@497 533 try {
darcy@497 534 name = file.getCanonicalPath();
darcy@497 535 } catch (IOException e) {
darcy@497 536 name = file.getAbsolutePath();
darcy@497 537 }
darcy@497 538 name = name.replace(File.separatorChar, '/');
darcy@497 539 if (!name.startsWith("/")) {
darcy@497 540 name = "/" + name;
darcy@497 541 }
darcy@497 542 // If the file does not exist, then assume that it's a directory
darcy@497 543 if (!file.isFile()) {
darcy@497 544 name = name + "/";
darcy@497 545 }
darcy@497 546 try {
darcy@497 547 return new URL("file", "", name);
darcy@497 548 } catch (MalformedURLException e) {
darcy@497 549 throw new IllegalArgumentException(file.toString());
darcy@497 550 }
darcy@497 551 }
duke@1 552 }

mercurial