src.com.mebigfatguy.fbdelta.FBDeltaTask Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fb-delta Show documentation
Show all versions of fb-delta Show documentation
An ant task to report differences between two findbugs xml files.
package com.mebigfatguy.fbdelta;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class FBDeltaTask extends Task {
private File baseReport;
private File updateReport;
private File outputReport;
private String changedPropertyName;
public void setBaseReport(File base) {
baseReport = base;
}
public void setUpdateReport(File update) {
updateReport = update;
}
public void setOutputReport(File output) {
outputReport = output;
}
public void setChanged(String changedProperty) {
changedPropertyName = changedProperty;
}
@Override
public void execute() {
try {
if ((baseReport == null) || (!baseReport.isFile())) {
throw new BuildException("'baseReport' is not specified or is invalid");
}
if ((updateReport == null) || (!updateReport.isFile())) {
throw new BuildException("'updateReport' is not specified or is invalid");
}
Map>> baseData = parseReport(baseReport);
Map>> updateData = parseReport(updateReport);
removeDuplicates(baseData, updateData);
if (outputReport != null) {
outputReport.getParentFile().mkdirs();
try (PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputReport), StandardCharsets.UTF_8)))) {
pw.println("");
pw.println("\t");
for (Map.Entry>> clsEntry : baseData.entrySet()) {
pw.println("\t\t");
for (Map.Entry> typeEntry : clsEntry.getValue().entrySet()) {
pw.println("\t\t\t ");
}
pw.println("\t\t ");
}
pw.println("\t ");
pw.println("\t");
for (Map.Entry>> clsEntry : updateData.entrySet()) {
pw.println("\t\t");
for (Map.Entry> typeEntry : clsEntry.getValue().entrySet()) {
pw.println("\t\t\t ");
}
pw.println("\t\t ");
}
pw.println("\t ");
pw.println(" ");
}
}
if ((changedPropertyName != null) && (!baseData.isEmpty() || !updateData.isEmpty())) {
getProject().setProperty(changedPropertyName, String.valueOf(Boolean.TRUE));
}
} catch (Exception e) {
throw new BuildException("Failed to run fb-delta on report of " + baseReport + " to " + updateReport, e);
}
}
private Map>> parseReport(File reportFile)
throws IOException, SAXException, ParserConfigurationException, XPathExpressionException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document d = db.parse(reportFile);
XPathFactory xpf = XPathFactory.newInstance();
XPath xp = xpf.newXPath();
XPathExpression bugInstanceXPE = xp.compile("/BugCollection/BugInstance");
XPathExpression classNameXPE = xp.compile("./Class/@classname");
XPathExpression bugXPE = xp.compile("./Method|./Field");
XPathExpression sourceLineXPE = xp.compile("./Class/SourceLine");
Map>> report = new TreeMap<>();
NodeList bugsNodes = (NodeList) bugInstanceXPE.evaluate(d, XPathConstants.NODESET);
for (int i = 0; i < bugsNodes.getLength(); i++) {
Element bugElement = (Element) bugsNodes.item(i);
String type = bugElement.getAttribute("type");
String className = (String) classNameXPE.evaluate(bugElement, XPathConstants.STRING);
Element bugDetailElement = (Element) bugXPE.evaluate(bugElement, XPathConstants.NODE);
String bugData = null;
if (bugDetailElement != null) {
bugData = bugDetailElement.getAttribute("name") + bugDetailElement.getAttribute("signature");
} else {
Element sourceLineElement = (Element) sourceLineXPE.evaluate(bugElement, XPathConstants.NODE);
if (sourceLineElement != null) {
bugData = sourceLineElement.getAttribute("start") + "-" + sourceLineElement.getAttribute("end");
} else {
bugData = "UNKNOWN " + Math.random();
}
}
Map> classBugs = report.get(className);
if (classBugs == null) {
classBugs = new TreeMap<>();
report.put(className, classBugs);
}
Set bugs = classBugs.get(type);
if (bugs == null) {
bugs = new HashSet<>();
classBugs.put(type, bugs);
}
bugs.add(bugData);
}
return report;
}
private void removeDuplicates(Map>> baseData, Map>> updateData) {
Iterator>>> clsIt = baseData.entrySet().iterator();
while (clsIt.hasNext()) {
Map.Entry>> clsEntry = clsIt.next();
String clsName = clsEntry.getKey();
Map> baseTypeMap = clsEntry.getValue();
Map> updateTypeMap = updateData.get(clsName);
if (updateTypeMap == null) {
continue;
}
Iterator>> typeIt = baseTypeMap.entrySet().iterator();
while (typeIt.hasNext()) {
Map.Entry> typeEntry = typeIt.next();
String baseType = typeEntry.getKey();
Set baseBugs = typeEntry.getValue();
Set updateBugs = updateTypeMap.get(baseType);
if (updateBugs == null) {
continue;
}
if (baseBugs.size() == updateBugs.size()) {
// this ignores the add 1/remove 1 issue but probably not a problem
typeIt.remove();
updateTypeMap.remove(baseType);
continue;
}
Iterator bugIt = updateBugs.iterator();
while (bugIt.hasNext()) {
String bug = bugIt.next();
if (updateBugs.contains(bug)) {
bugIt.remove();
updateBugs.remove(bug);
}
}
if (baseBugs.isEmpty()) {
baseTypeMap.remove(baseType);
}
if (updateBugs.isEmpty()) {
updateTypeMap.remove(baseType);
}
}
if (baseTypeMap.isEmpty()) {
clsIt.remove();
}
if (updateTypeMap.isEmpty()) {
updateData.remove(clsName);
}
}
}
/**
* for debugging only
*/
public static void main(String[] args) {
FBDeltaTask t = new FBDeltaTask();
Project p = new Project();
t.setProject(p);
t.setBaseReport(new File("/home/dave/dev/fb-contrib/samples.xml"));
t.setUpdateReport(new File("/home/dave/dev/fb-contrib/target/samples.xml"));
t.setChanged("changed");
t.setOutputReport(new File("/home/dave/dev/fb-contrib/target/samples-delta.xml"));
t.execute();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy