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.Path; 41 import java.nio.file.attribute.BasicFileAttributes; 42 import javax.lang.model.element.Modifier; 43 import javax.lang.model.element.NestingKind; 44 import javax.tools.JavaFileObject; 45 46 import com.sun.tools.javac.util.BaseFileManager; 47 48 49 /** 50 * Implementation of JavaFileObject using java.nio.file API. 51 * 52 * <p>PathFileObjects are, for the most part, straightforward wrappers around 53 * Path objects. The primary complexity is the support for "inferBinaryName". 54 * This is left as an abstract method, implemented by each of a number of 55 * different factory methods, which compute the binary name based on 56 * information available at the time the file object is created. 57 * 58 * <p><b>This is NOT part of any supported API. 59 * If you write code that depends on this, you do so at your own risk. 60 * This code and its internal interfaces are subject to change or 61 * deletion without notice.</b> 62 */ 63 abstract class PathFileObject implements JavaFileObject { 64 private JavacPathFileManager fileManager; 65 private Path path; 66 67 /** 68 * Create a PathFileObject within a directory, such that the binary name 69 * can be inferred from the relationship to the parent directory. 70 */ 71 static PathFileObject createDirectoryPathFileObject(JavacPathFileManager fileManager, 72 final Path path, final Path dir) { 73 return new PathFileObject(fileManager, path) { 74 @Override 75 String inferBinaryName(Iterable<? extends Path> paths) { 76 return toBinaryName(dir.relativize(path)); 77 } 78 }; 79 } 80 81 /** 82 * Create a PathFileObject in a file system such as a jar file, such that 83 * the binary name can be inferred from its position within the filesystem. 84 */ 85 static PathFileObject createJarPathFileObject(JavacPathFileManager fileManager, 86 final Path path) { 87 return new PathFileObject(fileManager, path) { 88 @Override 89 String inferBinaryName(Iterable<? extends Path> paths) { 90 return toBinaryName(path); 91 } 92 }; 93 } 94 95 /** 96 * Create a PathFileObject whose binary name can be inferred from the 97 * relative path to a sibling. 98 */ 99 static PathFileObject createSiblingPathFileObject(JavacPathFileManager fileManager, 100 final Path path, final String relativePath) { 101 return new PathFileObject(fileManager, path) { 102 @Override 103 String inferBinaryName(Iterable<? extends Path> paths) { 104 return toBinaryName(relativePath, "/"); 105 } 106 }; 107 } 108 109 /** 110 * Create a PathFileObject whose binary name might be inferred from its 111 * position on a search path. 112 */ 113 static PathFileObject createSimplePathFileObject(JavacPathFileManager fileManager, 114 final Path path) { 115 return new PathFileObject(fileManager, path) { 116 @Override 117 String inferBinaryName(Iterable<? extends Path> paths) { 118 Path absPath = path.toAbsolutePath(); 119 for (Path p: paths) { 120 Path ap = p.toAbsolutePath(); 121 if (absPath.startsWith(ap)) { 122 try { 123 Path rp = ap.relativize(absPath); 124 if (rp != null) // maybe null if absPath same as ap 125 return toBinaryName(rp); 126 } catch (IllegalArgumentException e) { 127 // ignore this p if cannot relativize path to p 128 } 129 } 130 } 131 return null; 132 } 133 }; 134 } 135 136 protected PathFileObject(JavacPathFileManager fileManager, Path path) { 137 fileManager.getClass(); // null check 138 path.getClass(); // null check 139 this.fileManager = fileManager; 140 this.path = path; 141 } 142 143 abstract String inferBinaryName(Iterable<? extends Path> paths); 144 145 /** 146 * Return the Path for this object. 147 * @return the Path for this object. 148 */ 149 Path getPath() { 150 return path; 151 } 152 153 @Override 154 public Kind getKind() { 155 return BaseFileManager.getKind(path.getFileName().toString()); 156 } 157 158 @Override 159 public boolean isNameCompatible(String simpleName, Kind kind) { 160 simpleName.getClass(); 161 // null check 162 if (kind == Kind.OTHER && getKind() != kind) { 163 return false; 164 } 165 String sn = simpleName + kind.extension; 166 String pn = path.getFileName().toString(); 167 if (pn.equals(sn)) { 168 return true; 169 } 170 if (pn.equalsIgnoreCase(sn)) { 171 try { 172 // allow for Windows 173 return path.toRealPath(false).getFileName().toString().equals(sn); 174 } catch (IOException e) { 175 } 176 } 177 return false; 178 } 179 180 @Override 181 public NestingKind getNestingKind() { 182 return null; 183 } 184 185 @Override 186 public Modifier getAccessLevel() { 187 return null; 188 } 189 190 @Override 191 public URI toUri() { 192 return path.toUri(); 193 } 194 195 @Override 196 public String getName() { 197 return path.toString(); 198 } 199 200 @Override 201 public InputStream openInputStream() throws IOException { 202 return Files.newInputStream(path); 203 } 204 205 @Override 206 public OutputStream openOutputStream() throws IOException { 207 ensureParentDirectoriesExist(); 208 return Files.newOutputStream(path); 209 } 210 211 @Override 212 public Reader openReader(boolean ignoreEncodingErrors) throws IOException { 213 CharsetDecoder decoder = fileManager.getDecoder(fileManager.getEncodingName(), ignoreEncodingErrors); 214 return new InputStreamReader(openInputStream(), decoder); 215 } 216 217 @Override 218 public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { 219 CharBuffer cb = fileManager.getCachedContent(this); 220 if (cb == null) { 221 InputStream in = openInputStream(); 222 try { 223 ByteBuffer bb = fileManager.makeByteBuffer(in); 224 JavaFileObject prev = fileManager.log.useSource(this); 225 try { 226 cb = fileManager.decode(bb, ignoreEncodingErrors); 227 } finally { 228 fileManager.log.useSource(prev); 229 } 230 fileManager.recycleByteBuffer(bb); 231 if (!ignoreEncodingErrors) { 232 fileManager.cache(this, cb); 233 } 234 } finally { 235 in.close(); 236 } 237 } 238 return cb; 239 } 240 241 @Override 242 public Writer openWriter() throws IOException { 243 ensureParentDirectoriesExist(); 244 return new OutputStreamWriter(Files.newOutputStream(path), fileManager.getEncodingName()); 245 } 246 247 @Override 248 public long getLastModified() { 249 try { 250 return Files.getLastModifiedTime(path).toMillis(); 251 } catch (IOException e) { 252 return -1; 253 } 254 } 255 256 @Override 257 public boolean delete() { 258 try { 259 Files.delete(path); 260 return true; 261 } catch (IOException e) { 262 return false; 263 } 264 } 265 266 public boolean isSameFile(PathFileObject other) { 267 try { 268 return Files.isSameFile(path, other.path); 269 } catch (IOException e) { 270 return false; 271 } 272 } 273 274 @Override 275 public boolean equals(Object other) { 276 return (other instanceof PathFileObject && path.equals(((PathFileObject) other).path)); 277 } 278 279 @Override 280 public int hashCode() { 281 return path.hashCode(); 282 } 283 284 @Override 285 public String toString() { 286 return getClass().getSimpleName() + "[" + path + "]"; 287 } 288 289 private void ensureParentDirectoriesExist() throws IOException { 290 Path parent = path.getParent(); 291 if (parent != null) 292 Files.createDirectories(parent); 293 } 294 295 private long size() { 296 try { 297 return Files.size(path); 298 } catch (IOException e) { 299 return -1; 300 } 301 } 302 303 protected static String toBinaryName(Path relativePath) { 304 return toBinaryName(relativePath.toString(), 305 relativePath.getFileSystem().getSeparator()); 306 } 307 308 protected static String toBinaryName(String relativePath, String sep) { 309 return removeExtension(relativePath).replace(sep, "."); 310 } 311 312 protected static String removeExtension(String fileName) { 313 int lastDot = fileName.lastIndexOf("."); 314 return (lastDot == -1 ? fileName : fileName.substring(0, lastDot)); 315 } 316 }