KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > umd > cs > findbugs > ba > ClassHash


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.ba;
21
22 import java.io.IOException JavaDoc;
23 import java.nio.ByteBuffer JavaDoc;
24 import java.nio.CharBuffer JavaDoc;
25 import java.nio.charset.CharacterCodingException JavaDoc;
26 import java.nio.charset.Charset JavaDoc;
27 import java.nio.charset.CharsetEncoder JavaDoc;
28 import java.security.MessageDigest JavaDoc;
29 import java.security.NoSuchAlgorithmException JavaDoc;
30 import java.util.Arrays JavaDoc;
31 import java.util.Comparator JavaDoc;
32 import java.util.HashMap JavaDoc;
33 import java.util.Map JavaDoc;
34
35 import org.apache.bcel.classfile.Field;
36 import org.apache.bcel.classfile.JavaClass;
37 import org.apache.bcel.classfile.Method;
38
39 import edu.umd.cs.findbugs.xml.XMLOutput;
40 import edu.umd.cs.findbugs.xml.XMLWriteable;
41
42 /**
43  * Compute a hash of method names and signatures.
44  * This allows us to find out when a class has been renamed, but
45  * not changed in any other obvious way.
46  *
47  * @author David Hovemeyer
48  */

49 public class ClassHash implements XMLWriteable, Comparable JavaDoc<ClassHash> {
50     /**
51      * XML element name for a ClassHash.
52      */

53     public static final String JavaDoc CLASS_HASH_ELEMENT_NAME = "ClassHash";
54     
55     /**
56      * XML element name for a MethodHash.
57      */

58     public static final String JavaDoc METHOD_HASH_ELEMENT_NAME = "MethodHash";
59     
60     // Fields
61
private String JavaDoc className;
62     private byte[] classHash;
63     private Map JavaDoc<XMethod, MethodHash> methodHashMap;
64     
65     /**
66      * Constructor.
67      */

68     public ClassHash() {
69         this.methodHashMap = new HashMap JavaDoc<XMethod, MethodHash>();
70     }
71
72     /**
73      * Constructor.
74      *
75      * @param classHash pre-computed class hash
76      */

77     public ClassHash(String JavaDoc className, byte[] classHash) {
78         this();
79         this.className = className;
80         this.classHash = new byte[classHash.length];
81         System.arraycopy(classHash, 0, this.classHash, 0, classHash.length);
82     }
83
84     /**
85      * Set method hash for given method.
86      *
87      * @param method the method
88      * @param methodHash the method hash
89      */

90     public void setMethodHash(XMethod method, byte[] methodHash) {
91         methodHashMap.put(method, new MethodHash(
92                 method.getName(),
93                 method.getSignature(),
94                 method.isStatic(),
95                 methodHash));
96     }
97     
98     /**
99      * @return Returns the className.
100      */

101     public String JavaDoc getClassName() {
102         return className;
103     }
104
105     /**
106      * Get class hash.
107      *
108      * @return the class hash
109      */

110     public byte[] getClassHash() {
111         return classHash;
112     }
113     
114     /**
115      * Set class hash.
116      *
117      * @param classHash the class hash value to set
118      */

119     public void setClassHash(byte[] classHash) {
120         this.classHash= new byte[classHash.length];
121         System.arraycopy(classHash, 0, this.classHash, 0, classHash.length);
122     }
123     
124     /**
125      * Get method hash for given method.
126      *
127      * @param method the method
128      * @return the MethodHash
129      */

130     public MethodHash getMethodHash(XMethod method) {
131         return methodHashMap.get(method);
132     }
133     
134     /**
135      * Compute hash for given class and all of its methods.
136      *
137      * @param javaClass the class
138      * @return this object
139      */

140     public ClassHash computeHash(JavaClass javaClass) {
141         this.className = javaClass.getClassName();
142         
143         Method[] methodList = new Method[javaClass.getMethods().length];
144         
145         // Sort methods
146
System.arraycopy(javaClass.getMethods(), 0, methodList, 0, javaClass.getMethods().length);
147         Arrays.sort(methodList, new Comparator JavaDoc<Method>() {
148             public int compare(Method o1, Method o2) {
149                 // sort by name, then signature
150
int cmp = o1.getName().compareTo(o2.getName());
151                 if (cmp != 0)
152                     return cmp;
153                 return o1.getSignature().compareTo(o2.getSignature());
154                 
155             }
156         });
157         
158         Field[] fieldList = new Field[javaClass.getFields().length];
159         
160         // Sort fields
161
System.arraycopy(javaClass.getFields(), 0, fieldList, 0, javaClass.getFields().length);
162         Arrays.sort(fieldList, new Comparator JavaDoc<Field>() {
163             /* (non-Javadoc)
164              * @see java.util.Comparator#compare(T, T)
165              */

166             public int compare(Field o1, Field o2) {
167                 int cmp = o1.getName().compareTo(o2.getName());
168                 if (cmp != 0)
169                     return cmp;
170                 return o1.getSignature().compareTo(o2.getSignature());
171             }
172         });
173         
174         MessageDigest JavaDoc digest;
175         try {
176             digest = MessageDigest.getInstance("MD5");
177         } catch (NoSuchAlgorithmException JavaDoc e) {
178             throw new IllegalStateException JavaDoc("No algorithm for computing class hash", e);
179         }
180         
181         // Compute digest of method names and signatures, in order.
182
// Also, compute method hashes.
183
CharsetEncoder JavaDoc encoder = Charset.forName("UTF-8").newEncoder();
184         for (Method method : methodList) {
185             work(digest, method.getName(), encoder);
186             work(digest, method.getSignature(), encoder);
187             
188             MethodHash methodHash = new MethodHash().computeHash(method);
189             methodHashMap.put(XFactory.createXMethod(javaClass, method), methodHash);
190         }
191         
192         // Compute digest of field names and signatures.
193
for (Field field : fieldList) {
194             work(digest, field.getName(), encoder);
195             work(digest, field.getSignature(), encoder);
196         }
197         
198         classHash = digest.digest();
199         
200         return this;
201     }
202     
203     private static void work(MessageDigest JavaDoc digest, String JavaDoc s, CharsetEncoder JavaDoc encoder) {
204         try {
205             CharBuffer JavaDoc cbuf = CharBuffer.allocate(s.length());
206             cbuf.put(s);
207             cbuf.flip();
208             
209             ByteBuffer JavaDoc buf = encoder.encode(cbuf);
210 // System.out.println("pos="+buf.position() +",limit=" + buf.limit());
211
int nbytes = buf.limit();
212             byte[] encodedBytes = new byte[nbytes];
213             buf.get(encodedBytes);
214             
215             digest.update(encodedBytes);
216         } catch (CharacterCodingException JavaDoc e) {
217             // This should never happen, since we're encoding to UTF-8.
218
}
219     }
220     
221     public void writeXML(XMLOutput xmlOutput) throws IOException JavaDoc {
222         xmlOutput.startTag(CLASS_HASH_ELEMENT_NAME);
223         xmlOutput.addAttribute("class", className);
224         xmlOutput.addAttribute("value", hashToString(classHash));
225         xmlOutput.stopTag(false);
226
227         for (Map.Entry JavaDoc<XMethod, MethodHash> entry : methodHashMap.entrySet()) {
228             xmlOutput.startTag(METHOD_HASH_ELEMENT_NAME);
229             xmlOutput.addAttribute("name", entry.getKey().getName());
230             xmlOutput.addAttribute("signature", entry.getKey().getSignature());
231             xmlOutput.addAttribute("isStatic", String.valueOf(entry.getKey().isStatic()));
232             xmlOutput.addAttribute("value", hashToString(entry.getValue().getMethodHash()));
233             xmlOutput.stopTag(true);
234         }
235         
236         xmlOutput.closeTag(CLASS_HASH_ELEMENT_NAME);
237     }
238     
239     private static final char[] HEX_CHARS = {
240         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
241     };
242
243     /**
244      * Convert a hash to a string of hex digits.
245      *
246      * @param hash the hash
247      * @return a String representation of the hash
248      */

249     public static String JavaDoc hashToString(byte[] hash) {
250         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
251         for (byte b : hash) {
252             buf.append(HEX_CHARS[(b >> 4) & 0xF]);
253             buf.append(HEX_CHARS[b & 0xF]);
254         }
255         return buf.toString();
256     }
257
258     private static int hexDigitValue(char c) {
259         if (c >= '0' && c <= '9')
260             return c - '0';
261         else if (c >= 'a' && c <= 'f')
262             return 10 + (c - 'a');
263         else if (c >= 'A' && c <= 'F')
264             return 10 + (c - 'A');
265         else
266             throw new IllegalArgumentException JavaDoc("Illegal hex character: " + c);
267     }
268     
269     /**
270      * Convert a string of hex digits to a hash.
271      *
272      * @param s string of hex digits
273      * @return the hash value represented by the string
274      */

275     public static byte[] stringToHash(String JavaDoc s) {
276         if (s.length() % 2 != 0)
277             throw new IllegalArgumentException JavaDoc("Invalid hash string: " + s);
278         byte[] hash = new byte[s.length() / 2];
279         for (int i = 0; i < s.length(); i += 2) {
280             byte b = (byte) ((hexDigitValue(s.charAt(i)) << 4) + hexDigitValue(s.charAt(i+1)));
281             hash[i / 2] = b;
282         }
283         return hash;
284     }
285     
286     /**
287      * Return whether or not this class hash has the same hash value
288      * as the one given.
289      *
290      * @param other another ClassHash
291      * @return true if the hash values are the same, false if not
292      */

293     public boolean isSameHash(ClassHash other) {
294         return Arrays.equals(classHash, other.classHash);
295     }
296
297     public int hashCode() {
298         if (classHash == null)
299             return 0;
300
301         int result = 1;
302         for (byte element : classHash)
303             result = 31 * result + element;
304
305         return result;
306
307     }
308     public boolean equals(Object JavaDoc o) {
309         if (!(o instanceof ClassHash)) return false;
310         return isSameHash((ClassHash)o);
311     }
312     /* (non-Javadoc)
313      * @see java.lang.Comparable#compareTo(T)
314      */

315     public int compareTo(ClassHash other) {
316         int cmp = MethodHash.compareHashes(this.classHash, other.classHash);
317         //System.out.println(this + " <=> " + other + ": compareTo=" + cmp);
318
return cmp;
319     }
320         
321     /* (non-Javadoc)
322      * @see java.lang.Object#toString()
323      */

324     //@Override
325
@Override JavaDoc
326          public String JavaDoc toString() {
327         return getClassName() + ":" + hashToString(this.classHash);
328     }
329 }
330
Popular Tags