Locations.java revision 2674:e284f560acf6
1193323Sed/* 2193323Sed * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. 3193323Sed * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4193323Sed * 5193323Sed * This code is free software; you can redistribute it and/or modify it 6193323Sed * under the terms of the GNU General Public License version 2 only, as 7193323Sed * published by the Free Software Foundation. Oracle designates this 8193323Sed * particular file as subject to the "Classpath" exception as provided 9193323Sed * by Oracle in the LICENSE file that accompanied this code. 10193323Sed * 11193323Sed * This code is distributed in the hope that it will be useful, but WITHOUT 12193323Sed * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13193323Sed * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14193323Sed * version 2 for more details (a copy is included in the LICENSE file that 15193323Sed * accompanied this code). 16193323Sed * 17199481Srdivacky * You should have received a copy of the GNU General Public License version 18218893Sdim * 2 along with this work; if not, write to the Free Software Foundation, 19193323Sed * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20193323Sed * 21193323Sed * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22193323Sed * or visit www.oracle.com if you need additional information or have any 23198090Srdivacky * questions. 24245431Sdim */ 25193323Sedpackage com.sun.tools.javac.file; 26193323Sed 27193323Sedimport java.io.File; 28198090Srdivackyimport java.io.FileNotFoundException; 29218893Sdimimport java.io.IOException; 30263509Sdimimport java.net.MalformedURLException; 31206274Srdivackyimport java.net.URL; 32245431Sdimimport java.nio.file.Files; 33245431Sdimimport java.nio.file.Path; 34193323Sedimport java.nio.file.Paths; 35193323Sedimport java.util.ArrayList; 36212904Sdimimport java.util.Arrays; 37193323Sedimport java.util.Collection; 38193323Sedimport java.util.Collections; 39193323Sedimport java.util.EnumMap; 40212904Sdimimport java.util.EnumSet; 41193323Sedimport java.util.HashMap; 42210299Sedimport java.util.HashSet; 43210299Sedimport java.util.Iterator; 44212904Sdimimport java.util.LinkedHashSet; 45193323Sedimport java.util.Map; 46193323Sedimport java.util.Set; 47193323Sedimport java.util.regex.Pattern; 48193323Sedimport java.util.stream.Stream; 49193323Sedimport java.util.zip.ZipFile; 50193323Sed 51193323Sedimport javax.tools.JavaFileManager; 52193323Sedimport javax.tools.JavaFileManager.Location; 53193323Sedimport javax.tools.StandardJavaFileManager; 54193323Sedimport javax.tools.StandardLocation; 55193323Sed 56193323Sedimport com.sun.tools.javac.code.Lint; 57193323Sedimport com.sun.tools.javac.main.Option; 58193323Sedimport com.sun.tools.javac.util.ListBuffer; 59193323Sedimport com.sun.tools.javac.util.Log; 60193323Sedimport com.sun.tools.javac.util.StringUtils; 61193323Sed 62193323Sedimport static javax.tools.StandardLocation.CLASS_PATH; 63193323Sedimport static javax.tools.StandardLocation.PLATFORM_CLASS_PATH; 64193323Sedimport static javax.tools.StandardLocation.SOURCE_PATH; 65193323Sed 66193323Sedimport static com.sun.tools.javac.main.Option.BOOTCLASSPATH; 67193323Sedimport static com.sun.tools.javac.main.Option.DJAVA_ENDORSED_DIRS; 68193323Sedimport static com.sun.tools.javac.main.Option.DJAVA_EXT_DIRS; 69193323Sedimport static com.sun.tools.javac.main.Option.ENDORSEDDIRS; 70193323Sedimport static com.sun.tools.javac.main.Option.EXTDIRS; 71193323Sedimport static com.sun.tools.javac.main.Option.XBOOTCLASSPATH; 72193323Sedimport static com.sun.tools.javac.main.Option.XBOOTCLASSPATH_APPEND; 73193323Sedimport static com.sun.tools.javac.main.Option.XBOOTCLASSPATH_PREPEND; 74193323Sed 75193323Sed/** 76193323Sed * This class converts command line arguments, environment variables and system properties (in 77193323Sed * File.pathSeparator-separated String form) into a boot class path, user class path, and source 78193323Sed * path (in {@code Collection<String>} form). 79193323Sed * 80193323Sed * <p> 81193323Sed * <b>This is NOT part of any supported API. If you write code that depends on this, you do so at 82193323Sed * your own risk. This code and its internal interfaces are subject to change or deletion without 83193323Sed * notice.</b> 84198396Srdivacky */ 85198396Srdivackypublic class Locations { 86198396Srdivacky 87212904Sdim /** 88193323Sed * The log to use for warning output 89193323Sed */ 90193323Sed private Log log; 91193323Sed 92193323Sed /** 93193323Sed * Access to (possibly cached) file info 94193323Sed */ 95193323Sed private FSInfo fsInfo; 96193323Sed 97193323Sed /** 98193323Sed * Whether to warn about non-existent path elements 99193323Sed */ 100212904Sdim private boolean warn; 101198396Srdivacky 102198396Srdivacky public Locations() { 103198396Srdivacky initHandlers(); 104212904Sdim } 105212904Sdim 106212904Sdim // could replace Lint by "boolean warn" 107212904Sdim public void update(Log log, Lint lint, FSInfo fsInfo) { 108212904Sdim this.log = log; 109245431Sdim warn = lint.isEnabled(Lint.LintCategory.PATH); 110245431Sdim this.fsInfo = fsInfo; 111245431Sdim } 112245431Sdim 113212904Sdim public Collection<Path> bootClassPath() { 114212904Sdim return getLocation(PLATFORM_CLASS_PATH); 115212904Sdim } 116212904Sdim 117212904Sdim public boolean isDefaultBootClassPath() { 118245431Sdim BootClassPathLocationHandler h 119198396Srdivacky = (BootClassPathLocationHandler) getHandler(PLATFORM_CLASS_PATH); 120245431Sdim return h.isDefault(); 121193323Sed } 122193323Sed 123263509Sdim boolean isDefaultBootClassPathRtJar(Path file) { 124263509Sdim BootClassPathLocationHandler h 125193323Sed = (BootClassPathLocationHandler) getHandler(PLATFORM_CLASS_PATH); 126193323Sed return h.isDefaultRtJar(file); 127193323Sed } 128193323Sed 129193323Sed public Collection<Path> userClassPath() { 130193323Sed return getLocation(CLASS_PATH); 131193323Sed } 132193323Sed 133193323Sed public Collection<Path> sourcePath() { 134193323Sed Collection<Path> p = getLocation(SOURCE_PATH); 135193323Sed // TODO: this should be handled by the LocationHandler 136193323Sed return p == null || p.isEmpty() ? null : p; 137193323Sed } 138193323Sed 139193323Sed /** 140193323Sed * Split a search path into its elements. Empty path elements will be ignored. 141193323Sed * 142193323Sed * @param searchPath The search path to be split 143193323Sed * @return The elements of the path 144208599Srdivacky */ 145208599Srdivacky private static Iterable<Path> getPathEntries(String searchPath) { 146208599Srdivacky return getPathEntries(searchPath, null); 147208599Srdivacky } 148193323Sed 149193323Sed /** 150193323Sed * Split a search path into its elements. If emptyPathDefault is not null, all empty elements in the 151193323Sed * path, including empty elements at either end of the path, will be replaced with the value of 152193323Sed * emptyPathDefault. 153193323Sed * 154212904Sdim * @param searchPath The search path to be split 155193323Sed * @param emptyPathDefault The value to substitute for empty path elements, or null, to ignore 156198090Srdivacky * empty path elements 157198090Srdivacky * @return The elements of the path 158198090Srdivacky */ 159198090Srdivacky private static Iterable<Path> getPathEntries(String searchPath, Path emptyPathDefault) { 160198090Srdivacky ListBuffer<Path> entries = new ListBuffer<>(); 161198090Srdivacky for (String s: searchPath.split(Pattern.quote(File.pathSeparator), -1)) { 162198090Srdivacky if (s.isEmpty()) { 163198090Srdivacky if (emptyPathDefault != null) { 164193323Sed entries.add(emptyPathDefault); 165212904Sdim } 166212904Sdim } else { 167193323Sed entries.add(Paths.get(s)); 168212904Sdim } 169193323Sed } 170193323Sed return entries; 171193323Sed } 172193323Sed 173193323Sed /** 174193323Sed * Utility class to help evaluate a path option. Duplicate entries are ignored, jar class paths 175208599Srdivacky * can be expanded. 176208599Srdivacky */ 177208599Srdivacky private class SearchPath extends LinkedHashSet<Path> { 178208599Srdivacky 179208599Srdivacky private static final long serialVersionUID = 0; 180208599Srdivacky 181193323Sed private boolean expandJarClassPaths = false; 182193323Sed private final Set<Path> canonicalValues = new HashSet<>(); 183193323Sed 184193323Sed public SearchPath expandJarClassPaths(boolean x) { 185193323Sed expandJarClassPaths = x; 186226890Sdim return this; 187226890Sdim } 188226890Sdim 189226890Sdim /** 190193323Sed * What to use when path element is the empty string 191193323Sed */ 192193323Sed private Path emptyPathDefault = null; 193193323Sed 194193323Sed public SearchPath emptyPathDefault(Path x) { 195193323Sed emptyPathDefault = x; 196193323Sed return this; 197212904Sdim } 198193323Sed 199193323Sed public SearchPath addDirectories(String dirs, boolean warn) { 200193323Sed boolean prev = expandJarClassPaths; 201193323Sed expandJarClassPaths = true; 202193323Sed try { 203198090Srdivacky if (dirs != null) { 204198090Srdivacky for (Path dir : getPathEntries(dirs)) { 205198090Srdivacky addDirectory(dir, warn); 206198090Srdivacky } 207212904Sdim } 208212904Sdim return this; 209212904Sdim } finally { 210212904Sdim expandJarClassPaths = prev; 211212904Sdim } 212212904Sdim } 213212904Sdim 214212904Sdim public SearchPath addDirectories(String dirs) { 215212904Sdim return addDirectories(dirs, warn); 216212904Sdim } 217212904Sdim 218212904Sdim private void addDirectory(Path dir, boolean warn) { 219212904Sdim if (!Files.isDirectory(dir)) { 220212904Sdim if (warn) { 221212904Sdim log.warning(Lint.LintCategory.PATH, 222212904Sdim "dir.path.element.not.found", dir); 223252723Sdim } 224252723Sdim return; 225263509Sdim } 226263765Sdim 227263765Sdim try (Stream<Path> s = Files.list(dir)) { 228263765Sdim s.filter(dirEntry -> isArchive(dirEntry)) 229263765Sdim .forEach(dirEntry -> addFile(dirEntry, warn)); 230263509Sdim } catch (IOException ignore) { 231193323Sed } 232263509Sdim } 233263509Sdim 234193323Sed public SearchPath addFiles(String files, boolean warn) { 235193323Sed if (files != null) { 236193323Sed addFiles(getPathEntries(files, emptyPathDefault), warn); 237208599Srdivacky } 238208599Srdivacky return this; 239193323Sed } 240193323Sed 241226890Sdim public SearchPath addFiles(String files) { 242193323Sed return addFiles(files, warn); 243198090Srdivacky } 244212904Sdim 245212904Sdim public SearchPath addFiles(Iterable<? extends Path> files, boolean warn) { 246212904Sdim if (files != null) { 247263765Sdim for (Path file : files) { 248193323Sed addFile(file, warn); 249193323Sed } 250193323Sed } 251193323Sed return this; 252193323Sed } 253193323Sed 254193323Sed public SearchPath addFiles(Iterable<? extends Path> files) { 255193323Sed return addFiles(files, warn); 256193323Sed } 257193323Sed 258193323Sed public void addFile(Path file, boolean warn) { 259193323Sed if (contains(file)) { 260193323Sed // discard duplicates 261193323Sed return; 262193323Sed } 263193323Sed 264193323Sed if (!fsInfo.exists(file)) { 265193323Sed /* No such file or directory exists */ 266193323Sed if (warn) { 267226890Sdim log.warning(Lint.LintCategory.PATH, 268226890Sdim "path.element.not.found", file); 269226890Sdim } 270226890Sdim super.add(file); 271226890Sdim return; 272193323Sed } 273193323Sed 274193323Sed Path canonFile = fsInfo.getCanonicalFile(file); 275193323Sed if (canonicalValues.contains(canonFile)) { 276193323Sed /* Discard duplicates and avoid infinite recursion */ 277193323Sed return; 278212904Sdim } 279212904Sdim 280208599Srdivacky if (fsInfo.isFile(file)) { 281208599Srdivacky /* File is an ordinary file. */ 282208599Srdivacky if (!isArchive(file)) { 283208599Srdivacky /* Not a recognized extension; open it to see if 284193323Sed it looks like a valid zip file. */ 285193323Sed try { 286193323Sed ZipFile z = new ZipFile(file.toFile()); 287193323Sed z.close(); 288193323Sed if (warn) { 289193323Sed log.warning(Lint.LintCategory.PATH, 290193323Sed "unexpected.archive.file", file); 291193323Sed } 292212904Sdim } catch (IOException e) { 293193323Sed // FIXME: include e.getLocalizedMessage in warning 294193323Sed if (warn) { 295212904Sdim log.warning(Lint.LintCategory.PATH, 296193323Sed "invalid.archive.file", file); 297193323Sed } 298193323Sed return; 299212904Sdim } 300212904Sdim } 301212904Sdim } 302212904Sdim 303212904Sdim /* Now what we have left is either a directory or a file name 304212904Sdim conforming to archive naming convention */ 305212904Sdim super.add(file); 306212904Sdim canonicalValues.add(canonFile); 307212904Sdim 308212904Sdim if (expandJarClassPaths && fsInfo.isFile(file)) { 309212904Sdim addJarClassPath(file, warn); 310212904Sdim } 311212904Sdim } 312212904Sdim 313212904Sdim // Adds referenced classpath elements from a jar's Class-Path 314212904Sdim // Manifest entry. In some future release, we may want to 315212904Sdim // update this code to recognize URLs rather than simple 316212904Sdim // filenames, but if we do, we should redo all path-related code. 317212904Sdim private void addJarClassPath(Path jarFile, boolean warn) { 318212904Sdim try { 319212904Sdim for (Path f : fsInfo.getJarClassPath(jarFile)) { 320212904Sdim addFile(f, warn); 321212904Sdim } 322212904Sdim } catch (IOException e) { 323212904Sdim log.error("error.reading.file", jarFile, JavacFileManager.getMessage(e)); 324212904Sdim } 325212904Sdim } 326212904Sdim } 327212904Sdim 328212904Sdim /** 329212904Sdim * Base class for handling support for the representation of Locations. Implementations are 330212904Sdim * responsible for handling the interactions between the command line options for a location, 331212904Sdim * and API access via setLocation. 332212904Sdim * 333212904Sdim * @see #initHandlers 334212904Sdim * @see #getHandler 335212904Sdim */ 336212904Sdim protected abstract class LocationHandler { 337212904Sdim 338212904Sdim final Location location; 339212904Sdim final Set<Option> options; 340212904Sdim 341212904Sdim /** 342212904Sdim * Create a handler. The location and options provide a way to map from a location or an 343212904Sdim * option to the corresponding handler. 344212904Sdim * 345212904Sdim * @param location the location for which this is the handler 346212904Sdim * @param options the options affecting this location 347212904Sdim * @see #initHandlers 348212904Sdim */ 349212904Sdim protected LocationHandler(Location location, Option... options) { 350193323Sed this.location = location; 351193323Sed this.options = options.length == 0 352193323Sed ? EnumSet.noneOf(Option.class) 353193323Sed : EnumSet.copyOf(Arrays.asList(options)); 354193323Sed } 355193323Sed 356193323Sed /** 357193323Sed * @see JavaFileManager#handleOption 358193323Sed */ 359193323Sed abstract boolean handleOption(Option option, String value); 360193323Sed 361193323Sed /** 362193323Sed * @see StandardJavaFileManager#getLocation 363193323Sed */ 364193323Sed abstract Collection<Path> getLocation(); 365193323Sed 366193323Sed /** 367193323Sed * @see StandardJavaFileManager#setLocation 368193323Sed */ 369193323Sed abstract void setLocation(Iterable<? extends Path> files) throws IOException; 370193323Sed } 371193323Sed 372193323Sed /** 373193323Sed * General purpose implementation for output locations, such as -d/CLASS_OUTPUT and 374193323Sed * -s/SOURCE_OUTPUT. All options are treated as equivalent (i.e. aliases.) The value is a single 375193323Sed * file, possibly null. 376193323Sed */ 377245431Sdim private class OutputLocationHandler extends LocationHandler { 378193323Sed 379193323Sed private Path outputDir; 380245431Sdim 381245431Sdim OutputLocationHandler(Location location, Option... options) { 382245431Sdim super(location, options); 383245431Sdim } 384245431Sdim 385245431Sdim @Override 386245431Sdim boolean handleOption(Option option, String value) { 387245431Sdim if (!options.contains(option)) { 388212904Sdim return false; 389212904Sdim } 390212904Sdim 391212904Sdim // TODO: could/should validate outputDir exists and is a directory 392212904Sdim // need to decide how best to report issue for benefit of 393212904Sdim // direct API call on JavaFileManager.handleOption(specifies IAE) 394212904Sdim // vs. command line decoding. 395212904Sdim outputDir = (value == null) ? null : Paths.get(value); 396193323Sed return true; 397193323Sed } 398193323Sed 399193323Sed @Override 400193323Sed Collection<Path> getLocation() { 401193323Sed return (outputDir == null) ? null : Collections.singleton(outputDir); 402193323Sed } 403193323Sed 404193323Sed @Override 405193323Sed void setLocation(Iterable<? extends Path> files) throws IOException { 406193323Sed if (files == null) { 407193323Sed outputDir = null; 408193323Sed } else { 409193323Sed Iterator<? extends Path> pathIter = files.iterator(); 410193323Sed if (!pathIter.hasNext()) { 411193323Sed throw new IllegalArgumentException("empty path for directory"); 412193323Sed } 413193323Sed Path dir = pathIter.next(); 414193323Sed if (pathIter.hasNext()) { 415193323Sed throw new IllegalArgumentException("path too long for directory"); 416193323Sed } 417193323Sed if (!Files.exists(dir)) { 418193323Sed throw new FileNotFoundException(dir + ": does not exist"); 419193323Sed } else if (!Files.isDirectory(dir)) { 420193323Sed throw new IOException(dir + ": not a directory"); 421193323Sed } 422193323Sed outputDir = dir; 423193323Sed } 424193323Sed } 425193323Sed } 426193323Sed 427212904Sdim /** 428252723Sdim * General purpose implementation for search path locations, such as -sourcepath/SOURCE_PATH and 429252723Sdim * -processorPath/ANNOTATION_PROCESS_PATH. All options are treated as equivalent (i.e. aliases.) 430252723Sdim * The value is an ordered set of files and/or directories. 431193323Sed */ 432193323Sed private class SimpleLocationHandler extends LocationHandler { 433193323Sed 434212904Sdim protected Collection<Path> searchPath; 435193323Sed 436193323Sed SimpleLocationHandler(Location location, Option... options) { 437193323Sed super(location, options); 438193323Sed } 439212904Sdim 440212904Sdim @Override 441193323Sed boolean handleOption(Option option, String value) { 442193323Sed if (!options.contains(option)) { 443193323Sed return false; 444212904Sdim } 445245431Sdim searchPath = value == null ? null 446245431Sdim : Collections.unmodifiableCollection(createPath().addFiles(value)); 447252723Sdim return true; 448200581Srdivacky } 449208599Srdivacky 450208599Srdivacky @Override 451208599Srdivacky Collection<Path> getLocation() { 452208599Srdivacky return searchPath; 453208599Srdivacky } 454208599Srdivacky 455208599Srdivacky @Override 456193323Sed void setLocation(Iterable<? extends Path> files) { 457193323Sed SearchPath p; 458193323Sed if (files == null) { 459263765Sdim p = computePath(null); 460263765Sdim } else { 461263765Sdim p = createPath().addFiles(files); 462263765Sdim } 463193323Sed searchPath = Collections.unmodifiableCollection(p); 464193323Sed } 465193323Sed 466193323Sed protected SearchPath computePath(String value) { 467193323Sed return createPath().addFiles(value); 468193323Sed } 469193323Sed 470193323Sed protected SearchPath createPath() { 471193323Sed return new SearchPath(); 472193323Sed } 473193323Sed } 474193323Sed 475193323Sed /** 476210299Sed * Subtype of SimpleLocationHandler for -classpath/CLASS_PATH. If no value is given, a default 477212904Sdim * is provided, based on system properties and other values. 478212904Sdim */ 479193323Sed private class ClassPathLocationHandler extends SimpleLocationHandler { 480193323Sed 481193323Sed ClassPathLocationHandler() { 482193323Sed super(StandardLocation.CLASS_PATH, 483193323Sed Option.CLASSPATH, Option.CP); 484193323Sed } 485193323Sed 486193323Sed @Override 487193323Sed Collection<Path> getLocation() { 488193323Sed lazy(); 489193323Sed return searchPath; 490193323Sed } 491193323Sed 492193323Sed @Override 493198396Srdivacky protected SearchPath computePath(String value) { 494198396Srdivacky String cp = value; 495198396Srdivacky 496198396Srdivacky // CLASSPATH environment variable when run from `javac'. 497198396Srdivacky if (cp == null) { 498235633Sdim cp = System.getProperty("env.class.path"); 499198396Srdivacky } 500198396Srdivacky 501193323Sed // If invoked via a java VM (not the javac launcher), use the 502193323Sed // platform class path 503193323Sed if (cp == null && System.getProperty("application.home") == null) { 504193323Sed cp = System.getProperty("java.class.path"); 505193323Sed } 506193323Sed 507193323Sed // Default to current working directory. 508193323Sed if (cp == null) { 509212904Sdim cp = "."; 510212904Sdim } 511193323Sed 512212904Sdim return createPath().addFiles(cp); 513252723Sdim } 514193323Sed 515212904Sdim @Override 516212904Sdim protected SearchPath createPath() { 517212904Sdim return new SearchPath() 518199481Srdivacky .expandJarClassPaths(true) // Only search user jars for Class-Paths 519252723Sdim .emptyPathDefault(Paths.get(".")); // Empty path elt ==> current directory 520199481Srdivacky } 521193323Sed 522193323Sed private void lazy() { 523193323Sed if (searchPath == null) { 524193323Sed setLocation(null); 525193323Sed } 526193323Sed } 527193323Sed } 528193323Sed 529193323Sed /** 530193323Sed * Custom subtype of LocationHandler for PLATFORM_CLASS_PATH. Various options are supported for 531193323Sed * different components of the platform class path. Setting a value with setLocation overrides 532193323Sed * all existing option values. Setting any option overrides any value set with setLocation, and 533263765Sdim * reverts to using default values for options that have not been set. Setting -bootclasspath or 534199481Srdivacky * -Xbootclasspath overrides any existing value for -Xbootclasspath/p: and -Xbootclasspath/a:. 535193323Sed */ 536193323Sed private class BootClassPathLocationHandler extends LocationHandler { 537193323Sed 538193323Sed private Collection<Path> searchPath; 539193323Sed final Map<Option, String> optionValues = new EnumMap<>(Option.class); 540193323Sed 541193323Sed /** 542193323Sed * rt.jar as found on the default bootclasspath. If the user specified a bootclasspath, null 543212904Sdim * is used. 544193323Sed */ 545193323Sed private Path defaultBootClassPathRtJar = null; 546193323Sed 547198090Srdivacky /** 548198090Srdivacky * Is bootclasspath the default? 549198090Srdivacky */ 550198090Srdivacky private boolean isDefaultBootClassPath; 551198090Srdivacky 552198090Srdivacky BootClassPathLocationHandler() { 553198090Srdivacky super(StandardLocation.PLATFORM_CLASS_PATH, 554198090Srdivacky Option.BOOTCLASSPATH, Option.XBOOTCLASSPATH, 555198090Srdivacky Option.XBOOTCLASSPATH_PREPEND, 556198090Srdivacky Option.XBOOTCLASSPATH_APPEND, 557198090Srdivacky Option.ENDORSEDDIRS, Option.DJAVA_ENDORSED_DIRS, 558198090Srdivacky Option.EXTDIRS, Option.DJAVA_EXT_DIRS); 559198090Srdivacky } 560198090Srdivacky 561198090Srdivacky boolean isDefault() { 562198090Srdivacky lazy(); 563193323Sed return isDefaultBootClassPath; 564212904Sdim } 565193323Sed 566198090Srdivacky boolean isDefaultRtJar(Path file) { 567193323Sed lazy(); 568198090Srdivacky return file.equals(defaultBootClassPathRtJar); 569193323Sed } 570193323Sed 571193323Sed @Override 572193323Sed boolean handleOption(Option option, String value) { 573193323Sed if (!options.contains(option)) { 574193323Sed return false; 575 } 576 577 option = canonicalize(option); 578 optionValues.put(option, value); 579 if (option == BOOTCLASSPATH) { 580 optionValues.remove(XBOOTCLASSPATH_PREPEND); 581 optionValues.remove(XBOOTCLASSPATH_APPEND); 582 } 583 searchPath = null; // reset to "uninitialized" 584 return true; 585 } 586 // where 587 // TODO: would be better if option aliasing was handled at a higher 588 // level 589 private Option canonicalize(Option option) { 590 switch (option) { 591 case XBOOTCLASSPATH: 592 return Option.BOOTCLASSPATH; 593 case DJAVA_ENDORSED_DIRS: 594 return Option.ENDORSEDDIRS; 595 case DJAVA_EXT_DIRS: 596 return Option.EXTDIRS; 597 default: 598 return option; 599 } 600 } 601 602 @Override 603 Collection<Path> getLocation() { 604 lazy(); 605 return searchPath; 606 } 607 608 @Override 609 void setLocation(Iterable<? extends Path> files) { 610 if (files == null) { 611 searchPath = null; // reset to "uninitialized" 612 } else { 613 defaultBootClassPathRtJar = null; 614 isDefaultBootClassPath = false; 615 SearchPath p = new SearchPath().addFiles(files, false); 616 searchPath = Collections.unmodifiableCollection(p); 617 optionValues.clear(); 618 } 619 } 620 621 SearchPath computePath() { 622 defaultBootClassPathRtJar = null; 623 SearchPath path = new SearchPath(); 624 625 String bootclasspathOpt = optionValues.get(BOOTCLASSPATH); 626 String endorseddirsOpt = optionValues.get(ENDORSEDDIRS); 627 String extdirsOpt = optionValues.get(EXTDIRS); 628 String xbootclasspathPrependOpt = optionValues.get(XBOOTCLASSPATH_PREPEND); 629 String xbootclasspathAppendOpt = optionValues.get(XBOOTCLASSPATH_APPEND); 630 path.addFiles(xbootclasspathPrependOpt); 631 632 if (endorseddirsOpt != null) { 633 path.addDirectories(endorseddirsOpt); 634 } else { 635 path.addDirectories(System.getProperty("java.endorsed.dirs"), false); 636 } 637 638 if (bootclasspathOpt != null) { 639 path.addFiles(bootclasspathOpt); 640 } else { 641 // Standard system classes for this compiler's release. 642 String files = System.getProperty("sun.boot.class.path"); 643 path.addFiles(files, false); 644 Path rt_jar = Paths.get("rt.jar"); 645 for (Path file : getPathEntries(files)) { 646 if (file.getFileName().equals(rt_jar)) { 647 defaultBootClassPathRtJar = file; 648 } 649 } 650 } 651 652 path.addFiles(xbootclasspathAppendOpt); 653 654 // Strictly speaking, standard extensions are not bootstrap 655 // classes, but we treat them identically, so we'll pretend 656 // that they are. 657 if (extdirsOpt != null) { 658 path.addDirectories(extdirsOpt); 659 } else { 660 path.addDirectories(System.getProperty("java.ext.dirs"), false); 661 } 662 663 isDefaultBootClassPath 664 = (xbootclasspathPrependOpt == null) 665 && (bootclasspathOpt == null) 666 && (xbootclasspathAppendOpt == null); 667 668 return path; 669 } 670 671 private void lazy() { 672 if (searchPath == null) { 673 searchPath = Collections.unmodifiableCollection(computePath()); 674 } 675 } 676 } 677 678 Map<Location, LocationHandler> handlersForLocation; 679 Map<Option, LocationHandler> handlersForOption; 680 681 void initHandlers() { 682 handlersForLocation = new HashMap<>(); 683 handlersForOption = new EnumMap<>(Option.class); 684 685 LocationHandler[] handlers = { 686 new BootClassPathLocationHandler(), 687 new ClassPathLocationHandler(), 688 new SimpleLocationHandler(StandardLocation.SOURCE_PATH, Option.SOURCEPATH), 689 new SimpleLocationHandler(StandardLocation.ANNOTATION_PROCESSOR_PATH, Option.PROCESSORPATH), 690 new OutputLocationHandler((StandardLocation.CLASS_OUTPUT), Option.D), 691 new OutputLocationHandler((StandardLocation.SOURCE_OUTPUT), Option.S), 692 new OutputLocationHandler((StandardLocation.NATIVE_HEADER_OUTPUT), Option.H) 693 }; 694 695 for (LocationHandler h : handlers) { 696 handlersForLocation.put(h.location, h); 697 for (Option o : h.options) { 698 handlersForOption.put(o, h); 699 } 700 } 701 } 702 703 public boolean handleOption(Option option, String value) { 704 LocationHandler h = handlersForOption.get(option); 705 return (h == null ? false : h.handleOption(option, value)); 706 } 707 708 Collection<Path> getLocation(Location location) { 709 LocationHandler h = getHandler(location); 710 return (h == null ? null : h.getLocation()); 711 } 712 713 Path getOutputLocation(Location location) { 714 if (!location.isOutputLocation()) { 715 throw new IllegalArgumentException(); 716 } 717 LocationHandler h = getHandler(location); 718 return ((OutputLocationHandler) h).outputDir; 719 } 720 721 void setLocation(Location location, Iterable<? extends Path> files) throws IOException { 722 LocationHandler h = getHandler(location); 723 if (h == null) { 724 if (location.isOutputLocation()) { 725 h = new OutputLocationHandler(location); 726 } else { 727 h = new SimpleLocationHandler(location); 728 } 729 handlersForLocation.put(location, h); 730 } 731 h.setLocation(files); 732 } 733 734 protected LocationHandler getHandler(Location location) { 735 location.getClass(); // null check 736 return handlersForLocation.get(location); 737 } 738 739 /** 740 * Is this the name of an archive file? 741 */ 742 private boolean isArchive(Path file) { 743 String n = StringUtils.toLowerCase(file.getFileName().toString()); 744 return fsInfo.isFile(file) 745 && (n.endsWith(".jar") || n.endsWith(".zip")); 746 } 747 748 /** 749 * Utility method for converting a search path string to an array of directory and JAR file 750 * URLs. 751 * 752 * Note that this method is called by the DocletInvoker. 753 * 754 * @param path the search path string 755 * @return the resulting array of directory and JAR file URLs 756 */ 757 public static URL[] pathToURLs(String path) { 758 java.util.List<URL> urls = new ArrayList<>(); 759 for (String s: path.split(Pattern.quote(File.pathSeparator))) { 760 if (!s.isEmpty()) { 761 URL url = fileToURL(Paths.get(s)); 762 if (url != null) { 763 urls.add(url); 764 } 765 } 766 } 767 return urls.toArray(new URL[urls.size()]); 768 } 769 770 /** 771 * Returns the directory or JAR file URL corresponding to the specified local file name. 772 * 773 * @param file the Path object 774 * @return the resulting directory or JAR file URL, or null if unknown 775 */ 776 private static URL fileToURL(Path file) { 777 Path p; 778 try { 779 p = file.toRealPath(); 780 } catch (IOException e) { 781 p = file.toAbsolutePath(); 782 } 783 try { 784 return p.normalize().toUri().toURL(); 785 } catch (MalformedURLException e) { 786 return null; 787 } 788 } 789} 790