1 19 20 package edu.umd.cs.findbugs.model; 21 22 import java.io.IOException ; 23 import java.util.HashSet ; 24 import java.util.Iterator ; 25 import java.util.Set ; 26 27 import org.apache.bcel.Repository; 28 import org.apache.bcel.classfile.Code; 29 import org.apache.bcel.classfile.Field; 30 import org.apache.bcel.classfile.FieldOrMethod; 31 import org.apache.bcel.classfile.JavaClass; 32 import org.apache.bcel.classfile.Method; 33 34 import edu.umd.cs.findbugs.ba.Hierarchy; 35 import edu.umd.cs.findbugs.ba.JavaClassAndMethod; 36 import edu.umd.cs.findbugs.ba.SignatureParser; 37 import edu.umd.cs.findbugs.xml.XMLAttributeList; 38 import edu.umd.cs.findbugs.xml.XMLOutput; 39 import edu.umd.cs.findbugs.xml.XMLWriteable; 40 41 47 public class ClassFeatureSet implements XMLWriteable { 48 public static final String CLASS_NAME_KEY = "Class:"; 49 public static final String METHOD_NAME_KEY = "Method:"; 50 public static final String CODE_LENGTH_KEY = "CodeLength:"; 51 public static final String FIELD_NAME_KEY = "Field:"; 52 53 private String className; 54 private boolean isInterface; 55 private Set <String > featureSet; 56 57 61 public ClassFeatureSet() { 62 this.featureSet = new HashSet <String >(); 63 } 64 65 68 public static final int MIN_CODE_LENGTH = 10; 69 70 76 public ClassFeatureSet initialize(JavaClass javaClass) { 77 this.className = javaClass.getClassName(); 78 this.isInterface = javaClass.isInterface(); 79 80 addFeature(CLASS_NAME_KEY + transformClassName(javaClass.getClassName())); 81 82 for (Method method : javaClass.getMethods()) { 83 if (!isSynthetic(method)) { 84 String transformedMethodSignature = transformMethodSignature(method.getSignature()); 85 86 if (method.isStatic() || !overridesSuperclassMethod(javaClass, method)) { 87 addFeature(METHOD_NAME_KEY + method.getName() + ":" + transformedMethodSignature); 88 } 89 90 Code code = method.getCode(); 91 if (code != null && code.getCode() != null && code.getCode().length >= MIN_CODE_LENGTH) { 92 addFeature(CODE_LENGTH_KEY + method.getName() + ":" + transformedMethodSignature + 93 ":" + code.getCode().length); 94 } 95 } 96 } 97 98 for (Field field : javaClass.getFields()) { 99 if (!isSynthetic(field)) { 100 addFeature( 101 FIELD_NAME_KEY + field.getName() + ":" + 102 transformSignature(field.getSignature())); 103 } 104 } 105 106 return this; 107 } 108 109 117 private boolean overridesSuperclassMethod(JavaClass javaClass, Method method) { 118 if (method.isStatic()) 119 return false; 120 121 try { 122 JavaClass[] superclassList = javaClass.getSuperClasses(); 123 if (superclassList != null) { 124 JavaClassAndMethod match = 125 Hierarchy.findMethod(superclassList, method.getName(), method.getSignature(), Hierarchy.INSTANCE_METHOD); 126 if (match != null) 127 return true; 128 } 129 130 JavaClass[] interfaceList = javaClass.getAllInterfaces(); 131 if (interfaceList != null) { 132 JavaClassAndMethod match = 133 Hierarchy.findMethod(interfaceList, method.getName(), method.getSignature(), Hierarchy.INSTANCE_METHOD); 134 if (match != null) 135 return true; 136 } 137 138 return false; 139 } catch (ClassNotFoundException e) { 140 return true; 141 } 142 } 143 144 150 private boolean isSynthetic(FieldOrMethod member) { 151 if (member.isSynthetic()) return true; 153 154 String name = member.getName(); 155 156 if (name.startsWith("class$")) 157 return true; 158 159 if (name.startsWith("access$")) 160 return true; 161 162 return false; 163 } 164 165 168 public String getClassName() { 169 return className; 170 } 171 172 175 public void setClassName(String className) { 176 this.className = className; 177 } 178 179 182 public boolean isInterface() { 183 return isInterface; 184 } 185 186 189 public void setInterface(boolean isInterface) { 190 this.isInterface = isInterface; 191 } 192 193 public int getNumFeatures() { 194 return featureSet.size(); 195 } 196 197 public void addFeature(String feature) { 198 featureSet.add(feature); 199 } 200 201 public Iterator <String > featureIterator() { 202 return featureSet.iterator(); 203 } 204 205 public boolean hasFeature(String feature) { 206 return featureSet.contains(feature); 207 } 208 209 215 public static String transformClassName(String className) { 216 int lastDot = className.lastIndexOf('.'); 217 if (lastDot >= 0) { 218 String pkg = className.substring(0, lastDot); 219 if (!isUnlikelyToBeRenamed(pkg)) { 220 className = className.substring(lastDot + 1); 221 } 222 } 223 return className; 224 } 225 226 233 public static boolean isUnlikelyToBeRenamed(String pkg) { 234 return pkg.startsWith("java."); 235 } 236 237 244 public static String transformMethodSignature(String signature) { 245 StringBuffer buf = new StringBuffer (); 246 247 buf.append('('); 248 249 SignatureParser parser = new SignatureParser(signature); 250 for (Iterator <String > i = parser.parameterSignatureIterator(); i.hasNext(); ) { 251 String param = i.next(); 252 param = transformSignature(param); 253 buf.append(param); 254 } 255 256 buf.append(')'); 257 258 return buf.toString(); 259 } 260 261 268 public static String transformSignature(String signature) { 269 StringBuffer buf = new StringBuffer (); 270 271 int lastBracket = signature.lastIndexOf('['); 272 if (lastBracket > 0) { 273 buf.append(signature.substring(0, lastBracket+1)); 274 signature = signature.substring(lastBracket+1); 275 } 276 277 if (signature.startsWith("L")) { 278 signature = signature.substring(1, signature.length() - 1).replace('/', '.'); 279 signature = transformClassName(signature); 280 signature = "L" + signature.replace('.', '/') + ";"; 281 } 282 buf.append(signature); 283 284 return buf.toString(); 285 } 286 287 291 public static final int MIN_FEATURES = 5; 292 293 296 public static final double MIN_MATCH = 0.60; 297 298 302 public static final double EXACT_CLASS_NAME_MATCH = MIN_MATCH + 0.1; 303 304 public static double similarity(ClassFeatureSet a, ClassFeatureSet b) { 305 if (a.isInterface() != b.isInterface()) 307 return 0.0; 308 309 if (a.getNumFeatures() < MIN_FEATURES || b.getNumFeatures() < MIN_FEATURES) 310 return a.getClassName().equals(b.getClassName()) ? EXACT_CLASS_NAME_MATCH : 0.0; 311 312 int numMatch = 0; 313 int max = Math.max(a.getNumFeatures(), b.getNumFeatures()); 314 315 for (Iterator <String > i = a.featureIterator(); i.hasNext();) { 316 String feature = i.next(); 317 if (b.hasFeature(feature)) { 318 ++numMatch; 319 } 320 } 321 322 return ((double) numMatch / (double) max); 323 324 } 325 326 public boolean similarTo(ClassFeatureSet other) { 327 return similarity(this, other) >= MIN_MATCH; 328 } 329 330 public static void main(String [] args) throws Exception { 331 if (args.length != 2) { 332 System.err.println("Usage: " + ClassFeatureSet.class.getName() + " <class 1> <class 2>"); 333 System.exit(1); 334 } 335 336 JavaClass a = Repository.lookupClass(args[0]); 337 JavaClass b = Repository.lookupClass(args[1]); 338 339 ClassFeatureSet aFeatures = new ClassFeatureSet().initialize(a); 340 ClassFeatureSet bFeatures = new ClassFeatureSet().initialize(b); 341 342 System.out.println("Similarity is " + similarity(aFeatures, bFeatures)); 343 System.out.println("Classes are" + (aFeatures.similarTo(bFeatures) ? "" : " not") + " similar"); 344 } 345 346 public static final String ELEMENT_NAME = "ClassFeatureSet"; 347 public static final String FEATURE_ELEMENT_NAME = "Feature"; 348 349 352 public void writeXML(XMLOutput xmlOutput) throws IOException { 353 xmlOutput.openTag(ELEMENT_NAME, new XMLAttributeList().addAttribute("class", className)); 354 for (Iterator <String > i = featureIterator(); i.hasNext(); ) { 355 String feature = i.next(); 356 xmlOutput.openCloseTag( 357 FEATURE_ELEMENT_NAME, 358 new XMLAttributeList().addAttribute("value", feature)); 359 } 360 xmlOutput.closeTag(ELEMENT_NAME); 361 } 362 } 363 | Popular Tags |