1 19 20 package edu.umd.cs.findbugs.ba; 21 22 import java.io.IOException ; 23 import java.nio.ByteBuffer ; 24 import java.nio.CharBuffer ; 25 import java.nio.charset.CharacterCodingException ; 26 import java.nio.charset.Charset ; 27 import java.nio.charset.CharsetEncoder ; 28 import java.security.MessageDigest ; 29 import java.security.NoSuchAlgorithmException ; 30 import java.util.Arrays ; 31 import java.util.Comparator ; 32 import java.util.HashMap ; 33 import java.util.Map ; 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 49 public class ClassHash implements XMLWriteable, Comparable <ClassHash> { 50 53 public static final String CLASS_HASH_ELEMENT_NAME = "ClassHash"; 54 55 58 public static final String METHOD_HASH_ELEMENT_NAME = "MethodHash"; 59 60 private String className; 62 private byte[] classHash; 63 private Map <XMethod, MethodHash> methodHashMap; 64 65 68 public ClassHash() { 69 this.methodHashMap = new HashMap <XMethod, MethodHash>(); 70 } 71 72 77 public ClassHash(String 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 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 101 public String getClassName() { 102 return className; 103 } 104 105 110 public byte[] getClassHash() { 111 return classHash; 112 } 113 114 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 130 public MethodHash getMethodHash(XMethod method) { 131 return methodHashMap.get(method); 132 } 133 134 140 public ClassHash computeHash(JavaClass javaClass) { 141 this.className = javaClass.getClassName(); 142 143 Method[] methodList = new Method[javaClass.getMethods().length]; 144 145 System.arraycopy(javaClass.getMethods(), 0, methodList, 0, javaClass.getMethods().length); 147 Arrays.sort(methodList, new Comparator <Method>() { 148 public int compare(Method o1, Method o2) { 149 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 System.arraycopy(javaClass.getFields(), 0, fieldList, 0, javaClass.getFields().length); 162 Arrays.sort(fieldList, new Comparator <Field>() { 163 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 digest; 175 try { 176 digest = MessageDigest.getInstance("MD5"); 177 } catch (NoSuchAlgorithmException e) { 178 throw new IllegalStateException ("No algorithm for computing class hash", e); 179 } 180 181 CharsetEncoder 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 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 digest, String s, CharsetEncoder encoder) { 204 try { 205 CharBuffer cbuf = CharBuffer.allocate(s.length()); 206 cbuf.put(s); 207 cbuf.flip(); 208 209 ByteBuffer buf = encoder.encode(cbuf); 210 int nbytes = buf.limit(); 212 byte[] encodedBytes = new byte[nbytes]; 213 buf.get(encodedBytes); 214 215 digest.update(encodedBytes); 216 } catch (CharacterCodingException e) { 217 } 219 } 220 221 public void writeXML(XMLOutput xmlOutput) throws IOException { 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 <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 249 public static String hashToString(byte[] hash) { 250 StringBuffer buf = new StringBuffer (); 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 ("Illegal hex character: " + c); 267 } 268 269 275 public static byte[] stringToHash(String s) { 276 if (s.length() % 2 != 0) 277 throw new IllegalArgumentException ("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 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 o) { 309 if (!(o instanceof ClassHash)) return false; 310 return isSameHash((ClassHash)o); 311 } 312 315 public int compareTo(ClassHash other) { 316 int cmp = MethodHash.compareHashes(this.classHash, other.classHash); 317 return cmp; 319 } 320 321 324 @Override 326 public String toString() { 327 return getClassName() + ":" + hashToString(this.classHash); 328 } 329 } 330 | Popular Tags |