summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/convert
diff options
context:
space:
mode:
authorGravatar jeff2015-01-13 23:25:04 -0500
committerGravatar jeff2015-01-13 23:25:04 -0500
commit959cb5fd4f9586ec3bd265b452fe25fe1db82e3f (patch)
treebdd8a2c52c2fe053ba3460614bde8542e5378dbe /src/cuchaz/enigma/convert
parentgot rid of gradle in favor of ivy+ssjb (diff)
downloadenigma-fork-959cb5fd4f9586ec3bd265b452fe25fe1db82e3f.tar.gz
enigma-fork-959cb5fd4f9586ec3bd265b452fe25fe1db82e3f.tar.xz
enigma-fork-959cb5fd4f9586ec3bd265b452fe25fe1db82e3f.zip
source format change
don't hate me too much if you were planning a big merge. =P
Diffstat (limited to 'src/cuchaz/enigma/convert')
-rw-r--r--src/cuchaz/enigma/convert/ClassIdentity.java436
-rw-r--r--src/cuchaz/enigma/convert/ClassMatcher.java412
-rw-r--r--src/cuchaz/enigma/convert/ClassMatching.java152
-rw-r--r--src/cuchaz/enigma/convert/ClassNamer.java53
4 files changed, 429 insertions, 624 deletions
diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java
index 1de345f..7340403 100644
--- a/src/cuchaz/enigma/convert/ClassIdentity.java
+++ b/src/cuchaz/enigma/convert/ClassIdentity.java
@@ -57,8 +57,8 @@ import cuchaz.enigma.mapping.MethodEntry;
57import cuchaz.enigma.mapping.SignatureUpdater; 57import cuchaz.enigma.mapping.SignatureUpdater;
58import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; 58import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater;
59 59
60public class ClassIdentity 60public class ClassIdentity {
61{ 61
62 private ClassEntry m_classEntry; 62 private ClassEntry m_classEntry;
63 private SidedClassNamer m_namer; 63 private SidedClassNamer m_namer;
64 private Multiset<String> m_fields; 64 private Multiset<String> m_fields;
@@ -70,419 +70,339 @@ public class ClassIdentity
70 private Multiset<String> m_implementations; 70 private Multiset<String> m_implementations;
71 private Multiset<String> m_references; 71 private Multiset<String> m_references;
72 72
73 public ClassIdentity( CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences ) 73 public ClassIdentity(CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences) {
74 {
75 m_namer = namer; 74 m_namer = namer;
76 75
77 // stuff from the bytecode 76 // stuff from the bytecode
78 77
79 m_classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); 78 m_classEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
80 m_fields = HashMultiset.create(); 79 m_fields = HashMultiset.create();
81 for( CtField field : c.getDeclaredFields() ) 80 for (CtField field : c.getDeclaredFields()) {
82 { 81 m_fields.add(scrubSignature(field.getSignature()));
83 m_fields.add( scrubSignature( field.getSignature() ) );
84 } 82 }
85 m_methods = HashMultiset.create(); 83 m_methods = HashMultiset.create();
86 for( CtMethod method : c.getDeclaredMethods() ) 84 for (CtMethod method : c.getDeclaredMethods()) {
87 { 85 m_methods.add(scrubSignature(method.getSignature()) + "0x" + getBehaviorSignature(method));
88 m_methods.add( scrubSignature( method.getSignature() ) + "0x" + getBehaviorSignature( method ) );
89 } 86 }
90 m_constructors = HashMultiset.create(); 87 m_constructors = HashMultiset.create();
91 for( CtConstructor constructor : c.getDeclaredConstructors() ) 88 for (CtConstructor constructor : c.getDeclaredConstructors()) {
92 { 89 m_constructors.add(scrubSignature(constructor.getSignature()) + "0x" + getBehaviorSignature(constructor));
93 m_constructors.add( scrubSignature( constructor.getSignature() ) + "0x" + getBehaviorSignature( constructor ) );
94 } 90 }
95 m_staticInitializer = ""; 91 m_staticInitializer = "";
96 if( c.getClassInitializer() != null ) 92 if (c.getClassInitializer() != null) {
97 { 93 m_staticInitializer = getBehaviorSignature(c.getClassInitializer());
98 m_staticInitializer = getBehaviorSignature( c.getClassInitializer() );
99 } 94 }
100 m_extends = ""; 95 m_extends = "";
101 if( c.getClassFile().getSuperclass() != null ) 96 if (c.getClassFile().getSuperclass() != null) {
102 { 97 m_extends = scrubClassName(c.getClassFile().getSuperclass());
103 m_extends = scrubClassName( c.getClassFile().getSuperclass() );
104 } 98 }
105 m_implements = HashMultiset.create(); 99 m_implements = HashMultiset.create();
106 for( String interfaceName : c.getClassFile().getInterfaces() ) 100 for (String interfaceName : c.getClassFile().getInterfaces()) {
107 { 101 m_implements.add(scrubClassName(interfaceName));
108 m_implements.add( scrubClassName( interfaceName ) );
109 } 102 }
110 103
111 // stuff from the jar index 104 // stuff from the jar index
112 105
113 m_implementations = HashMultiset.create(); 106 m_implementations = HashMultiset.create();
114 ClassImplementationsTreeNode implementationsNode = index.getClassImplementations( null, m_classEntry ); 107 ClassImplementationsTreeNode implementationsNode = index.getClassImplementations(null, m_classEntry);
115 if( implementationsNode != null ) 108 if (implementationsNode != null) {
116 { 109 @SuppressWarnings("unchecked")
117 @SuppressWarnings( "unchecked" )
118 Enumeration<ClassImplementationsTreeNode> implementations = implementationsNode.children(); 110 Enumeration<ClassImplementationsTreeNode> implementations = implementationsNode.children();
119 while( implementations.hasMoreElements() ) 111 while (implementations.hasMoreElements()) {
120 {
121 ClassImplementationsTreeNode node = implementations.nextElement(); 112 ClassImplementationsTreeNode node = implementations.nextElement();
122 m_implementations.add( scrubClassName( node.getClassEntry().getName() ) ); 113 m_implementations.add(scrubClassName(node.getClassEntry().getName()));
123 } 114 }
124 } 115 }
125 116
126 m_references = HashMultiset.create(); 117 m_references = HashMultiset.create();
127 if( useReferences ) 118 if (useReferences) {
128 { 119 for (CtField field : c.getDeclaredFields()) {
129 for( CtField field : c.getDeclaredFields() ) 120 FieldEntry fieldEntry = new FieldEntry(m_classEntry, field.getName());
130 { 121 for (EntryReference<FieldEntry,BehaviorEntry> reference : index.getFieldReferences(fieldEntry)) {
131 FieldEntry fieldEntry = new FieldEntry( m_classEntry, field.getName() ); 122 addReference(reference);
132 for( EntryReference<FieldEntry,BehaviorEntry> reference : index.getFieldReferences( fieldEntry ) )
133 {
134 addReference( reference );
135 } 123 }
136 } 124 }
137 for( CtMethod method : c.getDeclaredMethods() ) 125 for (CtMethod method : c.getDeclaredMethods()) {
138 { 126 MethodEntry methodEntry = new MethodEntry(m_classEntry, method.getName(), method.getSignature());
139 MethodEntry methodEntry = new MethodEntry( m_classEntry, method.getName(), method.getSignature() ); 127 for (EntryReference<BehaviorEntry,BehaviorEntry> reference : index.getBehaviorReferences(methodEntry)) {
140 for( EntryReference<BehaviorEntry,BehaviorEntry> reference : index.getBehaviorReferences( methodEntry ) ) 128 addReference(reference);
141 {
142 addReference( reference );
143 } 129 }
144 } 130 }
145 for( CtConstructor constructor : c.getDeclaredConstructors() ) 131 for (CtConstructor constructor : c.getDeclaredConstructors()) {
146 { 132 ConstructorEntry constructorEntry = new ConstructorEntry(m_classEntry, constructor.getSignature());
147 ConstructorEntry constructorEntry = new ConstructorEntry( m_classEntry, constructor.getSignature() ); 133 for (EntryReference<BehaviorEntry,BehaviorEntry> reference : index.getBehaviorReferences(constructorEntry)) {
148 for( EntryReference<BehaviorEntry,BehaviorEntry> reference : index.getBehaviorReferences( constructorEntry ) ) 134 addReference(reference);
149 {
150 addReference( reference );
151 } 135 }
152 } 136 }
153 } 137 }
154 } 138 }
155 139
156 private void addReference( EntryReference<? extends Entry,BehaviorEntry> reference ) 140 private void addReference(EntryReference<? extends Entry,BehaviorEntry> reference) {
157 { 141 if (reference.context.getSignature() != null) {
158 if( reference.context.getSignature() != null ) 142 m_references.add(String.format("%s_%s", scrubClassName(reference.context.getClassName()), scrubSignature(reference.context.getSignature())));
159 { 143 } else {
160 m_references.add( String.format( "%s_%s", 144 m_references.add(String.format("%s_<clinit>", scrubClassName(reference.context.getClassName())));
161 scrubClassName( reference.context.getClassName() ),
162 scrubSignature( reference.context.getSignature() )
163 ) );
164 }
165 else
166 {
167 m_references.add( String.format( "%s_<clinit>",
168 scrubClassName( reference.context.getClassName() )
169 ) );
170 } 145 }
171 } 146 }
172 147
173 public ClassEntry getClassEntry( ) 148 public ClassEntry getClassEntry() {
174 {
175 return m_classEntry; 149 return m_classEntry;
176 } 150 }
177 151
178 @Override 152 @Override
179 public String toString( ) 153 public String toString() {
180 {
181 StringBuilder buf = new StringBuilder(); 154 StringBuilder buf = new StringBuilder();
182 buf.append( "class: " ); 155 buf.append("class: ");
183 buf.append( m_classEntry.getName() ); 156 buf.append(m_classEntry.getName());
184 buf.append( " " ); 157 buf.append(" ");
185 buf.append( hashCode() ); 158 buf.append(hashCode());
186 buf.append( "\n" ); 159 buf.append("\n");
187 for( String field : m_fields ) 160 for (String field : m_fields) {
188 { 161 buf.append("\tfield ");
189 buf.append( "\tfield " ); 162 buf.append(field);
190 buf.append( field ); 163 buf.append("\n");
191 buf.append( "\n" );
192 } 164 }
193 for( String method : m_methods ) 165 for (String method : m_methods) {
194 { 166 buf.append("\tmethod ");
195 buf.append( "\tmethod " ); 167 buf.append(method);
196 buf.append( method ); 168 buf.append("\n");
197 buf.append( "\n" );
198 } 169 }
199 for( String constructor : m_constructors ) 170 for (String constructor : m_constructors) {
200 { 171 buf.append("\tconstructor ");
201 buf.append( "\tconstructor " ); 172 buf.append(constructor);
202 buf.append( constructor ); 173 buf.append("\n");
203 buf.append( "\n" );
204 } 174 }
205 if( m_staticInitializer.length() > 0 ) 175 if (m_staticInitializer.length() > 0) {
206 { 176 buf.append("\tinitializer ");
207 buf.append( "\tinitializer " ); 177 buf.append(m_staticInitializer);
208 buf.append( m_staticInitializer ); 178 buf.append("\n");
209 buf.append( "\n" );
210 } 179 }
211 if( m_extends.length() > 0 ) 180 if (m_extends.length() > 0) {
212 { 181 buf.append("\textends ");
213 buf.append( "\textends " ); 182 buf.append(m_extends);
214 buf.append( m_extends ); 183 buf.append("\n");
215 buf.append( "\n" );
216 } 184 }
217 for( String interfaceName : m_implements ) 185 for (String interfaceName : m_implements) {
218 { 186 buf.append("\timplements ");
219 buf.append( "\timplements " ); 187 buf.append(interfaceName);
220 buf.append( interfaceName ); 188 buf.append("\n");
221 buf.append( "\n" );
222 } 189 }
223 for( String implementation : m_implementations ) 190 for (String implementation : m_implementations) {
224 { 191 buf.append("\timplemented by ");
225 buf.append( "\timplemented by " ); 192 buf.append(implementation);
226 buf.append( implementation ); 193 buf.append("\n");
227 buf.append( "\n" );
228 } 194 }
229 for( String reference : m_references ) 195 for (String reference : m_references) {
230 { 196 buf.append("\treference ");
231 buf.append( "\treference " ); 197 buf.append(reference);
232 buf.append( reference ); 198 buf.append("\n");
233 buf.append( "\n" );
234 } 199 }
235 return buf.toString(); 200 return buf.toString();
236 } 201 }
237 202
238 private String scrubClassName( String className ) 203 private String scrubClassName(String className) {
239 { 204 return scrubSignature("L" + Descriptor.toJvmName(className) + ";");
240 return scrubSignature( "L" + Descriptor.toJvmName( className ) + ";" );
241 } 205 }
242 206
243 private String scrubSignature( String signature ) 207 private String scrubSignature(String signature) {
244 { 208 return SignatureUpdater.update(signature, new ClassNameUpdater() {
245 return SignatureUpdater.update( signature, new ClassNameUpdater( )
246 {
247 private Map<String,String> m_classNames = Maps.newHashMap(); 209 private Map<String,String> m_classNames = Maps.newHashMap();
248 210
249 @Override 211 @Override
250 public String update( String className ) 212 public String update(String className) {
251 {
252 // classes not in the none package can be passed through 213 // classes not in the none package can be passed through
253 ClassEntry classEntry = new ClassEntry( className ); 214 ClassEntry classEntry = new ClassEntry(className);
254 if( !classEntry.getPackageName().equals( Constants.NonePackage ) ) 215 if (!classEntry.getPackageName().equals(Constants.NonePackage)) {
255 {
256 return className; 216 return className;
257 } 217 }
258 218
259 // is this class ourself? 219 // is this class ourself?
260 if( className.equals( m_classEntry.getName() ) ) 220 if (className.equals(m_classEntry.getName())) {
261 {
262 return "CSelf"; 221 return "CSelf";
263 } 222 }
264 223
265 // try the namer 224 // try the namer
266 if( m_namer != null ) 225 if (m_namer != null) {
267 { 226 String newName = m_namer.getName(className);
268 String newName = m_namer.getName( className ); 227 if (newName != null) {
269 if( newName != null )
270 {
271 return newName; 228 return newName;
272 } 229 }
273 } 230 }
274 231
275 // otherwise, use local naming 232 // otherwise, use local naming
276 if( !m_classNames.containsKey( className ) ) 233 if (!m_classNames.containsKey(className)) {
277 { 234 m_classNames.put(className, getNewClassName());
278 m_classNames.put( className, getNewClassName() );
279 } 235 }
280 return m_classNames.get( className ); 236 return m_classNames.get(className);
281 } 237 }
282 238
283 private String getNewClassName( ) 239 private String getNewClassName() {
284 { 240 return String.format("C%03d", m_classNames.size());
285 return String.format( "C%03d", m_classNames.size() );
286 } 241 }
287 } ); 242 });
288 } 243 }
289 244
290 private boolean isClassMatchedUniquely( String className ) 245 private boolean isClassMatchedUniquely(String className) {
291 { 246 return m_namer != null && m_namer.getName(Descriptor.toJvmName(className)) != null;
292 return m_namer != null && m_namer.getName( Descriptor.toJvmName( className ) ) != null;
293 } 247 }
294 248
295 private String getBehaviorSignature( CtBehavior behavior ) 249 private String getBehaviorSignature(CtBehavior behavior) {
296 { 250 try {
297 try
298 {
299 // does this method have an implementation? 251 // does this method have an implementation?
300 if( behavior.getMethodInfo().getCodeAttribute() == null ) 252 if (behavior.getMethodInfo().getCodeAttribute() == null) {
301 {
302 return "(none)"; 253 return "(none)";
303 } 254 }
304 255
305 // compute the hash from the opcodes 256 // compute the hash from the opcodes
306 ConstPool constants = behavior.getMethodInfo().getConstPool(); 257 ConstPool constants = behavior.getMethodInfo().getConstPool();
307 final MessageDigest digest = MessageDigest.getInstance( "MD5" ); 258 final MessageDigest digest = MessageDigest.getInstance("MD5");
308 CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator(); 259 CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator();
309 while( iter.hasNext() ) 260 while (iter.hasNext()) {
310 {
311 int pos = iter.next(); 261 int pos = iter.next();
312 262
313 // update the hash with the opcode 263 // update the hash with the opcode
314 int opcode = iter.byteAt( pos ); 264 int opcode = iter.byteAt(pos);
315 digest.update( (byte)opcode ); 265 digest.update((byte)opcode);
316 266
317 switch( opcode ) 267 switch (opcode) {
318 { 268 case Opcode.LDC: {
319 case Opcode.LDC: 269 int constIndex = iter.byteAt(pos + 1);
320 { 270 updateHashWithConstant(digest, constants, constIndex);
321 int constIndex = iter.byteAt( pos + 1 );
322 updateHashWithConstant( digest, constants, constIndex );
323 } 271 }
324 break; 272 break;
325 273
326 case Opcode.LDC_W: 274 case Opcode.LDC_W:
327 case Opcode.LDC2_W: 275 case Opcode.LDC2_W: {
328 { 276 int constIndex = (iter.byteAt(pos + 1) << 8) | iter.byteAt(pos + 2);
329 int constIndex = ( iter.byteAt( pos + 1 ) << 8 ) | iter.byteAt( pos + 2 ); 277 updateHashWithConstant(digest, constants, constIndex);
330 updateHashWithConstant( digest, constants, constIndex );
331 } 278 }
332 break; 279 break;
333 } 280 }
334 } 281 }
335 282
336 // update hash with method and field accesses 283 // update hash with method and field accesses
337 behavior.instrument( new ExprEditor( ) 284 behavior.instrument(new ExprEditor() {
338 {
339 @Override 285 @Override
340 public void edit( MethodCall call ) 286 public void edit(MethodCall call) {
341 { 287 updateHashWithString(digest, scrubClassName(call.getClassName()));
342 updateHashWithString( digest, scrubClassName( call.getClassName() ) ); 288 updateHashWithString(digest, scrubSignature(call.getSignature()));
343 updateHashWithString( digest, scrubSignature( call.getSignature() ) ); 289 if (isClassMatchedUniquely(call.getClassName())) {
344 if( isClassMatchedUniquely( call.getClassName() ) ) 290 updateHashWithString(digest, call.getMethodName());
345 {
346 updateHashWithString( digest, call.getMethodName() );
347 } 291 }
348 } 292 }
349 293
350 @Override 294 @Override
351 public void edit( FieldAccess access ) 295 public void edit(FieldAccess access) {
352 { 296 updateHashWithString(digest, scrubClassName(access.getClassName()));
353 updateHashWithString( digest, scrubClassName( access.getClassName() ) ); 297 updateHashWithString(digest, scrubSignature(access.getSignature()));
354 updateHashWithString( digest, scrubSignature( access.getSignature() ) ); 298 if (isClassMatchedUniquely(access.getClassName())) {
355 if( isClassMatchedUniquely( access.getClassName() ) ) 299 updateHashWithString(digest, access.getFieldName());
356 {
357 updateHashWithString( digest, access.getFieldName() );
358 } 300 }
359 } 301 }
360 302
361 @Override 303 @Override
362 public void edit( ConstructorCall call ) 304 public void edit(ConstructorCall call) {
363 { 305 updateHashWithString(digest, scrubClassName(call.getClassName()));
364 updateHashWithString( digest, scrubClassName( call.getClassName() ) ); 306 updateHashWithString(digest, scrubSignature(call.getSignature()));
365 updateHashWithString( digest, scrubSignature( call.getSignature() ) );
366 } 307 }
367 308
368 @Override 309 @Override
369 public void edit( NewExpr expr ) 310 public void edit(NewExpr expr) {
370 { 311 updateHashWithString(digest, scrubClassName(expr.getClassName()));
371 updateHashWithString( digest, scrubClassName( expr.getClassName() ) );
372 } 312 }
373 } ); 313 });
374 314
375 // convert the hash to a hex string 315 // convert the hash to a hex string
376 return toHex( digest.digest() ); 316 return toHex(digest.digest());
377 } 317 } catch (BadBytecode | NoSuchAlgorithmException | CannotCompileException ex) {
378 catch( BadBytecode | NoSuchAlgorithmException | CannotCompileException ex ) 318 throw new Error(ex);
379 {
380 throw new Error( ex );
381 } 319 }
382 } 320 }
383 321
384 private void updateHashWithConstant( MessageDigest digest, ConstPool constants, int index ) 322 private void updateHashWithConstant(MessageDigest digest, ConstPool constants, int index) {
385 { 323 ConstPoolEditor editor = new ConstPoolEditor(constants);
386 ConstPoolEditor editor = new ConstPoolEditor( constants ); 324 ConstInfoAccessor item = editor.getItem(index);
387 ConstInfoAccessor item = editor.getItem( index ); 325 if (item.getType() == InfoType.StringInfo) {
388 if( item.getType() == InfoType.StringInfo ) 326 updateHashWithString(digest, constants.getStringInfo(index));
389 {
390 updateHashWithString( digest, constants.getStringInfo( index ) );
391 } 327 }
392 // TODO: other constants 328 // TODO: other constants
393 } 329 }
394 330
395 private void updateHashWithString( MessageDigest digest, String val ) 331 private void updateHashWithString(MessageDigest digest, String val) {
396 { 332 try {
397 try 333 digest.update(val.getBytes("UTF8"));
398 { 334 } catch (UnsupportedEncodingException ex) {
399 digest.update( val.getBytes( "UTF8" ) ); 335 throw new Error(ex);
400 }
401 catch( UnsupportedEncodingException ex )
402 {
403 throw new Error( ex );
404 } 336 }
405 } 337 }
406 338
407 private String toHex( byte[] bytes ) 339 private String toHex(byte[] bytes) {
408 {
409 // function taken from: 340 // function taken from:
410 // http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java 341 // http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java
411 final char[] hexArray = "0123456789ABCDEF".toCharArray(); 342 final char[] hexArray = "0123456789ABCDEF".toCharArray();
412 char[] hexChars = new char[bytes.length * 2]; 343 char[] hexChars = new char[bytes.length * 2];
413 for( int j = 0; j < bytes.length; j++ ) 344 for (int j = 0; j < bytes.length; j++) {
414 {
415 int v = bytes[j] & 0xFF; 345 int v = bytes[j] & 0xFF;
416 hexChars[j * 2] = hexArray[v >>> 4]; 346 hexChars[j * 2] = hexArray[v >>> 4];
417 hexChars[j * 2 + 1] = hexArray[v & 0x0F]; 347 hexChars[j * 2 + 1] = hexArray[v & 0x0F];
418 } 348 }
419 return new String( hexChars ); 349 return new String(hexChars);
420 } 350 }
421 351
422 @Override 352 @Override
423 public boolean equals( Object other ) 353 public boolean equals(Object other) {
424 { 354 if (other instanceof ClassIdentity) {
425 if( other instanceof ClassIdentity ) 355 return equals((ClassIdentity)other);
426 {
427 return equals( (ClassIdentity)other );
428 } 356 }
429 return false; 357 return false;
430 } 358 }
431 359
432 public boolean equals( ClassIdentity other ) 360 public boolean equals(ClassIdentity other) {
433 { 361 return m_fields.equals(other.m_fields)
434 return m_fields.equals( other.m_fields ) 362 && m_methods.equals(other.m_methods)
435 && m_methods.equals( other.m_methods ) 363 && m_constructors.equals(other.m_constructors)
436 && m_constructors.equals( other.m_constructors ) 364 && m_staticInitializer.equals(other.m_staticInitializer)
437 && m_staticInitializer.equals( other.m_staticInitializer ) 365 && m_extends.equals(other.m_extends)
438 && m_extends.equals( other.m_extends ) 366 && m_implements.equals(other.m_implements)
439 && m_implements.equals( other.m_implements ) 367 && m_implementations.equals(other.m_implementations)
440 && m_implementations.equals( other.m_implementations ) 368 && m_references.equals(other.m_references);
441 && m_references.equals( other.m_references );
442 } 369 }
443 370
444 @Override 371 @Override
445 public int hashCode( ) 372 public int hashCode() {
446 {
447 List<Object> objs = Lists.newArrayList(); 373 List<Object> objs = Lists.newArrayList();
448 objs.addAll( m_fields ); 374 objs.addAll(m_fields);
449 objs.addAll( m_methods ); 375 objs.addAll(m_methods);
450 objs.addAll( m_constructors ); 376 objs.addAll(m_constructors);
451 objs.add( m_staticInitializer ); 377 objs.add(m_staticInitializer);
452 objs.add( m_extends ); 378 objs.add(m_extends);
453 objs.addAll( m_implements ); 379 objs.addAll(m_implements);
454 objs.addAll( m_implementations ); 380 objs.addAll(m_implementations);
455 objs.addAll( m_references ); 381 objs.addAll(m_references);
456 return Util.combineHashesOrdered( objs ); 382 return Util.combineHashesOrdered(objs);
457 } 383 }
458 384
459 public int getMatchScore( ClassIdentity other ) 385 public int getMatchScore(ClassIdentity other) {
460 { 386 return getNumMatches(m_fields, other.m_fields)
461 return getNumMatches( m_fields, other.m_fields ) 387 + getNumMatches(m_methods, other.m_methods)
462 + getNumMatches( m_methods, other.m_methods ) 388 + getNumMatches(m_constructors, other.m_constructors);
463 + getNumMatches( m_constructors, other.m_constructors );
464 } 389 }
465 390
466 public int getMaxMatchScore( ) 391 public int getMaxMatchScore() {
467 {
468 return m_fields.size() + m_methods.size() + m_constructors.size(); 392 return m_fields.size() + m_methods.size() + m_constructors.size();
469 } 393 }
470 394
471 public boolean matches( CtClass c ) 395 public boolean matches(CtClass c) {
472 {
473 // just compare declaration counts 396 // just compare declaration counts
474 return m_fields.size() == c.getDeclaredFields().length 397 return m_fields.size() == c.getDeclaredFields().length
475 && m_methods.size() == c.getDeclaredMethods().length 398 && m_methods.size() == c.getDeclaredMethods().length
476 && m_constructors.size() == c.getDeclaredConstructors().length; 399 && m_constructors.size() == c.getDeclaredConstructors().length;
477 } 400 }
478 401
479 private int getNumMatches( Multiset<String> a, Multiset<String> b ) 402 private int getNumMatches(Multiset<String> a, Multiset<String> b) {
480 {
481 int numMatches = 0; 403 int numMatches = 0;
482 for( String val : a ) 404 for (String val : a) {
483 { 405 if (b.contains(val)) {
484 if( b.contains( val ) )
485 {
486 numMatches++; 406 numMatches++;
487 } 407 }
488 } 408 }
diff --git a/src/cuchaz/enigma/convert/ClassMatcher.java b/src/cuchaz/enigma/convert/ClassMatcher.java
index 290d90a..fc39ed0 100644
--- a/src/cuchaz/enigma/convert/ClassMatcher.java
+++ b/src/cuchaz/enigma/convert/ClassMatcher.java
@@ -49,102 +49,92 @@ import cuchaz.enigma.mapping.MappingsWriter;
49import cuchaz.enigma.mapping.MethodEntry; 49import cuchaz.enigma.mapping.MethodEntry;
50import cuchaz.enigma.mapping.MethodMapping; 50import cuchaz.enigma.mapping.MethodMapping;
51 51
52public class ClassMatcher 52public class ClassMatcher {
53{ 53
54 public static void main( String[] args ) 54 public static void main(String[] args) throws IOException, MappingParseException {
55 throws IOException, MappingParseException
56 {
57 // TEMP 55 // TEMP
58 JarFile sourceJar = new JarFile( new File( "input/1.8-pre3.jar" ) ); 56 JarFile sourceJar = new JarFile(new File("input/1.8-pre3.jar"));
59 JarFile destJar = new JarFile( new File( "input/1.8.jar" ) ); 57 JarFile destJar = new JarFile(new File("input/1.8.jar"));
60 File inMappingsFile = new File( "../Enigma Mappings/1.8-pre3.mappings" ); 58 File inMappingsFile = new File("../Enigma Mappings/1.8-pre3.mappings");
61 File outMappingsFile = new File( "../Enigma Mappings/1.8.mappings" ); 59 File outMappingsFile = new File("../Enigma Mappings/1.8.mappings");
62 60
63 // define a matching to use when the automated system cannot find a match 61 // define a matching to use when the automated system cannot find a match
64 Map<String,String> fallbackMatching = Maps.newHashMap(); 62 Map<String,String> fallbackMatching = Maps.newHashMap();
65 fallbackMatching.put( "none/ayb", "none/ayf" ); 63 fallbackMatching.put("none/ayb", "none/ayf");
66 fallbackMatching.put( "none/ayd", "none/ayd" ); 64 fallbackMatching.put("none/ayd", "none/ayd");
67 fallbackMatching.put( "none/bgk", "unknown/bgk" ); 65 fallbackMatching.put("none/bgk", "unknown/bgk");
68 66
69 // do the conversion 67 // do the conversion
70 Mappings mappings = new MappingsReader().read( new FileReader( inMappingsFile ) ); 68 Mappings mappings = new MappingsReader().read(new FileReader(inMappingsFile));
71 convertMappings( sourceJar, destJar, mappings, fallbackMatching ); 69 convertMappings(sourceJar, destJar, mappings, fallbackMatching);
72 70
73 // write out the converted mappings 71 // write out the converted mappings
74 FileWriter writer = new FileWriter( outMappingsFile ); 72 FileWriter writer = new FileWriter(outMappingsFile);
75 new MappingsWriter().write( writer, mappings ); 73 new MappingsWriter().write(writer, mappings);
76 writer.close(); 74 writer.close();
77 System.out.println( "Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath() ); 75 System.out.println("Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath());
78 } 76 }
79 77
80 private static void convertMappings( JarFile sourceJar, JarFile destJar, Mappings mappings, Map<String,String> fallbackMatching ) 78 private static void convertMappings(JarFile sourceJar, JarFile destJar, Mappings mappings, Map<String,String> fallbackMatching) {
81 {
82 // index jars 79 // index jars
83 System.out.println( "Indexing source jar..." ); 80 System.out.println("Indexing source jar...");
84 JarIndex sourceIndex = new JarIndex(); 81 JarIndex sourceIndex = new JarIndex();
85 sourceIndex.indexJar( sourceJar, false ); 82 sourceIndex.indexJar(sourceJar, false);
86 System.out.println( "Indexing dest jar..." ); 83 System.out.println("Indexing dest jar...");
87 JarIndex destIndex = new JarIndex(); 84 JarIndex destIndex = new JarIndex();
88 destIndex.indexJar( destJar, false ); 85 destIndex.indexJar(destJar, false);
89 TranslatingTypeLoader sourceLoader = new TranslatingTypeLoader( sourceJar, sourceIndex ); 86 TranslatingTypeLoader sourceLoader = new TranslatingTypeLoader(sourceJar, sourceIndex);
90 TranslatingTypeLoader destLoader = new TranslatingTypeLoader( destJar, destIndex ); 87 TranslatingTypeLoader destLoader = new TranslatingTypeLoader(destJar, destIndex);
91 88
92 // compute the matching 89 // compute the matching
93 ClassMatching matching = computeMatching( sourceIndex, sourceLoader, destIndex, destLoader ); 90 ClassMatching matching = computeMatching(sourceIndex, sourceLoader, destIndex, destLoader);
94 Map<String,Map.Entry<ClassIdentity,List<ClassIdentity>>> matchingIndex = matching.getIndex(); 91 Map<String,Map.Entry<ClassIdentity,List<ClassIdentity>>> matchingIndex = matching.getIndex();
95 92
96 // get all the obf class names used in the mappings 93 // get all the obf class names used in the mappings
97 Set<String> usedClassNames = mappings.getAllObfClassNames(); 94 Set<String> usedClassNames = mappings.getAllObfClassNames();
98 Set<String> allClassNames = Sets.newHashSet(); 95 Set<String> allClassNames = Sets.newHashSet();
99 for( ClassEntry classEntry : sourceIndex.getObfClassEntries() ) 96 for (ClassEntry classEntry : sourceIndex.getObfClassEntries()) {
100 { 97 allClassNames.add(classEntry.getName());
101 allClassNames.add( classEntry.getName() );
102 } 98 }
103 usedClassNames.retainAll( allClassNames ); 99 usedClassNames.retainAll(allClassNames);
104 System.out.println( "Used " + usedClassNames.size() + " classes in the mappings" ); 100 System.out.println("Used " + usedClassNames.size() + " classes in the mappings");
105 101
106 // probabilistically match the non-uniquely-matched source classes 102 // probabilistically match the non-uniquely-matched source classes
107 for( Map.Entry<ClassIdentity,List<ClassIdentity>> entry : matchingIndex.values() ) 103 for (Map.Entry<ClassIdentity,List<ClassIdentity>> entry : matchingIndex.values()) {
108 {
109 ClassIdentity sourceClass = entry.getKey(); 104 ClassIdentity sourceClass = entry.getKey();
110 List<ClassIdentity> destClasses = entry.getValue(); 105 List<ClassIdentity> destClasses = entry.getValue();
111 106
112 // skip classes that are uniquely matched 107 // skip classes that are uniquely matched
113 if( destClasses.size() == 1 ) 108 if (destClasses.size() == 1) {
114 {
115 continue; 109 continue;
116 } 110 }
117 111
118 // skip classes that aren't used in the mappings 112 // skip classes that aren't used in the mappings
119 if( !usedClassNames.contains( sourceClass.getClassEntry().getName() ) ) 113 if (!usedClassNames.contains(sourceClass.getClassEntry().getName())) {
120 {
121 continue; 114 continue;
122 } 115 }
123 116
124 System.out.println( "No exact match for source class " + sourceClass.getClassEntry() ); 117 System.out.println("No exact match for source class " + sourceClass.getClassEntry());
125 118
126 // find the closest classes 119 // find the closest classes
127 Multimap<Integer,ClassIdentity> scoredMatches = ArrayListMultimap.create(); 120 Multimap<Integer,ClassIdentity> scoredMatches = ArrayListMultimap.create();
128 for( ClassIdentity c : destClasses ) 121 for (ClassIdentity c : destClasses) {
129 { 122 scoredMatches.put(sourceClass.getMatchScore(c), c);
130 scoredMatches.put( sourceClass.getMatchScore( c ), c );
131 } 123 }
132 List<Integer> scores = new ArrayList<Integer>( scoredMatches.keySet() ); 124 List<Integer> scores = new ArrayList<Integer>(scoredMatches.keySet());
133 Collections.sort( scores, Collections.reverseOrder() ); 125 Collections.sort(scores, Collections.reverseOrder());
134 printScoredMatches( sourceClass.getMaxMatchScore(), scores, scoredMatches ); 126 printScoredMatches(sourceClass.getMaxMatchScore(), scores, scoredMatches);
135 127
136 // does the best match have a non-zero score and the same name? 128 // does the best match have a non-zero score and the same name?
137 int bestScore = scores.get( 0 ); 129 int bestScore = scores.get(0);
138 Collection<ClassIdentity> bestMatches = scoredMatches.get( bestScore ); 130 Collection<ClassIdentity> bestMatches = scoredMatches.get(bestScore);
139 if( bestScore > 0 && bestMatches.size() == 1 ) 131 if (bestScore > 0 && bestMatches.size() == 1) {
140 {
141 ClassIdentity bestMatch = bestMatches.iterator().next(); 132 ClassIdentity bestMatch = bestMatches.iterator().next();
142 if( bestMatch.getClassEntry().equals( sourceClass.getClassEntry() ) ) 133 if (bestMatch.getClassEntry().equals(sourceClass.getClassEntry())) {
143 {
144 // use it 134 // use it
145 System.out.println( "\tAutomatically choosing likely match: " + bestMatch.getClassEntry().getName() ); 135 System.out.println("\tAutomatically choosing likely match: " + bestMatch.getClassEntry().getName());
146 destClasses.clear(); 136 destClasses.clear();
147 destClasses.add( bestMatch ); 137 destClasses.add(bestMatch);
148 } 138 }
149 } 139 }
150 } 140 }
@@ -152,63 +142,46 @@ public class ClassMatcher
152 // group the matching into unique and non-unique matches 142 // group the matching into unique and non-unique matches
153 BiMap<String,String> matchedClassNames = HashBiMap.create(); 143 BiMap<String,String> matchedClassNames = HashBiMap.create();
154 Set<String> unmatchedSourceClassNames = Sets.newHashSet(); 144 Set<String> unmatchedSourceClassNames = Sets.newHashSet();
155 for( String className : usedClassNames ) 145 for (String className : usedClassNames) {
156 {
157 // is there a match for this class? 146 // is there a match for this class?
158 Map.Entry<ClassIdentity,List<ClassIdentity>> entry = matchingIndex.get( className ); 147 Map.Entry<ClassIdentity,List<ClassIdentity>> entry = matchingIndex.get(className);
159 ClassIdentity sourceClass = entry.getKey(); 148 ClassIdentity sourceClass = entry.getKey();
160 List<ClassIdentity> matches = entry.getValue(); 149 List<ClassIdentity> matches = entry.getValue();
161 150
162 if( matches.size() == 1 ) 151 if (matches.size() == 1) {
163 {
164 // unique match! We're good to go! 152 // unique match! We're good to go!
165 matchedClassNames.put( 153 matchedClassNames.put(sourceClass.getClassEntry().getName(), matches.get(0).getClassEntry().getName());
166 sourceClass.getClassEntry().getName(), 154 } else {
167 matches.get( 0 ).getClassEntry().getName()
168 );
169 }
170 else
171 {
172 // no match, check the fallback matching 155 // no match, check the fallback matching
173 String fallbackMatch = fallbackMatching.get( className ); 156 String fallbackMatch = fallbackMatching.get(className);
174 if( fallbackMatch != null ) 157 if (fallbackMatch != null) {
175 { 158 matchedClassNames.put(sourceClass.getClassEntry().getName(), fallbackMatch);
176 matchedClassNames.put( 159 } else {
177 sourceClass.getClassEntry().getName(), 160 unmatchedSourceClassNames.add(className);
178 fallbackMatch
179 );
180 }
181 else
182 {
183 unmatchedSourceClassNames.add( className );
184 } 161 }
185 } 162 }
186 } 163 }
187 164
188 // report unmatched classes 165 // report unmatched classes
189 if( !unmatchedSourceClassNames.isEmpty() ) 166 if (!unmatchedSourceClassNames.isEmpty()) {
190 { 167 System.err.println("ERROR: there were unmatched classes!");
191 System.err.println( "ERROR: there were unmatched classes!" ); 168 for (String className : unmatchedSourceClassNames) {
192 for( String className : unmatchedSourceClassNames ) 169 System.err.println("\t" + className);
193 {
194 System.err.println( "\t" + className );
195 } 170 }
196 return; 171 return;
197 } 172 }
198 173
199 // get the class name changes from the matched class names 174 // get the class name changes from the matched class names
200 Map<String,String> classChanges = Maps.newHashMap(); 175 Map<String,String> classChanges = Maps.newHashMap();
201 for( Map.Entry<String,String> entry : matchedClassNames.entrySet() ) 176 for (Map.Entry<String,String> entry : matchedClassNames.entrySet()) {
202 { 177 if (!entry.getKey().equals(entry.getValue())) {
203 if( !entry.getKey().equals( entry.getValue() ) ) 178 classChanges.put(entry.getKey(), entry.getValue());
204 { 179 System.out.println(String.format("Class change: %s -> %s", entry.getKey(), entry.getValue()));
205 classChanges.put( entry.getKey(), entry.getValue() );
206 System.out.println( String.format( "Class change: %s -> %s", entry.getKey(), entry.getValue() ) );
207 /* DEBUG 180 /* DEBUG
208 System.out.println( String.format( "\n%s\n%s", 181 System.out.println(String.format("\n%s\n%s",
209 new ClassIdentity( sourceLoader.loadClass( entry.getKey() ), null, sourceIndex, false, false ), 182 new ClassIdentity(sourceLoader.loadClass(entry.getKey()), null, sourceIndex, false, false),
210 new ClassIdentity( destLoader.loadClass( entry.getValue() ), null, destIndex, false, false ) 183 new ClassIdentity( destLoader.loadClass(entry.getValue()), null, destIndex, false, false)
211 ) ); 184 ));
212 */ 185 */
213 } 186 }
214 } 187 }
@@ -217,52 +190,42 @@ public class ClassMatcher
217 // ie. if we have the mappings a->b, b->c, we have to apply b->c before a->b 190 // ie. if we have the mappings a->b, b->c, we have to apply b->c before a->b
218 LinkedHashMap<String,String> orderedClassChanges = Maps.newLinkedHashMap(); 191 LinkedHashMap<String,String> orderedClassChanges = Maps.newLinkedHashMap();
219 int numChangesLeft = classChanges.size(); 192 int numChangesLeft = classChanges.size();
220 while( !classChanges.isEmpty() ) 193 while (!classChanges.isEmpty()) {
221 {
222 Iterator<Map.Entry<String,String>> iter = classChanges.entrySet().iterator(); 194 Iterator<Map.Entry<String,String>> iter = classChanges.entrySet().iterator();
223 while( iter.hasNext() ) 195 while (iter.hasNext()) {
224 {
225 Map.Entry<String,String> entry = iter.next(); 196 Map.Entry<String,String> entry = iter.next();
226 if( classChanges.get( entry.getValue() ) == null ) 197 if (classChanges.get(entry.getValue()) == null) {
227 { 198 orderedClassChanges.put(entry.getKey(), entry.getValue());
228 orderedClassChanges.put( entry.getKey(), entry.getValue() );
229 iter.remove(); 199 iter.remove();
230 } 200 }
231 } 201 }
232 202
233 // did we remove any changes? 203 // did we remove any changes?
234 if( numChangesLeft - classChanges.size() > 0 ) 204 if (numChangesLeft - classChanges.size() > 0) {
235 {
236 // keep going 205 // keep going
237 numChangesLeft = classChanges.size(); 206 numChangesLeft = classChanges.size();
238 } 207 } else {
239 else
240 {
241 // can't sort anymore. There must be a loop 208 // can't sort anymore. There must be a loop
242 break; 209 break;
243 } 210 }
244 } 211 }
245 if( classChanges.size() > 0 ) 212 if (classChanges.size() > 0) {
246 { 213 throw new Error(String.format("Unable to sort %d/%d class changes!", classChanges.size(), matchedClassNames.size()));
247 throw new Error( String.format( "Unable to sort %d/%d class changes!", classChanges.size(), matchedClassNames.size() ) );
248 } 214 }
249 215
250 // convert the mappings in the correct class order 216 // convert the mappings in the correct class order
251 for( Map.Entry<String,String> entry : orderedClassChanges.entrySet() ) 217 for (Map.Entry<String,String> entry : orderedClassChanges.entrySet()) {
252 { 218 mappings.renameObfClass(entry.getKey(), entry.getValue());
253 mappings.renameObfClass( entry.getKey(), entry.getValue() );
254 } 219 }
255 220
256 // check the method matches 221 // check the method matches
257 System.out.println( "Checking methods..." ); 222 System.out.println("Checking methods...");
258 for( ClassMapping classMapping : mappings.classes() ) 223 for (ClassMapping classMapping : mappings.classes()) {
259 { 224 ClassEntry classEntry = new ClassEntry(classMapping.getObfName());
260 ClassEntry classEntry = new ClassEntry( classMapping.getObfName() ); 225 for (MethodMapping methodMapping : classMapping.methods()) {
261 for( MethodMapping methodMapping : classMapping.methods() ) 226
262 {
263 // skip constructors 227 // skip constructors
264 if( methodMapping.getObfName().equals( "<init>" ) ) 228 if (methodMapping.getObfName().equals("<init>")) {
265 {
266 continue; 229 continue;
267 } 230 }
268 231
@@ -271,56 +234,51 @@ public class ClassMatcher
271 methodMapping.getObfName(), 234 methodMapping.getObfName(),
272 methodMapping.getObfSignature() 235 methodMapping.getObfSignature()
273 ); 236 );
274 if( !destIndex.containsObfBehavior( methodEntry ) ) 237 if (!destIndex.containsObfBehavior(methodEntry)) {
275 { 238 System.err.println("WARNING: method doesn't match: " + methodEntry);
276 System.err.println( "WARNING: method doesn't match: " + methodEntry );
277 239
278 // show the available methods 240 // show the available methods
279 System.err.println( "\tAvailable dest methods:" ); 241 System.err.println("\tAvailable dest methods:");
280 CtClass c = destLoader.loadClass( classMapping.getObfName() ); 242 CtClass c = destLoader.loadClass(classMapping.getObfName());
281 for( CtBehavior behavior : c.getDeclaredBehaviors() ) 243 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
282 {
283 MethodEntry declaredMethodEntry = new MethodEntry( 244 MethodEntry declaredMethodEntry = new MethodEntry(
284 new ClassEntry( classMapping.getObfName() ), 245 new ClassEntry(classMapping.getObfName()),
285 behavior.getName(), 246 behavior.getName(),
286 behavior.getSignature() 247 behavior.getSignature()
287 ); 248 );
288 System.err.println( "\t\t" + declaredMethodEntry ); 249 System.err.println("\t\t" + declaredMethodEntry);
289 } 250 }
290 251
291 System.err.println( "\tAvailable source methods:" ); 252 System.err.println("\tAvailable source methods:");
292 c = sourceLoader.loadClass( matchedClassNames.inverse().get( classMapping.getObfName() ) ); 253 c = sourceLoader.loadClass(matchedClassNames.inverse().get(classMapping.getObfName()));
293 for( CtBehavior behavior : c.getDeclaredBehaviors() ) 254 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
294 {
295 MethodEntry declaredMethodEntry = new MethodEntry( 255 MethodEntry declaredMethodEntry = new MethodEntry(
296 new ClassEntry( classMapping.getObfName() ), 256 new ClassEntry(classMapping.getObfName()),
297 behavior.getName(), 257 behavior.getName(),
298 behavior.getSignature() 258 behavior.getSignature()
299 ); 259 );
300 System.err.println( "\t\t" + declaredMethodEntry ); 260 System.err.println("\t\t" + declaredMethodEntry);
301 } 261 }
302 } 262 }
303 } 263 }
304 } 264 }
305 265
306 System.out.println( "Done!" ); 266 System.out.println("Done!");
307 } 267 }
308 268
309 public static ClassMatching computeMatching( JarIndex sourceIndex, TranslatingTypeLoader sourceLoader, JarIndex destIndex, TranslatingTypeLoader destLoader ) 269 public static ClassMatching computeMatching(JarIndex sourceIndex, TranslatingTypeLoader sourceLoader, JarIndex destIndex, TranslatingTypeLoader destLoader) {
310 { 270
311 System.out.println( "Matching classes..." ); 271 System.out.println("Matching classes...");
272
312 ClassMatching matching = null; 273 ClassMatching matching = null;
313 for( boolean useReferences : Arrays.asList( false, true ) ) 274 for (boolean useReferences : Arrays.asList(false, true)) {
314 {
315 int numMatches = 0; 275 int numMatches = 0;
316 do 276 do {
317 {
318 SidedClassNamer sourceNamer = null; 277 SidedClassNamer sourceNamer = null;
319 SidedClassNamer destNamer = null; 278 SidedClassNamer destNamer = null;
320 if( matching != null ) 279 if (matching != null) {
321 {
322 // build a class namer 280 // build a class namer
323 ClassNamer namer = new ClassNamer( matching.getUniqueMatches() ); 281 ClassNamer namer = new ClassNamer(matching.getUniqueMatches());
324 sourceNamer = namer.getSourceNamer(); 282 sourceNamer = namer.getSourceNamer();
325 destNamer = namer.getDestNamer(); 283 destNamer = namer.getDestNamer();
326 284
@@ -331,158 +289,126 @@ public class ClassMatcher
331 // get the entries left to match 289 // get the entries left to match
332 Set<ClassEntry> sourceClassEntries = Sets.newHashSet(); 290 Set<ClassEntry> sourceClassEntries = Sets.newHashSet();
333 Set<ClassEntry> destClassEntries = Sets.newHashSet(); 291 Set<ClassEntry> destClassEntries = Sets.newHashSet();
334 if( matching == null ) 292 if (matching == null) {
335 { 293 sourceClassEntries.addAll(sourceIndex.getObfClassEntries());
336 sourceClassEntries.addAll( sourceIndex.getObfClassEntries() ); 294 destClassEntries.addAll(destIndex.getObfClassEntries());
337 destClassEntries.addAll( destIndex.getObfClassEntries() );
338 matching = new ClassMatching(); 295 matching = new ClassMatching();
339 } 296 } else {
340 else 297 for (Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry : matching.getAmbiguousMatches().entrySet()) {
341 { 298 for (ClassIdentity c : entry.getKey()) {
342 for( Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry : matching.getAmbiguousMatches().entrySet() ) 299 sourceClassEntries.add(c.getClassEntry());
343 { 300 matching.removeSource(c);
344 for( ClassIdentity c : entry.getKey() )
345 {
346 sourceClassEntries.add( c.getClassEntry() );
347 matching.removeSource( c );
348 } 301 }
349 for( ClassIdentity c : entry.getValue() ) 302 for (ClassIdentity c : entry.getValue()) {
350 { 303 destClassEntries.add(c.getClassEntry());
351 destClassEntries.add( c.getClassEntry() ); 304 matching.removeDest(c);
352 matching.removeDest( c );
353 } 305 }
354 } 306 }
355 for( ClassIdentity c : matching.getUnmatchedSourceClasses() ) 307 for (ClassIdentity c : matching.getUnmatchedSourceClasses()) {
356 { 308 sourceClassEntries.add(c.getClassEntry());
357 sourceClassEntries.add( c.getClassEntry() ); 309 matching.removeSource(c);
358 matching.removeSource( c );
359 } 310 }
360 for( ClassIdentity c : matching.getUnmatchedDestClasses() ) 311 for (ClassIdentity c : matching.getUnmatchedDestClasses()) {
361 { 312 destClassEntries.add(c.getClassEntry());
362 destClassEntries.add( c.getClassEntry() ); 313 matching.removeDest(c);
363 matching.removeDest( c );
364 } 314 }
365 } 315 }
366 316
367 // compute a matching for the classes 317 // compute a matching for the classes
368 for( ClassEntry classEntry : sourceClassEntries ) 318 for (ClassEntry classEntry : sourceClassEntries) {
369 { 319 CtClass c = sourceLoader.loadClass(classEntry.getName());
370 CtClass c = sourceLoader.loadClass( classEntry.getName() ); 320 ClassIdentity sourceClass = new ClassIdentity(c, sourceNamer, sourceIndex, useReferences);
371 ClassIdentity sourceClass = new ClassIdentity( c, sourceNamer, sourceIndex, useReferences ); 321 matching.addSource(sourceClass);
372 matching.addSource( sourceClass );
373 } 322 }
374 for( ClassEntry classEntry : destClassEntries ) 323 for (ClassEntry classEntry : destClassEntries) {
375 { 324 CtClass c = destLoader.loadClass(classEntry.getName());
376 CtClass c = destLoader.loadClass( classEntry.getName() ); 325 ClassIdentity destClass = new ClassIdentity(c, destNamer, destIndex, useReferences);
377 ClassIdentity destClass = new ClassIdentity( c, destNamer, destIndex, useReferences ); 326 matching.matchDestClass(destClass);
378 matching.matchDestClass( destClass );
379 } 327 }
380 328
381 // TEMP 329 // TEMP
382 System.out.println( matching ); 330 System.out.println(matching);
383 } 331 } while (matching.getUniqueMatches().size() - numMatches > 0);
384 while( matching.getUniqueMatches().size() - numMatches > 0 );
385 } 332 }
386 333
387 // check the class matches 334 // check the class matches
388 System.out.println( "Checking class matches..." ); 335 System.out.println("Checking class matches...");
389 ClassNamer namer = new ClassNamer( matching.getUniqueMatches() ); 336 ClassNamer namer = new ClassNamer(matching.getUniqueMatches());
390 SidedClassNamer sourceNamer = namer.getSourceNamer(); 337 SidedClassNamer sourceNamer = namer.getSourceNamer();
391 SidedClassNamer destNamer = namer.getDestNamer(); 338 SidedClassNamer destNamer = namer.getDestNamer();
392 for( Map.Entry<ClassIdentity,ClassIdentity> entry : matching.getUniqueMatches().entrySet() ) 339 for (Map.Entry<ClassIdentity,ClassIdentity> entry : matching.getUniqueMatches().entrySet()) {
393 { 340
394 // check source 341 // check source
395 ClassIdentity sourceClass = entry.getKey(); 342 ClassIdentity sourceClass = entry.getKey();
396 CtClass sourceC = sourceLoader.loadClass( sourceClass.getClassEntry().getName() ); 343 CtClass sourceC = sourceLoader.loadClass(sourceClass.getClassEntry().getName());
397 assert( sourceC != null ) 344 assert (sourceC != null) : "Unable to load source class " + sourceClass.getClassEntry();
398 : "Unable to load source class " + sourceClass.getClassEntry(); 345 assert (sourceClass.matches(sourceC)) : "Source " + sourceClass + " doesn't match " + new ClassIdentity(sourceC, sourceNamer, sourceIndex, false);
399 assert( sourceClass.matches( sourceC ) )
400 : "Source " + sourceClass + " doesn't match " + new ClassIdentity( sourceC, sourceNamer, sourceIndex, false );
401 346
402 // check dest 347 // check dest
403 ClassIdentity destClass = entry.getValue(); 348 ClassIdentity destClass = entry.getValue();
404 CtClass destC = destLoader.loadClass( destClass.getClassEntry().getName() ); 349 CtClass destC = destLoader.loadClass(destClass.getClassEntry().getName());
405 assert( destC != null ) 350 assert (destC != null) : "Unable to load dest class " + destClass.getClassEntry();
406 : "Unable to load dest class " + destClass.getClassEntry(); 351 assert (destClass.matches(destC)) : "Dest " + destClass + " doesn't match " + new ClassIdentity(destC, destNamer, destIndex, false);
407 assert( destClass.matches( destC ) )
408 : "Dest " + destClass + " doesn't match " + new ClassIdentity( destC, destNamer, destIndex, false );
409 } 352 }
410 353
411 // warn about the ambiguous matchings 354 // warn about the ambiguous matchings
412 List<Map.Entry<List<ClassIdentity>,List<ClassIdentity>>> ambiguousMatches = new ArrayList<Map.Entry<List<ClassIdentity>,List<ClassIdentity>>>( matching.getAmbiguousMatches().entrySet() ); 355 List<Map.Entry<List<ClassIdentity>,List<ClassIdentity>>> ambiguousMatches = new ArrayList<Map.Entry<List<ClassIdentity>,List<ClassIdentity>>>(matching.getAmbiguousMatches().entrySet());
413 Collections.sort( ambiguousMatches, new Comparator<Map.Entry<List<ClassIdentity>,List<ClassIdentity>>>( ) 356 Collections.sort(ambiguousMatches, new Comparator<Map.Entry<List<ClassIdentity>,List<ClassIdentity>>>() {
414 {
415 @Override 357 @Override
416 public int compare( Map.Entry<List<ClassIdentity>,List<ClassIdentity>> a, Map.Entry<List<ClassIdentity>,List<ClassIdentity>> b ) 358 public int compare(Map.Entry<List<ClassIdentity>,List<ClassIdentity>> a, Map.Entry<List<ClassIdentity>,List<ClassIdentity>> b) {
417 { 359 String aName = a.getKey().get(0).getClassEntry().getName();
418 String aName = a.getKey().get( 0 ).getClassEntry().getName(); 360 String bName = b.getKey().get(0).getClassEntry().getName();
419 String bName = b.getKey().get( 0 ).getClassEntry().getName(); 361 return aName.compareTo(bName);
420 return aName.compareTo( bName );
421 } 362 }
422 } ); 363 });
423 for( Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry : ambiguousMatches ) 364 for (Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry : ambiguousMatches) {
424 { 365 System.out.println("Ambiguous matching:");
425 System.out.println( "Ambiguous matching:" ); 366 System.out.println("\tSource: " + getClassNames(entry.getKey()));
426 System.out.println( "\tSource: " + getClassNames( entry.getKey() ) ); 367 System.out.println("\tDest: " + getClassNames(entry.getValue()));
427 System.out.println( "\tDest: " + getClassNames( entry.getValue() ) );
428 } 368 }
429 369
430 /* DEBUG 370 /* DEBUG
431 Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry = ambiguousMatches.get( 7 ); 371 Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry = ambiguousMatches.get( 7 );
432 for( ClassIdentity c : entry.getKey() ) 372 for (ClassIdentity c : entry.getKey()) {
433 { 373 System.out.println(c);
434 System.out.println( c );
435 } 374 }
436 for( ClassIdentity c : entry.getKey() ) 375 for(ClassIdentity c : entry.getKey()) {
437 { 376 System.out.println(decompile(sourceLoader, c.getClassEntry()));
438 System.out.println( decompile( sourceLoader, c.getClassEntry() ) );
439 } 377 }
440 */ 378 */
441 379
442 return matching; 380 return matching;
443 } 381 }
444 382
445 private static void printScoredMatches( int maxScore, List<Integer> scores, Multimap<Integer,ClassIdentity> scoredMatches ) 383 private static void printScoredMatches(int maxScore, List<Integer> scores, Multimap<Integer,ClassIdentity> scoredMatches) {
446 {
447 int numScoredMatchesShown = 0; 384 int numScoredMatchesShown = 0;
448 for( int score : scores ) 385 for (int score : scores) {
449 { 386 for (ClassIdentity scoredMatch : scoredMatches.get(score)) {
450 for( ClassIdentity scoredMatch : scoredMatches.get( score ) ) 387 System.out.println(String.format("\tScore: %3d %3.0f%% %s", score, 100.0 * score / maxScore, scoredMatch.getClassEntry().getName()));
451 { 388 if (numScoredMatchesShown++ > 10) {
452 System.out.println( String.format( "\tScore: %3d %3.0f%% %s",
453 score,
454 100.0*score/maxScore,
455 scoredMatch.getClassEntry().getName()
456 ) );
457
458 if( numScoredMatchesShown++ > 10 )
459 {
460 return; 389 return;
461 } 390 }
462 } 391 }
463 } 392 }
464 } 393 }
465 394
466 private static List<String> getClassNames( Collection<ClassIdentity> classes ) 395 private static List<String> getClassNames(Collection<ClassIdentity> classes) {
467 {
468 List<String> out = Lists.newArrayList(); 396 List<String> out = Lists.newArrayList();
469 for( ClassIdentity c : classes ) 397 for (ClassIdentity c : classes) {
470 { 398 out.add(c.getClassEntry().getName());
471 out.add( c.getClassEntry().getName() );
472 } 399 }
473 Collections.sort( out ); 400 Collections.sort(out);
474 return out; 401 return out;
475 } 402 }
476 403
477 /* DEBUG 404 /* DEBUG
478 private static String decompile( TranslatingTypeLoader loader, ClassEntry classEntry ) 405 private static String decompile(TranslatingTypeLoader loader, ClassEntry classEntry) {
479 {
480 PlainTextOutput output = new PlainTextOutput(); 406 PlainTextOutput output = new PlainTextOutput();
481 DecompilerSettings settings = DecompilerSettings.javaDefaults(); 407 DecompilerSettings settings = DecompilerSettings.javaDefaults();
482 settings.setForceExplicitImports( true ); 408 settings.setForceExplicitImports(true);
483 settings.setShowSyntheticMembers( true ); 409 settings.setShowSyntheticMembers(true);
484 settings.setTypeLoader( loader ); 410 settings.setTypeLoader(loader);
485 Decompiler.decompile( classEntry.getName(), output, settings ); 411 Decompiler.decompile(classEntry.getName(), output, settings);
486 return output.toString(); 412 return output.toString();
487 } 413 }
488 */ 414 */
diff --git a/src/cuchaz/enigma/convert/ClassMatching.java b/src/cuchaz/enigma/convert/ClassMatching.java
index e45c0e1..53b6f7f 100644
--- a/src/cuchaz/enigma/convert/ClassMatching.java
+++ b/src/cuchaz/enigma/convert/ClassMatching.java
@@ -24,180 +24,150 @@ import com.google.common.collect.Lists;
24import com.google.common.collect.Maps; 24import com.google.common.collect.Maps;
25import com.google.common.collect.Multimap; 25import com.google.common.collect.Multimap;
26 26
27public class ClassMatching 27public class ClassMatching {
28{ 28
29 private Multimap<ClassIdentity,ClassIdentity> m_sourceClasses; 29 private Multimap<ClassIdentity,ClassIdentity> m_sourceClasses;
30 private Multimap<ClassIdentity,ClassIdentity> m_matchedDestClasses; 30 private Multimap<ClassIdentity,ClassIdentity> m_matchedDestClasses;
31 private List<ClassIdentity> m_unmatchedDestClasses; 31 private List<ClassIdentity> m_unmatchedDestClasses;
32 32
33 public ClassMatching( ) 33 public ClassMatching() {
34 {
35 m_sourceClasses = ArrayListMultimap.create(); 34 m_sourceClasses = ArrayListMultimap.create();
36 m_matchedDestClasses = ArrayListMultimap.create(); 35 m_matchedDestClasses = ArrayListMultimap.create();
37 m_unmatchedDestClasses = Lists.newArrayList(); 36 m_unmatchedDestClasses = Lists.newArrayList();
38 } 37 }
39 38
40 public void addSource( ClassIdentity c ) 39 public void addSource(ClassIdentity c) {
41 { 40 m_sourceClasses.put(c, c);
42 m_sourceClasses.put( c, c );
43 } 41 }
44 42
45 public void matchDestClass( ClassIdentity destClass ) 43 public void matchDestClass(ClassIdentity destClass) {
46 { 44 Collection<ClassIdentity> matchedSourceClasses = m_sourceClasses.get(destClass);
47 Collection<ClassIdentity> matchedSourceClasses = m_sourceClasses.get( destClass ); 45 if (matchedSourceClasses.isEmpty()) {
48 if( matchedSourceClasses.isEmpty() )
49 {
50 // no match 46 // no match
51 m_unmatchedDestClasses.add( destClass ); 47 m_unmatchedDestClasses.add(destClass);
52 } 48 } else {
53 else
54 {
55 // found a match 49 // found a match
56 m_matchedDestClasses.put( destClass, destClass ); 50 m_matchedDestClasses.put(destClass, destClass);
57 51
58 // DEBUG 52 // DEBUG
59 ClassIdentity sourceClass = matchedSourceClasses.iterator().next(); 53 ClassIdentity sourceClass = matchedSourceClasses.iterator().next();
60 assert( sourceClass.hashCode() == destClass.hashCode() ); 54 assert (sourceClass.hashCode() == destClass.hashCode());
61 assert( sourceClass.equals( destClass ) ); 55 assert (sourceClass.equals(destClass));
62 } 56 }
63 } 57 }
64 58
65 public void removeSource( ClassIdentity sourceClass ) 59 public void removeSource(ClassIdentity sourceClass) {
66 { 60 m_sourceClasses.remove(sourceClass, sourceClass);
67 m_sourceClasses.remove( sourceClass, sourceClass );
68 } 61 }
69 62
70 public void removeDest( ClassIdentity destClass ) 63 public void removeDest(ClassIdentity destClass) {
71 { 64 m_matchedDestClasses.remove(destClass, destClass);
72 m_matchedDestClasses.remove( destClass, destClass ); 65 m_unmatchedDestClasses.remove(destClass);
73 m_unmatchedDestClasses.remove( destClass );
74 } 66 }
75 67
76 public List<ClassIdentity> getSourceClasses( ) 68 public List<ClassIdentity> getSourceClasses() {
77 { 69 return new ArrayList<ClassIdentity>(m_sourceClasses.values());
78 return new ArrayList<ClassIdentity>( m_sourceClasses.values() );
79 } 70 }
80 71
81 public List<ClassIdentity> getDestClasses( ) 72 public List<ClassIdentity> getDestClasses() {
82 {
83 List<ClassIdentity> classes = Lists.newArrayList(); 73 List<ClassIdentity> classes = Lists.newArrayList();
84 classes.addAll( m_matchedDestClasses.values() ); 74 classes.addAll(m_matchedDestClasses.values());
85 classes.addAll( m_unmatchedDestClasses ); 75 classes.addAll(m_unmatchedDestClasses);
86 return classes; 76 return classes;
87 } 77 }
88 78
89 public BiMap<ClassIdentity,ClassIdentity> getUniqueMatches( ) 79 public BiMap<ClassIdentity,ClassIdentity> getUniqueMatches() {
90 {
91 BiMap<ClassIdentity,ClassIdentity> uniqueMatches = HashBiMap.create(); 80 BiMap<ClassIdentity,ClassIdentity> uniqueMatches = HashBiMap.create();
92 for( ClassIdentity sourceClass : m_sourceClasses.keySet() ) 81 for (ClassIdentity sourceClass : m_sourceClasses.keySet()) {
93 { 82 Collection<ClassIdentity> matchedSourceClasses = m_sourceClasses.get(sourceClass);
94 Collection<ClassIdentity> matchedSourceClasses = m_sourceClasses.get( sourceClass ); 83 Collection<ClassIdentity> matchedDestClasses = m_matchedDestClasses.get(sourceClass);
95 Collection<ClassIdentity> matchedDestClasses = m_matchedDestClasses.get( sourceClass ); 84 if (matchedSourceClasses.size() == 1 && matchedDestClasses.size() == 1) {
96 if( matchedSourceClasses.size() == 1 && matchedDestClasses.size() == 1 )
97 {
98 ClassIdentity matchedSourceClass = matchedSourceClasses.iterator().next(); 85 ClassIdentity matchedSourceClass = matchedSourceClasses.iterator().next();
99 ClassIdentity matchedDestClass = matchedDestClasses.iterator().next(); 86 ClassIdentity matchedDestClass = matchedDestClasses.iterator().next();
100 uniqueMatches.put( matchedSourceClass, matchedDestClass ); 87 uniqueMatches.put(matchedSourceClass, matchedDestClass);
101 } 88 }
102 } 89 }
103 return uniqueMatches; 90 return uniqueMatches;
104 } 91 }
105 92
106 public BiMap<List<ClassIdentity>,List<ClassIdentity>> getAmbiguousMatches( ) 93 public BiMap<List<ClassIdentity>,List<ClassIdentity>> getAmbiguousMatches() {
107 {
108 BiMap<List<ClassIdentity>,List<ClassIdentity>> ambiguousMatches = HashBiMap.create(); 94 BiMap<List<ClassIdentity>,List<ClassIdentity>> ambiguousMatches = HashBiMap.create();
109 for( ClassIdentity sourceClass : m_sourceClasses.keySet() ) 95 for (ClassIdentity sourceClass : m_sourceClasses.keySet()) {
110 { 96 Collection<ClassIdentity> matchedSourceClasses = m_sourceClasses.get(sourceClass);
111 Collection<ClassIdentity> matchedSourceClasses = m_sourceClasses.get( sourceClass ); 97 Collection<ClassIdentity> matchedDestClasses = m_matchedDestClasses.get(sourceClass);
112 Collection<ClassIdentity> matchedDestClasses = m_matchedDestClasses.get( sourceClass ); 98 if (matchedSourceClasses.size() > 1 && matchedDestClasses.size() > 1) {
113 if( matchedSourceClasses.size() > 1 && matchedDestClasses.size() > 1 )
114 {
115 ambiguousMatches.put( 99 ambiguousMatches.put(
116 new ArrayList<ClassIdentity>( matchedSourceClasses ), 100 new ArrayList<ClassIdentity>(matchedSourceClasses),
117 new ArrayList<ClassIdentity>( matchedDestClasses ) 101 new ArrayList<ClassIdentity>(matchedDestClasses)
118 ); 102 );
119 } 103 }
120 } 104 }
121 return ambiguousMatches; 105 return ambiguousMatches;
122 } 106 }
123 107
124 public int getNumAmbiguousSourceMatches( ) 108 public int getNumAmbiguousSourceMatches() {
125 {
126 int num = 0; 109 int num = 0;
127 for( Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry : getAmbiguousMatches().entrySet() ) 110 for (Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry : getAmbiguousMatches().entrySet()) {
128 {
129 num += entry.getKey().size(); 111 num += entry.getKey().size();
130 } 112 }
131 return num; 113 return num;
132 } 114 }
133 115
134 public int getNumAmbiguousDestMatches( ) 116 public int getNumAmbiguousDestMatches() {
135 {
136 int num = 0; 117 int num = 0;
137 for( Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry : getAmbiguousMatches().entrySet() ) 118 for (Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry : getAmbiguousMatches().entrySet()) {
138 {
139 num += entry.getValue().size(); 119 num += entry.getValue().size();
140 } 120 }
141 return num; 121 return num;
142 } 122 }
143 123
144 public List<ClassIdentity> getUnmatchedSourceClasses( ) 124 public List<ClassIdentity> getUnmatchedSourceClasses() {
145 {
146 List<ClassIdentity> classes = Lists.newArrayList(); 125 List<ClassIdentity> classes = Lists.newArrayList();
147 for( ClassIdentity sourceClass : getSourceClasses() ) 126 for (ClassIdentity sourceClass : getSourceClasses()) {
148 { 127 if (m_matchedDestClasses.get(sourceClass).isEmpty()) {
149 if( m_matchedDestClasses.get( sourceClass ).isEmpty() ) 128 classes.add(sourceClass);
150 {
151 classes.add( sourceClass );
152 } 129 }
153 } 130 }
154 return classes; 131 return classes;
155 } 132 }
156 133
157 public List<ClassIdentity> getUnmatchedDestClasses( ) 134 public List<ClassIdentity> getUnmatchedDestClasses() {
158 { 135 return new ArrayList<ClassIdentity>(m_unmatchedDestClasses);
159 return new ArrayList<ClassIdentity>( m_unmatchedDestClasses );
160 } 136 }
161 137
162 public Map<String,Map.Entry<ClassIdentity,List<ClassIdentity>>> getIndex( ) 138 public Map<String,Map.Entry<ClassIdentity,List<ClassIdentity>>> getIndex() {
163 {
164 Map<String,Map.Entry<ClassIdentity,List<ClassIdentity>>> conversion = Maps.newHashMap(); 139 Map<String,Map.Entry<ClassIdentity,List<ClassIdentity>>> conversion = Maps.newHashMap();
165 for( Map.Entry<ClassIdentity,ClassIdentity> entry : getUniqueMatches().entrySet() ) 140 for (Map.Entry<ClassIdentity,ClassIdentity> entry : getUniqueMatches().entrySet()) {
166 {
167 conversion.put( 141 conversion.put(
168 entry.getKey().getClassEntry().getName(), 142 entry.getKey().getClassEntry().getName(),
169 new AbstractMap.SimpleEntry<ClassIdentity,List<ClassIdentity>>( entry.getKey(), Arrays.asList( entry.getValue() ) ) 143 new AbstractMap.SimpleEntry<ClassIdentity,List<ClassIdentity>>(entry.getKey(), Arrays.asList(entry.getValue()))
170 ); 144 );
171 } 145 }
172 for( Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry : getAmbiguousMatches().entrySet() ) 146 for (Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry : getAmbiguousMatches().entrySet()) {
173 { 147 for (ClassIdentity sourceClass : entry.getKey()) {
174 for( ClassIdentity sourceClass : entry.getKey() )
175 {
176 conversion.put( 148 conversion.put(
177 sourceClass.getClassEntry().getName(), 149 sourceClass.getClassEntry().getName(),
178 new AbstractMap.SimpleEntry<ClassIdentity,List<ClassIdentity>>( sourceClass, entry.getValue() ) 150 new AbstractMap.SimpleEntry<ClassIdentity,List<ClassIdentity>>(sourceClass, entry.getValue())
179 ); 151 );
180 } 152 }
181 } 153 }
182 for( ClassIdentity sourceClass : getUnmatchedSourceClasses() ) 154 for (ClassIdentity sourceClass : getUnmatchedSourceClasses()) {
183 {
184 conversion.put( 155 conversion.put(
185 sourceClass.getClassEntry().getName(), 156 sourceClass.getClassEntry().getName(),
186 new AbstractMap.SimpleEntry<ClassIdentity,List<ClassIdentity>>( sourceClass, getUnmatchedDestClasses() ) 157 new AbstractMap.SimpleEntry<ClassIdentity,List<ClassIdentity>>(sourceClass, getUnmatchedDestClasses())
187 ); 158 );
188 } 159 }
189 return conversion; 160 return conversion;
190 } 161 }
191 162
192 @Override 163 @Override
193 public String toString( ) 164 public String toString() {
194 {
195 StringBuilder buf = new StringBuilder(); 165 StringBuilder buf = new StringBuilder();
196 buf.append( String.format( "%12s%8s%8s\n", "", "Source", "Dest" ) ); 166 buf.append(String.format("%12s%8s%8s\n", "", "Source", "Dest"));
197 buf.append( String.format( "%12s%8d%8d\n", "Classes", getSourceClasses().size(), getDestClasses().size() ) ); 167 buf.append(String.format("%12s%8d%8d\n", "Classes", getSourceClasses().size(), getDestClasses().size()));
198 buf.append( String.format( "%12s%8d%8d\n", "Unique", getUniqueMatches().size(), getUniqueMatches().size() ) ); 168 buf.append(String.format("%12s%8d%8d\n", "Unique", getUniqueMatches().size(), getUniqueMatches().size()));
199 buf.append( String.format( "%12s%8d%8d\n", "Ambiguous", getNumAmbiguousSourceMatches(), getNumAmbiguousDestMatches() ) ); 169 buf.append(String.format("%12s%8d%8d\n", "Ambiguous", getNumAmbiguousSourceMatches(), getNumAmbiguousDestMatches()));
200 buf.append( String.format( "%12s%8d%8d\n", "Unmatched", getUnmatchedSourceClasses().size(), getUnmatchedDestClasses().size() ) ); 170 buf.append(String.format("%12s%8d%8d\n", "Unmatched", getUnmatchedSourceClasses().size(), getUnmatchedDestClasses().size()));
201 return buf.toString(); 171 return buf.toString();
202 } 172 }
203} 173}
diff --git a/src/cuchaz/enigma/convert/ClassNamer.java b/src/cuchaz/enigma/convert/ClassNamer.java
index a01aec5..1b6e81c 100644
--- a/src/cuchaz/enigma/convert/ClassNamer.java
+++ b/src/cuchaz/enigma/convert/ClassNamer.java
@@ -15,60 +15,49 @@ import java.util.Map;
15import com.google.common.collect.BiMap; 15import com.google.common.collect.BiMap;
16import com.google.common.collect.Maps; 16import com.google.common.collect.Maps;
17 17
18public class ClassNamer 18public class ClassNamer {
19{ 19
20 public interface SidedClassNamer 20 public interface SidedClassNamer {
21 { 21 String getName(String name);
22 String getName( String name );
23 } 22 }
24 23
25 private Map<String,String> m_sourceNames; 24 private Map<String,String> m_sourceNames;
26 private Map<String,String> m_destNames; 25 private Map<String,String> m_destNames;
27 26
28 public ClassNamer( BiMap<ClassIdentity,ClassIdentity> mappings ) 27 public ClassNamer(BiMap<ClassIdentity,ClassIdentity> mappings) {
29 {
30 // convert the identity mappings to name maps 28 // convert the identity mappings to name maps
31 m_sourceNames = Maps.newHashMap(); 29 m_sourceNames = Maps.newHashMap();
32 m_destNames = Maps.newHashMap(); 30 m_destNames = Maps.newHashMap();
33 int i = 0; 31 int i = 0;
34 for( Map.Entry<ClassIdentity,ClassIdentity> entry : mappings.entrySet() ) 32 for (Map.Entry<ClassIdentity,ClassIdentity> entry : mappings.entrySet()) {
35 { 33 String name = String.format("M%04d", i++);
36 String name = String.format( "M%04d", i++ ); 34 m_sourceNames.put(entry.getKey().getClassEntry().getName(), name);
37 m_sourceNames.put( entry.getKey().getClassEntry().getName(), name ); 35 m_destNames.put(entry.getValue().getClassEntry().getName(), name);
38 m_destNames.put( entry.getValue().getClassEntry().getName(), name );
39 } 36 }
40 } 37 }
41 38
42 public String getSourceName( String name ) 39 public String getSourceName(String name) {
43 { 40 return m_sourceNames.get(name);
44 return m_sourceNames.get( name );
45 } 41 }
46 42
47 public String getDestName( String name ) 43 public String getDestName(String name) {
48 { 44 return m_destNames.get(name);
49 return m_destNames.get( name );
50 } 45 }
51 46
52 public SidedClassNamer getSourceNamer( ) 47 public SidedClassNamer getSourceNamer() {
53 { 48 return new SidedClassNamer() {
54 return new SidedClassNamer( )
55 {
56 @Override 49 @Override
57 public String getName( String name ) 50 public String getName(String name) {
58 { 51 return getSourceName(name);
59 return getSourceName( name );
60 } 52 }
61 }; 53 };
62 } 54 }
63 55
64 public SidedClassNamer getDestNamer( ) 56 public SidedClassNamer getDestNamer() {
65 { 57 return new SidedClassNamer() {
66 return new SidedClassNamer( )
67 {
68 @Override 58 @Override
69 public String getName( String name ) 59 public String getName(String name) {
70 { 60 return getDestName(name);
71 return getDestName( name );
72 } 61 }
73 }; 62 };
74 } 63 }