1 /* 2 * Copyright (c) 2005, 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.file; 27 28 import java.io.File; 29 import java.io.FileInputStream; 30 import java.io.FileOutputStream; 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.io.OutputStream; 34 import java.io.OutputStreamWriter; 35 import java.io.Writer; 36 import java.lang.ref.Reference; 37 import java.lang.ref.SoftReference; 38 import java.net.URI; 39 import java.nio.ByteBuffer; 40 import java.nio.CharBuffer; 41 import java.nio.charset.CharsetDecoder; 42 import javax.tools.JavaFileObject; 43 import java.text.Normalizer; 44 45 /** 46 * A subclass of JavaFileObject representing regular files. 47 * 48 * <p><b>This is NOT part of any supported API. 49 * If you write code that depends on this, you do so at your own risk. 50 * This code and its internal interfaces are subject to change or 51 * deletion without notice.</b> 52 */ 53 class RegularFileObject extends BaseFileObject { 54 55 /** Have the parent directories been created? 56 */ 57 private boolean hasParents = false; 58 private String name; 59 final File file; 60 private Reference<File> absFileRef; 61 final static boolean isMacOS = System.getProperty("os.name", "").contains("OS X"); 62 63 public RegularFileObject(JavacFileManager fileManager, File f) { 64 this(fileManager, f.getName(), f); 65 } 66 67 public RegularFileObject(JavacFileManager fileManager, String name, File f) { 68 super(fileManager); 69 if (f.isDirectory()) { 70 throw new IllegalArgumentException("directories not supported"); 71 } 72 this.name = name; 73 this.file = f; 74 } 75 76 @Override 77 public URI toUri() { 78 return file.toURI().normalize(); 79 } 80 81 @Override 82 public String getName() { 83 return file.getPath(); 84 } 85 86 @Override 87 public String getShortName() { 88 return name; 89 } 90 91 @Override 92 public JavaFileObject.Kind getKind() { 93 return getKind(name); 94 } 95 96 @Override 97 public InputStream openInputStream() throws IOException { 98 return new FileInputStream(file); 99 } 100 101 @Override 102 public OutputStream openOutputStream() throws IOException { 103 fileManager.flushCache(this); 104 ensureParentDirectoriesExist(); 105 return new FileOutputStream(file); 106 } 107 108 @Override 109 public CharBuffer getCharContent(boolean ignoreEncodingErrors) throws IOException { 110 CharBuffer cb = fileManager.getCachedContent(this); 111 if (cb == null) { 112 InputStream in = new FileInputStream(file); 113 try { 114 ByteBuffer bb = fileManager.makeByteBuffer(in); 115 JavaFileObject prev = fileManager.log.useSource(this); 116 try { 117 cb = fileManager.decode(bb, ignoreEncodingErrors); 118 } finally { 119 fileManager.log.useSource(prev); 120 } 121 fileManager.recycleByteBuffer(bb); 122 if (!ignoreEncodingErrors) { 123 fileManager.cache(this, cb); 124 } 125 } finally { 126 in.close(); 127 } 128 } 129 return cb; 130 } 131 132 @Override 133 public Writer openWriter() throws IOException { 134 fileManager.flushCache(this); 135 ensureParentDirectoriesExist(); 136 return new OutputStreamWriter(new FileOutputStream(file), fileManager.getEncodingName()); 137 } 138 139 @Override 140 public long getLastModified() { 141 return file.lastModified(); 142 } 143 144 @Override 145 public boolean delete() { 146 return file.delete(); 147 } 148 149 @Override 150 protected CharsetDecoder getDecoder(boolean ignoreEncodingErrors) { 151 return fileManager.getDecoder(fileManager.getEncodingName(), ignoreEncodingErrors); 152 } 153 154 @Override 155 protected String inferBinaryName(Iterable<? extends File> path) { 156 String fPath = file.getPath(); 157 //System.err.println("RegularFileObject " + file + " " +r.getPath()); 158 for (File dir: path) { 159 //System.err.println("dir: " + dir); 160 String dPath = dir.getPath(); 161 if (dPath.length() == 0) 162 dPath = System.getProperty("user.dir"); 163 if (!dPath.endsWith(File.separator)) 164 dPath += File.separator; 165 if (fPath.regionMatches(true, 0, dPath, 0, dPath.length()) 166 && new File(fPath.substring(0, dPath.length())).equals(new File(dPath))) { 167 String relativeName = fPath.substring(dPath.length()); 168 return removeExtension(relativeName).replace(File.separatorChar, '.'); 169 } 170 } 171 return null; 172 } 173 174 @Override 175 public boolean isNameCompatible(String cn, JavaFileObject.Kind kind) { 176 cn.getClass(); 177 // null check 178 if (kind == Kind.OTHER && getKind() != kind) { 179 return false; 180 } 181 String n = cn + kind.extension; 182 if (name.equals(n)) { 183 return true; 184 } 185 if (isMacOS && Normalizer.isNormalized(name, Normalizer.Form.NFD) 186 && Normalizer.isNormalized(n, Normalizer.Form.NFC)) { 187 // On Mac OS X it is quite possible to file name and class 188 // name normalized in a different way - in that case we have to normalize file name 189 // to the Normal Form Compised (NFC) 190 String normName = Normalizer.normalize(name, Normalizer.Form.NFC); 191 if (normName.equals(n)) { 192 this.name = normName; 193 return true; 194 } 195 } 196 197 if (name.equalsIgnoreCase(n)) { 198 try { 199 // allow for Windows 200 return file.getCanonicalFile().getName().equals(n); 201 } catch (IOException e) { 202 } 203 } 204 return false; 205 } 206 207 private void ensureParentDirectoriesExist() throws IOException { 208 if (!hasParents) { 209 File parent = file.getParentFile(); 210 if (parent != null && !parent.exists()) { 211 if (!parent.mkdirs()) { 212 if (!parent.exists() || !parent.isDirectory()) { 213 throw new IOException("could not create parent directories"); 214 } 215 } 216 } 217 hasParents = true; 218 } 219 } 220 221 /** 222 * Check if two file objects are equal. 223 * Two RegularFileObjects are equal if the absolute paths of the underlying 224 * files are equal. 225 */ 226 @Override 227 public boolean equals(Object other) { 228 if (this == other) 229 return true; 230 231 if (!(other instanceof RegularFileObject)) 232 return false; 233 234 RegularFileObject o = (RegularFileObject) other; 235 return getAbsoluteFile().equals(o.getAbsoluteFile()); 236 } 237 238 @Override 239 public int hashCode() { 240 return getAbsoluteFile().hashCode(); 241 } 242 243 private File getAbsoluteFile() { 244 File absFile = (absFileRef == null ? null : absFileRef.get()); 245 if (absFile == null) { 246 absFile = file.getAbsoluteFile(); 247 absFileRef = new SoftReference<File>(absFile); 248 } 249 return absFile; 250 } 251 }