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;
61 m_classes = new ArrayList<BugClass>();
62 m_methods = new ArrayList<BugMethod>();
63 m_lines = new ArrayList<SourceLine>();
65 m_locations = new ArrayList<Location>(Arrays.asList(locations));
66 m_locals = new ArrayList<LocalVariable>(Arrays.asList(variables));
69 public String getCategory() { return m_category; }
70 public String getType() { return m_type; }
71 public List<LocalVariable> getVariables() { return Collections.unmodifiableList(m_locals); }
72 public List<Location> getLocations() { return Collections.unmodifiableList(m_locations); }
75 public void endContents(String uri, String localName, String qName, String chars)
76 throws XmlParseException
82 public void complete()
88 public void endExternal(String uri, String localName, String qName)
89 throws XmlParseException
91 if (BugClass.TAG.equals(localName)) {
92 ParseResult[] collected = collectParsedChildren(BugClass.class);
93 for (ParseResult pr : collected) {
94 assert(pr instanceof BugClass);
95 m_classes.add((BugClass) pr);
98 else if (BugMethod.TAG.equals(localName)) {
99 ParseResult[] collected = collectParsedChildren(BugMethod.class);
100 for (ParseResult pr : collected) {
101 assert(pr instanceof BugMethod);
102 m_methods.add((BugMethod)pr);
105 else if (LocalVariable.TAG.equals(localName)) {
106 ParseResult[] collected = collectParsedChildren(LocalVariable.class);
107 for (ParseResult pr : collected) {
108 assert(pr instanceof LocalVariable);
109 m_locals.add((LocalVariable)pr);
112 else if (SourceLine.TAG.equals(localName)) {
113 ParseResult[] collected = collectParsedChildren(SourceLine.class);
114 for (ParseResult pr : collected) {
115 assert(pr instanceof SourceLine);
116 m_lines.add((SourceLine)pr);
122 public void handleMainAttributes(Attributes attr) throws MissingAttributeException
124 m_type = this.getRequiredAttr(TAG, attr, TYPE);
125 m_category = this.getRequiredAttr(TAG, attr, CATEGORY);
129 public void dump(PrintWriter pw, int indent)
131 int childIndent = indent + 2;
132 String margin = String.format("%" + indent + "s", "");
134 pw.println(margin + TAG + " (" + m_type + ")");
135 pw.println(margin + CATEGORY + " (" + m_category + ")");
136 for (BugClass bc : m_classes) {
137 bc.dump(pw, childIndent);
139 for (BugMethod bm : m_methods) {
140 bm.dump(pw, childIndent);
142 for (LocalVariable lv : m_locals) {
144 lv.dump(pw, childIndent);
147 for (SourceLine sl : m_lines) {
148 sl.dump(pw, childIndent);
150 for (Location loc : m_locations) {
152 loc.dump(pw, childIndent);
157 // Note that this is a heuristic, "fuzzy", equals.
158 // Two BugInstances will be considered equal if:
159 // - they refer to the same bug type
160 // - they took place in the same class and method
161 // - the variable names referred to (if any) match
162 // In particular, this equality test does not check
163 // for line numbers being equal. This is by design;
164 // we want to consider two bugs to be the "same bug"
165 // even if other changes in the file have shifted
166 // line numbers a bit.
168 public boolean equals(Object obj)
173 if (obj instanceof BugInstance) {
174 BugInstance that = (BugInstance)obj;
176 if (! Util.objsAreEqual(this.m_type, that.m_type)) {
180 if (! Util.objsAreEqual(this.m_category, that.m_category)) {
184 Location thisLoc = this.getPrincipalLocation();
185 Location thatLoc = that.getPrincipalLocation();
186 if (null == thisLoc) {
187 if (null == thatLoc) {
192 if (! thisLoc.fuzzyEquals(thatLoc)) {
203 public int hashCode()
205 int code = Util.objHashCode(m_type)
206 ^ Util.objHashCode(m_category)
207 ^ Util.objHashCode(getPrincipalLocation());
211 // Get the "principal" Location.
212 // This should be the place where the bug is reported.
213 Location getPrincipalLocation()
215 if (null == m_locations) {
219 for (int idx = 0; idx < m_locations.size(); ++idx) {
220 Location loc = m_locations.get(idx);
221 if (Location.METHOD_CALLED.equals(loc.getMethodRole())) {
222 // METHOD_CALLED locations describe the method that is being called,
223 // but the bug is located in the caller, not in the callee.
224 // Thus, ignore this information about the callee.
233 private void computeLocations()
235 assert(null != m_classes);
236 assert(null != m_methods);
237 assert(null != m_lines);
242 Somewhat unfortunate special case.
243 The primary "location" for a bug instance is split between tags.
244 Most bugs have a pattern like this:
254 The primary location for a bug is given by the <Method> with no role attribute,
255 but the <SourceLine/> inside that method describes the whole range of lines
256 covered by that Method, not the spot where the bug is located--that is given
257 by the <SourceLine/> that is a direct child fo the <BugInstance/>.
260 BugMethod primaryMethod = null;
261 SourceLine primaryLine = null;
263 for (BugMethod method : m_methods) {
264 if (null != method.getRole()) {
265 primaryMethod = method;
269 if (m_lines.size() > 0) {
270 primaryLine = m_lines.get(0);
273 if ((null != primaryMethod) && (null != primaryLine)) {
274 m_locations.add(new Location(primaryMethod, primaryLine));
277 for (BugMethod method : m_methods) {
278 if (primaryMethod != method) {
279 m_locations.add(new Location(method));
282 for (SourceLine line : m_lines) {
283 if (primaryLine != line) {
284 m_locations.add(new Location(line));
287 for (BugClass clazz : m_classes) {
288 m_locations.add(new Location(clazz));