# HG changeset patch # User jjg # Date 1319564885 25200 # Node ID e95eb04c68cc41c46cc873166601343e75fb348b # Parent d91978895fac8775bab787f02649e61fc1c24573 7104039: refactor/cleanup javac Paths class Reviewed-by: mcimadamore diff -r d91978895fac -r e95eb04c68cc langtools/src/share/classes/com/sun/tools/apt/main/Main.java --- a/langtools/src/share/classes/com/sun/tools/apt/main/Main.java Tue Oct 25 15:40:34 2011 +0100 +++ b/langtools/src/share/classes/com/sun/tools/apt/main/Main.java Tue Oct 25 10:48:05 2011 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,7 +56,7 @@ import com.sun.tools.apt.util.Bark; import com.sun.mirror.apt.AnnotationProcessorFactory; -import static com.sun.tools.javac.file.Paths.pathToURLs; +import static com.sun.tools.javac.file.Locations.pathToURLs; /** This class provides a commandline interface to the apt build-time * tool. diff -r d91978895fac -r e95eb04c68cc langtools/src/share/classes/com/sun/tools/javac/file/JavacFileManager.java --- a/langtools/src/share/classes/com/sun/tools/javac/file/JavacFileManager.java Tue Oct 25 15:40:34 2011 +0100 +++ b/langtools/src/share/classes/com/sun/tools/javac/file/JavacFileManager.java Tue Oct 25 10:48:05 2011 -0700 @@ -54,17 +54,14 @@ import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; -import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.file.RelativePath.RelativeFile; import com.sun.tools.javac.file.RelativePath.RelativeDirectory; -import com.sun.tools.javac.main.OptionName; import com.sun.tools.javac.util.BaseFileManager; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import static javax.tools.StandardLocation.*; -import static com.sun.tools.javac.main.OptionName.*; /** * This class provides access to the source, class and other files @@ -89,23 +86,9 @@ private boolean contextUseOptimizedZip; private ZipFileIndexCache zipFileIndexCache; - private final File uninited = new File("U N I N I T E D"); - private final Set sourceOrClass = EnumSet.of(JavaFileObject.Kind.SOURCE, JavaFileObject.Kind.CLASS); - /** The standard output directory, primarily used for classes. - * Initialized by the "-d" option. - * If classOutDir = null, files are written into same directory as the sources - * they were generated from. - */ - private File classOutDir = uninited; - - /** The output directory, used when generating sources while processing annotations. - * Initialized by the "-s" option. - */ - private File sourceOutDir = uninited; - protected boolean mmappedIO; protected boolean ignoreSymbolFile; @@ -169,7 +152,7 @@ @Override public boolean isDefaultBootClassPath() { - return searchPaths.isDefaultBootClassPath(); + return locations.isDefaultBootClassPath(); } public JavaFileObject getFileForInput(String name) { @@ -483,7 +466,7 @@ */ private Archive openArchive(File zipFileName, boolean useOptimizedZip) throws IOException { File origZipFileName = zipFileName; - if (!ignoreSymbolFile && searchPaths.isDefaultBootClassPathRtJar(zipFileName)) { + if (!ignoreSymbolFile && locations.isDefaultBootClassPathRtJar(zipFileName)) { File file = zipFileName.getParentFile().getParentFile(); // ${java.home} if (new File(file.getName()).equals(new File("jre"))) file = file.getParentFile(); @@ -770,7 +753,7 @@ } else if (location == SOURCE_OUTPUT) { dir = (getSourceOutDir() != null ? getSourceOutDir() : getClassOutDir()); } else { - Iterable path = searchPaths.getPathForLocation(location); + Iterable path = locations.getLocation(location); dir = null; for (File f: path) { dir = f; @@ -805,64 +788,20 @@ throws IOException { nullCheck(location); - searchPaths.lazy(); - - final File dir = location.isOutputLocation() ? getOutputDirectory(path) : null; - - if (location == CLASS_OUTPUT) - classOutDir = getOutputLocation(dir, D); - else if (location == SOURCE_OUTPUT) - sourceOutDir = getOutputLocation(dir, S); - else - searchPaths.setPathForLocation(location, path); - } - // where - private File getOutputDirectory(Iterable path) throws IOException { - if (path == null) - return null; - Iterator pathIter = path.iterator(); - if (!pathIter.hasNext()) - throw new IllegalArgumentException("empty path for directory"); - File dir = pathIter.next(); - if (pathIter.hasNext()) - throw new IllegalArgumentException("path too long for directory"); - if (!dir.exists()) - throw new FileNotFoundException(dir + ": does not exist"); - else if (!dir.isDirectory()) - throw new IOException(dir + ": not a directory"); - return dir; - } - - private File getOutputLocation(File dir, OptionName defaultOptionName) { - if (dir != null) - return dir; - String arg = options.get(defaultOptionName); - if (arg == null) - return null; - return new File(arg); + locations.setLocation(location, path); } public Iterable getLocation(Location location) { nullCheck(location); - searchPaths.lazy(); - if (location == CLASS_OUTPUT) { - return (getClassOutDir() == null ? null : List.of(getClassOutDir())); - } else if (location == SOURCE_OUTPUT) { - return (getSourceOutDir() == null ? null : List.of(getSourceOutDir())); - } else - return searchPaths.getPathForLocation(location); + return locations.getLocation(location); } private File getClassOutDir() { - if (classOutDir == uninited) - classOutDir = getOutputLocation(null, D); - return classOutDir; + return locations.getOutputLocation(CLASS_OUTPUT); } private File getSourceOutDir() { - if (sourceOutDir == uninited) - sourceOutDir = getOutputLocation(null, S); - return sourceOutDir; + return locations.getOutputLocation(SOURCE_OUTPUT); } /** diff -r d91978895fac -r e95eb04c68cc langtools/src/share/classes/com/sun/tools/javac/file/Locations.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/src/share/classes/com/sun/tools/javac/file/Locations.java Tue Oct 25 10:48:05 2011 -0700 @@ -0,0 +1,769 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.tools.javac.file; + +import java.io.FileNotFoundException; +import java.util.Iterator; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.zip.ZipFile; +import javax.tools.JavaFileManager.Location; +import javax.tools.StandardLocation; + +import com.sun.tools.javac.code.Lint; +import com.sun.tools.javac.main.OptionName; +import com.sun.tools.javac.util.ListBuffer; +import com.sun.tools.javac.util.Log; +import com.sun.tools.javac.util.Options; + +import javax.tools.JavaFileManager; +import static javax.tools.StandardLocation.*; +import static com.sun.tools.javac.main.OptionName.*; + +/** This class converts command line arguments, environment variables + * and system properties (in File.pathSeparator-separated String form) + * into a boot class path, user class path, and source path (in + * Collection form). + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + */ +public class Locations { + + /** The log to use for warning output */ + private Log log; + + /** Collection of command-line options */ + private Options options; + + /** Handler for -Xlint options */ + private Lint lint; + + /** Access to (possibly cached) file info */ + private FSInfo fsInfo; + + /** Whether to warn about non-existent path elements */ + private boolean warn; + + // TODO: remove need for this + private boolean inited = false; // TODO? caching bad? + + public Locations() { + initHandlers(); + } + + public void update(Log log, Options options, Lint lint, FSInfo fsInfo) { + this.log = log; + this.options = options; + this.lint = lint; + this.fsInfo = fsInfo; + } + + public Collection bootClassPath() { + return getLocation(PLATFORM_CLASS_PATH); + } + + public boolean isDefaultBootClassPath() { + BootClassPathLocationHandler h = + (BootClassPathLocationHandler) getHandler(PLATFORM_CLASS_PATH); + return h.isDefault(); + } + + boolean isDefaultBootClassPathRtJar(File file) { + BootClassPathLocationHandler h = + (BootClassPathLocationHandler) getHandler(PLATFORM_CLASS_PATH); + return h.isDefaultRtJar(file); + } + + public Collection userClassPath() { + return getLocation(CLASS_PATH); + } + + public Collection sourcePath() { + Collection p = getLocation(SOURCE_PATH); + // TODO: this should be handled by the LocationHandler + return p == null || p.isEmpty() ? null : p; + } + + /** + * Split a path into its elements. Empty path elements will be ignored. + * @param path The path to be split + * @return The elements of the path + */ + private static Iterable getPathEntries(String path) { + return getPathEntries(path, null); + } + + /** + * Split a path into its elements. If emptyPathDefault is not null, all + * empty elements in the path, including empty elements at either end of + * the path, will be replaced with the value of emptyPathDefault. + * @param path The path to be split + * @param emptyPathDefault The value to substitute for empty path elements, + * or null, to ignore empty path elements + * @return The elements of the path + */ + private static Iterable getPathEntries(String path, File emptyPathDefault) { + ListBuffer entries = new ListBuffer(); + int start = 0; + while (start <= path.length()) { + int sep = path.indexOf(File.pathSeparatorChar, start); + if (sep == -1) + sep = path.length(); + if (start < sep) + entries.add(new File(path.substring(start, sep))); + else if (emptyPathDefault != null) + entries.add(emptyPathDefault); + start = sep + 1; + } + return entries; + } + + /** + * Utility class to help evaluate a path option. + * Duplicate entries are ignored, jar class paths can be expanded. + */ + private class Path extends LinkedHashSet { + private static final long serialVersionUID = 0; + + private boolean expandJarClassPaths = false; + private Set canonicalValues = new HashSet(); + + public Path expandJarClassPaths(boolean x) { + expandJarClassPaths = x; + return this; + } + + /** What to use when path element is the empty string */ + private File emptyPathDefault = null; + + public Path emptyPathDefault(File x) { + emptyPathDefault = x; + return this; + } + + public Path() { super(); } + + public Path addDirectories(String dirs, boolean warn) { + boolean prev = expandJarClassPaths; + expandJarClassPaths = true; + try { + if (dirs != null) + for (File dir : getPathEntries(dirs)) + addDirectory(dir, warn); + return this; + } finally { + expandJarClassPaths = prev; + } + } + + public Path addDirectories(String dirs) { + return addDirectories(dirs, warn); + } + + private void addDirectory(File dir, boolean warn) { + if (!dir.isDirectory()) { + if (warn) + log.warning(Lint.LintCategory.PATH, + "dir.path.element.not.found", dir); + return; + } + + File[] files = dir.listFiles(); + if (files == null) + return; + + for (File direntry : files) { + if (isArchive(direntry)) + addFile(direntry, warn); + } + } + + public Path addFiles(String files, boolean warn) { + if (files != null) { + addFiles(getPathEntries(files, emptyPathDefault), warn); + } + return this; + } + + public Path addFiles(String files) { + return addFiles(files, warn); + } + + public Path addFiles(Iterable files, boolean warn) { + if (files != null) { + for (File file: files) + addFile(file, warn); + } + return this; + } + + public Path addFiles(Iterable files) { + return addFiles(files, warn); + } + + public void addFile(File file, boolean warn) { + if (contains(file)) { + // discard duplicates + return; + } + + if (! fsInfo.exists(file)) { + /* No such file or directory exists */ + if (warn) { + log.warning(Lint.LintCategory.PATH, + "path.element.not.found", file); + } + super.add(file); + return; + } + + File canonFile = fsInfo.getCanonicalFile(file); + if (canonicalValues.contains(canonFile)) { + /* Discard duplicates and avoid infinite recursion */ + return; + } + + if (fsInfo.isFile(file)) { + /* File is an ordinary file. */ + if (!isArchive(file)) { + /* Not a recognized extension; open it to see if + it looks like a valid zip file. */ + try { + ZipFile z = new ZipFile(file); + z.close(); + if (warn) { + log.warning(Lint.LintCategory.PATH, + "unexpected.archive.file", file); + } + } catch (IOException e) { + // FIXME: include e.getLocalizedMessage in warning + if (warn) { + log.warning(Lint.LintCategory.PATH, + "invalid.archive.file", file); + } + return; + } + } + } + + /* Now what we have left is either a directory or a file name + conforming to archive naming convention */ + super.add(file); + canonicalValues.add(canonFile); + + if (expandJarClassPaths && fsInfo.isFile(file)) + addJarClassPath(file, warn); + } + + // Adds referenced classpath elements from a jar's Class-Path + // Manifest entry. In some future release, we may want to + // update this code to recognize URLs rather than simple + // filenames, but if we do, we should redo all path-related code. + private void addJarClassPath(File jarFile, boolean warn) { + try { + for (File f: fsInfo.getJarClassPath(jarFile)) { + addFile(f, warn); + } + } catch (IOException e) { + log.error("error.reading.file", jarFile, JavacFileManager.getMessage(e)); + } + } + } + + /** + * Base class for handling support for the representation of Locations. + * Implementations are responsible for handling the interactions between + * the command line options for a location, and API access via setLocation. + * @see #initHandlers + * @see #getHandler + */ + protected abstract class LocationHandler { + final Location location; + final Set options; + + /** + * Create a handler. The location and options provide a way to map + * from a location or an option to the corresponding handler. + * @see #initHandlers + */ + protected LocationHandler(Location location, OptionName... options) { + this.location = location; + this.options = EnumSet.copyOf(Arrays.asList(options)); + } + + // TODO: TEMPORARY, while Options still used for command line options + void update(Options optionTable) { + for (OptionName o: options) { + String v = optionTable.get(o); + if (v != null) { + handleOption(o, v); + } + } + } + + /** @see JavaFileManager#handleOption. */ + abstract boolean handleOption(OptionName option, String value); + /** @see JavaFileManager#getLocation. */ + abstract Collection getLocation(); + /** @see JavaFileManager#setLocation. */ + abstract void setLocation(Iterable files) throws IOException; + } + + /** + * General purpose implementation for output locations, + * such as -d/CLASS_OUTPUT and -s/SOURCE_OUTPUT. + * All options are treated as equivalent (i.e. aliases.) + * The value is a single file, possibly null. + */ + private class OutputLocationHandler extends LocationHandler { + private File outputDir; + + OutputLocationHandler(Location location, OptionName... options) { + super(location, options); + } + + @Override + boolean handleOption(OptionName option, String value) { + if (!options.contains(option)) + return false; + + // TODO: could/should validate outputDir exists and is a directory + // need to decide how best to report issue for benefit of + // direct API call on JavaFileManager.handleOption(specifies IAE) + // vs. command line decoding. + outputDir = new File(value); + return true; + } + + @Override + Collection getLocation() { + return (outputDir == null) ? null : Collections.singleton(outputDir); + } + + @Override + void setLocation(Iterable files) throws IOException { + if (files == null) { + outputDir = null; + } else { + Iterator pathIter = files.iterator(); + if (!pathIter.hasNext()) + throw new IllegalArgumentException("empty path for directory"); + File dir = pathIter.next(); + if (pathIter.hasNext()) + throw new IllegalArgumentException("path too long for directory"); + if (!dir.exists()) + throw new FileNotFoundException(dir + ": does not exist"); + else if (!dir.isDirectory()) + throw new IOException(dir + ": not a directory"); + outputDir = dir; + } + } + } + + /** + * General purpose implementation for search path locations, + * such as -sourcepath/SOURCE_PATH and -processorPath/ANNOTATION_PROCESS_PATH. + * All options are treated as equivalent (i.e. aliases.) + * The value is an ordered set of files and/or directories. + */ + private class SimpleLocationHandler extends LocationHandler { + protected Collection searchPath; + + SimpleLocationHandler(Location location, OptionName... options) { + super(location, options); + } + + @Override + boolean handleOption(OptionName option, String value) { + if (!options.contains(option)) + return false; + searchPath = value == null ? null : + Collections.unmodifiableCollection(computePath(value)); + return true; + } + + protected Path computePath(String value) { + return new Path().addFiles(value); + } + + @Override + Collection getLocation() { + return searchPath; + } + + @Override + void setLocation(Iterable files) { + Path p; + if (files == null) { + p = computePath(null); + } else { + p = new Path().addFiles(files); + } + searchPath = Collections.unmodifiableCollection(p); + } + } + + /** + * Subtype of SimpleLocationHandler for -classpath/CLASS_PATH. + * If no value is given, a default is provided, based on system properties + * and other values. + */ + private class ClassPathLocationHandler extends SimpleLocationHandler { + ClassPathLocationHandler() { + super(StandardLocation.CLASS_PATH, + OptionName.CLASSPATH, OptionName.CP); + } + + @Override + Collection getLocation() { + lazy(); + return searchPath; + } + + @Override + protected Path computePath(String value) { + String cp = value; + + // CLASSPATH environment variable when run from `javac'. + if (cp == null) cp = System.getProperty("env.class.path"); + + // If invoked via a java VM (not the javac launcher), use the + // platform class path + if (cp == null && System.getProperty("application.home") == null) + cp = System.getProperty("java.class.path"); + + // Default to current working directory. + if (cp == null) cp = "."; + + return new Path() + .expandJarClassPaths(true) // Only search user jars for Class-Paths + .emptyPathDefault(new File(".")) // Empty path elt ==> current directory + .addFiles(cp); + } + + private void lazy() { + if (searchPath == null) + setLocation(null); + } + } + + /** + * Custom subtype of LocationHandler for PLATFORM_CLASS_PATH. + * Various options are supported for different components of the + * platform class path. + * Setting a value with setLocation overrides all existing option values. + * Setting any option overrides any value set with setLocation, and reverts + * to using default values for options that have not been set. + * Setting -bootclasspath or -Xbootclasspath overrides any existing + * value for -Xbootclasspath/p: and -Xbootclasspath/a:. + */ + private class BootClassPathLocationHandler extends LocationHandler { + private Collection searchPath; + final Map optionValues = new EnumMap(OptionName.class); + + /** + * rt.jar as found on the default bootclasspath. + * If the user specified a bootclasspath, null is used. + */ + private File defaultBootClassPathRtJar = null; + + /** + * Is bootclasspath the default? + */ + private boolean isDefaultBootClassPath; + + BootClassPathLocationHandler() { + super(StandardLocation.PLATFORM_CLASS_PATH, + OptionName.BOOTCLASSPATH, OptionName.XBOOTCLASSPATH, + OptionName.XBOOTCLASSPATH_PREPEND, + OptionName.XBOOTCLASSPATH_APPEND, + OptionName.ENDORSEDDIRS, OptionName.DJAVA_ENDORSED_DIRS, + OptionName.EXTDIRS, OptionName.DJAVA_EXT_DIRS); + } + + boolean isDefault() { + lazy(); + return isDefaultBootClassPath; + } + + boolean isDefaultRtJar(File file) { + lazy(); + return file.equals(defaultBootClassPathRtJar); + } + + @Override + boolean handleOption(OptionName option, String value) { + if (!options.contains(option)) + return false; + + option = canonicalize(option); + optionValues.put(option, value); + if (option == BOOTCLASSPATH) { + optionValues.remove(XBOOTCLASSPATH_PREPEND); + optionValues.remove(XBOOTCLASSPATH_APPEND); + } + searchPath = null; // reset to "uninitialized" + return true; + } + // where + // TODO: would be better if option aliasing was handled at a higher + // level + private OptionName canonicalize(OptionName option) { + switch (option) { + case XBOOTCLASSPATH: + return OptionName.BOOTCLASSPATH; + case DJAVA_ENDORSED_DIRS: + return OptionName.ENDORSEDDIRS; + case DJAVA_EXT_DIRS: + return OptionName.EXTDIRS; + default: + return option; + } + } + + @Override + Collection getLocation() { + lazy(); + return searchPath; + } + + @Override + void setLocation(Iterable files) { + if (files == null) { + searchPath = null; // reset to "uninitialized" + } else { + defaultBootClassPathRtJar = null; + isDefaultBootClassPath = false; + Path p = new Path().addFiles(files, false); + searchPath = Collections.unmodifiableCollection(p); + optionValues.clear(); + } + } + + Path computePath() { + defaultBootClassPathRtJar = null; + Path path = new Path(); + + String bootclasspathOpt = optionValues.get(BOOTCLASSPATH); + String endorseddirsOpt = optionValues.get(ENDORSEDDIRS); + String extdirsOpt = optionValues.get(EXTDIRS); + String xbootclasspathPrependOpt = optionValues.get(XBOOTCLASSPATH_PREPEND); + String xbootclasspathAppendOpt = optionValues.get(XBOOTCLASSPATH_APPEND); + + path.addFiles(xbootclasspathPrependOpt); + + if (endorseddirsOpt != null) + path.addDirectories(endorseddirsOpt); + else + path.addDirectories(System.getProperty("java.endorsed.dirs"), false); + + if (bootclasspathOpt != null) { + path.addFiles(bootclasspathOpt); + } else { + // Standard system classes for this compiler's release. + String files = System.getProperty("sun.boot.class.path"); + path.addFiles(files, false); + File rt_jar = new File("rt.jar"); + for (File file : getPathEntries(files)) { + if (new File(file.getName()).equals(rt_jar)) + defaultBootClassPathRtJar = file; + } + } + + path.addFiles(xbootclasspathAppendOpt); + + // Strictly speaking, standard extensions are not bootstrap + // classes, but we treat them identically, so we'll pretend + // that they are. + if (extdirsOpt != null) + path.addDirectories(extdirsOpt); + else + path.addDirectories(System.getProperty("java.ext.dirs"), false); + + isDefaultBootClassPath = + (xbootclasspathPrependOpt == null) && + (bootclasspathOpt == null) && + (xbootclasspathAppendOpt == null); + + return path; + } + + private void lazy() { + if (searchPath == null) + searchPath = Collections.unmodifiableCollection(computePath()); + } + } + + Map handlersForLocation; + Map handlersForOption; + + void initHandlers() { + handlersForLocation = new HashMap(); + handlersForOption = new EnumMap(OptionName.class); + + LocationHandler[] handlers = { + new BootClassPathLocationHandler(), + new ClassPathLocationHandler(), + new SimpleLocationHandler(StandardLocation.SOURCE_PATH, OptionName.SOURCEPATH), + new SimpleLocationHandler(StandardLocation.ANNOTATION_PROCESSOR_PATH, OptionName.PROCESSORPATH), + new OutputLocationHandler((StandardLocation.CLASS_OUTPUT), OptionName.D), + new OutputLocationHandler((StandardLocation.SOURCE_OUTPUT), OptionName.S) + }; + + for (LocationHandler h: handlers) { + handlersForLocation.put(h.location, h); + for (OptionName o: h.options) + handlersForOption.put(o, h); + } + } + + boolean handleOption(OptionName option, String value) { + LocationHandler h = handlersForOption.get(option); + return (h == null ? false : h.handleOption(option, value)); + } + + Collection getLocation(Location location) { + LocationHandler h = getHandler(location); + return (h == null ? null : h.getLocation()); + } + + File getOutputLocation(Location location) { + if (!location.isOutputLocation()) + throw new IllegalArgumentException(); + LocationHandler h = getHandler(location); + return ((OutputLocationHandler) h).outputDir; + } + + void setLocation(Location location, Iterable files) throws IOException { + LocationHandler h = getHandler(location); + if (h == null) { + if (location.isOutputLocation()) + h = new OutputLocationHandler(location); + else + h = new SimpleLocationHandler(location); + handlersForLocation.put(location, h); + } + h.setLocation(files); + } + + protected LocationHandler getHandler(Location location) { + location.getClass(); // null check + lazy(); + return handlersForLocation.get(location); + } + +// TOGO + protected void lazy() { + if (!inited) { + warn = lint.isEnabled(Lint.LintCategory.PATH); + + for (LocationHandler h: handlersForLocation.values()) { + h.update(options); + } + + inited = true; + } + } + + /** Is this the name of an archive file? */ + private boolean isArchive(File file) { + String n = file.getName().toLowerCase(); + return fsInfo.isFile(file) + && (n.endsWith(".jar") || n.endsWith(".zip")); + } + + /** + * Utility method for converting a search path string to an array + * of directory and JAR file URLs. + * + * Note that this method is called by apt and the DocletInvoker. + * + * @param path the search path string + * @return the resulting array of directory and JAR file URLs + */ + public static URL[] pathToURLs(String path) { + StringTokenizer st = new StringTokenizer(path, File.pathSeparator); + URL[] urls = new URL[st.countTokens()]; + int count = 0; + while (st.hasMoreTokens()) { + URL url = fileToURL(new File(st.nextToken())); + if (url != null) { + urls[count++] = url; + } + } + if (urls.length != count) { + URL[] tmp = new URL[count]; + System.arraycopy(urls, 0, tmp, 0, count); + urls = tmp; + } + return urls; + } + + /** + * Returns the directory or JAR file URL corresponding to the specified + * local file name. + * + * @param file the File object + * @return the resulting directory or JAR file URL, or null if unknown + */ + private static URL fileToURL(File file) { + String name; + try { + name = file.getCanonicalPath(); + } catch (IOException e) { + name = file.getAbsolutePath(); + } + name = name.replace(File.separatorChar, '/'); + if (!name.startsWith("/")) { + name = "/" + name; + } + // If the file does not exist, then assume that it's a directory + if (!file.isFile()) { + name = name + "/"; + } + try { + return new URL("file", "", name); + } catch (MalformedURLException e) { + throw new IllegalArgumentException(file.toString()); + } + } +} diff -r d91978895fac -r e95eb04c68cc langtools/src/share/classes/com/sun/tools/javac/file/Paths.java --- a/langtools/src/share/classes/com/sun/tools/javac/file/Paths.java Tue Oct 25 15:40:34 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,540 +0,0 @@ -/* - * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.sun.tools.javac.file; - -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.StringTokenizer; -import java.util.zip.ZipFile; -import javax.tools.JavaFileManager.Location; - -import com.sun.tools.javac.code.Lint; -import com.sun.tools.javac.util.ListBuffer; -import com.sun.tools.javac.util.Log; -import com.sun.tools.javac.util.Options; - -import static javax.tools.StandardLocation.*; -import static com.sun.tools.javac.main.OptionName.*; - -/** This class converts command line arguments, environment variables - * and system properties (in File.pathSeparator-separated String form) - * into a boot class path, user class path, and source path (in - * Collection form). - * - *

This is NOT part of any supported API. - * If you write code that depends on this, you do so at your own risk. - * This code and its internal interfaces are subject to change or - * deletion without notice. - */ -public class Paths { - - /** The log to use for warning output */ - private Log log; - - /** Collection of command-line options */ - private Options options; - - /** Handler for -Xlint options */ - private Lint lint; - - /** Access to (possibly cached) file info */ - private FSInfo fsInfo; - - public Paths() { - pathsForLocation = new HashMap(16); - } - - public void update(Log log, Options options, Lint lint, FSInfo fsInfo) { - this.log = log; - this.options = options; - this.lint = lint; - this.fsInfo = fsInfo; - } - - /** Whether to warn about non-existent path elements */ - private boolean warn; - - private Map pathsForLocation; - - private boolean inited = false; // TODO? caching bad? - - /** - * rt.jar as found on the default bootclass path. If the user specified a - * bootclasspath, null is used. - */ - private File defaultBootClassPathRtJar = null; - - /** - * Is bootclasspath the default? - */ - private boolean isDefaultBootClassPath; - - Path getPathForLocation(Location location) { - Path path = pathsForLocation.get(location); - if (path == null) - setPathForLocation(location, null); - return pathsForLocation.get(location); - } - - void setPathForLocation(Location location, Iterable path) { - // TODO? if (inited) throw new IllegalStateException - // TODO: otherwise reset sourceSearchPath, classSearchPath as needed - Path p; - if (path == null) { - if (location == CLASS_PATH) - p = computeUserClassPath(); - else if (location == PLATFORM_CLASS_PATH) - p = computeBootClassPath(); // sets isDefaultBootClassPath - else if (location == ANNOTATION_PROCESSOR_PATH) - p = computeAnnotationProcessorPath(); - else if (location == SOURCE_PATH) - p = computeSourcePath(); - else - // no defaults for other paths - p = null; - } else { - if (location == PLATFORM_CLASS_PATH) { - defaultBootClassPathRtJar = null; - isDefaultBootClassPath = false; - } - p = new Path(); - for (File f: path) - p.addFile(f, warn); // TODO: is use of warn appropriate? - } - pathsForLocation.put(location, p); - } - - public boolean isDefaultBootClassPath() { - lazy(); - return isDefaultBootClassPath; - } - - protected void lazy() { - if (!inited) { - warn = lint.isEnabled(Lint.LintCategory.PATH); - - pathsForLocation.put(PLATFORM_CLASS_PATH, computeBootClassPath()); - pathsForLocation.put(CLASS_PATH, computeUserClassPath()); - pathsForLocation.put(SOURCE_PATH, computeSourcePath()); - - inited = true; - } - } - - public Collection bootClassPath() { - lazy(); - return Collections.unmodifiableCollection(getPathForLocation(PLATFORM_CLASS_PATH)); - } - public Collection userClassPath() { - lazy(); - return Collections.unmodifiableCollection(getPathForLocation(CLASS_PATH)); - } - public Collection sourcePath() { - lazy(); - Path p = getPathForLocation(SOURCE_PATH); - return p == null || p.size() == 0 - ? null - : Collections.unmodifiableCollection(p); - } - - boolean isDefaultBootClassPathRtJar(File file) { - return file.equals(defaultBootClassPathRtJar); - } - - /** - * Split a path into its elements. Empty path elements will be ignored. - * @param path The path to be split - * @return The elements of the path - */ - private static Iterable getPathEntries(String path) { - return getPathEntries(path, null); - } - - /** - * Split a path into its elements. If emptyPathDefault is not null, all - * empty elements in the path, including empty elements at either end of - * the path, will be replaced with the value of emptyPathDefault. - * @param path The path to be split - * @param emptyPathDefault The value to substitute for empty path elements, - * or null, to ignore empty path elements - * @return The elements of the path - */ - private static Iterable getPathEntries(String path, File emptyPathDefault) { - ListBuffer entries = new ListBuffer(); - int start = 0; - while (start <= path.length()) { - int sep = path.indexOf(File.pathSeparatorChar, start); - if (sep == -1) - sep = path.length(); - if (start < sep) - entries.add(new File(path.substring(start, sep))); - else if (emptyPathDefault != null) - entries.add(emptyPathDefault); - start = sep + 1; - } - return entries; - } - - private class Path extends LinkedHashSet { - private static final long serialVersionUID = 0; - - private boolean expandJarClassPaths = false; - private Set canonicalValues = new HashSet(); - - public Path expandJarClassPaths(boolean x) { - expandJarClassPaths = x; - return this; - } - - /** What to use when path element is the empty string */ - private File emptyPathDefault = null; - - public Path emptyPathDefault(File x) { - emptyPathDefault = x; - return this; - } - - public Path() { super(); } - - public Path addDirectories(String dirs, boolean warn) { - boolean prev = expandJarClassPaths; - expandJarClassPaths = true; - try { - if (dirs != null) - for (File dir : getPathEntries(dirs)) - addDirectory(dir, warn); - return this; - } finally { - expandJarClassPaths = prev; - } - } - - public Path addDirectories(String dirs) { - return addDirectories(dirs, warn); - } - - private void addDirectory(File dir, boolean warn) { - if (!dir.isDirectory()) { - if (warn) - log.warning(Lint.LintCategory.PATH, - "dir.path.element.not.found", dir); - return; - } - - File[] files = dir.listFiles(); - if (files == null) - return; - - for (File direntry : files) { - if (isArchive(direntry)) - addFile(direntry, warn); - } - } - - public Path addFiles(String files, boolean warn) { - if (files != null) { - for (File file : getPathEntries(files, emptyPathDefault)) - addFile(file, warn); - } - return this; - } - - public Path addFiles(String files) { - return addFiles(files, warn); - } - - public void addFile(File file, boolean warn) { - if (contains(file)) { - // discard duplicates - return; - } - - if (! fsInfo.exists(file)) { - /* No such file or directory exists */ - if (warn) { - log.warning(Lint.LintCategory.PATH, - "path.element.not.found", file); - } - super.add(file); - return; - } - - File canonFile = fsInfo.getCanonicalFile(file); - if (canonicalValues.contains(canonFile)) { - /* Discard duplicates and avoid infinite recursion */ - return; - } - - if (fsInfo.isFile(file)) { - /* File is an ordinary file. */ - if (!isArchive(file)) { - /* Not a recognized extension; open it to see if - it looks like a valid zip file. */ - try { - ZipFile z = new ZipFile(file); - z.close(); - if (warn) { - log.warning(Lint.LintCategory.PATH, - "unexpected.archive.file", file); - } - } catch (IOException e) { - // FIXME: include e.getLocalizedMessage in warning - if (warn) { - log.warning(Lint.LintCategory.PATH, - "invalid.archive.file", file); - } - return; - } - } - } - - /* Now what we have left is either a directory or a file name - conforming to archive naming convention */ - super.add(file); - canonicalValues.add(canonFile); - - if (expandJarClassPaths && fsInfo.isFile(file)) - addJarClassPath(file, warn); - } - - // Adds referenced classpath elements from a jar's Class-Path - // Manifest entry. In some future release, we may want to - // update this code to recognize URLs rather than simple - // filenames, but if we do, we should redo all path-related code. - private void addJarClassPath(File jarFile, boolean warn) { - try { - for (File f: fsInfo.getJarClassPath(jarFile)) { - addFile(f, warn); - } - } catch (IOException e) { - log.error("error.reading.file", jarFile, JavacFileManager.getMessage(e)); - } - } - } - - private Path computeBootClassPath() { - defaultBootClassPathRtJar = null; - Path path = new Path(); - - String bootclasspathOpt = options.get(BOOTCLASSPATH); - String endorseddirsOpt = options.get(ENDORSEDDIRS); - String extdirsOpt = options.get(EXTDIRS); - String xbootclasspathPrependOpt = options.get(XBOOTCLASSPATH_PREPEND); - String xbootclasspathAppendOpt = options.get(XBOOTCLASSPATH_APPEND); - - path.addFiles(xbootclasspathPrependOpt); - - if (endorseddirsOpt != null) - path.addDirectories(endorseddirsOpt); - else - path.addDirectories(System.getProperty("java.endorsed.dirs"), false); - - if (bootclasspathOpt != null) { - path.addFiles(bootclasspathOpt); - } else { - // Standard system classes for this compiler's release. - String files = System.getProperty("sun.boot.class.path"); - path.addFiles(files, false); - File rt_jar = new File("rt.jar"); - for (File file : getPathEntries(files)) { - if (new File(file.getName()).equals(rt_jar)) - defaultBootClassPathRtJar = file; - } - } - - path.addFiles(xbootclasspathAppendOpt); - - // Strictly speaking, standard extensions are not bootstrap - // classes, but we treat them identically, so we'll pretend - // that they are. - if (extdirsOpt != null) - path.addDirectories(extdirsOpt); - else - path.addDirectories(System.getProperty("java.ext.dirs"), false); - - isDefaultBootClassPath = - (xbootclasspathPrependOpt == null) && - (bootclasspathOpt == null) && - (xbootclasspathAppendOpt == null); - - return path; - } - - private Path computeUserClassPath() { - String cp = options.get(CLASSPATH); - - // CLASSPATH environment variable when run from `javac'. - if (cp == null) cp = System.getProperty("env.class.path"); - - // If invoked via a java VM (not the javac launcher), use the - // platform class path - if (cp == null && System.getProperty("application.home") == null) - cp = System.getProperty("java.class.path"); - - // Default to current working directory. - if (cp == null) cp = "."; - - return new Path() - .expandJarClassPaths(true) // Only search user jars for Class-Paths - .emptyPathDefault(new File(".")) // Empty path elt ==> current directory - .addFiles(cp); - } - - private Path computeSourcePath() { - String sourcePathArg = options.get(SOURCEPATH); - if (sourcePathArg == null) - return null; - - return new Path().addFiles(sourcePathArg); - } - - private Path computeAnnotationProcessorPath() { - String processorPathArg = options.get(PROCESSORPATH); - if (processorPathArg == null) - return null; - - return new Path().addFiles(processorPathArg); - } - - /** The actual effective locations searched for sources */ - private Path sourceSearchPath; - - public Collection sourceSearchPath() { - if (sourceSearchPath == null) { - lazy(); - Path sourcePath = getPathForLocation(SOURCE_PATH); - Path userClassPath = getPathForLocation(CLASS_PATH); - sourceSearchPath = sourcePath != null ? sourcePath : userClassPath; - } - return Collections.unmodifiableCollection(sourceSearchPath); - } - - /** The actual effective locations searched for classes */ - private Path classSearchPath; - - public Collection classSearchPath() { - if (classSearchPath == null) { - lazy(); - Path bootClassPath = getPathForLocation(PLATFORM_CLASS_PATH); - Path userClassPath = getPathForLocation(CLASS_PATH); - classSearchPath = new Path(); - classSearchPath.addAll(bootClassPath); - classSearchPath.addAll(userClassPath); - } - return Collections.unmodifiableCollection(classSearchPath); - } - - /** The actual effective locations for non-source, non-class files */ - private Path otherSearchPath; - - Collection otherSearchPath() { - if (otherSearchPath == null) { - lazy(); - Path userClassPath = getPathForLocation(CLASS_PATH); - Path sourcePath = getPathForLocation(SOURCE_PATH); - if (sourcePath == null) - otherSearchPath = userClassPath; - else { - otherSearchPath = new Path(); - otherSearchPath.addAll(userClassPath); - otherSearchPath.addAll(sourcePath); - } - } - return Collections.unmodifiableCollection(otherSearchPath); - } - - /** Is this the name of an archive file? */ - private boolean isArchive(File file) { - String n = file.getName().toLowerCase(); - return fsInfo.isFile(file) - && (n.endsWith(".jar") || n.endsWith(".zip")); - } - - /** - * Utility method for converting a search path string to an array - * of directory and JAR file URLs. - * - * Note that this method is called by apt and the DocletInvoker. - * - * @param path the search path string - * @return the resulting array of directory and JAR file URLs - */ - public static URL[] pathToURLs(String path) { - StringTokenizer st = new StringTokenizer(path, File.pathSeparator); - URL[] urls = new URL[st.countTokens()]; - int count = 0; - while (st.hasMoreTokens()) { - URL url = fileToURL(new File(st.nextToken())); - if (url != null) { - urls[count++] = url; - } - } - if (urls.length != count) { - URL[] tmp = new URL[count]; - System.arraycopy(urls, 0, tmp, 0, count); - urls = tmp; - } - return urls; - } - - /** - * Returns the directory or JAR file URL corresponding to the specified - * local file name. - * - * @param file the File object - * @return the resulting directory or JAR file URL, or null if unknown - */ - private static URL fileToURL(File file) { - String name; - try { - name = file.getCanonicalPath(); - } catch (IOException e) { - name = file.getAbsolutePath(); - } - name = name.replace(File.separatorChar, '/'); - if (!name.startsWith("/")) { - name = "/" + name; - } - // If the file does not exist, then assume that it's a directory - if (!file.isFile()) { - name = name + "/"; - } - try { - return new URL("file", "", name); - } catch (MalformedURLException e) { - throw new IllegalArgumentException(file.toString()); - } - } -} diff -r d91978895fac -r e95eb04c68cc langtools/src/share/classes/com/sun/tools/javac/nio/JavacPathFileManager.java --- a/langtools/src/share/classes/com/sun/tools/javac/nio/JavacPathFileManager.java Tue Oct 25 15:40:34 2011 +0100 +++ b/langtools/src/share/classes/com/sun/tools/javac/nio/JavacPathFileManager.java Tue Oct 25 10:48:05 2011 -0700 @@ -169,7 +169,7 @@ @Override public boolean isDefaultBootClassPath() { - return searchPaths.isDefaultBootClassPath(); + return locations.isDefaultBootClassPath(); } // @@ -227,13 +227,13 @@ if (locn instanceof StandardLocation) { switch ((StandardLocation) locn) { case CLASS_PATH: - files = searchPaths.userClassPath(); + files = locations.userClassPath(); break; case PLATFORM_CLASS_PATH: - files = searchPaths.bootClassPath(); + files = locations.bootClassPath(); break; case SOURCE_PATH: - files = searchPaths.sourcePath(); + files = locations.sourcePath(); break; case CLASS_OUTPUT: { String arg = options.get(D); diff -r d91978895fac -r e95eb04c68cc langtools/src/share/classes/com/sun/tools/javac/util/BaseFileManager.java --- a/langtools/src/share/classes/com/sun/tools/javac/util/BaseFileManager.java Tue Oct 25 15:40:34 2011 +0100 +++ b/langtools/src/share/classes/com/sun/tools/javac/util/BaseFileManager.java Tue Oct 25 10:48:05 2011 -0700 @@ -52,7 +52,7 @@ import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Source; import com.sun.tools.javac.file.FSInfo; -import com.sun.tools.javac.file.Paths; +import com.sun.tools.javac.file.Locations; import com.sun.tools.javac.main.JavacOption; import com.sun.tools.javac.main.OptionName; import com.sun.tools.javac.main.RecognizedOptions; @@ -67,7 +67,7 @@ protected BaseFileManager(Charset charset) { this.charset = charset; byteBufferCache = new ByteBufferCache(); - searchPaths = createPaths(); + locations = createLocations(); } /** @@ -77,11 +77,11 @@ log = Log.instance(context); options = Options.instance(context); classLoaderClass = options.get("procloader"); - searchPaths.update(log, options, Lint.instance(context), FSInfo.instance(context)); + locations.update(log, options, Lint.instance(context), FSInfo.instance(context)); } - protected Paths createPaths() { - return new Paths(); + protected Locations createLocations() { + return new Locations(); } /** @@ -98,7 +98,7 @@ protected String classLoaderClass; - protected Paths searchPaths; + protected Locations locations; protected Source getSource() { String sourceName = options.get(OptionName.SOURCE); diff -r d91978895fac -r e95eb04c68cc langtools/src/share/classes/com/sun/tools/javadoc/DocletInvoker.java --- a/langtools/src/share/classes/com/sun/tools/javadoc/DocletInvoker.java Tue Oct 25 15:40:34 2011 +0100 +++ b/langtools/src/share/classes/com/sun/tools/javadoc/DocletInvoker.java Tue Oct 25 10:48:05 2011 -0700 @@ -80,7 +80,7 @@ cpString = appendPath(System.getProperty("env.class.path"), cpString); cpString = appendPath(System.getProperty("java.class.path"), cpString); cpString = appendPath(docletPath, cpString); - URL[] urls = com.sun.tools.javac.file.Paths.pathToURLs(cpString); + URL[] urls = com.sun.tools.javac.file.Locations.pathToURLs(cpString); if (docletParentClassLoader == null) appClassLoader = new URLClassLoader(urls, getDelegationClassLoader(docletClassName)); else