X-Git-Url: http://jaekl.net/gitweb/?a=blobdiff_plain;f=prod%2Fnet%2Fjaekl%2Fcfb%2Fxml%2FBugInstance.java;h=b805096c67551c7f3a35847b12f6660bc35acca0;hb=f1c4313e9229dd2d5f7fd984169cbdb89fef4cd5;hp=ee5bac0474aa3a24e38dcbfae3c8f7b7fb1f1db9;hpb=598968590bf67cf87d3243878f7ebb2ff8015065;p=cfb.git diff --git a/prod/net/jaekl/cfb/xml/BugInstance.java b/prod/net/jaekl/cfb/xml/BugInstance.java index ee5bac0..b805096 100644 --- a/prod/net/jaekl/cfb/xml/BugInstance.java +++ b/prod/net/jaekl/cfb/xml/BugInstance.java @@ -2,11 +2,14 @@ package net.jaekl.cfb.xml; import java.io.PrintWriter; import java.util.ArrayList; - -import javax.tools.JavaFileManager.Location; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import org.xml.sax.Attributes; +import net.jaekl.cfb.store.Location; +import net.jaekl.cfb.util.Util; import net.jaekl.qd.xml.MissingAttributeException; import net.jaekl.qd.xml.ParseResult; import net.jaekl.qd.xml.XmlParseException; @@ -19,31 +22,66 @@ public class BugInstance extends ParseResult { { BugMethod.TAG, BugMethod.class}, { LocalVariable.TAG, LocalVariable.class}, { SourceLine.TAG, SourceLine.class} }; + static final String CATEGORY = "category"; static final String TYPE = "type"; + Long m_id; + String m_category; String m_type; ArrayList m_classes; ArrayList m_methods; ArrayList m_locals; ArrayList m_lines; + ArrayList m_locations; public BugInstance() { super(TAG, INTERNAL, EXTERNAL); - m_type = null; + m_id = null; + m_category = m_type = null; m_classes = new ArrayList(); m_methods = new ArrayList(); m_locals = new ArrayList(); m_lines = new ArrayList(); + m_locations = new ArrayList(); } + public BugInstance(Long id, + String category, + String type, + Location[] locations, + LocalVariable[] variables) + { + super(TAG, INTERNAL, EXTERNAL); + + m_id = id; + m_category = category; + m_type = type; + + m_classes = new ArrayList(); + m_methods = new ArrayList(); + m_lines = new ArrayList(); + + m_locations = new ArrayList(Arrays.asList(locations)); + m_locals = new ArrayList(Arrays.asList(variables)); + } + + public String getCategory() { return m_category; } public String getType() { return m_type; } + public List getVariables() { return Collections.unmodifiableList(m_locals); } + public List getLocations() { return Collections.unmodifiableList(m_locations); } @Override public void endContents(String uri, String localName, String qName, String chars) throws XmlParseException { - // no-op + // no operation + } + + @Override + public void complete() + { + computeLocations(); } @Override @@ -84,6 +122,7 @@ public class BugInstance extends ParseResult { public void handleMainAttributes(Attributes attr) throws MissingAttributeException { m_type = this.getRequiredAttr(TAG, attr, TYPE); + m_category = this.getRequiredAttr(TAG, attr, CATEGORY); } @Override @@ -93,6 +132,7 @@ public class BugInstance extends ParseResult { String margin = String.format("%" + indent + "s", ""); pw.println(margin + TAG + " (" + m_type + ")"); + pw.println(margin + CATEGORY + " (" + m_category + ")"); for (BugClass bc : m_classes) { bc.dump(pw, childIndent); } @@ -100,10 +140,152 @@ public class BugInstance extends ParseResult { bm.dump(pw, childIndent); } for (LocalVariable lv : m_locals) { - lv.dump(pw, childIndent); + if (null != lv) { + lv.dump(pw, childIndent); + } } for (SourceLine sl : m_lines) { sl.dump(pw, childIndent); } + for (Location loc : m_locations) { + if (null != loc) { + loc.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; + } + + Location thisLoc = this.getPrincipalLocation(); + Location thatLoc = that.getPrincipalLocation(); + if (null == thisLoc) { + if (null == thatLoc) { + return false; + } + } + else { + if (! thisLoc.fuzzyEquals(thatLoc)) { + return false; + } + } + + return true; + } + return false; + } + + @Override + public int hashCode() + { + int code = Util.objHashCode(m_type) + ^ Util.objHashCode(m_category) + ^ Util.objHashCode(getPrincipalLocation()); + return code; + } + + // Get the "principal" Location. + // This should be the place where the bug is reported. + Location getPrincipalLocation() + { + if (null == m_locations) { + return null; + } + + for (int idx = 0; idx < m_locations.size(); ++idx) { + Location loc = m_locations.get(idx); + if (Location.METHOD_CALLED.equals(loc.getMethodRole())) { + // METHOD_CALLED locations describe the method that is being called, + // but the bug is located in the caller, not in the callee. + // Thus, ignore this information about the callee. + continue; + } + return loc; + } + + return null; + } + + private void computeLocations() + { + assert(null != m_classes); + assert(null != m_methods); + assert(null != m_lines); + + m_locations.clear(); + + /* + Somewhat unfortunate special case. + The primary "location" for a bug instance is split between tags. + Most bugs have a pattern like this: + + ... + + + + ... + + + + The primary location for a bug is given by the with no role attribute, + but the inside that method describes the whole range of lines + covered by that Method, not the spot where the bug is located--that is given + by the that is a direct child fo the . + */ + + BugMethod primaryMethod = null; + SourceLine primaryLine = null; + + for (BugMethod method : m_methods) { + if (null != method.getRole()) { + primaryMethod = method; + break; + } + } + if (m_lines.size() > 0) { + primaryLine = m_lines.get(0); + } + + if ((null != primaryMethod) && (null != primaryLine)) { + m_locations.add(new Location(primaryMethod, primaryLine)); + } + + for (BugMethod method : m_methods) { + if (primaryMethod != method) { + m_locations.add(new Location(method)); + } + } + for (SourceLine line : m_lines) { + if (primaryLine != line) { + m_locations.add(new Location(line)); + } + } + for (BugClass clazz : m_classes) { + m_locations.add(new Location(clazz)); + } } }