1 package net.jaekl.cfb.xml;
3 import java.io.PrintWriter;
4 import java.util.ArrayList;
5 import java.util.Arrays;
6 import java.util.Collections;
9 import org.xml.sax.Attributes;
11 import net.jaekl.cfb.store.Location;
12 import net.jaekl.cfb.util.Util;
13 import net.jaekl.qd.xml.MissingAttributeException;
14 import net.jaekl.qd.xml.ParseResult;
15 import net.jaekl.qd.xml.XmlParseException;
17 public class BugInstance extends ParseResult {
19 static final String TAG = "BugInstance";
20 static final String[] INTERNAL = { };
21 static final Object[][] EXTERNAL = { { BugClass.TAG, BugClass.class},
22 { BugMethod.TAG, BugMethod.class},
23 { LocalVariable.TAG, LocalVariable.class},
24 { SourceLine.TAG, SourceLine.class} };
25 static final String CATEGORY = "category";
26 static final String TYPE = "type";
31 ArrayList<BugClass> m_classes;
32 ArrayList<BugMethod> m_methods;
33 ArrayList<LocalVariable> m_locals;
34 ArrayList<SourceLine> m_lines;
35 ArrayList<Location> m_locations;
37 public BugInstance() {
38 super(TAG, INTERNAL, EXTERNAL);
41 m_category = m_type = null;
42 m_classes = new ArrayList<BugClass>();
43 m_methods = new ArrayList<BugMethod>();
44 m_locals = new ArrayList<LocalVariable>();
45 m_lines = new ArrayList<SourceLine>();
46 m_locations = new ArrayList<Location>();
49 public BugInstance(Long id,
53 LocalVariable[] variables)
55 super(TAG, INTERNAL, EXTERNAL);
58 m_category = category;
63 m_locations = new ArrayList<Location>(Arrays.asList(locations));
64 m_locals = new ArrayList<LocalVariable>(Arrays.asList(variables));
67 public String getCategory() { return m_category; }
68 public String getType() { return m_type; }
69 public List<LocalVariable> getVariables() { return Collections.unmodifiableList(m_locals); }
70 public List<Location> getLocations() { return Collections.unmodifiableList(m_locations); }
73 public void endContents(String uri, String localName, String qName, String chars)
74 throws XmlParseException
80 public void complete()
86 public void endExternal(String uri, String localName, String qName)
87 throws XmlParseException
89 if (BugClass.TAG.equals(localName)) {
90 ParseResult[] collected = collectParsedChildren(BugClass.class);
91 for (ParseResult pr : collected) {
92 assert(pr instanceof BugClass);
93 m_classes.add((BugClass) pr);
96 else if (BugMethod.TAG.equals(localName)) {
97 ParseResult[] collected = collectParsedChildren(BugMethod.class);
98 for (ParseResult pr : collected) {
99 assert(pr instanceof BugMethod);
100 m_methods.add((BugMethod)pr);
103 else if (LocalVariable.TAG.equals(localName)) {
104 ParseResult[] collected = collectParsedChildren(LocalVariable.class);
105 for (ParseResult pr : collected) {
106 assert(pr instanceof LocalVariable);
107 m_locals.add((LocalVariable)pr);
110 else if (SourceLine.TAG.equals(localName)) {
111 ParseResult[] collected = collectParsedChildren(SourceLine.class);
112 for (ParseResult pr : collected) {
113 assert(pr instanceof SourceLine);
114 m_lines.add((SourceLine)pr);
120 public void handleMainAttributes(Attributes attr) throws MissingAttributeException
122 m_type = this.getRequiredAttr(TAG, attr, TYPE);
123 m_category = this.getRequiredAttr(TAG, attr, CATEGORY);
127 public void dump(PrintWriter pw, int indent)
129 int childIndent = indent + 2;
130 String margin = String.format("%" + indent + "s", "");
132 pw.println(margin + TAG + " (" + m_type + ")");
133 pw.println(margin + CATEGORY + " (" + m_category + ")");
134 for (BugClass bc : m_classes) {
135 bc.dump(pw, childIndent);
137 for (BugMethod bm : m_methods) {
138 bm.dump(pw, childIndent);
140 for (LocalVariable lv : m_locals) {
141 lv.dump(pw, childIndent);
143 for (SourceLine sl : m_lines) {
144 sl.dump(pw, childIndent);
148 // Note that this is a heuristic, "fuzzy", equals.
149 // Two BugInstances will be considered equal if:
150 // - they refer to the same bug type
151 // - they took place in the same class and method
152 // - the variable names referred to (if any) match
153 // In particular, this equality test does not check
154 // for line numbers being equal. This is by design;
155 // we want to consider two bugs to be the "same bug"
156 // even if other changes in the file have shifted
157 // line numbers a bit.
159 public boolean equals(Object obj)
164 if (obj instanceof BugInstance) {
165 BugInstance that = (BugInstance)obj;
167 if (! Util.objsAreEqual(this.m_type, that.m_type)) {
171 if (! Util.objsAreEqual(this.m_category, that.m_category)) {
175 Location thisLoc = this.getPrincipalLocation();
176 Location thatLoc = that.getPrincipalLocation();
177 if (null == thisLoc) {
178 if (null == thatLoc) {
183 if (! Util.objsAreEqual(thisLoc.getClassName(), thatLoc.getClassName())) {
186 if (! Util.objsAreEqual(thisLoc.getMethodName(), thatLoc.getMethodName())) {
191 if (! Util.objsAreEqual(this.getVariables(), that.getVariables())) {
201 public int hashCode()
203 int code = Util.objHashCode(m_type)
204 * Util.objHashCode(m_category)
205 * Util.objHashCode(getPrincipalLocation())
206 * Util.objHashCode(getVariables());
210 // Get the "principal" Location.
211 // This should be the place where the bug is reported.
212 Location getPrincipalLocation()
214 if (null != m_locations && m_locations.size() > 0) {
215 return m_locations.get(0);
220 private void computeLocations()
222 assert(null != m_classes);
223 assert(null != m_methods);
224 assert(null != m_lines);
229 Somewhat unfortunate special case.
230 The primary "location" for a bug instance is split between tags.
231 Most bugs have a pattern like this:
241 The primary location for a bug is given by the <Method> with no role attribute,
242 but the <SourceLine/> inside that method describes the whole range of lines
243 covered by that Method, not the spot where the bug is located--that is given
244 by the <SourceLine/> that is a direct child fo the <BugInstance/>.
247 BugMethod primaryMethod = null;
248 SourceLine primaryLine = null;
250 for (BugMethod method : m_methods) {
251 if (null != method.getRole()) {
252 primaryMethod = method;
256 if (m_lines.size() > 0) {
257 primaryLine = m_lines.get(0);
260 if ((null != primaryMethod) && (null != primaryLine)) {
261 m_locations.add(new Location(primaryMethod, primaryLine));
264 for (BugMethod method : m_methods) {
265 if (primaryMethod != method) {
266 m_locations.add(new Location(method));
269 for (SourceLine line : m_lines) {
270 if (primaryLine != line) {
271 m_locations.add(new Location(line));
274 for (BugClass clazz : m_classes) {
275 m_locations.add(new Location(clazz));