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; }