content: /**
   * Copyright wro4j@2011
   */
  package ro.isdc.wro.maven.plugin;
  
  import java.io.File;
  import java.io.FileOutputStream;
  import java.io.IOException;
  
  import org.apache.commons.io.FileUtils;
  
  import ro.isdc.wro.WroRuntimeException;
  import ro.isdc.wro.extensions.support.lint.LintReport;
  import ro.isdc.wro.extensions.support.lint.ReportXmlFormatter;
  import ro.isdc.wro.extensions.support.lint.ReportXmlFormatter.FormatterType;
  import ro.isdc.wro.extensions.support.lint.ResourceLintReport;
  import ro.isdc.wro.maven.plugin.support.ProgressIndicator;
  import ro.isdc.wro.model.resource.Resource;
  
  
  /**
   * Contains common behavior for mojos responsible for static code analysis, example: csslint, jslint, jshint.
   * <p/>
   * Type <T> indicates the type of lint errors reported by this mojo.
   *
   * @author Alex Objelean
   * @created 25 Jan 2013
   * @since 1.6.3
   */
  public abstract class AbstractLinterMojo<T>
      extends AbstractSingleProcessorMojo {
    /**
     * When true, all the plugin won't stop its execution and will log all found errors.
     *
     * @parameter default-value="false" property="failNever"
     * @optional
     */
    private boolean failNever;
    /**
     * Allows build to fail when the first error is encountered without searching for next errors. This flag is true by
     * default. When interested in all errors, set this flag to false.
     *
     * @parameter default-value="true" property="failFast"
     * @optional
     */
    private boolean failFast = true;
    /**
     * Counts maximum acceptable number of jshint errors, useful for progressive code quality enhancement strategy.
     *
     * @parameter property="failThreshold"
     * @optional
     */
    private int failThreshold = 0;
    /**
     * Contains errors found during jslint processing which will be reported eventually.
     */
    private LintReport<T> lintReport;
    private ProgressIndicator progressIndicator;
  
    /**
     * Add a single report to the registry of found errors.
     *
     * @param report
     *          to add.
     */
    protected final void addReport(final ResourceLintReport<T> report) {
      lintReport.addReport(report);
    }
  
    /**
     * {@inheritDoc}
     */
    @Override
    protected void onBeforeExecute() {
      progressIndicator = new ProgressIndicator(getLog());
      getLog().info("failNever: " + failNever);
      progressIndicator.reset();
  
      // validate report format before actual plugin execution (fail fast).
      validateReportFormat();
      lintReport = new LintReport<T>();
      FileUtils.deleteQuietly(getReportFile());
    }
  
    /**
     * {@inheritDoc}
     */
    @Override
    protected void onAfterExecute() {
      super.onAfterExecute();
      progressIndicator.logSummary();
      generateReport();
      checkFailStatus();
    }
  
    private void generateReport() {
      if (shouldGenerateReport()) {
        try {
          getReportFile().getParentFile().mkdirs();
          getReportFile().createNewFile();
          getLog().debug("creating report at location: " + getReportFile());
          final FormatterType type = FormatterType.getByFormat(getReportFormat());
          if (type != null) {
            createXmlFormatter(lintReport, type).write(new FileOutputStream(getReportFile()));
          }
        } catch (final IOException e) {
          getLog().error("Could not create report file: " + getReportFile(), e);
        }
      }
    }
  
    /**
     * Check whether the build should fail.
     */
    private void checkFailStatus() {
      if (!failFast && isStatusFailed()) {
        throw new WroRuntimeException("Build status: failed.");
      }
    }
  
    /**
     * @return an instance of {@link ReportXmlFormatter} responsible for generating lint report.
     */
    protected abstract ReportXmlFormatter createXmlFormatter(LintReport<T> lintReport, FormatterType type);
  
    /**
     * A method which should be invoked on each new resource processing, having as a side effect an increment of the
     * counter holding the number of total processed resources.
     */
    protected final void onProcessingResource(final Resource resource) {
      progressIndicator.onProcessingResource(resource);
    }
  
    /**
     * @return true if the build can fail due to found errors based on existing configuration.
     */
    protected final boolean isFailAllowed() {
      return failFast && isStatusFailed();
    }
  
    /**
     * @return the file where the report should be written.
     */
    protected abstract File getReportFile();
  
    /**
     * @return the preferred format of the report.
     */
    protected abstract String getReportFormat();
  
    protected final ProgressIndicator getProgressIndicator() {
      return progressIndicator;
    }
  
    /**
     * @return true if the build status is failed.
     */
    private boolean isStatusFailed() {
      final int foundErrors = progressIndicator.getTotalFoundErrors();
      return !failNever && foundErrors > 0 && (foundErrors >= failThreshold);
    }
  
    private void validateReportFormat() {
      if (FormatterType.getByFormat(getReportFormat()) == null) {
        throw new WroRuntimeException("Usupported report format: " + getReportFormat() + ". Valid formats are: "
            + FormatterType.getSupportedFormatsAsCSV());
      }
    }
  
    private boolean shouldGenerateReport() {
      return getReportFile() != null;
    }
  
    /**
     * Used by unit test to check if mojo doesn't fail.
     * @VisibleForTesting
     */
    void onException(final Exception e) {
    }
  
    /**
     * @param failNever the failFast to set.
     */
    @Override
    public void setFailNever(final boolean failNever) {
      this.failNever = failNever;
    }
  
  
    public void setFailThreshold(final int failThreshold) {
      this.failThreshold = failThreshold;
    }
  
    /**
     * @return the failNever flag.
     * @VisibleForTesting
     */
    @Override
    public boolean isFailNever() {
      return failNever;
    }
  
    /**
     * @return the failFast flag.
     * @VisibleForTesting
     */
    boolean isFailFast() {
      return failFast;
    }
  
    /**
     * @VisibleForTesting
     */
    LintReport<T> getLintReport() {
      return lintReport;
    }
  
    /**
     * @param failFast flag to set.
     */
    public void setFailFast(final boolean failFast) {
      this.failFast = failFast;
    }
  }
  