1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.metaschema.databind.codegen;
7   
8   import gov.nist.secauto.metaschema.core.model.IModule;
9   import gov.nist.secauto.metaschema.databind.codegen.config.IBindingConfiguration;
10  import gov.nist.secauto.metaschema.databind.codegen.typeinfo.IMetaschemaClassFactory;
11  import gov.nist.secauto.metaschema.databind.codegen.typeinfo.ITypeResolver;
12  
13  import java.io.IOException;
14  import java.nio.file.Path;
15  import java.util.Collection;
16  import java.util.HashMap;
17  import java.util.Map;
18  import java.util.stream.Stream;
19  
20  import edu.umd.cs.findbugs.annotations.NonNull;
21  import edu.umd.cs.findbugs.annotations.Nullable;
22  
23  /**
24   * Information about Java classes generated for a collection of Module modules.
25   */
26  public interface IProduction {
27  
28    /**
29     * Get information about the Java classes generated for each Module module in
30     * the collection.
31     *
32     * @return the Java class information for each module
33     */
34    @NonNull
35    Collection<IGeneratedModuleClass> getModuleProductions();
36  
37    /**
38     * Get information about the Java classes generated for the provided Module
39     * {@code module}.
40     *
41     * @param module
42     *          the Module module to get information for
43     * @return the Java class information for the module or {@code null} if this
44     *         production did not involve generating classes for the provided module
45     */
46    @Nullable
47    IGeneratedModuleClass getModuleProduction(@NonNull IModule module);
48  
49    /**
50     * Get a stream of all definition Java classes generated as part of this
51     * production.
52     * <p>
53     * This will include each unique class generated for all Module modules
54     * associated with this production.
55     *
56     * @return the stream of generated Java classes
57     */
58    @NonNull
59    Collection<IGeneratedDefinitionClass> getGlobalDefinitionClasses();
60  
61    /**
62     * Get a stream of all Java classes generated as part of this production,
63     * including module, definition, and package-info classes.
64     *
65     * @return the stream of generated Java classes
66     */
67    @NonNull
68    Stream<? extends IGeneratedClass> getGeneratedClasses();
69  
70    /**
71     * Create a new production for the provided set of Module {@code modules}.
72     *
73     * @param modules
74     *          the Module modules to generate and compile classes for
75     * @param bindingConfiguration
76     *          binding customizations that can be used to set namespaces, class
77     *          names, and other aspects of generated classes
78     * @param classDir
79     *          the directory to generate and compile classes in
80     * @return the production information
81     * @throws IOException
82     *           if an error occurred while generating or compiling the classes
83     */
84    @NonNull
85    static IProduction of( // NOPMD - intentional
86        @NonNull Collection<? extends IModule> modules,
87        @NonNull IBindingConfiguration bindingConfiguration,
88        @NonNull Path classDir) throws IOException {
89  
90      ITypeResolver typeResolver = ITypeResolver.newTypeResolver(bindingConfiguration);
91  
92      IMetaschemaClassFactory classFactory = IMetaschemaClassFactory.newInstance(typeResolver);
93  
94      ProductionImpl retval = new ProductionImpl();
95      for (IModule module : modules) {
96        assert module != null;
97        retval.addModule(module, classFactory, classDir);
98      }
99  
100     Map<String, PackageMetadata> packageNameToPackageMetadataMap = new HashMap<>(); // NOPMD - no concurrency
101     for (IGeneratedModuleClass moduleProduction : retval.getModuleProductions()) {
102       String packageName = moduleProduction.getPackageName();
103 
104       PackageMetadata metadata = packageNameToPackageMetadataMap.get(packageName);
105       if (metadata == null) {
106         metadata = new PackageMetadata(moduleProduction); // NOPMD - intentional
107         packageNameToPackageMetadataMap.put(metadata.getPackageName(), metadata);
108       } else {
109         metadata.addModule(moduleProduction);
110       }
111     }
112 
113     for (PackageMetadata metadata : packageNameToPackageMetadataMap.values()) {
114       assert metadata != null;
115       retval.addPackage(
116           metadata,
117           classFactory,
118           classDir);
119     }
120     return retval;
121   }
122 }