Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
net.rumati.maven.plugins.findbugs.FindBugsReport Maven / Gradle / Ivy
package net.rumati.maven.plugins.findbugs;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.doxia.sink.Sink;
import org.apache.maven.doxia.siterenderer.Renderer;
import org.apache.maven.project.MavenProject;
import org.apache.maven.reporting.AbstractMavenReport;
import org.apache.maven.reporting.MavenReportException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Generates the FindBugs report.
* @goal findbugs
* @execute phase="compile"
* @requiresDependencyResolution compile
* @requiresProject
*/
public class FindBugsReport
extends AbstractMavenReport
{
/**
* The output directory for the report.
* @parameter default-value="${project.reporting.outputDirectory}"
* @required
* @readonly
*/
private File outputDirectory;
/**
* The project for which to run the report.
* @parameter default-value="${project}"
* @required
* @readonly
*/
private MavenProject project;
/**
* The site renderer to use when creating content.
* @component
*/
private Renderer siteRenderer;
/**
* The location of the JXR cross referenced source. This for use with the
* Maven JXR Plugin .
* If this parameter is provided, the report will link to source at which a bug occurs.
* This should be a relative path to the report output directory, for
* example ./xref .
* @parameter
*/
private String xrefPath;
/**
* The minimum priority which a bug must have to appear on the report. Possible values are
* low , medium and high . If threshold is low ,
* bugs of priorities low , medium and high will be reported. If
* threshold is medium only bugs of priorities medium and high
* will be reported. If threshold is high only high priority bugs
* will be reported.
* @parameter default-value="medium"
*/
private String threshold;
/**
* The amount of effort to use when looking for bugs. Possible values are
* min , less , default , more and max .
* @parameter default-value="default"
*/
private String effort;
/**
* An exclude filter file, with a list of rules for bugs to exclude from the report.
* @parameter
*/
private File excludeFilterFile;
@Override
protected Renderer getSiteRenderer()
{
return siteRenderer;
}
@Override
protected String getOutputDirectory()
{
return outputDirectory.getAbsolutePath();
}
@Override
protected MavenProject getProject()
{
return project;
}
@Override
public boolean canGenerateReport()
{
return new File(project.getBuild().getOutputDirectory()).isDirectory();
}
private void writeFindBugsProjectFile(MavenProject project, File outputFile)
throws ParserConfigurationException, TransformerException, IOException
{
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Element projectElement = doc.createElement("Project");
doc.appendChild(projectElement);
projectElement.setAttribute("projectName", project.getName());
Element jar = doc.createElement("Jar");
jar.setTextContent(project.getBuild().getOutputDirectory());
projectElement.appendChild(jar);
for (Object o : project.getCompileSourceRoots()){
Element el = doc.createElement("SrcDir");
el.setTextContent(o.toString());
projectElement.appendChild(el);
}
for (Object o : project.getArtifacts()){
Artifact a = (Artifact)o;
Element el = doc.createElement("AuxClasspathEntry");
el.setTextContent(a.getFile().getAbsolutePath());
projectElement.appendChild(el);
}
Transformer trans = TransformerFactory.newInstance().newTransformer();
trans.setOutputProperty(OutputKeys.METHOD, "xml");
trans.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
trans.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
trans.setOutputProperty(OutputKeys.INDENT, "yes");
trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
OutputStream out = new FileOutputStream(outputFile);
try {
trans.transform(new DOMSource(doc), new StreamResult(out));
}finally{
out.close();
}
}
@Override
protected void executeReport(Locale locale)
throws MavenReportException
{
if (!canGenerateReport()){
return;
}
try{
File tmpProjectFile = File.createTempFile("findbugs", ".fbp");
try{
writeFindBugsProjectFile(project, tmpProjectFile);
File outputFile = File.createTempFile("findbug", ".xml");
try{
String bugPriorityLevel = null;
if (threshold.toLowerCase().equals("low")){
bugPriorityLevel = "-low";
}else if (threshold.toLowerCase().equals("medium")){
bugPriorityLevel = "-medium";
}else if (threshold.toLowerCase().equals("high")){
bugPriorityLevel = "-high";
}else{
throw new MavenReportException("Unkown threshold: " + threshold);
}
String effortArg = null;
if (effort.toLowerCase().equals("min")){
effortArg = "-effort:min";
}else if (effort.toLowerCase().equals("less")){
effortArg = "-effort:less";
}else if (effort.toLowerCase().equals("default")){
effortArg = "-effort:default";
}else if (effort.toLowerCase().equals("more")){
effortArg = "-effort:more";
}else if (effort.toLowerCase().equals("max")){
effortArg = "-effort:max";
}
List args = new LinkedList();
args.add("-textui");
args.add(effortArg);
args.add(bugPriorityLevel);
if (excludeFilterFile != null){
if (!excludeFilterFile.exists()){
throw new MavenReportException("Exclude file does not exist: " + excludeFilterFile.getAbsolutePath());
}
args.add("-exclude");
args.add(excludeFilterFile.getAbsolutePath());
}
args.add("-project");
args.add(tmpProjectFile.getAbsolutePath());
args.add("-xml:withMessages");
args.add("-output");
args.add(outputFile.getAbsolutePath());
edu.umd.cs.findbugs.LaunchAppropriateUI.main(args.toArray(new String[args.size()]));
Document doc =
DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(outputFile);
XPath xpath = XPathFactory.newInstance().newXPath();
Sink sink = getSink();
sink.head();
sink.title();
sink.text("FindBugs Report");
sink.title_();
sink.head_();
sink.body();
sink.section1();
sink.sectionTitle1();
sink.text("FindBugs Report");
sink.sectionTitle1_();
sink.paragraph();
sink.text("This is a report of possible bugs found by the ");
sink.link("http://findbugs.sourceforge.net/");
sink.text("FindBugs");
sink.link_();
sink.text(" program, which uses static analysis to find bugs in Java code. The report was generated with the following parameters:");
sink.paragraph_();
sink.text("FindBugs version: " + xpath.evaluate("/BugCollection/attribute::version", doc));
sink.lineBreak();
sink.text("Effort: " + effort.toLowerCase());
sink.lineBreak();
sink.text("Bug Priority Threshold: " + threshold.toLowerCase());
sink.section1_();
if (doSummary(sink, xpath, doc)){
/*
* Only do reports if bugs were actually found.
*/
doBugsByClassReport(sink, doc, xpath);
doBugsByCategoryReport(sink, doc, xpath);
}
sink.body_();
sink.flush();
sink.close();
}finally{
outputFile.delete();
}
}finally{
tmpProjectFile.delete();
}
}catch (Exception e){
throw new MavenReportException("Error creating report", e);
}
}
/**
* Generated the summary part of the report, returning an indication of whether or not bugs were found.
* @param sink
* @param xpath
* @param doc
* @return true
if bugs were found, or false
if no bugs were reported.
* @throws XPathExpressionException
*/
private boolean doSummary(Sink sink, XPath xpath, Document doc)
throws XPathExpressionException
{
sink.section1();
sink.sectionTitle1();
sink.text("Summary");
sink.sectionTitle1_();
try{
String totalBugs = xpath.evaluate("/BugCollection/FindBugsSummary/attribute::total_bugs", doc);
if (totalBugs.equals("0")){
sink.paragraph();
sink.text("No bugs were found!");
sink.paragraph_();
return false;
}
String p1 = xpath.evaluate("/BugCollection/FindBugsSummary/attribute::priority_1", doc);
String p2 = xpath.evaluate("/BugCollection/FindBugsSummary/attribute::priority_2", doc);
String p3 = xpath.evaluate("/BugCollection/FindBugsSummary/attribute::priority_3", doc);
boolean hasP1 = false;
if (p1 != null && p1.length() > 0 && !p1.equals("0")){
hasP1 = true;
}
boolean hasP2 = false;
if (p2 != null && p2.length() > 0 && !p2.equals("0")){
hasP2 = true;
}
boolean hasP3 = false;
if (p3 != null && p3.length() > 0 && !p3.equals("0")){
hasP3 = true;
}
sink.paragraph();
sink.bold();
sink.text(totalBugs);
sink.bold_();
sink.text(" bug");
if (Integer.parseInt(totalBugs) > 1){
sink.text("s");
}
if (hasP1){
sink.text(", consisting of ");
sink.bold();
sink.text(p1);
sink.bold_();
sink.text(" ");
sink.italic();
sink.text("High");
sink.italic_();
sink.text(" priority bug");
if (Integer.parseInt(p1) > 1){
sink.text("s");
}
}
if (hasP2){
if (!hasP1){
sink.text(", consisting of ");
}else{
if (hasP3){
sink.text(", ");
}else{
sink.text(" and ");
}
}
sink.bold();
sink.text(p2);
sink.bold_();
sink.text(" ");
sink.italic();
sink.text("Medium");
sink.italic_();
sink.text(" priority bug");
if (Integer.parseInt(p2) > 1){
sink.text("s");
}
}
if (p3 != null && p3.length() > 0 && !p3.equals("0")){
if (hasP1 || hasP2){
sink.text(" and ");
}else{
sink.text(", consisting of ");
}
sink.bold();
sink.text(p3);
sink.bold_();
sink.text(" ");
sink.italic();
sink.text("Low");
sink.italic_();
sink.text(" priority bug");
if (Integer.parseInt(p3) > 1){
sink.text("s");
}
}
sink.text(" found in ");
sink.bold();
sink.text(xpath.evaluate("/BugCollection/FindBugsSummary/attribute::total_size", doc));
sink.bold_();
sink.text(" lines of code, in ");
sink.bold();
sink.text(xpath.evaluate("/BugCollection/FindBugsSummary/attribute::total_classes", doc));
sink.bold_();
sink.text(" classes, in ");
sink.bold();
sink.text(xpath.evaluate("/BugCollection/FindBugsSummary/attribute::num_packages", doc));
sink.bold_();
sink.text(" package(s).");
sink.paragraph_();
sink.paragraph();
sink.text("Here are some entry points to the report:");
sink.paragraph_();
sink.list();
sink.listItem();
sink.link("#report.BugsByClass");
sink.text("Bugs by class");
sink.link_();
sink.listItem_();
sink.listItem();
sink.link("#report.BugsByCategory");
sink.text("Bugs by category");
sink.link_();
sink.listItem_();
sink.list_();
}finally{
sink.section1_();
}
return true;
}
public String getOutputName()
{
return "findbugs";
}
public String getName(Locale locale)
{
return "FindBugs Report";
}
public String getDescription(Locale locale)
{
return "Source code static analysis and bug report";
}
private void doBugsByClassReport(Sink sink, Document doc, XPath xpath)
throws XPathExpressionException, UnsupportedEncodingException
{
sink.section1();
sink.sectionTitle1();
sink.text("Bugs By Class");
sink.anchor("report.BugsByClass");
sink.sectionTitle1_();
sink.paragraph();
sink.text("This is a list of bugs, by class.");
sink.paragraph_();
sink.table();
sink.tableRow();
sink.tableHeaderCell();
sink.text("Package");
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text("Classes");
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text("Lines");
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text("Bugs");
sink.tableHeaderCell_();
sink.tableRow_();
NodeList packageNodes =
(NodeList)xpath.evaluate("/BugCollection/FindBugsSummary/PackageStats[@total_bugs>0]", doc, XPathConstants.NODESET);
for (int packageNo = 0; packageNo < packageNodes.getLength(); packageNo++){
Node packageNode = packageNodes.item(packageNo);
String packageName = xpath.evaluate("attribute::package", packageNode);
sink.tableRow();
sink.tableCell();
sink.link("#package." + packageName);
sink.text(packageName);
sink.link_();
sink.tableCell_();
sink.tableCell();
sink.text(xpath.evaluate("attribute::total_types", packageNode));
sink.tableCell_();
sink.tableCell();
sink.text(xpath.evaluate("attribute::total_size", packageNode));
sink.tableCell_();
sink.tableCell();
sink.text(xpath.evaluate("attribute::total_bugs", packageNode));
sink.tableCell_();
sink.tableRow_();
}
sink.table_();
for (int packageNo = 0; packageNo < packageNodes.getLength(); packageNo++){
Node packageNode = packageNodes.item(packageNo);
String packageName = xpath.evaluate("attribute::package", packageNode);
sink.section2();
sink.sectionTitle2();
sink.anchor("package." + packageName);
sink.text("Package: " + packageName);
sink.sectionTitle2_();
sink.table();
sink.tableRow();
sink.tableHeaderCell();
sink.text("Class Name");
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text("Lines");
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text("Bugs");
sink.tableHeaderCell_();
sink.tableRow_();
NodeList classNodes = (NodeList)xpath.evaluate("child::*[@bugs>0]", packageNode, XPathConstants.NODESET);
for (int classNo = 0; classNo < classNodes.getLength(); classNo++){
Node classNode = classNodes.item(classNo);
String className = xpath.evaluate("attribute::class", classNode);
String bugs = xpath.evaluate("attribute::bugs", classNode);
String lines = xpath.evaluate("attribute::size", classNode);
sink.tableRow();
sink.tableCell();
sink.link("#class." + className);
sink.text(className);
sink.link_();
sink.tableCell_();
sink.tableCell();
sink.text(lines);
sink.tableCell_();
sink.tableCell();
sink.text(bugs);
sink.tableCell_();
sink.tableRow_();
}
sink.table_();
for (int classNo = 0; classNo < classNodes.getLength(); classNo++){
Node classNode = classNodes.item(classNo);
String className = xpath.evaluate("attribute::class", classNode);
sink.section3();
sink.sectionTitle3();
sink.rawText(" ");
sink.text("Class: " + className);
sink.sectionTitle3_();
sink.table();
sink.tableRow();
sink.tableHeaderCell();
sink.text("Category");
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text("Lines");
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text("Bug");
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text("Details");
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text("Priority");
sink.tableHeaderCell_();
sink.tableRow_();
NodeList bugNodes = (NodeList)xpath.evaluate("/BugCollection/BugInstance[child::Class/attribute::classname=\""
+ className + "\" and child::Class/attribute::primary=\"true\"]", doc, XPathConstants.NODESET);
for (int bugNo = 0; bugNo < bugNodes.getLength(); bugNo++){
Node bugNode = bugNodes.item(bugNo);
String categoryCode = xpath.evaluate("attribute::category", bugNode);
String categoryName = xpath.evaluate("/BugCollection/BugCategory[@category=\"" + categoryCode
+ "\"]/Description", doc);
sink.tableRow();
sink.tableCell();
sink.text(categoryName);
sink.tableCell_();
sink.tableCell();
String start = xpath.evaluate("child::SourceLine/attribute::start", bugNode);
String end = xpath.evaluate("child::SourceLine/attribute::end", bugNode);
if (xrefPath != null){
sink.rawText(getXrefLink(className, start, end));
}else{
if (start.equals(end)){
sink.text(start);
}else{
sink.text(start + "-" + end);
}
}
sink.tableCell_();
sink.tableCell();
sink.text(xpath.evaluate("LongMessage", bugNode));
sink.tableCell_();
sink.tableCell();
sink.link("#type." + xpath.evaluate("attribute::type", bugNode));
sink.text("Details");
sink.link_();
sink.tableCell_();
sink.tableCell();
String priority = xpath.evaluate("attribute::priority", bugNode);
if (priority.equals("1")){
priority = "High";
}else if (priority.equals("2")){
priority = "Medium";
}else{
priority = "Low";
}
sink.text(priority);
sink.tableCell_();
sink.tableRow_();
}
sink.table_();
sink.section3_();
}
sink.section2_();
}
sink.section1_();
}
private String getXrefLink(String className, String lineStart, String lineEnd)
{
if (!xrefPath.endsWith("/")){
xrefPath = xrefPath + "/";
}
className = className.replace(".", "/");
int idx = className.indexOf("$");
if (idx >= 0){
className = className.substring(0, idx);
}
String link = "" + lineStart;
if (!lineStart.equals(lineEnd)){
link += "-" + lineEnd;
}
link += " ";
return link;
}
private void doBugsByCategoryReport(Sink sink, Document doc, XPath xpath)
throws XPathExpressionException, UnsupportedEncodingException
{
sink.section1();
sink.sectionTitle1();
sink.text("Bugs By Category");
sink.anchor("report.BugsByCategory");
sink.sectionTitle1_();
sink.paragraph();
sink.text("This is a list of bugs, by category.");
sink.paragraph_();
sink.table();
sink.tableRow();
sink.tableHeaderCell();
sink.text("Category / Bug Pattern");
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text("Bugs");
sink.tableHeaderCell_();
sink.tableRow_();
NodeList categoryNodes = (NodeList)xpath.evaluate("/BugCollection/BugCategory", doc, XPathConstants.NODESET);
for (int categoryNo = 0; categoryNo < categoryNodes.getLength(); categoryNo++){
Node categoryNode = categoryNodes.item(categoryNo);
String categoryCode = xpath.evaluate("attribute::category", categoryNode);
String categoryDescription = xpath.evaluate("Description", categoryNode);
sink.tableRow();
sink.tableCell();
sink.bold();
sink.link("#category." + categoryCode);
sink.text(categoryDescription);
sink.link_();
sink.bold_();
sink.tableCell_();
sink.tableCell();
sink.bold();
sink.text(""
+ ((NodeList)xpath.evaluate("/BugCollection/BugInstance[@category=\"" + categoryCode + "\"]", doc, XPathConstants.NODESET)).getLength());
sink.bold_();
sink.tableCell_();
sink.tableRow_();
NodeList typeNodes =
(NodeList)xpath.evaluate("/BugCollection/BugPattern[@category=\"" + categoryCode + "\"]", doc, XPathConstants.NODESET);
for (int typeNo = 0; typeNo < typeNodes.getLength(); typeNo++){
Node typeNode = typeNodes.item(typeNo);
String typeCode = xpath.evaluate("attribute::type", typeNode);
String typeDescription = xpath.evaluate("ShortDescription", typeNode);
sink.tableRow();
sink.tableCell();
sink.rawText("");
sink.link("#type." + typeCode);
sink.text(typeDescription);
sink.link_();
sink.rawText(" ");
sink.listItem_();
sink.list_();
sink.tableCell_();
sink.tableCell();
sink.text(""
+ ((NodeList)xpath.evaluate("/BugCollection/BugInstance[@type=\"" + typeCode + "\"]", doc, XPathConstants.NODESET)).getLength());
sink.tableCell_();
sink.tableRow_();
}
}
sink.table_();
for (int categoryNo = 0; categoryNo < categoryNodes.getLength(); categoryNo++){
Node categoryNode = categoryNodes.item(categoryNo);
String categoryCode = xpath.evaluate("attribute::category", categoryNode);
String categoryDescription = xpath.evaluate("Description", categoryNode);
sink.section2();
sink.sectionTitle2();
sink.text(categoryDescription);
sink.anchor("category." + categoryCode);
sink.sectionTitle2_();
sink.table();
sink.tableRow();
sink.tableHeaderCell();
sink.text("Bug Pattern");
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text("Bugs");
sink.tableHeaderCell_();
sink.tableRow_();
NodeList typeNodes =
(NodeList)xpath.evaluate("/BugCollection/BugPattern[@category=\"" + categoryCode + "\"]", doc, XPathConstants.NODESET);
for (int typeNo = 0; typeNo < typeNodes.getLength(); typeNo++){
Node typeNode = typeNodes.item(typeNo);
String typeCode = xpath.evaluate("attribute::type", typeNode);
String typeDescription = xpath.evaluate("ShortDescription", typeNode);
NodeList bugsNodes =
(NodeList)xpath.evaluate("/BugCollection/BugInstance[@type=\"" + typeCode + "\"]", doc, XPathConstants.NODESET);
sink.tableRow();
sink.tableCell();
sink.link("#type." + typeCode);
sink.text(typeDescription);
sink.link_();
sink.tableCell_();
sink.tableCell();
sink.text("" + bugsNodes.getLength());
sink.tableCell_();
sink.tableRow_();
}
sink.table_();
for (int typeNo = 0; typeNo < typeNodes.getLength(); typeNo++){
Node typeNode = typeNodes.item(typeNo);
String typeCode = xpath.evaluate("attribute::type", typeNode);
String typeDescription = xpath.evaluate("ShortDescription", typeNode);
String typeDetails = xpath.evaluate("Details", typeNode);
sink.section3();
sink.sectionTitle3();
sink.text(typeDescription);
sink.anchor("type." + typeCode);
sink.sectionTitle3_();
sink.rawText(typeDetails);
sink.table();
sink.tableRow();
sink.tableHeaderCell();
sink.text("Class");
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text("Lines");
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text("Details");
sink.tableHeaderCell_();
sink.tableRow_();
SortedSet bugTypeClasses = new TreeSet();
NodeList bugNodes = (NodeList)xpath.evaluate("/BugCollection/BugInstance[@type=\"" + typeCode
+ "\"]/Class[@primary=\"true\"]", doc, XPathConstants.NODESET);
for (int bugno = 0; bugno < bugNodes.getLength(); bugno++){
bugTypeClasses.add(xpath.evaluate("attribute::classname", bugNodes.item(bugno)));
}
for (String className : bugTypeClasses){
bugNodes = (NodeList)xpath.evaluate("/BugCollection/BugInstance[@type=\"" + typeCode + "\"]/Class[@classname=\""
+ className + "\"]", doc, XPathConstants.NODESET);
for (int bugno = 0; bugno < bugNodes.getLength(); bugno++){
Node bugClassNode = bugNodes.item(bugno);
sink.tableRow();
sink.tableCell();
sink.link("#class." + className);
sink.text(className);
sink.link_();
sink.tableCell_();
String start = xpath.evaluate("../child::SourceLine/attribute::start", bugClassNode);
String end = xpath.evaluate("../child::SourceLine/attribute::end", bugClassNode);
String details = xpath.evaluate("../LongMessage", bugClassNode);
sink.tableCell();
if (xrefPath == null){
if (start.equals(end)){
sink.text(start);
}else{
sink.text(start + "-" + end);
}
}else{
sink.rawText(getXrefLink(className, start, end));
}
sink.tableCell_();
sink.tableCell();
sink.text(details);
sink.tableCell_();
sink.tableRow_();
}
}
sink.table_();
sink.section3_();
}
sink.section2_();
}
sink.section1_();
}
}