X-Git-Url: http://jaekl.net/gitweb/?a=blobdiff_plain;f=prod%2Fnet%2Fjaekl%2Fcfb%2Fxml%2FBugInstance.java;h=b805096c67551c7f3a35847b12f6660bc35acca0;hb=7ac6be132ecd6872971a1de56f033b4434d3173a;hp=81ebcfcd671f4abdf8afa16fe62bec1175116abd;hpb=08a530ef53cc4756f5e632b69c78830872ebd9f4;p=cfb.git diff --git a/prod/net/jaekl/cfb/xml/BugInstance.java b/prod/net/jaekl/cfb/xml/BugInstance.java index 81ebcfc..b805096 100644 --- a/prod/net/jaekl/cfb/xml/BugInstance.java +++ b/prod/net/jaekl/cfb/xml/BugInstance.java @@ -1,34 +1,291 @@ package net.jaekl.cfb.xml; +import java.io.PrintWriter; +import java.util.ArrayList; +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; public class BugInstance extends ParseResult { - static final String ROOT_TAG = "BugInstance"; + static final String TAG = "BugInstance"; static final String[] INTERNAL = { }; - static final Object[][] EXTERNAL = { { BugClass.ROOT_TAG, BugClass.class}, - { BugMethod.ROOT_TAG, BugMethod.class}, - { SourceLine.ROOT_TAG, SourceLine.class} }; + static final Object[][] EXTERNAL = { { BugClass.TAG, BugClass.class}, + { 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(ROOT_TAG, INTERNAL, EXTERNAL); + super(TAG, INTERNAL, EXTERNAL); + + 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(); } - @Override - public void endContents(String uri, String localName, String qName, - String chars, Attributes attr) throws XmlParseException { - // TODO Auto-generated method stub + 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 operation + } + + @Override + public void complete() + { + computeLocations(); } @Override public void endExternal(String uri, String localName, String qName) - throws XmlParseException { - // TODO Auto-generated method stub + throws XmlParseException + { + if (BugClass.TAG.equals(localName)) { + ParseResult[] collected = collectParsedChildren(BugClass.class); + for (ParseResult pr : collected) { + assert(pr instanceof BugClass); + m_classes.add((BugClass) pr); + } + } + else if (BugMethod.TAG.equals(localName)) { + ParseResult[] collected = collectParsedChildren(BugMethod.class); + for (ParseResult pr : collected) { + assert(pr instanceof BugMethod); + m_methods.add((BugMethod)pr); + } + } + else if (LocalVariable.TAG.equals(localName)) { + ParseResult[] collected = collectParsedChildren(LocalVariable.class); + for (ParseResult pr : collected) { + assert(pr instanceof LocalVariable); + m_locals.add((LocalVariable)pr); + } + } + else if (SourceLine.TAG.equals(localName)) { + ParseResult[] collected = collectParsedChildren(SourceLine.class); + for (ParseResult pr : collected) { + assert(pr instanceof SourceLine); + m_lines.add((SourceLine)pr); + } + } + } + + @Override + public void handleMainAttributes(Attributes attr) throws MissingAttributeException + { + m_type = this.getRequiredAttr(TAG, attr, TYPE); + m_category = this.getRequiredAttr(TAG, attr, CATEGORY); + } + @Override + public void dump(PrintWriter pw, int indent) + { + int childIndent = indent + 2; + 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); + } + for (BugMethod bm : m_methods) { + bm.dump(pw, childIndent); + } + for (LocalVariable lv : m_locals) { + 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)); + } + } }