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

Sat, 01 Dec 2007 00:00:00 +0000

author
duke
date
Sat, 01 Dec 2007 00:00:00 +0000
changeset 1
9a66ca7c79fa
child 14
58039502942e
permissions
-rw-r--r--

Initial load

     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.ZipException;
    42 import java.util.zip.ZipFile;
    43 import com.sun.tools.javac.code.Lint;
    44 import com.sun.tools.javac.util.Context;
    45 import com.sun.tools.javac.util.Log;
    46 import com.sun.tools.javac.util.Options;
    47 import com.sun.tools.javac.util.Position;
    48 import java.util.ArrayList;
    49 import java.util.concurrent.ConcurrentHashMap;
    50 import java.util.concurrent.locks.Lock;
    51 import java.util.concurrent.locks.ReentrantLock;
    52 import javax.tools.JavaFileManager.Location;
    54 import static com.sun.tools.javac.main.OptionName.*;
    55 import static javax.tools.StandardLocation.*;
    57 /** This class converts command line arguments, environment variables
    58  *  and system properties (in File.pathSeparator-separated String form)
    59  *  into a boot class path, user class path, and source path (in
    60  *  Collection<String> form).
    61  *
    62  *  <p><b>This is NOT part of any API supported by Sun Microsystems.  If
    63  *  you write code that depends on this, you do so at your own risk.
    64  *  This code and its internal interfaces are subject to change or
    65  *  deletion without notice.</b>
    66  */
    67 public class Paths {
    69     /** The context key for the todo list */
    70     protected static final Context.Key<Paths> pathsKey =
    71         new Context.Key<Paths>();
    73     /** Get the Paths instance for this context. */
    74     public static Paths instance(Context context) {
    75         Paths instance = context.get(pathsKey);
    76         if (instance == null)
    77             instance = new Paths(context);
    78         return instance;
    79     }
    81     /** The log to use for warning output */
    82     private Log log;
    84     /** Collection of command-line options */
    85     private Options options;
    87     /** Handler for -Xlint options */
    88     private Lint lint;
    90     private static boolean NON_BATCH_MODE = System.getProperty("nonBatchMode") != null;// TODO: Use -XD compiler switch for this.
    91     private static Map<File, PathEntry> pathExistanceCache = new ConcurrentHashMap<File, PathEntry>();
    92     private static Map<File, java.util.List<String>> manifestEntries = new ConcurrentHashMap<File, java.util.List<String>>();
    93     private static Map<File, Boolean> isDirectory = new ConcurrentHashMap<File, Boolean>();
    94     private static Lock lock = new ReentrantLock();
    96     public static void clearPathExistanceCache() {
    97             pathExistanceCache.clear();
    98     }
   100     static class PathEntry {
   101         boolean exists = false;
   102         boolean isFile = false;
   103         File cannonicalPath = null;
   104     }
   106     protected Paths(Context context) {
   107         context.put(pathsKey, this);
   108         pathsForLocation = new HashMap<Location,Path>(16);
   109         setContext(context);
   110     }
   112     void setContext(Context context) {
   113         log = Log.instance(context);
   114         options = Options.instance(context);
   115         lint = Lint.instance(context);
   116     }
   118     /** Whether to warn about non-existent path elements */
   119     private boolean warn;
   121     private Map<Location, Path> pathsForLocation;
   123     private boolean inited = false; // TODO? caching bad?
   125     /**
   126      * rt.jar as found on the default bootclass path.  If the user specified a
   127      * bootclasspath, null is used.
   128      */
   129     private File bootClassPathRtJar = null;
   131     Path getPathForLocation(Location location) {
   132         Path path = pathsForLocation.get(location);
   133         if (path == null)
   134             setPathForLocation(location, null);
   135         return pathsForLocation.get(location);
   136     }
   138     void setPathForLocation(Location location, Iterable<? extends File> path) {
   139         // TODO? if (inited) throw new IllegalStateException
   140         // TODO: otherwise reset sourceSearchPath, classSearchPath as needed
   141         Path p;
   142         if (path == null) {
   143             if (location == CLASS_PATH)
   144                 p = computeUserClassPath();
   145             else if (location == PLATFORM_CLASS_PATH)
   146                 p = computeBootClassPath();
   147             else if (location == ANNOTATION_PROCESSOR_PATH)
   148                 p = computeAnnotationProcessorPath();
   149             else if (location == SOURCE_PATH)
   150                 p = computeSourcePath();
   151             else
   152                 // no defaults for other paths
   153                 p = null;
   154         } else {
   155             p = new Path();
   156             for (File f: path)
   157                 p.addFile(f, warn); // TODO: is use of warn appropriate?
   158         }
   159         pathsForLocation.put(location, p);
   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 isBootClassPathRtJar(File file) {
   191         return file.equals(bootClassPathRtJar);
   192     }
   194     private static class PathIterator implements Iterable<String> {
   195         private int pos = 0;
   196         private final String path;
   197         private final String emptyPathDefault;
   199         public PathIterator(String path, String emptyPathDefault) {
   200             this.path = path;
   201             this.emptyPathDefault = emptyPathDefault;
   202         }
   203         public PathIterator(String path) { this(path, null); }
   204         public Iterator<String> iterator() {
   205             return new Iterator<String>() {
   206                 public boolean hasNext() {
   207                     return pos <= path.length();
   208                 }
   209                 public String next() {
   210                     int beg = pos;
   211                     int end = path.indexOf(File.pathSeparator, beg);
   212                     if (end == -1)
   213                         end = path.length();
   214                     pos = end + 1;
   216                     if (beg == end && emptyPathDefault != null)
   217                         return emptyPathDefault;
   218                     else
   219                         return path.substring(beg, end);
   220                 }
   221                 public void remove() {
   222                     throw new UnsupportedOperationException();
   223                 }
   224             };
   225         }
   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 String emptyPathDefault = null;
   242         public Path emptyPathDefault(String 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 (String dir : new PathIterator(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(String dir, boolean warn) {
   261             if (! new File(dir).isDirectory()) {
   262                 if (warn)
   263                     log.warning("dir.path.element.not.found", dir);
   264                 return;
   265             }
   267             File[] files = new File(dir).listFiles();
   268             if (files == null)
   269                 return;
   271             for (File direntry : files) {
   272                 if (isArchive(direntry))
   273                     addFile(direntry, warn);
   274             }
   275         }
   277         public Path addFiles(String files, boolean warn) {
   278             if (files != null)
   279                 for (String file : new PathIterator(files, emptyPathDefault))
   280                     addFile(file, warn);
   281             return this;
   282         }
   284         public Path addFiles(String files) {
   285             return addFiles(files, warn);
   286         }
   288         public Path addFile(String file, boolean warn) {
   289             addFile(new File(file), warn);
   290             return this;
   291         }
   293         public void addFile(File file, boolean warn) {
   294             boolean foundInCache = false;
   295             PathEntry pe = null;
   296             if (!NON_BATCH_MODE) {
   297                     pe = pathExistanceCache.get(file);
   298                     if (pe != null) {
   299                         foundInCache = true;
   300                     }
   301                     else {
   302                         pe = new PathEntry();
   303                     }
   304             }
   305             else {
   306                 pe = new PathEntry();
   307             }
   309             File canonFile;
   310             try {
   311                 if (!foundInCache) {
   312                     pe.cannonicalPath = file.getCanonicalFile();
   313                 }
   314                 else {
   315                    canonFile = pe.cannonicalPath;
   316                 }
   317             } catch (IOException e) {
   318                 pe.cannonicalPath = canonFile = file;
   319             }
   321             if (contains(file) || canonicalValues.contains(pe.cannonicalPath)) {
   322                 /* Discard duplicates and avoid infinite recursion */
   323                 return;
   324             }
   326             if (!foundInCache) {
   327                 pe.exists = file.exists();
   328                 pe.isFile = file.isFile();
   329                 if (!NON_BATCH_MODE) {
   330                     pathExistanceCache.put(file, pe);
   331                 }
   332             }
   334             if (! pe.exists) {
   335                 /* No such file or directory exists */
   336                 if (warn)
   337                     log.warning("path.element.not.found", file);
   338             } else if (pe.isFile) {
   339                 /* File is an ordinary file. */
   340                 if (!isArchive(file)) {
   341                     /* Not a recognized extension; open it to see if
   342                      it looks like a valid zip file. */
   343                     try {
   344                         ZipFile z = new ZipFile(file);
   345                         z.close();
   346                         if (warn)
   347                             log.warning("unexpected.archive.file", file);
   348                     } catch (IOException e) {
   349                         // FIXME: include e.getLocalizedMessage in warning
   350                         if (warn)
   351                             log.warning("invalid.archive.file", file);
   352                         return;
   353                     }
   354                 }
   355             }
   357             /* Now what we have left is either a directory or a file name
   358                confirming to archive naming convention */
   359             super.add(file);
   360             canonicalValues.add(pe.cannonicalPath);
   362             if (expandJarClassPaths && file.exists() && file.isFile())
   363                 addJarClassPath(file, warn);
   364         }
   366         // Adds referenced classpath elements from a jar's Class-Path
   367         // Manifest entry.  In some future release, we may want to
   368         // update this code to recognize URLs rather than simple
   369         // filenames, but if we do, we should redo all path-related code.
   370         private void addJarClassPath(File jarFile, boolean warn) {
   371             try {
   372                 java.util.List<String> manifestsList = manifestEntries.get(jarFile);
   373                 if (!NON_BATCH_MODE) {
   374                     lock.lock();
   375                     try {
   376                         if (manifestsList != null) {
   377                             for (String entr : manifestsList) {
   378                                 addFile(new File(entr), warn);
   379                             }
   380                             return;
   381                         }
   382                     }
   383                     finally {
   384                         lock.unlock();
   385                     }
   386                 }
   388                 if (!NON_BATCH_MODE) {
   389                     manifestsList = new ArrayList<String>();
   390                     manifestEntries.put(jarFile, manifestsList);
   391                 }
   393                 String jarParent = jarFile.getParent();
   394                 JarFile jar = new JarFile(jarFile);
   396                 try {
   397                     Manifest man = jar.getManifest();
   398                     if (man == null) return;
   400                     Attributes attr = man.getMainAttributes();
   401                     if (attr == null) return;
   403                     String path = attr.getValue(Attributes.Name.CLASS_PATH);
   404                     if (path == null) return;
   406                     for (StringTokenizer st = new StringTokenizer(path);
   407                          st.hasMoreTokens();) {
   408                         String elt = st.nextToken();
   409                         File f = (jarParent == null ? new File(elt) : new File(jarParent, elt));
   410                         addFile(f, warn);
   412                         if (!NON_BATCH_MODE) {
   413                             lock.lock();
   414                             try {
   415                                 manifestsList.add(elt);
   416                             }
   417                             finally {
   418                                 lock.unlock();
   419                             }
   420                         }
   421                     }
   422                 } finally {
   423                     jar.close();
   424                 }
   425             } catch (IOException e) {
   426                 log.error("error.reading.file", jarFile, e.getLocalizedMessage());
   427             }
   428         }
   429     }
   431     private Path computeBootClassPath() {
   432         bootClassPathRtJar = null;
   433         String optionValue;
   434         Path path = new Path();
   436         path.addFiles(options.get(XBOOTCLASSPATH_PREPEND));
   438         if ((optionValue = options.get(ENDORSEDDIRS)) != null)
   439             path.addDirectories(optionValue);
   440         else
   441             path.addDirectories(System.getProperty("java.endorsed.dirs"), false);
   443         if ((optionValue = options.get(BOOTCLASSPATH)) != null) {
   444             path.addFiles(optionValue);
   445         } else {
   446             // Standard system classes for this compiler's release.
   447             String files = System.getProperty("sun.boot.class.path");
   448             path.addFiles(files, false);
   449             File rt_jar = new File("rt.jar");
   450             for (String file : new PathIterator(files, null)) {
   451                 File f = new File(file);
   452                 if (new File(f.getName()).equals(rt_jar))
   453                     bootClassPathRtJar = f;
   454             }
   455         }
   457         path.addFiles(options.get(XBOOTCLASSPATH_APPEND));
   459         // Strictly speaking, standard extensions are not bootstrap
   460         // classes, but we treat them identically, so we'll pretend
   461         // that they are.
   462         if ((optionValue = options.get(EXTDIRS)) != null)
   463             path.addDirectories(optionValue);
   464         else
   465             path.addDirectories(System.getProperty("java.ext.dirs"), false);
   467         return path;
   468     }
   470     private Path computeUserClassPath() {
   471         String cp = options.get(CLASSPATH);
   473         // CLASSPATH environment variable when run from `javac'.
   474         if (cp == null) cp = System.getProperty("env.class.path");
   476         // If invoked via a java VM (not the javac launcher), use the
   477         // platform class path
   478         if (cp == null && System.getProperty("application.home") == null)
   479             cp = System.getProperty("java.class.path");
   481         // Default to current working directory.
   482         if (cp == null) cp = ".";
   484         return new Path()
   485             .expandJarClassPaths(true) // Only search user jars for Class-Paths
   486             .emptyPathDefault(".")     // Empty path elt ==> current directory
   487             .addFiles(cp);
   488     }
   490     private Path computeSourcePath() {
   491         String sourcePathArg = options.get(SOURCEPATH);
   492         if (sourcePathArg == null)
   493             return null;
   495         return new Path().addFiles(sourcePathArg);
   496     }
   498     private Path computeAnnotationProcessorPath() {
   499         String processorPathArg = options.get(PROCESSORPATH);
   500         if (processorPathArg == null)
   501             return null;
   503         return new Path().addFiles(processorPathArg);
   504     }
   506     /** The actual effective locations searched for sources */
   507     private Path sourceSearchPath;
   509     public Collection<File> sourceSearchPath() {
   510         if (sourceSearchPath == null) {
   511             lazy();
   512             Path sourcePath = getPathForLocation(SOURCE_PATH);
   513             Path userClassPath = getPathForLocation(CLASS_PATH);
   514             sourceSearchPath = sourcePath != null ? sourcePath : userClassPath;
   515         }
   516         return Collections.unmodifiableCollection(sourceSearchPath);
   517     }
   519     /** The actual effective locations searched for classes */
   520     private Path classSearchPath;
   522     public Collection<File> classSearchPath() {
   523         if (classSearchPath == null) {
   524             lazy();
   525             Path bootClassPath = getPathForLocation(PLATFORM_CLASS_PATH);
   526             Path userClassPath = getPathForLocation(CLASS_PATH);
   527             classSearchPath = new Path();
   528             classSearchPath.addAll(bootClassPath);
   529             classSearchPath.addAll(userClassPath);
   530         }
   531         return Collections.unmodifiableCollection(classSearchPath);
   532     }
   534     /** The actual effective locations for non-source, non-class files */
   535     private Path otherSearchPath;
   537     Collection<File> otherSearchPath() {
   538         if (otherSearchPath == null) {
   539             lazy();
   540             Path userClassPath = getPathForLocation(CLASS_PATH);
   541             Path sourcePath = getPathForLocation(SOURCE_PATH);
   542             if (sourcePath == null)
   543                 otherSearchPath = userClassPath;
   544             else {
   545                 otherSearchPath = new Path();
   546                 otherSearchPath.addAll(userClassPath);
   547                 otherSearchPath.addAll(sourcePath);
   548             }
   549         }
   550         return Collections.unmodifiableCollection(otherSearchPath);
   551     }
   553     /** Is this the name of an archive file? */
   554     private static boolean isArchive(File file) {
   555         String n = file.getName().toLowerCase();
   556         boolean isFile = false;
   557         if (!NON_BATCH_MODE) {
   558             Boolean isf = isDirectory.get(file);
   559             if (isf == null) {
   560                 isFile = file.isFile();
   561                 isDirectory.put(file, isFile);
   562             }
   563             else {
   564                 isFile = isf;
   565             }
   566         }
   567         else {
   568             isFile = file.isFile();
   569         }
   571         return isFile
   572             && (n.endsWith(".jar") || n.endsWith(".zip"));
   573     }
   574 }

mercurial