KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > umd > cs > findbugs > SAXBugCollectionHandler


1 /*
2  * FindBugs - Find bugs in Java programs
3  * Copyright (C) 2004-2005 University of Maryland
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */

19
20 package edu.umd.cs.findbugs;
21
22 import java.io.FileReader JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.regex.Pattern JavaDoc;
25
26 import org.xml.sax.Attributes JavaDoc;
27 import org.xml.sax.InputSource JavaDoc;
28 import org.xml.sax.SAXException JavaDoc;
29 import org.xml.sax.XMLReader JavaDoc;
30 import org.xml.sax.helpers.DefaultHandler JavaDoc;
31 import org.xml.sax.helpers.XMLReaderFactory JavaDoc;
32
33 import edu.umd.cs.findbugs.ba.ClassHash;
34 import edu.umd.cs.findbugs.model.ClassFeatureSet;
35
36 /**
37  * Build a BugCollection based on SAX events.
38  * This is intended to replace the old DOM-based parsing
39  * of XML bug result files, which was very slow.
40  *
41  * @author David Hovemeyer
42  */

43 public class SAXBugCollectionHandler extends DefaultHandler JavaDoc {
44     private BugCollection bugCollection;
45     private Project project;
46
47     private ArrayList JavaDoc<String JavaDoc> elementStack;
48     private StringBuffer JavaDoc textBuffer;
49     private BugInstance bugInstance;
50     private PackageMemberAnnotation packageMemberAnnotation;
51     private AnalysisError analysisError;
52 // private ClassHash classHash;
53
private ClassFeatureSet classFeatureSet;
54     private ArrayList JavaDoc<String JavaDoc> stackTrace;
55     private int nestingOfIgnoredElements = 0;
56
57     public SAXBugCollectionHandler(BugCollection bugCollection, Project project) {
58         this.bugCollection = bugCollection;
59         this.project = project;
60
61         this.elementStack = new ArrayList JavaDoc<String JavaDoc>();
62         this.textBuffer = new StringBuffer JavaDoc();
63         this.stackTrace = new ArrayList JavaDoc<String JavaDoc>();
64     }
65
66     Pattern JavaDoc ignoredElement = Pattern.compile("Message|ShortMessage|LongMessage|BugCategory|BugPattern|BugCode");
67     
68     public boolean discardedElement(String JavaDoc qName) {
69         return ignoredElement.matcher(qName).matches();
70         
71     }
72
73     @Override JavaDoc
74     public void startElement(String JavaDoc uri, String JavaDoc name, String JavaDoc qName, Attributes JavaDoc attributes)
75         throws SAXException JavaDoc {
76         // URI should always be empty.
77
// So, qName is the name of the element.
78

79         if (discardedElement(qName)) {
80             nestingOfIgnoredElements++;
81         } else if (nestingOfIgnoredElements > 0) {
82             // ignore it
83
} else if (elementStack.isEmpty()) {
84             // We should be parsing the outer BugCollection element.
85
if (!qName.equals("BugCollection"))
86                 throw new SAXException JavaDoc(
87                         "Invalid top-level element (expected BugCollection, saw " + qName + ")");
88             
89             // Read and set the sequence number.
90
String JavaDoc sequence = attributes.getValue("sequence");
91             long seqval = parseLong(sequence, 0L);
92             bugCollection.setSequenceNumber(seqval);
93             
94             // Read and set timestamp.
95
String JavaDoc timestamp = attributes.getValue("timestamp");
96             long tsval = parseLong(timestamp, -1L);
97             bugCollection.setTimestamp(tsval);
98             
99             // Set release name, if present.
100
String JavaDoc releaseName = attributes.getValue("release");
101             bugCollection.setReleaseName((releaseName != null) ? releaseName : "");
102         } else {
103             String JavaDoc outerElement = elementStack.get(elementStack.size() - 1);
104
105             if (outerElement.equals("BugCollection")) {
106                 // Parsing a top-level element of the BugCollection
107
if (qName.equals("Project")) {
108                     // Project element
109
String JavaDoc filename = attributes.getValue(Project.FILENAME_ATTRIBUTE_NAME);
110                     if (filename != null)
111                         project.setProjectFileName(filename);
112                 } else if (qName.equals("BugInstance")) {
113                     // BugInstance element - get required type and priority attributes
114
String JavaDoc type = getRequiredAttribute(attributes, "type", qName);
115                     String JavaDoc priority = getRequiredAttribute(attributes, "priority", qName);
116
117                     try {
118                         int prio = Integer.parseInt(priority);
119                         bugInstance = new BugInstance(type, prio);
120                     } catch (NumberFormatException JavaDoc e) {
121                         throw new SAXException JavaDoc("BugInstance with invalid priority value \"" +
122                             priority + "\"", e);
123                     }
124                     
125                     String JavaDoc uniqueId = attributes.getValue("uid");
126                     if (uniqueId != null) {
127                         bugInstance.setUniqueId(uniqueId);
128                     }
129
130                     String JavaDoc firstVersion = attributes.getValue("first");
131                     if (firstVersion != null) {
132                         bugInstance.setFirstVersion(Long.parseLong(firstVersion));
133                     }
134                     String JavaDoc lastVersion = attributes.getValue("last");
135                     if (lastVersion != null) {
136                         bugInstance.setLastVersion(Long.parseLong(lastVersion));
137                     }
138                     
139                     if (bugInstance.getLastVersion() >= 0 &&
140                             bugInstance.getFirstVersion() > bugInstance.getLastVersion())
141                         throw new IllegalStateException JavaDoc("huh");
142                     
143                     String JavaDoc introducedByChange = attributes.getValue("introducedByChange");
144                     if (introducedByChange != null) {
145                         bugInstance.setIntroducedByChangeOfExistingClass(TigerSubstitutes.parseBoolean(introducedByChange));
146                     }
147                     String JavaDoc removedByChange = attributes.getValue("removedByChange");
148                     if (removedByChange != null) {
149                         bugInstance.setRemovedByChangeOfPersistingClass(TigerSubstitutes.parseBoolean(removedByChange));
150                     }
151                     String JavaDoc oldInstanceHash = attributes.getValue("instanceHash");
152                     if (oldInstanceHash != null) {
153                         bugInstance.setOldInstanceHash(oldInstanceHash);
154                         }
155                     
156                     
157                 } else if (qName.equals("FindBugsSummary")) {
158                     String JavaDoc timestamp = getRequiredAttribute(attributes, "timestamp", qName);
159                     try {
160                         bugCollection.getProjectStats().setTimestamp(timestamp);
161                     } catch (java.text.ParseException JavaDoc e) {
162                         throw new SAXException JavaDoc("Unparseable sequence number: '" + timestamp + "'", e);
163                     }
164                 }
165             } else if (outerElement.equals("BugInstance")) {
166                 // Parsing an attribute or property of a BugInstance
167
BugAnnotation bugAnnotation = null;
168                 if (qName.equals("Class")) {
169                     String JavaDoc className = getRequiredAttribute(attributes, "classname", qName);
170                     bugAnnotation = packageMemberAnnotation = new ClassAnnotation(className);
171                 } else if (qName.equals("Type")) {
172                     String JavaDoc typeDescriptor = getRequiredAttribute(attributes, "descriptor", qName);
173                     bugAnnotation = new TypeAnnotation(typeDescriptor);
174                 } else if (qName.equals("Method") || qName.equals("Field")) {
175                     String JavaDoc classname = getRequiredAttribute(attributes, "classname", qName);
176                     String JavaDoc fieldOrMethodName = getRequiredAttribute(attributes, "name", qName);
177                     String JavaDoc signature = getRequiredAttribute(attributes, "signature", qName);
178                     if (qName.equals("Method")) {
179                         String JavaDoc isStatic = attributes.getValue("isStatic");
180                         if (isStatic == null) {
181                             isStatic = "false"; // Hack for old data
182
}
183
184                         bugAnnotation = packageMemberAnnotation =
185                             new MethodAnnotation(classname, fieldOrMethodName, signature, Boolean.valueOf(isStatic));
186
187                     } else {
188                         String JavaDoc isStatic = getRequiredAttribute(attributes, "isStatic", qName);
189                         bugAnnotation = packageMemberAnnotation =
190                             new FieldAnnotation(classname, fieldOrMethodName, signature, Boolean.valueOf(isStatic));
191                     }
192                     
193                 } else if (qName.equals("SourceLine")) {
194                     SourceLineAnnotation sourceAnnotation = createSourceLineAnnotation(qName, attributes);
195                     if (!sourceAnnotation.isSynthetic())
196                         bugAnnotation = sourceAnnotation;
197                 } else if (qName.equals("Int")) {
198                     try {
199                         String JavaDoc value = getRequiredAttribute(attributes, "value", qName);
200                         bugAnnotation = new IntAnnotation(Integer.parseInt(value));
201                     } catch (NumberFormatException JavaDoc e) {
202                         throw new SAXException JavaDoc("Bad integer value in Int");
203                     }
204                 } else if (qName.equals("String")) {
205                         String JavaDoc value = getRequiredAttribute(attributes, "value", qName);
206                         bugAnnotation = new StringAnnotation(value);
207                 } else if (qName.equals("LocalVariable")) {
208                     try {
209                         String JavaDoc varName = getRequiredAttribute(attributes, "name", qName);
210                         int register = Integer.parseInt(getRequiredAttribute(attributes, "register", qName));
211                         int pc = Integer.parseInt(getRequiredAttribute(attributes, "pc", qName));
212                         bugAnnotation = new LocalVariableAnnotation(varName, register, pc);
213                     } catch (NumberFormatException JavaDoc e) {
214                         throw new SAXException JavaDoc("Invalid integer value in attribute of LocalVariable element");
215                     }
216                 } else if (qName.equals("Property")) {
217                     // A BugProperty.
218
String JavaDoc propName = getRequiredAttribute(attributes, "name", qName);
219                     String JavaDoc propValue = getRequiredAttribute(attributes, "value", qName);
220                     bugInstance.setProperty(propName, propValue);
221                 } else if (qName.equals("UserAnnotation")) {
222                     // ignore AnnotationText for now; will handle in endElement
223
String JavaDoc s = attributes.getValue("designation"); // optional
224
BugDesignation userDesignation = bugInstance.getNonnullUserDesignation();
225                     if (s != null) userDesignation.setDesignationKey(s);
226                     s = attributes.getValue("user"); // optional
227
if (s != null) userDesignation.setUser(s);
228                     s = attributes.getValue("timestamp"); // optional
229
if (s != null) try {
230                         long timestamp = Long.valueOf(s);
231                         userDesignation.setTimestamp(timestamp);
232                     }
233                     catch (NumberFormatException JavaDoc nfe) {
234                         // ok to contine -- just won't set a timestamp for the user designation.
235
// but is there anyplace to report this?
236
}
237                 } else throw new SAXException JavaDoc("Unknown bug annotation named " + qName);
238
239                 if (bugAnnotation != null) {
240                     String JavaDoc role = attributes.getValue("role");
241                     if (role != null)
242                         bugAnnotation.setDescription(role);
243                     setAnnotationRole(attributes, bugAnnotation);
244                     bugInstance.add(bugAnnotation);
245                 }
246             } else if (outerElement.equals("Method") || outerElement.equals("Field") || outerElement.equals("Class")) {
247                 if (qName.equals("SourceLine")) {
248                     // package member elements can contain nested SourceLine elements.
249
packageMemberAnnotation.setSourceLines(createSourceLineAnnotation(qName, attributes));
250                 }
251             } else if (outerElement.equals(BugCollection.ERRORS_ELEMENT_NAME)) {
252                 if (qName.equals(BugCollection.ANALYSIS_ERROR_ELEMENT_NAME) ||
253                         qName.equals(BugCollection.ERROR_ELEMENT_NAME)) {
254                     analysisError = new AnalysisError("Unknown error");
255                     stackTrace.clear();
256                 }
257             } else if (outerElement.equals("PackageStats")) {
258                 if (qName.equals("ClassStats")) {
259                     String JavaDoc className = getRequiredAttribute(attributes, "class", qName);
260                     Boolean JavaDoc isInterface = Boolean.valueOf(
261                         getRequiredAttribute(attributes, "interface", qName));
262                     int size = Integer.valueOf(
263                         getRequiredAttribute(attributes, "size", qName));
264                     bugCollection.getProjectStats().addClass(className, isInterface, size);
265                 }
266             } else if (outerElement.equals("ClassFeatures")) {
267                 if (qName.equals(ClassFeatureSet.ELEMENT_NAME)) {
268                     String JavaDoc className = getRequiredAttribute(attributes, "class", qName);
269                     classFeatureSet = new ClassFeatureSet();
270                     classFeatureSet.setClassName(className);
271                 }
272             } else if (outerElement.equals(ClassFeatureSet.ELEMENT_NAME)) {
273                 if (qName.equals(ClassFeatureSet.FEATURE_ELEMENT_NAME)) {
274                     String JavaDoc value = getRequiredAttribute(attributes, "value", qName);
275                     classFeatureSet.addFeature(value);
276                 }
277             } else if (outerElement.equals(BugCollection.HISTORY_ELEMENT_NAME)) {
278                 if (qName.equals(AppVersion.ELEMENT_NAME)) {
279                     try {
280                         String JavaDoc sequence = getRequiredAttribute(attributes, "sequence", qName);
281                         String JavaDoc timestamp = attributes.getValue("timestamp");
282                         String JavaDoc releaseName = attributes.getValue("release");
283                         String JavaDoc codeSize = attributes.getValue("codeSize");
284                         String JavaDoc numClasses = attributes.getValue("numClasses");
285                         AppVersion appVersion = new AppVersion(Long.valueOf(sequence));
286                         if (timestamp != null)
287                             appVersion.setTimestamp(Long.valueOf(timestamp));
288                         if (releaseName != null)
289                             appVersion.setReleaseName(releaseName);
290                         if (codeSize != null)
291                             appVersion.setCodeSize(Integer.parseInt(codeSize));
292                         if (numClasses != null)
293                             appVersion.setNumClasses(Integer.parseInt(numClasses));
294                         
295                         bugCollection.addAppVersion(appVersion);
296                     } catch (NumberFormatException JavaDoc e) {
297                         throw new SAXException JavaDoc("Invalid AppVersion element", e);
298                     }
299                 }
300             }
301         }
302
303         textBuffer.delete(0, textBuffer.length());
304         elementStack.add(qName);
305     }
306
307     private long parseLong(String JavaDoc s, long defaultValue) {
308         long value;
309         try {
310             value = (s != null) ? Long.parseLong(s) : defaultValue;
311         } catch (NumberFormatException JavaDoc e) {
312             value = defaultValue;
313         }
314         return value;
315     }
316
317     /**
318      * Extract a hash value from an element.
319      *
320      * @param qName name of element containing hash value
321      * @param attributes element attributes
322      * @return the decoded hash value
323      * @throws SAXException
324      */

325     private byte[] extractHash(String JavaDoc qName, Attributes JavaDoc attributes) throws SAXException JavaDoc {
326         String JavaDoc encodedHash = getRequiredAttribute(attributes, "value", qName);
327         byte[] hash;
328         try {
329             //System.out.println("Extract hash " + encodedHash);
330
hash= ClassHash.stringToHash(encodedHash);
331         } catch (IllegalArgumentException JavaDoc e) {
332             throw new SAXException JavaDoc("Invalid class hash", e);
333         }
334         return hash;
335     }
336
337     private void setAnnotationRole(Attributes JavaDoc attributes, BugAnnotation bugAnnotation) {
338         String JavaDoc role = attributes.getValue("role");
339         if (role != null)
340             bugAnnotation.setDescription(role);
341     }
342
343     private SourceLineAnnotation createSourceLineAnnotation(String JavaDoc qName, Attributes JavaDoc attributes)
344             throws SAXException JavaDoc {
345         String JavaDoc classname = getRequiredAttribute(attributes, "classname", qName);
346         String JavaDoc sourceFile = attributes.getValue("sourcefile");
347         if (sourceFile == null)
348             sourceFile = SourceLineAnnotation.UNKNOWN_SOURCE_FILE;
349         String JavaDoc startLine = attributes.getValue("start"); // "start"/"end" are now optional
350
String JavaDoc endLine = attributes.getValue("end"); // (were too many "-1"s in the xml)
351
String JavaDoc startBytecode = attributes.getValue("startBytecode");
352         String JavaDoc endBytecode = attributes.getValue("endBytecode");
353
354         try {
355             int sl = startLine != null ? Integer.parseInt(startLine) : -1;
356             int el = endLine != null ? Integer.parseInt(endLine) : -1;
357             int sb = startBytecode != null ? Integer.parseInt(startBytecode) : -1;
358             int eb = endBytecode != null ? Integer.parseInt(endBytecode) : -1;
359
360             SourceLineAnnotation annotation =
361                 new SourceLineAnnotation(classname, sourceFile, sl, el, sb, eb);
362             
363             return annotation;
364         } catch (NumberFormatException JavaDoc e) {
365             throw new SAXException JavaDoc("Bad integer value in SourceLine element", e);
366         }
367     }
368
369
370     @Override JavaDoc
371     public void endElement(String JavaDoc uri, String JavaDoc name, String JavaDoc qName) throws SAXException JavaDoc {
372         // URI should always be empty.
373
// So, qName is the name of the element.
374

375         if (discardedElement(qName)) {
376             nestingOfIgnoredElements--;
377         } else if (nestingOfIgnoredElements > 0) {
378             // ignore it
379
} else if (elementStack.size() > 1) {
380             String JavaDoc outerElement = elementStack.get(elementStack.size() - 2);
381
382             if (outerElement.equals("BugCollection")) {
383                 if (qName.equals("BugInstance")) {
384                     bugCollection.add(bugInstance, false);
385                    // TODO: check this
386
if (bugInstance.getLastVersion() == -1)
387                         bugCollection.getProjectStats().addBug(bugInstance);
388                 }
389             } else if (outerElement.equals("Project")) {
390                 //System.out.println("Adding project element " + qName + ": " + textBuffer.toString());
391
if (qName.equals("Jar"))
392                     project.addFile(textBuffer.toString());
393                 else if (qName.equals("SrcDir"))
394                     project.addSourceDir(textBuffer.toString());
395                 else if (qName.equals("AuxClasspathEntry"))
396                     project.addAuxClasspathEntry(textBuffer.toString());
397             } else if (outerElement.equals("BugInstance")) {
398                 if (qName.equals("UserAnnotation")) {
399                     bugInstance.setAnnotationText(textBuffer.toString());
400                 }
401             } else if (outerElement.equals(BugCollection.ERRORS_ELEMENT_NAME)) {
402                 if (qName.equals(BugCollection.ANALYSIS_ERROR_ELEMENT_NAME)) {
403                     analysisError.setMessage(textBuffer.toString());
404                     bugCollection.addError(analysisError);
405                 } else if (qName.equals(BugCollection.ERROR_ELEMENT_NAME)) {
406                     if (stackTrace.size() > 0) {
407                         analysisError.setStackTrace(stackTrace.toArray(new String JavaDoc[stackTrace.size()]));
408                     }
409                     bugCollection.addError(analysisError);
410                 } else if (qName.equals(BugCollection.MISSING_CLASS_ELEMENT_NAME)) {
411                     bugCollection.addMissingClass(textBuffer.toString());
412                 }
413                 
414             } else if (outerElement.equals(BugCollection.ERROR_ELEMENT_NAME)) {
415                 if (qName.equals(BugCollection.ERROR_MESSAGE_ELEMENT_NAME)) {
416                     analysisError.setMessage(textBuffer.toString());
417                 } else if (qName.equals(BugCollection.ERROR_EXCEPTION_ELEMENT_NAME)) {
418                     analysisError.setExceptionMessage(textBuffer.toString());
419                 } else if (qName.equals(BugCollection.ERROR_STACK_TRACE_ELEMENT_NAME)) {
420                     stackTrace.add(textBuffer.toString());
421                 }
422             } else if (outerElement.equals("ClassFeatures")) {
423                 if (qName.equals(ClassFeatureSet.ELEMENT_NAME)) {
424                     bugCollection.setClassFeatureSet(classFeatureSet);
425                     classFeatureSet = null;
426                 }
427             }
428         }
429
430         elementStack.remove(elementStack.size() - 1);
431     }
432
433     @Override JavaDoc
434     public void characters(char[] ch, int start, int length) {
435         textBuffer.append(ch, start, length);
436     }
437
438     private static String JavaDoc getRequiredAttribute(Attributes JavaDoc attributes, String JavaDoc attrName, String JavaDoc elementName)
439         throws SAXException JavaDoc {
440         String JavaDoc value = attributes.getValue(attrName);
441         if (value == null)
442             throw new SAXException JavaDoc(elementName + " element missing " + attrName + " attribute");
443         return value;
444     }
445
446     // Just a test driver
447
public static void main(String JavaDoc[] argv) throws Exception JavaDoc {
448         XMLReader JavaDoc xr = XMLReaderFactory.createXMLReader();
449
450         BugCollection bugCollection = new SortedBugCollection();
451         Project project = new Project();
452
453         SAXBugCollectionHandler handler = new SAXBugCollectionHandler(bugCollection, project);
454         xr.setContentHandler(handler);
455         xr.setErrorHandler(handler);
456
457         // Parse each file provided on the
458
// command line.
459
for (String JavaDoc aArgv : argv) {
460             FileReader JavaDoc r = new FileReader JavaDoc(aArgv);
461             xr.parse(new InputSource JavaDoc(r));
462         }
463     }
464 }
465
466 // vim:ts=4
467
Popular Tags