Home » openjdk-7 » com.sun.tools » javac » file » [javadoc | source]

    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.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;
   42   
   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;
   48   
   49   import static javax.tools.StandardLocation.*;
   50   import static com.sun.tools.javac.main.OptionName.*;
   51   
   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 {
   63   
   64       /** The context key for the todo list */
   65       protected static final Context.Key<Paths> pathsKey =
   66           new Context.Key<Paths>();
   67   
   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       }
   78   
   79       /** The log to use for warning output */
   80       private Log log;
   81   
   82       /** Collection of command-line options */
   83       private Options options;
   84   
   85       /** Handler for -Xlint options */
   86       private Lint lint;
   87   
   88       /** Access to (possibly cached) file info */
   89       private FSInfo fsInfo;
   90   
   91       protected Paths(Context context) {
   92           context.put(pathsKey, this);
   93           pathsForLocation = new HashMap<Location,Path>(16);
   94           setContext(context);
   95       }
   96   
   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       }
  103   
  104       /** Whether to warn about non-existent path elements */
  105       private boolean warn;
  106   
  107       private Map<Location, Path> pathsForLocation;
  108   
  109       private boolean inited = false; // TODO? caching bad?
  110   
  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;
  116   
  117       /**
  118        *  Is bootclasspath the default?
  119        */
  120       private boolean isDefaultBootClassPath;
  121   
  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       }
  128   
  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       }
  156   
  157       public boolean isDefaultBootClassPath() {
  158           lazy();
  159           return isDefaultBootClassPath;
  160       }
  161   
  162       protected void lazy() {
  163           if (!inited) {
  164               warn = lint.isEnabled(Lint.LintCategory.PATH);
  165   
  166               pathsForLocation.put(PLATFORM_CLASS_PATH, computeBootClassPath());
  167               pathsForLocation.put(CLASS_PATH, computeUserClassPath());
  168               pathsForLocation.put(SOURCE_PATH, computeSourcePath());
  169   
  170               inited = true;
  171           }
  172       }
  173   
  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       }
  189   
  190       boolean isDefaultBootClassPathRtJar(File file) {
  191           return file.equals(defaultBootClassPathRtJar);
  192       }
  193   
  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       }
  202   
  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       }
  227   
  228       private class Path extends LinkedHashSet<File> {
  229           private static final long serialVersionUID = 0;
  230   
  231           private boolean expandJarClassPaths = false;
  232           private Set<File> canonicalValues = new HashSet<File>();
  233   
  234           public Path expandJarClassPaths(boolean x) {
  235               expandJarClassPaths = x;
  236               return this;
  237           }
  238   
  239           /** What to use when path element is the empty string */
  240           private File emptyPathDefault = null;
  241   
  242           public Path emptyPathDefault(File x) {
  243               emptyPathDefault = x;
  244               return this;
  245           }
  246   
  247           public Path() { super(); }
  248   
  249           public Path addDirectories(String dirs, boolean warn) {
  250               boolean prev = expandJarClassPaths;
  251               expandJarClassPaths = true;
  252               try {
  253                   if (dirs != null)
  254                       for (File dir : getPathEntries(dirs))
  255                           addDirectory(dir, warn);
  256                   return this;
  257               } finally {
  258                   expandJarClassPaths = prev;
  259               }
  260           }
  261   
  262           public Path addDirectories(String dirs) {
  263               return addDirectories(dirs, warn);
  264           }
  265   
  266           private void addDirectory(File dir, boolean warn) {
  267               if (!dir.isDirectory()) {
  268                   if (warn)
  269                       log.warning(Lint.LintCategory.PATH,
  270                               "dir.path.element.not.found", dir);
  271                   return;
  272               }
  273   
  274               File[] files = dir.listFiles();
  275               if (files == null)
  276                   return;
  277   
  278               for (File direntry : files) {
  279                   if (isArchive(direntry))
  280                       addFile(direntry, warn);
  281               }
  282           }
  283   
  284           public Path addFiles(String files, boolean warn) {
  285               if (files != null) {
  286                   for (File file : getPathEntries(files, emptyPathDefault))
  287                       addFile(file, warn);
  288               }
  289               return this;
  290           }
  291   
  292           public Path addFiles(String files) {
  293               return addFiles(files, warn);
  294           }
  295   
  296           public void addFile(File file, boolean warn) {
  297               if (contains(file)) {
  298                   // discard duplicates
  299                   return;
  300               }
  301   
  302               if (! fsInfo.exists(file)) {
  303                   /* No such file or directory exists */
  304                   if (warn) {
  305                       log.warning(Lint.LintCategory.PATH,
  306                               "path.element.not.found", file);
  307                   }
  308                   super.add(file);
  309                   return;
  310               }
  311   
  312               File canonFile = fsInfo.getCanonicalFile(file);
  313               if (canonicalValues.contains(canonFile)) {
  314                   /* Discard duplicates and avoid infinite recursion */
  315                   return;
  316               }
  317   
  318               if (fsInfo.isFile(file)) {
  319                   /* File is an ordinary file. */
  320                   if (!isArchive(file)) {
  321                       /* Not a recognized extension; open it to see if
  322                        it looks like a valid zip file. */
  323                       try {
  324                           ZipFile z = new ZipFile(file);
  325                           z.close();
  326                           if (warn) {
  327                               log.warning(Lint.LintCategory.PATH,
  328                                       "unexpected.archive.file", file);
  329                           }
  330                       } catch (IOException e) {
  331                           // FIXME: include e.getLocalizedMessage in warning
  332                           if (warn) {
  333                               log.warning(Lint.LintCategory.PATH,
  334                                       "invalid.archive.file", file);
  335                           }
  336                           return;
  337                       }
  338                   }
  339               }
  340   
  341               /* Now what we have left is either a directory or a file name
  342                  conforming to archive naming convention */
  343               super.add(file);
  344               canonicalValues.add(canonFile);
  345   
  346               if (expandJarClassPaths && fsInfo.isFile(file))
  347                   addJarClassPath(file, warn);
  348           }
  349   
  350           // Adds referenced classpath elements from a jar's Class-Path
  351           // Manifest entry.  In some future release, we may want to
  352           // update this code to recognize URLs rather than simple
  353           // filenames, but if we do, we should redo all path-related code.
  354           private void addJarClassPath(File jarFile, boolean warn) {
  355               try {
  356                   for (File f: fsInfo.getJarClassPath(jarFile)) {
  357                       addFile(f, warn);
  358                   }
  359               } catch (IOException e) {
  360                   log.error("error.reading.file", jarFile, JavacFileManager.getMessage(e));
  361               }
  362           }
  363       }
  364   
  365       private Path computeBootClassPath() {
  366           defaultBootClassPathRtJar = null;
  367           Path path = new Path();
  368   
  369           String bootclasspathOpt = options.get(BOOTCLASSPATH);
  370           String endorseddirsOpt = options.get(ENDORSEDDIRS);
  371           String extdirsOpt = options.get(EXTDIRS);
  372           String xbootclasspathPrependOpt = options.get(XBOOTCLASSPATH_PREPEND);
  373           String xbootclasspathAppendOpt = options.get(XBOOTCLASSPATH_APPEND);
  374   
  375           path.addFiles(xbootclasspathPrependOpt);
  376   
  377           if (endorseddirsOpt != null)
  378               path.addDirectories(endorseddirsOpt);
  379           else
  380               path.addDirectories(System.getProperty("java.endorsed.dirs"), false);
  381   
  382           if (bootclasspathOpt != null) {
  383               path.addFiles(bootclasspathOpt);
  384           } else {
  385               // Standard system classes for this compiler's release.
  386               String files = System.getProperty("sun.boot.class.path");
  387               path.addFiles(files, false);
  388               File rt_jar = new File("rt.jar");
  389               for (File file : getPathEntries(files)) {
  390                   if (new File(file.getName()).equals(rt_jar))
  391                       defaultBootClassPathRtJar = file;
  392               }
  393           }
  394   
  395           path.addFiles(xbootclasspathAppendOpt);
  396   
  397           // Strictly speaking, standard extensions are not bootstrap
  398           // classes, but we treat them identically, so we'll pretend
  399           // that they are.
  400           if (extdirsOpt != null)
  401               path.addDirectories(extdirsOpt);
  402           else
  403               path.addDirectories(System.getProperty("java.ext.dirs"), false);
  404   
  405           isDefaultBootClassPath =
  406                   (xbootclasspathPrependOpt == null) &&
  407                   (bootclasspathOpt == null) &&
  408                   (xbootclasspathAppendOpt == null);
  409   
  410           return path;
  411       }
  412   
  413       private Path computeUserClassPath() {
  414           String cp = options.get(CLASSPATH);
  415   
  416           // CLASSPATH environment variable when run from `javac'.
  417           if (cp == null) cp = System.getProperty("env.class.path");
  418   
  419           // If invoked via a java VM (not the javac launcher), use the
  420           // platform class path
  421           if (cp == null && System.getProperty("application.home") == null)
  422               cp = System.getProperty("java.class.path");
  423   
  424           // Default to current working directory.
  425           if (cp == null) cp = ".";
  426   
  427           return new Path()
  428               .expandJarClassPaths(true)        // Only search user jars for Class-Paths
  429               .emptyPathDefault(new File("."))  // Empty path elt ==> current directory
  430               .addFiles(cp);
  431       }
  432   
  433       private Path computeSourcePath() {
  434           String sourcePathArg = options.get(SOURCEPATH);
  435           if (sourcePathArg == null)
  436               return null;
  437   
  438           return new Path().addFiles(sourcePathArg);
  439       }
  440   
  441       private Path computeAnnotationProcessorPath() {
  442           String processorPathArg = options.get(PROCESSORPATH);
  443           if (processorPathArg == null)
  444               return null;
  445   
  446           return new Path().addFiles(processorPathArg);
  447       }
  448   
  449       /** The actual effective locations searched for sources */
  450       private Path sourceSearchPath;
  451   
  452       public Collection<File> sourceSearchPath() {
  453           if (sourceSearchPath == null) {
  454               lazy();
  455               Path sourcePath = getPathForLocation(SOURCE_PATH);
  456               Path userClassPath = getPathForLocation(CLASS_PATH);
  457               sourceSearchPath = sourcePath != null ? sourcePath : userClassPath;
  458           }
  459           return Collections.unmodifiableCollection(sourceSearchPath);
  460       }
  461   
  462       /** The actual effective locations searched for classes */
  463       private Path classSearchPath;
  464   
  465       public Collection<File> classSearchPath() {
  466           if (classSearchPath == null) {
  467               lazy();
  468               Path bootClassPath = getPathForLocation(PLATFORM_CLASS_PATH);
  469               Path userClassPath = getPathForLocation(CLASS_PATH);
  470               classSearchPath = new Path();
  471               classSearchPath.addAll(bootClassPath);
  472               classSearchPath.addAll(userClassPath);
  473           }
  474           return Collections.unmodifiableCollection(classSearchPath);
  475       }
  476   
  477       /** The actual effective locations for non-source, non-class files */
  478       private Path otherSearchPath;
  479   
  480       Collection<File> otherSearchPath() {
  481           if (otherSearchPath == null) {
  482               lazy();
  483               Path userClassPath = getPathForLocation(CLASS_PATH);
  484               Path sourcePath = getPathForLocation(SOURCE_PATH);
  485               if (sourcePath == null)
  486                   otherSearchPath = userClassPath;
  487               else {
  488                   otherSearchPath = new Path();
  489                   otherSearchPath.addAll(userClassPath);
  490                   otherSearchPath.addAll(sourcePath);
  491               }
  492           }
  493           return Collections.unmodifiableCollection(otherSearchPath);
  494       }
  495   
  496       /** Is this the name of an archive file? */
  497       private boolean isArchive(File file) {
  498           String n = file.getName().toLowerCase();
  499           return fsInfo.isFile(file)
  500               && (n.endsWith(".jar") || n.endsWith(".zip"));
  501       }
  502   
  503       /**
  504        * Utility method for converting a search path string to an array
  505        * of directory and JAR file URLs.
  506        *
  507        * Note that this method is called by apt and the DocletInvoker.
  508        *
  509        * @param path the search path string
  510        * @return the resulting array of directory and JAR file URLs
  511        */
  512       public static URL[] pathToURLs(String path) {
  513           StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
  514           URL[] urls = new URL[st.countTokens()];
  515           int count = 0;
  516           while (st.hasMoreTokens()) {
  517               URL url = fileToURL(new File(st.nextToken()));
  518               if (url != null) {
  519                   urls[count++] = url;
  520               }
  521           }
  522           if (urls.length != count) {
  523               URL[] tmp = new URL[count];
  524               System.arraycopy(urls, 0, tmp, 0, count);
  525               urls = tmp;
  526           }
  527           return urls;
  528       }
  529   
  530       /**
  531        * Returns the directory or JAR file URL corresponding to the specified
  532        * local file name.
  533        *
  534        * @param file the File object
  535        * @return the resulting directory or JAR file URL, or null if unknown
  536        */
  537       private static URL fileToURL(File file) {
  538           String name;
  539           try {
  540               name = file.getCanonicalPath();
  541           } catch (IOException e) {
  542               name = file.getAbsolutePath();
  543           }
  544           name = name.replace(File.separatorChar, '/');
  545           if (!name.startsWith("/")) {
  546               name = "/" + name;
  547           }
  548           // If the file does not exist, then assume that it's a directory
  549           if (!file.isFile()) {
  550               name = name + "/";
  551           }
  552           try {
  553               return new URL("file", "", name);
  554           } catch (MalformedURLException e) {
  555               throw new IllegalArgumentException(file.toString());
  556           }
  557       }
  558   }

Home » openjdk-7 » com.sun.tools » javac » file » [javadoc | source]