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 { Field.TAG, Field.class },
24 { LocalVariable.TAG, LocalVariable.class },
25 { SourceLine.TAG, SourceLine.class } };
26 static final String CATEGORY = "category";
27 static final String TYPE = "type";
32 ArrayList<BugClass> m_classes;
33 ArrayList<BugMethod> m_methods;
34 ArrayList<Variable> m_vars;
35 ArrayList<SourceLine> m_lines;
36 ArrayList<Location> m_locations;
38 public BugInstance() {
39 super(TAG, INTERNAL, EXTERNAL);
42 m_category = m_type = null;
43 m_classes = new ArrayList<BugClass>();
44 m_methods = new ArrayList<BugMethod>();
45 m_vars = new ArrayList<Variable>();
46 m_lines = new ArrayList<SourceLine>();
47 m_locations = new ArrayList<Location>();
50 public BugInstance(Long id,
56 super(TAG, INTERNAL, EXTERNAL);
59 m_category = category;
62 m_classes = new ArrayList<BugClass>();
63 m_methods = new ArrayList<BugMethod>();
64 m_lines = new ArrayList<SourceLine>();
66 m_locations = new ArrayList<Location>(Arrays.asList(locations));
67 m_vars = new ArrayList<Variable>(Arrays.asList(variables));
70 public String getCategory() { return m_category; }
71 public String getType() { return m_type; }
72 public List<Variable> getVariables() { return Collections.unmodifiableList(m_vars); }
73 public List<Location> getLocations() { return Collections.unmodifiableList(m_locations); }
76 public void endContents(String uri, String localName, String qName, String chars)
77 throws XmlParseException
83 public void complete()
89 public void endExternal(String uri, String localName, String qName)
90 throws XmlParseException
92 if (BugClass.TAG.equals(localName)) {
93 ParseResult[] collected = collectParsedChildren(BugClass.class);
94 for (ParseResult pr : collected) {
95 assert(pr instanceof BugClass);
96 m_classes.add((BugClass) pr);
99 else if (BugMethod.TAG.equals(localName)) {
100 ParseResult[] collected = collectParsedChildren(BugMethod.class);
101 for (ParseResult pr : collected) {
102 assert(pr instanceof BugMethod);
103 m_methods.add((BugMethod)pr);
106 else if (Field.TAG.equals(localName)) {
107 ParseResult[] collected = collectParsedChildren(Field.class);
108 for (ParseResult pr : collected) {
109 assert(pr instanceof Field);
110 m_vars.add((Field)pr);
113 else if (LocalVariable.TAG.equals(localName)) {
114 ParseResult[] collected = collectParsedChildren(LocalVariable.class);
115 for (ParseResult pr : collected) {
116 assert(pr instanceof LocalVariable);
117 m_vars.add((LocalVariable)pr);
120 else if (SourceLine.TAG.equals(localName)) {
121 ParseResult[] collected = collectParsedChildren(SourceLine.class);
122 for (ParseResult pr : collected) {
123 assert(pr instanceof SourceLine);
124 m_lines.add((SourceLine)pr);
130 public void handleMainAttributes(Attributes attr) throws MissingAttributeException
132 m_type = this.getRequiredAttr(TAG, attr, TYPE);
133 m_category = this.getRequiredAttr(TAG, attr, CATEGORY);
137 public void dump(PrintWriter pw, int indent)
139 int childIndent = indent + 2;
140 String margin = String.format("%" + indent + "s", "");
142 pw.println(margin + TAG + " (" + m_type + ")");
143 pw.println(margin + CATEGORY + " (" + m_category + ")");
144 for (BugClass bc : m_classes) {
145 bc.dump(pw, childIndent);
147 for (BugMethod bm : m_methods) {
148 bm.dump(pw, childIndent);
150 for (Variable var : m_vars) {
151 pw.println(margin + " " + var.getDescription());
153 for (SourceLine sl : m_lines) {
154 sl.dump(pw, childIndent);
156 for (Location loc : m_locations) {
158 loc.dump(pw, childIndent);
163 // Note that this is a heuristic, "fuzzy", equals.
164 // Two BugInstances will be considered equal if:
165 // - they refer to the same bug type
166 // - they took place in the same class and method
167 // - the variable names referred to (if any) match
168 // In particular, this equality test does not check
169 // for line numbers being equal. This is by design;
170 // we want to consider two bugs to be the "same bug"
171 // even if other changes in the file have shifted
172 // line numbers a bit.
174 public boolean equals(Object obj)
179 if (obj instanceof BugInstance) {
180 BugInstance that = (BugInstance)obj;
182 if (! Util.objsAreEqual(this.m_type, that.m_type)) {
186 if (! Util.objsAreEqual(this.m_category, that.m_category)) {
190 Location thisLoc = this.getPrincipalLocation();
191 Location thatLoc = that.getPrincipalLocation();
192 if (null == thisLoc) {
193 if (null == thatLoc) {
198 if (! thisLoc.fuzzyEquals(thatLoc)) {
209 public int hashCode()
211 int code = Util.objHashCode(m_type)
212 ^ Util.objHashCode(m_category)
213 ^ Util.objHashCode(getPrincipalLocation());
217 // Get the "principal" Location.
218 // This should be the place where the bug is reported.
219 Location getPrincipalLocation()
221 if (null == m_locations) {
225 for (int idx = 0; idx < m_locations.size(); ++idx) {
226 Location loc = m_locations.get(idx);
227 if (Location.METHOD_CALLED.equals(loc.getMethodRole())) {
228 // METHOD_CALLED locations describe the method that is being called,
229 // but the bug is located in the caller, not in the callee.
230 // Thus, ignore this information about the callee.
239 private void computeLocations()
241 assert(null != m_classes);
242 assert(null != m_methods);
243 assert(null != m_lines);
248 Somewhat unfortunate special case.
249 The primary "location" for a bug instance is split between tags.
250 Most bugs have a pattern like this:
260 The primary location for a bug is given by the <Method> with no role attribute,
261 but the <SourceLine/> inside that method describes the whole range of lines
262 covered by that Method, not the spot where the bug is located--that is given
263 by the <SourceLine/> that is a direct child fo the <BugInstance/>.
266 BugMethod primaryMethod = null;
267 SourceLine primaryLine = null;
269 for (BugMethod method : m_methods) {
270 if (null != method.getRole()) {
271 primaryMethod = method;
275 if (m_lines.size() > 0) {
276 primaryLine = m_lines.get(0);
279 if ((null != primaryMethod) && (null != primaryLine)) {
280 m_locations.add(new Location(primaryMethod, primaryLine));
283 for (BugMethod method : m_methods) {
284 if (primaryMethod != method) {
285 m_locations.add(new Location(method));
288 for (SourceLine line : m_lines) {
289 if (primaryLine != line) {
290 m_locations.add(new Location(line));
293 for (BugClass clazz : m_classes) {
294 m_locations.add(new Location(clazz));