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 && m_locations.size() > 0) {
216 return m_locations.get(0);
221 private void computeLocations()
223 assert(null != m_classes);
224 assert(null != m_methods);
225 assert(null != m_lines);
230 Somewhat unfortunate special case.
231 The primary "location" for a bug instance is split between tags.
232 Most bugs have a pattern like this:
242 The primary location for a bug is given by the <Method> with no role attribute,
243 but the <SourceLine/> inside that method describes the whole range of lines
244 covered by that Method, not the spot where the bug is located--that is given
245 by the <SourceLine/> that is a direct child fo the <BugInstance/>.
248 BugMethod primaryMethod = null;
249 SourceLine primaryLine = null;
251 for (BugMethod method : m_methods) {
252 if (null != method.getRole()) {
253 primaryMethod = method;
257 if (m_lines.size() > 0) {
258 primaryLine = m_lines.get(0);
261 if ((null != primaryMethod) && (null != primaryLine)) {
262 m_locations.add(new Location(primaryMethod, primaryLine));
265 for (BugMethod method : m_methods) {
266 if (primaryMethod != method) {
267 m_locations.add(new Location(method));
270 for (SourceLine line : m_lines) {
271 if (primaryLine != line) {
272 m_locations.add(new Location(line));
275 for (BugClass clazz : m_classes) {
276 m_locations.add(new Location(clazz));