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

src.com.mebigfatguy.fbdelta.FBDeltaTask Maven / Gradle / Ivy

There is a newer version: 0.6.0
Show newest version
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