package hudson.plugins.violations;
import java.io.File;
import java.io.IOException;
import java.util.logging.Logger;
import org.apache.tools.ant.types.FileSet;
import hudson.FilePath;
import hudson.FilePath.FileCallable;
import hudson.model.BuildListener;
import hudson.remoting.VirtualChannel;
import hudson.plugins.violations.util.StringUtil;
import hudson.plugins.violations.model.FullBuildModel;
import hudson.plugins.violations.model.FullFileModel;
import hudson.plugins.violations.model.Violation;
import hudson.plugins.violations.generate.GenerateXML;
/**
* Collects the violations xml files, analyses them, renders
* html reports to the working directory, and returns a summary
* Violations report.
*/
public class ViolationsCollector implements FileCallable<ViolationsReport> {
private static final Logger LOG = Logger.getLogger(
ViolationsCollector.class.getName());
private static final String[] NO_STRINGS = new String[]{};
private final boolean mavenProject;
/** Working directory to copy xml files to. */
private final FilePath targetDir;
private final FilePath htmlDir;
private final ViolationsConfig config;
private FullBuildModel model;
private File workspace;
/**
* Constructor.
* @param mavenProject true if this a maven project, false otherwise
* @param targetDir working directory to copy xml results to
* @param htmlDir working directory to html reports to.
* @param config the violations configuration.
*/
public ViolationsCollector(
boolean mavenProject, FilePath targetDir, FilePath htmlDir,
ViolationsConfig config) {
this.mavenProject = mavenProject;
this.targetDir = targetDir;
this.htmlDir = htmlDir;
this.config = config;
}
private boolean empty(String str) {
return (str == null) || str.equals("");
}
/**
* Create a report.
* @param workspace the current workspace.
* @param channel the virtual channel.
* @return the report.
* @throws IOException if there is a problem.
*/
public ViolationsReport invoke(File workspace, VirtualChannel channel)
throws IOException {
this.workspace = workspace;
// If the faux project path has been set, use that instead of
// the given workspace
if (!StringUtil.isBlank(config.getFauxProjectPath())) {
this.workspace = new File(config.getFauxProjectPath());
LOG.fine("Using faux workspace " + this.workspace);
}
String[] sourcePaths = null;
if (mavenProject) {
sourcePaths = new String[] {
workspace.toString() + "/src/main/java"
};
} else {
// get the source path directories (if any)
sourcePaths = findAbsoluteDirs(
workspace, config.getSourcePathPattern());
}
for (String sp: sourcePaths) {
LOG.fine("Using extra sourcePath " + sp);
}
// Create the report
ViolationsReport report = new ViolationsReport();
report.setConfig(config);
// Build up the model
this.model = new FullBuildModel();
for (String type: config.getTypeConfigs().keySet()) {
TypeConfig c = config.getTypeConfigs().get(type);
TypeDescriptor typeDescriptor = TypeDescriptor.TYPES.get(type);
if (typeDescriptor == null) {
continue;
}
if (mavenProject && (typeDescriptor.getMavenTargets() != null)) {
doType(c, typeDescriptor, sourcePaths, report);
continue;
}
if (empty(c.getPattern())) {
continue;
}
doType(c, typeDescriptor, sourcePaths, report);
}
model.cleanup();
// -----
// Write out the xml reports
// ----
try {
new GenerateXML(targetDir , model, config).execute();
} catch (InterruptedException ex) {
throw new IOException(ex);
}
// -----
// Create the summary report
// ----
for (String type: model.getTypeMap().keySet()) {
report.getViolations().put(
type, model.getCountNumber(type));
doSeverities(report, type);
}
return report;
}
/**
* Get all the severities of a particular type and
* update the severity array of the type summary.
*/
private void doSeverities(ViolationsReport report, String type) {
TypeSummary summary = report.getTypeSummary(type);
for (FullFileModel file: model.getFileModelMap().values()) {
if (file.getTypeMap().get(type) == null) {
continue;
}
for (Violation v: file.getTypeMap().get(type)) {
summary.getSeverityArray()[v.getSeverityLevel()]++;
}
}
}
// TODO: to many if, else, break and return
private void doType(
TypeConfig c, TypeDescriptor t, String[] sourcePaths,
ViolationsReport report)
throws IOException {
String[] fileNames = null;
if (mavenProject
&& t.getMavenTargets() != null
&& !c.isUsePattern()) {
for (String name: t.getMavenTargets()) {
fileNames = findFiles(workspace, "target/" + name);
if (fileNames.length != 0) {
break;
}
}
if (fileNames.length == 0) {
return;
}
} else {
fileNames = findFiles(workspace, c.getPattern());
if (fileNames.length == 0) {
if (!mavenProject) {
report.getViolations().put(
c.getType(), -1);
report.getTypeSummary(c.getType()).setErrorMessage(
"No violation report files of type " + c.getType()
+ " with pattern " + c.getPattern() + " were found!");
}
return;
}
}
model.addType(c.getType());
for (String fileName: fileNames) {
t.createParser().parse(model, workspace, fileName, sourcePaths);
}
}
// TO DO : PLACE THESE IN A UTILITY CLASS
/**
* Returns an array with the filenames of the files
* that match an Ant pattern using the workspace as the base
* directory.
*
* @param workspaceRoot root directory of the workspace
*
* @return the filenames found.
*/
private String[] findFiles(
final File workspaceRoot, String pattern) {
if (StringUtil.isBlank(pattern)) {
return NO_STRINGS;
}
FileSet fileSet = new FileSet();
org.apache.tools.ant.Project project
= new org.apache.tools.ant.Project();
fileSet.setProject(project);
fileSet.setDir(workspaceRoot);
fileSet.setIncludes(pattern);
return fileSet.getDirectoryScanner(project).getIncludedFiles();
}
/**
* Returns an array with the relative filenames of directories
* that match an Ant pattern using the workspace as the base
* directory.
*
* @param workspaceRoot root directory of the workspace
*
* @return the filenames found.
*/
private String[] findDirs(
final File workspaceRoot, String pattern) {
if (StringUtil.isBlank(pattern)) {
return NO_STRINGS;
}
FileSet fileSet = new FileSet();
org.apache.tools.ant.Project project
= new org.apache.tools.ant.Project();
fileSet.setProject(project);
fileSet.setDir(workspaceRoot);
fileSet.setIncludes(pattern);
return fileSet.getDirectoryScanner(project).getIncludedDirectories();
}
/**
* Returns an array with the absolute filenames of directories
* that match an Ant pattern using the workspace as the base
* directory.
*
* @param workspaceRoot root directory of the workspace
*
* @return the filenames found.
*/
private String[] findAbsoluteDirs(
final File workspaceRoot, String pattern) {
String[] relative = findDirs(workspaceRoot, pattern);
if (relative.length == 0) {
return relative;
}
String[] absolute = new String[relative.length];
for (int i = 0; i < relative.length; ++i) {
absolute[i] = new File(
workspaceRoot, relative[i]).getAbsolutePath();
}
return absolute;
}
private static final long serialVersionUID = 1L;
}