Wed, 30 Apr 2014 23:26:43 +0100
8038023: Compiler crash ClassCastException
Summary: Add additional checks on results of ClassReader.readPool
Reviewed-by: vromero
jjg@450 | 1 | /* |
jjg@1413 | 2 | * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. |
jjg@450 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
jjg@450 | 4 | * |
jjg@450 | 5 | * This code is free software; you can redistribute it and/or modify it |
jjg@450 | 6 | * under the terms of the GNU General Public License version 2 only, as |
ohair@554 | 7 | * published by the Free Software Foundation. Oracle designates this |
jjg@450 | 8 | * particular file as subject to the "Classpath" exception as provided |
ohair@554 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
jjg@450 | 10 | * |
jjg@450 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
jjg@450 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
jjg@450 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
jjg@450 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
jjg@450 | 15 | * accompanied this code). |
jjg@450 | 16 | * |
jjg@450 | 17 | * You should have received a copy of the GNU General Public License version |
jjg@450 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
jjg@450 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
jjg@450 | 20 | * |
ohair@554 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
ohair@554 | 22 | * or visit www.oracle.com if you need additional information or have any |
ohair@554 | 23 | * questions. |
jjg@450 | 24 | */ |
jjg@450 | 25 | |
jjg@450 | 26 | package com.sun.tools.javac.nio; |
jjg@450 | 27 | |
jjg@450 | 28 | import java.io.File; |
jjg@450 | 29 | import java.io.IOException; |
jjg@450 | 30 | import java.net.MalformedURLException; |
jjg@450 | 31 | import java.net.URL; |
jjg@450 | 32 | import java.nio.charset.Charset; |
jjg@450 | 33 | import java.nio.file.Files; |
jjg@450 | 34 | import java.nio.file.FileSystem; |
jjg@450 | 35 | import java.nio.file.FileSystems; |
jjg@450 | 36 | import java.nio.file.FileVisitOption; |
jjg@450 | 37 | import java.nio.file.FileVisitResult; |
jjg@450 | 38 | import java.nio.file.Path; |
jjg@450 | 39 | import java.nio.file.SimpleFileVisitor; |
jjg@450 | 40 | import java.nio.file.attribute.BasicFileAttributes; |
jjg@450 | 41 | import java.util.ArrayList; |
jjg@450 | 42 | import java.util.Arrays; |
jjg@450 | 43 | import java.util.Collection; |
jjg@450 | 44 | import java.util.Collections; |
jjg@450 | 45 | import java.util.EnumSet; |
jjg@450 | 46 | import java.util.HashMap; |
jjg@450 | 47 | import java.util.Iterator; |
jjg@450 | 48 | import java.util.LinkedHashSet; |
jjg@450 | 49 | import java.util.Map; |
jjg@450 | 50 | import java.util.Set; |
jjg@450 | 51 | import javax.lang.model.SourceVersion; |
jjg@450 | 52 | import javax.tools.FileObject; |
jjg@450 | 53 | import javax.tools.JavaFileManager; |
jjg@450 | 54 | import javax.tools.JavaFileObject; |
jjg@450 | 55 | import javax.tools.JavaFileObject.Kind; |
jjg@450 | 56 | import javax.tools.StandardLocation; |
jjg@450 | 57 | |
jjg@450 | 58 | import static java.nio.file.FileVisitOption.*; |
jjg@450 | 59 | import static javax.tools.StandardLocation.*; |
jjg@450 | 60 | |
jjg@450 | 61 | import com.sun.tools.javac.util.BaseFileManager; |
jjg@450 | 62 | import com.sun.tools.javac.util.Context; |
jjg@450 | 63 | import com.sun.tools.javac.util.List; |
jjg@450 | 64 | import com.sun.tools.javac.util.ListBuffer; |
jjg@450 | 65 | |
jjg@1157 | 66 | import static com.sun.tools.javac.main.Option.*; |
jjg@450 | 67 | |
jjg@450 | 68 | |
jjg@450 | 69 | // NOTE the imports carefully for this compilation unit. |
jjg@450 | 70 | // |
jjg@450 | 71 | // Path: java.nio.file.Path -- the new NIO type for which this file manager exists |
jjg@450 | 72 | // |
jjg@450 | 73 | // Paths: com.sun.tools.javac.file.Paths -- legacy javac type for handling path options |
jjg@450 | 74 | // The other Paths (java.nio.file.Paths) is not used |
jjg@450 | 75 | |
jjg@450 | 76 | // NOTE this and related classes depend on new API in JDK 7. |
jjg@450 | 77 | // This requires special handling while bootstrapping the JDK build, |
jjg@450 | 78 | // when these classes might not yet have been compiled. To workaround |
jjg@450 | 79 | // this, the build arranges to make stubs of these classes available |
jjg@450 | 80 | // when compiling this and related classes. The set of stub files |
jjg@450 | 81 | // is specified in make/build.properties. |
jjg@450 | 82 | |
jjg@450 | 83 | /** |
jjg@450 | 84 | * Implementation of PathFileManager: a JavaFileManager based on the use |
jjg@450 | 85 | * of java.nio.file.Path. |
jjg@450 | 86 | * |
jjg@450 | 87 | * <p>Just as a Path is somewhat analagous to a File, so too is this |
jjg@450 | 88 | * JavacPathFileManager analogous to JavacFileManager, as it relates to the |
jjg@450 | 89 | * support of FileObjects based on File objects (i.e. just RegularFileObject, |
jjg@450 | 90 | * not ZipFileObject and its variants.) |
jjg@450 | 91 | * |
jjg@450 | 92 | * <p>The default values for the standard locations supported by this file |
jjg@450 | 93 | * manager are the same as the default values provided by JavacFileManager -- |
jjg@450 | 94 | * i.e. as determined by the javac.file.Paths class. To override these values, |
jjg@450 | 95 | * call {@link #setLocation}. |
jjg@450 | 96 | * |
jjg@450 | 97 | * <p>To reduce confusion with Path objects, the locations such as "class path", |
jjg@450 | 98 | * "source path", etc, are generically referred to here as "search paths". |
jjg@450 | 99 | * |
jjg@581 | 100 | * <p><b>This is NOT part of any supported API. |
jjg@581 | 101 | * If you write code that depends on this, you do so at your own risk. |
jjg@450 | 102 | * This code and its internal interfaces are subject to change or |
jjg@450 | 103 | * deletion without notice.</b> |
jjg@450 | 104 | */ |
jjg@450 | 105 | public class JavacPathFileManager extends BaseFileManager implements PathFileManager { |
jjg@450 | 106 | protected FileSystem defaultFileSystem; |
jjg@450 | 107 | |
jjg@450 | 108 | /** |
jjg@450 | 109 | * Create a JavacPathFileManager using a given context, optionally registering |
jjg@450 | 110 | * it as the JavaFileManager for that context. |
jjg@450 | 111 | */ |
jjg@450 | 112 | public JavacPathFileManager(Context context, boolean register, Charset charset) { |
jjg@450 | 113 | super(charset); |
jjg@450 | 114 | if (register) |
jjg@450 | 115 | context.put(JavaFileManager.class, this); |
jjg@450 | 116 | pathsForLocation = new HashMap<Location, PathsForLocation>(); |
jjg@450 | 117 | fileSystems = new HashMap<Path,FileSystem>(); |
jjg@450 | 118 | setContext(context); |
jjg@450 | 119 | } |
jjg@450 | 120 | |
jjg@450 | 121 | /** |
jjg@450 | 122 | * Set the context for JavacPathFileManager. |
jjg@450 | 123 | */ |
jjg@450 | 124 | @Override |
jjg@1111 | 125 | public void setContext(Context context) { |
jjg@450 | 126 | super.setContext(context); |
jjg@450 | 127 | } |
jjg@450 | 128 | |
jjg@450 | 129 | @Override |
jjg@450 | 130 | public FileSystem getDefaultFileSystem() { |
jjg@450 | 131 | if (defaultFileSystem == null) |
jjg@450 | 132 | defaultFileSystem = FileSystems.getDefault(); |
jjg@450 | 133 | return defaultFileSystem; |
jjg@450 | 134 | } |
jjg@450 | 135 | |
jjg@450 | 136 | @Override |
jjg@450 | 137 | public void setDefaultFileSystem(FileSystem fs) { |
jjg@450 | 138 | defaultFileSystem = fs; |
jjg@450 | 139 | } |
jjg@450 | 140 | |
jjg@450 | 141 | @Override |
jjg@450 | 142 | public void flush() throws IOException { |
jjg@450 | 143 | contentCache.clear(); |
jjg@450 | 144 | } |
jjg@450 | 145 | |
jjg@450 | 146 | @Override |
jjg@450 | 147 | public void close() throws IOException { |
jjg@450 | 148 | for (FileSystem fs: fileSystems.values()) |
jjg@450 | 149 | fs.close(); |
jjg@450 | 150 | } |
jjg@450 | 151 | |
jjg@450 | 152 | @Override |
jjg@450 | 153 | public ClassLoader getClassLoader(Location location) { |
jjg@450 | 154 | nullCheck(location); |
jjg@450 | 155 | Iterable<? extends Path> path = getLocation(location); |
jjg@450 | 156 | if (path == null) |
jjg@450 | 157 | return null; |
jjg@450 | 158 | ListBuffer<URL> lb = new ListBuffer<URL>(); |
jjg@450 | 159 | for (Path p: path) { |
jjg@450 | 160 | try { |
jjg@450 | 161 | lb.append(p.toUri().toURL()); |
jjg@450 | 162 | } catch (MalformedURLException e) { |
jjg@450 | 163 | throw new AssertionError(e); |
jjg@450 | 164 | } |
jjg@450 | 165 | } |
jjg@450 | 166 | |
jjg@450 | 167 | return getClassLoader(lb.toArray(new URL[lb.size()])); |
jjg@450 | 168 | } |
jjg@450 | 169 | |
jjg@757 | 170 | @Override |
jjg@757 | 171 | public boolean isDefaultBootClassPath() { |
jjg@1116 | 172 | return locations.isDefaultBootClassPath(); |
jjg@757 | 173 | } |
jjg@757 | 174 | |
jjg@450 | 175 | // <editor-fold defaultstate="collapsed" desc="Location handling"> |
jjg@450 | 176 | |
jjg@450 | 177 | public boolean hasLocation(Location location) { |
jjg@450 | 178 | return (getLocation(location) != null); |
jjg@450 | 179 | } |
jjg@450 | 180 | |
jjg@450 | 181 | public Iterable<? extends Path> getLocation(Location location) { |
jjg@450 | 182 | nullCheck(location); |
jjg@450 | 183 | lazyInitSearchPaths(); |
jjg@450 | 184 | PathsForLocation path = pathsForLocation.get(location); |
jjg@450 | 185 | if (path == null && !pathsForLocation.containsKey(location)) { |
jjg@450 | 186 | setDefaultForLocation(location); |
jjg@450 | 187 | path = pathsForLocation.get(location); |
jjg@450 | 188 | } |
jjg@450 | 189 | return path; |
jjg@450 | 190 | } |
jjg@450 | 191 | |
jjg@450 | 192 | private Path getOutputLocation(Location location) { |
jjg@450 | 193 | Iterable<? extends Path> paths = getLocation(location); |
jjg@450 | 194 | return (paths == null ? null : paths.iterator().next()); |
jjg@450 | 195 | } |
jjg@450 | 196 | |
jjg@450 | 197 | public void setLocation(Location location, Iterable<? extends Path> searchPath) |
jjg@450 | 198 | throws IOException |
jjg@450 | 199 | { |
jjg@450 | 200 | nullCheck(location); |
jjg@450 | 201 | lazyInitSearchPaths(); |
jjg@450 | 202 | if (searchPath == null) { |
jjg@450 | 203 | setDefaultForLocation(location); |
jjg@450 | 204 | } else { |
jjg@450 | 205 | if (location.isOutputLocation()) |
jjg@450 | 206 | checkOutputPath(searchPath); |
jjg@450 | 207 | PathsForLocation pl = new PathsForLocation(); |
jjg@450 | 208 | for (Path p: searchPath) |
jjg@450 | 209 | pl.add(p); // TODO -Xlint:path warn if path not found |
jjg@450 | 210 | pathsForLocation.put(location, pl); |
jjg@450 | 211 | } |
jjg@450 | 212 | } |
jjg@450 | 213 | |
jjg@450 | 214 | private void checkOutputPath(Iterable<? extends Path> searchPath) throws IOException { |
jjg@450 | 215 | Iterator<? extends Path> pathIter = searchPath.iterator(); |
jjg@450 | 216 | if (!pathIter.hasNext()) |
jjg@450 | 217 | throw new IllegalArgumentException("empty path for directory"); |
jjg@450 | 218 | Path path = pathIter.next(); |
jjg@450 | 219 | if (pathIter.hasNext()) |
jjg@450 | 220 | throw new IllegalArgumentException("path too long for directory"); |
alanb@847 | 221 | if (!isDirectory(path)) |
jjg@450 | 222 | throw new IOException(path + ": not a directory"); |
jjg@450 | 223 | } |
jjg@450 | 224 | |
jjg@450 | 225 | private void setDefaultForLocation(Location locn) { |
jjg@450 | 226 | Collection<File> files = null; |
jjg@450 | 227 | if (locn instanceof StandardLocation) { |
jjg@450 | 228 | switch ((StandardLocation) locn) { |
jjg@450 | 229 | case CLASS_PATH: |
jjg@1116 | 230 | files = locations.userClassPath(); |
jjg@450 | 231 | break; |
jjg@450 | 232 | case PLATFORM_CLASS_PATH: |
jjg@1116 | 233 | files = locations.bootClassPath(); |
jjg@450 | 234 | break; |
jjg@450 | 235 | case SOURCE_PATH: |
jjg@1116 | 236 | files = locations.sourcePath(); |
jjg@450 | 237 | break; |
jjg@450 | 238 | case CLASS_OUTPUT: { |
jjg@450 | 239 | String arg = options.get(D); |
jjg@450 | 240 | files = (arg == null ? null : Collections.singleton(new File(arg))); |
jjg@450 | 241 | break; |
jjg@450 | 242 | } |
jjg@450 | 243 | case SOURCE_OUTPUT: { |
jjg@450 | 244 | String arg = options.get(S); |
jjg@450 | 245 | files = (arg == null ? null : Collections.singleton(new File(arg))); |
jjg@450 | 246 | break; |
jjg@450 | 247 | } |
jjg@450 | 248 | } |
jjg@450 | 249 | } |
jjg@450 | 250 | |
jjg@450 | 251 | PathsForLocation pl = new PathsForLocation(); |
jjg@450 | 252 | if (files != null) { |
jjg@450 | 253 | for (File f: files) |
jjg@450 | 254 | pl.add(f.toPath()); |
jjg@450 | 255 | } |
jjg@1413 | 256 | if (!pl.isEmpty()) |
jjg@1413 | 257 | pathsForLocation.put(locn, pl); |
jjg@450 | 258 | } |
jjg@450 | 259 | |
jjg@450 | 260 | private void lazyInitSearchPaths() { |
jjg@450 | 261 | if (!inited) { |
jjg@450 | 262 | setDefaultForLocation(PLATFORM_CLASS_PATH); |
jjg@450 | 263 | setDefaultForLocation(CLASS_PATH); |
jjg@450 | 264 | setDefaultForLocation(SOURCE_PATH); |
jjg@450 | 265 | inited = true; |
jjg@450 | 266 | } |
jjg@450 | 267 | } |
jjg@450 | 268 | // where |
jjg@450 | 269 | private boolean inited = false; |
jjg@450 | 270 | |
jjg@450 | 271 | private Map<Location, PathsForLocation> pathsForLocation; |
jjg@450 | 272 | |
jjg@450 | 273 | private static class PathsForLocation extends LinkedHashSet<Path> { |
jjg@450 | 274 | private static final long serialVersionUID = 6788510222394486733L; |
jjg@450 | 275 | } |
jjg@450 | 276 | |
jjg@450 | 277 | // </editor-fold> |
jjg@450 | 278 | |
jjg@450 | 279 | // <editor-fold defaultstate="collapsed" desc="FileObject handling"> |
jjg@450 | 280 | |
jjg@450 | 281 | @Override |
jjg@450 | 282 | public Path getPath(FileObject fo) { |
jjg@450 | 283 | nullCheck(fo); |
jjg@450 | 284 | if (!(fo instanceof PathFileObject)) |
jjg@450 | 285 | throw new IllegalArgumentException(); |
jjg@450 | 286 | return ((PathFileObject) fo).getPath(); |
jjg@450 | 287 | } |
jjg@450 | 288 | |
jjg@450 | 289 | @Override |
jjg@450 | 290 | public boolean isSameFile(FileObject a, FileObject b) { |
jjg@450 | 291 | nullCheck(a); |
jjg@450 | 292 | nullCheck(b); |
jjg@450 | 293 | if (!(a instanceof PathFileObject)) |
jjg@450 | 294 | throw new IllegalArgumentException("Not supported: " + a); |
jjg@450 | 295 | if (!(b instanceof PathFileObject)) |
jjg@450 | 296 | throw new IllegalArgumentException("Not supported: " + b); |
jjg@450 | 297 | return ((PathFileObject) a).isSameFile((PathFileObject) b); |
jjg@450 | 298 | } |
jjg@450 | 299 | |
jjg@450 | 300 | @Override |
jjg@450 | 301 | public Iterable<JavaFileObject> list(Location location, |
jjg@450 | 302 | String packageName, Set<Kind> kinds, boolean recurse) |
jjg@450 | 303 | throws IOException { |
jjg@450 | 304 | // validatePackageName(packageName); |
jjg@450 | 305 | nullCheck(packageName); |
jjg@450 | 306 | nullCheck(kinds); |
jjg@450 | 307 | |
jjg@450 | 308 | Iterable<? extends Path> paths = getLocation(location); |
jjg@450 | 309 | if (paths == null) |
jjg@450 | 310 | return List.nil(); |
jjg@450 | 311 | ListBuffer<JavaFileObject> results = new ListBuffer<JavaFileObject>(); |
jjg@450 | 312 | |
jjg@450 | 313 | for (Path path : paths) |
jjg@450 | 314 | list(path, packageName, kinds, recurse, results); |
jjg@450 | 315 | |
jjg@450 | 316 | return results.toList(); |
jjg@450 | 317 | } |
jjg@450 | 318 | |
jjg@450 | 319 | private void list(Path path, String packageName, final Set<Kind> kinds, |
jjg@450 | 320 | boolean recurse, final ListBuffer<JavaFileObject> results) |
jjg@450 | 321 | throws IOException { |
alanb@847 | 322 | if (!Files.exists(path)) |
jjg@450 | 323 | return; |
jjg@450 | 324 | |
jjg@450 | 325 | final Path pathDir; |
jjg@450 | 326 | if (isDirectory(path)) |
jjg@450 | 327 | pathDir = path; |
jjg@450 | 328 | else { |
jjg@450 | 329 | FileSystem fs = getFileSystem(path); |
jjg@450 | 330 | if (fs == null) |
jjg@450 | 331 | return; |
jjg@450 | 332 | pathDir = fs.getRootDirectories().iterator().next(); |
jjg@450 | 333 | } |
jjg@450 | 334 | String sep = path.getFileSystem().getSeparator(); |
jjg@450 | 335 | Path packageDir = packageName.isEmpty() ? pathDir |
jjg@450 | 336 | : pathDir.resolve(packageName.replace(".", sep)); |
alanb@847 | 337 | if (!Files.exists(packageDir)) |
jjg@450 | 338 | return; |
jjg@450 | 339 | |
jjg@450 | 340 | /* Alternate impl of list, superceded by use of Files.walkFileTree */ |
jjg@450 | 341 | // Deque<Path> queue = new LinkedList<Path>(); |
jjg@450 | 342 | // queue.add(packageDir); |
jjg@450 | 343 | // |
jjg@450 | 344 | // Path dir; |
jjg@450 | 345 | // while ((dir = queue.poll()) != null) { |
jjg@450 | 346 | // DirectoryStream<Path> ds = dir.newDirectoryStream(); |
jjg@450 | 347 | // try { |
jjg@450 | 348 | // for (Path p: ds) { |
alanb@847 | 349 | // String name = p.getFileName().toString(); |
jjg@450 | 350 | // if (isDirectory(p)) { |
jjg@450 | 351 | // if (recurse && SourceVersion.isIdentifier(name)) { |
jjg@450 | 352 | // queue.add(p); |
jjg@450 | 353 | // } |
jjg@450 | 354 | // } else { |
jjg@450 | 355 | // if (kinds.contains(getKind(name))) { |
jjg@450 | 356 | // JavaFileObject fe = |
jjg@450 | 357 | // PathFileObject.createDirectoryPathFileObject(this, p, pathDir); |
jjg@450 | 358 | // results.append(fe); |
jjg@450 | 359 | // } |
jjg@450 | 360 | // } |
jjg@450 | 361 | // } |
jjg@450 | 362 | // } finally { |
jjg@450 | 363 | // ds.close(); |
jjg@450 | 364 | // } |
jjg@450 | 365 | // } |
jjg@450 | 366 | int maxDepth = (recurse ? Integer.MAX_VALUE : 1); |
alanb@701 | 367 | Set<FileVisitOption> opts = EnumSet.of(FOLLOW_LINKS); |
jjg@450 | 368 | Files.walkFileTree(packageDir, opts, maxDepth, |
jjg@450 | 369 | new SimpleFileVisitor<Path>() { |
jjg@450 | 370 | @Override |
alanb@701 | 371 | public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { |
alanb@847 | 372 | Path name = dir.getFileName(); |
jjg@803 | 373 | if (name == null || SourceVersion.isIdentifier(name.toString())) // JSR 292? |
jjg@450 | 374 | return FileVisitResult.CONTINUE; |
jjg@450 | 375 | else |
jjg@450 | 376 | return FileVisitResult.SKIP_SUBTREE; |
jjg@450 | 377 | } |
jjg@450 | 378 | |
jjg@450 | 379 | @Override |
jjg@450 | 380 | public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { |
alanb@847 | 381 | if (attrs.isRegularFile() && kinds.contains(getKind(file.getFileName().toString()))) { |
jjg@450 | 382 | JavaFileObject fe = |
jjg@450 | 383 | PathFileObject.createDirectoryPathFileObject( |
jjg@450 | 384 | JavacPathFileManager.this, file, pathDir); |
jjg@450 | 385 | results.append(fe); |
jjg@450 | 386 | } |
jjg@450 | 387 | return FileVisitResult.CONTINUE; |
jjg@450 | 388 | } |
jjg@450 | 389 | }); |
jjg@450 | 390 | } |
jjg@450 | 391 | |
jjg@450 | 392 | @Override |
jjg@450 | 393 | public Iterable<? extends JavaFileObject> getJavaFileObjectsFromPaths( |
jjg@450 | 394 | Iterable<? extends Path> paths) { |
jjg@450 | 395 | ArrayList<PathFileObject> result; |
jjg@450 | 396 | if (paths instanceof Collection<?>) |
jjg@450 | 397 | result = new ArrayList<PathFileObject>(((Collection<?>)paths).size()); |
jjg@450 | 398 | else |
jjg@450 | 399 | result = new ArrayList<PathFileObject>(); |
jjg@450 | 400 | for (Path p: paths) |
jjg@450 | 401 | result.add(PathFileObject.createSimplePathFileObject(this, nullCheck(p))); |
jjg@450 | 402 | return result; |
jjg@450 | 403 | } |
jjg@450 | 404 | |
jjg@450 | 405 | @Override |
jjg@450 | 406 | public Iterable<? extends JavaFileObject> getJavaFileObjects(Path... paths) { |
jjg@450 | 407 | return getJavaFileObjectsFromPaths(Arrays.asList(nullCheck(paths))); |
jjg@450 | 408 | } |
jjg@450 | 409 | |
jjg@450 | 410 | @Override |
jjg@450 | 411 | public JavaFileObject getJavaFileForInput(Location location, |
jjg@450 | 412 | String className, Kind kind) throws IOException { |
jjg@450 | 413 | return getFileForInput(location, getRelativePath(className, kind)); |
jjg@450 | 414 | } |
jjg@450 | 415 | |
jjg@450 | 416 | @Override |
jjg@450 | 417 | public FileObject getFileForInput(Location location, |
jjg@450 | 418 | String packageName, String relativeName) throws IOException { |
jjg@450 | 419 | return getFileForInput(location, getRelativePath(packageName, relativeName)); |
jjg@450 | 420 | } |
jjg@450 | 421 | |
jjg@450 | 422 | private JavaFileObject getFileForInput(Location location, String relativePath) |
jjg@450 | 423 | throws IOException { |
jjg@450 | 424 | for (Path p: getLocation(location)) { |
jjg@450 | 425 | if (isDirectory(p)) { |
jjg@450 | 426 | Path f = resolve(p, relativePath); |
alanb@847 | 427 | if (Files.exists(f)) |
jjg@450 | 428 | return PathFileObject.createDirectoryPathFileObject(this, f, p); |
jjg@450 | 429 | } else { |
jjg@450 | 430 | FileSystem fs = getFileSystem(p); |
jjg@450 | 431 | if (fs != null) { |
jjg@450 | 432 | Path file = getPath(fs, relativePath); |
alanb@847 | 433 | if (Files.exists(file)) |
jjg@450 | 434 | return PathFileObject.createJarPathFileObject(this, file); |
jjg@450 | 435 | } |
jjg@450 | 436 | } |
jjg@450 | 437 | } |
jjg@450 | 438 | return null; |
jjg@450 | 439 | } |
jjg@450 | 440 | |
jjg@450 | 441 | @Override |
jjg@450 | 442 | public JavaFileObject getJavaFileForOutput(Location location, |
jjg@450 | 443 | String className, Kind kind, FileObject sibling) throws IOException { |
jjg@450 | 444 | return getFileForOutput(location, getRelativePath(className, kind), sibling); |
jjg@450 | 445 | } |
jjg@450 | 446 | |
jjg@450 | 447 | @Override |
jjg@450 | 448 | public FileObject getFileForOutput(Location location, String packageName, |
jjg@450 | 449 | String relativeName, FileObject sibling) |
jjg@450 | 450 | throws IOException { |
jjg@450 | 451 | return getFileForOutput(location, getRelativePath(packageName, relativeName), sibling); |
jjg@450 | 452 | } |
jjg@450 | 453 | |
jjg@450 | 454 | private JavaFileObject getFileForOutput(Location location, |
jjg@450 | 455 | String relativePath, FileObject sibling) { |
jjg@450 | 456 | Path dir = getOutputLocation(location); |
jjg@450 | 457 | if (dir == null) { |
jjg@450 | 458 | if (location == CLASS_OUTPUT) { |
jjg@450 | 459 | Path siblingDir = null; |
jjg@450 | 460 | if (sibling != null && sibling instanceof PathFileObject) { |
jjg@450 | 461 | siblingDir = ((PathFileObject) sibling).getPath().getParent(); |
jjg@450 | 462 | } |
jjg@450 | 463 | return PathFileObject.createSiblingPathFileObject(this, |
jjg@450 | 464 | siblingDir.resolve(getBaseName(relativePath)), |
jjg@450 | 465 | relativePath); |
jjg@450 | 466 | } else if (location == SOURCE_OUTPUT) { |
jjg@450 | 467 | dir = getOutputLocation(CLASS_OUTPUT); |
jjg@450 | 468 | } |
jjg@450 | 469 | } |
jjg@450 | 470 | |
jjg@450 | 471 | Path file; |
jjg@450 | 472 | if (dir != null) { |
jjg@450 | 473 | file = resolve(dir, relativePath); |
jjg@450 | 474 | return PathFileObject.createDirectoryPathFileObject(this, file, dir); |
jjg@450 | 475 | } else { |
jjg@450 | 476 | file = getPath(getDefaultFileSystem(), relativePath); |
jjg@450 | 477 | return PathFileObject.createSimplePathFileObject(this, file); |
jjg@450 | 478 | } |
jjg@450 | 479 | |
jjg@450 | 480 | } |
jjg@450 | 481 | |
jjg@450 | 482 | @Override |
jjg@450 | 483 | public String inferBinaryName(Location location, JavaFileObject fo) { |
jjg@450 | 484 | nullCheck(fo); |
jjg@450 | 485 | // Need to match the path semantics of list(location, ...) |
jjg@450 | 486 | Iterable<? extends Path> paths = getLocation(location); |
jjg@450 | 487 | if (paths == null) { |
jjg@450 | 488 | return null; |
jjg@450 | 489 | } |
jjg@450 | 490 | |
jjg@450 | 491 | if (!(fo instanceof PathFileObject)) |
jjg@450 | 492 | throw new IllegalArgumentException(fo.getClass().getName()); |
jjg@450 | 493 | |
jjg@450 | 494 | return ((PathFileObject) fo).inferBinaryName(paths); |
jjg@450 | 495 | } |
jjg@450 | 496 | |
jjg@450 | 497 | private FileSystem getFileSystem(Path p) throws IOException { |
jjg@450 | 498 | FileSystem fs = fileSystems.get(p); |
jjg@450 | 499 | if (fs == null) { |
alanb@847 | 500 | fs = FileSystems.newFileSystem(p, null); |
jjg@450 | 501 | fileSystems.put(p, fs); |
jjg@450 | 502 | } |
jjg@450 | 503 | return fs; |
jjg@450 | 504 | } |
jjg@450 | 505 | |
jjg@450 | 506 | private Map<Path,FileSystem> fileSystems; |
jjg@450 | 507 | |
jjg@450 | 508 | // </editor-fold> |
jjg@450 | 509 | |
jjg@450 | 510 | // <editor-fold defaultstate="collapsed" desc="Utility methods"> |
jjg@450 | 511 | |
jjg@450 | 512 | private static String getRelativePath(String className, Kind kind) { |
jjg@450 | 513 | return className.replace(".", "/") + kind.extension; |
jjg@450 | 514 | } |
jjg@450 | 515 | |
jjg@450 | 516 | private static String getRelativePath(String packageName, String relativeName) { |
jjg@1413 | 517 | return packageName.isEmpty() |
jjg@1413 | 518 | ? relativeName : packageName.replace(".", "/") + "/" + relativeName; |
jjg@450 | 519 | } |
jjg@450 | 520 | |
jjg@450 | 521 | private static String getBaseName(String relativePath) { |
jjg@450 | 522 | int lastSep = relativePath.lastIndexOf("/"); |
jjg@450 | 523 | return relativePath.substring(lastSep + 1); // safe if "/" not found |
jjg@450 | 524 | } |
jjg@450 | 525 | |
jjg@450 | 526 | private static boolean isDirectory(Path path) throws IOException { |
alanb@847 | 527 | BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class); |
jjg@450 | 528 | return attrs.isDirectory(); |
jjg@450 | 529 | } |
jjg@450 | 530 | |
jjg@450 | 531 | private static Path getPath(FileSystem fs, String relativePath) { |
jjg@450 | 532 | return fs.getPath(relativePath.replace("/", fs.getSeparator())); |
jjg@450 | 533 | } |
jjg@450 | 534 | |
jjg@450 | 535 | private static Path resolve(Path base, String relativePath) { |
jjg@450 | 536 | FileSystem fs = base.getFileSystem(); |
jjg@450 | 537 | Path rp = fs.getPath(relativePath.replace("/", fs.getSeparator())); |
jjg@450 | 538 | return base.resolve(rp); |
jjg@450 | 539 | } |
jjg@450 | 540 | |
jjg@450 | 541 | // </editor-fold> |
jjg@450 | 542 | |
jjg@450 | 543 | } |