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

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

author
jjg
date
Fri, 11 Dec 2009 14:26:27 -0800
changeset 450
4011f49b4af8
parent 285
4ce1c1400334
child 497
16b9b7f45933
permissions
-rw-r--r--

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

     1 /*
     2  * Copyright 2003-2008 Sun Microsystems, Inc.  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.  Sun designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
    23  * have any questions.
    24  */
    26 package com.sun.tools.javac.file;
    28 import java.io.File;
    29 import java.io.IOException;
    30 import java.util.HashMap;
    31 import java.util.HashSet;
    32 import java.util.Map;
    33 import java.util.Set;
    34 import java.util.Collection;
    35 import java.util.Collections;
    36 import java.util.LinkedHashSet;
    37 import java.util.zip.ZipFile;
    38 import javax.tools.JavaFileManager.Location;
    40 import com.sun.tools.javac.code.Lint;
    41 import com.sun.tools.javac.util.Context;
    42 import com.sun.tools.javac.util.ListBuffer;
    43 import com.sun.tools.javac.util.Log;
    44 import com.sun.tools.javac.util.Options;
    46 import static javax.tools.StandardLocation.*;
    47 import static com.sun.tools.javac.main.OptionName.*;
    49 /** This class converts command line arguments, environment variables
    50  *  and system properties (in File.pathSeparator-separated String form)
    51  *  into a boot class path, user class path, and source path (in
    52  *  Collection<String> form).
    53  *
    54  *  <p><b>This is NOT part of any API supported by Sun Microsystems.  If
    55  *  you write code that depends on this, you do so at your own risk.
    56  *  This code and its internal interfaces are subject to change or
    57  *  deletion without notice.</b>
    58  */
    59 public class Paths {
    61     /** The context key for the todo list */
    62     protected static final Context.Key<Paths> pathsKey =
    63         new Context.Key<Paths>();
    65     /** Get the Paths instance for this context.
    66      *  @param context the context
    67      *  @return the Paths instance for this context
    68      */
    69     public static Paths instance(Context context) {
    70         Paths instance = context.get(pathsKey);
    71         if (instance == null)
    72             instance = new Paths(context);
    73         return instance;
    74     }
    76     /** The log to use for warning output */
    77     private Log log;
    79     /** Collection of command-line options */
    80     private Options options;
    82     /** Handler for -Xlint options */
    83     private Lint lint;
    85     /** Access to (possibly cached) file info */
    86     private FSInfo fsInfo;
    88     protected Paths(Context context) {
    89         context.put(pathsKey, this);
    90         pathsForLocation = new HashMap<Location,Path>(16);
    91         setContext(context);
    92     }
    94     void setContext(Context context) {
    95         log = Log.instance(context);
    96         options = Options.instance(context);
    97         lint = Lint.instance(context);
    98         fsInfo = FSInfo.instance(context);
    99     }
   101     /** Whether to warn about non-existent path elements */
   102     private boolean warn;
   104     private Map<Location, Path> pathsForLocation;
   106     private boolean inited = false; // TODO? caching bad?
   108     /**
   109      * rt.jar as found on the default bootclass path.  If the user specified a
   110      * bootclasspath, null is used.
   111      */
   112     private File bootClassPathRtJar = null;
   114     Path getPathForLocation(Location location) {
   115         Path path = pathsForLocation.get(location);
   116         if (path == null)
   117             setPathForLocation(location, null);
   118         return pathsForLocation.get(location);
   119     }
   121     void setPathForLocation(Location location, Iterable<? extends File> path) {
   122         // TODO? if (inited) throw new IllegalStateException
   123         // TODO: otherwise reset sourceSearchPath, classSearchPath as needed
   124         Path p;
   125         if (path == null) {
   126             if (location == CLASS_PATH)
   127                 p = computeUserClassPath();
   128             else if (location == PLATFORM_CLASS_PATH)
   129                 p = computeBootClassPath();
   130             else if (location == ANNOTATION_PROCESSOR_PATH)
   131                 p = computeAnnotationProcessorPath();
   132             else if (location == SOURCE_PATH)
   133                 p = computeSourcePath();
   134             else
   135                 // no defaults for other paths
   136                 p = null;
   137         } else {
   138             p = new Path();
   139             for (File f: path)
   140                 p.addFile(f, warn); // TODO: is use of warn appropriate?
   141         }
   142         pathsForLocation.put(location, p);
   143     }
   145     protected void lazy() {
   146         if (!inited) {
   147             warn = lint.isEnabled(Lint.LintCategory.PATH);
   149             pathsForLocation.put(PLATFORM_CLASS_PATH, computeBootClassPath());
   150             pathsForLocation.put(CLASS_PATH, computeUserClassPath());
   151             pathsForLocation.put(SOURCE_PATH, computeSourcePath());
   153             inited = true;
   154         }
   155     }
   157     public Collection<File> bootClassPath() {
   158         lazy();
   159         return Collections.unmodifiableCollection(getPathForLocation(PLATFORM_CLASS_PATH));
   160     }
   161     public Collection<File> userClassPath() {
   162         lazy();
   163         return Collections.unmodifiableCollection(getPathForLocation(CLASS_PATH));
   164     }
   165     public Collection<File> sourcePath() {
   166         lazy();
   167         Path p = getPathForLocation(SOURCE_PATH);
   168         return p == null || p.size() == 0
   169             ? null
   170             : Collections.unmodifiableCollection(p);
   171     }
   173     boolean isBootClassPathRtJar(File file) {
   174         return file.equals(bootClassPathRtJar);
   175     }
   177     /**
   178      * Split a path into its elements. Empty path elements will be ignored.
   179      * @param path The path to be split
   180      * @return The elements of the path
   181      */
   182     private static Iterable<File> getPathEntries(String path) {
   183         return getPathEntries(path, null);
   184     }
   186     /**
   187      * Split a path into its elements. If emptyPathDefault is not null, all
   188      * empty elements in the path, including empty elements at either end of
   189      * the path, will be replaced with the value of emptyPathDefault.
   190      * @param path The path to be split
   191      * @param emptyPathDefault The value to substitute for empty path elements,
   192      *  or null, to ignore empty path elements
   193      * @return The elements of the path
   194      */
   195     private static Iterable<File> getPathEntries(String path, File emptyPathDefault) {
   196         ListBuffer<File> entries = new ListBuffer<File>();
   197         int start = 0;
   198         while (start <= path.length()) {
   199             int sep = path.indexOf(File.pathSeparatorChar, start);
   200             if (sep == -1)
   201                 sep = path.length();
   202             if (start < sep)
   203                 entries.add(new File(path.substring(start, sep)));
   204             else if (emptyPathDefault != null)
   205                 entries.add(emptyPathDefault);
   206             start = sep + 1;
   207         }
   208         return entries;
   209     }
   211     private class Path extends LinkedHashSet<File> {
   212         private static final long serialVersionUID = 0;
   214         private boolean expandJarClassPaths = false;
   215         private Set<File> canonicalValues = new HashSet<File>();
   217         public Path expandJarClassPaths(boolean x) {
   218             expandJarClassPaths = x;
   219             return this;
   220         }
   222         /** What to use when path element is the empty string */
   223         private File emptyPathDefault = null;
   225         public Path emptyPathDefault(File x) {
   226             emptyPathDefault = x;
   227             return this;
   228         }
   230         public Path() { super(); }
   232         public Path addDirectories(String dirs, boolean warn) {
   233             if (dirs != null)
   234                 for (File dir : getPathEntries(dirs))
   235                     addDirectory(dir, warn);
   236             return this;
   237         }
   239         public Path addDirectories(String dirs) {
   240             return addDirectories(dirs, warn);
   241         }
   243         private void addDirectory(File dir, boolean warn) {
   244             if (!dir.isDirectory()) {
   245                 if (warn)
   246                     log.warning("dir.path.element.not.found", dir);
   247                 return;
   248             }
   250             File[] files = dir.listFiles();
   251             if (files == null)
   252                 return;
   254             for (File direntry : files) {
   255                 if (isArchive(direntry))
   256                     addFile(direntry, warn);
   257             }
   258         }
   260         public Path addFiles(String files, boolean warn) {
   261             if (files != null)
   262                 for (File file : getPathEntries(files, emptyPathDefault))
   263                     addFile(file, warn);
   264             return this;
   265         }
   267         public Path addFiles(String files) {
   268             return addFiles(files, warn);
   269         }
   271         public void addFile(File file, boolean warn) {
   272             File canonFile = fsInfo.getCanonicalFile(file);
   273             if (contains(file) || canonicalValues.contains(canonFile)) {
   274                 /* Discard duplicates and avoid infinite recursion */
   275                 return;
   276             }
   278             if (! fsInfo.exists(file)) {
   279                 /* No such file or directory exists */
   280                 if (warn)
   281                     log.warning("path.element.not.found", file);
   282             } else if (fsInfo.isFile(file)) {
   283                 /* File is an ordinary file. */
   284                 if (!isArchive(file)) {
   285                     /* Not a recognized extension; open it to see if
   286                      it looks like a valid zip file. */
   287                     try {
   288                         ZipFile z = new ZipFile(file);
   289                         z.close();
   290                         if (warn)
   291                             log.warning("unexpected.archive.file", file);
   292                     } catch (IOException e) {
   293                         // FIXME: include e.getLocalizedMessage in warning
   294                         if (warn)
   295                             log.warning("invalid.archive.file", file);
   296                         return;
   297                     }
   298                 }
   299             }
   301             /* Now what we have left is either a directory or a file name
   302                confirming to archive naming convention */
   303             super.add(file);
   304             canonicalValues.add(canonFile);
   306             if (expandJarClassPaths && fsInfo.exists(file) && fsInfo.isFile(file))
   307                 addJarClassPath(file, warn);
   308         }
   310         // Adds referenced classpath elements from a jar's Class-Path
   311         // Manifest entry.  In some future release, we may want to
   312         // update this code to recognize URLs rather than simple
   313         // filenames, but if we do, we should redo all path-related code.
   314         private void addJarClassPath(File jarFile, boolean warn) {
   315             try {
   316                 for (File f: fsInfo.getJarClassPath(jarFile)) {
   317                     addFile(f, warn);
   318                 }
   319             } catch (IOException e) {
   320                 log.error("error.reading.file", jarFile, e.getLocalizedMessage());
   321             }
   322         }
   323     }
   325     private Path computeBootClassPath() {
   326         bootClassPathRtJar = null;
   327         String optionValue;
   328         Path path = new Path();
   330         path.addFiles(options.get(XBOOTCLASSPATH_PREPEND));
   332         if ((optionValue = options.get(ENDORSEDDIRS)) != null)
   333             path.addDirectories(optionValue);
   334         else
   335             path.addDirectories(System.getProperty("java.endorsed.dirs"), false);
   337         if ((optionValue = options.get(BOOTCLASSPATH)) != null) {
   338             path.addFiles(optionValue);
   339         } else {
   340             // Standard system classes for this compiler's release.
   341             String files = System.getProperty("sun.boot.class.path");
   342             path.addFiles(files, false);
   343             File rt_jar = new File("rt.jar");
   344             for (File file : getPathEntries(files)) {
   345                 if (new File(file.getName()).equals(rt_jar))
   346                     bootClassPathRtJar = file;
   347             }
   348         }
   350         path.addFiles(options.get(XBOOTCLASSPATH_APPEND));
   352         // Strictly speaking, standard extensions are not bootstrap
   353         // classes, but we treat them identically, so we'll pretend
   354         // that they are.
   355         if ((optionValue = options.get(EXTDIRS)) != null)
   356             path.addDirectories(optionValue);
   357         else
   358             path.addDirectories(System.getProperty("java.ext.dirs"), false);
   360         return path;
   361     }
   363     private Path computeUserClassPath() {
   364         String cp = options.get(CLASSPATH);
   366         // CLASSPATH environment variable when run from `javac'.
   367         if (cp == null) cp = System.getProperty("env.class.path");
   369         // If invoked via a java VM (not the javac launcher), use the
   370         // platform class path
   371         if (cp == null && System.getProperty("application.home") == null)
   372             cp = System.getProperty("java.class.path");
   374         // Default to current working directory.
   375         if (cp == null) cp = ".";
   377         return new Path()
   378             .expandJarClassPaths(true)        // Only search user jars for Class-Paths
   379             .emptyPathDefault(new File("."))  // Empty path elt ==> current directory
   380             .addFiles(cp);
   381     }
   383     private Path computeSourcePath() {
   384         String sourcePathArg = options.get(SOURCEPATH);
   385         if (sourcePathArg == null)
   386             return null;
   388         return new Path().addFiles(sourcePathArg);
   389     }
   391     private Path computeAnnotationProcessorPath() {
   392         String processorPathArg = options.get(PROCESSORPATH);
   393         if (processorPathArg == null)
   394             return null;
   396         return new Path().addFiles(processorPathArg);
   397     }
   399     /** The actual effective locations searched for sources */
   400     private Path sourceSearchPath;
   402     public Collection<File> sourceSearchPath() {
   403         if (sourceSearchPath == null) {
   404             lazy();
   405             Path sourcePath = getPathForLocation(SOURCE_PATH);
   406             Path userClassPath = getPathForLocation(CLASS_PATH);
   407             sourceSearchPath = sourcePath != null ? sourcePath : userClassPath;
   408         }
   409         return Collections.unmodifiableCollection(sourceSearchPath);
   410     }
   412     /** The actual effective locations searched for classes */
   413     private Path classSearchPath;
   415     public Collection<File> classSearchPath() {
   416         if (classSearchPath == null) {
   417             lazy();
   418             Path bootClassPath = getPathForLocation(PLATFORM_CLASS_PATH);
   419             Path userClassPath = getPathForLocation(CLASS_PATH);
   420             classSearchPath = new Path();
   421             classSearchPath.addAll(bootClassPath);
   422             classSearchPath.addAll(userClassPath);
   423         }
   424         return Collections.unmodifiableCollection(classSearchPath);
   425     }
   427     /** The actual effective locations for non-source, non-class files */
   428     private Path otherSearchPath;
   430     Collection<File> otherSearchPath() {
   431         if (otherSearchPath == null) {
   432             lazy();
   433             Path userClassPath = getPathForLocation(CLASS_PATH);
   434             Path sourcePath = getPathForLocation(SOURCE_PATH);
   435             if (sourcePath == null)
   436                 otherSearchPath = userClassPath;
   437             else {
   438                 otherSearchPath = new Path();
   439                 otherSearchPath.addAll(userClassPath);
   440                 otherSearchPath.addAll(sourcePath);
   441             }
   442         }
   443         return Collections.unmodifiableCollection(otherSearchPath);
   444     }
   446     /** Is this the name of an archive file? */
   447     private boolean isArchive(File file) {
   448         String n = file.getName().toLowerCase();
   449         return fsInfo.isFile(file)
   450             && (n.endsWith(".jar") || n.endsWith(".zip"));
   451     }
   452 }

mercurial