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

Fri, 14 Mar 2008 16:09:30 -0700

author
jjg
date
Fri, 14 Mar 2008 16:09:30 -0700
changeset 14
58039502942e
parent 1
9a66ca7c79fa
permissions
-rw-r--r--

6638501: Regression with Javac in JDK6 U4 b03?
Summary: replace some String paths with File paths in Paths.java
Reviewed-by: ksrini

     1 /*
     2  * Copyright 2003-2006 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.util;
    27 import java.io.File;
    28 import java.io.IOException;
    29 import java.util.HashMap;
    30 import java.util.HashSet;
    31 import java.util.Map;
    32 import java.util.Set;
    33 import java.util.jar.JarFile;
    34 import java.util.jar.Manifest;
    35 import java.util.jar.Attributes;
    36 import java.util.Collection;
    37 import java.util.Collections;
    38 import java.util.LinkedHashSet;
    39 import java.util.Iterator;
    40 import java.util.StringTokenizer;
    41 import java.util.zip.ZipFile;
    42 import com.sun.tools.javac.code.Lint;
    43 import java.util.ArrayList;
    44 import java.util.concurrent.ConcurrentHashMap;
    45 import java.util.concurrent.locks.Lock;
    46 import java.util.concurrent.locks.ReentrantLock;
    47 import javax.tools.JavaFileManager.Location;
    49 import static com.sun.tools.javac.main.OptionName.*;
    50 import static javax.tools.StandardLocation.*;
    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 API supported by Sun Microsystems.  If
    58  *  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     private static boolean NON_BATCH_MODE = System.getProperty("nonBatchMode") != null;// TODO: Use -XD compiler switch for this.
    89     private static Map<File, PathEntry> pathExistanceCache = new ConcurrentHashMap<File, PathEntry>();
    90     private static Map<File, java.util.List<File>> manifestEntries = new ConcurrentHashMap<File, java.util.List<File>>();
    91     private static Map<File, Boolean> isDirectory = new ConcurrentHashMap<File, Boolean>();
    92     private static Lock lock = new ReentrantLock();
    94     public static void clearPathExistanceCache() {
    95             pathExistanceCache.clear();
    96     }
    98     static class PathEntry {
    99         boolean exists = false;
   100         boolean isFile = false;
   101         File cannonicalPath = null;
   102     }
   104     protected Paths(Context context) {
   105         context.put(pathsKey, this);
   106         pathsForLocation = new HashMap<Location,Path>(16);
   107         setContext(context);
   108     }
   110     void setContext(Context context) {
   111         log = Log.instance(context);
   112         options = Options.instance(context);
   113         lint = Lint.instance(context);
   114     }
   116     /** Whether to warn about non-existent path elements */
   117     private boolean warn;
   119     private Map<Location, Path> pathsForLocation;
   121     private boolean inited = false; // TODO? caching bad?
   123     /**
   124      * rt.jar as found on the default bootclass path.  If the user specified a
   125      * bootclasspath, null is used.
   126      */
   127     private File bootClassPathRtJar = null;
   129     Path getPathForLocation(Location location) {
   130         Path path = pathsForLocation.get(location);
   131         if (path == null)
   132             setPathForLocation(location, null);
   133         return pathsForLocation.get(location);
   134     }
   136     void setPathForLocation(Location location, Iterable<? extends File> path) {
   137         // TODO? if (inited) throw new IllegalStateException
   138         // TODO: otherwise reset sourceSearchPath, classSearchPath as needed
   139         Path p;
   140         if (path == null) {
   141             if (location == CLASS_PATH)
   142                 p = computeUserClassPath();
   143             else if (location == PLATFORM_CLASS_PATH)
   144                 p = computeBootClassPath();
   145             else if (location == ANNOTATION_PROCESSOR_PATH)
   146                 p = computeAnnotationProcessorPath();
   147             else if (location == SOURCE_PATH)
   148                 p = computeSourcePath();
   149             else
   150                 // no defaults for other paths
   151                 p = null;
   152         } else {
   153             p = new Path();
   154             for (File f: path)
   155                 p.addFile(f, warn); // TODO: is use of warn appropriate?
   156         }
   157         pathsForLocation.put(location, p);
   158     }
   160     protected void lazy() {
   161         if (!inited) {
   162             warn = lint.isEnabled(Lint.LintCategory.PATH);
   164             pathsForLocation.put(PLATFORM_CLASS_PATH, computeBootClassPath());
   165             pathsForLocation.put(CLASS_PATH, computeUserClassPath());
   166             pathsForLocation.put(SOURCE_PATH, computeSourcePath());
   168             inited = true;
   169         }
   170     }
   172     public Collection<File> bootClassPath() {
   173         lazy();
   174         return Collections.unmodifiableCollection(getPathForLocation(PLATFORM_CLASS_PATH));
   175     }
   176     public Collection<File> userClassPath() {
   177         lazy();
   178         return Collections.unmodifiableCollection(getPathForLocation(CLASS_PATH));
   179     }
   180     public Collection<File> sourcePath() {
   181         lazy();
   182         Path p = getPathForLocation(SOURCE_PATH);
   183         return p == null || p.size() == 0
   184             ? null
   185             : Collections.unmodifiableCollection(p);
   186     }
   188     boolean isBootClassPathRtJar(File file) {
   189         return file.equals(bootClassPathRtJar);
   190     }
   192     private static class PathIterator implements Iterable<String> {
   193         private int pos = 0;
   194         private final String path;
   195         private final String emptyPathDefault;
   197         public PathIterator(String path, String emptyPathDefault) {
   198             this.path = path;
   199             this.emptyPathDefault = emptyPathDefault;
   200         }
   201         public PathIterator(String path) { this(path, null); }
   202         public Iterator<String> iterator() {
   203             return new Iterator<String>() {
   204                 public boolean hasNext() {
   205                     return pos <= path.length();
   206                 }
   207                 public String next() {
   208                     int beg = pos;
   209                     int end = path.indexOf(File.pathSeparator, beg);
   210                     if (end == -1)
   211                         end = path.length();
   212                     pos = end + 1;
   214                     if (beg == end && emptyPathDefault != null)
   215                         return emptyPathDefault;
   216                     else
   217                         return path.substring(beg, end);
   218                 }
   219                 public void remove() {
   220                     throw new UnsupportedOperationException();
   221                 }
   222             };
   223         }
   224     }
   226     private class Path extends LinkedHashSet<File> {
   227         private static final long serialVersionUID = 0;
   229         private boolean expandJarClassPaths = false;
   230         private Set<File> canonicalValues = new HashSet<File>();
   232         public Path expandJarClassPaths(boolean x) {
   233             expandJarClassPaths = x;
   234             return this;
   235         }
   237         /** What to use when path element is the empty string */
   238         private String emptyPathDefault = null;
   240         public Path emptyPathDefault(String x) {
   241             emptyPathDefault = x;
   242             return this;
   243         }
   245         public Path() { super(); }
   247         public Path addDirectories(String dirs, boolean warn) {
   248             if (dirs != null)
   249                 for (String dir : new PathIterator(dirs))
   250                     addDirectory(dir, warn);
   251             return this;
   252         }
   254         public Path addDirectories(String dirs) {
   255             return addDirectories(dirs, warn);
   256         }
   258         private void addDirectory(String dir, boolean warn) {
   259             if (! new File(dir).isDirectory()) {
   260                 if (warn)
   261                     log.warning("dir.path.element.not.found", dir);
   262                 return;
   263             }
   265             File[] files = new File(dir).listFiles();
   266             if (files == null)
   267                 return;
   269             for (File direntry : files) {
   270                 if (isArchive(direntry))
   271                     addFile(direntry, warn);
   272             }
   273         }
   275         public Path addFiles(String files, boolean warn) {
   276             if (files != null)
   277                 for (String file : new PathIterator(files, emptyPathDefault))
   278                     addFile(file, warn);
   279             return this;
   280         }
   282         public Path addFiles(String files) {
   283             return addFiles(files, warn);
   284         }
   286         public Path addFile(String file, boolean warn) {
   287             addFile(new File(file), warn);
   288             return this;
   289         }
   291         public void addFile(File file, boolean warn) {
   292             boolean foundInCache = false;
   293             PathEntry pe = null;
   294             if (!NON_BATCH_MODE) {
   295                     pe = pathExistanceCache.get(file);
   296                     if (pe != null) {
   297                         foundInCache = true;
   298                     }
   299                     else {
   300                         pe = new PathEntry();
   301                     }
   302             }
   303             else {
   304                 pe = new PathEntry();
   305             }
   307             File canonFile;
   308             try {
   309                 if (!foundInCache) {
   310                     pe.cannonicalPath = file.getCanonicalFile();
   311                 }
   312                 else {
   313                    canonFile = pe.cannonicalPath;
   314                 }
   315             } catch (IOException e) {
   316                 pe.cannonicalPath = canonFile = file;
   317             }
   319             if (contains(file) || canonicalValues.contains(pe.cannonicalPath)) {
   320                 /* Discard duplicates and avoid infinite recursion */
   321                 return;
   322             }
   324             if (!foundInCache) {
   325                 pe.exists = file.exists();
   326                 pe.isFile = file.isFile();
   327                 if (!NON_BATCH_MODE) {
   328                     pathExistanceCache.put(file, pe);
   329                 }
   330             }
   332             if (! pe.exists) {
   333                 /* No such file or directory exists */
   334                 if (warn)
   335                     log.warning("path.element.not.found", file);
   336             } else if (pe.isFile) {
   337                 /* File is an ordinary file. */
   338                 if (!isArchive(file)) {
   339                     /* Not a recognized extension; open it to see if
   340                      it looks like a valid zip file. */
   341                     try {
   342                         ZipFile z = new ZipFile(file);
   343                         z.close();
   344                         if (warn)
   345                             log.warning("unexpected.archive.file", file);
   346                     } catch (IOException e) {
   347                         // FIXME: include e.getLocalizedMessage in warning
   348                         if (warn)
   349                             log.warning("invalid.archive.file", file);
   350                         return;
   351                     }
   352                 }
   353             }
   355             /* Now what we have left is either a directory or a file name
   356                confirming to archive naming convention */
   357             super.add(file);
   358             canonicalValues.add(pe.cannonicalPath);
   360             if (expandJarClassPaths && file.exists() && file.isFile())
   361                 addJarClassPath(file, warn);
   362         }
   364         // Adds referenced classpath elements from a jar's Class-Path
   365         // Manifest entry.  In some future release, we may want to
   366         // update this code to recognize URLs rather than simple
   367         // filenames, but if we do, we should redo all path-related code.
   368         private void addJarClassPath(File jarFile, boolean warn) {
   369             try {
   370                 java.util.List<File> manifestsList = manifestEntries.get(jarFile);
   371                 if (!NON_BATCH_MODE) {
   372                     lock.lock();
   373                     try {
   374                         if (manifestsList != null) {
   375                             for (File entr : manifestsList) {
   376                                 addFile(entr, warn);
   377                             }
   378                             return;
   379                         }
   380                     }
   381                     finally {
   382                         lock.unlock();
   383                     }
   384                 }
   386                 if (!NON_BATCH_MODE) {
   387                     manifestsList = new ArrayList<File>();
   388                     manifestEntries.put(jarFile, manifestsList);
   389                 }
   391                 String jarParent = jarFile.getParent();
   392                 JarFile jar = new JarFile(jarFile);
   394                 try {
   395                     Manifest man = jar.getManifest();
   396                     if (man == null) return;
   398                     Attributes attr = man.getMainAttributes();
   399                     if (attr == null) return;
   401                     String path = attr.getValue(Attributes.Name.CLASS_PATH);
   402                     if (path == null) return;
   404                     for (StringTokenizer st = new StringTokenizer(path);
   405                          st.hasMoreTokens();) {
   406                         String elt = st.nextToken();
   407                         File f = (jarParent == null ? new File(elt) : new File(jarParent, elt));
   408                         addFile(f, warn);
   410                         if (!NON_BATCH_MODE) {
   411                             lock.lock();
   412                             try {
   413                                 manifestsList.add(f);
   414                             }
   415                             finally {
   416                                 lock.unlock();
   417                             }
   418                         }
   419                     }
   420                 } finally {
   421                     jar.close();
   422                 }
   423             } catch (IOException e) {
   424                 log.error("error.reading.file", jarFile, e.getLocalizedMessage());
   425             }
   426         }
   427     }
   429     private Path computeBootClassPath() {
   430         bootClassPathRtJar = null;
   431         String optionValue;
   432         Path path = new Path();
   434         path.addFiles(options.get(XBOOTCLASSPATH_PREPEND));
   436         if ((optionValue = options.get(ENDORSEDDIRS)) != null)
   437             path.addDirectories(optionValue);
   438         else
   439             path.addDirectories(System.getProperty("java.endorsed.dirs"), false);
   441         if ((optionValue = options.get(BOOTCLASSPATH)) != null) {
   442             path.addFiles(optionValue);
   443         } else {
   444             // Standard system classes for this compiler's release.
   445             String files = System.getProperty("sun.boot.class.path");
   446             path.addFiles(files, false);
   447             File rt_jar = new File("rt.jar");
   448             for (String file : new PathIterator(files, null)) {
   449                 File f = new File(file);
   450                 if (new File(f.getName()).equals(rt_jar))
   451                     bootClassPathRtJar = f;
   452             }
   453         }
   455         path.addFiles(options.get(XBOOTCLASSPATH_APPEND));
   457         // Strictly speaking, standard extensions are not bootstrap
   458         // classes, but we treat them identically, so we'll pretend
   459         // that they are.
   460         if ((optionValue = options.get(EXTDIRS)) != null)
   461             path.addDirectories(optionValue);
   462         else
   463             path.addDirectories(System.getProperty("java.ext.dirs"), false);
   465         return path;
   466     }
   468     private Path computeUserClassPath() {
   469         String cp = options.get(CLASSPATH);
   471         // CLASSPATH environment variable when run from `javac'.
   472         if (cp == null) cp = System.getProperty("env.class.path");
   474         // If invoked via a java VM (not the javac launcher), use the
   475         // platform class path
   476         if (cp == null && System.getProperty("application.home") == null)
   477             cp = System.getProperty("java.class.path");
   479         // Default to current working directory.
   480         if (cp == null) cp = ".";
   482         return new Path()
   483             .expandJarClassPaths(true) // Only search user jars for Class-Paths
   484             .emptyPathDefault(".")     // Empty path elt ==> current directory
   485             .addFiles(cp);
   486     }
   488     private Path computeSourcePath() {
   489         String sourcePathArg = options.get(SOURCEPATH);
   490         if (sourcePathArg == null)
   491             return null;
   493         return new Path().addFiles(sourcePathArg);
   494     }
   496     private Path computeAnnotationProcessorPath() {
   497         String processorPathArg = options.get(PROCESSORPATH);
   498         if (processorPathArg == null)
   499             return null;
   501         return new Path().addFiles(processorPathArg);
   502     }
   504     /** The actual effective locations searched for sources */
   505     private Path sourceSearchPath;
   507     public Collection<File> sourceSearchPath() {
   508         if (sourceSearchPath == null) {
   509             lazy();
   510             Path sourcePath = getPathForLocation(SOURCE_PATH);
   511             Path userClassPath = getPathForLocation(CLASS_PATH);
   512             sourceSearchPath = sourcePath != null ? sourcePath : userClassPath;
   513         }
   514         return Collections.unmodifiableCollection(sourceSearchPath);
   515     }
   517     /** The actual effective locations searched for classes */
   518     private Path classSearchPath;
   520     public Collection<File> classSearchPath() {
   521         if (classSearchPath == null) {
   522             lazy();
   523             Path bootClassPath = getPathForLocation(PLATFORM_CLASS_PATH);
   524             Path userClassPath = getPathForLocation(CLASS_PATH);
   525             classSearchPath = new Path();
   526             classSearchPath.addAll(bootClassPath);
   527             classSearchPath.addAll(userClassPath);
   528         }
   529         return Collections.unmodifiableCollection(classSearchPath);
   530     }
   532     /** The actual effective locations for non-source, non-class files */
   533     private Path otherSearchPath;
   535     Collection<File> otherSearchPath() {
   536         if (otherSearchPath == null) {
   537             lazy();
   538             Path userClassPath = getPathForLocation(CLASS_PATH);
   539             Path sourcePath = getPathForLocation(SOURCE_PATH);
   540             if (sourcePath == null)
   541                 otherSearchPath = userClassPath;
   542             else {
   543                 otherSearchPath = new Path();
   544                 otherSearchPath.addAll(userClassPath);
   545                 otherSearchPath.addAll(sourcePath);
   546             }
   547         }
   548         return Collections.unmodifiableCollection(otherSearchPath);
   549     }
   551     /** Is this the name of an archive file? */
   552     private static boolean isArchive(File file) {
   553         String n = file.getName().toLowerCase();
   554         boolean isFile = false;
   555         if (!NON_BATCH_MODE) {
   556             Boolean isf = isDirectory.get(file);
   557             if (isf == null) {
   558                 isFile = file.isFile();
   559                 isDirectory.put(file, isFile);
   560             }
   561             else {
   562                 isFile = isf;
   563             }
   564         }
   565         else {
   566             isFile = file.isFile();
   567         }
   569         return isFile
   570             && (n.endsWith(".jar") || n.endsWith(".zip"));
   571     }
   572 }

mercurial