JavacFiler.java revision 3922:3fdaf9e50f5c
1254721Semaste/* 2254721Semaste * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. 3353358Sdim * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4353358Sdim * 5353358Sdim * This code is free software; you can redistribute it and/or modify it 6254721Semaste * under the terms of the GNU General Public License version 2 only, as 7254721Semaste * published by the Free Software Foundation. Oracle designates this 8254721Semaste * particular file as subject to the "Classpath" exception as provided 9254721Semaste * by Oracle in the LICENSE file that accompanied this code. 10254721Semaste * 11254721Semaste * This code is distributed in the hope that it will be useful, but WITHOUT 12254721Semaste * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13314564Sdim * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14341825Sdim * version 2 for more details (a copy is included in the LICENSE file that 15344779Sdim * accompanied this code). 16254721Semaste * 17254721Semaste * You should have received a copy of the GNU General Public License version 18254721Semaste * 2 along with this work; if not, write to the Free Software Foundation, 19254721Semaste * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20314564Sdim * 21314564Sdim * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22314564Sdim * or visit www.oracle.com if you need additional information or have any 23314564Sdim * questions. 24314564Sdim */ 25254721Semaste 26314564Sdimpackage com.sun.tools.javac.processing; 27254721Semaste 28314564Sdimimport java.io.Closeable; 29314564Sdimimport java.io.FileNotFoundException; 30314564Sdimimport java.io.InputStream; 31314564Sdimimport java.io.OutputStream; 32254721Semasteimport java.io.FilterOutputStream; 33314564Sdimimport java.io.Reader; 34254721Semasteimport java.io.Writer; 35254721Semasteimport java.io.FilterWriter; 36321369Sdimimport java.io.PrintWriter; 37321369Sdimimport java.io.IOException; 38321369Sdimimport java.util.*; 39314564Sdim 40314564Sdimimport static java.util.Collections.*; 41314564Sdim 42314564Sdimimport javax.annotation.processing.*; 43314564Sdimimport javax.lang.model.SourceVersion; 44314564Sdimimport javax.lang.model.element.NestingKind; 45314564Sdimimport javax.lang.model.element.Modifier; 46314564Sdimimport javax.lang.model.element.Element; 47314564Sdimimport javax.tools.*; 48314564Sdimimport javax.tools.JavaFileManager.Location; 49314564Sdim 50314564Sdimimport static javax.tools.StandardLocation.SOURCE_OUTPUT; 51314564Sdimimport static javax.tools.StandardLocation.CLASS_OUTPUT; 52314564Sdim 53314564Sdimimport com.sun.tools.javac.code.Lint; 54314564Sdimimport com.sun.tools.javac.code.Symbol.ClassSymbol; 55314564Sdimimport com.sun.tools.javac.code.Symbol.ModuleSymbol; 56314564Sdimimport com.sun.tools.javac.code.Symtab; 57314564Sdimimport com.sun.tools.javac.comp.Modules; 58314564Sdimimport com.sun.tools.javac.model.JavacElements; 59314564Sdimimport com.sun.tools.javac.util.*; 60314564Sdimimport com.sun.tools.javac.util.DefinedBy.Api; 61314564Sdim 62314564Sdimimport static com.sun.tools.javac.code.Lint.LintCategory.PROCESSING; 63314564Sdim 64314564Sdim/** 65254721Semaste * The FilerImplementation class must maintain a number of 66254721Semaste * constraints. First, multiple attempts to open the same path within 67314564Sdim * the same invocation of the tool results in an IOException being 68314564Sdim * thrown. For example, trying to open the same source file twice: 69254721Semaste * 70254721Semaste * <pre> 71360784Sdim * createSourceFile("foo.Bar") 72360784Sdim * ... 73314564Sdim * createSourceFile("foo.Bar") 74341825Sdim * </pre> 75341825Sdim * 76254721Semaste * is disallowed as is opening a text file that happens to have 77 * the same name as a source file: 78 * 79 * <pre> 80 * createSourceFile("foo.Bar") 81 * ... 82 * createTextFile(SOURCE_TREE, "foo", new File("Bar"), null) 83 * </pre> 84 * 85 * <p>Additionally, creating a source file that corresponds to an 86 * already created class file (or vice versa) also results in an 87 * IOException since each type can only be created once. However, if 88 * the Filer is used to create a text file named *.java that happens 89 * to correspond to an existing class file, a warning is *not* 90 * generated. Similarly, a warning is not generated for a binary file 91 * named *.class and an existing source file. 92 * 93 * <p>The reason for this difference is that source files and class 94 * files are registered with the tool and can get passed on as 95 * declarations to the next round of processing. Files that are just 96 * named *.java and *.class are not processed in that manner; although 97 * having extra source files and class files on the source path and 98 * class path can alter the behavior of the tool and any final 99 * compile. 100 * 101 * <p><b>This is NOT part of any supported API. 102 * If you write code that depends on this, you do so at your own risk. 103 * This code and its internal interfaces are subject to change or 104 * deletion without notice.</b> 105 */ 106public class JavacFiler implements Filer, Closeable { 107 // TODO: Implement different transaction model for updating the 108 // Filer's record keeping on file close. 109 110 private static final String ALREADY_OPENED = 111 "Output stream or writer has already been opened."; 112 private static final String NOT_FOR_READING = 113 "FileObject was not opened for reading."; 114 private static final String NOT_FOR_WRITING = 115 "FileObject was not opened for writing."; 116 117 /** 118 * Wrap a JavaFileObject to manage writing by the Filer. 119 */ 120 private class FilerOutputFileObject extends ForwardingFileObject<FileObject> { 121 private boolean opened = false; 122 private ModuleSymbol mod; 123 private String name; 124 125 FilerOutputFileObject(ModuleSymbol mod, String name, FileObject fileObject) { 126 super(fileObject); 127 this.mod = mod; 128 this.name = name; 129 } 130 131 @Override @DefinedBy(Api.COMPILER) 132 public synchronized OutputStream openOutputStream() throws IOException { 133 if (opened) 134 throw new IOException(ALREADY_OPENED); 135 opened = true; 136 return new FilerOutputStream(mod, name, fileObject); 137 } 138 139 @Override @DefinedBy(Api.COMPILER) 140 public synchronized Writer openWriter() throws IOException { 141 if (opened) 142 throw new IOException(ALREADY_OPENED); 143 opened = true; 144 return new FilerWriter(mod, name, fileObject); 145 } 146 147 // Three anti-literacy methods 148 @Override @DefinedBy(Api.COMPILER) 149 public InputStream openInputStream() throws IOException { 150 throw new IllegalStateException(NOT_FOR_READING); 151 } 152 153 @Override @DefinedBy(Api.COMPILER) 154 public Reader openReader(boolean ignoreEncodingErrors) throws IOException { 155 throw new IllegalStateException(NOT_FOR_READING); 156 } 157 158 @Override @DefinedBy(Api.COMPILER) 159 public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { 160 throw new IllegalStateException(NOT_FOR_READING); 161 } 162 163 @Override @DefinedBy(Api.COMPILER) 164 public boolean delete() { 165 return false; 166 } 167 } 168 169 private class FilerOutputJavaFileObject extends FilerOutputFileObject implements JavaFileObject { 170 private final JavaFileObject javaFileObject; 171 FilerOutputJavaFileObject(ModuleSymbol mod, String name, JavaFileObject javaFileObject) { 172 super(mod, name, javaFileObject); 173 this.javaFileObject = javaFileObject; 174 } 175 176 @DefinedBy(Api.COMPILER) 177 public JavaFileObject.Kind getKind() { 178 return javaFileObject.getKind(); 179 } 180 181 @DefinedBy(Api.COMPILER) 182 public boolean isNameCompatible(String simpleName, 183 JavaFileObject.Kind kind) { 184 return javaFileObject.isNameCompatible(simpleName, kind); 185 } 186 187 @DefinedBy(Api.COMPILER) 188 public NestingKind getNestingKind() { 189 return javaFileObject.getNestingKind(); 190 } 191 192 @DefinedBy(Api.COMPILER) 193 public Modifier getAccessLevel() { 194 return javaFileObject.getAccessLevel(); 195 } 196 } 197 198 /** 199 * Wrap a JavaFileObject to manage reading by the Filer. 200 */ 201 private class FilerInputFileObject extends ForwardingFileObject<FileObject> { 202 FilerInputFileObject(FileObject fileObject) { 203 super(fileObject); 204 } 205 206 @Override @DefinedBy(Api.COMPILER) 207 public OutputStream openOutputStream() throws IOException { 208 throw new IllegalStateException(NOT_FOR_WRITING); 209 } 210 211 @Override @DefinedBy(Api.COMPILER) 212 public Writer openWriter() throws IOException { 213 throw new IllegalStateException(NOT_FOR_WRITING); 214 } 215 216 @Override @DefinedBy(Api.COMPILER) 217 public boolean delete() { 218 return false; 219 } 220 } 221 222 private class FilerInputJavaFileObject extends FilerInputFileObject implements JavaFileObject { 223 private final JavaFileObject javaFileObject; 224 FilerInputJavaFileObject(JavaFileObject javaFileObject) { 225 super(javaFileObject); 226 this.javaFileObject = javaFileObject; 227 } 228 229 @DefinedBy(Api.COMPILER) 230 public JavaFileObject.Kind getKind() { 231 return javaFileObject.getKind(); 232 } 233 234 @DefinedBy(Api.COMPILER) 235 public boolean isNameCompatible(String simpleName, 236 JavaFileObject.Kind kind) { 237 return javaFileObject.isNameCompatible(simpleName, kind); 238 } 239 240 @DefinedBy(Api.COMPILER) 241 public NestingKind getNestingKind() { 242 return javaFileObject.getNestingKind(); 243 } 244 245 @DefinedBy(Api.COMPILER) 246 public Modifier getAccessLevel() { 247 return javaFileObject.getAccessLevel(); 248 } 249 } 250 251 252 /** 253 * Wrap a {@code OutputStream} returned from the {@code 254 * JavaFileManager} to properly register source or class files 255 * when they are closed. 256 */ 257 private class FilerOutputStream extends FilterOutputStream { 258 ModuleSymbol mod; 259 String typeName; 260 FileObject fileObject; 261 boolean closed = false; 262 263 /** 264 * @param typeName name of class or {@code null} if just a 265 * binary file 266 */ 267 FilerOutputStream(ModuleSymbol mod, String typeName, FileObject fileObject) throws IOException { 268 super(fileObject.openOutputStream()); 269 this.mod = mod; 270 this.typeName = typeName; 271 this.fileObject = fileObject; 272 } 273 274 public synchronized void close() throws IOException { 275 if (!closed) { 276 closed = true; 277 /* 278 * If an IOException occurs when closing the underlying 279 * stream, still try to process the file. 280 */ 281 282 closeFileObject(mod, typeName, fileObject); 283 out.close(); 284 } 285 } 286 } 287 288 /** 289 * Wrap a {@code Writer} returned from the {@code JavaFileManager} 290 * to properly register source or class files when they are 291 * closed. 292 */ 293 private class FilerWriter extends FilterWriter { 294 ModuleSymbol mod; 295 String typeName; 296 FileObject fileObject; 297 boolean closed = false; 298 299 /** 300 * @param fileObject the fileObject to be written to 301 * @param typeName name of source file or {@code null} if just a 302 * text file 303 */ 304 FilerWriter(ModuleSymbol mod, String typeName, FileObject fileObject) throws IOException { 305 super(fileObject.openWriter()); 306 this.mod = mod; 307 this.typeName = typeName; 308 this.fileObject = fileObject; 309 } 310 311 public synchronized void close() throws IOException { 312 if (!closed) { 313 closed = true; 314 /* 315 * If an IOException occurs when closing the underlying 316 * Writer, still try to process the file. 317 */ 318 319 closeFileObject(mod, typeName, fileObject); 320 out.close(); 321 } 322 } 323 } 324 325 JavaFileManager fileManager; 326 JavacElements elementUtils; 327 Log log; 328 Modules modules; 329 Names names; 330 Symtab syms; 331 Context context; 332 boolean lastRound; 333 334 private final boolean lint; 335 336 /** 337 * Initial inputs passed to the tool. This set must be 338 * synchronized. 339 */ 340 private final Set<FileObject> initialInputs; 341 342 /** 343 * Logical names of all created files. This set must be 344 * synchronized. 345 */ 346 private final Set<FileObject> fileObjectHistory; 347 348 /** 349 * Names of types that have had files created but not closed. 350 */ 351 private final Set<String> openTypeNames; 352 353 /** 354 * Names of source files closed in this round. This set must be 355 * synchronized. Its iterators should preserve insertion order. 356 */ 357 private Set<String> generatedSourceNames; 358 359 /** 360 * Names and class files of the class files closed in this round. 361 * This set must be synchronized. Its iterators should preserve 362 * insertion order. 363 */ 364 private final Map<ModuleSymbol, Map<String, JavaFileObject>> generatedClasses; 365 366 /** 367 * JavaFileObjects for source files closed in this round. This 368 * set must be synchronized. Its iterators should preserve 369 * insertion order. 370 */ 371 private Set<JavaFileObject> generatedSourceFileObjects; 372 373 /** 374 * Names of all created source files. Its iterators should 375 * preserve insertion order. 376 */ 377 private final Set<Pair<ModuleSymbol, String>> aggregateGeneratedSourceNames; 378 379 /** 380 * Names of all created class files. Its iterators should 381 * preserve insertion order. 382 */ 383 private final Set<Pair<ModuleSymbol, String>> aggregateGeneratedClassNames; 384 385 private final Set<String> initialClassNames; 386 387 JavacFiler(Context context) { 388 this.context = context; 389 fileManager = context.get(JavaFileManager.class); 390 elementUtils = JavacElements.instance(context); 391 392 log = Log.instance(context); 393 modules = Modules.instance(context); 394 names = Names.instance(context); 395 syms = Symtab.instance(context); 396 397 initialInputs = synchronizedSet(new LinkedHashSet<>()); 398 fileObjectHistory = synchronizedSet(new LinkedHashSet<>()); 399 generatedSourceNames = synchronizedSet(new LinkedHashSet<>()); 400 generatedSourceFileObjects = synchronizedSet(new LinkedHashSet<>()); 401 402 generatedClasses = synchronizedMap(new LinkedHashMap<>()); 403 404 openTypeNames = synchronizedSet(new LinkedHashSet<>()); 405 406 aggregateGeneratedSourceNames = new LinkedHashSet<>(); 407 aggregateGeneratedClassNames = new LinkedHashSet<>(); 408 initialClassNames = new LinkedHashSet<>(); 409 410 lint = (Lint.instance(context)).isEnabled(PROCESSING); 411 } 412 413 @Override @DefinedBy(Api.ANNOTATION_PROCESSING) 414 public JavaFileObject createSourceFile(CharSequence nameAndModule, 415 Element... originatingElements) throws IOException { 416 Pair<ModuleSymbol, String> moduleAndClass = checkOrInferModule(nameAndModule); 417 return createSourceOrClassFile(moduleAndClass.fst, true, moduleAndClass.snd); 418 } 419 420 @Override @DefinedBy(Api.ANNOTATION_PROCESSING) 421 public JavaFileObject createClassFile(CharSequence nameAndModule, 422 Element... originatingElements) throws IOException { 423 Pair<ModuleSymbol, String> moduleAndClass = checkOrInferModule(nameAndModule); 424 return createSourceOrClassFile(moduleAndClass.fst, false, moduleAndClass.snd); 425 } 426 427 private Pair<ModuleSymbol, String> checkOrInferModule(CharSequence moduleAndPkg) throws FilerException { 428 String moduleAndPkgString = moduleAndPkg.toString(); 429 int slash = moduleAndPkgString.indexOf('/'); 430 431 if (slash != (-1)) { 432 //module name specified: 433 String module = moduleAndPkgString.substring(0, slash); 434 435 ModuleSymbol explicitModule = syms.getModule(names.fromString(module)); 436 437 if (explicitModule == null) { 438 throw new FilerException("Module: " + module + " does not exist."); 439 } 440 441 if (!modules.isRootModule(explicitModule)) { 442 throw new FilerException("Cannot write to the given module!"); 443 } 444 445 return Pair.of(explicitModule, moduleAndPkgString.substring(slash + 1)); 446 } else { 447 if (modules.multiModuleMode) { 448 throw new FilerException("No module to write to specified!"); 449 } 450 451 return Pair.of(modules.getDefaultModule(), moduleAndPkgString); 452 } 453 } 454 455 private JavaFileObject createSourceOrClassFile(ModuleSymbol mod, boolean isSourceFile, String name) throws IOException { 456 Assert.checkNonNull(mod); 457 458 if (lint) { 459 int periodIndex = name.lastIndexOf("."); 460 if (periodIndex != -1) { 461 String base = name.substring(periodIndex); 462 String extn = (isSourceFile ? ".java" : ".class"); 463 if (base.equals(extn)) 464 log.warning("proc.suspicious.class.name", name, extn); 465 } 466 } 467 checkNameAndExistence(mod, name, isSourceFile); 468 Location loc = (isSourceFile ? SOURCE_OUTPUT : CLASS_OUTPUT); 469 470 if (modules.multiModuleMode) { 471 loc = this.fileManager.getLocationForModule(loc, mod.name.toString()); 472 } 473 JavaFileObject.Kind kind = (isSourceFile ? 474 JavaFileObject.Kind.SOURCE : 475 JavaFileObject.Kind.CLASS); 476 477 JavaFileObject fileObject = 478 fileManager.getJavaFileForOutput(loc, name, kind, null); 479 checkFileReopening(fileObject, true); 480 481 if (lastRound) 482 log.warning("proc.file.create.last.round", name); 483 484 if (isSourceFile) 485 aggregateGeneratedSourceNames.add(Pair.of(mod, name)); 486 else 487 aggregateGeneratedClassNames.add(Pair.of(mod, name)); 488 openTypeNames.add(name); 489 490 return new FilerOutputJavaFileObject(mod, name, fileObject); 491 } 492 493 @Override @DefinedBy(Api.ANNOTATION_PROCESSING) 494 public FileObject createResource(JavaFileManager.Location location, 495 CharSequence moduleAndPkg, 496 CharSequence relativeName, 497 Element... originatingElements) throws IOException { 498 Pair<ModuleSymbol, String> moduleAndPackage = checkOrInferModule(moduleAndPkg); 499 ModuleSymbol msym = moduleAndPackage.fst; 500 String pkg = moduleAndPackage.snd; 501 502 locationCheck(location); 503 504 if (modules.multiModuleMode) { 505 Assert.checkNonNull(msym); 506 location = this.fileManager.getLocationForModule(location, msym.name.toString()); 507 } 508 509 String strPkg = pkg.toString(); 510 if (strPkg.length() > 0) 511 checkName(strPkg); 512 513 FileObject fileObject = 514 fileManager.getFileForOutput(location, strPkg, 515 relativeName.toString(), null); 516 checkFileReopening(fileObject, true); 517 518 if (fileObject instanceof JavaFileObject) 519 return new FilerOutputJavaFileObject(msym, null, (JavaFileObject)fileObject); 520 else 521 return new FilerOutputFileObject(msym, null, fileObject); 522 } 523 524 private void locationCheck(JavaFileManager.Location location) { 525 if (location instanceof StandardLocation) { 526 StandardLocation stdLoc = (StandardLocation) location; 527 if (!stdLoc.isOutputLocation()) 528 throw new IllegalArgumentException("Resource creation not supported in location " + 529 stdLoc); 530 } 531 } 532 533 @Override @DefinedBy(Api.ANNOTATION_PROCESSING) 534 public FileObject getResource(JavaFileManager.Location location, 535 CharSequence moduleAndPkg, 536 CharSequence relativeName) throws IOException { 537 Pair<ModuleSymbol, String> moduleAndPackage = checkOrInferModule(moduleAndPkg); 538 ModuleSymbol msym = moduleAndPackage.fst; 539 String pkg = moduleAndPackage.snd; 540 541 if (modules.multiModuleMode) { 542 Assert.checkNonNull(msym); 543 location = this.fileManager.getLocationForModule(location, msym.name.toString()); 544 } 545 546 if (pkg.length() > 0) 547 checkName(pkg); 548 549 // TODO: Only support reading resources in selected output 550 // locations? Only allow reading of non-source, non-class 551 // files from the supported input locations? 552 553 // In the following, getFileForInput is the "obvious" method 554 // to use, but it does not have the "obvious" semantics for 555 // SOURCE_OUTPUT and CLASS_OUTPUT. Conversely, getFileForOutput 556 // does not have the correct semantics for any "path" location 557 // with more than one component. So, for now, we use a hybrid 558 // invocation. 559 FileObject fileObject; 560 if (location.isOutputLocation()) { 561 fileObject = fileManager.getFileForOutput(location, 562 pkg, 563 relativeName.toString(), 564 null); 565 } else { 566 fileObject = fileManager.getFileForInput(location, 567 pkg, 568 relativeName.toString()); 569 } 570 if (fileObject == null) { 571 String name = (pkg.length() == 0) 572 ? relativeName.toString() : (pkg + "/" + relativeName); 573 throw new FileNotFoundException(name); 574 } 575 576 // If the path was already opened for writing, throw an exception. 577 checkFileReopening(fileObject, false); 578 return new FilerInputFileObject(fileObject); 579 } 580 581 private void checkName(String name) throws FilerException { 582 checkName(name, false); 583 } 584 585 private void checkName(String name, boolean allowUnnamedPackageInfo) throws FilerException { 586 if (!SourceVersion.isName(name) && !isPackageInfo(name, allowUnnamedPackageInfo)) { 587 if (lint) 588 log.warning("proc.illegal.file.name", name); 589 throw new FilerException("Illegal name " + name); 590 } 591 } 592 593 private boolean isPackageInfo(String name, boolean allowUnnamedPackageInfo) { 594 // Is the name of the form "package-info" or 595 // "foo.bar.package-info"? 596 final String PKG_INFO = "package-info"; 597 int periodIndex = name.lastIndexOf("."); 598 if (periodIndex == -1) { 599 return allowUnnamedPackageInfo ? name.equals(PKG_INFO) : false; 600 } else { 601 // "foo.bar.package-info." illegal 602 String prefix = name.substring(0, periodIndex); 603 String simple = name.substring(periodIndex+1); 604 return SourceVersion.isName(prefix) && simple.equals(PKG_INFO); 605 } 606 } 607 608 private void checkNameAndExistence(ModuleSymbol mod, String typename, boolean allowUnnamedPackageInfo) throws FilerException { 609 // TODO: Check if type already exists on source or class path? 610 // If so, use warning message key proc.type.already.exists 611 checkName(typename, allowUnnamedPackageInfo); 612 ClassSymbol existing; 613 boolean alreadySeen = aggregateGeneratedSourceNames.contains(Pair.of(mod, typename)) || 614 aggregateGeneratedClassNames.contains(Pair.of(mod, typename)) || 615 initialClassNames.contains(typename) || 616 ((existing = elementUtils.getTypeElement(typename)) != null && 617 initialInputs.contains(existing.sourcefile)); 618 if (alreadySeen) { 619 if (lint) 620 log.warning("proc.type.recreate", typename); 621 throw new FilerException("Attempt to recreate a file for type " + typename); 622 } 623 if (!mod.isUnnamed() && !typename.contains(".")) { 624 throw new FilerException("Attempt to create a type in unnamed package of a named module: " + typename); 625 } 626 } 627 628 /** 629 * Check to see if the file has already been opened; if so, throw 630 * an exception, otherwise add it to the set of files. 631 */ 632 private void checkFileReopening(FileObject fileObject, boolean forWriting) throws FilerException { 633 if (isInFileObjectHistory(fileObject, forWriting)) { 634 if (lint) 635 log.warning("proc.file.reopening", fileObject.getName()); 636 throw new FilerException("Attempt to reopen a file for path " + fileObject.getName()); 637 } 638 if (forWriting) 639 fileObjectHistory.add(fileObject); 640 } 641 642 private boolean isInFileObjectHistory(FileObject fileObject, boolean forWriting) { 643 if (forWriting) { 644 for(FileObject veteran : initialInputs) { 645 try { 646 if (fileManager.isSameFile(veteran, fileObject)) { 647 return true; 648 } 649 } catch (IllegalArgumentException e) { 650 //ignore... 651 } 652 } 653 for (String className : initialClassNames) { 654 try { 655 ClassSymbol existing = elementUtils.getTypeElement(className); 656 if ( existing != null 657 && ( (existing.sourcefile != null && fileManager.isSameFile(existing.sourcefile, fileObject)) 658 || (existing.classfile != null && fileManager.isSameFile(existing.classfile, fileObject)))) { 659 return true; 660 } 661 } catch (IllegalArgumentException e) { 662 //ignore... 663 } 664 } 665 } 666 667 for(FileObject veteran : fileObjectHistory) { 668 if (fileManager.isSameFile(veteran, fileObject)) { 669 return true; 670 } 671 } 672 673 return false; 674 } 675 676 public boolean newFiles() { 677 return (!generatedSourceNames.isEmpty()) 678 || (!generatedClasses.isEmpty()); 679 } 680 681 public Set<String> getGeneratedSourceNames() { 682 return generatedSourceNames; 683 } 684 685 public Set<JavaFileObject> getGeneratedSourceFileObjects() { 686 return generatedSourceFileObjects; 687 } 688 689 public Map<ModuleSymbol, Map<String, JavaFileObject>> getGeneratedClasses() { 690 return generatedClasses; 691 } 692 693 public void warnIfUnclosedFiles() { 694 if (!openTypeNames.isEmpty()) 695 log.warning("proc.unclosed.type.files", openTypeNames.toString()); 696 } 697 698 /** 699 * Update internal state for a new round. 700 */ 701 public void newRound() { 702 clearRoundState(); 703 } 704 705 void setLastRound(boolean lastRound) { 706 this.lastRound = lastRound; 707 } 708 709 public void setInitialState(Collection<? extends JavaFileObject> initialInputs, 710 Collection<String> initialClassNames) { 711 this.initialInputs.addAll(initialInputs); 712 this.initialClassNames.addAll(initialClassNames); 713 } 714 715 public void close() { 716 clearRoundState(); 717 // Cross-round state 718 initialClassNames.clear(); 719 initialInputs.clear(); 720 fileObjectHistory.clear(); 721 openTypeNames.clear(); 722 aggregateGeneratedSourceNames.clear(); 723 aggregateGeneratedClassNames.clear(); 724 } 725 726 private void clearRoundState() { 727 generatedSourceNames.clear(); 728 generatedSourceFileObjects.clear(); 729 generatedClasses.clear(); 730 } 731 732 /** 733 * Debugging function to display internal state. 734 */ 735 public void displayState() { 736 PrintWriter xout = context.get(Log.logKey).getWriter(Log.WriterKind.STDERR); 737 xout.println("File Object History : " + fileObjectHistory); 738 xout.println("Open Type Names : " + openTypeNames); 739 xout.println("Gen. Src Names : " + generatedSourceNames); 740 xout.println("Gen. Cls Names : " + generatedClasses.keySet()); 741 xout.println("Agg. Gen. Src Names : " + aggregateGeneratedSourceNames); 742 xout.println("Agg. Gen. Cls Names : " + aggregateGeneratedClassNames); 743 } 744 745 public String toString() { 746 return "javac Filer"; 747 } 748 749 /** 750 * Upon close, register files opened by create{Source, Class}File 751 * for annotation processing. 752 */ 753 private void closeFileObject(ModuleSymbol mod, String typeName, FileObject fileObject) { 754 /* 755 * If typeName is non-null, the file object was opened as a 756 * source or class file by the user. If a file was opened as 757 * a resource, typeName will be null and the file is *not* 758 * subject to annotation processing. 759 */ 760 if ((typeName != null)) { 761 if (!(fileObject instanceof JavaFileObject)) 762 throw new AssertionError("JavaFileOject not found for " + fileObject); 763 JavaFileObject javaFileObject = (JavaFileObject)fileObject; 764 switch(javaFileObject.getKind()) { 765 case SOURCE: 766 generatedSourceNames.add(typeName); 767 generatedSourceFileObjects.add(javaFileObject); 768 openTypeNames.remove(typeName); 769 break; 770 771 case CLASS: 772 generatedClasses.computeIfAbsent(mod, m -> Collections.synchronizedMap(new LinkedHashMap<>())).put(typeName, javaFileObject); 773 openTypeNames.remove(typeName); 774 break; 775 776 default: 777 break; 778 } 779 } 780 } 781 782} 783