package hudson.plugins.violations; import hudson.FilePath; import hudson.FilePath.FileCallable; import hudson.plugins.violations.generate.GenerateXML; import hudson.plugins.violations.model.FullBuildModel; import hudson.plugins.violations.model.FullFileModel; import hudson.plugins.violations.model.Violation; import hudson.plugins.violations.util.StringUtil; import hudson.remoting.VirtualChannel; import hudson.util.IOException2; import java.io.File; import java.io.IOException; import java.util.logging.Logger; import org.apache.tools.ant.types.FileSet; import org.jenkinsci.remoting.RoleChecker; /** * 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 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.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. */ @Override 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 IOException2(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; // Needed to build with Jenkins 1.609, dont @Override since it will cause // errors when building for older Jenkins public void checkRoles(RoleChecker checker) throws SecurityException { } }