KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * FindBugs - Find bugs in Java programs
3  * Copyright (C) 2003-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.IOException JavaDoc;
23 import java.io.Serializable JavaDoc;
24 import java.math.BigInteger JavaDoc;
25 import java.security.MessageDigest JavaDoc;
26 import java.util.ArrayList JavaDoc;
27 import java.util.Collection JavaDoc;
28 import java.util.HashSet JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.NoSuchElementException JavaDoc;
31 import java.util.Set JavaDoc;
32 import java.util.StringTokenizer JavaDoc;
33
34 import org.apache.bcel.Constants;
35 import org.apache.bcel.classfile.JavaClass;
36 import org.apache.bcel.classfile.Method;
37 import org.apache.bcel.generic.ConstantPoolGen;
38 import org.apache.bcel.generic.InstructionHandle;
39 import org.apache.bcel.generic.InvokeInstruction;
40 import org.apache.bcel.generic.MethodGen;
41
42 import edu.umd.cs.findbugs.annotations.NonNull;
43 import edu.umd.cs.findbugs.annotations.Nullable;
44 import edu.umd.cs.findbugs.annotations.CheckForNull;
45 import edu.umd.cs.findbugs.ba.AnalysisContext;
46 import edu.umd.cs.findbugs.ba.ClassContext;
47 import edu.umd.cs.findbugs.ba.JavaClassAndMethod;
48 import edu.umd.cs.findbugs.ba.Location;
49 import edu.umd.cs.findbugs.ba.XFactory;
50 import edu.umd.cs.findbugs.ba.XField;
51 import edu.umd.cs.findbugs.ba.XMethod;
52 import edu.umd.cs.findbugs.ba.bcp.FieldVariable;
53 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
54 import edu.umd.cs.findbugs.classfile.FieldDescriptor;
55 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
56 import edu.umd.cs.findbugs.util.ClassName;
57 import edu.umd.cs.findbugs.visitclass.DismantleBytecode;
58 import edu.umd.cs.findbugs.visitclass.PreorderVisitor;
59 import edu.umd.cs.findbugs.xml.XMLAttributeList;
60 import edu.umd.cs.findbugs.xml.XMLOutput;
61
62 /**
63  * An instance of a bug pattern.
64  * A BugInstance consists of several parts:
65  * <p/>
66  * <ul>
67  * <li> the type, which is a string indicating what kind of bug it is;
68  * used as a key for the FindBugsMessages resource bundle
69  * <li> the priority; how likely this instance is to actually be a bug
70  * <li> a list of <em>annotations</em>
71  * </ul>
72  * <p/>
73  * The annotations describe classes, methods, fields, source locations,
74  * and other relevant context information about the bug instance.
75  * Every BugInstance must have at least one ClassAnnotation, which
76  * describes the class in which the instance was found. This is the
77  * "primary class annotation".
78  * <p/>
79  * <p> BugInstance objects are built up by calling a string of <code>add</code>
80  * methods. (These methods all "return this", so they can be chained).
81  * Some of the add methods are specialized to get information automatically from
82  * a BetterVisitor or DismantleBytecode object.
83  *
84  * @author David Hovemeyer
85  * @see BugAnnotation
86  */

87 public class BugInstance implements Comparable JavaDoc<BugInstance>, XMLWriteableWithMessages, Serializable JavaDoc, Cloneable JavaDoc {
88     private static final long serialVersionUID = 1L;
89     
90     private String JavaDoc type;
91     private int priority;
92     private ArrayList JavaDoc<BugAnnotation> annotationList;
93     private int cachedHashCode;
94     private @CheckForNull BugDesignation userDesignation;
95     private BugProperty propertyListHead, propertyListTail;
96     private String JavaDoc uniqueId;
97     private String JavaDoc oldInstanceHash;
98     private String JavaDoc instanceHash;
99     private int instanceOccurrenceNum;
100     private int instanceOccurrenceMax;
101     
102     
103     /*
104      * The following fields are used for tracking Bug instances across multiple versions of software.
105      * They are meaningless in a BugCollection for just one version of software.
106      */

107     private long firstVersion = 0;
108     private long lastVersion = -1;
109     private boolean introducedByChangeOfExistingClass;
110     private boolean removedByChangeOfPersistingClass;
111     
112     /**
113      * This value is used to indicate that the cached hashcode
114      * is invalid, and should be recomputed.
115      */

116     private static final int INVALID_HASH_CODE = 0;
117     
118     /**
119      * This value is used to indicate whether BugInstances should be reprioritized very low,
120      * when the BugPattern is marked as experimental
121      */

122     private static boolean adjustExperimental = false;
123     
124     /**
125      * Constructor.
126      *
127      * @param type the bug type
128      * @param priority the bug priority
129      */

130     public BugInstance(String JavaDoc type, int priority) {
131         this.type = type;
132         this.priority = priority < Detector.HIGH_PRIORITY
133             ? Detector.HIGH_PRIORITY : priority;
134         annotationList = new ArrayList JavaDoc<BugAnnotation>(4);
135         cachedHashCode = INVALID_HASH_CODE;
136         
137         if (adjustExperimental && isExperimental())
138             this.priority = Detector.EXP_PRIORITY;
139     }
140     
141     //@Override
142
@Override JavaDoc
143     public Object JavaDoc clone() {
144         BugInstance dup;
145         
146         try {
147             dup = (BugInstance) super.clone();
148             
149             // Do deep copying of mutable objects
150
for (int i = 0; i < dup.annotationList.size(); ++i) {
151                 dup.annotationList.set(i, (BugAnnotation) dup.annotationList.get(i).clone());
152             }
153             dup.propertyListHead = dup.propertyListTail = null;
154             for (Iterator JavaDoc<BugProperty> i = propertyIterator(); i.hasNext(); ) {
155                 dup.addProperty((BugProperty) i.next().clone());
156             }
157
158             return dup;
159         } catch (CloneNotSupportedException JavaDoc e) {
160             throw new AssertionError JavaDoc(e);
161         }
162     }
163
164     /**
165      * Create a new BugInstance.
166      * This is the constructor that should be used by Detectors.
167      *
168      * @param detector the Detector that is reporting the BugInstance
169      * @param type the bug type
170      * @param priority the bug priority
171      */

172     public BugInstance(Detector detector, String JavaDoc type, int priority) {
173         this(type, priority);
174         
175         if (detector != null) {
176             // Adjust priority if required
177
DetectorFactory factory =
178                 DetectorFactoryCollection.instance().getFactoryByClassName(detector.getClass().getName());
179             if (factory != null) {
180                 this.priority += factory.getPriorityAdjustment();
181                 if (this.priority < 0)
182                     this.priority = 0;
183             }
184         }
185         
186         if (adjustExperimental && isExperimental())
187             this.priority = Detector.EXP_PRIORITY;
188     }
189         
190     public static void setAdjustExperimental(boolean adjust) {
191         adjustExperimental = adjust;
192     }
193     
194     /* ----------------------------------------------------------------------
195      * Accessors
196      * ---------------------------------------------------------------------- */

197
198     /**
199      * Get the bug type.
200      */

201     public String JavaDoc getType() {
202         return type;
203     }
204
205     /**
206      * Get the BugPattern.
207      */

208     public BugPattern getBugPattern() {
209         return I18N.instance().lookupBugPattern(getType());
210     }
211
212     /**
213      * Get the bug priority.
214      */

215     public int getPriority() {
216         return priority;
217     }
218
219     /**
220      * Set the bug priority.
221      */

222     public void setPriority(int p) {
223         priority = Math.max(Detector.HIGH_PRIORITY, Math.min(Detector.IGNORE_PRIORITY, p));
224     }
225     public void raisePriority() {
226         priority = Math.max(Detector.HIGH_PRIORITY, Math.min(Detector.IGNORE_PRIORITY, priority-1));
227     }
228     public void lowerPriority() {
229         priority = Math.max(Detector.HIGH_PRIORITY, Math.min(Detector.IGNORE_PRIORITY, priority+1));
230     }
231
232     public void lowerPriorityALot() {
233         priority = Math.max(Detector.HIGH_PRIORITY, Math.min(Detector.IGNORE_PRIORITY, priority+2));
234     }
235
236     /**
237      * Is this bug instance the result of an experimental detector?
238      */

239     public boolean isExperimental() {
240         BugPattern pattern = I18N.instance().lookupBugPattern(type);
241         return (pattern != null) && pattern.isExperimental();
242     }
243
244     /**
245      * Get the primary class annotation, which indicates where the bug occurs.
246      */

247     public ClassAnnotation getPrimaryClass() {
248         return (ClassAnnotation) findAnnotationOfType(ClassAnnotation.class);
249     }
250
251     /**
252      * Get the primary method annotation, which indicates where the bug occurs.
253      */

254     public MethodAnnotation getPrimaryMethod() {
255         return (MethodAnnotation) findAnnotationOfType(MethodAnnotation.class);
256     }
257     /**
258      * Get the primary method annotation, which indicates where the bug occurs.
259      */

260     public FieldAnnotation getPrimaryField() {
261         return (FieldAnnotation) findAnnotationOfType(FieldAnnotation.class);
262     }
263
264     
265     public BugInstance lowerPriorityIfDeprecated() {
266         MethodAnnotation m = getPrimaryMethod();
267         if (m != null && AnalysisContext.currentXFactory().getDeprecated().contains(XFactory.createXMethod(m)))
268                 priority++;
269         FieldAnnotation f = getPrimaryField();
270         if (f != null && AnalysisContext.currentXFactory().getDeprecated().contains(XFactory.createXField(f)))
271             priority++;
272         return this;
273     }
274     /**
275      * Find the first BugAnnotation in the list of annotations
276      * that is the same type or a subtype as the given Class parameter.
277      *
278      * @param cls the Class parameter
279      * @return the first matching BugAnnotation of the given type,
280      * or null if there is no such BugAnnotation
281      */

282     private BugAnnotation findAnnotationOfType(Class JavaDoc<? extends BugAnnotation> cls) {
283         for (Iterator JavaDoc<BugAnnotation> i = annotationIterator(); i.hasNext();) {
284             BugAnnotation annotation = i.next();
285             if (cls.isAssignableFrom(annotation.getClass()))
286                 return annotation;
287         }
288         return null;
289     }
290     
291     public LocalVariableAnnotation getPrimaryLocalVariableAnnotation() {
292         for (BugAnnotation annotation : annotationList)
293             if (annotation instanceof LocalVariableAnnotation)
294                 return (LocalVariableAnnotation) annotation;
295         return null;
296     }
297     /**
298      * Get the primary source line annotation.
299      * There is guaranteed to be one (unless some Detector constructed
300      * an invalid BugInstance).
301      *
302      * @return the source line annotation
303      */

304     public SourceLineAnnotation getPrimarySourceLineAnnotation() {
305         // Highest priority: return the first top level source line annotation
306
for (BugAnnotation annotation : annotationList) {
307             if (annotation instanceof SourceLineAnnotation)
308                 return (SourceLineAnnotation) annotation;
309         }
310         
311         // Next: Try primary method, primary field, primary class
312
SourceLineAnnotation srcLine;
313         if ((srcLine = inspectPackageMemberSourceLines(getPrimaryMethod())) != null)
314             return srcLine;
315         if ((srcLine = inspectPackageMemberSourceLines(getPrimaryField())) != null)
316             return srcLine;
317         if ((srcLine = inspectPackageMemberSourceLines(getPrimaryClass())) != null)
318             return srcLine;
319         
320         // Last resort: throw exception
321
throw new IllegalStateException JavaDoc("BugInstance must contain at least one class, method, or field annotation");
322     }
323
324     public String JavaDoc getInstanceKey() {
325         StringBuffer JavaDoc buf = new StringBuffer JavaDoc(type);
326         for (BugAnnotation annotation : annotationList) {
327             if (annotation instanceof SourceLineAnnotation) {
328                 // do nothing
329
} else {
330                 buf.append(":");
331                 buf.append(annotation.format("hash", null));
332             }
333         }
334         return buf.toString();
335     }
336     /**
337      * If given PackageMemberAnnotation is non-null, return its
338      * SourceLineAnnotation.
339      *
340      * @param packageMember
341      * a PackageMemberAnnotation
342      * @return the PackageMemberAnnotation's SourceLineAnnotation, or null if
343      * there is no SourceLineAnnotation
344      */

345     private SourceLineAnnotation inspectPackageMemberSourceLines(PackageMemberAnnotation packageMember) {
346         return (packageMember != null) ? packageMember.getSourceLines() : null;
347     }
348
349     /**
350      * Get an Iterator over all bug annotations.
351      */

352     public Iterator JavaDoc<BugAnnotation> annotationIterator() {
353         return annotationList.iterator();
354     }
355
356     /**
357      * Get the abbreviation of this bug instance's BugPattern.
358      * This is the same abbreviation used by the BugCode which
359      * the BugPattern is a particular species of.
360      */

361     public String JavaDoc getAbbrev() {
362         BugPattern pattern = I18N.instance().lookupBugPattern(getType());
363         return pattern != null ? pattern.getAbbrev() : "<unknown bug pattern>";
364     }
365
366     /** set the user designation object. This will clobber any
367      * existing annotationText (or any other BugDesignation field). */

368     public void setUserDesignation(BugDesignation bd) {
369         userDesignation = bd;
370     }
371     /** return the user designation object, which may be null.
372      *
373      * A previous calls to getSafeUserDesignation(), setAnnotationText(),
374      * or setUserDesignation() will ensure it will be non-null
375      * [barring an intervening setUserDesignation(null)].
376      * @see #getNonnullUserDesignation() */

377     @Nullable public BugDesignation getUserDesignation() {
378         return userDesignation;
379     }
380     /** return the user designation object, creating one if
381      * necessary. So calling
382      * <code>getSafeUserDesignation().setDesignation("HARMLESS")</code>
383      * will always work without the possibility of a NullPointerException.
384      * @see #getUserDesignation() */

385     @NonNull public BugDesignation getNonnullUserDesignation() {
386         if (userDesignation == null)
387             userDesignation = new BugDesignation();
388         return userDesignation;
389     }
390     
391
392     /** Get the user designation key.
393      * E.g., "MOSTLY_HARMLESS", "CRITICAL", "NOT_A_BUG", etc.
394      *
395      * If the user designation object is null,returns UNCLASSIFIED.
396      *
397      * To set the user designation key, call
398      * <code>getSafeUserDesignation().setDesignation("HARMLESS")</code>.
399      *
400      * @see I18N#getUserDesignation(String key)
401      * @return the user designation key
402      */

403     @NonNull public String JavaDoc getUserDesignationKey() {
404         BugDesignation userDesignation = this.userDesignation;
405         if (userDesignation == null) return BugDesignation.UNCLASSIFIED;
406         return userDesignation.getDesignationKey();
407     }
408
409     /**
410      * Set the user annotation text.
411      *
412      * @param annotationText the user annotation text
413      */

414     public void setAnnotationText(String JavaDoc annotationText) {
415         getNonnullUserDesignation().setAnnotationText(annotationText);
416     }
417
418     /**
419      * Get the user annotation text.
420      *
421      * @return the user annotation text
422      */

423     @NonNull public String JavaDoc getAnnotationText() {
424         BugDesignation userDesignation = this.userDesignation;
425         if (userDesignation == null) return "";
426         String JavaDoc s = userDesignation.getAnnotationText();
427         if (s == null) return "";
428         return s;
429     }
430
431     /**
432      * Determine whether or not the annotation text contains
433      * the given word.
434      *
435      * @param word the word
436      * @return true if the annotation text contains the word, false otherwise
437      */

438     public boolean annotationTextContainsWord(String JavaDoc word) {
439         return getTextAnnotationWords().contains(word);
440     }
441
442     /**
443      * Get set of words in the text annotation.
444      */

445     public Set JavaDoc<String JavaDoc> getTextAnnotationWords() {
446         HashSet JavaDoc<String JavaDoc> result = new HashSet JavaDoc<String JavaDoc>();
447
448         StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(getAnnotationText(), " \t\r\n\f.,:;-");
449         while (tok.hasMoreTokens()) {
450             result.add(tok.nextToken());
451         }
452         return result;
453     }
454     
455     /**
456      * Get the BugInstance's unique id.
457      *
458      * @return the unique id, or null if no unique id has been assigned
459      *
460      * Deprecated, since it isn't persistent
461      */

462     @Deprecated JavaDoc
463     public String JavaDoc getUniqueId() {
464         return uniqueId;
465     }
466     
467     /**
468      * Set the unique id of the BugInstance.
469      *
470      * @param uniqueId the unique id
471      *
472      * * Deprecated, since it isn't persistent
473      */

474     @Deprecated JavaDoc
475      void setUniqueId(String JavaDoc uniqueId) {
476         this.uniqueId = uniqueId;
477     }
478
479     
480     
481     /* ----------------------------------------------------------------------
482      * Property accessors
483      * ---------------------------------------------------------------------- */

484     
485     private class BugPropertyIterator implements Iterator JavaDoc<BugProperty> {
486         private BugProperty prev, cur;
487         private boolean removed;
488         
489         /* (non-Javadoc)
490          * @see java.util.Iterator#hasNext()
491          */

492         public boolean hasNext() {
493             return findNext() != null;
494         }
495         /* (non-Javadoc)
496          * @see java.util.Iterator#next()
497          */

498         public BugProperty next() {
499             BugProperty next = findNext();
500             if (next == null)
501                 throw new NoSuchElementException JavaDoc();
502             prev = cur;
503             cur = next;
504             removed = false;
505             return cur;
506         }
507         
508         /* (non-Javadoc)
509          * @see java.util.Iterator#remove()
510          */

511         public void remove() {
512             if (cur == null || removed)
513                 throw new IllegalStateException JavaDoc();
514             if (prev == null) {
515                 propertyListHead = cur.getNext();
516             } else {
517                 prev.setNext(cur.getNext());
518             }
519             if (cur == propertyListTail) {
520                 propertyListTail = prev;
521             }
522             removed = true;
523         }
524         
525         private BugProperty findNext() {
526             return cur == null ? propertyListHead : cur.getNext();
527         }
528
529     }
530
531     /**
532      * Get value of given property.
533      *
534      * @param name name of the property to get
535      * @return the value of the named property, or null if
536      * the property has not been set
537      */

538     public String JavaDoc getProperty(String JavaDoc name) {
539         BugProperty prop = lookupProperty(name);
540         return prop != null ? prop.getValue() : null;
541     }
542     
543     /**
544      * Get value of given property, returning given default
545      * value if the property has not been set.
546      *
547      * @param name name of the property to get
548      * @param defaultValue default value to return if propery is not set
549      * @return the value of the named property, or the default
550      * value if the property has not been set
551      */

552     public String JavaDoc getProperty(String JavaDoc name, String JavaDoc defaultValue) {
553         String JavaDoc value = getProperty(name);
554         return value != null ? value : defaultValue;
555     }
556     
557     /**
558      * Get an Iterator over the properties defined in this BugInstance.
559      *
560      * @return Iterator over properties
561      */

562     public Iterator JavaDoc<BugProperty> propertyIterator() {
563         return new BugPropertyIterator();
564     }
565     
566     /**
567      * Set value of given property.
568      *
569      * @param name name of the property to set
570      * @param value the value of the property
571      * @return this object, so calls can be chained
572      */

573     public BugInstance setProperty(String JavaDoc name, String JavaDoc value) {
574         BugProperty prop = lookupProperty(name);
575         if (prop != null) {
576             prop.setValue(value);
577         } else {
578             prop = new BugProperty(name, value);
579             addProperty(prop);
580         }
581         return this;
582     }
583     
584     /**
585      * Look up a property by name.
586      *
587      * @param name name of the property to look for
588      * @return the BugProperty with the given name,
589      * or null if the property has not been set
590      */

591     public BugProperty lookupProperty(String JavaDoc name) {
592         BugProperty prop = propertyListHead;
593         
594         while (prop != null) {
595             if (prop.getName().equals(name))
596                 break;
597             prop = prop.getNext();
598         }
599         
600         return prop;
601     }
602     
603     /**
604      * Delete property with given name.
605      *
606      * @param name name of the property to delete
607      * @return true if a property with that name was deleted,
608      * or false if there is no such property
609      */

610     public boolean deleteProperty(String JavaDoc name) {
611         BugProperty prev = null;
612         BugProperty prop = propertyListHead;
613         
614         while (prop != null) {
615             if (prop.getName().equals(name))
616                 break;
617             prev = prop;
618             prop = prop.getNext();
619         }
620         
621         if (prop != null) {
622             if (prev != null) {
623                 // Deleted node in interior or at tail of list
624
prev.setNext(prop.getNext());
625             } else {
626                 // Deleted node at head of list
627
propertyListHead = prop.getNext();
628             }
629             
630             if (prop.getNext() == null) {
631                 // Deleted node at end of list
632
propertyListTail = prev;
633             }
634             
635             return true;
636         } else {
637             // No such property
638
return false;
639         }
640     }
641     
642     private void addProperty(BugProperty prop) {
643         if (propertyListTail != null) {
644             propertyListTail.setNext(prop);
645             propertyListTail = prop;
646         } else {
647             propertyListHead = propertyListTail = prop;
648         }
649         prop.setNext(null);
650     }
651     
652     /* ----------------------------------------------------------------------
653      * Generic BugAnnotation adders
654      * ---------------------------------------------------------------------- */

655     
656     /**
657      * Add a Collection of BugAnnotations.
658      *
659      * @param annotationCollection Collection of BugAnnotations
660      */

661     public BugInstance addAnnotations(Collection JavaDoc<? extends BugAnnotation> annotationCollection) {
662         for (BugAnnotation annotation : annotationCollection) {
663             add(annotation);
664         }
665         return this;
666     }
667
668     /* ----------------------------------------------------------------------
669      * Combined annotation adders
670      * ---------------------------------------------------------------------- */

671     
672     public BugInstance addClassAndMethod(MethodDescriptor methodDescriptor) {
673         addClass(methodDescriptor.getClassName());
674         add(MethodAnnotation.fromMethodDescriptor(methodDescriptor));
675         return this;
676     }
677
678     /**
679      * Add a class annotation and a method annotation for the class and method
680      * which the given visitor is currently visiting.
681      *
682      * @param visitor the BetterVisitor
683      * @return this object
684      */

685     public BugInstance addClassAndMethod(PreorderVisitor visitor) {
686         addClass(visitor);
687         addMethod(visitor);
688         return this;
689     }
690
691     /**
692      * Add class and method annotations for given method.
693      *
694      * @param methodAnnotation the method
695      * @return this object
696      */

697     public BugInstance addClassAndMethod(MethodAnnotation methodAnnotation) {
698         addClass(methodAnnotation.getClassName());
699         addMethod(methodAnnotation);
700         return this;
701     }
702
703     /**
704      * Add class and method annotations for given method.
705      *
706      * @param methodGen the method
707      * @param sourceFile source file the method is defined in
708      * @return this object
709      */

710     public BugInstance addClassAndMethod(MethodGen methodGen, String JavaDoc sourceFile) {
711         addClass(methodGen.getClassName());
712         addMethod(methodGen, sourceFile);
713         return this;
714     }
715     
716     
717     /**
718      * Add class and method annotations for given class and method.
719      *
720      * @param javaClass the class
721      * @param method the method
722      * @return this object
723      */

724     public BugInstance addClassAndMethod(JavaClass javaClass, Method method) {
725         addClass(javaClass.getClassName());
726         addMethod(javaClass, method);
727         return this;
728     }
729
730     /* ----------------------------------------------------------------------
731      * Class annotation adders
732      * ---------------------------------------------------------------------- */

733
734     /**
735      * Add a class annotation. If this is the first class annotation added,
736      * it becomes the primary class annotation.
737      *
738      * @param className the name of the class
739      * @param sourceFileName the source file of the class
740      * @return this object
741      * @deprecated use addClass(String) instead
742      */

743     public BugInstance addClass(String JavaDoc className, String JavaDoc sourceFileName) {
744         ClassAnnotation classAnnotation = new ClassAnnotation(className);
745         add(classAnnotation);
746         return this;
747     }
748
749     /**
750      * Add a class annotation. If this is the first class annotation added,
751      * it becomes the primary class annotation.
752      *
753      * @param className the name of the class
754      * @return this object
755      */

756     public BugInstance addClass(String JavaDoc className) {
757         className = ClassName.toDottedClassName(className);
758         ClassAnnotation classAnnotation = new ClassAnnotation(className);
759         add(classAnnotation);
760         return this;
761     }
762
763     /**
764      * Add a class annotation. If this is the first class annotation added,
765      * it becomes the primary class annotation.
766      *
767      * @param classDescriptor the class to add
768      * @return this object
769      */

770     public BugInstance addClass(ClassDescriptor classDescriptor) {
771         add(ClassAnnotation.fromClassDescriptor(classDescriptor));
772         return this;
773     }
774
775     /**
776      * Add a class annotation. If this is the first class annotation added,
777      * it becomes the primary class annotation.
778      *
779      * @param jclass the JavaClass object for the class
780      * @return this object
781      */

782     public BugInstance addClass(JavaClass jclass) {
783         addClass(jclass.getClassName());
784         return this;
785     }
786
787     /**
788      * Add a class annotation for the class that the visitor is currently visiting.
789      *
790      * @param visitor the BetterVisitor
791      * @return this object
792      */

793     public BugInstance addClass(PreorderVisitor visitor) {
794         String JavaDoc className = visitor.getDottedClassName();
795         addClass(className);
796         return this;
797     }
798
799     /**
800      * Add a class annotation for the superclass of the class the visitor
801      * is currently visiting.
802      *
803      * @param visitor the BetterVisitor
804      * @return this object
805      */

806     public BugInstance addSuperclass(PreorderVisitor visitor) {
807         String JavaDoc className = visitor.getSuperclassName();
808         addClass(className);
809         return this;
810     }
811
812     /* ----------------------------------------------------------------------
813      * Type annotation adders
814      * ---------------------------------------------------------------------- */

815
816     /**
817      * Add a type annotation. Handy for referring to array types.
818      *
819      * <p>For information on type descriptors,
820      * <br>see http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#14152
821      * <br>or http://www.murrayc.com/learning/java/java_classfileformat.shtml#TypeDescriptors
822      *
823      * @param typeDescriptor a jvm type descriptor, such as "[I"
824      * @return this object
825      */

826     public BugInstance addType(String JavaDoc typeDescriptor) {
827         TypeAnnotation typeAnnotation = new TypeAnnotation(typeDescriptor);
828         add(typeAnnotation);
829         return this;
830     }
831     
832     public BugInstance addTypeOfNamedClass(String JavaDoc typeName) {
833         TypeAnnotation typeAnnotation = new TypeAnnotation("L" + typeName.replace('.','/')+";");
834         add(typeAnnotation);
835         return this;
836     }
837     /* ----------------------------------------------------------------------
838      * Field annotation adders
839      * ---------------------------------------------------------------------- */

840
841     /**
842      * Add a field annotation.
843      *
844      * @param className name of the class containing the field
845      * @param fieldName the name of the field
846      * @param fieldSig type signature of the field
847      * @param isStatic whether or not the field is static
848      * @return this object
849      */

850     public BugInstance addField(String JavaDoc className, String JavaDoc fieldName, String JavaDoc fieldSig, boolean isStatic) {
851         addField(new FieldAnnotation(className, fieldName, fieldSig, isStatic));
852         return this;
853     }
854
855     /**
856      * Add a field annotation
857      *
858      * @param fieldAnnotation the field annotation
859      * @return this object
860      */

861     public BugInstance addField(FieldAnnotation fieldAnnotation) {
862         add(fieldAnnotation);
863         return this;
864     }
865
866     /**
867      * Add a field annotation for a FieldVariable matched in a ByteCodePattern.
868      *
869      * @param field the FieldVariable
870      * @return this object
871      */

872     public BugInstance addField(FieldVariable field) {
873         return addField(field.getClassName(), field.getFieldName(), field.getFieldSig(), field.isStatic());
874     }
875
876     /**
877      * Add a field annotation for an XField.
878      *
879      * @param xfield the XField
880      * @return this object
881      */

882     public BugInstance addField(XField xfield) {
883         return addField(xfield.getClassName(), xfield.getName(), xfield.getSignature(), xfield.isStatic());
884     }
885     
886     /**
887      * Add a field annotation for a FieldDescriptor.
888      *
889      * @param fieldDescriptor the FieldDescriptor
890      * @return this object
891      */

892     public BugInstance addField(FieldDescriptor fieldDescriptor) {
893         FieldAnnotation fieldAnnotation = FieldAnnotation.fromFieldDescriptor(fieldDescriptor);
894         add(fieldAnnotation);
895         return this;
896     }
897
898     /**
899      * Add a field annotation for the field which has just been accessed
900      * by the method currently being visited by given visitor.
901      * Assumes that a getfield/putfield or getstatic/putstatic
902      * has just been seen.
903      *
904      * @param visitor the DismantleBytecode object
905      * @return this object
906      */

907     public BugInstance addReferencedField(DismantleBytecode visitor) {
908         FieldAnnotation f = FieldAnnotation.fromReferencedField(visitor);
909         addField(f);
910         return this;
911     }
912
913     /**
914      * Add a field annotation for the field referenced by the FieldAnnotation parameter
915      */

916     public BugInstance addReferencedField(FieldAnnotation fa) {
917         addField(fa);
918         return this;
919     }
920
921     /**
922      * Add a field annotation for the field which is being visited by
923      * given visitor.
924      *
925      * @param visitor the visitor
926      * @return this object
927      */

928     public BugInstance addVisitedField(PreorderVisitor visitor) {
929         FieldAnnotation f = FieldAnnotation.fromVisitedField(visitor);
930         addField(f);
931         return this;
932     }
933
934     /* ----------------------------------------------------------------------
935      * Method annotation adders
936      * ---------------------------------------------------------------------- */

937
938     /**
939      * Add a method annotation. If this is the first method annotation added,
940      * it becomes the primary method annotation.
941      *
942      * @param className name of the class containing the method
943      * @param methodName name of the method
944      * @param methodSig type signature of the method
945      * @param isStatic true if the method is static, false otherwise
946      * @return this object
947      */

948     public BugInstance addMethod(String JavaDoc className, String JavaDoc methodName, String JavaDoc methodSig, boolean isStatic) {
949         addMethod(MethodAnnotation.fromForeignMethod(className, methodName, methodSig, isStatic));
950         return this;
951     }
952
953     /**
954      * Add a method annotation. If this is the first method annotation added,
955      * it becomes the primary method annotation.
956      * If the method has source line information, then a SourceLineAnnotation
957      * is added to the method.
958      *
959      * @param methodGen the MethodGen object for the method
960      * @param sourceFile source file method is defined in
961      * @return this object
962      */

963     public BugInstance addMethod(MethodGen methodGen, String JavaDoc sourceFile) {
964         String JavaDoc className = methodGen.getClassName();
965         MethodAnnotation methodAnnotation =
966                 new MethodAnnotation(className, methodGen.getName(), methodGen.getSignature(), methodGen.isStatic());
967         addMethod(methodAnnotation);
968         addSourceLinesForMethod(methodAnnotation, SourceLineAnnotation.fromVisitedMethod(methodGen, sourceFile));
969         return this;
970     }
971     
972     /**
973      * Add a method annotation. If this is the first method annotation added,
974      * it becomes the primary method annotation.
975      * If the method has source line information, then a SourceLineAnnotation
976      * is added to the method.
977      *
978      * @param javaClass the class the method is defined in
979      * @param method the method
980      * @return this object
981      */

982     public BugInstance addMethod(JavaClass javaClass, Method method) {
983         MethodAnnotation methodAnnotation =
984             new MethodAnnotation(javaClass.getClassName(), method.getName(), method.getSignature(), method.isStatic());
985         SourceLineAnnotation methodSourceLines = SourceLineAnnotation.forEntireMethod(
986                 javaClass,
987                 method);
988         methodAnnotation.setSourceLines(methodSourceLines);
989         addMethod(methodAnnotation);
990         return this;
991     }
992     
993     /**
994      * Add a method annotation. If this is the first method annotation added,
995      * it becomes the primary method annotation.
996      * If the method has source line information, then a SourceLineAnnotation
997      * is added to the method.
998      *
999      * @param classAndMethod JavaClassAndMethod identifying the method to add
1000     * @return this object
1001     */

1002    public BugInstance addMethod(JavaClassAndMethod classAndMethod) {
1003        return addMethod(classAndMethod.getJavaClass(), classAndMethod.getMethod());
1004    }
1005
1006    /**
1007     * Add a method annotation for the method which the given visitor is currently visiting.
1008     * If the method has source line information, then a SourceLineAnnotation
1009     * is added to the method.
1010     *
1011     * @param visitor the BetterVisitor
1012     * @return this object
1013     */

1014    public BugInstance addMethod(PreorderVisitor visitor) {
1015        MethodAnnotation methodAnnotation = MethodAnnotation.fromVisitedMethod(visitor);
1016        addMethod(methodAnnotation);
1017        addSourceLinesForMethod(methodAnnotation, SourceLineAnnotation.fromVisitedMethod(visitor));
1018        return this;
1019    }
1020
1021    /**
1022     * Add a method annotation for the method which has been called
1023     * by the method currently being visited by given visitor.
1024     * Assumes that the visitor has just looked at an invoke instruction
1025     * of some kind.
1026     *
1027     * @param visitor the DismantleBytecode object
1028     * @return this object
1029     */

1030    public BugInstance addCalledMethod(DismantleBytecode visitor) {
1031        return addMethod(MethodAnnotation.fromCalledMethod(visitor));
1032    }
1033
1034    /**
1035     * Add a method annotation.
1036     *
1037     * @param className name of class containing called method
1038     * @param methodName name of called method
1039     * @param methodSig signature of called method
1040     * @param isStatic true if called method is static, false if not
1041     * @return this object
1042     */

1043    public BugInstance addCalledMethod(String JavaDoc className, String JavaDoc methodName, String JavaDoc methodSig, boolean isStatic) {
1044        return addMethod(MethodAnnotation.fromCalledMethod(className, methodName, methodSig, isStatic));
1045    }
1046
1047    /**
1048     * Add a method annotation for the method which is called by given
1049     * instruction.
1050     *
1051     * @param methodGen the method containing the call
1052     * @param inv the InvokeInstruction
1053     * @return this object
1054     */

1055    public BugInstance addCalledMethod(MethodGen methodGen, InvokeInstruction inv) {
1056        ConstantPoolGen cpg = methodGen.getConstantPool();
1057        String JavaDoc className = inv.getClassName(cpg);
1058        String JavaDoc methodName = inv.getMethodName(cpg);
1059        String JavaDoc methodSig = inv.getSignature(cpg);
1060        addMethod(className, methodName, methodSig, inv.getOpcode() == Constants.INVOKESTATIC);
1061        describe("METHOD_CALLED");
1062        return this;
1063    }
1064    
1065    /**
1066     * Add a MethodAnnotation from an XMethod.
1067     *
1068     * @param xmethod the XMethod
1069     * @return this object
1070     */

1071    public BugInstance addMethod(XMethod xmethod) {
1072        addMethod(MethodAnnotation.fromXMethod(xmethod));
1073        return this;
1074    }
1075
1076    /**
1077     * Add a method annotation. If this is the first method annotation added,
1078     * it becomes the primary method annotation.
1079     *
1080     * @param methodAnnotation the method annotation
1081     * @return this object
1082     */

1083    public BugInstance addMethod(MethodAnnotation methodAnnotation) {
1084        add(methodAnnotation);
1085        return this;
1086    }
1087
1088    /* ----------------------------------------------------------------------
1089     * Integer annotation adders
1090     * ---------------------------------------------------------------------- */

1091
1092    /**
1093     * Add an integer annotation.
1094     *
1095     * @param value the integer value
1096     * @return this object
1097     */

1098    public BugInstance addInt(int value) {
1099        add(new IntAnnotation(value));
1100        return this;
1101    }
1102
1103    /**
1104     * Add a String annotation.
1105     *
1106     * @param value the String value
1107     * @return this object
1108     */

1109    public BugInstance addString(String JavaDoc value) {
1110        add(new StringAnnotation(value));
1111        return this;
1112    }
1113
1114    /* ----------------------------------------------------------------------
1115     * Source line annotation adders
1116     * ---------------------------------------------------------------------- */

1117
1118    /**
1119     * Add a source line annotation.
1120     *
1121     * @param sourceLine the source line annotation
1122     * @return this object
1123     */

1124    public BugInstance addSourceLine(SourceLineAnnotation sourceLine) {
1125        add(sourceLine);
1126        return this;
1127    }
1128
1129    /**
1130     * Add a source line annotation for instruction whose PC is given
1131     * in the method that the given visitor is currently visiting.
1132     * Note that if the method does not have line number information, then
1133     * no source line annotation will be added.
1134     *
1135     * @param visitor a BytecodeScanningDetector that is currently visiting the method
1136     * @param pc bytecode offset of the instruction
1137     * @return this object
1138     */

1139    public BugInstance addSourceLine(BytecodeScanningDetector visitor, int pc) {
1140        SourceLineAnnotation sourceLineAnnotation =
1141            SourceLineAnnotation.fromVisitedInstruction(visitor.getClassContext(), visitor, pc);
1142        if (sourceLineAnnotation != null)
1143            add(sourceLineAnnotation);
1144        return this;
1145    }
1146
1147    /**
1148     * Add a source line annotation for instruction whose PC is given
1149     * in the method that the given visitor is currently visiting.
1150     * Note that if the method does not have line number information, then
1151     * no source line annotation will be added.
1152     *
1153     * @param classContext the ClassContext
1154     * @param visitor a PreorderVisitor that is currently visiting the method
1155     * @param pc bytecode offset of the instruction
1156     * @return this object
1157     */

1158    public BugInstance addSourceLine(ClassContext classContext, PreorderVisitor visitor, int pc) {
1159        SourceLineAnnotation sourceLineAnnotation =
1160            SourceLineAnnotation.fromVisitedInstruction(classContext, visitor, pc);
1161        if (sourceLineAnnotation != null)
1162            add(sourceLineAnnotation);
1163        return this;
1164    }
1165
1166    /**
1167     * Add a source line annotation for the given instruction in the given method.
1168     * Note that if the method does not have line number information, then
1169     * no source line annotation will be added.
1170     *
1171     * @param classContext the ClassContext
1172     * @param methodGen the method being visited
1173     * @param sourceFile source file the method is defined in
1174     * @param handle the InstructionHandle containing the visited instruction
1175     * @return this object
1176     */

1177    public BugInstance addSourceLine(ClassContext classContext, MethodGen methodGen, String JavaDoc sourceFile, @NonNull InstructionHandle handle) {
1178        SourceLineAnnotation sourceLineAnnotation =
1179            SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen, sourceFile, handle);
1180        if (sourceLineAnnotation != null)
1181            add(sourceLineAnnotation);
1182        return this;
1183    }
1184
1185    /**
1186     * Add a source line annotation describing a range of instructions.
1187     *
1188     * @param classContext the ClassContext
1189     * @param methodGen the method
1190     * @param sourceFile source file the method is defined in
1191     * @param start the start instruction in the range
1192     * @param end the end instruction in the range (inclusive)
1193     * @return this object
1194     */

1195    public BugInstance addSourceLine(ClassContext classContext, MethodGen methodGen, String JavaDoc sourceFile, InstructionHandle start, InstructionHandle end) {
1196        // Make sure start and end are really in the right order.
1197
if (start.getPosition() > end.getPosition()) {
1198            InstructionHandle tmp = start;
1199            start = end;
1200            end = tmp;
1201        }
1202        SourceLineAnnotation sourceLineAnnotation =
1203            SourceLineAnnotation.fromVisitedInstructionRange(classContext, methodGen, sourceFile, start, end);
1204        if (sourceLineAnnotation != null)
1205            add(sourceLineAnnotation);
1206        return this;
1207    }
1208
1209    /**
1210     * Add source line annotation for given Location in a method.
1211     *
1212     * @param classContext the ClassContext
1213     * @param method the Method
1214     * @param location the Location in the method
1215     * @return this BugInstance
1216     */

1217    public BugInstance addSourceLine(ClassContext classContext, Method method, Location location) {
1218        MethodGen methodGen = classContext.getMethodGen(method);
1219        return addSourceLine(
1220                classContext,
1221                methodGen,
1222                classContext.getJavaClass().getSourceFileName(),
1223                location.getHandle());
1224    }
1225
1226    /**
1227     * Add a source line annotation describing the
1228     * source line numbers for a range of instructions in the method being
1229     * visited by the given visitor.
1230     * Note that if the method does not have line number information, then
1231     * no source line annotation will be added.
1232     *
1233     * @param visitor a BetterVisitor which is visiting the method
1234     * @param startPC the bytecode offset of the start instruction in the range
1235     * @param endPC the bytecode offset of the end instruction in the range
1236     * @return this object
1237     */

1238    public BugInstance addSourceLineRange(BytecodeScanningDetector visitor, int startPC, int endPC) {
1239        SourceLineAnnotation sourceLineAnnotation =
1240            SourceLineAnnotation.fromVisitedInstructionRange(visitor.getClassContext(), visitor, startPC, endPC);
1241        if (sourceLineAnnotation != null)
1242            add(sourceLineAnnotation);
1243        return this;
1244    }
1245
1246    /**
1247     * Add a source line annotation describing the
1248     * source line numbers for a range of instructions in the method being
1249     * visited by the given visitor.
1250     * Note that if the method does not have line number information, then
1251     * no source line annotation will be added.
1252     *
1253     * @param classContext the ClassContext
1254     * @param visitor a BetterVisitor which is visiting the method
1255     * @param startPC the bytecode offset of the start instruction in the range
1256     * @param endPC the bytecode offset of the end instruction in the range
1257     * @return this object
1258     */

1259    public BugInstance addSourceLineRange(ClassContext classContext, PreorderVisitor visitor, int startPC, int endPC) {
1260        SourceLineAnnotation sourceLineAnnotation =
1261            SourceLineAnnotation.fromVisitedInstructionRange(classContext, visitor, startPC, endPC);
1262        if (sourceLineAnnotation != null)
1263            add(sourceLineAnnotation);
1264        return this;
1265    }
1266
1267    /**
1268     * Add a source line annotation for instruction currently being visited
1269     * by given visitor.
1270     * Note that if the method does not have line number information, then
1271     * no source line annotation will be added.
1272     *
1273     * @param visitor a BytecodeScanningDetector visitor that is currently visiting the instruction
1274     * @return this object
1275     */

1276    public BugInstance addSourceLine(BytecodeScanningDetector visitor) {
1277        SourceLineAnnotation sourceLineAnnotation =
1278            SourceLineAnnotation.fromVisitedInstruction(visitor);
1279        if (sourceLineAnnotation != null)
1280            add(sourceLineAnnotation);
1281        return this;
1282    }
1283
1284    /**
1285     * Add a non-specific source line annotation.
1286     * This will result in the entire source file being displayed.
1287     *
1288     * @param className the class name
1289     * @param sourceFile the source file name
1290     * @return this object
1291     */

1292    public BugInstance addUnknownSourceLine(String JavaDoc className, String JavaDoc sourceFile) {
1293        SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.createUnknown(className, sourceFile);
1294        if (sourceLineAnnotation != null)
1295            add(sourceLineAnnotation);
1296        return this;
1297    }
1298
1299    /* ----------------------------------------------------------------------
1300     * Formatting support
1301     * ---------------------------------------------------------------------- */

1302
1303    /**
1304     * Format a string describing this bug instance.
1305     *
1306     * @return the description
1307     */

1308    public String JavaDoc getMessageWithoutPrefix() {
1309        BugPattern bugPattern = I18N.instance().lookupBugPattern(type);
1310        String JavaDoc pattern, shortPattern;
1311        if (bugPattern == null)
1312            shortPattern = pattern = "Error: missing bug pattern for key " + type;
1313        else {
1314            pattern = bugPattern.getLongDescription();
1315            shortPattern = bugPattern.getShortDescription();
1316        }
1317        try {
1318            FindBugsMessageFormat format = new FindBugsMessageFormat(pattern);
1319            return format.format(annotationList.toArray(new BugAnnotation[annotationList.size()]), getPrimaryClass());
1320        } catch (RuntimeException JavaDoc e) {
1321            AnalysisContext.logError("Error generating bug msg ", e);
1322            return shortPattern + " [Error generating customized description]";
1323        }
1324    }
1325    /**
1326     * Format a string describing this bug instance.
1327     *
1328     * @return the description
1329     */

1330    public String JavaDoc getMessage() {
1331        String JavaDoc pattern = I18N.instance().getMessage(type);
1332        FindBugsMessageFormat format = new FindBugsMessageFormat(pattern);
1333        try {
1334            return format.format(annotationList.toArray(new BugAnnotation[annotationList.size()]), getPrimaryClass());
1335        } catch (RuntimeException JavaDoc e) {
1336            AnalysisContext.logError("Error generating bug msg ", e);
1337            BugPattern bugPattern = I18N.instance().lookupBugPattern(type);
1338            if (bugPattern == null)
1339                return "Error: missing bug pattern for key " + type;
1340            return bugPattern.getShortDescription() + " [Error generating customized description]";
1341        }
1342    }
1343
1344    /**
1345     * Add a description to the most recently added bug annotation.
1346     *
1347     * @param description the description to add
1348     * @return this object
1349     */

1350    public BugInstance describe(String JavaDoc description) {
1351        annotationList.get(annotationList.size() - 1).setDescription(description);
1352        return this;
1353    }
1354
1355    /**
1356     * Convert to String.
1357     * This method returns the "short" message describing the bug,
1358     * as opposed to the longer format returned by getMessage().
1359     * The short format is appropriate for the tree view in a GUI,
1360     * where the annotations are listed separately as part of the overall
1361     * bug instance.
1362     */

1363    @Override JavaDoc
1364    public String JavaDoc toString() {
1365        return I18N.instance().getShortMessage(type);
1366    }
1367
1368    /* ----------------------------------------------------------------------
1369     * XML Conversion support
1370     * ---------------------------------------------------------------------- */

1371
1372    public void writeXML(XMLOutput xmlOutput) throws IOException JavaDoc {
1373        writeXML(xmlOutput, false);
1374    }
1375    
1376
1377    public void writeXML(XMLOutput xmlOutput, boolean addMessages) throws IOException JavaDoc {
1378        XMLAttributeList attributeList = new XMLAttributeList()
1379            .addAttribute("type", type)
1380            .addAttribute("priority", String.valueOf(priority));
1381
1382        BugPattern pattern = getBugPattern();
1383        if (pattern != null) {
1384            // The bug abbreviation and pattern category are
1385
// emitted into the XML for informational purposes only.
1386
// (The information is redundant, but might be useful
1387
// for processing tools that want to make sense of
1388
// bug instances without looking at the plugin descriptor.)
1389
attributeList.addAttribute("abbrev", pattern.getAbbrev());
1390            attributeList.addAttribute("category", pattern.getCategory());
1391        }
1392        
1393        
1394        if (addMessages) {
1395            // Add a uid attribute, if we have a unique id.
1396
if (getUniqueId() != null) {
1397                attributeList.addAttribute("uid", getUniqueId());
1398            }
1399            attributeList.addAttribute("instanceHash", getInstanceHash());
1400            attributeList.addAttribute("instanceOccurrenceNum", Integer.toString(getInstanceOccurrenceNum()));
1401            attributeList.addAttribute("instanceOccurrenceMax", Integer.toString(getInstanceOccurrenceMax()));
1402        
1403        }
1404        if (firstVersion > 0) attributeList.addAttribute("first", Long.toString(firstVersion));
1405        if (lastVersion >= 0) attributeList.addAttribute("last", Long.toString(lastVersion));
1406        if (introducedByChangeOfExistingClass)
1407            attributeList.addAttribute("introducedByChange", "true");
1408        if (removedByChangeOfPersistingClass)
1409            attributeList.addAttribute("removedByChange", "true");
1410
1411        xmlOutput.openTag(ELEMENT_NAME, attributeList);
1412
1413        if (userDesignation != null) {
1414            userDesignation.writeXML(xmlOutput);
1415        }
1416
1417        if (addMessages) {
1418            BugPattern bugPattern = getBugPattern();
1419            
1420            xmlOutput.openTag("ShortMessage");
1421            xmlOutput.writeText(bugPattern != null ? bugPattern.getShortDescription() : this.toString());
1422            xmlOutput.closeTag("ShortMessage");
1423            
1424            xmlOutput.openTag("LongMessage");
1425            xmlOutput.writeText(this.getMessageWithoutPrefix());
1426            xmlOutput.closeTag("LongMessage");
1427        }
1428
1429        boolean foundSourceAnnotation = false;
1430        for (BugAnnotation annotation : annotationList) {
1431            if (annotation instanceof SourceLineAnnotation)
1432                foundSourceAnnotation = true;
1433            annotation.writeXML(xmlOutput, addMessages);
1434        }
1435        if (!foundSourceAnnotation && addMessages) {
1436            SourceLineAnnotation synth = getPrimarySourceLineAnnotation();
1437            if (synth != null) {
1438                synth.setSynthetic(true);
1439                synth.writeXML(xmlOutput, addMessages);
1440            }
1441        }
1442        
1443        if (propertyListHead != null) {
1444            BugProperty prop = propertyListHead;
1445            while (prop != null) {
1446                prop.writeXML(xmlOutput);
1447                prop = prop.getNext();
1448            }
1449        }
1450
1451        xmlOutput.closeTag(ELEMENT_NAME);
1452    }
1453
1454    private static final String JavaDoc ELEMENT_NAME = "BugInstance";
1455    private static final String JavaDoc USER_ANNOTATION_ELEMENT_NAME = "UserAnnotation";
1456
1457    /* ----------------------------------------------------------------------
1458     * Implementation
1459     * ---------------------------------------------------------------------- */

1460
1461    public BugInstance add(BugAnnotation annotation) {
1462        if (annotation == null)
1463            throw new IllegalStateException JavaDoc("Missing BugAnnotation!");
1464
1465        // Add to list
1466
annotationList.add(annotation);
1467
1468        // This object is being modified, so the cached hashcode
1469
// must be invalidated
1470
cachedHashCode = INVALID_HASH_CODE;
1471        return this;
1472    }
1473
1474    private void addSourceLinesForMethod(MethodAnnotation methodAnnotation, SourceLineAnnotation sourceLineAnnotation) {
1475        if (sourceLineAnnotation != null) {
1476            // Note: we don't add the source line annotation directly to
1477
// the bug instance. Instead, we stash it in the MethodAnnotation.
1478
// It is much more useful there, and it would just be distracting
1479
// if it were displayed in the UI, since it would compete for attention
1480
// with the actual bug location source line annotation (which is much
1481
// more important and interesting).
1482
methodAnnotation.setSourceLines(sourceLineAnnotation);
1483        }
1484    }
1485
1486    @Override JavaDoc
1487    public int hashCode() {
1488        if (cachedHashCode == INVALID_HASH_CODE) {
1489            int hashcode = type.hashCode() + priority;
1490            Iterator JavaDoc<BugAnnotation> i = annotationIterator();
1491            while (i.hasNext())
1492                hashcode += i.next().hashCode();
1493            if (hashcode == INVALID_HASH_CODE)
1494                hashcode = INVALID_HASH_CODE+1;
1495            cachedHashCode = hashcode;
1496        }
1497
1498        return cachedHashCode;
1499    }
1500
1501    @Override JavaDoc
1502    public boolean equals(Object JavaDoc o) {
1503        if (!(o instanceof BugInstance))
1504            return false;
1505        BugInstance other = (BugInstance) o;
1506        if (!type.equals(other.type) || priority != other.priority)
1507            return false;
1508        if (annotationList.size() != other.annotationList.size())
1509            return false;
1510        int numAnnotations = annotationList.size();
1511        for (int i = 0; i < numAnnotations; ++i) {
1512            BugAnnotation lhs = annotationList.get(i);
1513            BugAnnotation rhs = other.annotationList.get(i);
1514            if (!lhs.equals(rhs))
1515                return false;
1516        }
1517
1518        return true;
1519    }
1520
1521    public int compareTo(BugInstance other) {
1522        int cmp;
1523        cmp = type.compareTo(other.type);
1524        if (cmp != 0)
1525            return cmp;
1526        cmp = priority - other.priority;
1527        if (cmp != 0)
1528            return cmp;
1529
1530        // Compare BugAnnotations lexicographically
1531
int pfxLen = Math.min(annotationList.size(), other.annotationList.size());
1532        for (int i = 0; i < pfxLen; ++i) {
1533            BugAnnotation lhs = annotationList.get(i);
1534            BugAnnotation rhs = other.annotationList.get(i);
1535            cmp = lhs.compareTo(rhs);
1536            if (cmp != 0)
1537                return cmp;
1538        }
1539
1540        // All elements in prefix were the same,
1541
// so use number of elements to decide
1542
return annotationList.size() - other.annotationList.size();
1543    }
1544
1545    /**
1546     * @param firstVersion The firstVersion to set.
1547     */

1548    public void setFirstVersion(long firstVersion) {
1549        this.firstVersion = firstVersion;
1550        if (lastVersion >= 0 && firstVersion > lastVersion)
1551            throw new IllegalArgumentException JavaDoc(
1552                firstVersion + ".." + lastVersion);
1553    }
1554
1555    /**
1556     * @return Returns the firstVersion.
1557     */

1558    public long getFirstVersion() {
1559        return firstVersion;
1560    }
1561
1562    /**
1563     * @param lastVersion The lastVersion to set.
1564     */

1565    public void setLastVersion(long lastVersion) {
1566        if (lastVersion >= 0 && firstVersion > lastVersion)
1567            throw new IllegalArgumentException JavaDoc(
1568                firstVersion + ".." + lastVersion);
1569        this.lastVersion = lastVersion;
1570    }
1571
1572    /**
1573     * @return Returns the lastVersion.
1574     */

1575    public long getLastVersion() {
1576        return lastVersion;
1577    }
1578
1579    /**
1580     * @param introducedByChangeOfExistingClass The introducedByChangeOfExistingClass to set.
1581     */

1582    public void setIntroducedByChangeOfExistingClass(boolean introducedByChangeOfExistingClass) {
1583        this.introducedByChangeOfExistingClass = introducedByChangeOfExistingClass;
1584    }
1585
1586    /**
1587     * @return Returns the introducedByChangeOfExistingClass.
1588     */

1589    public boolean isIntroducedByChangeOfExistingClass() {
1590        return introducedByChangeOfExistingClass;
1591    }
1592
1593    /**
1594     * @param removedByChangeOfPersistingClass The removedByChangeOfPersistingClass to set.
1595     */

1596    public void setRemovedByChangeOfPersistingClass(boolean removedByChangeOfPersistingClass) {
1597        this.removedByChangeOfPersistingClass = removedByChangeOfPersistingClass;
1598    }
1599
1600    /**
1601     * @return Returns the removedByChangeOfPersistingClass.
1602     */

1603    public boolean isRemovedByChangeOfPersistingClass() {
1604        return removedByChangeOfPersistingClass;
1605    }
1606
1607    /**
1608     * @param instanceHash The instanceHash to set.
1609     */

1610    public void setInstanceHash(String JavaDoc instanceHash) {
1611        this.instanceHash = instanceHash;
1612    }
1613    /**
1614     * @param oldInstanceHash The oldInstanceHash to set.
1615     */

1616    public void setOldInstanceHash(String JavaDoc oldInstanceHash) {
1617        this.oldInstanceHash = oldInstanceHash;
1618    }
1619    /**
1620     * @return Returns the instanceHash.
1621     */

1622    public String JavaDoc getInstanceHash() {
1623        if (instanceHash != null) return instanceHash;
1624            MessageDigest JavaDoc digest = null;
1625            try { digest = MessageDigest.getInstance("MD5");
1626            } catch (Exception JavaDoc e2) {
1627                // OK, we won't digest
1628
}
1629            instanceHash = getInstanceKey();
1630            if (digest != null) {
1631                byte [] data = digest.digest(instanceHash.getBytes());
1632                String JavaDoc tmp = new BigInteger JavaDoc(1,data).toString(16);
1633                instanceHash = tmp;
1634            }
1635            return instanceHash;
1636    }
1637
1638    public boolean isInstanceHashConsistent() {
1639        return oldInstanceHash == null || instanceHash.equals(oldInstanceHash);
1640    }
1641    /**
1642     * @param instanceOccurrenceNum The instanceOccurrenceNum to set.
1643     */

1644    public void setInstanceOccurrenceNum(int instanceOccurrenceNum) {
1645        this.instanceOccurrenceNum = instanceOccurrenceNum;
1646    }
1647
1648    /**
1649     * @return Returns the instanceOccurrenceNum.
1650     */

1651    public int getInstanceOccurrenceNum() {
1652        return instanceOccurrenceNum;
1653    }
1654
1655    /**
1656     * @param instanceOccurrenceMax The instanceOccurrenceMax to set.
1657     */

1658    public void setInstanceOccurrenceMax(int instanceOccurrenceMax) {
1659        this.instanceOccurrenceMax = instanceOccurrenceMax;
1660    }
1661
1662    /**
1663     * @return Returns the instanceOccurrenceMax.
1664     */

1665    public int getInstanceOccurrenceMax() {
1666        return instanceOccurrenceMax;
1667    }
1668}
1669
1670// vim:ts=4
1671
Popular Tags