Add computation of deltas (differences between Analysis runs).
[cfb.git] / prod / net / jaekl / cfb / xml / BugInstance.java
1 package net.jaekl.cfb.xml;
2
3 import java.io.PrintWriter;
4 import java.util.ArrayList;
5 import java.util.Collections;
6 import java.util.List;
7
8 import org.xml.sax.Attributes;
9
10 import net.jaekl.cfb.util.Util;
11 import net.jaekl.qd.xml.MissingAttributeException;
12 import net.jaekl.qd.xml.ParseResult;
13 import net.jaekl.qd.xml.XmlParseException;
14
15 public class BugInstance extends ParseResult {
16
17         static final String TAG = "BugInstance";
18         static final String[] INTERNAL = {  };
19         static final Object[][] EXTERNAL = { { BugClass.TAG, BugClass.class},
20                                                  { BugMethod.TAG, BugMethod.class},
21                                                  { LocalVariable.TAG, LocalVariable.class},
22                                                  { SourceLine.TAG, SourceLine.class} };
23         static final String CATEGORY = "category";
24         static final String TYPE = "type";
25
26         String m_category;
27         String m_type;
28         ArrayList<BugClass> m_classes;
29         ArrayList<BugMethod> m_methods;
30         ArrayList<LocalVariable> m_locals;
31         ArrayList<SourceLine> m_lines;
32         
33         public BugInstance() {
34                 super(TAG, INTERNAL, EXTERNAL);
35                 
36                 m_category = m_type = null;
37                 m_classes = new ArrayList<BugClass>();
38                 m_methods = new ArrayList<BugMethod>();
39                 m_locals = new ArrayList<LocalVariable>();
40                 m_lines = new ArrayList<SourceLine>();
41         }
42         
43         public String getCategory() { return m_category; }
44         public String getType() { return m_type; }
45         public List<BugClass> getClasses() { return Collections.unmodifiableList(m_classes); }
46         public List<BugMethod> getMethods() { return Collections.unmodifiableList(m_methods); }
47         public List<SourceLine> getLines() { return Collections.unmodifiableList(m_lines); }
48         public List<LocalVariable> getVariables() { return Collections.unmodifiableList(m_locals); }
49         
50         @Override
51         public void endContents(String uri, String localName, String qName, String chars) 
52                 throws XmlParseException 
53         {
54                 // no-op
55         }
56
57         @Override
58         public void endExternal(String uri, String localName, String qName)
59                 throws XmlParseException 
60         {
61                 if (BugClass.TAG.equals(localName)) {
62                         ParseResult[] collected = collectParsedChildren(BugClass.class);
63                         for (ParseResult pr : collected) {
64                                 assert(pr instanceof BugClass);
65                                 m_classes.add((BugClass) pr);
66                         }
67                 }
68                 else if (BugMethod.TAG.equals(localName)) {
69                         ParseResult[] collected = collectParsedChildren(BugMethod.class);
70                         for (ParseResult pr : collected) {
71                                 assert(pr instanceof BugMethod);
72                                 m_methods.add((BugMethod)pr);
73                         }                       
74                 }
75                 else if (LocalVariable.TAG.equals(localName)) {
76                         ParseResult[] collected = collectParsedChildren(LocalVariable.class);
77                         for (ParseResult pr : collected) {
78                                 assert(pr instanceof LocalVariable);
79                                 m_locals.add((LocalVariable)pr);
80                         }                       
81                 }
82                 else if (SourceLine.TAG.equals(localName)) {
83                         ParseResult[] collected = collectParsedChildren(SourceLine.class);
84                         for (ParseResult pr : collected) {
85                                 assert(pr instanceof SourceLine);
86                                 m_lines.add((SourceLine)pr);
87                         }
88                 }
89         }
90         
91         @Override
92         public void handleMainAttributes(Attributes attr) throws MissingAttributeException
93         {
94                 m_type = this.getRequiredAttr(TAG, attr, TYPE);
95                 m_category = this.getRequiredAttr(TAG, attr, CATEGORY);
96         }
97
98         @Override 
99         public void dump(PrintWriter pw, int indent)
100         {
101                 int childIndent = indent + 2;
102                 String margin = String.format("%" + indent + "s", "");
103                 
104                 pw.println(margin + TAG + " (" + m_type + ")");
105                 pw.println(margin + CATEGORY + " (" + m_category + ")");
106                 for (BugClass bc : m_classes) {
107                         bc.dump(pw, childIndent);
108                 }
109                 for (BugMethod bm : m_methods) {
110                         bm.dump(pw, childIndent);
111                 }
112                 for (LocalVariable lv : m_locals) {
113                         lv.dump(pw, childIndent);
114                 }
115                 for (SourceLine sl : m_lines) {
116                         sl.dump(pw, childIndent);
117                 }
118         }
119         
120         // Note that this is a heuristic, "fuzzy", equals.
121         // Two BugInstances will be considered equal if:
122         //   - they refer to the same bug type
123         //   - they took place in the same class and method
124         //   - the variable names referred to (if any) match
125         // In particular, this equality test does not check 
126         // for line numbers being equal.  This is by design;
127         // we want to consider two bugs to be the "same bug" 
128         // even if other changes in the file have shifted 
129         // line numbers a bit.
130         @Override
131         public boolean equals(Object obj) 
132         {
133                 if (null == obj) { 
134                         return false;
135                 }
136                 if (obj instanceof BugInstance) {
137                         BugInstance that = (BugInstance)obj;
138                         
139                         if (! Util.objsAreEqual(this.m_type, that.m_type)) {
140                                 return false;
141                         }
142                         
143                         if (! Util.objsAreEqual(this.m_category, that.m_category)) {
144                                 return false;
145                         }
146
147                         BugMethod thisMethod = this.getPrincipalMethod();
148                         BugMethod thatMethod = that.getPrincipalMethod();
149                         if (null == thisMethod) {
150                                 if (null == thatMethod) {
151                                         return false;
152                                 }
153                         }
154                         else {
155                                 if (! Util.objsAreEqual(thisMethod.getClassName(), thatMethod.getClassName())) {
156                                         return false;
157                                 }
158                                 if (! Util.objsAreEqual(thisMethod.getMethodName(), thatMethod.getMethodName())) {
159                                         return false;
160                                 }
161                         }
162                         
163                         if (! Util.objsAreEqual(this.getVariables(), that.getVariables())) {
164                                 return false;
165                         }
166                         
167                         return true;
168                 }
169                 return false;
170         }
171         
172         @Override
173         public int hashCode() 
174         {
175                 int code = Util.objHashCode(m_type)
176                                  * Util.objHashCode(m_category)
177                                  * Util.objHashCode(getPrincipalMethod())
178                                  * Util.objHashCode(getVariables());
179                 return code;
180         }
181         
182         // Get the "principal" method.
183         // This should be the place where the bug is reported.
184         BugMethod getPrincipalMethod()
185         {
186                 List<BugMethod> bugMethods = getMethods();
187                 if ((null == bugMethods) || (0 == bugMethods.size())) {
188                         return null;
189                 }
190                 return bugMethods.get(0);
191         }
192 }