001/*
002 * SPDX-FileCopyrightText: none
003 * SPDX-License-Identifier: CC0-1.0
004 */
005
006package gov.nist.secauto.metaschema.databind.codegen;
007
008import gov.nist.secauto.metaschema.core.model.IModule;
009import gov.nist.secauto.metaschema.databind.codegen.config.IBindingConfiguration;
010import gov.nist.secauto.metaschema.databind.codegen.typeinfo.IMetaschemaClassFactory;
011import gov.nist.secauto.metaschema.databind.codegen.typeinfo.ITypeResolver;
012
013import java.io.IOException;
014import java.nio.file.Path;
015import java.util.Collection;
016import java.util.HashMap;
017import java.util.Map;
018import java.util.stream.Stream;
019
020import edu.umd.cs.findbugs.annotations.NonNull;
021import edu.umd.cs.findbugs.annotations.Nullable;
022
023/**
024 * Information about Java classes generated for a collection of Module modules.
025 */
026public interface IProduction {
027
028  /**
029   * Get information about the Java classes generated for each Module module in
030   * the collection.
031   *
032   * @return the Java class information for each module
033   */
034  @NonNull
035  Collection<IGeneratedModuleClass> getModuleProductions();
036
037  /**
038   * Get information about the Java classes generated for the provided Module
039   * {@code module}.
040   *
041   * @param module
042   *          the Module module to get information for
043   * @return the Java class information for the module or {@code null} if this
044   *         production did not involve generating classes for the provided module
045   */
046  @Nullable
047  IGeneratedModuleClass getModuleProduction(@NonNull IModule module);
048
049  /**
050   * Get a stream of all definition Java classes generated as part of this
051   * production.
052   * <p>
053   * This will include each unique class generated for all Module modules
054   * associated with this production.
055   *
056   * @return the stream of generated Java classes
057   */
058  @NonNull
059  Collection<IGeneratedDefinitionClass> getGlobalDefinitionClasses();
060
061  /**
062   * Get a stream of all Java classes generated as part of this production,
063   * including module, definition, and package-info classes.
064   *
065   * @return the stream of generated Java classes
066   */
067  @NonNull
068  Stream<? extends IGeneratedClass> getGeneratedClasses();
069
070  /**
071   * Create a new production for the provided set of Module {@code modules}.
072   *
073   * @param modules
074   *          the Module modules to generate and compile classes for
075   * @param bindingConfiguration
076   *          binding customizations that can be used to set namespaces, class
077   *          names, and other aspects of generated classes
078   * @param classDir
079   *          the directory to generate and compile classes in
080   * @return the production information
081   * @throws IOException
082   *           if an error occurred while generating or compiling the classes
083   */
084  @NonNull
085  static IProduction of( // NOPMD - intentional
086      @NonNull Collection<? extends IModule> modules,
087      @NonNull IBindingConfiguration bindingConfiguration,
088      @NonNull Path classDir) throws IOException {
089
090    ITypeResolver typeResolver = ITypeResolver.newTypeResolver(bindingConfiguration);
091
092    IMetaschemaClassFactory classFactory = IMetaschemaClassFactory.newInstance(typeResolver);
093
094    ProductionImpl retval = new ProductionImpl();
095    for (IModule module : modules) {
096      assert module != null;
097      retval.addModule(module, classFactory, classDir);
098    }
099
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}