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