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