PathFileObject.java revision 2983:599930f29330
1172106Srwatson/* 2172106Srwatson * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. 3172106Srwatson * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4172106Srwatson * 5172106Srwatson * This code is free software; you can redistribute it and/or modify it 6172106Srwatson * under the terms of the GNU General Public License version 2 only, as 7172106Srwatson * published by the Free Software Foundation. Oracle designates this 8172106Srwatson * particular file as subject to the "Classpath" exception as provided 9172106Srwatson * by Oracle in the LICENSE file that accompanied this code. 10172106Srwatson * 11172106Srwatson * This code is distributed in the hope that it will be useful, but WITHOUT 12172106Srwatson * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13172106Srwatson * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14172106Srwatson * version 2 for more details (a copy is included in the LICENSE file that 15172106Srwatson * accompanied this code). 16172106Srwatson * 17172106Srwatson * You should have received a copy of the GNU General Public License version 18172106Srwatson * 2 along with this work; if not, write to the Free Software Foundation, 19172106Srwatson * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20172106Srwatson * 21172106Srwatson * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22172106Srwatson * or visit www.oracle.com if you need additional information or have any 23172106Srwatson * questions. 24172106Srwatson */ 25172106Srwatson 26172106Srwatsonpackage com.sun.tools.javac.file; 27172106Srwatson 28172106Srwatsonimport java.io.IOException; 29172106Srwatsonimport java.io.InputStream; 30172106Srwatsonimport java.io.InputStreamReader; 31172106Srwatsonimport java.io.OutputStream; 32172106Srwatsonimport java.io.OutputStreamWriter; 33172106Srwatsonimport java.io.Reader; 34228975Suqsimport java.io.Writer; 35172106Srwatsonimport java.net.URI; 36172106Srwatsonimport java.nio.ByteBuffer; 37172106Srwatsonimport java.nio.CharBuffer; 38172106Srwatsonimport java.nio.charset.CharsetDecoder; 39172106Srwatsonimport java.nio.file.Files; 40172106Srwatsonimport java.nio.file.LinkOption; 41172106Srwatsonimport java.nio.file.Path; 42172106Srwatsonimport java.util.Objects; 43172106Srwatson 44172106Srwatsonimport javax.lang.model.element.Modifier; 45172106Srwatsonimport javax.lang.model.element.NestingKind; 46172106Srwatsonimport javax.tools.JavaFileObject; 47172106Srwatson 48172106Srwatsonimport com.sun.tools.javac.util.DefinedBy; 49172106Srwatsonimport com.sun.tools.javac.util.DefinedBy.Api; 50172106Srwatson 51172106Srwatson 52172106Srwatson/** 53172106Srwatson * Implementation of JavaFileObject using java.nio.file API. 54172106Srwatson * 55172106Srwatson * <p>PathFileObjects are, for the most part, straightforward wrappers around 56172106Srwatson * Path objects. The primary complexity is the support for "inferBinaryName". 57172106Srwatson * This is left as an abstract method, implemented by each of a number of 58172106Srwatson * different factory methods, which compute the binary name based on 59172106Srwatson * information available at the time the file object is created. 60172106Srwatson * 61172106Srwatson * <p><b>This is NOT part of any supported API. 62172106Srwatson * If you write code that depends on this, you do so at your own risk. 63172106Srwatson * This code and its internal interfaces are subject to change or 64172106Srwatson * deletion without notice.</b> 65172106Srwatson */ 66172106Srwatsonpublic abstract class PathFileObject implements JavaFileObject { 67172106Srwatson private final BaseFileManager fileManager; 68172106Srwatson private final Path path; 69172106Srwatson 70172106Srwatson /** 71172106Srwatson * Create a PathFileObject within a directory, such that the binary name 72172106Srwatson * can be inferred from the relationship to the parent directory. 73172106Srwatson */ 74172106Srwatson static PathFileObject createDirectoryPathFileObject(BaseFileManager fileManager, 75172106Srwatson final Path path, final Path dir) { 76172106Srwatson return new PathFileObject(fileManager, path) { 77172106Srwatson @Override 78172106Srwatson public String inferBinaryName(Iterable<? extends Path> paths) { 79172106Srwatson return toBinaryName(dir.relativize(path)); 80172106Srwatson } 81172106Srwatson }; 82172106Srwatson } 83172106Srwatson 84172106Srwatson /** 85172106Srwatson * Create a PathFileObject in a file system such as a jar file, such that 86172106Srwatson * the binary name can be inferred from its position within the filesystem. 87172106Srwatson */ 88172106Srwatson public static PathFileObject createJarPathFileObject(BaseFileManager fileManager, 89172106Srwatson final Path path) { 90172106Srwatson return new PathFileObject(fileManager, path) { 91172106Srwatson @Override 92172106Srwatson public String inferBinaryName(Iterable<? extends Path> paths) { 93172106Srwatson return toBinaryName(path); 94172106Srwatson } 95172106Srwatson }; 96172106Srwatson } 97172106Srwatson 98172106Srwatson /** 99172106Srwatson * Create a PathFileObject in a modular file system, such as jrt:, such that 100172106Srwatson * the binary name can be inferred from its position within the filesystem. 101172106Srwatson */ 102172106Srwatson public static PathFileObject createJRTPathFileObject(BaseFileManager fileManager, 103172106Srwatson final Path path) { 104172106Srwatson return new PathFileObject(fileManager, path) { 105172106Srwatson @Override 106172106Srwatson public String inferBinaryName(Iterable<? extends Path> paths) { 107172106Srwatson // use subpath to ignore the leading /modules/MODULE-NAME 108172106Srwatson return toBinaryName(path.subpath(2, path.getNameCount())); 109172106Srwatson } 110172106Srwatson }; 111172106Srwatson } 112172106Srwatson 113172106Srwatson /** 114172106Srwatson * Create a PathFileObject whose binary name can be inferred from the 115172106Srwatson * relative path to a sibling. 116172106Srwatson */ 117172106Srwatson static PathFileObject createSiblingPathFileObject(BaseFileManager fileManager, 118172106Srwatson final Path path, final String relativePath) { 119172106Srwatson return new PathFileObject(fileManager, path) { 120172106Srwatson @Override 121172106Srwatson public String inferBinaryName(Iterable<? extends Path> paths) { 122172106Srwatson return toBinaryName(relativePath, "/"); 123172106Srwatson } 124172106Srwatson }; 125172106Srwatson } 126172106Srwatson 127172106Srwatson /** 128172106Srwatson * Create a PathFileObject whose binary name might be inferred from its 129172106Srwatson * position on a search path. 130172106Srwatson */ 131172106Srwatson static PathFileObject createSimplePathFileObject(BaseFileManager fileManager, 132172106Srwatson final Path path) { 133172106Srwatson return new PathFileObject(fileManager, path) { 134172106Srwatson @Override 135172106Srwatson public String inferBinaryName(Iterable<? extends Path> paths) { 136172106Srwatson Path absPath = path.toAbsolutePath(); 137172106Srwatson for (Path p: paths) { 138172106Srwatson Path ap = p.toAbsolutePath(); 139172106Srwatson if (absPath.startsWith(ap)) { 140172106Srwatson try { 141172106Srwatson Path rp = ap.relativize(absPath); 142172106Srwatson if (rp != null) // maybe null if absPath same as ap 143172106Srwatson return toBinaryName(rp); 144172106Srwatson } catch (IllegalArgumentException e) { 145172106Srwatson // ignore this p if cannot relativize path to p 146172106Srwatson } 147172106Srwatson } 148172106Srwatson } 149172106Srwatson return null; 150172106Srwatson } 151172106Srwatson }; 152172106Srwatson } 153172106Srwatson 154172106Srwatson protected PathFileObject(BaseFileManager fileManager, Path path) { 155172106Srwatson this.fileManager = Objects.requireNonNull(fileManager); 156172106Srwatson this.path = Objects.requireNonNull(path); 157172106Srwatson } 158172106Srwatson 159172106Srwatson public abstract String inferBinaryName(Iterable<? extends Path> paths); 160172106Srwatson 161172106Srwatson /** 162172106Srwatson * Return the Path for this object. 163172106Srwatson * @return the Path for this object. 164172106Srwatson */ 165172106Srwatson public Path getPath() { 166172106Srwatson return path; 167172106Srwatson } 168172106Srwatson 169172106Srwatson @Override @DefinedBy(Api.COMPILER) 170172106Srwatson public Kind getKind() { 171172106Srwatson return BaseFileManager.getKind(path.getFileName().toString()); 172172106Srwatson } 173172106Srwatson 174172106Srwatson @Override @DefinedBy(Api.COMPILER) 175172106Srwatson public boolean isNameCompatible(String simpleName, Kind kind) { 176172106Srwatson Objects.requireNonNull(simpleName); 177172106Srwatson // null check 178172106Srwatson if (kind == Kind.OTHER && getKind() != kind) { 179172106Srwatson return false; 180172106Srwatson } 181172106Srwatson String sn = simpleName + kind.extension; 182172106Srwatson String pn = path.getFileName().toString(); 183172106Srwatson if (pn.equals(sn)) { 184172106Srwatson return true; 185172106Srwatson } 186172106Srwatson if (pn.equalsIgnoreCase(sn)) { 187172106Srwatson try { 188172106Srwatson // allow for Windows 189172106Srwatson return path.toRealPath(LinkOption.NOFOLLOW_LINKS).getFileName().toString().equals(sn); 190172106Srwatson } catch (IOException e) { 191172106Srwatson } 192172106Srwatson } 193172106Srwatson return false; 194172106Srwatson } 195172106Srwatson 196172106Srwatson @Override @DefinedBy(Api.COMPILER) 197172106Srwatson public NestingKind getNestingKind() { 198172106Srwatson return null; 199172106Srwatson } 200172106Srwatson 201172106Srwatson @Override @DefinedBy(Api.COMPILER) 202172106Srwatson public Modifier getAccessLevel() { 203172106Srwatson return null; 204172106Srwatson } 205172106Srwatson 206172106Srwatson @Override @DefinedBy(Api.COMPILER) 207172106Srwatson public URI toUri() { 208172106Srwatson return path.toUri(); 209 } 210 211 @Override @DefinedBy(Api.COMPILER) 212 public String getName() { 213 return path.toString(); 214 } 215 216 @Override @DefinedBy(Api.COMPILER) 217 public InputStream openInputStream() throws IOException { 218 return Files.newInputStream(path); 219 } 220 221 @Override @DefinedBy(Api.COMPILER) 222 public OutputStream openOutputStream() throws IOException { 223 fileManager.flushCache(this); 224 ensureParentDirectoriesExist(); 225 return Files.newOutputStream(path); 226 } 227 228 @Override @DefinedBy(Api.COMPILER) 229 public Reader openReader(boolean ignoreEncodingErrors) throws IOException { 230 CharsetDecoder decoder = fileManager.getDecoder(fileManager.getEncodingName(), ignoreEncodingErrors); 231 return new InputStreamReader(openInputStream(), decoder); 232 } 233 234 @Override @DefinedBy(Api.COMPILER) 235 public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { 236 CharBuffer cb = fileManager.getCachedContent(this); 237 if (cb == null) { 238 try (InputStream in = openInputStream()) { 239 ByteBuffer bb = fileManager.makeByteBuffer(in); 240 JavaFileObject prev = fileManager.log.useSource(this); 241 try { 242 cb = fileManager.decode(bb, ignoreEncodingErrors); 243 } finally { 244 fileManager.log.useSource(prev); 245 } 246 fileManager.recycleByteBuffer(bb); 247 if (!ignoreEncodingErrors) { 248 fileManager.cache(this, cb); 249 } 250 } 251 } 252 return cb; 253 } 254 255 @Override @DefinedBy(Api.COMPILER) 256 public Writer openWriter() throws IOException { 257 fileManager.flushCache(this); 258 ensureParentDirectoriesExist(); 259 return new OutputStreamWriter(Files.newOutputStream(path), fileManager.getEncodingName()); 260 } 261 262 @Override @DefinedBy(Api.COMPILER) 263 public long getLastModified() { 264 try { 265 return Files.getLastModifiedTime(path).toMillis(); 266 } catch (IOException e) { 267 return -1; 268 } 269 } 270 271 @Override @DefinedBy(Api.COMPILER) 272 public boolean delete() { 273 try { 274 Files.delete(path); 275 return true; 276 } catch (IOException e) { 277 return false; 278 } 279 } 280 281 public boolean isSameFile(PathFileObject other) { 282 try { 283 return Files.isSameFile(path, other.path); 284 } catch (IOException e) { 285 return false; 286 } 287 } 288 289 @Override 290 public boolean equals(Object other) { 291 return (other instanceof PathFileObject && path.equals(((PathFileObject) other).path)); 292 } 293 294 @Override 295 public int hashCode() { 296 return path.hashCode(); 297 } 298 299 @Override 300 public String toString() { 301 return getClass().getSimpleName() + "[" + path + "]"; 302 } 303 304 private void ensureParentDirectoriesExist() throws IOException { 305 Path parent = path.getParent(); 306 if (parent != null) 307 Files.createDirectories(parent); 308 } 309 310 private long size() { 311 try { 312 return Files.size(path); 313 } catch (IOException e) { 314 return -1; 315 } 316 } 317 318 protected static String toBinaryName(Path relativePath) { 319 return toBinaryName(relativePath.toString(), 320 relativePath.getFileSystem().getSeparator()); 321 } 322 323 protected static String toBinaryName(String relativePath, String sep) { 324 return removeExtension(relativePath).replace(sep, "."); 325 } 326 327 protected static String removeExtension(String fileName) { 328 int lastDot = fileName.lastIndexOf("."); 329 return (lastDot == -1 ? fileName : fileName.substring(0, lastDot)); 330 } 331} 332