langtools/src/share/classes/com/sun/tools/javac/file/Locations.java
changeset 10818 e95eb04c68cc
parent 10813 95b39a692cd0
child 10819 e1a8df45d8b0
equal deleted inserted replaced
10817:d91978895fac 10818:e95eb04c68cc
       
     1 /*
       
     2  * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package com.sun.tools.javac.file;
       
    27 
       
    28 import java.io.FileNotFoundException;
       
    29 import java.util.Iterator;
       
    30 import java.io.File;
       
    31 import java.io.IOException;
       
    32 import java.net.MalformedURLException;
       
    33 import java.net.URL;
       
    34 import java.util.Arrays;
       
    35 import java.util.Collection;
       
    36 import java.util.Collections;
       
    37 import java.util.EnumMap;
       
    38 import java.util.EnumSet;
       
    39 import java.util.HashMap;
       
    40 import java.util.HashSet;
       
    41 import java.util.LinkedHashSet;
       
    42 import java.util.Map;
       
    43 import java.util.Set;
       
    44 import java.util.StringTokenizer;
       
    45 import java.util.zip.ZipFile;
       
    46 import javax.tools.JavaFileManager.Location;
       
    47 import javax.tools.StandardLocation;
       
    48 
       
    49 import com.sun.tools.javac.code.Lint;
       
    50 import com.sun.tools.javac.main.OptionName;
       
    51 import com.sun.tools.javac.util.ListBuffer;
       
    52 import com.sun.tools.javac.util.Log;
       
    53 import com.sun.tools.javac.util.Options;
       
    54 
       
    55 import javax.tools.JavaFileManager;
       
    56 import static javax.tools.StandardLocation.*;
       
    57 import static com.sun.tools.javac.main.OptionName.*;
       
    58 
       
    59 /** This class converts command line arguments, environment variables
       
    60  *  and system properties (in File.pathSeparator-separated String form)
       
    61  *  into a boot class path, user class path, and source path (in
       
    62  *  Collection<String> form).
       
    63  *
       
    64  *  <p><b>This is NOT part of any supported API.
       
    65  *  If you write code that depends on this, you do so at your own risk.
       
    66  *  This code and its internal interfaces are subject to change or
       
    67  *  deletion without notice.</b>
       
    68  */
       
    69 public class Locations {
       
    70 
       
    71     /** The log to use for warning output */
       
    72     private Log log;
       
    73 
       
    74     /** Collection of command-line options */
       
    75     private Options options;
       
    76 
       
    77     /** Handler for -Xlint options */
       
    78     private Lint lint;
       
    79 
       
    80     /** Access to (possibly cached) file info */
       
    81     private FSInfo fsInfo;
       
    82 
       
    83     /** Whether to warn about non-existent path elements */
       
    84     private boolean warn;
       
    85 
       
    86     // TODO: remove need for this
       
    87     private boolean inited = false; // TODO? caching bad?
       
    88 
       
    89     public Locations() {
       
    90         initHandlers();
       
    91     }
       
    92 
       
    93     public void update(Log log, Options options, Lint lint, FSInfo fsInfo) {
       
    94         this.log = log;
       
    95         this.options = options;
       
    96         this.lint = lint;
       
    97         this.fsInfo = fsInfo;
       
    98     }
       
    99 
       
   100     public Collection<File> bootClassPath() {
       
   101         return getLocation(PLATFORM_CLASS_PATH);
       
   102     }
       
   103 
       
   104     public boolean isDefaultBootClassPath() {
       
   105         BootClassPathLocationHandler h =
       
   106                 (BootClassPathLocationHandler) getHandler(PLATFORM_CLASS_PATH);
       
   107         return h.isDefault();
       
   108     }
       
   109 
       
   110     boolean isDefaultBootClassPathRtJar(File file) {
       
   111         BootClassPathLocationHandler h =
       
   112                 (BootClassPathLocationHandler) getHandler(PLATFORM_CLASS_PATH);
       
   113         return h.isDefaultRtJar(file);
       
   114     }
       
   115 
       
   116     public Collection<File> userClassPath() {
       
   117         return getLocation(CLASS_PATH);
       
   118     }
       
   119 
       
   120     public Collection<File> sourcePath() {
       
   121         Collection<File> p = getLocation(SOURCE_PATH);
       
   122         // TODO: this should be handled by the LocationHandler
       
   123         return p == null || p.isEmpty() ? null : p;
       
   124     }
       
   125 
       
   126     /**
       
   127      * Split a path into its elements. Empty path elements will be ignored.
       
   128      * @param path The path to be split
       
   129      * @return The elements of the path
       
   130      */
       
   131     private static Iterable<File> getPathEntries(String path) {
       
   132         return getPathEntries(path, null);
       
   133     }
       
   134 
       
   135     /**
       
   136      * Split a path into its elements. If emptyPathDefault is not null, all
       
   137      * empty elements in the path, including empty elements at either end of
       
   138      * the path, will be replaced with the value of emptyPathDefault.
       
   139      * @param path The path to be split
       
   140      * @param emptyPathDefault The value to substitute for empty path elements,
       
   141      *  or null, to ignore empty path elements
       
   142      * @return The elements of the path
       
   143      */
       
   144     private static Iterable<File> getPathEntries(String path, File emptyPathDefault) {
       
   145         ListBuffer<File> entries = new ListBuffer<File>();
       
   146         int start = 0;
       
   147         while (start <= path.length()) {
       
   148             int sep = path.indexOf(File.pathSeparatorChar, start);
       
   149             if (sep == -1)
       
   150                 sep = path.length();
       
   151             if (start < sep)
       
   152                 entries.add(new File(path.substring(start, sep)));
       
   153             else if (emptyPathDefault != null)
       
   154                 entries.add(emptyPathDefault);
       
   155             start = sep + 1;
       
   156         }
       
   157         return entries;
       
   158     }
       
   159 
       
   160     /**
       
   161      * Utility class to help evaluate a path option.
       
   162      * Duplicate entries are ignored, jar class paths can be expanded.
       
   163      */
       
   164     private class Path extends LinkedHashSet<File> {
       
   165         private static final long serialVersionUID = 0;
       
   166 
       
   167         private boolean expandJarClassPaths = false;
       
   168         private Set<File> canonicalValues = new HashSet<File>();
       
   169 
       
   170         public Path expandJarClassPaths(boolean x) {
       
   171             expandJarClassPaths = x;
       
   172             return this;
       
   173         }
       
   174 
       
   175         /** What to use when path element is the empty string */
       
   176         private File emptyPathDefault = null;
       
   177 
       
   178         public Path emptyPathDefault(File x) {
       
   179             emptyPathDefault = x;
       
   180             return this;
       
   181         }
       
   182 
       
   183         public Path() { super(); }
       
   184 
       
   185         public Path addDirectories(String dirs, boolean warn) {
       
   186             boolean prev = expandJarClassPaths;
       
   187             expandJarClassPaths = true;
       
   188             try {
       
   189                 if (dirs != null)
       
   190                     for (File dir : getPathEntries(dirs))
       
   191                         addDirectory(dir, warn);
       
   192                 return this;
       
   193             } finally {
       
   194                 expandJarClassPaths = prev;
       
   195             }
       
   196         }
       
   197 
       
   198         public Path addDirectories(String dirs) {
       
   199             return addDirectories(dirs, warn);
       
   200         }
       
   201 
       
   202         private void addDirectory(File dir, boolean warn) {
       
   203             if (!dir.isDirectory()) {
       
   204                 if (warn)
       
   205                     log.warning(Lint.LintCategory.PATH,
       
   206                             "dir.path.element.not.found", dir);
       
   207                 return;
       
   208             }
       
   209 
       
   210             File[] files = dir.listFiles();
       
   211             if (files == null)
       
   212                 return;
       
   213 
       
   214             for (File direntry : files) {
       
   215                 if (isArchive(direntry))
       
   216                     addFile(direntry, warn);
       
   217             }
       
   218         }
       
   219 
       
   220         public Path addFiles(String files, boolean warn) {
       
   221             if (files != null) {
       
   222                 addFiles(getPathEntries(files, emptyPathDefault), warn);
       
   223             }
       
   224             return this;
       
   225         }
       
   226 
       
   227         public Path addFiles(String files) {
       
   228             return addFiles(files, warn);
       
   229         }
       
   230 
       
   231         public Path addFiles(Iterable<? extends File> files, boolean warn) {
       
   232             if (files != null) {
       
   233                 for (File file: files)
       
   234                     addFile(file, warn);
       
   235             }
       
   236             return this;
       
   237         }
       
   238 
       
   239         public Path addFiles(Iterable<? extends File> files) {
       
   240             return addFiles(files, warn);
       
   241         }
       
   242 
       
   243         public void addFile(File file, boolean warn) {
       
   244             if (contains(file)) {
       
   245                 // discard duplicates
       
   246                 return;
       
   247             }
       
   248 
       
   249             if (! fsInfo.exists(file)) {
       
   250                 /* No such file or directory exists */
       
   251                 if (warn) {
       
   252                     log.warning(Lint.LintCategory.PATH,
       
   253                             "path.element.not.found", file);
       
   254                 }
       
   255                 super.add(file);
       
   256                 return;
       
   257             }
       
   258 
       
   259             File canonFile = fsInfo.getCanonicalFile(file);
       
   260             if (canonicalValues.contains(canonFile)) {
       
   261                 /* Discard duplicates and avoid infinite recursion */
       
   262                 return;
       
   263             }
       
   264 
       
   265             if (fsInfo.isFile(file)) {
       
   266                 /* File is an ordinary file. */
       
   267                 if (!isArchive(file)) {
       
   268                     /* Not a recognized extension; open it to see if
       
   269                      it looks like a valid zip file. */
       
   270                     try {
       
   271                         ZipFile z = new ZipFile(file);
       
   272                         z.close();
       
   273                         if (warn) {
       
   274                             log.warning(Lint.LintCategory.PATH,
       
   275                                     "unexpected.archive.file", file);
       
   276                         }
       
   277                     } catch (IOException e) {
       
   278                         // FIXME: include e.getLocalizedMessage in warning
       
   279                         if (warn) {
       
   280                             log.warning(Lint.LintCategory.PATH,
       
   281                                     "invalid.archive.file", file);
       
   282                         }
       
   283                         return;
       
   284                     }
       
   285                 }
       
   286             }
       
   287 
       
   288             /* Now what we have left is either a directory or a file name
       
   289                conforming to archive naming convention */
       
   290             super.add(file);
       
   291             canonicalValues.add(canonFile);
       
   292 
       
   293             if (expandJarClassPaths && fsInfo.isFile(file))
       
   294                 addJarClassPath(file, warn);
       
   295         }
       
   296 
       
   297         // Adds referenced classpath elements from a jar's Class-Path
       
   298         // Manifest entry.  In some future release, we may want to
       
   299         // update this code to recognize URLs rather than simple
       
   300         // filenames, but if we do, we should redo all path-related code.
       
   301         private void addJarClassPath(File jarFile, boolean warn) {
       
   302             try {
       
   303                 for (File f: fsInfo.getJarClassPath(jarFile)) {
       
   304                     addFile(f, warn);
       
   305                 }
       
   306             } catch (IOException e) {
       
   307                 log.error("error.reading.file", jarFile, JavacFileManager.getMessage(e));
       
   308             }
       
   309         }
       
   310     }
       
   311 
       
   312     /**
       
   313      * Base class for handling support for the representation of Locations.
       
   314      * Implementations are responsible for handling the interactions between
       
   315      * the command line options for a location, and API access via setLocation.
       
   316      * @see #initHandlers
       
   317      * @see #getHandler
       
   318      */
       
   319     protected abstract class LocationHandler {
       
   320         final Location location;
       
   321         final Set<OptionName> options;
       
   322 
       
   323         /**
       
   324          * Create a handler. The location and options provide a way to map
       
   325          * from a location or an option to the corresponding handler.
       
   326          * @see #initHandlers
       
   327          */
       
   328         protected LocationHandler(Location location, OptionName... options) {
       
   329             this.location = location;
       
   330             this.options = EnumSet.copyOf(Arrays.asList(options));
       
   331         }
       
   332 
       
   333         // TODO: TEMPORARY, while Options still used for command line options
       
   334         void update(Options optionTable) {
       
   335             for (OptionName o: options) {
       
   336                 String v = optionTable.get(o);
       
   337                 if (v != null) {
       
   338                     handleOption(o, v);
       
   339                 }
       
   340             }
       
   341         }
       
   342 
       
   343         /** @see JavaFileManager#handleOption. */
       
   344         abstract boolean handleOption(OptionName option, String value);
       
   345         /** @see JavaFileManager#getLocation. */
       
   346         abstract Collection<File> getLocation();
       
   347         /** @see JavaFileManager#setLocation. */
       
   348         abstract void setLocation(Iterable<? extends File> files) throws IOException;
       
   349     }
       
   350 
       
   351     /**
       
   352      * General purpose implementation for output locations,
       
   353      * such as -d/CLASS_OUTPUT and -s/SOURCE_OUTPUT.
       
   354      * All options are treated as equivalent (i.e. aliases.)
       
   355      * The value is a single file, possibly null.
       
   356      */
       
   357     private class OutputLocationHandler extends LocationHandler {
       
   358         private File outputDir;
       
   359 
       
   360         OutputLocationHandler(Location location, OptionName... options) {
       
   361             super(location, options);
       
   362         }
       
   363 
       
   364         @Override
       
   365         boolean handleOption(OptionName option, String value) {
       
   366             if (!options.contains(option))
       
   367                 return false;
       
   368 
       
   369             // TODO: could/should validate outputDir exists and is a directory
       
   370             // need to decide how best to report issue for benefit of
       
   371             // direct API call on JavaFileManager.handleOption(specifies IAE)
       
   372             // vs. command line decoding.
       
   373             outputDir = new File(value);
       
   374             return true;
       
   375         }
       
   376 
       
   377         @Override
       
   378         Collection<File> getLocation() {
       
   379             return (outputDir == null) ? null : Collections.singleton(outputDir);
       
   380         }
       
   381 
       
   382         @Override
       
   383         void setLocation(Iterable<? extends File> files) throws IOException {
       
   384             if (files == null) {
       
   385                 outputDir = null;
       
   386             } else {
       
   387                 Iterator<? extends File> pathIter = files.iterator();
       
   388                 if (!pathIter.hasNext())
       
   389                     throw new IllegalArgumentException("empty path for directory");
       
   390                 File dir = pathIter.next();
       
   391                 if (pathIter.hasNext())
       
   392                     throw new IllegalArgumentException("path too long for directory");
       
   393                 if (!dir.exists())
       
   394                     throw new FileNotFoundException(dir + ": does not exist");
       
   395                 else if (!dir.isDirectory())
       
   396                     throw new IOException(dir + ": not a directory");
       
   397                 outputDir = dir;
       
   398             }
       
   399         }
       
   400     }
       
   401 
       
   402     /**
       
   403      * General purpose implementation for search path locations,
       
   404      * such as -sourcepath/SOURCE_PATH and -processorPath/ANNOTATION_PROCESS_PATH.
       
   405      * All options are treated as equivalent (i.e. aliases.)
       
   406      * The value is an ordered set of files and/or directories.
       
   407      */
       
   408     private class SimpleLocationHandler extends LocationHandler {
       
   409         protected Collection<File> searchPath;
       
   410 
       
   411         SimpleLocationHandler(Location location, OptionName... options) {
       
   412             super(location, options);
       
   413         }
       
   414 
       
   415         @Override
       
   416         boolean handleOption(OptionName option, String value) {
       
   417             if (!options.contains(option))
       
   418                 return false;
       
   419             searchPath = value == null ? null :
       
   420                     Collections.unmodifiableCollection(computePath(value));
       
   421             return true;
       
   422         }
       
   423 
       
   424         protected Path computePath(String value) {
       
   425             return new Path().addFiles(value);
       
   426         }
       
   427 
       
   428         @Override
       
   429         Collection<File> getLocation() {
       
   430             return searchPath;
       
   431         }
       
   432 
       
   433         @Override
       
   434         void setLocation(Iterable<? extends File> files) {
       
   435             Path p;
       
   436             if (files == null) {
       
   437                 p = computePath(null);
       
   438             } else {
       
   439                 p = new Path().addFiles(files);
       
   440             }
       
   441             searchPath = Collections.unmodifiableCollection(p);
       
   442         }
       
   443     }
       
   444 
       
   445     /**
       
   446      * Subtype of SimpleLocationHandler for -classpath/CLASS_PATH.
       
   447      * If no value is given, a default is provided, based on system properties
       
   448      * and other values.
       
   449      */
       
   450     private class ClassPathLocationHandler extends SimpleLocationHandler {
       
   451         ClassPathLocationHandler() {
       
   452             super(StandardLocation.CLASS_PATH,
       
   453                     OptionName.CLASSPATH, OptionName.CP);
       
   454         }
       
   455 
       
   456         @Override
       
   457         Collection<File> getLocation() {
       
   458             lazy();
       
   459             return searchPath;
       
   460         }
       
   461 
       
   462         @Override
       
   463         protected Path computePath(String value) {
       
   464             String cp = value;
       
   465 
       
   466             // CLASSPATH environment variable when run from `javac'.
       
   467             if (cp == null) cp = System.getProperty("env.class.path");
       
   468 
       
   469             // If invoked via a java VM (not the javac launcher), use the
       
   470             // platform class path
       
   471             if (cp == null && System.getProperty("application.home") == null)
       
   472                 cp = System.getProperty("java.class.path");
       
   473 
       
   474             // Default to current working directory.
       
   475             if (cp == null) cp = ".";
       
   476 
       
   477             return new Path()
       
   478                 .expandJarClassPaths(true)        // Only search user jars for Class-Paths
       
   479                 .emptyPathDefault(new File("."))  // Empty path elt ==> current directory
       
   480                 .addFiles(cp);
       
   481             }
       
   482 
       
   483         private void lazy() {
       
   484             if (searchPath == null)
       
   485                 setLocation(null);
       
   486         }
       
   487     }
       
   488 
       
   489     /**
       
   490      * Custom subtype of LocationHandler for PLATFORM_CLASS_PATH.
       
   491      * Various options are supported for different components of the
       
   492      * platform class path.
       
   493      * Setting a value with setLocation overrides all existing option values.
       
   494      * Setting any option overrides any value set with setLocation, and reverts
       
   495      * to using default values for options that have not been set.
       
   496      * Setting -bootclasspath or -Xbootclasspath overrides any existing
       
   497      * value for -Xbootclasspath/p: and -Xbootclasspath/a:.
       
   498      */
       
   499     private class BootClassPathLocationHandler extends LocationHandler {
       
   500         private Collection<File> searchPath;
       
   501         final Map<OptionName, String> optionValues = new EnumMap<OptionName,String>(OptionName.class);
       
   502 
       
   503         /**
       
   504          * rt.jar as found on the default bootclasspath.
       
   505          * If the user specified a bootclasspath, null is used.
       
   506          */
       
   507         private File defaultBootClassPathRtJar = null;
       
   508 
       
   509         /**
       
   510          *  Is bootclasspath the default?
       
   511          */
       
   512         private boolean isDefaultBootClassPath;
       
   513 
       
   514         BootClassPathLocationHandler() {
       
   515             super(StandardLocation.PLATFORM_CLASS_PATH,
       
   516                     OptionName.BOOTCLASSPATH, OptionName.XBOOTCLASSPATH,
       
   517                     OptionName.XBOOTCLASSPATH_PREPEND,
       
   518                     OptionName.XBOOTCLASSPATH_APPEND,
       
   519                     OptionName.ENDORSEDDIRS, OptionName.DJAVA_ENDORSED_DIRS,
       
   520                     OptionName.EXTDIRS, OptionName.DJAVA_EXT_DIRS);
       
   521         }
       
   522 
       
   523         boolean isDefault() {
       
   524             lazy();
       
   525             return isDefaultBootClassPath;
       
   526         }
       
   527 
       
   528         boolean isDefaultRtJar(File file) {
       
   529             lazy();
       
   530             return file.equals(defaultBootClassPathRtJar);
       
   531         }
       
   532 
       
   533         @Override
       
   534         boolean handleOption(OptionName option, String value) {
       
   535             if (!options.contains(option))
       
   536                 return false;
       
   537 
       
   538             option = canonicalize(option);
       
   539             optionValues.put(option, value);
       
   540             if (option == BOOTCLASSPATH) {
       
   541                 optionValues.remove(XBOOTCLASSPATH_PREPEND);
       
   542                 optionValues.remove(XBOOTCLASSPATH_APPEND);
       
   543             }
       
   544             searchPath = null;  // reset to "uninitialized"
       
   545             return true;
       
   546         }
       
   547         // where
       
   548             // TODO: would be better if option aliasing was handled at a higher
       
   549             // level
       
   550             private OptionName canonicalize(OptionName option) {
       
   551                 switch (option) {
       
   552                     case XBOOTCLASSPATH:
       
   553                         return OptionName.BOOTCLASSPATH;
       
   554                     case DJAVA_ENDORSED_DIRS:
       
   555                         return OptionName.ENDORSEDDIRS;
       
   556                     case DJAVA_EXT_DIRS:
       
   557                         return OptionName.EXTDIRS;
       
   558                     default:
       
   559                         return option;
       
   560                 }
       
   561             }
       
   562 
       
   563         @Override
       
   564         Collection<File> getLocation() {
       
   565             lazy();
       
   566             return searchPath;
       
   567         }
       
   568 
       
   569         @Override
       
   570         void setLocation(Iterable<? extends File> files) {
       
   571             if (files == null) {
       
   572                 searchPath = null;  // reset to "uninitialized"
       
   573             } else {
       
   574                 defaultBootClassPathRtJar = null;
       
   575                 isDefaultBootClassPath = false;
       
   576                 Path p = new Path().addFiles(files, false);
       
   577                 searchPath = Collections.unmodifiableCollection(p);
       
   578                 optionValues.clear();
       
   579             }
       
   580         }
       
   581 
       
   582         Path computePath() {
       
   583             defaultBootClassPathRtJar = null;
       
   584             Path path = new Path();
       
   585 
       
   586             String bootclasspathOpt = optionValues.get(BOOTCLASSPATH);
       
   587             String endorseddirsOpt = optionValues.get(ENDORSEDDIRS);
       
   588             String extdirsOpt = optionValues.get(EXTDIRS);
       
   589             String xbootclasspathPrependOpt = optionValues.get(XBOOTCLASSPATH_PREPEND);
       
   590             String xbootclasspathAppendOpt = optionValues.get(XBOOTCLASSPATH_APPEND);
       
   591 
       
   592             path.addFiles(xbootclasspathPrependOpt);
       
   593 
       
   594             if (endorseddirsOpt != null)
       
   595                 path.addDirectories(endorseddirsOpt);
       
   596             else
       
   597                 path.addDirectories(System.getProperty("java.endorsed.dirs"), false);
       
   598 
       
   599             if (bootclasspathOpt != null) {
       
   600                 path.addFiles(bootclasspathOpt);
       
   601             } else {
       
   602                 // Standard system classes for this compiler's release.
       
   603                 String files = System.getProperty("sun.boot.class.path");
       
   604                 path.addFiles(files, false);
       
   605                 File rt_jar = new File("rt.jar");
       
   606                 for (File file : getPathEntries(files)) {
       
   607                     if (new File(file.getName()).equals(rt_jar))
       
   608                         defaultBootClassPathRtJar = file;
       
   609                 }
       
   610             }
       
   611 
       
   612             path.addFiles(xbootclasspathAppendOpt);
       
   613 
       
   614             // Strictly speaking, standard extensions are not bootstrap
       
   615             // classes, but we treat them identically, so we'll pretend
       
   616             // that they are.
       
   617             if (extdirsOpt != null)
       
   618                 path.addDirectories(extdirsOpt);
       
   619             else
       
   620                 path.addDirectories(System.getProperty("java.ext.dirs"), false);
       
   621 
       
   622             isDefaultBootClassPath =
       
   623                     (xbootclasspathPrependOpt == null) &&
       
   624                     (bootclasspathOpt == null) &&
       
   625                     (xbootclasspathAppendOpt == null);
       
   626 
       
   627             return path;
       
   628         }
       
   629 
       
   630         private void lazy() {
       
   631             if (searchPath == null)
       
   632                 searchPath = Collections.unmodifiableCollection(computePath());
       
   633         }
       
   634     }
       
   635 
       
   636     Map<Location, LocationHandler> handlersForLocation;
       
   637     Map<OptionName, LocationHandler> handlersForOption;
       
   638 
       
   639     void initHandlers() {
       
   640         handlersForLocation = new HashMap<Location, LocationHandler>();
       
   641         handlersForOption = new EnumMap<OptionName, LocationHandler>(OptionName.class);
       
   642 
       
   643         LocationHandler[] handlers = {
       
   644             new BootClassPathLocationHandler(),
       
   645             new ClassPathLocationHandler(),
       
   646             new SimpleLocationHandler(StandardLocation.SOURCE_PATH, OptionName.SOURCEPATH),
       
   647             new SimpleLocationHandler(StandardLocation.ANNOTATION_PROCESSOR_PATH, OptionName.PROCESSORPATH),
       
   648             new OutputLocationHandler((StandardLocation.CLASS_OUTPUT), OptionName.D),
       
   649             new OutputLocationHandler((StandardLocation.SOURCE_OUTPUT), OptionName.S)
       
   650         };
       
   651 
       
   652         for (LocationHandler h: handlers) {
       
   653             handlersForLocation.put(h.location, h);
       
   654             for (OptionName o: h.options)
       
   655                 handlersForOption.put(o, h);
       
   656         }
       
   657     }
       
   658 
       
   659     boolean handleOption(OptionName option, String value) {
       
   660         LocationHandler h = handlersForOption.get(option);
       
   661         return (h == null ? false : h.handleOption(option, value));
       
   662     }
       
   663 
       
   664     Collection<File> getLocation(Location location) {
       
   665         LocationHandler h = getHandler(location);
       
   666         return (h == null ? null : h.getLocation());
       
   667     }
       
   668 
       
   669     File getOutputLocation(Location location) {
       
   670         if (!location.isOutputLocation())
       
   671             throw new IllegalArgumentException();
       
   672         LocationHandler h = getHandler(location);
       
   673         return ((OutputLocationHandler) h).outputDir;
       
   674     }
       
   675 
       
   676     void setLocation(Location location, Iterable<? extends File> files) throws IOException {
       
   677         LocationHandler h = getHandler(location);
       
   678         if (h == null) {
       
   679             if (location.isOutputLocation())
       
   680                 h = new OutputLocationHandler(location);
       
   681             else
       
   682                 h = new SimpleLocationHandler(location);
       
   683             handlersForLocation.put(location, h);
       
   684         }
       
   685         h.setLocation(files);
       
   686     }
       
   687 
       
   688     protected LocationHandler getHandler(Location location) {
       
   689         location.getClass(); // null check
       
   690         lazy();
       
   691         return handlersForLocation.get(location);
       
   692     }
       
   693 
       
   694 // TOGO
       
   695     protected void lazy() {
       
   696         if (!inited) {
       
   697             warn = lint.isEnabled(Lint.LintCategory.PATH);
       
   698 
       
   699             for (LocationHandler h: handlersForLocation.values()) {
       
   700                 h.update(options);
       
   701             }
       
   702 
       
   703             inited = true;
       
   704         }
       
   705     }
       
   706 
       
   707     /** Is this the name of an archive file? */
       
   708     private boolean isArchive(File file) {
       
   709         String n = file.getName().toLowerCase();
       
   710         return fsInfo.isFile(file)
       
   711             && (n.endsWith(".jar") || n.endsWith(".zip"));
       
   712     }
       
   713 
       
   714     /**
       
   715      * Utility method for converting a search path string to an array
       
   716      * of directory and JAR file URLs.
       
   717      *
       
   718      * Note that this method is called by apt and the DocletInvoker.
       
   719      *
       
   720      * @param path the search path string
       
   721      * @return the resulting array of directory and JAR file URLs
       
   722      */
       
   723     public static URL[] pathToURLs(String path) {
       
   724         StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
       
   725         URL[] urls = new URL[st.countTokens()];
       
   726         int count = 0;
       
   727         while (st.hasMoreTokens()) {
       
   728             URL url = fileToURL(new File(st.nextToken()));
       
   729             if (url != null) {
       
   730                 urls[count++] = url;
       
   731             }
       
   732         }
       
   733         if (urls.length != count) {
       
   734             URL[] tmp = new URL[count];
       
   735             System.arraycopy(urls, 0, tmp, 0, count);
       
   736             urls = tmp;
       
   737         }
       
   738         return urls;
       
   739     }
       
   740 
       
   741     /**
       
   742      * Returns the directory or JAR file URL corresponding to the specified
       
   743      * local file name.
       
   744      *
       
   745      * @param file the File object
       
   746      * @return the resulting directory or JAR file URL, or null if unknown
       
   747      */
       
   748     private static URL fileToURL(File file) {
       
   749         String name;
       
   750         try {
       
   751             name = file.getCanonicalPath();
       
   752         } catch (IOException e) {
       
   753             name = file.getAbsolutePath();
       
   754         }
       
   755         name = name.replace(File.separatorChar, '/');
       
   756         if (!name.startsWith("/")) {
       
   757             name = "/" + name;
       
   758         }
       
   759         // If the file does not exist, then assume that it's a directory
       
   760         if (!file.isFile()) {
       
   761             name = name + "/";
       
   762         }
       
   763         try {
       
   764             return new URL("file", "", name);
       
   765         } catch (MalformedURLException e) {
       
   766             throw new IllegalArgumentException(file.toString());
       
   767         }
       
   768     }
       
   769 }