--- /dev/null
+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);
+ }
+ }
+ }
+}
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();
}
+
+ // 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();
+ }
}
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;
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);
+ }
}
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;
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)) );
+ }
}