JavacFiler.java revision 4056:adef848660f9
1317027Sdim/* 2317027Sdim * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved. 3317027Sdim * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4317027Sdim * 5317027Sdim * This code is free software; you can redistribute it and/or modify it 6317027Sdim * under the terms of the GNU General Public License version 2 only, as 7317027Sdim * published by the Free Software Foundation. Oracle designates this 8317027Sdim * particular file as subject to the "Classpath" exception as provided 9317027Sdim * by Oracle in the LICENSE file that accompanied this code. 10317027Sdim * 11317027Sdim * This code is distributed in the hope that it will be useful, but WITHOUT 12317027Sdim * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13317027Sdim * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14317027Sdim * version 2 for more details (a copy is included in the LICENSE file that 15317027Sdim * accompanied this code). 16317027Sdim * 17317027Sdim * You should have received a copy of the GNU General Public License version 18317027Sdim * 2 along with this work; if not, write to the Free Software Foundation, 19317027Sdim * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20317027Sdim * 21317027Sdim * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22317027Sdim * or visit www.oracle.com if you need additional information or have any 23317027Sdim * questions. 24317027Sdim */ 25317027Sdim 26317027Sdimpackage com.sun.tools.javac.processing; 27317027Sdim 28317027Sdimimport java.io.Closeable; 29317027Sdimimport java.io.FileNotFoundException; 30317027Sdimimport java.io.InputStream; 31317027Sdimimport java.io.OutputStream; 32317027Sdimimport java.io.FilterOutputStream; 33317027Sdimimport java.io.Reader; 34317027Sdimimport java.io.Writer; 35317027Sdimimport java.io.FilterWriter; 36317027Sdimimport java.io.PrintWriter; 37317027Sdimimport java.io.IOException; 38317027Sdimimport java.util.*; 39317027Sdim 40317027Sdimimport static java.util.Collections.*; 41317027Sdim 42317027Sdimimport javax.annotation.processing.*; 43317027Sdimimport javax.lang.model.SourceVersion; 44317027Sdimimport javax.lang.model.element.NestingKind; 45317027Sdimimport javax.lang.model.element.Modifier; 46317027Sdimimport javax.lang.model.element.Element; 47317027Sdimimport javax.tools.*; 48317027Sdimimport javax.tools.JavaFileManager.Location; 49317027Sdim 50317027Sdimimport static javax.tools.StandardLocation.SOURCE_OUTPUT; 51317027Sdimimport static javax.tools.StandardLocation.CLASS_OUTPUT; 52317027Sdim 53317027Sdimimport com.sun.tools.javac.code.Lint; 54317027Sdimimport com.sun.tools.javac.code.Symbol.ClassSymbol; 55317027Sdimimport com.sun.tools.javac.code.Symbol.ModuleSymbol; 56317027Sdimimport com.sun.tools.javac.code.Symtab; 57318384Sdimimport com.sun.tools.javac.comp.Modules; 58317027Sdimimport com.sun.tools.javac.model.JavacElements; 59317027Sdimimport com.sun.tools.javac.util.*; 60317027Sdimimport com.sun.tools.javac.util.DefinedBy.Api; 61317027Sdim 62317027Sdimimport static com.sun.tools.javac.code.Lint.LintCategory.PROCESSING; 63317027Sdimimport com.sun.tools.javac.code.Symbol.PackageSymbol; 64317027Sdimimport com.sun.tools.javac.main.Option; 65317027Sdim 66317027Sdim/** 67317027Sdim * The FilerImplementation class must maintain a number of 68317027Sdim * constraints. First, multiple attempts to open the same path within 69317027Sdim * the same invocation of the tool results in an IOException being 70317027Sdim * thrown. For example, trying to open the same source file twice: 71317027Sdim * 72317027Sdim * <pre> 73317027Sdim * createSourceFile("foo.Bar") 74317027Sdim * ... 75317027Sdim * createSourceFile("foo.Bar") 76317027Sdim * </pre> 77317027Sdim * 78317027Sdim * is disallowed as is opening a text file that happens to have 79317027Sdim * the same name as a source file: 80317027Sdim * 81317027Sdim * <pre> 82317027Sdim * createSourceFile("foo.Bar") 83317027Sdim * ... 84317027Sdim * createTextFile(SOURCE_TREE, "foo", new File("Bar"), null) 85317027Sdim * </pre> 86317027Sdim * 87317027Sdim * <p>Additionally, creating a source file that corresponds to an 88317027Sdim * already created class file (or vice versa) also results in an 89317027Sdim * IOException since each type can only be created once. However, if 90317027Sdim * the Filer is used to create a text file named *.java that happens 91317027Sdim * to correspond to an existing class file, a warning is *not* 92317027Sdim * generated. Similarly, a warning is not generated for a binary file 93317027Sdim * named *.class and an existing source file. 94317027Sdim * 95317027Sdim * <p>The reason for this difference is that source files and class 96317027Sdim * files are registered with the tool and can get passed on as 97317027Sdim * declarations to the next round of processing. Files that are just 98317027Sdim * named *.java and *.class are not processed in that manner; although 99317027Sdim * having extra source files and class files on the source path and 100317027Sdim * class path can alter the behavior of the tool and any final 101317027Sdim * compile. 102 * 103 * <p><b>This is NOT part of any supported API. 104 * If you write code that depends on this, you do so at your own risk. 105 * This code and its internal interfaces are subject to change or 106 * deletion without notice.</b> 107 */ 108public class JavacFiler implements Filer, Closeable { 109 // TODO: Implement different transaction model for updating the 110 // Filer's record keeping on file close. 111 112 private static final String ALREADY_OPENED = 113 "Output stream or writer has already been opened."; 114 private static final String NOT_FOR_READING = 115 "FileObject was not opened for reading."; 116 private static final String NOT_FOR_WRITING = 117 "FileObject was not opened for writing."; 118 119 /** 120 * Wrap a JavaFileObject to manage writing by the Filer. 121 */ 122 private class FilerOutputFileObject extends ForwardingFileObject<FileObject> { 123 private boolean opened = false; 124 private ModuleSymbol mod; 125 private String name; 126 127 FilerOutputFileObject(ModuleSymbol mod, String name, FileObject fileObject) { 128 super(fileObject); 129 this.mod = mod; 130 this.name = name; 131 } 132 133 @Override @DefinedBy(Api.COMPILER) 134 public synchronized OutputStream openOutputStream() throws IOException { 135 if (opened) 136 throw new IOException(ALREADY_OPENED); 137 opened = true; 138 return new FilerOutputStream(mod, name, fileObject); 139 } 140 141 @Override @DefinedBy(Api.COMPILER) 142 public synchronized Writer openWriter() throws IOException { 143 if (opened) 144 throw new IOException(ALREADY_OPENED); 145 opened = true; 146 return new FilerWriter(mod, name, fileObject); 147 } 148 149 // Three anti-literacy methods 150 @Override @DefinedBy(Api.COMPILER) 151 public InputStream openInputStream() throws IOException { 152 throw new IllegalStateException(NOT_FOR_READING); 153 } 154 155 @Override @DefinedBy(Api.COMPILER) 156 public Reader openReader(boolean ignoreEncodingErrors) throws IOException { 157 throw new IllegalStateException(NOT_FOR_READING); 158 } 159 160 @Override @DefinedBy(Api.COMPILER) 161 public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { 162 throw new IllegalStateException(NOT_FOR_READING); 163 } 164 165 @Override @DefinedBy(Api.COMPILER) 166 public boolean delete() { 167 return false; 168 } 169 } 170 171 private class FilerOutputJavaFileObject extends FilerOutputFileObject implements JavaFileObject { 172 private final JavaFileObject javaFileObject; 173 FilerOutputJavaFileObject(ModuleSymbol mod, String name, JavaFileObject javaFileObject) { 174 super(mod, name, javaFileObject); 175 this.javaFileObject = javaFileObject; 176 } 177 178 @DefinedBy(Api.COMPILER) 179 public JavaFileObject.Kind getKind() { 180 return javaFileObject.getKind(); 181 } 182 183 @DefinedBy(Api.COMPILER) 184 public boolean isNameCompatible(String simpleName, 185 JavaFileObject.Kind kind) { 186 return javaFileObject.isNameCompatible(simpleName, kind); 187 } 188 189 @DefinedBy(Api.COMPILER) 190 public NestingKind getNestingKind() { 191 return javaFileObject.getNestingKind(); 192 } 193 194 @DefinedBy(Api.COMPILER) 195 public Modifier getAccessLevel() { 196 return javaFileObject.getAccessLevel(); 197 } 198 } 199 200 /** 201 * Wrap a JavaFileObject to manage reading by the Filer. 202 */ 203 private class FilerInputFileObject extends ForwardingFileObject<FileObject> { 204 FilerInputFileObject(FileObject fileObject) { 205 super(fileObject); 206 } 207 208 @Override @DefinedBy(Api.COMPILER) 209 public OutputStream openOutputStream() throws IOException { 210 throw new IllegalStateException(NOT_FOR_WRITING); 211 } 212 213 @Override @DefinedBy(Api.COMPILER) 214 public Writer openWriter() throws IOException { 215 throw new IllegalStateException(NOT_FOR_WRITING); 216 } 217 218 @Override @DefinedBy(Api.COMPILER) 219 public boolean delete() { 220 return false; 221 } 222 } 223 224 private class FilerInputJavaFileObject extends FilerInputFileObject implements JavaFileObject { 225 private final JavaFileObject javaFileObject; 226 FilerInputJavaFileObject(JavaFileObject javaFileObject) { 227 super(javaFileObject); 228 this.javaFileObject = javaFileObject; 229 } 230 231 @DefinedBy(Api.COMPILER) 232 public JavaFileObject.Kind getKind() { 233 return javaFileObject.getKind(); 234 } 235 236 @DefinedBy(Api.COMPILER) 237 public boolean isNameCompatible(String simpleName, 238 JavaFileObject.Kind kind) { 239 return javaFileObject.isNameCompatible(simpleName, kind); 240 } 241 242 @DefinedBy(Api.COMPILER) 243 public NestingKind getNestingKind() { 244 return javaFileObject.getNestingKind(); 245 } 246 247 @DefinedBy(Api.COMPILER) 248 public Modifier getAccessLevel() { 249 return javaFileObject.getAccessLevel(); 250 } 251 } 252 253 254 /** 255 * Wrap a {@code OutputStream} returned from the {@code 256 * JavaFileManager} to properly register source or class files 257 * when they are closed. 258 */ 259 private class FilerOutputStream extends FilterOutputStream { 260 ModuleSymbol mod; 261 String typeName; 262 FileObject fileObject; 263 boolean closed = false; 264 265 /** 266 * @param typeName name of class or {@code null} if just a 267 * binary file 268 */ 269 FilerOutputStream(ModuleSymbol mod, String typeName, FileObject fileObject) throws IOException { 270 super(fileObject.openOutputStream()); 271 this.mod = mod; 272 this.typeName = typeName; 273 this.fileObject = fileObject; 274 } 275 276 public synchronized void close() throws IOException { 277 if (!closed) { 278 closed = true; 279 /* 280 * If an IOException occurs when closing the underlying 281 * stream, still try to process the file. 282 */ 283 284 closeFileObject(mod, typeName, fileObject); 285 out.close(); 286 } 287 } 288 } 289 290 /** 291 * Wrap a {@code Writer} returned from the {@code JavaFileManager} 292 * to properly register source or class files when they are 293 * closed. 294 */ 295 private class FilerWriter extends FilterWriter { 296 ModuleSymbol mod; 297 String typeName; 298 FileObject fileObject; 299 boolean closed = false; 300 301 /** 302 * @param fileObject the fileObject to be written to 303 * @param typeName name of source file or {@code null} if just a 304 * text file 305 */ 306 FilerWriter(ModuleSymbol mod, String typeName, FileObject fileObject) throws IOException { 307 super(fileObject.openWriter()); 308 this.mod = mod; 309 this.typeName = typeName; 310 this.fileObject = fileObject; 311 } 312 313 public synchronized void close() throws IOException { 314 if (!closed) { 315 closed = true; 316 /* 317 * If an IOException occurs when closing the underlying 318 * Writer, still try to process the file. 319 */ 320 321 closeFileObject(mod, typeName, fileObject); 322 out.close(); 323 } 324 } 325 } 326 327 JavaFileManager fileManager; 328 JavacElements elementUtils; 329 Log log; 330 Modules modules; 331 Names names; 332 Symtab syms; 333 Context context; 334 boolean lastRound; 335 336 private final boolean lint; 337 338 /** 339 * Initial inputs passed to the tool. This set must be 340 * synchronized. 341 */ 342 private final Set<FileObject> initialInputs; 343 344 /** 345 * Logical names of all created files. This set must be 346 * synchronized. 347 */ 348 private final Set<FileObject> fileObjectHistory; 349 350 /** 351 * Names of types that have had files created but not closed. 352 */ 353 private final Set<String> openTypeNames; 354 355 /** 356 * Names of source files closed in this round. This set must be 357 * synchronized. Its iterators should preserve insertion order. 358 */ 359 private Set<String> generatedSourceNames; 360 361 /** 362 * Names and class files of the class files closed in this round. 363 * This set must be synchronized. Its iterators should preserve 364 * insertion order. 365 */ 366 private final Map<ModuleSymbol, Map<String, JavaFileObject>> generatedClasses; 367 368 /** 369 * JavaFileObjects for source files closed in this round. This 370 * set must be synchronized. Its iterators should preserve 371 * insertion order. 372 */ 373 private Set<JavaFileObject> generatedSourceFileObjects; 374 375 /** 376 * Names of all created source files. Its iterators should 377 * preserve insertion order. 378 */ 379 private final Set<Pair<ModuleSymbol, String>> aggregateGeneratedSourceNames; 380 381 /** 382 * Names of all created class files. Its iterators should 383 * preserve insertion order. 384 */ 385 private final Set<Pair<ModuleSymbol, String>> aggregateGeneratedClassNames; 386 387 private final Set<String> initialClassNames; 388 389 private final String defaultTargetModule; 390 391 JavacFiler(Context context) { 392 this.context = context; 393 fileManager = context.get(JavaFileManager.class); 394 elementUtils = JavacElements.instance(context); 395 396 log = Log.instance(context); 397 modules = Modules.instance(context); 398 names = Names.instance(context); 399 syms = Symtab.instance(context); 400 401 initialInputs = synchronizedSet(new LinkedHashSet<>()); 402 fileObjectHistory = synchronizedSet(new LinkedHashSet<>()); 403 generatedSourceNames = synchronizedSet(new LinkedHashSet<>()); 404 generatedSourceFileObjects = synchronizedSet(new LinkedHashSet<>()); 405 406 generatedClasses = synchronizedMap(new LinkedHashMap<>()); 407 408 openTypeNames = synchronizedSet(new LinkedHashSet<>()); 409 410 aggregateGeneratedSourceNames = new LinkedHashSet<>(); 411 aggregateGeneratedClassNames = new LinkedHashSet<>(); 412 initialClassNames = new LinkedHashSet<>(); 413 414 lint = (Lint.instance(context)).isEnabled(PROCESSING); 415 416 Options options = Options.instance(context); 417 418 defaultTargetModule = options.get(Option.DEFAULT_MODULE_FOR_CREATED_FILES); 419 } 420 421 @Override @DefinedBy(Api.ANNOTATION_PROCESSING) 422 public JavaFileObject createSourceFile(CharSequence nameAndModule, 423 Element... originatingElements) throws IOException { 424 Pair<ModuleSymbol, String> moduleAndClass = checkOrInferModule(nameAndModule); 425 return createSourceOrClassFile(moduleAndClass.fst, true, moduleAndClass.snd); 426 } 427 428 @Override @DefinedBy(Api.ANNOTATION_PROCESSING) 429 public JavaFileObject createClassFile(CharSequence nameAndModule, 430 Element... originatingElements) throws IOException { 431 Pair<ModuleSymbol, String> moduleAndClass = checkOrInferModule(nameAndModule); 432 return createSourceOrClassFile(moduleAndClass.fst, false, moduleAndClass.snd); 433 } 434 435 private Pair<ModuleSymbol, String> checkOrInferModule(CharSequence moduleAndPkg) throws FilerException { 436 String moduleAndPkgString = moduleAndPkg.toString(); 437 int slash = moduleAndPkgString.indexOf('/'); 438 String module; 439 String pkg; 440 441 if (slash == (-1)) { 442 //module name not specified: 443 int lastDot = moduleAndPkgString.lastIndexOf('.'); 444 String pack = lastDot != (-1) ? moduleAndPkgString.substring(0, lastDot) : ""; 445 ModuleSymbol msym = inferModule(pack); 446 447 if (msym != null) { 448 return Pair.of(msym, moduleAndPkgString); 449 } 450 451 if (defaultTargetModule == null) { 452 throw new FilerException("Cannot determine target module."); 453 } 454 455 module = defaultTargetModule; 456 pkg = moduleAndPkgString; 457 } else { 458 //module name specified: 459 module = moduleAndPkgString.substring(0, slash); 460 pkg = moduleAndPkgString.substring(slash + 1); 461 } 462 463 ModuleSymbol explicitModule = syms.getModule(names.fromString(module)); 464 465 if (explicitModule == null) { 466 throw new FilerException("Module: " + module + " does not exist."); 467 } 468 469 if (!modules.isRootModule(explicitModule)) { 470 throw new FilerException("Cannot write to the given module."); 471 } 472 473 return Pair.of(explicitModule, pkg); 474 } 475 476 private JavaFileObject createSourceOrClassFile(ModuleSymbol mod, boolean isSourceFile, String name) throws IOException { 477 Assert.checkNonNull(mod); 478 479 if (lint) { 480 int periodIndex = name.lastIndexOf("."); 481 if (periodIndex != -1) { 482 String base = name.substring(periodIndex); 483 String extn = (isSourceFile ? ".java" : ".class"); 484 if (base.equals(extn)) 485 log.warning("proc.suspicious.class.name", name, extn); 486 } 487 } 488 checkNameAndExistence(mod, name, isSourceFile); 489 Location loc = (isSourceFile ? SOURCE_OUTPUT : CLASS_OUTPUT); 490 491 if (modules.multiModuleMode) { 492 loc = this.fileManager.getLocationForModule(loc, mod.name.toString()); 493 } 494 JavaFileObject.Kind kind = (isSourceFile ? 495 JavaFileObject.Kind.SOURCE : 496 JavaFileObject.Kind.CLASS); 497 498 JavaFileObject fileObject = 499 fileManager.getJavaFileForOutput(loc, name, kind, null); 500 checkFileReopening(fileObject, true); 501 502 if (lastRound) 503 log.warning("proc.file.create.last.round", name); 504 505 if (isSourceFile) 506 aggregateGeneratedSourceNames.add(Pair.of(mod, name)); 507 else 508 aggregateGeneratedClassNames.add(Pair.of(mod, name)); 509 openTypeNames.add(name); 510 511 return new FilerOutputJavaFileObject(mod, name, fileObject); 512 } 513 514 @Override @DefinedBy(Api.ANNOTATION_PROCESSING) 515 public FileObject createResource(JavaFileManager.Location location, 516 CharSequence moduleAndPkg, 517 CharSequence relativeName, 518 Element... originatingElements) throws IOException { 519 Tuple3<Location, ModuleSymbol, String> locationModuleAndPackage = checkOrInferModule(location, moduleAndPkg, true); 520 location = locationModuleAndPackage.a; 521 ModuleSymbol msym = locationModuleAndPackage.b; 522 String pkg = locationModuleAndPackage.c; 523 524 locationCheck(location); 525 526 String strPkg = pkg.toString(); 527 if (strPkg.length() > 0) 528 checkName(strPkg); 529 530 FileObject fileObject = 531 fileManager.getFileForOutput(location, strPkg, 532 relativeName.toString(), null); 533 checkFileReopening(fileObject, true); 534 535 if (fileObject instanceof JavaFileObject) 536 return new FilerOutputJavaFileObject(msym, null, (JavaFileObject)fileObject); 537 else 538 return new FilerOutputFileObject(msym, null, fileObject); 539 } 540 541 private void locationCheck(JavaFileManager.Location location) { 542 if (location instanceof StandardLocation) { 543 StandardLocation stdLoc = (StandardLocation) location; 544 if (!stdLoc.isOutputLocation()) 545 throw new IllegalArgumentException("Resource creation not supported in location " + 546 stdLoc); 547 } 548 } 549 550 @Override @DefinedBy(Api.ANNOTATION_PROCESSING) 551 public FileObject getResource(JavaFileManager.Location location, 552 CharSequence moduleAndPkg, 553 CharSequence relativeName) throws IOException { 554 Tuple3<Location, ModuleSymbol, String> locationModuleAndPackage = checkOrInferModule(location, moduleAndPkg, false); 555 location = locationModuleAndPackage.a; 556 String pkg = locationModuleAndPackage.c; 557 558 if (pkg.length() > 0) 559 checkName(pkg); 560 561 // TODO: Only support reading resources in selected output 562 // locations? Only allow reading of non-source, non-class 563 // files from the supported input locations? 564 565 // In the following, getFileForInput is the "obvious" method 566 // to use, but it does not have the "obvious" semantics for 567 // SOURCE_OUTPUT and CLASS_OUTPUT. Conversely, getFileForOutput 568 // does not have the correct semantics for any "path" location 569 // with more than one component. So, for now, we use a hybrid 570 // invocation. 571 FileObject fileObject; 572 if (location.isOutputLocation()) { 573 fileObject = fileManager.getFileForOutput(location, 574 pkg, 575 relativeName.toString(), 576 null); 577 } else { 578 fileObject = fileManager.getFileForInput(location, 579 pkg, 580 relativeName.toString()); 581 } 582 if (fileObject == null) { 583 String name = (pkg.length() == 0) 584 ? relativeName.toString() : (pkg + "/" + relativeName); 585 throw new FileNotFoundException(name); 586 } 587 588 // If the path was already opened for writing, throw an exception. 589 checkFileReopening(fileObject, false); 590 return new FilerInputFileObject(fileObject); 591 } 592 593 private Tuple3<JavaFileManager.Location, ModuleSymbol, String> checkOrInferModule(JavaFileManager.Location location, 594 CharSequence moduleAndPkg, 595 boolean write) throws IOException { 596 String moduleAndPkgString = moduleAndPkg.toString(); 597 int slash = moduleAndPkgString.indexOf('/'); 598 boolean multiModuleLocation = location.isModuleOrientedLocation() || 599 (modules.multiModuleMode && location.isOutputLocation()); 600 String module; 601 String pkg; 602 603 if (slash == (-1)) { 604 //module name not specified: 605 if (!multiModuleLocation) { 606 //package oriented location: 607 return new Tuple3<>(location, modules.getDefaultModule(), moduleAndPkgString); 608 } 609 610 if (location.isOutputLocation()) { 611 ModuleSymbol msym = inferModule(moduleAndPkgString); 612 613 if (msym != null) { 614 Location moduleLoc = 615 fileManager.getLocationForModule(location, msym.name.toString()); 616 return new Tuple3<>(moduleLoc, msym, moduleAndPkgString); 617 } 618 } 619 620 if (defaultTargetModule == null) { 621 throw new FilerException("No module specified and the location is either " + 622 "a module-oriented location, or a multi-module " + 623 "output location."); 624 } 625 626 module = defaultTargetModule; 627 pkg = moduleAndPkgString; 628 } else { 629 //module name specified: 630 module = moduleAndPkgString.substring(0, slash); 631 pkg = moduleAndPkgString.substring(slash + 1); 632 } 633 634 if (multiModuleLocation) { 635 ModuleSymbol explicitModule = syms.getModule(names.fromString(module)); 636 637 if (explicitModule == null) { 638 throw new FilerException("Module: " + module + " does not exist."); 639 } 640 641 if (write && !modules.isRootModule(explicitModule)) { 642 throw new FilerException("Cannot write to the given module."); 643 } 644 645 Location moduleLoc = fileManager.getLocationForModule(location, module); 646 647 return new Tuple3<>(moduleLoc, explicitModule, pkg); 648 } else { 649 throw new FilerException("Module specified but the location is neither " + 650 "a module-oriented location, nor a multi-module " + 651 "output location."); 652 } 653 } 654 655 static final class Tuple3<A, B, C> { 656 final A a; 657 final B b; 658 final C c; 659 660 public Tuple3(A a, B b, C c) { 661 this.a = a; 662 this.b = b; 663 this.c = c; 664 } 665 } 666 667 private ModuleSymbol inferModule(String pkg) { 668 if (modules.getDefaultModule() == syms.noModule) 669 return modules.getDefaultModule(); 670 671 Set<ModuleSymbol> rootModules = modules.getRootModules(); 672 673 if (rootModules.size() == 1) { 674 return rootModules.iterator().next(); 675 } 676 677 PackageSymbol pack = elementUtils.getPackageElement(pkg); 678 679 if (pack != null && pack.modle != syms.unnamedModule) { 680 return pack.modle; 681 } 682 683 return null; 684 } 685 686 private void checkName(String name) throws FilerException { 687 checkName(name, false); 688 } 689 690 private void checkName(String name, boolean allowUnnamedPackageInfo) throws FilerException { 691 if (!SourceVersion.isName(name) && !isPackageInfo(name, allowUnnamedPackageInfo)) { 692 if (lint) 693 log.warning("proc.illegal.file.name", name); 694 throw new FilerException("Illegal name " + name); 695 } 696 } 697 698 private boolean isPackageInfo(String name, boolean allowUnnamedPackageInfo) { 699 // Is the name of the form "package-info" or 700 // "foo.bar.package-info"? 701 final String PKG_INFO = "package-info"; 702 int periodIndex = name.lastIndexOf("."); 703 if (periodIndex == -1) { 704 return allowUnnamedPackageInfo ? name.equals(PKG_INFO) : false; 705 } else { 706 // "foo.bar.package-info." illegal 707 String prefix = name.substring(0, periodIndex); 708 String simple = name.substring(periodIndex+1); 709 return SourceVersion.isName(prefix) && simple.equals(PKG_INFO); 710 } 711 } 712 713 private void checkNameAndExistence(ModuleSymbol mod, String typename, boolean allowUnnamedPackageInfo) throws FilerException { 714 // TODO: Check if type already exists on source or class path? 715 // If so, use warning message key proc.type.already.exists 716 checkName(typename, allowUnnamedPackageInfo); 717 ClassSymbol existing; 718 boolean alreadySeen = aggregateGeneratedSourceNames.contains(Pair.of(mod, typename)) || 719 aggregateGeneratedClassNames.contains(Pair.of(mod, typename)) || 720 initialClassNames.contains(typename) || 721 ((existing = elementUtils.getTypeElement(typename)) != null && 722 initialInputs.contains(existing.sourcefile)); 723 if (alreadySeen) { 724 if (lint) 725 log.warning("proc.type.recreate", typename); 726 throw new FilerException("Attempt to recreate a file for type " + typename); 727 } 728 if (!mod.isUnnamed() && !typename.contains(".")) { 729 throw new FilerException("Attempt to create a type in unnamed package of a named module: " + typename); 730 } 731 } 732 733 /** 734 * Check to see if the file has already been opened; if so, throw 735 * an exception, otherwise add it to the set of files. 736 */ 737 private void checkFileReopening(FileObject fileObject, boolean forWriting) throws FilerException { 738 if (isInFileObjectHistory(fileObject, forWriting)) { 739 if (lint) 740 log.warning("proc.file.reopening", fileObject.getName()); 741 throw new FilerException("Attempt to reopen a file for path " + fileObject.getName()); 742 } 743 if (forWriting) 744 fileObjectHistory.add(fileObject); 745 } 746 747 private boolean isInFileObjectHistory(FileObject fileObject, boolean forWriting) { 748 if (forWriting) { 749 for(FileObject veteran : initialInputs) { 750 try { 751 if (fileManager.isSameFile(veteran, fileObject)) { 752 return true; 753 } 754 } catch (IllegalArgumentException e) { 755 //ignore... 756 } 757 } 758 for (String className : initialClassNames) { 759 try { 760 ClassSymbol existing = elementUtils.getTypeElement(className); 761 if ( existing != null 762 && ( (existing.sourcefile != null && fileManager.isSameFile(existing.sourcefile, fileObject)) 763 || (existing.classfile != null && fileManager.isSameFile(existing.classfile, fileObject)))) { 764 return true; 765 } 766 } catch (IllegalArgumentException e) { 767 //ignore... 768 } 769 } 770 } 771 772 for(FileObject veteran : fileObjectHistory) { 773 if (fileManager.isSameFile(veteran, fileObject)) { 774 return true; 775 } 776 } 777 778 return false; 779 } 780 781 public boolean newFiles() { 782 return (!generatedSourceNames.isEmpty()) 783 || (!generatedClasses.isEmpty()); 784 } 785 786 public Set<String> getGeneratedSourceNames() { 787 return generatedSourceNames; 788 } 789 790 public Set<JavaFileObject> getGeneratedSourceFileObjects() { 791 return generatedSourceFileObjects; 792 } 793 794 public Map<ModuleSymbol, Map<String, JavaFileObject>> getGeneratedClasses() { 795 return generatedClasses; 796 } 797 798 public void warnIfUnclosedFiles() { 799 if (!openTypeNames.isEmpty()) 800 log.warning("proc.unclosed.type.files", openTypeNames.toString()); 801 } 802 803 /** 804 * Update internal state for a new round. 805 */ 806 public void newRound() { 807 clearRoundState(); 808 } 809 810 void setLastRound(boolean lastRound) { 811 this.lastRound = lastRound; 812 } 813 814 public void setInitialState(Collection<? extends JavaFileObject> initialInputs, 815 Collection<String> initialClassNames) { 816 this.initialInputs.addAll(initialInputs); 817 this.initialClassNames.addAll(initialClassNames); 818 } 819 820 public void close() { 821 clearRoundState(); 822 // Cross-round state 823 initialClassNames.clear(); 824 initialInputs.clear(); 825 fileObjectHistory.clear(); 826 openTypeNames.clear(); 827 aggregateGeneratedSourceNames.clear(); 828 aggregateGeneratedClassNames.clear(); 829 } 830 831 private void clearRoundState() { 832 generatedSourceNames.clear(); 833 generatedSourceFileObjects.clear(); 834 generatedClasses.clear(); 835 } 836 837 /** 838 * Debugging function to display internal state. 839 */ 840 public void displayState() { 841 PrintWriter xout = context.get(Log.logKey).getWriter(Log.WriterKind.STDERR); 842 xout.println("File Object History : " + fileObjectHistory); 843 xout.println("Open Type Names : " + openTypeNames); 844 xout.println("Gen. Src Names : " + generatedSourceNames); 845 xout.println("Gen. Cls Names : " + generatedClasses.keySet()); 846 xout.println("Agg. Gen. Src Names : " + aggregateGeneratedSourceNames); 847 xout.println("Agg. Gen. Cls Names : " + aggregateGeneratedClassNames); 848 } 849 850 public String toString() { 851 return "javac Filer"; 852 } 853 854 /** 855 * Upon close, register files opened by create{Source, Class}File 856 * for annotation processing. 857 */ 858 private void closeFileObject(ModuleSymbol mod, String typeName, FileObject fileObject) { 859 /* 860 * If typeName is non-null, the file object was opened as a 861 * source or class file by the user. If a file was opened as 862 * a resource, typeName will be null and the file is *not* 863 * subject to annotation processing. 864 */ 865 if ((typeName != null)) { 866 if (!(fileObject instanceof JavaFileObject)) 867 throw new AssertionError("JavaFileOject not found for " + fileObject); 868 JavaFileObject javaFileObject = (JavaFileObject)fileObject; 869 switch(javaFileObject.getKind()) { 870 case SOURCE: 871 generatedSourceNames.add(typeName); 872 generatedSourceFileObjects.add(javaFileObject); 873 openTypeNames.remove(typeName); 874 break; 875 876 case CLASS: 877 generatedClasses.computeIfAbsent(mod, m -> Collections.synchronizedMap(new LinkedHashMap<>())).put(typeName, javaFileObject); 878 openTypeNames.remove(typeName); 879 break; 880 881 default: 882 break; 883 } 884 } 885 } 886 887} 888