summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/convert/ClassIdentity.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/cuchaz/enigma/convert/ClassIdentity.java')
-rw-r--r--src/main/java/cuchaz/enigma/convert/ClassIdentity.java821
1 files changed, 410 insertions, 411 deletions
diff --git a/src/main/java/cuchaz/enigma/convert/ClassIdentity.java b/src/main/java/cuchaz/enigma/convert/ClassIdentity.java
index f72bf70..a395b75 100644
--- a/src/main/java/cuchaz/enigma/convert/ClassIdentity.java
+++ b/src/main/java/cuchaz/enigma/convert/ClassIdentity.java
@@ -8,18 +8,10 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.convert; 12package cuchaz.enigma.convert;
12 13
13import com.google.common.collect.*; 14import com.google.common.collect.*;
14
15import java.io.UnsupportedEncodingException;
16import java.security.MessageDigest;
17import java.security.NoSuchAlgorithmException;
18import java.util.Enumeration;
19import java.util.List;
20import java.util.Map;
21import java.util.Set;
22
23import cuchaz.enigma.analysis.ClassImplementationsTreeNode; 15import cuchaz.enigma.analysis.ClassImplementationsTreeNode;
24import cuchaz.enigma.analysis.EntryReference; 16import cuchaz.enigma.analysis.EntryReference;
25import cuchaz.enigma.analysis.JarIndex; 17import cuchaz.enigma.analysis.JarIndex;
@@ -33,408 +25,415 @@ import javassist.*;
33import javassist.bytecode.*; 25import javassist.bytecode.*;
34import javassist.expr.*; 26import javassist.expr.*;
35 27
28import java.io.UnsupportedEncodingException;
29import java.security.MessageDigest;
30import java.security.NoSuchAlgorithmException;
31import java.util.Enumeration;
32import java.util.List;
33import java.util.Map;
34import java.util.Set;
35
36public class ClassIdentity { 36public class ClassIdentity {
37 37
38 private ClassEntry classEntry; 38 private ClassEntry classEntry;
39 private SidedClassNamer namer; 39 private SidedClassNamer namer;
40 private Multiset<String> fields; 40 private final ClassNameReplacer classNameReplacer = new ClassNameReplacer() {
41 private Multiset<String> methods; 41
42 private Multiset<String> constructors; 42 private Map<String, String> classNames = Maps.newHashMap();
43 private String staticInitializer; 43
44 private String extendz; 44 @Override
45 private Multiset<String> implementz; 45 public String replace(String className) {
46 private Set<String> stringLiterals; 46
47 private Multiset<String> implementations; 47 // classes not in the none package can be passed through
48 private Multiset<String> references; 48 ClassEntry classEntry = new ClassEntry(className);
49 private String outer; 49 if (classEntry.getPackageName() != null) {
50 50 return className;
51 private final ClassNameReplacer classNameReplacer = new ClassNameReplacer() { 51 }
52 52
53 private Map<String, String> classNames = Maps.newHashMap(); 53 // is this class ourself?
54 54 if (className.equals(classEntry.getName())) {
55 @Override 55 return "CSelf";
56 public String replace(String className) { 56 }
57 57
58 // classes not in the none package can be passed through 58 // try the namer
59 ClassEntry classEntry = new ClassEntry(className); 59 if (namer != null) {
60 if (classEntry.getPackageName() != null) { 60 String newName = namer.getName(className);
61 return className; 61 if (newName != null) {
62 } 62 return newName;
63 63 }
64 // is this class ourself? 64 }
65 if (className.equals(classEntry.getName())) { 65
66 return "CSelf"; 66 // otherwise, use local naming
67 } 67 if (!classNames.containsKey(className)) {
68 68 classNames.put(className, getNewClassName());
69 // try the namer 69 }
70 if (namer != null) { 70 return classNames.get(className);
71 String newName = namer.getName(className); 71 }
72 if (newName != null) { 72
73 return newName; 73 private String getNewClassName() {
74 } 74 return String.format("C%03d", classNames.size());
75 } 75 }
76 76 };
77 // otherwise, use local naming 77 private Multiset<String> fields;
78 if (!classNames.containsKey(className)) { 78 private Multiset<String> methods;
79 classNames.put(className, getNewClassName()); 79 private Multiset<String> constructors;
80 } 80 private String staticInitializer;
81 return classNames.get(className); 81 private String extendz;
82 } 82 private Multiset<String> implementz;
83 83 private Set<String> stringLiterals;
84 private String getNewClassName() { 84 private Multiset<String> implementations;
85 return String.format("C%03d", classNames.size()); 85 private Multiset<String> references;
86 } 86 private String outer;
87 }; 87
88 88 public ClassIdentity(CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences) {
89 public ClassIdentity(CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences) { 89 this.namer = namer;
90 this.namer = namer; 90
91 91 // stuff from the bytecode
92 // stuff from the bytecode 92
93 93 this.classEntry = EntryFactory.getClassEntry(c);
94 this.classEntry = EntryFactory.getClassEntry(c); 94 this.fields = HashMultiset.create();
95 this.fields = HashMultiset.create(); 95 for (CtField field : c.getDeclaredFields()) {
96 for (CtField field : c.getDeclaredFields()) { 96 this.fields.add(scrubType(field.getSignature()));
97 this.fields.add(scrubType(field.getSignature())); 97 }
98 } 98 this.methods = HashMultiset.create();
99 this.methods = HashMultiset.create(); 99 for (CtMethod method : c.getDeclaredMethods()) {
100 for (CtMethod method : c.getDeclaredMethods()) { 100 this.methods.add(scrubSignature(method.getSignature()) + "0x" + getBehaviorSignature(method));
101 this.methods.add(scrubSignature(method.getSignature()) + "0x" + getBehaviorSignature(method)); 101 }
102 } 102 this.constructors = HashMultiset.create();
103 this.constructors = HashMultiset.create(); 103 for (CtConstructor constructor : c.getDeclaredConstructors()) {
104 for (CtConstructor constructor : c.getDeclaredConstructors()) { 104 this.constructors.add(scrubSignature(constructor.getSignature()) + "0x" + getBehaviorSignature(constructor));
105 this.constructors.add(scrubSignature(constructor.getSignature()) + "0x" + getBehaviorSignature(constructor)); 105 }
106 } 106 this.staticInitializer = "";
107 this.staticInitializer = ""; 107 if (c.getClassInitializer() != null) {
108 if (c.getClassInitializer() != null) { 108 this.staticInitializer = getBehaviorSignature(c.getClassInitializer());
109 this.staticInitializer = getBehaviorSignature(c.getClassInitializer()); 109 }
110 } 110 this.extendz = "";
111 this.extendz = ""; 111 if (c.getClassFile().getSuperclass() != null) {
112 if (c.getClassFile().getSuperclass() != null) { 112 this.extendz = scrubClassName(Descriptor.toJvmName(c.getClassFile().getSuperclass()));
113 this.extendz = scrubClassName(Descriptor.toJvmName(c.getClassFile().getSuperclass())); 113 }
114 } 114 this.implementz = HashMultiset.create();
115 this.implementz = HashMultiset.create(); 115 for (String interfaceName : c.getClassFile().getInterfaces()) {
116 for (String interfaceName : c.getClassFile().getInterfaces()) { 116 this.implementz.add(scrubClassName(Descriptor.toJvmName(interfaceName)));
117 this.implementz.add(scrubClassName(Descriptor.toJvmName(interfaceName))); 117 }
118 } 118
119 119 this.stringLiterals = Sets.newHashSet();
120 this.stringLiterals = Sets.newHashSet(); 120 ConstPool constants = c.getClassFile().getConstPool();
121 ConstPool constants = c.getClassFile().getConstPool(); 121 for (int i = 1; i < constants.getSize(); i++) {
122 for (int i = 1; i < constants.getSize(); i++) { 122 if (constants.getTag(i) == ConstPool.CONST_String) {
123 if (constants.getTag(i) == ConstPool.CONST_String) { 123 this.stringLiterals.add(constants.getStringInfo(i));
124 this.stringLiterals.add(constants.getStringInfo(i)); 124 }
125 } 125 }
126 } 126
127 127 // stuff from the jar index
128 // stuff from the jar index 128
129 129 this.implementations = HashMultiset.create();
130 this.implementations = HashMultiset.create(); 130 ClassImplementationsTreeNode implementationsNode = index.getClassImplementations(null, this.classEntry);
131 ClassImplementationsTreeNode implementationsNode = index.getClassImplementations(null, this.classEntry); 131 if (implementationsNode != null) {
132 if (implementationsNode != null) { 132 @SuppressWarnings("unchecked")
133 @SuppressWarnings("unchecked") 133 Enumeration<ClassImplementationsTreeNode> implementations = implementationsNode.children();
134 Enumeration<ClassImplementationsTreeNode> implementations = implementationsNode.children(); 134 while (implementations.hasMoreElements()) {
135 while (implementations.hasMoreElements()) { 135 ClassImplementationsTreeNode node = implementations.nextElement();
136 ClassImplementationsTreeNode node = implementations.nextElement(); 136 this.implementations.add(scrubClassName(node.getClassEntry().getName()));
137 this.implementations.add(scrubClassName(node.getClassEntry().getName())); 137 }
138 } 138 }
139 } 139
140 140 this.references = HashMultiset.create();
141 this.references = HashMultiset.create(); 141 if (useReferences) {
142 if (useReferences) { 142 for (CtField field : c.getDeclaredFields()) {
143 for (CtField field : c.getDeclaredFields()) { 143 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field);
144 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); 144 index.getFieldReferences(fieldEntry).forEach(this::addReference);
145 index.getFieldReferences(fieldEntry).forEach(this::addReference); 145 }
146 } 146 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
147 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 147 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
148 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); 148 index.getBehaviorReferences(behaviorEntry).forEach(this::addReference);
149 index.getBehaviorReferences(behaviorEntry).forEach(this::addReference); 149 }
150 } 150 }
151 } 151
152 152 this.outer = null;
153 this.outer = null; 153 if (this.classEntry.isInnerClass()) {
154 if (this.classEntry.isInnerClass()) { 154 this.outer = this.classEntry.getOuterClassName();
155 this.outer = this.classEntry.getOuterClassName(); 155 }
156 } 156 }
157 } 157
158 158 private void addReference(EntryReference<? extends Entry, BehaviorEntry> reference) {
159 private void addReference(EntryReference<? extends Entry, BehaviorEntry> reference) { 159 if (reference.context.getSignature() != null) {
160 if (reference.context.getSignature() != null) { 160 this.references.add(String.format("%s_%s",
161 this.references.add(String.format("%s_%s", 161 scrubClassName(reference.context.getClassName()),
162 scrubClassName(reference.context.getClassName()), 162 scrubSignature(reference.context.getSignature())
163 scrubSignature(reference.context.getSignature()) 163 ));
164 )); 164 } else {
165 } else { 165 this.references.add(String.format("%s_<clinit>",
166 this.references.add(String.format("%s_<clinit>", 166 scrubClassName(reference.context.getClassName())
167 scrubClassName(reference.context.getClassName()) 167 ));
168 )); 168 }
169 } 169 }
170 } 170
171 171 public ClassEntry getClassEntry() {
172 public ClassEntry getClassEntry() { 172 return this.classEntry;
173 return this.classEntry; 173 }
174 } 174
175 175 @Override
176 @Override 176 public String toString() {
177 public String toString() { 177 StringBuilder buf = new StringBuilder();
178 StringBuilder buf = new StringBuilder(); 178 buf.append("class: ");
179 buf.append("class: "); 179 buf.append(this.classEntry.getName());
180 buf.append(this.classEntry.getName()); 180 buf.append(" ");
181 buf.append(" "); 181 buf.append(hashCode());
182 buf.append(hashCode()); 182 buf.append("\n");
183 buf.append("\n"); 183 for (String field : this.fields) {
184 for (String field : this.fields) { 184 buf.append("\tfield ");
185 buf.append("\tfield "); 185 buf.append(field);
186 buf.append(field); 186 buf.append("\n");
187 buf.append("\n"); 187 }
188 } 188 for (String method : this.methods) {
189 for (String method : this.methods) { 189 buf.append("\tmethod ");
190 buf.append("\tmethod "); 190 buf.append(method);
191 buf.append(method); 191 buf.append("\n");
192 buf.append("\n"); 192 }
193 } 193 for (String constructor : this.constructors) {
194 for (String constructor : this.constructors) { 194 buf.append("\tconstructor ");
195 buf.append("\tconstructor "); 195 buf.append(constructor);
196 buf.append(constructor); 196 buf.append("\n");
197 buf.append("\n"); 197 }
198 } 198 if (!this.staticInitializer.isEmpty()) {
199 if (this.staticInitializer.length() > 0) { 199 buf.append("\tinitializer ");
200 buf.append("\tinitializer "); 200 buf.append(this.staticInitializer);
201 buf.append(this.staticInitializer); 201 buf.append("\n");
202 buf.append("\n"); 202 }
203 } 203 if (!this.extendz.isEmpty()) {
204 if (this.extendz.length() > 0) { 204 buf.append("\textends ");
205 buf.append("\textends "); 205 buf.append(this.extendz);
206 buf.append(this.extendz); 206 buf.append("\n");
207 buf.append("\n"); 207 }
208 } 208 for (String interfaceName : this.implementz) {
209 for (String interfaceName : this.implementz) { 209 buf.append("\timplements ");
210 buf.append("\timplements "); 210 buf.append(interfaceName);
211 buf.append(interfaceName); 211 buf.append("\n");
212 buf.append("\n"); 212 }
213 } 213 for (String implementation : this.implementations) {
214 for (String implementation : this.implementations) { 214 buf.append("\timplemented by ");
215 buf.append("\timplemented by "); 215 buf.append(implementation);
216 buf.append(implementation); 216 buf.append("\n");
217 buf.append("\n"); 217 }
218 } 218 for (String reference : this.references) {
219 for (String reference : this.references) { 219 buf.append("\treference ");
220 buf.append("\treference "); 220 buf.append(reference);
221 buf.append(reference); 221 buf.append("\n");
222 buf.append("\n"); 222 }
223 } 223 buf.append("\touter ");
224 buf.append("\touter "); 224 buf.append(this.outer);
225 buf.append(this.outer); 225 buf.append("\n");
226 buf.append("\n"); 226 return buf.toString();
227 return buf.toString(); 227 }
228 } 228
229 229 private String scrubClassName(String className) {
230 private String scrubClassName(String className) { 230 return classNameReplacer.replace(className);
231 return classNameReplacer.replace(className); 231 }
232 } 232
233 233 private String scrubType(String typeName) {
234 private String scrubType(String typeName) { 234 return scrubType(new Type(typeName)).toString();
235 return scrubType(new Type(typeName)).toString(); 235 }
236 } 236
237 237 private Type scrubType(Type type) {
238 private Type scrubType(Type type) { 238 if (type.hasClass()) {
239 if (type.hasClass()) { 239 return new Type(type, classNameReplacer);
240 return new Type(type, classNameReplacer); 240 } else {
241 } else { 241 return type;
242 return type; 242 }
243 } 243 }
244 } 244
245 245 private String scrubSignature(String signature) {
246 private String scrubSignature(String signature) { 246 return scrubSignature(new Signature(signature)).toString();
247 return scrubSignature(new Signature(signature)).toString(); 247 }
248 } 248
249 249 private Signature scrubSignature(Signature signature) {
250 private Signature scrubSignature(Signature signature) { 250 return new Signature(signature, classNameReplacer);
251 return new Signature(signature, classNameReplacer); 251 }
252 } 252
253 253 private boolean isClassMatchedUniquely(String className) {
254 private boolean isClassMatchedUniquely(String className) { 254 return this.namer != null && this.namer.getName(Descriptor.toJvmName(className)) != null;
255 return this.namer != null && this.namer.getName(Descriptor.toJvmName(className)) != null; 255 }
256 } 256
257 257 private String getBehaviorSignature(CtBehavior behavior) {
258 private String getBehaviorSignature(CtBehavior behavior) { 258 try {
259 try { 259 // does this method have an implementation?
260 // does this method have an implementation? 260 if (behavior.getMethodInfo().getCodeAttribute() == null) {
261 if (behavior.getMethodInfo().getCodeAttribute() == null) { 261 return "(none)";
262 return "(none)"; 262 }
263 } 263
264 264 // compute the hash from the opcodes
265 // compute the hash from the opcodes 265 ConstPool constants = behavior.getMethodInfo().getConstPool();
266 ConstPool constants = behavior.getMethodInfo().getConstPool(); 266 final MessageDigest digest = MessageDigest.getInstance("MD5");
267 final MessageDigest digest = MessageDigest.getInstance("MD5"); 267 CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator();
268 CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator(); 268 while (iter.hasNext()) {
269 while (iter.hasNext()) { 269 int pos = iter.next();
270 int pos = iter.next(); 270
271 271 // update the hash with the opcode
272 // update the hash with the opcode 272 int opcode = iter.byteAt(pos);
273 int opcode = iter.byteAt(pos); 273 digest.update((byte) opcode);
274 digest.update((byte) opcode); 274 int constIndex;
275 int constIndex; 275 switch (opcode) {
276 switch (opcode) { 276 case Opcode.LDC:
277 case Opcode.LDC: 277 constIndex = iter.byteAt(pos + 1);
278 constIndex = iter.byteAt(pos + 1); 278 updateHashWithConstant(digest, constants, constIndex);
279 updateHashWithConstant(digest, constants, constIndex); 279 break;
280 break; 280
281 281 case Opcode.LDC_W:
282 case Opcode.LDC_W: 282 case Opcode.LDC2_W:
283 case Opcode.LDC2_W: 283 constIndex = (iter.byteAt(pos + 1) << 8) | iter.byteAt(pos + 2);
284 constIndex = (iter.byteAt(pos + 1) << 8) | iter.byteAt(pos + 2); 284 updateHashWithConstant(digest, constants, constIndex);
285 updateHashWithConstant(digest, constants, constIndex); 285 break;
286 break; 286 default:
287 default: 287 break;
288 break; 288 }
289 } 289 }
290 } 290
291 291 // update hash with method and field accesses
292 // update hash with method and field accesses 292 behavior.instrument(new ExprEditor() {
293 behavior.instrument(new ExprEditor() { 293 @Override
294 @Override 294 public void edit(MethodCall call) {
295 public void edit(MethodCall call) { 295 updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(call.getClassName())));
296 updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(call.getClassName()))); 296 updateHashWithString(digest, scrubSignature(call.getSignature()));
297 updateHashWithString(digest, scrubSignature(call.getSignature())); 297 if (isClassMatchedUniquely(call.getClassName())) {
298 if (isClassMatchedUniquely(call.getClassName())) { 298 updateHashWithString(digest, call.getMethodName());
299 updateHashWithString(digest, call.getMethodName()); 299 }
300 } 300 }
301 } 301
302 302 @Override
303 @Override 303 public void edit(FieldAccess access) {
304 public void edit(FieldAccess access) { 304 updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(access.getClassName())));
305 updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(access.getClassName()))); 305 updateHashWithString(digest, scrubType(access.getSignature()));
306 updateHashWithString(digest, scrubType(access.getSignature())); 306 if (isClassMatchedUniquely(access.getClassName())) {
307 if (isClassMatchedUniquely(access.getClassName())) { 307 updateHashWithString(digest, access.getFieldName());
308 updateHashWithString(digest, access.getFieldName()); 308 }
309 } 309 }
310 } 310
311 311 @Override
312 @Override 312 public void edit(ConstructorCall call) {
313 public void edit(ConstructorCall call) { 313 updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(call.getClassName())));
314 updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(call.getClassName()))); 314 updateHashWithString(digest, scrubSignature(call.getSignature()));
315 updateHashWithString(digest, scrubSignature(call.getSignature())); 315 }
316 } 316
317 317 @Override
318 @Override 318 public void edit(NewExpr expr) {
319 public void edit(NewExpr expr) { 319 updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(expr.getClassName())));
320 updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(expr.getClassName()))); 320 }
321 } 321 });
322 }); 322
323 323 // convert the hash to a hex string
324 // convert the hash to a hex string 324 return toHex(digest.digest());
325 return toHex(digest.digest()); 325 } catch (BadBytecode | NoSuchAlgorithmException | CannotCompileException ex) {
326 } catch (BadBytecode | NoSuchAlgorithmException | CannotCompileException ex) { 326 throw new Error(ex);
327 throw new Error(ex); 327 }
328 } 328 }
329 } 329
330 330 private void updateHashWithConstant(MessageDigest digest, ConstPool constants, int index) {
331 private void updateHashWithConstant(MessageDigest digest, ConstPool constants, int index) { 331 ConstPoolEditor editor = new ConstPoolEditor(constants);
332 ConstPoolEditor editor = new ConstPoolEditor(constants); 332 ConstInfoAccessor item = editor.getItem(index);
333 ConstInfoAccessor item = editor.getItem(index); 333 if (item.getType() == InfoType.StringInfo) {
334 if (item.getType() == InfoType.StringInfo) { 334 updateHashWithString(digest, constants.getStringInfo(index));
335 updateHashWithString(digest, constants.getStringInfo(index)); 335 }
336 } 336 // TODO: other constants
337 // TODO: other constants 337 }
338 } 338
339 339 private void updateHashWithString(MessageDigest digest, String val) {
340 private void updateHashWithString(MessageDigest digest, String val) { 340 try {
341 try { 341 digest.update(val.getBytes("UTF8"));
342 digest.update(val.getBytes("UTF8")); 342 } catch (UnsupportedEncodingException ex) {
343 } catch (UnsupportedEncodingException ex) { 343 throw new Error(ex);
344 throw new Error(ex); 344 }
345 } 345 }
346 } 346
347 347 private String toHex(byte[] bytes) {
348 private String toHex(byte[] bytes) { 348 // function taken from:
349 // function taken from: 349 // http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java
350 // http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java 350 final char[] hexArray = "0123456789ABCDEF".toCharArray();
351 final char[] hexArray = "0123456789ABCDEF".toCharArray(); 351 char[] hexChars = new char[bytes.length * 2];
352 char[] hexChars = new char[bytes.length * 2]; 352 for (int j = 0; j < bytes.length; j++) {
353 for (int j = 0; j < bytes.length; j++) { 353 int v = bytes[j] & 0xFF;
354 int v = bytes[j] & 0xFF; 354 hexChars[j * 2] = hexArray[v >>> 4];
355 hexChars[j * 2] = hexArray[v >>> 4]; 355 hexChars[j * 2 + 1] = hexArray[v & 0x0F];
356 hexChars[j * 2 + 1] = hexArray[v & 0x0F]; 356 }
357 } 357 return new String(hexChars);
358 return new String(hexChars); 358 }
359 } 359
360 360 @Override
361 @Override 361 public boolean equals(Object other) {
362 public boolean equals(Object other) { 362 return other instanceof ClassIdentity && equals((ClassIdentity) other);
363 return other instanceof ClassIdentity && equals((ClassIdentity) other); 363 }
364 } 364
365 365 public boolean equals(ClassIdentity other) {
366 public boolean equals(ClassIdentity other) { 366 return this.fields.equals(other.fields)
367 return this.fields.equals(other.fields) 367 && this.methods.equals(other.methods)
368 && this.methods.equals(other.methods) 368 && this.constructors.equals(other.constructors)
369 && this.constructors.equals(other.constructors) 369 && this.staticInitializer.equals(other.staticInitializer)
370 && this.staticInitializer.equals(other.staticInitializer) 370 && this.extendz.equals(other.extendz)
371 && this.extendz.equals(other.extendz) 371 && this.implementz.equals(other.implementz)
372 && this.implementz.equals(other.implementz) 372 && this.implementations.equals(other.implementations)
373 && this.implementations.equals(other.implementations) 373 && this.references.equals(other.references);
374 && this.references.equals(other.references); 374 }
375 } 375
376 376 @Override
377 @Override 377 public int hashCode() {
378 public int hashCode() { 378 List<Object> objs = Lists.newArrayList();
379 List<Object> objs = Lists.newArrayList(); 379 objs.addAll(this.fields);
380 objs.addAll(this.fields); 380 objs.addAll(this.methods);
381 objs.addAll(this.methods); 381 objs.addAll(this.constructors);
382 objs.addAll(this.constructors); 382 objs.add(this.staticInitializer);
383 objs.add(this.staticInitializer); 383 objs.add(this.extendz);
384 objs.add(this.extendz); 384 objs.addAll(this.implementz);
385 objs.addAll(this.implementz); 385 objs.addAll(this.implementations);
386 objs.addAll(this.implementations); 386 objs.addAll(this.references);
387 objs.addAll(this.references); 387 return Utils.combineHashesOrdered(objs);
388 return Utils.combineHashesOrdered(objs); 388 }
389 } 389
390 390 public int getMatchScore(ClassIdentity other) {
391 public int getMatchScore(ClassIdentity other) { 391 return 2 * getNumMatches(this.extendz, other.extendz)
392 return 2 * getNumMatches(this.extendz, other.extendz) 392 + 2 * getNumMatches(this.outer, other.outer)
393 + 2 * getNumMatches(this.outer, other.outer) 393 + 2 * getNumMatches(this.implementz, other.implementz)
394 + 2 * getNumMatches(this.implementz, other.implementz) 394 + getNumMatches(this.stringLiterals, other.stringLiterals)
395 + getNumMatches(this.stringLiterals, other.stringLiterals) 395 + getNumMatches(this.fields, other.fields)
396 + getNumMatches(this.fields, other.fields) 396 + getNumMatches(this.methods, other.methods)
397 + getNumMatches(this.methods, other.methods) 397 + getNumMatches(this.constructors, other.constructors);
398 + getNumMatches(this.constructors, other.constructors); 398 }
399 } 399
400 400 public int getMaxMatchScore() {
401 public int getMaxMatchScore() { 401 return 2 + 2 + 2 * this.implementz.size() + this.stringLiterals.size() + this.fields.size() + this.methods.size() + this.constructors.size();
402 return 2 + 2 + 2 * this.implementz.size() + this.stringLiterals.size() + this.fields.size() + this.methods.size() + this.constructors.size(); 402 }
403 } 403
404 404 public boolean matches(CtClass c) {
405 public boolean matches(CtClass c) { 405 // just compare declaration counts
406 // just compare declaration counts 406 return this.fields.size() == c.getDeclaredFields().length
407 return this.fields.size() == c.getDeclaredFields().length 407 && this.methods.size() == c.getDeclaredMethods().length
408 && this.methods.size() == c.getDeclaredMethods().length 408 && this.constructors.size() == c.getDeclaredConstructors().length;
409 && this.constructors.size() == c.getDeclaredConstructors().length; 409 }
410 } 410
411 411 private int getNumMatches(Set<String> a, Set<String> b) {
412 private int getNumMatches(Set<String> a, Set<String> b) { 412 int numMatches = 0;
413 int numMatches = 0; 413 for (String val : a) {
414 for (String val : a) { 414 if (b.contains(val)) {
415 if (b.contains(val)) { 415 numMatches++;
416 numMatches++; 416 }
417 } 417 }
418 } 418 return numMatches;
419 return numMatches; 419 }
420 } 420
421 421 private int getNumMatches(Multiset<String> a, Multiset<String> b) {
422 private int getNumMatches(Multiset<String> a, Multiset<String> b) { 422 int numMatches = 0;
423 int numMatches = 0; 423 for (String val : a) {
424 for (String val : a) { 424 if (b.contains(val)) {
425 if (b.contains(val)) { 425 numMatches++;
426 numMatches++; 426 }
427 } 427 }
428 } 428 return numMatches;
429 return numMatches; 429 }
430 } 430
431 431 private int getNumMatches(String a, String b) {
432 private int getNumMatches(String a, String b) { 432 if (a == null && b == null) {
433 if (a == null && b == null) { 433 return 1;
434 return 1; 434 } else if (a != null && b != null && a.equals(b)) {
435 } else if (a != null && b != null && a.equals(b)) { 435 return 1;
436 return 1; 436 }
437 } 437 return 0;
438 return 0; 438 }
439 }
440} 439}