1 /* 2 * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.javac.nio; 27 28 import java.io.IOException; 29 import java.io.InputStream; 30 import java.io.InputStreamReader; 31 import java.io.OutputStream; 32 import java.io.OutputStreamWriter; 33 import java.io.Reader; 34 import java.io.Writer; 35 import java.net.URI; 36 import java.nio.ByteBuffer; 37 import java.nio.CharBuffer; 38 import java.nio.charset.CharsetDecoder; 39 import java.nio.file.Files; 40 import java.nio.file.LinkOption; 41 import java.nio.file.Path; 42 import java.nio.file.attribute.BasicFileAttributes; 43 import javax.lang.model.element.Modifier; 44 import javax.lang.model.element.NestingKind; 45 import javax.tools.JavaFileObject; 46 47 import com.sun.tools.javac.util.BaseFileManager; 48 49 50 /** 51 * Implementation of JavaFileObject using java.nio.file API. 52 * 53 * <p>PathFileObjects are, for the most part, straightforward wrappers around 54 * Path objects. The primary complexity is the support for "inferBinaryName". 55 * This is left as an abstract method, implemented by each of a number of 56 * different factory methods, which compute the binary name based on 57 * information available at the time the file object is created. 58 * 59 * <p><b>This is NOT part of any supported API. 60 * If you write code that depends on this, you do so at your own risk. 61 * This code and its internal interfaces are subject to change or 62 * deletion without notice.</b> 63 */ 64 abstract class PathFileObject implements JavaFileObject { 65 private JavacPathFileManager fileManager; 66 private Path path; 67 68 /** 69 * Create a PathFileObject within a directory, such that the binary name 70 * can be inferred from the relationship to the parent directory. 71 */ 72 static PathFileObject createDirectoryPathFileObject(JavacPathFileManager fileManager, 73 final Path path, final Path dir) { 74 return new PathFileObject(fileManager, path) { 75 @Override 76 String inferBinaryName(Iterable<? extends Path> paths) { 77 return toBinaryName(dir.relativize(path)); 78 } 79 }; 80 } 81 82 /** 83 * Create a PathFileObject in a file system such as a jar file, such that 84 * the binary name can be inferred from its position within the filesystem. 85 */ 86 static PathFileObject createJarPathFileObject(JavacPathFileManager fileManager, 87 final Path path) { 88 return new PathFileObject(fileManager, path) { 89 @Override 90 String inferBinaryName(Iterable<? extends Path> paths) { 91 return toBinaryName(path); 92 } 93 }; 94 } 95 96 /** 97 * Create a PathFileObject whose binary name can be inferred from the 98 * relative path to a sibling. 99 */ 100 static PathFileObject createSiblingPathFileObject(JavacPathFileManager fileManager, 101 final Path path, final String relativePath) { 102 return new PathFileObject(fileManager, path) { 103 @Override 104 String inferBinaryName(Iterable<? extends Path> paths) { 105 return toBinaryName(relativePath, "/"); 106 } 107 }; 108 } 109 110 /** 111 * Create a PathFileObject whose binary name might be inferred from its 112 * position on a search path. 113 */ 114 static PathFileObject createSimplePathFileObject(JavacPathFileManager fileManager, 115 final Path path) { 116 return new PathFileObject(fileManager, path) { 117 @Override 118 String inferBinaryName(Iterable<? extends Path> paths) { 119 Path absPath = path.toAbsolutePath(); 120 for (Path p: paths) { 121 Path ap = p.toAbsolutePath(); 122 if (absPath.startsWith(ap)) { 123 try { 124 Path rp = ap.relativize(absPath); 125 if (rp != null) // maybe null if absPath same as ap 126 return toBinaryName(rp); 127 } catch (IllegalArgumentException e) { 128 // ignore this p if cannot relativize path to p 129 } 130 } 131 } 132 return null; 133 } 134 }; 135 } 136 137 protected PathFileObject(JavacPathFileManager fileManager, Path path) { 138 fileManager.getClass(); // null check 139 path.getClass(); // null check 140 this.fileManager = fileManager; 141 this.path = path; 142 } 143 144 abstract String inferBinaryName(Iterable<? extends Path> paths); 145 146 /** 147 * Return the Path for this object. 148 * @return the Path for this object. 149 */ 150 Path getPath() { 151 return path; 152 } 153 154 @Override 155 public Kind getKind() { 156 return BaseFileManager.getKind(path.getFileName().toString()); 157 } 158 159 @Override 160 public boolean isNameCompatible(String simpleName, Kind kind) { 161 simpleName.getClass(); 162 // null check 163 if (kind == Kind.OTHER && getKind() != kind) { 164 return false; 165 } 166 String sn = simpleName + kind.extension; 167 String pn = path.getFileName().toString(); 168 if (pn.equals(sn)) { 169 return true; 170 } 171 if (pn.equalsIgnoreCase(sn)) { 172 try { 173 // allow for Windows 174 return path.toRealPath(LinkOption.NOFOLLOW_LINKS).getFileName().toString().equals(sn); 175 } catch (IOException e) { 176 } 177 } 178 return false; 179 } 180 181 @Override 182 public NestingKind getNestingKind() { 183 return null; 184 } 185 186 @Override 187 public Modifier getAccessLevel() { 188 return null; 189 } 190 191 @Override 192 public URI toUri() { 193 return path.toUri(); 194 } 195 196 @Override 197 public String getName() { 198 return path.toString(); 199 } 200 201 @Override 202 public InputStream openInputStream() throws IOException { 203 return Files.newInputStream(path); 204 } 205 206 @Override 207 public OutputStream openOutputStream() throws IOException { 208 ensureParentDirectoriesExist(); 209 return Files.newOutputStream(path); 210 } 211 212 @Override 213 public Reader openReader(boolean ignoreEncodingErrors) throws IOException { 214 CharsetDecoder decoder = fileManager.getDecoder(fileManager.getEncodingName(), ignoreEncodingErrors); 215 return new InputStreamReader(openInputStream(), decoder); 216 } 217 218 @Override 219 public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { 220 CharBuffer cb = fileManager.getCachedContent(this); 221 if (cb == null) { 222 InputStream in = openInputStream(); 223 try { 224 ByteBuffer bb = fileManager.makeByteBuffer(in); 225 JavaFileObject prev = fileManager.log.useSource(this); 226 try { 227 cb = fileManager.decode(bb, ignoreEncodingErrors); 228 } finally { 229 fileManager.log.useSource(prev); 230 } 231 fileManager.recycleByteBuffer(bb); 232 if (!ignoreEncodingErrors) { 233 fileManager.cache(this, cb); 234 } 235 } finally { 236 in.close(); 237 } 238 } 239 return cb; 240 } 241 242 @Override 243 public Writer openWriter() throws IOException { 244 ensureParentDirectoriesExist(); 245 return new OutputStreamWriter(Files.newOutputStream(path), fileManager.getEncodingName()); 246 } 247 248 @Override 249 public long getLastModified() { 250 try { 251 return Files.getLastModifiedTime(path).toMillis(); 252 } catch (IOException e) { 253 return -1; 254 } 255 } 256 257 @Override 258 public boolean delete() { 259 try { 260 Files.delete(path); 261 return true; 262 } catch (IOException e) { 263 return false; 264 } 265 } 266 267 public boolean isSameFile(PathFileObject other) { 268 try { 269 return Files.isSameFile(path, other.path); 270 } catch (IOException e) { 271 return false; 272 } 273 } 274 275 @Override 276 public boolean equals(Object other) { 277 return (other instanceof PathFileObject && path.equals(((PathFileObject) other).path)); 278 } 279 280 @Override 281 public int hashCode() { 282 return path.hashCode(); 283 } 284 285 @Override 286 public String toString() { 287 return getClass().getSimpleName() + "[" + path + "]"; 288 } 289 290 private void ensureParentDirectoriesExist() throws IOException { 291 Path parent = path.getParent(); 292 if (parent != null) 293 Files.createDirectories(parent); 294 } 295 296 private long size() { 297 try { 298 return Files.size(path); 299 } catch (IOException e) { 300 return -1; 301 } 302 } 303 304 protected static String toBinaryName(Path relativePath) { 305 return toBinaryName(relativePath.toString(), 306 relativePath.getFileSystem().getSeparator()); 307 } 308 309 protected static String toBinaryName(String relativePath, String sep) { 310 return removeExtension(relativePath).replace(sep, "."); 311 } 312 313 protected static String removeExtension(String fileName) { 314 int lastDot = fileName.lastIndexOf("."); 315 return (lastDot == -1 ? fileName : fileName.substring(0, lastDot)); 316 } 317 }