KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * FindBugs - Find Bugs in Java programs
3  * Copyright (C) 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.Serializable JavaDoc;
23 import java.util.HashSet JavaDoc;
24 import java.util.IdentityHashMap JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.Map JavaDoc;
27 import java.util.NoSuchElementException JavaDoc;
28 import java.util.TreeMap JavaDoc;
29
30 import edu.umd.cs.findbugs.ba.ClassHash;
31 import edu.umd.cs.findbugs.model.ClassNameRewriter;
32
33 /**
34  * A slightly more intellegent way of comparing BugInstances from two versions
35  * to see if they are the "same". Uses class and method hashes to try to
36  * handle renamings, at least for simple cases. (<em>Hashes disabled for the
37  * time being.</em>) Uses opcode context to try to identify code that is the
38  * same, even if it moves within the method. Also compares by bug abbreviation
39  * rather than bug type, since the "same" bug can change type if the context
40  * changes (e.g., "definitely null" to "null on simple path" for a null pointer
41  * dereference). Also, we often change bug types between different versions
42  * of FindBugs.
43  *
44  * @see edu.umd.cs.findbugs.BugInstance
45  * @see edu.umd.cs.findbugs.VersionInsensitiveBugComparator
46  * @author David Hovemeyer
47  */

48 public class FuzzyBugComparator implements WarningComparator {
49     private static final boolean DEBUG = false;
50
51     // Don't use hashes for now. Still ironing out issues there.
52
private static final boolean USE_HASHES = false;
53
54     private static final long serialVersionUID = 1L;
55
56     /**
57      * Filter ignored BugAnnotations from given Iterator.
58      */

59     private static class FilteringBugAnnotationIterator implements Iterator JavaDoc<BugAnnotation> {
60         
61         Iterator JavaDoc<BugAnnotation> iter;
62         BugAnnotation next;
63         
64         public FilteringBugAnnotationIterator(Iterator JavaDoc<BugAnnotation> iter) {
65             this.iter = iter;
66         }
67         
68         private void findNext() {
69             if (next == null) {
70                 while (iter.hasNext()) {
71                     BugAnnotation candidate = iter.next();
72                     if (!ignore(candidate)) {
73                         next = candidate;
74                         break;
75                     }
76                 }
77             }
78         }
79         
80         /* (non-Javadoc)
81          * @see java.util.Iterator#hasNext()
82          */

83         public boolean hasNext() {
84             findNext();
85             return next != null;
86         }
87         
88         /* (non-Javadoc)
89          * @see java.util.Iterator#next()
90          */

91         public BugAnnotation next() {
92             findNext();
93             if (next == null)
94                 throw new NoSuchElementException JavaDoc();
95             BugAnnotation result = next;
96             next = null;
97             return result;
98         }
99         
100         /* (non-Javadoc)
101          * @see java.util.Iterator#remove()
102          */

103         public void remove() {
104             throw new UnsupportedOperationException JavaDoc();
105         }
106     }
107     
108     /** Keep track of which BugCollections the various BugInstances have come from. */
109     private IdentityHashMap JavaDoc<BugInstance, BugCollection> bugCollectionMap;
110     
111     private ClassNameRewriter classNameRewriter;
112     
113     /**
114      * Map of class hashes to canonicate class names used for comparison purposes.
115      */

116     //private Map<ClassHash, String> classHashToCanonicalClassNameMap;
117

118     public FuzzyBugComparator() {
119         if (DEBUG) System.out.println("Created fuzzy comparator");
120         this.bugCollectionMap = new IdentityHashMap JavaDoc<BugInstance, BugCollection>();
121         //this.classHashToCanonicalClassNameMap = new TreeMap<ClassHash, String>();
122
}
123     
124     /**
125      * Register a BugCollection. This allows us to find the class and method
126      * hashes for BugInstances to be compared.
127      *
128      * @param bugCollection a BugCollection
129      */

130     public void registerBugCollection(BugCollection bugCollection) {
131         // For now, nothing to do
132
}
133
134     /* (non-Javadoc)
135      * @see edu.umd.cs.findbugs.WarningComparator#setClassNameRewriter(edu.umd.cs.findbugs.model.MovedClassMap)
136      */

137     public void setClassNameRewriter(ClassNameRewriter classNameRewriter) {
138         this.classNameRewriter = classNameRewriter;
139     }
140     
141     public int compare(BugInstance lhs, BugInstance rhs) {
142         int cmp;
143         
144         if (DEBUG) System.out.println("Fuzzy comparison");
145         
146         // Bug abbreviations must match.
147
BugPattern lhsPattern = lhs.getBugPattern();
148         BugPattern rhsPattern = rhs.getBugPattern();
149         
150         if (lhsPattern == null || rhsPattern == null) {
151             if (DEBUG) {
152                 if (lhsPattern == null)
153                     System.out.println("Missing pattern: " + lhs.getType());
154                 if (rhsPattern == null)
155                     System.out.println("Missing pattern: " + rhs.getType());
156             }
157             String JavaDoc lhsCode = getCode(lhs.getType());
158             String JavaDoc rhsCode = getCode(rhs.getType());
159             if ((cmp = lhsCode.compareTo(rhsCode)) != 0)
160                 return cmp;
161         } else {
162             if ((cmp = lhsPattern.getAbbrev().compareTo(rhsPattern.getAbbrev())) != 0)
163                 return cmp;
164         }
165         
166         BugCollection lhsCollection = bugCollectionMap.get(lhs);
167         BugCollection rhsCollection = bugCollectionMap.get(rhs);
168         
169         // Scan through bug annotations, comparing fuzzily if possible
170

171         Iterator JavaDoc<BugAnnotation> lhsIter = new FilteringBugAnnotationIterator(lhs.annotationIterator());
172         Iterator JavaDoc<BugAnnotation> rhsIter = new FilteringBugAnnotationIterator(rhs.annotationIterator());
173         
174         while (lhsIter.hasNext() && rhsIter.hasNext()) {
175             BugAnnotation lhsAnnotation = lhsIter.next();
176             BugAnnotation rhsAnnotation = rhsIter.next();
177             
178             if (DEBUG) System.out.println("Compare annotations: " + lhsAnnotation + "," + rhsAnnotation);
179
180             // Annotation classes must match exactly
181
cmp = lhsAnnotation.getClass().getName().compareTo(rhsAnnotation.getClass().getName());
182             if (cmp != 0) {
183                 if (DEBUG) System.out.println("annotation class mismatch: " + lhsAnnotation.getClass().getName() +
184                         "," + rhsAnnotation.getClass().getName());
185                 return cmp;
186             }
187             
188             if (lhsAnnotation.getClass() == ClassAnnotation.class)
189                 cmp = compareClasses(lhsCollection, rhsCollection, (ClassAnnotation) lhsAnnotation, (ClassAnnotation) rhsAnnotation);
190             else if (lhsAnnotation.getClass() == MethodAnnotation.class)
191                 cmp = compareMethods(lhsCollection, rhsCollection, (MethodAnnotation) lhsAnnotation, (MethodAnnotation) rhsAnnotation);
192             else if (lhsAnnotation.getClass() == SourceLineAnnotation.class)
193                 cmp = compareSourceLines(lhsCollection, rhsCollection, (SourceLineAnnotation) lhsAnnotation, (SourceLineAnnotation) rhsAnnotation);
194             else
195                 // everything else just compare directly
196
cmp = lhsAnnotation.compareTo(rhsAnnotation);
197             
198             if (cmp != 0)
199                 return cmp;
200         }
201         
202         // Number of bug annotations must match
203
if (!lhsIter.hasNext() && !rhsIter.hasNext()) {
204             if (DEBUG) System.out.println("Match!");
205             return 0;
206         } else
207             return (lhsIter.hasNext() ? 1 : -1);
208     }
209
210     /**
211      * @param type
212      * @return the code of the Bug
213      */

214     private String JavaDoc getCode(String JavaDoc type) {
215         int bar = type.indexOf('_');
216         if (bar < 0)
217             return "";
218         else
219             return type.substring(0, bar);
220     }
221
222     private static int compareNullElements(Object JavaDoc a, Object JavaDoc b) {
223         if (a != null)
224             return 1;
225         else if (b != null)
226             return -1;
227         else
228             return 0;
229     }
230     
231     public int compareClasses(BugCollection lhsCollection, BugCollection rhsCollection, ClassAnnotation lhsClass, ClassAnnotation rhsClass) {
232         if (lhsClass == null || rhsClass == null) {
233             return compareNullElements(lhsClass, rhsClass);
234         } else {
235             return compareClassesByName(lhsCollection, rhsCollection, lhsClass.getClassName(), rhsClass.getClassName());
236         }
237     }
238     
239     // Compare classes: either exact fully qualified name must match, or class hash must match
240
public int compareClassesByName(BugCollection lhsCollection, BugCollection rhsCollection, String JavaDoc lhsClassName, String JavaDoc rhsClassName) {
241         
242         lhsClassName = rewriteClassName(lhsClassName);
243         rhsClassName = rewriteClassName(rhsClassName);
244
245         return lhsClassName.compareTo(rhsClassName);
246     }
247     
248     /**
249      * @param className
250      * @return the rewritten class name
251      */

252     private String JavaDoc rewriteClassName(String JavaDoc className) {
253         if (classNameRewriter != null) {
254             className = classNameRewriter.rewriteClassName(className);
255         }
256         return className;
257     }
258
259     // Compare methods: either exact name and signature must match, or method hash must match
260
public int compareMethods(BugCollection lhsCollection, BugCollection rhsCollection, MethodAnnotation lhsMethod, MethodAnnotation rhsMethod) {
261         if (lhsMethod == null || rhsMethod == null) {
262             return compareNullElements(lhsMethod, rhsMethod);
263         }
264
265         // Compare for exact match
266
int cmp = lhsMethod.compareTo(rhsMethod);
267         
268         return cmp;
269     }
270     
271     /**
272      * For now, just look at the 2 preceeding and succeeding opcodes
273      * for fuzzy source line matching.
274      */

275     private static final int NUM_CONTEXT_OPCODES = 2;
276     
277     /**
278      * Compare source line annotations.
279      *
280      * @param rhsCollection lhs BugCollection
281      * @param lhsCollection rhs BugCollection
282      * @param lhs a SourceLineAnnotation
283      * @param rhs another SourceLineAnnotation
284      * @return comparison of lhs and rhs
285      */

286     public int compareSourceLines(BugCollection lhsCollection, BugCollection rhsCollection, SourceLineAnnotation lhs, SourceLineAnnotation rhs) {
287         if (lhs == null || rhs == null) {
288             return compareNullElements(lhs, rhs);
289         }
290         
291         // Classes must match fuzzily.
292
int cmp = compareClassesByName(lhsCollection, rhsCollection, lhs.getClassName(), rhs.getClassName());
293         if (cmp != 0)
294             return cmp;
295         
296         return 0;
297     }
298     
299     // See "FindBugsAnnotationDescriptions.properties"
300
private static final HashSet JavaDoc<String JavaDoc> significantDescriptionSet = new HashSet JavaDoc<String JavaDoc>();
301     static {
302         // Classes, methods, and fields are significant.
303
significantDescriptionSet.add("CLASS_DEFAULT");
304         significantDescriptionSet.add("CLASS_EXCEPTION");
305         significantDescriptionSet.add("CLASS_REFTYPE");
306         significantDescriptionSet.add("INTERFACE_TYPE");
307         significantDescriptionSet.add("METHOD_DEFAULT");
308         significantDescriptionSet.add("METHOD_CALLED");
309         significantDescriptionSet.add("METHOD_DANGEROUS_TARGET"); // but do NOT use safe targets
310
significantDescriptionSet.add("METHOD_DECLARED_NONNULL");
311         significantDescriptionSet.add("FIELD_DEFAULT");
312         significantDescriptionSet.add("FIELD_ON");
313         significantDescriptionSet.add("FIELD_SUPER");
314         significantDescriptionSet.add("FIELD_MASKED");
315         significantDescriptionSet.add("FIELD_MASKING");
316         // Many int annotations are NOT significant: e.g., sync %, biased locked %, bytecode offset, etc.
317
// The null parameter annotations, however, are definitely significant.
318
significantDescriptionSet.add("INT_NULL_ARG");
319         significantDescriptionSet.add("INT_MAYBE_NULL_ARG");
320         significantDescriptionSet.add("INT_NONNULL_PARAM");
321         // Only DEFAULT source line annotations are significant.
322
significantDescriptionSet.add("SOURCE_LINE_DEFAULT");
323     }
324     
325     public static boolean ignore(BugAnnotation annotation) {
326         return !significantDescriptionSet.contains(annotation.getDescription());
327     }
328 }
329
Popular Tags