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