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