package com.buschmais.jqassistant.mojo;
import com.buschmais.jqassistant.core.analysis.api.*;
import com.buschmais.jqassistant.core.analysis.api.rule.AbstractExecutable;
import com.buschmais.jqassistant.core.analysis.api.rule.Concept;
import com.buschmais.jqassistant.core.analysis.api.rule.Constraint;
import com.buschmais.jqassistant.core.analysis.api.rule.RuleSet;
import com.buschmais.jqassistant.core.analysis.impl.AnalyzerImpl;
import com.buschmais.jqassistant.core.report.impl.CompositeReportWriter;
import com.buschmais.jqassistant.core.report.impl.InMemoryReportWriter;
import com.buschmais.jqassistant.core.report.impl.XmlReportWriter;
import com.buschmais.jqassistant.core.store.api.Store;
import com.buschmais.jqassistant.core.store.api.descriptor.FullQualifiedNameDescriptor;
import org.apache.commons.io.IOUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Runs analysis according to the defined rules.
*/
@Mojo(name = "analyze", defaultPhase = LifecyclePhase.VERIFY)
public class AnalyzeMojo extends AbstractAnalysisAggregatorMojo {
/**
* Indicates if the plugin shall fail if a constraint violation is detected.
*/
@Parameter(property = "jqassistant.failOnConstraintViolations", defaultValue = "true")
protected boolean failOnConstraintViolations;
@Override
public void aggregate(MavenProject baseProject, Set<MavenProject> projects, Store store) throws MojoExecutionException, MojoFailureException {
getLog().info("Executing analysis for '" + baseProject.getName() + "'.");
final RuleSet ruleSet = resolveEffectiveRules(baseProject);
InMemoryReportWriter inMemoryReportWriter = new InMemoryReportWriter();
FileWriter xmlReportFileWriter;
try {
xmlReportFileWriter = new FileWriter(getXmlReportFile(baseProject));
} catch (IOException e) {
throw new MojoExecutionException("Cannot create XML report file.", e);
}
XmlReportWriter xmlReportWriter;
try {
xmlReportWriter = new XmlReportWriter(xmlReportFileWriter);
} catch (ExecutionListenerException e) {
throw new MojoExecutionException("Cannot create XML report file writer.", e);
}
List<ExecutionListener> reportWriters = new LinkedList<>();
reportWriters.add(inMemoryReportWriter);
reportWriters.add(xmlReportWriter);
try {
CompositeReportWriter reportWriter = new CompositeReportWriter(reportWriters);
Analyzer analyzer = new AnalyzerImpl(store, reportWriter);
try {
analyzer.execute(ruleSet);
} catch (AnalyzerException e) {
throw new MojoExecutionException("Analysis failed.", e);
}
} finally {
IOUtils.closeQuietly(xmlReportFileWriter);
}
store.beginTransaction();
try {
verifyConceptResults(inMemoryReportWriter);
verifyConstraintViolations(inMemoryReportWriter);
} finally {
store.commitTransaction();
}
}
/**
* Verifies the concept results returned by the {@link InMemoryReportWriter}
* .
* <p>
* A warning is logged for each concept which did not return a result (i.e.
* has not been applied).
* </p>
*
* @param inMemoryReportWriter The {@link InMemoryReportWriter}.
*/
private void verifyConceptResults(InMemoryReportWriter inMemoryReportWriter) {
List<Result<Concept>> conceptResults = inMemoryReportWriter.getConceptResults();
for (Result<Concept> conceptResult : conceptResults) {
if (conceptResult.getRows().isEmpty()) {
getLog().warn("Concept '" + conceptResult.getExecutable().getId() + "' returned an empty result.");
}
}
}
/**
* Verifies the constraint violations returned by the
* {@link InMemoryReportWriter}.
*
* @param inMemoryReportWriter The {@link InMemoryReportWriter}.
* @throws MojoFailureException If constraint violations are detected.
*/
private void verifyConstraintViolations(InMemoryReportWriter inMemoryReportWriter) throws MojoFailureException {
List<Result<Constraint>> constraintViolations = inMemoryReportWriter.getConstraintViolations();
int violations = 0;
for (Result<Constraint> constraintViolation : constraintViolations) {
if (!constraintViolation.isEmpty()) {
AbstractExecutable constraint = constraintViolation.getExecutable();
getLog().error(constraint.getId() + ": " + constraint.getDescription());
for (Map<String, Object> columns : constraintViolation.getRows()) {
StringBuilder message = new StringBuilder();
for (Map.Entry<String, Object> entry : columns.entrySet()) {
if (message.length() > 0) {
message.append(", ");
}
message.append(entry.getKey());
message.append('=');
Object value = entry.getValue();
message.append(value instanceof FullQualifiedNameDescriptor ? ((FullQualifiedNameDescriptor) value).getFullQualifiedName() : value.toString());
}
getLog().error(" " + message.toString());
}
violations++;
}
}
if (failOnConstraintViolations && violations > 0) {
throw new MojoFailureException(violations + " constraints have been violated!");
}
}
/**
* Returns the {@link File} to write the XML report to.
*
* @return The {@link File} to write the XML report to.
* @throws MojoExecutionException If the file cannot be determined.
*/
private File getXmlReportFile(MavenProject baseProject) throws MojoExecutionException {
File selectedXmlReportFile = BaseProjectResolver.getReportFile(baseProject, xmlReportFile, REPORT_XML);
selectedXmlReportFile.getParentFile().mkdirs();
return selectedXmlReportFile;
}
}