001/*
002 * SPDX-FileCopyrightText: none
003 * SPDX-License-Identifier: CC0-1.0
004 */
005
006package gov.nist.secauto.metaschema.maven.plugin;
007
008import gov.nist.secauto.metaschema.core.model.IModule;
009import gov.nist.secauto.metaschema.core.model.MetaschemaException;
010import gov.nist.secauto.metaschema.core.util.ObjectUtils;
011import gov.nist.secauto.metaschema.databind.IBindingContext;
012import gov.nist.secauto.metaschema.databind.codegen.JavaGenerator;
013import gov.nist.secauto.metaschema.databind.codegen.config.DefaultBindingConfiguration;
014
015import org.apache.maven.plugin.MojoExecutionException;
016import org.apache.maven.plugins.annotations.LifecyclePhase;
017import org.apache.maven.plugins.annotations.Mojo;
018import org.apache.maven.plugins.annotations.Parameter;
019
020import java.io.File;
021import java.io.IOException;
022import java.util.Arrays;
023import java.util.Collections;
024import java.util.List;
025import java.util.Set;
026
027import edu.umd.cs.findbugs.annotations.NonNull;
028
029/**
030 * Goal which generates Java source files for a given set of Metaschema modules.
031 */
032@Mojo(name = "generate-sources", defaultPhase = LifecyclePhase.GENERATE_SOURCES)
033public class GenerateSourcesMojo
034    extends AbstractMetaschemaMojo {
035  private static final String STALE_FILE_NAME = "generateSourcesStaleFile";
036
037  /**
038   * A set of binding configurations.
039   */
040  @Parameter
041  protected File[] configs;
042
043  /**
044   * <p>
045   * Gets the last part of the stale filename.
046   * </p>
047   * <p>
048   * The full stale filename will be generated by pre-pending
049   * {@code "." + getExecution().getExecutionId()} to this staleFileName.
050   *
051   * @return the stale filename postfix
052   */
053  @Override
054  protected String getStaleFileName() {
055    return STALE_FILE_NAME;
056  }
057
058  /**
059   * Retrieve a list of binding configurations.
060   *
061   * @return the collection of binding configurations
062   */
063  protected List<File> getConfigs() {
064    List<File> retval;
065    if (configs == null) {
066      retval = Collections.emptyList();
067    } else {
068      retval = Arrays.asList(configs);
069    }
070    return retval;
071  }
072
073  /**
074   * Generate the Java source files for the provided Metaschemas.
075   *
076   * @param modules
077   *          the collection of Metaschema modules to generate sources for
078   * @throws MojoExecutionException
079   *           if an error occurred while generating sources
080   */
081  protected void generate(@NonNull Set<IModule> modules) throws MojoExecutionException {
082    DefaultBindingConfiguration bindingConfiguration = new DefaultBindingConfiguration();
083    for (File config : getConfigs()) {
084      try {
085        if (getLog().isInfoEnabled()) {
086          getLog().info("Loading binding configuration: " + config.getPath());
087        }
088        bindingConfiguration.load(config);
089      } catch (IOException ex) {
090        throw new MojoExecutionException(
091            String.format("Unable to load binding configuration from '%s'.", config.getPath()), ex);
092      }
093    }
094
095    try {
096      if (getLog().isInfoEnabled()) {
097        getLog().info("Generating Java classes in: " + getOutputDirectory().getPath());
098      }
099      JavaGenerator.generate(modules, ObjectUtils.notNull(getOutputDirectory().toPath()),
100          bindingConfiguration);
101    } catch (IOException ex) {
102      throw new MojoExecutionException("Creation of Java classes failed.", ex);
103    }
104  }
105
106  @SuppressWarnings("PMD.AvoidCatchingGenericException")
107  @Override
108  public void execute() throws MojoExecutionException {
109    File staleFile = getStaleFile();
110    try {
111      staleFile = ObjectUtils.notNull(staleFile.getCanonicalFile());
112    } catch (IOException ex) {
113      if (getLog().isWarnEnabled()) {
114        getLog().warn("Unable to resolve canonical path to stale file. Treating it as not existing.", ex);
115      }
116    }
117
118    boolean generate;
119    if (shouldExecutionBeSkipped()) {
120      if (getLog().isDebugEnabled()) {
121        getLog().debug(String.format("Source file generation is configured to be skipped. Skipping."));
122      }
123      generate = false;
124    } else if (staleFile.exists()) {
125      generate = isGenerationRequired();
126    } else {
127      if (getLog().isInfoEnabled()) {
128        getLog().info(String.format("Stale file '%s' doesn't exist! Generating source files.", staleFile.getPath()));
129      }
130      generate = true;
131    }
132
133    if (generate) {
134      performGeneration();
135      createStaleFile(staleFile);
136
137      // for m2e
138      getBuildContext().refresh(getOutputDirectory());
139    }
140
141    // add generated sources to Maven
142    try {
143      getMavenProject().addCompileSourceRoot(getOutputDirectory().getCanonicalFile().getPath());
144    } catch (IOException ex) {
145      throw new MojoExecutionException("Unable to add output directory to maven sources.", ex);
146    }
147  }
148
149  @SuppressWarnings("PMD.AvoidCatchingGenericException")
150  private void performGeneration() throws MojoExecutionException {
151    File outputDir = getOutputDirectory();
152    if (getLog().isDebugEnabled()) {
153      getLog().debug(String.format("Using outputDirectory: %s", outputDir.getPath()));
154    }
155
156    if (!outputDir.exists() && !outputDir.mkdirs()) {
157      throw new MojoExecutionException("Unable to create output directory: " + outputDir);
158    }
159
160    IBindingContext bindingContext;
161    try {
162      bindingContext = newBindingContext();
163    } catch (MetaschemaException | IOException ex) {
164      throw new MojoExecutionException("Failed to create the binding context", ex);
165    }
166
167    // generate Java sources based on provided metaschema sources
168    Set<IModule> modules;
169    try {
170      modules = getModulesToGenerateFor(bindingContext);
171    } catch (Exception ex) {
172      throw new MojoExecutionException("Loading of metaschema modules failed", ex);
173    }
174
175    generate(modules);
176  }
177}