Tue, 28 Dec 2010 15:54:52 -0800
6962318: Update copyright year
Reviewed-by: xdono
duke@1 | 1 | /* |
ohair@798 | 2 | * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. |
duke@1 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
duke@1 | 4 | * |
duke@1 | 5 | * This code is free software; you can redistribute it and/or modify it |
duke@1 | 6 | * under the terms of the GNU General Public License version 2 only, as |
ohair@554 | 7 | * published by the Free Software Foundation. Oracle designates this |
duke@1 | 8 | * particular file as subject to the "Classpath" exception as provided |
ohair@554 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
duke@1 | 10 | * |
duke@1 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
duke@1 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
duke@1 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
duke@1 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
duke@1 | 15 | * accompanied this code). |
duke@1 | 16 | * |
duke@1 | 17 | * You should have received a copy of the GNU General Public License version |
duke@1 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
duke@1 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
duke@1 | 20 | * |
ohair@554 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
ohair@554 | 22 | * or visit www.oracle.com if you need additional information or have any |
ohair@554 | 23 | * questions. |
duke@1 | 24 | */ |
duke@1 | 25 | |
duke@1 | 26 | package com.sun.tools.apt.mirror.apt; |
duke@1 | 27 | |
duke@1 | 28 | |
duke@1 | 29 | import java.io.*; |
duke@1 | 30 | import java.util.Collection; |
duke@1 | 31 | import java.util.EnumMap; |
duke@1 | 32 | import java.util.HashSet; |
duke@1 | 33 | import java.util.Set; |
duke@1 | 34 | |
duke@1 | 35 | import com.sun.mirror.apt.Filer; |
duke@1 | 36 | import com.sun.tools.apt.mirror.declaration.DeclarationMaker; |
duke@1 | 37 | import com.sun.tools.javac.util.Context; |
duke@1 | 38 | import com.sun.tools.javac.util.Options; |
duke@1 | 39 | import com.sun.tools.javac.util.Position; |
duke@1 | 40 | import com.sun.tools.apt.util.Bark; |
duke@1 | 41 | |
duke@1 | 42 | import static com.sun.mirror.apt.Filer.Location.*; |
duke@1 | 43 | |
duke@1 | 44 | |
duke@1 | 45 | /** |
duke@1 | 46 | * Implementation of Filer. |
duke@1 | 47 | */ |
darcy@331 | 48 | @SuppressWarnings("deprecation") |
duke@1 | 49 | public class FilerImpl implements Filer { |
duke@1 | 50 | /* |
duke@1 | 51 | * The Filer class must maintain a number of constraints. First, |
duke@1 | 52 | * multiple attempts to open the same path within the same |
duke@1 | 53 | * invocation of apt results in an IOException being thrown. For |
duke@1 | 54 | * example, trying to open the same source file twice: |
duke@1 | 55 | * |
duke@1 | 56 | * createSourceFile("foo.Bar") |
duke@1 | 57 | * ... |
duke@1 | 58 | * createSourceFile("foo.Bar") |
duke@1 | 59 | * |
duke@1 | 60 | * is disallowed as is opening a text file that happens to have |
duke@1 | 61 | * the same name as a source file: |
duke@1 | 62 | * |
duke@1 | 63 | * createSourceFile("foo.Bar") |
duke@1 | 64 | * ... |
duke@1 | 65 | * createTextFile(SOURCE_TREE, "foo", new File("Bar"), null) |
duke@1 | 66 | * |
duke@1 | 67 | * Additionally, creating a source file that corresponds to an |
duke@1 | 68 | * already created class file (or vice versa) generates at least a |
duke@1 | 69 | * warning. This is an error if -XclassesAsDecls is being used |
duke@1 | 70 | * since you can't create the same type twice. However, if the |
duke@1 | 71 | * Filer is used to create a text file named *.java that happens |
duke@1 | 72 | * to correspond to an existing class file, a warning is *not* |
duke@1 | 73 | * generated. Similarly, a warning is not generated for a binary |
duke@1 | 74 | * file named *.class and an existing source file. |
duke@1 | 75 | * |
duke@1 | 76 | * The reason for this difference is that source files and class |
duke@1 | 77 | * files are registered with apt and can get passed on as |
duke@1 | 78 | * declarations to the next round of processing. Files that are |
duke@1 | 79 | * just named *.java and *.class are not processed in that manner; |
duke@1 | 80 | * although having extra source files and class files on the |
duke@1 | 81 | * source path and class path can alter the behavior of the tool |
duke@1 | 82 | * and any final compile. |
duke@1 | 83 | */ |
duke@1 | 84 | |
duke@1 | 85 | private enum FileKind { |
duke@1 | 86 | SOURCE { |
duke@1 | 87 | void register(File file, String name, FilerImpl that) throws IOException { |
duke@1 | 88 | // Check for corresponding class file |
duke@1 | 89 | if (that.filesCreated.contains(new File(that.locations.get(CLASS_TREE), |
duke@1 | 90 | that.nameToPath(name, ".class")))) { |
duke@1 | 91 | |
duke@1 | 92 | that.bark.aptWarning("CorrespondingClassFile", name); |
duke@1 | 93 | if (that.opts.get("-XclassesAsDecls") != null) |
duke@1 | 94 | throw new IOException(); |
duke@1 | 95 | } |
duke@1 | 96 | that.sourceFileNames.add(file.getPath()); |
duke@1 | 97 | } |
duke@1 | 98 | }, |
duke@1 | 99 | |
duke@1 | 100 | CLASS { |
duke@1 | 101 | void register(File file, String name, FilerImpl that) throws IOException { |
duke@1 | 102 | if (that.filesCreated.contains(new File(that.locations.get(SOURCE_TREE), |
duke@1 | 103 | that.nameToPath(name, ".java")))) { |
duke@1 | 104 | that.bark.aptWarning("CorrespondingSourceFile", name); |
duke@1 | 105 | if (that.opts.get("-XclassesAsDecls") != null) |
duke@1 | 106 | throw new IOException(); |
duke@1 | 107 | } |
duke@1 | 108 | // Track the binary name instead of the filesystem location |
duke@1 | 109 | that.classFileNames.add(name); |
duke@1 | 110 | } |
duke@1 | 111 | }, |
duke@1 | 112 | |
duke@1 | 113 | OTHER { |
duke@1 | 114 | // Nothing special to do |
duke@1 | 115 | void register(File file, String name, FilerImpl that) throws IOException {} |
duke@1 | 116 | }; |
duke@1 | 117 | |
duke@1 | 118 | abstract void register(File file, String name, FilerImpl that) throws IOException; |
duke@1 | 119 | } |
duke@1 | 120 | |
duke@1 | 121 | private final Options opts; |
duke@1 | 122 | private final DeclarationMaker declMaker; |
jjg@789 | 123 | private final com.sun.tools.apt.main.AptJavaCompiler comp; |
duke@1 | 124 | |
duke@1 | 125 | // Platform's default encoding |
duke@1 | 126 | private final static String DEFAULT_ENCODING = |
duke@1 | 127 | new OutputStreamWriter(new ByteArrayOutputStream()).getEncoding(); |
duke@1 | 128 | |
duke@1 | 129 | private String encoding; // name of charset used for source files |
duke@1 | 130 | |
duke@1 | 131 | private final EnumMap<Location, File> locations; // where new files go |
duke@1 | 132 | |
duke@1 | 133 | |
duke@1 | 134 | private static final Context.Key<FilerImpl> filerKey = |
duke@1 | 135 | new Context.Key<FilerImpl>(); |
duke@1 | 136 | |
duke@1 | 137 | // Set of files opened. |
duke@1 | 138 | private Collection<Flushable> wc; |
duke@1 | 139 | |
duke@1 | 140 | private Bark bark; |
duke@1 | 141 | |
duke@1 | 142 | // All created files. |
duke@1 | 143 | private final Set<File> filesCreated; |
duke@1 | 144 | |
duke@1 | 145 | // Names of newly created source files |
duke@1 | 146 | private HashSet<String> sourceFileNames = new HashSet<String>(); |
duke@1 | 147 | |
duke@1 | 148 | // Names of newly created class files |
duke@1 | 149 | private HashSet<String> classFileNames = new HashSet<String>(); |
duke@1 | 150 | |
duke@1 | 151 | private boolean roundOver; |
duke@1 | 152 | |
duke@1 | 153 | public static FilerImpl instance(Context context) { |
duke@1 | 154 | FilerImpl instance = context.get(filerKey); |
duke@1 | 155 | if (instance == null) { |
duke@1 | 156 | instance = new FilerImpl(context); |
duke@1 | 157 | } |
duke@1 | 158 | return instance; |
duke@1 | 159 | } |
duke@1 | 160 | |
duke@1 | 161 | // flush all output streams; |
duke@1 | 162 | public void flush() { |
duke@1 | 163 | for(Flushable opendedFile: wc) { |
duke@1 | 164 | try { |
duke@1 | 165 | opendedFile.flush(); |
duke@1 | 166 | if (opendedFile instanceof FileOutputStream) { |
duke@1 | 167 | try { |
duke@1 | 168 | ((FileOutputStream) opendedFile).getFD().sync() ; |
duke@1 | 169 | } catch (java.io.SyncFailedException sfe) {} |
duke@1 | 170 | } |
duke@1 | 171 | } catch (IOException e) { } |
duke@1 | 172 | } |
duke@1 | 173 | } |
duke@1 | 174 | |
duke@1 | 175 | private FilerImpl(Context context) { |
duke@1 | 176 | context.put(filerKey, this); |
duke@1 | 177 | opts = Options.instance(context); |
duke@1 | 178 | declMaker = DeclarationMaker.instance(context); |
duke@1 | 179 | bark = Bark.instance(context); |
jjg@789 | 180 | comp = com.sun.tools.apt.main.AptJavaCompiler.instance(context); |
duke@1 | 181 | roundOver = false; |
duke@1 | 182 | this.filesCreated = comp.getAggregateGenFiles(); |
duke@1 | 183 | |
duke@1 | 184 | // Encoding |
duke@1 | 185 | encoding = opts.get("-encoding"); |
duke@1 | 186 | if (encoding == null) { |
duke@1 | 187 | encoding = DEFAULT_ENCODING; |
duke@1 | 188 | } |
duke@1 | 189 | |
duke@1 | 190 | wc = new HashSet<Flushable>(); |
duke@1 | 191 | |
duke@1 | 192 | // Locations |
duke@1 | 193 | locations = new EnumMap<Location, File>(Location.class); |
duke@1 | 194 | String s = opts.get("-s"); // location for new source files |
duke@1 | 195 | String d = opts.get("-d"); // location for new class files |
duke@1 | 196 | locations.put(SOURCE_TREE, new File(s != null ? s : ".")); |
duke@1 | 197 | locations.put(CLASS_TREE, new File(d != null ? d : ".")); |
duke@1 | 198 | } |
duke@1 | 199 | |
duke@1 | 200 | |
duke@1 | 201 | /** |
duke@1 | 202 | * {@inheritDoc} |
duke@1 | 203 | */ |
duke@1 | 204 | public PrintWriter createSourceFile(String name) throws IOException { |
duke@1 | 205 | String pathname = nameToPath(name, ".java"); |
duke@1 | 206 | File file = new File(locations.get(SOURCE_TREE), |
duke@1 | 207 | pathname); |
duke@1 | 208 | PrintWriter pw = getPrintWriter(file, encoding, name, FileKind.SOURCE); |
duke@1 | 209 | return pw; |
duke@1 | 210 | } |
duke@1 | 211 | |
duke@1 | 212 | /** |
duke@1 | 213 | * {@inheritDoc} |
duke@1 | 214 | */ |
duke@1 | 215 | public OutputStream createClassFile(String name) throws IOException { |
duke@1 | 216 | String pathname = nameToPath(name, ".class"); |
duke@1 | 217 | File file = new File(locations.get(CLASS_TREE), |
duke@1 | 218 | pathname); |
duke@1 | 219 | OutputStream os = getOutputStream(file, name, FileKind.CLASS); |
duke@1 | 220 | return os; |
duke@1 | 221 | } |
duke@1 | 222 | |
duke@1 | 223 | /** |
duke@1 | 224 | * {@inheritDoc} |
duke@1 | 225 | */ |
duke@1 | 226 | public PrintWriter createTextFile(Location loc, |
duke@1 | 227 | String pkg, |
duke@1 | 228 | File relPath, |
duke@1 | 229 | String charsetName) throws IOException { |
duke@1 | 230 | File file = (pkg.length() == 0) |
duke@1 | 231 | ? relPath |
duke@1 | 232 | : new File(nameToPath(pkg), relPath.getPath()); |
duke@1 | 233 | if (charsetName == null) { |
duke@1 | 234 | charsetName = encoding; |
duke@1 | 235 | } |
duke@1 | 236 | return getPrintWriter(loc, file.getPath(), charsetName, null, FileKind.OTHER); |
duke@1 | 237 | } |
duke@1 | 238 | |
duke@1 | 239 | /** |
duke@1 | 240 | * {@inheritDoc} |
duke@1 | 241 | */ |
duke@1 | 242 | public OutputStream createBinaryFile(Location loc, |
duke@1 | 243 | String pkg, |
duke@1 | 244 | File relPath) throws IOException { |
duke@1 | 245 | File file = (pkg.length() == 0) |
duke@1 | 246 | ? relPath |
duke@1 | 247 | : new File(nameToPath(pkg), relPath.getPath()); |
duke@1 | 248 | return getOutputStream(loc, file.getPath(), null, FileKind.OTHER); |
duke@1 | 249 | } |
duke@1 | 250 | |
duke@1 | 251 | |
duke@1 | 252 | /** |
duke@1 | 253 | * Converts the canonical name of a top-level type or package to a |
duke@1 | 254 | * pathname. Suffix is ".java" or ".class" or "". |
duke@1 | 255 | */ |
duke@1 | 256 | private String nameToPath(String name, String suffix) throws IOException { |
duke@1 | 257 | if (!DeclarationMaker.isJavaIdentifier(name.replace('.', '_'))) { |
duke@1 | 258 | bark.aptWarning("IllegalFileName", name); |
duke@1 | 259 | throw new IOException(); |
duke@1 | 260 | } |
duke@1 | 261 | return name.replace('.', File.separatorChar) + suffix; |
duke@1 | 262 | } |
duke@1 | 263 | |
duke@1 | 264 | private String nameToPath(String name) throws IOException { |
duke@1 | 265 | return nameToPath(name, ""); |
duke@1 | 266 | } |
duke@1 | 267 | |
duke@1 | 268 | /** |
duke@1 | 269 | * Returns a writer for a text file given its location, its |
duke@1 | 270 | * pathname relative to that location, and its encoding. |
duke@1 | 271 | */ |
duke@1 | 272 | private PrintWriter getPrintWriter(Location loc, String pathname, |
duke@1 | 273 | String encoding, String name, FileKind kind) throws IOException { |
duke@1 | 274 | File file = new File(locations.get(loc), pathname); |
duke@1 | 275 | return getPrintWriter(file, encoding, name, kind); |
duke@1 | 276 | } |
duke@1 | 277 | |
duke@1 | 278 | /** |
duke@1 | 279 | * Returns a writer for a text file given its encoding. |
duke@1 | 280 | */ |
duke@1 | 281 | private PrintWriter getPrintWriter(File file, |
duke@1 | 282 | String encoding, String name, FileKind kind) throws IOException { |
duke@1 | 283 | prepareFile(file, name, kind); |
duke@1 | 284 | PrintWriter pw = |
duke@1 | 285 | new PrintWriter( |
duke@1 | 286 | new BufferedWriter( |
duke@1 | 287 | new OutputStreamWriter(new FileOutputStream(file), |
duke@1 | 288 | encoding))); |
duke@1 | 289 | wc.add(pw); |
duke@1 | 290 | return pw; |
duke@1 | 291 | } |
duke@1 | 292 | |
duke@1 | 293 | /** |
duke@1 | 294 | * Returns an output stream for a binary file given its location |
duke@1 | 295 | * and its pathname relative to that location. |
duke@1 | 296 | */ |
duke@1 | 297 | private OutputStream getOutputStream(Location loc, String pathname, String name, FileKind kind) |
duke@1 | 298 | throws IOException { |
duke@1 | 299 | File file = new File(locations.get(loc), pathname); |
duke@1 | 300 | return getOutputStream(file, name, kind); |
duke@1 | 301 | } |
duke@1 | 302 | |
duke@1 | 303 | private OutputStream getOutputStream(File file, String name, FileKind kind) throws IOException { |
duke@1 | 304 | prepareFile(file, name, kind); |
duke@1 | 305 | OutputStream os = new FileOutputStream(file); |
duke@1 | 306 | wc.add(os); |
duke@1 | 307 | return os; |
duke@1 | 308 | |
duke@1 | 309 | } |
duke@1 | 310 | |
duke@1 | 311 | public Set<String> getSourceFileNames() { |
duke@1 | 312 | return sourceFileNames; |
duke@1 | 313 | } |
duke@1 | 314 | |
duke@1 | 315 | public Set<String> getClassFileNames() { |
duke@1 | 316 | return classFileNames; |
duke@1 | 317 | } |
duke@1 | 318 | |
duke@1 | 319 | public void roundOver() { |
duke@1 | 320 | roundOver = true; |
duke@1 | 321 | } |
duke@1 | 322 | |
duke@1 | 323 | /** |
duke@1 | 324 | * Checks that the file has not already been created during this |
duke@1 | 325 | * invocation. If not, creates intermediate directories, and |
duke@1 | 326 | * deletes the file if it already exists. |
duke@1 | 327 | */ |
duke@1 | 328 | private void prepareFile(File file, String name, FileKind kind) throws IOException { |
duke@1 | 329 | if (roundOver) { |
duke@1 | 330 | bark.aptWarning("NoNewFilesAfterRound", file.toString()); |
duke@1 | 331 | throw new IOException(); |
duke@1 | 332 | } |
duke@1 | 333 | |
duke@1 | 334 | if (filesCreated.contains(file)) { |
duke@1 | 335 | bark.aptWarning("FileReopening", file.toString()); |
duke@1 | 336 | throw new IOException(); |
duke@1 | 337 | } else { |
duke@1 | 338 | if (file.exists()) { |
duke@1 | 339 | file.delete(); |
duke@1 | 340 | } else { |
duke@1 | 341 | File parent = file.getParentFile(); |
duke@1 | 342 | if (parent != null && !parent.exists()) { |
duke@1 | 343 | if(!parent.mkdirs()) { |
duke@1 | 344 | bark.aptWarning("BadParentDirectory", file.toString()); |
duke@1 | 345 | throw new IOException(); |
duke@1 | 346 | } |
duke@1 | 347 | } |
duke@1 | 348 | } |
duke@1 | 349 | |
duke@1 | 350 | kind.register(file, name, this); |
duke@1 | 351 | filesCreated.add(file); |
duke@1 | 352 | } |
duke@1 | 353 | } |
duke@1 | 354 | } |