Add computation of deltas (differences between Analysis runs).
authorChris Jaekl <cejaekl@yahoo.com>
Mon, 28 Sep 2015 13:41:10 +0000 (22:41 +0900)
committerChris Jaekl <cejaekl@yahoo.com>
Mon, 28 Sep 2015 13:41:10 +0000 (22:41 +0900)
prod/net/jaekl/cfb/analyze/Delta.java [new file with mode: 0644]
prod/net/jaekl/cfb/util/Util.java
prod/net/jaekl/cfb/xml/BugInstance.java
prod/net/jaekl/cfb/xml/LocalVariable.java

diff --git a/prod/net/jaekl/cfb/analyze/Delta.java b/prod/net/jaekl/cfb/analyze/Delta.java
new file mode 100644 (file)
index 0000000..e2853e0
--- /dev/null
@@ -0,0 +1,57 @@
+package net.jaekl.cfb.analyze;
+
+import java.util.HashSet;
+
+import net.jaekl.cfb.xml.BugInstance;
+
+// Compute and store the delta (difference) between two analyses
+
+public class Delta {
+       HashSet<BugInstance> m_fixed;           // bugs that have been fixed
+       HashSet<BugInstance> m_common;  // bugs that are present in both versions
+       HashSet<BugInstance> m_new;             // bugs introduced in the new version
+       
+       public Delta(Analysis before, Analysis after)
+       {
+               m_fixed = new HashSet<BugInstance>();
+               m_common = new HashSet<BugInstance>();
+               m_new = new HashSet<BugInstance>();
+               
+               computeDelta(before, after);
+       }
+       
+       public BugInstance[] getFixed() { return m_fixed.toArray(new BugInstance[m_fixed.size()]); }
+       public int getNumFixed() { return m_fixed.size(); }
+       
+       public BugInstance[] getCommon() { return m_common.toArray(new BugInstance[m_common.size()]); }
+       public int getNumCommon() { return m_common.size(); }
+       
+       public BugInstance[] getNew() { return m_new.toArray(new BugInstance[m_new.size()]); }
+       public int getNumNew() { return m_new.size(); }
+       
+       void computeDelta(Analysis before, Analysis after)
+       {
+               m_fixed.clear();
+               m_common.clear();
+               m_new.clear();
+               
+               HashSet<BugInstance> beforeBugs = new HashSet<BugInstance>();
+               
+               beforeBugs.addAll(before.getBugCollection().getBugs());
+               
+               for (BugInstance bug : after.getBugCollection().getBugs()) {
+                       if (beforeBugs.contains(bug)) {
+                               m_common.add(bug);
+                       }
+                       else {
+                               m_new.add(bug);
+                       }
+               }
+               
+               for (BugInstance bug : before.getBugCollection().getBugs()) {
+                       if (! m_common.contains(bug)) {
+                               m_fixed.add(bug);
+                       }
+               }
+       }
+}
index 30f4c90c64bc40f63fc9afc7b5d312edd54b8c35..309e20dde6e74fd672074cde15ad87aa041a6803 100644 (file)
@@ -6,10 +6,32 @@ import java.io.PrintWriter;
 import java.io.StringWriter;
 
 public class Util {
 import java.io.StringWriter;
 
 public class Util {
-       public static String stringify(Throwable thr) {
+       public static String stringify(Throwable thr) 
+       {
                StringWriter sw = new StringWriter();
                PrintWriter pw = new PrintWriter(sw);
                thr.printStackTrace(pw);
                return sw.toString();
        }
                StringWriter sw = new StringWriter();
                PrintWriter pw = new PrintWriter(sw);
                thr.printStackTrace(pw);
                return sw.toString();
        }
+       
+       // Test for equality, while taking care to avoid 
+       // dereferencing a null pointer.
+       // Note that two null pointers are considered equal.
+       public static boolean objsAreEqual(Object a, Object b) 
+       {
+               if ((null == a) || (null == b)) {
+                       return (a == b);
+               }
+               
+               return a.equals(b);
+       }
+       
+       // Return 1 if obj is null, or obj.hashCode() otherwise
+       public static int objHashCode(Object obj)
+       {
+               if (null == obj) {
+                       return 1;
+               }
+               return obj.hashCode();
+       }
 }
 }
index 3386e3f70fd2d47da0d820e174c617f5592e73b5..100251f9e9a37fce9235b30beb1492ebec24adc3 100644 (file)
@@ -7,6 +7,7 @@ import java.util.List;
 
 import org.xml.sax.Attributes;
 
 
 import org.xml.sax.Attributes;
 
+import net.jaekl.cfb.util.Util;
 import net.jaekl.qd.xml.MissingAttributeException;
 import net.jaekl.qd.xml.ParseResult;
 import net.jaekl.qd.xml.XmlParseException;
 import net.jaekl.qd.xml.MissingAttributeException;
 import net.jaekl.qd.xml.ParseResult;
 import net.jaekl.qd.xml.XmlParseException;
@@ -115,4 +116,77 @@ public class BugInstance extends ParseResult {
                        sl.dump(pw, childIndent);
                }
        }
                        sl.dump(pw, childIndent);
                }
        }
+       
+       // Note that this is a heuristic, "fuzzy", equals.
+       // Two BugInstances will be considered equal if:
+       //   - they refer to the same bug type
+       //   - they took place in the same class and method
+       //   - the variable names referred to (if any) match
+       // In particular, this equality test does not check 
+       // for line numbers being equal.  This is by design;
+       // we want to consider two bugs to be the "same bug" 
+       // even if other changes in the file have shifted 
+       // line numbers a bit.
+       @Override
+       public boolean equals(Object obj) 
+       {
+               if (null == obj) { 
+                       return false;
+               }
+               if (obj instanceof BugInstance) {
+                       BugInstance that = (BugInstance)obj;
+                       
+                       if (! Util.objsAreEqual(this.m_type, that.m_type)) {
+                               return false;
+                       }
+                       
+                       if (! Util.objsAreEqual(this.m_category, that.m_category)) {
+                               return false;
+                       }
+
+                       BugMethod thisMethod = this.getPrincipalMethod();
+                       BugMethod thatMethod = that.getPrincipalMethod();
+                       if (null == thisMethod) {
+                               if (null == thatMethod) {
+                                       return false;
+                               }
+                       }
+                       else {
+                               if (! Util.objsAreEqual(thisMethod.getClassName(), thatMethod.getClassName())) {
+                                       return false;
+                               }
+                               if (! Util.objsAreEqual(thisMethod.getMethodName(), thatMethod.getMethodName())) {
+                                       return false;
+                               }
+                       }
+                       
+                       if (! Util.objsAreEqual(this.getVariables(), that.getVariables())) {
+                               return false;
+                       }
+                       
+                       return true;
+               }
+               return false;
+       }
+       
+       @Override
+       public int hashCode() 
+       {
+               int code = Util.objHashCode(m_type)
+                                * Util.objHashCode(m_category)
+                                * Util.objHashCode(getPrincipalMethod())
+                                * Util.objHashCode(getVariables());
+               return code;
+       }
+       
+       // Get the "principal" method.
+       // This should be the place where the bug is reported.
+       BugMethod getPrincipalMethod()
+       {
+               List<BugMethod> bugMethods = getMethods();
+               if ((null == bugMethods) || (0 == bugMethods.size())) {
+                       return null;
+               }
+               return bugMethods.get(0);
+       }
 }
 }
index a4d26d81b194043416c7b1f219b17e654ed025f6..277bf2dba69bb82d15ce67fc526fa5676afbc83d 100644 (file)
@@ -4,6 +4,7 @@ import java.io.PrintWriter;
 
 import org.xml.sax.Attributes;
 
 
 import org.xml.sax.Attributes;
 
+import net.jaekl.cfb.util.Util;
 import net.jaekl.qd.xml.MissingAttributeException;
 import net.jaekl.qd.xml.ParseResult;
 import net.jaekl.qd.xml.XmlParseException;
 import net.jaekl.qd.xml.MissingAttributeException;
 import net.jaekl.qd.xml.ParseResult;
 import net.jaekl.qd.xml.XmlParseException;
@@ -59,4 +60,24 @@ public class LocalVariable extends ParseResult {
                pw.println(tab + NAME + "=" + m_name);
                pw.println(tab + ROLE + "=" + m_role);
        }
                pw.println(tab + NAME + "=" + m_name);
                pw.println(tab + ROLE + "=" + m_role);
        }
+       
+       @Override
+       public boolean equals(Object obj) 
+       {
+               if (null == obj) {
+                       return false;
+               }
+               if (obj instanceof LocalVariable) {
+                       LocalVariable that = (LocalVariable)obj;
+                       return (   Util.objsAreEqual(this.m_name, that.m_name) 
+                                   && Util.objsAreEqual(this.m_role, that.m_role) );
+               }
+               return false;
+       }
+       
+       @Override
+       public int hashCode()
+       {
+               return ( (1 + Util.objHashCode(m_name)) * (1 + Util.objHashCode(m_role)) );
+       }
 }
 }