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