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.Collections;
12  import java.util.HashMap;
13  import java.util.List;
14  import java.util.Map;
15  import java.util.stream.Collectors;
16  import java.util.stream.Stream;
17  
18  import dev.metaschema.core.model.IModule;
19  import dev.metaschema.core.util.ObjectUtils;
20  import dev.metaschema.databind.codegen.typeinfo.IMetaschemaClassFactory;
21  import edu.umd.cs.findbugs.annotations.NonNull;
22  
23  /**
24   * Default implementation of {@link IProduction} that tracks generated classes
25   * for modules and packages.
26   */
27  class ProductionImpl implements IProduction {
28  
29    @NonNull
30    private final Map<IModule, IGeneratedModuleClass> moduleToProductionMap // NOPMD - immutable
31        = new HashMap<>();
32    @NonNull
33    private final Map<String, IPackageProduction> packageNameToProductionMap // NOPMD - immutable
34        = new HashMap<>();
35  
36    /**
37     * Add a module and its imports to this production.
38     *
39     * @param module
40     *          the module to add
41     * @param classFactory
42     *          the class factory to use for generation
43     * @param targetDirectory
44     *          the target directory for generated classes
45     * @throws IOException
46     *           if an error occurs during generation
47     */
48    public void addModule(
49        @NonNull IModule module,
50        @NonNull IMetaschemaClassFactory classFactory,
51        @NonNull Path targetDirectory) throws IOException {
52      for (IModule importedModule : module.getImportedModules()) {
53        assert importedModule != null;
54        addModule(importedModule, classFactory, targetDirectory);
55      }
56  
57      if (moduleToProductionMap.get(module) == null) {
58        IGeneratedModuleClass metaschemaClass = classFactory.generateClass(module, targetDirectory);
59        moduleToProductionMap.put(module, metaschemaClass);
60      }
61    }
62  
63    /**
64     * Add a package to this production.
65     *
66     * @param metadata
67     *          the package metadata
68     * @param classFactory
69     *          the class factory to use for generation
70     * @param targetDirectory
71     *          the target directory for generated classes
72     * @return the generated package production
73     * @throws IOException
74     *           if an error occurs during generation
75     */
76    protected IPackageProduction addPackage(
77        @NonNull PackageMetadata metadata,
78        @NonNull IMetaschemaClassFactory classFactory,
79        @NonNull Path targetDirectory)
80        throws IOException {
81      String javaPackage = metadata.getPackageName();
82  
83      IPackageProduction retval
84          = new PackageProductionImpl(
85              metadata,
86              classFactory,
87              targetDirectory);
88      packageNameToProductionMap.put(javaPackage, retval);
89      return retval;
90    }
91  
92    @Override
93    @SuppressWarnings("null")
94    public Collection<IGeneratedModuleClass> getModuleProductions() {
95      return Collections.unmodifiableCollection(moduleToProductionMap.values());
96    }
97  
98    /**
99     * Get all package productions in this production.
100    *
101    * @return an unmodifiable collection of package productions
102    */
103   @SuppressWarnings("null")
104   @NonNull
105   protected Collection<IPackageProduction> getPackageProductions() {
106     return Collections.unmodifiableCollection(packageNameToProductionMap.values());
107   }
108 
109   @Override
110   public IGeneratedModuleClass getModuleProduction(IModule module) {
111     return moduleToProductionMap.get(module);
112   }
113 
114   @Override
115   public List<IGeneratedDefinitionClass> getGlobalDefinitionClasses() {
116     return ObjectUtils.notNull(getModuleProductions().stream()
117         .flatMap(metaschema -> metaschema.getGeneratedDefinitionClasses().stream())
118         .collect(Collectors.toUnmodifiableList()));
119   }
120 
121   @Override
122   public Stream<? extends IGeneratedClass> getGeneratedClasses() {
123     return ObjectUtils.notNull(Stream.concat(
124         // generated definitions and Metaschema module
125         getModuleProductions().stream()
126             .flatMap(module -> Stream.concat(
127                 Stream.of(module),
128                 module.getGeneratedDefinitionClasses().stream())),
129         // generated package-info.java
130         getPackageProductions().stream()
131             .flatMap(javaPackage -> Stream.of(javaPackage.getGeneratedClass()))));
132   }
133 
134 }