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

Thu, 13 Jan 2011 11:48:10 -0800

author
jjg
date
Thu, 13 Jan 2011 11:48:10 -0800
changeset 818
d33d8c381aa1
parent 809
e63b1f8341ce
child 874
e0c16199b2e0
permissions
-rw-r--r--

6430241: Hard to disable symbol file feature through API
Reviewed-by: mcimadamore

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

mercurial