Package org.jacoco.ant

Source Code of org.jacoco.ant.ReportTask$CheckFormatterElement

/*******************************************************************************
* Copyright (c) 2009, 2014 Mountainminds GmbH & Co. KG and Contributors
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*    Marc R. Hoffmann - initial API and implementation
*   
*******************************************************************************/
package org.jacoco.ant;

import static java.lang.String.format;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Resource;
import org.apache.tools.ant.types.resources.FileResource;
import org.apache.tools.ant.types.resources.Union;
import org.apache.tools.ant.util.FileUtils;
import org.jacoco.core.analysis.Analyzer;
import org.jacoco.core.analysis.CoverageBuilder;
import org.jacoco.core.analysis.IBundleCoverage;
import org.jacoco.core.analysis.IClassCoverage;
import org.jacoco.core.analysis.ICoverageNode;
import org.jacoco.core.data.ExecutionDataStore;
import org.jacoco.core.data.SessionInfoStore;
import org.jacoco.core.tools.ExecFileLoader;
import org.jacoco.report.FileMultiReportOutput;
import org.jacoco.report.IMultiReportOutput;
import org.jacoco.report.IReportGroupVisitor;
import org.jacoco.report.IReportVisitor;
import org.jacoco.report.MultiReportVisitor;
import org.jacoco.report.ZipMultiReportOutput;
import org.jacoco.report.check.IViolationsOutput;
import org.jacoco.report.check.Limit;
import org.jacoco.report.check.Rule;
import org.jacoco.report.check.RulesChecker;
import org.jacoco.report.csv.CSVFormatter;
import org.jacoco.report.html.HTMLFormatter;
import org.jacoco.report.xml.XMLFormatter;

/**
* Task for coverage report generation.
*/
public class ReportTask extends Task {

  /**
   * The source files are specified in a resource collection with additional
   * attributes.
   */
  public static class SourceFilesElement extends Union {

    String encoding = null;

    int tabWidth = 4;

    /**
     * Defines the optional source file encoding. If not set the platform
     * default is used.
     *
     * @param encoding
     *            source file encoding
     */
    public void setEncoding(final String encoding) {
      this.encoding = encoding;
    }

    /**
     * Sets the tab stop width for the source pages. Default value is 4.
     *
     * @param tabWidth
     *            number of characters per tab stop
     */
    public void setTabwidth(final int tabWidth) {
      if (tabWidth <= 0) {
        throw new BuildException("Tab width must be greater than 0");
      }
      this.tabWidth = tabWidth;
    }

  }

  /**
   * Container element for class file groups.
   */
  public static class GroupElement {

    private final List<GroupElement> children = new ArrayList<GroupElement>();

    private final Union classfiles = new Union();

    private final SourceFilesElement sourcefiles = new SourceFilesElement();

    private String name;

    /**
     * Sets the name of the group.
     *
     * @param name
     *            name of the group
     */
    public void setName(final String name) {
      this.name = name;
    }

    /**
     * Creates a new child group.
     *
     * @return new child group
     */
    public GroupElement createGroup() {
      final GroupElement group = new GroupElement();
      children.add(group);
      return group;
    }

    /**
     * Returns the nested resource collection for class files.
     *
     * @return resource collection for class files
     */
    public Union createClassfiles() {
      return classfiles;
    }

    /**
     * Returns the nested resource collection for source files.
     *
     * @return resource collection for source files
     */
    public SourceFilesElement createSourcefiles() {
      return sourcefiles;
    }

  }

  /**
   * Interface for child elements that define formatters.
   */
  private abstract class FormatterElement {

    abstract IReportVisitor createVisitor() throws IOException;

    void finish() {
    }
  }

  /**
   * Formatter element for HTML reports.
   */
  public class HTMLFormatterElement extends FormatterElement {

    private File destdir;

    private File destfile;

    private String footer = "";

    private String encoding = "UTF-8";

    private Locale locale = Locale.getDefault();

    /**
     * Sets the output directory for the report.
     *
     * @param destdir
     *            output directory
     */
    public void setDestdir(final File destdir) {
      this.destdir = destdir;
    }

    /**
     * Sets the Zip output file for the report.
     *
     * @param destfile
     *            Zip output file
     */
    public void setDestfile(final File destfile) {
      this.destfile = destfile;
    }

    /**
     * Sets an optional footer text that will be displayed on every report
     * page.
     *
     * @param text
     *            footer text
     */
    public void setFooter(final String text) {
      this.footer = text;
    }

    /**
     * Sets the output encoding for generated HTML files. Default is UTF-8.
     *
     * @param encoding
     *            output encoding
     */
    public void setEncoding(final String encoding) {
      this.encoding = encoding;
    }

    /**
     * Sets the locale for generated text output. By default the platform
     * locale is used.
     *
     * @param locale
     *            text locale
     */
    public void setLocale(final Locale locale) {
      this.locale = locale;
    }

    @Override
    public IReportVisitor createVisitor() throws IOException {
      final IMultiReportOutput output;
      if (destfile != null) {
        if (destdir != null) {
          throw new BuildException(
              "Either destination directory or file must be supplied, not both",
              getLocation());
        }
        final FileOutputStream stream = new FileOutputStream(destfile);
        output = new ZipMultiReportOutput(stream);

      } else {
        if (destdir == null) {
          throw new BuildException(
              "Destination directory or file must be supplied for html report",
              getLocation());
        }
        output = new FileMultiReportOutput(destdir);
      }
      final HTMLFormatter formatter = new HTMLFormatter();
      formatter.setFooterText(footer);
      formatter.setOutputEncoding(encoding);
      formatter.setLocale(locale);
      return formatter.createVisitor(output);
    }

  }

  /**
   * Formatter element for CSV reports.
   */
  public class CSVFormatterElement extends FormatterElement {

    private File destfile;

    private String encoding = "UTF-8";

    /**
     * Sets the output file for the report.
     *
     * @param destfile
     *            output file
     */
    public void setDestfile(final File destfile) {
      this.destfile = destfile;
    }

    @Override
    public IReportVisitor createVisitor() throws IOException {
      if (destfile == null) {
        throw new BuildException(
            "Destination file must be supplied for csv report",
            getLocation());
      }
      final CSVFormatter formatter = new CSVFormatter();
      formatter.setOutputEncoding(encoding);
      return formatter.createVisitor(new FileOutputStream(destfile));
    }

    /**
     * Sets the output encoding for generated XML file. Default is UTF-8.
     *
     * @param encoding
     *            output encoding
     */
    public void setEncoding(final String encoding) {
      this.encoding = encoding;
    }

  }

  /**
   * Formatter element for XML reports.
   */
  public class XMLFormatterElement extends FormatterElement {

    private File destfile;

    private String encoding = "UTF-8";

    /**
     * Sets the output file for the report.
     *
     * @param destfile
     *            output file
     */
    public void setDestfile(final File destfile) {
      this.destfile = destfile;
    }

    /**
     * Sets the output encoding for generated XML file. Default is UTF-8.
     *
     * @param encoding
     *            output encoding
     */
    public void setEncoding(final String encoding) {
      this.encoding = encoding;
    }

    @Override
    public IReportVisitor createVisitor() throws IOException {
      if (destfile == null) {
        throw new BuildException(
            "Destination file must be supplied for xml report",
            getLocation());
      }
      final XMLFormatter formatter = new XMLFormatter();
      formatter.setOutputEncoding(encoding);
      return formatter.createVisitor(new FileOutputStream(destfile));
    }

  }

  /**
   * Formatter element for coverage checks.
   */
  public class CheckFormatterElement extends FormatterElement implements
      IViolationsOutput {

    private final List<Rule> rules = new ArrayList<Rule>();
    private boolean violations = false;
    private boolean failOnViolation = true;
    private String violationsPropery = null;

    /**
     * Creates and adds a new rule.
     *
     * @return new rule
     */
    public Rule createRule() {
      final Rule rule = new Rule();
      rules.add(rule);
      return rule;
    }

    /**
     * Sets whether the build should fail in case of a violation. Default is
     * <code>true</code>.
     *
     * @param flag
     *            if <code>true</code> the build fails on violation
     */
    public void setFailOnViolation(final boolean flag) {
      this.failOnViolation = flag;
    }

    /**
     * Sets the name of a property to append the violation messages to.
     *
     * @param property
     *            name of a property
     */
    public void setViolationsProperty(final String property) {
      this.violationsPropery = property;
    }

    @Override
    public IReportVisitor createVisitor() throws IOException {
      final RulesChecker formatter = new RulesChecker();
      formatter.setRules(rules);
      return formatter.createVisitor(this);
    }

    public void onViolation(final ICoverageNode node, final Rule rule,
        final Limit limit, final String message) {
      log(message, Project.MSG_ERR);
      violations = true;
      if (violationsPropery != null) {
        final String old = getProject().getProperty(violationsPropery);
        final String value = old == null ? message : String.format(
            "%s\n%s", old, message);
        getProject().setProperty(violationsPropery, value);
      }
    }

    @Override
    void finish() {
      if (violations && failOnViolation) {
        throw new BuildException(
            "Coverage check failed due to violated rules.",
            getLocation());
      }
    }
  }

  private final Union executiondataElement = new Union();

  private SessionInfoStore sessionInfoStore;

  private ExecutionDataStore executionDataStore;

  private final GroupElement structure = new GroupElement();

  private final List<FormatterElement> formatters = new ArrayList<FormatterElement>();

  /**
   * Returns the nested resource collection for execution data files.
   *
   * @return resource collection for execution files
   */
  public Union createExecutiondata() {
    return executiondataElement;
  }

  /**
   * Returns the root group element that defines the report structure.
   *
   * @return root group element
   */
  public GroupElement createStructure() {
    return structure;
  }

  /**
   * Creates a new HTML report element.
   *
   * @return HTML report element
   */
  public HTMLFormatterElement createHtml() {
    final HTMLFormatterElement element = new HTMLFormatterElement();
    formatters.add(element);
    return element;
  }

  /**
   * Creates a new CSV report element.
   *
   * @return CSV report element
   */
  public CSVFormatterElement createCsv() {
    final CSVFormatterElement element = new CSVFormatterElement();
    formatters.add(element);
    return element;
  }

  /**
   * Creates a new coverage check element.
   *
   * @return coverage check element
   */
  public CheckFormatterElement createCheck() {
    final CheckFormatterElement element = new CheckFormatterElement();
    formatters.add(element);
    return element;
  }

  /**
   * Creates a new XML report element.
   *
   * @return CSV report element
   */
  public XMLFormatterElement createXml() {
    final XMLFormatterElement element = new XMLFormatterElement();
    formatters.add(element);
    return element;
  }

  @Override
  public void execute() throws BuildException {
    loadExecutionData();
    try {
      final IReportVisitor visitor = createVisitor();
      visitor.visitInfo(sessionInfoStore.getInfos(),
          executionDataStore.getContents());
      createReport(visitor, structure);
      visitor.visitEnd();
      for (final FormatterElement f : formatters) {
        f.finish();
      }
    } catch (final IOException e) {
      throw new BuildException("Error while creating report", e,
          getLocation());
    }
  }

  private void loadExecutionData() {
    final ExecFileLoader loader = new ExecFileLoader();
    for (final Iterator<?> i = executiondataElement.iterator(); i.hasNext();) {
      final Resource resource = (Resource) i.next();
      log(format("Loading execution data file %s", resource));
      InputStream in = null;
      try {
        in = resource.getInputStream();
        loader.load(in);
      } catch (final IOException e) {
        throw new BuildException(format(
            "Unable to read execution data file %s", resource), e,
            getLocation());
      } finally {
        FileUtils.close(in);
      }
    }
    sessionInfoStore = loader.getSessionInfoStore();
    executionDataStore = loader.getExecutionDataStore();
  }

  private IReportVisitor createVisitor() throws IOException {
    final List<IReportVisitor> visitors = new ArrayList<IReportVisitor>();
    for (final FormatterElement f : formatters) {
      visitors.add(f.createVisitor());
    }
    return new MultiReportVisitor(visitors);
  }

  private void createReport(final IReportGroupVisitor visitor,
      final GroupElement group) throws IOException {
    if (group.name == null) {
      throw new BuildException("Group name must be supplied",
          getLocation());
    }
    if (group.children.size() > 0) {
      final IReportGroupVisitor groupVisitor = visitor
          .visitGroup(group.name);
      for (final GroupElement child : group.children) {
        createReport(groupVisitor, child);
      }
    } else {
      final IBundleCoverage bundle = createBundle(group);
      final SourceFilesElement sourcefiles = group.sourcefiles;
      final AntResourcesLocator locator = new AntResourcesLocator(
          sourcefiles.encoding, sourcefiles.tabWidth);
      locator.addAll(sourcefiles.iterator());
      if (!locator.isEmpty()) {
        checkForMissingDebugInformation(bundle);
      }
      visitor.visitBundle(bundle, locator);
    }
  }

  private IBundleCoverage createBundle(final GroupElement group)
      throws IOException {
    final CoverageBuilder builder = new CoverageBuilder();
    final Analyzer analyzer = new Analyzer(executionDataStore, builder);
    for (final Iterator<?> i = group.classfiles.iterator(); i.hasNext();) {
      final Resource resource = (Resource) i.next();
      if (resource.isDirectory() && resource instanceof FileResource) {
        analyzer.analyzeAll(((FileResource) resource).getFile());
      } else {
        final InputStream in = resource.getInputStream();
        analyzer.analyzeAll(in, resource.getName());
        in.close();
      }
    }
    final IBundleCoverage bundle = builder.getBundle(group.name);
    logBundleInfo(bundle, builder.getNoMatchClasses());
    return bundle;
  }

  private void logBundleInfo(final IBundleCoverage bundle,
      final Collection<IClassCoverage> nomatch) {
    log(format("Writing bundle '%s' with %s classes", bundle.getName(),
        Integer.valueOf(bundle.getClassCounter().getTotalCount())));
    if (!nomatch.isEmpty()) {
      log(format(
          "Classes in bundle '%s' do no match with execution data. "
              + "For report generation the same class files must be used as at runtime.",
          bundle.getName()), Project.MSG_WARN);
      for (final IClassCoverage c : nomatch) {
        log(format("Execution data for class %s does not match.",
            c.getName()), Project.MSG_WARN);
      }
    }
  }

  private void checkForMissingDebugInformation(final ICoverageNode node) {
    if (node.getClassCounter().getTotalCount() > 0
        && node.getLineCounter().getTotalCount() == 0) {
      log(format(
          "To enable source code annotation class files for bundle '%s' have to be compiled with debug information.",
          node.getName()), Project.MSG_WARN);
    }
  }

}
TOP

Related Classes of org.jacoco.ant.ReportTask$CheckFormatterElement

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.