All Downloads are FREE. Search and download functionalities are using the official Maven repository.

hudson.plugins.findbugs.parser.FindBugsParser Maven / Gradle / Ivy

Go to download

This plug-in generates the trend report for FindBugs, an open source program which uses static analysis to look for bugs in Java code.

The newest version!
package hudson.plugins.findbugs.parser; // NOPMD

import hudson.plugins.analysis.core.AnnotationParser;
import hudson.plugins.analysis.util.model.FileAnnotation;
import hudson.plugins.analysis.util.model.LineRange;
import hudson.plugins.analysis.util.model.Priority;
import hudson.plugins.findbugs.FindBugsMessages;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.digester.Digester;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.xerces.parsers.SAXParser;
import org.dom4j.DocumentException;
import org.jvnet.localizer.LocaleProvider;
import org.xml.sax.SAXException;

import edu.umd.cs.findbugs.BugAnnotation;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.Project;
import edu.umd.cs.findbugs.SortedBugCollection;
import edu.umd.cs.findbugs.SourceLineAnnotation;
import edu.umd.cs.findbugs.ba.SourceFile;
import edu.umd.cs.findbugs.ba.SourceFinder;
import edu.umd.cs.findbugs.cloud.Cloud;

/**
 * A parser for the native FindBugs XML files (ant task, batch file or
 * maven-findbugs-plugin >= 1.2).
 *
 * @author Ulli Hafner
 */
// CHECKSTYLE:COUPLING-OFF
public class FindBugsParser implements AnnotationParser {
    /** Unique ID of this class. */
    private static final long serialVersionUID = 8306319007761954027L;

    /** Property of SAX parser factory. */
    static final String SAX_DRIVER_PROPERTY = "org.xml.sax.driver";

    private static final String DOT = ".";
    private static final String SLASH = "/";

    private static final int DAY_IN_MSEC = 1000 * 60 * 60 * 24;
    private static final int HIGH_PRIORITY_LOWEST_RANK = 4;
    private static final int NORMAL_PRIORITY_LOWEST_RANK = 9;

    /** Collection of source folders. */
    @edu.umd.cs.findbugs.annotations.SuppressWarnings("SE")
    private final List mavenSources = new ArrayList();

    /**
     * Creates a new instance of {@link FindBugsParser}.
     */
    public FindBugsParser() {
        this(new ArrayList());
    }

    /**
     * Creates a new instance of {@link FindBugsParser}.
     *
     * @param sourceFolders
     *            a collection of folders to scan for source files. If empty,
     *            the source folders are guessed.
     */
    public FindBugsParser(final Collection sourceFolders) {
        mavenSources.addAll(sourceFolders);
    }

    /** {@inheritDoc} */
    public Collection parse(final File file, final String moduleName) throws InvocationTargetException {
        try {
            Collection sources = new ArrayList(mavenSources);
            if (sources.isEmpty()) {
                String moduleRoot = StringUtils.substringBefore(file.getAbsolutePath().replace('\\', '/'), "/target/");
                sources.add(moduleRoot + "/src/main/java");
                sources.add(moduleRoot + "/src/test/java");
                sources.add(moduleRoot + "/src");
            }
            return parse(file, sources, moduleName);
        }
        catch (IOException exception) {
            throw new InvocationTargetException(exception);
        }
        catch (SAXException exception) {
            throw new InvocationTargetException(exception);
        }
        catch (DocumentException exception) {
            throw new InvocationTargetException(exception);
        }
    }

    /**
     * Returns the parsed FindBugs analysis file. This scanner accepts files in
     * the native FindBugs format.
     *
     * @param file
     *            the FindBugs analysis file
     * @param sources
     *            a collection of folders to scan for source files
     * @param moduleName
     *            name of maven module
     * @return the parsed result (stored in the module instance)
     * @throws IOException
     *             if the file could not be parsed
     * @throws DocumentException
     *             if the file could not be read
     * @throws SAXException
     *             if the file could not be read
     */
    public Collection parse(final File file, final Collection sources, final String moduleName)
            throws IOException, DocumentException, SAXException {
        FileInputStream input = null;
        try {
            input = new FileInputStream(file);
            Map hashToMessageMapping = createHashToMessageMapping(input);
            IOUtils.closeQuietly(input);

            input = new FileInputStream(file);
            return parse(input, sources, moduleName, hashToMessageMapping);
        }
        finally {
            IOUtils.closeQuietly(input);
        }
    }

    /**
     * Creates a mapping of FindBugs warnings to messages. A bug is represented
     * by its unique hash code.
     *
     * @param file
     *            the FindBugs XML file
     * @return the map of warning messages
     * @throws SAXException
     *             if the file contains no valid XML
     * @throws IOException
     *             signals that an I/O exception has occurred.
     */
    public Map createHashToMessageMapping(final InputStream file) throws SAXException, IOException {
        Digester digester = new Digester();
        digester.setValidating(false);
        digester.setClassLoader(FindBugsParser.class.getClassLoader());

        String rootXPath = "BugCollection/BugInstance";
        digester.addObjectCreate(rootXPath, XmlBugInstance.class);
        digester.addSetProperties(rootXPath);

        String fileXPath = rootXPath + "/LongMessage";
        digester.addCallMethod(fileXPath, "setMessage", 0);

        digester.addSetNext(rootXPath, "add", Object.class.getName());
        ArrayList bugs = new ArrayList();
        digester.push(bugs);
        digester.parse(file);

        HashMap mapping = new HashMap();
        for (XmlBugInstance bug : bugs) {
            mapping.put(bug.getInstanceHash(), bug.getMessage());
        }
        return mapping;
    }

    /**
     * Returns the parsed FindBugs analysis file. This scanner accepts files in
     * the native FindBugs format.
     *
     * @param file
     *            the FindBugs analysis file
     * @param sources
     *            a collection of folders to scan for source files
     * @param moduleName
     *            name of maven module
     * @param hashToMessageMapping
     *            mapping of hash codes to messages
     * @return the parsed result (stored in the module instance)
     * @throws IOException
     *             if the file could not be parsed
     * @throws DocumentException in case of a parser exception
     */
    public Collection parse(final InputStream file, final Collection sources,
            final String moduleName, final Map hashToMessageMapping) throws IOException,
            DocumentException {
        SortedBugCollection collection = readXml(file);

        Project project = collection.getProject();
        for (String sourceFolder : sources) {
            project.addSourceDir(sourceFolder);
        }

        SourceFinder sourceFinder = new SourceFinder(project);
        String actualName = extractModuleName(moduleName, project);

        ArrayList annotations = new ArrayList();
        Collection bugs = collection.getCollection();
        for (BugInstance warning : bugs) {
            SourceLineAnnotation sourceLine = warning.getPrimarySourceLineAnnotation();

            String message = warning.getMessage();
            if (message.contains("TEST: Unknown")) {
                message = FindBugsMessages.getInstance().getShortMessage(warning.getType(), LocaleProvider.getLocale());
            }
            Bug bug = new Bug(getPriority(warning),
                    StringUtils.defaultIfEmpty(hashToMessageMapping.get(warning.getInstanceHash()), message),
                    warning.getBugPattern().getCategory(),
                    warning.getType(), sourceLine.getStartLine(), sourceLine.getEndLine());
            bug.setInstanceHash(warning.getInstanceHash());

            boolean ignore = setCloudInformation(collection, warning, bug);
            if (!ignore) {
                bug.setNotAProblem(ignore);
                bug.setFileName(findSourceFile(project, sourceFinder, sourceLine));
                bug.setPackageName(warning.getPrimaryClass().getPackageName());
                bug.setModuleName(actualName);
                setAffectedLines(warning, bug);

                annotations.add(bug);
            }
        }
        return annotations;
    }

    private SortedBugCollection readXml(final InputStream file) throws IOException, DocumentException {
        String oldProperty = System.getProperty(SAX_DRIVER_PROPERTY);
        if (oldProperty != null) {
            System.setProperty(SAX_DRIVER_PROPERTY, SAXParser.class.getName());
        }
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(FindBugsParser.class.getClassLoader());
        SortedBugCollection collection = new SortedBugCollection();
        collection.readXML(file);
        Thread.currentThread().setContextClassLoader(contextClassLoader);
        if (oldProperty != null) {
            System.setProperty(SAX_DRIVER_PROPERTY, oldProperty);
        }
        return collection;
    }

    /**
     * Sets the cloud information.
     *
     * @param collection
     *            the warnings collection
     * @param warning
     *            the warning
     * @param bug
     *            the bug
     * @return true, if this warning is not a bug and should be ignored
     */
    private boolean setCloudInformation(final SortedBugCollection collection, final BugInstance warning, final Bug bug) {
        Cloud cloud = collection.getCloud();
        bug.setShouldBeInCloud(cloud.isOnlineCloud());
        bug.setDetailsUrlTemplate(cloud.getBugDetailsUrlTemplate());
        long firstSeen = cloud.getFirstSeen(warning);
        bug.setInCloud(cloud.isInCloud(warning));
        bug.setFirstSeen(firstSeen);
        int ageInDays = (int) ((collection.getAnalysisTimestamp() - firstSeen) / DAY_IN_MSEC);
        bug.setAgeInDays(ageInDays);
        bug.setReviewCount(cloud.getNumberReviewers(warning));

        return cloud.overallClassificationIsNotAProblem(warning);
    }

    private void setAffectedLines(final BugInstance warning, final Bug bug) {
        Iterator annotationIterator = warning.annotationIterator();
        while (annotationIterator.hasNext()) {
            BugAnnotation bugAnnotation = annotationIterator.next();
            if (bugAnnotation instanceof SourceLineAnnotation) {
                SourceLineAnnotation annotation = (SourceLineAnnotation)bugAnnotation;
                bug.addLineRange(new LineRange(annotation.getStartLine(), annotation.getEndLine()));
            }
        }
    }

    private String findSourceFile(final Project project, final SourceFinder sourceFinder, final SourceLineAnnotation sourceLine) {
        try {
            SourceFile sourceFile = sourceFinder.findSourceFile(sourceLine);
            return sourceFile.getFullFileName();
        }
        catch (IOException exception) {
            Logger.getLogger(getClass().getName()).log(Level.WARNING,
                    "Can't resolve absolute file name for file " + sourceLine.getSourceFile()
                    + ", dir list = " + project.getSourceDirList().toString());
            return sourceLine.getPackageName().replace(DOT, SLASH) + SLASH + sourceLine.getSourceFile();
        }
    }

    /**
     * Maps the FindBugs library rank to plug-in priority enumeration.
     *
     * @param warning
     *            the FindBugs warning
     * @return mapped priority enumeration
     */
    private Priority getPriority(final BugInstance warning) {
        int rank = warning.getBugRank();
        if (rank <= HIGH_PRIORITY_LOWEST_RANK) {
            return Priority.HIGH;
        }
        if (rank <= NORMAL_PRIORITY_LOWEST_RANK) {
            return Priority.NORMAL;
        }
        return Priority.LOW;
    }

    /**
     * Extracts the module name from the specified project. If empty then the
     * provided default name is used.
     *
     * @param defaultName
     *            the default module name to use
     * @param project
     *            the maven 2 project
     * @return the module name to use
     */
    private String extractModuleName(final String defaultName, final Project project) {
        if (StringUtils.isBlank(project.getProjectName())) {
            return defaultName;
        }
        else {
            return project.getProjectName();
        }
    }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy