001: /*
002: * FindBugs - Find bugs in Java programs
003: * Copyright (C) 2004-2005 University of Maryland
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019:
020: package edu.umd.cs.findbugs;
021:
022: import java.io.File;
023: import java.util.ArrayList;
024: import java.util.Stack;
025: import java.util.regex.Pattern;
026:
027: import org.xml.sax.Attributes;
028: import org.xml.sax.SAXException;
029: import org.xml.sax.helpers.DefaultHandler;
030:
031: import edu.umd.cs.findbugs.ba.ClassHash;
032: import edu.umd.cs.findbugs.filter.AndMatcher;
033: import edu.umd.cs.findbugs.filter.BugMatcher;
034: import edu.umd.cs.findbugs.filter.ClassMatcher;
035: import edu.umd.cs.findbugs.filter.CompoundMatcher;
036: import edu.umd.cs.findbugs.filter.DesignationMatcher;
037: import edu.umd.cs.findbugs.filter.FieldMatcher;
038: import edu.umd.cs.findbugs.filter.Filter;
039: import edu.umd.cs.findbugs.filter.FirstVersionMatcher;
040: import edu.umd.cs.findbugs.filter.LastVersionMatcher;
041: import edu.umd.cs.findbugs.filter.LocalMatcher;
042: import edu.umd.cs.findbugs.filter.Matcher;
043: import edu.umd.cs.findbugs.filter.MethodMatcher;
044: import edu.umd.cs.findbugs.filter.OrMatcher;
045: import edu.umd.cs.findbugs.filter.PriorityMatcher;
046: import edu.umd.cs.findbugs.model.ClassFeatureSet;
047: import edu.umd.cs.findbugs.util.Strings;
048:
049: /**
050: * Build a BugCollection based on SAX events.
051: * This is intended to replace the old DOM-based parsing
052: * of XML bug result files, which was very slow.
053: *
054: * @author David Hovemeyer
055: */
056: public class SAXBugCollectionHandler extends DefaultHandler {
057: /**
058: *
059: */
060: private static final String FIND_BUGS_FILTER = "FindBugsFilter";
061: /**
062: *
063: */
064: private static final String PROJECT = "Project";
065: /**
066: *
067: */
068: private static final String BUG_COLLECTION = "BugCollection";
069: private BugCollection bugCollection;
070: private Project project;
071: private Stack<CompoundMatcher> matcherStack = new Stack<CompoundMatcher>();
072: private Filter filter;
073:
074: private ArrayList<String> elementStack;
075: private StringBuffer textBuffer;
076: private BugInstance bugInstance;
077: private PackageMemberAnnotation packageMemberAnnotation;
078: private AnalysisError analysisError;
079: // private ClassHash classHash;
080: private ClassFeatureSet classFeatureSet;
081: private ArrayList<String> stackTrace;
082: private int nestingOfIgnoredElements = 0;
083: private final File base;
084: private final String topLevelName;
085:
086: public SAXBugCollectionHandler(String topLevelName,
087: BugCollection bugCollection, Project project, File base) {
088: this .topLevelName = topLevelName;
089: this .bugCollection = bugCollection;
090: this .project = project;
091:
092: this .elementStack = new ArrayList<String>();
093: this .textBuffer = new StringBuffer();
094: this .stackTrace = new ArrayList<String>();
095: this .base = base;
096:
097: }
098:
099: public SAXBugCollectionHandler(BugCollection bugCollection,
100: Project project, File base) {
101: this (BUG_COLLECTION, bugCollection, project, base);
102: }
103:
104: public SAXBugCollectionHandler(Project project, File base) {
105: this (PROJECT, null, project, base);
106: }
107:
108: public SAXBugCollectionHandler(Filter filter, File base) {
109: this (FIND_BUGS_FILTER, null, null, base);
110: this .filter = filter;
111: pushCompoundMatcher(filter);
112: }
113:
114: Pattern ignoredElement = Pattern
115: .compile("Message|ShortMessage|LongMessage");
116:
117: public boolean discardedElement(String qName) {
118: return ignoredElement.matcher(qName).matches();
119:
120: }
121:
122: private static boolean DEBUG = false;
123:
124: @Override
125: public void startElement(String uri, String name, String qName,
126: Attributes attributes) throws SAXException {
127: // URI should always be empty.
128: // So, qName is the name of the element.
129:
130: if (discardedElement(qName)) {
131: nestingOfIgnoredElements++;
132: } else if (nestingOfIgnoredElements > 0) {
133: // ignore it
134: } else {
135: // We should be parsing the outer BugCollection element.
136: if (elementStack.isEmpty() && !qName.equals(topLevelName))
137: throw new SAXException(
138: "Invalid top-level element (expected "
139: + topLevelName + ", saw " + qName + ")");
140:
141: if (qName.equals(BUG_COLLECTION)) {
142: // Read and set the sequence number.
143: String version = attributes.getValue("version");
144: if (bugCollection instanceof SortedBugCollection)
145: ((SortedBugCollection) bugCollection)
146: .setAnalysisVersion(version);
147:
148: // Read and set the sequence number.
149: String sequence = attributes.getValue("sequence");
150: long seqval = parseLong(sequence, 0L);
151: bugCollection.setSequenceNumber(seqval);
152:
153: // Read and set timestamp.
154: String timestamp = attributes.getValue("timestamp");
155: long tsval = parseLong(timestamp, -1L);
156: bugCollection.setTimestamp(tsval);
157: // Read and set timestamp.
158: String analysisTimestamp = attributes
159: .getValue("analysisTimestamp");
160: if (analysisTimestamp != null) {
161: bugCollection.setAnalysisTimestamp(parseLong(
162: analysisTimestamp, -1L));
163: }
164:
165: // Set release name, if present.
166: String releaseName = attributes.getValue("release");
167: bugCollection
168: .setReleaseName((releaseName != null) ? releaseName
169: : "");
170: } else if (isTopLevelFilter(qName)) {
171: if (project != null) {
172: filter = new Filter();
173: project.setSuppressionFilter(filter);
174: }
175: matcherStack.clear();
176: pushCompoundMatcher(filter);
177: } else if (qName.equals(PROJECT)) {
178: // Project element
179: String filename = attributes
180: .getValue(Project.FILENAME_ATTRIBUTE_NAME);
181: if (filename != null)
182: project.setProjectFileName(filename);
183: String projectName = attributes
184: .getValue(Project.PROJECTNAME_ATTRIBUTE_NAME);
185: if (projectName != null)
186: project.setProjectName(projectName);
187: } else {
188: String outerElement = elementStack.get(elementStack
189: .size() - 1);
190: if (outerElement.equals(BUG_COLLECTION)) {
191: // Parsing a top-level element of the BugCollection
192: if (qName.equals("BugInstance")) {
193: // BugInstance element - get required type and priority attributes
194: String type = getRequiredAttribute(attributes,
195: "type", qName);
196: String priority = getRequiredAttribute(
197: attributes, "priority", qName);
198:
199: try {
200: int prio = Integer.parseInt(priority);
201: bugInstance = new BugInstance(type, prio);
202: } catch (NumberFormatException e) {
203: throw new SAXException(
204: "BugInstance with invalid priority value \""
205: + priority + "\"", e);
206: }
207:
208: String uniqueId = attributes.getValue("uid");
209: if (uniqueId != null) {
210: bugInstance.setUniqueId(uniqueId);
211: }
212:
213: String firstVersion = attributes
214: .getValue("first");
215: if (firstVersion != null) {
216: bugInstance.setFirstVersion(Long
217: .parseLong(firstVersion));
218: }
219: String lastVersion = attributes
220: .getValue("last");
221: if (lastVersion != null) {
222: bugInstance.setLastVersion(Long
223: .parseLong(lastVersion));
224: }
225:
226: if (bugInstance.getLastVersion() >= 0
227: && bugInstance.getFirstVersion() > bugInstance
228: .getLastVersion())
229: throw new IllegalStateException("huh");
230:
231: String introducedByChange = attributes
232: .getValue("introducedByChange");
233: if (introducedByChange != null) {
234: bugInstance
235: .setIntroducedByChangeOfExistingClass(TigerSubstitutes
236: .parseBoolean(introducedByChange));
237: }
238: String removedByChange = attributes
239: .getValue("removedByChange");
240: if (removedByChange != null) {
241: bugInstance
242: .setRemovedByChangeOfPersistingClass(TigerSubstitutes
243: .parseBoolean(removedByChange));
244: }
245: String oldInstanceHash = attributes
246: .getValue("instanceHash");
247: if (oldInstanceHash != null) {
248: bugInstance
249: .setOldInstanceHash(oldInstanceHash);
250: }
251:
252: } else if (qName.equals("FindBugsSummary")) {
253: String timestamp = getRequiredAttribute(
254: attributes, "timestamp", qName);
255: try {
256: bugCollection.getProjectStats()
257: .setTimestamp(timestamp);
258: } catch (java.text.ParseException e) {
259: throw new SAXException(
260: "Unparseable sequence number: '"
261: + timestamp + "'", e);
262: }
263: }
264: } else if (outerElement.equals("BugInstance")) {
265: parseBugInstanceContents(qName, attributes);
266: } else if (outerElement.equals("Method")
267: || outerElement.equals("Field")
268: || outerElement.equals("Class")) {
269: if (qName.equals("SourceLine")) {
270: // package member elements can contain nested SourceLine elements.
271: packageMemberAnnotation
272: .setSourceLines(createSourceLineAnnotation(
273: qName, attributes));
274: }
275: } else if (outerElement
276: .equals(BugCollection.ERRORS_ELEMENT_NAME)) {
277: if (qName
278: .equals(BugCollection.ANALYSIS_ERROR_ELEMENT_NAME)
279: || qName
280: .equals(BugCollection.ERROR_ELEMENT_NAME)) {
281: analysisError = new AnalysisError(
282: "Unknown error");
283: stackTrace.clear();
284: }
285: } else if (outerElement.equals("PackageStats")) {
286: if (qName.equals("ClassStats")) {
287: String className = getRequiredAttribute(
288: attributes, "class", qName);
289: Boolean isInterface = Boolean
290: .valueOf(getRequiredAttribute(
291: attributes, "interface", qName));
292: int size = Integer
293: .valueOf(getRequiredAttribute(
294: attributes, "size", qName));
295: String sourceFile = attributes
296: .getValue("sourceFile");
297: bugCollection.getProjectStats().addClass(
298: className, sourceFile, isInterface,
299: size);
300: }
301:
302: } else if (isTopLevelFilter(outerElement)
303: || outerElement.equals("Match")
304: || outerElement.equals("And")
305: || outerElement.equals("Or")) {
306: parseMatcher(qName, attributes);
307: } else if (outerElement.equals("ClassFeatures")) {
308: if (qName.equals(ClassFeatureSet.ELEMENT_NAME)) {
309: String className = getRequiredAttribute(
310: attributes, "class", qName);
311: classFeatureSet = new ClassFeatureSet();
312: classFeatureSet.setClassName(className);
313: }
314: } else if (outerElement
315: .equals(ClassFeatureSet.ELEMENT_NAME)) {
316: if (qName
317: .equals(ClassFeatureSet.FEATURE_ELEMENT_NAME)) {
318: String value = getRequiredAttribute(attributes,
319: "value", qName);
320: classFeatureSet.addFeature(value);
321: }
322: } else if (outerElement
323: .equals(BugCollection.HISTORY_ELEMENT_NAME)) {
324: if (qName.equals(AppVersion.ELEMENT_NAME)) {
325: try {
326: String sequence = getRequiredAttribute(
327: attributes, "sequence", qName);
328: String timestamp = attributes
329: .getValue("timestamp");
330: String releaseName = attributes
331: .getValue("release");
332: String codeSize = attributes
333: .getValue("codeSize");
334: String numClasses = attributes
335: .getValue("numClasses");
336: AppVersion appVersion = new AppVersion(Long
337: .valueOf(sequence));
338: if (timestamp != null)
339: appVersion.setTimestamp(Long
340: .valueOf(timestamp));
341: if (releaseName != null)
342: appVersion.setReleaseName(releaseName);
343: if (codeSize != null)
344: appVersion.setCodeSize(Integer
345: .parseInt(codeSize));
346: if (numClasses != null)
347: appVersion.setNumClasses(Integer
348: .parseInt(numClasses));
349:
350: bugCollection.addAppVersion(appVersion);
351: } catch (NumberFormatException e) {
352: throw new SAXException(
353: "Invalid AppVersion element", e);
354: }
355: }
356: }
357: }
358: }
359:
360: textBuffer.delete(0, textBuffer.length());
361: elementStack.add(qName);
362: }
363:
364: private boolean isTopLevelFilter(String qName) {
365: return qName.equals(FIND_BUGS_FILTER)
366: || qName.equals("SuppressionFilter");
367: }
368:
369: private void addMatcher(Matcher m) {
370: if (m == null)
371: throw new IllegalArgumentException(
372: "matcher must not be null");
373:
374: CompoundMatcher peek = matcherStack.peek();
375: if (peek == null)
376: throw new NullPointerException("Top of stack is null");
377: peek.addChild(m);
378: if (nextMatchedIsDisabled) {
379: if (peek instanceof Filter)
380: ((Filter) peek).disable(m);
381: else
382: assert false;
383: nextMatchedIsDisabled = false;
384: }
385: }
386:
387: private void pushCompoundMatcherAsChild(CompoundMatcher m) {
388: addMatcher(m);
389: pushCompoundMatcher(m);
390: }
391:
392: private void pushCompoundMatcher(CompoundMatcher m) {
393: if (m == null)
394: throw new IllegalArgumentException(
395: "matcher must not be null");
396: matcherStack.push(m);
397: }
398:
399: boolean nextMatchedIsDisabled;
400:
401: private void parseMatcher(String qName, Attributes attributes)
402: throws SAXException {
403: if (DEBUG)
404: System.out.println(elementStack + " " + qName + " "
405: + matcherStack);
406: String disabled = attributes.getValue("disabled");
407: nextMatchedIsDisabled = "true".equals(disabled);
408: if (qName.equals("Bug")) {
409: addMatcher(new BugMatcher(attributes.getValue("code"),
410: attributes.getValue("pattern"), attributes
411: .getValue("category")));
412: } else if (qName.equals("Class")) {
413: addMatcher(new ClassMatcher(attributes.getValue("name")));
414: } else if (qName.equals("FirstVersion")) {
415: addMatcher(new FirstVersionMatcher(getRequiredAttribute(
416: attributes, "value", qName), getRequiredAttribute(
417: attributes, "relOp", qName)));
418: } else if (qName.equals("LastVersion")) {
419: addMatcher(new LastVersionMatcher(getRequiredAttribute(
420: attributes, "value", qName), getRequiredAttribute(
421: attributes, "relOp", qName)));
422: } else if (qName.equals("Designation")) {
423: addMatcher(new DesignationMatcher(getRequiredAttribute(
424: attributes, "designation", qName)));
425: } else if (qName.equals("BugCode")) {
426: addMatcher(new BugMatcher(attributes.getValue("name"), "",
427: ""));
428: } else if (qName.equals("Local")) {
429: addMatcher(new LocalMatcher(attributes.getValue("name")));
430: } else if (qName.equals("BugPattern")) {
431: addMatcher(new BugMatcher("", attributes.getValue("name"),
432: ""));
433: } else if (qName.equals("Priority")) {
434: addMatcher(new PriorityMatcher(attributes.getValue("value")));
435: } else if (qName.equals("Package")) {
436: String pName = attributes.getValue("name");
437: pName = pName.startsWith("~") ? pName : "~"
438: + Strings.replace(pName, ".", "\\.");
439: addMatcher(new ClassMatcher(pName + "\\.[^.]+"));
440: } else if (qName.equals("Method")) {
441: String name = attributes.getValue("name");
442: String params = attributes.getValue("params");
443: String returns = attributes.getValue("returns");
444: addMatcher(new MethodMatcher(name, params, returns));
445: } else if (qName.equals("Field")) {
446: String name = attributes.getValue("name");
447: String type = attributes.getValue("type");
448: addMatcher(new FieldMatcher(name, type));
449: } else if (qName.equals("Or")) {
450: CompoundMatcher matcher = new OrMatcher();
451: pushCompoundMatcherAsChild(matcher);
452: } else if (qName.equals("And") || qName.equals("Match")) {
453: AndMatcher matcher = new AndMatcher();
454: pushCompoundMatcherAsChild(matcher);
455: if (qName.equals("Match")) {
456: String classregex = attributes.getValue("classregex");
457: String classMatch = attributes.getValue("class");
458:
459: if (classregex != null)
460: addMatcher(new ClassMatcher("~" + classregex));
461: else if (classMatch != null)
462: addMatcher(new ClassMatcher(classMatch));
463: }
464: }
465: nextMatchedIsDisabled = false;
466: }
467:
468: private void parseBugInstanceContents(String qName,
469: Attributes attributes) throws SAXException {
470: // Parsing an attribute or property of a BugInstance
471: BugAnnotation bugAnnotation = null;
472: if (qName.equals("Class")) {
473: String className = getRequiredAttribute(attributes,
474: "classname", qName);
475: bugAnnotation = packageMemberAnnotation = new ClassAnnotation(
476: className);
477: } else if (qName.equals("Type")) {
478: String typeDescriptor = getRequiredAttribute(attributes,
479: "descriptor", qName);
480: bugAnnotation = new TypeAnnotation(typeDescriptor);
481: } else if (qName.equals("Method") || qName.equals("Field")) {
482: String classname = getRequiredAttribute(attributes,
483: "classname", qName);
484: String fieldOrMethodName = getRequiredAttribute(attributes,
485: "name", qName);
486: String signature = getRequiredAttribute(attributes,
487: "signature", qName);
488: if (qName.equals("Method")) {
489: String isStatic = attributes.getValue("isStatic");
490: if (isStatic == null) {
491: isStatic = "false"; // Hack for old data
492: }
493:
494: bugAnnotation = packageMemberAnnotation = new MethodAnnotation(
495: classname, fieldOrMethodName, signature,
496: Boolean.valueOf(isStatic));
497:
498: } else {
499: String isStatic = getRequiredAttribute(attributes,
500: "isStatic", qName);
501: bugAnnotation = packageMemberAnnotation = new FieldAnnotation(
502: classname, fieldOrMethodName, signature,
503: Boolean.valueOf(isStatic));
504: }
505:
506: } else if (qName.equals("SourceLine")) {
507: SourceLineAnnotation sourceAnnotation = createSourceLineAnnotation(
508: qName, attributes);
509: if (!sourceAnnotation.isSynthetic())
510: bugAnnotation = sourceAnnotation;
511: } else if (qName.equals("Int")) {
512: try {
513: String value = getRequiredAttribute(attributes,
514: "value", qName);
515: bugAnnotation = new IntAnnotation(Integer
516: .parseInt(value));
517: } catch (NumberFormatException e) {
518: throw new SAXException("Bad integer value in Int");
519: }
520: } else if (qName.equals("String")) {
521: String value = getRequiredAttribute(attributes, "value",
522: qName);
523: bugAnnotation = new StringAnnotation(value);
524: } else if (qName.equals("LocalVariable")) {
525: try {
526: String varName = getRequiredAttribute(attributes,
527: "name", qName);
528: int register = Integer.parseInt(getRequiredAttribute(
529: attributes, "register", qName));
530: int pc = Integer.parseInt(getRequiredAttribute(
531: attributes, "pc", qName));
532: bugAnnotation = new LocalVariableAnnotation(varName,
533: register, pc);
534: } catch (NumberFormatException e) {
535: throw new SAXException(
536: "Invalid integer value in attribute of LocalVariable element");
537: }
538: } else if (qName.equals("Property")) {
539: // A BugProperty.
540: String propName = getRequiredAttribute(attributes, "name",
541: qName);
542: String propValue = getRequiredAttribute(attributes,
543: "value", qName);
544: bugInstance.setProperty(propName, propValue);
545: } else if (qName.equals("UserAnnotation")) {
546: // ignore AnnotationText for now; will handle in endElement
547: String s = attributes.getValue("designation"); // optional
548: BugDesignation userDesignation = bugInstance
549: .getNonnullUserDesignation();
550: if (s != null)
551: userDesignation.setDesignationKey(s);
552: s = attributes.getValue("user"); // optional
553: if (s != null)
554: userDesignation.setUser(s);
555: s = attributes.getValue("timestamp"); // optional
556: if (s != null)
557: try {
558: long timestamp = Long.valueOf(s);
559: userDesignation.setTimestamp(timestamp);
560: } catch (NumberFormatException nfe) {
561: // ok to contine -- just won't set a timestamp for the user designation.
562: // but is there anyplace to report this?
563: }
564: } else
565: throw new SAXException("Unknown bug annotation named "
566: + qName);
567:
568: if (bugAnnotation != null) {
569: String role = attributes.getValue("role");
570: if (role != null)
571: bugAnnotation.setDescription(role);
572: setAnnotationRole(attributes, bugAnnotation);
573: bugInstance.add(bugAnnotation);
574: }
575: }
576:
577: private long parseLong(String s, long defaultValue) {
578: long value;
579: try {
580: value = (s != null) ? Long.parseLong(s) : defaultValue;
581: } catch (NumberFormatException e) {
582: value = defaultValue;
583: }
584: return value;
585: }
586:
587: /**
588: * Extract a hash value from an element.
589: *
590: * @param qName name of element containing hash value
591: * @param attributes element attributes
592: * @return the decoded hash value
593: * @throws SAXException
594: */
595: private byte[] extractHash(String qName, Attributes attributes)
596: throws SAXException {
597: String encodedHash = getRequiredAttribute(attributes, "value",
598: qName);
599: byte[] hash;
600: try {
601: //System.out.println("Extract hash " + encodedHash);
602: hash = ClassHash.stringToHash(encodedHash);
603: } catch (IllegalArgumentException e) {
604: throw new SAXException("Invalid class hash", e);
605: }
606: return hash;
607: }
608:
609: private void setAnnotationRole(Attributes attributes,
610: BugAnnotation bugAnnotation) {
611: String role = attributes.getValue("role");
612: if (role != null)
613: bugAnnotation.setDescription(role);
614: }
615:
616: private SourceLineAnnotation createSourceLineAnnotation(
617: String qName, Attributes attributes) throws SAXException {
618: String classname = getRequiredAttribute(attributes,
619: "classname", qName);
620: String sourceFile = attributes.getValue("sourcefile");
621: if (sourceFile == null)
622: sourceFile = SourceLineAnnotation.UNKNOWN_SOURCE_FILE;
623: String startLine = attributes.getValue("start"); // "start"/"end" are now optional
624: String endLine = attributes.getValue("end"); // (were too many "-1"s in the xml)
625: String startBytecode = attributes.getValue("startBytecode");
626: String endBytecode = attributes.getValue("endBytecode");
627:
628: try {
629: int sl = startLine != null ? Integer.parseInt(startLine)
630: : -1;
631: int el = endLine != null ? Integer.parseInt(endLine) : -1;
632: int sb = startBytecode != null ? Integer
633: .parseInt(startBytecode) : -1;
634: int eb = endBytecode != null ? Integer
635: .parseInt(endBytecode) : -1;
636:
637: SourceLineAnnotation annotation = new SourceLineAnnotation(
638: classname, sourceFile, sl, el, sb, eb);
639:
640: return annotation;
641: } catch (NumberFormatException e) {
642: throw new SAXException(
643: "Bad integer value in SourceLine element", e);
644: }
645: }
646:
647: @Override
648: public void endElement(String uri, String name, String qName)
649: throws SAXException {
650: // URI should always be empty.
651: // So, qName is the name of the element.
652:
653: if (discardedElement(qName)) {
654: nestingOfIgnoredElements--;
655: } else if (nestingOfIgnoredElements > 0) {
656: // ignore it
657: } else if (elementStack.size() > 1) {
658: String outerElement = elementStack
659: .get(elementStack.size() - 2);
660:
661: if (qName.equals("Or") || qName.equals("And")
662: || qName.equals("Match") || isTopLevelFilter(qName)) {
663: if (DEBUG)
664: System.out.println(" ending " + elementStack + " "
665: + qName + " " + matcherStack);
666:
667: matcherStack.pop();
668: } else if (outerElement.equals(BUG_COLLECTION)) {
669: if (qName.equals("BugInstance")) {
670: bugCollection.add(bugInstance, false);
671: // TODO: check this
672: if (bugInstance.getLastVersion() == -1)
673: bugCollection.getProjectStats().addBug(
674: bugInstance);
675: }
676: } else if (outerElement.equals(PROJECT)) {
677: //System.out.println("Adding project element " + qName + ": " + textBuffer.toString());
678: if (qName.equals("Jar"))
679: project.addFile(textBuffer.toString());
680: else if (qName.equals("SrcDir"))
681: project.addSourceDir(textBuffer.toString());
682: else if (qName.equals("AuxClasspathEntry"))
683: project.addAuxClasspathEntry(textBuffer.toString());
684: } else if (outerElement.equals("BugInstance")) {
685: if (qName.equals("UserAnnotation")) {
686: bugInstance
687: .setAnnotationText(textBuffer.toString());
688: }
689: } else if (outerElement
690: .equals(BugCollection.ERRORS_ELEMENT_NAME)) {
691: if (qName
692: .equals(BugCollection.ANALYSIS_ERROR_ELEMENT_NAME)) {
693: analysisError.setMessage(textBuffer.toString());
694: bugCollection.addError(analysisError);
695: } else if (qName
696: .equals(BugCollection.ERROR_ELEMENT_NAME)) {
697: if (stackTrace.size() > 0) {
698: analysisError
699: .setStackTrace(stackTrace
700: .toArray(new String[stackTrace
701: .size()]));
702: }
703: bugCollection.addError(analysisError);
704: } else if (qName
705: .equals(BugCollection.MISSING_CLASS_ELEMENT_NAME)) {
706: bugCollection
707: .addMissingClass(textBuffer.toString());
708: }
709:
710: } else if (outerElement
711: .equals(BugCollection.ERROR_ELEMENT_NAME)) {
712: if (qName
713: .equals(BugCollection.ERROR_MESSAGE_ELEMENT_NAME)) {
714: analysisError.setMessage(textBuffer.toString());
715: } else if (qName
716: .equals(BugCollection.ERROR_EXCEPTION_ELEMENT_NAME)) {
717: analysisError.setExceptionMessage(textBuffer
718: .toString());
719: } else if (qName
720: .equals(BugCollection.ERROR_STACK_TRACE_ELEMENT_NAME)) {
721: stackTrace.add(textBuffer.toString());
722: }
723: } else if (outerElement.equals("ClassFeatures")) {
724: if (qName.equals(ClassFeatureSet.ELEMENT_NAME)) {
725: bugCollection.setClassFeatureSet(classFeatureSet);
726: classFeatureSet = null;
727: }
728: }
729: }
730:
731: elementStack.remove(elementStack.size() - 1);
732: }
733:
734: @Override
735: public void characters(char[] ch, int start, int length) {
736: textBuffer.append(ch, start, length);
737: }
738:
739: private static String getRequiredAttribute(Attributes attributes,
740: String attrName, String elementName) throws SAXException {
741: String value = attributes.getValue(attrName);
742: if (value == null)
743: throw new SAXException(elementName + " element missing "
744: + attrName + " attribute");
745: return value;
746: }
747:
748: }
749:
750: // vim:ts=4
|