summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Thog2017-03-08 08:17:04 +0100
committerGravatar Thog2017-03-08 08:17:04 +0100
commit6e464ea251cab63c776ece0b2a356f1498ffa294 (patch)
tree5ed30c03f5ac4cd2d6877874f5ede576049954f7
parentDrop unix case style and implement hashCode when equals is overrided (diff)
downloadenigma-6e464ea251cab63c776ece0b2a356f1498ffa294.tar.gz
enigma-6e464ea251cab63c776ece0b2a356f1498ffa294.tar.xz
enigma-6e464ea251cab63c776ece0b2a356f1498ffa294.zip
Follow Fabric guidelines
-rw-r--r--src/main/java/cuchaz/enigma/CommandMain.java375
-rw-r--r--src/main/java/cuchaz/enigma/Constants.java11
-rw-r--r--src/main/java/cuchaz/enigma/ConvertMain.java669
-rw-r--r--src/main/java/cuchaz/enigma/Deobfuscator.java1163
-rw-r--r--src/main/java/cuchaz/enigma/ExceptionIgnorer.java31
-rw-r--r--src/main/java/cuchaz/enigma/Main.java74
-rw-r--r--src/main/java/cuchaz/enigma/TranslatingTypeLoader.java402
-rw-r--r--src/main/java/cuchaz/enigma/analysis/Access.java59
-rw-r--r--src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java131
-rw-r--r--src/main/java/cuchaz/enigma/analysis/BridgeMarker.java35
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java95
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java105
-rw-r--r--src/main/java/cuchaz/enigma/analysis/EntryReference.java207
-rw-r--r--src/main/java/cuchaz/enigma/analysis/EntryRenamer.java232
-rw-r--r--src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java107
-rw-r--r--src/main/java/cuchaz/enigma/analysis/JarClassIterator.java194
-rw-r--r--src/main/java/cuchaz/enigma/analysis/JarIndex.java1615
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java147
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java167
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java5
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndex.java305
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java358
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java129
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java722
-rw-r--r--src/main/java/cuchaz/enigma/analysis/Token.java80
-rw-r--r--src/main/java/cuchaz/enigma/analysis/TranslationIndex.java543
-rw-r--r--src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java833
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java60
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java60
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java1028
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java291
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java483
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/InfoType.java492
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java239
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java249
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java75
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java111
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java77
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java217
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java100
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java99
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java99
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java77
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java99
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java77
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java23
-rw-r--r--src/main/java/cuchaz/enigma/convert/ClassForest.java79
-rw-r--r--src/main/java/cuchaz/enigma/convert/ClassIdentifier.java61
-rw-r--r--src/main/java/cuchaz/enigma/convert/ClassIdentity.java821
-rw-r--r--src/main/java/cuchaz/enigma/convert/ClassMatch.java106
-rw-r--r--src/main/java/cuchaz/enigma/convert/ClassMatches.java273
-rw-r--r--src/main/java/cuchaz/enigma/convert/ClassMatching.java256
-rw-r--r--src/main/java/cuchaz/enigma/convert/ClassNamer.java71
-rw-r--r--src/main/java/cuchaz/enigma/convert/FieldMatches.java261
-rw-r--r--src/main/java/cuchaz/enigma/convert/MappingsConverter.java1363
-rw-r--r--src/main/java/cuchaz/enigma/convert/MatchesReader.java157
-rw-r--r--src/main/java/cuchaz/enigma/convert/MatchesWriter.java185
-rw-r--r--src/main/java/cuchaz/enigma/convert/MemberMatches.java315
-rw-r--r--src/main/java/cuchaz/enigma/gui/BrowserCaret.java17
-rw-r--r--src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java990
-rw-r--r--src/main/java/cuchaz/enigma/gui/ClassSelector.java968
-rw-r--r--src/main/java/cuchaz/enigma/gui/CodeReader.java362
-rw-r--r--src/main/java/cuchaz/enigma/gui/Gui.java1594
-rw-r--r--src/main/java/cuchaz/enigma/gui/GuiController.java640
-rw-r--r--src/main/java/cuchaz/enigma/gui/GuiTricks.java41
-rw-r--r--src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java815
-rw-r--r--src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java42
-rw-r--r--src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java35
-rw-r--r--src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java93
-rw-r--r--src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java118
-rw-r--r--src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java157
-rw-r--r--src/main/java/cuchaz/enigma/gui/elements/MenuBar.java400
-rw-r--r--src/main/java/cuchaz/enigma/gui/elements/PopupMenuBar.java139
-rw-r--r--src/main/java/cuchaz/enigma/gui/filechooser/FileChooserAny.java11
-rw-r--r--src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFile.java6
-rw-r--r--src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java10
-rw-r--r--src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java87
-rw-r--r--src/main/java/cuchaz/enigma/gui/highlight/DeobfuscatedHighlightPainter.java9
-rw-r--r--src/main/java/cuchaz/enigma/gui/highlight/ObfuscatedHighlightPainter.java9
-rw-r--r--src/main/java/cuchaz/enigma/gui/highlight/OtherHighlightPainter.java9
-rw-r--r--src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java22
-rw-r--r--src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java81
-rw-r--r--src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java81
-rw-r--r--src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java31
-rw-r--r--src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java108
-rw-r--r--src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java40
-rw-r--r--src/main/java/cuchaz/enigma/gui/panels/PanelObf.java57
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java185
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java55
-rw-r--r--src/main/java/cuchaz/enigma/mapping/BehaviorEntry.java3
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ClassEntry.java295
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ClassMapping.java1042
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java3
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java175
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Entry.java9
-rw-r--r--src/main/java/cuchaz/enigma/mapping/EntryFactory.java217
-rw-r--r--src/main/java/cuchaz/enigma/mapping/FieldEntry.java139
-rw-r--r--src/main/java/cuchaz/enigma/mapping/FieldMapping.java180
-rw-r--r--src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java186
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Mappings.java453
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsChecker.java155
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java340
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java70
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java633
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java112
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MemberMapping.java6
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodEntry.java145
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodMapping.java393
-rw-r--r--src/main/java/cuchaz/enigma/mapping/NameValidator.java89
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java91
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Signature.java154
-rw-r--r--src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java125
-rw-r--r--src/main/java/cuchaz/enigma/mapping/TranslationDirection.java27
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Translator.java655
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Type.java431
-rw-r--r--src/main/java/cuchaz/enigma/throwables/IllegalNameException.java41
-rw-r--r--src/main/java/cuchaz/enigma/throwables/MappingConflict.java6
-rw-r--r--src/main/java/cuchaz/enigma/throwables/MappingParseException.java25
-rw-r--r--src/main/java/cuchaz/enigma/utils/ReadableToken.java25
-rw-r--r--src/main/java/cuchaz/enigma/utils/Utils.java114
-rw-r--r--src/test/java/cuchaz/enigma/TestDeobfed.java28
-rw-r--r--src/test/java/cuchaz/enigma/TestDeobfuscator.java29
-rw-r--r--src/test/java/cuchaz/enigma/TestEntryFactory.java49
-rw-r--r--src/test/java/cuchaz/enigma/TestInnerClasses.java53
-rw-r--r--src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java63
-rw-r--r--src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java117
-rw-r--r--src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java67
-rw-r--r--src/test/java/cuchaz/enigma/TestSignature.java40
-rw-r--r--src/test/java/cuchaz/enigma/TestSourceIndex.java30
-rw-r--r--src/test/java/cuchaz/enigma/TestTokensConstructors.java45
-rw-r--r--src/test/java/cuchaz/enigma/TestTranslator.java19
-rw-r--r--src/test/java/cuchaz/enigma/TestType.java46
-rw-r--r--src/test/java/cuchaz/enigma/TokenChecker.java32
-rw-r--r--src/test/java/cuchaz/enigma/inputs/Keep.java3
-rw-r--r--src/test/java/cuchaz/enigma/inputs/constructors/BaseClass.java7
-rw-r--r--src/test/java/cuchaz/enigma/inputs/constructors/Caller.java17
-rw-r--r--src/test/java/cuchaz/enigma/inputs/constructors/DefaultConstructable.java3
-rw-r--r--src/test/java/cuchaz/enigma/inputs/constructors/SubClass.java11
-rw-r--r--src/test/java/cuchaz/enigma/inputs/constructors/SubSubClass.java5
-rw-r--r--src/test/java/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java11
-rw-r--r--src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java5
-rw-r--r--src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java13
-rw-r--r--src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java9
-rw-r--r--src/test/java/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java5
-rw-r--r--src/test/java/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java5
-rw-r--r--src/test/java/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java21
-rw-r--r--src/test/java/cuchaz/enigma/inputs/innerClasses/D_Simple.java5
-rw-r--r--src/test/java/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java9
-rw-r--r--src/test/java/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java16
-rw-r--r--src/test/java/cuchaz/enigma/inputs/loneClass/LoneClass.java9
-rw-r--r--src/test/java/cuchaz/enigma/inputs/translation/A_Basic.java13
-rw-r--r--src/test/java/cuchaz/enigma/inputs/translation/B_BaseClass.java9
-rw-r--r--src/test/java/cuchaz/enigma/inputs/translation/C_SubClass.java9
-rw-r--r--src/test/java/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java5
-rw-r--r--src/test/java/cuchaz/enigma/inputs/translation/E_Bridges.java4
-rw-r--r--src/test/java/cuchaz/enigma/inputs/translation/F_ObjectMethods.java7
-rw-r--r--src/test/java/cuchaz/enigma/inputs/translation/G_OuterClass.java18
-rw-r--r--src/test/java/cuchaz/enigma/inputs/translation/H_NamelessClass.java20
-rw-r--r--src/test/java/cuchaz/enigma/inputs/translation/I_Generics.java22
159 files changed, 15475 insertions, 15653 deletions
diff --git a/src/main/java/cuchaz/enigma/CommandMain.java b/src/main/java/cuchaz/enigma/CommandMain.java
index b0a4107c..f546eb18 100644
--- a/src/main/java/cuchaz/enigma/CommandMain.java
+++ b/src/main/java/cuchaz/enigma/CommandMain.java
@@ -8,201 +8,198 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma;
12 11
13import java.io.File; 12package cuchaz.enigma;
14import java.util.jar.JarFile;
15 13
16import cuchaz.enigma.Deobfuscator.ProgressListener; 14import cuchaz.enigma.Deobfuscator.ProgressListener;
17import cuchaz.enigma.mapping.Mappings; 15import cuchaz.enigma.mapping.Mappings;
18import cuchaz.enigma.mapping.MappingsEnigmaReader; 16import cuchaz.enigma.mapping.MappingsEnigmaReader;
19 17
18import java.io.File;
19import java.util.jar.JarFile;
20
20public class CommandMain { 21public class CommandMain {
21 22
22 public static class ConsoleProgressListener implements ProgressListener { 23 public static void main(String[] args) throws Exception {
23 24 try {
24 private static final int ReportTime = 5000; // 5s 25 // process the command
25 26 String command = getArg(args, 0, "command", true);
26 private int totalWork; 27 if (command.equalsIgnoreCase("deobfuscate")) {
27 private long startTime; 28 deobfuscate(args);
28 private long lastReportTime; 29 } else if (command.equalsIgnoreCase("decompile")) {
29 30 decompile(args);
30 @Override 31 } else if (command.equalsIgnoreCase("protectify")) {
31 public void init(int totalWork, String title) { 32 protectify(args);
32 this.totalWork = totalWork; 33 } else if (command.equalsIgnoreCase("publify")) {
33 this.startTime = System.currentTimeMillis(); 34 publify(args);
34 this.lastReportTime = this.startTime; 35 } else if (command.equalsIgnoreCase("convertmappings")) {
35 System.out.println(title); 36 convertMappings(args);
36 } 37 } else {
37 38 throw new IllegalArgumentException("Command not recognized: " + command);
38 @Override 39 }
39 public void onProgress(int numDone, String message) { 40 } catch (IllegalArgumentException ex) {
40 long now = System.currentTimeMillis(); 41 System.out.println(ex.getMessage());
41 boolean isLastUpdate = numDone == this.totalWork; 42 printHelp();
42 boolean shouldReport = isLastUpdate || now - this.lastReportTime > ReportTime; 43 }
43 44 }
44 if (shouldReport) { 45
45 int percent = numDone * 100 / this.totalWork; 46 private static void printHelp() {
46 System.out.println(String.format("\tProgress: %3d%%", percent)); 47 System.out.println(String.format("%s - %s", Constants.NAME, Constants.VERSION));
47 this.lastReportTime = now; 48 System.out.println("Usage:");
48 } 49 System.out.println("\tjava -cp enigma.jar cuchaz.enigma.CommandMain <command>");
49 if (isLastUpdate) { 50 System.out.println("\twhere <command> is one of:");
50 double elapsedSeconds = (now - this.startTime) / 1000.0; 51 System.out.println("\t\tdeobfuscate <in jar> <out jar> [<mappings file>]");
51 System.out.println(String.format("Finished in %.1f seconds", elapsedSeconds)); 52 System.out.println("\t\tdecompile <in jar> <out folder> [<mappings file>]");
52 } 53 System.out.println("\t\tprotectify <in jar> <out jar>");
53 } 54 System.out.println("\t\tpublify <in jar> <out jar>");
54 } 55 System.out.println("\t\tconvertmappings <enigma mappings> <converted mappings> <ENIGMA_FILE|ENIGMA_DIRECTORY|SRG_FILE>");
55 56 }
56 public static void main(String[] args) throws Exception { 57
57 try { 58 private static void decompile(String[] args) throws Exception {
58 // process the command 59 File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true));
59 String command = getArg(args, 0, "command", true); 60 File fileJarOut = getWritableFolder(getArg(args, 2, "out folder", true));
60 if (command.equalsIgnoreCase("deobfuscate")) { 61 File fileMappings = getReadableFile(getArg(args, 3, "mappings file", false));
61 deobfuscate(args); 62 Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn));
62 } else if (command.equalsIgnoreCase("decompile")) { 63 deobfuscator.writeSources(fileJarOut, new ConsoleProgressListener());
63 decompile(args); 64 }
64 } else if (command.equalsIgnoreCase("protectify")) { 65
65 protectify(args); 66 private static void deobfuscate(String[] args) throws Exception {
66 } else if (command.equalsIgnoreCase("publify")) { 67 File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true));
67 publify(args); 68 File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true));
68 } else if (command.equalsIgnoreCase("convertmappings")) { 69 File fileMappings = getReadableFile(getArg(args, 3, "mappings file", false));
69 convertMappings(args); 70 Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn));
70 } 71 deobfuscator.writeJar(fileJarOut, new ConsoleProgressListener());
71 else { 72 }
72 throw new IllegalArgumentException("Command not recognized: " + command); 73
73 } 74 private static void protectify(String[] args) throws Exception {
74 } catch (IllegalArgumentException ex) { 75 File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true));
75 System.out.println(ex.getMessage()); 76 File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true));
76 printHelp(); 77 Deobfuscator deobfuscator = getDeobfuscator(null, new JarFile(fileJarIn));
77 } 78 deobfuscator.protectifyJar(fileJarOut, new ConsoleProgressListener());
78 } 79 }
79 80
80 private static void printHelp() { 81 private static void publify(String[] args) throws Exception {
81 System.out.println(String.format("%s - %s", Constants.NAME, Constants.VERSION)); 82 File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true));
82 System.out.println("Usage:"); 83 File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true));
83 System.out.println("\tjava -cp enigma.jar cuchaz.enigma.CommandMain <command>"); 84 Deobfuscator deobfuscator = getDeobfuscator(null, new JarFile(fileJarIn));
84 System.out.println("\twhere <command> is one of:"); 85 deobfuscator.publifyJar(fileJarOut, new ConsoleProgressListener());
85 System.out.println("\t\tdeobfuscate <in jar> <out jar> [<mappings file>]"); 86 }
86 System.out.println("\t\tdecompile <in jar> <out folder> [<mappings file>]"); 87
87 System.out.println("\t\tprotectify <in jar> <out jar>"); 88 private static Deobfuscator getDeobfuscator(File fileMappings, JarFile jar) throws Exception {
88 System.out.println("\t\tpublify <in jar> <out jar>"); 89 System.out.println("Reading jar...");
89 System.out.println("\t\tconvertmappings <enigma mappings> <converted mappings> <ENIGMA_FILE|ENIGMA_DIRECTORY|SRG_FILE>"); 90 Deobfuscator deobfuscator = new Deobfuscator(jar);
90 } 91 if (fileMappings != null) {
91 92 System.out.println("Reading mappings...");
92 private static void decompile(String[] args) throws Exception { 93 Mappings mappings = new MappingsEnigmaReader().read(fileMappings);
93 File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); 94 deobfuscator.setMappings(mappings);
94 File fileJarOut = getWritableFolder(getArg(args, 2, "out folder", true)); 95 }
95 File fileMappings = getReadableFile(getArg(args, 3, "mappings file", false)); 96 return deobfuscator;
96 Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); 97 }
97 deobfuscator.writeSources(fileJarOut, new ConsoleProgressListener()); 98
98 } 99 private static void convertMappings(String[] args) throws Exception {
99 100 File fileMappings = getReadableFile(getArg(args, 1, "enigma mapping", true));
100 private static void deobfuscate(String[] args) throws Exception { 101 File result = getWritableFile(getArg(args, 2, "enigma mapping", true));
101 File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); 102 String name = getArg(args, 3, "format type", true);
102 File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); 103 Mappings.FormatType formatType;
103 File fileMappings = getReadableFile(getArg(args, 3, "mappings file", false)); 104 try {
104 Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); 105 formatType = Mappings.FormatType.valueOf(name.toUpperCase());
105 deobfuscator.writeJar(fileJarOut, new ConsoleProgressListener()); 106 } catch (IllegalArgumentException e) {
106 } 107 throw new IllegalArgumentException(name + "is not a valid mapping format!");
107 108 }
108 private static void protectify(String[] args) throws Exception { 109
109 File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); 110 System.out.println("Reading mappings...");
110 File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); 111 Mappings mappings = new MappingsEnigmaReader().read(fileMappings);
111 Deobfuscator deobfuscator = getDeobfuscator(null, new JarFile(fileJarIn)); 112 System.out.println("Saving new mappings...");
112 deobfuscator.protectifyJar(fileJarOut, new ConsoleProgressListener()); 113 switch (formatType) {
113 } 114 case SRG_FILE:
114 115 mappings.saveSRGMappings(result);
115 private static void publify(String[] args) throws Exception { 116 break;
116 File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); 117 default:
117 File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); 118 mappings.saveEnigmaMappings(result, Mappings.FormatType.ENIGMA_FILE != formatType);
118 Deobfuscator deobfuscator = getDeobfuscator(null, new JarFile(fileJarIn)); 119 break;
119 deobfuscator.publifyJar(fileJarOut, new ConsoleProgressListener()); 120 }
120 } 121 }
121 122
122 private static Deobfuscator getDeobfuscator(File fileMappings, JarFile jar) throws Exception { 123 private static String getArg(String[] args, int i, String name, boolean required) {
123 System.out.println("Reading jar..."); 124 if (i >= args.length) {
124 Deobfuscator deobfuscator = new Deobfuscator(jar); 125 if (required) {
125 if (fileMappings != null) { 126 throw new IllegalArgumentException(name + " is required");
126 System.out.println("Reading mappings..."); 127 } else {
127 Mappings mappings = new MappingsEnigmaReader().read(fileMappings); 128 return null;
128 deobfuscator.setMappings(mappings); 129 }
129 } 130 }
130 return deobfuscator; 131 return args[i];
131 } 132 }
132 133
133 private static void convertMappings(String[] args) throws Exception { 134 private static File getWritableFile(String path) {
134 File fileMappings = getReadableFile(getArg(args, 1, "enigma mapping", true)); 135 if (path == null) {
135 File result = getWritableFile(getArg(args, 2, "enigma mapping", true)); 136 return null;
136 String name = getArg(args, 3, "format type", true); 137 }
137 Mappings.FormatType formatType; 138 File file = new File(path).getAbsoluteFile();
138 try 139 File dir = file.getParentFile();
139 { 140 if (dir == null) {
140 formatType = Mappings.FormatType.valueOf(name.toUpperCase()); 141 throw new IllegalArgumentException("Cannot write file: " + path);
141 } catch (IllegalArgumentException e) 142 }
142 { 143 // quick fix to avoid stupid stuff in Gradle code
143 throw new IllegalArgumentException(name + "is not a valid mapping format!"); 144 if (!dir.isDirectory()) {
144 } 145 dir.mkdirs();
145 146 }
146 System.out.println("Reading mappings..."); 147 return file;
147 Mappings mappings = new MappingsEnigmaReader().read(fileMappings); 148 }
148 System.out.println("Saving new mappings..."); 149
149 switch (formatType) 150 private static File getWritableFolder(String path) {
150 { 151 if (path == null) {
151 case SRG_FILE: 152 return null;
152 mappings.saveSRGMappings(result); 153 }
153 break; 154 File dir = new File(path).getAbsoluteFile();
154 default: 155 if (!dir.exists()) {
155 mappings.saveEnigmaMappings(result, Mappings.FormatType.ENIGMA_FILE != formatType); 156 throw new IllegalArgumentException("Cannot write to folder: " + dir);
156 break; 157 }
157 } 158 return dir;
158 } 159 }
159 160
160 private static String getArg(String[] args, int i, String name, boolean required) { 161 private static File getReadableFile(String path) {
161 if (i >= args.length) { 162 if (path == null) {
162 if (required) { 163 return null;
163 throw new IllegalArgumentException(name + " is required"); 164 }
164 } else { 165 File file = new File(path).getAbsoluteFile();
165 return null; 166 if (!file.exists()) {
166 } 167 throw new IllegalArgumentException("Cannot find file: " + file.getAbsolutePath());
167 } 168 }
168 return args[i]; 169 return file;
169 } 170 }
170 171
171 private static File getWritableFile(String path) { 172 public static class ConsoleProgressListener implements ProgressListener {
172 if (path == null) { 173
173 return null; 174 private static final int ReportTime = 5000; // 5s
174 } 175
175 File file = new File(path).getAbsoluteFile(); 176 private int totalWork;
176 File dir = file.getParentFile(); 177 private long startTime;
177 if (dir == null) { 178 private long lastReportTime;
178 throw new IllegalArgumentException("Cannot write file: " + path); 179
179 } 180 @Override
180 // quick fix to avoid stupid stuff in Gradle code 181 public void init(int totalWork, String title) {
181 if (!dir.isDirectory()) { 182 this.totalWork = totalWork;
182 dir.mkdirs(); 183 this.startTime = System.currentTimeMillis();
183 } 184 this.lastReportTime = this.startTime;
184 return file; 185 System.out.println(title);
185 } 186 }
186 187
187 private static File getWritableFolder(String path) { 188 @Override
188 if (path == null) { 189 public void onProgress(int numDone, String message) {
189 return null; 190 long now = System.currentTimeMillis();
190 } 191 boolean isLastUpdate = numDone == this.totalWork;
191 File dir = new File(path).getAbsoluteFile(); 192 boolean shouldReport = isLastUpdate || now - this.lastReportTime > ReportTime;
192 if (!dir.exists()) { 193
193 throw new IllegalArgumentException("Cannot write to folder: " + dir); 194 if (shouldReport) {
194 } 195 int percent = numDone * 100 / this.totalWork;
195 return dir; 196 System.out.println(String.format("\tProgress: %3d%%", percent));
196 } 197 this.lastReportTime = now;
197 198 }
198 private static File getReadableFile(String path) { 199 if (isLastUpdate) {
199 if (path == null) { 200 double elapsedSeconds = (now - this.startTime) / 1000.0;
200 return null; 201 System.out.println(String.format("Finished in %.1f seconds", elapsedSeconds));
201 } 202 }
202 File file = new File(path).getAbsoluteFile(); 203 }
203 if (!file.exists()) { 204 }
204 throw new IllegalArgumentException("Cannot find file: " + file.getAbsolutePath());
205 }
206 return file;
207 }
208} 205}
diff --git a/src/main/java/cuchaz/enigma/Constants.java b/src/main/java/cuchaz/enigma/Constants.java
index 04730480..b08438a3 100644
--- a/src/main/java/cuchaz/enigma/Constants.java
+++ b/src/main/java/cuchaz/enigma/Constants.java
@@ -8,12 +8,13 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma; 12package cuchaz.enigma;
12 13
13public class Constants { 14public class Constants {
14 public static final String NAME = "Enigma"; 15 public static final String NAME = "Enigma";
15 public static final String VERSION = "0.11.0 (Fabric Fork)"; 16 public static final String VERSION = "0.11.0 (Fabric Fork)";
16 public static final String URL = "http://www.cuchazinteractive.com/enigma"; 17 public static final String URL = "http://www.cuchazinteractive.com/enigma";
17 public static final int MiB = 1024 * 1024; // 1 mebibyte 18 public static final int MiB = 1024 * 1024; // 1 mebibyte
18 public static final int KiB = 1024; // 1 kebibyte 19 public static final int KiB = 1024; // 1 kebibyte
19} 20}
diff --git a/src/main/java/cuchaz/enigma/ConvertMain.java b/src/main/java/cuchaz/enigma/ConvertMain.java
index 48e7f27c..3d58f57c 100644
--- a/src/main/java/cuchaz/enigma/ConvertMain.java
+++ b/src/main/java/cuchaz/enigma/ConvertMain.java
@@ -8,11 +8,8 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma;
12 11
13import java.io.File; 12package cuchaz.enigma;
14import java.io.IOException;
15import java.util.jar.JarFile;
16 13
17import cuchaz.enigma.convert.*; 14import cuchaz.enigma.convert.*;
18import cuchaz.enigma.gui.ClassMatchingGui; 15import cuchaz.enigma.gui.ClassMatchingGui;
@@ -21,338 +18,340 @@ import cuchaz.enigma.mapping.*;
21import cuchaz.enigma.throwables.MappingConflict; 18import cuchaz.enigma.throwables.MappingConflict;
22import cuchaz.enigma.throwables.MappingParseException; 19import cuchaz.enigma.throwables.MappingParseException;
23 20
21import java.io.File;
22import java.io.IOException;
23import java.util.jar.JarFile;
24 24
25public class ConvertMain { 25public class ConvertMain {
26 26
27 public static void main(String[] args) 27 public static void main(String[] args)
28 throws IOException, MappingParseException { 28 throws IOException, MappingParseException {
29 try { 29 try {
30 //Get all are args 30 //Get all are args
31 String JarOld = getArg(args, 1, "Path to Old Jar", true); 31 String JarOld = getArg(args, 1, "Path to Old Jar", true);
32 String JarNew = getArg(args, 2, "Path to New Jar", true); 32 String JarNew = getArg(args, 2, "Path to New Jar", true);
33 String OldMappings = getArg(args, 3, "Path to old .mappings file", true); 33 String OldMappings = getArg(args, 3, "Path to old .mappings file", true);
34 String NewMappings = getArg(args, 4, "Path to new .mappings file", true); 34 String NewMappings = getArg(args, 4, "Path to new .mappings file", true);
35 String ClassMatches = getArg(args, 5, "Path to Class .matches file", true); 35 String ClassMatches = getArg(args, 5, "Path to Class .matches file", true);
36 String FieldMatches = getArg(args, 6, "Path to Field .matches file", true); 36 String FieldMatches = getArg(args, 6, "Path to Field .matches file", true);
37 String MethodMatches = getArg(args, 7, "Path to Method .matches file", true); 37 String MethodMatches = getArg(args, 7, "Path to Method .matches file", true);
38 //OldJar 38 //OldJar
39 JarFile sourceJar = new JarFile(new File(JarOld)); 39 JarFile sourceJar = new JarFile(new File(JarOld));
40 //NewJar 40 //NewJar
41 JarFile destJar = new JarFile(new File(JarNew)); 41 JarFile destJar = new JarFile(new File(JarNew));
42 //Get the mapping files 42 //Get the mapping files
43 File inMappingsFile = new File(OldMappings); 43 File inMappingsFile = new File(OldMappings);
44 File outMappingsFile = new File(NewMappings); 44 File outMappingsFile = new File(NewMappings);
45 Mappings mappings = new MappingsEnigmaReader().read(inMappingsFile); 45 Mappings mappings = new MappingsEnigmaReader().read(inMappingsFile);
46 //Make the Match Files.. 46 //Make the Match Files..
47 File classMatchesFile = new File(ClassMatches); 47 File classMatchesFile = new File(ClassMatches);
48 File fieldMatchesFile = new File(FieldMatches); 48 File fieldMatchesFile = new File(FieldMatches);
49 File methodMatchesFile = new File(MethodMatches); 49 File methodMatchesFile = new File(MethodMatches);
50 50
51 String command = getArg(args, 0, "command", true); 51 String command = getArg(args, 0, "command", true);
52 52
53 if (command.equalsIgnoreCase("computeClassMatches")) { 53 if (command.equalsIgnoreCase("computeClassMatches")) {
54 computeClassMatches(classMatchesFile, sourceJar, destJar, mappings); 54 computeClassMatches(classMatchesFile, sourceJar, destJar, mappings);
55 convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile); 55 convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile);
56 } else if (command.equalsIgnoreCase("editClassMatches")) { 56 } else if (command.equalsIgnoreCase("editClassMatches")) {
57 editClasssMatches(classMatchesFile, sourceJar, destJar, mappings); 57 editClasssMatches(classMatchesFile, sourceJar, destJar, mappings);
58 convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile); 58 convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile);
59 } else if (command.equalsIgnoreCase("computeFieldMatches")) { 59 } else if (command.equalsIgnoreCase("computeFieldMatches")) {
60 computeFieldMatches(fieldMatchesFile, destJar, outMappingsFile, classMatchesFile); 60 computeFieldMatches(fieldMatchesFile, destJar, outMappingsFile, classMatchesFile);
61 convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile); 61 convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile);
62 } else if (command.equalsIgnoreCase("editFieldMatches")) { 62 } else if (command.equalsIgnoreCase("editFieldMatches")) {
63 editFieldMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, fieldMatchesFile); 63 editFieldMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, fieldMatchesFile);
64 convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile); 64 convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile);
65 } else if (command.equalsIgnoreCase("computeMethodMatches")) { 65 } else if (command.equalsIgnoreCase("computeMethodMatches")) {
66 computeMethodMatches(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile); 66 computeMethodMatches(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile);
67 convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile); 67 convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile);
68 } else if (command.equalsIgnoreCase("editMethodMatches")) { 68 } else if (command.equalsIgnoreCase("editMethodMatches")) {
69 editMethodMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, methodMatchesFile); 69 editMethodMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, methodMatchesFile);
70 convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile); 70 convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile);
71 } else if (command.equalsIgnoreCase("convertMappings")) { 71 } else if (command.equalsIgnoreCase("convertMappings")) {
72 convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile); 72 convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile);
73 } 73 }
74 } catch (MappingConflict ex) { 74 } catch (MappingConflict ex) {
75 System.out.println(ex.getMessage()); 75 System.out.println(ex.getMessage());
76 ex.printStackTrace(); 76 ex.printStackTrace();
77 } catch (IllegalArgumentException ex) { 77 } catch (IllegalArgumentException ex) {
78 System.out.println(ex.getMessage()); 78 System.out.println(ex.getMessage());
79 printHelp(); 79 printHelp();
80 } 80 }
81 } 81 }
82 82
83 private static void printHelp() { 83 private static void printHelp() {
84 System.out.println(String.format("%s - %s", Constants.NAME, Constants.VERSION)); 84 System.out.println(String.format("%s - %s", Constants.NAME, Constants.VERSION));
85 System.out.println("Usage:"); 85 System.out.println("Usage:");
86 System.out.println("\tjava -cp enigma.jar cuchaz.enigma.ConvertMain <command> <old-jar> <new-jar> <old-mappings> <new-mappings> <class-matches> <field-matches> <method-matches>"); 86 System.out.println("\tjava -cp enigma.jar cuchaz.enigma.ConvertMain <command> <old-jar> <new-jar> <old-mappings> <new-mappings> <class-matches> <field-matches> <method-matches>");
87 System.out.println("\tWhere <command> is one of:"); 87 System.out.println("\tWhere <command> is one of:");
88 System.out.println("\t\tcomputeClassMatches"); 88 System.out.println("\t\tcomputeClassMatches");
89 System.out.println("\t\teditClassMatches"); 89 System.out.println("\t\teditClassMatches");
90 System.out.println("\t\tcomputeFieldMatches"); 90 System.out.println("\t\tcomputeFieldMatches");
91 System.out.println("\t\teditFieldMatches"); 91 System.out.println("\t\teditFieldMatches");
92 System.out.println("\t\teditMethodMatches"); 92 System.out.println("\t\teditMethodMatches");
93 System.out.println("\t\tconvertMappings"); 93 System.out.println("\t\tconvertMappings");
94 System.out.println("\tWhere <old-jar> is the already mapped jar."); 94 System.out.println("\tWhere <old-jar> is the already mapped jar.");
95 System.out.println("\tWhere <new-jar> is the unmapped jar."); 95 System.out.println("\tWhere <new-jar> is the unmapped jar.");
96 System.out.println("\tWhere <old-mappings> is the path to the mappings for the old jar."); 96 System.out.println("\tWhere <old-mappings> is the path to the mappings for the old jar.");
97 System.out.println("\tWhere <new-mappings> is the new mappings. (Where you want to save them and there name)"); 97 System.out.println("\tWhere <new-mappings> is the new mappings. (Where you want to save them and there name)");
98 System.out.println("\tWhere <class-matches> is the class matches file."); 98 System.out.println("\tWhere <class-matches> is the class matches file.");
99 System.out.println("\tWhere <field-matches> is the field matches file."); 99 System.out.println("\tWhere <field-matches> is the field matches file.");
100 System.out.println("\tWhere <method-matches> is the method matches file."); 100 System.out.println("\tWhere <method-matches> is the method matches file.");
101 } 101 }
102 102
103 //Copy of getArg from CommandMain.... Should make a utils class. 103 //Copy of getArg from CommandMain.... Should make a utils class.
104 private static String getArg(String[] args, int i, String name, boolean required) { 104 private static String getArg(String[] args, int i, String name, boolean required) {
105 if (i >= args.length) { 105 if (i >= args.length) {
106 if (required) { 106 if (required) {
107 throw new IllegalArgumentException(name + " is required"); 107 throw new IllegalArgumentException(name + " is required");
108 } else { 108 } else {
109 return null; 109 return null;
110 } 110 }
111 } 111 }
112 return args[i]; 112 return args[i];
113 } 113 }
114 114
115 private static void computeClassMatches(File classMatchesFile, JarFile sourceJar, JarFile destJar, Mappings mappings) 115 private static void computeClassMatches(File classMatchesFile, JarFile sourceJar, JarFile destJar, Mappings mappings)
116 throws IOException { 116 throws IOException {
117 ClassMatches classMatches = MappingsConverter.computeClassMatches(sourceJar, destJar, mappings); 117 ClassMatches classMatches = MappingsConverter.computeClassMatches(sourceJar, destJar, mappings);
118 MatchesWriter.writeClasses(classMatches, classMatchesFile); 118 MatchesWriter.writeClasses(classMatches, classMatchesFile);
119 System.out.println("Wrote:\n\t" + classMatchesFile.getAbsolutePath()); 119 System.out.println("Wrote:\n\t" + classMatchesFile.getAbsolutePath());
120 } 120 }
121 121
122 private static void editClasssMatches(final File classMatchesFile, JarFile sourceJar, JarFile destJar, Mappings mappings) 122 private static void editClasssMatches(final File classMatchesFile, JarFile sourceJar, JarFile destJar, Mappings mappings)
123 throws IOException { 123 throws IOException {
124 System.out.println("Reading class matches..."); 124 System.out.println("Reading class matches...");
125 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); 125 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile);
126 Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); 126 Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar);
127 deobfuscators.source.setMappings(mappings); 127 deobfuscators.source.setMappings(mappings);
128 System.out.println("Starting GUI..."); 128 System.out.println("Starting GUI...");
129 new ClassMatchingGui(classMatches, deobfuscators.source, deobfuscators.dest).setSaveListener(matches -> 129 new ClassMatchingGui(classMatches, deobfuscators.source, deobfuscators.dest).setSaveListener(matches ->
130 { 130 {
131 try { 131 try {
132 MatchesWriter.writeClasses(matches, classMatchesFile); 132 MatchesWriter.writeClasses(matches, classMatchesFile);
133 } catch (IOException ex) { 133 } catch (IOException ex) {
134 throw new Error(ex); 134 throw new Error(ex);
135 } 135 }
136 }); 136 });
137 } 137 }
138 138
139 @SuppressWarnings("unused") 139 @SuppressWarnings("unused")
140 private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile) 140 private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile)
141 throws IOException, MappingConflict { 141 throws IOException, MappingConflict {
142 System.out.println("Reading class matches..."); 142 System.out.println("Reading class matches...");
143 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); 143 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile);
144 Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); 144 Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar);
145 deobfuscators.source.setMappings(mappings); 145 deobfuscators.source.setMappings(mappings);
146 146
147 Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest); 147 Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest);
148 new MappingsEnigmaWriter().write(outMappingsFile, newMappings, true); 148 new MappingsEnigmaWriter().write(outMappingsFile, newMappings, true);
149 System.out.println("Write converted mappings to: " + outMappingsFile.getAbsolutePath()); 149 System.out.println("Write converted mappings to: " + outMappingsFile.getAbsolutePath());
150 } 150 }
151 151
152 private static void computeFieldMatches(File memberMatchesFile, JarFile destJar, File destMappingsFile, File classMatchesFile) 152 private static void computeFieldMatches(File memberMatchesFile, JarFile destJar, File destMappingsFile, File classMatchesFile)
153 throws IOException, MappingParseException { 153 throws IOException, MappingParseException {
154 154
155 System.out.println("Reading class matches..."); 155 System.out.println("Reading class matches...");
156 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); 156 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile);
157 System.out.println("Reading mappings..."); 157 System.out.println("Reading mappings...");
158 Mappings destMappings = new MappingsEnigmaReader().read(destMappingsFile); 158 Mappings destMappings = new MappingsEnigmaReader().read(destMappingsFile);
159 System.out.println("Indexing dest jar..."); 159 System.out.println("Indexing dest jar...");
160 Deobfuscator destDeobfuscator = new Deobfuscator(destJar); 160 Deobfuscator destDeobfuscator = new Deobfuscator(destJar);
161 161
162 System.out.println("Writing matches..."); 162 System.out.println("Writing matches...");
163 163
164 // get the matched and unmatched mappings 164 // get the matched and unmatched mappings
165 MemberMatches<FieldEntry> fieldMatches = MappingsConverter.computeMemberMatches( 165 MemberMatches<FieldEntry> fieldMatches = MappingsConverter.computeMemberMatches(
166 destDeobfuscator, 166 destDeobfuscator,
167 destMappings, 167 destMappings,
168 classMatches, 168 classMatches,
169 MappingsConverter.getFieldDoer() 169 MappingsConverter.getFieldDoer()
170 ); 170 );
171 171
172 MatchesWriter.writeMembers(fieldMatches, memberMatchesFile); 172 MatchesWriter.writeMembers(fieldMatches, memberMatchesFile);
173 System.out.println("Wrote:\n\t" + memberMatchesFile.getAbsolutePath()); 173 System.out.println("Wrote:\n\t" + memberMatchesFile.getAbsolutePath());
174 } 174 }
175 175
176 private static void editFieldMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchesFile, final File fieldMatchesFile) 176 private static void editFieldMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchesFile, final File fieldMatchesFile)
177 throws IOException, MappingParseException { 177 throws IOException, MappingParseException {
178 178
179 System.out.println("Reading matches..."); 179 System.out.println("Reading matches...");
180 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); 180 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile);
181 MemberMatches<FieldEntry> fieldMatches = MatchesReader.readMembers(fieldMatchesFile); 181 MemberMatches<FieldEntry> fieldMatches = MatchesReader.readMembers(fieldMatchesFile);
182 182
183 // prep deobfuscators 183 // prep deobfuscators
184 Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); 184 Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar);
185 deobfuscators.source.setMappings(sourceMappings); 185 deobfuscators.source.setMappings(sourceMappings);
186 Mappings destMappings = new MappingsEnigmaReader().read(destMappingsFile); 186 Mappings destMappings = new MappingsEnigmaReader().read(destMappingsFile);
187 MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex()); 187 MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex());
188 checker.dropBrokenMappings(destMappings); 188 checker.dropBrokenMappings(destMappings);
189 deobfuscators.dest.setMappings(destMappings); 189 deobfuscators.dest.setMappings(destMappings);
190 190
191 new MemberMatchingGui<>(classMatches, fieldMatches, deobfuscators.source, deobfuscators.dest).setSaveListener( 191 new MemberMatchingGui<>(classMatches, fieldMatches, deobfuscators.source, deobfuscators.dest).setSaveListener(
192 matches -> 192 matches ->
193 { 193 {
194 try { 194 try {
195 MatchesWriter.writeMembers(matches, fieldMatchesFile); 195 MatchesWriter.writeMembers(matches, fieldMatchesFile);
196 } catch (IOException ex) { 196 } catch (IOException ex) {
197 throw new Error(ex); 197 throw new Error(ex);
198 } 198 }
199 }); 199 });
200 } 200 }
201 201
202 @SuppressWarnings("unused") 202 @SuppressWarnings("unused")
203 private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile, File fieldMatchesFile) 203 private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile, File fieldMatchesFile)
204 throws IOException, MappingConflict { 204 throws IOException, MappingConflict {
205 205
206 System.out.println("Reading matches..."); 206 System.out.println("Reading matches...");
207 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); 207 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile);
208 MemberMatches<FieldEntry> fieldMatches = MatchesReader.readMembers(fieldMatchesFile); 208 MemberMatches<FieldEntry> fieldMatches = MatchesReader.readMembers(fieldMatchesFile);
209 209
210 Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); 210 Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar);
211 deobfuscators.source.setMappings(mappings); 211 deobfuscators.source.setMappings(mappings);
212 212
213 // apply matches 213 // apply matches
214 Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest); 214 Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest);
215 MappingsConverter.applyMemberMatches(newMappings, classMatches, fieldMatches, MappingsConverter.getFieldDoer()); 215 MappingsConverter.applyMemberMatches(newMappings, classMatches, fieldMatches, MappingsConverter.getFieldDoer());
216 216
217 // write out the converted mappings 217 // write out the converted mappings
218 218
219 new MappingsEnigmaWriter().write(outMappingsFile, newMappings, true); 219 new MappingsEnigmaWriter().write(outMappingsFile, newMappings, true);
220 System.out.println("Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath()); 220 System.out.println("Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath());
221 } 221 }
222 222
223 223 private static void computeMethodMatches(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings sourceMappings, File classMatchesFile, File fieldMatchesFile, File methodMatchesFile)
224 private static void computeMethodMatches(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings sourceMappings, File classMatchesFile, File fieldMatchesFile, File methodMatchesFile) 224 throws IOException, MappingParseException {
225 throws IOException, MappingParseException { 225
226 226 System.out.println("Reading class matches...");
227 System.out.println("Reading class matches..."); 227 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile);
228 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); 228 System.out.println("Reading dest mappings...");
229 System.out.println("Reading dest mappings..."); 229 Mappings destMappings = new MappingsEnigmaReader().read(outMappingsFile);
230 Mappings destMappings = new MappingsEnigmaReader().read(outMappingsFile); 230 System.out.println("Indexing dest jar...");
231 System.out.println("Indexing dest jar..."); 231 Deobfuscator destDeobfuscator = new Deobfuscator(destJar);
232 Deobfuscator destDeobfuscator = new Deobfuscator(destJar); 232 System.out.println("Indexing source jar...");
233 System.out.println("Indexing source jar..."); 233 Deobfuscator sourceDeobfuscator = new Deobfuscator(sourceJar);
234 Deobfuscator sourceDeobfuscator = new Deobfuscator(sourceJar); 234
235 235 System.out.println("Writing method matches...");
236 System.out.println("Writing method matches..."); 236
237 237 // get the matched and unmatched mappings
238 // get the matched and unmatched mappings 238 MemberMatches<BehaviorEntry> methodMatches = MappingsConverter.computeMethodsMatches(
239 MemberMatches<BehaviorEntry> methodMatches = MappingsConverter.computeMethodsMatches( 239 destDeobfuscator,
240 destDeobfuscator, 240 destMappings,
241 destMappings, 241 sourceDeobfuscator,
242 sourceDeobfuscator, 242 sourceMappings,
243 sourceMappings, 243 classMatches,
244 classMatches, 244 MappingsConverter.getMethodDoer()
245 MappingsConverter.getMethodDoer() 245 );
246 ); 246
247 247 MatchesWriter.writeMembers(methodMatches, methodMatchesFile);
248 MatchesWriter.writeMembers(methodMatches, methodMatchesFile); 248 System.out.println("Wrote:\n\t" + methodMatchesFile.getAbsolutePath());
249 System.out.println("Wrote:\n\t" + methodMatchesFile.getAbsolutePath()); 249 }
250 } 250
251 251 private static void editMethodMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchesFile, final File methodMatchesFile)
252 private static void editMethodMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchesFile, final File methodMatchesFile) 252 throws IOException, MappingParseException {
253 throws IOException, MappingParseException { 253
254 254 System.out.println("Reading matches...");
255 System.out.println("Reading matches..."); 255 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile);
256 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); 256 MemberMatches<BehaviorEntry> methodMatches = MatchesReader.readMembers(methodMatchesFile);
257 MemberMatches<BehaviorEntry> methodMatches = MatchesReader.readMembers(methodMatchesFile); 257
258 258 // prep deobfuscators
259 // prep deobfuscators 259 Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar);
260 Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); 260 deobfuscators.source.setMappings(sourceMappings);
261 deobfuscators.source.setMappings(sourceMappings); 261 Mappings destMappings = new MappingsEnigmaReader().read(destMappingsFile);
262 Mappings destMappings = new MappingsEnigmaReader().read(destMappingsFile); 262 MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex());
263 MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex()); 263 checker.dropBrokenMappings(destMappings);
264 checker.dropBrokenMappings(destMappings); 264 deobfuscators.dest.setMappings(destMappings);
265 deobfuscators.dest.setMappings(destMappings); 265
266 266 new MemberMatchingGui<>(classMatches, methodMatches, deobfuscators.source, deobfuscators.dest).setSaveListener(
267 new MemberMatchingGui<>(classMatches, methodMatches, deobfuscators.source, deobfuscators.dest).setSaveListener( 267 matches ->
268 matches -> 268 {
269 { 269 try {
270 try { 270 MatchesWriter.writeMembers(matches, methodMatchesFile);
271 MatchesWriter.writeMembers(matches, methodMatchesFile); 271 } catch (IOException ex) {
272 } catch (IOException ex) { 272 throw new Error(ex);
273 throw new Error(ex); 273 }
274 } 274 });
275 }); 275 }
276 } 276
277 277 private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile, File fieldMatchesFile, File methodMatchesFile)
278 private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile, File fieldMatchesFile, File methodMatchesFile) 278 throws IOException, MappingConflict {
279 throws IOException, MappingConflict { 279
280 280 System.out.println("Reading matches...");
281 System.out.println("Reading matches..."); 281 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile);
282 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); 282 MemberMatches<FieldEntry> fieldMatches = MatchesReader.readMembers(fieldMatchesFile);
283 MemberMatches<FieldEntry> fieldMatches = MatchesReader.readMembers(fieldMatchesFile); 283 MemberMatches<BehaviorEntry> methodMatches = MatchesReader.readMembers(methodMatchesFile);
284 MemberMatches<BehaviorEntry> methodMatches = MatchesReader.readMembers(methodMatchesFile); 284
285 285 Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar);
286 Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); 286 deobfuscators.source.setMappings(mappings);
287 deobfuscators.source.setMappings(mappings); 287
288 288 // apply matches
289 // apply matches 289 Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest);
290 Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest); 290 MappingsConverter.applyMemberMatches(newMappings, classMatches, fieldMatches, MappingsConverter.getFieldDoer());
291 MappingsConverter.applyMemberMatches(newMappings, classMatches, fieldMatches, MappingsConverter.getFieldDoer()); 291 MappingsConverter.applyMemberMatches(newMappings, classMatches, methodMatches, MappingsConverter.getMethodDoer());
292 MappingsConverter.applyMemberMatches(newMappings, classMatches, methodMatches, MappingsConverter.getMethodDoer()); 292
293 293 // check the final mappings
294 // check the final mappings 294 MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex());
295 MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex()); 295 checker.dropBrokenMappings(newMappings);
296 checker.dropBrokenMappings(newMappings); 296
297 297 for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedClassMappings().entrySet()) {
298 for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedClassMappings().entrySet()) { 298 System.out.println("WARNING: Broken class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")");
299 System.out.println("WARNING: Broken class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")"); 299 }
300 } 300 for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedInnerClassMappings().entrySet()) {
301 for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedInnerClassMappings().entrySet()) { 301 System.out.println("WARNING: Broken inner class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")");
302 System.out.println("WARNING: Broken inner class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")"); 302 }
303 } 303 for (java.util.Map.Entry<FieldEntry, FieldMapping> mapping : checker.getDroppedFieldMappings().entrySet()) {
304 for (java.util.Map.Entry<FieldEntry, FieldMapping> mapping : checker.getDroppedFieldMappings().entrySet()) { 304 System.out.println("WARNING: Broken field entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")");
305 System.out.println("WARNING: Broken field entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")"); 305 }
306 } 306 for (java.util.Map.Entry<BehaviorEntry, MethodMapping> mapping : checker.getDroppedMethodMappings().entrySet()) {
307 for (java.util.Map.Entry<BehaviorEntry, MethodMapping> mapping : checker.getDroppedMethodMappings().entrySet()) { 307 System.out.println("WARNING: Broken behavior entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")");
308 System.out.println("WARNING: Broken behavior entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")"); 308 }
309 } 309
310 310 // write out the converted mappings
311 // write out the converted mappings 311 new MappingsEnigmaWriter().write(outMappingsFile, newMappings, true);
312 new MappingsEnigmaWriter().write(outMappingsFile, newMappings, true); 312 System.out.println("Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath());
313 System.out.println("Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath()); 313 }
314 } 314
315 315 private static class Deobfuscators {
316 private static class Deobfuscators { 316
317 317 public Deobfuscator source;
318 public Deobfuscator source; 318 public Deobfuscator dest;
319 public Deobfuscator dest; 319
320 320 public Deobfuscators(JarFile sourceJar, JarFile destJar) {
321 public Deobfuscators(JarFile sourceJar, JarFile destJar) { 321 System.out.println("Indexing source jar...");
322 System.out.println("Indexing source jar..."); 322 IndexerThread sourceIndexer = new IndexerThread(sourceJar);
323 IndexerThread sourceIndexer = new IndexerThread(sourceJar); 323 sourceIndexer.start();
324 sourceIndexer.start(); 324 System.out.println("Indexing dest jar...");
325 System.out.println("Indexing dest jar..."); 325 IndexerThread destIndexer = new IndexerThread(destJar);
326 IndexerThread destIndexer = new IndexerThread(destJar); 326 destIndexer.start();
327 destIndexer.start(); 327 sourceIndexer.joinOrBail();
328 sourceIndexer.joinOrBail(); 328 destIndexer.joinOrBail();
329 destIndexer.joinOrBail(); 329 source = sourceIndexer.deobfuscator;
330 source = sourceIndexer.deobfuscator; 330 dest = destIndexer.deobfuscator;
331 dest = destIndexer.deobfuscator; 331 }
332 } 332 }
333 } 333
334 334 private static class IndexerThread extends Thread {
335 private static class IndexerThread extends Thread { 335
336 336 public Deobfuscator deobfuscator;
337 private JarFile jarFile; 337 private JarFile jarFile;
338 public Deobfuscator deobfuscator; 338
339 339 public IndexerThread(JarFile jarFile) {
340 public IndexerThread(JarFile jarFile) { 340 this.jarFile = jarFile;
341 this.jarFile = jarFile; 341 deobfuscator = null;
342 deobfuscator = null; 342 }
343 } 343
344 344 public void joinOrBail() {
345 public void joinOrBail() { 345 try {
346 try { 346 join();
347 join(); 347 } catch (InterruptedException ex) {
348 } catch (InterruptedException ex) { 348 throw new Error(ex);
349 throw new Error(ex); 349 }
350 } 350 }
351 } 351
352 352 @Override
353 @Override 353 public void run() {
354 public void run() { 354 deobfuscator = new Deobfuscator(jarFile);
355 deobfuscator = new Deobfuscator(jarFile); 355 }
356 } 356 }
357 }
358} \ No newline at end of file 357} \ No newline at end of file
diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java
index 2602abcc..934d02a5 100644
--- a/src/main/java/cuchaz/enigma/Deobfuscator.java
+++ b/src/main/java/cuchaz/enigma/Deobfuscator.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma; 12package cuchaz.enigma;
12 13
13import com.google.common.base.Charsets; 14import com.google.common.base.Charsets;
@@ -40,587 +41,583 @@ import java.util.jar.JarOutputStream;
40 41
41public class Deobfuscator { 42public class Deobfuscator {
42 43
43 public interface ProgressListener { 44 private final JarFile jar;
44 void init(int totalWork, String title); 45 private final DecompilerSettings settings;
45 46 private final JarIndex jarIndex;
46 void onProgress(int numDone, String message); 47 private final MappingsRenamer renamer;
47 } 48 private final Map<TranslationDirection, Translator> translatorCache;
48 49 private Mappings mappings;
49 private final JarFile jar; 50 public Deobfuscator(JarFile jar) {
50 private final DecompilerSettings settings; 51 this.jar = jar;
51 private final JarIndex jarIndex; 52
52 private final MappingsRenamer renamer; 53 // build the jar index
53 private final Map<TranslationDirection, Translator> translatorCache; 54 this.jarIndex = new JarIndex();
54 private Mappings mappings; 55 this.jarIndex.indexJar(this.jar, true);
55 56
56 public Deobfuscator(JarFile jar) { 57 // config the decompiler
57 this.jar = jar; 58 this.settings = DecompilerSettings.javaDefaults();
58 59 this.settings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true));
59 // build the jar index 60 this.settings.setForceExplicitImports(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitImports", true));
60 this.jarIndex = new JarIndex(); 61 this.settings.setForceExplicitTypeArguments(
61 this.jarIndex.indexJar(this.jar, true); 62 Utils.getSystemPropertyAsBoolean("enigma.forceExplicitTypeArguments", true));
62 63 // DEBUG
63 // config the decompiler 64 this.settings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false));
64 this.settings = DecompilerSettings.javaDefaults(); 65 this.settings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false));
65 this.settings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true)); 66
66 this.settings.setForceExplicitImports(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitImports", true)); 67 // init defaults
67 this.settings.setForceExplicitTypeArguments( 68 this.translatorCache = Maps.newTreeMap();
68 Utils.getSystemPropertyAsBoolean("enigma.forceExplicitTypeArguments", true)); 69 this.renamer = new MappingsRenamer(this.jarIndex, null);
69 // DEBUG 70 // init mappings
70 this.settings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false)); 71 setMappings(new Mappings());
71 this.settings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false)); 72 }
72 73
73 // init defaults 74 public JarFile getJar() {
74 this.translatorCache = Maps.newTreeMap(); 75 return this.jar;
75 this.renamer = new MappingsRenamer(this.jarIndex, null); 76 }
76 // init mappings 77
77 setMappings(new Mappings()); 78 public String getJarName() {
78 } 79 return this.jar.getName();
79 80 }
80 public JarFile getJar() { 81
81 return this.jar; 82 public JarIndex getJarIndex() {
82 } 83 return this.jarIndex;
83 84 }
84 public String getJarName() { 85
85 return this.jar.getName(); 86 public Mappings getMappings() {
86 } 87 return this.mappings;
87 88 }
88 public JarIndex getJarIndex() { 89
89 return this.jarIndex; 90 public void setMappings(Mappings val) {
90 } 91 setMappings(val, true);
91 92 }
92 public Mappings getMappings() { 93
93 return this.mappings; 94 public void setMappings(Mappings val, boolean warnAboutDrops) {
94 } 95 if (val == null) {
95 96 val = new Mappings();
96 public void setMappings(Mappings val) { 97 }
97 setMappings(val, true); 98
98 } 99 // drop mappings that don't match the jar
99 100 MappingsChecker checker = new MappingsChecker(this.jarIndex);
100 public void setMappings(Mappings val, boolean warnAboutDrops) { 101 checker.dropBrokenMappings(val);
101 if (val == null) { 102 if (warnAboutDrops) {
102 val = new Mappings(); 103 for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedClassMappings().entrySet()) {
103 } 104 System.out.println("WARNING: Couldn't find class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
104 105 }
105 // drop mappings that don't match the jar 106 for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedInnerClassMappings().entrySet()) {
106 MappingsChecker checker = new MappingsChecker(this.jarIndex); 107 System.out.println("WARNING: Couldn't find inner class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
107 checker.dropBrokenMappings(val); 108 }
108 if (warnAboutDrops) { 109 for (java.util.Map.Entry<FieldEntry, FieldMapping> mapping : checker.getDroppedFieldMappings().entrySet()) {
109 for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedClassMappings().entrySet()) { 110 System.out.println("WARNING: Couldn't find field entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
110 System.out.println("WARNING: Couldn't find class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); 111 }
111 } 112 for (java.util.Map.Entry<BehaviorEntry, MethodMapping> mapping : checker.getDroppedMethodMappings().entrySet()) {
112 for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedInnerClassMappings().entrySet()) { 113 System.out.println("WARNING: Couldn't find behavior entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
113 System.out.println("WARNING: Couldn't find inner class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); 114 }
114 } 115 }
115 for (java.util.Map.Entry<FieldEntry, FieldMapping> mapping : checker.getDroppedFieldMappings().entrySet()) { 116
116 System.out.println("WARNING: Couldn't find field entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); 117 this.mappings = val;
117 } 118 this.renamer.setMappings(mappings);
118 for (java.util.Map.Entry<BehaviorEntry, MethodMapping> mapping : checker.getDroppedMethodMappings().entrySet()) { 119 this.translatorCache.clear();
119 System.out.println("WARNING: Couldn't find behavior entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); 120 }
120 } 121
121 } 122 public Translator getTranslator(TranslationDirection direction) {
122 123 return this.translatorCache.computeIfAbsent(direction,
123 this.mappings = val; 124 k -> this.mappings.getTranslator(direction, this.jarIndex.getTranslationIndex()));
124 this.renamer.setMappings(mappings); 125 }
125 this.translatorCache.clear(); 126
126 } 127 public void getSeparatedClasses(List<ClassEntry> obfClasses, List<ClassEntry> deobfClasses) {
127 128 for (ClassEntry obfClassEntry : this.jarIndex.getObfClassEntries()) {
128 public Translator getTranslator(TranslationDirection direction) { 129 // skip inner classes
129 return this.translatorCache.computeIfAbsent(direction, 130 if (obfClassEntry.isInnerClass()) {
130 k -> this.mappings.getTranslator(direction, this.jarIndex.getTranslationIndex())); 131 continue;
131 } 132 }
132 133
133 public void getSeparatedClasses(List<ClassEntry> obfClasses, List<ClassEntry> deobfClasses) { 134 // separate the classes
134 for (ClassEntry obfClassEntry : this.jarIndex.getObfClassEntries()) { 135 ClassEntry deobfClassEntry = deobfuscateEntry(obfClassEntry);
135 // skip inner classes 136 if (!deobfClassEntry.equals(obfClassEntry)) {
136 if (obfClassEntry.isInnerClass()) { 137 // if the class has a mapping, clearly it's deobfuscated
137 continue; 138 deobfClasses.add(deobfClassEntry);
138 } 139 } else if (obfClassEntry.getPackageName() != null) {
139 140 // also call it deobufscated if it's not in the none package
140 // separate the classes 141 deobfClasses.add(obfClassEntry);
141 ClassEntry deobfClassEntry = deobfuscateEntry(obfClassEntry); 142 } else {
142 if (!deobfClassEntry.equals(obfClassEntry)) { 143 // otherwise, assume it's still obfuscated
143 // if the class has a mapping, clearly it's deobfuscated 144 obfClasses.add(obfClassEntry);
144 deobfClasses.add(deobfClassEntry); 145 }
145 } else if (obfClassEntry.getPackageName() != null) { 146 }
146 // also call it deobufscated if it's not in the none package 147 }
147 deobfClasses.add(obfClassEntry); 148
148 } else { 149 public TranslatingTypeLoader createTypeLoader() {
149 // otherwise, assume it's still obfuscated 150 return new TranslatingTypeLoader(
150 obfClasses.add(obfClassEntry); 151 this.jar,
151 } 152 this.jarIndex,
152 } 153 getTranslator(TranslationDirection.Obfuscating),
153 } 154 getTranslator(TranslationDirection.Deobfuscating)
154 155 );
155 public TranslatingTypeLoader createTypeLoader() 156 }
156 { 157
157 return new TranslatingTypeLoader( 158 public CompilationUnit getSourceTree(String className) {
158 this.jar, 159
159 this.jarIndex, 160 // we don't know if this class name is obfuscated or deobfuscated
160 getTranslator(TranslationDirection.Obfuscating), 161 // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out
161 getTranslator(TranslationDirection.Deobfuscating) 162 // the decompiler only sees classes after deobfuscation, so we need to load it by the deobfuscated name if there is one
162 ); 163
163 } 164 // first, assume class name is deobf
164 165 String deobfClassName = className;
165 public CompilationUnit getSourceTree(String className) { 166
166 167 // if it wasn't actually deobf, then we can find a mapping for it and get the deobf name
167 // we don't know if this class name is obfuscated or deobfuscated 168 ClassMapping classMapping = this.mappings.getClassByObf(className);
168 // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out 169 if (classMapping != null && classMapping.getDeobfName() != null) {
169 // the decompiler only sees classes after deobfuscation, so we need to load it by the deobfuscated name if there is one 170 deobfClassName = classMapping.getDeobfName();
170 171 }
171 // first, assume class name is deobf 172
172 String deobfClassName = className; 173 // set the type loader
173 174 TranslatingTypeLoader loader = createTypeLoader();
174 // if it wasn't actually deobf, then we can find a mapping for it and get the deobf name 175 this.settings.setTypeLoader(loader);
175 ClassMapping classMapping = this.mappings.getClassByObf(className); 176
176 if (classMapping != null && classMapping.getDeobfName() != null) { 177 // see if procyon can find the type
177 deobfClassName = classMapping.getDeobfName(); 178 TypeReference type = new MetadataSystem(loader).lookupType(deobfClassName);
178 } 179 if (type == null) {
179 180 throw new Error(String.format("Unable to find type: %s (deobf: %s)\nTried class names: %s",
180 // set the type loader 181 className, deobfClassName, loader.getClassNamesToTry(deobfClassName)
181 TranslatingTypeLoader loader = createTypeLoader(); 182 ));
182 this.settings.setTypeLoader(loader); 183 }
183 184 TypeDefinition resolvedType = type.resolve();
184 // see if procyon can find the type 185
185 TypeReference type = new MetadataSystem(loader).lookupType(deobfClassName); 186 // decompile it!
186 if (type == null) { 187 DecompilerContext context = new DecompilerContext();
187 throw new Error(String.format("Unable to find type: %s (deobf: %s)\nTried class names: %s", 188 context.setCurrentType(resolvedType);
188 className, deobfClassName, loader.getClassNamesToTry(deobfClassName) 189 context.setSettings(this.settings);
189 )); 190 AstBuilder builder = new AstBuilder(context);
190 } 191 builder.addType(resolvedType);
191 TypeDefinition resolvedType = type.resolve(); 192 builder.runTransformations(null);
192 193 return builder.getCompilationUnit();
193 // decompile it! 194 }
194 DecompilerContext context = new DecompilerContext(); 195
195 context.setCurrentType(resolvedType); 196 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source) {
196 context.setSettings(this.settings); 197 return getSourceIndex(sourceTree, source, null);
197 AstBuilder builder = new AstBuilder(context); 198 }
198 builder.addType(resolvedType); 199
199 builder.runTransformations(null); 200 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source, Boolean ignoreBadTokens) {
200 return builder.getCompilationUnit(); 201
201 } 202 // build the source index
202 203 SourceIndex index;
203 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source) { 204 if (ignoreBadTokens != null) {
204 return getSourceIndex(sourceTree, source, null); 205 index = new SourceIndex(source, ignoreBadTokens);
205 } 206 } else {
206 207 index = new SourceIndex(source);
207 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source, Boolean ignoreBadTokens) { 208 }
208 209 sourceTree.acceptVisitor(new SourceIndexVisitor(), index);
209 // build the source index 210
210 SourceIndex index; 211 // DEBUG
211 if (ignoreBadTokens != null) { 212 // sourceTree.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null );
212 index = new SourceIndex(source, ignoreBadTokens); 213
213 } else { 214 // resolve all the classes in the source references
214 index = new SourceIndex(source); 215 for (Token token : index.referenceTokens()) {
215 } 216 EntryReference<Entry, Entry> deobfReference = index.getDeobfReference(token);
216 sourceTree.acceptVisitor(new SourceIndexVisitor(), index); 217
217 218 // get the obfuscated entry
218 // DEBUG 219 Entry obfEntry = obfuscateEntry(deobfReference.entry);
219 // sourceTree.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null ); 220
220 221 // try to resolve the class
221 // resolve all the classes in the source references 222 ClassEntry resolvedObfClassEntry = this.jarIndex.getTranslationIndex().resolveEntryClass(obfEntry);
222 for (Token token : index.referenceTokens()) { 223 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getClassEntry())) {
223 EntryReference<Entry, Entry> deobfReference = index.getDeobfReference(token); 224 // change the class of the entry
224 225 obfEntry = obfEntry.cloneToNewClass(resolvedObfClassEntry);
225 // get the obfuscated entry 226
226 Entry obfEntry = obfuscateEntry(deobfReference.entry); 227 // save the new deobfuscated reference
227 228 deobfReference.entry = deobfuscateEntry(obfEntry);
228 // try to resolve the class 229 index.replaceDeobfReference(token, deobfReference);
229 ClassEntry resolvedObfClassEntry = this.jarIndex.getTranslationIndex().resolveEntryClass(obfEntry); 230 }
230 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getClassEntry())) { 231
231 // change the class of the entry 232 // DEBUG
232 obfEntry = obfEntry.cloneToNewClass(resolvedObfClassEntry); 233 // System.out.println( token + " -> " + reference + " -> " + index.getReferenceToken( reference ) );
233 234 }
234 // save the new deobfuscated reference 235
235 deobfReference.entry = deobfuscateEntry(obfEntry); 236 return index;
236 index.replaceDeobfReference(token, deobfReference); 237 }
237 } 238
238 239 public String getSource(CompilationUnit sourceTree) {
239 // DEBUG 240 // render the AST into source
240 // System.out.println( token + " -> " + reference + " -> " + index.getReferenceToken( reference ) ); 241 StringWriter buf = new StringWriter();
241 } 242 sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null);
242 243 sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(buf), this.settings), null);
243 return index; 244 return buf.toString();
244 } 245 }
245 246
246 public String getSource(CompilationUnit sourceTree) { 247 public void writeSources(File dirOut, ProgressListener progress) {
247 // render the AST into source 248 // get the classes to decompile
248 StringWriter buf = new StringWriter(); 249 Set<ClassEntry> classEntries = Sets.newHashSet();
249 sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null); 250 for (ClassEntry obfClassEntry : this.jarIndex.getObfClassEntries()) {
250 sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(buf), this.settings), null); 251 // skip inner classes
251 return buf.toString(); 252 if (obfClassEntry.isInnerClass()) {
252 } 253 continue;
253 254 }
254 public void writeSources(File dirOut, ProgressListener progress) { 255
255 // get the classes to decompile 256 classEntries.add(obfClassEntry);
256 Set<ClassEntry> classEntries = Sets.newHashSet(); 257 }
257 for (ClassEntry obfClassEntry : this.jarIndex.getObfClassEntries()) { 258
258 // skip inner classes 259 if (progress != null) {
259 if (obfClassEntry.isInnerClass()) { 260 progress.init(classEntries.size(), "Decompiling classes...");
260 continue; 261 }
261 } 262
262 263 // DEOBFUSCATE ALL THE THINGS!! @_@
263 classEntries.add(obfClassEntry); 264 int i = 0;
264 } 265 for (ClassEntry obfClassEntry : classEntries) {
265 266 ClassEntry deobfClassEntry = deobfuscateEntry(new ClassEntry(obfClassEntry));
266 if (progress != null) { 267 if (progress != null) {
267 progress.init(classEntries.size(), "Decompiling classes..."); 268 progress.onProgress(i++, deobfClassEntry.toString());
268 } 269 }
269 270
270 // DEOBFUSCATE ALL THE THINGS!! @_@ 271 try {
271 int i = 0; 272 // get the source
272 for (ClassEntry obfClassEntry : classEntries) { 273 String source = getSource(getSourceTree(obfClassEntry.getName()));
273 ClassEntry deobfClassEntry = deobfuscateEntry(new ClassEntry(obfClassEntry)); 274
274 if (progress != null) { 275 // write the file
275 progress.onProgress(i++, deobfClassEntry.toString()); 276 File file = new File(dirOut, deobfClassEntry.getName().replace('.', '/') + ".java");
276 } 277 file.getParentFile().mkdirs();
277 278 try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8)) {
278 try { 279 out.write(source);
279 // get the source 280 }
280 String source = getSource(getSourceTree(obfClassEntry.getName())); 281 } catch (Throwable t) {
281 282 // don't crash the whole world here, just log the error and keep going
282 // write the file 283 // TODO: set up logback via log4j
283 File file = new File(dirOut, deobfClassEntry.getName().replace('.', '/') + ".java"); 284 System.err.println("Unable to deobfuscate class " + deobfClassEntry + " (" + obfClassEntry + ")");
284 file.getParentFile().mkdirs(); 285 t.printStackTrace(System.err);
285 try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8)) { 286 }
286 out.write(source); 287 }
287 } 288 if (progress != null) {
288 } catch (Throwable t) { 289 progress.onProgress(i, "Done!");
289 // don't crash the whole world here, just log the error and keep going 290 }
290 // TODO: set up logback via log4j 291 }
291 System.err.println("Unable to deobfuscate class " + deobfClassEntry.toString() + " (" + obfClassEntry.toString() + ")"); 292
292 t.printStackTrace(System.err); 293 private void addAllPotentialAncestors(Set<ClassEntry> classEntries, ClassEntry classObfEntry) {
293 } 294 for (ClassEntry interfaceEntry : jarIndex.getTranslationIndex().getInterfaces(classObfEntry)) {
294 } 295 if (classEntries.add(interfaceEntry)) {
295 if (progress != null) { 296 addAllPotentialAncestors(classEntries, interfaceEntry);
296 progress.onProgress(i, "Done!"); 297 }
297 } 298 }
298 } 299
299 300 ClassEntry superClassEntry = jarIndex.getTranslationIndex().getSuperclass(classObfEntry);
300 private void addAllPotentialAncestors(Set<ClassEntry> classEntries, ClassEntry classObfEntry) { 301 if (superClassEntry != null && classEntries.add(superClassEntry)) {
301 for (ClassEntry interfaceEntry : jarIndex.getTranslationIndex().getInterfaces(classObfEntry)) { 302 addAllPotentialAncestors(classEntries, superClassEntry);
302 if (classEntries.add(interfaceEntry)) { 303 }
303 addAllPotentialAncestors(classEntries, interfaceEntry); 304 }
304 } 305
305 } 306 private boolean isBehaviorProvider(ClassEntry classObfEntry, BehaviorEntry behaviorEntry) {
306 307 if (behaviorEntry instanceof MethodEntry) {
307 ClassEntry superClassEntry = jarIndex.getTranslationIndex().getSuperclass(classObfEntry); 308 MethodEntry methodEntry = (MethodEntry) behaviorEntry;
308 if (superClassEntry != null && classEntries.add(superClassEntry)) { 309
309 addAllPotentialAncestors(classEntries, superClassEntry); 310 Set<ClassEntry> classEntries = new HashSet<>();
310 } 311 addAllPotentialAncestors(classEntries, classObfEntry);
311 } 312
312 313 for (ClassEntry parentEntry : classEntries) {
313 private boolean isBehaviorProvider(ClassEntry classObfEntry, BehaviorEntry behaviorEntry) { 314 MethodEntry ancestorMethodEntry = new MethodEntry(parentEntry, methodEntry.getName(), methodEntry.getSignature());
314 if (behaviorEntry instanceof MethodEntry) { 315 if (jarIndex.containsObfBehavior(ancestorMethodEntry)) {
315 MethodEntry methodEntry = (MethodEntry) behaviorEntry; 316 return false;
316 317 }
317 Set<ClassEntry> classEntries = new HashSet<>(); 318 }
318 addAllPotentialAncestors(classEntries, classObfEntry); 319 }
319 320
320 for (ClassEntry parentEntry : classEntries) { 321 return true;
321 MethodEntry ancestorMethodEntry = new MethodEntry(parentEntry, methodEntry.getName(), methodEntry.getSignature()); 322 }
322 if (jarIndex.containsObfBehavior(ancestorMethodEntry)) { 323
323 return false; 324 public void rebuildMethodNames(ProgressListener progress) {
324 } 325 int i = 0;
325 } 326 Map<ClassMapping, Map<Entry, String>> renameClassMap = new HashMap<>();
326 } 327
327 328 progress.init(getMappings().classes().size() * 3, "Rebuilding method names");
328 return true; 329
329 } 330 for (ClassMapping classMapping : Lists.newArrayList(getMappings().classes())) {
330 331 Map<Entry, String> renameEntries = new HashMap<>();
331 public void rebuildMethodNames(ProgressListener progress) { 332
332 int i = 0; 333 progress.onProgress(i++, classMapping.getDeobfName());
333 Map<ClassMapping, Map<Entry, String>> renameClassMap = new HashMap<>(); 334
334 335 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) {
335 progress.init(getMappings().classes().size() * 3, "Rebuilding method names"); 336 ClassEntry classObfEntry = classMapping.getObfEntry();
336 337 BehaviorEntry obfEntry = methodMapping.getObfEntry(classObfEntry);
337 for (ClassMapping classMapping : Lists.newArrayList(getMappings().classes())) { 338
338 Map<Entry, String> renameEntries = new HashMap<>(); 339 if (isBehaviorProvider(classObfEntry, obfEntry)) {
339 340 if (hasDeobfuscatedName(obfEntry) && !(obfEntry instanceof ConstructorEntry)
340 progress.onProgress(i++, classMapping.getDeobfName()); 341 && !(methodMapping.getDeobfName().equals(methodMapping.getObfName()))) {
341 342 renameEntries.put(obfEntry, methodMapping.getDeobfName());
342 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { 343 }
343 ClassEntry classObfEntry = classMapping.getObfEntry(); 344
344 BehaviorEntry obfEntry = methodMapping.getObfEntry(classObfEntry); 345 for (ArgumentMapping argumentMapping : Lists.newArrayList(methodMapping.arguments())) {
345 346 Entry argObfEntry = argumentMapping.getObfEntry(obfEntry);
346 if (isBehaviorProvider(classObfEntry, obfEntry)) { 347 if (hasDeobfuscatedName(argObfEntry)) {
347 if (hasDeobfuscatedName(obfEntry) && !(obfEntry instanceof ConstructorEntry) 348 renameEntries.put(argObfEntry, deobfuscateEntry(argObfEntry).getName());
348 && !(methodMapping.getDeobfName().equals(methodMapping.getObfName()))) { 349 }
349 renameEntries.put(obfEntry, methodMapping.getDeobfName()); 350 }
350 } 351 }
351 352 }
352 for (ArgumentMapping argumentMapping : Lists.newArrayList(methodMapping.arguments())) { 353
353 Entry argObfEntry = argumentMapping.getObfEntry(obfEntry); 354 renameClassMap.put(classMapping, renameEntries);
354 if (hasDeobfuscatedName(argObfEntry)) { 355 }
355 renameEntries.put(argObfEntry, deobfuscateEntry(argObfEntry).getName()); 356
356 } 357 for (Map.Entry<ClassMapping, Map<Entry, String>> renameClassMapEntry : renameClassMap.entrySet()) {
357 } 358 progress.onProgress(i++, renameClassMapEntry.getKey().getDeobfName());
358 } 359
359 } 360 for (Map.Entry<Entry, String> entry : renameClassMapEntry.getValue().entrySet()) {
360 361 Entry obfEntry = entry.getKey();
361 renameClassMap.put(classMapping, renameEntries); 362
362 } 363 removeMapping(obfEntry);
363 364 }
364 for (Map.Entry<ClassMapping, Map<Entry, String>> renameClassMapEntry : renameClassMap.entrySet()) { 365 }
365 progress.onProgress(i++, renameClassMapEntry.getKey().getDeobfName()); 366
366 367 for (Map.Entry<ClassMapping, Map<Entry, String>> renameClassMapEntry : renameClassMap.entrySet()) {
367 for (Map.Entry<Entry, String> entry : renameClassMapEntry.getValue().entrySet()) { 368 progress.onProgress(i++, renameClassMapEntry.getKey().getDeobfName());
368 Entry obfEntry = entry.getKey(); 369
369 370 for (Map.Entry<Entry, String> entry : renameClassMapEntry.getValue().entrySet()) {
370 removeMapping(obfEntry); 371 Entry obfEntry = entry.getKey();
371 } 372 String name = entry.getValue();
372 } 373
373 374 rename(obfEntry, name);
374 for (Map.Entry<ClassMapping, Map<Entry, String>> renameClassMapEntry : renameClassMap.entrySet()) { 375 }
375 progress.onProgress(i++, renameClassMapEntry.getKey().getDeobfName()); 376 }
376 377 }
377 for (Map.Entry<Entry, String> entry : renameClassMapEntry.getValue().entrySet()) { 378
378 Entry obfEntry = entry.getKey(); 379 public void writeJar(File out, ProgressListener progress) {
379 String name = entry.getValue(); 380 transformJar(out, progress, createTypeLoader()::transformClass);
380 381 }
381 rename(obfEntry, name); 382
382 } 383 public void protectifyJar(File out, ProgressListener progress) {
383 } 384 transformJar(out, progress, ClassProtectifier::protectify);
384 } 385 }
385 386
386 public void writeJar(File out, ProgressListener progress) { 387 public void publifyJar(File out, ProgressListener progress) {
387 transformJar(out, progress, createTypeLoader()::transformClass); 388 transformJar(out, progress, ClassPublifier::publify);
388 } 389 }
389 390
390 public void protectifyJar(File out, ProgressListener progress) { 391 public void transformJar(File out, ProgressListener progress, ClassTransformer transformer) {
391 transformJar(out, progress, ClassProtectifier::protectify); 392 try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) {
392 } 393 if (progress != null) {
393 394 progress.init(JarClassIterator.getClassEntries(this.jar).size(), "Transforming classes...");
394 public void publifyJar(File out, ProgressListener progress) { 395 }
395 transformJar(out, progress, ClassPublifier::publify); 396
396 } 397 int i = 0;
397 398 for (CtClass c : JarClassIterator.classes(this.jar)) {
398 public interface ClassTransformer { 399 if (progress != null) {
399 CtClass transform(CtClass c) throws Exception; 400 progress.onProgress(i++, c.getName());
400 } 401 }
401 402
402 public void transformJar(File out, ProgressListener progress, ClassTransformer transformer) { 403 try {
403 try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) { 404 c = transformer.transform(c);
404 if (progress != null) { 405 outJar.putNextEntry(new JarEntry(c.getName().replace('.', '/') + ".class"));
405 progress.init(JarClassIterator.getClassEntries(this.jar).size(), "Transforming classes..."); 406 outJar.write(c.toBytecode());
406 } 407 outJar.closeEntry();
407 408 } catch (Throwable t) {
408 int i = 0; 409 throw new Error("Unable to transform class " + c.getName(), t);
409 for (CtClass c : JarClassIterator.classes(this.jar)) { 410 }
410 if (progress != null) { 411 }
411 progress.onProgress(i++, c.getName()); 412 if (progress != null) {
412 } 413 progress.onProgress(i, "Done!");
413 414 }
414 try { 415
415 c = transformer.transform(c); 416 outJar.close();
416 outJar.putNextEntry(new JarEntry(c.getName().replace('.', '/') + ".class")); 417 } catch (IOException ex) {
417 outJar.write(c.toBytecode()); 418 throw new Error("Unable to write to Jar file!");
418 outJar.closeEntry(); 419 }
419 } catch (Throwable t) { 420 }
420 throw new Error("Unable to transform class " + c.getName(), t); 421
421 } 422 public <T extends Entry> T obfuscateEntry(T deobfEntry) {
422 } 423 if (deobfEntry == null) {
423 if (progress != null) { 424 return null;
424 progress.onProgress(i, "Done!"); 425 }
425 } 426 return getTranslator(TranslationDirection.Obfuscating).translateEntry(deobfEntry);
426 427 }
427 outJar.close(); 428
428 } catch (IOException ex) { 429 public <T extends Entry> T deobfuscateEntry(T obfEntry) {
429 throw new Error("Unable to write to Jar file!"); 430 if (obfEntry == null) {
430 } 431 return null;
431 } 432 }
432 433 return getTranslator(TranslationDirection.Deobfuscating).translateEntry(obfEntry);
433 public <T extends Entry> T obfuscateEntry(T deobfEntry) { 434 }
434 if (deobfEntry == null) { 435
435 return null; 436 public <E extends Entry, C extends Entry> EntryReference<E, C> obfuscateReference(EntryReference<E, C> deobfReference) {
436 } 437 if (deobfReference == null) {
437 return getTranslator(TranslationDirection.Obfuscating).translateEntry(deobfEntry); 438 return null;
438 } 439 }
439 440 return new EntryReference<>(obfuscateEntry(deobfReference.entry), obfuscateEntry(deobfReference.context), deobfReference);
440 public <T extends Entry> T deobfuscateEntry(T obfEntry) { 441 }
441 if (obfEntry == null) { 442
442 return null; 443 public <E extends Entry, C extends Entry> EntryReference<E, C> deobfuscateReference(EntryReference<E, C> obfReference) {
443 } 444 if (obfReference == null) {
444 return getTranslator(TranslationDirection.Deobfuscating).translateEntry(obfEntry); 445 return null;
445 } 446 }
446 447 return new EntryReference<>(deobfuscateEntry(obfReference.entry), deobfuscateEntry(obfReference.context), obfReference);
447 public <E extends Entry, C extends Entry> EntryReference<E, C> obfuscateReference(EntryReference<E, C> deobfReference) { 448 }
448 if (deobfReference == null) { 449
449 return null; 450 public boolean isObfuscatedIdentifier(Entry obfEntry) {
450 } 451 return isObfuscatedIdentifier(obfEntry, false);
451 return new EntryReference<>(obfuscateEntry(deobfReference.entry), obfuscateEntry(deobfReference.context), deobfReference); 452 }
452 } 453
453 454 public boolean isObfuscatedIdentifier(Entry obfEntry, boolean hack) {
454 public <E extends Entry, C extends Entry> EntryReference<E, C> deobfuscateReference(EntryReference<E, C> obfReference) { 455
455 if (obfReference == null) { 456 if (obfEntry instanceof MethodEntry) {
456 return null; 457
457 } 458 // HACKHACK: Object methods are not obfuscated identifiers
458 return new EntryReference<>(deobfuscateEntry(obfReference.entry), deobfuscateEntry(obfReference.context), obfReference); 459 MethodEntry obfMethodEntry = (MethodEntry) obfEntry;
459 } 460 String name = obfMethodEntry.getName();
460 461 String sig = obfMethodEntry.getSignature().toString();
461 public boolean isObfuscatedIdentifier(Entry obfEntry) { 462 if (name.equals("clone") && sig.equals("()Ljava/lang/Object;")) {
462 return isObfuscatedIdentifier(obfEntry, false); 463 return false;
463 } 464 } else if (name.equals("equals") && sig.equals("(Ljava/lang/Object;)Z")) {
464 465 return false;
465 public boolean isObfuscatedIdentifier(Entry obfEntry, boolean hack) { 466 } else if (name.equals("finalize") && sig.equals("()V")) {
466 467 return false;
467 if (obfEntry instanceof MethodEntry) { 468 } else if (name.equals("getClass") && sig.equals("()Ljava/lang/Class;")) {
468 469 return false;
469 // HACKHACK: Object methods are not obfuscated identifiers 470 } else if (name.equals("hashCode") && sig.equals("()I")) {
470 MethodEntry obfMethodEntry = (MethodEntry) obfEntry; 471 return false;
471 String name = obfMethodEntry.getName(); 472 } else if (name.equals("notify") && sig.equals("()V")) {
472 String sig = obfMethodEntry.getSignature().toString(); 473 return false;
473 if (name.equals("clone") && sig.equals("()Ljava/lang/Object;")) { 474 } else if (name.equals("notifyAll") && sig.equals("()V")) {
474 return false; 475 return false;
475 } else if (name.equals("equals") && sig.equals("(Ljava/lang/Object;)Z")) { 476 } else if (name.equals("toString") && sig.equals("()Ljava/lang/String;")) {
476 return false; 477 return false;
477 } else if (name.equals("finalize") && sig.equals("()V")) { 478 } else if (name.equals("wait") && sig.equals("()V")) {
478 return false; 479 return false;
479 } else if (name.equals("getClass") && sig.equals("()Ljava/lang/Class;")) { 480 } else if (name.equals("wait") && sig.equals("(J)V")) {
480 return false; 481 return false;
481 } else if (name.equals("hashCode") && sig.equals("()I")) { 482 } else if (name.equals("wait") && sig.equals("(JI)V")) {
482 return false; 483 return false;
483 } else if (name.equals("notify") && sig.equals("()V")) { 484 }
484 return false; 485
485 } else if (name.equals("notifyAll") && sig.equals("()V")) { 486 // FIXME: HACK EVEN MORE HACK!
486 return false; 487 if (hack && this.jarIndex.containsObfEntry(obfEntry.getClassEntry()))
487 } else if (name.equals("toString") && sig.equals("()Ljava/lang/String;")) { 488 return true;
488 return false; 489 }
489 } else if (name.equals("wait") && sig.equals("()V")) { 490
490 return false; 491 return this.jarIndex.containsObfEntry(obfEntry);
491 } else if (name.equals("wait") && sig.equals("(J)V")) { 492 }
492 return false; 493
493 } else if (name.equals("wait") && sig.equals("(JI)V")) { 494 public boolean isRenameable(EntryReference<Entry, Entry> obfReference, boolean activeHack) {
494 return false; 495 return obfReference.isNamed() && isObfuscatedIdentifier(obfReference.getNameableEntry(), activeHack);
495 } 496 }
496 497
497 // FIXME: HACK EVEN MORE HACK! 498 public boolean isRenameable(EntryReference<Entry, Entry> obfReference) {
498 if (hack && this.jarIndex.containsObfEntry(obfEntry.getClassEntry())) 499 return isRenameable(obfReference, false);
499 return true; 500 }
500 } 501
501 502 public boolean hasDeobfuscatedName(Entry obfEntry) {
502 return this.jarIndex.containsObfEntry(obfEntry); 503 Translator translator = getTranslator(TranslationDirection.Deobfuscating);
503 } 504 if (obfEntry instanceof ClassEntry) {
504 505 ClassEntry obfClass = (ClassEntry) obfEntry;
505 public boolean isRenameable(EntryReference<Entry, Entry> obfReference, boolean activeHack) { 506 List<ClassMapping> mappingChain = this.mappings.getClassMappingChain(obfClass);
506 return obfReference.isNamed() && isObfuscatedIdentifier(obfReference.getNameableEntry(), activeHack); 507 ClassMapping classMapping = mappingChain.get(mappingChain.size() - 1);
507 } 508 return classMapping != null && classMapping.getDeobfName() != null;
508 509 } else if (obfEntry instanceof FieldEntry) {
509 public boolean isRenameable(EntryReference<Entry, Entry> obfReference) { 510 return translator.translate((FieldEntry) obfEntry) != null;
510 return isRenameable(obfReference, false); 511 } else if (obfEntry instanceof MethodEntry) {
511 } 512 return translator.translate((MethodEntry) obfEntry) != null;
512 513 } else if (obfEntry instanceof ConstructorEntry) {
513 // NOTE: these methods are a bit messy... oh well 514 // constructors have no names
514 515 return false;
515 public boolean hasDeobfuscatedName(Entry obfEntry) { 516 } else if (obfEntry instanceof ArgumentEntry) {
516 Translator translator = getTranslator(TranslationDirection.Deobfuscating); 517 return translator.translate((ArgumentEntry) obfEntry) != null;
517 if (obfEntry instanceof ClassEntry) { 518 } else if (obfEntry instanceof LocalVariableEntry) {
518 ClassEntry obfClass = (ClassEntry) obfEntry; 519 // TODO: Implement it
519 List<ClassMapping> mappingChain = this.mappings.getClassMappingChain(obfClass); 520 //return translator.translate((LocalVariableEntry)obfEntry) != null;
520 ClassMapping classMapping = mappingChain.get(mappingChain.size() - 1); 521 return false;
521 return classMapping != null && classMapping.getDeobfName() != null; 522 } else {
522 } else if (obfEntry instanceof FieldEntry) { 523 throw new Error("Unknown entry type: " + obfEntry.getClass().getName());
523 return translator.translate((FieldEntry) obfEntry) != null; 524 }
524 } else if (obfEntry instanceof MethodEntry) { 525 }
525 return translator.translate((MethodEntry) obfEntry) != null; 526
526 } else if (obfEntry instanceof ConstructorEntry) { 527 public void rename(Entry obfEntry, String newName) {
527 // constructors have no names 528 rename(obfEntry, newName, true);
528 return false; 529 }
529 } else if (obfEntry instanceof ArgumentEntry) { 530
530 return translator.translate((ArgumentEntry) obfEntry) != null; 531 // NOTE: these methods are a bit messy... oh well
531 } else if (obfEntry instanceof LocalVariableEntry) { 532
532 // TODO: Implement it 533 public void rename(Entry obfEntry, String newName, boolean clearCache) {
533 //return translator.translate((LocalVariableEntry)obfEntry) != null; 534 if (obfEntry instanceof ClassEntry) {
534 return false; 535 this.renamer.setClassName((ClassEntry) obfEntry, Descriptor.toJvmName(newName));
535 } else { 536 } else if (obfEntry instanceof FieldEntry) {
536 throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); 537 this.renamer.setFieldName((FieldEntry) obfEntry, newName);
537 } 538 } else if (obfEntry instanceof MethodEntry) {
538 } 539 this.renamer.setMethodTreeName((MethodEntry) obfEntry, newName);
539 540 } else if (obfEntry instanceof ConstructorEntry) {
540 public void rename(Entry obfEntry, String newName) { 541 throw new IllegalArgumentException("Cannot rename constructors");
541 rename(obfEntry, newName, true); 542 } else if (obfEntry instanceof ArgumentEntry) {
542 } 543 this.renamer.setArgumentTreeName((ArgumentEntry) obfEntry, newName);
543 544 } else if (obfEntry instanceof LocalVariableEntry) {
544 public void rename(Entry obfEntry, String newName, boolean clearCache) { 545 // TODO: Implement it
545 if (obfEntry instanceof ClassEntry) { 546 } else {
546 this.renamer.setClassName((ClassEntry) obfEntry, Descriptor.toJvmName(newName)); 547 throw new Error("Unknown entry type: " + obfEntry.getClass().getName());
547 } else if (obfEntry instanceof FieldEntry) { 548 }
548 this.renamer.setFieldName((FieldEntry) obfEntry, newName); 549
549 } else if (obfEntry instanceof MethodEntry) { 550 // clear caches
550 this.renamer.setMethodTreeName((MethodEntry) obfEntry, newName); 551 if (clearCache)
551 } else if (obfEntry instanceof ConstructorEntry) { 552 this.translatorCache.clear();
552 throw new IllegalArgumentException("Cannot rename constructors"); 553 }
553 } else if (obfEntry instanceof ArgumentEntry) { 554
554 this.renamer.setArgumentTreeName((ArgumentEntry) obfEntry, newName); 555 public void removeMapping(Entry obfEntry) {
555 } else if (obfEntry instanceof LocalVariableEntry) { 556 if (obfEntry instanceof ClassEntry) {
556 // TODO: Implement it 557 this.renamer.removeClassMapping((ClassEntry) obfEntry);
557 } else { 558 } else if (obfEntry instanceof FieldEntry) {
558 throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); 559 this.renamer.removeFieldMapping((FieldEntry) obfEntry);
559 } 560 } else if (obfEntry instanceof MethodEntry) {
560 561 this.renamer.removeMethodTreeMapping((MethodEntry) obfEntry);
561 // clear caches 562 } else if (obfEntry instanceof ConstructorEntry) {
562 if (clearCache) 563 throw new IllegalArgumentException("Cannot rename constructors");
563 this.translatorCache.clear(); 564 } else if (obfEntry instanceof ArgumentEntry) {
564 } 565 this.renamer.removeArgumentMapping((ArgumentEntry) obfEntry);
565 566 } else {
566 public void removeMapping(Entry obfEntry) { 567 throw new Error("Unknown entry type: " + obfEntry);
567 if (obfEntry instanceof ClassEntry) { 568 }
568 this.renamer.removeClassMapping((ClassEntry) obfEntry); 569
569 } else if (obfEntry instanceof FieldEntry) { 570 // clear caches
570 this.renamer.removeFieldMapping((FieldEntry) obfEntry); 571 this.translatorCache.clear();
571 } else if (obfEntry instanceof MethodEntry) { 572 }
572 this.renamer.removeMethodTreeMapping((MethodEntry) obfEntry); 573
573 } else if (obfEntry instanceof ConstructorEntry) { 574 public void markAsDeobfuscated(Entry obfEntry) {
574 throw new IllegalArgumentException("Cannot rename constructors"); 575 if (obfEntry instanceof ClassEntry) {
575 } else if (obfEntry instanceof ArgumentEntry) { 576 this.renamer.markClassAsDeobfuscated((ClassEntry) obfEntry);
576 this.renamer.removeArgumentMapping((ArgumentEntry) obfEntry); 577 } else if (obfEntry instanceof FieldEntry) {
577 } else { 578 this.renamer.markFieldAsDeobfuscated((FieldEntry) obfEntry);
578 throw new Error("Unknown entry type: " + obfEntry); 579 } else if (obfEntry instanceof MethodEntry) {
579 } 580 this.renamer.markMethodTreeAsDeobfuscated((MethodEntry) obfEntry);
580 581 } else if (obfEntry instanceof ConstructorEntry) {
581 // clear caches 582 throw new IllegalArgumentException("Cannot rename constructors");
582 this.translatorCache.clear(); 583 } else if (obfEntry instanceof ArgumentEntry) {
583 } 584 this.renamer.markArgumentAsDeobfuscated((ArgumentEntry) obfEntry);
584 585 } else if (obfEntry instanceof LocalVariableEntry) {
585 public void markAsDeobfuscated(Entry obfEntry) { 586 // TODO: Implement it
586 if (obfEntry instanceof ClassEntry) { 587 } else {
587 this.renamer.markClassAsDeobfuscated((ClassEntry) obfEntry); 588 throw new Error("Unknown entry type: " + obfEntry);
588 } else if (obfEntry instanceof FieldEntry) { 589 }
589 this.renamer.markFieldAsDeobfuscated((FieldEntry) obfEntry); 590
590 } else if (obfEntry instanceof MethodEntry) { 591 // clear caches
591 this.renamer.markMethodTreeAsDeobfuscated((MethodEntry) obfEntry); 592 this.translatorCache.clear();
592 } else if (obfEntry instanceof ConstructorEntry) { 593 }
593 throw new IllegalArgumentException("Cannot rename constructors"); 594
594 } else if (obfEntry instanceof ArgumentEntry) { 595 public void changeModifier(Entry entry, Mappings.EntryModifier modifierEntry) {
595 this.renamer.markArgumentAsDeobfuscated((ArgumentEntry) obfEntry); 596 Entry obfEntry = obfuscateEntry(entry);
596 } else if (obfEntry instanceof LocalVariableEntry) { 597 if (obfEntry instanceof ClassEntry)
597 // TODO: Implement it 598 this.renamer.setClassModifier((ClassEntry) obfEntry, modifierEntry);
598 } else { 599 else if (obfEntry instanceof FieldEntry)
599 throw new Error("Unknown entry type: " + obfEntry); 600 this.renamer.setFieldModifier((FieldEntry) obfEntry, modifierEntry);
600 } 601 else if (obfEntry instanceof BehaviorEntry)
601 602 this.renamer.setMethodModifier((BehaviorEntry) obfEntry, modifierEntry);
602 // clear caches 603 else
603 this.translatorCache.clear(); 604 throw new Error("Unknown entry type: " + obfEntry);
604 } 605 }
605 606
606 public void changeModifier(Entry entry, Mappings.EntryModifier modifierEntry) 607 public Mappings.EntryModifier getModifier(Entry obEntry) {
607 { 608 Entry entry = obfuscateEntry(obEntry);
608 Entry obfEntry = obfuscateEntry(entry); 609 if (entry != null)
609 if (obfEntry instanceof ClassEntry) 610 obEntry = entry;
610 this.renamer.setClassModifier((ClassEntry) obfEntry, modifierEntry); 611 return getTranslator(TranslationDirection.Deobfuscating).getModifier(obEntry);
611 else if (obfEntry instanceof FieldEntry) 612 }
612 this.renamer.setFieldModifier((FieldEntry) obfEntry, modifierEntry); 613
613 else if (obfEntry instanceof BehaviorEntry) 614 public interface ProgressListener {
614 this.renamer.setMethodModifier((BehaviorEntry) obfEntry, modifierEntry); 615 void init(int totalWork, String title);
615 else 616
616 throw new Error("Unknown entry type: " + obfEntry); 617 void onProgress(int numDone, String message);
617 } 618 }
618 619
619 public Mappings.EntryModifier getModifier(Entry obEntry) 620 public interface ClassTransformer {
620 { 621 CtClass transform(CtClass c) throws Exception;
621 Entry entry = obfuscateEntry(obEntry); 622 }
622 if (entry != null)
623 obEntry = entry;
624 return getTranslator(TranslationDirection.Deobfuscating).getModifier(obEntry);
625 }
626} 623}
diff --git a/src/main/java/cuchaz/enigma/ExceptionIgnorer.java b/src/main/java/cuchaz/enigma/ExceptionIgnorer.java
index fc89fa6d..84331cc1 100644
--- a/src/main/java/cuchaz/enigma/ExceptionIgnorer.java
+++ b/src/main/java/cuchaz/enigma/ExceptionIgnorer.java
@@ -8,27 +8,28 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma; 12package cuchaz.enigma;
12 13
13public class ExceptionIgnorer { 14public class ExceptionIgnorer {
14 15
15 public static boolean shouldIgnore(Throwable t) { 16 public static boolean shouldIgnore(Throwable t) {
16 17
17 // is this that pesky concurrent access bug in the highlight painter system? 18 // is this that pesky concurrent access bug in the highlight painter system?
18 // (ancient ui code is ancient) 19 // (ancient ui code is ancient)
19 if (t instanceof ArrayIndexOutOfBoundsException) { 20 if (t instanceof ArrayIndexOutOfBoundsException) {
20 StackTraceElement[] stackTrace = t.getStackTrace(); 21 StackTraceElement[] stackTrace = t.getStackTrace();
21 if (stackTrace.length > 1) { 22 if (stackTrace.length > 1) {
22 23
23 // does this stack frame match javax.swing.text.DefaultHighlighter.paint*() ? 24 // does this stack frame match javax.swing.text.DefaultHighlighter.paint*() ?
24 StackTraceElement frame = stackTrace[1]; 25 StackTraceElement frame = stackTrace[1];
25 if (frame.getClassName().equals("javax.swing.text.DefaultHighlighter") && frame.getMethodName().startsWith("paint")) { 26 if (frame.getClassName().equals("javax.swing.text.DefaultHighlighter") && frame.getMethodName().startsWith("paint")) {
26 return true; 27 return true;
27 } 28 }
28 } 29 }
29 } 30 }
30 31
31 return false; 32 return false;
32 } 33 }
33 34
34} 35}
diff --git a/src/main/java/cuchaz/enigma/Main.java b/src/main/java/cuchaz/enigma/Main.java
index 73709868..fa8d0622 100644
--- a/src/main/java/cuchaz/enigma/Main.java
+++ b/src/main/java/cuchaz/enigma/Main.java
@@ -8,48 +8,48 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma; 12package cuchaz.enigma;
12 13
14import cuchaz.enigma.gui.Gui;
15
16import javax.swing.*;
13import java.io.File; 17import java.io.File;
14import java.util.jar.JarFile; 18import java.util.jar.JarFile;
15 19
16import javax.swing.UIManager;
17
18import cuchaz.enigma.gui.Gui;
19
20public class Main { 20public class Main {
21 21
22 public static void main(String[] args) throws Exception { 22 public static void main(String[] args) throws Exception {
23 if (System.getProperty("enigma.useSystemLookAndFeel", "true").equals("true")) 23 if (System.getProperty("enigma.useSystemLookAndFeel", "true").equals("true"))
24 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 24 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
25 Gui gui = new Gui(); 25 Gui gui = new Gui();
26 26
27 // parse command-line args 27 // parse command-line args
28 if (args.length >= 1) { 28 if (args.length >= 1) {
29 gui.getController().openJar(new JarFile(getFile(args[0]))); 29 gui.getController().openJar(new JarFile(getFile(args[0])));
30 } 30 }
31 if (args.length >= 2) { 31 if (args.length >= 2) {
32 gui.getController().openEnigmaMappings(getFile(args[1])); 32 gui.getController().openEnigmaMappings(getFile(args[1]));
33 } 33 }
34 34
35 // DEBUG 35 // DEBUG
36 //gui.getController().openDeclaration(new ClassEntry("none/byp")); 36 //gui.getController().openDeclaration(new ClassEntry("none/byp"));
37 } 37 }
38 38
39 private static File getFile(String path) { 39 private static File getFile(String path) {
40 // expand ~ to the home dir 40 // expand ~ to the home dir
41 if (path.startsWith("~")) { 41 if (path.startsWith("~")) {
42 // get the home dir 42 // get the home dir
43 File dirHome = new File(System.getProperty("user.home")); 43 File dirHome = new File(System.getProperty("user.home"));
44 44
45 // is the path just ~/ or is it ~user/ ? 45 // is the path just ~/ or is it ~user/ ?
46 if (path.startsWith("~/")) { 46 if (path.startsWith("~/")) {
47 return new File(dirHome, path.substring(2)); 47 return new File(dirHome, path.substring(2));
48 } else { 48 } else {
49 return new File(dirHome.getParentFile(), path.substring(1)); 49 return new File(dirHome.getParentFile(), path.substring(1));
50 } 50 }
51 } 51 }
52 52
53 return new File(path); 53 return new File(path);
54 } 54 }
55} 55}
diff --git a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
index 73405662..7304f722 100644
--- a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
+++ b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
@@ -8,14 +8,24 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma; 12package cuchaz.enigma;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
14import com.google.common.collect.Maps; 15import com.google.common.collect.Maps;
15
16import com.strobel.assembler.metadata.Buffer; 16import com.strobel.assembler.metadata.Buffer;
17import com.strobel.assembler.metadata.ClasspathTypeLoader; 17import com.strobel.assembler.metadata.ClasspathTypeLoader;
18import com.strobel.assembler.metadata.ITypeLoader; 18import com.strobel.assembler.metadata.ITypeLoader;
19import cuchaz.enigma.analysis.BridgeMarker;
20import cuchaz.enigma.analysis.JarIndex;
21import cuchaz.enigma.bytecode.ClassTranslator;
22import cuchaz.enigma.bytecode.InnerClassWriter;
23import cuchaz.enigma.bytecode.LocalVariableRenamer;
24import cuchaz.enigma.bytecode.MethodParameterWriter;
25import cuchaz.enigma.mapping.ClassEntry;
26import cuchaz.enigma.mapping.Translator;
27import javassist.*;
28import javassist.bytecode.Descriptor;
19 29
20import java.io.ByteArrayOutputStream; 30import java.io.ByteArrayOutputStream;
21import java.io.IOException; 31import java.io.IOException;
@@ -25,205 +35,197 @@ import java.util.Map;
25import java.util.jar.JarEntry; 35import java.util.jar.JarEntry;
26import java.util.jar.JarFile; 36import java.util.jar.JarFile;
27 37
28import cuchaz.enigma.analysis.BridgeMarker;
29import cuchaz.enigma.analysis.JarIndex;
30import cuchaz.enigma.bytecode.*;
31import cuchaz.enigma.mapping.ClassEntry;
32import cuchaz.enigma.mapping.Translator;
33import javassist.*;
34import javassist.bytecode.Descriptor;
35
36public class TranslatingTypeLoader implements ITypeLoader { 38public class TranslatingTypeLoader implements ITypeLoader {
37 39
38 private JarFile jar; 40 private JarFile jar;
39 private JarIndex jarIndex; 41 private JarIndex jarIndex;
40 private Translator obfuscatingTranslator; 42 private Translator obfuscatingTranslator;
41 private Translator deobfuscatingTranslator; 43 private Translator deobfuscatingTranslator;
42 private Map<String, byte[]> cache; 44 private Map<String, byte[]> cache;
43 private ClasspathTypeLoader defaultTypeLoader; 45 private ClasspathTypeLoader defaultTypeLoader;
44 46
45 public TranslatingTypeLoader(JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator) { 47 public TranslatingTypeLoader(JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator) {
46 this.jar = jar; 48 this.jar = jar;
47 this.jarIndex = jarIndex; 49 this.jarIndex = jarIndex;
48 this.obfuscatingTranslator = obfuscatingTranslator; 50 this.obfuscatingTranslator = obfuscatingTranslator;
49 this.deobfuscatingTranslator = deobfuscatingTranslator; 51 this.deobfuscatingTranslator = deobfuscatingTranslator;
50 this.cache = Maps.newHashMap(); 52 this.cache = Maps.newHashMap();
51 this.defaultTypeLoader = new ClasspathTypeLoader(); 53 this.defaultTypeLoader = new ClasspathTypeLoader();
52 } 54 }
53 55
54 public void clearCache() { 56 public void clearCache() {
55 this.cache.clear(); 57 this.cache.clear();
56 } 58 }
57 59
58 @Override 60 @Override
59 public boolean tryLoadType(String className, Buffer out) { 61 public boolean tryLoadType(String className, Buffer out) {
60 62
61 // check the cache 63 // check the cache
62 byte[] data; 64 byte[] data;
63 if (this.cache.containsKey(className)) { 65 if (this.cache.containsKey(className)) {
64 data = this.cache.get(className); 66 data = this.cache.get(className);
65 } else { 67 } else {
66 data = loadType(className); 68 data = loadType(className);
67 this.cache.put(className, data); 69 this.cache.put(className, data);
68 } 70 }
69 71
70 if (data == null) { 72 if (data == null) {
71 // chain to default type loader 73 // chain to default type loader
72 return this.defaultTypeLoader.tryLoadType(className, out); 74 return this.defaultTypeLoader.tryLoadType(className, out);
73 } 75 }
74 76
75 // send the class to the decompiler 77 // send the class to the decompiler
76 out.reset(data.length); 78 out.reset(data.length);
77 System.arraycopy(data, 0, out.array(), out.position(), data.length); 79 System.arraycopy(data, 0, out.array(), out.position(), data.length);
78 out.position(0); 80 out.position(0);
79 return true; 81 return true;
80 } 82 }
81 83
82 public CtClass loadClass(String deobfClassName) { 84 public CtClass loadClass(String deobfClassName) {
83 85
84 byte[] data = loadType(deobfClassName); 86 byte[] data = loadType(deobfClassName);
85 if (data == null) { 87 if (data == null) {
86 return null; 88 return null;
87 } 89 }
88 90
89 // return a javassist handle for the class 91 // return a javassist handle for the class
90 String javaClassFileName = Descriptor.toJavaName(deobfClassName); 92 String javaClassFileName = Descriptor.toJavaName(deobfClassName);
91 ClassPool classPool = new ClassPool(); 93 ClassPool classPool = new ClassPool();
92 classPool.insertClassPath(new ByteArrayClassPath(javaClassFileName, data)); 94 classPool.insertClassPath(new ByteArrayClassPath(javaClassFileName, data));
93 try { 95 try {
94 return classPool.get(javaClassFileName); 96 return classPool.get(javaClassFileName);
95 } catch (NotFoundException ex) { 97 } catch (NotFoundException ex) {
96 throw new Error(ex); 98 throw new Error(ex);
97 } 99 }
98 } 100 }
99 101
100 private byte[] loadType(String className) { 102 private byte[] loadType(String className) {
101 103
102 // NOTE: don't know if class name is obf or deobf 104 // NOTE: don't know if class name is obf or deobf
103 ClassEntry classEntry = new ClassEntry(className); 105 ClassEntry classEntry = new ClassEntry(className);
104 ClassEntry obfClassEntry = this.obfuscatingTranslator.translateEntry(classEntry); 106 ClassEntry obfClassEntry = this.obfuscatingTranslator.translateEntry(classEntry);
105 107
106 // is this an inner class referenced directly? (ie trying to load b instead of a$b) 108 // is this an inner class referenced directly? (ie trying to load b instead of a$b)
107 if (!obfClassEntry.isInnerClass()) { 109 if (!obfClassEntry.isInnerClass()) {
108 List<ClassEntry> classChain = this.jarIndex.getObfClassChain(obfClassEntry); 110 List<ClassEntry> classChain = this.jarIndex.getObfClassChain(obfClassEntry);
109 if (classChain.size() > 1) { 111 if (classChain.size() > 1) {
110 System.err.println(String.format("WARNING: no class %s after inner class reconstruction. Try %s", 112 System.err.println(String.format("WARNING: no class %s after inner class reconstruction. Try %s",
111 className, obfClassEntry.buildClassEntry(classChain) 113 className, obfClassEntry.buildClassEntry(classChain)
112 )); 114 ));
113 return null; 115 return null;
114 } 116 }
115 } 117 }
116 118
117 // is this a class we should even know about? 119 // is this a class we should even know about?
118 if (!this.jarIndex.containsObfClass(obfClassEntry)) { 120 if (!this.jarIndex.containsObfClass(obfClassEntry)) {
119 return null; 121 return null;
120 } 122 }
121 123
122 // DEBUG 124 // DEBUG
123 //System.out.println(String.format("Looking for %s (obf: %s)", classEntry.getName(), obfClassEntry.getName())); 125 //System.out.println(String.format("Looking for %s (obf: %s)", classEntry.getName(), obfClassEntry.getName()));
124 126
125 // find the class in the jar 127 // find the class in the jar
126 String classInJarName = findClassInJar(obfClassEntry); 128 String classInJarName = findClassInJar(obfClassEntry);
127 if (classInJarName == null) { 129 if (classInJarName == null) {
128 // couldn't find it 130 // couldn't find it
129 return null; 131 return null;
130 } 132 }
131 133
132 try { 134 try {
133 // read the class file into a buffer 135 // read the class file into a buffer
134 ByteArrayOutputStream data = new ByteArrayOutputStream(); 136 ByteArrayOutputStream data = new ByteArrayOutputStream();
135 byte[] buf = new byte[1024 * 1024]; // 1 KiB 137 byte[] buf = new byte[1024 * 1024]; // 1 KiB
136 InputStream in = this.jar.getInputStream(this.jar.getJarEntry(classInJarName + ".class")); 138 InputStream in = this.jar.getInputStream(this.jar.getJarEntry(classInJarName + ".class"));
137 while (true) { 139 while (true) {
138 int bytesRead = in.read(buf); 140 int bytesRead = in.read(buf);
139 if (bytesRead <= 0) { 141 if (bytesRead <= 0) {
140 break; 142 break;
141 } 143 }
142 data.write(buf, 0, bytesRead); 144 data.write(buf, 0, bytesRead);
143 } 145 }
144 data.close(); 146 data.close();
145 in.close(); 147 in.close();
146 buf = data.toByteArray(); 148 buf = data.toByteArray();
147 149
148 // load the javassist handle to the raw class 150 // load the javassist handle to the raw class
149 ClassPool classPool = new ClassPool(); 151 ClassPool classPool = new ClassPool();
150 String classInJarJavaName = Descriptor.toJavaName(classInJarName); 152 String classInJarJavaName = Descriptor.toJavaName(classInJarName);
151 classPool.insertClassPath(new ByteArrayClassPath(classInJarJavaName, buf)); 153 classPool.insertClassPath(new ByteArrayClassPath(classInJarJavaName, buf));
152 CtClass c = classPool.get(classInJarJavaName); 154 CtClass c = classPool.get(classInJarJavaName);
153 155
154 c = transformClass(c); 156 c = transformClass(c);
155 157
156 // sanity checking 158 // sanity checking
157 assertClassName(c, classEntry); 159 assertClassName(c, classEntry);
158 160
159 // DEBUG 161 // DEBUG
160 //Util.writeClass( c ); 162 //Util.writeClass( c );
161 163
162 // we have a transformed class! 164 // we have a transformed class!
163 return c.toBytecode(); 165 return c.toBytecode();
164 } catch (IOException | NotFoundException | CannotCompileException ex) { 166 } catch (IOException | NotFoundException | CannotCompileException ex) {
165 throw new Error(ex); 167 throw new Error(ex);
166 } 168 }
167 } 169 }
168 170
169 private String findClassInJar(ClassEntry obfClassEntry) { 171 private String findClassInJar(ClassEntry obfClassEntry) {
170 172
171 // try to find the class in the jar 173 // try to find the class in the jar
172 for (String className : getClassNamesToTry(obfClassEntry)) { 174 for (String className : getClassNamesToTry(obfClassEntry)) {
173 JarEntry jarEntry = this.jar.getJarEntry(className + ".class"); 175 JarEntry jarEntry = this.jar.getJarEntry(className + ".class");
174 if (jarEntry != null) { 176 if (jarEntry != null) {
175 return className; 177 return className;
176 } 178 }
177 } 179 }
178 180
179 // didn't find it ;_; 181 // didn't find it ;_;
180 return null; 182 return null;
181 } 183 }
182 184
183 public List<String> getClassNamesToTry(String className) { 185 public List<String> getClassNamesToTry(String className) {
184 return getClassNamesToTry(this.obfuscatingTranslator.translateEntry(new ClassEntry(className))); 186 return getClassNamesToTry(this.obfuscatingTranslator.translateEntry(new ClassEntry(className)));
185 } 187 }
186 188
187 public List<String> getClassNamesToTry(ClassEntry obfClassEntry) { 189 public List<String> getClassNamesToTry(ClassEntry obfClassEntry) {
188 List<String> classNamesToTry = Lists.newArrayList(); 190 List<String> classNamesToTry = Lists.newArrayList();
189 classNamesToTry.add(obfClassEntry.getName()); 191 classNamesToTry.add(obfClassEntry.getName());
190 if (obfClassEntry.isInnerClass()) { 192 if (obfClassEntry.isInnerClass()) {
191 // try just the inner class name 193 // try just the inner class name
192 classNamesToTry.add(obfClassEntry.getInnermostClassName()); 194 classNamesToTry.add(obfClassEntry.getInnermostClassName());
193 } 195 }
194 return classNamesToTry; 196 return classNamesToTry;
195 } 197 }
196 198
197 public CtClass transformClass(CtClass c) 199 public CtClass transformClass(CtClass c)
198 throws IOException, NotFoundException, CannotCompileException { 200 throws IOException, NotFoundException, CannotCompileException {
199 201
200 // reconstruct inner classes 202 // reconstruct inner classes
201 new InnerClassWriter(this.jarIndex, this.deobfuscatingTranslator).write(c); 203 new InnerClassWriter(this.jarIndex, this.deobfuscatingTranslator).write(c);
202 204
203 // re-get the javassist handle since we changed class names 205 // re-get the javassist handle since we changed class names
204 ClassEntry obfClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); 206 ClassEntry obfClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
205 String javaClassReconstructedName = Descriptor.toJavaName(obfClassEntry.getName()); 207 String javaClassReconstructedName = Descriptor.toJavaName(obfClassEntry.getName());
206 ClassPool classPool = new ClassPool(); 208 ClassPool classPool = new ClassPool();
207 classPool.insertClassPath(new ByteArrayClassPath(javaClassReconstructedName, c.toBytecode())); 209 classPool.insertClassPath(new ByteArrayClassPath(javaClassReconstructedName, c.toBytecode()));
208 c = classPool.get(javaClassReconstructedName); 210 c = classPool.get(javaClassReconstructedName);
209 211
210 // check that the file is correct after inner class reconstruction (ie cause Javassist to fail fast if something is wrong) 212 // check that the file is correct after inner class reconstruction (ie cause Javassist to fail fast if something is wrong)
211 assertClassName(c, obfClassEntry); 213 assertClassName(c, obfClassEntry);
212 214
213 // do all kinds of deobfuscating transformations on the class 215 // do all kinds of deobfuscating transformations on the class
214 new BridgeMarker(this.jarIndex).markBridges(c); 216 new BridgeMarker(this.jarIndex).markBridges(c);
215 new MethodParameterWriter(this.deobfuscatingTranslator).writeMethodArguments(c); 217 new MethodParameterWriter(this.deobfuscatingTranslator).writeMethodArguments(c);
216 new LocalVariableRenamer(this.deobfuscatingTranslator).rename(c); 218 new LocalVariableRenamer(this.deobfuscatingTranslator).rename(c);
217 new ClassTranslator(this.deobfuscatingTranslator).translate(c); 219 new ClassTranslator(this.deobfuscatingTranslator).translate(c);
218 220
219 return c; 221 return c;
220 } 222 }
221 223
222 private void assertClassName(CtClass c, ClassEntry obfClassEntry) { 224 private void assertClassName(CtClass c, ClassEntry obfClassEntry) {
223 String name1 = Descriptor.toJvmName(c.getName()); 225 String name1 = Descriptor.toJvmName(c.getName());
224 assert (name1.equals(obfClassEntry.getName())) : String.format("Looking for %s, instead found %s", obfClassEntry.getName(), name1); 226 assert (name1.equals(obfClassEntry.getName())) : String.format("Looking for %s, instead found %s", obfClassEntry.getName(), name1);
225 227
226 String name2 = Descriptor.toJvmName(c.getClassFile().getName()); 228 String name2 = Descriptor.toJvmName(c.getClassFile().getName());
227 assert (name2.equals(obfClassEntry.getName())) : String.format("Looking for %s, instead found %s", obfClassEntry.getName(), name2); 229 assert (name2.equals(obfClassEntry.getName())) : String.format("Looking for %s, instead found %s", obfClassEntry.getName(), name2);
228 } 230 }
229} 231}
diff --git a/src/main/java/cuchaz/enigma/analysis/Access.java b/src/main/java/cuchaz/enigma/analysis/Access.java
index b8a7b2c8..547d85ef 100644
--- a/src/main/java/cuchaz/enigma/analysis/Access.java
+++ b/src/main/java/cuchaz/enigma/analysis/Access.java
@@ -8,40 +8,41 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.analysis;
12 11
13import java.lang.reflect.Modifier; 12package cuchaz.enigma.analysis;
14 13
15import javassist.CtBehavior; 14import javassist.CtBehavior;
16import javassist.CtField; 15import javassist.CtField;
17 16
17import java.lang.reflect.Modifier;
18
18public enum Access { 19public enum Access {
19 20
20 PUBLIC, PROTECTED, PACKAGE, PRIVATE; 21 PUBLIC, PROTECTED, PACKAGE, PRIVATE;
21 22
22 public static Access get(CtBehavior behavior) { 23 public static Access get(CtBehavior behavior) {
23 return get(behavior.getModifiers()); 24 return get(behavior.getModifiers());
24 } 25 }
25 26
26 public static Access get(CtField field) { 27 public static Access get(CtField field) {
27 return get(field.getModifiers()); 28 return get(field.getModifiers());
28 } 29 }
29 30
30 public static Access get(int modifiers) { 31 public static Access get(int modifiers) {
31 boolean isPublic = Modifier.isPublic(modifiers); 32 boolean isPublic = Modifier.isPublic(modifiers);
32 boolean isProtected = Modifier.isProtected(modifiers); 33 boolean isProtected = Modifier.isProtected(modifiers);
33 boolean isPrivate = Modifier.isPrivate(modifiers); 34 boolean isPrivate = Modifier.isPrivate(modifiers);
34 35
35 if (isPublic && !isProtected && !isPrivate) { 36 if (isPublic && !isProtected && !isPrivate) {
36 return PUBLIC; 37 return PUBLIC;
37 } else if (!isPublic && isProtected && !isPrivate) { 38 } else if (!isPublic && isProtected && !isPrivate) {
38 return PROTECTED; 39 return PROTECTED;
39 } else if (!isPublic && !isProtected && isPrivate) { 40 } else if (!isPublic && !isProtected && isPrivate) {
40 return PRIVATE; 41 return PRIVATE;
41 } else if (!isPublic && !isProtected && !isPrivate) { 42 } else if (!isPublic && !isProtected && !isPrivate) {
42 return PACKAGE; 43 return PACKAGE;
43 } 44 }
44 // assume public by default 45 // assume public by default
45 return PUBLIC; 46 return PUBLIC;
46 } 47 }
47} 48}
diff --git a/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java
index 52d5b311..6556b2cf 100644
--- a/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.Sets; 14import com.google.common.collect.Sets;
@@ -20,85 +21,73 @@ import javax.swing.tree.TreeNode;
20import java.util.Set; 21import java.util.Set;
21 22
22public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode 23public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode
23 implements ReferenceTreeNode<BehaviorEntry, BehaviorEntry> 24 implements ReferenceTreeNode<BehaviorEntry, BehaviorEntry> {
24{
25 25
26 private Translator deobfuscatingTranslator; 26 private Translator deobfuscatingTranslator;
27 private BehaviorEntry entry; 27 private BehaviorEntry entry;
28 private EntryReference<BehaviorEntry, BehaviorEntry> reference; 28 private EntryReference<BehaviorEntry, BehaviorEntry> reference;
29 private Access access; 29 private Access access;
30 30
31 public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, BehaviorEntry entry) 31 public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, BehaviorEntry entry) {
32 { 32 this.deobfuscatingTranslator = deobfuscatingTranslator;
33 this.deobfuscatingTranslator = deobfuscatingTranslator; 33 this.entry = entry;
34 this.entry = entry; 34 this.reference = null;
35 this.reference = null; 35 }
36 }
37 36
38 public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, 37 public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator,
39 EntryReference<BehaviorEntry, BehaviorEntry> reference, Access access) 38 EntryReference<BehaviorEntry, BehaviorEntry> reference, Access access) {
40 { 39 this.deobfuscatingTranslator = deobfuscatingTranslator;
41 this.deobfuscatingTranslator = deobfuscatingTranslator; 40 this.entry = reference.entry;
42 this.entry = reference.entry; 41 this.reference = reference;
43 this.reference = reference; 42 this.access = access;
44 this.access = access; 43 }
45 }
46 44
47 @Override public BehaviorEntry getEntry() 45 @Override
48 { 46 public BehaviorEntry getEntry() {
49 return this.entry; 47 return this.entry;
50 } 48 }
51 49
52 @Override public EntryReference<BehaviorEntry, BehaviorEntry> getReference() 50 @Override
53 { 51 public EntryReference<BehaviorEntry, BehaviorEntry> getReference() {
54 return this.reference; 52 return this.reference;
55 } 53 }
56 54
57 @Override public String toString() 55 @Override
58 { 56 public String toString() {
59 if (this.reference != null) 57 if (this.reference != null) {
60 { 58 return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context),
61 return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context), 59 this.access);
62 this.access); 60 }
63 } 61 return this.deobfuscatingTranslator.translateEntry(this.entry).toString();
64 return this.deobfuscatingTranslator.translateEntry(this.entry).toString(); 62 }
65 }
66 63
67 public void load(JarIndex index, boolean recurse) 64 public void load(JarIndex index, boolean recurse) {
68 { 65 // get all the child nodes
69 // get all the child nodes 66 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : index.getBehaviorReferences(this.entry)) {
70 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : index.getBehaviorReferences(this.entry)) 67 add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry)));
71 { 68 }
72 add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry)));
73 }
74 69
75 if (recurse && this.children != null) 70 if (recurse && this.children != null) {
76 { 71 for (Object child : this.children) {
77 for (Object child : this.children) 72 if (child instanceof BehaviorReferenceTreeNode) {
78 { 73 BehaviorReferenceTreeNode node = (BehaviorReferenceTreeNode) child;
79 if (child instanceof BehaviorReferenceTreeNode)
80 {
81 BehaviorReferenceTreeNode node = (BehaviorReferenceTreeNode) child;
82 74
83 // don't recurse into ancestor 75 // don't recurse into ancestor
84 Set<Entry> ancestors = Sets.newHashSet(); 76 Set<Entry> ancestors = Sets.newHashSet();
85 TreeNode n = node; 77 TreeNode n = node;
86 while (n.getParent() != null) 78 while (n.getParent() != null) {
87 { 79 n = n.getParent();
88 n = n.getParent(); 80 if (n instanceof BehaviorReferenceTreeNode) {
89 if (n instanceof BehaviorReferenceTreeNode) 81 ancestors.add(((BehaviorReferenceTreeNode) n).getEntry());
90 { 82 }
91 ancestors.add(((BehaviorReferenceTreeNode) n).getEntry()); 83 }
92 } 84 if (ancestors.contains(node.getEntry())) {
93 } 85 continue;
94 if (ancestors.contains(node.getEntry())) 86 }
95 {
96 continue;
97 }
98 87
99 node.load(index, true); 88 node.load(index, true);
100 } 89 }
101 } 90 }
102 } 91 }
103 } 92 }
104} 93}
diff --git a/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java b/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java
index 0f4be7d9..81e750c1 100644
--- a/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java
+++ b/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import cuchaz.enigma.mapping.EntryFactory; 14import cuchaz.enigma.mapping.EntryFactory;
@@ -18,26 +19,26 @@ import javassist.bytecode.AccessFlag;
18 19
19public class BridgeMarker { 20public class BridgeMarker {
20 21
21 private JarIndex jarIndex; 22 private JarIndex jarIndex;
22 23
23 public BridgeMarker(JarIndex jarIndex) { 24 public BridgeMarker(JarIndex jarIndex) {
24 this.jarIndex = jarIndex; 25 this.jarIndex = jarIndex;
25 } 26 }
26 27
27 public void markBridges(CtClass c) { 28 public void markBridges(CtClass c) {
28 29
29 for (CtMethod method : c.getDeclaredMethods()) { 30 for (CtMethod method : c.getDeclaredMethods()) {
30 MethodEntry methodEntry = EntryFactory.getMethodEntry(method); 31 MethodEntry methodEntry = EntryFactory.getMethodEntry(method);
31 32
32 // is this a bridge method? 33 // is this a bridge method?
33 MethodEntry bridgedMethodEntry = this.jarIndex.getBridgedMethod(methodEntry); 34 MethodEntry bridgedMethodEntry = this.jarIndex.getBridgedMethod(methodEntry);
34 if (bridgedMethodEntry != null) { 35 if (bridgedMethodEntry != null) {
35 36
36 // it's a bridge method! add the bridge flag 37 // it's a bridge method! add the bridge flag
37 int flags = method.getMethodInfo().getAccessFlags(); 38 int flags = method.getMethodInfo().getAccessFlags();
38 flags |= AccessFlag.BRIDGE; 39 flags |= AccessFlag.BRIDGE;
39 method.getMethodInfo().setAccessFlags(flags); 40 method.getMethodInfo().setAccessFlags(flags);
40 } 41 }
41 } 42 }
42 } 43 }
43} 44}
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
index 70ece243..f2fb2f8d 100644
--- a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
@@ -8,69 +8,68 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
14
15import java.util.List;
16
17import javax.swing.tree.DefaultMutableTreeNode;
18
19import cuchaz.enigma.mapping.ClassEntry; 15import cuchaz.enigma.mapping.ClassEntry;
20import cuchaz.enigma.mapping.MethodEntry; 16import cuchaz.enigma.mapping.MethodEntry;
21import cuchaz.enigma.mapping.Translator; 17import cuchaz.enigma.mapping.Translator;
22 18
19import javax.swing.tree.DefaultMutableTreeNode;
20import java.util.List;
21
23public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { 22public class ClassImplementationsTreeNode extends DefaultMutableTreeNode {
24 23
25 private Translator deobfuscatingTranslator; 24 private Translator deobfuscatingTranslator;
26 private ClassEntry entry; 25 private ClassEntry entry;
27 26
28 public ClassImplementationsTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) { 27 public ClassImplementationsTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) {
29 this.deobfuscatingTranslator = deobfuscatingTranslator; 28 this.deobfuscatingTranslator = deobfuscatingTranslator;
30 this.entry = entry; 29 this.entry = entry;
31 } 30 }
32 31
33 public ClassEntry getClassEntry() { 32 public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) {
34 return this.entry; 33 // is this the node?
35 } 34 if (node.entry.equals(entry.getClassEntry())) {
35 return node;
36 }
36 37
37 public String getDeobfClassName() { 38 // recurse
38 return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); 39 for (int i = 0; i < node.getChildCount(); i++) {
39 } 40 ClassImplementationsTreeNode foundNode = findNode((ClassImplementationsTreeNode) node.getChildAt(i), entry);
41 if (foundNode != null) {
42 return foundNode;
43 }
44 }
45 return null;
46 }
40 47
41 @Override 48 public ClassEntry getClassEntry() {
42 public String toString() { 49 return this.entry;
43 String className = getDeobfClassName(); 50 }
44 if (className == null) {
45 className = this.entry.getClassName();
46 }
47 return className;
48 }
49 51
50 public void load(JarIndex index) { 52 public String getDeobfClassName() {
51 // get all method implementations 53 return this.deobfuscatingTranslator.translateClass(this.entry.getClassName());
52 List<ClassImplementationsTreeNode> nodes = Lists.newArrayList(); 54 }
53 for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) {
54 nodes.add(new ClassImplementationsTreeNode(this.deobfuscatingTranslator, new ClassEntry(implementingClassName)));
55 }
56 55
57 // add them to this node 56 @Override
58 nodes.forEach(this::add); 57 public String toString() {
59 } 58 String className = getDeobfClassName();
59 if (className == null) {
60 className = this.entry.getClassName();
61 }
62 return className;
63 }
60 64
61 public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) { 65 public void load(JarIndex index) {
62 // is this the node? 66 // get all method implementations
63 if (node.entry.equals(entry.getClassEntry())) { 67 List<ClassImplementationsTreeNode> nodes = Lists.newArrayList();
64 return node; 68 for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) {
65 } 69 nodes.add(new ClassImplementationsTreeNode(this.deobfuscatingTranslator, new ClassEntry(implementingClassName)));
70 }
66 71
67 // recurse 72 // add them to this node
68 for (int i = 0; i < node.getChildCount(); i++) { 73 nodes.forEach(this::add);
69 ClassImplementationsTreeNode foundNode = findNode((ClassImplementationsTreeNode) node.getChildAt(i), entry); 74 }
70 if (foundNode != null) {
71 return foundNode;
72 }
73 }
74 return null;
75 }
76} 75}
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
index 8a60fc72..24e7cb0b 100644
--- a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
@@ -8,74 +8,73 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
14
15import java.util.List;
16
17import javax.swing.tree.DefaultMutableTreeNode;
18
19import cuchaz.enigma.mapping.ClassEntry; 15import cuchaz.enigma.mapping.ClassEntry;
20import cuchaz.enigma.mapping.Translator; 16import cuchaz.enigma.mapping.Translator;
21 17
18import javax.swing.tree.DefaultMutableTreeNode;
19import java.util.List;
20
22public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { 21public class ClassInheritanceTreeNode extends DefaultMutableTreeNode {
23 22
24 private Translator deobfuscatingTranslator; 23 private Translator deobfuscatingTranslator;
25 private String obfClassName; 24 private String obfClassName;
26 25
27 public ClassInheritanceTreeNode(Translator deobfuscatingTranslator, String obfClassName) { 26 public ClassInheritanceTreeNode(Translator deobfuscatingTranslator, String obfClassName) {
28 this.deobfuscatingTranslator = deobfuscatingTranslator; 27 this.deobfuscatingTranslator = deobfuscatingTranslator;
29 this.obfClassName = obfClassName; 28 this.obfClassName = obfClassName;
30 } 29 }
31 30
32 public String getObfClassName() { 31 public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) {
33 return this.obfClassName; 32 // is this the node?
34 } 33 if (node.getObfClassName().equals(entry.getName())) {
34 return node;
35 }
35 36
36 public String getDeobfClassName() { 37 // recurse
37 return this.deobfuscatingTranslator.translateClass(this.obfClassName); 38 for (int i = 0; i < node.getChildCount(); i++) {
38 } 39 ClassInheritanceTreeNode foundNode = findNode((ClassInheritanceTreeNode) node.getChildAt(i), entry);
40 if (foundNode != null) {
41 return foundNode;
42 }
43 }
44 return null;
45 }
39 46
40 @Override 47 public String getObfClassName() {
41 public String toString() { 48 return this.obfClassName;
42 String deobfClassName = getDeobfClassName(); 49 }
43 if (deobfClassName != null) {
44 return deobfClassName;
45 }
46 return this.obfClassName;
47 }
48 50
49 public void load(TranslationIndex ancestries, boolean recurse) { 51 public String getDeobfClassName() {
50 // get all the child nodes 52 return this.deobfuscatingTranslator.translateClass(this.obfClassName);
51 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList(); 53 }
52 for (ClassEntry subclassEntry : ancestries.getSubclass(new ClassEntry(this.obfClassName))) {
53 nodes.add(new ClassInheritanceTreeNode(this.deobfuscatingTranslator, subclassEntry.getName()));
54 }
55 54
56 // add them to this node 55 @Override
57 nodes.forEach(this::add); 56 public String toString() {
57 String deobfClassName = getDeobfClassName();
58 if (deobfClassName != null) {
59 return deobfClassName;
60 }
61 return this.obfClassName;
62 }
58 63
59 if (recurse) { 64 public void load(TranslationIndex ancestries, boolean recurse) {
60 for (ClassInheritanceTreeNode node : nodes) { 65 // get all the child nodes
61 node.load(ancestries, true); 66 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList();
62 } 67 for (ClassEntry subclassEntry : ancestries.getSubclass(new ClassEntry(this.obfClassName))) {
63 } 68 nodes.add(new ClassInheritanceTreeNode(this.deobfuscatingTranslator, subclassEntry.getName()));
64 } 69 }
65 70
66 public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) { 71 // add them to this node
67 // is this the node? 72 nodes.forEach(this::add);
68 if (node.getObfClassName().equals(entry.getName())) {
69 return node;
70 }
71 73
72 // recurse 74 if (recurse) {
73 for (int i = 0; i < node.getChildCount(); i++) { 75 for (ClassInheritanceTreeNode node : nodes) {
74 ClassInheritanceTreeNode foundNode = findNode((ClassInheritanceTreeNode) node.getChildAt(i), entry); 76 node.load(ancestries, true);
75 if (foundNode != null) { 77 }
76 return foundNode; 78 }
77 } 79 }
78 }
79 return null;
80 }
81} 80}
diff --git a/src/main/java/cuchaz/enigma/analysis/EntryReference.java b/src/main/java/cuchaz/enigma/analysis/EntryReference.java
index ad4baf81..3761fca8 100644
--- a/src/main/java/cuchaz/enigma/analysis/EntryReference.java
+++ b/src/main/java/cuchaz/enigma/analysis/EntryReference.java
@@ -8,116 +8,117 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.analysis;
12 11
13import java.util.Arrays; 12package cuchaz.enigma.analysis;
14import java.util.List;
15 13
16import cuchaz.enigma.mapping.ClassEntry; 14import cuchaz.enigma.mapping.ClassEntry;
17import cuchaz.enigma.mapping.ConstructorEntry; 15import cuchaz.enigma.mapping.ConstructorEntry;
18import cuchaz.enigma.mapping.Entry; 16import cuchaz.enigma.mapping.Entry;
19import cuchaz.enigma.utils.Utils; 17import cuchaz.enigma.utils.Utils;
20 18
19import java.util.Arrays;
20import java.util.List;
21
21public class EntryReference<E extends Entry, C extends Entry> { 22public class EntryReference<E extends Entry, C extends Entry> {
22 23
23 private static final List<String> ConstructorNonNames = Arrays.asList("this", "super", "static"); 24 private static final List<String> ConstructorNonNames = Arrays.asList("this", "super", "static");
24 public E entry; 25 public E entry;
25 public C context; 26 public C context;
26 27
27 private boolean sourceName; 28 private boolean sourceName;
28 29
29 public EntryReference(E entry, String sourceName) { 30 public EntryReference(E entry, String sourceName) {
30 this(entry, sourceName, null); 31 this(entry, sourceName, null);
31 } 32 }
32 33
33 public EntryReference(E entry, String sourceName, C context) { 34 public EntryReference(E entry, String sourceName, C context) {
34 if (entry == null) { 35 if (entry == null) {
35 throw new IllegalArgumentException("Entry cannot be null!"); 36 throw new IllegalArgumentException("Entry cannot be null!");
36 } 37 }
37 38
38 this.entry = entry; 39 this.entry = entry;
39 this.context = context; 40 this.context = context;
40 41
41 this.sourceName = sourceName != null && sourceName.length() > 0; 42 this.sourceName = sourceName != null && !sourceName.isEmpty();
42 if (entry instanceof ConstructorEntry && ConstructorNonNames.contains(sourceName)) { 43 if (entry instanceof ConstructorEntry && ConstructorNonNames.contains(sourceName)) {
43 this.sourceName = false; 44 this.sourceName = false;
44 } 45 }
45 } 46 }
46 47
47 public EntryReference(E entry, C context, EntryReference<E, C> other) { 48 public EntryReference(E entry, C context, EntryReference<E, C> other) {
48 this.entry = entry; 49 this.entry = entry;
49 this.context = context; 50 this.context = context;
50 this.sourceName = other.sourceName; 51 this.sourceName = other.sourceName;
51 } 52 }
52 53
53 public ClassEntry getLocationClassEntry() { 54 public ClassEntry getLocationClassEntry() {
54 if (context != null) { 55 if (context != null) {
55 return context.getClassEntry(); 56 return context.getClassEntry();
56 } 57 }
57 return entry.getClassEntry(); 58 return entry.getClassEntry();
58 } 59 }
59 60
60 public boolean isNamed() { 61 public boolean isNamed() {
61 return this.sourceName; 62 return this.sourceName;
62 } 63 }
63 64
64 public Entry getNameableEntry() { 65 public Entry getNameableEntry() {
65 if (entry instanceof ConstructorEntry) { 66 if (entry instanceof ConstructorEntry) {
66 // renaming a constructor really means renaming the class 67 // renaming a constructor really means renaming the class
67 return entry.getClassEntry(); 68 return entry.getClassEntry();
68 } 69 }
69 return entry; 70 return entry;
70 } 71 }
71 72
72 public String getNamableName() { 73 public String getNamableName() {
73 if (getNameableEntry() instanceof ClassEntry) { 74 if (getNameableEntry() instanceof ClassEntry) {
74 ClassEntry classEntry = (ClassEntry) getNameableEntry(); 75 ClassEntry classEntry = (ClassEntry) getNameableEntry();
75 if (classEntry.isInnerClass()) { 76 if (classEntry.isInnerClass()) {
76 // make sure we only rename the inner class name 77 // make sure we only rename the inner class name
77 return classEntry.getInnermostClassName(); 78 return classEntry.getInnermostClassName();
78 } 79 }
79 } 80 }
80 81
81 return getNameableEntry().getName(); 82 return getNameableEntry().getName();
82 } 83 }
83 84
84 @Override 85 @Override
85 public int hashCode() { 86 public int hashCode() {
86 if (context != null) { 87 if (context != null) {
87 return Utils.combineHashesOrdered(entry.hashCode(), context.hashCode()); 88 return Utils.combineHashesOrdered(entry.hashCode(), context.hashCode());
88 } 89 }
89 return entry.hashCode(); 90 return entry.hashCode();
90 } 91 }
91 92
92 @Override 93 @Override
93 public boolean equals(Object other) { 94 public boolean equals(Object other) {
94 return other instanceof EntryReference && equals((EntryReference<?, ?>) other); 95 return other instanceof EntryReference && equals((EntryReference<?, ?>) other);
95 } 96 }
96 97
97 public boolean equals(EntryReference<?, ?> other) { 98 public boolean equals(EntryReference<?, ?> other) {
98 // check entry first 99 // check entry first
99 boolean isEntrySame = entry.equals(other.entry); 100 boolean isEntrySame = entry.equals(other.entry);
100 if (!isEntrySame) { 101 if (!isEntrySame) {
101 return false; 102 return false;
102 } 103 }
103 104
104 // check caller 105 // check caller
105 if (context == null && other.context == null) { 106 if (context == null && other.context == null) {
106 return true; 107 return true;
107 } else if (context != null && other.context != null) { 108 } else if (context != null && other.context != null) {
108 return context.equals(other.context); 109 return context.equals(other.context);
109 } 110 }
110 return false; 111 return false;
111 } 112 }
112 113
113 @Override 114 @Override
114 public String toString() { 115 public String toString() {
115 StringBuilder buf = new StringBuilder(); 116 StringBuilder buf = new StringBuilder();
116 buf.append(entry); 117 buf.append(entry);
117 if (context != null) { 118 if (context != null) {
118 buf.append(" called from "); 119 buf.append(" called from ");
119 buf.append(context); 120 buf.append(context);
120 } 121 }
121 return buf.toString(); 122 return buf.toString();
122 } 123 }
123} 124}
diff --git a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java b/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
index 7233fcf9..75806c33 100644
--- a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
+++ b/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
@@ -8,140 +8,140 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
14import com.google.common.collect.Multimap; 15import com.google.common.collect.Multimap;
15import com.google.common.collect.Sets; 16import com.google.common.collect.Sets;
17import cuchaz.enigma.mapping.*;
16 18
17import java.util.AbstractMap; 19import java.util.AbstractMap;
18import java.util.List; 20import java.util.List;
19import java.util.Map; 21import java.util.Map;
20import java.util.Set; 22import java.util.Set;
21 23
22import cuchaz.enigma.mapping.*;
23
24public class EntryRenamer { 24public class EntryRenamer {
25 25
26 public static <T> void renameClassesInSet(Map<String, String> renames, Set<T> set) { 26 public static <T> void renameClassesInSet(Map<String, String> renames, Set<T> set) {
27 List<T> entries = Lists.newArrayList(); 27 List<T> entries = Lists.newArrayList();
28 for (T val : set) { 28 for (T val : set) {
29 entries.add(renameClassesInThing(renames, val)); 29 entries.add(renameClassesInThing(renames, val));
30 } 30 }
31 set.clear(); 31 set.clear();
32 set.addAll(entries); 32 set.addAll(entries);
33 } 33 }
34 34
35 public static <Key, Val> void renameClassesInMap(Map<String, String> renames, Map<Key, Val> map) { 35 public static <Key, Val> void renameClassesInMap(Map<String, String> renames, Map<Key, Val> map) {
36 // for each key/value pair... 36 // for each key/value pair...
37 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet(); 37 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet();
38 for (Map.Entry<Key, Val> entry : map.entrySet()) { 38 for (Map.Entry<Key, Val> entry : map.entrySet()) {
39 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameClassesInThing(renames, entry.getKey()), renameClassesInThing(renames, entry.getValue()))); 39 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameClassesInThing(renames, entry.getKey()), renameClassesInThing(renames, entry.getValue())));
40 } 40 }
41 map.clear(); 41 map.clear();
42 for (Map.Entry<Key, Val> entry : entriesToAdd) { 42 for (Map.Entry<Key, Val> entry : entriesToAdd) {
43 map.put(entry.getKey(), entry.getValue()); 43 map.put(entry.getKey(), entry.getValue());
44 } 44 }
45 } 45 }
46 46
47 public static <Key, Val> void renameClassesInMultimap(Map<String, String> renames, Multimap<Key, Val> map) { 47 public static <Key, Val> void renameClassesInMultimap(Map<String, String> renames, Multimap<Key, Val> map) {
48 // for each key/value pair... 48 // for each key/value pair...
49 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet(); 49 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet();
50 for (Map.Entry<Key, Val> entry : map.entries()) { 50 for (Map.Entry<Key, Val> entry : map.entries()) {
51 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameClassesInThing(renames, entry.getKey()), renameClassesInThing(renames, entry.getValue()))); 51 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameClassesInThing(renames, entry.getKey()), renameClassesInThing(renames, entry.getValue())));
52 } 52 }
53 map.clear(); 53 map.clear();
54 for (Map.Entry<Key, Val> entry : entriesToAdd) { 54 for (Map.Entry<Key, Val> entry : entriesToAdd) {
55 map.put(entry.getKey(), entry.getValue()); 55 map.put(entry.getKey(), entry.getValue());
56 } 56 }
57 } 57 }
58 58
59 public static <Key, Val> void renameMethodsInMultimap(Map<MethodEntry, MethodEntry> renames, Multimap<Key, Val> map) { 59 public static <Key, Val> void renameMethodsInMultimap(Map<MethodEntry, MethodEntry> renames, Multimap<Key, Val> map) {
60 // for each key/value pair... 60 // for each key/value pair...
61 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet(); 61 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet();
62 for (Map.Entry<Key, Val> entry : map.entries()) { 62 for (Map.Entry<Key, Val> entry : map.entries()) {
63 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameMethodsInThing(renames, entry.getKey()), renameMethodsInThing(renames, entry.getValue()))); 63 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameMethodsInThing(renames, entry.getKey()), renameMethodsInThing(renames, entry.getValue())));
64 } 64 }
65 map.clear(); 65 map.clear();
66 for (Map.Entry<Key, Val> entry : entriesToAdd) { 66 for (Map.Entry<Key, Val> entry : entriesToAdd) {
67 map.put(entry.getKey(), entry.getValue()); 67 map.put(entry.getKey(), entry.getValue());
68 } 68 }
69 } 69 }
70 70
71 public static <Key, Val> void renameMethodsInMap(Map<MethodEntry, MethodEntry> renames, Map<Key, Val> map) { 71 public static <Key, Val> void renameMethodsInMap(Map<MethodEntry, MethodEntry> renames, Map<Key, Val> map) {
72 // for each key/value pair... 72 // for each key/value pair...
73 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet(); 73 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet();
74 for (Map.Entry<Key, Val> entry : map.entrySet()) { 74 for (Map.Entry<Key, Val> entry : map.entrySet()) {
75 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameMethodsInThing(renames, entry.getKey()), renameMethodsInThing(renames, entry.getValue()))); 75 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameMethodsInThing(renames, entry.getKey()), renameMethodsInThing(renames, entry.getValue())));
76 } 76 }
77 map.clear(); 77 map.clear();
78 for (Map.Entry<Key, Val> entry : entriesToAdd) { 78 for (Map.Entry<Key, Val> entry : entriesToAdd) {
79 map.put(entry.getKey(), entry.getValue()); 79 map.put(entry.getKey(), entry.getValue());
80 } 80 }
81 } 81 }
82 82
83 @SuppressWarnings("unchecked") 83 @SuppressWarnings("unchecked")
84 public static <T> T renameMethodsInThing(Map<MethodEntry,MethodEntry> renames, T thing) { 84 public static <T> T renameMethodsInThing(Map<MethodEntry, MethodEntry> renames, T thing) {
85 if (thing instanceof MethodEntry) { 85 if (thing instanceof MethodEntry) {
86 MethodEntry methodEntry = (MethodEntry)thing; 86 MethodEntry methodEntry = (MethodEntry) thing;
87 MethodEntry newMethodEntry = renames.get(methodEntry); 87 MethodEntry newMethodEntry = renames.get(methodEntry);
88 if (newMethodEntry != null) { 88 if (newMethodEntry != null) {
89 return (T)new MethodEntry( 89 return (T) new MethodEntry(
90 methodEntry.getClassEntry(), 90 methodEntry.getClassEntry(),
91 newMethodEntry.getName(), 91 newMethodEntry.getName(),
92 methodEntry.getSignature() 92 methodEntry.getSignature()
93 ); 93 );
94 } 94 }
95 return thing; 95 return thing;
96 } else if (thing instanceof ArgumentEntry) { 96 } else if (thing instanceof ArgumentEntry) {
97 ArgumentEntry argumentEntry = (ArgumentEntry)thing; 97 ArgumentEntry argumentEntry = (ArgumentEntry) thing;
98 return (T)new ArgumentEntry( 98 return (T) new ArgumentEntry(
99 renameMethodsInThing(renames, argumentEntry.getBehaviorEntry()), 99 renameMethodsInThing(renames, argumentEntry.getBehaviorEntry()),
100 argumentEntry.getIndex(), 100 argumentEntry.getIndex(),
101 argumentEntry.getName() 101 argumentEntry.getName()
102 ); 102 );
103 } else if (thing instanceof EntryReference) { 103 } else if (thing instanceof EntryReference) {
104 EntryReference<Entry,Entry> reference = (EntryReference<Entry,Entry>)thing; 104 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing;
105 reference.entry = renameMethodsInThing(renames, reference.entry); 105 reference.entry = renameMethodsInThing(renames, reference.entry);
106 reference.context = renameMethodsInThing(renames, reference.context); 106 reference.context = renameMethodsInThing(renames, reference.context);
107 return thing; 107 return thing;
108 } 108 }
109 return thing; 109 return thing;
110 } 110 }
111 111
112 @SuppressWarnings("unchecked") 112 @SuppressWarnings("unchecked")
113 public static <T> T renameClassesInThing(final Map<String, String> renames, T thing) { 113 public static <T> T renameClassesInThing(final Map<String, String> renames, T thing) {
114 if (thing instanceof String) { 114 if (thing instanceof String) {
115 String stringEntry = (String) thing; 115 String stringEntry = (String) thing;
116 if (renames.containsKey(stringEntry)) { 116 if (renames.containsKey(stringEntry)) {
117 return (T) renames.get(stringEntry); 117 return (T) renames.get(stringEntry);
118 } 118 }
119 } else if (thing instanceof ClassEntry) { 119 } else if (thing instanceof ClassEntry) {
120 ClassEntry classEntry = (ClassEntry) thing; 120 ClassEntry classEntry = (ClassEntry) thing;
121 return (T) new ClassEntry(renameClassesInThing(renames, classEntry.getClassName())); 121 return (T) new ClassEntry(renameClassesInThing(renames, classEntry.getClassName()));
122 } else if (thing instanceof FieldEntry) { 122 } else if (thing instanceof FieldEntry) {
123 FieldEntry fieldEntry = (FieldEntry) thing; 123 FieldEntry fieldEntry = (FieldEntry) thing;
124 return (T) new FieldEntry(renameClassesInThing(renames, fieldEntry.getClassEntry()), fieldEntry.getName(), renameClassesInThing(renames, fieldEntry.getType())); 124 return (T) new FieldEntry(renameClassesInThing(renames, fieldEntry.getClassEntry()), fieldEntry.getName(), renameClassesInThing(renames, fieldEntry.getType()));
125 } else if (thing instanceof ConstructorEntry) { 125 } else if (thing instanceof ConstructorEntry) {
126 ConstructorEntry constructorEntry = (ConstructorEntry) thing; 126 ConstructorEntry constructorEntry = (ConstructorEntry) thing;
127 return (T) new ConstructorEntry(renameClassesInThing(renames, constructorEntry.getClassEntry()), renameClassesInThing(renames, constructorEntry.getSignature())); 127 return (T) new ConstructorEntry(renameClassesInThing(renames, constructorEntry.getClassEntry()), renameClassesInThing(renames, constructorEntry.getSignature()));
128 } else if (thing instanceof MethodEntry) { 128 } else if (thing instanceof MethodEntry) {
129 MethodEntry methodEntry = (MethodEntry) thing; 129 MethodEntry methodEntry = (MethodEntry) thing;
130 return (T) new MethodEntry(renameClassesInThing(renames, methodEntry.getClassEntry()), methodEntry.getName(), renameClassesInThing(renames, methodEntry.getSignature())); 130 return (T) new MethodEntry(renameClassesInThing(renames, methodEntry.getClassEntry()), methodEntry.getName(), renameClassesInThing(renames, methodEntry.getSignature()));
131 } else if (thing instanceof ArgumentEntry) { 131 } else if (thing instanceof ArgumentEntry) {
132 ArgumentEntry argumentEntry = (ArgumentEntry) thing; 132 ArgumentEntry argumentEntry = (ArgumentEntry) thing;
133 return (T) new ArgumentEntry(renameClassesInThing(renames, argumentEntry.getBehaviorEntry()), argumentEntry.getIndex(), argumentEntry.getName()); 133 return (T) new ArgumentEntry(renameClassesInThing(renames, argumentEntry.getBehaviorEntry()), argumentEntry.getIndex(), argumentEntry.getName());
134 } else if (thing instanceof EntryReference) { 134 } else if (thing instanceof EntryReference) {
135 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing; 135 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing;
136 reference.entry = renameClassesInThing(renames, reference.entry); 136 reference.entry = renameClassesInThing(renames, reference.entry);
137 reference.context = renameClassesInThing(renames, reference.context); 137 reference.context = renameClassesInThing(renames, reference.context);
138 return thing; 138 return thing;
139 } else if (thing instanceof Signature) { 139 } else if (thing instanceof Signature) {
140 return (T) new Signature((Signature) thing, className -> renameClassesInThing(renames, className)); 140 return (T) new Signature((Signature) thing, className -> renameClassesInThing(renames, className));
141 } else if (thing instanceof Type) { 141 } else if (thing instanceof Type) {
142 return (T) new Type((Type) thing, className -> renameClassesInThing(renames, className)); 142 return (T) new Type((Type) thing, className -> renameClassesInThing(renames, className));
143 } 143 }
144 144
145 return thing; 145 return thing;
146 } 146 }
147} 147}
diff --git a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
index 70cd0590..34d2eff1 100644
--- a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
@@ -8,72 +8,73 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.analysis;
12 11
13import javax.swing.tree.DefaultMutableTreeNode; 12package cuchaz.enigma.analysis;
14 13
15import cuchaz.enigma.mapping.BehaviorEntry; 14import cuchaz.enigma.mapping.BehaviorEntry;
16import cuchaz.enigma.mapping.FieldEntry; 15import cuchaz.enigma.mapping.FieldEntry;
17import cuchaz.enigma.mapping.Translator; 16import cuchaz.enigma.mapping.Translator;
18 17
18import javax.swing.tree.DefaultMutableTreeNode;
19
19public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, BehaviorEntry> { 20public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, BehaviorEntry> {
20 21
21 private Translator deobfuscatingTranslator; 22 private Translator deobfuscatingTranslator;
22 private FieldEntry entry; 23 private FieldEntry entry;
23 private EntryReference<FieldEntry, BehaviorEntry> reference; 24 private EntryReference<FieldEntry, BehaviorEntry> reference;
24 private Access access; 25 private Access access;
25 26
26 public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) { 27 public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) {
27 this.deobfuscatingTranslator = deobfuscatingTranslator; 28 this.deobfuscatingTranslator = deobfuscatingTranslator;
28 this.entry = entry; 29 this.entry = entry;
29 this.reference = null; 30 this.reference = null;
30 } 31 }
31 32
32 private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference<FieldEntry, BehaviorEntry> reference, Access access) { 33 private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference<FieldEntry, BehaviorEntry> reference, Access access) {
33 this.deobfuscatingTranslator = deobfuscatingTranslator; 34 this.deobfuscatingTranslator = deobfuscatingTranslator;
34 this.entry = reference.entry; 35 this.entry = reference.entry;
35 this.reference = reference; 36 this.reference = reference;
36 this.access = access; 37 this.access = access;
37 } 38 }
38 39
39 @Override 40 @Override
40 public FieldEntry getEntry() { 41 public FieldEntry getEntry() {
41 return this.entry; 42 return this.entry;
42 } 43 }
43 44
44 @Override 45 @Override
45 public EntryReference<FieldEntry, BehaviorEntry> getReference() { 46 public EntryReference<FieldEntry, BehaviorEntry> getReference() {
46 return this.reference; 47 return this.reference;
47 } 48 }
48 49
49 @Override 50 @Override
50 public String toString() { 51 public String toString() {
51 if (this.reference != null) { 52 if (this.reference != null) {
52 return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context), this.access); 53 return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context), this.access);
53 } 54 }
54 return this.deobfuscatingTranslator.translateEntry(this.entry).toString(); 55 return this.deobfuscatingTranslator.translateEntry(this.entry).toString();
55 } 56 }
56 57
57 public void load(JarIndex index, boolean recurse) { 58 public void load(JarIndex index, boolean recurse) {
58 // get all the child nodes 59 // get all the child nodes
59 if (this.reference == null) { 60 if (this.reference == null) {
60 for (EntryReference<FieldEntry, BehaviorEntry> reference : index.getFieldReferences(this.entry)) { 61 for (EntryReference<FieldEntry, BehaviorEntry> reference : index.getFieldReferences(this.entry)) {
61 add(new FieldReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry))); 62 add(new FieldReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry)));
62 } 63 }
63 } else { 64 } else {
64 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : index.getBehaviorReferences(this.reference.context)) { 65 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : index.getBehaviorReferences(this.reference.context)) {
65 add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.reference.context))); 66 add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.reference.context)));
66 } 67 }
67 } 68 }
68 69
69 if (recurse && children != null) { 70 if (recurse && children != null) {
70 for (Object node : children) { 71 for (Object node : children) {
71 if (node instanceof BehaviorReferenceTreeNode) { 72 if (node instanceof BehaviorReferenceTreeNode) {
72 ((BehaviorReferenceTreeNode) node).load(index, true); 73 ((BehaviorReferenceTreeNode) node).load(index, true);
73 } else if (node instanceof FieldReferenceTreeNode) { 74 } else if (node instanceof FieldReferenceTreeNode) {
74 ((FieldReferenceTreeNode) node).load(index, true); 75 ((FieldReferenceTreeNode) node).load(index, true);
75 } 76 }
76 } 77 }
77 } 78 }
78 } 79 }
79} 80}
diff --git a/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java b/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java
index 0d18105e..87d3797d 100644
--- a/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java
+++ b/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java
@@ -8,9 +8,17 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import cuchaz.enigma.Constants;
16import cuchaz.enigma.mapping.ClassEntry;
17import javassist.ByteArrayClassPath;
18import javassist.ClassPool;
19import javassist.CtClass;
20import javassist.NotFoundException;
21import javassist.bytecode.Descriptor;
14 22
15import java.io.ByteArrayOutputStream; 23import java.io.ByteArrayOutputStream;
16import java.io.IOException; 24import java.io.IOException;
@@ -21,103 +29,95 @@ import java.util.List;
21import java.util.jar.JarEntry; 29import java.util.jar.JarEntry;
22import java.util.jar.JarFile; 30import java.util.jar.JarFile;
23 31
24import cuchaz.enigma.Constants;
25import cuchaz.enigma.mapping.ClassEntry;
26import javassist.ByteArrayClassPath;
27import javassist.ClassPool;
28import javassist.CtClass;
29import javassist.NotFoundException;
30import javassist.bytecode.Descriptor;
31
32public class JarClassIterator implements Iterator<CtClass> { 32public class JarClassIterator implements Iterator<CtClass> {
33 33
34 private JarFile jar; 34 private JarFile jar;
35 private Iterator<JarEntry> iter; 35 private Iterator<JarEntry> iter;
36 36
37 public JarClassIterator(JarFile jar) { 37 public JarClassIterator(JarFile jar) {
38 this.jar = jar; 38 this.jar = jar;
39 39
40 // get the jar entries that correspond to classes 40 // get the jar entries that correspond to classes
41 List<JarEntry> classEntries = Lists.newArrayList(); 41 List<JarEntry> classEntries = Lists.newArrayList();
42 Enumeration<JarEntry> entries = this.jar.entries(); 42 Enumeration<JarEntry> entries = this.jar.entries();
43 while (entries.hasMoreElements()) { 43 while (entries.hasMoreElements()) {
44 JarEntry entry = entries.nextElement(); 44 JarEntry entry = entries.nextElement();
45 45
46 // is this a class file? 46 // is this a class file?
47 if (entry.getName().endsWith(".class")) { 47 if (entry.getName().endsWith(".class")) {
48 classEntries.add(entry); 48 classEntries.add(entry);
49 } 49 }
50 } 50 }
51 this.iter = classEntries.iterator(); 51 this.iter = classEntries.iterator();
52 } 52 }
53 53
54 @Override 54 public static List<ClassEntry> getClassEntries(JarFile jar) {
55 public boolean hasNext() { 55 List<ClassEntry> classEntries = Lists.newArrayList();
56 return this.iter.hasNext(); 56 Enumeration<JarEntry> entries = jar.entries();
57 } 57 while (entries.hasMoreElements()) {
58 58 JarEntry entry = entries.nextElement();
59 @Override 59
60 public CtClass next() { 60 // is this a class file?
61 JarEntry entry = this.iter.next(); 61 if (!entry.isDirectory() && entry.getName().endsWith(".class")) {
62 try { 62 classEntries.add(getClassEntry(entry));
63 return getClass(this.jar, entry); 63 }
64 } catch (IOException | NotFoundException ex) { 64 }
65 throw new Error("Unable to load class: " + entry.getName()); 65 return classEntries;
66 } 66 }
67 } 67
68 68 public static Iterable<CtClass> classes(final JarFile jar) {
69 @Override 69 return () -> new JarClassIterator(jar);
70 public void remove() { 70 }
71 throw new UnsupportedOperationException(); 71
72 } 72 private static CtClass getClass(JarFile jar, JarEntry entry) throws IOException, NotFoundException {
73 73 // read the class into a buffer
74 public static List<ClassEntry> getClassEntries(JarFile jar) { 74 ByteArrayOutputStream bos = new ByteArrayOutputStream();
75 List<ClassEntry> classEntries = Lists.newArrayList(); 75 byte[] buf = new byte[Constants.KiB];
76 Enumeration<JarEntry> entries = jar.entries(); 76 int totalNumBytesRead = 0;
77 while (entries.hasMoreElements()) { 77 InputStream in = jar.getInputStream(entry);
78 JarEntry entry = entries.nextElement(); 78 while (in.available() > 0) {
79 79 int numBytesRead = in.read(buf);
80 // is this a class file? 80 if (numBytesRead < 0) {
81 if (!entry.isDirectory() && entry.getName().endsWith(".class")) { 81 break;
82 classEntries.add(getClassEntry(entry)); 82 }
83 } 83 bos.write(buf, 0, numBytesRead);
84 } 84
85 return classEntries; 85 // sanity checking
86 } 86 totalNumBytesRead += numBytesRead;
87 87 if (totalNumBytesRead > Constants.MiB) {
88 public static Iterable<CtClass> classes(final JarFile jar) { 88 throw new Error("Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!");
89 return () -> new JarClassIterator(jar); 89 }
90 } 90 }
91 91
92 private static CtClass getClass(JarFile jar, JarEntry entry) throws IOException, NotFoundException { 92 // get a javassist handle for the class
93 // read the class into a buffer 93 String className = Descriptor.toJavaName(getClassEntry(entry).getName());
94 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 94 ClassPool classPool = new ClassPool();
95 byte[] buf = new byte[Constants.KiB]; 95 classPool.appendSystemPath();
96 int totalNumBytesRead = 0; 96 classPool.insertClassPath(new ByteArrayClassPath(className, bos.toByteArray()));
97 InputStream in = jar.getInputStream(entry); 97 return classPool.get(className);
98 while (in.available() > 0) { 98 }
99 int numBytesRead = in.read(buf); 99
100 if (numBytesRead < 0) { 100 private static ClassEntry getClassEntry(JarEntry entry) {
101 break; 101 return new ClassEntry(entry.getName().substring(0, entry.getName().length() - ".class".length()));
102 } 102 }
103 bos.write(buf, 0, numBytesRead); 103
104 104 @Override
105 // sanity checking 105 public boolean hasNext() {
106 totalNumBytesRead += numBytesRead; 106 return this.iter.hasNext();
107 if (totalNumBytesRead > Constants.MiB) { 107 }
108 throw new Error("Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!"); 108
109 } 109 @Override
110 } 110 public CtClass next() {
111 111 JarEntry entry = this.iter.next();
112 // get a javassist handle for the class 112 try {
113 String className = Descriptor.toJavaName(getClassEntry(entry).getName()); 113 return getClass(this.jar, entry);
114 ClassPool classPool = new ClassPool(); 114 } catch (IOException | NotFoundException ex) {
115 classPool.appendSystemPath(); 115 throw new Error("Unable to load class: " + entry.getName());
116 classPool.insertClassPath(new ByteArrayClassPath(className, bos.toByteArray())); 116 }
117 return classPool.get(className); 117 }
118 } 118
119 119 @Override
120 private static ClassEntry getClassEntry(JarEntry entry) { 120 public void remove() {
121 return new ClassEntry(entry.getName().substring(0, entry.getName().length() - ".class".length())); 121 throw new UnsupportedOperationException();
122 } 122 }
123} 123}
diff --git a/src/main/java/cuchaz/enigma/analysis/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/JarIndex.java
index e8f74cc5..ea87dda5 100644
--- a/src/main/java/cuchaz/enigma/analysis/JarIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/JarIndex.java
@@ -8,825 +8,824 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.*; 14import com.google.common.collect.*;
14
15import java.lang.reflect.Modifier;
16import java.util.*;
17import java.util.jar.JarFile;
18
19import cuchaz.enigma.mapping.*; 15import cuchaz.enigma.mapping.*;
20import cuchaz.enigma.mapping.Translator; 16import cuchaz.enigma.mapping.Translator;
21import javassist.*; 17import javassist.*;
22import javassist.bytecode.*; 18import javassist.bytecode.*;
23import javassist.expr.*; 19import javassist.expr.*;
24 20
21import java.lang.reflect.Modifier;
22import java.util.*;
23import java.util.jar.JarFile;
24
25public class JarIndex { 25public class JarIndex {
26 26
27 private Set<ClassEntry> obfClassEntries; 27 private Set<ClassEntry> obfClassEntries;
28 private TranslationIndex translationIndex; 28 private TranslationIndex translationIndex;
29 private Map<Entry, Access> access; 29 private Map<Entry, Access> access;
30 private Multimap<ClassEntry, FieldEntry> fields; 30 private Multimap<ClassEntry, FieldEntry> fields;
31 private Multimap<ClassEntry, BehaviorEntry> behaviors; 31 private Multimap<ClassEntry, BehaviorEntry> behaviors;
32 private Multimap<String, MethodEntry> methodImplementations; 32 private Multimap<String, MethodEntry> methodImplementations;
33 private Multimap<BehaviorEntry, EntryReference<BehaviorEntry, BehaviorEntry>> behaviorReferences; 33 private Multimap<BehaviorEntry, EntryReference<BehaviorEntry, BehaviorEntry>> behaviorReferences;
34 private Multimap<FieldEntry, EntryReference<FieldEntry, BehaviorEntry>> fieldReferences; 34 private Multimap<FieldEntry, EntryReference<FieldEntry, BehaviorEntry>> fieldReferences;
35 private Multimap<ClassEntry, ClassEntry> innerClassesByOuter; 35 private Multimap<ClassEntry, ClassEntry> innerClassesByOuter;
36 private Map<ClassEntry, ClassEntry> outerClassesByInner; 36 private Map<ClassEntry, ClassEntry> outerClassesByInner;
37 private Map<ClassEntry, BehaviorEntry> anonymousClasses; 37 private Map<ClassEntry, BehaviorEntry> anonymousClasses;
38 private Map<MethodEntry, MethodEntry> bridgedMethods; 38 private Map<MethodEntry, MethodEntry> bridgedMethods;
39 private Set<MethodEntry> syntheticMethods; 39 private Set<MethodEntry> syntheticMethods;
40 40
41 public JarIndex() { 41 public JarIndex() {
42 this.obfClassEntries = Sets.newHashSet(); 42 this.obfClassEntries = Sets.newHashSet();
43 this.translationIndex = new TranslationIndex(); 43 this.translationIndex = new TranslationIndex();
44 this.access = Maps.newHashMap(); 44 this.access = Maps.newHashMap();
45 this.fields = HashMultimap.create(); 45 this.fields = HashMultimap.create();
46 this.behaviors = HashMultimap.create(); 46 this.behaviors = HashMultimap.create();
47 this.methodImplementations = HashMultimap.create(); 47 this.methodImplementations = HashMultimap.create();
48 this.behaviorReferences = HashMultimap.create(); 48 this.behaviorReferences = HashMultimap.create();
49 this.fieldReferences = HashMultimap.create(); 49 this.fieldReferences = HashMultimap.create();
50 this.innerClassesByOuter = HashMultimap.create(); 50 this.innerClassesByOuter = HashMultimap.create();
51 this.outerClassesByInner = Maps.newHashMap(); 51 this.outerClassesByInner = Maps.newHashMap();
52 this.anonymousClasses = Maps.newHashMap(); 52 this.anonymousClasses = Maps.newHashMap();
53 this.bridgedMethods = Maps.newHashMap(); 53 this.bridgedMethods = Maps.newHashMap();
54 this.syntheticMethods = Sets.newHashSet(); 54 this.syntheticMethods = Sets.newHashSet();
55 } 55 }
56 56
57 public void indexJar(JarFile jar, boolean buildInnerClasses) { 57 public void indexJar(JarFile jar, boolean buildInnerClasses) {
58 58
59 // step 1: read the class names 59 // step 1: read the class names
60 this.obfClassEntries.addAll(JarClassIterator.getClassEntries(jar)); 60 this.obfClassEntries.addAll(JarClassIterator.getClassEntries(jar));
61 61
62 // step 2: index field/method/constructor access 62 // step 2: index field/method/constructor access
63 for (CtClass c : JarClassIterator.classes(jar)) { 63 for (CtClass c : JarClassIterator.classes(jar)) {
64 for (CtField field : c.getDeclaredFields()) { 64 for (CtField field : c.getDeclaredFields()) {
65 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); 65 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field);
66 this.access.put(fieldEntry, Access.get(field)); 66 this.access.put(fieldEntry, Access.get(field));
67 this.fields.put(fieldEntry.getClassEntry(), fieldEntry); 67 this.fields.put(fieldEntry.getClassEntry(), fieldEntry);
68 } 68 }
69 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 69 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
70 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); 70 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
71 this.access.put(behaviorEntry, Access.get(behavior)); 71 this.access.put(behaviorEntry, Access.get(behavior));
72 this.behaviors.put(behaviorEntry.getClassEntry(), behaviorEntry); 72 this.behaviors.put(behaviorEntry.getClassEntry(), behaviorEntry);
73 } 73 }
74 } 74 }
75 75
76 // step 3: index extends, implements, fields, and methods 76 // step 3: index extends, implements, fields, and methods
77 for (CtClass c : JarClassIterator.classes(jar)) { 77 for (CtClass c : JarClassIterator.classes(jar)) {
78 this.translationIndex.indexClass(c); 78 this.translationIndex.indexClass(c);
79 String className = Descriptor.toJvmName(c.getName()); 79 String className = Descriptor.toJvmName(c.getName());
80 for (String interfaceName : c.getClassFile().getInterfaces()) { 80 for (String interfaceName : c.getClassFile().getInterfaces()) {
81 className = Descriptor.toJvmName(className); 81 className = Descriptor.toJvmName(className);
82 interfaceName = Descriptor.toJvmName(interfaceName); 82 interfaceName = Descriptor.toJvmName(interfaceName);
83 if (className.equals(interfaceName)) { 83 if (className.equals(interfaceName)) {
84 throw new IllegalArgumentException("Class cannot be its own interface! " + className); 84 throw new IllegalArgumentException("Class cannot be its own interface! " + className);
85 } 85 }
86 } 86 }
87 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 87 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
88 indexBehavior(behavior); 88 indexBehavior(behavior);
89 } 89 }
90 } 90 }
91 91
92 // step 4: index field, method, constructor references 92 // step 4: index field, method, constructor references
93 for (CtClass c : JarClassIterator.classes(jar)) { 93 for (CtClass c : JarClassIterator.classes(jar)) {
94 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 94 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
95 indexBehaviorReferences(behavior); 95 indexBehaviorReferences(behavior);
96 } 96 }
97 } 97 }
98 98
99 if (buildInnerClasses) { 99 if (buildInnerClasses) {
100 100
101 // step 5: index inner classes and anonymous classes 101 // step 5: index inner classes and anonymous classes
102 for (CtClass c : JarClassIterator.classes(jar)) { 102 for (CtClass c : JarClassIterator.classes(jar)) {
103 ClassEntry innerClassEntry = EntryFactory.getClassEntry(c); 103 ClassEntry innerClassEntry = EntryFactory.getClassEntry(c);
104 ClassEntry outerClassEntry = findOuterClass(c); 104 ClassEntry outerClassEntry = findOuterClass(c);
105 if (outerClassEntry != null) { 105 if (outerClassEntry != null) {
106 this.innerClassesByOuter.put(outerClassEntry, innerClassEntry); 106 this.innerClassesByOuter.put(outerClassEntry, innerClassEntry);
107 boolean innerWasAdded = this.outerClassesByInner.put(innerClassEntry, outerClassEntry) == null; 107 boolean innerWasAdded = this.outerClassesByInner.put(innerClassEntry, outerClassEntry) == null;
108 assert (innerWasAdded); 108 assert (innerWasAdded);
109 109
110 BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassEntry); 110 BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassEntry);
111 if (enclosingBehavior != null) { 111 if (enclosingBehavior != null) {
112 this.anonymousClasses.put(innerClassEntry, enclosingBehavior); 112 this.anonymousClasses.put(innerClassEntry, enclosingBehavior);
113 113
114 // DEBUG 114 // DEBUG
115 //System.out.println("ANONYMOUS: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName()); 115 //System.out.println("ANONYMOUS: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName());
116 }/* else { 116 }/* else {
117 // DEBUG 117 // DEBUG
118 //System.out.println("INNER: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName()); 118 //System.out.println("INNER: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName());
119 }*/ 119 }*/
120 } 120 }
121 } 121 }
122 122
123 // step 6: update other indices with inner class info 123 // step 6: update other indices with inner class info
124 Map<String, String> renames = Maps.newHashMap(); 124 Map<String, String> renames = Maps.newHashMap();
125 for (ClassEntry innerClassEntry : this.innerClassesByOuter.values()) { 125 for (ClassEntry innerClassEntry : this.innerClassesByOuter.values()) {
126 String newName = innerClassEntry.buildClassEntry(getObfClassChain(innerClassEntry)).getName(); 126 String newName = innerClassEntry.buildClassEntry(getObfClassChain(innerClassEntry)).getName();
127 if (!innerClassEntry.getName().equals(newName)) { 127 if (!innerClassEntry.getName().equals(newName)) {
128 // DEBUG 128 // DEBUG
129 //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName); 129 //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName);
130 renames.put(innerClassEntry.getName(), newName); 130 renames.put(innerClassEntry.getName(), newName);
131 } 131 }
132 } 132 }
133 EntryRenamer.renameClassesInSet(renames, this.obfClassEntries); 133 EntryRenamer.renameClassesInSet(renames, this.obfClassEntries);
134 this.translationIndex.renameClasses(renames); 134 this.translationIndex.renameClasses(renames);
135 EntryRenamer.renameClassesInMultimap(renames, this.methodImplementations); 135 EntryRenamer.renameClassesInMultimap(renames, this.methodImplementations);
136 EntryRenamer.renameClassesInMultimap(renames, this.behaviorReferences); 136 EntryRenamer.renameClassesInMultimap(renames, this.behaviorReferences);
137 EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences); 137 EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences);
138 EntryRenamer.renameClassesInMap(renames, this.access); 138 EntryRenamer.renameClassesInMap(renames, this.access);
139 } 139 }
140 } 140 }
141 141
142 private void indexBehavior(CtBehavior behavior) { 142 private void indexBehavior(CtBehavior behavior) {
143 // get the behavior entry 143 // get the behavior entry
144 final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); 144 final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
145 if (behaviorEntry instanceof MethodEntry) { 145 if (behaviorEntry instanceof MethodEntry) {
146 MethodEntry methodEntry = (MethodEntry) behaviorEntry; 146 MethodEntry methodEntry = (MethodEntry) behaviorEntry;
147 147
148 // is synthetic 148 // is synthetic
149 if ((behavior.getModifiers() & AccessFlag.SYNTHETIC) != 0) { 149 if ((behavior.getModifiers() & AccessFlag.SYNTHETIC) != 0) {
150 syntheticMethods.add(methodEntry); 150 syntheticMethods.add(methodEntry);
151 } 151 }
152 152
153 // index implementation 153 // index implementation
154 this.methodImplementations.put(behaviorEntry.getClassName(), methodEntry); 154 this.methodImplementations.put(behaviorEntry.getClassName(), methodEntry);
155 155
156 // look for bridge and bridged methods 156 // look for bridge and bridged methods
157 CtMethod bridgedMethod = getBridgedMethod((CtMethod) behavior); 157 CtMethod bridgedMethod = getBridgedMethod((CtMethod) behavior);
158 if (bridgedMethod != null) { 158 if (bridgedMethod != null) {
159 this.bridgedMethods.put(methodEntry, EntryFactory.getMethodEntry(bridgedMethod)); 159 this.bridgedMethods.put(methodEntry, EntryFactory.getMethodEntry(bridgedMethod));
160 } 160 }
161 } 161 }
162 // looks like we don't care about constructors here 162 // looks like we don't care about constructors here
163 } 163 }
164 164
165 private void indexBehaviorReferences(CtBehavior behavior) { 165 private void indexBehaviorReferences(CtBehavior behavior) {
166 // index method calls 166 // index method calls
167 final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); 167 final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
168 try { 168 try {
169 behavior.instrument(new ExprEditor() { 169 behavior.instrument(new ExprEditor() {
170 @Override 170 @Override
171 public void edit(MethodCall call) { 171 public void edit(MethodCall call) {
172 MethodEntry calledMethodEntry = EntryFactory.getMethodEntry(call); 172 MethodEntry calledMethodEntry = EntryFactory.getMethodEntry(call);
173 ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledMethodEntry); 173 ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledMethodEntry);
174 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) { 174 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) {
175 calledMethodEntry = new MethodEntry( 175 calledMethodEntry = new MethodEntry(
176 resolvedClassEntry, 176 resolvedClassEntry,
177 calledMethodEntry.getName(), 177 calledMethodEntry.getName(),
178 calledMethodEntry.getSignature() 178 calledMethodEntry.getSignature()
179 ); 179 );
180 } 180 }
181 EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>( 181 EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>(
182 calledMethodEntry, 182 calledMethodEntry,
183 call.getMethodName(), 183 call.getMethodName(),
184 behaviorEntry 184 behaviorEntry
185 ); 185 );
186 behaviorReferences.put(calledMethodEntry, reference); 186 behaviorReferences.put(calledMethodEntry, reference);
187 } 187 }
188 188
189 @Override 189 @Override
190 public void edit(FieldAccess call) { 190 public void edit(FieldAccess call) {
191 FieldEntry calledFieldEntry = EntryFactory.getFieldEntry(call); 191 FieldEntry calledFieldEntry = EntryFactory.getFieldEntry(call);
192 ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledFieldEntry); 192 ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledFieldEntry);
193 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) { 193 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) {
194 calledFieldEntry = new FieldEntry(calledFieldEntry, resolvedClassEntry); 194 calledFieldEntry = new FieldEntry(calledFieldEntry, resolvedClassEntry);
195 } 195 }
196 EntryReference<FieldEntry, BehaviorEntry> reference = new EntryReference<>( 196 EntryReference<FieldEntry, BehaviorEntry> reference = new EntryReference<>(
197 calledFieldEntry, 197 calledFieldEntry,
198 call.getFieldName(), 198 call.getFieldName(),
199 behaviorEntry 199 behaviorEntry
200 ); 200 );
201 fieldReferences.put(calledFieldEntry, reference); 201 fieldReferences.put(calledFieldEntry, reference);
202 } 202 }
203 203
204 @Override 204 @Override
205 public void edit(ConstructorCall call) { 205 public void edit(ConstructorCall call) {
206 ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call); 206 ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call);
207 EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>( 207 EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>(
208 calledConstructorEntry, 208 calledConstructorEntry,
209 call.getMethodName(), 209 call.getMethodName(),
210 behaviorEntry 210 behaviorEntry
211 ); 211 );
212 behaviorReferences.put(calledConstructorEntry, reference); 212 behaviorReferences.put(calledConstructorEntry, reference);
213 } 213 }
214 214
215 @Override 215 @Override
216 public void edit(NewExpr call) { 216 public void edit(NewExpr call) {
217 ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call); 217 ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call);
218 EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>( 218 EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>(
219 calledConstructorEntry, 219 calledConstructorEntry,
220 call.getClassName(), 220 call.getClassName(),
221 behaviorEntry 221 behaviorEntry
222 ); 222 );
223 behaviorReferences.put(calledConstructorEntry, reference); 223 behaviorReferences.put(calledConstructorEntry, reference);
224 } 224 }
225 }); 225 });
226 } catch (CannotCompileException ex) { 226 } catch (CannotCompileException ex) {
227 throw new Error(ex); 227 throw new Error(ex);
228 } 228 }
229 } 229 }
230 230
231 private CtMethod getBridgedMethod(CtMethod method) { 231 private CtMethod getBridgedMethod(CtMethod method) {
232 232
233 // bridge methods just call another method, cast it to the return type, and return the result 233 // bridge methods just call another method, cast it to the return type, and return the result
234 // let's see if we can detect this scenario 234 // let's see if we can detect this scenario
235 235
236 // skip non-synthetic methods 236 // skip non-synthetic methods
237 if ((method.getModifiers() & AccessFlag.SYNTHETIC) == 0) { 237 if ((method.getModifiers() & AccessFlag.SYNTHETIC) == 0) {
238 return null; 238 return null;
239 } 239 }
240 240
241 // get all the called methods 241 // get all the called methods
242 final List<MethodCall> methodCalls = Lists.newArrayList(); 242 final List<MethodCall> methodCalls = Lists.newArrayList();
243 try { 243 try {
244 method.instrument(new ExprEditor() { 244 method.instrument(new ExprEditor() {
245 @Override 245 @Override
246 public void edit(MethodCall call) { 246 public void edit(MethodCall call) {
247 methodCalls.add(call); 247 methodCalls.add(call);
248 } 248 }
249 }); 249 });
250 } catch (CannotCompileException ex) { 250 } catch (CannotCompileException ex) {
251 // this is stupid... we're not even compiling anything 251 // this is stupid... we're not even compiling anything
252 throw new Error(ex); 252 throw new Error(ex);
253 } 253 }
254 254
255 // is there just one? 255 // is there just one?
256 if (methodCalls.size() != 1) { 256 if (methodCalls.size() != 1) {
257 return null; 257 return null;
258 } 258 }
259 MethodCall call = methodCalls.get(0); 259 MethodCall call = methodCalls.get(0);
260 260
261 try { 261 try {
262 // we have a bridge method! 262 // we have a bridge method!
263 return call.getMethod(); 263 return call.getMethod();
264 } catch (NotFoundException ex) { 264 } catch (NotFoundException ex) {
265 // can't find the type? not a bridge method 265 // can't find the type? not a bridge method
266 return null; 266 return null;
267 } 267 }
268 } 268 }
269 269
270 private ClassEntry findOuterClass(CtClass c) { 270 private ClassEntry findOuterClass(CtClass c) {
271 271
272 ClassEntry classEntry = EntryFactory.getClassEntry(c); 272 ClassEntry classEntry = EntryFactory.getClassEntry(c);
273 273
274 // does this class already have an outer class? 274 // does this class already have an outer class?
275 if (classEntry.isInnerClass()) { 275 if (classEntry.isInnerClass()) {
276 return classEntry.getOuterClassEntry(); 276 return classEntry.getOuterClassEntry();
277 } 277 }
278 278
279 // inner classes: 279 // inner classes:
280 // have constructors that can (illegally) set synthetic fields 280 // have constructors that can (illegally) set synthetic fields
281 // the outer class is the only class that calls constructors 281 // the outer class is the only class that calls constructors
282 282
283 // use the synthetic fields to find the synthetic constructors 283 // use the synthetic fields to find the synthetic constructors
284 for (CtConstructor constructor : c.getDeclaredConstructors()) { 284 for (CtConstructor constructor : c.getDeclaredConstructors()) {
285 Set<String> syntheticFieldTypes = Sets.newHashSet(); 285 Set<String> syntheticFieldTypes = Sets.newHashSet();
286 if (!isIllegalConstructor(syntheticFieldTypes, constructor)) { 286 if (!isIllegalConstructor(syntheticFieldTypes, constructor)) {
287 continue; 287 continue;
288 } 288 }
289 289
290 ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); 290 ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor);
291 291
292 // gather the classes from the illegally-set synthetic fields 292 // gather the classes from the illegally-set synthetic fields
293 Set<ClassEntry> illegallySetClasses = Sets.newHashSet(); 293 Set<ClassEntry> illegallySetClasses = Sets.newHashSet();
294 for (String type : syntheticFieldTypes) { 294 for (String type : syntheticFieldTypes) {
295 if (type.startsWith("L")) { 295 if (type.startsWith("L")) {
296 ClassEntry outerClassEntry = new ClassEntry(type.substring(1, type.length() - 1)); 296 ClassEntry outerClassEntry = new ClassEntry(type.substring(1, type.length() - 1));
297 if (isSaneOuterClass(outerClassEntry, classEntry)) { 297 if (isSaneOuterClass(outerClassEntry, classEntry)) {
298 illegallySetClasses.add(outerClassEntry); 298 illegallySetClasses.add(outerClassEntry);
299 } 299 }
300 } 300 }
301 } 301 }
302 302
303 // who calls this constructor? 303 // who calls this constructor?
304 Set<ClassEntry> callerClasses = Sets.newHashSet(); 304 Set<ClassEntry> callerClasses = Sets.newHashSet();
305 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : getBehaviorReferences(constructorEntry)) { 305 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : getBehaviorReferences(constructorEntry)) {
306 306
307 // make sure it's not a call to super 307 // make sure it's not a call to super
308 if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) { 308 if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) {
309 309
310 // is the entry a superclass of the context? 310 // is the entry a superclass of the context?
311 ClassEntry calledClassEntry = reference.entry.getClassEntry(); 311 ClassEntry calledClassEntry = reference.entry.getClassEntry();
312 ClassEntry superclassEntry = this.translationIndex.getSuperclass(reference.context.getClassEntry()); 312 ClassEntry superclassEntry = this.translationIndex.getSuperclass(reference.context.getClassEntry());
313 if (superclassEntry != null && superclassEntry.equals(calledClassEntry)) { 313 if (superclassEntry != null && superclassEntry.equals(calledClassEntry)) {
314 // it's a super call, skip 314 // it's a super call, skip
315 continue; 315 continue;
316 } 316 }
317 } 317 }
318 318
319 if (isSaneOuterClass(reference.context.getClassEntry(), classEntry)) { 319 if (isSaneOuterClass(reference.context.getClassEntry(), classEntry)) {
320 callerClasses.add(reference.context.getClassEntry()); 320 callerClasses.add(reference.context.getClassEntry());
321 } 321 }
322 } 322 }
323 323
324 // do we have an answer yet? 324 // do we have an answer yet?
325 if (callerClasses.isEmpty()) { 325 if (callerClasses.isEmpty()) {
326 if (illegallySetClasses.size() == 1) { 326 if (illegallySetClasses.size() == 1) {
327 return illegallySetClasses.iterator().next(); 327 return illegallySetClasses.iterator().next();
328 } else { 328 } else {
329 System.out.println(String.format("WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry)); 329 System.out.println(String.format("WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry));
330 } 330 }
331 } else { 331 } else {
332 if (callerClasses.size() == 1) { 332 if (callerClasses.size() == 1) {
333 return callerClasses.iterator().next(); 333 return callerClasses.iterator().next();
334 } else { 334 } else {
335 // multiple callers, do the illegally set classes narrow it down? 335 // multiple callers, do the illegally set classes narrow it down?
336 Set<ClassEntry> intersection = Sets.newHashSet(callerClasses); 336 Set<ClassEntry> intersection = Sets.newHashSet(callerClasses);
337 intersection.retainAll(illegallySetClasses); 337 intersection.retainAll(illegallySetClasses);
338 if (intersection.size() == 1) { 338 if (intersection.size() == 1) {
339 return intersection.iterator().next(); 339 return intersection.iterator().next();
340 } else { 340 } else {
341 System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses)); 341 System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses));
342 } 342 }
343 } 343 }
344 } 344 }
345 } 345 }
346 346
347 return null; 347 return null;
348 } 348 }
349 349
350 private boolean isSaneOuterClass(ClassEntry outerClassEntry, ClassEntry innerClassEntry) { 350 private boolean isSaneOuterClass(ClassEntry outerClassEntry, ClassEntry innerClassEntry) {
351 351
352 // clearly this would be silly 352 // clearly this would be silly
353 if (outerClassEntry.equals(innerClassEntry)) { 353 if (outerClassEntry.equals(innerClassEntry)) {
354 return false; 354 return false;
355 } 355 }
356 356
357 // is the outer class in the jar? 357 // is the outer class in the jar?
358 return this.obfClassEntries.contains(outerClassEntry); 358 return this.obfClassEntries.contains(outerClassEntry);
359 359
360 } 360 }
361 361
362 @SuppressWarnings("unchecked") 362 @SuppressWarnings("unchecked")
363 private boolean isIllegalConstructor(Set<String> syntheticFieldTypes, CtConstructor constructor) { 363 private boolean isIllegalConstructor(Set<String> syntheticFieldTypes, CtConstructor constructor) {
364 364
365 // illegal constructors only set synthetic member fields, then call super() 365 // illegal constructors only set synthetic member fields, then call super()
366 String className = constructor.getDeclaringClass().getName(); 366 String className = constructor.getDeclaringClass().getName();
367 367
368 // collect all the field accesses, constructor calls, and method calls 368 // collect all the field accesses, constructor calls, and method calls
369 final List<FieldAccess> illegalFieldWrites = Lists.newArrayList(); 369 final List<FieldAccess> illegalFieldWrites = Lists.newArrayList();
370 final List<ConstructorCall> constructorCalls = Lists.newArrayList(); 370 final List<ConstructorCall> constructorCalls = Lists.newArrayList();
371 try { 371 try {
372 constructor.instrument(new ExprEditor() { 372 constructor.instrument(new ExprEditor() {
373 @Override 373 @Override
374 public void edit(FieldAccess fieldAccess) { 374 public void edit(FieldAccess fieldAccess) {
375 if (fieldAccess.isWriter() && constructorCalls.isEmpty()) { 375 if (fieldAccess.isWriter() && constructorCalls.isEmpty()) {
376 illegalFieldWrites.add(fieldAccess); 376 illegalFieldWrites.add(fieldAccess);
377 } 377 }
378 } 378 }
379 379
380 @Override 380 @Override
381 public void edit(ConstructorCall constructorCall) { 381 public void edit(ConstructorCall constructorCall) {
382 constructorCalls.add(constructorCall); 382 constructorCalls.add(constructorCall);
383 } 383 }
384 }); 384 });
385 } catch (CannotCompileException ex) { 385 } catch (CannotCompileException ex) {
386 // we're not compiling anything... this is stupid 386 // we're not compiling anything... this is stupid
387 throw new Error(ex); 387 throw new Error(ex);
388 } 388 }
389 389
390 // are there any illegal field writes? 390 // are there any illegal field writes?
391 if (illegalFieldWrites.isEmpty()) { 391 if (illegalFieldWrites.isEmpty()) {
392 return false; 392 return false;
393 } 393 }
394 394
395 // are all the writes to synthetic fields? 395 // are all the writes to synthetic fields?
396 for (FieldAccess fieldWrite : illegalFieldWrites) { 396 for (FieldAccess fieldWrite : illegalFieldWrites) {
397 397
398 // all illegal writes have to be to the local class 398 // all illegal writes have to be to the local class
399 if (!fieldWrite.getClassName().equals(className)) { 399 if (!fieldWrite.getClassName().equals(className)) {
400 System.err.println(String.format("WARNING: illegal write to non-member field %s.%s", fieldWrite.getClassName(), fieldWrite.getFieldName())); 400 System.err.println(String.format("WARNING: illegal write to non-member field %s.%s", fieldWrite.getClassName(), fieldWrite.getFieldName()));
401 return false; 401 return false;
402 } 402 }
403 403
404 // find the field 404 // find the field
405 FieldInfo fieldInfo = null; 405 FieldInfo fieldInfo = null;
406 for (FieldInfo info : (List<FieldInfo>) constructor.getDeclaringClass().getClassFile().getFields()) { 406 for (FieldInfo info : (List<FieldInfo>) constructor.getDeclaringClass().getClassFile().getFields()) {
407 if (info.getName().equals(fieldWrite.getFieldName()) && info.getDescriptor().equals(fieldWrite.getSignature())) { 407 if (info.getName().equals(fieldWrite.getFieldName()) && info.getDescriptor().equals(fieldWrite.getSignature())) {
408 fieldInfo = info; 408 fieldInfo = info;
409 break; 409 break;
410 } 410 }
411 } 411 }
412 if (fieldInfo == null) { 412 if (fieldInfo == null) {
413 // field is in a superclass or something, can't be a local synthetic member 413 // field is in a superclass or something, can't be a local synthetic member
414 return false; 414 return false;
415 } 415 }
416 416
417 // is this field synthetic? 417 // is this field synthetic?
418 boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; 418 boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0;
419 if (isSynthetic) { 419 if (isSynthetic) {
420 syntheticFieldTypes.add(fieldInfo.getDescriptor()); 420 syntheticFieldTypes.add(fieldInfo.getDescriptor());
421 } else { 421 } else {
422 System.err.println(String.format("WARNING: illegal write to non synthetic field %s %s.%s", fieldInfo.getDescriptor(), className, fieldInfo.getName())); 422 System.err.println(String.format("WARNING: illegal write to non synthetic field %s %s.%s", fieldInfo.getDescriptor(), className, fieldInfo.getName()));
423 return false; 423 return false;
424 } 424 }
425 } 425 }
426 426
427 // we passed all the tests! 427 // we passed all the tests!
428 return true; 428 return true;
429 } 429 }
430 430
431 private BehaviorEntry isAnonymousClass(CtClass c, ClassEntry outerClassEntry) { 431 private BehaviorEntry isAnonymousClass(CtClass c, ClassEntry outerClassEntry) {
432 432
433 // is this class already marked anonymous? 433 // is this class already marked anonymous?
434 EnclosingMethodAttribute enclosingMethodAttribute = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); 434 EnclosingMethodAttribute enclosingMethodAttribute = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag);
435 if (enclosingMethodAttribute != null) { 435 if (enclosingMethodAttribute != null) {
436 if (enclosingMethodAttribute.methodIndex() > 0) { 436 if (enclosingMethodAttribute.methodIndex() > 0) {
437 return EntryFactory.getBehaviorEntry( 437 return EntryFactory.getBehaviorEntry(
438 Descriptor.toJvmName(enclosingMethodAttribute.className()), 438 Descriptor.toJvmName(enclosingMethodAttribute.className()),
439 enclosingMethodAttribute.methodName(), 439 enclosingMethodAttribute.methodName(),
440 enclosingMethodAttribute.methodDescriptor() 440 enclosingMethodAttribute.methodDescriptor()
441 ); 441 );
442 } else { 442 } else {
443 // an attribute but no method? assume not anonymous 443 // an attribute but no method? assume not anonymous
444 return null; 444 return null;
445 } 445 }
446 } 446 }
447 447
448 // if there's an inner class attribute, but not an enclosing method attribute, then it's not anonymous 448 // if there's an inner class attribute, but not an enclosing method attribute, then it's not anonymous
449 InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); 449 InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
450 if (innerClassesAttribute != null) { 450 if (innerClassesAttribute != null) {
451 return null; 451 return null;
452 } 452 }
453 453
454 ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); 454 ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
455 455
456 // anonymous classes: 456 // anonymous classes:
457 // can't be abstract 457 // can't be abstract
458 // have only one constructor 458 // have only one constructor
459 // it's called exactly once by the outer class 459 // it's called exactly once by the outer class
460 // the type the instance is assigned to can't be this type 460 // the type the instance is assigned to can't be this type
461 461
462 // is abstract? 462 // is abstract?
463 if (Modifier.isAbstract(c.getModifiers())) { 463 if (Modifier.isAbstract(c.getModifiers())) {
464 return null; 464 return null;
465 } 465 }
466 466
467 // is there exactly one constructor? 467 // is there exactly one constructor?
468 if (c.getDeclaredConstructors().length != 1) { 468 if (c.getDeclaredConstructors().length != 1) {
469 return null; 469 return null;
470 } 470 }
471 CtConstructor constructor = c.getDeclaredConstructors()[0]; 471 CtConstructor constructor = c.getDeclaredConstructors()[0];
472 472
473 // is this constructor called exactly once? 473 // is this constructor called exactly once?
474 ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); 474 ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor);
475 Collection<EntryReference<BehaviorEntry, BehaviorEntry>> references = getBehaviorReferences(constructorEntry); 475 Collection<EntryReference<BehaviorEntry, BehaviorEntry>> references = getBehaviorReferences(constructorEntry);
476 if (references.size() != 1) { 476 if (references.size() != 1) {
477 return null; 477 return null;
478 } 478 }
479 479
480 // does the caller use this type? 480 // does the caller use this type?
481 BehaviorEntry caller = references.iterator().next().context; 481 BehaviorEntry caller = references.iterator().next().context;
482 for (FieldEntry fieldEntry : getReferencedFields(caller)) { 482 for (FieldEntry fieldEntry : getReferencedFields(caller)) {
483 if (fieldEntry.getType().hasClass() && fieldEntry.getType().getClassEntry().equals(innerClassEntry)) { 483 if (fieldEntry.getType().hasClass() && fieldEntry.getType().getClassEntry().equals(innerClassEntry)) {
484 // caller references this type, so it can't be anonymous 484 // caller references this type, so it can't be anonymous
485 return null; 485 return null;
486 } 486 }
487 } 487 }
488 for (BehaviorEntry behaviorEntry : getReferencedBehaviors(caller)) { 488 for (BehaviorEntry behaviorEntry : getReferencedBehaviors(caller)) {
489 if (behaviorEntry.getSignature().hasClass(innerClassEntry)) { 489 if (behaviorEntry.getSignature().hasClass(innerClassEntry)) {
490 return null; 490 return null;
491 } 491 }
492 } 492 }
493 493
494 return caller; 494 return caller;
495 } 495 }
496 496
497 public Set<ClassEntry> getObfClassEntries() { 497 public Set<ClassEntry> getObfClassEntries() {
498 return this.obfClassEntries; 498 return this.obfClassEntries;
499 } 499 }
500 500
501 public Collection<FieldEntry> getObfFieldEntries() { 501 public Collection<FieldEntry> getObfFieldEntries() {
502 return this.fields.values(); 502 return this.fields.values();
503 } 503 }
504 504
505 public Collection<FieldEntry> getObfFieldEntries(ClassEntry classEntry) { 505 public Collection<FieldEntry> getObfFieldEntries(ClassEntry classEntry) {
506 return this.fields.get(classEntry); 506 return this.fields.get(classEntry);
507 } 507 }
508 508
509 public Collection<BehaviorEntry> getObfBehaviorEntries() { 509 public Collection<BehaviorEntry> getObfBehaviorEntries() {
510 return this.behaviors.values(); 510 return this.behaviors.values();
511 } 511 }
512 512
513 public Collection<BehaviorEntry> getObfBehaviorEntries(ClassEntry classEntry) { 513 public Collection<BehaviorEntry> getObfBehaviorEntries(ClassEntry classEntry) {
514 return this.behaviors.get(classEntry); 514 return this.behaviors.get(classEntry);
515 } 515 }
516 516
517 public TranslationIndex getTranslationIndex() { 517 public TranslationIndex getTranslationIndex() {
518 return this.translationIndex; 518 return this.translationIndex;
519 } 519 }
520 520
521 public Access getAccess(Entry entry) { 521 public Access getAccess(Entry entry) {
522 return this.access.get(entry); 522 return this.access.get(entry);
523 } 523 }
524 524
525 public ClassInheritanceTreeNode getClassInheritance(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) { 525 public ClassInheritanceTreeNode getClassInheritance(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) {
526 526
527 // get the root node 527 // get the root node
528 List<String> ancestry = Lists.newArrayList(); 528 List<String> ancestry = Lists.newArrayList();
529 ancestry.add(obfClassEntry.getName()); 529 ancestry.add(obfClassEntry.getName());
530 for (ClassEntry classEntry : this.translationIndex.getAncestry(obfClassEntry)) { 530 for (ClassEntry classEntry : this.translationIndex.getAncestry(obfClassEntry)) {
531 if (containsObfClass(classEntry)) { 531 if (containsObfClass(classEntry)) {
532 ancestry.add(classEntry.getName()); 532 ancestry.add(classEntry.getName());
533 } 533 }
534 } 534 }
535 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( 535 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode(
536 deobfuscatingTranslator, 536 deobfuscatingTranslator,
537 ancestry.get(ancestry.size() - 1) 537 ancestry.get(ancestry.size() - 1)
538 ); 538 );
539 539
540 // expand all children recursively 540 // expand all children recursively
541 rootNode.load(this.translationIndex, true); 541 rootNode.load(this.translationIndex, true);
542 542
543 return rootNode; 543 return rootNode;
544 } 544 }
545 545
546 public ClassImplementationsTreeNode getClassImplementations(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) { 546 public ClassImplementationsTreeNode getClassImplementations(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) {
547 547
548 // is this even an interface? 548 // is this even an interface?
549 if (isInterface(obfClassEntry.getClassName())) { 549 if (isInterface(obfClassEntry.getClassName())) {
550 ClassImplementationsTreeNode node = new ClassImplementationsTreeNode(deobfuscatingTranslator, obfClassEntry); 550 ClassImplementationsTreeNode node = new ClassImplementationsTreeNode(deobfuscatingTranslator, obfClassEntry);
551 node.load(this); 551 node.load(this);
552 return node; 552 return node;
553 } 553 }
554 return null; 554 return null;
555 } 555 }
556 556
557 public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { 557 public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) {
558 558
559 // travel to the ancestor implementation 559 // travel to the ancestor implementation
560 ClassEntry baseImplementationClassEntry = obfMethodEntry.getClassEntry(); 560 ClassEntry baseImplementationClassEntry = obfMethodEntry.getClassEntry();
561 for (ClassEntry ancestorClassEntry : this.translationIndex.getAncestry(obfMethodEntry.getClassEntry())) { 561 for (ClassEntry ancestorClassEntry : this.translationIndex.getAncestry(obfMethodEntry.getClassEntry())) {
562 MethodEntry ancestorMethodEntry = new MethodEntry( 562 MethodEntry ancestorMethodEntry = new MethodEntry(
563 new ClassEntry(ancestorClassEntry), 563 new ClassEntry(ancestorClassEntry),
564 obfMethodEntry.getName(), 564 obfMethodEntry.getName(),
565 obfMethodEntry.getSignature() 565 obfMethodEntry.getSignature()
566 ); 566 );
567 if (containsObfBehavior(ancestorMethodEntry)) { 567 if (containsObfBehavior(ancestorMethodEntry)) {
568 baseImplementationClassEntry = ancestorClassEntry; 568 baseImplementationClassEntry = ancestorClassEntry;
569 } 569 }
570 } 570 }
571 571
572 // make a root node at the base 572 // make a root node at the base
573 MethodEntry methodEntry = new MethodEntry( 573 MethodEntry methodEntry = new MethodEntry(
574 baseImplementationClassEntry, 574 baseImplementationClassEntry,
575 obfMethodEntry.getName(), 575 obfMethodEntry.getName(),
576 obfMethodEntry.getSignature() 576 obfMethodEntry.getSignature()
577 ); 577 );
578 MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( 578 MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode(
579 deobfuscatingTranslator, 579 deobfuscatingTranslator,
580 methodEntry, 580 methodEntry,
581 containsObfBehavior(methodEntry) 581 containsObfBehavior(methodEntry)
582 ); 582 );
583 583
584 // expand the full tree 584 // expand the full tree
585 rootNode.load(this, true); 585 rootNode.load(this, true);
586 586
587 return rootNode; 587 return rootNode;
588 } 588 }
589 589
590 public List<MethodImplementationsTreeNode> getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { 590 public List<MethodImplementationsTreeNode> getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) {
591 591
592 List<MethodEntry> interfaceMethodEntries = Lists.newArrayList(); 592 List<MethodEntry> interfaceMethodEntries = Lists.newArrayList();
593 593
594 // is this method on an interface? 594 // is this method on an interface?
595 if (isInterface(obfMethodEntry.getClassName())) { 595 if (isInterface(obfMethodEntry.getClassName())) {
596 interfaceMethodEntries.add(obfMethodEntry); 596 interfaceMethodEntries.add(obfMethodEntry);
597 } else { 597 } else {
598 // get the interface class 598 // get the interface class
599 for (ClassEntry interfaceEntry : getInterfaces(obfMethodEntry.getClassName())) { 599 for (ClassEntry interfaceEntry : getInterfaces(obfMethodEntry.getClassName())) {
600 600
601 // is this method defined in this interface? 601 // is this method defined in this interface?
602 MethodEntry methodInterface = new MethodEntry( 602 MethodEntry methodInterface = new MethodEntry(
603 interfaceEntry, 603 interfaceEntry,
604 obfMethodEntry.getName(), 604 obfMethodEntry.getName(),
605 obfMethodEntry.getSignature() 605 obfMethodEntry.getSignature()
606 ); 606 );
607 if (containsObfBehavior(methodInterface)) { 607 if (containsObfBehavior(methodInterface)) {
608 interfaceMethodEntries.add(methodInterface); 608 interfaceMethodEntries.add(methodInterface);
609 } 609 }
610 } 610 }
611 } 611 }
612 612
613 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList(); 613 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList();
614 if (!interfaceMethodEntries.isEmpty()) { 614 if (!interfaceMethodEntries.isEmpty()) {
615 for (MethodEntry interfaceMethodEntry : interfaceMethodEntries) { 615 for (MethodEntry interfaceMethodEntry : interfaceMethodEntries) {
616 MethodImplementationsTreeNode node = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry); 616 MethodImplementationsTreeNode node = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry);
617 node.load(this); 617 node.load(this);
618 nodes.add(node); 618 nodes.add(node);
619 } 619 }
620 } 620 }
621 return nodes; 621 return nodes;
622 } 622 }
623 623
624 public Set<MethodEntry> getRelatedMethodImplementations(MethodEntry obfMethodEntry) { 624 public Set<MethodEntry> getRelatedMethodImplementations(MethodEntry obfMethodEntry) {
625 Set<MethodEntry> methodEntries = Sets.newHashSet(); 625 Set<MethodEntry> methodEntries = Sets.newHashSet();
626 getRelatedMethodImplementations(methodEntries, getMethodInheritance(new Translator(), obfMethodEntry)); 626 getRelatedMethodImplementations(methodEntries, getMethodInheritance(new Translator(), obfMethodEntry));
627 return methodEntries; 627 return methodEntries;
628 } 628 }
629 629
630 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) { 630 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) {
631 MethodEntry methodEntry = node.getMethodEntry(); 631 MethodEntry methodEntry = node.getMethodEntry();
632 632
633 if (containsObfBehavior(methodEntry)) { 633 if (containsObfBehavior(methodEntry)) {
634 // collect the entry 634 // collect the entry
635 methodEntries.add(methodEntry); 635 methodEntries.add(methodEntry);
636 } 636 }
637 637
638 // look at bridged methods! 638 // look at bridged methods!
639 MethodEntry bridgedEntry = getBridgedMethod(methodEntry); 639 MethodEntry bridgedEntry = getBridgedMethod(methodEntry);
640 while (bridgedEntry != null) { 640 while (bridgedEntry != null) {
641 methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry)); 641 methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry));
642 bridgedEntry = getBridgedMethod(bridgedEntry); 642 bridgedEntry = getBridgedMethod(bridgedEntry);
643 } 643 }
644 644
645 // look at interface methods too 645 // look at interface methods too
646 for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(new Translator(), methodEntry)) { 646 for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(new Translator(), methodEntry)) {
647 getRelatedMethodImplementations(methodEntries, implementationsNode); 647 getRelatedMethodImplementations(methodEntries, implementationsNode);
648 } 648 }
649 649
650 // recurse 650 // recurse
651 for (int i = 0; i < node.getChildCount(); i++) { 651 for (int i = 0; i < node.getChildCount(); i++) {
652 getRelatedMethodImplementations(methodEntries, (MethodInheritanceTreeNode) node.getChildAt(i)); 652 getRelatedMethodImplementations(methodEntries, (MethodInheritanceTreeNode) node.getChildAt(i));
653 } 653 }
654 } 654 }
655 655
656 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) { 656 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) {
657 MethodEntry methodEntry = node.getMethodEntry(); 657 MethodEntry methodEntry = node.getMethodEntry();
658 if (containsObfBehavior(methodEntry)) { 658 if (containsObfBehavior(methodEntry)) {
659 // collect the entry 659 // collect the entry
660 methodEntries.add(methodEntry); 660 methodEntries.add(methodEntry);
661 } 661 }
662 662
663 // look at bridged methods! 663 // look at bridged methods!
664 MethodEntry bridgedEntry = getBridgedMethod(methodEntry); 664 MethodEntry bridgedEntry = getBridgedMethod(methodEntry);
665 while (bridgedEntry != null) { 665 while (bridgedEntry != null) {
666 methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry)); 666 methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry));
667 bridgedEntry = getBridgedMethod(bridgedEntry); 667 bridgedEntry = getBridgedMethod(bridgedEntry);
668 } 668 }
669 669
670 // recurse 670 // recurse
671 for (int i = 0; i < node.getChildCount(); i++) { 671 for (int i = 0; i < node.getChildCount(); i++) {
672 getRelatedMethodImplementations(methodEntries, (MethodImplementationsTreeNode) node.getChildAt(i)); 672 getRelatedMethodImplementations(methodEntries, (MethodImplementationsTreeNode) node.getChildAt(i));
673 } 673 }
674 } 674 }
675 675
676 public Collection<EntryReference<FieldEntry, BehaviorEntry>> getFieldReferences(FieldEntry fieldEntry) { 676 public Collection<EntryReference<FieldEntry, BehaviorEntry>> getFieldReferences(FieldEntry fieldEntry) {
677 return this.fieldReferences.get(fieldEntry); 677 return this.fieldReferences.get(fieldEntry);
678 } 678 }
679 679
680 public Collection<FieldEntry> getReferencedFields(BehaviorEntry behaviorEntry) { 680 public Collection<FieldEntry> getReferencedFields(BehaviorEntry behaviorEntry) {
681 // linear search is fast enough for now 681 // linear search is fast enough for now
682 Set<FieldEntry> fieldEntries = Sets.newHashSet(); 682 Set<FieldEntry> fieldEntries = Sets.newHashSet();
683 for (EntryReference<FieldEntry, BehaviorEntry> reference : this.fieldReferences.values()) { 683 for (EntryReference<FieldEntry, BehaviorEntry> reference : this.fieldReferences.values()) {
684 if (reference.context == behaviorEntry) { 684 if (reference.context == behaviorEntry) {
685 fieldEntries.add(reference.entry); 685 fieldEntries.add(reference.entry);
686 } 686 }
687 } 687 }
688 return fieldEntries; 688 return fieldEntries;
689 } 689 }
690 690
691 public Collection<EntryReference<BehaviorEntry, BehaviorEntry>> getBehaviorReferences(BehaviorEntry behaviorEntry) { 691 public Collection<EntryReference<BehaviorEntry, BehaviorEntry>> getBehaviorReferences(BehaviorEntry behaviorEntry) {
692 return this.behaviorReferences.get(behaviorEntry); 692 return this.behaviorReferences.get(behaviorEntry);
693 } 693 }
694 694
695 public Collection<BehaviorEntry> getReferencedBehaviors(BehaviorEntry behaviorEntry) { 695 public Collection<BehaviorEntry> getReferencedBehaviors(BehaviorEntry behaviorEntry) {
696 // linear search is fast enough for now 696 // linear search is fast enough for now
697 Set<BehaviorEntry> behaviorEntries = Sets.newHashSet(); 697 Set<BehaviorEntry> behaviorEntries = Sets.newHashSet();
698 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : this.behaviorReferences.values()) { 698 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : this.behaviorReferences.values()) {
699 if (reference.context == behaviorEntry) { 699 if (reference.context == behaviorEntry) {
700 behaviorEntries.add(reference.entry); 700 behaviorEntries.add(reference.entry);
701 } 701 }
702 } 702 }
703 return behaviorEntries; 703 return behaviorEntries;
704 } 704 }
705 705
706 public Collection<ClassEntry> getInnerClasses(ClassEntry obfOuterClassEntry) { 706 public Collection<ClassEntry> getInnerClasses(ClassEntry obfOuterClassEntry) {
707 return this.innerClassesByOuter.get(obfOuterClassEntry); 707 return this.innerClassesByOuter.get(obfOuterClassEntry);
708 } 708 }
709 709
710 public ClassEntry getOuterClass(ClassEntry obfInnerClassEntry) { 710 public ClassEntry getOuterClass(ClassEntry obfInnerClassEntry) {
711 return this.outerClassesByInner.get(obfInnerClassEntry); 711 return this.outerClassesByInner.get(obfInnerClassEntry);
712 } 712 }
713 713
714 public boolean isAnonymousClass(ClassEntry obfInnerClassEntry) { 714 public boolean isAnonymousClass(ClassEntry obfInnerClassEntry) {
715 return this.anonymousClasses.containsKey(obfInnerClassEntry); 715 return this.anonymousClasses.containsKey(obfInnerClassEntry);
716 } 716 }
717 717
718 public boolean isSyntheticMethod(MethodEntry methodEntry) { 718 public boolean isSyntheticMethod(MethodEntry methodEntry) {
719 return this.syntheticMethods.contains(methodEntry); 719 return this.syntheticMethods.contains(methodEntry);
720 } 720 }
721 721
722 public BehaviorEntry getAnonymousClassCaller(ClassEntry obfInnerClassName) { 722 public BehaviorEntry getAnonymousClassCaller(ClassEntry obfInnerClassName) {
723 return this.anonymousClasses.get(obfInnerClassName); 723 return this.anonymousClasses.get(obfInnerClassName);
724 } 724 }
725 725
726 public Set<ClassEntry> getInterfaces(String className) { 726 public Set<ClassEntry> getInterfaces(String className) {
727 ClassEntry classEntry = new ClassEntry(className); 727 ClassEntry classEntry = new ClassEntry(className);
728 Set<ClassEntry> interfaces = new HashSet<>(); 728 Set<ClassEntry> interfaces = new HashSet<>();
729 interfaces.addAll(this.translationIndex.getInterfaces(classEntry)); 729 interfaces.addAll(this.translationIndex.getInterfaces(classEntry));
730 for (ClassEntry ancestor : this.translationIndex.getAncestry(classEntry)) { 730 for (ClassEntry ancestor : this.translationIndex.getAncestry(classEntry)) {
731 interfaces.addAll(this.translationIndex.getInterfaces(ancestor)); 731 interfaces.addAll(this.translationIndex.getInterfaces(ancestor));
732 } 732 }
733 return interfaces; 733 return interfaces;
734 } 734 }
735 735
736 public Set<String> getImplementingClasses(String targetInterfaceName) { 736 public Set<String> getImplementingClasses(String targetInterfaceName) {
737 737
738 // linear search is fast enough for now 738 // linear search is fast enough for now
739 Set<String> classNames = Sets.newHashSet(); 739 Set<String> classNames = Sets.newHashSet();
740 for (Map.Entry<ClassEntry, ClassEntry> entry : this.translationIndex.getClassInterfaces()) { 740 for (Map.Entry<ClassEntry, ClassEntry> entry : this.translationIndex.getClassInterfaces()) {
741 ClassEntry classEntry = entry.getKey(); 741 ClassEntry classEntry = entry.getKey();
742 ClassEntry interfaceEntry = entry.getValue(); 742 ClassEntry interfaceEntry = entry.getValue();
743 if (interfaceEntry.getName().equals(targetInterfaceName)) { 743 if (interfaceEntry.getName().equals(targetInterfaceName)) {
744 String className = classEntry.getClassName(); 744 String className = classEntry.getClassName();
745 classNames.add(className); 745 classNames.add(className);
746 if (isInterface(className)) { 746 if (isInterface(className)) {
747 classNames.addAll(getImplementingClasses(className)); 747 classNames.addAll(getImplementingClasses(className));
748 } 748 }
749 749
750 this.translationIndex.getSubclassNamesRecursively(classNames, classEntry); 750 this.translationIndex.getSubclassNamesRecursively(classNames, classEntry);
751 } 751 }
752 } 752 }
753 return classNames; 753 return classNames;
754 } 754 }
755 755
756 public boolean isInterface(String className) { 756 public boolean isInterface(String className) {
757 return this.translationIndex.isInterface(new ClassEntry(className)); 757 return this.translationIndex.isInterface(new ClassEntry(className));
758 } 758 }
759 759
760 public boolean containsObfClass(ClassEntry obfClassEntry) { 760 public boolean containsObfClass(ClassEntry obfClassEntry) {
761 return this.obfClassEntries.contains(obfClassEntry); 761 return this.obfClassEntries.contains(obfClassEntry);
762 } 762 }
763 763
764 public boolean containsObfField(FieldEntry obfFieldEntry) { 764 public boolean containsObfField(FieldEntry obfFieldEntry) {
765 return this.access.containsKey(obfFieldEntry); 765 return this.access.containsKey(obfFieldEntry);
766 } 766 }
767 767
768 public boolean containsObfBehavior(BehaviorEntry obfBehaviorEntry) { 768 public boolean containsObfBehavior(BehaviorEntry obfBehaviorEntry) {
769 return this.access.containsKey(obfBehaviorEntry); 769 return this.access.containsKey(obfBehaviorEntry);
770 } 770 }
771 771
772 public boolean containsEntryWithSameName(Entry entry) 772 public boolean containsEntryWithSameName(Entry entry) {
773 { 773 for (Entry target : this.access.keySet())
774 for (Entry target : this.access.keySet()) 774 if (target.getName().equals(entry.getName()) && entry.getClass().isInstance(target.getClass()))
775 if (target.getName().equals(entry.getName()) && entry.getClass().isInstance(target.getClass())) 775 return true;
776 return true; 776 return false;
777 return false; 777 }
778 } 778
779 779 public boolean containsObfArgument(ArgumentEntry obfArgumentEntry) {
780 public boolean containsObfArgument(ArgumentEntry obfArgumentEntry) { 780 // check the behavior
781 // check the behavior 781 if (!containsObfBehavior(obfArgumentEntry.getBehaviorEntry())) {
782 if (!containsObfBehavior(obfArgumentEntry.getBehaviorEntry())) { 782 return false;
783 return false; 783 }
784 } 784
785 785 // check the argument
786 // check the argument 786 return obfArgumentEntry.getIndex() < obfArgumentEntry.getBehaviorEntry().getSignature().getArgumentTypes().size();
787 return obfArgumentEntry.getIndex() < obfArgumentEntry.getBehaviorEntry().getSignature().getArgumentTypes().size(); 787
788 788 }
789 } 789
790 790 public boolean containsObfEntry(Entry obfEntry) {
791 public boolean containsObfEntry(Entry obfEntry) { 791 if (obfEntry instanceof ClassEntry) {
792 if (obfEntry instanceof ClassEntry) { 792 return containsObfClass((ClassEntry) obfEntry);
793 return containsObfClass((ClassEntry) obfEntry); 793 } else if (obfEntry instanceof FieldEntry) {
794 } else if (obfEntry instanceof FieldEntry) { 794 return containsObfField((FieldEntry) obfEntry);
795 return containsObfField((FieldEntry) obfEntry); 795 } else if (obfEntry instanceof BehaviorEntry) {
796 } else if (obfEntry instanceof BehaviorEntry) { 796 return containsObfBehavior((BehaviorEntry) obfEntry);
797 return containsObfBehavior((BehaviorEntry) obfEntry); 797 } else if (obfEntry instanceof ArgumentEntry) {
798 } else if (obfEntry instanceof ArgumentEntry) { 798 return containsObfArgument((ArgumentEntry) obfEntry);
799 return containsObfArgument((ArgumentEntry) obfEntry); 799 } else if (obfEntry instanceof LocalVariableEntry) {
800 } else if (obfEntry instanceof LocalVariableEntry) { 800 // TODO: Implement it
801 // TODO: Implement it 801 return false;
802 return false; 802 } else {
803 } else { 803 throw new Error("Entry type not supported: " + obfEntry.getClass().getName());
804 throw new Error("Entry type not supported: " + obfEntry.getClass().getName()); 804 }
805 } 805 }
806 } 806
807 807 public MethodEntry getBridgedMethod(MethodEntry bridgeMethodEntry) {
808 public MethodEntry getBridgedMethod(MethodEntry bridgeMethodEntry) { 808 return this.bridgedMethods.get(bridgeMethodEntry);
809 return this.bridgedMethods.get(bridgeMethodEntry); 809 }
810 } 810
811 811 public List<ClassEntry> getObfClassChain(ClassEntry obfClassEntry) {
812 public List<ClassEntry> getObfClassChain(ClassEntry obfClassEntry) { 812
813 813 // build class chain in inner-to-outer order
814 // build class chain in inner-to-outer order 814 List<ClassEntry> obfClassChain = Lists.newArrayList(obfClassEntry);
815 List<ClassEntry> obfClassChain = Lists.newArrayList(obfClassEntry); 815 ClassEntry checkClassEntry = obfClassEntry;
816 ClassEntry checkClassEntry = obfClassEntry; 816 while (true) {
817 while (true) { 817 ClassEntry obfOuterClassEntry = getOuterClass(checkClassEntry);
818 ClassEntry obfOuterClassEntry = getOuterClass(checkClassEntry); 818 if (obfOuterClassEntry != null) {
819 if (obfOuterClassEntry != null) { 819 obfClassChain.add(obfOuterClassEntry);
820 obfClassChain.add(obfOuterClassEntry); 820 checkClassEntry = obfOuterClassEntry;
821 checkClassEntry = obfOuterClassEntry; 821 } else {
822 } else { 822 break;
823 break; 823 }
824 } 824 }
825 } 825
826 826 // switch to outer-to-inner order
827 // switch to outer-to-inner order 827 Collections.reverse(obfClassChain);
828 Collections.reverse(obfClassChain); 828
829 829 return obfClassChain;
830 return obfClassChain; 830 }
831 }
832} 831}
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
index 9bd6219a..bacb1aac 100644
--- a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
@@ -8,87 +8,86 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
14
15import java.util.List;
16
17import javax.swing.tree.DefaultMutableTreeNode;
18
19import cuchaz.enigma.mapping.ClassEntry; 15import cuchaz.enigma.mapping.ClassEntry;
20import cuchaz.enigma.mapping.MethodEntry; 16import cuchaz.enigma.mapping.MethodEntry;
21import cuchaz.enigma.mapping.Translator; 17import cuchaz.enigma.mapping.Translator;
22 18
19import javax.swing.tree.DefaultMutableTreeNode;
20import java.util.List;
21
23public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { 22public class MethodImplementationsTreeNode extends DefaultMutableTreeNode {
24 23
25 private Translator deobfuscatingTranslator; 24 private Translator deobfuscatingTranslator;
26 private MethodEntry entry; 25 private MethodEntry entry;
27 26
28 public MethodImplementationsTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) { 27 public MethodImplementationsTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) {
29 if (entry == null) { 28 if (entry == null) {
30 throw new IllegalArgumentException("Entry cannot be null!"); 29 throw new IllegalArgumentException("Entry cannot be null!");
31 } 30 }
32 31
33 this.deobfuscatingTranslator = deobfuscatingTranslator; 32 this.deobfuscatingTranslator = deobfuscatingTranslator;
34 this.entry = entry; 33 this.entry = entry;
35 } 34 }
36 35
37 public MethodEntry getMethodEntry() { 36 public static MethodImplementationsTreeNode findNode(MethodImplementationsTreeNode node, MethodEntry entry) {
38 return this.entry; 37 // is this the node?
39 } 38 if (node.getMethodEntry().equals(entry)) {
40 39 return node;
41 public String getDeobfClassName() { 40 }
42 return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); 41
43 } 42 // recurse
44 43 for (int i = 0; i < node.getChildCount(); i++) {
45 public String getDeobfMethodName() { 44 MethodImplementationsTreeNode foundNode = findNode((MethodImplementationsTreeNode) node.getChildAt(i), entry);
46 return this.deobfuscatingTranslator.translate(this.entry); 45 if (foundNode != null) {
47 } 46 return foundNode;
48 47 }
49 @Override 48 }
50 public String toString() { 49 return null;
51 String className = getDeobfClassName(); 50 }
52 if (className == null) { 51
53 className = this.entry.getClassName(); 52 public MethodEntry getMethodEntry() {
54 } 53 return this.entry;
55 54 }
56 String methodName = getDeobfMethodName(); 55
57 if (methodName == null) { 56 public String getDeobfClassName() {
58 methodName = this.entry.getName(); 57 return this.deobfuscatingTranslator.translateClass(this.entry.getClassName());
59 } 58 }
60 return className + "." + methodName + "()"; 59
61 } 60 public String getDeobfMethodName() {
62 61 return this.deobfuscatingTranslator.translate(this.entry);
63 public void load(JarIndex index) { 62 }
64 63
65 // get all method implementations 64 @Override
66 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList(); 65 public String toString() {
67 for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) { 66 String className = getDeobfClassName();
68 MethodEntry methodEntry = new MethodEntry(new ClassEntry(implementingClassName), this.entry.getName(), this.entry.getSignature() 67 if (className == null) {
69 ); 68 className = this.entry.getClassName();
70 if (index.containsObfBehavior(methodEntry)) { 69 }
71 nodes.add(new MethodImplementationsTreeNode(this.deobfuscatingTranslator, methodEntry)); 70
72 } 71 String methodName = getDeobfMethodName();
73 } 72 if (methodName == null) {
74 73 methodName = this.entry.getName();
75 // add them to this node 74 }
76 nodes.forEach(this::add); 75 return className + "." + methodName + "()";
77 } 76 }
78 77
79 public static MethodImplementationsTreeNode findNode(MethodImplementationsTreeNode node, MethodEntry entry) { 78 public void load(JarIndex index) {
80 // is this the node? 79
81 if (node.getMethodEntry().equals(entry)) { 80 // get all method implementations
82 return node; 81 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList();
83 } 82 for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) {
84 83 MethodEntry methodEntry = new MethodEntry(new ClassEntry(implementingClassName), this.entry.getName(), this.entry.getSignature()
85 // recurse 84 );
86 for (int i = 0; i < node.getChildCount(); i++) { 85 if (index.containsObfBehavior(methodEntry)) {
87 MethodImplementationsTreeNode foundNode = findNode((MethodImplementationsTreeNode) node.getChildAt(i), entry); 86 nodes.add(new MethodImplementationsTreeNode(this.deobfuscatingTranslator, methodEntry));
88 if (foundNode != null) { 87 }
89 return foundNode; 88 }
90 } 89
91 } 90 // add them to this node
92 return null; 91 nodes.forEach(this::add);
93 } 92 }
94} 93}
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
index b65b8c10..4f84dd09 100644
--- a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
@@ -8,97 +8,96 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
14
15import java.util.List;
16
17import javax.swing.tree.DefaultMutableTreeNode;
18
19import cuchaz.enigma.mapping.ClassEntry; 15import cuchaz.enigma.mapping.ClassEntry;
20import cuchaz.enigma.mapping.MethodEntry; 16import cuchaz.enigma.mapping.MethodEntry;
21import cuchaz.enigma.mapping.Translator; 17import cuchaz.enigma.mapping.Translator;
22 18
19import javax.swing.tree.DefaultMutableTreeNode;
20import java.util.List;
21
23public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { 22public class MethodInheritanceTreeNode extends DefaultMutableTreeNode {
24 23
25 private Translator deobfuscatingTranslator; 24 private Translator deobfuscatingTranslator;
26 private MethodEntry entry; 25 private MethodEntry entry;
27 private boolean isImplemented; 26 private boolean isImplemented;
28 27
29 public MethodInheritanceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry, boolean isImplemented) { 28 public MethodInheritanceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry, boolean isImplemented) {
30 this.deobfuscatingTranslator = deobfuscatingTranslator; 29 this.deobfuscatingTranslator = deobfuscatingTranslator;
31 this.entry = entry; 30 this.entry = entry;
32 this.isImplemented = isImplemented; 31 this.isImplemented = isImplemented;
33 } 32 }
34 33
35 public MethodEntry getMethodEntry() { 34 public static MethodInheritanceTreeNode findNode(MethodInheritanceTreeNode node, MethodEntry entry) {
36 return this.entry; 35 // is this the node?
37 } 36 if (node.getMethodEntry().equals(entry)) {
38 37 return node;
39 public String getDeobfClassName() { 38 }
40 return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); 39
41 } 40 // recurse
42 41 for (int i = 0; i < node.getChildCount(); i++) {
43 public String getDeobfMethodName() { 42 MethodInheritanceTreeNode foundNode = findNode((MethodInheritanceTreeNode) node.getChildAt(i), entry);
44 return this.deobfuscatingTranslator.translate(this.entry); 43 if (foundNode != null) {
45 } 44 return foundNode;
46 45 }
47 public boolean isImplemented() { 46 }
48 return this.isImplemented; 47 return null;
49 } 48 }
50 49
51 @Override 50 public MethodEntry getMethodEntry() {
52 public String toString() { 51 return this.entry;
53 String className = getDeobfClassName(); 52 }
54 if (className == null) { 53
55 className = this.entry.getClassName(); 54 public String getDeobfClassName() {
56 } 55 return this.deobfuscatingTranslator.translateClass(this.entry.getClassName());
57 56 }
58 if (!this.isImplemented) { 57
59 return className; 58 public String getDeobfMethodName() {
60 } else { 59 return this.deobfuscatingTranslator.translate(this.entry);
61 String methodName = getDeobfMethodName(); 60 }
62 if (methodName == null) { 61
63 methodName = this.entry.getName(); 62 public boolean isImplemented() {
64 } 63 return this.isImplemented;
65 return className + "." + methodName + "()"; 64 }
66 } 65
67 } 66 @Override
68 67 public String toString() {
69 public void load(JarIndex index, boolean recurse) { 68 String className = getDeobfClassName();
70 // get all the child nodes 69 if (className == null) {
71 List<MethodInheritanceTreeNode> nodes = Lists.newArrayList(); 70 className = this.entry.getClassName();
72 for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(this.entry.getClassEntry())) { 71 }
73 MethodEntry methodEntry = new MethodEntry(subclassEntry, this.entry.getName(), this.entry.getSignature() 72
74 ); 73 if (!this.isImplemented) {
75 nodes.add(new MethodInheritanceTreeNode(this.deobfuscatingTranslator, methodEntry, index.containsObfBehavior(methodEntry) 74 return className;
76 )); 75 } else {
77 } 76 String methodName = getDeobfMethodName();
78 77 if (methodName == null) {
79 // add them to this node 78 methodName = this.entry.getName();
80 nodes.forEach(this::add); 79 }
81 80 return className + "." + methodName + "()";
82 if (recurse) { 81 }
83 for (MethodInheritanceTreeNode node : nodes) { 82 }
84 node.load(index, true); 83
85 } 84 public void load(JarIndex index, boolean recurse) {
86 } 85 // get all the child nodes
87 } 86 List<MethodInheritanceTreeNode> nodes = Lists.newArrayList();
88 87 for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(this.entry.getClassEntry())) {
89 public static MethodInheritanceTreeNode findNode(MethodInheritanceTreeNode node, MethodEntry entry) { 88 MethodEntry methodEntry = new MethodEntry(subclassEntry, this.entry.getName(), this.entry.getSignature()
90 // is this the node? 89 );
91 if (node.getMethodEntry().equals(entry)) { 90 nodes.add(new MethodInheritanceTreeNode(this.deobfuscatingTranslator, methodEntry, index.containsObfBehavior(methodEntry)
92 return node; 91 ));
93 } 92 }
94 93
95 // recurse 94 // add them to this node
96 for (int i = 0; i < node.getChildCount(); i++) { 95 nodes.forEach(this::add);
97 MethodInheritanceTreeNode foundNode = findNode((MethodInheritanceTreeNode) node.getChildAt(i), entry); 96
98 if (foundNode != null) { 97 if (recurse) {
99 return foundNode; 98 for (MethodInheritanceTreeNode node : nodes) {
100 } 99 node.load(index, true);
101 } 100 }
102 return null; 101 }
103 } 102 }
104} 103}
diff --git a/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java
index 93923467..04693637 100644
--- a/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java
@@ -8,12 +8,13 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import cuchaz.enigma.mapping.Entry; 14import cuchaz.enigma.mapping.Entry;
14 15
15public interface ReferenceTreeNode<E extends Entry, C extends Entry> { 16public interface ReferenceTreeNode<E extends Entry, C extends Entry> {
16 E getEntry(); 17 E getEntry();
17 18
18 EntryReference<E, C> getReference(); 19 EntryReference<E, C> getReference();
19} 20}
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
index 719930e9..19250c8d 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
@@ -8,174 +8,173 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.HashMultimap; 14import com.google.common.collect.HashMultimap;
14import com.google.common.collect.Lists; 15import com.google.common.collect.Lists;
15import com.google.common.collect.Maps; 16import com.google.common.collect.Maps;
16import com.google.common.collect.Multimap; 17import com.google.common.collect.Multimap;
17
18import com.strobel.decompiler.languages.Region; 18import com.strobel.decompiler.languages.Region;
19import com.strobel.decompiler.languages.java.ast.AstNode; 19import com.strobel.decompiler.languages.java.ast.AstNode;
20import com.strobel.decompiler.languages.java.ast.Identifier; 20import com.strobel.decompiler.languages.java.ast.Identifier;
21import cuchaz.enigma.mapping.Entry;
21 22
22import java.util.Collection; 23import java.util.Collection;
23import java.util.List; 24import java.util.List;
24import java.util.Map; 25import java.util.Map;
25import java.util.TreeMap; 26import java.util.TreeMap;
26 27
27import cuchaz.enigma.mapping.Entry;
28
29public class SourceIndex { 28public class SourceIndex {
30 29
31 private String source; 30 private String source;
32 private TreeMap<Token, EntryReference<Entry, Entry>> tokenToReference; 31 private TreeMap<Token, EntryReference<Entry, Entry>> tokenToReference;
33 private Multimap<EntryReference<Entry, Entry>, Token> referenceToTokens; 32 private Multimap<EntryReference<Entry, Entry>, Token> referenceToTokens;
34 private Map<Entry, Token> declarationToToken; 33 private Map<Entry, Token> declarationToToken;
35 private List<Integer> lineOffsets; 34 private List<Integer> lineOffsets;
36 private boolean ignoreBadTokens; 35 private boolean ignoreBadTokens;
37 36
38 public SourceIndex(String source) { 37 public SourceIndex(String source) {
39 this(source, true); 38 this(source, true);
40 } 39 }
41 40
42 public SourceIndex(String source, boolean ignoreBadTokens) { 41 public SourceIndex(String source, boolean ignoreBadTokens) {
43 this.source = source; 42 this.source = source;
44 this.ignoreBadTokens = ignoreBadTokens; 43 this.ignoreBadTokens = ignoreBadTokens;
45 this.tokenToReference = Maps.newTreeMap(); 44 this.tokenToReference = Maps.newTreeMap();
46 this.referenceToTokens = HashMultimap.create(); 45 this.referenceToTokens = HashMultimap.create();
47 this.declarationToToken = Maps.newHashMap(); 46 this.declarationToToken = Maps.newHashMap();
48 this.lineOffsets = Lists.newArrayList(); 47 this.lineOffsets = Lists.newArrayList();
49 48
50 // count the lines 49 // count the lines
51 this.lineOffsets.add(0); 50 this.lineOffsets.add(0);
52 for (int i = 0; i < source.length(); i++) { 51 for (int i = 0; i < source.length(); i++) {
53 if (source.charAt(i) == '\n') { 52 if (source.charAt(i) == '\n') {
54 this.lineOffsets.add(i + 1); 53 this.lineOffsets.add(i + 1);
55 } 54 }
56 } 55 }
57 } 56 }
58 57
59 public String getSource() { 58 public String getSource() {
60 return this.source; 59 return this.source;
61 } 60 }
62 61
63 public Token getToken(AstNode node) { 62 public Token getToken(AstNode node) {
64 63
65 // get the text of the node 64 // get the text of the node
66 String name = ""; 65 String name = "";
67 if (node instanceof Identifier) { 66 if (node instanceof Identifier) {
68 name = ((Identifier) node).getName(); 67 name = ((Identifier) node).getName();
69 } 68 }
70 69
71 // get a token for this node's region 70 // get a token for this node's region
72 Region region = node.getRegion(); 71 Region region = node.getRegion();
73 if (region.getBeginLine() == 0 || region.getEndLine() == 0) { 72 if (region.getBeginLine() == 0 || region.getEndLine() == 0) {
74 // DEBUG 73 // DEBUG
75 System.err.println(String.format("WARNING: %s \"%s\" has invalid region: %s", node.getNodeType(), name, region)); 74 System.err.println(String.format("WARNING: %s \"%s\" has invalid region: %s", node.getNodeType(), name, region));
76 return null; 75 return null;
77 } 76 }
78 Token token = new Token(toPos(region.getBeginLine(), region.getBeginColumn()), toPos(region.getEndLine(), region.getEndColumn()), this.source); 77 Token token = new Token(toPos(region.getBeginLine(), region.getBeginColumn()), toPos(region.getEndLine(), region.getEndColumn()), this.source);
79 if (token.start == 0) { 78 if (token.start == 0) {
80 // DEBUG 79 // DEBUG
81 System.err.println(String.format("WARNING: %s \"%s\" has invalid start: %s", node.getNodeType(), name, region)); 80 System.err.println(String.format("WARNING: %s \"%s\" has invalid start: %s", node.getNodeType(), name, region));
82 return null; 81 return null;
83 } 82 }
84 83
85 // DEBUG 84 // DEBUG
86 // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) ); 85 // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) );
87 86
88 // if the token has a $ in it, something's wrong. Ignore this token 87 // if the token has a $ in it, something's wrong. Ignore this token
89 if (name.lastIndexOf('$') >= 0 && this.ignoreBadTokens) { 88 if (name.lastIndexOf('$') >= 0 && this.ignoreBadTokens) {
90 // DEBUG 89 // DEBUG
91 System.err.println(String.format("WARNING: %s \"%s\" is probably a bad token. It was ignored", node.getNodeType(), name)); 90 System.err.println(String.format("WARNING: %s \"%s\" is probably a bad token. It was ignored", node.getNodeType(), name));
92 return null; 91 return null;
93 } 92 }
94 93
95 return token; 94 return token;
96 } 95 }
97 96
98 public void addReference(AstNode node, Entry deobfEntry, Entry deobfContext) { 97 public void addReference(AstNode node, Entry deobfEntry, Entry deobfContext) {
99 Token token = getToken(node); 98 Token token = getToken(node);
100 if (token != null) { 99 if (token != null) {
101 EntryReference<Entry, Entry> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext); 100 EntryReference<Entry, Entry> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext);
102 this.tokenToReference.put(token, deobfReference); 101 this.tokenToReference.put(token, deobfReference);
103 this.referenceToTokens.put(deobfReference, token); 102 this.referenceToTokens.put(deobfReference, token);
104 } 103 }
105 } 104 }
106 105
107 public void addDeclaration(AstNode node, Entry deobfEntry) { 106 public void addDeclaration(AstNode node, Entry deobfEntry) {
108 Token token = getToken(node); 107 Token token = getToken(node);
109 if (token != null) { 108 if (token != null) {
110 EntryReference<Entry, Entry> reference = new EntryReference<>(deobfEntry, token.text); 109 EntryReference<Entry, Entry> reference = new EntryReference<>(deobfEntry, token.text);
111 this.tokenToReference.put(token, reference); 110 this.tokenToReference.put(token, reference);
112 this.referenceToTokens.put(reference, token); 111 this.referenceToTokens.put(reference, token);
113 this.declarationToToken.put(deobfEntry, token); 112 this.declarationToToken.put(deobfEntry, token);
114 } 113 }
115 } 114 }
116 115
117 public Token getReferenceToken(int pos) { 116 public Token getReferenceToken(int pos) {
118 Token token = this.tokenToReference.floorKey(new Token(pos, pos, null)); 117 Token token = this.tokenToReference.floorKey(new Token(pos, pos, null));
119 if (token != null && token.contains(pos)) { 118 if (token != null && token.contains(pos)) {
120 return token; 119 return token;
121 } 120 }
122 return null; 121 return null;
123 } 122 }
124 123
125 public Collection<Token> getReferenceTokens(EntryReference<Entry, Entry> deobfReference) { 124 public Collection<Token> getReferenceTokens(EntryReference<Entry, Entry> deobfReference) {
126 return this.referenceToTokens.get(deobfReference); 125 return this.referenceToTokens.get(deobfReference);
127 } 126 }
128 127
129 public EntryReference<Entry, Entry> getDeobfReference(Token token) { 128 public EntryReference<Entry, Entry> getDeobfReference(Token token) {
130 if (token == null) { 129 if (token == null) {
131 return null; 130 return null;
132 } 131 }
133 return this.tokenToReference.get(token); 132 return this.tokenToReference.get(token);
134 } 133 }
135 134
136 public void replaceDeobfReference(Token token, EntryReference<Entry, Entry> newDeobfReference) { 135 public void replaceDeobfReference(Token token, EntryReference<Entry, Entry> newDeobfReference) {
137 EntryReference<Entry, Entry> oldDeobfReference = this.tokenToReference.get(token); 136 EntryReference<Entry, Entry> oldDeobfReference = this.tokenToReference.get(token);
138 this.tokenToReference.put(token, newDeobfReference); 137 this.tokenToReference.put(token, newDeobfReference);
139 Collection<Token> tokens = this.referenceToTokens.get(oldDeobfReference); 138 Collection<Token> tokens = this.referenceToTokens.get(oldDeobfReference);
140 this.referenceToTokens.removeAll(oldDeobfReference); 139 this.referenceToTokens.removeAll(oldDeobfReference);
141 this.referenceToTokens.putAll(newDeobfReference, tokens); 140 this.referenceToTokens.putAll(newDeobfReference, tokens);
142 } 141 }
143 142
144 public Iterable<Token> referenceTokens() { 143 public Iterable<Token> referenceTokens() {
145 return this.tokenToReference.keySet(); 144 return this.tokenToReference.keySet();
146 } 145 }
147 146
148 public Iterable<Token> declarationTokens() { 147 public Iterable<Token> declarationTokens() {
149 return this.declarationToToken.values(); 148 return this.declarationToToken.values();
150 } 149 }
151 150
152 public Iterable<Entry> declarations() { 151 public Iterable<Entry> declarations() {
153 return this.declarationToToken.keySet(); 152 return this.declarationToToken.keySet();
154 } 153 }
155 154
156 public Token getDeclarationToken(Entry deobfEntry) { 155 public Token getDeclarationToken(Entry deobfEntry) {
157 return this.declarationToToken.get(deobfEntry); 156 return this.declarationToToken.get(deobfEntry);
158 } 157 }
159 158
160 public int getLineNumber(int pos) { 159 public int getLineNumber(int pos) {
161 // line number is 1-based 160 // line number is 1-based
162 int line = 0; 161 int line = 0;
163 for (Integer offset : this.lineOffsets) { 162 for (Integer offset : this.lineOffsets) {
164 if (offset > pos) { 163 if (offset > pos) {
165 break; 164 break;
166 } 165 }
167 line++; 166 line++;
168 } 167 }
169 return line; 168 return line;
170 } 169 }
171 170
172 public int getColumnNumber(int pos) { 171 public int getColumnNumber(int pos) {
173 // column number is 1-based 172 // column number is 1-based
174 return pos - this.lineOffsets.get(getLineNumber(pos) - 1) + 1; 173 return pos - this.lineOffsets.get(getLineNumber(pos) - 1) + 1;
175 } 174 }
176 175
177 private int toPos(int line, int col) { 176 private int toPos(int line, int col) {
178 // line and col are 1-based 177 // line and col are 1-based
179 return this.lineOffsets.get(line - 1) + col - 1; 178 return this.lineOffsets.get(line - 1) + col - 1;
180 } 179 }
181} 180}
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java
index bfd5a562..4febf256 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.HashMultimap; 14import com.google.common.collect.HashMultimap;
@@ -26,186 +27,179 @@ import java.util.Map;
26 27
27public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { 28public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
28 29
29 private BehaviorEntry behaviorEntry; 30 private BehaviorEntry behaviorEntry;
30 31
31 // TODO: Really fix Procyon index problem with inner classes 32 // TODO: Really fix Procyon index problem with inner classes
32 private int argumentPosition; 33 private int argumentPosition;
33 private int localsPosition; 34 private int localsPosition;
34 private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create(); 35 private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create();
35 private Map<String, Entry> identifierEntryCache = new HashMap<>(); 36 private Map<String, Entry> identifierEntryCache = new HashMap<>();
36 37
37 public SourceIndexBehaviorVisitor(BehaviorEntry behaviorEntry) { 38 public SourceIndexBehaviorVisitor(BehaviorEntry behaviorEntry) {
38 this.behaviorEntry = behaviorEntry; 39 this.behaviorEntry = behaviorEntry;
39 this.argumentPosition = 0; 40 this.argumentPosition = 0;
40 this.localsPosition = 0; 41 this.localsPosition = 0;
41 } 42 }
42 43
43 @Override 44 @Override
44 public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) { 45 public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) {
45 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 46 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
46 47
47 // get the behavior entry 48 // get the behavior entry
48 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 49 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
49 BehaviorEntry behaviorEntry = null; 50 BehaviorEntry behaviorEntry = null;
50 if (ref instanceof MethodReference) { 51 if (ref instanceof MethodReference) {
51 MethodReference methodRef = (MethodReference) ref; 52 MethodReference methodRef = (MethodReference) ref;
52 if (methodRef.isConstructor()) { 53 if (methodRef.isConstructor()) {
53 behaviorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature())); 54 behaviorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature()));
54 } else if (methodRef.isTypeInitializer()) { 55 } else if (methodRef.isTypeInitializer()) {
55 behaviorEntry = new ConstructorEntry(classEntry); 56 behaviorEntry = new ConstructorEntry(classEntry);
56 } else { 57 } else {
57 behaviorEntry = new MethodEntry(classEntry, ref.getName(), new Signature(ref.getErasedSignature())); 58 behaviorEntry = new MethodEntry(classEntry, ref.getName(), new Signature(ref.getErasedSignature()));
58 } 59 }
59 } 60 }
60 if (behaviorEntry != null) { 61 if (behaviorEntry != null) {
61 // get the node for the token 62 // get the node for the token
62 AstNode tokenNode = null; 63 AstNode tokenNode = null;
63 if (node.getTarget() instanceof MemberReferenceExpression) { 64 if (node.getTarget() instanceof MemberReferenceExpression) {
64 tokenNode = ((MemberReferenceExpression) node.getTarget()).getMemberNameToken(); 65 tokenNode = ((MemberReferenceExpression) node.getTarget()).getMemberNameToken();
65 } else if (node.getTarget() instanceof SuperReferenceExpression) { 66 } else if (node.getTarget() instanceof SuperReferenceExpression) {
66 tokenNode = node.getTarget(); 67 tokenNode = node.getTarget();
67 } else if (node.getTarget() instanceof ThisReferenceExpression) { 68 } else if (node.getTarget() instanceof ThisReferenceExpression) {
68 tokenNode = node.getTarget(); 69 tokenNode = node.getTarget();
69 } 70 }
70 if (tokenNode != null) { 71 if (tokenNode != null) {
71 index.addReference(tokenNode, behaviorEntry, this.behaviorEntry); 72 index.addReference(tokenNode, behaviorEntry, this.behaviorEntry);
72 } 73 }
73 } 74 }
74 75
75 // Check for identifier 76 // Check for identifier
76 node.getArguments().stream().filter(expression -> expression instanceof IdentifierExpression) 77 node.getArguments().stream().filter(expression -> expression instanceof IdentifierExpression)
77 .forEach(expression -> this.checkIdentifier((IdentifierExpression) expression, index)); 78 .forEach(expression -> this.checkIdentifier((IdentifierExpression) expression, index));
78 return recurse(node, index); 79 return recurse(node, index);
79 } 80 }
80 81
81 @Override 82 @Override
82 public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { 83 public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) {
83 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 84 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
84 if (ref != null) { 85 if (ref != null) {
85 // make sure this is actually a field 86 // make sure this is actually a field
86 if (ref.getErasedSignature().indexOf('(') >= 0) { 87 if (ref.getErasedSignature().indexOf('(') >= 0) {
87 throw new Error("Expected a field here! got " + ref); 88 throw new Error("Expected a field here! got " + ref);
88 } 89 }
89 90
90 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 91 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
91 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); 92 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature()));
92 index.addReference(node.getMemberNameToken(), fieldEntry, this.behaviorEntry); 93 index.addReference(node.getMemberNameToken(), fieldEntry, this.behaviorEntry);
93 } 94 }
94 95
95 return recurse(node, index); 96 return recurse(node, index);
96 } 97 }
97 98
98 @Override 99 @Override
99 public Void visitSimpleType(SimpleType node, SourceIndex index) { 100 public Void visitSimpleType(SimpleType node, SourceIndex index) {
100 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); 101 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE);
101 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { 102 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) {
102 ClassEntry classEntry = new ClassEntry(ref.getInternalName()); 103 ClassEntry classEntry = new ClassEntry(ref.getInternalName());
103 index.addReference(node.getIdentifierToken(), classEntry, this.behaviorEntry); 104 index.addReference(node.getIdentifierToken(), classEntry, this.behaviorEntry);
104 } 105 }
105 106
106 return recurse(node, index); 107 return recurse(node, index);
107 } 108 }
108 109
109 @Override 110 @Override
110 public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { 111 public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) {
111 ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION); 112 ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION);
112 if (def.getMethod() instanceof MemberReference && def.getMethod() instanceof MethodReference) 113 if (def.getMethod() instanceof MemberReference && def.getMethod() instanceof MethodReference) {
113 { 114 ArgumentEntry argumentEntry = new ArgumentEntry(ProcyonEntryFactory.getBehaviorEntry((MethodReference) def.getMethod()),
114 ArgumentEntry argumentEntry = new ArgumentEntry(ProcyonEntryFactory.getBehaviorEntry((MethodReference) def.getMethod()), 115 argumentPosition++, node.getName());
115 argumentPosition++, node.getName()); 116 Identifier identifier = node.getNameToken();
116 Identifier identifier = node.getNameToken(); 117 // cache the argument entry and the identifier
117 // cache the argument entry and the identifier 118 identifierEntryCache.put(identifier.getName(), argumentEntry);
118 identifierEntryCache.put(identifier.getName(), argumentEntry); 119 index.addDeclaration(identifier, argumentEntry);
119 index.addDeclaration(identifier, argumentEntry); 120 }
120 } 121
121 122 return recurse(node, index);
122 return recurse(node, index); 123 }
123 } 124
124 125 @Override
125 @Override 126 public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) {
126 public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { 127 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
127 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 128 if (ref != null) {
128 if (ref != null) { 129 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
129 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 130 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature()));
130 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); 131 index.addReference(node.getIdentifierToken(), fieldEntry, this.behaviorEntry);
131 index.addReference(node.getIdentifierToken(), fieldEntry, this.behaviorEntry); 132 } else
132 } 133 this.checkIdentifier(node, index);
133 else 134 return recurse(node, index);
134 this.checkIdentifier(node, index); 135 }
135 return recurse(node, index); 136
136 } 137 private void checkIdentifier(IdentifierExpression node, SourceIndex index) {
137 138 if (identifierEntryCache.containsKey(node.getIdentifier())) // If it's in the argument cache, create a token!
138 private void checkIdentifier(IdentifierExpression node, SourceIndex index) 139 index.addDeclaration(node.getIdentifierToken(), identifierEntryCache.get(node.getIdentifier()));
139 { 140 else
140 if (identifierEntryCache.containsKey(node.getIdentifier())) // If it's in the argument cache, create a token! 141 unmatchedIdentifier.put(node.getIdentifier(), node.getIdentifierToken()); // Not matched actually, put it!
141 index.addDeclaration(node.getIdentifierToken(), identifierEntryCache.get(node.getIdentifier())); 142 }
142 else 143
143 unmatchedIdentifier.put(node.getIdentifier(), node.getIdentifierToken()); // Not matched actually, put it! 144 private void addDeclarationToUnmatched(String key, SourceIndex index) {
144 } 145 Entry entry = identifierEntryCache.get(key);
145 146
146 private void addDeclarationToUnmatched(String key, SourceIndex index) 147 // This cannot happened in theory
147 { 148 if (entry == null)
148 Entry entry = identifierEntryCache.get(key); 149 return;
149 150 for (Identifier identifier : unmatchedIdentifier.get(key))
150 // This cannot happened in theory 151 index.addDeclaration(identifier, entry);
151 if (entry == null) 152 unmatchedIdentifier.removeAll(key);
152 return; 153 }
153 for (Identifier identifier : unmatchedIdentifier.get(key)) 154
154 index.addDeclaration(identifier, entry); 155 @Override
155 unmatchedIdentifier.removeAll(key); 156 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) {
156 } 157 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
157 158 if (ref != null) {
158 @Override 159 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
159 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { 160 ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature()));
160 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 161 if (node.getType() instanceof SimpleType) {
161 if (ref != null) { 162 SimpleType simpleTypeNode = (SimpleType) node.getType();
162 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 163 index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, this.behaviorEntry);
163 ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature())); 164 }
164 if (node.getType() instanceof SimpleType) { 165 }
165 SimpleType simpleTypeNode = (SimpleType) node.getType(); 166
166 index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, this.behaviorEntry); 167 return recurse(node, index);
167 } 168 }
168 } 169
169 170 @Override
170 return recurse(node, index); 171 public Void visitForEachStatement(ForEachStatement node, SourceIndex index) {
171 } 172 if (node.getVariableType() instanceof SimpleType) {
172 173 SimpleType type = (SimpleType) node.getVariableType();
173 @Override 174 TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE);
174 public Void visitForEachStatement(ForEachStatement node, SourceIndex index) { 175 Identifier identifier = node.getVariableNameToken();
175 if (node.getVariableType() instanceof SimpleType) 176 String signature = Descriptor.of(typeReference.getErasedDescription());
176 { 177 LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, localsPosition++, identifier.getName(), new Type(signature));
177 SimpleType type = (SimpleType) node.getVariableType(); 178 identifierEntryCache.put(identifier.getName(), localVariableEntry);
178 TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE); 179 addDeclarationToUnmatched(identifier.getName(), index);
179 Identifier identifier = node.getVariableNameToken(); 180 index.addDeclaration(identifier, localVariableEntry);
180 String signature = Descriptor.of(typeReference.getErasedDescription()); 181 }
181 LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, localsPosition++, identifier.getName(), new Type(signature)); 182 return recurse(node, index);
182 identifierEntryCache.put(identifier.getName(), localVariableEntry); 183 }
183 addDeclarationToUnmatched(identifier.getName(), index); 184
184 index.addDeclaration(identifier, localVariableEntry); 185 @Override
185 } 186 public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) {
186 return recurse(node, index); 187 AstNodeCollection<VariableInitializer> variables = node.getVariables();
187 } 188
188 189 // Single assignation
189 @Override 190 if (variables.size() == 1) {
190 public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) { 191 VariableInitializer initializer = variables.firstOrNullObject();
191 AstNodeCollection<VariableInitializer> variables = node.getVariables(); 192 if (initializer != null && node.getType() instanceof SimpleType) {
192 193 SimpleType type = (SimpleType) node.getType();
193 // Single assignation 194 TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE);
194 if (variables.size() == 1) 195 String signature = Descriptor.of(typeReference.getErasedDescription());
195 { 196 Identifier identifier = initializer.getNameToken();
196 VariableInitializer initializer = variables.firstOrNullObject(); 197 LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, localsPosition++, initializer.getName(), new Type(signature));
197 if (initializer != null && node.getType() instanceof SimpleType) 198 identifierEntryCache.put(identifier.getName(), localVariableEntry);
198 { 199 addDeclarationToUnmatched(identifier.getName(), index);
199 SimpleType type = (SimpleType) node.getType(); 200 index.addDeclaration(identifier, localVariableEntry);
200 TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE); 201 }
201 String signature = Descriptor.of(typeReference.getErasedDescription()); 202 }
202 Identifier identifier = initializer.getNameToken(); 203 return recurse(node, index);
203 LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, localsPosition++, initializer.getName(), new Type(signature)); 204 }
204 identifierEntryCache.put(identifier.getName(), localVariableEntry);
205 addDeclarationToUnmatched(identifier.getName(), index);
206 index.addDeclaration(identifier, localVariableEntry);
207 }
208 }
209 return recurse(node, index);
210 }
211} 205}
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
index 2a212222..11482163 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.strobel.assembler.metadata.FieldDefinition; 14import com.strobel.assembler.metadata.FieldDefinition;
@@ -20,79 +21,79 @@ import cuchaz.enigma.mapping.*;
20 21
21public class SourceIndexClassVisitor extends SourceIndexVisitor { 22public class SourceIndexClassVisitor extends SourceIndexVisitor {
22 23
23 private ClassEntry classEntry; 24 private ClassEntry classEntry;
24 25
25 public SourceIndexClassVisitor(ClassEntry classEntry) { 26 public SourceIndexClassVisitor(ClassEntry classEntry) {
26 this.classEntry = classEntry; 27 this.classEntry = classEntry;
27 } 28 }
28 29
29 @Override 30 @Override
30 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { 31 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) {
31 // is this this class, or a subtype? 32 // is this this class, or a subtype?
32 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); 33 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
33 ClassEntry classEntry = new ClassEntry(def.getInternalName()); 34 ClassEntry classEntry = new ClassEntry(def.getInternalName());
34 if (!classEntry.equals(this.classEntry)) { 35 if (!classEntry.equals(this.classEntry)) {
35 // it's a sub-type, recurse 36 // it's a sub-type, recurse
36 index.addDeclaration(node.getNameToken(), classEntry); 37 index.addDeclaration(node.getNameToken(), classEntry);
37 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); 38 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index);
38 } 39 }
39 40
40 return recurse(node, index); 41 return recurse(node, index);
41 } 42 }
42 43
43 @Override 44 @Override
44 public Void visitSimpleType(SimpleType node, SourceIndex index) { 45 public Void visitSimpleType(SimpleType node, SourceIndex index) {
45 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); 46 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE);
46 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { 47 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) {
47 ClassEntry classEntry = new ClassEntry(ref.getInternalName()); 48 ClassEntry classEntry = new ClassEntry(ref.getInternalName());
48 index.addReference(node.getIdentifierToken(), classEntry, this.classEntry); 49 index.addReference(node.getIdentifierToken(), classEntry, this.classEntry);
49 } 50 }
50 51
51 return recurse(node, index); 52 return recurse(node, index);
52 } 53 }
53 54
54 @Override 55 @Override
55 public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { 56 public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) {
56 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); 57 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
57 BehaviorEntry behaviorEntry = ProcyonEntryFactory.getBehaviorEntry(def); 58 BehaviorEntry behaviorEntry = ProcyonEntryFactory.getBehaviorEntry(def);
58 AstNode tokenNode = node.getNameToken(); 59 AstNode tokenNode = node.getNameToken();
59 if (behaviorEntry instanceof ConstructorEntry) { 60 if (behaviorEntry instanceof ConstructorEntry) {
60 ConstructorEntry constructorEntry = (ConstructorEntry) behaviorEntry; 61 ConstructorEntry constructorEntry = (ConstructorEntry) behaviorEntry;
61 if (constructorEntry.isStatic()) { 62 if (constructorEntry.isStatic()) {
62 // for static initializers, check elsewhere for the token node 63 // for static initializers, check elsewhere for the token node
63 tokenNode = node.getModifiers().firstOrNullObject(); 64 tokenNode = node.getModifiers().firstOrNullObject();
64 } 65 }
65 } 66 }
66 index.addDeclaration(tokenNode, behaviorEntry); 67 index.addDeclaration(tokenNode, behaviorEntry);
67 return node.acceptVisitor(new SourceIndexBehaviorVisitor(behaviorEntry), index); 68 return node.acceptVisitor(new SourceIndexBehaviorVisitor(behaviorEntry), index);
68 } 69 }
69 70
70 @Override 71 @Override
71 public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { 72 public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) {
72 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); 73 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
73 ConstructorEntry constructorEntry = ProcyonEntryFactory.getConstructorEntry(def); 74 ConstructorEntry constructorEntry = ProcyonEntryFactory.getConstructorEntry(def);
74 index.addDeclaration(node.getNameToken(), constructorEntry); 75 index.addDeclaration(node.getNameToken(), constructorEntry);
75 return node.acceptVisitor(new SourceIndexBehaviorVisitor(constructorEntry), index); 76 return node.acceptVisitor(new SourceIndexBehaviorVisitor(constructorEntry), index);
76 } 77 }
77 78
78 @Override 79 @Override
79 public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { 80 public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) {
80 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); 81 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION);
81 FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); 82 FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def);
82 assert (node.getVariables().size() == 1); 83 assert (node.getVariables().size() == 1);
83 VariableInitializer variable = node.getVariables().firstOrNullObject(); 84 VariableInitializer variable = node.getVariables().firstOrNullObject();
84 index.addDeclaration(variable.getNameToken(), fieldEntry); 85 index.addDeclaration(variable.getNameToken(), fieldEntry);
85 86
86 return recurse(node, index); 87 return recurse(node, index);
87 } 88 }
88 89
89 @Override 90 @Override
90 public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { 91 public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) {
91 // treat enum declarations as field declarations 92 // treat enum declarations as field declarations
92 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); 93 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION);
93 FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); 94 FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def);
94 index.addDeclaration(node.getNameToken(), fieldEntry); 95 index.addDeclaration(node.getNameToken(), fieldEntry);
95 96
96 return recurse(node, index); 97 return recurse(node, index);
97 } 98 }
98} 99}
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
index 40381f43..a94a55b7 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
@@ -8,374 +8,374 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.strobel.assembler.metadata.TypeDefinition; 14import com.strobel.assembler.metadata.TypeDefinition;
14import com.strobel.decompiler.languages.java.ast.*; 15import com.strobel.decompiler.languages.java.ast.*;
15import com.strobel.decompiler.patterns.Pattern; 16import com.strobel.decompiler.patterns.Pattern;
16
17import cuchaz.enigma.mapping.ClassEntry; 17import cuchaz.enigma.mapping.ClassEntry;
18 18
19public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> { 19public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> {
20 20
21 @Override 21 @Override
22 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { 22 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) {
23 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); 23 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
24 ClassEntry classEntry = new ClassEntry(def.getInternalName()); 24 ClassEntry classEntry = new ClassEntry(def.getInternalName());
25 index.addDeclaration(node.getNameToken(), classEntry); 25 index.addDeclaration(node.getNameToken(), classEntry);
26 26
27 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); 27 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index);
28 } 28 }
29 29
30 protected Void recurse(AstNode node, SourceIndex index) { 30 protected Void recurse(AstNode node, SourceIndex index) {
31 for (final AstNode child : node.getChildren()) { 31 for (final AstNode child : node.getChildren()) {
32 child.acceptVisitor(this, index); 32 child.acceptVisitor(this, index);
33 } 33 }
34 return null; 34 return null;
35 } 35 }
36 36
37 @Override 37 @Override
38 public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { 38 public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) {
39 return recurse(node, index); 39 return recurse(node, index);
40 } 40 }
41 41
42 @Override 42 @Override
43 public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { 43 public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) {
44 return recurse(node, index); 44 return recurse(node, index);
45 } 45 }
46 46
47 @Override 47 @Override
48 public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { 48 public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) {
49 return recurse(node, index); 49 return recurse(node, index);
50 } 50 }
51 51
52 @Override 52 @Override
53 public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { 53 public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) {
54 return recurse(node, index); 54 return recurse(node, index);
55 } 55 }
56 56
57 @Override 57 @Override
58 public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { 58 public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) {
59 return recurse(node, index); 59 return recurse(node, index);
60 } 60 }
61 61
62 @Override 62 @Override
63 public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) { 63 public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) {
64 return recurse(node, index); 64 return recurse(node, index);
65 } 65 }
66 66
67 @Override 67 @Override
68 public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { 68 public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) {
69 return recurse(node, index); 69 return recurse(node, index);
70 } 70 }
71 71
72 @Override 72 @Override
73 public Void visitSimpleType(SimpleType node, SourceIndex index) { 73 public Void visitSimpleType(SimpleType node, SourceIndex index) {
74 return recurse(node, index); 74 return recurse(node, index);
75 } 75 }
76 76
77 @Override 77 @Override
78 public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { 78 public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) {
79 return recurse(node, index); 79 return recurse(node, index);
80 } 80 }
81 81
82 @Override 82 @Override
83 public Void visitComment(Comment node, SourceIndex index) { 83 public Void visitComment(Comment node, SourceIndex index) {
84 return recurse(node, index); 84 return recurse(node, index);
85 } 85 }
86 86
87 @Override 87 @Override
88 public Void visitPatternPlaceholder(AstNode node, Pattern pattern, SourceIndex index) { 88 public Void visitPatternPlaceholder(AstNode node, Pattern pattern, SourceIndex index) {
89 return recurse(node, index); 89 return recurse(node, index);
90 } 90 }
91 91
92 @Override 92 @Override
93 public Void visitTypeReference(TypeReferenceExpression node, SourceIndex index) { 93 public Void visitTypeReference(TypeReferenceExpression node, SourceIndex index) {
94 return recurse(node, index); 94 return recurse(node, index);
95 } 95 }
96 96
97 @Override 97 @Override
98 public Void visitJavaTokenNode(JavaTokenNode node, SourceIndex index) { 98 public Void visitJavaTokenNode(JavaTokenNode node, SourceIndex index) {
99 return recurse(node, index); 99 return recurse(node, index);
100 } 100 }
101 101
102 @Override 102 @Override
103 public Void visitIdentifier(Identifier node, SourceIndex index) { 103 public Void visitIdentifier(Identifier node, SourceIndex index) {
104 return recurse(node, index); 104 return recurse(node, index);
105 } 105 }
106 106
107 @Override 107 @Override
108 public Void visitNullReferenceExpression(NullReferenceExpression node, SourceIndex index) { 108 public Void visitNullReferenceExpression(NullReferenceExpression node, SourceIndex index) {
109 return recurse(node, index); 109 return recurse(node, index);
110 } 110 }
111 111
112 @Override 112 @Override
113 public Void visitThisReferenceExpression(ThisReferenceExpression node, SourceIndex index) { 113 public Void visitThisReferenceExpression(ThisReferenceExpression node, SourceIndex index) {
114 return recurse(node, index); 114 return recurse(node, index);
115 } 115 }
116 116
117 @Override 117 @Override
118 public Void visitSuperReferenceExpression(SuperReferenceExpression node, SourceIndex index) { 118 public Void visitSuperReferenceExpression(SuperReferenceExpression node, SourceIndex index) {
119 return recurse(node, index); 119 return recurse(node, index);
120 } 120 }
121 121
122 @Override 122 @Override
123 public Void visitClassOfExpression(ClassOfExpression node, SourceIndex index) { 123 public Void visitClassOfExpression(ClassOfExpression node, SourceIndex index) {
124 return recurse(node, index); 124 return recurse(node, index);
125 } 125 }
126 126
127 @Override 127 @Override
128 public Void visitBlockStatement(BlockStatement node, SourceIndex index) { 128 public Void visitBlockStatement(BlockStatement node, SourceIndex index) {
129 return recurse(node, index); 129 return recurse(node, index);
130 } 130 }
131 131
132 @Override 132 @Override
133 public Void visitExpressionStatement(ExpressionStatement node, SourceIndex index) { 133 public Void visitExpressionStatement(ExpressionStatement node, SourceIndex index) {
134 return recurse(node, index); 134 return recurse(node, index);
135 } 135 }
136 136
137 @Override 137 @Override
138 public Void visitBreakStatement(BreakStatement node, SourceIndex index) { 138 public Void visitBreakStatement(BreakStatement node, SourceIndex index) {
139 return recurse(node, index); 139 return recurse(node, index);
140 } 140 }
141 141
142 @Override 142 @Override
143 public Void visitContinueStatement(ContinueStatement node, SourceIndex index) { 143 public Void visitContinueStatement(ContinueStatement node, SourceIndex index) {
144 return recurse(node, index); 144 return recurse(node, index);
145 } 145 }
146 146
147 @Override 147 @Override
148 public Void visitDoWhileStatement(DoWhileStatement node, SourceIndex index) { 148 public Void visitDoWhileStatement(DoWhileStatement node, SourceIndex index) {
149 return recurse(node, index); 149 return recurse(node, index);
150 } 150 }
151 151
152 @Override 152 @Override
153 public Void visitEmptyStatement(EmptyStatement node, SourceIndex index) { 153 public Void visitEmptyStatement(EmptyStatement node, SourceIndex index) {
154 return recurse(node, index); 154 return recurse(node, index);
155 } 155 }
156 156
157 @Override 157 @Override
158 public Void visitIfElseStatement(IfElseStatement node, SourceIndex index) { 158 public Void visitIfElseStatement(IfElseStatement node, SourceIndex index) {
159 return recurse(node, index); 159 return recurse(node, index);
160 } 160 }
161 161
162 @Override 162 @Override
163 public Void visitLabelStatement(LabelStatement node, SourceIndex index) { 163 public Void visitLabelStatement(LabelStatement node, SourceIndex index) {
164 return recurse(node, index); 164 return recurse(node, index);
165 } 165 }
166 166
167 @Override 167 @Override
168 public Void visitLabeledStatement(LabeledStatement node, SourceIndex index) { 168 public Void visitLabeledStatement(LabeledStatement node, SourceIndex index) {
169 return recurse(node, index); 169 return recurse(node, index);
170 } 170 }
171 171
172 @Override 172 @Override
173 public Void visitReturnStatement(ReturnStatement node, SourceIndex index) { 173 public Void visitReturnStatement(ReturnStatement node, SourceIndex index) {
174 return recurse(node, index); 174 return recurse(node, index);
175 } 175 }
176 176
177 @Override 177 @Override
178 public Void visitSwitchStatement(SwitchStatement node, SourceIndex index) { 178 public Void visitSwitchStatement(SwitchStatement node, SourceIndex index) {
179 return recurse(node, index); 179 return recurse(node, index);
180 } 180 }
181 181
182 @Override 182 @Override
183 public Void visitSwitchSection(SwitchSection node, SourceIndex index) { 183 public Void visitSwitchSection(SwitchSection node, SourceIndex index) {
184 return recurse(node, index); 184 return recurse(node, index);
185 } 185 }
186 186
187 @Override 187 @Override
188 public Void visitCaseLabel(CaseLabel node, SourceIndex index) { 188 public Void visitCaseLabel(CaseLabel node, SourceIndex index) {
189 return recurse(node, index); 189 return recurse(node, index);
190 } 190 }
191 191
192 @Override 192 @Override
193 public Void visitThrowStatement(ThrowStatement node, SourceIndex index) { 193 public Void visitThrowStatement(ThrowStatement node, SourceIndex index) {
194 return recurse(node, index); 194 return recurse(node, index);
195 } 195 }
196 196
197 @Override 197 @Override
198 public Void visitCatchClause(CatchClause node, SourceIndex index) { 198 public Void visitCatchClause(CatchClause node, SourceIndex index) {
199 return recurse(node, index); 199 return recurse(node, index);
200 } 200 }
201 201
202 @Override 202 @Override
203 public Void visitAnnotation(Annotation node, SourceIndex index) { 203 public Void visitAnnotation(Annotation node, SourceIndex index) {
204 return recurse(node, index); 204 return recurse(node, index);
205 } 205 }
206 206
207 @Override 207 @Override
208 public Void visitNewLine(NewLineNode node, SourceIndex index) { 208 public Void visitNewLine(NewLineNode node, SourceIndex index) {
209 return recurse(node, index); 209 return recurse(node, index);
210 } 210 }
211 211
212 @Override 212 @Override
213 public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) { 213 public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) {
214 return recurse(node, index); 214 return recurse(node, index);
215 } 215 }
216 216
217 @Override 217 @Override
218 public Void visitVariableInitializer(VariableInitializer node, SourceIndex index) { 218 public Void visitVariableInitializer(VariableInitializer node, SourceIndex index) {
219 return recurse(node, index); 219 return recurse(node, index);
220 } 220 }
221 221
222 @Override 222 @Override
223 public Void visitText(TextNode node, SourceIndex index) { 223 public Void visitText(TextNode node, SourceIndex index) {
224 return recurse(node, index); 224 return recurse(node, index);
225 } 225 }
226 226
227 @Override 227 @Override
228 public Void visitImportDeclaration(ImportDeclaration node, SourceIndex index) { 228 public Void visitImportDeclaration(ImportDeclaration node, SourceIndex index) {
229 return recurse(node, index); 229 return recurse(node, index);
230 } 230 }
231 231
232 @Override 232 @Override
233 public Void visitInitializerBlock(InstanceInitializer node, SourceIndex index) { 233 public Void visitInitializerBlock(InstanceInitializer node, SourceIndex index) {
234 return recurse(node, index); 234 return recurse(node, index);
235 } 235 }
236 236
237 @Override 237 @Override
238 public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, SourceIndex index) { 238 public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, SourceIndex index) {
239 return recurse(node, index); 239 return recurse(node, index);
240 } 240 }
241 241
242 @Override 242 @Override
243 public Void visitCompilationUnit(CompilationUnit node, SourceIndex index) { 243 public Void visitCompilationUnit(CompilationUnit node, SourceIndex index) {
244 return recurse(node, index); 244 return recurse(node, index);
245 } 245 }
246 246
247 @Override 247 @Override
248 public Void visitPackageDeclaration(PackageDeclaration node, SourceIndex index) { 248 public Void visitPackageDeclaration(PackageDeclaration node, SourceIndex index) {
249 return recurse(node, index); 249 return recurse(node, index);
250 } 250 }
251 251
252 @Override 252 @Override
253 public Void visitArraySpecifier(ArraySpecifier node, SourceIndex index) { 253 public Void visitArraySpecifier(ArraySpecifier node, SourceIndex index) {
254 return recurse(node, index); 254 return recurse(node, index);
255 } 255 }
256 256
257 @Override 257 @Override
258 public Void visitComposedType(ComposedType node, SourceIndex index) { 258 public Void visitComposedType(ComposedType node, SourceIndex index) {
259 return recurse(node, index); 259 return recurse(node, index);
260 } 260 }
261 261
262 @Override 262 @Override
263 public Void visitWhileStatement(WhileStatement node, SourceIndex index) { 263 public Void visitWhileStatement(WhileStatement node, SourceIndex index) {
264 return recurse(node, index); 264 return recurse(node, index);
265 } 265 }
266 266
267 @Override 267 @Override
268 public Void visitPrimitiveExpression(PrimitiveExpression node, SourceIndex index) { 268 public Void visitPrimitiveExpression(PrimitiveExpression node, SourceIndex index) {
269 return recurse(node, index); 269 return recurse(node, index);
270 } 270 }
271 271
272 @Override 272 @Override
273 public Void visitCastExpression(CastExpression node, SourceIndex index) { 273 public Void visitCastExpression(CastExpression node, SourceIndex index) {
274 return recurse(node, index); 274 return recurse(node, index);
275 } 275 }
276 276
277 @Override 277 @Override
278 public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, SourceIndex index) { 278 public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, SourceIndex index) {
279 return recurse(node, index); 279 return recurse(node, index);
280 } 280 }
281 281
282 @Override 282 @Override
283 public Void visitInstanceOfExpression(InstanceOfExpression node, SourceIndex index) { 283 public Void visitInstanceOfExpression(InstanceOfExpression node, SourceIndex index) {
284 return recurse(node, index); 284 return recurse(node, index);
285 } 285 }
286 286
287 @Override 287 @Override
288 public Void visitIndexerExpression(IndexerExpression node, SourceIndex index) { 288 public Void visitIndexerExpression(IndexerExpression node, SourceIndex index) {
289 return recurse(node, index); 289 return recurse(node, index);
290 } 290 }
291 291
292 @Override 292 @Override
293 public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, SourceIndex index) { 293 public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, SourceIndex index) {
294 return recurse(node, index); 294 return recurse(node, index);
295 } 295 }
296 296
297 @Override 297 @Override
298 public Void visitConditionalExpression(ConditionalExpression node, SourceIndex index) { 298 public Void visitConditionalExpression(ConditionalExpression node, SourceIndex index) {
299 return recurse(node, index); 299 return recurse(node, index);
300 } 300 }
301 301
302 @Override 302 @Override
303 public Void visitArrayInitializerExpression(ArrayInitializerExpression node, SourceIndex index) { 303 public Void visitArrayInitializerExpression(ArrayInitializerExpression node, SourceIndex index) {
304 return recurse(node, index); 304 return recurse(node, index);
305 } 305 }
306 306
307 @Override 307 @Override
308 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { 308 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) {
309 return recurse(node, index); 309 return recurse(node, index);
310 } 310 }
311 311
312 @Override 312 @Override
313 public Void visitArrayCreationExpression(ArrayCreationExpression node, SourceIndex index) { 313 public Void visitArrayCreationExpression(ArrayCreationExpression node, SourceIndex index) {
314 return recurse(node, index); 314 return recurse(node, index);
315 } 315 }
316 316
317 @Override 317 @Override
318 public Void visitAssignmentExpression(AssignmentExpression node, SourceIndex index) { 318 public Void visitAssignmentExpression(AssignmentExpression node, SourceIndex index) {
319 return recurse(node, index); 319 return recurse(node, index);
320 } 320 }
321 321
322 @Override 322 @Override
323 public Void visitForStatement(ForStatement node, SourceIndex index) { 323 public Void visitForStatement(ForStatement node, SourceIndex index) {
324 return recurse(node, index); 324 return recurse(node, index);
325 } 325 }
326 326
327 @Override 327 @Override
328 public Void visitForEachStatement(ForEachStatement node, SourceIndex index) { 328 public Void visitForEachStatement(ForEachStatement node, SourceIndex index) {
329 return recurse(node, index); 329 return recurse(node, index);
330 } 330 }
331 331
332 @Override 332 @Override
333 public Void visitTryCatchStatement(TryCatchStatement node, SourceIndex index) { 333 public Void visitTryCatchStatement(TryCatchStatement node, SourceIndex index) {
334 return recurse(node, index); 334 return recurse(node, index);
335 } 335 }
336 336
337 @Override 337 @Override
338 public Void visitGotoStatement(GotoStatement node, SourceIndex index) { 338 public Void visitGotoStatement(GotoStatement node, SourceIndex index) {
339 return recurse(node, index); 339 return recurse(node, index);
340 } 340 }
341 341
342 @Override 342 @Override
343 public Void visitParenthesizedExpression(ParenthesizedExpression node, SourceIndex index) { 343 public Void visitParenthesizedExpression(ParenthesizedExpression node, SourceIndex index) {
344 return recurse(node, index); 344 return recurse(node, index);
345 } 345 }
346 346
347 @Override 347 @Override
348 public Void visitSynchronizedStatement(SynchronizedStatement node, SourceIndex index) { 348 public Void visitSynchronizedStatement(SynchronizedStatement node, SourceIndex index) {
349 return recurse(node, index); 349 return recurse(node, index);
350 } 350 }
351 351
352 @Override 352 @Override
353 public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, SourceIndex index) { 353 public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, SourceIndex index) {
354 return recurse(node, index); 354 return recurse(node, index);
355 } 355 }
356 356
357 @Override 357 @Override
358 public Void visitWildcardType(WildcardType node, SourceIndex index) { 358 public Void visitWildcardType(WildcardType node, SourceIndex index) {
359 return recurse(node, index); 359 return recurse(node, index);
360 } 360 }
361 361
362 @Override 362 @Override
363 public Void visitMethodGroupExpression(MethodGroupExpression node, SourceIndex index) { 363 public Void visitMethodGroupExpression(MethodGroupExpression node, SourceIndex index) {
364 return recurse(node, index); 364 return recurse(node, index);
365 } 365 }
366 366
367 @Override 367 @Override
368 public Void visitAssertStatement(AssertStatement node, SourceIndex index) { 368 public Void visitAssertStatement(AssertStatement node, SourceIndex index) {
369 return recurse(node, index); 369 return recurse(node, index);
370 } 370 }
371 371
372 @Override 372 @Override
373 public Void visitLambdaExpression(LambdaExpression node, SourceIndex index) { 373 public Void visitLambdaExpression(LambdaExpression node, SourceIndex index) {
374 return recurse(node, index); 374 return recurse(node, index);
375 } 375 }
376 376
377 @Override 377 @Override
378 public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, SourceIndex index) { 378 public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, SourceIndex index) {
379 return recurse(node, index); 379 return recurse(node, index);
380 } 380 }
381} 381}
diff --git a/src/main/java/cuchaz/enigma/analysis/Token.java b/src/main/java/cuchaz/enigma/analysis/Token.java
index 42f4660a..266d2027 100644
--- a/src/main/java/cuchaz/enigma/analysis/Token.java
+++ b/src/main/java/cuchaz/enigma/analysis/Token.java
@@ -8,48 +8,48 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13public class Token implements Comparable<Token> { 14public class Token implements Comparable<Token> {
14 15
15 public int start; 16 public int start;
16 public int end; 17 public int end;
17 public String text; 18 public String text;
18 19
19 public Token(int start, int end, String source) { 20 public Token(int start, int end, String source) {
20 this.start = start; 21 this.start = start;
21 this.end = end; 22 this.end = end;
22 if (source != null) { 23 if (source != null) {
23 this.text = source.substring(start, end); 24 this.text = source.substring(start, end);
24 } 25 }
25 } 26 }
26 27
27 public boolean contains(int pos) { 28 public boolean contains(int pos) {
28 return pos >= start && pos <= end; 29 return pos >= start && pos <= end;
29 } 30 }
30 31
31 @Override 32 @Override
32 public int compareTo(Token other) { 33 public int compareTo(Token other) {
33 return start - other.start; 34 return start - other.start;
34 } 35 }
35 36
36 @Override 37 @Override
37 public boolean equals(Object other) { 38 public boolean equals(Object other) {
38 return other instanceof Token && equals((Token) other); 39 return other instanceof Token && equals((Token) other);
39 } 40 }
40 41
41 @Override 42 @Override
42 public int hashCode() 43 public int hashCode() {
43 { 44 return Integer.hashCode(start) + Integer.hashCode(end) + (text != null ? text.hashCode() : 0);
44 return Integer.hashCode(start) + Integer.hashCode(end) + (text != null ? text.hashCode() : 0); 45 }
45 } 46
46 47 public boolean equals(Token other) {
47 public boolean equals(Token other) { 48 return start == other.start && end == other.end;
48 return start == other.start && end == other.end; 49 }
49 } 50
50 51 @Override
51 @Override 52 public String toString() {
52 public String toString() { 53 return String.format("[%d,%d]", start, end);
53 return String.format("[%d,%d]", start, end); 54 }
54 }
55} 55}
diff --git a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
index d51131f6..26be05b4 100644
--- a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
@@ -8,291 +8,288 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.HashMultimap; 14import com.google.common.collect.HashMultimap;
14import com.google.common.collect.Lists; 15import com.google.common.collect.Lists;
15import com.google.common.collect.Maps; 16import com.google.common.collect.Maps;
16import com.google.common.collect.Multimap; 17import com.google.common.collect.Multimap;
17
18import java.util.Collection;
19import java.util.List;
20import java.util.Map;
21import java.util.Set;
22
23import cuchaz.enigma.mapping.*; 18import cuchaz.enigma.mapping.*;
24import javassist.CtBehavior; 19import javassist.CtBehavior;
25import javassist.CtClass; 20import javassist.CtClass;
26import javassist.CtField; 21import javassist.CtField;
27import javassist.bytecode.Descriptor; 22import javassist.bytecode.Descriptor;
28 23
24import java.util.Collection;
25import java.util.List;
26import java.util.Map;
27import java.util.Set;
28
29public class TranslationIndex { 29public class TranslationIndex {
30 30
31 private Map<ClassEntry, ClassEntry> superclasses; 31 private Map<ClassEntry, ClassEntry> superclasses;
32 private Multimap<ClassEntry, FieldEntry> fieldEntries; 32 private Multimap<ClassEntry, FieldEntry> fieldEntries;
33 private Multimap<ClassEntry, BehaviorEntry> behaviorEntries; 33 private Multimap<ClassEntry, BehaviorEntry> behaviorEntries;
34 private Multimap<ClassEntry, ClassEntry> interfaces; 34 private Multimap<ClassEntry, ClassEntry> interfaces;
35 35
36 public TranslationIndex() { 36 public TranslationIndex() {
37 this.superclasses = Maps.newHashMap(); 37 this.superclasses = Maps.newHashMap();
38 this.fieldEntries = HashMultimap.create(); 38 this.fieldEntries = HashMultimap.create();
39 this.behaviorEntries = HashMultimap.create(); 39 this.behaviorEntries = HashMultimap.create();
40 this.interfaces = HashMultimap.create(); 40 this.interfaces = HashMultimap.create();
41 } 41 }
42 42
43 public TranslationIndex(TranslationIndex other, Translator translator) { 43 public TranslationIndex(TranslationIndex other, Translator translator) {
44 // translate the superclasses 44 // translate the superclasses
45 this.superclasses = Maps.newHashMap(); 45 this.superclasses = Maps.newHashMap();
46 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.superclasses.entrySet()) { 46 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.superclasses.entrySet()) {
47 this.superclasses.put(translator.translateEntry(mapEntry.getKey()), translator.translateEntry(mapEntry.getValue())); 47 this.superclasses.put(translator.translateEntry(mapEntry.getKey()), translator.translateEntry(mapEntry.getValue()));
48 } 48 }
49 49
50 // translate the interfaces 50 // translate the interfaces
51 this.interfaces = HashMultimap.create(); 51 this.interfaces = HashMultimap.create();
52 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.interfaces.entries()) { 52 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.interfaces.entries()) {
53 this.interfaces.put( 53 this.interfaces.put(
54 translator.translateEntry(mapEntry.getKey()), 54 translator.translateEntry(mapEntry.getKey()),
55 translator.translateEntry(mapEntry.getValue()) 55 translator.translateEntry(mapEntry.getValue())
56 ); 56 );
57 } 57 }
58 58
59 // translate the fields 59 // translate the fields
60 this.fieldEntries = HashMultimap.create(); 60 this.fieldEntries = HashMultimap.create();
61 for (Map.Entry<ClassEntry, FieldEntry> mapEntry : other.fieldEntries.entries()) { 61 for (Map.Entry<ClassEntry, FieldEntry> mapEntry : other.fieldEntries.entries()) {
62 this.fieldEntries.put( 62 this.fieldEntries.put(
63 translator.translateEntry(mapEntry.getKey()), 63 translator.translateEntry(mapEntry.getKey()),
64 translator.translateEntry(mapEntry.getValue()) 64 translator.translateEntry(mapEntry.getValue())
65 ); 65 );
66 } 66 }
67 67
68 this.behaviorEntries = HashMultimap.create(); 68 this.behaviorEntries = HashMultimap.create();
69 for (Map.Entry<ClassEntry, BehaviorEntry> mapEntry : other.behaviorEntries.entries()) { 69 for (Map.Entry<ClassEntry, BehaviorEntry> mapEntry : other.behaviorEntries.entries()) {
70 this.behaviorEntries.put( 70 this.behaviorEntries.put(
71 translator.translateEntry(mapEntry.getKey()), 71 translator.translateEntry(mapEntry.getKey()),
72 translator.translateEntry(mapEntry.getValue()) 72 translator.translateEntry(mapEntry.getValue())
73 ); 73 );
74 } 74 }
75 } 75 }
76 76
77 public void indexClass(CtClass c) { 77 public void indexClass(CtClass c) {
78 indexClass(c, true); 78 indexClass(c, true);
79 } 79 }
80 80
81 public void indexClass(CtClass c, boolean indexMembers) { 81 public void indexClass(CtClass c, boolean indexMembers) {
82 ClassEntry classEntry = EntryFactory.getClassEntry(c); 82 ClassEntry classEntry = EntryFactory.getClassEntry(c);
83 if (isJre(classEntry)) { 83 if (isJre(classEntry)) {
84 return; 84 return;
85 } 85 }
86 86
87 // add the superclass 87 // add the superclass
88 ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c); 88 ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c);
89 if (superclassEntry != null) { 89 if (superclassEntry != null) {
90 this.superclasses.put(classEntry, superclassEntry); 90 this.superclasses.put(classEntry, superclassEntry);
91 } 91 }
92 92
93 // add the interfaces 93 // add the interfaces
94 for (String interfaceClassName : c.getClassFile().getInterfaces()) { 94 for (String interfaceClassName : c.getClassFile().getInterfaces()) {
95 ClassEntry interfaceClassEntry = new ClassEntry(Descriptor.toJvmName(interfaceClassName)); 95 ClassEntry interfaceClassEntry = new ClassEntry(Descriptor.toJvmName(interfaceClassName));
96 if (!isJre(interfaceClassEntry)) { 96 if (!isJre(interfaceClassEntry)) {
97 97
98 this.interfaces.put(classEntry, interfaceClassEntry); 98 this.interfaces.put(classEntry, interfaceClassEntry);
99 } 99 }
100 } 100 }
101 101
102 if (indexMembers) { 102 if (indexMembers) {
103 // add fields 103 // add fields
104 for (CtField field : c.getDeclaredFields()) { 104 for (CtField field : c.getDeclaredFields()) {
105 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); 105 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field);
106 this.fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry); 106 this.fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry);
107 } 107 }
108 108
109 // add behaviors 109 // add behaviors
110 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 110 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
111 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); 111 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
112 this.behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); 112 this.behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry);
113 } 113 }
114 } 114 }
115 } 115 }
116 116
117 public void renameClasses(Map<String, String> renames) { 117 public void renameClasses(Map<String, String> renames) {
118 EntryRenamer.renameClassesInMap(renames, this.superclasses); 118 EntryRenamer.renameClassesInMap(renames, this.superclasses);
119 EntryRenamer.renameClassesInMultimap(renames, this.fieldEntries); 119 EntryRenamer.renameClassesInMultimap(renames, this.fieldEntries);
120 EntryRenamer.renameClassesInMultimap(renames, this.behaviorEntries); 120 EntryRenamer.renameClassesInMultimap(renames, this.behaviorEntries);
121 } 121 }
122 122
123 public ClassEntry getSuperclass(ClassEntry classEntry) { 123 public ClassEntry getSuperclass(ClassEntry classEntry) {
124 return this.superclasses.get(classEntry); 124 return this.superclasses.get(classEntry);
125 } 125 }
126 126
127 public List<ClassEntry> getAncestry(ClassEntry classEntry) { 127 public List<ClassEntry> getAncestry(ClassEntry classEntry) {
128 List<ClassEntry> ancestors = Lists.newArrayList(); 128 List<ClassEntry> ancestors = Lists.newArrayList();
129 while (classEntry != null) { 129 while (classEntry != null) {
130 classEntry = getSuperclass(classEntry); 130 classEntry = getSuperclass(classEntry);
131 if (classEntry != null) { 131 if (classEntry != null) {
132 ancestors.add(classEntry); 132 ancestors.add(classEntry);
133 } 133 }
134 } 134 }
135 return ancestors; 135 return ancestors;
136 } 136 }
137 137
138 public List<ClassEntry> getSubclass(ClassEntry classEntry) { 138 public List<ClassEntry> getSubclass(ClassEntry classEntry) {
139 // linear search is fast enough for now 139 // linear search is fast enough for now
140 List<ClassEntry> subclasses = Lists.newArrayList(); 140 List<ClassEntry> subclasses = Lists.newArrayList();
141 for (Map.Entry<ClassEntry, ClassEntry> entry : this.superclasses.entrySet()) { 141 for (Map.Entry<ClassEntry, ClassEntry> entry : this.superclasses.entrySet()) {
142 ClassEntry subclass = entry.getKey(); 142 ClassEntry subclass = entry.getKey();
143 ClassEntry superclass = entry.getValue(); 143 ClassEntry superclass = entry.getValue();
144 if (classEntry.equals(superclass)) { 144 if (classEntry.equals(superclass)) {
145 subclasses.add(subclass); 145 subclasses.add(subclass);
146 } 146 }
147 } 147 }
148 return subclasses; 148 return subclasses;
149 } 149 }
150 150
151 public void getSubclassesRecursively(Set<ClassEntry> out, ClassEntry classEntry) { 151 public void getSubclassesRecursively(Set<ClassEntry> out, ClassEntry classEntry) {
152 for (ClassEntry subclassEntry : getSubclass(classEntry)) { 152 for (ClassEntry subclassEntry : getSubclass(classEntry)) {
153 out.add(subclassEntry); 153 out.add(subclassEntry);
154 getSubclassesRecursively(out, subclassEntry); 154 getSubclassesRecursively(out, subclassEntry);
155 } 155 }
156 } 156 }
157 157
158 public void getSubclassNamesRecursively(Set<String> out, ClassEntry classEntry) { 158 public void getSubclassNamesRecursively(Set<String> out, ClassEntry classEntry) {
159 for (ClassEntry subclassEntry : getSubclass(classEntry)) { 159 for (ClassEntry subclassEntry : getSubclass(classEntry)) {
160 out.add(subclassEntry.getName()); 160 out.add(subclassEntry.getName());
161 getSubclassNamesRecursively(out, subclassEntry); 161 getSubclassNamesRecursively(out, subclassEntry);
162 } 162 }
163 } 163 }
164 164
165 public Collection<Map.Entry<ClassEntry, ClassEntry>> getClassInterfaces() { 165 public Collection<Map.Entry<ClassEntry, ClassEntry>> getClassInterfaces() {
166 return this.interfaces.entries(); 166 return this.interfaces.entries();
167 } 167 }
168 168
169 public Collection<ClassEntry> getInterfaces(ClassEntry classEntry) { 169 public Collection<ClassEntry> getInterfaces(ClassEntry classEntry) {
170 return this.interfaces.get(classEntry); 170 return this.interfaces.get(classEntry);
171 } 171 }
172 172
173 public boolean isInterface(ClassEntry classEntry) { 173 public boolean isInterface(ClassEntry classEntry) {
174 return this.interfaces.containsValue(classEntry); 174 return this.interfaces.containsValue(classEntry);
175 } 175 }
176 176
177 public boolean entryExists(Entry entry) { 177 public boolean entryExists(Entry entry) {
178 if (entry instanceof FieldEntry) { 178 if (entry instanceof FieldEntry) {
179 return fieldExists((FieldEntry) entry); 179 return fieldExists((FieldEntry) entry);
180 } else if (entry instanceof BehaviorEntry) { 180 } else if (entry instanceof BehaviorEntry) {
181 return behaviorExists((BehaviorEntry) entry); 181 return behaviorExists((BehaviorEntry) entry);
182 } else if (entry instanceof ArgumentEntry) { 182 } else if (entry instanceof ArgumentEntry) {
183 return behaviorExists(((ArgumentEntry) entry).getBehaviorEntry()); 183 return behaviorExists(((ArgumentEntry) entry).getBehaviorEntry());
184 } else if (entry instanceof LocalVariableEntry) { 184 } else if (entry instanceof LocalVariableEntry) {
185 return behaviorExists(((LocalVariableEntry) entry).getBehaviorEntry()); 185 return behaviorExists(((LocalVariableEntry) entry).getBehaviorEntry());
186 } 186 }
187 throw new IllegalArgumentException("Cannot check existence for " + entry.getClass()); 187 throw new IllegalArgumentException("Cannot check existence for " + entry.getClass());
188 } 188 }
189 189
190 public boolean fieldExists(FieldEntry fieldEntry) { 190 public boolean fieldExists(FieldEntry fieldEntry) {
191 return this.fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry); 191 return this.fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry);
192 } 192 }
193 193
194 public boolean behaviorExists(BehaviorEntry behaviorEntry) { 194 public boolean behaviorExists(BehaviorEntry behaviorEntry) {
195 return this.behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry); 195 return this.behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry);
196 } 196 }
197 197
198 public ClassEntry resolveEntryClass(Entry entry) { 198 public ClassEntry resolveEntryClass(Entry entry) {
199 return resolveEntryClass(entry, false); 199 return resolveEntryClass(entry, false);
200 } 200 }
201 201
202 public ClassEntry resolveEntryClass(Entry entry, boolean checkSuperclassBeforeChild) { 202 public ClassEntry resolveEntryClass(Entry entry, boolean checkSuperclassBeforeChild) {
203 if (entry instanceof ClassEntry) { 203 if (entry instanceof ClassEntry) {
204 return (ClassEntry) entry; 204 return (ClassEntry) entry;
205 } 205 }
206 206
207 ClassEntry superclassEntry = resolveSuperclass(entry, checkSuperclassBeforeChild); 207 ClassEntry superclassEntry = resolveSuperclass(entry, checkSuperclassBeforeChild);
208 if (superclassEntry != null) { 208 if (superclassEntry != null) {
209 return superclassEntry; 209 return superclassEntry;
210 } 210 }
211 211
212 ClassEntry interfaceEntry = resolveInterface(entry); 212 ClassEntry interfaceEntry = resolveInterface(entry);
213 if (interfaceEntry != null) { 213 if (interfaceEntry != null) {
214 return interfaceEntry; 214 return interfaceEntry;
215 } 215 }
216 216
217 return null; 217 return null;
218 } 218 }
219 219
220 public ClassEntry resolveSuperclass(Entry entry, boolean checkSuperclassBeforeChild) { 220 public ClassEntry resolveSuperclass(Entry entry, boolean checkSuperclassBeforeChild) {
221 221
222 // Default case 222 // Default case
223 if (!checkSuperclassBeforeChild) 223 if (!checkSuperclassBeforeChild)
224 return resolveSuperclass(entry); 224 return resolveSuperclass(entry);
225 225
226 // Save the original entry 226 // Save the original entry
227 Entry originalEntry = entry; 227 Entry originalEntry = entry;
228 228
229 // Get all possible superclasses and reverse the list 229 // Get all possible superclasses and reverse the list
230 List<ClassEntry> superclasses = Lists.reverse(getAncestry(originalEntry.getClassEntry())); 230 List<ClassEntry> superclasses = Lists.reverse(getAncestry(originalEntry.getClassEntry()));
231 231
232 boolean existInEntry = false; 232 boolean existInEntry = false;
233 233
234 for (ClassEntry classEntry : superclasses) 234 for (ClassEntry classEntry : superclasses) {
235 { 235 entry = entry.cloneToNewClass(classEntry);
236 entry = entry.cloneToNewClass(classEntry); 236 existInEntry = entryExists(entry);
237 existInEntry = entryExists(entry); 237
238 238 // Check for possible entry in interfaces of superclasses
239 // Check for possible entry in interfaces of superclasses 239 ClassEntry interfaceEntry = resolveInterface(entry);
240 ClassEntry interfaceEntry = resolveInterface(entry); 240 if (interfaceEntry != null)
241 if (interfaceEntry != null) 241 return interfaceEntry;
242 return interfaceEntry; 242 if (existInEntry)
243 if (existInEntry) 243 break;
244 break; 244 }
245 } 245
246 246 // Doesn't exists in superclasses? check the child or return null
247 // Doesn't exists in superclasses? check the child or return null 247 if (!existInEntry)
248 if (!existInEntry) 248 return !entryExists(originalEntry) ? null : originalEntry.getClassEntry();
249 return !entryExists(originalEntry) ? null : originalEntry.getClassEntry(); 249
250 250 return entry.getClassEntry();
251 return entry.getClassEntry(); 251 }
252 } 252
253 253 public ClassEntry resolveSuperclass(Entry entry) {
254 public ClassEntry resolveSuperclass(Entry entry) 254 // this entry could refer to a method on a class where the method is not actually implemented
255 { 255 // travel up the inheritance tree to find the closest implementation
256 // this entry could refer to a method on a class where the method is not actually implemented 256
257 // travel up the inheritance tree to find the closest implementation 257 while (!entryExists(entry)) {
258 258 // is there a parent class?
259 while (!entryExists(entry)) { 259 ClassEntry superclassEntry = getSuperclass(entry.getClassEntry());
260 // is there a parent class? 260 if (superclassEntry == null) {
261 ClassEntry superclassEntry = getSuperclass(entry.getClassEntry()); 261 // this is probably a method from a class in a library
262 if (superclassEntry == null) { 262 // we can't trace the implementation up any higher unless we index the library
263 // this is probably a method from a class in a library 263 return null;
264 // we can't trace the implementation up any higher unless we index the library 264 }
265 return null; 265
266 } 266 // move up to the parent class
267 267 entry = entry.cloneToNewClass(superclassEntry);
268 // move up to the parent class 268 }
269 entry = entry.cloneToNewClass(superclassEntry); 269 return entry.getClassEntry();
270 } 270 }
271 return entry.getClassEntry(); 271
272 } 272 public ClassEntry resolveInterface(Entry entry) {
273 273 // the interfaces for any class is a forest
274 public ClassEntry resolveInterface(Entry entry) { 274 // so let's look at all the trees
275 // the interfaces for any class is a forest 275
276 // so let's look at all the trees 276 for (ClassEntry interfaceEntry : this.interfaces.get(entry.getClassEntry())) {
277 277 Collection<ClassEntry> subInterface = this.interfaces.get(interfaceEntry);
278 for (ClassEntry interfaceEntry : this.interfaces.get(entry.getClassEntry())) { 278 if (subInterface != null && !subInterface.isEmpty()) {
279 Collection<ClassEntry> subInterface = this.interfaces.get(interfaceEntry); 279 ClassEntry result = resolveInterface(entry.cloneToNewClass(interfaceEntry));
280 if (subInterface != null && !subInterface.isEmpty()) 280 if (result != null)
281 { 281 return result;
282 ClassEntry result = resolveInterface(entry.cloneToNewClass(interfaceEntry)); 282 }
283 if (result != null) 283 ClassEntry resolvedClassEntry = resolveSuperclass(entry.cloneToNewClass(interfaceEntry));
284 return result; 284 if (resolvedClassEntry != null) {
285 } 285 return resolvedClassEntry;
286 ClassEntry resolvedClassEntry = resolveSuperclass(entry.cloneToNewClass(interfaceEntry)); 286 }
287 if (resolvedClassEntry != null) { 287 }
288 return resolvedClassEntry; 288 return null;
289 } 289 }
290 } 290
291 return null; 291 private boolean isJre(ClassEntry classEntry) {
292 } 292 String packageName = classEntry.getPackageName();
293 293 return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax"));
294 private boolean isJre(ClassEntry classEntry) { 294 }
295 String packageName = classEntry.getPackageName();
296 return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax"));
297 }
298} 295}
diff --git a/src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java b/src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java
index cc025da7..c98fb9ef 100644
--- a/src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.strobel.componentmodel.Key; 14import com.strobel.componentmodel.Key;
@@ -19,420 +20,420 @@ import java.nio.charset.Charset;
19 20
20public class TreeDumpVisitor implements IAstVisitor<Void, Void> { 21public class TreeDumpVisitor implements IAstVisitor<Void, Void> {
21 22
22 private File file; 23 private File file;
23 private Writer out; 24 private Writer out;
24 25
25 public TreeDumpVisitor(File file) { 26 public TreeDumpVisitor(File file) {
26 this.file = file; 27 this.file = file;
27 } 28 }
28 29
29 @Override 30 @Override
30 public Void visitCompilationUnit(CompilationUnit node, Void ignored) { 31 public Void visitCompilationUnit(CompilationUnit node, Void ignored) {
31 try { 32 try {
32 out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8")); 33 out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8"));
33 recurse(node, ignored); 34 recurse(node, ignored);
34 out.close(); 35 out.close();
35 return null; 36 return null;
36 } catch (IOException ex) { 37 } catch (IOException ex) {
37 throw new Error(ex); 38 throw new Error(ex);
38 } 39 }
39 } 40 }
40 41
41 private Void recurse(AstNode node, Void ignored) { 42 private Void recurse(AstNode node, Void ignored) {
42 // show the tree 43 // show the tree
43 try { 44 try {
44 out.write(getIndent(node) + node.getClass().getSimpleName() + " " + getText(node) + " " + dumpUserData(node) + " " + node.getRegion() + "\n"); 45 out.write(getIndent(node) + node.getClass().getSimpleName() + " " + getText(node) + " " + dumpUserData(node) + " " + node.getRegion() + "\n");
45 } catch (IOException ex) { 46 } catch (IOException ex) {
46 throw new Error(ex); 47 throw new Error(ex);
47 } 48 }
48 49
49 // recurse 50 // recurse
50 for (final AstNode child : node.getChildren()) { 51 for (final AstNode child : node.getChildren()) {
51 child.acceptVisitor(this, ignored); 52 child.acceptVisitor(this, ignored);
52 } 53 }
53 return null; 54 return null;
54 } 55 }
55 56
56 private String getText(AstNode node) { 57 private String getText(AstNode node) {
57 if (node instanceof Identifier) { 58 if (node instanceof Identifier) {
58 return "\"" + ((Identifier) node).getName() + "\""; 59 return "\"" + ((Identifier) node).getName() + "\"";
59 } 60 }
60 return ""; 61 return "";
61 } 62 }
62 63
63 private String dumpUserData(AstNode node) { 64 private String dumpUserData(AstNode node) {
64 StringBuilder buf = new StringBuilder(); 65 StringBuilder buf = new StringBuilder();
65 for (Key<?> key : Keys.ALL_KEYS) { 66 for (Key<?> key : Keys.ALL_KEYS) {
66 Object val = node.getUserData(key); 67 Object val = node.getUserData(key);
67 if (val != null) { 68 if (val != null) {
68 buf.append(String.format(" [%s=%s]", key, val)); 69 buf.append(String.format(" [%s=%s]", key, val));
69 } 70 }
70 } 71 }
71 return buf.toString(); 72 return buf.toString();
72 } 73 }
73 74
74 private String getIndent(AstNode node) { 75 private String getIndent(AstNode node) {
75 StringBuilder buf = new StringBuilder(); 76 StringBuilder buf = new StringBuilder();
76 int depth = getDepth(node); 77 int depth = getDepth(node);
77 for (int i = 0; i < depth; i++) { 78 for (int i = 0; i < depth; i++) {
78 buf.append("\t"); 79 buf.append("\t");
79 } 80 }
80 return buf.toString(); 81 return buf.toString();
81 } 82 }
82 83
83 private int getDepth(AstNode node) { 84 private int getDepth(AstNode node) {
84 int depth = -1; 85 int depth = -1;
85 while (node != null) { 86 while (node != null) {
86 depth++; 87 depth++;
87 node = node.getParent(); 88 node = node.getParent();
88 } 89 }
89 return depth; 90 return depth;
90 } 91 }
91 92
92 // OVERRIDES WE DON'T CARE ABOUT 93 // OVERRIDES WE DON'T CARE ABOUT
93 94
94 @Override 95 @Override
95 public Void visitInvocationExpression(InvocationExpression node, Void ignored) { 96 public Void visitInvocationExpression(InvocationExpression node, Void ignored) {
96 return recurse(node, ignored); 97 return recurse(node, ignored);
97 } 98 }
98 99
99 @Override 100 @Override
100 public Void visitMemberReferenceExpression(MemberReferenceExpression node, Void ignored) { 101 public Void visitMemberReferenceExpression(MemberReferenceExpression node, Void ignored) {
101 return recurse(node, ignored); 102 return recurse(node, ignored);
102 } 103 }
103 104
104 @Override 105 @Override
105 public Void visitSimpleType(SimpleType node, Void ignored) { 106 public Void visitSimpleType(SimpleType node, Void ignored) {
106 return recurse(node, ignored); 107 return recurse(node, ignored);
107 } 108 }
108 109
109 @Override 110 @Override
110 public Void visitMethodDeclaration(MethodDeclaration node, Void ignored) { 111 public Void visitMethodDeclaration(MethodDeclaration node, Void ignored) {
111 return recurse(node, ignored); 112 return recurse(node, ignored);
112 } 113 }
113 114
114 @Override 115 @Override
115 public Void visitConstructorDeclaration(ConstructorDeclaration node, Void ignored) { 116 public Void visitConstructorDeclaration(ConstructorDeclaration node, Void ignored) {
116 return recurse(node, ignored); 117 return recurse(node, ignored);
117 } 118 }
118 119
119 @Override 120 @Override
120 public Void visitParameterDeclaration(ParameterDeclaration node, Void ignored) { 121 public Void visitParameterDeclaration(ParameterDeclaration node, Void ignored) {
121 return recurse(node, ignored); 122 return recurse(node, ignored);
122 } 123 }
123 124
124 @Override 125 @Override
125 public Void visitFieldDeclaration(FieldDeclaration node, Void ignored) { 126 public Void visitFieldDeclaration(FieldDeclaration node, Void ignored) {
126 return recurse(node, ignored); 127 return recurse(node, ignored);
127 } 128 }
128 129
129 @Override 130 @Override
130 public Void visitTypeDeclaration(TypeDeclaration node, Void ignored) { 131 public Void visitTypeDeclaration(TypeDeclaration node, Void ignored) {
131 return recurse(node, ignored); 132 return recurse(node, ignored);
132 } 133 }
133 134
134 @Override 135 @Override
135 public Void visitComment(Comment node, Void ignored) { 136 public Void visitComment(Comment node, Void ignored) {
136 return recurse(node, ignored); 137 return recurse(node, ignored);
137 } 138 }
138 139
139 @Override 140 @Override
140 public Void visitPatternPlaceholder(AstNode node, Pattern pattern, Void ignored) { 141 public Void visitPatternPlaceholder(AstNode node, Pattern pattern, Void ignored) {
141 return recurse(node, ignored); 142 return recurse(node, ignored);
142 } 143 }
143 144
144 @Override 145 @Override
145 public Void visitTypeReference(TypeReferenceExpression node, Void ignored) { 146 public Void visitTypeReference(TypeReferenceExpression node, Void ignored) {
146 return recurse(node, ignored); 147 return recurse(node, ignored);
147 } 148 }
148 149
149 @Override 150 @Override
150 public Void visitJavaTokenNode(JavaTokenNode node, Void ignored) { 151 public Void visitJavaTokenNode(JavaTokenNode node, Void ignored) {
151 return recurse(node, ignored); 152 return recurse(node, ignored);
152 } 153 }
153 154
154 @Override 155 @Override
155 public Void visitIdentifier(Identifier node, Void ignored) { 156 public Void visitIdentifier(Identifier node, Void ignored) {
156 return recurse(node, ignored); 157 return recurse(node, ignored);
157 } 158 }
158 159
159 @Override 160 @Override
160 public Void visitNullReferenceExpression(NullReferenceExpression node, Void ignored) { 161 public Void visitNullReferenceExpression(NullReferenceExpression node, Void ignored) {
161 return recurse(node, ignored); 162 return recurse(node, ignored);
162 } 163 }
163 164
164 @Override 165 @Override
165 public Void visitThisReferenceExpression(ThisReferenceExpression node, Void ignored) { 166 public Void visitThisReferenceExpression(ThisReferenceExpression node, Void ignored) {
166 return recurse(node, ignored); 167 return recurse(node, ignored);
167 } 168 }
168 169
169 @Override 170 @Override
170 public Void visitSuperReferenceExpression(SuperReferenceExpression node, Void ignored) { 171 public Void visitSuperReferenceExpression(SuperReferenceExpression node, Void ignored) {
171 return recurse(node, ignored); 172 return recurse(node, ignored);
172 } 173 }
173 174
174 @Override 175 @Override
175 public Void visitClassOfExpression(ClassOfExpression node, Void ignored) { 176 public Void visitClassOfExpression(ClassOfExpression node, Void ignored) {
176 return recurse(node, ignored); 177 return recurse(node, ignored);
177 } 178 }
178 179
179 @Override 180 @Override
180 public Void visitBlockStatement(BlockStatement node, Void ignored) { 181 public Void visitBlockStatement(BlockStatement node, Void ignored) {
181 return recurse(node, ignored); 182 return recurse(node, ignored);
182 } 183 }
183 184
184 @Override 185 @Override
185 public Void visitExpressionStatement(ExpressionStatement node, Void ignored) { 186 public Void visitExpressionStatement(ExpressionStatement node, Void ignored) {
186 return recurse(node, ignored); 187 return recurse(node, ignored);
187 } 188 }
188 189
189 @Override 190 @Override
190 public Void visitBreakStatement(BreakStatement node, Void ignored) { 191 public Void visitBreakStatement(BreakStatement node, Void ignored) {
191 return recurse(node, ignored); 192 return recurse(node, ignored);
192 } 193 }
193 194
194 @Override 195 @Override
195 public Void visitContinueStatement(ContinueStatement node, Void ignored) { 196 public Void visitContinueStatement(ContinueStatement node, Void ignored) {
196 return recurse(node, ignored); 197 return recurse(node, ignored);
197 } 198 }
198 199
199 @Override 200 @Override
200 public Void visitDoWhileStatement(DoWhileStatement node, Void ignored) { 201 public Void visitDoWhileStatement(DoWhileStatement node, Void ignored) {
201 return recurse(node, ignored); 202 return recurse(node, ignored);
202 } 203 }
203 204
204 @Override 205 @Override
205 public Void visitEmptyStatement(EmptyStatement node, Void ignored) { 206 public Void visitEmptyStatement(EmptyStatement node, Void ignored) {
206 return recurse(node, ignored); 207 return recurse(node, ignored);
207 } 208 }
208 209
209 @Override 210 @Override
210 public Void visitIfElseStatement(IfElseStatement node, Void ignored) { 211 public Void visitIfElseStatement(IfElseStatement node, Void ignored) {
211 return recurse(node, ignored); 212 return recurse(node, ignored);
212 } 213 }
213 214
214 @Override 215 @Override
215 public Void visitLabelStatement(LabelStatement node, Void ignored) { 216 public Void visitLabelStatement(LabelStatement node, Void ignored) {
216 return recurse(node, ignored); 217 return recurse(node, ignored);
217 } 218 }
218 219
219 @Override 220 @Override
220 public Void visitLabeledStatement(LabeledStatement node, Void ignored) { 221 public Void visitLabeledStatement(LabeledStatement node, Void ignored) {
221 return recurse(node, ignored); 222 return recurse(node, ignored);
222 } 223 }
223 224
224 @Override 225 @Override
225 public Void visitReturnStatement(ReturnStatement node, Void ignored) { 226 public Void visitReturnStatement(ReturnStatement node, Void ignored) {
226 return recurse(node, ignored); 227 return recurse(node, ignored);
227 } 228 }
228 229
229 @Override 230 @Override
230 public Void visitSwitchStatement(SwitchStatement node, Void ignored) { 231 public Void visitSwitchStatement(SwitchStatement node, Void ignored) {
231 return recurse(node, ignored); 232 return recurse(node, ignored);
232 } 233 }
233 234
234 @Override 235 @Override
235 public Void visitSwitchSection(SwitchSection node, Void ignored) { 236 public Void visitSwitchSection(SwitchSection node, Void ignored) {
236 return recurse(node, ignored); 237 return recurse(node, ignored);
237 } 238 }
238 239
239 @Override 240 @Override
240 public Void visitCaseLabel(CaseLabel node, Void ignored) { 241 public Void visitCaseLabel(CaseLabel node, Void ignored) {
241 return recurse(node, ignored); 242 return recurse(node, ignored);
242 } 243 }
243 244
244 @Override 245 @Override
245 public Void visitThrowStatement(ThrowStatement node, Void ignored) { 246 public Void visitThrowStatement(ThrowStatement node, Void ignored) {
246 return recurse(node, ignored); 247 return recurse(node, ignored);
247 } 248 }
248 249
249 @Override 250 @Override
250 public Void visitCatchClause(CatchClause node, Void ignored) { 251 public Void visitCatchClause(CatchClause node, Void ignored) {
251 return recurse(node, ignored); 252 return recurse(node, ignored);
252 } 253 }
253 254
254 @Override 255 @Override
255 public Void visitAnnotation(Annotation node, Void ignored) { 256 public Void visitAnnotation(Annotation node, Void ignored) {
256 return recurse(node, ignored); 257 return recurse(node, ignored);
257 } 258 }
258 259
259 @Override 260 @Override
260 public Void visitNewLine(NewLineNode node, Void ignored) { 261 public Void visitNewLine(NewLineNode node, Void ignored) {
261 return recurse(node, ignored); 262 return recurse(node, ignored);
262 } 263 }
263 264
264 @Override 265 @Override
265 public Void visitVariableDeclaration(VariableDeclarationStatement node, Void ignored) { 266 public Void visitVariableDeclaration(VariableDeclarationStatement node, Void ignored) {
266 return recurse(node, ignored); 267 return recurse(node, ignored);
267 } 268 }
268 269
269 @Override 270 @Override
270 public Void visitVariableInitializer(VariableInitializer node, Void ignored) { 271 public Void visitVariableInitializer(VariableInitializer node, Void ignored) {
271 return recurse(node, ignored); 272 return recurse(node, ignored);
272 } 273 }
273 274
274 @Override 275 @Override
275 public Void visitText(TextNode node, Void ignored) { 276 public Void visitText(TextNode node, Void ignored) {
276 return recurse(node, ignored); 277 return recurse(node, ignored);
277 } 278 }
278 279
279 @Override 280 @Override
280 public Void visitImportDeclaration(ImportDeclaration node, Void ignored) { 281 public Void visitImportDeclaration(ImportDeclaration node, Void ignored) {
281 return recurse(node, ignored); 282 return recurse(node, ignored);
282 } 283 }
283 284
284 @Override 285 @Override
285 public Void visitInitializerBlock(InstanceInitializer node, Void ignored) { 286 public Void visitInitializerBlock(InstanceInitializer node, Void ignored) {
286 return recurse(node, ignored); 287 return recurse(node, ignored);
287 } 288 }
288 289
289 @Override 290 @Override
290 public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, Void ignored) { 291 public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, Void ignored) {
291 return recurse(node, ignored); 292 return recurse(node, ignored);
292 } 293 }
293 294
294 @Override 295 @Override
295 public Void visitPackageDeclaration(PackageDeclaration node, Void ignored) { 296 public Void visitPackageDeclaration(PackageDeclaration node, Void ignored) {
296 return recurse(node, ignored); 297 return recurse(node, ignored);
297 } 298 }
298 299
299 @Override 300 @Override
300 public Void visitArraySpecifier(ArraySpecifier node, Void ignored) { 301 public Void visitArraySpecifier(ArraySpecifier node, Void ignored) {
301 return recurse(node, ignored); 302 return recurse(node, ignored);
302 } 303 }
303 304
304 @Override 305 @Override
305 public Void visitComposedType(ComposedType node, Void ignored) { 306 public Void visitComposedType(ComposedType node, Void ignored) {
306 return recurse(node, ignored); 307 return recurse(node, ignored);
307 } 308 }
308 309
309 @Override 310 @Override
310 public Void visitWhileStatement(WhileStatement node, Void ignored) { 311 public Void visitWhileStatement(WhileStatement node, Void ignored) {
311 return recurse(node, ignored); 312 return recurse(node, ignored);
312 } 313 }
313 314
314 @Override 315 @Override
315 public Void visitPrimitiveExpression(PrimitiveExpression node, Void ignored) { 316 public Void visitPrimitiveExpression(PrimitiveExpression node, Void ignored) {
316 return recurse(node, ignored); 317 return recurse(node, ignored);
317 } 318 }
318 319
319 @Override 320 @Override
320 public Void visitCastExpression(CastExpression node, Void ignored) { 321 public Void visitCastExpression(CastExpression node, Void ignored) {
321 return recurse(node, ignored); 322 return recurse(node, ignored);
322 } 323 }
323 324
324 @Override 325 @Override
325 public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, Void ignored) { 326 public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, Void ignored) {
326 return recurse(node, ignored); 327 return recurse(node, ignored);
327 } 328 }
328 329
329 @Override 330 @Override
330 public Void visitInstanceOfExpression(InstanceOfExpression node, Void ignored) { 331 public Void visitInstanceOfExpression(InstanceOfExpression node, Void ignored) {
331 return recurse(node, ignored); 332 return recurse(node, ignored);
332 } 333 }
333 334
334 @Override 335 @Override
335 public Void visitIndexerExpression(IndexerExpression node, Void ignored) { 336 public Void visitIndexerExpression(IndexerExpression node, Void ignored) {
336 return recurse(node, ignored); 337 return recurse(node, ignored);
337 } 338 }
338 339
339 @Override 340 @Override
340 public Void visitIdentifierExpression(IdentifierExpression node, Void ignored) { 341 public Void visitIdentifierExpression(IdentifierExpression node, Void ignored) {
341 return recurse(node, ignored); 342 return recurse(node, ignored);
342 } 343 }
343 344
344 @Override 345 @Override
345 public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, Void ignored) { 346 public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, Void ignored) {
346 return recurse(node, ignored); 347 return recurse(node, ignored);
347 } 348 }
348 349
349 @Override 350 @Override
350 public Void visitConditionalExpression(ConditionalExpression node, Void ignored) { 351 public Void visitConditionalExpression(ConditionalExpression node, Void ignored) {
351 return recurse(node, ignored); 352 return recurse(node, ignored);
352 } 353 }
353 354
354 @Override 355 @Override
355 public Void visitArrayInitializerExpression(ArrayInitializerExpression node, Void ignored) { 356 public Void visitArrayInitializerExpression(ArrayInitializerExpression node, Void ignored) {
356 return recurse(node, ignored); 357 return recurse(node, ignored);
357 } 358 }
358 359
359 @Override 360 @Override
360 public Void visitObjectCreationExpression(ObjectCreationExpression node, Void ignored) { 361 public Void visitObjectCreationExpression(ObjectCreationExpression node, Void ignored) {
361 return recurse(node, ignored); 362 return recurse(node, ignored);
362 } 363 }
363 364
364 @Override 365 @Override
365 public Void visitArrayCreationExpression(ArrayCreationExpression node, Void ignored) { 366 public Void visitArrayCreationExpression(ArrayCreationExpression node, Void ignored) {
366 return recurse(node, ignored); 367 return recurse(node, ignored);
367 } 368 }
368 369
369 @Override 370 @Override
370 public Void visitAssignmentExpression(AssignmentExpression node, Void ignored) { 371 public Void visitAssignmentExpression(AssignmentExpression node, Void ignored) {
371 return recurse(node, ignored); 372 return recurse(node, ignored);
372 } 373 }
373 374
374 @Override 375 @Override
375 public Void visitForStatement(ForStatement node, Void ignored) { 376 public Void visitForStatement(ForStatement node, Void ignored) {
376 return recurse(node, ignored); 377 return recurse(node, ignored);
377 } 378 }
378 379
379 @Override 380 @Override
380 public Void visitForEachStatement(ForEachStatement node, Void ignored) { 381 public Void visitForEachStatement(ForEachStatement node, Void ignored) {
381 return recurse(node, ignored); 382 return recurse(node, ignored);
382 } 383 }
383 384
384 @Override 385 @Override
385 public Void visitTryCatchStatement(TryCatchStatement node, Void ignored) { 386 public Void visitTryCatchStatement(TryCatchStatement node, Void ignored) {
386 return recurse(node, ignored); 387 return recurse(node, ignored);
387 } 388 }
388 389
389 @Override 390 @Override
390 public Void visitGotoStatement(GotoStatement node, Void ignored) { 391 public Void visitGotoStatement(GotoStatement node, Void ignored) {
391 return recurse(node, ignored); 392 return recurse(node, ignored);
392 } 393 }
393 394
394 @Override 395 @Override
395 public Void visitParenthesizedExpression(ParenthesizedExpression node, Void ignored) { 396 public Void visitParenthesizedExpression(ParenthesizedExpression node, Void ignored) {
396 return recurse(node, ignored); 397 return recurse(node, ignored);
397 } 398 }
398 399
399 @Override 400 @Override
400 public Void visitSynchronizedStatement(SynchronizedStatement node, Void ignored) { 401 public Void visitSynchronizedStatement(SynchronizedStatement node, Void ignored) {
401 return recurse(node, ignored); 402 return recurse(node, ignored);
402 } 403 }
403 404
404 @Override 405 @Override
405 public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, Void ignored) { 406 public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, Void ignored) {
406 return recurse(node, ignored); 407 return recurse(node, ignored);
407 } 408 }
408 409
409 @Override 410 @Override
410 public Void visitWildcardType(WildcardType node, Void ignored) { 411 public Void visitWildcardType(WildcardType node, Void ignored) {
411 return recurse(node, ignored); 412 return recurse(node, ignored);
412 } 413 }
413 414
414 @Override 415 @Override
415 public Void visitMethodGroupExpression(MethodGroupExpression node, Void ignored) { 416 public Void visitMethodGroupExpression(MethodGroupExpression node, Void ignored) {
416 return recurse(node, ignored); 417 return recurse(node, ignored);
417 } 418 }
418 419
419 @Override 420 @Override
420 public Void visitEnumValueDeclaration(EnumValueDeclaration node, Void ignored) { 421 public Void visitEnumValueDeclaration(EnumValueDeclaration node, Void ignored) {
421 return recurse(node, ignored); 422 return recurse(node, ignored);
422 } 423 }
423 424
424 @Override 425 @Override
425 public Void visitAssertStatement(AssertStatement node, Void ignored) { 426 public Void visitAssertStatement(AssertStatement node, Void ignored) {
426 return recurse(node, ignored); 427 return recurse(node, ignored);
427 } 428 }
428 429
429 @Override 430 @Override
430 public Void visitLambdaExpression(LambdaExpression node, Void ignored) { 431 public Void visitLambdaExpression(LambdaExpression node, Void ignored) {
431 return recurse(node, ignored); 432 return recurse(node, ignored);
432 } 433 }
433 434
434 @Override 435 @Override
435 public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, Void ignored) { 436 public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, Void ignored) {
436 return recurse(node, ignored); 437 return recurse(node, ignored);
437 } 438 }
438} 439}
diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java b/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java
index ad5bab0c..6ec576e0 100644
--- a/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java
+++ b/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode; 12package cuchaz.enigma.bytecode;
12 13
13import javassist.CtBehavior; 14import javassist.CtBehavior;
@@ -16,36 +17,35 @@ import javassist.CtField;
16import javassist.bytecode.AccessFlag; 17import javassist.bytecode.AccessFlag;
17import javassist.bytecode.InnerClassesAttribute; 18import javassist.bytecode.InnerClassesAttribute;
18 19
19
20public class ClassProtectifier { 20public class ClassProtectifier {
21 21
22 public static CtClass protectify(CtClass c) { 22 public static CtClass protectify(CtClass c) {
23 23
24 // protectify all the fields 24 // protectify all the fields
25 for (CtField field : c.getDeclaredFields()) { 25 for (CtField field : c.getDeclaredFields()) {
26 field.setModifiers(protectify(field.getModifiers())); 26 field.setModifiers(protectify(field.getModifiers()));
27 } 27 }
28 28
29 // protectify all the methods and constructors 29 // protectify all the methods and constructors
30 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 30 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
31 behavior.setModifiers(protectify(behavior.getModifiers())); 31 behavior.setModifiers(protectify(behavior.getModifiers()));
32 } 32 }
33 33
34 // protectify all the inner classes 34 // protectify all the inner classes
35 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); 35 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
36 if (attr != null) { 36 if (attr != null) {
37 for (int i = 0; i < attr.tableLength(); i++) { 37 for (int i = 0; i < attr.tableLength(); i++) {
38 attr.setAccessFlags(i, protectify(attr.accessFlags(i))); 38 attr.setAccessFlags(i, protectify(attr.accessFlags(i)));
39 } 39 }
40 } 40 }
41 41
42 return c; 42 return c;
43 } 43 }
44 44
45 private static int protectify(int flags) { 45 private static int protectify(int flags) {
46 if (AccessFlag.isPrivate(flags)) { 46 if (AccessFlag.isPrivate(flags)) {
47 flags = AccessFlag.setProtected(flags); 47 flags = AccessFlag.setProtected(flags);
48 } 48 }
49 return flags; 49 return flags;
50 } 50 }
51} 51}
diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java b/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java
index 0aa7fac7..d627fe91 100644
--- a/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java
+++ b/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode; 12package cuchaz.enigma.bytecode;
12 13
13import javassist.CtBehavior; 14import javassist.CtBehavior;
@@ -16,36 +17,35 @@ import javassist.CtField;
16import javassist.bytecode.AccessFlag; 17import javassist.bytecode.AccessFlag;
17import javassist.bytecode.InnerClassesAttribute; 18import javassist.bytecode.InnerClassesAttribute;
18 19
19
20public class ClassPublifier { 20public class ClassPublifier {
21 21
22 public static CtClass publify(CtClass c) { 22 public static CtClass publify(CtClass c) {
23 23
24 // publify all the fields 24 // publify all the fields
25 for (CtField field : c.getDeclaredFields()) { 25 for (CtField field : c.getDeclaredFields()) {
26 field.setModifiers(publify(field.getModifiers())); 26 field.setModifiers(publify(field.getModifiers()));
27 } 27 }
28 28
29 // publify all the methods and constructors 29 // publify all the methods and constructors
30 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 30 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
31 behavior.setModifiers(publify(behavior.getModifiers())); 31 behavior.setModifiers(publify(behavior.getModifiers()));
32 } 32 }
33 33
34 // publify all the inner classes 34 // publify all the inner classes
35 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); 35 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
36 if (attr != null) { 36 if (attr != null) {
37 for (int i = 0; i < attr.tableLength(); i++) { 37 for (int i = 0; i < attr.tableLength(); i++) {
38 attr.setAccessFlags(i, publify(attr.accessFlags(i))); 38 attr.setAccessFlags(i, publify(attr.accessFlags(i)));
39 } 39 }
40 } 40 }
41 41
42 return c; 42 return c;
43 } 43 }
44 44
45 private static int publify(int flags) { 45 private static int publify(int flags) {
46 if (!AccessFlag.isPublic(flags)) { 46 if (!AccessFlag.isPublic(flags)) {
47 flags = AccessFlag.setPublic(flags); 47 flags = AccessFlag.setPublic(flags);
48 } 48 }
49 return flags; 49 return flags;
50 } 50 }
51} 51}
diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java b/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java
index d874633d..a52cab6d 100644
--- a/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java
+++ b/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java
@@ -8,532 +8,532 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.bytecode;
12 11
13import java.lang.reflect.InvocationTargetException; 12package cuchaz.enigma.bytecode;
14import java.lang.reflect.Method;
15import java.util.Arrays;
16import java.util.HashMap;
17import java.util.List;
18import java.util.Map;
19 13
20import cuchaz.enigma.mapping.ClassEntry; 14import cuchaz.enigma.mapping.ClassEntry;
21import cuchaz.enigma.mapping.ClassNameReplacer; 15import cuchaz.enigma.mapping.ClassNameReplacer;
22import cuchaz.enigma.mapping.Mappings; 16import cuchaz.enigma.mapping.Mappings;
23import cuchaz.enigma.mapping.Translator; 17import cuchaz.enigma.mapping.Translator;
24import javassist.*; 18import javassist.CtBehavior;
19import javassist.CtClass;
20import javassist.CtField;
21import javassist.Modifier;
25import javassist.bytecode.*; 22import javassist.bytecode.*;
26import javassist.bytecode.SignatureAttribute.*; 23import javassist.bytecode.SignatureAttribute.*;
27 24
25import java.lang.reflect.InvocationTargetException;
26import java.lang.reflect.Method;
27import java.util.Arrays;
28import java.util.HashMap;
29import java.util.List;
30import java.util.Map;
31
28public class ClassRenamer { 32public class ClassRenamer {
29 33
30 private enum SignatureType { 34 public static void applyModifier(Object obj, Mappings.EntryModifier modifier) {
31 Class { 35 int mod = -1;
32 @Override 36 if (obj instanceof CtField)
33 public String rename(String signature, ReplacerClassMap map) { 37 mod = ((CtField) obj).getModifiers();
34 return renameClassSignature(signature, map); 38 else if (obj instanceof CtBehavior)
35 } 39 mod = ((CtBehavior) obj).getModifiers();
36 }, 40 else if (obj instanceof CtClass)
37 Field { 41 mod = ((CtClass) obj).getModifiers();
38 @Override 42
39 public String rename(String signature, ReplacerClassMap map) { 43 if (mod != -1) {
40 return renameFieldSignature(signature, map); 44 switch (modifier) {
41 } 45 case PRIVATE:
42 }, 46 mod = Modifier.setPrivate(mod);
43 Method { 47 break;
44 @Override 48 case PROTECTED:
45 public String rename(String signature, ReplacerClassMap map) { 49 mod = Modifier.setProtected(mod);
46 return renameMethodSignature(signature, map); 50 break;
47 } 51 case PUBLIC:
48 }; 52 mod = Modifier.setPublic(mod);
49 53 break;
50 public abstract String rename(String signature, ReplacerClassMap map); 54 default:
51 } 55 break;
52 56 }
53 private static class ReplacerClassMap extends HashMap<String, String> { 57 if (obj instanceof CtField)
54 58 ((CtField) obj).setModifiers(mod);
55 private ClassNameReplacer replacer; 59 else if (obj instanceof CtBehavior)
56 60 ((CtBehavior) obj).setModifiers(mod);
57 public ReplacerClassMap(ClassNameReplacer replacer) { 61 else
58 this.replacer = replacer; 62 ((CtClass) obj).setModifiers(mod);
59 } 63 }
60 64 }
61 @Override 65
62 public String get(Object obj) { 66 public static void renameClasses(CtClass c, final Translator translator) {
63 if (obj instanceof String) { 67 renameClasses(c, className -> {
64 return get((String) obj); 68 ClassEntry entry = translator.translateEntry(new ClassEntry(className));
65 } 69 if (entry != null) {
66 return null; 70 return entry.getName();
67 } 71 }
68 72 return null;
69 public String get(String className) { 73 });
70 return replacer.replace(className); 74 }
71 } 75
72 } 76 public static void moveAllClassesOutOfDefaultPackage(CtClass c, final String newPackageName) {
73 77 renameClasses(c, className -> {
74 public static void applyModifier(Object obj, Mappings.EntryModifier modifier) 78 ClassEntry entry = new ClassEntry(className);
75 { 79 if (entry.isInDefaultPackage()) {
76 int mod = -1; 80 return newPackageName + "/" + entry.getName();
77 if (obj instanceof CtField) 81 }
78 mod = ((CtField) obj).getModifiers(); 82 return null;
79 else if (obj instanceof CtBehavior) 83 });
80 mod = ((CtBehavior) obj).getModifiers(); 84 }
81 else if (obj instanceof CtClass) 85
82 mod = ((CtClass) obj).getModifiers(); 86 public static void moveAllClassesIntoDefaultPackage(CtClass c, final String oldPackageName) {
83 87 renameClasses(c, className -> {
84 if (mod != -1) 88 ClassEntry entry = new ClassEntry(className);
85 { 89 if (entry.getPackageName().equals(oldPackageName)) {
86 switch (modifier) 90 return entry.getSimpleName();
87 { 91 }
88 case PRIVATE: 92 return null;
89 mod = Modifier.setPrivate(mod); 93 });
90 break; 94 }
91 case PROTECTED: 95
92 mod = Modifier.setProtected(mod); 96 @SuppressWarnings("unchecked")
93 break; 97 public static void renameClasses(CtClass c, ClassNameReplacer replacer) {
94 case PUBLIC: 98
95 mod = Modifier.setPublic(mod); 99 // sadly, we can't use CtClass.renameClass() because SignatureAttribute.renameClass() is extremely buggy =(
96 break; 100
97 default: 101 ReplacerClassMap map = new ReplacerClassMap(replacer);
98 break; 102 ClassFile classFile = c.getClassFile();
99 } 103
100 if (obj instanceof CtField) 104 // rename the constant pool (covers ClassInfo, MethodTypeInfo, and NameAndTypeInfo)
101 ((CtField) obj).setModifiers(mod); 105 ConstPool constPool = c.getClassFile().getConstPool();
102 else if (obj instanceof CtBehavior) 106 constPool.renameClass(map);
103 ((CtBehavior) obj).setModifiers(mod); 107
104 else 108 // rename class attributes
105 ((CtClass) obj).setModifiers(mod); 109 renameAttributes(classFile.getAttributes(), map, SignatureType.Class);
106 } 110
107 } 111 // rename methods
108 112 for (MethodInfo methodInfo : (List<MethodInfo>) classFile.getMethods()) {
109 public static void renameClasses(CtClass c, final Translator translator) { 113 methodInfo.setDescriptor(Descriptor.rename(methodInfo.getDescriptor(), map));
110 renameClasses(c, className -> { 114 renameAttributes(methodInfo.getAttributes(), map, SignatureType.Method);
111 ClassEntry entry = translator.translateEntry(new ClassEntry(className)); 115 }
112 if (entry != null) { 116
113 return entry.getName(); 117 // rename fields
114 } 118 for (FieldInfo fieldInfo : (List<FieldInfo>) classFile.getFields()) {
115 return null; 119 fieldInfo.setDescriptor(Descriptor.rename(fieldInfo.getDescriptor(), map));
116 }); 120 renameAttributes(fieldInfo.getAttributes(), map, SignatureType.Field);
117 } 121 }
118 122
119 public static void moveAllClassesOutOfDefaultPackage(CtClass c, final String newPackageName) { 123 // rename the class name itself last
120 renameClasses(c, className -> { 124 // NOTE: don't use the map here, because setName() calls the buggy SignatureAttribute.renameClass()
121 ClassEntry entry = new ClassEntry(className); 125 // we only want to replace exactly this class name
122 if (entry.isInDefaultPackage()) { 126 String newName = renameClassName(c.getName(), map);
123 return newPackageName + "/" + entry.getName(); 127 if (newName != null) {
124 } 128 c.setName(newName);
125 return null; 129 }
126 }); 130
127 } 131 // replace simple names in the InnerClasses attribute too
128 132 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
129 public static void moveAllClassesIntoDefaultPackage(CtClass c, final String oldPackageName) { 133 if (attr != null) {
130 renameClasses(c, className -> { 134 for (int i = 0; i < attr.tableLength(); i++) {
131 ClassEntry entry = new ClassEntry(className); 135
132 if (entry.getPackageName().equals(oldPackageName)) { 136 String innerName = attr.innerClass(i);
133 return entry.getSimpleName(); 137 // get the inner class full name (which has already been translated)
134 } 138 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName));
135 return null; 139
136 }); 140 if (attr.innerNameIndex(i) != 0) {
137 } 141 // update the inner name
138 142 attr.setInnerNameIndex(i, constPool.addUtf8Info(classEntry.getInnermostClassName()));
139 @SuppressWarnings("unchecked") 143 }
140 public static void renameClasses(CtClass c, ClassNameReplacer replacer) {
141
142 // sadly, we can't use CtClass.renameClass() because SignatureAttribute.renameClass() is extremely buggy =(
143
144 ReplacerClassMap map = new ReplacerClassMap(replacer);
145 ClassFile classFile = c.getClassFile();
146
147 // rename the constant pool (covers ClassInfo, MethodTypeInfo, and NameAndTypeInfo)
148 ConstPool constPool = c.getClassFile().getConstPool();
149 constPool.renameClass(map);
150
151 // rename class attributes
152 renameAttributes(classFile.getAttributes(), map, SignatureType.Class);
153
154 // rename methods
155 for (MethodInfo methodInfo : (List<MethodInfo>) classFile.getMethods()) {
156 methodInfo.setDescriptor(Descriptor.rename(methodInfo.getDescriptor(), map));
157 renameAttributes(methodInfo.getAttributes(), map, SignatureType.Method);
158 }
159
160 // rename fields
161 for (FieldInfo fieldInfo : (List<FieldInfo>) classFile.getFields()) {
162 fieldInfo.setDescriptor(Descriptor.rename(fieldInfo.getDescriptor(), map));
163 renameAttributes(fieldInfo.getAttributes(), map, SignatureType.Field);
164 }
165
166 // rename the class name itself last
167 // NOTE: don't use the map here, because setName() calls the buggy SignatureAttribute.renameClass()
168 // we only want to replace exactly this class name
169 String newName = renameClassName(c.getName(), map);
170 if (newName != null) {
171 c.setName(newName);
172 }
173
174 // replace simple names in the InnerClasses attribute too
175 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
176 if (attr != null) {
177 for (int i = 0; i < attr.tableLength(); i++) {
178
179 String innerName = attr.innerClass(i);
180 // get the inner class full name (which has already been translated)
181 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName));
182
183 if (attr.innerNameIndex(i) != 0) {
184 // update the inner name
185 attr.setInnerNameIndex(i, constPool.addUtf8Info(classEntry.getInnermostClassName()));
186 }
187 144
188 /* DEBUG 145 /* DEBUG
189 System.out.println(String.format("\tDEOBF: %s-> ATTR: %s,%s,%s", classEntry, attr.outerClass(i), attr.innerClass(i), attr.innerName(i))); 146 System.out.println(String.format("\tDEOBF: %s-> ATTR: %s,%s,%s", classEntry, attr.outerClass(i), attr.innerClass(i), attr.innerName(i)));
190 */ 147 */
191 } 148 }
192 } 149 }
193 } 150 }
194 151
195 @SuppressWarnings("unchecked") 152 @SuppressWarnings("unchecked")
196 private static void renameAttributes(List<AttributeInfo> attributes, ReplacerClassMap map, SignatureType type) { 153 private static void renameAttributes(List<AttributeInfo> attributes, ReplacerClassMap map, SignatureType type) {
197 try { 154 try {
198 155
199 // make the rename class method accessible 156 // make the rename class method accessible
200 Method renameClassMethod = AttributeInfo.class.getDeclaredMethod("renameClass", Map.class); 157 Method renameClassMethod = AttributeInfo.class.getDeclaredMethod("renameClass", Map.class);
201 renameClassMethod.setAccessible(true); 158 renameClassMethod.setAccessible(true);
202 159
203 for (AttributeInfo attribute : attributes) { 160 for (AttributeInfo attribute : attributes) {
204 if (attribute instanceof SignatureAttribute) { 161 if (attribute instanceof SignatureAttribute) {
205 // this has to be handled specially because SignatureAttribute.renameClass() is buggy as hell 162 // this has to be handled specially because SignatureAttribute.renameClass() is buggy as hell
206 SignatureAttribute signatureAttribute = (SignatureAttribute) attribute; 163 SignatureAttribute signatureAttribute = (SignatureAttribute) attribute;
207 String newSignature = type.rename(signatureAttribute.getSignature(), map); 164 String newSignature = type.rename(signatureAttribute.getSignature(), map);
208 if (newSignature != null) { 165 if (newSignature != null) {
209 signatureAttribute.setSignature(newSignature); 166 signatureAttribute.setSignature(newSignature);
210 } 167 }
211 } else if (attribute instanceof CodeAttribute) { 168 } else if (attribute instanceof CodeAttribute) {
212 // code attributes have signature attributes too (indirectly) 169 // code attributes have signature attributes too (indirectly)
213 CodeAttribute codeAttribute = (CodeAttribute) attribute; 170 CodeAttribute codeAttribute = (CodeAttribute) attribute;
214 renameAttributes(codeAttribute.getAttributes(), map, type); 171 renameAttributes(codeAttribute.getAttributes(), map, type);
215 } else if (attribute instanceof LocalVariableTypeAttribute) { 172 } else if (attribute instanceof LocalVariableTypeAttribute) {
216 // lvt attributes have signature attributes too 173 // lvt attributes have signature attributes too
217 LocalVariableTypeAttribute localVariableAttribute = (LocalVariableTypeAttribute) attribute; 174 LocalVariableTypeAttribute localVariableAttribute = (LocalVariableTypeAttribute) attribute;
218 renameLocalVariableTypeAttribute(localVariableAttribute, map); 175 renameLocalVariableTypeAttribute(localVariableAttribute, map);
219 } else { 176 } else {
220 renameClassMethod.invoke(attribute, map); 177 renameClassMethod.invoke(attribute, map);
221 } 178 }
222 } 179 }
223 180
224 } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { 181 } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
225 throw new Error("Unable to call javassist methods by reflection!", ex); 182 throw new Error("Unable to call javassist methods by reflection!", ex);
226 } 183 }
227 } 184 }
228 185
229 private static void renameLocalVariableTypeAttribute(LocalVariableTypeAttribute attribute, ReplacerClassMap map) { 186 private static void renameLocalVariableTypeAttribute(LocalVariableTypeAttribute attribute, ReplacerClassMap map) {
230 187
231 // adapted from LocalVariableAttribute.renameClass() 188 // adapted from LocalVariableAttribute.renameClass()
232 ConstPool cp = attribute.getConstPool(); 189 ConstPool cp = attribute.getConstPool();
233 int n = attribute.tableLength(); 190 int n = attribute.tableLength();
234 byte[] info = attribute.get(); 191 byte[] info = attribute.get();
235 for (int i = 0; i < n; ++i) { 192 for (int i = 0; i < n; ++i) {
236 int pos = i * 10 + 2; 193 int pos = i * 10 + 2;
237 int index = ByteArray.readU16bit(info, pos + 6); 194 int index = ByteArray.readU16bit(info, pos + 6);
238 if (index != 0) { 195 if (index != 0) {
239 String signature = cp.getUtf8Info(index); 196 String signature = cp.getUtf8Info(index);
240 String newSignature = renameLocalVariableSignature(signature, map); 197 String newSignature = renameLocalVariableSignature(signature, map);
241 if (newSignature != null) { 198 if (newSignature != null) {
242 ByteArray.write16bit(cp.addUtf8Info(newSignature), info, pos + 6); 199 ByteArray.write16bit(cp.addUtf8Info(newSignature), info, pos + 6);
243 } 200 }
244 } 201 }
245 } 202 }
246 } 203 }
247 204
248 private static String renameLocalVariableSignature(String signature, ReplacerClassMap map) { 205 private static String renameLocalVariableSignature(String signature, ReplacerClassMap map) {
249 206
250 // for some reason, signatures with . in them don't count as field signatures 207 // for some reason, signatures with . in them don't count as field signatures
251 // looks like anonymous classes delimit with . in stead of $ 208 // looks like anonymous classes delimit with . in stead of $
252 // convert the . to $, but keep track of how many we replace 209 // convert the . to $, but keep track of how many we replace
253 // we need to put them back after we translate 210 // we need to put them back after we translate
254 int start = signature.lastIndexOf('$') + 1; 211 int start = signature.lastIndexOf('$') + 1;
255 int numConverted = 0; 212 int numConverted = 0;
256 StringBuilder buf = new StringBuilder(signature); 213 StringBuilder buf = new StringBuilder(signature);
257 for (int i = buf.length() - 1; i >= start; i--) { 214 for (int i = buf.length() - 1; i >= start; i--) {
258 char c = buf.charAt(i); 215 char c = buf.charAt(i);
259 if (c == '.') { 216 if (c == '.') {
260 buf.setCharAt(i, '$'); 217 buf.setCharAt(i, '$');
261 numConverted++; 218 numConverted++;
262 } 219 }
263 } 220 }
264 signature = buf.toString(); 221 signature = buf.toString();
265 222
266 // translate 223 // translate
267 String newSignature = renameFieldSignature(signature, map); 224 String newSignature = renameFieldSignature(signature, map);
268 if (newSignature != null) { 225 if (newSignature != null) {
269 226
270 // put the delimiters back 227 // put the delimiters back
271 buf = new StringBuilder(newSignature); 228 buf = new StringBuilder(newSignature);
272 for (int i = buf.length() - 1; i >= 0 && numConverted > 0; i--) { 229 for (int i = buf.length() - 1; i >= 0 && numConverted > 0; i--) {
273 char c = buf.charAt(i); 230 char c = buf.charAt(i);
274 if (c == '$') { 231 if (c == '$') {
275 buf.setCharAt(i, '.'); 232 buf.setCharAt(i, '.');
276 numConverted--; 233 numConverted--;
277 } 234 }
278 } 235 }
279 assert (numConverted == 0); 236 assert (numConverted == 0);
280 newSignature = buf.toString(); 237 newSignature = buf.toString();
281 238
282 return newSignature; 239 return newSignature;
283 } 240 }
284 241
285 return null; 242 return null;
286 } 243 }
287 244
288 private static String renameClassSignature(String signature, ReplacerClassMap map) { 245 private static String renameClassSignature(String signature, ReplacerClassMap map) {
289 try { 246 try {
290 ClassSignature type = renameType(SignatureAttribute.toClassSignature(signature), map); 247 ClassSignature type = renameType(SignatureAttribute.toClassSignature(signature), map);
291 return type.encode(); 248 return type.encode();
292 } catch (BadBytecode ex) { 249 } catch (BadBytecode ex) {
293 throw new Error("Can't parse field signature: " + signature); 250 throw new Error("Can't parse field signature: " + signature);
294 } 251 }
295 } 252 }
296 253
297 private static String renameFieldSignature(String signature, ReplacerClassMap map) { 254 private static String renameFieldSignature(String signature, ReplacerClassMap map) {
298 try { 255 try {
299 ObjectType type = renameType(SignatureAttribute.toFieldSignature(signature), map); 256 ObjectType type = renameType(SignatureAttribute.toFieldSignature(signature), map);
300 if (type != null) { 257 if (type != null) {
301 return type.encode(); 258 return type.encode();
302 } 259 }
303 return null; 260 return null;
304 } catch (BadBytecode ex) { 261 } catch (BadBytecode ex) {
305 throw new Error("Can't parse class signature: " + signature); 262 throw new Error("Can't parse class signature: " + signature);
306 } 263 }
307 } 264 }
308 265
309 private static String renameMethodSignature(String signature, ReplacerClassMap map) { 266 private static String renameMethodSignature(String signature, ReplacerClassMap map) {
310 try { 267 try {
311 MethodSignature type = renameType(SignatureAttribute.toMethodSignature(signature), map); 268 MethodSignature type = renameType(SignatureAttribute.toMethodSignature(signature), map);
312 return type.encode(); 269 return type.encode();
313 } catch (BadBytecode ex) { 270 } catch (BadBytecode ex) {
314 throw new Error("Can't parse method signature: " + signature); 271 throw new Error("Can't parse method signature: " + signature);
315 } 272 }
316 } 273 }
317 274
318 private static TypeParameter[] renameTypeParameter(TypeParameter[] typeParamTypes, ReplacerClassMap map) 275 private static TypeParameter[] renameTypeParameter(TypeParameter[] typeParamTypes, ReplacerClassMap map) {
319 { 276 if (typeParamTypes != null) {
320 if (typeParamTypes != null) { 277 typeParamTypes = Arrays.copyOf(typeParamTypes, typeParamTypes.length);
321 typeParamTypes = Arrays.copyOf(typeParamTypes, typeParamTypes.length); 278 for (int i = 0; i < typeParamTypes.length; i++) {
322 for (int i = 0; i < typeParamTypes.length; i++) { 279 TypeParameter newParamType = renameType(typeParamTypes[i], map);
323 TypeParameter newParamType = renameType(typeParamTypes[i], map); 280 if (newParamType != null) {
324 if (newParamType != null) { 281 typeParamTypes[i] = newParamType;
325 typeParamTypes[i] = newParamType; 282 }
326 } 283 }
327 } 284 }
328 } 285 return typeParamTypes;
329 return typeParamTypes; 286 }
330 } 287
331 288 private static ClassSignature renameType(ClassSignature type, ReplacerClassMap map) {
332 private static ClassSignature renameType(ClassSignature type, ReplacerClassMap map) { 289
333 290 TypeParameter[] typeParamTypes = renameTypeParameter(type.getParameters(), map);
334 TypeParameter[] typeParamTypes = renameTypeParameter(type.getParameters(), map); 291
335 292 ClassType superclassType = type.getSuperClass();
336 ClassType superclassType = type.getSuperClass(); 293 if (superclassType != ClassType.OBJECT) {
337 if (superclassType != ClassType.OBJECT) { 294 ClassType newSuperclassType = renameType(superclassType, map);
338 ClassType newSuperclassType = renameType(superclassType, map); 295 if (newSuperclassType != null) {
339 if (newSuperclassType != null) { 296 superclassType = newSuperclassType;
340 superclassType = newSuperclassType; 297 }
341 } 298 }
342 } 299
343 300 ClassType[] interfaceTypes = type.getInterfaces();
344 ClassType[] interfaceTypes = type.getInterfaces(); 301 if (interfaceTypes != null) {
345 if (interfaceTypes != null) { 302 interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length);
346 interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length); 303 for (int i = 0; i < interfaceTypes.length; i++) {
347 for (int i = 0; i < interfaceTypes.length; i++) { 304 ClassType newInterfaceType = renameType(interfaceTypes[i], map);
348 ClassType newInterfaceType = renameType(interfaceTypes[i], map); 305 if (newInterfaceType != null) {
349 if (newInterfaceType != null) { 306 interfaceTypes[i] = newInterfaceType;
350 interfaceTypes[i] = newInterfaceType; 307 }
351 } 308 }
352 } 309 }
353 } 310
354 311 return new ClassSignature(typeParamTypes, superclassType, interfaceTypes);
355 return new ClassSignature(typeParamTypes, superclassType, interfaceTypes); 312 }
356 } 313
357 314 private static MethodSignature renameType(MethodSignature type, ReplacerClassMap map) {
358 private static MethodSignature renameType(MethodSignature type, ReplacerClassMap map) { 315
359 316 TypeParameter[] typeParamTypes = renameTypeParameter(type.getTypeParameters(), map);
360 TypeParameter[] typeParamTypes = renameTypeParameter(type.getTypeParameters(), map); 317
361 318 Type[] paramTypes = type.getParameterTypes();
362 Type[] paramTypes = type.getParameterTypes(); 319 if (paramTypes != null) {
363 if (paramTypes != null) { 320 paramTypes = Arrays.copyOf(paramTypes, paramTypes.length);
364 paramTypes = Arrays.copyOf(paramTypes, paramTypes.length); 321 for (int i = 0; i < paramTypes.length; i++) {
365 for (int i = 0; i < paramTypes.length; i++) { 322 Type newParamType = renameType(paramTypes[i], map);
366 Type newParamType = renameType(paramTypes[i], map); 323 if (newParamType != null) {
367 if (newParamType != null) { 324 paramTypes[i] = newParamType;
368 paramTypes[i] = newParamType; 325 }
369 } 326 }
370 } 327 }
371 } 328
372 329 Type returnType = type.getReturnType();
373 Type returnType = type.getReturnType(); 330 if (returnType != null) {
374 if (returnType != null) { 331 Type newReturnType = renameType(returnType, map);
375 Type newReturnType = renameType(returnType, map); 332 if (newReturnType != null) {
376 if (newReturnType != null) { 333 returnType = newReturnType;
377 returnType = newReturnType; 334 }
378 } 335 }
379 } 336
380 337 ObjectType[] exceptionTypes = type.getExceptionTypes();
381 ObjectType[] exceptionTypes = type.getExceptionTypes(); 338 if (exceptionTypes != null) {
382 if (exceptionTypes != null) { 339 exceptionTypes = Arrays.copyOf(exceptionTypes, exceptionTypes.length);
383 exceptionTypes = Arrays.copyOf(exceptionTypes, exceptionTypes.length); 340 for (int i = 0; i < exceptionTypes.length; i++) {
384 for (int i = 0; i < exceptionTypes.length; i++) { 341 ObjectType newExceptionType = renameType(exceptionTypes[i], map);
385 ObjectType newExceptionType = renameType(exceptionTypes[i], map); 342 if (newExceptionType != null) {
386 if (newExceptionType != null) { 343 exceptionTypes[i] = newExceptionType;
387 exceptionTypes[i] = newExceptionType; 344 }
388 } 345 }
389 } 346 }
390 } 347
391 348 return new MethodSignature(typeParamTypes, paramTypes, returnType, exceptionTypes);
392 return new MethodSignature(typeParamTypes, paramTypes, returnType, exceptionTypes); 349 }
393 } 350
394 351 private static Type renameType(Type type, ReplacerClassMap map) {
395 private static Type renameType(Type type, ReplacerClassMap map) { 352 if (type instanceof ObjectType) {
396 if (type instanceof ObjectType) { 353 return renameType((ObjectType) type, map);
397 return renameType((ObjectType) type, map); 354 } else if (type instanceof BaseType) {
398 } else if (type instanceof BaseType) { 355 return renameType((BaseType) type, map);
399 return renameType((BaseType) type, map); 356 } else {
400 } else { 357 throw new Error("Don't know how to rename type " + type.getClass());
401 throw new Error("Don't know how to rename type " + type.getClass()); 358 }
402 } 359 }
403 } 360
404 361 private static ObjectType renameType(ObjectType type, ReplacerClassMap map) {
405 private static ObjectType renameType(ObjectType type, ReplacerClassMap map) { 362 if (type instanceof ArrayType) {
406 if (type instanceof ArrayType) { 363 return renameType((ArrayType) type, map);
407 return renameType((ArrayType) type, map); 364 } else if (type instanceof ClassType) {
408 } else if (type instanceof ClassType) { 365 return renameType((ClassType) type, map);
409 return renameType((ClassType) type, map); 366 } else if (type instanceof TypeVariable) {
410 } else if (type instanceof TypeVariable) { 367 return renameType((TypeVariable) type, map);
411 return renameType((TypeVariable) type, map); 368 } else {
412 } else { 369 throw new Error("Don't know how to rename type " + type.getClass());
413 throw new Error("Don't know how to rename type " + type.getClass()); 370 }
414 } 371 }
415 } 372
416 373 private static BaseType renameType(BaseType type, ReplacerClassMap map) {
417 private static BaseType renameType(BaseType type, ReplacerClassMap map) { 374 // don't have to rename primitives
418 // don't have to rename primitives 375 return null;
419 return null; 376 }
420 } 377
421 378 private static TypeVariable renameType(TypeVariable type, ReplacerClassMap map) {
422 private static TypeVariable renameType(TypeVariable type, ReplacerClassMap map) { 379 // don't have to rename template args
423 // don't have to rename template args 380 return null;
424 return null; 381 }
425 } 382
426 383 private static ClassType renameType(ClassType type, ReplacerClassMap map) {
427 private static ClassType renameType(ClassType type, ReplacerClassMap map) { 384
428 385 // translate type args
429 // translate type args 386 TypeArgument[] args = type.getTypeArguments();
430 TypeArgument[] args = type.getTypeArguments(); 387 if (args != null) {
431 if (args != null) { 388 args = Arrays.copyOf(args, args.length);
432 args = Arrays.copyOf(args, args.length); 389 for (int i = 0; i < args.length; i++) {
433 for (int i = 0; i < args.length; i++) { 390 TypeArgument newType = renameType(args[i], map);
434 TypeArgument newType = renameType(args[i], map); 391 if (newType != null) {
435 if (newType != null) { 392 args[i] = newType;
436 args[i] = newType; 393 }
437 } 394 }
438 } 395 }
439 } 396
440 397 if (type instanceof NestedClassType) {
441 if (type instanceof NestedClassType) { 398 NestedClassType nestedType = (NestedClassType) type;
442 NestedClassType nestedType = (NestedClassType) type; 399
443 400 // translate the name
444 // translate the name 401 String name = getClassName(type);
445 String name = getClassName(type); 402 String newName = map.get(name);
446 String newName = map.get(name); 403 if (newName != null) {
447 if (newName != null) { 404 name = new ClassEntry(newName).getInnermostClassName();
448 name = new ClassEntry(newName).getInnermostClassName(); 405 }
449 } 406
450 407 // translate the parent class too
451 // translate the parent class too 408 ClassType parent = renameType(nestedType.getDeclaringClass(), map);
452 ClassType parent = renameType(nestedType.getDeclaringClass(), map); 409 if (parent == null) {
453 if (parent == null) { 410 parent = nestedType.getDeclaringClass();
454 parent = nestedType.getDeclaringClass(); 411 }
455 } 412
456 413 return new NestedClassType(parent, name, args);
457 return new NestedClassType(parent, name, args); 414 } else {
458 } else { 415
459 416 // translate the name
460 // translate the name 417 String name = type.getName();
461 String name = type.getName(); 418 String newName = renameClassName(name, map);
462 String newName = renameClassName(name, map); 419 if (newName != null) {
463 if (newName != null) { 420 name = newName;
464 name = newName; 421 }
465 } 422
466 423 return new ClassType(name, args);
467 return new ClassType(name, args); 424 }
468 } 425 }
469 } 426
470 427 private static String getClassName(ClassType type) {
471 private static String getClassName(ClassType type) { 428 if (type instanceof NestedClassType) {
472 if (type instanceof NestedClassType) { 429 NestedClassType nestedType = (NestedClassType) type;
473 NestedClassType nestedType = (NestedClassType) type; 430 return getClassName(nestedType.getDeclaringClass()) + "$" + Descriptor.toJvmName(type.getName().replace('.', '$'));
474 return getClassName(nestedType.getDeclaringClass()) + "$" + Descriptor.toJvmName(type.getName().replace('.', '$')); 431 } else {
475 } else { 432 return Descriptor.toJvmName(type.getName());
476 return Descriptor.toJvmName(type.getName()); 433 }
477 } 434 }
478 } 435
479 436 private static String renameClassName(String name, ReplacerClassMap map) {
480 private static String renameClassName(String name, ReplacerClassMap map) { 437 String newName = map.get(Descriptor.toJvmName(name));
481 String newName = map.get(Descriptor.toJvmName(name)); 438 if (newName != null) {
482 if (newName != null) { 439 return Descriptor.toJavaName(newName);
483 return Descriptor.toJavaName(newName); 440 }
484 } 441 return null;
485 return null; 442 }
486 } 443
487 444 private static TypeArgument renameType(TypeArgument type, ReplacerClassMap map) {
488 private static TypeArgument renameType(TypeArgument type, ReplacerClassMap map) { 445 ObjectType subType = type.getType();
489 ObjectType subType = type.getType(); 446 if (subType != null) {
490 if (subType != null) { 447 ObjectType newSubType = renameType(subType, map);
491 ObjectType newSubType = renameType(subType, map); 448 if (newSubType != null) {
492 if (newSubType != null) { 449 switch (type.getKind()) {
493 switch (type.getKind()) { 450 case ' ':
494 case ' ': 451 return new TypeArgument(newSubType);
495 return new TypeArgument(newSubType); 452 case '+':
496 case '+': 453 return TypeArgument.subclassOf(newSubType);
497 return TypeArgument.subclassOf(newSubType); 454 case '-':
498 case '-': 455 return TypeArgument.superOf(newSubType);
499 return TypeArgument.superOf(newSubType); 456 default:
500 default: 457 throw new Error("Unknown type kind: " + type.getKind());
501 throw new Error("Unknown type kind: " + type.getKind()); 458 }
502 } 459 }
503 } 460 }
504 } 461 return null;
505 return null; 462 }
506 } 463
507 464 private static ArrayType renameType(ArrayType type, ReplacerClassMap map) {
508 private static ArrayType renameType(ArrayType type, ReplacerClassMap map) { 465 Type newSubType = renameType(type.getComponentType(), map);
509 Type newSubType = renameType(type.getComponentType(), map); 466 if (newSubType != null) {
510 if (newSubType != null) { 467 return new ArrayType(type.getDimension(), newSubType);
511 return new ArrayType(type.getDimension(), newSubType); 468 }
512 } 469 return null;
513 return null; 470 }
514 } 471
515 472 private static TypeParameter renameType(TypeParameter type, ReplacerClassMap map) {
516 private static TypeParameter renameType(TypeParameter type, ReplacerClassMap map) { 473
517 474 ObjectType superclassType = type.getClassBound();
518 ObjectType superclassType = type.getClassBound(); 475 if (superclassType != null) {
519 if (superclassType != null) { 476 ObjectType newSuperclassType = renameType(superclassType, map);
520 ObjectType newSuperclassType = renameType(superclassType, map); 477 if (newSuperclassType != null) {
521 if (newSuperclassType != null) { 478 superclassType = newSuperclassType;
522 superclassType = newSuperclassType; 479 }
523 } 480 }
524 } 481
525 482 ObjectType[] interfaceTypes = type.getInterfaceBound();
526 ObjectType[] interfaceTypes = type.getInterfaceBound(); 483 if (interfaceTypes != null) {
527 if (interfaceTypes != null) { 484 interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length);
528 interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length); 485 for (int i = 0; i < interfaceTypes.length; i++) {
529 for (int i = 0; i < interfaceTypes.length; i++) { 486 ObjectType newInterfaceType = renameType(interfaceTypes[i], map);
530 ObjectType newInterfaceType = renameType(interfaceTypes[i], map); 487 if (newInterfaceType != null) {
531 if (newInterfaceType != null) { 488 interfaceTypes[i] = newInterfaceType;
532 interfaceTypes[i] = newInterfaceType; 489 }
533 } 490 }
534 } 491 }
535 } 492
536 493 return new TypeParameter(type.getName(), superclassType, interfaceTypes);
537 return new TypeParameter(type.getName(), superclassType, interfaceTypes); 494 }
538 } 495
496 private enum SignatureType {
497 Class {
498 @Override
499 public String rename(String signature, ReplacerClassMap map) {
500 return renameClassSignature(signature, map);
501 }
502 },
503 Field {
504 @Override
505 public String rename(String signature, ReplacerClassMap map) {
506 return renameFieldSignature(signature, map);
507 }
508 },
509 Method {
510 @Override
511 public String rename(String signature, ReplacerClassMap map) {
512 return renameMethodSignature(signature, map);
513 }
514 };
515
516 public abstract String rename(String signature, ReplacerClassMap map);
517 }
518
519 private static class ReplacerClassMap extends HashMap<String, String> {
520
521 private ClassNameReplacer replacer;
522
523 public ReplacerClassMap(ClassNameReplacer replacer) {
524 this.replacer = replacer;
525 }
526
527 @Override
528 public String get(Object obj) {
529 if (obj instanceof String) {
530 return get((String) obj);
531 }
532 return null;
533 }
534
535 public String get(String className) {
536 return replacer.replace(className);
537 }
538 }
539} 539}
diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java b/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java
index 62ebfafb..1ebf6561 100644
--- a/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java
+++ b/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java
@@ -8,155 +8,158 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode; 12package cuchaz.enigma.bytecode;
12 13
13import cuchaz.enigma.mapping.*; 14import cuchaz.enigma.mapping.*;
14import cuchaz.enigma.mapping.Translator; 15import javassist.CtBehavior;
15import javassist.*; 16import javassist.CtClass;
17import javassist.CtField;
18import javassist.CtMethod;
16import javassist.bytecode.*; 19import javassist.bytecode.*;
17 20
18public class ClassTranslator { 21public class ClassTranslator {
19 22
20 private Translator translator; 23 private Translator translator;
21 24
22 public ClassTranslator(Translator translator) { 25 public ClassTranslator(Translator translator) {
23 this.translator = translator; 26 this.translator = translator;
24 } 27 }
25 28
26 public void translate(CtClass c) { 29 public void translate(CtClass c) {
27 30
28 // NOTE: the order of these translations is very important 31 // NOTE: the order of these translations is very important
29 32
30 // translate all the field and method references in the code by editing the constant pool 33 // translate all the field and method references in the code by editing the constant pool
31 ConstPool constants = c.getClassFile().getConstPool(); 34 ConstPool constants = c.getClassFile().getConstPool();
32 ConstPoolEditor editor = new ConstPoolEditor(constants); 35 ConstPoolEditor editor = new ConstPoolEditor(constants);
33 for (int i = 1; i < constants.getSize(); i++) { 36 for (int i = 1; i < constants.getSize(); i++) {
34 switch (constants.getTag(i)) { 37 switch (constants.getTag(i)) {
35 38
36 case ConstPool.CONST_Fieldref: { 39 case ConstPool.CONST_Fieldref: {
37 40
38 // translate the name and type 41 // translate the name and type
39 FieldEntry entry = EntryFactory.getFieldEntry( 42 FieldEntry entry = EntryFactory.getFieldEntry(
40 Descriptor.toJvmName(constants.getFieldrefClassName(i)), 43 Descriptor.toJvmName(constants.getFieldrefClassName(i)),
41 constants.getFieldrefName(i), 44 constants.getFieldrefName(i),
42 constants.getFieldrefType(i) 45 constants.getFieldrefType(i)
43 ); 46 );
44 FieldEntry translatedEntry = this.translator.translateEntry(entry); 47 FieldEntry translatedEntry = this.translator.translateEntry(entry);
45 if (!entry.equals(translatedEntry)) { 48 if (!entry.equals(translatedEntry)) {
46 editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getType().toString()); 49 editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getType().toString());
47 } 50 }
48 } 51 }
49 break; 52 break;
50 53
51 case ConstPool.CONST_Methodref: 54 case ConstPool.CONST_Methodref:
52 case ConstPool.CONST_InterfaceMethodref: { 55 case ConstPool.CONST_InterfaceMethodref: {
53 56
54 // translate the name and type (ie signature) 57 // translate the name and type (ie signature)
55 BehaviorEntry entry = EntryFactory.getBehaviorEntry( 58 BehaviorEntry entry = EntryFactory.getBehaviorEntry(
56 Descriptor.toJvmName(editor.getMemberrefClassname(i)), 59 Descriptor.toJvmName(editor.getMemberrefClassname(i)),
57 editor.getMemberrefName(i), 60 editor.getMemberrefName(i),
58 editor.getMemberrefType(i) 61 editor.getMemberrefType(i)
59 ); 62 );
60 BehaviorEntry translatedEntry = this.translator.translateEntry(entry); 63 BehaviorEntry translatedEntry = this.translator.translateEntry(entry);
61 if (!entry.equals(translatedEntry)) { 64 if (!entry.equals(translatedEntry)) {
62 editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString()); 65 editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString());
63 } 66 }
64 } 67 }
65 break; 68 break;
66 default: 69 default:
67 break; 70 break;
68 } 71 }
69 } 72 }
70 73
71 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); 74 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
72 Mappings.EntryModifier modifier = this.translator.getModifier(classEntry); 75 Mappings.EntryModifier modifier = this.translator.getModifier(classEntry);
73 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) 76 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
74 ClassRenamer.applyModifier(c, modifier); 77 ClassRenamer.applyModifier(c, modifier);
75 78
76 // translate all the fields 79 // translate all the fields
77 for (CtField field : c.getDeclaredFields()) { 80 for (CtField field : c.getDeclaredFields()) {
78 81
79 // translate the name 82 // translate the name
80 FieldEntry entry = EntryFactory.getFieldEntry(field); 83 FieldEntry entry = EntryFactory.getFieldEntry(field);
81 String translatedName = this.translator.translate(entry); 84 String translatedName = this.translator.translate(entry);
82 modifier = this.translator.getModifier(entry); 85 modifier = this.translator.getModifier(entry);
83 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) 86 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
84 ClassRenamer.applyModifier(field, modifier); 87 ClassRenamer.applyModifier(field, modifier);
85 88
86 if (translatedName != null) { 89 if (translatedName != null) {
87 field.setName(translatedName); 90 field.setName(translatedName);
88 } 91 }
89 92
90 // translate the type 93 // translate the type
91 Type translatedType = this.translator.translateType(entry.getType()); 94 Type translatedType = this.translator.translateType(entry.getType());
92 field.getFieldInfo().setDescriptor(translatedType.toString()); 95 field.getFieldInfo().setDescriptor(translatedType.toString());
93 } 96 }
94 97
95 // translate all the methods and constructors 98 // translate all the methods and constructors
96 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 99 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
97 100
98 BehaviorEntry entry = EntryFactory.getBehaviorEntry(behavior); 101 BehaviorEntry entry = EntryFactory.getBehaviorEntry(behavior);
99 102
100 modifier = this.translator.getModifier(entry); 103 modifier = this.translator.getModifier(entry);
101 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) 104 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
102 ClassRenamer.applyModifier(behavior, modifier); 105 ClassRenamer.applyModifier(behavior, modifier);
103 106
104 if (behavior instanceof CtMethod) { 107 if (behavior instanceof CtMethod) {
105 CtMethod method = (CtMethod) behavior; 108 CtMethod method = (CtMethod) behavior;
106 109
107 // translate the name 110 // translate the name
108 String translatedName = this.translator.translate(entry); 111 String translatedName = this.translator.translate(entry);
109 if (translatedName != null) { 112 if (translatedName != null) {
110 method.setName(translatedName); 113 method.setName(translatedName);
111 } 114 }
112 } 115 }
113 116
114 if (entry.getSignature() != null) { 117 if (entry.getSignature() != null) {
115 // translate the signature 118 // translate the signature
116 Signature translatedSignature = this.translator.translateSignature(entry.getSignature()); 119 Signature translatedSignature = this.translator.translateSignature(entry.getSignature());
117 behavior.getMethodInfo().setDescriptor(translatedSignature.toString()); 120 behavior.getMethodInfo().setDescriptor(translatedSignature.toString());
118 } 121 }
119 } 122 }
120 123
121 // translate the EnclosingMethod attribute 124 // translate the EnclosingMethod attribute
122 EnclosingMethodAttribute enclosingMethodAttr = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); 125 EnclosingMethodAttribute enclosingMethodAttr = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag);
123 if (enclosingMethodAttr != null) { 126 if (enclosingMethodAttr != null) {
124 127
125 if (enclosingMethodAttr.methodIndex() == 0) { 128 if (enclosingMethodAttr.methodIndex() == 0) {
126 BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry(Descriptor.toJvmName(enclosingMethodAttr.className())); 129 BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry(Descriptor.toJvmName(enclosingMethodAttr.className()));
127 BehaviorEntry deobfBehaviorEntry = this.translator.translateEntry(obfBehaviorEntry); 130 BehaviorEntry deobfBehaviorEntry = this.translator.translateEntry(obfBehaviorEntry);
128 c.getClassFile().addAttribute(new EnclosingMethodAttribute( 131 c.getClassFile().addAttribute(new EnclosingMethodAttribute(
129 constants, 132 constants,
130 deobfBehaviorEntry.getClassName() 133 deobfBehaviorEntry.getClassName()
131 )); 134 ));
132 } else { 135 } else {
133 BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry( 136 BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry(
134 Descriptor.toJvmName(enclosingMethodAttr.className()), 137 Descriptor.toJvmName(enclosingMethodAttr.className()),
135 enclosingMethodAttr.methodName(), 138 enclosingMethodAttr.methodName(),
136 enclosingMethodAttr.methodDescriptor() 139 enclosingMethodAttr.methodDescriptor()
137 ); 140 );
138 BehaviorEntry deobfBehaviorEntry = this.translator.translateEntry(obfBehaviorEntry); 141 BehaviorEntry deobfBehaviorEntry = this.translator.translateEntry(obfBehaviorEntry);
139 c.getClassFile().addAttribute(new EnclosingMethodAttribute( 142 c.getClassFile().addAttribute(new EnclosingMethodAttribute(
140 constants, 143 constants,
141 deobfBehaviorEntry.getClassName(), 144 deobfBehaviorEntry.getClassName(),
142 deobfBehaviorEntry.getName(), 145 deobfBehaviorEntry.getName(),
143 deobfBehaviorEntry.getSignature().toString() 146 deobfBehaviorEntry.getSignature().toString()
144 )); 147 ));
145 } 148 }
146 } 149 }
147 150
148 // translate all the class names referenced in the code 151 // translate all the class names referenced in the code
149 // the above code only changed method/field/reference names and types, but not the rest of the class references 152 // the above code only changed method/field/reference names and types, but not the rest of the class references
150 ClassRenamer.renameClasses(c, this.translator); 153 ClassRenamer.renameClasses(c, this.translator);
151 154
152 // translate the source file attribute too 155 // translate the source file attribute too
153 ClassEntry deobfClassEntry = this.translator.translateEntry(classEntry); 156 ClassEntry deobfClassEntry = this.translator.translateEntry(classEntry);
154 if (deobfClassEntry != null) { 157 if (deobfClassEntry != null) {
155 String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassEntry().getSimpleName()) + ".java"; 158 String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassEntry().getSimpleName()) + ".java";
156 c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile)); 159 c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile));
157 } 160 }
158 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); 161 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
159 if (attr != null) 162 if (attr != null)
160 InnerClassWriter.changeModifier(c, attr, translator); 163 InnerClassWriter.changeModifier(c, attr, translator);
161 } 164 }
162} 165}
diff --git a/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java b/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java
index 256df61e..1932730d 100644
--- a/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java
@@ -8,8 +8,15 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode; 12package cuchaz.enigma.bytecode;
12 13
14import cuchaz.enigma.bytecode.accessors.ClassInfoAccessor;
15import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor;
16import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor;
17import javassist.bytecode.ConstPool;
18import javassist.bytecode.Descriptor;
19
13import java.io.DataInputStream; 20import java.io.DataInputStream;
14import java.io.DataOutputStream; 21import java.io.DataOutputStream;
15import java.lang.reflect.Constructor; 22import java.lang.reflect.Constructor;
@@ -17,247 +24,241 @@ import java.lang.reflect.Field;
17import java.lang.reflect.Method; 24import java.lang.reflect.Method;
18import java.util.HashMap; 25import java.util.HashMap;
19 26
20import cuchaz.enigma.bytecode.accessors.ClassInfoAccessor;
21import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor;
22import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor;
23import javassist.bytecode.ConstPool;
24import javassist.bytecode.Descriptor;
25
26public class ConstPoolEditor { 27public class ConstPoolEditor {
27 28
28 private static Method getItem; 29 private static Method getItem;
29 private static Method addItem; 30 private static Method addItem;
30 private static Method addItem0; 31 private static Method addItem0;
31 private static Field items; 32 private static Field items;
32 private static Field cache; 33 private static Field cache;
33 private static Field numItems; 34 private static Field numItems;
34 private static Field objects; 35 private static Field objects;
35 private static Field elements; 36 private static Field elements;
36 private static Method methodWritePool; 37 private static Method methodWritePool;
37 private static Constructor<ConstPool> constructorPool; 38 private static Constructor<ConstPool> constructorPool;
38 39
39 static { 40 static {
40 try { 41 try {
41 getItem = ConstPool.class.getDeclaredMethod("getItem", int.class); 42 getItem = ConstPool.class.getDeclaredMethod("getItem", int.class);
42 getItem.setAccessible(true); 43 getItem.setAccessible(true);
43 44
44 addItem = ConstPool.class.getDeclaredMethod("addItem", Class.forName("javassist.bytecode.ConstInfo")); 45 addItem = ConstPool.class.getDeclaredMethod("addItem", Class.forName("javassist.bytecode.ConstInfo"));
45 addItem.setAccessible(true); 46 addItem.setAccessible(true);
46 47
47 addItem0 = ConstPool.class.getDeclaredMethod("addItem0", Class.forName("javassist.bytecode.ConstInfo")); 48 addItem0 = ConstPool.class.getDeclaredMethod("addItem0", Class.forName("javassist.bytecode.ConstInfo"));
48 addItem0.setAccessible(true); 49 addItem0.setAccessible(true);
49 50
50 items = ConstPool.class.getDeclaredField("items"); 51 items = ConstPool.class.getDeclaredField("items");
51 items.setAccessible(true); 52 items.setAccessible(true);
52 53
53 cache = ConstPool.class.getDeclaredField("itemsCache"); 54 cache = ConstPool.class.getDeclaredField("itemsCache");
54 cache.setAccessible(true); 55 cache.setAccessible(true);
55 56
56 numItems = ConstPool.class.getDeclaredField("numOfItems"); 57 numItems = ConstPool.class.getDeclaredField("numOfItems");
57 numItems.setAccessible(true); 58 numItems.setAccessible(true);
58 59
59 objects = Class.forName("javassist.bytecode.LongVector").getDeclaredField("objects"); 60 objects = Class.forName("javassist.bytecode.LongVector").getDeclaredField("objects");
60 objects.setAccessible(true); 61 objects.setAccessible(true);
61 62
62 elements = Class.forName("javassist.bytecode.LongVector").getDeclaredField("elements"); 63 elements = Class.forName("javassist.bytecode.LongVector").getDeclaredField("elements");
63 elements.setAccessible(true); 64 elements.setAccessible(true);
64 65
65 methodWritePool = ConstPool.class.getDeclaredMethod("write", DataOutputStream.class); 66 methodWritePool = ConstPool.class.getDeclaredMethod("write", DataOutputStream.class);
66 methodWritePool.setAccessible(true); 67 methodWritePool.setAccessible(true);
67 68
68 constructorPool = ConstPool.class.getDeclaredConstructor(DataInputStream.class); 69 constructorPool = ConstPool.class.getDeclaredConstructor(DataInputStream.class);
69 constructorPool.setAccessible(true); 70 constructorPool.setAccessible(true);
70 } catch (Exception ex) { 71 } catch (Exception ex) {
71 throw new Error(ex); 72 throw new Error(ex);
72 } 73 }
73 } 74 }
74 75
75 private ConstPool pool; 76 private ConstPool pool;
76 77
77 public ConstPoolEditor(ConstPool pool) { 78 public ConstPoolEditor(ConstPool pool) {
78 this.pool = pool; 79 this.pool = pool;
79 } 80 }
80 81
81 public void writePool(DataOutputStream out) { 82 public static ConstPool readPool(DataInputStream in) {
82 try { 83 try {
83 methodWritePool.invoke(this.pool, out); 84 return constructorPool.newInstance(in);
84 } catch (Exception ex) { 85 } catch (Exception ex) {
85 throw new Error(ex); 86 throw new Error(ex);
86 } 87 }
87 } 88 }
88 89
89 public static ConstPool readPool(DataInputStream in) { 90 public static ConstPool newConstPool() {
90 try { 91 // const pool expects the name of a class to initialize itself
91 return constructorPool.newInstance(in); 92 // but we want an empty pool
92 } catch (Exception ex) { 93 // so give it a bogus name, and then clear the entries afterwards
93 throw new Error(ex); 94 ConstPool pool = new ConstPool("a");
94 } 95
95 } 96 ConstPoolEditor editor = new ConstPoolEditor(pool);
96 97 int size = pool.getSize();
97 public String getMemberrefClassname(int memberrefIndex) { 98 for (int i = 0; i < size - 1; i++) {
98 return Descriptor.toJvmName(this.pool.getClassInfo(this.pool.getMemberClass(memberrefIndex))); 99 editor.removeLastItem();
99 } 100 }
100 101
101 public String getMemberrefName(int memberrefIndex) { 102 // make sure the pool is actually empty
102 return this.pool.getUtf8Info(this.pool.getNameAndTypeName(this.pool.getMemberNameAndType(memberrefIndex))); 103 // although, in this case "empty" means one thing in it
103 } 104 // the JVM spec says index 0 should be reserved
104 105 assert (pool.getSize() == 1);
105 public String getMemberrefType(int memberrefIndex) { 106 assert (editor.getItem(0) == null);
106 return this.pool.getUtf8Info(this.pool.getNameAndTypeDescriptor(this.pool.getMemberNameAndType(memberrefIndex))); 107 assert (editor.getItem(1) == null);
107 } 108 assert (editor.getItem(2) == null);
108 109 assert (editor.getItem(3) == null);
109 public ConstInfoAccessor getItem(int index) { 110
110 try { 111 // also, clear the cache
111 Object entry = getItem.invoke(this.pool, index); 112 editor.getCache().clear();
112 if (entry == null) { 113
113 return null; 114 return pool;
114 } 115 }
115 return new ConstInfoAccessor(entry); 116
116 } catch (Exception ex) { 117 public void writePool(DataOutputStream out) {
117 throw new Error(ex); 118 try {
118 } 119 methodWritePool.invoke(this.pool, out);
119 } 120 } catch (Exception ex) {
120 121 throw new Error(ex);
121 public int addItem(Object item) { 122 }
122 try { 123 }
123 return (Integer) addItem.invoke(this.pool, item); 124
124 } catch (Exception ex) { 125 public String getMemberrefClassname(int memberrefIndex) {
125 throw new Error(ex); 126 return Descriptor.toJvmName(this.pool.getClassInfo(this.pool.getMemberClass(memberrefIndex)));
126 } 127 }
127 } 128
128 129 public String getMemberrefName(int memberrefIndex) {
129 public int addItemForceNew(Object item) { 130 return this.pool.getUtf8Info(this.pool.getNameAndTypeName(this.pool.getMemberNameAndType(memberrefIndex)));
130 try { 131 }
131 return (Integer) addItem0.invoke(this.pool, item); 132
132 } catch (Exception ex) { 133 public String getMemberrefType(int memberrefIndex) {
133 throw new Error(ex); 134 return this.pool.getUtf8Info(this.pool.getNameAndTypeDescriptor(this.pool.getMemberNameAndType(memberrefIndex)));
134 } 135 }
135 } 136
136 137 public ConstInfoAccessor getItem(int index) {
137 @SuppressWarnings("rawtypes") 138 try {
138 public void removeLastItem() { 139 Object entry = getItem.invoke(this.pool, index);
139 try { 140 if (entry == null) {
140 // remove the item from the cache 141 return null;
141 HashMap cache = getCache(); 142 }
142 if (cache != null) { 143 return new ConstInfoAccessor(entry);
143 Object item = getItem(this.pool.getSize() - 1); 144 } catch (Exception ex) {
144 cache.remove(item); 145 throw new Error(ex);
145 } 146 }
146 147 }
147 // remove the actual item 148
148 // based off of LongVector.addElement() 149 public int addItem(Object item) {
149 Object item = items.get(this.pool); 150 try {
150 Object[][] object = (Object[][]) objects.get(items); 151 return (Integer) addItem.invoke(this.pool, item);
151 int numElements = (Integer) elements.get(items) - 1; 152 } catch (Exception ex) {
152 int nth = numElements >> 7; 153 throw new Error(ex);
153 int offset = numElements & (128 - 1); 154 }
154 object[nth][offset] = null; 155 }
155 156
156 // decrement the number of items 157 public int addItemForceNew(Object item) {
157 elements.set(item, numElements); 158 try {
158 numItems.set(this.pool, (Integer) numItems.get(this.pool) - 1); 159 return (Integer) addItem0.invoke(this.pool, item);
159 } catch (Exception ex) { 160 } catch (Exception ex) {
160 throw new Error(ex); 161 throw new Error(ex);
161 } 162 }
162 } 163 }
163 164
164 @SuppressWarnings("rawtypes") 165 @SuppressWarnings("rawtypes")
165 public HashMap getCache() { 166 public void removeLastItem() {
166 try { 167 try {
167 return (HashMap) cache.get(this.pool); 168 // remove the item from the cache
168 } catch (Exception ex) { 169 HashMap cache = getCache();
169 throw new Error(ex); 170 if (cache != null) {
170 } 171 Object item = getItem(this.pool.getSize() - 1);
171 } 172 cache.remove(item);
172 173 }
173 @SuppressWarnings({"rawtypes", "unchecked"}) 174
174 public void changeMemberrefNameAndType(int memberrefIndex, String newName, String newType) { 175 // remove the actual item
175 // NOTE: when changing values, we always need to copy-on-write 176 // based off of LongVector.addElement()
176 try { 177 Object item = items.get(this.pool);
177 // get the memberref item 178 Object[][] object = (Object[][]) objects.get(items);
178 Object item = getItem(memberrefIndex).getItem(); 179 int numElements = (Integer) elements.get(items) - 1;
179 180 int nth = numElements >> 7;
180 // update the cache 181 int offset = numElements & (128 - 1);
181 HashMap cache = getCache(); 182 object[nth][offset] = null;
182 if (cache != null) { 183
183 cache.remove(item); 184 // decrement the number of items
184 } 185 elements.set(item, numElements);
185 186 numItems.set(this.pool, (Integer) numItems.get(this.pool) - 1);
186 new MemberRefInfoAccessor(item).setNameAndTypeIndex(this.pool.addNameAndTypeInfo(newName, newType)); 187 } catch (Exception ex) {
187 188 throw new Error(ex);
188 // update the cache 189 }
189 if (cache != null) { 190 }
190 cache.put(item, item); 191
191 } 192 @SuppressWarnings("rawtypes")
192 } catch (Exception ex) { 193 public HashMap getCache() {
193 throw new Error(ex); 194 try {
194 } 195 return (HashMap) cache.get(this.pool);
195 196 } catch (Exception ex) {
196 // make sure the change worked 197 throw new Error(ex);
197 assert (newName.equals(getMemberrefName(memberrefIndex))); 198 }
198 assert (newType.equals(getMemberrefType(memberrefIndex))); 199 }
199 } 200
200 201 @SuppressWarnings({ "rawtypes", "unchecked" })
201 @SuppressWarnings({"rawtypes", "unchecked"}) 202 public void changeMemberrefNameAndType(int memberrefIndex, String newName, String newType) {
202 public void changeClassName(int classNameIndex, String newName) { 203 // NOTE: when changing values, we always need to copy-on-write
203 // NOTE: when changing values, we always need to copy-on-write 204 try {
204 try { 205 // get the memberref item
205 // get the class item 206 Object item = getItem(memberrefIndex).getItem();
206 Object item = getItem(classNameIndex).getItem(); 207
207 208 // update the cache
208 // update the cache 209 HashMap cache = getCache();
209 HashMap cache = getCache(); 210 if (cache != null) {
210 if (cache != null) { 211 cache.remove(item);
211 cache.remove(item); 212 }
212 } 213
213 214 new MemberRefInfoAccessor(item).setNameAndTypeIndex(this.pool.addNameAndTypeInfo(newName, newType));
214 // add the new name and repoint the name-and-type to it 215
215 new ClassInfoAccessor(item).setNameIndex(this.pool.addUtf8Info(newName)); 216 // update the cache
216 217 if (cache != null) {
217 // update the cache 218 cache.put(item, item);
218 if (cache != null) { 219 }
219 cache.put(item, item); 220 } catch (Exception ex) {
220 } 221 throw new Error(ex);
221 } catch (Exception ex) { 222 }
222 throw new Error(ex); 223
223 } 224 // make sure the change worked
224 } 225 assert (newName.equals(getMemberrefName(memberrefIndex)));
225 226 assert (newType.equals(getMemberrefType(memberrefIndex)));
226 public static ConstPool newConstPool() { 227 }
227 // const pool expects the name of a class to initialize itself 228
228 // but we want an empty pool 229 @SuppressWarnings({ "rawtypes", "unchecked" })
229 // so give it a bogus name, and then clear the entries afterwards 230 public void changeClassName(int classNameIndex, String newName) {
230 ConstPool pool = new ConstPool("a"); 231 // NOTE: when changing values, we always need to copy-on-write
231 232 try {
232 ConstPoolEditor editor = new ConstPoolEditor(pool); 233 // get the class item
233 int size = pool.getSize(); 234 Object item = getItem(classNameIndex).getItem();
234 for (int i = 0; i < size - 1; i++) { 235
235 editor.removeLastItem(); 236 // update the cache
236 } 237 HashMap cache = getCache();
237 238 if (cache != null) {
238 // make sure the pool is actually empty 239 cache.remove(item);
239 // although, in this case "empty" means one thing in it 240 }
240 // the JVM spec says index 0 should be reserved 241
241 assert (pool.getSize() == 1); 242 // add the new name and repoint the name-and-type to it
242 assert (editor.getItem(0) == null); 243 new ClassInfoAccessor(item).setNameIndex(this.pool.addUtf8Info(newName));
243 assert (editor.getItem(1) == null); 244
244 assert (editor.getItem(2) == null); 245 // update the cache
245 assert (editor.getItem(3) == null); 246 if (cache != null) {
246 247 cache.put(item, item);
247 // also, clear the cache 248 }
248 editor.getCache().clear(); 249 } catch (Exception ex) {
249 250 throw new Error(ex);
250 return pool; 251 }
251 } 252 }
252 253
253 public String dump() { 254 public String dump() {
254 StringBuilder buf = new StringBuilder(); 255 StringBuilder buf = new StringBuilder();
255 for (int i = 1; i < this.pool.getSize(); i++) { 256 for (int i = 1; i < this.pool.getSize(); i++) {
256 buf.append(String.format("%4d", i)); 257 buf.append(String.format("%4d", i));
257 buf.append(" "); 258 buf.append(" ");
258 buf.append(getItem(i).toString()); 259 buf.append(getItem(i));
259 buf.append("\n"); 260 buf.append("\n");
260 } 261 }
261 return buf.toString(); 262 return buf.toString();
262 } 263 }
263} 264}
diff --git a/src/main/java/cuchaz/enigma/bytecode/InfoType.java b/src/main/java/cuchaz/enigma/bytecode/InfoType.java
index 21b04173..9013d581 100644
--- a/src/main/java/cuchaz/enigma/bytecode/InfoType.java
+++ b/src/main/java/cuchaz/enigma/bytecode/InfoType.java
@@ -8,259 +8,259 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode; 12package cuchaz.enigma.bytecode;
12 13
13import com.google.common.collect.Maps; 14import com.google.common.collect.Maps;
15import cuchaz.enigma.bytecode.accessors.*;
14 16
15import java.util.Collection; 17import java.util.Collection;
16import java.util.Map; 18import java.util.Map;
17 19
18import cuchaz.enigma.bytecode.accessors.*;
19
20public enum InfoType { 20public enum InfoType {
21 21
22 Utf8Info(1), 22 Utf8Info(1),
23 IntegerInfo(3), 23 IntegerInfo(3),
24 FloatInfo(4), 24 FloatInfo(4),
25 LongInfo(5), 25 LongInfo(5),
26 DoubleInfo(6), 26 DoubleInfo(6),
27 ClassInfo(7) { 27 ClassInfo(7) {
28 @Override 28 @Override
29 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 29 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
30 ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); 30 ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem());
31 gatherIndexTree(indices, editor, accessor.getNameIndex()); 31 gatherIndexTree(indices, editor, accessor.getNameIndex());
32 } 32 }
33 33
34 @Override 34 @Override
35 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { 35 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
36 ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); 36 ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem());
37 accessor.setNameIndex(remapIndex(map, accessor.getNameIndex())); 37 accessor.setNameIndex(remapIndex(map, accessor.getNameIndex()));
38 } 38 }
39 39
40 @Override 40 @Override
41 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { 41 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
42 ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); 42 ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem());
43 ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex()); 43 ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex());
44 return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag(); 44 return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag();
45 } 45 }
46 }, 46 },
47 StringInfo(8) { 47 StringInfo(8) {
48 @Override 48 @Override
49 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 49 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
50 StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); 50 StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem());
51 gatherIndexTree(indices, editor, accessor.getStringIndex()); 51 gatherIndexTree(indices, editor, accessor.getStringIndex());
52 } 52 }
53 53
54 @Override 54 @Override
55 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { 55 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
56 StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); 56 StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem());
57 accessor.setStringIndex(remapIndex(map, accessor.getStringIndex())); 57 accessor.setStringIndex(remapIndex(map, accessor.getStringIndex()));
58 } 58 }
59 59
60 @Override 60 @Override
61 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { 61 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
62 StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); 62 StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem());
63 ConstInfoAccessor stringEntry = pool.getItem(accessor.getStringIndex()); 63 ConstInfoAccessor stringEntry = pool.getItem(accessor.getStringIndex());
64 return stringEntry != null && stringEntry.getTag() == Utf8Info.getTag(); 64 return stringEntry != null && stringEntry.getTag() == Utf8Info.getTag();
65 } 65 }
66 }, 66 },
67 FieldRefInfo(9) { 67 FieldRefInfo(9) {
68 @Override 68 @Override
69 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 69 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
70 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); 70 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem());
71 gatherIndexTree(indices, editor, accessor.getClassIndex()); 71 gatherIndexTree(indices, editor, accessor.getClassIndex());
72 gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex()); 72 gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex());
73 } 73 }
74 74
75 @Override 75 @Override
76 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { 76 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
77 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); 77 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem());
78 accessor.setClassIndex(remapIndex(map, accessor.getClassIndex())); 78 accessor.setClassIndex(remapIndex(map, accessor.getClassIndex()));
79 accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex())); 79 accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex()));
80 } 80 }
81 81
82 @Override 82 @Override
83 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { 83 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
84 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); 84 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem());
85 ConstInfoAccessor classEntry = pool.getItem(accessor.getClassIndex()); 85 ConstInfoAccessor classEntry = pool.getItem(accessor.getClassIndex());
86 ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex()); 86 ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex());
87 return classEntry != null && classEntry.getTag() == ClassInfo.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); 87 return classEntry != null && classEntry.getTag() == ClassInfo.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag();
88 } 88 }
89 }, 89 },
90 // same as FieldRefInfo 90 // same as FieldRefInfo
91 MethodRefInfo(10) { 91 MethodRefInfo(10) {
92 @Override 92 @Override
93 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 93 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
94 FieldRefInfo.gatherIndexTree(indices, editor, entry); 94 FieldRefInfo.gatherIndexTree(indices, editor, entry);
95 } 95 }
96 96
97 @Override 97 @Override
98 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { 98 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
99 FieldRefInfo.remapIndices(map, entry); 99 FieldRefInfo.remapIndices(map, entry);
100 } 100 }
101 101
102 @Override 102 @Override
103 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { 103 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
104 return FieldRefInfo.subIndicesAreValid(entry, pool); 104 return FieldRefInfo.subIndicesAreValid(entry, pool);
105 } 105 }
106 }, 106 },
107 // same as FieldRefInfo 107 // same as FieldRefInfo
108 InterfaceMethodRefInfo(11) { 108 InterfaceMethodRefInfo(11) {
109 @Override 109 @Override
110 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 110 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
111 FieldRefInfo.gatherIndexTree(indices, editor, entry); 111 FieldRefInfo.gatherIndexTree(indices, editor, entry);
112 } 112 }
113 113
114 @Override 114 @Override
115 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { 115 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
116 FieldRefInfo.remapIndices(map, entry); 116 FieldRefInfo.remapIndices(map, entry);
117 } 117 }
118 118
119 @Override 119 @Override
120 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { 120 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
121 return FieldRefInfo.subIndicesAreValid(entry, pool); 121 return FieldRefInfo.subIndicesAreValid(entry, pool);
122 } 122 }
123 }, 123 },
124 NameAndTypeInfo(12) { 124 NameAndTypeInfo(12) {
125 @Override 125 @Override
126 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 126 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
127 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); 127 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem());
128 gatherIndexTree(indices, editor, accessor.getNameIndex()); 128 gatherIndexTree(indices, editor, accessor.getNameIndex());
129 gatherIndexTree(indices, editor, accessor.getTypeIndex()); 129 gatherIndexTree(indices, editor, accessor.getTypeIndex());
130 } 130 }
131 131
132 @Override 132 @Override
133 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { 133 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
134 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); 134 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem());
135 accessor.setNameIndex(remapIndex(map, accessor.getNameIndex())); 135 accessor.setNameIndex(remapIndex(map, accessor.getNameIndex()));
136 accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); 136 accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex()));
137 } 137 }
138 138
139 @Override 139 @Override
140 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { 140 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
141 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); 141 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem());
142 ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex()); 142 ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex());
143 ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); 143 ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex());
144 return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag() && typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); 144 return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag() && typeEntry != null && typeEntry.getTag() == Utf8Info.getTag();
145 } 145 }
146 }, 146 },
147 MethodHandleInfo(15) { 147 MethodHandleInfo(15) {
148 @Override 148 @Override
149 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 149 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
150 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); 150 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem());
151 gatherIndexTree(indices, editor, accessor.getTypeIndex()); 151 gatherIndexTree(indices, editor, accessor.getTypeIndex());
152 gatherIndexTree(indices, editor, accessor.getMethodRefIndex()); 152 gatherIndexTree(indices, editor, accessor.getMethodRefIndex());
153 } 153 }
154 154
155 @Override 155 @Override
156 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { 156 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
157 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); 157 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem());
158 accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); 158 accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex()));
159 accessor.setMethodRefIndex(remapIndex(map, accessor.getMethodRefIndex())); 159 accessor.setMethodRefIndex(remapIndex(map, accessor.getMethodRefIndex()));
160 } 160 }
161 161
162 @Override 162 @Override
163 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { 163 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
164 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); 164 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem());
165 ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); 165 ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex());
166 ConstInfoAccessor methodRefEntry = pool.getItem(accessor.getMethodRefIndex()); 166 ConstInfoAccessor methodRefEntry = pool.getItem(accessor.getMethodRefIndex());
167 return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag() && methodRefEntry != null && methodRefEntry.getTag() == MethodRefInfo.getTag(); 167 return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag() && methodRefEntry != null && methodRefEntry.getTag() == MethodRefInfo.getTag();
168 } 168 }
169 }, 169 },
170 MethodTypeInfo(16) { 170 MethodTypeInfo(16) {
171 @Override 171 @Override
172 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 172 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
173 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); 173 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem());
174 gatherIndexTree(indices, editor, accessor.getTypeIndex()); 174 gatherIndexTree(indices, editor, accessor.getTypeIndex());
175 } 175 }
176 176
177 @Override 177 @Override
178 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { 178 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
179 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); 179 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem());
180 accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); 180 accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex()));
181 } 181 }
182 182
183 @Override 183 @Override
184 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { 184 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
185 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); 185 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem());
186 ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); 186 ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex());
187 return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); 187 return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag();
188 } 188 }
189 }, 189 },
190 InvokeDynamicInfo(18) { 190 InvokeDynamicInfo(18) {
191 @Override 191 @Override
192 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 192 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
193 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); 193 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem());
194 gatherIndexTree(indices, editor, accessor.getBootstrapIndex()); 194 gatherIndexTree(indices, editor, accessor.getBootstrapIndex());
195 gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex()); 195 gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex());
196 } 196 }
197 197
198 @Override 198 @Override
199 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { 199 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
200 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); 200 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem());
201 accessor.setBootstrapIndex(remapIndex(map, accessor.getBootstrapIndex())); 201 accessor.setBootstrapIndex(remapIndex(map, accessor.getBootstrapIndex()));
202 accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex())); 202 accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex()));
203 } 203 }
204 204
205 @Override 205 @Override
206 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { 206 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
207 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); 207 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem());
208 ConstInfoAccessor bootstrapEntry = pool.getItem(accessor.getBootstrapIndex()); 208 ConstInfoAccessor bootstrapEntry = pool.getItem(accessor.getBootstrapIndex());
209 ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex()); 209 ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex());
210 return bootstrapEntry != null && bootstrapEntry.getTag() == Utf8Info.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); 210 return bootstrapEntry != null && bootstrapEntry.getTag() == Utf8Info.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag();
211 } 211 }
212 }; 212 };
213 213
214 private static Map<Integer, InfoType> types; 214 private static Map<Integer, InfoType> types;
215 215
216 static { 216 static {
217 types = Maps.newTreeMap(); 217 types = Maps.newTreeMap();
218 for (InfoType type : values()) { 218 for (InfoType type : values()) {
219 types.put(type.getTag(), type); 219 types.put(type.getTag(), type);
220 } 220 }
221 } 221 }
222 222
223 private int tag; 223 private int tag;
224 224
225 InfoType(int tag) { 225 InfoType(int tag) {
226 this.tag = tag; 226 this.tag = tag;
227 } 227 }
228 228
229 public int getTag() { 229 public static InfoType getByTag(int tag) {
230 return this.tag; 230 return types.get(tag);
231 } 231 }
232 232
233 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 233 public static void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, int index) {
234 // by default, do nothing 234 // add own index
235 } 235 indices.add(index);
236 236
237 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { 237 // recurse
238 // by default, do nothing 238 ConstInfoAccessor entry = editor.getItem(index);
239 } 239 entry.getType().gatherIndexTree(indices, editor, entry);
240 240 }
241 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { 241
242 // by default, everything is good 242 private static int remapIndex(Map<Integer, Integer> map, int index) {
243 return true; 243 Integer newIndex = map.get(index);
244 } 244 if (newIndex == null) {
245 245 newIndex = index;
246 public static InfoType getByTag(int tag) { 246 }
247 return types.get(tag); 247 return newIndex;
248 } 248 }
249 249
250 public static void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, int index) { 250 public int getTag() {
251 // add own index 251 return this.tag;
252 indices.add(index); 252 }
253 253
254 // recurse 254 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
255 ConstInfoAccessor entry = editor.getItem(index); 255 // by default, do nothing
256 entry.getType().gatherIndexTree(indices, editor, entry); 256 }
257 } 257
258 258 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
259 private static int remapIndex(Map<Integer, Integer> map, int index) { 259 // by default, do nothing
260 Integer newIndex = map.get(index); 260 }
261 if (newIndex == null) { 261
262 newIndex = index; 262 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
263 } 263 // by default, everything is good
264 return newIndex; 264 return true;
265 } 265 }
266} 266}
diff --git a/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java
index eb70c23d..5f8be908 100644
--- a/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java
+++ b/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java
@@ -8,13 +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.bytecode; 12package cuchaz.enigma.bytecode;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
14
15import java.util.Collection;
16import java.util.List;
17
18import cuchaz.enigma.analysis.JarIndex; 15import cuchaz.enigma.analysis.JarIndex;
19import cuchaz.enigma.mapping.*; 16import cuchaz.enigma.mapping.*;
20import javassist.ClassPool; 17import javassist.ClassPool;
@@ -22,126 +19,126 @@ import javassist.CtClass;
22import javassist.NotFoundException; 19import javassist.NotFoundException;
23import javassist.bytecode.*; 20import javassist.bytecode.*;
24 21
22import java.util.Collection;
23import java.util.List;
24
25public class InnerClassWriter { 25public class InnerClassWriter {
26 26
27 private JarIndex index; 27 private JarIndex index;
28 private Translator deobfuscatorTranslator; 28 private Translator deobfuscatorTranslator;
29 29
30 public InnerClassWriter(JarIndex index, Translator deobfuscatorTranslator) { 30 public InnerClassWriter(JarIndex index, Translator deobfuscatorTranslator) {
31 this.index = index; 31 this.index = index;
32 this.deobfuscatorTranslator = deobfuscatorTranslator; 32 this.deobfuscatorTranslator = deobfuscatorTranslator;
33 } 33 }
34 34
35 public void write(CtClass c) { 35 // FIXME: modiffier is not applied to inner class
36 36 public static void changeModifier(CtClass c, InnerClassesAttribute attr, Translator translator) {
37 // don't change anything if there's already an attribute there 37 ClassPool pool = c.getClassPool();
38 InnerClassesAttribute oldAttr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); 38 for (int i = 0; i < attr.tableLength(); i++) {
39 if (oldAttr != null) { 39
40 // bail! 40 String innerName = attr.innerClass(i);
41 return; 41 // get the inner class full name (which has already been translated)
42 } 42 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName));
43 43 try {
44 ClassEntry obfClassEntry = EntryFactory.getClassEntry(c); 44 CtClass innerClass = pool.get(innerName);
45 List<ClassEntry> obfClassChain = this.index.getObfClassChain(obfClassEntry); 45 Mappings.EntryModifier modifier = translator.getModifier(classEntry);
46 46 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
47 boolean isInnerClass = obfClassChain.size() > 1; 47 ClassRenamer.applyModifier(innerClass, modifier);
48 if (isInnerClass) { 48 } catch (NotFoundException e) {
49 49 // This shouldn't be possible in theory
50 // it's an inner class, rename it to the fully qualified name 50 //e.printStackTrace();
51 c.setName(obfClassEntry.buildClassEntry(obfClassChain).getName()); 51 }
52 52 }
53 BehaviorEntry caller = this.index.getAnonymousClassCaller(obfClassEntry); 53 }
54 if (caller != null) { 54
55 55 public void write(CtClass c) {
56 // write the enclosing method attribute 56
57 if (caller.getName().equals("<clinit>")) { 57 // don't change anything if there's already an attribute there
58 c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName())); 58 InnerClassesAttribute oldAttr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
59 } else { 59 if (oldAttr != null) {
60 c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName(), caller.getName(), caller.getSignature().toString())); 60 // bail!
61 } 61 return;
62 } 62 }
63 } 63
64 64 ClassEntry obfClassEntry = EntryFactory.getClassEntry(c);
65 // does this class have any inner classes? 65 List<ClassEntry> obfClassChain = this.index.getObfClassChain(obfClassEntry);
66 Collection<ClassEntry> obfInnerClassEntries = this.index.getInnerClasses(obfClassEntry); 66
67 67 boolean isInnerClass = obfClassChain.size() > 1;
68 if (isInnerClass || !obfInnerClassEntries.isEmpty()) { 68 if (isInnerClass) {
69 69
70 // create an inner class attribute 70 // it's an inner class, rename it to the fully qualified name
71 InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); 71 c.setName(obfClassEntry.buildClassEntry(obfClassChain).getName());
72 c.getClassFile().addAttribute(attr); 72
73 73 BehaviorEntry caller = this.index.getAnonymousClassCaller(obfClassEntry);
74 // write the ancestry, but not the outermost class 74 if (caller != null) {
75 for (int i = 1; i < obfClassChain.size(); i++) { 75
76 ClassEntry obfInnerClassEntry = obfClassChain.get(i); 76 // write the enclosing method attribute
77 writeInnerClass(attr, obfClassChain, obfInnerClassEntry); 77 if (caller.getName().equals("<clinit>")) {
78 78 c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName()));
79 // update references to use the fully qualified inner class name 79 } else {
80 c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(obfClassChain).getName()); 80 c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName(), caller.getName(), caller.getSignature().toString()));
81 } 81 }
82 82 }
83 // write the inner classes 83 }
84 for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) { 84
85 85 // does this class have any inner classes?
86 // extend the class chain 86 Collection<ClassEntry> obfInnerClassEntries = this.index.getInnerClasses(obfClassEntry);
87 List<ClassEntry> extendedObfClassChain = Lists.newArrayList(obfClassChain); 87
88 extendedObfClassChain.add(obfInnerClassEntry); 88 if (isInnerClass || !obfInnerClassEntries.isEmpty()) {
89 89
90 writeInnerClass(attr, extendedObfClassChain, obfInnerClassEntry); 90 // create an inner class attribute
91 91 InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool());
92 // update references to use the fully qualified inner class name 92 c.getClassFile().addAttribute(attr);
93 c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(extendedObfClassChain).getName()); 93
94 } 94 // write the ancestry, but not the outermost class
95 } 95 for (int i = 1; i < obfClassChain.size(); i++) {
96 } 96 ClassEntry obfInnerClassEntry = obfClassChain.get(i);
97 97 writeInnerClass(attr, obfClassChain, obfInnerClassEntry);
98 // FIXME: modiffier is not applied to inner class 98
99 public static void changeModifier(CtClass c, InnerClassesAttribute attr, Translator translator) 99 // update references to use the fully qualified inner class name
100 { 100 c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(obfClassChain).getName());
101 ClassPool pool = c.getClassPool(); 101 }
102 for (int i = 0; i < attr.tableLength(); i++) { 102
103 103 // write the inner classes
104 String innerName = attr.innerClass(i); 104 for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) {
105 // get the inner class full name (which has already been translated) 105
106 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName)); 106 // extend the class chain
107 try 107 List<ClassEntry> extendedObfClassChain = Lists.newArrayList(obfClassChain);
108 { 108 extendedObfClassChain.add(obfInnerClassEntry);
109 CtClass innerClass = pool.get(innerName); 109
110 Mappings.EntryModifier modifier = translator.getModifier(classEntry); 110 writeInnerClass(attr, extendedObfClassChain, obfInnerClassEntry);
111 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) 111
112 ClassRenamer.applyModifier(innerClass, modifier); 112 // update references to use the fully qualified inner class name
113 } catch (NotFoundException e) 113 c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(extendedObfClassChain).getName());
114 { 114 }
115 // This shouldn't be possible in theory 115 }
116 //e.printStackTrace(); 116 }
117 } 117
118 } 118 private void writeInnerClass(InnerClassesAttribute attr, List<ClassEntry> obfClassChain, ClassEntry obfClassEntry) {
119 } 119
120 120 // get the new inner class name
121 private void writeInnerClass(InnerClassesAttribute attr, List<ClassEntry> obfClassChain, ClassEntry obfClassEntry) { 121 ClassEntry obfInnerClassEntry = obfClassEntry.buildClassEntry(obfClassChain);
122 122 ClassEntry obfOuterClassEntry = obfInnerClassEntry.getOuterClassEntry();
123 // get the new inner class name 123
124 ClassEntry obfInnerClassEntry = obfClassEntry.buildClassEntry(obfClassChain); 124 // here's what the JVM spec says about the InnerClasses attribute
125 ClassEntry obfOuterClassEntry = obfInnerClassEntry.getOuterClassEntry(); 125 // append(inner, parent, 0 if anonymous else simple name, flags);
126 126
127 // here's what the JVM spec says about the InnerClasses attribute 127 // update the attribute with this inner class
128 // append(inner, parent, 0 if anonymous else simple name, flags); 128 ConstPool constPool = attr.getConstPool();
129 129 int innerClassIndex = constPool.addClassInfo(obfInnerClassEntry.getName());
130 // update the attribute with this inner class 130 int parentClassIndex = constPool.addClassInfo(obfOuterClassEntry.getName());
131 ConstPool constPool = attr.getConstPool(); 131 int innerClassNameIndex = 0;
132 int innerClassIndex = constPool.addClassInfo(obfInnerClassEntry.getName()); 132 int accessFlags = AccessFlag.PUBLIC;
133 int parentClassIndex = constPool.addClassInfo(obfOuterClassEntry.getName()); 133 // TODO: need to figure out if we can put static or not
134 int innerClassNameIndex = 0; 134 if (!this.index.isAnonymousClass(obfClassEntry)) {
135 int accessFlags = AccessFlag.PUBLIC; 135 innerClassNameIndex = constPool.addUtf8Info(obfInnerClassEntry.getInnermostClassName());
136 // TODO: need to figure out if we can put static or not 136 }
137 if (!this.index.isAnonymousClass(obfClassEntry)) { 137
138 innerClassNameIndex = constPool.addUtf8Info(obfInnerClassEntry.getInnermostClassName()); 138 attr.append(innerClassIndex, parentClassIndex, innerClassNameIndex, accessFlags);
139 }
140
141 attr.append(innerClassIndex, parentClassIndex, innerClassNameIndex, accessFlags);
142 139
143 /* DEBUG 140 /* DEBUG
144 System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", 141 System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)",
145 obfClassEntry, 142 obfClassEntry,
146 attr.innerClass(attr.tableLength() - 1), 143 attr.innerClass(attr.tableLength() - 1),
147 attr.outerClass(attr.tableLength() - 1), 144 attr.outerClass(attr.tableLength() - 1),
@@ -150,5 +147,5 @@ public class InnerClassWriter {
150 obfClassEntry.getName() 147 obfClassEntry.getName()
151 )); 148 ));
152 */ 149 */
153 } 150 }
154} 151}
diff --git a/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java b/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java
index 24b5f363..8909d816 100644
--- a/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java
+++ b/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode; 12package cuchaz.enigma.bytecode;
12 13
13import cuchaz.enigma.mapping.*; 14import cuchaz.enigma.mapping.*;
@@ -15,131 +16,129 @@ import javassist.CtBehavior;
15import javassist.CtClass; 16import javassist.CtClass;
16import javassist.bytecode.*; 17import javassist.bytecode.*;
17 18
18
19public class LocalVariableRenamer { 19public class LocalVariableRenamer {
20 20
21 private Translator translator; 21 private Translator translator;
22 22
23 public LocalVariableRenamer(Translator translator) { 23 public LocalVariableRenamer(Translator translator) {
24 this.translator = translator; 24 this.translator = translator;
25 } 25 }
26 26
27 public void rename(CtClass c) { 27 public void rename(CtClass c) {
28 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 28 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
29 29
30 // if there's a local variable table, just rename everything to v1, v2, v3, ... for now 30 // if there's a local variable table, just rename everything to v1, v2, v3, ... for now
31 CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute(); 31 CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute();
32 if (codeAttribute == null) { 32 if (codeAttribute == null) {
33 continue; 33 continue;
34 } 34 }
35 35
36 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); 36 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
37 ConstPool constants = c.getClassFile().getConstPool(); 37 ConstPool constants = c.getClassFile().getConstPool();
38 38
39 LocalVariableAttribute table = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag); 39 LocalVariableAttribute table = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
40 if (table != null) { 40 if (table != null) {
41 renameLVT(behaviorEntry, constants, table); 41 renameLVT(behaviorEntry, constants, table);
42 } 42 }
43 43
44 LocalVariableTypeAttribute typeTable = (LocalVariableTypeAttribute) codeAttribute.getAttribute(LocalVariableAttribute.typeTag); 44 LocalVariableTypeAttribute typeTable = (LocalVariableTypeAttribute) codeAttribute.getAttribute(LocalVariableAttribute.typeTag);
45 if (typeTable != null) { 45 if (typeTable != null) {
46 renameLVTT(typeTable, table); 46 renameLVTT(typeTable, table);
47 } 47 }
48 } 48 }
49 } 49 }
50 50
51 // DEBUG 51 // DEBUG
52 @SuppressWarnings("unused") 52 @SuppressWarnings("unused")
53 private void dumpTable(LocalVariableAttribute table) { 53 private void dumpTable(LocalVariableAttribute table) {
54 for (int i = 0; i < table.tableLength(); i++) { 54 for (int i = 0; i < table.tableLength(); i++) {
55 System.out.println(String.format("\t%d (%d): %s %s", 55 System.out.println(String.format("\t%d (%d): %s %s",
56 i, table.index(i), table.variableName(i), table.descriptor(i) 56 i, table.index(i), table.variableName(i), table.descriptor(i)
57 )); 57 ));
58 } 58 }
59 } 59 }
60 60
61 private void renameLVT(BehaviorEntry behaviorEntry, ConstPool constants, LocalVariableAttribute table) { 61 private void renameLVT(BehaviorEntry behaviorEntry, ConstPool constants, LocalVariableAttribute table) {
62 62
63 // skip empty tables 63 // skip empty tables
64 if (table.tableLength() <= 0) { 64 if (table.tableLength() <= 0) {
65 return; 65 return;
66 } 66 }
67 67
68 // where do we start counting variables? 68 // where do we start counting variables?
69 int starti = 0; 69 int starti = 0;
70 if (table.variableName(0).equals("this")) { 70 if (table.variableName(0).equals("this")) {
71 // skip the "this" variable 71 // skip the "this" variable
72 starti = 1; 72 starti = 1;
73 } 73 }
74 74
75 // rename method arguments first 75 // rename method arguments first
76 int numArgs = 0; 76 int numArgs = 0;
77 if (behaviorEntry.getSignature() != null) { 77 if (behaviorEntry.getSignature() != null) {
78 numArgs = behaviorEntry.getSignature().getArgumentTypes().size(); 78 numArgs = behaviorEntry.getSignature().getArgumentTypes().size();
79 79
80 boolean isNestedClassConstructor = false; 80 boolean isNestedClassConstructor = false;
81 81
82 // If the behavior is a constructor and if it have more than one arg, it's probably from a nested! 82 // If the behavior is a constructor and if it have more than one arg, it's probably from a nested!
83 if (behaviorEntry instanceof ConstructorEntry && behaviorEntry.getClassEntry() != null && behaviorEntry.getClassEntry().isInnerClass() && numArgs >= 1) 83 if (behaviorEntry instanceof ConstructorEntry && behaviorEntry.getClassEntry() != null && behaviorEntry.getClassEntry().isInnerClass() && numArgs >= 1) {
84 { 84 // Get the first arg type
85 // Get the first arg type 85 Type firstArg = behaviorEntry.getSignature().getArgumentTypes().get(0);
86 Type firstArg = behaviorEntry.getSignature().getArgumentTypes().get(0); 86
87 87 // If the arg is a class and if the class name match the outer class name of the constructor, it's definitely a constructor of a nested class
88 // If the arg is a class and if the class name match the outer class name of the constructor, it's definitely a constructor of a nested class 88 if (firstArg.isClass() && firstArg.getClassEntry().equals(behaviorEntry.getClassEntry().getOuterClassEntry())) {
89 if (firstArg.isClass() && firstArg.getClassEntry().equals(behaviorEntry.getClassEntry().getOuterClassEntry())) { 89 isNestedClassConstructor = true;
90 isNestedClassConstructor = true; 90 numArgs--;
91 numArgs--; 91 }
92 } 92 }
93 } 93
94 94 for (int i = starti; i < starti + numArgs && i < table.tableLength(); i++) {
95 for (int i = starti; i < starti + numArgs && i < table.tableLength(); i++) { 95 int argi = i - starti;
96 int argi = i - starti; 96 String argName = this.translator.translate(new ArgumentEntry(behaviorEntry, argi, ""));
97 String argName = this.translator.translate(new ArgumentEntry(behaviorEntry, argi, "")); 97 if (argName == null) {
98 if (argName == null) { 98 Type argType = behaviorEntry.getSignature().getArgumentTypes().get(isNestedClassConstructor ? argi + 1 : argi);
99 Type argType = behaviorEntry.getSignature().getArgumentTypes().get(isNestedClassConstructor ? argi + 1 : argi); 99 // Unfortunately each of these have different name getters, so they have different code paths
100 // Unfortunately each of these have different name getters, so they have different code paths 100 if (argType.isPrimitive()) {
101 if (argType.isPrimitive()) { 101 Type.Primitive argCls = argType.getPrimitive();
102 Type.Primitive argCls = argType.getPrimitive(); 102 argName = "a" + argCls.name() + (argi + 1);
103 argName = "a" + argCls.name() + (argi + 1); 103 } else if (argType.isArray()) {
104 } else if (argType.isArray()) { 104 // List types would require this whole block again, so just go with aListx
105 // List types would require this whole block again, so just go with aListx 105 argName = "aList" + (argi + 1);
106 argName = "aList" + (argi + 1); 106 } else if (argType.isClass()) {
107 } else if (argType.isClass()) { 107 ClassEntry argClsTrans = this.translator.translateEntry(argType.getClassEntry());
108 ClassEntry argClsTrans = this.translator.translateEntry(argType.getClassEntry()); 108 argName = "a" + argClsTrans.getSimpleName().replace("$", "") + (argi + 1);
109 argName = "a" + argClsTrans.getSimpleName().replace("$", "") + (argi + 1); 109 } else {
110 } else { 110 argName = "a" + (argi + 1);
111 argName = "a" + (argi + 1); 111 }
112 } 112 }
113 } 113 renameVariable(table, i, constants.addUtf8Info(argName));
114 renameVariable(table, i, constants.addUtf8Info(argName)); 114 }
115 } 115 }
116 } 116
117 117 // then rename the rest of the args, if any
118 // then rename the rest of the args, if any 118 for (int i = starti + numArgs; i < table.tableLength(); i++) {
119 for (int i = starti + numArgs; i < table.tableLength(); i++) { 119 int firstIndex = table.index(starti + numArgs);
120 int firstIndex = table.index(starti + numArgs); 120 renameVariable(table, i, constants.addUtf8Info("v" + (table.index(i) - firstIndex + 1)));
121 renameVariable(table, i, constants.addUtf8Info("v" + (table.index(i) - firstIndex + 1))); 121 }
122 } 122 }
123 } 123
124 124 private void renameLVTT(LocalVariableTypeAttribute typeTable, LocalVariableAttribute table) {
125 private void renameLVTT(LocalVariableTypeAttribute typeTable, LocalVariableAttribute table) { 125 // rename args to the same names as in the LVT
126 // rename args to the same names as in the LVT 126 for (int i = 0; i < typeTable.tableLength(); i++) {
127 for (int i = 0; i < typeTable.tableLength(); i++) { 127 renameVariable(typeTable, i, getNameIndex(table, typeTable.index(i)));
128 renameVariable(typeTable, i, getNameIndex(table, typeTable.index(i))); 128 }
129 } 129 }
130 } 130
131 131 private void renameVariable(LocalVariableAttribute table, int i, int stringId) {
132 private void renameVariable(LocalVariableAttribute table, int i, int stringId) { 132 // based off of LocalVariableAttribute.nameIndex()
133 // based off of LocalVariableAttribute.nameIndex() 133 ByteArray.write16bit(stringId, table.get(), i * 10 + 6);
134 ByteArray.write16bit(stringId, table.get(), i * 10 + 6); 134 }
135 } 135
136 136 private int getNameIndex(LocalVariableAttribute table, int index) {
137 private int getNameIndex(LocalVariableAttribute table, int index) { 137 for (int i = 0; i < table.tableLength(); i++) {
138 for (int i = 0; i < table.tableLength(); i++) { 138 if (table.index(i) == index) {
139 if (table.index(i) == index) { 139 return table.nameIndex(i);
140 return table.nameIndex(i); 140 }
141 } 141 }
142 } 142 return 0;
143 return 0; 143 }
144 }
145} 144}
diff --git a/src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java b/src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java
index 28ad04ad..d63572e9 100644
--- a/src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java
+++ b/src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java
@@ -8,10 +8,8 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.bytecode;
12 11
13import java.util.ArrayList; 12package cuchaz.enigma.bytecode;
14import java.util.List;
15 13
16import cuchaz.enigma.mapping.*; 14import cuchaz.enigma.mapping.*;
17import javassist.CtBehavior; 15import javassist.CtBehavior;
@@ -19,48 +17,51 @@ import javassist.CtClass;
19import javassist.bytecode.CodeAttribute; 17import javassist.bytecode.CodeAttribute;
20import javassist.bytecode.LocalVariableAttribute; 18import javassist.bytecode.LocalVariableAttribute;
21 19
20import java.util.ArrayList;
21import java.util.List;
22
22public class MethodParameterWriter { 23public class MethodParameterWriter {
23 24
24 private Translator translator; 25 private Translator translator;
25 26
26 public MethodParameterWriter(Translator translator) { 27 public MethodParameterWriter(Translator translator) {
27 this.translator = translator; 28 this.translator = translator;
28 } 29 }
29 30
30 public void writeMethodArguments(CtClass c) { 31 public void writeMethodArguments(CtClass c) {
31 32
32 // Procyon will read method arguments from the "MethodParameters" attribute, so write those 33 // Procyon will read method arguments from the "MethodParameters" attribute, so write those
33 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 34 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
34 35
35 // if there's a local variable table here, don't write a MethodParameters attribute 36 // if there's a local variable table here, don't write a MethodParameters attribute
36 // let the local variable writer deal with it instead 37 // let the local variable writer deal with it instead
37 // procyon starts doing really weird things if we give it both attributes 38 // procyon starts doing really weird things if we give it both attributes
38 CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute(); 39 CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute();
39 if (codeAttribute != null && codeAttribute.getAttribute(LocalVariableAttribute.tag) != null) { 40 if (codeAttribute != null && codeAttribute.getAttribute(LocalVariableAttribute.tag) != null) {
40 continue; 41 continue;
41 } 42 }
42 43
43 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); 44 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
44 45
45 // get the number of arguments 46 // get the number of arguments
46 Signature signature = behaviorEntry.getSignature(); 47 Signature signature = behaviorEntry.getSignature();
47 if (signature == null) { 48 if (signature == null) {
48 // static initializers have no signatures, or arguments 49 // static initializers have no signatures, or arguments
49 continue; 50 continue;
50 } 51 }
51 int numParams = signature.getArgumentTypes().size(); 52 int numParams = signature.getArgumentTypes().size();
52 if (numParams <= 0) { 53 if (numParams <= 0) {
53 continue; 54 continue;
54 } 55 }
55 56
56 // get the list of argument names 57 // get the list of argument names
57 List<String> names = new ArrayList<>(numParams); 58 List<String> names = new ArrayList<>(numParams);
58 for (int i = 0; i < numParams; i++) { 59 for (int i = 0; i < numParams; i++) {
59 names.add(this.translator.translate(new ArgumentEntry(behaviorEntry, i, ""))); 60 names.add(this.translator.translate(new ArgumentEntry(behaviorEntry, i, "")));
60 } 61 }
61 62
62 // save the mappings to the class 63 // save the mappings to the class
63 MethodParametersAttribute.updateClass(behavior.getMethodInfo(), names); 64 MethodParametersAttribute.updateClass(behavior.getMethodInfo(), names);
64 } 65 }
65 } 66 }
66} 67}
diff --git a/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java b/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java
index bace3a0d..3f819abd 100644
--- a/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java
+++ b/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java
@@ -8,79 +8,80 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode; 12package cuchaz.enigma.bytecode;
12 13
14import javassist.bytecode.AttributeInfo;
15import javassist.bytecode.ConstPool;
16import javassist.bytecode.MethodInfo;
17
13import java.io.ByteArrayOutputStream; 18import java.io.ByteArrayOutputStream;
14import java.io.DataOutputStream; 19import java.io.DataOutputStream;
15import java.io.IOException; 20import java.io.IOException;
16import java.util.ArrayList; 21import java.util.ArrayList;
17import java.util.List; 22import java.util.List;
18 23
19import javassist.bytecode.AttributeInfo;
20import javassist.bytecode.ConstPool;
21import javassist.bytecode.MethodInfo;
22
23public class MethodParametersAttribute extends AttributeInfo { 24public class MethodParametersAttribute extends AttributeInfo {
24 25
25 private MethodParametersAttribute(ConstPool pool, List<Integer> parameterNameIndices) { 26 private MethodParametersAttribute(ConstPool pool, List<Integer> parameterNameIndices) {
26 super(pool, "MethodParameters", writeStruct(parameterNameIndices)); 27 super(pool, "MethodParameters", writeStruct(parameterNameIndices));
27 } 28 }
28 29
29 public static void updateClass(MethodInfo info, List<String> names) { 30 public static void updateClass(MethodInfo info, List<String> names) {
30 31
31 // add the names to the class const pool 32 // add the names to the class const pool
32 ConstPool constPool = info.getConstPool(); 33 ConstPool constPool = info.getConstPool();
33 List<Integer> parameterNameIndices = new ArrayList<>(); 34 List<Integer> parameterNameIndices = new ArrayList<>();
34 for (String name : names) { 35 for (String name : names) {
35 if (name != null) { 36 if (name != null) {
36 parameterNameIndices.add(constPool.addUtf8Info(name)); 37 parameterNameIndices.add(constPool.addUtf8Info(name));
37 } else { 38 } else {
38 parameterNameIndices.add(0); 39 parameterNameIndices.add(0);
39 } 40 }
40 } 41 }
41 42
42 // add the attribute to the method 43 // add the attribute to the method
43 info.addAttribute(new MethodParametersAttribute(constPool, parameterNameIndices)); 44 info.addAttribute(new MethodParametersAttribute(constPool, parameterNameIndices));
44 } 45 }
45 46
46 private static byte[] writeStruct(List<Integer> parameterNameIndices) { 47 private static byte[] writeStruct(List<Integer> parameterNameIndices) {
47 // JVM 8 Spec says the struct looks like this: 48 // JVM 8 Spec says the struct looks like this:
48 // http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.24 49 // http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.24
49 // uint8 num_params 50 // uint8 num_params
50 // for each param: 51 // for each param:
51 // uint16 name_index -> points to UTF8 entry in constant pool, or 0 for no entry 52 // uint16 name_index -> points to UTF8 entry in constant pool, or 0 for no entry
52 // uint16 access_flags -> don't care, just set to 0 53 // uint16 access_flags -> don't care, just set to 0
53 54
54 ByteArrayOutputStream buf = new ByteArrayOutputStream(); 55 ByteArrayOutputStream buf = new ByteArrayOutputStream();
55 DataOutputStream out = new DataOutputStream(buf); 56 DataOutputStream out = new DataOutputStream(buf);
56 57
57 // NOTE: java hates unsigned integers, so we have to be careful here 58 // NOTE: java hates unsigned integers, so we have to be careful here
58 // the writeShort(), writeByte() methods will read 16,8 low-order bits from the int argument 59 // the writeShort(), writeByte() methods will read 16,8 low-order bits from the int argument
59 // as long as the int argument is in range of the unsigned short/byte type, it will be written as an unsigned short/byte 60 // as long as the int argument is in range of the unsigned short/byte type, it will be written as an unsigned short/byte
60 // if the int is out of range, the byte stream won't look the way we want and weird things will happen 61 // if the int is out of range, the byte stream won't look the way we want and weird things will happen
61 final int SIZEOF_UINT8 = 1; 62 final int SIZEOF_UINT8 = 1;
62 final int SIZEOF_UINT16 = 2; 63 final int SIZEOF_UINT16 = 2;
63 final int MAX_UINT8 = (1 << 8) - 1; 64 final int MAX_UINT8 = (1 << 8) - 1;
64 final int MAX_UINT16 = (1 << 16) - 1; 65 final int MAX_UINT16 = (1 << 16) - 1;
65 66
66 try { 67 try {
67 assert (parameterNameIndices.size() >= 0 && parameterNameIndices.size() <= MAX_UINT8); 68 assert (parameterNameIndices.size() >= 0 && parameterNameIndices.size() <= MAX_UINT8);
68 out.writeByte(parameterNameIndices.size()); 69 out.writeByte(parameterNameIndices.size());
69 70
70 for (Integer index : parameterNameIndices) { 71 for (Integer index : parameterNameIndices) {
71 assert (index >= 0 && index <= MAX_UINT16); 72 assert (index >= 0 && index <= MAX_UINT16);
72 out.writeShort(index); 73 out.writeShort(index);
73 74
74 // just write 0 for the access flags 75 // just write 0 for the access flags
75 out.writeShort(0); 76 out.writeShort(0);
76 } 77 }
77 78
78 out.close(); 79 out.close();
79 byte[] data = buf.toByteArray(); 80 byte[] data = buf.toByteArray();
80 assert (data.length == SIZEOF_UINT8 + parameterNameIndices.size() * (SIZEOF_UINT16 + SIZEOF_UINT16)); 81 assert (data.length == SIZEOF_UINT8 + parameterNameIndices.size() * (SIZEOF_UINT16 + SIZEOF_UINT16));
81 return data; 82 return data;
82 } catch (IOException ex) { 83 } catch (IOException ex) {
83 throw new Error(ex); 84 throw new Error(ex);
84 } 85 }
85 } 86 }
86} 87}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java
index 66f22839..eaa6e901 100644
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java
@@ -8,48 +8,49 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode.accessors; 12package cuchaz.enigma.bytecode.accessors;
12 13
13import java.lang.reflect.Field; 14import java.lang.reflect.Field;
14 15
15public class ClassInfoAccessor { 16public class ClassInfoAccessor {
16 17
17 private Object item; 18 private static Class<?> clazz;
18 19 private static Field nameIndex;
19 private static Class<?> clazz; 20
20 private static Field nameIndex; 21 static {
21 22 try {
22 public ClassInfoAccessor(Object item) { 23 clazz = Class.forName("javassist.bytecode.ClassInfo");
23 this.item = item; 24 nameIndex = clazz.getDeclaredField("name");
24 } 25 nameIndex.setAccessible(true);
25 26 } catch (Exception ex) {
26 public int getNameIndex() { 27 throw new Error(ex);
27 try { 28 }
28 return (Integer) nameIndex.get(this.item); 29 }
29 } catch (Exception ex) { 30
30 throw new Error(ex); 31 private Object item;
31 } 32
32 } 33 public ClassInfoAccessor(Object item) {
33 34 this.item = item;
34 public void setNameIndex(int val) { 35 }
35 try { 36
36 nameIndex.set(this.item, val); 37 public static boolean isType(ConstInfoAccessor accessor) {
37 } catch (Exception ex) { 38 return clazz.isAssignableFrom(accessor.getItem().getClass());
38 throw new Error(ex); 39 }
39 } 40
40 } 41 public int getNameIndex() {
41 42 try {
42 public static boolean isType(ConstInfoAccessor accessor) { 43 return (Integer) nameIndex.get(this.item);
43 return clazz.isAssignableFrom(accessor.getItem().getClass()); 44 } catch (Exception ex) {
44 } 45 throw new Error(ex);
45 46 }
46 static { 47 }
47 try { 48
48 clazz = Class.forName("javassist.bytecode.ClassInfo"); 49 public void setNameIndex(int val) {
49 nameIndex = clazz.getDeclaredField("name"); 50 try {
50 nameIndex.setAccessible(true); 51 nameIndex.set(this.item, val);
51 } catch (Exception ex) { 52 } catch (Exception ex) {
52 throw new Error(ex); 53 throw new Error(ex);
53 } 54 }
54 } 55 }
55} 56}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java
index aa363d2a..27d991a3 100644
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java
@@ -8,122 +8,117 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.bytecode.accessors;
12 11
13import java.io.ByteArrayInputStream; 12package cuchaz.enigma.bytecode.accessors;
14import java.io.ByteArrayOutputStream;
15import java.io.DataInputStream;
16import java.io.DataOutputStream;
17import java.io.IOException;
18import java.io.OutputStreamWriter;
19import java.io.PrintWriter;
20import java.lang.reflect.Field;
21import java.lang.reflect.Method;
22 13
23import com.google.common.base.Charsets; 14import com.google.common.base.Charsets;
24import cuchaz.enigma.bytecode.InfoType; 15import cuchaz.enigma.bytecode.InfoType;
25 16
17import java.io.*;
18import java.lang.reflect.Field;
19import java.lang.reflect.Method;
20
26public class ConstInfoAccessor { 21public class ConstInfoAccessor {
27 22
28 private static Class<?> clazz; 23 private static Class<?> clazz;
29 private static Field index; 24 private static Field index;
30 private static Method getTag; 25 private static Method getTag;
31 26
32 private Object item; 27 static {
33 28 try {
34 public ConstInfoAccessor(Object item) { 29 clazz = Class.forName("javassist.bytecode.ConstInfo");
35 if (item == null) { 30 index = clazz.getDeclaredField("index");
36 throw new IllegalArgumentException("item cannot be null!"); 31 index.setAccessible(true);
37 } 32 getTag = clazz.getMethod("getTag");
38 this.item = item; 33 getTag.setAccessible(true);
39 } 34 } catch (Exception ex) {
40 35 throw new Error(ex);
41 public Object getItem() { 36 }
42 return this.item; 37 }
43 } 38
44 39 private Object item;
45 public int getIndex() { 40
46 try { 41 public ConstInfoAccessor(Object item) {
47 return (Integer) index.get(this.item); 42 if (item == null) {
48 } catch (Exception ex) { 43 throw new IllegalArgumentException("item cannot be null!");
49 throw new Error(ex); 44 }
50 } 45 this.item = item;
51 } 46 }
52 47
53 public int getTag() { 48 public Object getItem() {
54 try { 49 return this.item;
55 return (Integer) getTag.invoke(this.item); 50 }
56 } catch (Exception ex) { 51
57 throw new Error(ex); 52 public int getIndex() {
58 } 53 try {
59 } 54 return (Integer) index.get(this.item);
60 55 } catch (Exception ex) {
61 public ConstInfoAccessor copy() { 56 throw new Error(ex);
62 return new ConstInfoAccessor(copyItem()); 57 }
63 } 58 }
64 59
65 public Object copyItem() { 60 public int getTag() {
66 // I don't know of a simpler way to copy one of these silly things... 61 try {
67 try { 62 return (Integer) getTag.invoke(this.item);
68 // serialize the item 63 } catch (Exception ex) {
69 ByteArrayOutputStream buf = new ByteArrayOutputStream(); 64 throw new Error(ex);
70 DataOutputStream out = new DataOutputStream(buf); 65 }
71 write(out); 66 }
72 67
73 // deserialize the item 68 public ConstInfoAccessor copy() {
74 DataInputStream in = new DataInputStream(new ByteArrayInputStream(buf.toByteArray())); 69 return new ConstInfoAccessor(copyItem());
75 Object item = new ConstInfoAccessor(in).getItem(); 70 }
76 in.close(); 71
77 72 public Object copyItem() {
78 return item; 73 // I don't know of a simpler way to copy one of these silly things...
79 } catch (Exception ex) { 74 try {
80 throw new Error(ex); 75 // serialize the item
81 } 76 ByteArrayOutputStream buf = new ByteArrayOutputStream();
82 } 77 DataOutputStream out = new DataOutputStream(buf);
83 78 write(out);
84 public void write(DataOutputStream out) throws IOException { 79
85 try { 80 // deserialize the item
86 out.writeUTF(this.item.getClass().getName()); 81 DataInputStream in = new DataInputStream(new ByteArrayInputStream(buf.toByteArray()));
87 out.writeInt(getIndex()); 82 Object item = new ConstInfoAccessor(in).getItem();
88 83 in.close();
89 Method method = this.item.getClass().getMethod("write", DataOutputStream.class); 84
90 method.setAccessible(true); 85 return item;
91 method.invoke(this.item, out); 86 } catch (Exception ex) {
92 } catch (IOException ex) { 87 throw new Error(ex);
93 throw ex; 88 }
94 } catch (Exception ex) { 89 }
95 throw new Error(ex); 90
96 } 91 public void write(DataOutputStream out) throws IOException {
97 } 92 try {
98 93 out.writeUTF(this.item.getClass().getName());
99 @Override 94 out.writeInt(getIndex());
100 public String toString() { 95
101 try { 96 Method method = this.item.getClass().getMethod("write", DataOutputStream.class);
102 ByteArrayOutputStream buf = new ByteArrayOutputStream(); 97 method.setAccessible(true);
103 PrintWriter out = new PrintWriter(new OutputStreamWriter(buf, Charsets.UTF_8)); 98 method.invoke(this.item, out);
104 Method print = this.item.getClass().getMethod("print", PrintWriter.class); 99 } catch (IOException ex) {
105 print.setAccessible(true); 100 throw ex;
106 print.invoke(this.item, out); 101 } catch (Exception ex) {
107 out.close(); 102 throw new Error(ex);
108 return buf.toString("UTF-8").replace("\n", ""); 103 }
109 } catch (Exception ex) { 104 }
110 throw new Error(ex); 105
111 } 106 @Override
112 } 107 public String toString() {
113 108 try {
114 public InfoType getType() { 109 ByteArrayOutputStream buf = new ByteArrayOutputStream();
115 return InfoType.getByTag(getTag()); 110 PrintWriter out = new PrintWriter(new OutputStreamWriter(buf, Charsets.UTF_8));
116 } 111 Method print = this.item.getClass().getMethod("print", PrintWriter.class);
117 112 print.setAccessible(true);
118 static { 113 print.invoke(this.item, out);
119 try { 114 out.close();
120 clazz = Class.forName("javassist.bytecode.ConstInfo"); 115 return buf.toString("UTF-8").replace("\n", "");
121 index = clazz.getDeclaredField("index"); 116 } catch (Exception ex) {
122 index.setAccessible(true); 117 throw new Error(ex);
123 getTag = clazz.getMethod("getTag"); 118 }
124 getTag.setAccessible(true); 119 }
125 } catch (Exception ex) { 120
126 throw new Error(ex); 121 public InfoType getType() {
127 } 122 return InfoType.getByTag(getTag());
128 } 123 }
129} 124}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java
index 69aee160..aef35321 100644
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java
@@ -8,68 +8,68 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode.accessors; 12package cuchaz.enigma.bytecode.accessors;
12 13
13import java.lang.reflect.Field; 14import java.lang.reflect.Field;
14 15
15public class InvokeDynamicInfoAccessor { 16public class InvokeDynamicInfoAccessor {
16 17
17 private static Class<?> clazz; 18 private static Class<?> clazz;
18 private static Field bootstrapIndex; 19 private static Field bootstrapIndex;
19 private static Field nameAndTypeIndex; 20 private static Field nameAndTypeIndex;
20
21 21
22 private Object item; 22 static {
23 try {
24 clazz = Class.forName("javassist.bytecode.InvokeDynamicInfo");
25 bootstrapIndex = clazz.getDeclaredField("bootstrap");
26 bootstrapIndex.setAccessible(true);
27 nameAndTypeIndex = clazz.getDeclaredField("nameAndType");
28 nameAndTypeIndex.setAccessible(true);
29 } catch (Exception ex) {
30 throw new Error(ex);
31 }
32 }
23 33
24 public InvokeDynamicInfoAccessor(Object item) { 34 private Object item;
25 this.item = item;
26 }
27 35
28 public int getBootstrapIndex() { 36 public InvokeDynamicInfoAccessor(Object item) {
29 try { 37 this.item = item;
30 return (Integer) bootstrapIndex.get(this.item); 38 }
31 } catch (Exception ex) {
32 throw new Error(ex);
33 }
34 }
35 39
36 public void setBootstrapIndex(int val) { 40 public static boolean isType(ConstInfoAccessor accessor) {
37 try { 41 return clazz.isAssignableFrom(accessor.getItem().getClass());
38 bootstrapIndex.set(this.item, val); 42 }
39 } catch (Exception ex) {
40 throw new Error(ex);
41 }
42 }
43 43
44 public int getNameAndTypeIndex() { 44 public int getBootstrapIndex() {
45 try { 45 try {
46 return (Integer) nameAndTypeIndex.get(this.item); 46 return (Integer) bootstrapIndex.get(this.item);
47 } catch (Exception ex) { 47 } catch (Exception ex) {
48 throw new Error(ex); 48 throw new Error(ex);
49 } 49 }
50 } 50 }
51 51
52 public void setNameAndTypeIndex(int val) { 52 public void setBootstrapIndex(int val) {
53 try { 53 try {
54 nameAndTypeIndex.set(this.item, val); 54 bootstrapIndex.set(this.item, val);
55 } catch (Exception ex) { 55 } catch (Exception ex) {
56 throw new Error(ex); 56 throw new Error(ex);
57 } 57 }
58 } 58 }
59 59
60 public static boolean isType(ConstInfoAccessor accessor) { 60 public int getNameAndTypeIndex() {
61 return clazz.isAssignableFrom(accessor.getItem().getClass()); 61 try {
62 } 62 return (Integer) nameAndTypeIndex.get(this.item);
63 } catch (Exception ex) {
64 throw new Error(ex);
65 }
66 }
63 67
64 static { 68 public void setNameAndTypeIndex(int val) {
65 try { 69 try {
66 clazz = Class.forName("javassist.bytecode.InvokeDynamicInfo"); 70 nameAndTypeIndex.set(this.item, val);
67 bootstrapIndex = clazz.getDeclaredField("bootstrap"); 71 } catch (Exception ex) {
68 bootstrapIndex.setAccessible(true); 72 throw new Error(ex);
69 nameAndTypeIndex = clazz.getDeclaredField("nameAndType"); 73 }
70 nameAndTypeIndex.setAccessible(true); 74 }
71 } catch (Exception ex) {
72 throw new Error(ex);
73 }
74 }
75} 75}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java
index 0e0297be..058bb454 100644
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java
@@ -8,67 +8,68 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode.accessors; 12package cuchaz.enigma.bytecode.accessors;
12 13
13import java.lang.reflect.Field; 14import java.lang.reflect.Field;
14 15
15public class MemberRefInfoAccessor { 16public class MemberRefInfoAccessor {
16 17
17 private static Class<?> clazz; 18 private static Class<?> clazz;
18 private static Field classIndex; 19 private static Field classIndex;
19 private static Field nameAndTypeIndex; 20 private static Field nameAndTypeIndex;
20 21
21 private Object item; 22 static {
23 try {
24 clazz = Class.forName("javassist.bytecode.MemberrefInfo");
25 classIndex = clazz.getDeclaredField("classIndex");
26 classIndex.setAccessible(true);
27 nameAndTypeIndex = clazz.getDeclaredField("nameAndTypeIndex");
28 nameAndTypeIndex.setAccessible(true);
29 } catch (Exception ex) {
30 throw new Error(ex);
31 }
32 }
22 33
23 public MemberRefInfoAccessor(Object item) { 34 private Object item;
24 this.item = item;
25 }
26 35
27 public int getClassIndex() { 36 public MemberRefInfoAccessor(Object item) {
28 try { 37 this.item = item;
29 return (Integer) classIndex.get(this.item); 38 }
30 } catch (Exception ex) {
31 throw new Error(ex);
32 }
33 }
34 39
35 public void setClassIndex(int val) { 40 public static boolean isType(ConstInfoAccessor accessor) {
36 try { 41 return clazz.isAssignableFrom(accessor.getItem().getClass());
37 classIndex.set(this.item, val); 42 }
38 } catch (Exception ex) {
39 throw new Error(ex);
40 }
41 }
42 43
43 public int getNameAndTypeIndex() { 44 public int getClassIndex() {
44 try { 45 try {
45 return (Integer) nameAndTypeIndex.get(this.item); 46 return (Integer) classIndex.get(this.item);
46 } catch (Exception ex) { 47 } catch (Exception ex) {
47 throw new Error(ex); 48 throw new Error(ex);
48 } 49 }
49 } 50 }
50 51
51 public void setNameAndTypeIndex(int val) { 52 public void setClassIndex(int val) {
52 try { 53 try {
53 nameAndTypeIndex.set(this.item, val); 54 classIndex.set(this.item, val);
54 } catch (Exception ex) { 55 } catch (Exception ex) {
55 throw new Error(ex); 56 throw new Error(ex);
56 } 57 }
57 } 58 }
58 59
59 public static boolean isType(ConstInfoAccessor accessor) { 60 public int getNameAndTypeIndex() {
60 return clazz.isAssignableFrom(accessor.getItem().getClass()); 61 try {
61 } 62 return (Integer) nameAndTypeIndex.get(this.item);
63 } catch (Exception ex) {
64 throw new Error(ex);
65 }
66 }
62 67
63 static { 68 public void setNameAndTypeIndex(int val) {
64 try { 69 try {
65 clazz = Class.forName("javassist.bytecode.MemberrefInfo"); 70 nameAndTypeIndex.set(this.item, val);
66 classIndex = clazz.getDeclaredField("classIndex"); 71 } catch (Exception ex) {
67 classIndex.setAccessible(true); 72 throw new Error(ex);
68 nameAndTypeIndex = clazz.getDeclaredField("nameAndTypeIndex"); 73 }
69 nameAndTypeIndex.setAccessible(true); 74 }
70 } catch (Exception ex) {
71 throw new Error(ex);
72 }
73 }
74} 75}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java
index 9a7dd698..985e792e 100644
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java
@@ -8,67 +8,68 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode.accessors; 12package cuchaz.enigma.bytecode.accessors;
12 13
13import java.lang.reflect.Field; 14import java.lang.reflect.Field;
14 15
15public class MethodHandleInfoAccessor { 16public class MethodHandleInfoAccessor {
16 17
17 private static Class<?> clazz; 18 private static Class<?> clazz;
18 private static Field kindIndex; 19 private static Field kindIndex;
19 private static Field indexIndex; 20 private static Field indexIndex;
20 21
21 private Object item; 22 static {
23 try {
24 clazz = Class.forName("javassist.bytecode.MethodHandleInfo");
25 kindIndex = clazz.getDeclaredField("refKind");
26 kindIndex.setAccessible(true);
27 indexIndex = clazz.getDeclaredField("refIndex");
28 indexIndex.setAccessible(true);
29 } catch (Exception ex) {
30 throw new Error(ex);
31 }
32 }
22 33
23 public MethodHandleInfoAccessor(Object item) { 34 private Object item;
24 this.item = item;
25 }
26 35
27 public int getTypeIndex() { 36 public MethodHandleInfoAccessor(Object item) {
28 try { 37 this.item = item;
29 return (Integer) kindIndex.get(this.item); 38 }
30 } catch (Exception ex) {
31 throw new Error(ex);
32 }
33 }
34 39
35 public void setTypeIndex(int val) { 40 public static boolean isType(ConstInfoAccessor accessor) {
36 try { 41 return clazz.isAssignableFrom(accessor.getItem().getClass());
37 kindIndex.set(this.item, val); 42 }
38 } catch (Exception ex) {
39 throw new Error(ex);
40 }
41 }
42 43
43 public int getMethodRefIndex() { 44 public int getTypeIndex() {
44 try { 45 try {
45 return (Integer) indexIndex.get(this.item); 46 return (Integer) kindIndex.get(this.item);
46 } catch (Exception ex) { 47 } catch (Exception ex) {
47 throw new Error(ex); 48 throw new Error(ex);
48 } 49 }
49 } 50 }
50 51
51 public void setMethodRefIndex(int val) { 52 public void setTypeIndex(int val) {
52 try { 53 try {
53 indexIndex.set(this.item, val); 54 kindIndex.set(this.item, val);
54 } catch (Exception ex) { 55 } catch (Exception ex) {
55 throw new Error(ex); 56 throw new Error(ex);
56 } 57 }
57 } 58 }
58 59
59 public static boolean isType(ConstInfoAccessor accessor) { 60 public int getMethodRefIndex() {
60 return clazz.isAssignableFrom(accessor.getItem().getClass()); 61 try {
61 } 62 return (Integer) indexIndex.get(this.item);
63 } catch (Exception ex) {
64 throw new Error(ex);
65 }
66 }
62 67
63 static { 68 public void setMethodRefIndex(int val) {
64 try { 69 try {
65 clazz = Class.forName("javassist.bytecode.MethodHandleInfo"); 70 indexIndex.set(this.item, val);
66 kindIndex = clazz.getDeclaredField("refKind"); 71 } catch (Exception ex) {
67 kindIndex.setAccessible(true); 72 throw new Error(ex);
68 indexIndex = clazz.getDeclaredField("refIndex"); 73 }
69 indexIndex.setAccessible(true); 74 }
70 } catch (Exception ex) {
71 throw new Error(ex);
72 }
73 }
74} 75}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java
index 5ec9c3b4..10b0cb0c 100644
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java
@@ -8,49 +8,50 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode.accessors; 12package cuchaz.enigma.bytecode.accessors;
12 13
13import java.lang.reflect.Field; 14import java.lang.reflect.Field;
14 15
15public class MethodTypeInfoAccessor { 16public class MethodTypeInfoAccessor {
16 17
17 private static Class<?> clazz; 18 private static Class<?> clazz;
18 private static Field descriptorIndex; 19 private static Field descriptorIndex;
19 20
20 private Object item; 21 static {
21 22 try {
22 public MethodTypeInfoAccessor(Object item) { 23 clazz = Class.forName("javassist.bytecode.MethodTypeInfo");
23 this.item = item; 24 descriptorIndex = clazz.getDeclaredField("descriptor");
24 } 25 descriptorIndex.setAccessible(true);
25 26 } catch (Exception ex) {
26 public int getTypeIndex() { 27 throw new Error(ex);
27 try { 28 }
28 return (Integer) descriptorIndex.get(this.item); 29 }
29 } catch (Exception ex) { 30
30 throw new Error(ex); 31 private Object item;
31 } 32
32 } 33 public MethodTypeInfoAccessor(Object item) {
33 34 this.item = item;
34 public void setTypeIndex(int val) { 35 }
35 try { 36
36 descriptorIndex.set(this.item, val); 37 public static boolean isType(ConstInfoAccessor accessor) {
37 } catch (Exception ex) { 38 return clazz.isAssignableFrom(accessor.getItem().getClass());
38 throw new Error(ex); 39 }
39 } 40
40 } 41 public int getTypeIndex() {
41 42 try {
42 public static boolean isType(ConstInfoAccessor accessor) { 43 return (Integer) descriptorIndex.get(this.item);
43 return clazz.isAssignableFrom(accessor.getItem().getClass()); 44 } catch (Exception ex) {
44 } 45 throw new Error(ex);
45 46 }
46 static { 47 }
47 try { 48
48 clazz = Class.forName("javassist.bytecode.MethodTypeInfo"); 49 public void setTypeIndex(int val) {
49 descriptorIndex = clazz.getDeclaredField("descriptor"); 50 try {
50 descriptorIndex.setAccessible(true); 51 descriptorIndex.set(this.item, val);
51 } catch (Exception ex) { 52 } catch (Exception ex) {
52 throw new Error(ex); 53 throw new Error(ex);
53 } 54 }
54 } 55 }
55 56
56} 57}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java
index 95df37c1..cc7fdbe8 100644
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java
@@ -8,67 +8,68 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode.accessors; 12package cuchaz.enigma.bytecode.accessors;
12 13
13import java.lang.reflect.Field; 14import java.lang.reflect.Field;
14 15
15public class NameAndTypeInfoAccessor { 16public class NameAndTypeInfoAccessor {
16 17
17 private static Class<?> clazz; 18 private static Class<?> clazz;
18 private static Field nameIndex; 19 private static Field nameIndex;
19 private static Field typeIndex; 20 private static Field typeIndex;
20 21
21 private Object item; 22 static {
23 try {
24 clazz = Class.forName("javassist.bytecode.NameAndTypeInfo");
25 nameIndex = clazz.getDeclaredField("memberName");
26 nameIndex.setAccessible(true);
27 typeIndex = clazz.getDeclaredField("typeDescriptor");
28 typeIndex.setAccessible(true);
29 } catch (Exception ex) {
30 throw new Error(ex);
31 }
32 }
22 33
23 public NameAndTypeInfoAccessor(Object item) { 34 private Object item;
24 this.item = item;
25 }
26 35
27 public int getNameIndex() { 36 public NameAndTypeInfoAccessor(Object item) {
28 try { 37 this.item = item;
29 return (Integer) nameIndex.get(this.item); 38 }
30 } catch (Exception ex) {
31 throw new Error(ex);
32 }
33 }
34 39
35 public void setNameIndex(int val) { 40 public static boolean isType(ConstInfoAccessor accessor) {
36 try { 41 return clazz.isAssignableFrom(accessor.getItem().getClass());
37 nameIndex.set(this.item, val); 42 }
38 } catch (Exception ex) {
39 throw new Error(ex);
40 }
41 }
42 43
43 public int getTypeIndex() { 44 public int getNameIndex() {
44 try { 45 try {
45 return (Integer) typeIndex.get(this.item); 46 return (Integer) nameIndex.get(this.item);
46 } catch (Exception ex) { 47 } catch (Exception ex) {
47 throw new Error(ex); 48 throw new Error(ex);
48 } 49 }
49 } 50 }
50 51
51 public void setTypeIndex(int val) { 52 public void setNameIndex(int val) {
52 try { 53 try {
53 typeIndex.set(this.item, val); 54 nameIndex.set(this.item, val);
54 } catch (Exception ex) { 55 } catch (Exception ex) {
55 throw new Error(ex); 56 throw new Error(ex);
56 } 57 }
57 } 58 }
58 59
59 public static boolean isType(ConstInfoAccessor accessor) { 60 public int getTypeIndex() {
60 return clazz.isAssignableFrom(accessor.getItem().getClass()); 61 try {
61 } 62 return (Integer) typeIndex.get(this.item);
63 } catch (Exception ex) {
64 throw new Error(ex);
65 }
66 }
62 67
63 static { 68 public void setTypeIndex(int val) {
64 try { 69 try {
65 clazz = Class.forName("javassist.bytecode.NameAndTypeInfo"); 70 typeIndex.set(this.item, val);
66 nameIndex = clazz.getDeclaredField("memberName"); 71 } catch (Exception ex) {
67 nameIndex.setAccessible(true); 72 throw new Error(ex);
68 typeIndex = clazz.getDeclaredField("typeDescriptor"); 73 }
69 typeIndex.setAccessible(true); 74 }
70 } catch (Exception ex) {
71 throw new Error(ex);
72 }
73 }
74} 75}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java
index 1c55a443..5c68d4af 100644
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java
@@ -8,48 +8,49 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode.accessors; 12package cuchaz.enigma.bytecode.accessors;
12 13
13import java.lang.reflect.Field; 14import java.lang.reflect.Field;
14 15
15public class StringInfoAccessor { 16public class StringInfoAccessor {
16 17
17 private static Class<?> clazz; 18 private static Class<?> clazz;
18 private static Field stringIndex; 19 private static Field stringIndex;
19 20
20 private Object item; 21 static {
21 22 try {
22 public StringInfoAccessor(Object item) { 23 clazz = Class.forName("javassist.bytecode.StringInfo");
23 this.item = item; 24 stringIndex = clazz.getDeclaredField("string");
24 } 25 stringIndex.setAccessible(true);
25 26 } catch (Exception ex) {
26 public int getStringIndex() { 27 throw new Error(ex);
27 try { 28 }
28 return (Integer) stringIndex.get(this.item); 29 }
29 } catch (Exception ex) { 30
30 throw new Error(ex); 31 private Object item;
31 } 32
32 } 33 public StringInfoAccessor(Object item) {
33 34 this.item = item;
34 public void setStringIndex(int val) { 35 }
35 try { 36
36 stringIndex.set(this.item, val); 37 public static boolean isType(ConstInfoAccessor accessor) {
37 } catch (Exception ex) { 38 return clazz.isAssignableFrom(accessor.getItem().getClass());
38 throw new Error(ex); 39 }
39 } 40
40 } 41 public int getStringIndex() {
41 42 try {
42 public static boolean isType(ConstInfoAccessor accessor) { 43 return (Integer) stringIndex.get(this.item);
43 return clazz.isAssignableFrom(accessor.getItem().getClass()); 44 } catch (Exception ex) {
44 } 45 throw new Error(ex);
45 46 }
46 static { 47 }
47 try { 48
48 clazz = Class.forName("javassist.bytecode.StringInfo"); 49 public void setStringIndex(int val) {
49 stringIndex = clazz.getDeclaredField("string"); 50 try {
50 stringIndex.setAccessible(true); 51 stringIndex.set(this.item, val);
51 } catch (Exception ex) { 52 } catch (Exception ex) {
52 throw new Error(ex); 53 throw new Error(ex);
53 } 54 }
54 } 55 }
55} 56}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java
index 7a2cb667..cc3b41bc 100644
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java
@@ -8,21 +8,22 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode.accessors; 12package cuchaz.enigma.bytecode.accessors;
12 13
13public class Utf8InfoAccessor { 14public class Utf8InfoAccessor {
14 15
15 private static Class<?> clazz; 16 private static Class<?> clazz;
16 17
17 static { 18 static {
18 try { 19 try {
19 clazz = Class.forName("javassist.bytecode.Utf8Info"); 20 clazz = Class.forName("javassist.bytecode.Utf8Info");
20 } catch (Exception ex) { 21 } catch (Exception ex) {
21 throw new Error(ex); 22 throw new Error(ex);
22 } 23 }
23 } 24 }
24 25
25 public static boolean isType(ConstInfoAccessor accessor) { 26 public static boolean isType(ConstInfoAccessor accessor) {
26 return clazz.isAssignableFrom(accessor.getItem().getClass()); 27 return clazz.isAssignableFrom(accessor.getItem().getClass());
27 } 28 }
28} 29}
diff --git a/src/main/java/cuchaz/enigma/convert/ClassForest.java b/src/main/java/cuchaz/enigma/convert/ClassForest.java
index b08d48fb..4542fb33 100644
--- a/src/main/java/cuchaz/enigma/convert/ClassForest.java
+++ b/src/main/java/cuchaz/enigma/convert/ClassForest.java
@@ -8,53 +8,52 @@
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.HashMultimap; 14import com.google.common.collect.HashMultimap;
14import com.google.common.collect.Multimap; 15import com.google.common.collect.Multimap;
15
16import java.util.Collection;
17
18import cuchaz.enigma.mapping.ClassEntry; 16import cuchaz.enigma.mapping.ClassEntry;
19 17
18import java.util.Collection;
20 19
21public class ClassForest { 20public class ClassForest {
22 21
23 private ClassIdentifier identifier; 22 private ClassIdentifier identifier;
24 private Multimap<ClassIdentity, ClassEntry> forest; 23 private Multimap<ClassIdentity, ClassEntry> forest;
25 24
26 public ClassForest(ClassIdentifier identifier) { 25 public ClassForest(ClassIdentifier identifier) {
27 this.identifier = identifier; 26 this.identifier = identifier;
28 this.forest = HashMultimap.create(); 27 this.forest = HashMultimap.create();
29 } 28 }
30 29
31 public void addAll(Iterable<ClassEntry> entries) { 30 public void addAll(Iterable<ClassEntry> entries) {
32 for (ClassEntry entry : entries) { 31 for (ClassEntry entry : entries) {
33 add(entry); 32 add(entry);
34 } 33 }
35 } 34 }
36 35
37 public void add(ClassEntry entry) { 36 public void add(ClassEntry entry) {
38 try { 37 try {
39 this.forest.put(this.identifier.identify(entry), entry); 38 this.forest.put(this.identifier.identify(entry), entry);
40 } catch (ClassNotFoundException ex) { 39 } catch (ClassNotFoundException ex) {
41 throw new Error("Unable to find class " + entry.getName()); 40 throw new Error("Unable to find class " + entry.getName());
42 } 41 }
43 } 42 }
44 43
45 public Collection<ClassIdentity> identities() { 44 public Collection<ClassIdentity> identities() {
46 return this.forest.keySet(); 45 return this.forest.keySet();
47 } 46 }
48 47
49 public Collection<ClassEntry> classes() { 48 public Collection<ClassEntry> classes() {
50 return this.forest.values(); 49 return this.forest.values();
51 } 50 }
52 51
53 public Collection<ClassEntry> getClasses(ClassIdentity identity) { 52 public Collection<ClassEntry> getClasses(ClassIdentity identity) {
54 return this.forest.get(identity); 53 return this.forest.get(identity);
55 } 54 }
56 55
57 public boolean containsIdentity(ClassIdentity identity) { 56 public boolean containsIdentity(ClassIdentity identity) {
58 return this.forest.containsKey(identity); 57 return this.forest.containsKey(identity);
59 } 58 }
60} 59}
diff --git a/src/main/java/cuchaz/enigma/convert/ClassIdentifier.java b/src/main/java/cuchaz/enigma/convert/ClassIdentifier.java
index 557e6083..0a72073c 100644
--- a/src/main/java/cuchaz/enigma/convert/ClassIdentifier.java
+++ b/src/main/java/cuchaz/enigma/convert/ClassIdentifier.java
@@ -8,13 +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.Maps; 14import com.google.common.collect.Maps;
14
15import java.util.Map;
16import java.util.jar.JarFile;
17
18import cuchaz.enigma.TranslatingTypeLoader; 15import cuchaz.enigma.TranslatingTypeLoader;
19import cuchaz.enigma.analysis.JarIndex; 16import cuchaz.enigma.analysis.JarIndex;
20import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; 17import cuchaz.enigma.convert.ClassNamer.SidedClassNamer;
@@ -22,34 +19,36 @@ import cuchaz.enigma.mapping.ClassEntry;
22import cuchaz.enigma.mapping.Translator; 19import cuchaz.enigma.mapping.Translator;
23import javassist.CtClass; 20import javassist.CtClass;
24 21
22import java.util.Map;
23import java.util.jar.JarFile;
25 24
26public class ClassIdentifier { 25public class ClassIdentifier {
27 26
28 private JarIndex index; 27 private JarIndex index;
29 private SidedClassNamer namer; 28 private SidedClassNamer namer;
30 private boolean useReferences; 29 private boolean useReferences;
31 private TranslatingTypeLoader loader; 30 private TranslatingTypeLoader loader;
32 private Map<ClassEntry, ClassIdentity> cache; 31 private Map<ClassEntry, ClassIdentity> cache;
33 32
34 public ClassIdentifier(JarFile jar, JarIndex index, SidedClassNamer namer, boolean useReferences) { 33 public ClassIdentifier(JarFile jar, JarIndex index, SidedClassNamer namer, boolean useReferences) {
35 this.index = index; 34 this.index = index;
36 this.namer = namer; 35 this.namer = namer;
37 this.useReferences = useReferences; 36 this.useReferences = useReferences;
38 this.loader = new TranslatingTypeLoader(jar, index, new Translator(), new Translator()); 37 this.loader = new TranslatingTypeLoader(jar, index, new Translator(), new Translator());
39 this.cache = Maps.newHashMap(); 38 this.cache = Maps.newHashMap();
40 } 39 }
41 40
42 public ClassIdentity identify(ClassEntry classEntry) 41 public ClassIdentity identify(ClassEntry classEntry)
43 throws ClassNotFoundException { 42 throws ClassNotFoundException {
44 ClassIdentity identity = this.cache.get(classEntry); 43 ClassIdentity identity = this.cache.get(classEntry);
45 if (identity == null) { 44 if (identity == null) {
46 CtClass c = this.loader.loadClass(classEntry.getName()); 45 CtClass c = this.loader.loadClass(classEntry.getName());
47 if (c == null) { 46 if (c == null) {
48 throw new ClassNotFoundException(classEntry.getName()); 47 throw new ClassNotFoundException(classEntry.getName());
49 } 48 }
50 identity = new ClassIdentity(c, this.namer, this.index, this.useReferences); 49 identity = new ClassIdentity(c, this.namer, this.index, this.useReferences);
51 this.cache.put(classEntry, identity); 50 this.cache.put(classEntry, identity);
52 } 51 }
53 return identity; 52 return identity;
54 } 53 }
55} 54}
diff --git a/src/main/java/cuchaz/enigma/convert/ClassIdentity.java b/src/main/java/cuchaz/enigma/convert/ClassIdentity.java
index f72bf703..a395b755 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}
diff --git a/src/main/java/cuchaz/enigma/convert/ClassMatch.java b/src/main/java/cuchaz/enigma/convert/ClassMatch.java
index 9fa35f03..bb3e4f43 100644
--- a/src/main/java/cuchaz/enigma/convert/ClassMatch.java
+++ b/src/main/java/cuchaz/enigma/convert/ClassMatch.java
@@ -8,76 +8,76 @@
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.Sets; 14import com.google.common.collect.Sets;
15import cuchaz.enigma.mapping.ClassEntry;
16import cuchaz.enigma.utils.Utils;
14 17
15import java.util.Collection; 18import java.util.Collection;
16import java.util.Set; 19import java.util.Set;
17 20
18import cuchaz.enigma.mapping.ClassEntry;
19import cuchaz.enigma.utils.Utils;
20
21public class ClassMatch { 21public class ClassMatch {
22 22
23 public Set<ClassEntry> sourceClasses; 23 public Set<ClassEntry> sourceClasses;
24 public Set<ClassEntry> destClasses; 24 public Set<ClassEntry> destClasses;
25 25
26 public ClassMatch(Collection<ClassEntry> sourceClasses, Collection<ClassEntry> destClasses) { 26 public ClassMatch(Collection<ClassEntry> sourceClasses, Collection<ClassEntry> destClasses) {
27 this.sourceClasses = Sets.newHashSet(sourceClasses); 27 this.sourceClasses = Sets.newHashSet(sourceClasses);
28 this.destClasses = Sets.newHashSet(destClasses); 28 this.destClasses = Sets.newHashSet(destClasses);
29 } 29 }
30 30
31 public ClassMatch(ClassEntry sourceClass, ClassEntry destClass) { 31 public ClassMatch(ClassEntry sourceClass, ClassEntry destClass) {
32 sourceClasses = Sets.newHashSet(); 32 sourceClasses = Sets.newHashSet();
33 if (sourceClass != null) { 33 if (sourceClass != null) {
34 sourceClasses.add(sourceClass); 34 sourceClasses.add(sourceClass);
35 } 35 }
36 destClasses = Sets.newHashSet(); 36 destClasses = Sets.newHashSet();
37 if (destClass != null) { 37 if (destClass != null) {
38 destClasses.add(destClass); 38 destClasses.add(destClass);
39 } 39 }
40 } 40 }
41 41
42 public boolean isMatched() { 42 public boolean isMatched() {
43 return sourceClasses.size() > 0 && destClasses.size() > 0; 43 return !sourceClasses.isEmpty() && !destClasses.isEmpty();
44 } 44 }
45 45
46 public boolean isAmbiguous() { 46 public boolean isAmbiguous() {
47 return sourceClasses.size() > 1 || destClasses.size() > 1; 47 return sourceClasses.size() > 1 || destClasses.size() > 1;
48 } 48 }
49 49
50 public ClassEntry getUniqueSource() { 50 public ClassEntry getUniqueSource() {
51 if (sourceClasses.size() != 1) { 51 if (sourceClasses.size() != 1) {
52 throw new IllegalStateException("Match has ambiguous source!"); 52 throw new IllegalStateException("Match has ambiguous source!");
53 } 53 }
54 return sourceClasses.iterator().next(); 54 return sourceClasses.iterator().next();
55 } 55 }
56 56
57 public ClassEntry getUniqueDest() { 57 public ClassEntry getUniqueDest() {
58 if (destClasses.size() != 1) { 58 if (destClasses.size() != 1) {
59 throw new IllegalStateException("Match has ambiguous source!"); 59 throw new IllegalStateException("Match has ambiguous source!");
60 } 60 }
61 return destClasses.iterator().next(); 61 return destClasses.iterator().next();
62 } 62 }
63 63
64 public Set<ClassEntry> intersectSourceClasses(Set<ClassEntry> classes) { 64 public Set<ClassEntry> intersectSourceClasses(Set<ClassEntry> classes) {
65 Set<ClassEntry> intersection = Sets.newHashSet(sourceClasses); 65 Set<ClassEntry> intersection = Sets.newHashSet(sourceClasses);
66 intersection.retainAll(classes); 66 intersection.retainAll(classes);
67 return intersection; 67 return intersection;
68 } 68 }
69 69
70 @Override 70 @Override
71 public int hashCode() { 71 public int hashCode() {
72 return Utils.combineHashesOrdered(sourceClasses, destClasses); 72 return Utils.combineHashesOrdered(sourceClasses, destClasses);
73 } 73 }
74 74
75 @Override 75 @Override
76 public boolean equals(Object other) { 76 public boolean equals(Object other) {
77 return other instanceof ClassMatch && equals((ClassMatch) other); 77 return other instanceof ClassMatch && equals((ClassMatch) other);
78 } 78 }
79 79
80 public boolean equals(ClassMatch other) { 80 public boolean equals(ClassMatch other) {
81 return this.sourceClasses.equals(other.sourceClasses) && this.destClasses.equals(other.destClasses); 81 return this.sourceClasses.equals(other.sourceClasses) && this.destClasses.equals(other.destClasses);
82 } 82 }
83} 83}
diff --git a/src/main/java/cuchaz/enigma/convert/ClassMatches.java b/src/main/java/cuchaz/enigma/convert/ClassMatches.java
index 431c4f24..db2c550f 100644
--- a/src/main/java/cuchaz/enigma/convert/ClassMatches.java
+++ b/src/main/java/cuchaz/enigma/convert/ClassMatches.java
@@ -8,152 +8,151 @@
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.BiMap; 14import com.google.common.collect.BiMap;
14import com.google.common.collect.HashBiMap; 15import com.google.common.collect.HashBiMap;
15import com.google.common.collect.Maps; 16import com.google.common.collect.Maps;
16import com.google.common.collect.Sets; 17import com.google.common.collect.Sets;
17
18import java.util.*;
19
20import cuchaz.enigma.mapping.ClassEntry; 18import cuchaz.enigma.mapping.ClassEntry;
21 19
20import java.util.*;
22 21
23public class ClassMatches implements Iterable<ClassMatch> { 22public class ClassMatches implements Iterable<ClassMatch> {
24 23
25 private Collection<ClassMatch> matches; 24 private Collection<ClassMatch> matches;
26 private Map<ClassEntry, ClassMatch> matchesBySource; 25 private Map<ClassEntry, ClassMatch> matchesBySource;
27 private Map<ClassEntry, ClassMatch> matchesByDest; 26 private Map<ClassEntry, ClassMatch> matchesByDest;
28 private BiMap<ClassEntry, ClassEntry> uniqueMatches; 27 private BiMap<ClassEntry, ClassEntry> uniqueMatches;
29 private Map<ClassEntry, ClassMatch> ambiguousMatchesBySource; 28 private Map<ClassEntry, ClassMatch> ambiguousMatchesBySource;
30 private Map<ClassEntry, ClassMatch> ambiguousMatchesByDest; 29 private Map<ClassEntry, ClassMatch> ambiguousMatchesByDest;
31 private Set<ClassEntry> unmatchedSourceClasses; 30 private Set<ClassEntry> unmatchedSourceClasses;
32 private Set<ClassEntry> unmatchedDestClasses; 31 private Set<ClassEntry> unmatchedDestClasses;
33 32
34 public ClassMatches() { 33 public ClassMatches() {
35 this(new ArrayList<>()); 34 this(new ArrayList<>());
36 } 35 }
37 36
38 public ClassMatches(Collection<ClassMatch> matches) { 37 public ClassMatches(Collection<ClassMatch> matches) {
39 this.matches = matches; 38 this.matches = matches;
40 matchesBySource = Maps.newHashMap(); 39 matchesBySource = Maps.newHashMap();
41 matchesByDest = Maps.newHashMap(); 40 matchesByDest = Maps.newHashMap();
42 uniqueMatches = HashBiMap.create(); 41 uniqueMatches = HashBiMap.create();
43 ambiguousMatchesBySource = Maps.newHashMap(); 42 ambiguousMatchesBySource = Maps.newHashMap();
44 ambiguousMatchesByDest = Maps.newHashMap(); 43 ambiguousMatchesByDest = Maps.newHashMap();
45 unmatchedSourceClasses = Sets.newHashSet(); 44 unmatchedSourceClasses = Sets.newHashSet();
46 unmatchedDestClasses = Sets.newHashSet(); 45 unmatchedDestClasses = Sets.newHashSet();
47 46
48 for (ClassMatch match : matches) { 47 for (ClassMatch match : matches) {
49 indexMatch(match); 48 indexMatch(match);
50 } 49 }
51 } 50 }
52 51
53 public void add(ClassMatch match) { 52 public void add(ClassMatch match) {
54 matches.add(match); 53 matches.add(match);
55 indexMatch(match); 54 indexMatch(match);
56 } 55 }
57 56
58 public void remove(ClassMatch match) { 57 public void remove(ClassMatch match) {
59 for (ClassEntry sourceClass : match.sourceClasses) { 58 for (ClassEntry sourceClass : match.sourceClasses) {
60 matchesBySource.remove(sourceClass); 59 matchesBySource.remove(sourceClass);
61 uniqueMatches.remove(sourceClass); 60 uniqueMatches.remove(sourceClass);
62 ambiguousMatchesBySource.remove(sourceClass); 61 ambiguousMatchesBySource.remove(sourceClass);
63 unmatchedSourceClasses.remove(sourceClass); 62 unmatchedSourceClasses.remove(sourceClass);
64 } 63 }
65 for (ClassEntry destClass : match.destClasses) { 64 for (ClassEntry destClass : match.destClasses) {
66 matchesByDest.remove(destClass); 65 matchesByDest.remove(destClass);
67 uniqueMatches.inverse().remove(destClass); 66 uniqueMatches.inverse().remove(destClass);
68 ambiguousMatchesByDest.remove(destClass); 67 ambiguousMatchesByDest.remove(destClass);
69 unmatchedDestClasses.remove(destClass); 68 unmatchedDestClasses.remove(destClass);
70 } 69 }
71 matches.remove(match); 70 matches.remove(match);
72 } 71 }
73 72
74 public int size() { 73 public int size() {
75 return matches.size(); 74 return matches.size();
76 } 75 }
77 76
78 @Override 77 @Override
79 public Iterator<ClassMatch> iterator() { 78 public Iterator<ClassMatch> iterator() {
80 return matches.iterator(); 79 return matches.iterator();
81 } 80 }
82 81
83 private void indexMatch(ClassMatch match) { 82 private void indexMatch(ClassMatch match) {
84 if (!match.isMatched()) { 83 if (!match.isMatched()) {
85 // unmatched 84 // unmatched
86 unmatchedSourceClasses.addAll(match.sourceClasses); 85 unmatchedSourceClasses.addAll(match.sourceClasses);
87 unmatchedDestClasses.addAll(match.destClasses); 86 unmatchedDestClasses.addAll(match.destClasses);
88 } else { 87 } else {
89 if (match.isAmbiguous()) { 88 if (match.isAmbiguous()) {
90 // ambiguously matched 89 // ambiguously matched
91 for (ClassEntry entry : match.sourceClasses) { 90 for (ClassEntry entry : match.sourceClasses) {
92 ambiguousMatchesBySource.put(entry, match); 91 ambiguousMatchesBySource.put(entry, match);
93 } 92 }
94 for (ClassEntry entry : match.destClasses) { 93 for (ClassEntry entry : match.destClasses) {
95 ambiguousMatchesByDest.put(entry, match); 94 ambiguousMatchesByDest.put(entry, match);
96 } 95 }
97 } else { 96 } else {
98 // uniquely matched 97 // uniquely matched
99 uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); 98 uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest());
100 } 99 }
101 } 100 }
102 for (ClassEntry entry : match.sourceClasses) { 101 for (ClassEntry entry : match.sourceClasses) {
103 matchesBySource.put(entry, match); 102 matchesBySource.put(entry, match);
104 } 103 }
105 for (ClassEntry entry : match.destClasses) { 104 for (ClassEntry entry : match.destClasses) {
106 matchesByDest.put(entry, match); 105 matchesByDest.put(entry, match);
107 } 106 }
108 } 107 }
109 108
110 public BiMap<ClassEntry, ClassEntry> getUniqueMatches() { 109 public BiMap<ClassEntry, ClassEntry> getUniqueMatches() {
111 return uniqueMatches; 110 return uniqueMatches;
112 } 111 }
113 112
114 public Set<ClassEntry> getUnmatchedSourceClasses() { 113 public Set<ClassEntry> getUnmatchedSourceClasses() {
115 return unmatchedSourceClasses; 114 return unmatchedSourceClasses;
116 } 115 }
117 116
118 public Set<ClassEntry> getUnmatchedDestClasses() { 117 public Set<ClassEntry> getUnmatchedDestClasses() {
119 return unmatchedDestClasses; 118 return unmatchedDestClasses;
120 } 119 }
121 120
122 public Set<ClassEntry> getAmbiguouslyMatchedSourceClasses() { 121 public Set<ClassEntry> getAmbiguouslyMatchedSourceClasses() {
123 return ambiguousMatchesBySource.keySet(); 122 return ambiguousMatchesBySource.keySet();
124 } 123 }
125 124
126 public ClassMatch getAmbiguousMatchBySource(ClassEntry sourceClass) { 125 public ClassMatch getAmbiguousMatchBySource(ClassEntry sourceClass) {
127 return ambiguousMatchesBySource.get(sourceClass); 126 return ambiguousMatchesBySource.get(sourceClass);
128 } 127 }
129 128
130 public ClassMatch getMatchBySource(ClassEntry sourceClass) { 129 public ClassMatch getMatchBySource(ClassEntry sourceClass) {
131 return matchesBySource.get(sourceClass); 130 return matchesBySource.get(sourceClass);
132 } 131 }
133 132
134 public ClassMatch getMatchByDest(ClassEntry destClass) { 133 public ClassMatch getMatchByDest(ClassEntry destClass) {
135 return matchesByDest.get(destClass); 134 return matchesByDest.get(destClass);
136 } 135 }
137 136
138 public void removeSource(ClassEntry sourceClass) { 137 public void removeSource(ClassEntry sourceClass) {
139 ClassMatch match = matchesBySource.get(sourceClass); 138 ClassMatch match = matchesBySource.get(sourceClass);
140 if (match != null) { 139 if (match != null) {
141 remove(match); 140 remove(match);
142 match.sourceClasses.remove(sourceClass); 141 match.sourceClasses.remove(sourceClass);
143 if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { 142 if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) {
144 add(match); 143 add(match);
145 } 144 }
146 } 145 }
147 } 146 }
148 147
149 public void removeDest(ClassEntry destClass) { 148 public void removeDest(ClassEntry destClass) {
150 ClassMatch match = matchesByDest.get(destClass); 149 ClassMatch match = matchesByDest.get(destClass);
151 if (match != null) { 150 if (match != null) {
152 remove(match); 151 remove(match);
153 match.destClasses.remove(destClass); 152 match.destClasses.remove(destClass);
154 if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { 153 if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) {
155 add(match); 154 add(match);
156 } 155 }
157 } 156 }
158 } 157 }
159} 158}
diff --git a/src/main/java/cuchaz/enigma/convert/ClassMatching.java b/src/main/java/cuchaz/enigma/convert/ClassMatching.java
index b05df871..f302f130 100644
--- a/src/main/java/cuchaz/enigma/convert/ClassMatching.java
+++ b/src/main/java/cuchaz/enigma/convert/ClassMatching.java
@@ -8,12 +8,14 @@
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.BiMap; 14import com.google.common.collect.BiMap;
14import com.google.common.collect.HashBiMap; 15import com.google.common.collect.HashBiMap;
15import com.google.common.collect.Lists; 16import com.google.common.collect.Lists;
16import com.google.common.collect.Sets; 17import com.google.common.collect.Sets;
18import cuchaz.enigma.mapping.ClassEntry;
17 19
18import java.util.ArrayList; 20import java.util.ArrayList;
19import java.util.Collection; 21import java.util.Collection;
@@ -21,134 +23,132 @@ import java.util.List;
21import java.util.Map.Entry; 23import java.util.Map.Entry;
22import java.util.Set; 24import java.util.Set;
23 25
24import cuchaz.enigma.mapping.ClassEntry;
25
26public class ClassMatching { 26public class ClassMatching {
27 27
28 private ClassForest sourceClasses; 28 private ClassForest sourceClasses;
29 private ClassForest destClasses; 29 private ClassForest destClasses;
30 private BiMap<ClassEntry, ClassEntry> knownMatches; 30 private BiMap<ClassEntry, ClassEntry> knownMatches;
31 31
32 public ClassMatching(ClassIdentifier sourceIdentifier, ClassIdentifier destIdentifier) { 32 public ClassMatching(ClassIdentifier sourceIdentifier, ClassIdentifier destIdentifier) {
33 sourceClasses = new ClassForest(sourceIdentifier); 33 sourceClasses = new ClassForest(sourceIdentifier);
34 destClasses = new ClassForest(destIdentifier); 34 destClasses = new ClassForest(destIdentifier);
35 knownMatches = HashBiMap.create(); 35 knownMatches = HashBiMap.create();
36 } 36 }
37 37
38 public void addKnownMatches(BiMap<ClassEntry, ClassEntry> knownMatches) { 38 public void addKnownMatches(BiMap<ClassEntry, ClassEntry> knownMatches) {
39 this.knownMatches.putAll(knownMatches); 39 this.knownMatches.putAll(knownMatches);
40 } 40 }
41 41
42 public void match(Iterable<ClassEntry> sourceClasses, Iterable<ClassEntry> destClasses) { 42 public void match(Iterable<ClassEntry> sourceClasses, Iterable<ClassEntry> destClasses) {
43 for (ClassEntry sourceClass : sourceClasses) { 43 for (ClassEntry sourceClass : sourceClasses) {
44 if (!knownMatches.containsKey(sourceClass)) { 44 if (!knownMatches.containsKey(sourceClass)) {
45 this.sourceClasses.add(sourceClass); 45 this.sourceClasses.add(sourceClass);
46 } 46 }
47 } 47 }
48 for (ClassEntry destClass : destClasses) { 48 for (ClassEntry destClass : destClasses) {
49 if (!knownMatches.containsValue(destClass)) { 49 if (!knownMatches.containsValue(destClass)) {
50 this.destClasses.add(destClass); 50 this.destClasses.add(destClass);
51 } 51 }
52 } 52 }
53 } 53 }
54 54
55 public Collection<ClassMatch> matches() { 55 public Collection<ClassMatch> matches() {
56 List<ClassMatch> matches = Lists.newArrayList(); 56 List<ClassMatch> matches = Lists.newArrayList();
57 for (Entry<ClassEntry, ClassEntry> entry : knownMatches.entrySet()) { 57 for (Entry<ClassEntry, ClassEntry> entry : knownMatches.entrySet()) {
58 matches.add(new ClassMatch( 58 matches.add(new ClassMatch(
59 entry.getKey(), 59 entry.getKey(),
60 entry.getValue() 60 entry.getValue()
61 )); 61 ));
62 } 62 }
63 for (ClassIdentity identity : sourceClasses.identities()) { 63 for (ClassIdentity identity : sourceClasses.identities()) {
64 matches.add(new ClassMatch( 64 matches.add(new ClassMatch(
65 sourceClasses.getClasses(identity), 65 sourceClasses.getClasses(identity),
66 destClasses.getClasses(identity) 66 destClasses.getClasses(identity)
67 )); 67 ));
68 } 68 }
69 for (ClassIdentity identity : destClasses.identities()) { 69 for (ClassIdentity identity : destClasses.identities()) {
70 if (!sourceClasses.containsIdentity(identity)) { 70 if (!sourceClasses.containsIdentity(identity)) {
71 matches.add(new ClassMatch( 71 matches.add(new ClassMatch(
72 new ArrayList<>(), 72 new ArrayList<>(),
73 destClasses.getClasses(identity) 73 destClasses.getClasses(identity)
74 )); 74 ));
75 } 75 }
76 } 76 }
77 return matches; 77 return matches;
78 } 78 }
79 79
80 public Collection<ClassEntry> sourceClasses() { 80 public Collection<ClassEntry> sourceClasses() {
81 Set<ClassEntry> classes = Sets.newHashSet(); 81 Set<ClassEntry> classes = Sets.newHashSet();
82 for (ClassMatch match : matches()) { 82 for (ClassMatch match : matches()) {
83 classes.addAll(match.sourceClasses); 83 classes.addAll(match.sourceClasses);
84 } 84 }
85 return classes; 85 return classes;
86 } 86 }
87 87
88 public Collection<ClassEntry> destClasses() { 88 public Collection<ClassEntry> destClasses() {
89 Set<ClassEntry> classes = Sets.newHashSet(); 89 Set<ClassEntry> classes = Sets.newHashSet();
90 for (ClassMatch match : matches()) { 90 for (ClassMatch match : matches()) {
91 classes.addAll(match.destClasses); 91 classes.addAll(match.destClasses);
92 } 92 }
93 return classes; 93 return classes;
94 } 94 }
95 95
96 public BiMap<ClassEntry, ClassEntry> uniqueMatches() { 96 public BiMap<ClassEntry, ClassEntry> uniqueMatches() {
97 BiMap<ClassEntry, ClassEntry> uniqueMatches = HashBiMap.create(); 97 BiMap<ClassEntry, ClassEntry> uniqueMatches = HashBiMap.create();
98 for (ClassMatch match : matches()) { 98 for (ClassMatch match : matches()) {
99 if (match.isMatched() && !match.isAmbiguous()) { 99 if (match.isMatched() && !match.isAmbiguous()) {
100 uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); 100 uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest());
101 } 101 }
102 } 102 }
103 return uniqueMatches; 103 return uniqueMatches;
104 } 104 }
105 105
106 public Collection<ClassMatch> ambiguousMatches() { 106 public Collection<ClassMatch> ambiguousMatches() {
107 List<ClassMatch> ambiguousMatches = Lists.newArrayList(); 107 List<ClassMatch> ambiguousMatches = Lists.newArrayList();
108 for (ClassMatch match : matches()) { 108 for (ClassMatch match : matches()) {
109 if (match.isMatched() && match.isAmbiguous()) { 109 if (match.isMatched() && match.isAmbiguous()) {
110 ambiguousMatches.add(match); 110 ambiguousMatches.add(match);
111 } 111 }
112 } 112 }
113 return ambiguousMatches; 113 return ambiguousMatches;
114 } 114 }
115 115
116 public Collection<ClassEntry> unmatchedSourceClasses() { 116 public Collection<ClassEntry> unmatchedSourceClasses() {
117 List<ClassEntry> classes = Lists.newArrayList(); 117 List<ClassEntry> classes = Lists.newArrayList();
118 for (ClassMatch match : matches()) { 118 for (ClassMatch match : matches()) {
119 if (!match.isMatched() && !match.sourceClasses.isEmpty()) { 119 if (!match.isMatched() && !match.sourceClasses.isEmpty()) {
120 classes.addAll(match.sourceClasses); 120 classes.addAll(match.sourceClasses);
121 } 121 }
122 } 122 }
123 return classes; 123 return classes;
124 } 124 }
125 125
126 public Collection<ClassEntry> unmatchedDestClasses() { 126 public Collection<ClassEntry> unmatchedDestClasses() {
127 List<ClassEntry> classes = Lists.newArrayList(); 127 List<ClassEntry> classes = Lists.newArrayList();
128 for (ClassMatch match : matches()) { 128 for (ClassMatch match : matches()) {
129 if (!match.isMatched() && !match.destClasses.isEmpty()) { 129 if (!match.isMatched() && !match.destClasses.isEmpty()) {
130 classes.addAll(match.destClasses); 130 classes.addAll(match.destClasses);
131 } 131 }
132 } 132 }
133 return classes; 133 return classes;
134 } 134 }
135 135
136 @Override 136 @Override
137 public String toString() { 137 public String toString() {
138 138
139 // count the ambiguous classes 139 // count the ambiguous classes
140 int numAmbiguousSource = 0; 140 int numAmbiguousSource = 0;
141 int numAmbiguousDest = 0; 141 int numAmbiguousDest = 0;
142 for (ClassMatch match : ambiguousMatches()) { 142 for (ClassMatch match : ambiguousMatches()) {
143 numAmbiguousSource += match.sourceClasses.size(); 143 numAmbiguousSource += match.sourceClasses.size();
144 numAmbiguousDest += match.destClasses.size(); 144 numAmbiguousDest += match.destClasses.size();
145 } 145 }
146 146
147 String buf = String.format("%20s%8s%8s\n", "", "Source", "Dest") + String 147 String buf = String.format("%20s%8s%8s\n", "", "Source", "Dest") + String
148 .format("%20s%8d%8d\n", "Classes", sourceClasses().size(), destClasses().size()) + String 148 .format("%20s%8d%8d\n", "Classes", sourceClasses().size(), destClasses().size()) + String
149 .format("%20s%8d%8d\n", "Uniquely matched", uniqueMatches().size(), uniqueMatches().size()) + String 149 .format("%20s%8d%8d\n", "Uniquely matched", uniqueMatches().size(), uniqueMatches().size()) + String
150 .format("%20s%8d%8d\n", "Ambiguously matched", numAmbiguousSource, numAmbiguousDest) + String 150 .format("%20s%8d%8d\n", "Ambiguously matched", numAmbiguousSource, numAmbiguousDest) + String
151 .format("%20s%8d%8d\n", "Unmatched", unmatchedSourceClasses().size(), unmatchedDestClasses().size()); 151 .format("%20s%8d%8d\n", "Unmatched", unmatchedSourceClasses().size(), unmatchedDestClasses().size());
152 return buf; 152 return buf;
153 } 153 }
154} 154}
diff --git a/src/main/java/cuchaz/enigma/convert/ClassNamer.java b/src/main/java/cuchaz/enigma/convert/ClassNamer.java
index e471c7dd..39699101 100644
--- a/src/main/java/cuchaz/enigma/convert/ClassNamer.java
+++ b/src/main/java/cuchaz/enigma/convert/ClassNamer.java
@@ -8,49 +8,48 @@
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.BiMap; 14import com.google.common.collect.BiMap;
14import com.google.common.collect.Maps; 15import com.google.common.collect.Maps;
16import cuchaz.enigma.mapping.ClassEntry;
15 17
16import java.util.Map; 18import java.util.Map;
17 19
18import cuchaz.enigma.mapping.ClassEntry;
19
20public class ClassNamer { 20public class ClassNamer {
21 21
22 public interface SidedClassNamer { 22 private Map<String, String> sourceNames;
23 String getName(String name); 23 private Map<String, String> destNames;
24 } 24 public ClassNamer(BiMap<ClassEntry, ClassEntry> mappings) {
25 25 // convert the identity mappings to name maps
26 private Map<String, String> sourceNames; 26 this.sourceNames = Maps.newHashMap();
27 private Map<String, String> destNames; 27 this.destNames = Maps.newHashMap();
28 28 int i = 0;
29 public ClassNamer(BiMap<ClassEntry, ClassEntry> mappings) { 29 for (Map.Entry<ClassEntry, ClassEntry> entry : mappings.entrySet()) {
30 // convert the identity mappings to name maps 30 String name = String.format("M%04d", i++);
31 this.sourceNames = Maps.newHashMap(); 31 this.sourceNames.put(entry.getKey().getName(), name);
32 this.destNames = Maps.newHashMap(); 32 this.destNames.put(entry.getValue().getName(), name);
33 int i = 0; 33 }
34 for (Map.Entry<ClassEntry, ClassEntry> entry : mappings.entrySet()) { 34 }
35 String name = String.format("M%04d", i++); 35
36 this.sourceNames.put(entry.getKey().getName(), name); 36 public String getSourceName(String name) {
37 this.destNames.put(entry.getValue().getName(), name); 37 return this.sourceNames.get(name);
38 } 38 }
39 } 39
40 40 public String getDestName(String name) {
41 public String getSourceName(String name) { 41 return this.destNames.get(name);
42 return this.sourceNames.get(name); 42 }
43 } 43
44 44 public SidedClassNamer getSourceNamer() {
45 public String getDestName(String name) { 45 return this::getSourceName;
46 return this.destNames.get(name); 46 }
47 } 47
48 48 public SidedClassNamer getDestNamer() {
49 public SidedClassNamer getSourceNamer() { 49 return this::getDestName;
50 return this::getSourceName; 50 }
51 } 51
52 52 public interface SidedClassNamer {
53 public SidedClassNamer getDestNamer() { 53 String getName(String name);
54 return this::getDestName; 54 }
55 }
56} 55}
diff --git a/src/main/java/cuchaz/enigma/convert/FieldMatches.java b/src/main/java/cuchaz/enigma/convert/FieldMatches.java
index 236cd4d1..a528b276 100644
--- a/src/main/java/cuchaz/enigma/convert/FieldMatches.java
+++ b/src/main/java/cuchaz/enigma/convert/FieldMatches.java
@@ -8,144 +8,143 @@
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.util.Collection;
16import java.util.Set;
17
18import cuchaz.enigma.mapping.ClassEntry; 15import cuchaz.enigma.mapping.ClassEntry;
19import cuchaz.enigma.mapping.FieldEntry; 16import cuchaz.enigma.mapping.FieldEntry;
20 17
18import java.util.Collection;
19import java.util.Set;
21 20
22public class FieldMatches { 21public class FieldMatches {
23 22
24 private BiMap<FieldEntry, FieldEntry> matches; 23 private BiMap<FieldEntry, FieldEntry> matches;
25 private Multimap<ClassEntry, FieldEntry> matchedSourceFields; 24 private Multimap<ClassEntry, FieldEntry> matchedSourceFields;
26 private Multimap<ClassEntry, FieldEntry> unmatchedSourceFields; 25 private Multimap<ClassEntry, FieldEntry> unmatchedSourceFields;
27 private Multimap<ClassEntry, FieldEntry> unmatchedDestFields; 26 private Multimap<ClassEntry, FieldEntry> unmatchedDestFields;
28 private Multimap<ClassEntry, FieldEntry> unmatchableSourceFields; 27 private Multimap<ClassEntry, FieldEntry> unmatchableSourceFields;
29 28
30 public FieldMatches() { 29 public FieldMatches() {
31 matches = HashBiMap.create(); 30 matches = HashBiMap.create();
32 matchedSourceFields = HashMultimap.create(); 31 matchedSourceFields = HashMultimap.create();
33 unmatchedSourceFields = HashMultimap.create(); 32 unmatchedSourceFields = HashMultimap.create();
34 unmatchedDestFields = HashMultimap.create(); 33 unmatchedDestFields = HashMultimap.create();
35 unmatchableSourceFields = HashMultimap.create(); 34 unmatchableSourceFields = HashMultimap.create();
36 } 35 }
37 36
38 public void addMatch(FieldEntry srcField, FieldEntry destField) { 37 public void addMatch(FieldEntry srcField, FieldEntry destField) {
39 boolean wasAdded = matches.put(srcField, destField) == null; 38 boolean wasAdded = matches.put(srcField, destField) == null;
40 assert (wasAdded); 39 assert (wasAdded);
41 wasAdded = matchedSourceFields.put(srcField.getClassEntry(), srcField); 40 wasAdded = matchedSourceFields.put(srcField.getClassEntry(), srcField);
42 assert (wasAdded); 41 assert (wasAdded);
43 } 42 }
44 43
45 public void addUnmatchedSourceField(FieldEntry fieldEntry) { 44 public void addUnmatchedSourceField(FieldEntry fieldEntry) {
46 boolean wasAdded = unmatchedSourceFields.put(fieldEntry.getClassEntry(), fieldEntry); 45 boolean wasAdded = unmatchedSourceFields.put(fieldEntry.getClassEntry(), fieldEntry);
47 assert (wasAdded); 46 assert (wasAdded);
48 } 47 }
49 48
50 public void addUnmatchedSourceFields(Iterable<FieldEntry> fieldEntries) { 49 public void addUnmatchedSourceFields(Iterable<FieldEntry> fieldEntries) {
51 for (FieldEntry fieldEntry : fieldEntries) { 50 for (FieldEntry fieldEntry : fieldEntries) {
52 addUnmatchedSourceField(fieldEntry); 51 addUnmatchedSourceField(fieldEntry);
53 } 52 }
54 } 53 }
55 54
56 public void addUnmatchedDestField(FieldEntry fieldEntry) { 55 public void addUnmatchedDestField(FieldEntry fieldEntry) {
57 boolean wasAdded = unmatchedDestFields.put(fieldEntry.getClassEntry(), fieldEntry); 56 boolean wasAdded = unmatchedDestFields.put(fieldEntry.getClassEntry(), fieldEntry);
58 assert (wasAdded); 57 assert (wasAdded);
59 } 58 }
60 59
61 public void addUnmatchedDestFields(Iterable<FieldEntry> fieldEntries) { 60 public void addUnmatchedDestFields(Iterable<FieldEntry> fieldEntries) {
62 for (FieldEntry fieldEntry : fieldEntries) { 61 for (FieldEntry fieldEntry : fieldEntries) {
63 addUnmatchedDestField(fieldEntry); 62 addUnmatchedDestField(fieldEntry);
64 } 63 }
65 } 64 }
66 65
67 public void addUnmatchableSourceField(FieldEntry sourceField) { 66 public void addUnmatchableSourceField(FieldEntry sourceField) {
68 boolean wasAdded = unmatchableSourceFields.put(sourceField.getClassEntry(), sourceField); 67 boolean wasAdded = unmatchableSourceFields.put(sourceField.getClassEntry(), sourceField);
69 assert (wasAdded); 68 assert (wasAdded);
70 } 69 }
71 70
72 public Set<ClassEntry> getSourceClassesWithUnmatchedFields() { 71 public Set<ClassEntry> getSourceClassesWithUnmatchedFields() {
73 return unmatchedSourceFields.keySet(); 72 return unmatchedSourceFields.keySet();
74 } 73 }
75 74
76 public Collection<ClassEntry> getSourceClassesWithoutUnmatchedFields() { 75 public Collection<ClassEntry> getSourceClassesWithoutUnmatchedFields() {
77 Set<ClassEntry> out = Sets.newHashSet(); 76 Set<ClassEntry> out = Sets.newHashSet();
78 out.addAll(matchedSourceFields.keySet()); 77 out.addAll(matchedSourceFields.keySet());
79 out.removeAll(unmatchedSourceFields.keySet()); 78 out.removeAll(unmatchedSourceFields.keySet());
80 return out; 79 return out;
81 } 80 }
82 81
83 public Collection<FieldEntry> getUnmatchedSourceFields() { 82 public Collection<FieldEntry> getUnmatchedSourceFields() {
84 return unmatchedSourceFields.values(); 83 return unmatchedSourceFields.values();
85 } 84 }
86 85
87 public Collection<FieldEntry> getUnmatchedSourceFields(ClassEntry sourceClass) { 86 public Collection<FieldEntry> getUnmatchedSourceFields(ClassEntry sourceClass) {
88 return unmatchedSourceFields.get(sourceClass); 87 return unmatchedSourceFields.get(sourceClass);
89 } 88 }
90 89
91 public Collection<FieldEntry> getUnmatchedDestFields() { 90 public Collection<FieldEntry> getUnmatchedDestFields() {
92 return unmatchedDestFields.values(); 91 return unmatchedDestFields.values();
93 } 92 }
94 93
95 public Collection<FieldEntry> getUnmatchedDestFields(ClassEntry destClass) { 94 public Collection<FieldEntry> getUnmatchedDestFields(ClassEntry destClass) {
96 return unmatchedDestFields.get(destClass); 95 return unmatchedDestFields.get(destClass);
97 } 96 }
98 97
99 public Collection<FieldEntry> getUnmatchableSourceFields() { 98 public Collection<FieldEntry> getUnmatchableSourceFields() {
100 return unmatchableSourceFields.values(); 99 return unmatchableSourceFields.values();
101 } 100 }
102 101
103 public boolean hasSource(FieldEntry fieldEntry) { 102 public boolean hasSource(FieldEntry fieldEntry) {
104 return matches.containsKey(fieldEntry) || unmatchedSourceFields.containsValue(fieldEntry); 103 return matches.containsKey(fieldEntry) || unmatchedSourceFields.containsValue(fieldEntry);
105 } 104 }
106 105
107 public boolean hasDest(FieldEntry fieldEntry) { 106 public boolean hasDest(FieldEntry fieldEntry) {
108 return matches.containsValue(fieldEntry) || unmatchedDestFields.containsValue(fieldEntry); 107 return matches.containsValue(fieldEntry) || unmatchedDestFields.containsValue(fieldEntry);
109 } 108 }
110 109
111 public BiMap<FieldEntry, FieldEntry> matches() { 110 public BiMap<FieldEntry, FieldEntry> matches() {
112 return matches; 111 return matches;
113 } 112 }
114 113
115 public boolean isMatchedSourceField(FieldEntry sourceField) { 114 public boolean isMatchedSourceField(FieldEntry sourceField) {
116 return matches.containsKey(sourceField); 115 return matches.containsKey(sourceField);
117 } 116 }
118 117
119 public boolean isMatchedDestField(FieldEntry destField) { 118 public boolean isMatchedDestField(FieldEntry destField) {
120 return matches.containsValue(destField); 119 return matches.containsValue(destField);
121 } 120 }
122 121
123 public void makeMatch(FieldEntry sourceField, FieldEntry destField) { 122 public void makeMatch(FieldEntry sourceField, FieldEntry destField) {
124 boolean wasRemoved = unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField); 123 boolean wasRemoved = unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField);
125 assert (wasRemoved); 124 assert (wasRemoved);
126 wasRemoved = unmatchedDestFields.remove(destField.getClassEntry(), destField); 125 wasRemoved = unmatchedDestFields.remove(destField.getClassEntry(), destField);
127 assert (wasRemoved); 126 assert (wasRemoved);
128 addMatch(sourceField, destField); 127 addMatch(sourceField, destField);
129 } 128 }
130 129
131 public boolean isMatched(FieldEntry sourceField, FieldEntry destField) { 130 public boolean isMatched(FieldEntry sourceField, FieldEntry destField) {
132 FieldEntry match = matches.get(sourceField); 131 FieldEntry match = matches.get(sourceField);
133 return match != null && match.equals(destField); 132 return match != null && match.equals(destField);
134 } 133 }
135 134
136 public void unmakeMatch(FieldEntry sourceField, FieldEntry destField) { 135 public void unmakeMatch(FieldEntry sourceField, FieldEntry destField) {
137 boolean wasRemoved = matches.remove(sourceField) != null; 136 boolean wasRemoved = matches.remove(sourceField) != null;
138 assert (wasRemoved); 137 assert (wasRemoved);
139 wasRemoved = matchedSourceFields.remove(sourceField.getClassEntry(), sourceField); 138 wasRemoved = matchedSourceFields.remove(sourceField.getClassEntry(), sourceField);
140 assert (wasRemoved); 139 assert (wasRemoved);
141 addUnmatchedSourceField(sourceField); 140 addUnmatchedSourceField(sourceField);
142 addUnmatchedDestField(destField); 141 addUnmatchedDestField(destField);
143 } 142 }
144 143
145 public void makeSourceUnmatchable(FieldEntry sourceField) { 144 public void makeSourceUnmatchable(FieldEntry sourceField) {
146 assert (!isMatchedSourceField(sourceField)); 145 assert (!isMatchedSourceField(sourceField));
147 boolean wasRemoved = unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField); 146 boolean wasRemoved = unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField);
148 assert (wasRemoved); 147 assert (wasRemoved);
149 addUnmatchableSourceField(sourceField); 148 addUnmatchableSourceField(sourceField);
150 } 149 }
151} 150}
diff --git a/src/main/java/cuchaz/enigma/convert/MappingsConverter.java b/src/main/java/cuchaz/enigma/convert/MappingsConverter.java
index a5ded677..fa3e9362 100644
--- a/src/main/java/cuchaz/enigma/convert/MappingsConverter.java
+++ b/src/main/java/cuchaz/enigma/convert/MappingsConverter.java
@@ -8,6 +8,7 @@
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.*;
@@ -29,688 +30,682 @@ import java.util.jar.JarFile;
29 30
30public class MappingsConverter { 31public class MappingsConverter {
31 32
32 public static ClassMatches computeClassMatches(JarFile sourceJar, JarFile destJar, Mappings mappings) { 33 public static ClassMatches computeClassMatches(JarFile sourceJar, JarFile destJar, Mappings mappings) {
33 34
34 // index jars 35 // index jars
35 System.out.println("Indexing source jar..."); 36 System.out.println("Indexing source jar...");
36 JarIndex sourceIndex = new JarIndex(); 37 JarIndex sourceIndex = new JarIndex();
37 sourceIndex.indexJar(sourceJar, false); 38 sourceIndex.indexJar(sourceJar, false);
38 System.out.println("Indexing dest jar..."); 39 System.out.println("Indexing dest jar...");
39 JarIndex destIndex = new JarIndex(); 40 JarIndex destIndex = new JarIndex();
40 destIndex.indexJar(destJar, false); 41 destIndex.indexJar(destJar, false);
41 42
42 // compute the matching 43 // compute the matching
43 ClassMatching matching = computeMatching(sourceJar, sourceIndex, destJar, destIndex, null); 44 ClassMatching matching = computeMatching(sourceJar, sourceIndex, destJar, destIndex, null);
44 return new ClassMatches(matching.matches()); 45 return new ClassMatches(matching.matches());
45 } 46 }
46 47
47 public static ClassMatching computeMatching(JarFile sourceJar, JarIndex sourceIndex, JarFile destJar, JarIndex destIndex, BiMap<ClassEntry, ClassEntry> knownMatches) { 48 public static ClassMatching computeMatching(JarFile sourceJar, JarIndex sourceIndex, JarFile destJar, JarIndex destIndex, BiMap<ClassEntry, ClassEntry> knownMatches) {
48 49
49 System.out.println("Iteratively matching classes"); 50 System.out.println("Iteratively matching classes");
50 51
51 ClassMatching lastMatching = null; 52 ClassMatching lastMatching = null;
52 int round = 0; 53 int round = 0;
53 SidedClassNamer sourceNamer = null; 54 SidedClassNamer sourceNamer = null;
54 SidedClassNamer destNamer = null; 55 SidedClassNamer destNamer = null;
55 for (boolean useReferences : Arrays.asList(false, true)) { 56 for (boolean useReferences : Arrays.asList(false, true)) {
56 57
57 int numUniqueMatchesLastTime = 0; 58 int numUniqueMatchesLastTime = 0;
58 if (lastMatching != null) { 59 if (lastMatching != null) {
59 numUniqueMatchesLastTime = lastMatching.uniqueMatches().size(); 60 numUniqueMatchesLastTime = lastMatching.uniqueMatches().size();
60 } 61 }
61 62
62 while (true) { 63 while (true) {
63 64
64 System.out.println("Round " + (++round) + "..."); 65 System.out.println("Round " + (++round) + "...");
65 66
66 // init the matching with identity settings 67 // init the matching with identity settings
67 ClassMatching matching = new ClassMatching( 68 ClassMatching matching = new ClassMatching(
68 new ClassIdentifier(sourceJar, sourceIndex, sourceNamer, useReferences), 69 new ClassIdentifier(sourceJar, sourceIndex, sourceNamer, useReferences),
69 new ClassIdentifier(destJar, destIndex, destNamer, useReferences) 70 new ClassIdentifier(destJar, destIndex, destNamer, useReferences)
70 ); 71 );
71 72
72 if (knownMatches != null) { 73 if (knownMatches != null) {
73 matching.addKnownMatches(knownMatches); 74 matching.addKnownMatches(knownMatches);
74 } 75 }
75 76
76 if (lastMatching == null) { 77 if (lastMatching == null) {
77 // search all classes 78 // search all classes
78 matching.match(sourceIndex.getObfClassEntries(), destIndex.getObfClassEntries()); 79 matching.match(sourceIndex.getObfClassEntries(), destIndex.getObfClassEntries());
79 } else { 80 } else {
80 // we already know about these matches from last time 81 // we already know about these matches from last time
81 matching.addKnownMatches(lastMatching.uniqueMatches()); 82 matching.addKnownMatches(lastMatching.uniqueMatches());
82 83
83 // search unmatched and ambiguously-matched classes 84 // search unmatched and ambiguously-matched classes
84 matching.match(lastMatching.unmatchedSourceClasses(), lastMatching.unmatchedDestClasses()); 85 matching.match(lastMatching.unmatchedSourceClasses(), lastMatching.unmatchedDestClasses());
85 for (ClassMatch match : lastMatching.ambiguousMatches()) { 86 for (ClassMatch match : lastMatching.ambiguousMatches()) {
86 matching.match(match.sourceClasses, match.destClasses); 87 matching.match(match.sourceClasses, match.destClasses);
87 } 88 }
88 } 89 }
89 System.out.println(matching); 90 System.out.println(matching);
90 BiMap<ClassEntry, ClassEntry> uniqueMatches = matching.uniqueMatches(); 91 BiMap<ClassEntry, ClassEntry> uniqueMatches = matching.uniqueMatches();
91 92
92 // did we match anything new this time? 93 // did we match anything new this time?
93 if (uniqueMatches.size() > numUniqueMatchesLastTime) { 94 if (uniqueMatches.size() > numUniqueMatchesLastTime) {
94 numUniqueMatchesLastTime = uniqueMatches.size(); 95 numUniqueMatchesLastTime = uniqueMatches.size();
95 lastMatching = matching; 96 lastMatching = matching;
96 } else { 97 } else {
97 break; 98 break;
98 } 99 }
99 100
100 // update the namers 101 // update the namers
101 ClassNamer namer = new ClassNamer(uniqueMatches); 102 ClassNamer namer = new ClassNamer(uniqueMatches);
102 sourceNamer = namer.getSourceNamer(); 103 sourceNamer = namer.getSourceNamer();
103 destNamer = namer.getDestNamer(); 104 destNamer = namer.getDestNamer();
104 } 105 }
105 } 106 }
106 107
107 return lastMatching; 108 return lastMatching;
108 } 109 }
109 110
110 public static Mappings newMappings(ClassMatches matches, Mappings oldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) 111 public static Mappings newMappings(ClassMatches matches, Mappings oldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator)
111 throws MappingConflict { 112 throws MappingConflict {
112 // sort the unique matches by size of inner class chain 113 // sort the unique matches by size of inner class chain
113 Multimap<Integer, java.util.Map.Entry<ClassEntry, ClassEntry>> matchesByDestChainSize = HashMultimap.create(); 114 Multimap<Integer, java.util.Map.Entry<ClassEntry, ClassEntry>> matchesByDestChainSize = HashMultimap.create();
114 for (java.util.Map.Entry<ClassEntry, ClassEntry> match : matches.getUniqueMatches().entrySet()) { 115 for (java.util.Map.Entry<ClassEntry, ClassEntry> match : matches.getUniqueMatches().entrySet()) {
115 int chainSize = destDeobfuscator.getJarIndex().getObfClassChain(match.getValue()).size(); 116 int chainSize = destDeobfuscator.getJarIndex().getObfClassChain(match.getValue()).size();
116 matchesByDestChainSize.put(chainSize, match); 117 matchesByDestChainSize.put(chainSize, match);
117 } 118 }
118 119
119 // build the mappings (in order of small-to-large inner chains) 120 // build the mappings (in order of small-to-large inner chains)
120 Mappings newMappings = new Mappings(); 121 Mappings newMappings = new Mappings();
121 List<Integer> chainSizes = Lists.newArrayList(matchesByDestChainSize.keySet()); 122 List<Integer> chainSizes = Lists.newArrayList(matchesByDestChainSize.keySet());
122 Collections.sort(chainSizes); 123 Collections.sort(chainSizes);
123 for (int chainSize : chainSizes) { 124 for (int chainSize : chainSizes) {
124 for (java.util.Map.Entry<ClassEntry, ClassEntry> match : matchesByDestChainSize.get(chainSize)) { 125 for (java.util.Map.Entry<ClassEntry, ClassEntry> match : matchesByDestChainSize.get(chainSize)) {
125 // get class info 126 // get class info
126 ClassEntry obfSourceClassEntry = match.getKey(); 127 ClassEntry obfSourceClassEntry = match.getKey();
127 ClassEntry obfDestClassEntry = match.getValue(); 128 ClassEntry obfDestClassEntry = match.getValue();
128 List<ClassEntry> destClassChain = destDeobfuscator.getJarIndex().getObfClassChain(obfDestClassEntry); 129 List<ClassEntry> destClassChain = destDeobfuscator.getJarIndex().getObfClassChain(obfDestClassEntry);
129 130
130 ClassMapping sourceMapping; 131 ClassMapping sourceMapping;
131 if (obfSourceClassEntry.isInnerClass()) { 132 if (obfSourceClassEntry.isInnerClass()) {
132 List<ClassMapping> srcClassChain = sourceDeobfuscator.getMappings().getClassMappingChain(obfSourceClassEntry); 133 List<ClassMapping> srcClassChain = sourceDeobfuscator.getMappings().getClassMappingChain(obfSourceClassEntry);
133 sourceMapping = srcClassChain.get(srcClassChain.size() - 1); 134 sourceMapping = srcClassChain.get(srcClassChain.size() - 1);
134 } else { 135 } else {
135 sourceMapping = sourceDeobfuscator.getMappings().getClassByObf(obfSourceClassEntry); 136 sourceMapping = sourceDeobfuscator.getMappings().getClassByObf(obfSourceClassEntry);
136 } 137 }
137 138
138 if (sourceMapping == null) { 139 if (sourceMapping == null) {
139 // if this class was never deobfuscated, don't try to match it 140 // if this class was never deobfuscated, don't try to match it
140 continue; 141 continue;
141 } 142 }
142 143
143 // find out where to make the dest class mapping 144 // find out where to make the dest class mapping
144 if (destClassChain.size() == 1) { 145 if (destClassChain.size() == 1) {
145 // not an inner class, add directly to mappings 146 // not an inner class, add directly to mappings
146 newMappings.addClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, false)); 147 newMappings.addClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, false));
147 } else { 148 } else {
148 // inner class, find the outer class mapping 149 // inner class, find the outer class mapping
149 ClassMapping destMapping = null; 150 ClassMapping destMapping = null;
150 for (int i = 0; i < destClassChain.size() - 1; i++) { 151 for (int i = 0; i < destClassChain.size() - 1; i++) {
151 ClassEntry destChainClassEntry = destClassChain.get(i); 152 ClassEntry destChainClassEntry = destClassChain.get(i);
152 if (destMapping == null) { 153 if (destMapping == null) {
153 destMapping = newMappings.getClassByObf(destChainClassEntry); 154 destMapping = newMappings.getClassByObf(destChainClassEntry);
154 if (destMapping == null) { 155 if (destMapping == null) {
155 destMapping = new ClassMapping(destChainClassEntry.getName()); 156 destMapping = new ClassMapping(destChainClassEntry.getName());
156 newMappings.addClassMapping(destMapping); 157 newMappings.addClassMapping(destMapping);
157 } 158 }
158 } else { 159 } else {
159 destMapping = destMapping.getInnerClassByObfSimple(destChainClassEntry.getInnermostClassName()); 160 destMapping = destMapping.getInnerClassByObfSimple(destChainClassEntry.getInnermostClassName());
160 if (destMapping == null) { 161 if (destMapping == null) {
161 destMapping = new ClassMapping(destChainClassEntry.getName()); 162 destMapping = new ClassMapping(destChainClassEntry.getName());
162 destMapping.addInnerClassMapping(destMapping); 163 destMapping.addInnerClassMapping(destMapping);
163 } 164 }
164 } 165 }
165 } 166 }
166 destMapping.addInnerClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, true)); 167 destMapping.addInnerClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, true));
167 } 168 }
168 } 169 }
169 } 170 }
170 return newMappings; 171 return newMappings;
171 } 172 }
172 173
173 private static ClassMapping migrateClassMapping(ClassEntry newObfClass, ClassMapping oldClassMapping, final ClassMatches matches, boolean useSimpleName) { 174 private static ClassMapping migrateClassMapping(ClassEntry newObfClass, ClassMapping oldClassMapping, final ClassMatches matches, boolean useSimpleName) {
174 175
175 ClassNameReplacer replacer = className -> 176 ClassNameReplacer replacer = className ->
176 { 177 {
177 ClassEntry newClassEntry = matches.getUniqueMatches().get(new ClassEntry(className)); 178 ClassEntry newClassEntry = matches.getUniqueMatches().get(new ClassEntry(className));
178 if (newClassEntry != null) { 179 if (newClassEntry != null) {
179 return newClassEntry.getName(); 180 return newClassEntry.getName();
180 } 181 }
181 return null; 182 return null;
182 }; 183 };
183 184
184 ClassMapping newClassMapping; 185 ClassMapping newClassMapping;
185 String deobfName = oldClassMapping.getDeobfName(); 186 String deobfName = oldClassMapping.getDeobfName();
186 if (deobfName != null) { 187 if (deobfName != null) {
187 if (useSimpleName) { 188 if (useSimpleName) {
188 deobfName = new ClassEntry(deobfName).getSimpleName(); 189 deobfName = new ClassEntry(deobfName).getSimpleName();
189 } 190 }
190 newClassMapping = new ClassMapping(newObfClass.getName(), deobfName); 191 newClassMapping = new ClassMapping(newObfClass.getName(), deobfName);
191 } else { 192 } else {
192 newClassMapping = new ClassMapping(newObfClass.getName()); 193 newClassMapping = new ClassMapping(newObfClass.getName());
193 } 194 }
194 195
195 // migrate fields 196 // migrate fields
196 for (FieldMapping oldFieldMapping : oldClassMapping.fields()) { 197 for (FieldMapping oldFieldMapping : oldClassMapping.fields()) {
197 if (canMigrate(oldFieldMapping.getObfType(), matches)) { 198 if (canMigrate(oldFieldMapping.getObfType(), matches)) {
198 newClassMapping.addFieldMapping(new FieldMapping(oldFieldMapping, replacer)); 199 newClassMapping.addFieldMapping(new FieldMapping(oldFieldMapping, replacer));
199 } else { 200 } else {
200 System.out.println(String.format("Can't map field, dropping: %s.%s %s", 201 System.out.println(String.format("Can't map field, dropping: %s.%s %s",
201 oldClassMapping.getDeobfName(), 202 oldClassMapping.getDeobfName(),
202 oldFieldMapping.getDeobfName(), 203 oldFieldMapping.getDeobfName(),
203 oldFieldMapping.getObfType() 204 oldFieldMapping.getObfType()
204 )); 205 ));
205 } 206 }
206 } 207 }
207 208
208 // migrate methods 209 // migrate methods
209 for (MethodMapping oldMethodMapping : oldClassMapping.methods()) { 210 for (MethodMapping oldMethodMapping : oldClassMapping.methods()) {
210 if (canMigrate(oldMethodMapping.getObfSignature(), matches)) { 211 if (canMigrate(oldMethodMapping.getObfSignature(), matches)) {
211 newClassMapping.addMethodMapping(new MethodMapping(oldMethodMapping, replacer)); 212 newClassMapping.addMethodMapping(new MethodMapping(oldMethodMapping, replacer));
212 } else { 213 } else {
213 System.out.println(String.format("Can't map method, dropping: %s.%s %s", 214 System.out.println(String.format("Can't map method, dropping: %s.%s %s",
214 oldClassMapping.getDeobfName(), 215 oldClassMapping.getDeobfName(),
215 oldMethodMapping.getDeobfName(), 216 oldMethodMapping.getDeobfName(),
216 oldMethodMapping.getObfSignature() 217 oldMethodMapping.getObfSignature()
217 )); 218 ));
218 } 219 }
219 } 220 }
220 221
221 return newClassMapping; 222 return newClassMapping;
222 } 223 }
223 224
224 private static boolean canMigrate(Signature oldObfSignature, ClassMatches classMatches) { 225 private static boolean canMigrate(Signature oldObfSignature, ClassMatches classMatches) {
225 for (Type oldObfType : oldObfSignature.types()) { 226 for (Type oldObfType : oldObfSignature.types()) {
226 if (!canMigrate(oldObfType, classMatches)) { 227 if (!canMigrate(oldObfType, classMatches)) {
227 return false; 228 return false;
228 } 229 }
229 } 230 }
230 return true; 231 return true;
231 } 232 }
232 233
233 private static boolean canMigrate(Type oldObfType, ClassMatches classMatches) { 234 private static boolean canMigrate(Type oldObfType, ClassMatches classMatches) {
234 235
235 // non classes can be migrated 236 // non classes can be migrated
236 if (!oldObfType.hasClass()) { 237 if (!oldObfType.hasClass()) {
237 return true; 238 return true;
238 } 239 }
239 240
240 // non obfuscated classes can be migrated 241 // non obfuscated classes can be migrated
241 ClassEntry classEntry = oldObfType.getClassEntry(); 242 ClassEntry classEntry = oldObfType.getClassEntry();
242 if (classEntry.getPackageName() != null) { 243 if (classEntry.getPackageName() != null) {
243 return true; 244 return true;
244 } 245 }
245 246
246 // obfuscated classes with mappings can be migrated 247 // obfuscated classes with mappings can be migrated
247 return classMatches.getUniqueMatches().containsKey(classEntry); 248 return classMatches.getUniqueMatches().containsKey(classEntry);
248 } 249 }
249 250
250 public static void convertMappings(Mappings mappings, BiMap<ClassEntry, ClassEntry> changes) { 251 public static void convertMappings(Mappings mappings, BiMap<ClassEntry, ClassEntry> changes) {
251 252
252 // sort the changes so classes are renamed in the correct order 253 // sort the changes so classes are renamed in the correct order
253 // ie. if we have the mappings a->b, b->c, we have to apply b->c before a->b 254 // ie. if we have the mappings a->b, b->c, we have to apply b->c before a->b
254 LinkedHashMap<ClassEntry, ClassEntry> sortedChanges = Maps.newLinkedHashMap(); 255 LinkedHashMap<ClassEntry, ClassEntry> sortedChanges = Maps.newLinkedHashMap();
255 int numChangesLeft = changes.size(); 256 int numChangesLeft = changes.size();
256 while (!changes.isEmpty()) { 257 while (!changes.isEmpty()) {
257 Iterator<Map.Entry<ClassEntry, ClassEntry>> iter = changes.entrySet().iterator(); 258 Iterator<Map.Entry<ClassEntry, ClassEntry>> iter = changes.entrySet().iterator();
258 while (iter.hasNext()) { 259 while (iter.hasNext()) {
259 Map.Entry<ClassEntry, ClassEntry> change = iter.next(); 260 Map.Entry<ClassEntry, ClassEntry> change = iter.next();
260 if (changes.containsKey(change.getValue())) { 261 if (changes.containsKey(change.getValue())) {
261 sortedChanges.put(change.getKey(), change.getValue()); 262 sortedChanges.put(change.getKey(), change.getValue());
262 iter.remove(); 263 iter.remove();
263 } 264 }
264 } 265 }
265 266
266 // did we remove any changes? 267 // did we remove any changes?
267 if (numChangesLeft - changes.size() > 0) { 268 if (numChangesLeft - changes.size() > 0) {
268 // keep going 269 // keep going
269 numChangesLeft = changes.size(); 270 numChangesLeft = changes.size();
270 } else { 271 } else {
271 // can't sort anymore. There must be a loop 272 // can't sort anymore. There must be a loop
272 break; 273 break;
273 } 274 }
274 } 275 }
275 if (!changes.isEmpty()) { 276 if (!changes.isEmpty()) {
276 throw new Error("Unable to sort class changes! There must be a cycle."); 277 throw new Error("Unable to sort class changes! There must be a cycle.");
277 } 278 }
278 279
279 // convert the mappings in the correct class order 280 // convert the mappings in the correct class order
280 for (Map.Entry<ClassEntry, ClassEntry> entry : sortedChanges.entrySet()) { 281 for (Map.Entry<ClassEntry, ClassEntry> entry : sortedChanges.entrySet()) {
281 mappings.renameObfClass(entry.getKey().getName(), entry.getValue().getName()); 282 mappings.renameObfClass(entry.getKey().getName(), entry.getValue().getName());
282 } 283 }
283 } 284 }
284 285
285 public interface Doer<T extends Entry> { 286 public static Doer<FieldEntry> getFieldDoer() {
286 Collection<T> getDroppedEntries(MappingsChecker checker); 287 return new Doer<FieldEntry>() {
287 288
288 Collection<T> getObfEntries(JarIndex jarIndex); 289 @Override
289 290 public Collection<FieldEntry> getDroppedEntries(MappingsChecker checker) {
290 Collection<? extends MemberMapping<T>> getMappings(ClassMapping destClassMapping); 291 return checker.getDroppedFieldMappings().keySet();
291 292 }
292 Set<T> filterEntries(Collection<T> obfEntries, T obfSourceEntry, ClassMatches classMatches); 293
293 294 @Override
294 void setUpdateObfMember(ClassMapping classMapping, MemberMapping<T> memberMapping, T newEntry); 295 public Collection<FieldEntry> getObfEntries(JarIndex jarIndex) {
295 296 return jarIndex.getObfFieldEntries();
296 boolean hasObfMember(ClassMapping classMapping, T obfEntry); 297 }
297 298
298 void removeMemberByObf(ClassMapping classMapping, T obfEntry); 299 @Override
299 } 300 public Collection<? extends MemberMapping<FieldEntry>> getMappings(ClassMapping destClassMapping) {
300 301 return (Collection<? extends MemberMapping<FieldEntry>>) destClassMapping.fields();
301 public static Doer<FieldEntry> getFieldDoer() { 302 }
302 return new Doer<FieldEntry>() { 303
303 304 @Override
304 @Override 305 public Set<FieldEntry> filterEntries(Collection<FieldEntry> obfDestFields, FieldEntry obfSourceField, ClassMatches classMatches) {
305 public Collection<FieldEntry> getDroppedEntries(MappingsChecker checker) { 306 Set<FieldEntry> out = Sets.newHashSet();
306 return checker.getDroppedFieldMappings().keySet(); 307 for (FieldEntry obfDestField : obfDestFields) {
307 } 308 Type translatedDestType = translate(obfDestField.getType(), classMatches.getUniqueMatches().inverse());
308 309 if (translatedDestType.equals(obfSourceField.getType())) {
309 @Override 310 out.add(obfDestField);
310 public Collection<FieldEntry> getObfEntries(JarIndex jarIndex) { 311 }
311 return jarIndex.getObfFieldEntries(); 312 }
312 } 313 return out;
313 314 }
314 @Override 315
315 public Collection<? extends MemberMapping<FieldEntry>> getMappings(ClassMapping destClassMapping) { 316 @Override
316 return (Collection<? extends MemberMapping<FieldEntry>>) destClassMapping.fields(); 317 public void setUpdateObfMember(ClassMapping classMapping, MemberMapping<FieldEntry> memberMapping, FieldEntry newField) {
317 } 318 FieldMapping fieldMapping = (FieldMapping) memberMapping;
318 319 classMapping.setFieldObfNameAndType(fieldMapping.getObfName(), fieldMapping.getObfType(), newField.getName(), newField.getType());
319 @Override 320 }
320 public Set<FieldEntry> filterEntries(Collection<FieldEntry> obfDestFields, FieldEntry obfSourceField, ClassMatches classMatches) { 321
321 Set<FieldEntry> out = Sets.newHashSet(); 322 @Override
322 for (FieldEntry obfDestField : obfDestFields) { 323 public boolean hasObfMember(ClassMapping classMapping, FieldEntry obfField) {
323 Type translatedDestType = translate(obfDestField.getType(), classMatches.getUniqueMatches().inverse()); 324 return classMapping.getFieldByObf(obfField.getName(), obfField.getType()) != null;
324 if (translatedDestType.equals(obfSourceField.getType())) { 325 }
325 out.add(obfDestField); 326
326 } 327 @Override
327 } 328 public void removeMemberByObf(ClassMapping classMapping, FieldEntry obfField) {
328 return out; 329 classMapping.removeFieldMapping(classMapping.getFieldByObf(obfField.getName(), obfField.getType()));
329 } 330 }
330 331 };
331 @Override 332 }
332 public void setUpdateObfMember(ClassMapping classMapping, MemberMapping<FieldEntry> memberMapping, FieldEntry newField) { 333
333 FieldMapping fieldMapping = (FieldMapping) memberMapping; 334 public static Doer<BehaviorEntry> getMethodDoer() {
334 classMapping.setFieldObfNameAndType(fieldMapping.getObfName(), fieldMapping.getObfType(), newField.getName(), newField.getType()); 335 return new Doer<BehaviorEntry>() {
335 } 336
336 337 @Override
337 @Override 338 public Collection<BehaviorEntry> getDroppedEntries(MappingsChecker checker) {
338 public boolean hasObfMember(ClassMapping classMapping, FieldEntry obfField) { 339 return checker.getDroppedMethodMappings().keySet();
339 return classMapping.getFieldByObf(obfField.getName(), obfField.getType()) != null; 340 }
340 } 341
341 342 @Override
342 @Override 343 public Collection<BehaviorEntry> getObfEntries(JarIndex jarIndex) {
343 public void removeMemberByObf(ClassMapping classMapping, FieldEntry obfField) { 344 return jarIndex.getObfBehaviorEntries();
344 classMapping.removeFieldMapping(classMapping.getFieldByObf(obfField.getName(), obfField.getType())); 345 }
345 } 346
346 }; 347 @Override
347 } 348 public Collection<? extends MemberMapping<BehaviorEntry>> getMappings(ClassMapping destClassMapping) {
348 349 return (Collection<? extends MemberMapping<BehaviorEntry>>) destClassMapping.methods();
349 public static Doer<BehaviorEntry> getMethodDoer() { 350 }
350 return new Doer<BehaviorEntry>() { 351
351 352 @Override
352 @Override 353 public Set<BehaviorEntry> filterEntries(Collection<BehaviorEntry> obfDestFields, BehaviorEntry obfSourceField, ClassMatches classMatches) {
353 public Collection<BehaviorEntry> getDroppedEntries(MappingsChecker checker) { 354 Set<BehaviorEntry> out = Sets.newHashSet();
354 return checker.getDroppedMethodMappings().keySet(); 355 for (BehaviorEntry obfDestField : obfDestFields) {
355 } 356 // Try to translate the signature
356 357 Signature translatedDestSignature = translate(obfDestField.getSignature(), classMatches.getUniqueMatches().inverse());
357 @Override 358 if (translatedDestSignature != null && obfSourceField.getSignature() != null && translatedDestSignature.equals(obfSourceField.getSignature()))
358 public Collection<BehaviorEntry> getObfEntries(JarIndex jarIndex) { 359 out.add(obfDestField);
359 return jarIndex.getObfBehaviorEntries(); 360 }
360 } 361 return out;
361 362 }
362 @Override 363
363 public Collection<? extends MemberMapping<BehaviorEntry>> getMappings(ClassMapping destClassMapping) { 364 @Override
364 return (Collection<? extends MemberMapping<BehaviorEntry>>) destClassMapping.methods(); 365 public void setUpdateObfMember(ClassMapping classMapping, MemberMapping<BehaviorEntry> memberMapping, BehaviorEntry newBehavior) {
365 } 366 MethodMapping methodMapping = (MethodMapping) memberMapping;
366 367 classMapping.setMethodObfNameAndSignature(methodMapping.getObfName(), methodMapping.getObfSignature(), newBehavior.getName(), newBehavior.getSignature());
367 @Override 368 }
368 public Set<BehaviorEntry> filterEntries(Collection<BehaviorEntry> obfDestFields, BehaviorEntry obfSourceField, ClassMatches classMatches) { 369
369 Set<BehaviorEntry> out = Sets.newHashSet(); 370 @Override
370 for (BehaviorEntry obfDestField : obfDestFields) { 371 public boolean hasObfMember(ClassMapping classMapping, BehaviorEntry obfBehavior) {
371 // Try to translate the signature 372 return classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature()) != null;
372 Signature translatedDestSignature = translate(obfDestField.getSignature(), classMatches.getUniqueMatches().inverse()); 373 }
373 if (translatedDestSignature != null && obfSourceField.getSignature() != null && translatedDestSignature.equals(obfSourceField.getSignature())) 374
374 out.add(obfDestField); 375 @Override
375 } 376 public void removeMemberByObf(ClassMapping classMapping, BehaviorEntry obfBehavior) {
376 return out; 377 classMapping.removeMethodMapping(classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature()));
377 } 378 }
378 379 };
379 @Override 380 }
380 public void setUpdateObfMember(ClassMapping classMapping, MemberMapping<BehaviorEntry> memberMapping, BehaviorEntry newBehavior) { 381
381 MethodMapping methodMapping = (MethodMapping) memberMapping; 382 public static int compareMethodByteCode(CodeIterator sourceIt, CodeIterator destIt) {
382 classMapping.setMethodObfNameAndSignature(methodMapping.getObfName(), methodMapping.getObfSignature(), newBehavior.getName(), newBehavior.getSignature()); 383 int sourcePos = 0;
383 } 384 int destPos = 0;
384 385 while (sourceIt.hasNext() && destIt.hasNext()) {
385 @Override 386 try {
386 public boolean hasObfMember(ClassMapping classMapping, BehaviorEntry obfBehavior) { 387 sourcePos = sourceIt.next();
387 return classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature()) != null; 388 destPos = destIt.next();
388 } 389 if (sourceIt.byteAt(sourcePos) != destIt.byteAt(destPos))
389 390 return sourcePos;
390 @Override 391 } catch (BadBytecode badBytecode) {
391 public void removeMemberByObf(ClassMapping classMapping, BehaviorEntry obfBehavior) { 392 // Ignore bad bytecode (it might be a little bit dangerous...)
392 classMapping.removeMethodMapping(classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature())); 393 }
393 } 394 }
394 }; 395 if (sourcePos < destPos)
395 } 396 return sourcePos;
396 397 else if (destPos < sourcePos)
397 public static int compareMethodByteCode(CodeIterator sourceIt, CodeIterator destIt) 398 return destPos;
398 { 399 return sourcePos;
399 int sourcePos = 0; 400 }
400 int destPos = 0; 401
401 while (sourceIt.hasNext() && destIt.hasNext()) 402 public static BehaviorEntry compareMethods(CtClass destCtClass, CtClass sourceCtClass, BehaviorEntry obfSourceEntry,
402 { 403 Set<BehaviorEntry> obfDestEntries) {
403 try 404 try {
404 { 405 // Get the source method with Javassist
405 sourcePos = sourceIt.next(); 406 CtMethod sourceCtClassMethod = sourceCtClass.getMethod(obfSourceEntry.getName(), obfSourceEntry.getSignature().toString());
406 destPos = destIt.next(); 407 CodeAttribute sourceAttribute = sourceCtClassMethod.getMethodInfo().getCodeAttribute();
407 if (sourceIt.byteAt(sourcePos) != destIt.byteAt(destPos)) 408
408 return sourcePos; 409 // Empty method body, ignore!
409 } catch (BadBytecode badBytecode) 410 if (sourceAttribute == null)
410 { 411 return null;
411 // Ignore bad bytecode (it might be a little bit dangerous...) 412 for (BehaviorEntry desEntry : obfDestEntries) {
412 } 413 try {
413 } 414 CtMethod destCtClassMethod = destCtClass
414 if (sourcePos < destPos) 415 .getMethod(desEntry.getName(), desEntry.getSignature().toString());
415 return sourcePos; 416 CodeAttribute destAttribute = destCtClassMethod.getMethodInfo().getCodeAttribute();
416 else if (destPos < sourcePos) 417
417 return destPos; 418 // Ignore empty body methods
418 return sourcePos; 419 if (destAttribute == null)
419 } 420 continue;
420 421 CodeIterator destIterator = destAttribute.iterator();
421 public static BehaviorEntry compareMethods(CtClass destCtClass, CtClass sourceCtClass, BehaviorEntry obfSourceEntry, 422 int maxPos = compareMethodByteCode(sourceAttribute.iterator(), destIterator);
422 Set<BehaviorEntry> obfDestEntries) 423
423 { 424 // The bytecode is identical to the original method, assuming that the method is correct!
424 try 425 if (sourceAttribute.getCodeLength() == (maxPos + 1) && maxPos > 1)
425 { 426 return desEntry;
426 // Get the source method with Javassist 427 } catch (NotFoundException e) {
427 CtMethod sourceCtClassMethod = sourceCtClass.getMethod(obfSourceEntry.getName(), obfSourceEntry.getSignature().toString()); 428 e.printStackTrace();
428 CodeAttribute sourceAttribute = sourceCtClassMethod.getMethodInfo().getCodeAttribute(); 429 }
429 430 }
430 // Empty method body, ignore! 431 } catch (NotFoundException e) {
431 if (sourceAttribute == null) 432 e.printStackTrace();
432 return null; 433 return null;
433 for (BehaviorEntry desEntry : obfDestEntries) 434 }
434 { 435 return null;
435 try 436 }
436 { 437
437 CtMethod destCtClassMethod = destCtClass 438 public static MemberMatches<BehaviorEntry> computeMethodsMatches(Deobfuscator destDeobfuscator,
438 .getMethod(desEntry.getName(), desEntry.getSignature().toString()); 439 Mappings destMappings,
439 CodeAttribute destAttribute = destCtClassMethod.getMethodInfo().getCodeAttribute(); 440 Deobfuscator sourceDeobfuscator,
440 441 Mappings sourceMappings,
441 // Ignore empty body methods 442 ClassMatches classMatches,
442 if (destAttribute == null) 443 Doer<BehaviorEntry> doer) {
443 continue; 444
444 CodeIterator destIterator = destAttribute.iterator(); 445 MemberMatches<BehaviorEntry> memberMatches = new MemberMatches<>();
445 int maxPos = compareMethodByteCode(sourceAttribute.iterator(), destIterator); 446
446 447 // unmatched source fields are easy
447 // The bytecode is identical to the original method, assuming that the method is correct! 448 MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex());
448 if (sourceAttribute.getCodeLength() == (maxPos + 1) && maxPos > 1) 449 checker.dropBrokenMappings(destMappings);
449 return desEntry; 450 for (BehaviorEntry destObfEntry : doer.getDroppedEntries(checker)) {
450 } catch (NotFoundException e) 451 BehaviorEntry srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse());
451 { 452 memberMatches.addUnmatchedSourceEntry(srcObfEntry);
452 e.printStackTrace(); 453 }
453 } 454
454 } 455 // get matched fields (anything that's left after the checks/drops is matched(
455 } catch (NotFoundException e) 456 for (ClassMapping classMapping : destMappings.classes())
456 { 457 collectMatchedFields(memberMatches, classMapping, classMatches, doer);
457 e.printStackTrace(); 458
458 return null; 459 // get unmatched dest fields
459 } 460 doer.getObfEntries(destDeobfuscator.getJarIndex()).stream()
460 return null; 461 .filter(destEntry -> !memberMatches.isMatchedDestEntry(destEntry))
461 } 462 .forEach(memberMatches::addUnmatchedDestEntry);
462 463
463 public static MemberMatches<BehaviorEntry> computeMethodsMatches(Deobfuscator destDeobfuscator, Mappings destMappings, Deobfuscator sourceDeobfuscator, Mappings sourceMappings, ClassMatches classMatches, Doer<BehaviorEntry> doer) { 464 // Apply mappings to deobfuscator
464 465
465 MemberMatches<BehaviorEntry> memberMatches = new MemberMatches<>(); 466 // Create type loader
466 467 TranslatingTypeLoader destTypeLoader = destDeobfuscator.createTypeLoader();
467 // unmatched source fields are easy 468 TranslatingTypeLoader sourceTypeLoader = sourceDeobfuscator.createTypeLoader();
468 MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); 469
469 checker.dropBrokenMappings(destMappings); 470 System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries...");
470 for (BehaviorEntry destObfEntry : doer.getDroppedEntries(checker)) { 471
471 BehaviorEntry srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse()); 472 // go through the unmatched source fields and try to pick out the easy matches
472 memberMatches.addUnmatchedSourceEntry(srcObfEntry); 473 for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) {
473 } 474 for (BehaviorEntry obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) {
474 475
475 // get matched fields (anything that's left after the checks/drops is matched( 476 // get the possible dest matches
476 for (ClassMapping classMapping : destMappings.classes()) 477 ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass);
477 collectMatchedFields(memberMatches, classMapping, classMatches, doer); 478
478 479 // filter by type/signature
479 // get unmatched dest fields 480 Set<BehaviorEntry> obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches);
480 doer.getObfEntries(destDeobfuscator.getJarIndex()).stream() 481
481 .filter(destEntry -> !memberMatches.isMatchedDestEntry(destEntry)) 482 if (obfDestEntries.size() == 1) {
482 .forEach(memberMatches::addUnmatchedDestEntry); 483 // make the easy match
483 484 memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next());
484 // Apply mappings to deobfuscator 485 } else if (obfDestEntries.isEmpty()) {
485 486 // no match is possible =(
486 // Create type loader 487 memberMatches.makeSourceUnmatchable(obfSourceEntry, null);
487 TranslatingTypeLoader destTypeLoader = destDeobfuscator.createTypeLoader(); 488 } else {
488 TranslatingTypeLoader sourceTypeLoader = sourceDeobfuscator.createTypeLoader(); 489 // Multiple matches! Scan methods instructions
489 490 CtClass destCtClass = destTypeLoader.loadClass(obfDestClass.getClassName());
490 System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries..."); 491 CtClass sourceCtClass = sourceTypeLoader.loadClass(obfSourceClass.getClassName());
491 492 BehaviorEntry match = compareMethods(destCtClass, sourceCtClass, obfSourceEntry, obfDestEntries);
492 // go through the unmatched source fields and try to pick out the easy matches 493 // the method match correctly, match it on the member mapping!
493 for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) { 494 if (match != null)
494 for (BehaviorEntry obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) { 495 memberMatches.makeMatch(obfSourceEntry, match);
495 496 }
496 // get the possible dest matches 497 }
497 ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); 498 }
498 499
499 // filter by type/signature 500 System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries",
500 Set<BehaviorEntry> obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches); 501 memberMatches.getUnmatchedSourceEntries().size(),
501 502 memberMatches.getUnmatchableSourceEntries().size()
502 if (obfDestEntries.size() == 1) { 503 ));
503 // make the easy match 504
504 memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next()); 505 return memberMatches;
505 } else if (obfDestEntries.isEmpty()) { 506 }
506 // no match is possible =( 507
507 memberMatches.makeSourceUnmatchable(obfSourceEntry, null); 508 public static <T extends Entry> MemberMatches<T> computeMemberMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches, Doer<T> doer) {
508 } else 509
509 { 510 MemberMatches<T> memberMatches = new MemberMatches<>();
510 // Multiple matches! Scan methods instructions 511
511 CtClass destCtClass = destTypeLoader.loadClass(obfDestClass.getClassName()); 512 // unmatched source fields are easy
512 CtClass sourceCtClass = sourceTypeLoader.loadClass(obfSourceClass.getClassName()); 513 MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex());
513 BehaviorEntry match = compareMethods(destCtClass, sourceCtClass, obfSourceEntry, obfDestEntries); 514 checker.dropBrokenMappings(destMappings);
514 // the method match correctly, match it on the member mapping! 515 for (T destObfEntry : doer.getDroppedEntries(checker)) {
515 if (match != null) 516 T srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse());
516 memberMatches.makeMatch(obfSourceEntry, match); 517 memberMatches.addUnmatchedSourceEntry(srcObfEntry);
517 } 518 }
518 } 519
519 } 520 // get matched fields (anything that's left after the checks/drops is matched(
520 521 for (ClassMapping classMapping : destMappings.classes()) {
521 System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries", 522 collectMatchedFields(memberMatches, classMapping, classMatches, doer);
522 memberMatches.getUnmatchedSourceEntries().size(), 523 }
523 memberMatches.getUnmatchableSourceEntries().size() 524
524 )); 525 // get unmatched dest fields
525 526 for (T destEntry : doer.getObfEntries(destDeobfuscator.getJarIndex())) {
526 return memberMatches; 527 if (!memberMatches.isMatchedDestEntry(destEntry)) {
527 } 528 memberMatches.addUnmatchedDestEntry(destEntry);
528 529 }
529 public static <T extends Entry> MemberMatches<T> computeMemberMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches, Doer<T> doer) { 530 }
530 531
531 MemberMatches<T> memberMatches = new MemberMatches<>(); 532 System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries...");
532 533
533 // unmatched source fields are easy 534 // go through the unmatched source fields and try to pick out the easy matches
534 MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); 535 for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) {
535 checker.dropBrokenMappings(destMappings); 536 for (T obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) {
536 for (T destObfEntry : doer.getDroppedEntries(checker)) { 537
537 T srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse()); 538 // get the possible dest matches
538 memberMatches.addUnmatchedSourceEntry(srcObfEntry); 539 ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass);
539 } 540
540 541 // filter by type/signature
541 // get matched fields (anything that's left after the checks/drops is matched( 542 Set<T> obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches);
542 for (ClassMapping classMapping : destMappings.classes()) { 543
543 collectMatchedFields(memberMatches, classMapping, classMatches, doer); 544 if (obfDestEntries.size() == 1) {
544 } 545 // make the easy match
545 546 memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next());
546 // get unmatched dest fields 547 } else if (obfDestEntries.isEmpty()) {
547 for (T destEntry : doer.getObfEntries(destDeobfuscator.getJarIndex())) { 548 // no match is possible =(
548 if (!memberMatches.isMatchedDestEntry(destEntry)) { 549 memberMatches.makeSourceUnmatchable(obfSourceEntry, null);
549 memberMatches.addUnmatchedDestEntry(destEntry); 550 }
550 } 551 }
551 } 552 }
552 553
553 System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries..."); 554 System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries",
554 555 memberMatches.getUnmatchedSourceEntries().size(),
555 // go through the unmatched source fields and try to pick out the easy matches 556 memberMatches.getUnmatchableSourceEntries().size()
556 for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) { 557 ));
557 for (T obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) { 558
558 559 return memberMatches;
559 // get the possible dest matches 560 }
560 ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); 561
561 562 private static <T extends Entry> void collectMatchedFields(MemberMatches<T> memberMatches, ClassMapping destClassMapping, ClassMatches classMatches, Doer<T> doer) {
562 // filter by type/signature 563
563 Set<T> obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches); 564 // get the fields for this class
564 565 for (MemberMapping<T> destEntryMapping : doer.getMappings(destClassMapping)) {
565 if (obfDestEntries.size() == 1) { 566 T destObfField = destEntryMapping.getObfEntry(destClassMapping.getObfEntry());
566 // make the easy match 567 T srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse());
567 memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next()); 568 memberMatches.addMatch(srcObfField, destObfField);
568 } else if (obfDestEntries.isEmpty()) { 569 }
569 // no match is possible =( 570
570 memberMatches.makeSourceUnmatchable(obfSourceEntry, null); 571 // recurse
571 } 572 for (ClassMapping destInnerClassMapping : destClassMapping.innerClasses()) {
572 } 573 collectMatchedFields(memberMatches, destInnerClassMapping, classMatches, doer);
573 } 574 }
574 575 }
575 System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries", 576
576 memberMatches.getUnmatchedSourceEntries().size(), 577 @SuppressWarnings("unchecked")
577 memberMatches.getUnmatchableSourceEntries().size() 578 private static <T extends Entry> T translate(T in, BiMap<ClassEntry, ClassEntry> map) {
578 )); 579 if (in instanceof FieldEntry) {
579 580 return (T) new FieldEntry(
580 return memberMatches; 581 map.get(in.getClassEntry()),
581 } 582 in.getName(),
582 583 translate(((FieldEntry) in).getType(), map)
583 private static <T extends Entry> void collectMatchedFields(MemberMatches<T> memberMatches, ClassMapping destClassMapping, ClassMatches classMatches, Doer<T> doer) { 584 );
584 585 } else if (in instanceof MethodEntry) {
585 // get the fields for this class 586 return (T) new MethodEntry(
586 for (MemberMapping<T> destEntryMapping : doer.getMappings(destClassMapping)) { 587 map.get(in.getClassEntry()),
587 T destObfField = destEntryMapping.getObfEntry(destClassMapping.getObfEntry()); 588 in.getName(),
588 T srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse()); 589 translate(((MethodEntry) in).getSignature(), map)
589 memberMatches.addMatch(srcObfField, destObfField); 590 );
590 } 591 } else if (in instanceof ConstructorEntry) {
591 592 return (T) new ConstructorEntry(
592 // recurse 593 map.get(in.getClassEntry()),
593 for (ClassMapping destInnerClassMapping : destClassMapping.innerClasses()) { 594 translate(((ConstructorEntry) in).getSignature(), map)
594 collectMatchedFields(memberMatches, destInnerClassMapping, classMatches, doer); 595 );
595 } 596 }
596 } 597 throw new Error("Unhandled entry type: " + in.getClass());
597 598 }
598 @SuppressWarnings("unchecked") 599
599 private static <T extends Entry> T translate(T in, BiMap<ClassEntry, ClassEntry> map) { 600 private static Type translate(Type type, final BiMap<ClassEntry, ClassEntry> map) {
600 if (in instanceof FieldEntry) { 601 return new Type(type, inClassName ->
601 return (T) new FieldEntry( 602 {
602 map.get(in.getClassEntry()), 603 ClassEntry outClassEntry = map.get(new ClassEntry(inClassName));
603 in.getName(), 604 if (outClassEntry == null) {
604 translate(((FieldEntry) in).getType(), map) 605 return null;
605 ); 606 }
606 } else if (in instanceof MethodEntry) { 607 return outClassEntry.getName();
607 return (T) new MethodEntry( 608 });
608 map.get(in.getClassEntry()), 609 }
609 in.getName(), 610
610 translate(((MethodEntry) in).getSignature(), map) 611 private static Signature translate(Signature signature, final BiMap<ClassEntry, ClassEntry> map) {
611 ); 612 if (signature == null) {
612 } else if (in instanceof ConstructorEntry) { 613 return null;
613 return (T) new ConstructorEntry( 614 }
614 map.get(in.getClassEntry()), 615 return new Signature(signature, inClassName ->
615 translate(((ConstructorEntry) in).getSignature(), map) 616 {
616 ); 617 ClassEntry outClassEntry = map.get(new ClassEntry(inClassName));
617 } 618 if (outClassEntry == null) {
618 throw new Error("Unhandled entry type: " + in.getClass()); 619 return null;
619 } 620 }
620 621 return outClassEntry.getName();
621 private static Type translate(Type type, final BiMap<ClassEntry, ClassEntry> map) { 622 });
622 return new Type(type, inClassName -> 623 }
623 { 624
624 ClassEntry outClassEntry = map.get(new ClassEntry(inClassName)); 625 public static <T extends Entry> void applyMemberMatches(Mappings mappings, ClassMatches classMatches, MemberMatches<T> memberMatches, Doer<T> doer) {
625 if (outClassEntry == null) { 626 for (ClassMapping classMapping : mappings.classes()) {
626 return null; 627 applyMemberMatches(classMapping, classMatches, memberMatches, doer);
627 } 628 }
628 return outClassEntry.getName(); 629 }
629 }); 630
630 } 631 private static <T extends Entry> void applyMemberMatches(ClassMapping classMapping, ClassMatches classMatches, MemberMatches<T> memberMatches, Doer<T> doer) {
631 632
632 private static Signature translate(Signature signature, final BiMap<ClassEntry, ClassEntry> map) { 633 // get the classes
633 if (signature == null) { 634 ClassEntry obfDestClass = new ClassEntry(classMapping.getObfFullName());
634 return null; 635
635 } 636 // make a map of all the renames we need to make
636 return new Signature(signature, inClassName -> 637 Map<T, T> renames = Maps.newHashMap();
637 { 638 for (MemberMapping<T> memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) {
638 ClassEntry outClassEntry = map.get(new ClassEntry(inClassName)); 639 T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass);
639 if (outClassEntry == null) { 640 T obfSourceEntry = getSourceEntryFromDestMapping(memberMapping, obfDestClass, classMatches);
640 return null; 641
641 } 642 // but drop the unmatchable things
642 return outClassEntry.getName(); 643 if (memberMatches.isUnmatchableSourceEntry(obfSourceEntry)) {
643 }); 644 doer.removeMemberByObf(classMapping, obfOldDestEntry);
644 } 645 continue;
645 646 }
646 public static <T extends Entry> void applyMemberMatches(Mappings mappings, ClassMatches classMatches, MemberMatches<T> memberMatches, Doer<T> doer) { 647
647 for (ClassMapping classMapping : mappings.classes()) { 648 T obfNewDestEntry = memberMatches.matches().get(obfSourceEntry);
648 applyMemberMatches(classMapping, classMatches, memberMatches, doer); 649 if (obfNewDestEntry != null && !obfOldDestEntry.getName().equals(obfNewDestEntry.getName())) {
649 } 650 renames.put(obfOldDestEntry, obfNewDestEntry);
650 } 651 }
651 652 }
652 private static <T extends Entry> void applyMemberMatches(ClassMapping classMapping, ClassMatches classMatches, MemberMatches<T> memberMatches, Doer<T> doer) { 653
653 654 if (!renames.isEmpty()) {
654 // get the classes 655
655 ClassEntry obfDestClass = new ClassEntry(classMapping.getObfFullName()); 656 // apply to this class (should never need more than n passes)
656 657 int numRenamesAppliedThisRound;
657 // make a map of all the renames we need to make 658 do {
658 Map<T, T> renames = Maps.newHashMap(); 659 numRenamesAppliedThisRound = 0;
659 for (MemberMapping<T> memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) { 660
660 T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass); 661 for (MemberMapping<T> memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) {
661 T obfSourceEntry = getSourceEntryFromDestMapping(memberMapping, obfDestClass, classMatches); 662 T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass);
662 663 T obfNewDestEntry = renames.get(obfOldDestEntry);
663 // but drop the unmatchable things 664 if (obfNewDestEntry != null) {
664 if (memberMatches.isUnmatchableSourceEntry(obfSourceEntry)) { 665 // make sure this rename won't cause a collision
665 doer.removeMemberByObf(classMapping, obfOldDestEntry); 666 // otherwise, save it for the next round and try again next time
666 continue; 667 if (!doer.hasObfMember(classMapping, obfNewDestEntry)) {
667 } 668 doer.setUpdateObfMember(classMapping, memberMapping, obfNewDestEntry);
668 669 renames.remove(obfOldDestEntry);
669 T obfNewDestEntry = memberMatches.matches().get(obfSourceEntry); 670 numRenamesAppliedThisRound++;
670 if (obfNewDestEntry != null && !obfOldDestEntry.getName().equals(obfNewDestEntry.getName())) { 671 }
671 renames.put(obfOldDestEntry, obfNewDestEntry); 672 }
672 } 673 }
673 } 674 } while (numRenamesAppliedThisRound > 0);
674 675
675 if (!renames.isEmpty()) { 676 if (!renames.isEmpty()) {
676 677 System.err.println(String.format("WARNING: Couldn't apply all the renames for class %s. %d renames left.",
677 // apply to this class (should never need more than n passes) 678 classMapping.getObfFullName(), renames.size()
678 int numRenamesAppliedThisRound; 679 ));
679 do { 680 for (Map.Entry<T, T> entry : renames.entrySet()) {
680 numRenamesAppliedThisRound = 0; 681 System.err.println(String.format("\t%s -> %s", entry.getKey().getName(), entry.getValue().getName()));
681 682 }
682 for (MemberMapping<T> memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) { 683 }
683 T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass); 684 }
684 T obfNewDestEntry = renames.get(obfOldDestEntry); 685
685 if (obfNewDestEntry != null) { 686 // recurse
686 // make sure this rename won't cause a collision 687 for (ClassMapping innerClassMapping : classMapping.innerClasses()) {
687 // otherwise, save it for the next round and try again next time 688 applyMemberMatches(innerClassMapping, classMatches, memberMatches, doer);
688 if (!doer.hasObfMember(classMapping, obfNewDestEntry)) { 689 }
689 doer.setUpdateObfMember(classMapping, memberMapping, obfNewDestEntry); 690 }
690 renames.remove(obfOldDestEntry); 691
691 numRenamesAppliedThisRound++; 692 private static <T extends Entry> T getSourceEntryFromDestMapping(MemberMapping<T> destMemberMapping, ClassEntry obfDestClass, ClassMatches classMatches) {
692 } 693 return translate(destMemberMapping.getObfEntry(obfDestClass), classMatches.getUniqueMatches().inverse());
693 } 694 }
694 } 695
695 } while (numRenamesAppliedThisRound > 0); 696 public interface Doer<T extends Entry> {
696 697 Collection<T> getDroppedEntries(MappingsChecker checker);
697 if (!renames.isEmpty()) { 698
698 System.err.println(String.format("WARNING: Couldn't apply all the renames for class %s. %d renames left.", 699 Collection<T> getObfEntries(JarIndex jarIndex);
699 classMapping.getObfFullName(), renames.size() 700
700 )); 701 Collection<? extends MemberMapping<T>> getMappings(ClassMapping destClassMapping);
701 for (Map.Entry<T, T> entry : renames.entrySet()) { 702
702 System.err.println(String.format("\t%s -> %s", entry.getKey().getName(), entry.getValue().getName())); 703 Set<T> filterEntries(Collection<T> obfEntries, T obfSourceEntry, ClassMatches classMatches);
703 } 704
704 } 705 void setUpdateObfMember(ClassMapping classMapping, MemberMapping<T> memberMapping, T newEntry);
705 } 706
706 707 boolean hasObfMember(ClassMapping classMapping, T obfEntry);
707 // recurse 708
708 for (ClassMapping innerClassMapping : classMapping.innerClasses()) { 709 void removeMemberByObf(ClassMapping classMapping, T obfEntry);
709 applyMemberMatches(innerClassMapping, classMatches, memberMatches, doer); 710 }
710 }
711 }
712
713 private static <T extends Entry> T getSourceEntryFromDestMapping(MemberMapping<T> destMemberMapping, ClassEntry obfDestClass, ClassMatches classMatches) {
714 return translate(destMemberMapping.getObfEntry(obfDestClass), classMatches.getUniqueMatches().inverse());
715 }
716} 711}
diff --git a/src/main/java/cuchaz/enigma/convert/MatchesReader.java b/src/main/java/cuchaz/enigma/convert/MatchesReader.java
index d86d6c2a..1cf50fa4 100644
--- a/src/main/java/cuchaz/enigma/convert/MatchesReader.java
+++ b/src/main/java/cuchaz/enigma/convert/MatchesReader.java
@@ -8,99 +8,98 @@
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.Lists; 14import com.google.common.collect.Lists;
15import cuchaz.enigma.mapping.*;
14 16
15import java.io.*; 17import java.io.*;
16import java.nio.charset.Charset; 18import java.nio.charset.Charset;
17import java.util.Collection; 19import java.util.Collection;
18import java.util.List; 20import java.util.List;
19 21
20import cuchaz.enigma.mapping.*;
21
22
23public class MatchesReader { 22public class MatchesReader {
24 23
25 public static ClassMatches readClasses(File file) 24 public static ClassMatches readClasses(File file)
26 throws IOException { 25 throws IOException {
27 try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")))) { 26 try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")))) {
28 ClassMatches matches = new ClassMatches(); 27 ClassMatches matches = new ClassMatches();
29 String line; 28 String line;
30 while ((line = in.readLine()) != null) { 29 while ((line = in.readLine()) != null) {
31 matches.add(readClassMatch(line)); 30 matches.add(readClassMatch(line));
32 } 31 }
33 return matches; 32 return matches;
34 } 33 }
35 } 34 }
36 35
37 private static ClassMatch readClassMatch(String line) { 36 private static ClassMatch readClassMatch(String line) {
38 String[] sides = line.split(":", 2); 37 String[] sides = line.split(":", 2);
39 return new ClassMatch(readClasses(sides[0]), readClasses(sides[1])); 38 return new ClassMatch(readClasses(sides[0]), readClasses(sides[1]));
40 } 39 }
41 40
42 private static Collection<ClassEntry> readClasses(String in) { 41 private static Collection<ClassEntry> readClasses(String in) {
43 List<ClassEntry> entries = Lists.newArrayList(); 42 List<ClassEntry> entries = Lists.newArrayList();
44 for (String className : in.split(",")) { 43 for (String className : in.split(",")) {
45 className = className.trim(); 44 className = className.trim();
46 if (className.length() > 0) { 45 if (!className.isEmpty()) {
47 entries.add(new ClassEntry(className)); 46 entries.add(new ClassEntry(className));
48 } 47 }
49 } 48 }
50 return entries; 49 return entries;
51 } 50 }
52 51
53 public static <T extends Entry> MemberMatches<T> readMembers(File file) 52 public static <T extends Entry> MemberMatches<T> readMembers(File file)
54 throws IOException { 53 throws IOException {
55 try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")))) { 54 try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")))) {
56 MemberMatches<T> matches = new MemberMatches<>(); 55 MemberMatches<T> matches = new MemberMatches<>();
57 String line; 56 String line;
58 while ((line = in.readLine()) != null) { 57 while ((line = in.readLine()) != null) {
59 readMemberMatch(matches, line); 58 readMemberMatch(matches, line);
60 } 59 }
61 return matches; 60 return matches;
62 } 61 }
63 } 62 }
64 63
65 private static <T extends Entry> void readMemberMatch(MemberMatches<T> matches, String line) { 64 private static <T extends Entry> void readMemberMatch(MemberMatches<T> matches, String line) {
66 if (line.startsWith("!")) { 65 if (line.startsWith("!")) {
67 T source = readEntry(line.substring(1)); 66 T source = readEntry(line.substring(1));
68 matches.addUnmatchableSourceEntry(source); 67 matches.addUnmatchableSourceEntry(source);
69 } else { 68 } else {
70 String[] parts = line.split(":", 2); 69 String[] parts = line.split(":", 2);
71 T source = readEntry(parts[0]); 70 T source = readEntry(parts[0]);
72 T dest = readEntry(parts[1]); 71 T dest = readEntry(parts[1]);
73 if (source != null && dest != null) { 72 if (source != null && dest != null) {
74 matches.addMatch(source, dest); 73 matches.addMatch(source, dest);
75 } else if (source != null) { 74 } else if (source != null) {
76 matches.addUnmatchedSourceEntry(source); 75 matches.addUnmatchedSourceEntry(source);
77 } else if (dest != null) { 76 } else if (dest != null) {
78 matches.addUnmatchedDestEntry(dest); 77 matches.addUnmatchedDestEntry(dest);
79 } 78 }
80 } 79 }
81 } 80 }
82 81
83 @SuppressWarnings("unchecked") 82 @SuppressWarnings("unchecked")
84 private static <T extends Entry> T readEntry(String in) { 83 private static <T extends Entry> T readEntry(String in) {
85 if (in.length() <= 0) { 84 if (in.length() <= 0) {
86 return null; 85 return null;
87 } 86 }
88 String[] parts = in.split(" "); 87 String[] parts = in.split(" ");
89 if (parts.length == 3 && parts[2].indexOf('(') < 0) { 88 if (parts.length == 3 && parts[2].indexOf('(') < 0) {
90 return (T) new FieldEntry( 89 return (T) new FieldEntry(
91 new ClassEntry(parts[0]), 90 new ClassEntry(parts[0]),
92 parts[1], 91 parts[1],
93 new Type(parts[2]) 92 new Type(parts[2])
94 ); 93 );
95 } else { 94 } else {
96 assert (parts.length == 2 || parts.length == 3); 95 assert (parts.length == 2 || parts.length == 3);
97 if (parts.length == 2) { 96 if (parts.length == 2) {
98 return (T) EntryFactory.getBehaviorEntry(parts[0], parts[1]); 97 return (T) EntryFactory.getBehaviorEntry(parts[0], parts[1]);
99 } else if (parts.length == 3) { 98 } else if (parts.length == 3) {
100 return (T) EntryFactory.getBehaviorEntry(parts[0], parts[1], parts[2]); 99 return (T) EntryFactory.getBehaviorEntry(parts[0], parts[1], parts[2]);
101 } else { 100 } else {
102 throw new Error("Malformed behavior entry: " + in); 101 throw new Error("Malformed behavior entry: " + in);
103 } 102 }
104 } 103 }
105 } 104 }
106} 105}
diff --git a/src/main/java/cuchaz/enigma/convert/MatchesWriter.java b/src/main/java/cuchaz/enigma/convert/MatchesWriter.java
index dccbf6f1..8fe73265 100644
--- a/src/main/java/cuchaz/enigma/convert/MatchesWriter.java
+++ b/src/main/java/cuchaz/enigma/convert/MatchesWriter.java
@@ -8,113 +8,116 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.convert;
12 11
13import java.io.*; 12package cuchaz.enigma.convert;
14import java.nio.charset.Charset;
15import java.util.Map;
16 13
17import cuchaz.enigma.mapping.BehaviorEntry; 14import cuchaz.enigma.mapping.BehaviorEntry;
18import cuchaz.enigma.mapping.ClassEntry; 15import cuchaz.enigma.mapping.ClassEntry;
19import cuchaz.enigma.mapping.Entry; 16import cuchaz.enigma.mapping.Entry;
20import cuchaz.enigma.mapping.FieldEntry; 17import cuchaz.enigma.mapping.FieldEntry;
21 18
19import java.io.File;
20import java.io.FileOutputStream;
21import java.io.IOException;
22import java.io.OutputStreamWriter;
23import java.nio.charset.Charset;
24import java.util.Map;
22 25
23public class MatchesWriter { 26public class MatchesWriter {
24 27
25 public static void writeClasses(ClassMatches matches, File file) 28 public static void writeClasses(ClassMatches matches, File file)
26 throws IOException { 29 throws IOException {
27 try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8"))) { 30 try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8"))) {
28 for (ClassMatch match : matches) { 31 for (ClassMatch match : matches) {
29 writeClassMatch(out, match); 32 writeClassMatch(out, match);
30 } 33 }
31 } 34 }
32 } 35 }
33 36
34 private static void writeClassMatch(OutputStreamWriter out, ClassMatch match) 37 private static void writeClassMatch(OutputStreamWriter out, ClassMatch match)
35 throws IOException { 38 throws IOException {
36 writeClasses(out, match.sourceClasses); 39 writeClasses(out, match.sourceClasses);
37 out.write(":"); 40 out.write(":");
38 writeClasses(out, match.destClasses); 41 writeClasses(out, match.destClasses);
39 out.write("\n"); 42 out.write("\n");
40 } 43 }
41 44
42 private static void writeClasses(OutputStreamWriter out, Iterable<ClassEntry> classes) 45 private static void writeClasses(OutputStreamWriter out, Iterable<ClassEntry> classes)
43 throws IOException { 46 throws IOException {
44 boolean isFirst = true; 47 boolean isFirst = true;
45 for (ClassEntry entry : classes) { 48 for (ClassEntry entry : classes) {
46 if (isFirst) { 49 if (isFirst) {
47 isFirst = false; 50 isFirst = false;
48 } else { 51 } else {
49 out.write(","); 52 out.write(",");
50 } 53 }
51 out.write(entry.toString()); 54 out.write(entry.toString());
52 } 55 }
53 } 56 }
54 57
55 public static <T extends Entry> void writeMembers(MemberMatches<T> matches, File file) 58 public static <T extends Entry> void writeMembers(MemberMatches<T> matches, File file)
56 throws IOException { 59 throws IOException {
57 try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8"))) { 60 try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8"))) {
58 for (Map.Entry<T, T> match : matches.matches().entrySet()) { 61 for (Map.Entry<T, T> match : matches.matches().entrySet()) {
59 writeMemberMatch(out, match.getKey(), match.getValue()); 62 writeMemberMatch(out, match.getKey(), match.getValue());
60 } 63 }
61 for (T entry : matches.getUnmatchedSourceEntries()) { 64 for (T entry : matches.getUnmatchedSourceEntries()) {
62 writeMemberMatch(out, entry, null); 65 writeMemberMatch(out, entry, null);
63 } 66 }
64 for (T entry : matches.getUnmatchedDestEntries()) { 67 for (T entry : matches.getUnmatchedDestEntries()) {
65 writeMemberMatch(out, null, entry); 68 writeMemberMatch(out, null, entry);
66 } 69 }
67 for (T entry : matches.getUnmatchableSourceEntries()) { 70 for (T entry : matches.getUnmatchableSourceEntries()) {
68 writeUnmatchableEntry(out, entry); 71 writeUnmatchableEntry(out, entry);
69 } 72 }
70 } 73 }
71 } 74 }
72 75
73 private static <T extends Entry> void writeMemberMatch(OutputStreamWriter out, T source, T dest) 76 private static <T extends Entry> void writeMemberMatch(OutputStreamWriter out, T source, T dest)
74 throws IOException { 77 throws IOException {
75 if (source != null) { 78 if (source != null) {
76 writeEntry(out, source); 79 writeEntry(out, source);
77 } 80 }
78 out.write(":"); 81 out.write(":");
79 if (dest != null) { 82 if (dest != null) {
80 writeEntry(out, dest); 83 writeEntry(out, dest);
81 } 84 }
82 out.write("\n"); 85 out.write("\n");
83 } 86 }
84 87
85 private static <T extends Entry> void writeUnmatchableEntry(OutputStreamWriter out, T entry) 88 private static <T extends Entry> void writeUnmatchableEntry(OutputStreamWriter out, T entry)
86 throws IOException { 89 throws IOException {
87 out.write("!"); 90 out.write("!");
88 writeEntry(out, entry); 91 writeEntry(out, entry);
89 out.write("\n"); 92 out.write("\n");
90 } 93 }
91 94
92 private static <T extends Entry> void writeEntry(OutputStreamWriter out, T entry) 95 private static <T extends Entry> void writeEntry(OutputStreamWriter out, T entry)
93 throws IOException { 96 throws IOException {
94 if (entry instanceof FieldEntry) { 97 if (entry instanceof FieldEntry) {
95 writeField(out, (FieldEntry) entry); 98 writeField(out, (FieldEntry) entry);
96 } else if (entry instanceof BehaviorEntry) { 99 } else if (entry instanceof BehaviorEntry) {
97 writeBehavior(out, (BehaviorEntry) entry); 100 writeBehavior(out, (BehaviorEntry) entry);
98 } 101 }
99 } 102 }
100 103
101 private static void writeField(OutputStreamWriter out, FieldEntry fieldEntry) 104 private static void writeField(OutputStreamWriter out, FieldEntry fieldEntry)
102 throws IOException { 105 throws IOException {
103 out.write(fieldEntry.getClassName()); 106 out.write(fieldEntry.getClassName());
104 out.write(" "); 107 out.write(" ");
105 out.write(fieldEntry.getName()); 108 out.write(fieldEntry.getName());
106 out.write(" "); 109 out.write(" ");
107 out.write(fieldEntry.getType().toString()); 110 out.write(fieldEntry.getType().toString());
108 } 111 }
109 112
110 private static void writeBehavior(OutputStreamWriter out, BehaviorEntry behaviorEntry) 113 private static void writeBehavior(OutputStreamWriter out, BehaviorEntry behaviorEntry)
111 throws IOException { 114 throws IOException {
112 out.write(behaviorEntry.getClassName()); 115 out.write(behaviorEntry.getClassName());
113 out.write(" "); 116 out.write(" ");
114 out.write(behaviorEntry.getName()); 117 out.write(behaviorEntry.getName());
115 out.write(" "); 118 out.write(" ");
116 if (behaviorEntry.getSignature() != null) { 119 if (behaviorEntry.getSignature() != null) {
117 out.write(behaviorEntry.getSignature().toString()); 120 out.write(behaviorEntry.getSignature().toString());
118 } 121 }
119 } 122 }
120} 123}
diff --git a/src/main/java/cuchaz/enigma/convert/MemberMatches.java b/src/main/java/cuchaz/enigma/convert/MemberMatches.java
index 51cee859..bd743115 100644
--- a/src/main/java/cuchaz/enigma/convert/MemberMatches.java
+++ b/src/main/java/cuchaz/enigma/convert/MemberMatches.java
@@ -8,6 +8,7 @@
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.*;
@@ -18,165 +19,161 @@ import cuchaz.enigma.mapping.Entry;
18import java.util.Collection; 19import java.util.Collection;
19import java.util.Set; 20import java.util.Set;
20 21
21
22public class MemberMatches<T extends Entry> { 22public class MemberMatches<T extends Entry> {
23 23
24 private BiMap<T, T> matches; 24 private BiMap<T, T> matches;
25 private Multimap<ClassEntry, T> matchedSourceEntries; 25 private Multimap<ClassEntry, T> matchedSourceEntries;
26 private Multimap<ClassEntry, T> unmatchedSourceEntries; 26 private Multimap<ClassEntry, T> unmatchedSourceEntries;
27 private Multimap<ClassEntry, T> unmatchedDestEntries; 27 private Multimap<ClassEntry, T> unmatchedDestEntries;
28 private Multimap<ClassEntry, T> unmatchableSourceEntries; 28 private Multimap<ClassEntry, T> unmatchableSourceEntries;
29 29
30 public MemberMatches() { 30 public MemberMatches() {
31 matches = HashBiMap.create(); 31 matches = HashBiMap.create();
32 matchedSourceEntries = HashMultimap.create(); 32 matchedSourceEntries = HashMultimap.create();
33 unmatchedSourceEntries = HashMultimap.create(); 33 unmatchedSourceEntries = HashMultimap.create();
34 unmatchedDestEntries = HashMultimap.create(); 34 unmatchedDestEntries = HashMultimap.create();
35 unmatchableSourceEntries = HashMultimap.create(); 35 unmatchableSourceEntries = HashMultimap.create();
36 } 36 }
37 37
38 public void addMatch(T srcEntry, T destEntry) { 38 public void addMatch(T srcEntry, T destEntry) {
39 boolean wasAdded = matches.put(srcEntry, destEntry) == null; 39 boolean wasAdded = matches.put(srcEntry, destEntry) == null;
40 assert (wasAdded); 40 assert (wasAdded);
41 wasAdded = matchedSourceEntries.put(srcEntry.getClassEntry(), srcEntry); 41 wasAdded = matchedSourceEntries.put(srcEntry.getClassEntry(), srcEntry);
42 assert (wasAdded); 42 assert (wasAdded);
43 } 43 }
44 44
45 public void addUnmatchedSourceEntry(T sourceEntry) { 45 public void addUnmatchedSourceEntry(T sourceEntry) {
46 boolean wasAdded = unmatchedSourceEntries.put(sourceEntry.getClassEntry(), sourceEntry); 46 boolean wasAdded = unmatchedSourceEntries.put(sourceEntry.getClassEntry(), sourceEntry);
47 assert (wasAdded); 47 assert (wasAdded);
48 } 48 }
49 49
50 public void addUnmatchedSourceEntries(Iterable<T> sourceEntries) { 50 public void addUnmatchedSourceEntries(Iterable<T> sourceEntries) {
51 for (T sourceEntry : sourceEntries) { 51 for (T sourceEntry : sourceEntries) {
52 addUnmatchedSourceEntry(sourceEntry); 52 addUnmatchedSourceEntry(sourceEntry);
53 } 53 }
54 } 54 }
55 55
56 public void addUnmatchedDestEntry(T destEntry) { 56 public void addUnmatchedDestEntry(T destEntry) {
57 if (destEntry.getName().equals("<clinit>") || destEntry.getName().equals("<init>")) 57 if (destEntry.getName().equals("<clinit>") || destEntry.getName().equals("<init>"))
58 return; 58 return;
59 boolean wasAdded = unmatchedDestEntries.put(destEntry.getClassEntry(), destEntry); 59 boolean wasAdded = unmatchedDestEntries.put(destEntry.getClassEntry(), destEntry);
60 assert (wasAdded); 60 assert (wasAdded);
61 } 61 }
62 62
63 public void addUnmatchedDestEntries(Iterable<T> destEntriesntries) { 63 public void addUnmatchedDestEntries(Iterable<T> destEntriesntries) {
64 for (T entry : destEntriesntries) { 64 for (T entry : destEntriesntries) {
65 addUnmatchedDestEntry(entry); 65 addUnmatchedDestEntry(entry);
66 } 66 }
67 } 67 }
68 68
69 public void addUnmatchableSourceEntry(T sourceEntry) { 69 public void addUnmatchableSourceEntry(T sourceEntry) {
70 boolean wasAdded = unmatchableSourceEntries.put(sourceEntry.getClassEntry(), sourceEntry); 70 boolean wasAdded = unmatchableSourceEntries.put(sourceEntry.getClassEntry(), sourceEntry);
71 assert (wasAdded); 71 assert (wasAdded);
72 } 72 }
73 73
74 public Set<ClassEntry> getSourceClassesWithUnmatchedEntries() { 74 public Set<ClassEntry> getSourceClassesWithUnmatchedEntries() {
75 return unmatchedSourceEntries.keySet(); 75 return unmatchedSourceEntries.keySet();
76 } 76 }
77 77
78 public Collection<ClassEntry> getSourceClassesWithoutUnmatchedEntries() { 78 public Collection<ClassEntry> getSourceClassesWithoutUnmatchedEntries() {
79 Set<ClassEntry> out = Sets.newHashSet(); 79 Set<ClassEntry> out = Sets.newHashSet();
80 out.addAll(matchedSourceEntries.keySet()); 80 out.addAll(matchedSourceEntries.keySet());
81 out.removeAll(unmatchedSourceEntries.keySet()); 81 out.removeAll(unmatchedSourceEntries.keySet());
82 return out; 82 return out;
83 } 83 }
84 84
85 public Collection<T> getUnmatchedSourceEntries() { 85 public Collection<T> getUnmatchedSourceEntries() {
86 return unmatchedSourceEntries.values(); 86 return unmatchedSourceEntries.values();
87 } 87 }
88 88
89 public Collection<T> getUnmatchedSourceEntries(ClassEntry sourceClass) { 89 public Collection<T> getUnmatchedSourceEntries(ClassEntry sourceClass) {
90 return unmatchedSourceEntries.get(sourceClass); 90 return unmatchedSourceEntries.get(sourceClass);
91 } 91 }
92 92
93 public Collection<T> getUnmatchedDestEntries() { 93 public Collection<T> getUnmatchedDestEntries() {
94 return unmatchedDestEntries.values(); 94 return unmatchedDestEntries.values();
95 } 95 }
96 96
97 public Collection<T> getUnmatchedDestEntries(ClassEntry destClass) { 97 public Collection<T> getUnmatchedDestEntries(ClassEntry destClass) {
98 return unmatchedDestEntries.get(destClass); 98 return unmatchedDestEntries.get(destClass);
99 } 99 }
100 100
101 public Collection<T> getUnmatchableSourceEntries() { 101 public Collection<T> getUnmatchableSourceEntries() {
102 return unmatchableSourceEntries.values(); 102 return unmatchableSourceEntries.values();
103 } 103 }
104 104
105 public boolean hasSource(T sourceEntry) { 105 public boolean hasSource(T sourceEntry) {
106 return matches.containsKey(sourceEntry) || unmatchedSourceEntries.containsValue(sourceEntry); 106 return matches.containsKey(sourceEntry) || unmatchedSourceEntries.containsValue(sourceEntry);
107 } 107 }
108 108
109 public boolean hasDest(T destEntry) { 109 public boolean hasDest(T destEntry) {
110 return matches.containsValue(destEntry) || unmatchedDestEntries.containsValue(destEntry); 110 return matches.containsValue(destEntry) || unmatchedDestEntries.containsValue(destEntry);
111 } 111 }
112 112
113 public BiMap<T, T> matches() { 113 public BiMap<T, T> matches() {
114 return matches; 114 return matches;
115 } 115 }
116 116
117 public boolean isMatchedSourceEntry(T sourceEntry) { 117 public boolean isMatchedSourceEntry(T sourceEntry) {
118 return matches.containsKey(sourceEntry); 118 return matches.containsKey(sourceEntry);
119 } 119 }
120 120
121 public boolean isMatchedDestEntry(T destEntry) { 121 public boolean isMatchedDestEntry(T destEntry) {
122 return matches.containsValue(destEntry); 122 return matches.containsValue(destEntry);
123 } 123 }
124 124
125 public boolean isUnmatchableSourceEntry(T sourceEntry) { 125 public boolean isUnmatchableSourceEntry(T sourceEntry) {
126 return unmatchableSourceEntries.containsEntry(sourceEntry.getClassEntry(), sourceEntry); 126 return unmatchableSourceEntries.containsEntry(sourceEntry.getClassEntry(), sourceEntry);
127 } 127 }
128 public void makeMatch(T sourceEntry, T destEntry) { 128
129 makeMatch(sourceEntry, destEntry, null, null); 129 public void makeMatch(T sourceEntry, T destEntry) {
130 } 130 makeMatch(sourceEntry, destEntry, null, null);
131 131 }
132 public void makeMatch(T sourceEntry, T destEntry, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { 132
133 if (sourceDeobfuscator != null && destDeobfuscator != null) 133 public void makeMatch(T sourceEntry, T destEntry, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) {
134 { 134 if (sourceDeobfuscator != null && destDeobfuscator != null) {
135 makeMatch(sourceEntry, destEntry); 135 makeMatch(sourceEntry, destEntry);
136 sourceEntry = (T) sourceEntry.cloneToNewClass(sourceDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(sourceEntry, true)); 136 sourceEntry = (T) sourceEntry.cloneToNewClass(sourceDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(sourceEntry, true));
137 destEntry = (T) destEntry.cloneToNewClass(destDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(destEntry, true)); 137 destEntry = (T) destEntry.cloneToNewClass(destDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(destEntry, true));
138 } 138 }
139 boolean wasRemoved = unmatchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); 139 boolean wasRemoved = unmatchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry);
140 assert (wasRemoved); 140 assert (wasRemoved);
141 wasRemoved = unmatchedDestEntries.remove(destEntry.getClassEntry(), destEntry); 141 wasRemoved = unmatchedDestEntries.remove(destEntry.getClassEntry(), destEntry);
142 assert (wasRemoved); 142 assert (wasRemoved);
143 addMatch(sourceEntry, destEntry); 143 addMatch(sourceEntry, destEntry);
144 } 144 }
145 145
146 public boolean isMatched(T sourceEntry, T destEntry) { 146 public boolean isMatched(T sourceEntry, T destEntry) {
147 T match = matches.get(sourceEntry); 147 T match = matches.get(sourceEntry);
148 return match != null && match.equals(destEntry); 148 return match != null && match.equals(destEntry);
149 } 149 }
150 150
151 public void unmakeMatch(T sourceEntry, T destEntry, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) 151 public void unmakeMatch(T sourceEntry, T destEntry, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) {
152 { 152 if (sourceDeobfuscator != null && destDeobfuscator != null) {
153 if (sourceDeobfuscator != null && destDeobfuscator != null) 153 unmakeMatch(sourceEntry, destEntry, null, null);
154 { 154 sourceEntry = (T) sourceEntry.cloneToNewClass(
155 unmakeMatch(sourceEntry, destEntry, null, null); 155 sourceDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(sourceEntry, true));
156 sourceEntry = (T) sourceEntry.cloneToNewClass( 156 destEntry = (T) destEntry.cloneToNewClass(
157 sourceDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(sourceEntry, true)); 157 destDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(destEntry, true));
158 destEntry = (T) destEntry.cloneToNewClass( 158 }
159 destDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(destEntry, true)); 159
160 } 160 boolean wasRemoved = matches.remove(sourceEntry) != null;
161 161 assert (wasRemoved);
162 boolean wasRemoved = matches.remove(sourceEntry) != null; 162 wasRemoved = matchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry);
163 assert (wasRemoved); 163 assert (wasRemoved);
164 wasRemoved = matchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); 164 addUnmatchedSourceEntry(sourceEntry);
165 assert (wasRemoved); 165 addUnmatchedDestEntry(destEntry);
166 addUnmatchedSourceEntry(sourceEntry); 166 }
167 addUnmatchedDestEntry(destEntry); 167
168 } 168 public void makeSourceUnmatchable(T sourceEntry, Deobfuscator sourceDeobfuscator) {
169 169 if (sourceDeobfuscator != null) {
170 public void makeSourceUnmatchable(T sourceEntry, Deobfuscator sourceDeobfuscator) { 170 makeSourceUnmatchable(sourceEntry, null);
171 if (sourceDeobfuscator != null) 171 sourceEntry = (T) sourceEntry.cloneToNewClass(
172 { 172 sourceDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(sourceEntry, true));
173 makeSourceUnmatchable(sourceEntry, null); 173 }
174 sourceEntry = (T) sourceEntry.cloneToNewClass( 174 assert (!isMatchedSourceEntry(sourceEntry));
175 sourceDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(sourceEntry, true)); 175 boolean wasRemoved = unmatchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry);
176 } 176 assert (wasRemoved);
177 assert (!isMatchedSourceEntry(sourceEntry)); 177 addUnmatchableSourceEntry(sourceEntry);
178 boolean wasRemoved = unmatchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); 178 }
179 assert (wasRemoved);
180 addUnmatchableSourceEntry(sourceEntry);
181 }
182} 179}
diff --git a/src/main/java/cuchaz/enigma/gui/BrowserCaret.java b/src/main/java/cuchaz/enigma/gui/BrowserCaret.java
index bcdff51e..af105dbd 100644
--- a/src/main/java/cuchaz/enigma/gui/BrowserCaret.java
+++ b/src/main/java/cuchaz/enigma/gui/BrowserCaret.java
@@ -8,20 +8,21 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.gui; 12package cuchaz.enigma.gui;
12 13
13import javax.swing.text.DefaultCaret; 14import javax.swing.text.DefaultCaret;
14 15
15public class BrowserCaret extends DefaultCaret { 16public class BrowserCaret extends DefaultCaret {
16 17
17 @Override 18 @Override
18 public boolean isSelectionVisible() { 19 public boolean isSelectionVisible() {
19 return true; 20 return true;
20 } 21 }
21 22
22 @Override 23 @Override
23 public boolean isVisible() { 24 public boolean isVisible() {
24 return true; 25 return true;
25 } 26 }
26 27
27} 28}
diff --git a/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java b/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java
index dcbe1c5b..05501f40 100644
--- a/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java
+++ b/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.gui; 12package cuchaz.enigma.gui;
12 13
13import com.google.common.collect.BiMap; 14import com.google.common.collect.BiMap;
@@ -31,508 +32,505 @@ import java.util.Collection;
31import java.util.List; 32import java.util.List;
32import java.util.Map; 33import java.util.Map;
33 34
34
35public class ClassMatchingGui { 35public class ClassMatchingGui {
36 36
37 private enum SourceType { 37 // controls
38 Matched { 38 private JFrame frame;
39 @Override 39 private ClassSelector sourceClasses;
40 public Collection<ClassEntry> getSourceClasses(ClassMatches matches) { 40 private ClassSelector destClasses;
41 return matches.getUniqueMatches().keySet(); 41 private CodeReader sourceReader;
42 } 42 private CodeReader destReader;
43 }, 43 private JLabel sourceClassLabel;
44 Unmatched { 44 private JLabel destClassLabel;
45 @Override 45 private JButton matchButton;
46 public Collection<ClassEntry> getSourceClasses(ClassMatches matches) { 46 private Map<SourceType, JRadioButton> sourceTypeButtons;
47 return matches.getUnmatchedSourceClasses(); 47 private JCheckBox advanceCheck;
48 } 48 private JCheckBox top10Matches;
49 }, 49 private ClassMatches classMatches;
50 Ambiguous { 50 private Deobfuscator sourceDeobfuscator;
51 @Override 51 private Deobfuscator destDeobfuscator;
52 public Collection<ClassEntry> getSourceClasses(ClassMatches matches) { 52 private ClassEntry sourceClass;
53 return matches.getAmbiguouslyMatchedSourceClasses(); 53 private ClassEntry destClass;
54 } 54 private SourceType sourceType;
55 }; 55 private SaveListener saveListener;
56 56 public ClassMatchingGui(ClassMatches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) {
57 public JRadioButton newRadio(ActionListener listener, ButtonGroup group) { 57
58 JRadioButton button = new JRadioButton(name(), this == getDefault()); 58 classMatches = matches;
59 button.setActionCommand(name()); 59 this.sourceDeobfuscator = sourceDeobfuscator;
60 button.addActionListener(listener); 60 this.destDeobfuscator = destDeobfuscator;
61 group.add(button); 61
62 return button; 62 // init frame
63 } 63 frame = new JFrame(Constants.NAME + " - Class Matcher");
64 64 final Container pane = frame.getContentPane();
65 public abstract Collection<ClassEntry> getSourceClasses(ClassMatches matches); 65 pane.setLayout(new BorderLayout());
66 66
67 public static SourceType getDefault() { 67 // init source side
68 return values()[0]; 68 JPanel sourcePanel = new JPanel();
69 } 69 sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.PAGE_AXIS));
70 } 70 sourcePanel.setPreferredSize(new Dimension(200, 0));
71 71 pane.add(sourcePanel, BorderLayout.WEST);
72 public interface SaveListener { 72 sourcePanel.add(new JLabel("Source Classes"));
73 void save(ClassMatches matches); 73
74 } 74 // init source type radios
75 75 JPanel sourceTypePanel = new JPanel();
76 // controls 76 sourcePanel.add(sourceTypePanel);
77 private JFrame frame; 77 sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS));
78 private ClassSelector sourceClasses; 78 ActionListener sourceTypeListener = event -> setSourceType(SourceType.valueOf(event.getActionCommand()));
79 private ClassSelector destClasses; 79 ButtonGroup sourceTypeButtons = new ButtonGroup();
80 private CodeReader sourceReader; 80 this.sourceTypeButtons = Maps.newHashMap();
81 private CodeReader destReader; 81 for (SourceType sourceType : SourceType.values()) {
82 private JLabel sourceClassLabel; 82 JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons);
83 private JLabel destClassLabel; 83 this.sourceTypeButtons.put(sourceType, button);
84 private JButton matchButton; 84 sourceTypePanel.add(button);
85 private Map<SourceType, JRadioButton> sourceTypeButtons; 85 }
86 private JCheckBox advanceCheck; 86
87 private JCheckBox top10Matches; 87 sourceClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false);
88 88 sourceClasses.setSelectionListener(this::setSourceClass);
89 private ClassMatches classMatches; 89 JScrollPane sourceScroller = new JScrollPane(sourceClasses);
90 private Deobfuscator sourceDeobfuscator; 90 sourcePanel.add(sourceScroller);
91 private Deobfuscator destDeobfuscator; 91
92 private ClassEntry sourceClass; 92 // init dest side
93 private ClassEntry destClass; 93 JPanel destPanel = new JPanel();
94 private SourceType sourceType; 94 destPanel.setLayout(new BoxLayout(destPanel, BoxLayout.PAGE_AXIS));
95 private SaveListener saveListener; 95 destPanel.setPreferredSize(new Dimension(200, 0));
96 96 pane.add(destPanel, BorderLayout.WEST);
97 public ClassMatchingGui(ClassMatches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { 97 destPanel.add(new JLabel("Destination Classes"));
98 98
99 classMatches = matches; 99 top10Matches = new JCheckBox("Show only top 10 matches");
100 this.sourceDeobfuscator = sourceDeobfuscator; 100 destPanel.add(top10Matches);
101 this.destDeobfuscator = destDeobfuscator; 101 top10Matches.addActionListener(event -> toggleTop10Matches());
102 102
103 // init frame 103 destClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false);
104 frame = new JFrame(Constants.NAME + " - Class Matcher"); 104 destClasses.setSelectionListener(this::setDestClass);
105 final Container pane = frame.getContentPane(); 105 JScrollPane destScroller = new JScrollPane(destClasses);
106 pane.setLayout(new BorderLayout()); 106 destPanel.add(destScroller);
107 107
108 // init source side 108 JButton autoMatchButton = new JButton("AutoMatch");
109 JPanel sourcePanel = new JPanel(); 109 autoMatchButton.addActionListener(event -> autoMatch());
110 sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.PAGE_AXIS)); 110 destPanel.add(autoMatchButton);
111 sourcePanel.setPreferredSize(new Dimension(200, 0)); 111
112 pane.add(sourcePanel, BorderLayout.WEST); 112 // init source panels
113 sourcePanel.add(new JLabel("Source Classes")); 113 DefaultSyntaxKit.initKit();
114 114 sourceReader = new CodeReader();
115 // init source type radios 115 destReader = new CodeReader();
116 JPanel sourceTypePanel = new JPanel(); 116
117 sourcePanel.add(sourceTypePanel); 117 // init all the splits
118 sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS)); 118 JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane(
119 ActionListener sourceTypeListener = event -> setSourceType(SourceType.valueOf(event.getActionCommand())); 119 sourceReader));
120 ButtonGroup sourceTypeButtons = new ButtonGroup(); 120 splitLeft.setResizeWeight(0); // let the right side take all the slack
121 this.sourceTypeButtons = Maps.newHashMap(); 121 JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(destReader), destPanel);
122 for (SourceType sourceType : SourceType.values()) { 122 splitRight.setResizeWeight(1); // let the left side take all the slack
123 JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons); 123 JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, splitRight);
124 this.sourceTypeButtons.put(sourceType, button); 124 splitCenter.setResizeWeight(0.5); // resize 50:50
125 sourceTypePanel.add(button); 125 pane.add(splitCenter, BorderLayout.CENTER);
126 } 126 splitCenter.resetToPreferredSizes();
127 127
128 sourceClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false); 128 // init bottom panel
129 sourceClasses.setSelectionListener(this::setSourceClass); 129 JPanel bottomPanel = new JPanel();
130 JScrollPane sourceScroller = new JScrollPane(sourceClasses); 130 bottomPanel.setLayout(new FlowLayout());
131 sourcePanel.add(sourceScroller); 131
132 132 sourceClassLabel = new JLabel();
133 // init dest side 133 sourceClassLabel.setHorizontalAlignment(SwingConstants.RIGHT);
134 JPanel destPanel = new JPanel(); 134 destClassLabel = new JLabel();
135 destPanel.setLayout(new BoxLayout(destPanel, BoxLayout.PAGE_AXIS)); 135 destClassLabel.setHorizontalAlignment(SwingConstants.LEFT);
136 destPanel.setPreferredSize(new Dimension(200, 0)); 136
137 pane.add(destPanel, BorderLayout.WEST); 137 matchButton = new JButton();
138 destPanel.add(new JLabel("Destination Classes")); 138
139 139 advanceCheck = new JCheckBox("Advance to next likely match");
140 top10Matches = new JCheckBox("Show only top 10 matches"); 140 advanceCheck.addActionListener(event -> {
141 destPanel.add(top10Matches); 141 if (advanceCheck.isSelected()) {
142 top10Matches.addActionListener(event -> toggleTop10Matches()); 142 advance();
143 143 }
144 destClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false); 144 });
145 destClasses.setSelectionListener(this::setDestClass); 145
146 JScrollPane destScroller = new JScrollPane(destClasses); 146 bottomPanel.add(sourceClassLabel);
147 destPanel.add(destScroller); 147 bottomPanel.add(matchButton);
148 148 bottomPanel.add(destClassLabel);
149 JButton autoMatchButton = new JButton("AutoMatch"); 149 bottomPanel.add(advanceCheck);
150 autoMatchButton.addActionListener(event -> autoMatch()); 150 pane.add(bottomPanel, BorderLayout.SOUTH);
151 destPanel.add(autoMatchButton); 151
152 152 // show the frame
153 // init source panels 153 pane.doLayout();
154 DefaultSyntaxKit.initKit(); 154 frame.setSize(1024, 576);
155 sourceReader = new CodeReader(); 155 frame.setMinimumSize(new Dimension(640, 480));
156 destReader = new CodeReader(); 156 frame.setVisible(true);
157 157 frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
158 // init all the splits 158
159 JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane( 159 // init state
160 sourceReader)); 160 updateDestMappings();
161 splitLeft.setResizeWeight(0); // let the right side take all the slack 161 setSourceType(SourceType.getDefault());
162 JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(destReader), destPanel); 162 updateMatchButton();
163 splitRight.setResizeWeight(1); // let the left side take all the slack 163 saveListener = null;
164 JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, splitRight); 164 }
165 splitCenter.setResizeWeight(0.5); // resize 50:50 165
166 pane.add(splitCenter, BorderLayout.CENTER); 166 public void setSaveListener(SaveListener val) {
167 splitCenter.resetToPreferredSizes(); 167 saveListener = val;
168 168 }
169 // init bottom panel 169
170 JPanel bottomPanel = new JPanel(); 170 private void updateDestMappings() {
171 bottomPanel.setLayout(new FlowLayout()); 171 try {
172 172 Mappings newMappings = MappingsConverter.newMappings(classMatches,
173 sourceClassLabel = new JLabel(); 173 sourceDeobfuscator.getMappings(), sourceDeobfuscator, destDeobfuscator
174 sourceClassLabel.setHorizontalAlignment(SwingConstants.RIGHT); 174 );
175 destClassLabel = new JLabel(); 175
176 destClassLabel.setHorizontalAlignment(SwingConstants.LEFT); 176 // look for dropped mappings
177 177 MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex());
178 matchButton = new JButton(); 178 checker.dropBrokenMappings(newMappings);
179 179
180 advanceCheck = new JCheckBox("Advance to next likely match"); 180 // count them
181 advanceCheck.addActionListener(event -> { 181 int numDroppedFields = checker.getDroppedFieldMappings().size();
182 if (advanceCheck.isSelected()) { 182 int numDroppedMethods = checker.getDroppedMethodMappings().size();
183 advance(); 183 System.out.println(String.format(
184 } 184 "%d mappings from matched classes don't match the dest jar:\n\t%5d fields\n\t%5d methods",
185 }); 185 numDroppedFields + numDroppedMethods,
186 186 numDroppedFields,
187 bottomPanel.add(sourceClassLabel); 187 numDroppedMethods
188 bottomPanel.add(matchButton); 188 ));
189 bottomPanel.add(destClassLabel); 189
190 bottomPanel.add(advanceCheck); 190 destDeobfuscator.setMappings(newMappings);
191 pane.add(bottomPanel, BorderLayout.SOUTH); 191 } catch (MappingConflict ex) {
192 192 System.out.println(ex.getMessage());
193 // show the frame 193 ex.printStackTrace();
194 pane.doLayout(); 194 return;
195 frame.setSize(1024, 576); 195 }
196 frame.setMinimumSize(new Dimension(640, 480)); 196 }
197 frame.setVisible(true); 197
198 frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 198 protected void setSourceType(SourceType val) {
199 199
200 // init state 200 // show the source classes
201 updateDestMappings(); 201 sourceType = val;
202 setSourceType(SourceType.getDefault()); 202 sourceClasses.setClasses(deobfuscateClasses(sourceType.getSourceClasses(classMatches), sourceDeobfuscator));
203 updateMatchButton(); 203
204 saveListener = null; 204 // update counts
205 } 205 for (SourceType sourceType : SourceType.values()) {
206 206 sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)",
207 public void setSaveListener(SaveListener val) { 207 sourceType.name(),
208 saveListener = val; 208 sourceType.getSourceClasses(classMatches).size()
209 } 209 ));
210 210 }
211 private void updateDestMappings() { 211 }
212 try { 212
213 Mappings newMappings = MappingsConverter.newMappings(classMatches, 213 private Collection<ClassEntry> deobfuscateClasses(Collection<ClassEntry> in, Deobfuscator deobfuscator) {
214 sourceDeobfuscator.getMappings(), sourceDeobfuscator, destDeobfuscator 214 List<ClassEntry> out = Lists.newArrayList();
215 ); 215 for (ClassEntry entry : in) {
216 216
217 // look for dropped mappings 217 ClassEntry deobf = deobfuscator.deobfuscateEntry(entry);
218 MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); 218
219 checker.dropBrokenMappings(newMappings); 219 // make sure we preserve any scores
220 220 if (entry instanceof ScoredClassEntry) {
221 // count them 221 deobf = new ScoredClassEntry(deobf, ((ScoredClassEntry) entry).getScore());
222 int numDroppedFields = checker.getDroppedFieldMappings().size(); 222 }
223 int numDroppedMethods = checker.getDroppedMethodMappings().size(); 223
224 System.out.println(String.format( 224 out.add(deobf);
225 "%d mappings from matched classes don't match the dest jar:\n\t%5d fields\n\t%5d methods", 225 }
226 numDroppedFields + numDroppedMethods, 226 return out;
227 numDroppedFields, 227 }
228 numDroppedMethods 228
229 )); 229 protected void setSourceClass(ClassEntry classEntry) {
230 230
231 destDeobfuscator.setMappings(newMappings); 231 Runnable onGetDestClasses = null;
232 } catch (MappingConflict ex) { 232 if (advanceCheck.isSelected()) {
233 System.out.println(ex.getMessage()); 233 onGetDestClasses = this::pickBestDestClass;
234 ex.printStackTrace(); 234 }
235 return; 235
236 } 236 setSourceClass(classEntry, onGetDestClasses);
237 } 237 }
238 238
239 protected void setSourceType(SourceType val) { 239 protected void setSourceClass(ClassEntry classEntry, final Runnable onGetDestClasses) {
240 240
241 // show the source classes 241 // update the current source class
242 sourceType = val; 242 sourceClass = classEntry;
243 sourceClasses.setClasses(deobfuscateClasses(sourceType.getSourceClasses(classMatches), sourceDeobfuscator)); 243 sourceClassLabel.setText(sourceClass != null ? sourceClass.getName() : "");
244 244
245 // update counts 245 if (sourceClass != null) {
246 for (SourceType sourceType : SourceType.values()) { 246
247 sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)", 247 // show the dest class(es)
248 sourceType.name(), 248 ClassMatch match = classMatches.getMatchBySource(sourceDeobfuscator.obfuscateEntry(sourceClass));
249 sourceType.getSourceClasses(classMatches).size() 249 assert (match != null);
250 )); 250 if (match.destClasses.isEmpty()) {
251 } 251
252 } 252 destClasses.setClasses(null);
253 253
254 private Collection<ClassEntry> deobfuscateClasses(Collection<ClassEntry> in, Deobfuscator deobfuscator) { 254 // run in a separate thread to keep ui responsive
255 List<ClassEntry> out = Lists.newArrayList(); 255 new Thread(() ->
256 for (ClassEntry entry : in) { 256 {
257 257 destClasses.setClasses(deobfuscateClasses(getLikelyMatches(sourceClass), destDeobfuscator));
258 ClassEntry deobf = deobfuscator.deobfuscateEntry(entry); 258 destClasses.expandAll();
259 259
260 // make sure we preserve any scores 260 if (onGetDestClasses != null) {
261 if (entry instanceof ScoredClassEntry) { 261 onGetDestClasses.run();
262 deobf = new ScoredClassEntry(deobf, ((ScoredClassEntry) entry).getScore()); 262 }
263 } 263 }).start();
264 264
265 out.add(deobf); 265 } else {
266 } 266
267 return out; 267 destClasses.setClasses(deobfuscateClasses(match.destClasses, destDeobfuscator));
268 } 268 destClasses.expandAll();
269 269
270 protected void setSourceClass(ClassEntry classEntry) { 270 if (onGetDestClasses != null) {
271 271 onGetDestClasses.run();
272 Runnable onGetDestClasses = null; 272 }
273 if (advanceCheck.isSelected()) { 273 }
274 onGetDestClasses = this::pickBestDestClass; 274 }
275 } 275
276 276 setDestClass(null);
277 setSourceClass(classEntry, onGetDestClasses); 277 sourceReader.decompileClass(
278 } 278 sourceClass, sourceDeobfuscator, () -> sourceReader.navigateToClassDeclaration(sourceClass));
279 279
280 protected void setSourceClass(ClassEntry classEntry, final Runnable onGetDestClasses) { 280 updateMatchButton();
281 281 }
282 // update the current source class 282
283 sourceClass = classEntry; 283 private Collection<ClassEntry> getLikelyMatches(ClassEntry sourceClass) {
284 sourceClassLabel.setText(sourceClass != null ? sourceClass.getName() : ""); 284
285 285 ClassEntry obfSourceClass = sourceDeobfuscator.obfuscateEntry(sourceClass);
286 if (sourceClass != null) { 286
287 287 // set up identifiers
288 // show the dest class(es) 288 ClassNamer namer = new ClassNamer(classMatches.getUniqueMatches());
289 ClassMatch match = classMatches.getMatchBySource(sourceDeobfuscator.obfuscateEntry(sourceClass)); 289 ClassIdentifier sourceIdentifier = new ClassIdentifier(
290 assert (match != null); 290 sourceDeobfuscator.getJar(), sourceDeobfuscator.getJarIndex(),
291 if (match.destClasses.isEmpty()) { 291 namer.getSourceNamer(), true
292 292 );
293 destClasses.setClasses(null); 293 ClassIdentifier destIdentifier = new ClassIdentifier(
294 294 destDeobfuscator.getJar(), destDeobfuscator.getJarIndex(),
295 // run in a separate thread to keep ui responsive 295 namer.getDestNamer(), true
296 new Thread(() -> 296 );
297 {
298 destClasses.setClasses(deobfuscateClasses(getLikelyMatches(sourceClass), destDeobfuscator));
299 destClasses.expandAll();
300
301 if (onGetDestClasses != null) {
302 onGetDestClasses.run();
303 }
304 }).start();
305
306 } else {
307
308 destClasses.setClasses(deobfuscateClasses(match.destClasses, destDeobfuscator));
309 destClasses.expandAll();
310
311 if (onGetDestClasses != null) {
312 onGetDestClasses.run();
313 }
314 }
315 }
316
317 setDestClass(null);
318 sourceReader.decompileClass(
319 sourceClass, sourceDeobfuscator, () -> sourceReader.navigateToClassDeclaration(sourceClass));
320
321 updateMatchButton();
322 }
323
324 private Collection<ClassEntry> getLikelyMatches(ClassEntry sourceClass) {
325
326 ClassEntry obfSourceClass = sourceDeobfuscator.obfuscateEntry(sourceClass);
327
328 // set up identifiers
329 ClassNamer namer = new ClassNamer(classMatches.getUniqueMatches());
330 ClassIdentifier sourceIdentifier = new ClassIdentifier(
331 sourceDeobfuscator.getJar(), sourceDeobfuscator.getJarIndex(),
332 namer.getSourceNamer(), true
333 );
334 ClassIdentifier destIdentifier = new ClassIdentifier(
335 destDeobfuscator.getJar(), destDeobfuscator.getJarIndex(),
336 namer.getDestNamer(), true
337 );
338 297
339 try { 298 try {
340 299
341 // rank all the unmatched dest classes against the source class 300 // rank all the unmatched dest classes against the source class
342 ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass); 301 ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass);
343 List<ClassEntry> scoredDestClasses = Lists.newArrayList(); 302 List<ClassEntry> scoredDestClasses = Lists.newArrayList();
344 for (ClassEntry unmatchedDestClass : classMatches.getUnmatchedDestClasses()) { 303 for (ClassEntry unmatchedDestClass : classMatches.getUnmatchedDestClasses()) {
345 ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass); 304 ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass);
346 float score = 100.0f * (sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity)) 305 float score = 100.0f * (sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity))
347 / (sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore()); 306 / (sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore());
348 scoredDestClasses.add(new ScoredClassEntry(unmatchedDestClass, score)); 307 scoredDestClasses.add(new ScoredClassEntry(unmatchedDestClass, score));
349 } 308 }
350 309
351 if (top10Matches.isSelected() && scoredDestClasses.size() > 10) { 310 if (top10Matches.isSelected() && scoredDestClasses.size() > 10) {
352 scoredDestClasses.sort((a, b) -> 311 scoredDestClasses.sort((a, b) ->
353 { 312 {
354 ScoredClassEntry sa = (ScoredClassEntry) a; 313 ScoredClassEntry sa = (ScoredClassEntry) a;
355 ScoredClassEntry sb = (ScoredClassEntry) b; 314 ScoredClassEntry sb = (ScoredClassEntry) b;
356 return -Float.compare(sa.getScore(), sb.getScore()); 315 return -Float.compare(sa.getScore(), sb.getScore());
357 }); 316 });
358 scoredDestClasses = scoredDestClasses.subList(0, 10); 317 scoredDestClasses = scoredDestClasses.subList(0, 10);
359 } 318 }
360 319
361 return scoredDestClasses; 320 return scoredDestClasses;
362 321
363 } catch (ClassNotFoundException ex) { 322 } catch (ClassNotFoundException ex) {
364 throw new Error("Unable to find class " + ex.getMessage()); 323 throw new Error("Unable to find class " + ex.getMessage());
365 } 324 }
366 } 325 }
367 326
368 protected void setDestClass(ClassEntry classEntry) { 327 protected void setDestClass(ClassEntry classEntry) {
369 328
370 // update the current source class 329 // update the current source class
371 destClass = classEntry; 330 destClass = classEntry;
372 destClassLabel.setText(destClass != null ? destClass.getName() : ""); 331 destClassLabel.setText(destClass != null ? destClass.getName() : "");
373 332
374 destReader.decompileClass(destClass, destDeobfuscator, () -> destReader.navigateToClassDeclaration(destClass)); 333 destReader.decompileClass(destClass, destDeobfuscator, () -> destReader.navigateToClassDeclaration(destClass));
375 334
376 updateMatchButton(); 335 updateMatchButton();
377 } 336 }
378 337
379 private void updateMatchButton() { 338 private void updateMatchButton() {
380 339
381 ClassEntry obfSource = sourceDeobfuscator.obfuscateEntry(sourceClass); 340 ClassEntry obfSource = sourceDeobfuscator.obfuscateEntry(sourceClass);
382 ClassEntry obfDest = destDeobfuscator.obfuscateEntry(destClass); 341 ClassEntry obfDest = destDeobfuscator.obfuscateEntry(destClass);
383 342
384 BiMap<ClassEntry, ClassEntry> uniqueMatches = classMatches.getUniqueMatches(); 343 BiMap<ClassEntry, ClassEntry> uniqueMatches = classMatches.getUniqueMatches();
385 boolean twoSelected = sourceClass != null && destClass != null; 344 boolean twoSelected = sourceClass != null && destClass != null;
386 boolean isMatched = uniqueMatches.containsKey(obfSource) && uniqueMatches.containsValue(obfDest); 345 boolean isMatched = uniqueMatches.containsKey(obfSource) && uniqueMatches.containsValue(obfDest);
387 boolean canMatch = !uniqueMatches.containsKey(obfSource) && !uniqueMatches.containsValue(obfDest); 346 boolean canMatch = !uniqueMatches.containsKey(obfSource) && !uniqueMatches.containsValue(obfDest);
388 347
389 GuiTricks.deactivateButton(matchButton); 348 GuiTricks.deactivateButton(matchButton);
390 if (twoSelected) { 349 if (twoSelected) {
391 if (isMatched) { 350 if (isMatched) {
392 GuiTricks.activateButton(matchButton, "Unmatch", event -> onUnmatchClick()); 351 GuiTricks.activateButton(matchButton, "Unmatch", event -> onUnmatchClick());
393 } else if (canMatch) { 352 } else if (canMatch) {
394 GuiTricks.activateButton(matchButton, "Match", event -> onMatchClick()); 353 GuiTricks.activateButton(matchButton, "Match", event -> onMatchClick());
395 } 354 }
396 } 355 }
397 } 356 }
398 357
399 private void onMatchClick() { 358 private void onMatchClick() {
400 // precondition: source and dest classes are set correctly 359 // precondition: source and dest classes are set correctly
401 360
402 ClassEntry obfSource = sourceDeobfuscator.obfuscateEntry(sourceClass); 361 ClassEntry obfSource = sourceDeobfuscator.obfuscateEntry(sourceClass);
403 ClassEntry obfDest = destDeobfuscator.obfuscateEntry(destClass); 362 ClassEntry obfDest = destDeobfuscator.obfuscateEntry(destClass);
404 363
405 // remove the classes from their match 364 // remove the classes from their match
406 classMatches.removeSource(obfSource); 365 classMatches.removeSource(obfSource);
407 classMatches.removeDest(obfDest); 366 classMatches.removeDest(obfDest);
408 367
409 // add them as matched classes 368 // add them as matched classes
410 classMatches.add(new ClassMatch(obfSource, obfDest)); 369 classMatches.add(new ClassMatch(obfSource, obfDest));
411 370
412 ClassEntry nextClass = null; 371 ClassEntry nextClass = null;
413 if (advanceCheck.isSelected()) { 372 if (advanceCheck.isSelected()) {
414 nextClass = sourceClasses.getNextClass(sourceClass); 373 nextClass = sourceClasses.getNextClass(sourceClass);
415 } 374 }
416 375
417 save(); 376 save();
418 updateMatches(); 377 updateMatches();
419 378
420 if (nextClass != null) { 379 if (nextClass != null) {
421 advance(nextClass); 380 advance(nextClass);
422 } 381 }
423 } 382 }
424 383
425 private void onUnmatchClick() { 384 private void onUnmatchClick() {
426 // precondition: source and dest classes are set to a unique match 385 // precondition: source and dest classes are set to a unique match
427 386
428 ClassEntry obfSource = sourceDeobfuscator.obfuscateEntry(sourceClass); 387 ClassEntry obfSource = sourceDeobfuscator.obfuscateEntry(sourceClass);
429 388
430 // remove the source to break the match, then add the source back as unmatched 389 // remove the source to break the match, then add the source back as unmatched
431 classMatches.removeSource(obfSource); 390 classMatches.removeSource(obfSource);
432 classMatches.add(new ClassMatch(obfSource, null)); 391 classMatches.add(new ClassMatch(obfSource, null));
433 392
434 save(); 393 save();
435 updateMatches(); 394 updateMatches();
436 } 395 }
437 396
438 private void updateMatches() { 397 private void updateMatches() {
439 updateDestMappings(); 398 updateDestMappings();
440 setDestClass(null); 399 setDestClass(null);
441 destClasses.setClasses(null); 400 destClasses.setClasses(null);
442 updateMatchButton(); 401 updateMatchButton();
443 402
444 // remember where we were in the source tree 403 // remember where we were in the source tree
445 String packageName = sourceClasses.getSelectedPackage(); 404 String packageName = sourceClasses.getSelectedPackage();
446 405
447 setSourceType(sourceType); 406 setSourceType(sourceType);
448 407
449 sourceClasses.expandPackage(packageName); 408 sourceClasses.expandPackage(packageName);
450 } 409 }
451 410
452 private void save() { 411 private void save() {
453 if (saveListener != null) { 412 if (saveListener != null) {
454 saveListener.save(classMatches); 413 saveListener.save(classMatches);
455 } 414 }
456 } 415 }
457 416
458 private void autoMatch() { 417 private void autoMatch() {
459 418
460 System.out.println("Automatching..."); 419 System.out.println("Automatching...");
461 420
462 // compute a new matching 421 // compute a new matching
463 ClassMatching matching = MappingsConverter.computeMatching( 422 ClassMatching matching = MappingsConverter.computeMatching(
464 sourceDeobfuscator.getJar(), sourceDeobfuscator.getJarIndex(), 423 sourceDeobfuscator.getJar(), sourceDeobfuscator.getJarIndex(),
465 destDeobfuscator.getJar(), destDeobfuscator.getJarIndex(), 424 destDeobfuscator.getJar(), destDeobfuscator.getJarIndex(),
466 classMatches.getUniqueMatches() 425 classMatches.getUniqueMatches()
467 ); 426 );
468 ClassMatches newMatches = new ClassMatches(matching.matches()); 427 ClassMatches newMatches = new ClassMatches(matching.matches());
469 System.out.println(String.format("Automatch found %d new matches", 428 System.out.println(String.format("Automatch found %d new matches",
470 newMatches.getUniqueMatches().size() - classMatches.getUniqueMatches().size() 429 newMatches.getUniqueMatches().size() - classMatches.getUniqueMatches().size()
471 )); 430 ));
472 431
473 // update the current matches 432 // update the current matches
474 classMatches = newMatches; 433 classMatches = newMatches;
475 save(); 434 save();
476 updateMatches(); 435 updateMatches();
477 } 436 }
478 437
479 private void advance() { 438 private void advance() {
480 advance(null); 439 advance(null);
481 } 440 }
482 441
483 private void advance(ClassEntry sourceClass) { 442 private void advance(ClassEntry sourceClass) {
484 443
485 // make sure we have a source class 444 // make sure we have a source class
486 if (sourceClass == null) { 445 if (sourceClass == null) {
487 sourceClass = sourceClasses.getSelectedClass(); 446 sourceClass = sourceClasses.getSelectedClass();
488 if (sourceClass != null) { 447 if (sourceClass != null) {
489 sourceClass = sourceClasses.getNextClass(sourceClass); 448 sourceClass = sourceClasses.getNextClass(sourceClass);
490 } else { 449 } else {
491 sourceClass = sourceClasses.getFirstClass(); 450 sourceClass = sourceClasses.getFirstClass();
492 } 451 }
493 } 452 }
494 453
495 // set the source class 454 // set the source class
496 setSourceClass(sourceClass, this::pickBestDestClass); 455 setSourceClass(sourceClass, this::pickBestDestClass);
497 sourceClasses.setSelectionClass(sourceClass); 456 sourceClasses.setSelectionClass(sourceClass);
498 } 457 }
499 458
500 private void pickBestDestClass() { 459 private void pickBestDestClass() {
501 460
502 // then, pick the best dest class 461 // then, pick the best dest class
503 ClassEntry firstClass = null; 462 ClassEntry firstClass = null;
504 ScoredClassEntry bestDestClass = null; 463 ScoredClassEntry bestDestClass = null;
505 for (ClassSelectorPackageNode packageNode : destClasses.packageNodes()) { 464 for (ClassSelectorPackageNode packageNode : destClasses.packageNodes()) {
506 for (ClassSelectorClassNode classNode : destClasses.classNodes(packageNode)) { 465 for (ClassSelectorClassNode classNode : destClasses.classNodes(packageNode)) {
507 if (firstClass == null) { 466 if (firstClass == null) {
508 firstClass = classNode.getClassEntry(); 467 firstClass = classNode.getClassEntry();
509 } 468 }
510 if (classNode.getClassEntry() instanceof ScoredClassEntry) { 469 if (classNode.getClassEntry() instanceof ScoredClassEntry) {
511 ScoredClassEntry scoredClass = (ScoredClassEntry) classNode.getClassEntry(); 470 ScoredClassEntry scoredClass = (ScoredClassEntry) classNode.getClassEntry();
512 if (bestDestClass == null || bestDestClass.getScore() < scoredClass.getScore()) { 471 if (bestDestClass == null || bestDestClass.getScore() < scoredClass.getScore()) {
513 bestDestClass = scoredClass; 472 bestDestClass = scoredClass;
514 } 473 }
515 } 474 }
516 } 475 }
517 } 476 }
518 477
519 // pick the entry to show 478 // pick the entry to show
520 ClassEntry destClass = null; 479 ClassEntry destClass = null;
521 if (bestDestClass != null) { 480 if (bestDestClass != null) {
522 destClass = bestDestClass; 481 destClass = bestDestClass;
523 } else if (firstClass != null) { 482 } else if (firstClass != null) {
524 destClass = firstClass; 483 destClass = firstClass;
525 } 484 }
526 485
527 setDestClass(destClass); 486 setDestClass(destClass);
528 destClasses.setSelectionClass(destClass); 487 destClasses.setSelectionClass(destClass);
529 } 488 }
530 489
531 private void toggleTop10Matches() { 490 private void toggleTop10Matches() {
532 if (sourceClass != null) { 491 if (sourceClass != null) {
533 destClasses.clearSelection(); 492 destClasses.clearSelection();
534 destClasses.setClasses(deobfuscateClasses(getLikelyMatches(sourceClass), destDeobfuscator)); 493 destClasses.setClasses(deobfuscateClasses(getLikelyMatches(sourceClass), destDeobfuscator));
535 destClasses.expandAll(); 494 destClasses.expandAll();
536 } 495 }
537 } 496 }
497
498 private enum SourceType {
499 Matched {
500 @Override
501 public Collection<ClassEntry> getSourceClasses(ClassMatches matches) {
502 return matches.getUniqueMatches().keySet();
503 }
504 },
505 Unmatched {
506 @Override
507 public Collection<ClassEntry> getSourceClasses(ClassMatches matches) {
508 return matches.getUnmatchedSourceClasses();
509 }
510 },
511 Ambiguous {
512 @Override
513 public Collection<ClassEntry> getSourceClasses(ClassMatches matches) {
514 return matches.getAmbiguouslyMatchedSourceClasses();
515 }
516 };
517
518 public static SourceType getDefault() {
519 return values()[0];
520 }
521
522 public JRadioButton newRadio(ActionListener listener, ButtonGroup group) {
523 JRadioButton button = new JRadioButton(name(), this == getDefault());
524 button.setActionCommand(name());
525 button.addActionListener(listener);
526 group.add(button);
527 return button;
528 }
529
530 public abstract Collection<ClassEntry> getSourceClasses(ClassMatches matches);
531 }
532
533 public interface SaveListener {
534 void save(ClassMatches matches);
535 }
538} 536}
diff --git a/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/src/main/java/cuchaz/enigma/gui/ClassSelector.java
index 8ece0a0a..f7d7703f 100644
--- a/src/main/java/cuchaz/enigma/gui/ClassSelector.java
+++ b/src/main/java/cuchaz/enigma/gui/ClassSelector.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.gui; 12package cuchaz.enigma.gui;
12 13
13import com.google.common.collect.ArrayListMultimap; 14import com.google.common.collect.ArrayListMultimap;
@@ -29,505 +30,470 @@ import java.util.*;
29 30
30public class ClassSelector extends JTree { 31public class ClassSelector extends JTree {
31 32
32 public static final Comparator<ClassEntry> DEOBF_CLASS_COMPARATOR = Comparator.comparing(ClassEntry::getName); 33 public static final Comparator<ClassEntry> DEOBF_CLASS_COMPARATOR = Comparator.comparing(ClassEntry::getName);
33 private DefaultMutableTreeNode rootNodes; 34 private DefaultMutableTreeNode rootNodes;
34 35 private ClassSelectionListener selectionListener;
35 public interface ClassSelectionListener { 36 private RenameSelectionListener renameSelectionListener;
36 void onSelectClass(ClassEntry classEntry); 37 private Comparator<ClassEntry> comparator;
37 } 38 public ClassSelector(Gui gui, Comparator<ClassEntry> comparator, boolean isRenamable) {
38 39 this.comparator = comparator;
39 public interface RenameSelectionListener { 40
40 void onSelectionRename(Object prevData, Object data, DefaultMutableTreeNode node); 41 // configure the tree control
41 } 42 setEditable(gui != null);
42 43 setRootVisible(false);
43 private ClassSelectionListener selectionListener; 44 setShowsRootHandles(false);
44 private RenameSelectionListener renameSelectionListener; 45 setModel(null);
45 private Comparator<ClassEntry> comparator; 46
46 47 // hook events
47 public ClassSelector(Gui gui, Comparator<ClassEntry> comparator, boolean isRenamable) { 48 addMouseListener(new MouseAdapter() {
48 this.comparator = comparator; 49 @Override
49 50 public void mouseClicked(MouseEvent event) {
50 // configure the tree control 51 if (selectionListener != null && event.getClickCount() == 2) {
51 setEditable(gui != null); 52 // get the selected node
52 setRootVisible(false); 53 TreePath path = getSelectionPath();
53 setShowsRootHandles(false); 54 if (path != null && path.getLastPathComponent() instanceof ClassSelectorClassNode) {
54 setModel(null); 55 ClassSelectorClassNode node = (ClassSelectorClassNode) path.getLastPathComponent();
55 56 selectionListener.onSelectClass(node.getClassEntry());
56 // hook events 57 }
57 addMouseListener(new MouseAdapter() { 58 }
58 @Override 59 }
59 public void mouseClicked(MouseEvent event) { 60 });
60 if (selectionListener != null && event.getClickCount() == 2) { 61
61 // get the selected node 62 if (gui != null) {
62 TreePath path = getSelectionPath(); 63 final JTree tree = this;
63 if (path != null && path.getLastPathComponent() instanceof ClassSelectorClassNode) { 64
64 ClassSelectorClassNode node = (ClassSelectorClassNode) path.getLastPathComponent(); 65 final DefaultTreeCellEditor editor = new DefaultTreeCellEditor(tree,
65 selectionListener.onSelectClass(node.getClassEntry()); 66 (DefaultTreeCellRenderer) tree.getCellRenderer()) {
66 } 67 @Override
67 } 68 public boolean isCellEditable(EventObject event) {
68 } 69 return isRenamable && !(event instanceof MouseEvent) && super.isCellEditable(event);
69 }); 70 }
70 71 };
71 if (gui != null) 72 this.setCellEditor(editor);
72 { 73 editor.addCellEditorListener(new CellEditorListener() {
73 final JTree tree = this; 74 @Override
74 75 public void editingStopped(ChangeEvent e) {
75 final DefaultTreeCellEditor editor = new DefaultTreeCellEditor(tree, 76 String data = editor.getCellEditorValue().toString();
76 (DefaultTreeCellRenderer) tree.getCellRenderer()) 77 TreePath path = getSelectionPath();
77 { 78
78 @Override public boolean isCellEditable(EventObject event) 79 Object realPath = path.getLastPathComponent();
79 { 80 if (realPath != null && realPath instanceof DefaultMutableTreeNode && data != null) {
80 return isRenamable && !(event instanceof MouseEvent) && super.isCellEditable(event); 81 DefaultMutableTreeNode node = (DefaultMutableTreeNode) realPath;
81 } 82 TreeNode parentNode = node.getParent();
82 }; 83 if (parentNode == null)
83 this.setCellEditor(editor); 84 return;
84 editor.addCellEditorListener(new CellEditorListener() 85 boolean allowEdit = true;
85 { 86 for (int i = 0; i < parentNode.getChildCount(); i++) {
86 @Override public void editingStopped(ChangeEvent e) 87 TreeNode childNode = parentNode.getChildAt(i);
87 { 88 if (childNode != null && childNode.toString().equals(data) && childNode != node) {
88 String data = editor.getCellEditorValue().toString(); 89 allowEdit = false;
89 TreePath path = getSelectionPath(); 90 break;
90 91 }
91 Object realPath = path.getLastPathComponent(); 92 }
92 if (realPath != null && realPath instanceof DefaultMutableTreeNode && data != null) 93 if (allowEdit && renameSelectionListener != null) {
93 { 94 Object prevData = node.getUserObject();
94 DefaultMutableTreeNode node = (DefaultMutableTreeNode) realPath; 95 Object objectData = node.getUserObject() instanceof ClassEntry ? new ClassEntry(((ClassEntry) prevData).getPackageName() + "/" + data) : data;
95 TreeNode parentNode = node.getParent(); 96 try {
96 if (parentNode == null) 97 renameSelectionListener.onSelectionRename(node.getUserObject(), objectData, node);
97 return; 98 node.setUserObject(objectData); // Make sure that it's modified
98 boolean allowEdit = true; 99 } catch (IllegalNameException ex) {
99 for (int i = 0; i < parentNode.getChildCount(); i++) 100 JOptionPane.showOptionDialog(gui.getFrame(), ex.getMessage(), "Enigma - Error", JOptionPane.OK_OPTION,
100 { 101 JOptionPane.ERROR_MESSAGE, null, new String[] { "Ok" }, "OK");
101 TreeNode childNode = parentNode.getChildAt(i); 102 editor.cancelCellEditing();
102 if (childNode != null && childNode.toString().equals(data) && childNode != node) 103 }
103 { 104 } else
104 allowEdit = false; 105 editor.cancelCellEditing();
105 break; 106 }
106 } 107
107 } 108 }
108 if (allowEdit && renameSelectionListener != null) 109
109 { 110 @Override
110 Object prevData = node.getUserObject(); 111 public void editingCanceled(ChangeEvent e) {
111 Object objectData = node.getUserObject() instanceof ClassEntry ? new ClassEntry(((ClassEntry)prevData).getPackageName() + "/" + data) : data; 112 // NOP
112 try 113 }
113 { 114 });
114 renameSelectionListener.onSelectionRename(node.getUserObject(), objectData, node); 115 }
115 node.setUserObject(objectData); // Make sure that it's modified 116 // init defaults
116 } catch (IllegalNameException ex) 117 this.selectionListener = null;
117 { 118 this.renameSelectionListener = null;
118 JOptionPane.showOptionDialog(gui.getFrame(), ex.getMessage(), "Enigma - Error", JOptionPane.OK_OPTION, 119 }
119 JOptionPane.ERROR_MESSAGE, null, new String[] {"Ok"}, "OK"); 120
120 editor.cancelCellEditing(); 121 public boolean isDuplicate(Object[] nodes, String data) {
121 } 122 int count = 0;
122 } 123
123 else 124 for (Object node : nodes) {
124 editor.cancelCellEditing(); 125 if (node.toString().equals(data)) {
125 } 126 count++;
126 127 if (count == 2)
127 } 128 return true;
128 129 }
129 @Override public void editingCanceled(ChangeEvent e) 130 }
130 { 131 return false;
131 // NOP 132 }
132 } 133
133 }); 134 public void setSelectionListener(ClassSelectionListener val) {
134 } 135 this.selectionListener = val;
135 // init defaults 136 }
136 this.selectionListener = null; 137
137 this.renameSelectionListener = null; 138 public void setRenameSelectionListener(RenameSelectionListener renameSelectionListener) {
138 } 139 this.renameSelectionListener = renameSelectionListener;
139 140 }
140 public boolean isDuplicate(Object[] nodes, String data) 141
141 { 142 public void setClasses(Collection<ClassEntry> classEntries) {
142 int count = 0; 143 String state = getExpansionState(this, 0);
143 144 if (classEntries == null) {
144 for (Object node : nodes) 145 setModel(null);
145 { 146 return;
146 if (node.toString().equals(data)) 147 }
147 { 148
148 count++; 149 // build the package names
149 if (count == 2) 150 Map<String, ClassSelectorPackageNode> packages = Maps.newHashMap();
150 return true; 151 for (ClassEntry classEntry : classEntries) {
151 } 152 packages.put(classEntry.getPackageName(), null);
152 } 153 }
153 return false; 154
154 } 155 // sort the packages
155 156 List<String> sortedPackageNames = Lists.newArrayList(packages.keySet());
156 public void setSelectionListener(ClassSelectionListener val) { 157 sortedPackageNames.sort((a, b) ->
157 this.selectionListener = val; 158 {
158 } 159 // I can never keep this rule straight when writing these damn things...
159 160 // a < b => -1, a == b => 0, a > b => +1
160 public void setRenameSelectionListener(RenameSelectionListener renameSelectionListener) 161
161 { 162 if (b == null || a == null) {
162 this.renameSelectionListener = renameSelectionListener; 163 return 0;
163 } 164 }
164 165
165 public void setClasses(Collection<ClassEntry> classEntries) { 166 String[] aparts = a.split("/");
166 String state = getExpansionState(this, 0); 167 String[] bparts = b.split("/");
167 if (classEntries == null) { 168 for (int i = 0; true; i++) {
168 setModel(null); 169 if (i >= aparts.length) {
169 return; 170 return -1;
170 } 171 } else if (i >= bparts.length) {
171 172 return 1;
172 // build the package names 173 }
173 Map<String, ClassSelectorPackageNode> packages = Maps.newHashMap(); 174
174 for (ClassEntry classEntry : classEntries) { 175 int result = aparts[i].compareTo(bparts[i]);
175 packages.put(classEntry.getPackageName(), null); 176 if (result != 0) {
176 } 177 return result;
177 178 }
178 // sort the packages 179 }
179 List<String> sortedPackageNames = Lists.newArrayList(packages.keySet()); 180 });
180 sortedPackageNames.sort((a, b) -> 181
181 { 182 // create the rootNodes node and the package nodes
182 // I can never keep this rule straight when writing these damn things... 183 rootNodes = new DefaultMutableTreeNode();
183 // a < b => -1, a == b => 0, a > b => +1 184 for (String packageName : sortedPackageNames) {
184 185 ClassSelectorPackageNode node = new ClassSelectorPackageNode(packageName);
185 if (b == null || a == null) 186 packages.put(packageName, node);
186 { 187 rootNodes.add(node);
187 return 0; 188 }
188 } 189
189 190 // put the classes into packages
190 String[] aparts = a.split("/"); 191 Multimap<String, ClassEntry> packagedClassEntries = ArrayListMultimap.create();
191 String[] bparts = b.split("/"); 192 for (ClassEntry classEntry : classEntries) {
192 for (int i = 0; true; i++) 193 packagedClassEntries.put(classEntry.getPackageName(), classEntry);
193 { 194 }
194 if (i >= aparts.length) 195
195 { 196 // build the class nodes
196 return -1; 197 for (String packageName : packagedClassEntries.keySet()) {
197 } 198 // sort the class entries
198 else if (i >= bparts.length) 199 List<ClassEntry> classEntriesInPackage = Lists.newArrayList(packagedClassEntries.get(packageName));
199 { 200 classEntriesInPackage.sort(this.comparator);
200 return 1; 201
201 } 202 // create the nodes in order
202 203 for (ClassEntry classEntry : classEntriesInPackage) {
203 int result = aparts[i].compareTo(bparts[i]); 204 ClassSelectorPackageNode node = packages.get(packageName);
204 if (result != 0) 205 node.add(new ClassSelectorClassNode(classEntry));
205 { 206 }
206 return result; 207 }
207 } 208
208 } 209 // finally, update the tree control
209 }); 210 setModel(new DefaultTreeModel(rootNodes));
210 211
211 // create the rootNodes node and the package nodes 212 restoreExpanstionState(this, 0, state);
212 rootNodes = new DefaultMutableTreeNode(); 213 }
213 for (String packageName : sortedPackageNames) { 214
214 ClassSelectorPackageNode node = new ClassSelectorPackageNode(packageName); 215 public ClassEntry getSelectedClass() {
215 packages.put(packageName, node); 216 if (!isSelectionEmpty()) {
216 rootNodes.add(node); 217 Object selectedNode = getSelectionPath().getLastPathComponent();
217 } 218 if (selectedNode instanceof ClassSelectorClassNode) {
218 219 ClassSelectorClassNode classNode = (ClassSelectorClassNode) selectedNode;
219 // put the classes into packages 220 return classNode.getClassEntry();
220 Multimap<String, ClassEntry> packagedClassEntries = ArrayListMultimap.create(); 221 }
221 for (ClassEntry classEntry : classEntries) { 222 }
222 packagedClassEntries.put(classEntry.getPackageName(), classEntry); 223 return null;
223 } 224 }
224 225
225 // build the class nodes 226 public String getSelectedPackage() {
226 for (String packageName : packagedClassEntries.keySet()) { 227 if (!isSelectionEmpty()) {
227 // sort the class entries 228 Object selectedNode = getSelectionPath().getLastPathComponent();
228 List<ClassEntry> classEntriesInPackage = Lists.newArrayList(packagedClassEntries.get(packageName)); 229 if (selectedNode instanceof ClassSelectorPackageNode) {
229 classEntriesInPackage.sort(this.comparator); 230 ClassSelectorPackageNode packageNode = (ClassSelectorPackageNode) selectedNode;
230 231 return packageNode.getPackageName();
231 // create the nodes in order 232 } else if (selectedNode instanceof ClassSelectorClassNode) {
232 for (ClassEntry classEntry : classEntriesInPackage) { 233 ClassSelectorClassNode classNode = (ClassSelectorClassNode) selectedNode;
233 ClassSelectorPackageNode node = packages.get(packageName); 234 return classNode.getClassEntry().getPackageName();
234 node.add(new ClassSelectorClassNode(classEntry)); 235 }
235 } 236 }
236 } 237 return null;
237 238 }
238 // finally, update the tree control 239
239 setModel(new DefaultTreeModel(rootNodes)); 240 public boolean isDescendant(TreePath path1, TreePath path2) {
240 241 int count1 = path1.getPathCount();
241 restoreExpanstionState(this, 0, state); 242 int count2 = path2.getPathCount();
242 } 243 if (count1 <= count2) {
243 244 return false;
244 public ClassEntry getSelectedClass() { 245 }
245 if (!isSelectionEmpty()) { 246 while (count1 != count2) {
246 Object selectedNode = getSelectionPath().getLastPathComponent(); 247 path1 = path1.getParentPath();
247 if (selectedNode instanceof ClassSelectorClassNode) { 248 count1--;
248 ClassSelectorClassNode classNode = (ClassSelectorClassNode)selectedNode; 249 }
249 return classNode.getClassEntry(); 250 return path1.equals(path2);
250 } 251 }
251 } 252
252 return null; 253 public String getExpansionState(JTree tree, int row) {
253 } 254 TreePath rowPath = tree.getPathForRow(row);
254 255 StringBuilder buf = new StringBuilder();
255 public String getSelectedPackage() { 256 int rowCount = tree.getRowCount();
256 if (!isSelectionEmpty()) { 257 for (int i = row; i < rowCount; i++) {
257 Object selectedNode = getSelectionPath().getLastPathComponent(); 258 TreePath path = tree.getPathForRow(i);
258 if (selectedNode instanceof ClassSelectorPackageNode) { 259 if (i == row || isDescendant(path, rowPath)) {
259 ClassSelectorPackageNode packageNode = (ClassSelectorPackageNode)selectedNode; 260 if (tree.isExpanded(path)) {
260 return packageNode.getPackageName(); 261 buf.append(",").append((i - row));
261 } else if (selectedNode instanceof ClassSelectorClassNode) { 262 }
262 ClassSelectorClassNode classNode = (ClassSelectorClassNode)selectedNode; 263 } else {
263 return classNode.getClassEntry().getPackageName(); 264 break;
264 } 265 }
265 } 266 }
266 return null; 267 return buf.toString();
267 } 268 }
268 269
269 public boolean isDescendant(TreePath path1, TreePath path2) { 270 public void restoreExpanstionState(JTree tree, int row, String expansionState) {
270 int count1 = path1.getPathCount(); 271 StringTokenizer stok = new StringTokenizer(expansionState, ",");
271 int count2 = path2.getPathCount(); 272 while (stok.hasMoreTokens()) {
272 if (count1 <= count2) { 273 int token = row + Integer.parseInt(stok.nextToken());
273 return false; 274 tree.expandRow(token);
274 } 275 }
275 while (count1 != count2) { 276 }
276 path1 = path1.getParentPath(); 277
277 count1--; 278 public List<ClassSelectorPackageNode> packageNodes() {
278 } 279 List<ClassSelectorPackageNode> nodes = Lists.newArrayList();
279 return path1.equals(path2); 280 DefaultMutableTreeNode root = (DefaultMutableTreeNode) getModel().getRoot();
280 } 281 Enumeration<?> children = root.children();
281 282 while (children.hasMoreElements()) {
282 public String getExpansionState(JTree tree, int row) { 283 ClassSelectorPackageNode packageNode = (ClassSelectorPackageNode) children.nextElement();
283 TreePath rowPath = tree.getPathForRow(row); 284 nodes.add(packageNode);
284 StringBuilder buf = new StringBuilder(); 285 }
285 int rowCount = tree.getRowCount(); 286 return nodes;
286 for (int i = row; i < rowCount; i++) { 287 }
287 TreePath path = tree.getPathForRow(i); 288
288 if (i == row || isDescendant(path, rowPath)) { 289 public List<ClassSelectorClassNode> classNodes(ClassSelectorPackageNode packageNode) {
289 if (tree.isExpanded(path)) { 290 List<ClassSelectorClassNode> nodes = Lists.newArrayList();
290 buf.append(",").append(String.valueOf(i - row)); 291 Enumeration<?> children = packageNode.children();
291 } 292 while (children.hasMoreElements()) {
292 } else { 293 ClassSelectorClassNode classNode = (ClassSelectorClassNode) children.nextElement();
293 break; 294 nodes.add(classNode);
294 } 295 }
295 } 296 return nodes;
296 return buf.toString(); 297 }
297 } 298
298 299 public void expandPackage(String packageName) {
299 public void restoreExpanstionState(JTree tree, int row, String expansionState) { 300 if (packageName == null) {
300 StringTokenizer stok = new StringTokenizer(expansionState, ","); 301 return;
301 while (stok.hasMoreTokens()) { 302 }
302 int token = row + Integer.parseInt(stok.nextToken()); 303 for (ClassSelectorPackageNode packageNode : packageNodes()) {
303 tree.expandRow(token); 304 if (packageNode.getPackageName().equals(packageName)) {
304 } 305 expandPath(new TreePath(new Object[] { getModel().getRoot(), packageNode }));
305 } 306 return;
306 307 }
307 public List<ClassSelectorPackageNode> packageNodes() { 308 }
308 List<ClassSelectorPackageNode> nodes = Lists.newArrayList(); 309 }
309 DefaultMutableTreeNode root = (DefaultMutableTreeNode)getModel().getRoot(); 310
310 Enumeration<?> children = root.children(); 311 public void expandAll() {
311 while (children.hasMoreElements()) { 312 for (ClassSelectorPackageNode packageNode : packageNodes()) {
312 ClassSelectorPackageNode packageNode = (ClassSelectorPackageNode)children.nextElement(); 313 expandPath(new TreePath(new Object[] { getModel().getRoot(), packageNode }));
313 nodes.add(packageNode); 314 }
314 } 315 }
315 return nodes; 316
316 } 317 public ClassEntry getFirstClass() {
317 318 for (ClassSelectorPackageNode packageNode : packageNodes()) {
318 public List<ClassSelectorClassNode> classNodes(ClassSelectorPackageNode packageNode) { 319 for (ClassSelectorClassNode classNode : classNodes(packageNode)) {
319 List<ClassSelectorClassNode> nodes = Lists.newArrayList(); 320 return classNode.getClassEntry();
320 Enumeration<?> children = packageNode.children(); 321 }
321 while (children.hasMoreElements()) { 322 }
322 ClassSelectorClassNode classNode = (ClassSelectorClassNode)children.nextElement(); 323 return null;
323 nodes.add(classNode); 324 }
324 } 325
325 return nodes; 326 public ClassSelectorPackageNode getPackageNode(ClassEntry entry) {
326 } 327 for (ClassSelectorPackageNode packageNode : packageNodes()) {
327 328 if (packageNode.getPackageName().equals(entry.getPackageName())) {
328 public void expandPackage(String packageName) { 329 return packageNode;
329 if (packageName == null) { 330 }
330 return; 331 }
331 } 332 return null;
332 for (ClassSelectorPackageNode packageNode : packageNodes()) { 333 }
333 if (packageNode.getPackageName().equals(packageName)) { 334
334 expandPath(new TreePath(new Object[] {getModel().getRoot(), packageNode})); 335 public ClassSelectorPackageNode getPackageNode(ClassSelector selector, ClassEntry entry) {
335 return; 336 ClassSelectorPackageNode packageNode = getPackageNode(entry);
336 } 337
337 } 338 if (selector != null && packageNode == null && selector.getPackageNode(entry) != null)
338 } 339 return selector.getPackageNode(entry);
339 340 return packageNode;
340 public void expandAll() { 341 }
341 for (ClassSelectorPackageNode packageNode : packageNodes()) { 342
342 expandPath(new TreePath(new Object[] {getModel().getRoot(), packageNode})); 343 public ClassEntry getNextClass(ClassEntry entry) {
343 } 344 boolean foundIt = false;
344 } 345 for (ClassSelectorPackageNode packageNode : packageNodes()) {
345 346 if (!foundIt) {
346 public ClassEntry getFirstClass() { 347 // skip to the package with our target in it
347 for (ClassSelectorPackageNode packageNode : packageNodes()) { 348 if (packageNode.getPackageName().equals(entry.getPackageName())) {
348 for (ClassSelectorClassNode classNode : classNodes(packageNode)) { 349 for (ClassSelectorClassNode classNode : classNodes(packageNode)) {
349 return classNode.getClassEntry(); 350 if (!foundIt) {
350 } 351 if (classNode.getClassEntry().equals(entry)) {
351 } 352 foundIt = true;
352 return null; 353 }
353 } 354 } else {
354 355 // return the next class
355 public ClassSelectorPackageNode getPackageNode(ClassEntry entry) { 356 return classNode.getClassEntry();
356 for (ClassSelectorPackageNode packageNode : packageNodes()) { 357 }
357 if (packageNode.getPackageName().equals(entry.getPackageName())) { 358 }
358 return packageNode; 359 }
359 } 360 } else {
360 } 361 // return the next class
361 return null; 362 for (ClassSelectorClassNode classNode : classNodes(packageNode)) {
362 } 363 return classNode.getClassEntry();
363 364 }
364 public ClassSelectorPackageNode getPackageNode(ClassSelector selector, ClassEntry entry) 365 }
365 { 366 }
366 ClassSelectorPackageNode packageNode = getPackageNode(entry); 367 return null;
367 368 }
368 if (selector != null && packageNode == null && selector.getPackageNode(entry) != null) 369
369 return selector.getPackageNode(entry); 370 public void setSelectionClass(ClassEntry classEntry) {
370 return packageNode; 371 expandPackage(classEntry.getPackageName());
371 } 372 for (ClassSelectorPackageNode packageNode : packageNodes()) {
372 373 for (ClassSelectorClassNode classNode : classNodes(packageNode)) {
373 public ClassEntry getNextClass(ClassEntry entry) { 374 if (classNode.getClassEntry().equals(classEntry)) {
374 boolean foundIt = false; 375 setSelectionPath(new TreePath(new Object[] { getModel().getRoot(), packageNode, classNode }));
375 for (ClassSelectorPackageNode packageNode : packageNodes()) { 376 }
376 if (!foundIt) { 377 }
377 // skip to the package with our target in it 378 }
378 if (packageNode.getPackageName().equals(entry.getPackageName())) { 379 }
379 for (ClassSelectorClassNode classNode : classNodes(packageNode)) { 380
380 if (!foundIt) { 381 public void removeNode(ClassSelectorPackageNode packageNode, ClassEntry entry) {
381 if (classNode.getClassEntry().equals(entry)) { 382 DefaultTreeModel model = (DefaultTreeModel) getModel();
382 foundIt = true; 383
383 } 384 if (packageNode == null)
384 } else { 385 return;
385 // return the next class 386
386 return classNode.getClassEntry(); 387 for (int i = 0; i < packageNode.getChildCount(); i++) {
387 } 388 DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) packageNode.getChildAt(i);
388 } 389 if (childNode.getUserObject() instanceof ClassEntry && childNode.getUserObject().equals(entry)) {
389 } 390 model.removeNodeFromParent(childNode);
390 } else { 391 break;
391 // return the next class 392 }
392 for (ClassSelectorClassNode classNode : classNodes(packageNode)) { 393 }
393 return classNode.getClassEntry(); 394 }
394 } 395
395 } 396 public void removeNodeIfEmpty(ClassSelectorPackageNode packageNode) {
396 } 397 if (packageNode != null && packageNode.getChildCount() == 0)
397 return null; 398 ((DefaultTreeModel) getModel()).removeNodeFromParent(packageNode);
398 } 399 }
399 400
400 public void setSelectionClass(ClassEntry classEntry) { 401 public void moveClassTree(ClassEntry oldClassEntry, ClassEntry newClassEntry, ClassSelector otherSelector) {
401 expandPackage(classEntry.getPackageName()); 402 if (otherSelector == null)
402 for (ClassSelectorPackageNode packageNode : packageNodes()) { 403 removeNode(getPackageNode(oldClassEntry), oldClassEntry);
403 for (ClassSelectorClassNode classNode : classNodes(packageNode)) { 404 insertNode(getOrCreate(newClassEntry), newClassEntry);
404 if (classNode.getClassEntry().equals(classEntry)) { 405 }
405 setSelectionPath(new TreePath(new Object[] {getModel().getRoot(), packageNode, classNode})); 406
406 } 407 public ClassSelectorPackageNode getOrCreate(ClassEntry entry) {
407 } 408 DefaultTreeModel model = (DefaultTreeModel) getModel();
408 } 409 ClassSelectorPackageNode newPackageNode = getPackageNode(entry);
409 } 410 if (newPackageNode == null) {
410 411 newPackageNode = new ClassSelectorPackageNode(entry.getPackageName());
411 public void removeNode(ClassSelectorPackageNode packageNode, ClassEntry entry) 412 model.insertNodeInto(newPackageNode, (MutableTreeNode) model.getRoot(), getPlacementIndex(newPackageNode));
412 { 413 }
413 DefaultTreeModel model = (DefaultTreeModel) getModel(); 414 return newPackageNode;
414 415 }
415 if (packageNode == null) 416
416 return; 417 public void insertNode(ClassSelectorPackageNode packageNode, ClassEntry entry) {
417 418 DefaultTreeModel model = (DefaultTreeModel) getModel();
418 for (int i = 0; i < packageNode.getChildCount(); i++) 419 ClassSelectorClassNode classNode = new ClassSelectorClassNode(entry);
419 { 420 model.insertNodeInto(classNode, packageNode, getPlacementIndex(packageNode, classNode));
420 DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) packageNode.getChildAt(i); 421 }
421 if (childNode.getUserObject() instanceof ClassEntry && childNode.getUserObject().equals(entry)) 422
422 { 423 public void reload() {
423 model.removeNodeFromParent(childNode); 424 DefaultTreeModel model = (DefaultTreeModel) getModel();
424 break; 425 model.reload(sort(rootNodes));
425 } 426 }
426 } 427
427 } 428 private DefaultMutableTreeNode sort(DefaultMutableTreeNode node) {
428 429
429 public void removeNodeIfEmpty(ClassSelectorPackageNode packageNode) 430 for (int i = 0; i < node.getChildCount() - 1; i++) {
430 { 431 DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i);
431 if (packageNode != null && packageNode.getChildCount() == 0) 432 if (child == null)
432 ((DefaultTreeModel) getModel()).removeNodeFromParent(packageNode); 433 continue;
433 } 434 String nt = child.toString();
434 435
435 public void moveClassTree(ClassEntry oldClassEntry, ClassEntry newClassEntry, ClassSelector otherSelector) 436 for (int j = i + 1; j <= node.getChildCount() - 1; j++) {
436 { 437 DefaultMutableTreeNode prevNode = (DefaultMutableTreeNode) node.getChildAt(j);
437 if (otherSelector == null) 438 if (prevNode == null || prevNode.getUserObject() == null)
438 removeNode(getPackageNode(oldClassEntry), oldClassEntry); 439 continue;
439 insertNode(getOrCreate(newClassEntry), newClassEntry); 440 String np = prevNode.getUserObject().toString();
440 } 441
441 442 if (nt.compareToIgnoreCase(np) > 0) {
442 public ClassSelectorPackageNode getOrCreate(ClassEntry entry) 443 node.insert(child, j);
443 { 444 node.insert(prevNode, i);
444 DefaultTreeModel model = (DefaultTreeModel) getModel(); 445 }
445 ClassSelectorPackageNode newPackageNode = getPackageNode(entry); 446 }
446 if (newPackageNode == null) 447 if (child.getChildCount() > 0) {
447 { 448 sort(child);
448 newPackageNode = new ClassSelectorPackageNode(entry.getPackageName()); 449 }
449 model.insertNodeInto(newPackageNode, (MutableTreeNode) model.getRoot(), getPlacementIndex(newPackageNode)); 450 }
450 } 451
451 return newPackageNode; 452 for (int i = 0; i < node.getChildCount() - 1; i++) {
452 } 453 DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i);
453 454 for (int j = i + 1; j <= node.getChildCount() - 1; j++) {
454 public void insertNode(ClassSelectorPackageNode packageNode, ClassEntry entry) 455 DefaultMutableTreeNode prevNode = (DefaultMutableTreeNode) node.getChildAt(j);
455 { 456
456 DefaultTreeModel model = (DefaultTreeModel) getModel(); 457 if (!prevNode.isLeaf() && child.isLeaf()) {
457 ClassSelectorClassNode classNode = new ClassSelectorClassNode(entry); 458 node.insert(child, j);
458 model.insertNodeInto(classNode, packageNode, getPlacementIndex(packageNode, classNode)); 459 node.insert(prevNode, i);
459 } 460 }
460 461 }
461 public void reload() 462 }
462 { 463
463 DefaultTreeModel model = (DefaultTreeModel) getModel(); 464 return node;
464 model.reload(sort(rootNodes)); 465 }
465 } 466
466 467 private int getPlacementIndex(ClassSelectorPackageNode newPackageNode, ClassSelectorClassNode classNode) {
467 private DefaultMutableTreeNode sort(DefaultMutableTreeNode node) { 468 List<ClassSelectorClassNode> classNodes = classNodes(newPackageNode);
468 469 classNodes.add(classNode);
469 for(int i = 0; i < node.getChildCount() - 1; i++) { 470 classNodes.sort(Comparator.comparing(ClassSelectorClassNode::toString));
470 DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i); 471 for (int i = 0; i < classNodes.size(); i++)
471 if (child == null) 472 if (classNodes.get(i) == classNode)
472 continue; 473 return i;
473 String nt = child.toString(); 474
474 475 return 0;
475 for(int j = i + 1; j <= node.getChildCount() - 1; j++) { 476 }
476 DefaultMutableTreeNode prevNode = (DefaultMutableTreeNode) node.getChildAt(j); 477
477 if (prevNode == null || prevNode.getUserObject() == null) 478 private int getPlacementIndex(ClassSelectorPackageNode newPackageNode) {
478 continue; 479 List<ClassSelectorPackageNode> packageNodes = packageNodes();
479 String np = prevNode.getUserObject().toString(); 480 if (!packageNodes.contains(newPackageNode)) {
480 481 packageNodes.add(newPackageNode);
481 if(nt.compareToIgnoreCase(np) > 0) { 482 packageNodes.sort(Comparator.comparing(ClassSelectorPackageNode::toString));
482 node.insert(child, j); 483 }
483 node.insert(prevNode, i); 484
484 } 485 for (int i = 0; i < packageNodes.size(); i++)
485 } 486 if (packageNodes.get(i) == newPackageNode)
486 if(child.getChildCount() > 0) { 487 return i;
487 sort(child); 488
488 } 489 return 0;
489 } 490 }
490 491
491 for(int i = 0; i < node.getChildCount() - 1; i++) { 492 public interface ClassSelectionListener {
492 DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i); 493 void onSelectClass(ClassEntry classEntry);
493 for(int j = i + 1; j <= node.getChildCount() - 1; j++) { 494 }
494 DefaultMutableTreeNode prevNode = (DefaultMutableTreeNode) node.getChildAt(j); 495
495 496 public interface RenameSelectionListener {
496 if(!prevNode.isLeaf() && child.isLeaf()) { 497 void onSelectionRename(Object prevData, Object data, DefaultMutableTreeNode node);
497 node.insert(child, j); 498 }
498 node.insert(prevNode, i);
499 }
500 }
501 }
502
503 return node;
504 }
505
506 private int getPlacementIndex(ClassSelectorPackageNode newPackageNode, ClassSelectorClassNode classNode)
507 {
508 List<ClassSelectorClassNode> classNodes = classNodes(newPackageNode);
509 classNodes.add(classNode);
510 classNodes.sort(Comparator.comparing(ClassSelectorClassNode::toString));
511 for (int i = 0; i < classNodes.size(); i++)
512 if (classNodes.get(i) == classNode)
513 return i;
514
515 return 0;
516 }
517
518 private int getPlacementIndex(ClassSelectorPackageNode newPackageNode)
519 {
520 List<ClassSelectorPackageNode> packageNodes = packageNodes();
521 if (!packageNodes.contains(newPackageNode))
522 {
523 packageNodes.add(newPackageNode);
524 packageNodes.sort(Comparator.comparing(ClassSelectorPackageNode::toString));
525 }
526
527 for (int i = 0; i < packageNodes.size(); i++)
528 if (packageNodes.get(i) == newPackageNode)
529 return i;
530
531 return 0;
532 }
533} 499}
diff --git a/src/main/java/cuchaz/enigma/gui/CodeReader.java b/src/main/java/cuchaz/enigma/gui/CodeReader.java
index 8225d8f4..2e235dc6 100644
--- a/src/main/java/cuchaz/enigma/gui/CodeReader.java
+++ b/src/main/java/cuchaz/enigma/gui/CodeReader.java
@@ -8,20 +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.gui; 12package cuchaz.enigma.gui;
12 13
13import com.strobel.decompiler.languages.java.ast.CompilationUnit; 14import com.strobel.decompiler.languages.java.ast.CompilationUnit;
14
15import java.awt.Rectangle;
16import java.awt.event.ActionEvent;
17import java.awt.event.ActionListener;
18
19import javax.swing.JEditorPane;
20import javax.swing.SwingUtilities;
21import javax.swing.Timer;
22import javax.swing.text.BadLocationException;
23import javax.swing.text.Highlighter.HighlightPainter;
24
25import cuchaz.enigma.Deobfuscator; 15import cuchaz.enigma.Deobfuscator;
26import cuchaz.enigma.analysis.EntryReference; 16import cuchaz.enigma.analysis.EntryReference;
27import cuchaz.enigma.analysis.SourceIndex; 17import cuchaz.enigma.analysis.SourceIndex;
@@ -31,180 +21,184 @@ import cuchaz.enigma.mapping.ClassEntry;
31import cuchaz.enigma.mapping.Entry; 21import cuchaz.enigma.mapping.Entry;
32import de.sciss.syntaxpane.DefaultSyntaxKit; 22import de.sciss.syntaxpane.DefaultSyntaxKit;
33 23
24import javax.swing.*;
25import javax.swing.text.BadLocationException;
26import javax.swing.text.Highlighter.HighlightPainter;
27import java.awt.*;
28import java.awt.event.ActionEvent;
29import java.awt.event.ActionListener;
34 30
35public class CodeReader extends JEditorPane { 31public class CodeReader extends JEditorPane {
36 32
37 private static final long serialVersionUID = 3673180950485748810L; 33 private static final long serialVersionUID = 3673180950485748810L;
38 34
39 private static final Object lock = new Object(); 35 private static final Object lock = new Object();
40 36 private SelectionHighlightPainter selectionHighlightPainter;
41 public interface SelectionListener { 37 private SourceIndex sourceIndex;
42 void onSelect(EntryReference<Entry, Entry> reference); 38 private SelectionListener selectionListener;
43 } 39 public CodeReader() {
44 40
45 private SelectionHighlightPainter selectionHighlightPainter; 41 setEditable(false);
46 private SourceIndex sourceIndex; 42 setContentType("text/java");
47 private SelectionListener selectionListener; 43
48 44 // turn off token highlighting (it's wrong most of the time anyway...)
49 public CodeReader() { 45 DefaultSyntaxKit kit = (DefaultSyntaxKit) getEditorKit();
50 46 kit.toggleComponent(this, "de.sciss.syntaxpane.components.TokenMarker");
51 setEditable(false); 47
52 setContentType("text/java"); 48 // hook events
53 49 addCaretListener(event ->
54 // turn off token highlighting (it's wrong most of the time anyway...) 50 {
55 DefaultSyntaxKit kit = (DefaultSyntaxKit) getEditorKit(); 51 if (selectionListener != null && sourceIndex != null) {
56 kit.toggleComponent(this, "de.sciss.syntaxpane.components.TokenMarker"); 52 Token token = sourceIndex.getReferenceToken(event.getDot());
57 53 if (token != null) {
58 // hook events 54 selectionListener.onSelect(sourceIndex.getDeobfReference(token));
59 addCaretListener(event -> 55 } else {
60 { 56 selectionListener.onSelect(null);
61 if (selectionListener != null && sourceIndex != null) { 57 }
62 Token token = sourceIndex.getReferenceToken(event.getDot()); 58 }
63 if (token != null) { 59 });
64 selectionListener.onSelect(sourceIndex.getDeobfReference(token)); 60
65 } else { 61 selectionHighlightPainter = new SelectionHighlightPainter();
66 selectionListener.onSelect(null); 62 }
67 } 63
68 } 64 // HACKHACK: someday we can update the main GUI to use this code reader
69 }); 65 public static void navigateToToken(final JEditorPane editor, final Token token, final HighlightPainter highlightPainter) {
70 66
71 selectionHighlightPainter = new SelectionHighlightPainter(); 67 // set the caret position to the token
72 } 68 editor.setCaretPosition(token.start);
73 69 editor.grabFocus();
74 public void setSelectionListener(SelectionListener val) { 70
75 selectionListener = val; 71 try {
76 } 72 // make sure the token is visible in the scroll window
77 73 Rectangle start = editor.modelToView(token.start);
78 public void setCode(String code) { 74 Rectangle end = editor.modelToView(token.end);
79 // sadly, the java lexer is not thread safe, so we have to serialize all these calls 75 final Rectangle show = start.union(end);
80 synchronized (lock) { 76 show.grow(start.width * 10, start.height * 6);
81 setText(code); 77 SwingUtilities.invokeLater(() -> editor.scrollRectToVisible(show));
82 } 78 } catch (BadLocationException ex) {
83 } 79 throw new Error(ex);
84 80 }
85 public SourceIndex getSourceIndex() { 81
86 return sourceIndex; 82 // highlight the token momentarily
87 } 83 final Timer timer = new Timer(200, new ActionListener() {
88 84 private int counter = 0;
89 public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator) { 85 private Object highlight = null;
90 decompileClass(classEntry, deobfuscator, null); 86
91 } 87 @Override
92 88 public void actionPerformed(ActionEvent event) {
93 public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator, Runnable callback) { 89 if (counter % 2 == 0) {
94 decompileClass(classEntry, deobfuscator, null, callback); 90 try {
95 } 91 highlight = editor.getHighlighter().addHighlight(token.start, token.end, highlightPainter);
96 92 } catch (BadLocationException ex) {
97 public void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final Boolean ignoreBadTokens, final Runnable callback) { 93 // don't care
98 94 }
99 if (classEntry == null) { 95 } else if (highlight != null) {
100 setCode(null); 96 editor.getHighlighter().removeHighlight(highlight);
101 return; 97 }
102 } 98
103 99 if (counter++ > 6) {
104 setCode("(decompiling...)"); 100 Timer timer = (Timer) event.getSource();
105 101 timer.stop();
106 // run decompilation in a separate thread to keep ui responsive 102 }
107 new Thread(() -> 103 }
108 { 104 });
109 105 timer.start();
110 // decompile it 106 }
111 CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getOutermostClassName()); 107
112 String source = deobfuscator.getSource(sourceTree); 108 public void setSelectionListener(SelectionListener val) {
113 setCode(source); 109 selectionListener = val;
114 sourceIndex = deobfuscator.getSourceIndex(sourceTree, source, ignoreBadTokens); 110 }
115 111
116 if (callback != null) { 112 public void setCode(String code) {
117 callback.run(); 113 // sadly, the java lexer is not thread safe, so we have to serialize all these calls
118 } 114 synchronized (lock) {
119 }).start(); 115 setText(code);
120 } 116 }
121 117 }
122 public void navigateToClassDeclaration(ClassEntry classEntry) { 118
123 119 public SourceIndex getSourceIndex() {
124 // navigate to the class declaration 120 return sourceIndex;
125 Token token = sourceIndex.getDeclarationToken(classEntry); 121 }
126 if (token == null) { 122
127 // couldn't find the class declaration token, might be an anonymous class 123 public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator) {
128 // look for any declaration in that class instead 124 decompileClass(classEntry, deobfuscator, null);
129 for (Entry entry : sourceIndex.declarations()) { 125 }
130 if (entry.getClassEntry().equals(classEntry)) { 126
131 token = sourceIndex.getDeclarationToken(entry); 127 public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator, Runnable callback) {
132 break; 128 decompileClass(classEntry, deobfuscator, null, callback);
133 } 129 }
134 } 130
135 } 131 public void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final Boolean ignoreBadTokens, final Runnable callback) {
136 132
137 if (token != null) { 133 if (classEntry == null) {
138 navigateToToken(token); 134 setCode(null);
139 } else { 135 return;
140 // couldn't find anything =( 136 }
141 System.out.println("Unable to find declaration in source for " + classEntry); 137
142 } 138 setCode("(decompiling...)");
143 } 139
144 140 // run decompilation in a separate thread to keep ui responsive
145 public void navigateToToken(final Token token) { 141 new Thread(() ->
146 navigateToToken(this, token, selectionHighlightPainter); 142 {
147 } 143
148 144 // decompile it
149 // HACKHACK: someday we can update the main GUI to use this code reader 145 CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getOutermostClassName());
150 public static void navigateToToken(final JEditorPane editor, final Token token, final HighlightPainter highlightPainter) { 146 String source = deobfuscator.getSource(sourceTree);
151 147 setCode(source);
152 // set the caret position to the token 148 sourceIndex = deobfuscator.getSourceIndex(sourceTree, source, ignoreBadTokens);
153 editor.setCaretPosition(token.start); 149
154 editor.grabFocus(); 150 if (callback != null) {
155 151 callback.run();
156 try { 152 }
157 // make sure the token is visible in the scroll window 153 }).start();
158 Rectangle start = editor.modelToView(token.start); 154 }
159 Rectangle end = editor.modelToView(token.end); 155
160 final Rectangle show = start.union(end); 156 public void navigateToClassDeclaration(ClassEntry classEntry) {
161 show.grow(start.width * 10, start.height * 6); 157
162 SwingUtilities.invokeLater(() -> editor.scrollRectToVisible(show)); 158 // navigate to the class declaration
163 } catch (BadLocationException ex) { 159 Token token = sourceIndex.getDeclarationToken(classEntry);
164 throw new Error(ex); 160 if (token == null) {
165 } 161 // couldn't find the class declaration token, might be an anonymous class
166 162 // look for any declaration in that class instead
167 // highlight the token momentarily 163 for (Entry entry : sourceIndex.declarations()) {
168 final Timer timer = new Timer(200, new ActionListener() { 164 if (entry.getClassEntry().equals(classEntry)) {
169 private int counter = 0; 165 token = sourceIndex.getDeclarationToken(entry);
170 private Object highlight = null; 166 break;
171 167 }
172 @Override 168 }
173 public void actionPerformed(ActionEvent event) { 169 }
174 if (counter % 2 == 0) { 170
175 try { 171 if (token != null) {
176 highlight = editor.getHighlighter().addHighlight(token.start, token.end, highlightPainter); 172 navigateToToken(token);
177 } catch (BadLocationException ex) { 173 } else {
178 // don't care 174 // couldn't find anything =(
179 } 175 System.out.println("Unable to find declaration in source for " + classEntry);
180 } else if (highlight != null) { 176 }
181 editor.getHighlighter().removeHighlight(highlight); 177 }
182 } 178
183 179 public void navigateToToken(final Token token) {
184 if (counter++ > 6) { 180 navigateToToken(this, token, selectionHighlightPainter);
185 Timer timer = (Timer) event.getSource(); 181 }
186 timer.stop(); 182
187 } 183 public void setHighlightedTokens(Iterable<Token> tokens, HighlightPainter painter) {
188 } 184 for (Token token : tokens) {
189 }); 185 setHighlightedToken(token, painter);
190 timer.start(); 186 }
191 } 187 }
192 188
193 public void setHighlightedTokens(Iterable<Token> tokens, HighlightPainter painter) { 189 public void setHighlightedToken(Token token, HighlightPainter painter) {
194 for (Token token : tokens) { 190 try {
195 setHighlightedToken(token, painter); 191 getHighlighter().addHighlight(token.start, token.end, painter);
196 } 192 } catch (BadLocationException ex) {
197 } 193 throw new IllegalArgumentException(ex);
198 194 }
199 public void setHighlightedToken(Token token, HighlightPainter painter) { 195 }
200 try { 196
201 getHighlighter().addHighlight(token.start, token.end, painter); 197 public void clearHighlights() {
202 } catch (BadLocationException ex) { 198 getHighlighter().removeAllHighlights();
203 throw new IllegalArgumentException(ex); 199 }
204 } 200
205 } 201 public interface SelectionListener {
206 202 void onSelect(EntryReference<Entry, Entry> reference);
207 public void clearHighlights() { 203 }
208 getHighlighter().removeAllHighlights();
209 }
210} 204}
diff --git a/src/main/java/cuchaz/enigma/gui/Gui.java b/src/main/java/cuchaz/enigma/gui/Gui.java
index 7cb494fa..9f8d6fc4 100644
--- a/src/main/java/cuchaz/enigma/gui/Gui.java
+++ b/src/main/java/cuchaz/enigma/gui/Gui.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.gui; 12package cuchaz.enigma.gui;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
@@ -54,811 +55,790 @@ import java.util.function.Function;
54 55
55public class Gui { 56public class Gui {
56 57
57 private GuiController controller; 58 public final PopupMenuBar popupMenu;
58 59 private final PanelObf obfPanel;
59 private final PanelObf obfPanel; 60 private final PanelDeobf deobfPanel;
60 private final PanelDeobf deobfPanel; 61
61 62 private final MenuBar menuBar;
62 private final MenuBar menuBar; 63 // state
63 public final PopupMenuBar popupMenu; 64 public EntryReference<Entry, Entry> reference;
64 65 public JFileChooser jarFileChooser;
65 private JFrame frame; 66 public JFileChooser enigmaMappingsFileChooser;
66 private PanelEditor editor; 67 public JFileChooser exportSourceFileChooser;
67 private JPanel classesPanel; 68 public JFileChooser exportJarFileChooser;
68 private JSplitPane splitClasses; 69 private GuiController controller;
69 private PanelIdentifier infoPanel; 70 private JFrame frame;
70 private ObfuscatedHighlightPainter obfuscatedHighlightPainter; 71 private PanelEditor editor;
71 private DeobfuscatedHighlightPainter deobfuscatedHighlightPainter; 72 private JPanel classesPanel;
72 private OtherHighlightPainter otherHighlightPainter; 73 private JSplitPane splitClasses;
73 private SelectionHighlightPainter selectionHighlightPainter; 74 private PanelIdentifier infoPanel;
74 private JTree inheritanceTree; 75 private ObfuscatedHighlightPainter obfuscatedHighlightPainter;
75 private JTree implementationsTree; 76 private DeobfuscatedHighlightPainter deobfuscatedHighlightPainter;
76 private JTree callsTree; 77 private OtherHighlightPainter otherHighlightPainter;
77 private JList<Token> tokens; 78 private SelectionHighlightPainter selectionHighlightPainter;
78 private JTabbedPane tabs; 79 private JTree inheritanceTree;
79 80 private JTree implementationsTree;
80 // state 81 private JTree callsTree;
81 public EntryReference<Entry, Entry> reference; 82 private JList<Token> tokens;
82 83 private JTabbedPane tabs;
83 public JFileChooser jarFileChooser; 84
84 public JFileChooser enigmaMappingsFileChooser; 85 public Gui() {
85 86
86 public JFileChooser exportSourceFileChooser; 87 // init frame
87 public JFileChooser exportJarFileChooser; 88 this.frame = new JFrame(Constants.NAME);
88 89 final Container pane = this.frame.getContentPane();
89 public Gui() { 90 pane.setLayout(new BorderLayout());
90 91
91 // init frame 92 if (Boolean.parseBoolean(System.getProperty("enigma.catchExceptions", "true"))) {
92 this.frame = new JFrame(Constants.NAME); 93 // install a global exception handler to the event thread
93 final Container pane = this.frame.getContentPane(); 94 CrashDialog.init(this.frame);
94 pane.setLayout(new BorderLayout()); 95 Thread.setDefaultUncaughtExceptionHandler((thread, t) -> {
95 96 t.printStackTrace(System.err);
96 if (Boolean.parseBoolean(System.getProperty("enigma.catchExceptions", "true"))) { 97 if (!ExceptionIgnorer.shouldIgnore(t)) {
97 // install a global exception handler to the event thread 98 CrashDialog.show(t);
98 CrashDialog.init(this.frame); 99 }
99 Thread.setDefaultUncaughtExceptionHandler((thread, t) -> { 100 });
100 t.printStackTrace(System.err); 101 }
101 if (!ExceptionIgnorer.shouldIgnore(t)) { 102
102 CrashDialog.show(t); 103 this.controller = new GuiController(this);
103 } 104
104 }); 105 // init file choosers
105 } 106 this.jarFileChooser = new FileChooserFile();
106 107
107 this.controller = new GuiController(this); 108 this.enigmaMappingsFileChooser = new FileChooserAny();
108 109 this.exportSourceFileChooser = new FileChooserFolder();
109 // init file choosers 110 this.exportJarFileChooser = new FileChooserFile();
110 this.jarFileChooser = new FileChooserFile(); 111
111 112 this.obfPanel = new PanelObf(this);
112 113 this.deobfPanel = new PanelDeobf(this);
113 this.enigmaMappingsFileChooser = new FileChooserAny(); 114
114 this.exportSourceFileChooser = new FileChooserFolder(); 115 // set up classes panel (don't add the splitter yet)
115 this.exportJarFileChooser = new FileChooserFile(); 116 splitClasses = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, this.obfPanel, this.deobfPanel);
116 117 splitClasses.setResizeWeight(0.3);
117 this.obfPanel = new PanelObf(this); 118 this.classesPanel = new JPanel();
118 this.deobfPanel = new PanelDeobf(this); 119 this.classesPanel.setLayout(new BorderLayout());
119 120 this.classesPanel.setPreferredSize(new Dimension(250, 0));
120 // set up classes panel (don't add the splitter yet) 121
121 splitClasses = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, this.obfPanel, this.deobfPanel); 122 // init info panel
122 splitClasses.setResizeWeight(0.3); 123 infoPanel = new PanelIdentifier(this);
123 this.classesPanel = new JPanel(); 124 infoPanel.clearReference();
124 this.classesPanel.setLayout(new BorderLayout()); 125
125 this.classesPanel.setPreferredSize(new Dimension(250, 0)); 126 // init editor
126 127 DefaultSyntaxKit.initKit();
127 // init info panel 128 obfuscatedHighlightPainter = new ObfuscatedHighlightPainter();
128 infoPanel = new PanelIdentifier(this); 129 deobfuscatedHighlightPainter = new DeobfuscatedHighlightPainter();
129 infoPanel.clearReference(); 130 otherHighlightPainter = new OtherHighlightPainter();
130 131 selectionHighlightPainter = new SelectionHighlightPainter();
131 // init editor 132 this.editor = new PanelEditor(this);
132 DefaultSyntaxKit.initKit(); 133 JScrollPane sourceScroller = new JScrollPane(this.editor);
133 obfuscatedHighlightPainter = new ObfuscatedHighlightPainter(); 134 this.editor.setContentType("text/java");
134 deobfuscatedHighlightPainter = new DeobfuscatedHighlightPainter(); 135 DefaultSyntaxKit kit = (DefaultSyntaxKit) this.editor.getEditorKit();
135 otherHighlightPainter = new OtherHighlightPainter(); 136 kit.toggleComponent(this.editor, "de.sciss.syntaxpane.components.TokenMarker");
136 selectionHighlightPainter = new SelectionHighlightPainter(); 137
137 this.editor = new PanelEditor(this); 138 // init editor popup menu
138 JScrollPane sourceScroller = new JScrollPane(this.editor); 139 this.popupMenu = new PopupMenuBar(this);
139 this.editor.setContentType("text/java"); 140 this.editor.setComponentPopupMenu(this.popupMenu);
140 DefaultSyntaxKit kit = (DefaultSyntaxKit) this.editor.getEditorKit(); 141
141 kit.toggleComponent(this.editor, "de.sciss.syntaxpane.components.TokenMarker"); 142 // init inheritance panel
142 143 inheritanceTree = new JTree();
143 // init editor popup menu 144 inheritanceTree.setModel(null);
144 this.popupMenu = new PopupMenuBar(this); 145 inheritanceTree.addMouseListener(new MouseAdapter() {
145 this.editor.setComponentPopupMenu(this.popupMenu); 146 @Override
146 147 public void mouseClicked(MouseEvent event) {
147 // init inheritance panel 148 if (event.getClickCount() == 2) {
148 inheritanceTree = new JTree(); 149 // get the selected node
149 inheritanceTree.setModel(null); 150 TreePath path = inheritanceTree.getSelectionPath();
150 inheritanceTree.addMouseListener(new MouseAdapter() { 151 if (path == null) {
151 @Override 152 return;
152 public void mouseClicked(MouseEvent event) { 153 }
153 if (event.getClickCount() == 2) { 154
154 // get the selected node 155 Object node = path.getLastPathComponent();
155 TreePath path = inheritanceTree.getSelectionPath(); 156 if (node instanceof ClassInheritanceTreeNode) {
156 if (path == null) { 157 ClassInheritanceTreeNode classNode = (ClassInheritanceTreeNode) node;
157 return; 158 navigateTo(new ClassEntry(classNode.getObfClassName()));
158 } 159 } else if (node instanceof MethodInheritanceTreeNode) {
159 160 MethodInheritanceTreeNode methodNode = (MethodInheritanceTreeNode) node;
160 Object node = path.getLastPathComponent(); 161 if (methodNode.isImplemented()) {
161 if (node instanceof ClassInheritanceTreeNode) { 162 navigateTo(methodNode.getMethodEntry());
162 ClassInheritanceTreeNode classNode = (ClassInheritanceTreeNode) node; 163 }
163 navigateTo(new ClassEntry(classNode.getObfClassName())); 164 }
164 } else if (node instanceof MethodInheritanceTreeNode) { 165 }
165 MethodInheritanceTreeNode methodNode = (MethodInheritanceTreeNode) node; 166 }
166 if (methodNode.isImplemented()) { 167 });
167 navigateTo(methodNode.getMethodEntry()); 168 JPanel inheritancePanel = new JPanel();
168 } 169 inheritancePanel.setLayout(new BorderLayout());
169 } 170 inheritancePanel.add(new JScrollPane(inheritanceTree));
170 } 171
171 } 172 // init implementations panel
172 }); 173 implementationsTree = new JTree();
173 JPanel inheritancePanel = new JPanel(); 174 implementationsTree.setModel(null);
174 inheritancePanel.setLayout(new BorderLayout()); 175 implementationsTree.addMouseListener(new MouseAdapter() {
175 inheritancePanel.add(new JScrollPane(inheritanceTree)); 176 @Override
176 177 public void mouseClicked(MouseEvent event) {
177 // init implementations panel 178 if (event.getClickCount() == 2) {
178 implementationsTree = new JTree(); 179 // get the selected node
179 implementationsTree.setModel(null); 180 TreePath path = implementationsTree.getSelectionPath();
180 implementationsTree.addMouseListener(new MouseAdapter() { 181 if (path == null) {
181 @Override 182 return;
182 public void mouseClicked(MouseEvent event) { 183 }
183 if (event.getClickCount() == 2) { 184
184 // get the selected node 185 Object node = path.getLastPathComponent();
185 TreePath path = implementationsTree.getSelectionPath(); 186 if (node instanceof ClassImplementationsTreeNode) {
186 if (path == null) { 187 ClassImplementationsTreeNode classNode = (ClassImplementationsTreeNode) node;
187 return; 188 navigateTo(classNode.getClassEntry());
188 } 189 } else if (node instanceof MethodImplementationsTreeNode) {
189 190 MethodImplementationsTreeNode methodNode = (MethodImplementationsTreeNode) node;
190 Object node = path.getLastPathComponent(); 191 navigateTo(methodNode.getMethodEntry());
191 if (node instanceof ClassImplementationsTreeNode) { 192 }
192 ClassImplementationsTreeNode classNode = (ClassImplementationsTreeNode) node; 193 }
193 navigateTo(classNode.getClassEntry()); 194 }
194 } else if (node instanceof MethodImplementationsTreeNode) { 195 });
195 MethodImplementationsTreeNode methodNode = (MethodImplementationsTreeNode) node; 196 JPanel implementationsPanel = new JPanel();
196 navigateTo(methodNode.getMethodEntry()); 197 implementationsPanel.setLayout(new BorderLayout());
197 } 198 implementationsPanel.add(new JScrollPane(implementationsTree));
198 } 199
199 } 200 // init call panel
200 }); 201 callsTree = new JTree();
201 JPanel implementationsPanel = new JPanel(); 202 callsTree.setModel(null);
202 implementationsPanel.setLayout(new BorderLayout()); 203 callsTree.addMouseListener(new MouseAdapter() {
203 implementationsPanel.add(new JScrollPane(implementationsTree)); 204 @SuppressWarnings("unchecked")
204 205 @Override
205 // init call panel 206 public void mouseClicked(MouseEvent event) {
206 callsTree = new JTree(); 207 if (event.getClickCount() == 2) {
207 callsTree.setModel(null); 208 // get the selected node
208 callsTree.addMouseListener(new MouseAdapter() { 209 TreePath path = callsTree.getSelectionPath();
209 @SuppressWarnings("unchecked") 210 if (path == null) {
210 @Override 211 return;
211 public void mouseClicked(MouseEvent event) { 212 }
212 if (event.getClickCount() == 2) { 213
213 // get the selected node 214 Object node = path.getLastPathComponent();
214 TreePath path = callsTree.getSelectionPath(); 215 if (node instanceof ReferenceTreeNode) {
215 if (path == null) { 216 ReferenceTreeNode<Entry, Entry> referenceNode = ((ReferenceTreeNode<Entry, Entry>) node);
216 return; 217 if (referenceNode.getReference() != null) {
217 } 218 navigateTo(referenceNode.getReference());
218 219 } else {
219 Object node = path.getLastPathComponent(); 220 navigateTo(referenceNode.getEntry());
220 if (node instanceof ReferenceTreeNode) { 221 }
221 ReferenceTreeNode<Entry, Entry> referenceNode = ((ReferenceTreeNode<Entry, Entry>) node); 222 }
222 if (referenceNode.getReference() != null) { 223 }
223 navigateTo(referenceNode.getReference()); 224 }
224 } else { 225 });
225 navigateTo(referenceNode.getEntry()); 226 tokens = new JList<>();
226 } 227 tokens.setCellRenderer(new TokenListCellRenderer(this.controller));
227 } 228 tokens.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
228 } 229 tokens.setLayoutOrientation(JList.VERTICAL);
229 } 230 tokens.addMouseListener(new MouseAdapter() {
230 }); 231 @Override
231 tokens = new JList<>(); 232 public void mouseClicked(MouseEvent event) {
232 tokens.setCellRenderer(new TokenListCellRenderer(this.controller)); 233 if (event.getClickCount() == 2) {
233 tokens.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 234 Token selected = tokens.getSelectedValue();
234 tokens.setLayoutOrientation(JList.VERTICAL); 235 if (selected != null) {
235 tokens.addMouseListener(new MouseAdapter() { 236 showToken(selected);
236 @Override 237 }
237 public void mouseClicked(MouseEvent event) { 238 }
238 if (event.getClickCount() == 2) { 239 }
239 Token selected = tokens.getSelectedValue(); 240 });
240 if (selected != null) { 241 tokens.setPreferredSize(new Dimension(0, 200));
241 showToken(selected); 242 tokens.setMinimumSize(new Dimension(0, 200));
242 } 243 JSplitPane callPanel = new JSplitPane(
243 } 244 JSplitPane.VERTICAL_SPLIT,
244 } 245 true,
245 }); 246 new JScrollPane(callsTree),
246 tokens.setPreferredSize(new Dimension(0, 200)); 247 new JScrollPane(tokens)
247 tokens.setMinimumSize(new Dimension(0, 200)); 248 );
248 JSplitPane callPanel = new JSplitPane( 249 callPanel.setResizeWeight(1); // let the top side take all the slack
249 JSplitPane.VERTICAL_SPLIT, 250 callPanel.resetToPreferredSizes();
250 true, 251
251 new JScrollPane(callsTree), 252 // layout controls
252 new JScrollPane(tokens) 253 JPanel centerPanel = new JPanel();
253 ); 254 centerPanel.setLayout(new BorderLayout());
254 callPanel.setResizeWeight(1); // let the top side take all the slack 255 centerPanel.add(infoPanel, BorderLayout.NORTH);
255 callPanel.resetToPreferredSizes(); 256 centerPanel.add(sourceScroller, BorderLayout.CENTER);
256 257 tabs = new JTabbedPane();
257 // layout controls 258 tabs.setPreferredSize(new Dimension(250, 0));
258 JPanel centerPanel = new JPanel(); 259 tabs.addTab("Inheritance", inheritancePanel);
259 centerPanel.setLayout(new BorderLayout()); 260 tabs.addTab("Implementations", implementationsPanel);
260 centerPanel.add(infoPanel, BorderLayout.NORTH); 261 tabs.addTab("Call Graph", callPanel);
261 centerPanel.add(sourceScroller, BorderLayout.CENTER); 262 JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, tabs);
262 tabs = new JTabbedPane(); 263 splitRight.setResizeWeight(1); // let the left side take all the slack
263 tabs.setPreferredSize(new Dimension(250, 0)); 264 splitRight.resetToPreferredSizes();
264 tabs.addTab("Inheritance", inheritancePanel); 265 JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, this.classesPanel, splitRight);
265 tabs.addTab("Implementations", implementationsPanel); 266 splitCenter.setResizeWeight(0); // let the right side take all the slack
266 tabs.addTab("Call Graph", callPanel); 267 pane.add(splitCenter, BorderLayout.CENTER);
267 JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, tabs); 268
268 splitRight.setResizeWeight(1); // let the left side take all the slack 269 // init menus
269 splitRight.resetToPreferredSizes(); 270 this.menuBar = new MenuBar(this);
270 JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, this.classesPanel, splitRight); 271 this.frame.setJMenuBar(this.menuBar);
271 splitCenter.setResizeWeight(0); // let the right side take all the slack 272
272 pane.add(splitCenter, BorderLayout.CENTER); 273 // init state
273 274 onCloseJar();
274 // init menus 275
275 this.menuBar = new MenuBar(this); 276 this.frame.addWindowListener(new WindowAdapter() {
276 this.frame.setJMenuBar(this.menuBar); 277 @Override
277 278 public void windowClosing(WindowEvent event) {
278 // init state 279 close();
279 onCloseJar(); 280 }
280 281 });
281 this.frame.addWindowListener(new WindowAdapter() { 282
282 @Override 283 // show the frame
283 public void windowClosing(WindowEvent event) { 284 pane.doLayout();
284 close(); 285 this.frame.setSize(1024, 576);
285 } 286 this.frame.setMinimumSize(new Dimension(640, 480));
286 }); 287 this.frame.setVisible(true);
287 288 this.frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
288 // show the frame 289 }
289 pane.doLayout(); 290
290 this.frame.setSize(1024, 576); 291 public JFrame getFrame() {
291 this.frame.setMinimumSize(new Dimension(640, 480)); 292 return this.frame;
292 this.frame.setVisible(true); 293 }
293 this.frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); 294
294 } 295 public GuiController getController() {
295 296 return this.controller;
296 public JFrame getFrame() { 297 }
297 return this.frame; 298
298 } 299 public void onStartOpenJar() {
299 300 this.classesPanel.removeAll();
300 public GuiController getController() { 301 JPanel panel = new JPanel();
301 return this.controller; 302 panel.setLayout(new FlowLayout());
302 } 303 panel.add(new JLabel("Loading..."));
303 304 this.classesPanel.add(panel);
304 public void onStartOpenJar() { 305 redraw();
305 this.classesPanel.removeAll(); 306 }
306 JPanel panel = new JPanel(); 307
307 panel.setLayout(new FlowLayout()); 308 public void onFinishOpenJar(String jarName) {
308 panel.add(new JLabel("Loading...")); 309 // update gui
309 this.classesPanel.add(panel); 310 this.frame.setTitle(Constants.NAME + " - " + jarName);
310 redraw(); 311 this.classesPanel.removeAll();
311 } 312 this.classesPanel.add(splitClasses);
312 313 setSource(null);
313 public void onFinishOpenJar(String jarName) { 314
314 // update gui 315 // update menu
315 this.frame.setTitle(Constants.NAME + " - " + jarName); 316 this.menuBar.closeJarMenu.setEnabled(true);
316 this.classesPanel.removeAll(); 317 this.menuBar.openEnigmaMappingsMenu.setEnabled(true);
317 this.classesPanel.add(splitClasses); 318 this.menuBar.saveMappingsMenu.setEnabled(false);
318 setSource(null); 319 this.menuBar.saveMappingEnigmaFileMenu.setEnabled(true);
319 320 this.menuBar.saveMappingEnigmaDirectoryMenu.setEnabled(true);
320 // update menu 321 this.menuBar.saveMappingsSrgMenu.setEnabled(true);
321 this.menuBar.closeJarMenu.setEnabled(true); 322 this.menuBar.closeMappingsMenu.setEnabled(true);
322 this.menuBar.openEnigmaMappingsMenu.setEnabled(true); 323 this.menuBar.exportSourceMenu.setEnabled(true);
323 this.menuBar.saveMappingsMenu.setEnabled(false); 324 this.menuBar.exportJarMenu.setEnabled(true);
324 this.menuBar.saveMappingEnigmaFileMenu.setEnabled(true); 325
325 this.menuBar.saveMappingEnigmaDirectoryMenu.setEnabled(true); 326 redraw();
326 this.menuBar.saveMappingsSrgMenu.setEnabled(true); 327 }
327 this.menuBar.closeMappingsMenu.setEnabled(true); 328
328 this.menuBar.exportSourceMenu.setEnabled(true); 329 public void onCloseJar() {
329 this.menuBar.exportJarMenu.setEnabled(true); 330 // update gui
330 331 this.frame.setTitle(Constants.NAME);
331 redraw(); 332 setObfClasses(null);
332 } 333 setDeobfClasses(null);
333 334 setSource(null);
334 public void onCloseJar() { 335 this.classesPanel.removeAll();
335 // update gui 336
336 this.frame.setTitle(Constants.NAME); 337 // update menu
337 setObfClasses(null); 338 this.menuBar.closeJarMenu.setEnabled(false);
338 setDeobfClasses(null); 339 this.menuBar.openEnigmaMappingsMenu.setEnabled(false);
339 setSource(null); 340 this.menuBar.saveMappingsMenu.setEnabled(false);
340 this.classesPanel.removeAll(); 341 this.menuBar.saveMappingEnigmaFileMenu.setEnabled(false);
341 342 this.menuBar.saveMappingEnigmaDirectoryMenu.setEnabled(false);
342 // update menu 343 this.menuBar.saveMappingsSrgMenu.setEnabled(false);
343 this.menuBar.closeJarMenu.setEnabled(false); 344 this.menuBar.closeMappingsMenu.setEnabled(false);
344 this.menuBar.openEnigmaMappingsMenu.setEnabled(false); 345 this.menuBar.exportSourceMenu.setEnabled(false);
345 this.menuBar.saveMappingsMenu.setEnabled(false); 346 this.menuBar.exportJarMenu.setEnabled(false);
346 this.menuBar.saveMappingEnigmaFileMenu.setEnabled(false); 347
347 this.menuBar.saveMappingEnigmaDirectoryMenu.setEnabled(false); 348 redraw();
348 this.menuBar.saveMappingsSrgMenu.setEnabled(false); 349 }
349 this.menuBar.closeMappingsMenu.setEnabled(false); 350
350 this.menuBar.exportSourceMenu.setEnabled(false); 351 public void setObfClasses(Collection<ClassEntry> obfClasses) {
351 this.menuBar.exportJarMenu.setEnabled(false); 352 this.obfPanel.obfClasses.setClasses(obfClasses);
352 353 }
353 redraw(); 354
354 } 355 public void setDeobfClasses(Collection<ClassEntry> deobfClasses) {
355 356 this.deobfPanel.deobfClasses.setClasses(deobfClasses);
356 public void setObfClasses(Collection<ClassEntry> obfClasses) { 357 }
357 this.obfPanel.obfClasses.setClasses(obfClasses); 358
358 } 359 public void setMappingsFile(File file) {
359 360 this.enigmaMappingsFileChooser.setSelectedFile(file);
360 public void setDeobfClasses(Collection<ClassEntry> deobfClasses) { 361 this.menuBar.saveMappingsMenu.setEnabled(file != null);
361 this.deobfPanel.deobfClasses.setClasses(deobfClasses); 362 }
362 } 363
363 364 public void setSource(String source) {
364 public void setMappingsFile(File file) { 365 this.editor.getHighlighter().removeAllHighlights();
365 this.enigmaMappingsFileChooser.setSelectedFile(file); 366 this.editor.setText(source);
366 this.menuBar.saveMappingsMenu.setEnabled(file != null); 367 }
367 } 368
368 369 public void showToken(final Token token) {
369 public void setSource(String source) { 370 if (token == null) {
370 this.editor.getHighlighter().removeAllHighlights(); 371 throw new IllegalArgumentException("Token cannot be null!");
371 this.editor.setText(source); 372 }
372 } 373 CodeReader.navigateToToken(this.editor, token, selectionHighlightPainter);
373 374 redraw();
374 public void showToken(final Token token) { 375 }
375 if (token == null) { 376
376 throw new IllegalArgumentException("Token cannot be null!"); 377 public void showTokens(Collection<Token> tokens) {
377 } 378 Vector<Token> sortedTokens = new Vector<>(tokens);
378 CodeReader.navigateToToken(this.editor, token, selectionHighlightPainter); 379 Collections.sort(sortedTokens);
379 redraw(); 380 if (sortedTokens.size() > 1) {
380 } 381 // sort the tokens and update the tokens panel
381 382 this.tokens.setListData(sortedTokens);
382 public void showTokens(Collection<Token> tokens) { 383 this.tokens.setSelectedIndex(0);
383 Vector<Token> sortedTokens = new Vector<>(tokens); 384 } else {
384 Collections.sort(sortedTokens); 385 this.tokens.setListData(new Vector<>());
385 if (sortedTokens.size() > 1) { 386 }
386 // sort the tokens and update the tokens panel 387
387 this.tokens.setListData(sortedTokens); 388 // show the first token
388 this.tokens.setSelectedIndex(0); 389 showToken(sortedTokens.get(0));
389 } else { 390 }
390 this.tokens.setListData(new Vector<>()); 391
391 } 392 public void setHighlightedTokens(Iterable<Token> obfuscatedTokens, Iterable<Token> deobfuscatedTokens, Iterable<Token> otherTokens) {
392 393
393 // show the first token 394 // remove any old highlighters
394 showToken(sortedTokens.get(0)); 395 this.editor.getHighlighter().removeAllHighlights();
395 } 396
396 397 // color things based on the index
397 public void setHighlightedTokens(Iterable<Token> obfuscatedTokens, Iterable<Token> deobfuscatedTokens, Iterable<Token> otherTokens) { 398 if (obfuscatedTokens != null) {
398 399 setHighlightedTokens(obfuscatedTokens, obfuscatedHighlightPainter);
399 // remove any old highlighters 400 }
400 this.editor.getHighlighter().removeAllHighlights(); 401 if (deobfuscatedTokens != null) {
401 402 setHighlightedTokens(deobfuscatedTokens, deobfuscatedHighlightPainter);
402 // color things based on the index 403 }
403 if (obfuscatedTokens != null) { 404 if (otherTokens != null) {
404 setHighlightedTokens(obfuscatedTokens, obfuscatedHighlightPainter); 405 setHighlightedTokens(otherTokens, otherHighlightPainter);
405 } 406 }
406 if (deobfuscatedTokens != null) { 407
407 setHighlightedTokens(deobfuscatedTokens, deobfuscatedHighlightPainter); 408 redraw();
408 } 409 }
409 if (otherTokens != null) { 410
410 setHighlightedTokens(otherTokens, otherHighlightPainter); 411 private void setHighlightedTokens(Iterable<Token> tokens, Highlighter.HighlightPainter painter) {
411 } 412 for (Token token : tokens) {
412 413 try {
413 redraw(); 414 this.editor.getHighlighter().addHighlight(token.start, token.end, painter);
414 } 415 } catch (BadLocationException ex) {
415 416 throw new IllegalArgumentException(ex);
416 private void setHighlightedTokens(Iterable<Token> tokens, Highlighter.HighlightPainter painter) { 417 }
417 for (Token token : tokens) { 418 }
418 try { 419 }
419 this.editor.getHighlighter().addHighlight(token.start, token.end, painter); 420
420 } catch (BadLocationException ex) { 421 private void showReference(EntryReference<Entry, Entry> reference) {
421 throw new IllegalArgumentException(ex); 422 if (reference == null) {
422 } 423 infoPanel.clearReference();
423 } 424 return;
424 } 425 }
425 426
426 private void showReference(EntryReference<Entry, Entry> reference) { 427 this.reference = reference;
427 if (reference == null) { 428
428 infoPanel.clearReference(); 429 infoPanel.removeAll();
429 return; 430 if (reference.entry instanceof ClassEntry) {
430 } 431 showClassEntry((ClassEntry) this.reference.entry);
431 432 } else if (this.reference.entry instanceof FieldEntry) {
432 this.reference = reference; 433 showFieldEntry((FieldEntry) this.reference.entry);
433 434 } else if (this.reference.entry instanceof MethodEntry) {
434 infoPanel.removeAll(); 435 showMethodEntry((MethodEntry) this.reference.entry);
435 if (reference.entry instanceof ClassEntry) { 436 } else if (this.reference.entry instanceof ConstructorEntry) {
436 showClassEntry((ClassEntry) this.reference.entry); 437 showConstructorEntry((ConstructorEntry) this.reference.entry);
437 } else if (this.reference.entry instanceof FieldEntry) { 438 } else if (this.reference.entry instanceof ArgumentEntry) {
438 showFieldEntry((FieldEntry) this.reference.entry); 439 showArgumentEntry((ArgumentEntry) this.reference.entry);
439 } else if (this.reference.entry instanceof MethodEntry) { 440 } else if (this.reference.entry instanceof LocalVariableEntry) {
440 showMethodEntry((MethodEntry) this.reference.entry); 441 showLocalVariableEntry((LocalVariableEntry) this.reference.entry);
441 } else if (this.reference.entry instanceof ConstructorEntry) { 442 } else {
442 showConstructorEntry((ConstructorEntry) this.reference.entry); 443 throw new Error("Unknown entry type: " + this.reference.entry.getClass().getName());
443 } else if (this.reference.entry instanceof ArgumentEntry) { 444 }
444 showArgumentEntry((ArgumentEntry) this.reference.entry); 445
445 } else if (this.reference.entry instanceof LocalVariableEntry) { 446 redraw();
446 showLocalVariableEntry((LocalVariableEntry) this.reference.entry); 447 }
447 } else { 448
448 throw new Error("Unknown entry type: " + this.reference.entry.getClass().getName()); 449 private void showLocalVariableEntry(LocalVariableEntry entry) {
449 } 450 addNameValue(infoPanel, "Variable", entry.getName());
450 451 addNameValue(infoPanel, "Class", entry.getClassEntry().getName());
451 redraw(); 452 addNameValue(infoPanel, "Method", entry.getBehaviorEntry().getName());
452 } 453 addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex()));
453 454 addNameValue(infoPanel, "Type", entry.getType().toString());
454 private void showLocalVariableEntry(LocalVariableEntry entry) { 455 }
455 addNameValue(infoPanel, "Variable", entry.getName()); 456
456 addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); 457 private void showClassEntry(ClassEntry entry) {
457 addNameValue(infoPanel, "Method", entry.getBehaviorEntry().getName()); 458 addNameValue(infoPanel, "Class", entry.getName());
458 addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex())); 459 addModifierComboBox(infoPanel, "Modifier", entry);
459 addNameValue(infoPanel, "Type", entry.getType().toString()); 460 }
460 } 461
461 462 private void showFieldEntry(FieldEntry entry) {
462 private void showClassEntry(ClassEntry entry) { 463 addNameValue(infoPanel, "Field", entry.getName());
463 addNameValue(infoPanel, "Class", entry.getName()); 464 addNameValue(infoPanel, "Class", entry.getClassEntry().getName());
464 addModifierComboBox(infoPanel, "Modifier", entry); 465 addNameValue(infoPanel, "Type", entry.getType().toString());
465 } 466 addModifierComboBox(infoPanel, "Modifier", entry);
466 467 }
467 private void showFieldEntry(FieldEntry entry) { 468
468 addNameValue(infoPanel, "Field", entry.getName()); 469 private void showMethodEntry(MethodEntry entry) {
469 addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); 470 addNameValue(infoPanel, "Method", entry.getName());
470 addNameValue(infoPanel, "Type", entry.getType().toString()); 471 addNameValue(infoPanel, "Class", entry.getClassEntry().getName());
471 addModifierComboBox(infoPanel, "Modifier", entry); 472 addNameValue(infoPanel, "Signature", entry.getSignature().toString());
472 } 473 addModifierComboBox(infoPanel, "Modifier", entry);
473 474
474 private void showMethodEntry(MethodEntry entry) { 475 }
475 addNameValue(infoPanel, "Method", entry.getName()); 476
476 addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); 477 private void showConstructorEntry(ConstructorEntry entry) {
477 addNameValue(infoPanel, "Signature", entry.getSignature().toString()); 478 addNameValue(infoPanel, "Constructor", entry.getClassEntry().getName());
478 addModifierComboBox(infoPanel, "Modifier", entry); 479 if (!entry.isStatic()) {
479 480 addNameValue(infoPanel, "Signature", entry.getSignature().toString());
480 } 481 addModifierComboBox(infoPanel, "Modifier", entry);
481 482 }
482 private void showConstructorEntry(ConstructorEntry entry) { 483 }
483 addNameValue(infoPanel, "Constructor", entry.getClassEntry().getName()); 484
484 if (!entry.isStatic()) { 485 private void showArgumentEntry(ArgumentEntry entry) {
485 addNameValue(infoPanel, "Signature", entry.getSignature().toString()); 486 addNameValue(infoPanel, "Argument", entry.getName());
486 addModifierComboBox(infoPanel, "Modifier", entry); 487 addNameValue(infoPanel, "Class", entry.getClassEntry().getName());
487 } 488 addNameValue(infoPanel, "Method", entry.getBehaviorEntry().getName());
488 } 489 addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex()));
489 490 }
490 private void showArgumentEntry(ArgumentEntry entry) { 491
491 addNameValue(infoPanel, "Argument", entry.getName()); 492 private void addNameValue(JPanel container, String name, String value) {
492 addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); 493 JPanel panel = new JPanel();
493 addNameValue(infoPanel, "Method", entry.getBehaviorEntry().getName()); 494 panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0));
494 addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex())); 495 container.add(panel);
495 } 496
496 497 JLabel label = new JLabel(name + ":", JLabel.RIGHT);
497 private void addNameValue(JPanel container, String name, String value) { 498 label.setPreferredSize(new Dimension(100, label.getPreferredSize().height));
498 JPanel panel = new JPanel(); 499 panel.add(label);
499 panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0)); 500
500 container.add(panel); 501 panel.add(Utils.unboldLabel(new JLabel(value, JLabel.LEFT)));
501 502 }
502 JLabel label = new JLabel(name + ":", JLabel.RIGHT); 503
503 label.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); 504 private JComboBox<Mappings.EntryModifier> addModifierComboBox(JPanel container, String name, Entry entry) {
504 panel.add(label); 505 if (!getController().entryIsInJar(entry))
505 506 return null;
506 panel.add(Utils.unboldLabel(new JLabel(value, JLabel.LEFT))); 507 JPanel panel = new JPanel();
507 } 508 panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0));
508 509 container.add(panel);
509 private JComboBox<Mappings.EntryModifier> addModifierComboBox(JPanel container, String name, Entry entry) 510 JLabel label = new JLabel(name + ":", JLabel.RIGHT);
510 { 511 label.setPreferredSize(new Dimension(100, label.getPreferredSize().height));
511 if (!getController().entryIsInJar(entry)) 512 panel.add(label);
512 return null; 513 JComboBox<Mappings.EntryModifier> combo = new JComboBox<>(Mappings.EntryModifier.values());
513 JPanel panel = new JPanel(); 514 ((JLabel) combo.getRenderer()).setHorizontalAlignment(JLabel.LEFT);
514 panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0)); 515 combo.setPreferredSize(new Dimension(100, label.getPreferredSize().height));
515 container.add(panel); 516 combo.setSelectedIndex(getController().getDeobfuscator().getModifier(entry).ordinal());
516 JLabel label = new JLabel(name + ":", JLabel.RIGHT); 517 combo.addItemListener(getController()::modifierChange);
517 label.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); 518 panel.add(combo);
518 panel.add(label); 519 return combo;
519 JComboBox<Mappings.EntryModifier> combo = new JComboBox<>(Mappings.EntryModifier.values()); 520 }
520 ((JLabel)combo.getRenderer()).setHorizontalAlignment(JLabel.LEFT); 521
521 combo.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); 522 public void onCaretMove(int pos) {
522 combo.setSelectedIndex(getController().getDeobfuscator().getModifier(entry).ordinal()); 523
523 combo.addItemListener(getController()::modifierChange); 524 Token token = this.controller.getToken(pos);
524 panel.add(combo); 525 boolean isToken = token != null;
525 return combo; 526
526 } 527 reference = this.controller.getDeobfReference(token);
527 528 boolean isClassEntry = isToken && reference.entry instanceof ClassEntry;
528 public void onCaretMove(int pos) { 529 boolean isFieldEntry = isToken && reference.entry instanceof FieldEntry;
529 530 boolean isMethodEntry = isToken && reference.entry instanceof MethodEntry;
530 Token token = this.controller.getToken(pos); 531 boolean isConstructorEntry = isToken && reference.entry instanceof ConstructorEntry;
531 boolean isToken = token != null; 532 boolean isInJar = isToken && this.controller.entryIsInJar(reference.entry);
532 533 boolean isRenameable = isToken && this.controller.referenceIsRenameable(reference);
533 reference = this.controller.getDeobfReference(token); 534
534 boolean isClassEntry = isToken && reference.entry instanceof ClassEntry; 535 if (isToken) {
535 boolean isFieldEntry = isToken && reference.entry instanceof FieldEntry; 536 showReference(reference);
536 boolean isMethodEntry = isToken && reference.entry instanceof MethodEntry; 537 } else {
537 boolean isConstructorEntry = isToken && reference.entry instanceof ConstructorEntry; 538 infoPanel.clearReference();
538 boolean isInJar = isToken && this.controller.entryIsInJar(reference.entry); 539 }
539 boolean isRenameable = isToken && this.controller.referenceIsRenameable(reference); 540
540 541 this.popupMenu.renameMenu.setEnabled(isRenameable);
541 if (isToken) { 542 this.popupMenu.showInheritanceMenu.setEnabled(isClassEntry || isMethodEntry || isConstructorEntry);
542 showReference(reference); 543 this.popupMenu.showImplementationsMenu.setEnabled(isClassEntry || isMethodEntry);
543 } else { 544 this.popupMenu.showCallsMenu.setEnabled(isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry);
544 infoPanel.clearReference(); 545 this.popupMenu.openEntryMenu.setEnabled(isInJar && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry));
545 } 546 this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousLocation());
546 547 this.popupMenu.toggleMappingMenu.setEnabled(isRenameable);
547 this.popupMenu.renameMenu.setEnabled(isRenameable); 548
548 this.popupMenu.showInheritanceMenu.setEnabled(isClassEntry || isMethodEntry || isConstructorEntry); 549 if (isToken && this.controller.entryHasDeobfuscatedName(reference.entry)) {
549 this.popupMenu.showImplementationsMenu.setEnabled(isClassEntry || isMethodEntry); 550 this.popupMenu.toggleMappingMenu.setText("Reset to obfuscated");
550 this.popupMenu.showCallsMenu.setEnabled(isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry); 551 } else {
551 this.popupMenu.openEntryMenu.setEnabled(isInJar && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry)); 552 this.popupMenu.toggleMappingMenu.setText("Mark as deobfuscated");
552 this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousLocation()); 553 }
553 this.popupMenu.toggleMappingMenu.setEnabled(isRenameable); 554 }
554 555
555 if (isToken && this.controller.entryHasDeobfuscatedName(reference.entry)) { 556 public void navigateTo(Entry entry) {
556 this.popupMenu.toggleMappingMenu.setText("Reset to obfuscated"); 557 if (!this.controller.entryIsInJar(entry)) {
557 } else { 558 // entry is not in the jar. Ignore it
558 this.popupMenu.toggleMappingMenu.setText("Mark as deobfuscated"); 559 return;
559 } 560 }
560 } 561 if (reference != null) {
561 562 this.controller.savePreviousReference(reference);
562 public void navigateTo(Entry entry) { 563 }
563 if (!this.controller.entryIsInJar(entry)) { 564 this.controller.openDeclaration(entry);
564 // entry is not in the jar. Ignore it 565 }
565 return; 566
566 } 567 private void navigateTo(EntryReference<Entry, Entry> reference) {
567 if (reference != null) { 568 if (!this.controller.entryIsInJar(reference.getLocationClassEntry())) {
568 this.controller.savePreviousReference(reference); 569 return;
569 } 570 }
570 this.controller.openDeclaration(entry); 571 if (this.reference != null) {
571 } 572 this.controller.savePreviousReference(this.reference);
572 573 }
573 private void navigateTo(EntryReference<Entry, Entry> reference) { 574 this.controller.openReference(reference);
574 if (!this.controller.entryIsInJar(reference.getLocationClassEntry())) { 575 }
575 return; 576
576 } 577 public void startRename() {
577 if (this.reference != null) { 578
578 this.controller.savePreviousReference(this.reference); 579 // init the text box
579 } 580 final JTextField text = new JTextField();
580 this.controller.openReference(reference); 581 text.setText(reference.getNamableName());
581 } 582 text.setPreferredSize(new Dimension(360, text.getPreferredSize().height));
582 583 text.addKeyListener(new KeyAdapter() {
583 public void startRename() { 584 @Override
584 585 public void keyPressed(KeyEvent event) {
585 // init the text box 586 switch (event.getKeyCode()) {
586 final JTextField text = new JTextField(); 587 case KeyEvent.VK_ENTER:
587 text.setText(reference.getNamableName()); 588 finishRename(text, true);
588 text.setPreferredSize(new Dimension(360, text.getPreferredSize().height)); 589 break;
589 text.addKeyListener(new KeyAdapter() { 590
590 @Override 591 case KeyEvent.VK_ESCAPE:
591 public void keyPressed(KeyEvent event) { 592 finishRename(text, false);
592 switch (event.getKeyCode()) { 593 break;
593 case KeyEvent.VK_ENTER: 594 default:
594 finishRename(text, true); 595 break;
595 break; 596 }
596 597 }
597 case KeyEvent.VK_ESCAPE: 598 });
598 finishRename(text, false); 599
599 break; 600 // find the label with the name and replace it with the text box
600 default: 601 JPanel panel = (JPanel) infoPanel.getComponent(0);
601 break; 602 panel.remove(panel.getComponentCount() - 1);
602 } 603 panel.add(text);
603 } 604 text.grabFocus();
604 }); 605
605 606 int offset = text.getText().lastIndexOf('/') + 1;
606 // find the label with the name and replace it with the text box 607 // If it's a class and isn't in the default package, assume that it's deobfuscated.
607 JPanel panel = (JPanel) infoPanel.getComponent(0); 608 if (reference.getNameableEntry() instanceof ClassEntry && text.getText().contains("/") && offset != 0)
608 panel.remove(panel.getComponentCount() - 1); 609 text.select(offset, text.getText().length());
609 panel.add(text); 610 else
610 text.grabFocus(); 611 text.selectAll();
611 612
612 int offset = text.getText().lastIndexOf('/') + 1; 613 redraw();
613 // If it's a class and isn't in the default package, assume that it's deobfuscated. 614 }
614 if (reference.getNameableEntry() instanceof ClassEntry && text.getText().contains("/") && offset != 0) 615
615 text.select(offset, text.getText().length()); 616 private void finishRename(JTextField text, boolean saveName) {
616 else 617 String newName = text.getText();
617 text.selectAll(); 618 if (saveName && newName != null && !newName.isEmpty()) {
618 619 try {
619 redraw(); 620 this.controller.rename(reference, newName);
620 } 621 } catch (IllegalNameException ex) {
621 622 text.setBorder(BorderFactory.createLineBorder(Color.red, 1));
622 private void finishRename(JTextField text, boolean saveName) { 623 text.setToolTipText(ex.getReason());
623 String newName = text.getText(); 624 Utils.showToolTipNow(text);
624 if (saveName && newName != null && newName.length() > 0) { 625 }
625 try { 626 return;
626 this.controller.rename(reference, newName); 627 }
627 } catch (IllegalNameException ex) { 628
628 text.setBorder(BorderFactory.createLineBorder(Color.red, 1)); 629 // abort the rename
629 text.setToolTipText(ex.getReason()); 630 JPanel panel = (JPanel) infoPanel.getComponent(0);
630 Utils.showToolTipNow(text); 631 panel.remove(panel.getComponentCount() - 1);
631 } 632 panel.add(Utils.unboldLabel(new JLabel(reference.getNamableName(), JLabel.LEFT)));
632 return; 633
633 } 634 this.editor.grabFocus();
634 635
635 // abort the rename 636 redraw();
636 JPanel panel = (JPanel) infoPanel.getComponent(0); 637 }
637 panel.remove(panel.getComponentCount() - 1); 638
638 panel.add(Utils.unboldLabel(new JLabel(reference.getNamableName(), JLabel.LEFT))); 639 public void showInheritance() {
639 640
640 this.editor.grabFocus(); 641 if (reference == null) {
641 642 return;
642 redraw(); 643 }
643 } 644
644 645 inheritanceTree.setModel(null);
645 public void showInheritance() { 646
646 647 if (reference.entry instanceof ClassEntry) {
647 if (reference == null) { 648 // get the class inheritance
648 return; 649 ClassInheritanceTreeNode classNode = this.controller.getClassInheritance((ClassEntry) reference.entry);
649 } 650
650 651 // show the tree at the root
651 inheritanceTree.setModel(null); 652 TreePath path = getPathToRoot(classNode);
652 653 inheritanceTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0)));
653 if (reference.entry instanceof ClassEntry) { 654 inheritanceTree.expandPath(path);
654 // get the class inheritance 655 inheritanceTree.setSelectionRow(inheritanceTree.getRowForPath(path));
655 ClassInheritanceTreeNode classNode = this.controller.getClassInheritance((ClassEntry) reference.entry); 656 } else if (reference.entry instanceof MethodEntry) {
656 657 // get the method inheritance
657 // show the tree at the root 658 MethodInheritanceTreeNode classNode = this.controller.getMethodInheritance((MethodEntry) reference.entry);
658 TreePath path = getPathToRoot(classNode); 659
659 inheritanceTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); 660 // show the tree at the root
660 inheritanceTree.expandPath(path); 661 TreePath path = getPathToRoot(classNode);
661 inheritanceTree.setSelectionRow(inheritanceTree.getRowForPath(path)); 662 inheritanceTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0)));
662 } else if (reference.entry instanceof MethodEntry) { 663 inheritanceTree.expandPath(path);
663 // get the method inheritance 664 inheritanceTree.setSelectionRow(inheritanceTree.getRowForPath(path));
664 MethodInheritanceTreeNode classNode = this.controller.getMethodInheritance((MethodEntry) reference.entry); 665 }
665 666
666 // show the tree at the root 667 tabs.setSelectedIndex(0);
667 TreePath path = getPathToRoot(classNode); 668 redraw();
668 inheritanceTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); 669 }
669 inheritanceTree.expandPath(path); 670
670 inheritanceTree.setSelectionRow(inheritanceTree.getRowForPath(path)); 671 public void showImplementations() {
671 } 672
672 673 if (reference == null) {
673 tabs.setSelectedIndex(0); 674 return;
674 redraw(); 675 }
675 } 676
676 677 implementationsTree.setModel(null);
677 public void showImplementations() { 678
678 679 DefaultMutableTreeNode node = null;
679 if (reference == null) { 680
680 return; 681 // get the class implementations
681 } 682 if (reference.entry instanceof ClassEntry)
682 683 node = this.controller.getClassImplementations((ClassEntry) reference.entry);
683 implementationsTree.setModel(null); 684 else // get the method implementations
684 685 if (reference.entry instanceof MethodEntry)
685 DefaultMutableTreeNode node = null; 686 node = this.controller.getMethodImplementations((MethodEntry) reference.entry);
686 687
687 // get the class implementations 688 if (node != null) {
688 if (reference.entry instanceof ClassEntry) 689 // show the tree at the root
689 node = this.controller.getClassImplementations((ClassEntry) reference.entry); 690 TreePath path = getPathToRoot(node);
690 else // get the method implementations 691 implementationsTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0)));
691 if (reference.entry instanceof MethodEntry) 692 implementationsTree.expandPath(path);
692 node = this.controller.getMethodImplementations((MethodEntry) reference.entry); 693 implementationsTree.setSelectionRow(implementationsTree.getRowForPath(path));
693 694 }
694 if (node != null) { 695
695 // show the tree at the root 696 tabs.setSelectedIndex(1);
696 TreePath path = getPathToRoot(node); 697 redraw();
697 implementationsTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); 698 }
698 implementationsTree.expandPath(path); 699
699 implementationsTree.setSelectionRow(implementationsTree.getRowForPath(path)); 700 public void showCalls() {
700 } 701 if (reference == null) {
701 702 return;
702 tabs.setSelectedIndex(1); 703 }
703 redraw(); 704
704 } 705 if (reference.entry instanceof ClassEntry) {
705 706 // look for calls to the default constructor
706 public void showCalls() { 707 // TODO: get a list of all the constructors and find calls to all of them
707 if (reference == null) { 708 BehaviorReferenceTreeNode node = this.controller.getMethodReferences(new ConstructorEntry((ClassEntry) reference.entry, new Signature("()V")));
708 return; 709 callsTree.setModel(new DefaultTreeModel(node));
709 } 710 } else if (reference.entry instanceof FieldEntry) {
710 711 FieldReferenceTreeNode node = this.controller.getFieldReferences((FieldEntry) reference.entry);
711 if (reference.entry instanceof ClassEntry) { 712 callsTree.setModel(new DefaultTreeModel(node));
712 // look for calls to the default constructor 713 } else if (reference.entry instanceof MethodEntry) {
713 // TODO: get a list of all the constructors and find calls to all of them 714 BehaviorReferenceTreeNode node = this.controller.getMethodReferences((MethodEntry) reference.entry);
714 BehaviorReferenceTreeNode node = this.controller.getMethodReferences(new ConstructorEntry((ClassEntry) reference.entry, new Signature("()V"))); 715 callsTree.setModel(new DefaultTreeModel(node));
715 callsTree.setModel(new DefaultTreeModel(node)); 716 } else if (reference.entry instanceof ConstructorEntry) {
716 } else if (reference.entry instanceof FieldEntry) { 717 BehaviorReferenceTreeNode node = this.controller.getMethodReferences((ConstructorEntry) reference.entry);
717 FieldReferenceTreeNode node = this.controller.getFieldReferences((FieldEntry) reference.entry); 718 callsTree.setModel(new DefaultTreeModel(node));
718 callsTree.setModel(new DefaultTreeModel(node)); 719 }
719 } else if (reference.entry instanceof MethodEntry) { 720
720 BehaviorReferenceTreeNode node = this.controller.getMethodReferences((MethodEntry) reference.entry); 721 tabs.setSelectedIndex(2);
721 callsTree.setModel(new DefaultTreeModel(node)); 722 redraw();
722 } else if (reference.entry instanceof ConstructorEntry) { 723 }
723 BehaviorReferenceTreeNode node = this.controller.getMethodReferences((ConstructorEntry) reference.entry); 724
724 callsTree.setModel(new DefaultTreeModel(node)); 725 public void toggleMapping() {
725 } 726 if (this.controller.entryHasDeobfuscatedName(reference.entry)) {
726 727 this.controller.removeMapping(reference);
727 tabs.setSelectedIndex(2); 728 } else {
728 redraw(); 729 this.controller.markAsDeobfuscated(reference);
729 } 730 }
730 731 }
731 public void toggleMapping() { 732
732 if (this.controller.entryHasDeobfuscatedName(reference.entry)) { 733 private TreePath getPathToRoot(TreeNode node) {
733 this.controller.removeMapping(reference); 734 List<TreeNode> nodes = Lists.newArrayList();
734 } else { 735 TreeNode n = node;
735 this.controller.markAsDeobfuscated(reference); 736 do {
736 } 737 nodes.add(n);
737 } 738 n = n.getParent();
738 739 } while (n != null);
739 private TreePath getPathToRoot(TreeNode node) { 740 Collections.reverse(nodes);
740 List<TreeNode> nodes = Lists.newArrayList(); 741 return new TreePath(nodes.toArray());
741 TreeNode n = node; 742 }
742 do { 743
743 nodes.add(n); 744 public void showDiscardDiag(Function<Integer, Void> callback, String... options) {
744 n = n.getParent(); 745 int response = JOptionPane.showOptionDialog(this.frame, "Your mappings have not been saved yet. Do you want to save?", "Save your changes?", JOptionPane.YES_NO_CANCEL_OPTION,
745 } while (n != null); 746 JOptionPane.QUESTION_MESSAGE, null, options, options[2]);
746 Collections.reverse(nodes); 747 callback.apply(response);
747 return new TreePath(nodes.toArray()); 748 }
748 } 749
749 750 public void saveMapping() throws IOException {
750 public void showDiscardDiag(Function<Integer, Void> callback, String... options) 751 if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.frame) == JFileChooser.APPROVE_OPTION)
751 { 752 this.controller.saveMappings(this.enigmaMappingsFileChooser.getSelectedFile());
752 int response = JOptionPane.showOptionDialog(this.frame, "Your mappings have not been saved yet. Do you want to save?", "Save your changes?", JOptionPane.YES_NO_CANCEL_OPTION, 753 }
753 JOptionPane.QUESTION_MESSAGE, null, options, options[2]); 754
754 callback.apply(response); 755 public void close() {
755 } 756 if (!this.controller.isDirty()) {
756 757 // everything is saved, we can exit safely
757 public void saveMapping() throws IOException 758 this.frame.dispose();
758 { 759 System.exit(0);
759 if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.frame) == JFileChooser.APPROVE_OPTION) 760 } else {
760 this.controller.saveMappings(this.enigmaMappingsFileChooser.getSelectedFile()); 761 // ask to save before closing
761 } 762 showDiscardDiag((response) -> {
762 763 if (response == JOptionPane.YES_OPTION) {
763 public void close() { 764 try {
764 if (!this.controller.isDirty()) { 765 this.saveMapping();
765 // everything is saved, we can exit safely 766 this.frame.dispose();
766 this.frame.dispose(); 767
767 System.exit(0); 768 } catch (IOException ex) {
768 } else { 769 throw new Error(ex);
769 // ask to save before closing 770 }
770 showDiscardDiag((response) -> { 771 } else if (response == JOptionPane.NO_OPTION)
771 if (response == JOptionPane.YES_OPTION) 772 this.frame.dispose();
772 { 773
773 try { 774 return null;
774 this.saveMapping(); 775 }, "Save and exit", "Discard changes", "Cancel");
775 this.frame.dispose(); 776 }
776 777 }
777 } catch (IOException ex) { 778
778 throw new Error(ex); 779 public void redraw() {
779 } 780 this.frame.validate();
780 } 781 this.frame.repaint();
781 else if (response == JOptionPane.NO_OPTION) 782 }
782 this.frame.dispose(); 783
783 784 public void onPanelRename(Object prevData, Object data, DefaultMutableTreeNode node) throws IllegalNameException {
784 return null; 785 // package rename
785 }, "Save and exit", "Discard changes", "Cancel"); 786 if (data instanceof String) {
786 } 787 for (int i = 0; i < node.getChildCount(); i++) {
787 } 788 data = Descriptor.toJvmName((String) data);
788 789 DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) node.getChildAt(i);
789 public void redraw() { 790 ClassEntry prevDataChild = (ClassEntry) childNode.getUserObject();
790 this.frame.validate(); 791 ClassEntry dataChild = new ClassEntry(data + "/" + prevDataChild.getSimpleName());
791 this.frame.repaint(); 792 this.controller.rename(new EntryReference<>(prevDataChild, prevDataChild.getName()), dataChild.getName(), false, i + 1 == node.getChildCount());
792 } 793 childNode.setUserObject(dataChild);
793 794 }
794 public void onPanelRename(Object prevData, Object data, DefaultMutableTreeNode node) throws IllegalNameException 795 node.setUserObject(data);
795 { 796 // Ob package will never be modified, just reload deob view
796 // package rename 797 this.deobfPanel.deobfClasses.reload();
797 if (data instanceof String) 798 }
798 { 799 // class rename
799 for (int i = 0; i < node.getChildCount(); i++) 800 else if (data instanceof ClassEntry)
800 { 801 this.controller.rename(new EntryReference<>((ClassEntry) prevData, ((ClassEntry) prevData).getName()), ((ClassEntry) data).getName(), false, true);
801 data = Descriptor.toJvmName((String) data); 802 }
802 DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) node.getChildAt(i); 803
803 ClassEntry prevDataChild = (ClassEntry) childNode.getUserObject(); 804 public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName) {
804 ClassEntry dataChild = new ClassEntry(data + "/" + prevDataChild.getSimpleName()); 805 String oldEntry = deobfReference.entry.getClassEntry().getPackageName();
805 this.controller.rename(new EntryReference<>(prevDataChild, prevDataChild.getName()), dataChild.getName(), false, i + 1 == node.getChildCount()); 806 String newEntry = new ClassEntry(Descriptor.toJvmName(newName)).getPackageName();
806 childNode.setUserObject(dataChild); 807 moveClassTree(deobfReference, newName, oldEntry == null,
807 } 808 newEntry == null);
808 node.setUserObject(data); 809 }
809 // Ob package will never be modified, just reload deob view 810
810 this.deobfPanel.deobfClasses.reload(); 811 public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName, boolean isOldOb, boolean isNewOb) {
811 } 812 ClassEntry oldEntry = deobfReference.entry.getClassEntry();
812 // class rename 813 ClassEntry newEntry = new ClassEntry(Descriptor.toJvmName(newName));
813 else if (data instanceof ClassEntry) 814
814 this.controller.rename(new EntryReference<>((ClassEntry) prevData, ((ClassEntry) prevData).getName()), ((ClassEntry) data).getName(), false, true); 815 // Ob -> deob
815 } 816 if (isOldOb && !isNewOb) {
816 817 this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, obfPanel.obfClasses);
817 public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName) 818 ClassSelectorPackageNode packageNode = this.obfPanel.obfClasses.getPackageNode(oldEntry);
818 { 819 this.obfPanel.obfClasses.removeNode(packageNode, oldEntry);
819 String oldEntry = deobfReference.entry.getClassEntry().getPackageName(); 820 this.obfPanel.obfClasses.removeNodeIfEmpty(packageNode);
820 String newEntry = new ClassEntry(Descriptor.toJvmName(newName)).getPackageName(); 821 this.deobfPanel.deobfClasses.reload();
821 moveClassTree(deobfReference, newName, oldEntry == null, 822 this.obfPanel.obfClasses.reload();
822 newEntry == null); 823 }
823 } 824 // Deob -> ob
824 825 else if (isNewOb && !isOldOb) {
825 public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName, boolean isOldOb, boolean isNewOb) 826 this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, deobfPanel.deobfClasses);
826 { 827 ClassSelectorPackageNode packageNode = this.deobfPanel.deobfClasses.getPackageNode(oldEntry);
827 ClassEntry oldEntry = deobfReference.entry.getClassEntry(); 828 this.deobfPanel.deobfClasses.removeNode(packageNode, oldEntry);
828 ClassEntry newEntry = new ClassEntry(Descriptor.toJvmName(newName)); 829 this.deobfPanel.deobfClasses.removeNodeIfEmpty(packageNode);
829 830 this.deobfPanel.deobfClasses.reload();
830 // Ob -> deob 831 this.obfPanel.obfClasses.reload();
831 if (isOldOb && !isNewOb) 832 }
832 { 833 // Local move
833 this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, obfPanel.obfClasses); 834 else if (isOldOb) {
834 ClassSelectorPackageNode packageNode = this.obfPanel.obfClasses.getPackageNode(oldEntry); 835 this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, null);
835 this.obfPanel.obfClasses.removeNode(packageNode, oldEntry); 836 this.obfPanel.obfClasses.removeNodeIfEmpty(this.obfPanel.obfClasses.getPackageNode(oldEntry));
836 this.obfPanel.obfClasses.removeNodeIfEmpty(packageNode); 837 this.obfPanel.obfClasses.reload();
837 this.deobfPanel.deobfClasses.reload(); 838 } else {
838 this.obfPanel.obfClasses.reload(); 839 this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, null);
839 } 840 this.deobfPanel.deobfClasses.removeNodeIfEmpty(this.deobfPanel.deobfClasses.getPackageNode(oldEntry));
840 // Deob -> ob 841 this.deobfPanel.deobfClasses.reload();
841 else if (isNewOb && !isOldOb) 842 }
842 { 843 }
843 this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, deobfPanel.deobfClasses);
844 ClassSelectorPackageNode packageNode = this.deobfPanel.deobfClasses.getPackageNode(oldEntry);
845 this.deobfPanel.deobfClasses.removeNode(packageNode, oldEntry);
846 this.deobfPanel.deobfClasses.removeNodeIfEmpty(packageNode);
847 this.deobfPanel.deobfClasses.reload();
848 this.obfPanel.obfClasses.reload();
849 }
850 // Local move
851 else if (isOldOb)
852 {
853 this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, null);
854 this.obfPanel.obfClasses.removeNodeIfEmpty(this.obfPanel.obfClasses.getPackageNode(oldEntry));
855 this.obfPanel.obfClasses.reload();
856 }
857 else
858 {
859 this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, null);
860 this.deobfPanel.deobfClasses.removeNodeIfEmpty(this.deobfPanel.deobfClasses.getPackageNode(oldEntry));
861 this.deobfPanel.deobfClasses.reload();
862 }
863 }
864} 844}
diff --git a/src/main/java/cuchaz/enigma/gui/GuiController.java b/src/main/java/cuchaz/enigma/gui/GuiController.java
index 68fd4843..1b461da7 100644
--- a/src/main/java/cuchaz/enigma/gui/GuiController.java
+++ b/src/main/java/cuchaz/enigma/gui/GuiController.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.gui; 12package cuchaz.enigma.gui;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
@@ -30,326 +31,321 @@ import java.util.jar.JarFile;
30 31
31public class GuiController { 32public class GuiController {
32 33
33 private Deobfuscator deobfuscator; 34 private Deobfuscator deobfuscator;
34 private Gui gui; 35 private Gui gui;
35 private SourceIndex index; 36 private SourceIndex index;
36 private ClassEntry currentObfClass; 37 private ClassEntry currentObfClass;
37 private boolean isDirty; 38 private boolean isDirty;
38 private Deque<EntryReference<Entry, Entry>> referenceStack; 39 private Deque<EntryReference<Entry, Entry>> referenceStack;
39 40
40 public GuiController(Gui gui) { 41 public GuiController(Gui gui) {
41 this.gui = gui; 42 this.gui = gui;
42 this.deobfuscator = null; 43 this.deobfuscator = null;
43 this.index = null; 44 this.index = null;
44 this.currentObfClass = null; 45 this.currentObfClass = null;
45 this.isDirty = false; 46 this.isDirty = false;
46 this.referenceStack = Queues.newArrayDeque(); 47 this.referenceStack = Queues.newArrayDeque();
47 } 48 }
48 49
49 public boolean isDirty() { 50 public boolean isDirty() {
50 return this.isDirty; 51 return this.isDirty;
51 } 52 }
52 53
53 public void openJar(final JarFile jar) { 54 public void openJar(final JarFile jar) {
54 this.gui.onStartOpenJar(); 55 this.gui.onStartOpenJar();
55 this.deobfuscator = new Deobfuscator(jar); 56 this.deobfuscator = new Deobfuscator(jar);
56 this.gui.onFinishOpenJar(this.deobfuscator.getJarName()); 57 this.gui.onFinishOpenJar(this.deobfuscator.getJarName());
57 refreshClasses(); 58 refreshClasses();
58 } 59 }
59 60
60 public void closeJar() { 61 public void closeJar() {
61 this.deobfuscator = null; 62 this.deobfuscator = null;
62 this.gui.onCloseJar(); 63 this.gui.onCloseJar();
63 } 64 }
64 65
65 public void openEnigmaMappings(File file) throws IOException, MappingParseException { 66 public void openEnigmaMappings(File file) throws IOException, MappingParseException {
66 this.deobfuscator.setMappings(new MappingsEnigmaReader().read(file)); 67 this.deobfuscator.setMappings(new MappingsEnigmaReader().read(file));
67 this.isDirty = false; 68 this.isDirty = false;
68 this.gui.setMappingsFile(file); 69 this.gui.setMappingsFile(file);
69 refreshClasses(); 70 refreshClasses();
70 refreshCurrentClass(); 71 refreshCurrentClass();
71 } 72 }
72 73
73 public void saveMappings(File file) throws IOException { 74 public void saveMappings(File file) throws IOException {
74 Mappings mappings = this.deobfuscator.getMappings(); 75 Mappings mappings = this.deobfuscator.getMappings();
75 switch (mappings.getOriginMappingFormat()) 76 switch (mappings.getOriginMappingFormat()) {
76 { 77 case SRG_FILE:
77 case SRG_FILE: 78 saveSRGMappings(file);
78 saveSRGMappings(file); 79 break;
79 break; 80 default:
80 default: 81 saveEnigmaMappings(file, Mappings.FormatType.ENIGMA_FILE != mappings.getOriginMappingFormat());
81 saveEnigmaMappings(file, Mappings.FormatType.ENIGMA_FILE != mappings.getOriginMappingFormat()); 82 break;
82 break; 83 }
83 } 84
84 85 }
85 } 86
86 87 public void saveEnigmaMappings(File file, boolean isDirectoryFormat) throws IOException {
87 public void saveEnigmaMappings(File file, boolean isDirectoryFormat) throws IOException { 88 this.deobfuscator.getMappings().saveEnigmaMappings(file, isDirectoryFormat);
88 this.deobfuscator.getMappings().saveEnigmaMappings(file, isDirectoryFormat); 89 this.isDirty = false;
89 this.isDirty = false; 90 }
90 } 91
91 92 public void saveSRGMappings(File file) throws IOException {
92 public void saveSRGMappings(File file) throws IOException { 93 this.deobfuscator.getMappings().saveSRGMappings(file);
93 this.deobfuscator.getMappings().saveSRGMappings(file); 94 this.isDirty = false;
94 this.isDirty = false; 95 }
95 } 96
96 97 public void closeMappings() {
97 public void closeMappings() { 98 this.deobfuscator.setMappings(null);
98 this.deobfuscator.setMappings(null); 99 this.gui.setMappingsFile(null);
99 this.gui.setMappingsFile(null); 100 refreshClasses();
100 refreshClasses(); 101 refreshCurrentClass();
101 refreshCurrentClass(); 102 }
102 } 103
103 104 public void rebuildMethodNames() {
104 public void rebuildMethodNames() { 105 ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.rebuildMethodNames(progress));
105 ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.rebuildMethodNames(progress)); 106 }
106 } 107
107 108 public void exportSource(final File dirOut) {
108 public void exportSource(final File dirOut) { 109 ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeSources(dirOut, progress));
109 ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeSources(dirOut, progress)); 110 }
110 } 111
111 112 public void exportJar(final File fileOut) {
112 public void exportJar(final File fileOut) { 113 ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeJar(fileOut, progress));
113 ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeJar(fileOut, progress)); 114 }
114 } 115
115 116 public Token getToken(int pos) {
116 public Token getToken(int pos) { 117 if (this.index == null) {
117 if (this.index == null) { 118 return null;
118 return null; 119 }
119 } 120 return this.index.getReferenceToken(pos);
120 return this.index.getReferenceToken(pos); 121 }
121 } 122
122 123 public EntryReference<Entry, Entry> getDeobfReference(Token token) {
123 public EntryReference<Entry, Entry> getDeobfReference(Token token) { 124 if (this.index == null) {
124 if (this.index == null) { 125 return null;
125 return null; 126 }
126 } 127 return this.index.getDeobfReference(token);
127 return this.index.getDeobfReference(token); 128 }
128 } 129
129 130 public ReadableToken getReadableToken(Token token) {
130 public ReadableToken getReadableToken(Token token) { 131 if (this.index == null) {
131 if (this.index == null) { 132 return null;
132 return null; 133 }
133 } 134 return new ReadableToken(
134 return new ReadableToken( 135 this.index.getLineNumber(token.start),
135 this.index.getLineNumber(token.start), 136 this.index.getColumnNumber(token.start),
136 this.index.getColumnNumber(token.start), 137 this.index.getColumnNumber(token.end)
137 this.index.getColumnNumber(token.end) 138 );
138 ); 139 }
139 } 140
140 141 public boolean entryHasDeobfuscatedName(Entry deobfEntry) {
141 public boolean entryHasDeobfuscatedName(Entry deobfEntry) { 142 return this.deobfuscator.hasDeobfuscatedName(this.deobfuscator.obfuscateEntry(deobfEntry));
142 return this.deobfuscator.hasDeobfuscatedName(this.deobfuscator.obfuscateEntry(deobfEntry)); 143 }
143 } 144
144 145 public boolean entryIsInJar(Entry deobfEntry) {
145 public boolean entryIsInJar(Entry deobfEntry) { 146 return this.deobfuscator.isObfuscatedIdentifier(this.deobfuscator.obfuscateEntry(deobfEntry));
146 return this.deobfuscator.isObfuscatedIdentifier(this.deobfuscator.obfuscateEntry(deobfEntry)); 147 }
147 } 148
148 149 public boolean referenceIsRenameable(EntryReference<Entry, Entry> deobfReference) {
149 public boolean referenceIsRenameable(EntryReference<Entry, Entry> deobfReference) { 150 return this.deobfuscator.isRenameable(this.deobfuscator.obfuscateReference(deobfReference), true);
150 return this.deobfuscator.isRenameable(this.deobfuscator.obfuscateReference(deobfReference), true); 151 }
151 } 152
152 153 public ClassInheritanceTreeNode getClassInheritance(ClassEntry deobfClassEntry) {
153 public ClassInheritanceTreeNode getClassInheritance(ClassEntry deobfClassEntry) { 154 ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry);
154 ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry); 155 ClassInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getClassInheritance(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfClassEntry);
155 ClassInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getClassInheritance(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfClassEntry); 156 return ClassInheritanceTreeNode.findNode(rootNode, obfClassEntry);
156 return ClassInheritanceTreeNode.findNode(rootNode, obfClassEntry); 157 }
157 } 158
158 159 public ClassImplementationsTreeNode getClassImplementations(ClassEntry deobfClassEntry) {
159 public ClassImplementationsTreeNode getClassImplementations(ClassEntry deobfClassEntry) { 160 ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry);
160 ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry); 161 return this.deobfuscator.getJarIndex().getClassImplementations(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfClassEntry);
161 return this.deobfuscator.getJarIndex().getClassImplementations(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfClassEntry); 162 }
162 } 163
163 164 public MethodInheritanceTreeNode getMethodInheritance(MethodEntry deobfMethodEntry) {
164 public MethodInheritanceTreeNode getMethodInheritance(MethodEntry deobfMethodEntry) { 165 MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry);
165 MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry); 166 MethodInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getMethodInheritance(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfMethodEntry);
166 MethodInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getMethodInheritance(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfMethodEntry); 167 return MethodInheritanceTreeNode.findNode(rootNode, obfMethodEntry);
167 return MethodInheritanceTreeNode.findNode(rootNode, obfMethodEntry); 168 }
168 } 169
169 170 public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) {
170 public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) { 171 MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry);
171 MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry); 172 List<MethodImplementationsTreeNode> rootNodes = this.deobfuscator.getJarIndex().getMethodImplementations(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfMethodEntry);
172 List<MethodImplementationsTreeNode> rootNodes = this.deobfuscator.getJarIndex().getMethodImplementations(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfMethodEntry); 173 if (rootNodes.isEmpty()) {
173 if (rootNodes.isEmpty()) { 174 return null;
174 return null; 175 }
175 } 176 if (rootNodes.size() > 1) {
176 if (rootNodes.size() > 1) { 177 System.err.println("WARNING: Method " + deobfMethodEntry + " implements multiple interfaces. Only showing first one.");
177 System.err.println("WARNING: Method " + deobfMethodEntry + " implements multiple interfaces. Only showing first one."); 178 }
178 } 179 return MethodImplementationsTreeNode.findNode(rootNodes.get(0), obfMethodEntry);
179 return MethodImplementationsTreeNode.findNode(rootNodes.get(0), obfMethodEntry); 180 }
180 } 181
181 182 public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) {
182 public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) { 183 FieldEntry obfFieldEntry = this.deobfuscator.obfuscateEntry(deobfFieldEntry);
183 FieldEntry obfFieldEntry = this.deobfuscator.obfuscateEntry(deobfFieldEntry); 184 FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfFieldEntry);
184 FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfFieldEntry); 185 rootNode.load(this.deobfuscator.getJarIndex(), true);
185 rootNode.load(this.deobfuscator.getJarIndex(), true); 186 return rootNode;
186 return rootNode; 187 }
187 } 188
188 189 public BehaviorReferenceTreeNode getMethodReferences(BehaviorEntry deobfBehaviorEntry) {
189 public BehaviorReferenceTreeNode getMethodReferences(BehaviorEntry deobfBehaviorEntry) { 190 BehaviorEntry obfBehaviorEntry = this.deobfuscator.obfuscateEntry(deobfBehaviorEntry);
190 BehaviorEntry obfBehaviorEntry = this.deobfuscator.obfuscateEntry(deobfBehaviorEntry); 191 BehaviorReferenceTreeNode rootNode = new BehaviorReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfBehaviorEntry);
191 BehaviorReferenceTreeNode rootNode = new BehaviorReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfBehaviorEntry); 192 rootNode.load(this.deobfuscator.getJarIndex(), true);
192 rootNode.load(this.deobfuscator.getJarIndex(), true); 193 return rootNode;
193 return rootNode; 194 }
194 } 195
195 196 public void rename(EntryReference<Entry, Entry> deobfReference, String newName) {
196 public void rename(EntryReference<Entry, Entry> deobfReference, String newName) { 197 rename(deobfReference, newName, true, true);
197 rename(deobfReference, newName, true, true); 198 }
198 } 199
199 200 public void rename(EntryReference<Entry, Entry> deobfReference, String newName, boolean refreshClassTree, boolean clearTranslationCache) {
200 public void rename(EntryReference<Entry, Entry> deobfReference, String newName, boolean refreshClassTree, boolean clearTranslationCache) 201 EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference);
201 { 202 this.deobfuscator.rename(obfReference.getNameableEntry(), newName, clearTranslationCache);
202 EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); 203 this.isDirty = true;
203 this.deobfuscator.rename(obfReference.getNameableEntry(), newName, clearTranslationCache); 204
204 this.isDirty = true; 205 if (refreshClassTree && deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass())
205 206 this.gui.moveClassTree(deobfReference, newName);
206 if (refreshClassTree && deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass()) 207 refreshCurrentClass(obfReference);
207 this.gui.moveClassTree(deobfReference, newName); 208
208 refreshCurrentClass(obfReference); 209 }
209 210
210 } 211 public void removeMapping(EntryReference<Entry, Entry> deobfReference) {
211 212 EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference);
212 public void removeMapping(EntryReference<Entry, Entry> deobfReference) { 213 this.deobfuscator.removeMapping(obfReference.getNameableEntry());
213 EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); 214 this.isDirty = true;
214 this.deobfuscator.removeMapping(obfReference.getNameableEntry()); 215 if (deobfReference.entry instanceof ClassEntry)
215 this.isDirty = true; 216 this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), false, true);
216 if (deobfReference.entry instanceof ClassEntry) 217 refreshCurrentClass(obfReference);
217 this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), false, true); 218 }
218 refreshCurrentClass(obfReference); 219
219 } 220 public void markAsDeobfuscated(EntryReference<Entry, Entry> deobfReference) {
220 221 EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference);
221 public void markAsDeobfuscated(EntryReference<Entry, Entry> deobfReference) { 222 this.deobfuscator.markAsDeobfuscated(obfReference.getNameableEntry());
222 EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); 223 this.isDirty = true;
223 this.deobfuscator.markAsDeobfuscated(obfReference.getNameableEntry()); 224 if (deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass())
224 this.isDirty = true; 225 this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), true, false);
225 if (deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass()) 226 refreshCurrentClass(obfReference);
226 this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), true, false); 227 }
227 refreshCurrentClass(obfReference); 228
228 } 229 public void openDeclaration(Entry deobfEntry) {
229 230 if (deobfEntry == null) {
230 public void openDeclaration(Entry deobfEntry) { 231 throw new IllegalArgumentException("Entry cannot be null!");
231 if (deobfEntry == null) { 232 }
232 throw new IllegalArgumentException("Entry cannot be null!"); 233 openReference(new EntryReference<>(deobfEntry, deobfEntry.getName()));
233 } 234 }
234 openReference(new EntryReference<>(deobfEntry, deobfEntry.getName())); 235
235 } 236 public void openReference(EntryReference<Entry, Entry> deobfReference) {
236 237 if (deobfReference == null) {
237 public void openReference(EntryReference<Entry, Entry> deobfReference) { 238 throw new IllegalArgumentException("Reference cannot be null!");
238 if (deobfReference == null) { 239 }
239 throw new IllegalArgumentException("Reference cannot be null!"); 240
240 } 241 // get the reference target class
241 242 EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference);
242 // get the reference target class 243 ClassEntry obfClassEntry = obfReference.getLocationClassEntry().getOutermostClassEntry();
243 EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); 244 if (!this.deobfuscator.isObfuscatedIdentifier(obfClassEntry)) {
244 ClassEntry obfClassEntry = obfReference.getLocationClassEntry().getOutermostClassEntry(); 245 throw new IllegalArgumentException("Obfuscated class " + obfClassEntry + " was not found in the jar!");
245 if (!this.deobfuscator.isObfuscatedIdentifier(obfClassEntry)) { 246 }
246 throw new IllegalArgumentException("Obfuscated class " + obfClassEntry + " was not found in the jar!"); 247 if (this.currentObfClass == null || !this.currentObfClass.equals(obfClassEntry)) {
247 } 248 // deobfuscate the class, then navigate to the reference
248 if (this.currentObfClass == null || !this.currentObfClass.equals(obfClassEntry)) { 249 this.currentObfClass = obfClassEntry;
249 // deobfuscate the class, then navigate to the reference 250 deobfuscate(this.currentObfClass, obfReference);
250 this.currentObfClass = obfClassEntry; 251 } else {
251 deobfuscate(this.currentObfClass, obfReference); 252 showReference(obfReference);
252 } else { 253 }
253 showReference(obfReference); 254 }
254 } 255
255 } 256 private void showReference(EntryReference<Entry, Entry> obfReference) {
256 257 EntryReference<Entry, Entry> deobfReference = this.deobfuscator.deobfuscateReference(obfReference);
257 private void showReference(EntryReference<Entry, Entry> obfReference) { 258 Collection<Token> tokens = this.index.getReferenceTokens(deobfReference);
258 EntryReference<Entry, Entry> deobfReference = this.deobfuscator.deobfuscateReference(obfReference); 259 if (tokens.isEmpty()) {
259 Collection<Token> tokens = this.index.getReferenceTokens(deobfReference); 260 // DEBUG
260 if (tokens.isEmpty()) { 261 System.err.println(String.format("WARNING: no tokens found for %s in %s", deobfReference, this.currentObfClass));
261 // DEBUG 262 } else {
262 System.err.println(String.format("WARNING: no tokens found for %s in %s", deobfReference, this.currentObfClass)); 263 this.gui.showTokens(tokens);
263 } else { 264 }
264 this.gui.showTokens(tokens); 265 }
265 } 266
266 } 267 public void savePreviousReference(EntryReference<Entry, Entry> deobfReference) {
267 268 this.referenceStack.push(this.deobfuscator.obfuscateReference(deobfReference));
268 public void savePreviousReference(EntryReference<Entry, Entry> deobfReference) { 269 }
269 this.referenceStack.push(this.deobfuscator.obfuscateReference(deobfReference)); 270
270 } 271 public void openPreviousReference() {
271 272 if (hasPreviousLocation()) {
272 public void openPreviousReference() { 273 openReference(this.deobfuscator.deobfuscateReference(this.referenceStack.pop()));
273 if (hasPreviousLocation()) { 274 }
274 openReference(this.deobfuscator.deobfuscateReference(this.referenceStack.pop())); 275 }
275 } 276
276 } 277 public boolean hasPreviousLocation() {
277 278 return !this.referenceStack.isEmpty();
278 public boolean hasPreviousLocation() { 279 }
279 return !this.referenceStack.isEmpty(); 280
280 } 281 private void refreshClasses() {
281 282 List<ClassEntry> obfClasses = Lists.newArrayList();
282 private void refreshClasses() { 283 List<ClassEntry> deobfClasses = Lists.newArrayList();
283 List<ClassEntry> obfClasses = Lists.newArrayList(); 284 this.deobfuscator.getSeparatedClasses(obfClasses, deobfClasses);
284 List<ClassEntry> deobfClasses = Lists.newArrayList(); 285 this.gui.setObfClasses(obfClasses);
285 this.deobfuscator.getSeparatedClasses(obfClasses, deobfClasses); 286 this.gui.setDeobfClasses(deobfClasses);
286 this.gui.setObfClasses(obfClasses); 287 }
287 this.gui.setDeobfClasses(deobfClasses); 288
288 } 289 public void refreshCurrentClass() {
289 290 refreshCurrentClass(null);
290 public void refreshCurrentClass() { 291 }
291 refreshCurrentClass(null); 292
292 } 293 private void refreshCurrentClass(EntryReference<Entry, Entry> obfReference) {
293 294 if (this.currentObfClass != null) {
294 private void refreshCurrentClass(EntryReference<Entry, Entry> obfReference) { 295 deobfuscate(this.currentObfClass, obfReference);
295 if (this.currentObfClass != null) { 296 }
296 deobfuscate(this.currentObfClass, obfReference); 297 }
297 } 298
298 } 299 private void deobfuscate(final ClassEntry classEntry, final EntryReference<Entry, Entry> obfReference) {
299 300
300 private void deobfuscate(final ClassEntry classEntry, final EntryReference<Entry, Entry> obfReference) { 301 this.gui.setSource("(deobfuscating...)");
301 302
302 this.gui.setSource("(deobfuscating...)"); 303 // run the deobfuscator in a separate thread so we don't block the GUI event queue
303 304 new Thread(() ->
304 // run the deobfuscator in a separate thread so we don't block the GUI event queue 305 {
305 new Thread(() -> 306 // decompile,deobfuscate the bytecode
306 { 307 CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getClassName());
307 // decompile,deobfuscate the bytecode 308 if (sourceTree == null) {
308 CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getClassName()); 309 // decompilation of this class is not supported
309 if (sourceTree == null) { 310 gui.setSource("Unable to find class: " + classEntry);
310 // decompilation of this class is not supported 311 return;
311 gui.setSource("Unable to find class: " + classEntry); 312 }
312 return; 313 String source = deobfuscator.getSource(sourceTree);
313 } 314 index = deobfuscator.getSourceIndex(sourceTree, source);
314 String source = deobfuscator.getSource(sourceTree); 315 gui.setSource(index.getSource());
315 index = deobfuscator.getSourceIndex(sourceTree, source); 316 if (obfReference != null) {
316 gui.setSource(index.getSource()); 317 showReference(obfReference);
317 if (obfReference != null) { 318 }
318 showReference(obfReference); 319
319 } 320 // set the highlighted tokens
320 321 List<Token> obfuscatedTokens = Lists.newArrayList();
321 // set the highlighted tokens 322 List<Token> deobfuscatedTokens = Lists.newArrayList();
322 List<Token> obfuscatedTokens = Lists.newArrayList(); 323 List<Token> otherTokens = Lists.newArrayList();
323 List<Token> deobfuscatedTokens = Lists.newArrayList(); 324 for (Token token : index.referenceTokens()) {
324 List<Token> otherTokens = Lists.newArrayList(); 325 EntryReference<Entry, Entry> reference = index.getDeobfReference(token);
325 for (Token token : index.referenceTokens()) { 326 if (referenceIsRenameable(reference)) {
326 EntryReference<Entry, Entry> reference = index.getDeobfReference(token); 327 if (entryHasDeobfuscatedName(reference.getNameableEntry())) {
327 if (referenceIsRenameable(reference)) { 328 deobfuscatedTokens.add(token);
328 if (entryHasDeobfuscatedName(reference.getNameableEntry())) { 329 } else {
329 deobfuscatedTokens.add(token); 330 obfuscatedTokens.add(token);
330 } else { 331 }
331 obfuscatedTokens.add(token); 332 } else {
332 } 333 otherTokens.add(token);
333 } else { 334 }
334 otherTokens.add(token); 335 }
335 } 336 gui.setHighlightedTokens(obfuscatedTokens, deobfuscatedTokens, otherTokens);
336 } 337 }).start();
337 gui.setHighlightedTokens(obfuscatedTokens, deobfuscatedTokens, otherTokens); 338 }
338 }).start(); 339
339 } 340 public Deobfuscator getDeobfuscator() {
340 341 return deobfuscator;
341 public Deobfuscator getDeobfuscator() 342 }
342 { 343
343 return deobfuscator; 344 public void modifierChange(ItemEvent event) {
344 } 345 if (event.getStateChange() == ItemEvent.SELECTED) {
345 346 deobfuscator.changeModifier(gui.reference.entry, (Mappings.EntryModifier) event.getItem());
346 public void modifierChange(ItemEvent event) 347 this.isDirty = true;
347 { 348 refreshCurrentClass();
348 if (event.getStateChange() == ItemEvent.SELECTED) 349 }
349 { 350 }
350 deobfuscator.changeModifier(gui.reference.entry, (Mappings.EntryModifier) event.getItem());
351 this.isDirty = true;
352 refreshCurrentClass();
353 }
354 }
355} 351}
diff --git a/src/main/java/cuchaz/enigma/gui/GuiTricks.java b/src/main/java/cuchaz/enigma/gui/GuiTricks.java
index 85b65b0a..8bf57d38 100644
--- a/src/main/java/cuchaz/enigma/gui/GuiTricks.java
+++ b/src/main/java/cuchaz/enigma/gui/GuiTricks.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.gui; 12package cuchaz.enigma.gui;
12 13
13import javax.swing.*; 14import javax.swing.*;
@@ -17,26 +18,26 @@ import java.util.Arrays;
17 18
18public class GuiTricks { 19public class GuiTricks {
19 20
20 public static JLabel unboldLabel(JLabel label) { 21 public static JLabel unboldLabel(JLabel label) {
21 Font font = label.getFont(); 22 Font font = label.getFont();
22 label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD)); 23 label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD));
23 return label; 24 return label;
24 } 25 }
25 26
26 public static void deactivateButton(JButton button) { 27 public static void deactivateButton(JButton button) {
27 button.setEnabled(false); 28 button.setEnabled(false);
28 button.setText(""); 29 button.setText("");
29 for (ActionListener listener : Arrays.asList(button.getActionListeners())) { 30 for (ActionListener listener : Arrays.asList(button.getActionListeners())) {
30 button.removeActionListener(listener); 31 button.removeActionListener(listener);
31 } 32 }
32 } 33 }
33 34
34 public static void activateButton(JButton button, String text, ActionListener newListener) { 35 public static void activateButton(JButton button, String text, ActionListener newListener) {
35 button.setText(text); 36 button.setText(text);
36 button.setEnabled(true); 37 button.setEnabled(true);
37 for (ActionListener listener : Arrays.asList(button.getActionListeners())) { 38 for (ActionListener listener : Arrays.asList(button.getActionListeners())) {
38 button.removeActionListener(listener); 39 button.removeActionListener(listener);
39 } 40 }
40 button.addActionListener(newListener); 41 button.addActionListener(newListener);
41 } 42 }
42} 43}
diff --git a/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java b/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java
index 671f85fe..4f5231f1 100644
--- a/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java
+++ b/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java
@@ -8,25 +8,11 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.gui; 12package cuchaz.enigma.gui;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
14import com.google.common.collect.Maps; 15import com.google.common.collect.Maps;
15
16import java.awt.BorderLayout;
17import java.awt.Container;
18import java.awt.Dimension;
19import java.awt.FlowLayout;
20import java.awt.event.ActionListener;
21import java.awt.event.KeyAdapter;
22import java.awt.event.KeyEvent;
23import java.util.Collection;
24import java.util.List;
25import java.util.Map;
26
27import javax.swing.*;
28import javax.swing.text.Highlighter.HighlightPainter;
29
30import cuchaz.enigma.Constants; 16import cuchaz.enigma.Constants;
31import cuchaz.enigma.Deobfuscator; 17import cuchaz.enigma.Deobfuscator;
32import cuchaz.enigma.analysis.SourceIndex; 18import cuchaz.enigma.analysis.SourceIndex;
@@ -39,403 +25,410 @@ import cuchaz.enigma.mapping.ClassEntry;
39import cuchaz.enigma.mapping.Entry; 25import cuchaz.enigma.mapping.Entry;
40import de.sciss.syntaxpane.DefaultSyntaxKit; 26import de.sciss.syntaxpane.DefaultSyntaxKit;
41 27
28import javax.swing.*;
29import javax.swing.text.Highlighter.HighlightPainter;
30import java.awt.*;
31import java.awt.event.ActionListener;
32import java.awt.event.KeyAdapter;
33import java.awt.event.KeyEvent;
34import java.util.Collection;
35import java.util.List;
36import java.util.Map;
42 37
43public class MemberMatchingGui<T extends Entry> { 38public class MemberMatchingGui<T extends Entry> {
44 39
45 private enum SourceType { 40 // controls
46 Matched { 41 private JFrame frame;
47 @Override 42 private Map<SourceType, JRadioButton> sourceTypeButtons;
48 public <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches) { 43 private ClassSelector sourceClasses;
49 return matches.getSourceClassesWithoutUnmatchedEntries(); 44 private CodeReader sourceReader;
50 } 45 private CodeReader destReader;
51 }, 46 private JButton matchButton;
52 Unmatched { 47 private JButton unmatchableButton;
53 @Override 48 private JLabel sourceLabel;
54 public <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches) { 49 private JLabel destLabel;
55 return matches.getSourceClassesWithUnmatchedEntries(); 50 private HighlightPainter unmatchedHighlightPainter;
56 } 51 private HighlightPainter matchedHighlightPainter;
57 }; 52 private ClassMatches classMatches;
58 53 private MemberMatches<T> memberMatches;
59 public JRadioButton newRadio(ActionListener listener, ButtonGroup group) { 54 private Deobfuscator sourceDeobfuscator;
60 JRadioButton button = new JRadioButton(name(), this == getDefault()); 55 private Deobfuscator destDeobfuscator;
61 button.setActionCommand(name()); 56 private SaveListener<T> saveListener;
62 button.addActionListener(listener); 57 private SourceType sourceType;
63 group.add(button); 58 private ClassEntry obfSourceClass;
64 return button; 59 private ClassEntry obfDestClass;
65 } 60 private T obfSourceEntry;
66 61 private T obfDestEntry;
67 public abstract <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches); 62 public MemberMatchingGui(ClassMatches classMatches, MemberMatches<T> fieldMatches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) {
68 63
69 public static SourceType getDefault() { 64 this.classMatches = classMatches;
70 return values()[0]; 65 memberMatches = fieldMatches;
71 } 66 this.sourceDeobfuscator = sourceDeobfuscator;
72 } 67 this.destDeobfuscator = destDeobfuscator;
73 68
74 public interface SaveListener<T extends Entry> { 69 // init frame
75 void save(MemberMatches<T> matches); 70 frame = new JFrame(Constants.NAME + " - Member Matcher");
76 } 71 final Container pane = frame.getContentPane();
77 72 pane.setLayout(new BorderLayout());
78 // controls 73
79 private JFrame frame; 74 // init classes side
80 private Map<SourceType, JRadioButton> sourceTypeButtons; 75 JPanel classesPanel = new JPanel();
81 private ClassSelector sourceClasses; 76 classesPanel.setLayout(new BoxLayout(classesPanel, BoxLayout.PAGE_AXIS));
82 private CodeReader sourceReader; 77 classesPanel.setPreferredSize(new Dimension(200, 0));
83 private CodeReader destReader; 78 pane.add(classesPanel, BorderLayout.WEST);
84 private JButton matchButton; 79 classesPanel.add(new JLabel("Classes"));
85 private JButton unmatchableButton; 80
86 private JLabel sourceLabel; 81 // init source type radios
87 private JLabel destLabel; 82 JPanel sourceTypePanel = new JPanel();
88 private HighlightPainter unmatchedHighlightPainter; 83 classesPanel.add(sourceTypePanel);
89 private HighlightPainter matchedHighlightPainter; 84 sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS));
90 private ClassMatches classMatches; 85 ActionListener sourceTypeListener = event -> setSourceType(SourceType.valueOf(event.getActionCommand()));
91 private MemberMatches<T> memberMatches; 86 ButtonGroup sourceTypeButtons = new ButtonGroup();
92 private Deobfuscator sourceDeobfuscator; 87 this.sourceTypeButtons = Maps.newHashMap();
93 private Deobfuscator destDeobfuscator; 88 for (SourceType sourceType : SourceType.values()) {
94 private SaveListener<T> saveListener; 89 JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons);
95 private SourceType sourceType; 90 this.sourceTypeButtons.put(sourceType, button);
96 private ClassEntry obfSourceClass; 91 sourceTypePanel.add(button);
97 private ClassEntry obfDestClass; 92 }
98 private T obfSourceEntry; 93
99 private T obfDestEntry; 94 sourceClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false);
100 95 sourceClasses.setSelectionListener(this::setSourceClass);
101 public MemberMatchingGui(ClassMatches classMatches, MemberMatches<T> fieldMatches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { 96 JScrollPane sourceScroller = new JScrollPane(sourceClasses);
102 97 classesPanel.add(sourceScroller);
103 this.classMatches = classMatches; 98
104 memberMatches = fieldMatches; 99 // init readers
105 this.sourceDeobfuscator = sourceDeobfuscator; 100 DefaultSyntaxKit.initKit();
106 this.destDeobfuscator = destDeobfuscator; 101 sourceReader = new CodeReader();
107 102 sourceReader.setSelectionListener(reference ->
108 // init frame 103 {
109 frame = new JFrame(Constants.NAME + " - Member Matcher"); 104 if (reference != null) {
110 final Container pane = frame.getContentPane(); 105 onSelectSource(reference.entry);
111 pane.setLayout(new BorderLayout()); 106 } else {
112 107 onSelectSource(null);
113 // init classes side 108 }
114 JPanel classesPanel = new JPanel(); 109 });
115 classesPanel.setLayout(new BoxLayout(classesPanel, BoxLayout.PAGE_AXIS)); 110 destReader = new CodeReader();
116 classesPanel.setPreferredSize(new Dimension(200, 0)); 111 destReader.setSelectionListener(reference ->
117 pane.add(classesPanel, BorderLayout.WEST); 112 {
118 classesPanel.add(new JLabel("Classes")); 113 if (reference != null) {
119 114 onSelectDest(reference.entry);
120 // init source type radios 115 } else {
121 JPanel sourceTypePanel = new JPanel(); 116 onSelectDest(null);
122 classesPanel.add(sourceTypePanel); 117 }
123 sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS)); 118 });
124 ActionListener sourceTypeListener = event -> setSourceType(SourceType.valueOf(event.getActionCommand())); 119
125 ButtonGroup sourceTypeButtons = new ButtonGroup(); 120 // add key bindings
126 this.sourceTypeButtons = Maps.newHashMap(); 121 KeyAdapter keyListener = new KeyAdapter() {
127 for (SourceType sourceType : SourceType.values()) { 122 @Override
128 JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons); 123 public void keyPressed(KeyEvent event) {
129 this.sourceTypeButtons.put(sourceType, button); 124 if (event.getKeyCode() == KeyEvent.VK_M)
130 sourceTypePanel.add(button); 125 matchButton.doClick();
131 } 126 }
132 127 };
133 sourceClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false); 128 sourceReader.addKeyListener(keyListener);
134 sourceClasses.setSelectionListener(this::setSourceClass); 129 destReader.addKeyListener(keyListener);
135 JScrollPane sourceScroller = new JScrollPane(sourceClasses); 130
136 classesPanel.add(sourceScroller); 131 // init all the splits
137 132 JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(sourceReader), new JScrollPane(
138 // init readers 133 destReader));
139 DefaultSyntaxKit.initKit(); 134 splitRight.setResizeWeight(0.5); // resize 50:50
140 sourceReader = new CodeReader(); 135 JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, classesPanel, splitRight);
141 sourceReader.setSelectionListener(reference -> 136 splitLeft.setResizeWeight(0); // let the right side take all the slack
142 { 137 pane.add(splitLeft, BorderLayout.CENTER);
143 if (reference != null) { 138 splitLeft.resetToPreferredSizes();
144 onSelectSource(reference.entry); 139
145 } else { 140 // init bottom panel
146 onSelectSource(null); 141 JPanel bottomPanel = new JPanel();
147 } 142 bottomPanel.setLayout(new FlowLayout());
148 }); 143 pane.add(bottomPanel, BorderLayout.SOUTH);
149 destReader = new CodeReader(); 144
150 destReader.setSelectionListener(reference -> 145 matchButton = new JButton();
151 { 146 unmatchableButton = new JButton();
152 if (reference != null) { 147
153 onSelectDest(reference.entry); 148 sourceLabel = new JLabel();
154 } else { 149 bottomPanel.add(sourceLabel);
155 onSelectDest(null); 150 bottomPanel.add(matchButton);
156 } 151 bottomPanel.add(unmatchableButton);
157 }); 152 destLabel = new JLabel();
158 153 bottomPanel.add(destLabel);
159 // add key bindings 154
160 KeyAdapter keyListener = new KeyAdapter() { 155 // show the frame
161 @Override 156 pane.doLayout();
162 public void keyPressed(KeyEvent event) { 157 frame.setSize(1024, 576);
163 if (event.getKeyCode() == KeyEvent.VK_M) 158 frame.setMinimumSize(new Dimension(640, 480));
164 matchButton.doClick(); 159 frame.setVisible(true);
165 } 160 frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
166 }; 161
167 sourceReader.addKeyListener(keyListener); 162 unmatchedHighlightPainter = new ObfuscatedHighlightPainter();
168 destReader.addKeyListener(keyListener); 163 matchedHighlightPainter = new DeobfuscatedHighlightPainter();
169 164
170 // init all the splits 165 // init state
171 JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(sourceReader), new JScrollPane( 166 saveListener = null;
172 destReader)); 167 obfSourceClass = null;
173 splitRight.setResizeWeight(0.5); // resize 50:50 168 obfDestClass = null;
174 JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, classesPanel, splitRight); 169 obfSourceEntry = null;
175 splitLeft.setResizeWeight(0); // let the right side take all the slack 170 obfDestEntry = null;
176 pane.add(splitLeft, BorderLayout.CENTER); 171 setSourceType(SourceType.getDefault());
177 splitLeft.resetToPreferredSizes(); 172 updateButtons();
178 173 }
179 // init bottom panel 174
180 JPanel bottomPanel = new JPanel(); 175 protected void setSourceType(SourceType val) {
181 bottomPanel.setLayout(new FlowLayout()); 176 sourceType = val;
182 pane.add(bottomPanel, BorderLayout.SOUTH); 177 updateSourceClasses();
183 178 }
184 matchButton = new JButton(); 179
185 unmatchableButton = new JButton(); 180 public void setSaveListener(SaveListener<T> val) {
186 181 saveListener = val;
187 sourceLabel = new JLabel(); 182 }
188 bottomPanel.add(sourceLabel); 183
189 bottomPanel.add(matchButton); 184 private void updateSourceClasses() {
190 bottomPanel.add(unmatchableButton); 185
191 destLabel = new JLabel(); 186 String selectedPackage = sourceClasses.getSelectedPackage();
192 bottomPanel.add(destLabel); 187
193 188 List<ClassEntry> deobfClassEntries = Lists.newArrayList();
194 // show the frame 189 for (ClassEntry entry : sourceType.getObfSourceClasses(memberMatches)) {
195 pane.doLayout(); 190 deobfClassEntries.add(sourceDeobfuscator.deobfuscateEntry(entry));
196 frame.setSize(1024, 576); 191 }
197 frame.setMinimumSize(new Dimension(640, 480)); 192 sourceClasses.setClasses(deobfClassEntries);
198 frame.setVisible(true); 193
199 frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 194 if (selectedPackage != null) {
200 195 sourceClasses.expandPackage(selectedPackage);
201 unmatchedHighlightPainter = new ObfuscatedHighlightPainter(); 196 }
202 matchedHighlightPainter = new DeobfuscatedHighlightPainter(); 197
203 198 for (SourceType sourceType : SourceType.values()) {
204 // init state 199 sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)",
205 saveListener = null; 200 sourceType.name(), sourceType.getObfSourceClasses(memberMatches).size()
206 obfSourceClass = null; 201 ));
207 obfDestClass = null; 202 }
208 obfSourceEntry = null; 203 }
209 obfDestEntry = null; 204
210 setSourceType(SourceType.getDefault()); 205 protected void setSourceClass(ClassEntry sourceClass) {
211 updateButtons(); 206
212 } 207 obfSourceClass = sourceDeobfuscator.obfuscateEntry(sourceClass);
213 208 obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass);
214 protected void setSourceType(SourceType val) { 209 if (obfDestClass == null) {
215 sourceType = val; 210 throw new Error("No matching dest class for source class: " + obfSourceClass);
216 updateSourceClasses(); 211 }
217 } 212
218 213 sourceReader.decompileClass(obfSourceClass, sourceDeobfuscator, false, this::updateSourceHighlights);
219 public void setSaveListener(SaveListener<T> val) { 214 destReader.decompileClass(obfDestClass, destDeobfuscator, false, this::updateDestHighlights);
220 saveListener = val; 215 }
221 } 216
222 217 protected void updateSourceHighlights() {
223 private void updateSourceClasses() { 218 highlightEntries(sourceReader, sourceDeobfuscator, memberMatches.matches().keySet(), memberMatches.getUnmatchedSourceEntries());
224 219 }
225 String selectedPackage = sourceClasses.getSelectedPackage(); 220
226 221 protected void updateDestHighlights() {
227 List<ClassEntry> deobfClassEntries = Lists.newArrayList(); 222 highlightEntries(destReader, destDeobfuscator, memberMatches.matches().values(), memberMatches.getUnmatchedDestEntries());
228 for (ClassEntry entry : sourceType.getObfSourceClasses(memberMatches)) { 223 }
229 deobfClassEntries.add(sourceDeobfuscator.deobfuscateEntry(entry)); 224
230 } 225 private void highlightEntries(CodeReader reader, Deobfuscator deobfuscator, Collection<T> obfMatchedEntries, Collection<T> obfUnmatchedEntries) {
231 sourceClasses.setClasses(deobfClassEntries); 226 reader.clearHighlights();
232 227 // matched fields
233 if (selectedPackage != null) { 228 updateHighlighted(obfMatchedEntries, deobfuscator, reader, matchedHighlightPainter);
234 sourceClasses.expandPackage(selectedPackage); 229 // unmatched fields
235 } 230 updateHighlighted(obfUnmatchedEntries, deobfuscator, reader, unmatchedHighlightPainter);
236 231 }
237 for (SourceType sourceType : SourceType.values()) { 232
238 sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)", 233 private void updateHighlighted(Collection<T> entries, Deobfuscator deobfuscator, CodeReader reader, HighlightPainter painter) {
239 sourceType.name(), sourceType.getObfSourceClasses(memberMatches).size() 234 SourceIndex index = reader.getSourceIndex();
240 )); 235 for (T obfT : entries) {
241 } 236 T deobfT = deobfuscator.deobfuscateEntry(obfT);
242 } 237 Token token = index.getDeclarationToken(deobfT);
243 238 if (token != null) {
244 protected void setSourceClass(ClassEntry sourceClass) { 239 reader.setHighlightedToken(token, painter);
245 240 }
246 obfSourceClass = sourceDeobfuscator.obfuscateEntry(sourceClass); 241 }
247 obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); 242 }
248 if (obfDestClass == null) { 243
249 throw new Error("No matching dest class for source class: " + obfSourceClass); 244 private boolean isSelectionMatched() {
250 } 245 return obfSourceEntry != null && obfDestEntry != null
251 246 && memberMatches.isMatched(obfSourceEntry, obfDestEntry);
252 sourceReader.decompileClass(obfSourceClass, sourceDeobfuscator, false, this::updateSourceHighlights); 247 }
253 destReader.decompileClass(obfDestClass, destDeobfuscator, false, this::updateDestHighlights); 248
254 } 249 protected void onSelectSource(Entry source) {
255 250
256 protected void updateSourceHighlights() { 251 // start with no selection
257 highlightEntries(sourceReader, sourceDeobfuscator, memberMatches.matches().keySet(), memberMatches.getUnmatchedSourceEntries()); 252 if (isSelectionMatched()) {
258 } 253 setDest(null);
259 254 }
260 protected void updateDestHighlights() { 255 setSource(null);
261 highlightEntries(destReader, destDeobfuscator, memberMatches.matches().values(), memberMatches.getUnmatchedDestEntries()); 256
262 } 257 // then look for a valid source selection
263 258 if (source != null) {
264 private void highlightEntries(CodeReader reader, Deobfuscator deobfuscator, Collection<T> obfMatchedEntries, Collection<T> obfUnmatchedEntries) { 259
265 reader.clearHighlights(); 260 // this looks really scary, but it's actually ok
266 // matched fields 261 // Deobfuscator.obfuscateEntry can handle all implementations of Entry
267 updateHighlighted(obfMatchedEntries, deobfuscator, reader, matchedHighlightPainter); 262 // and MemberMatches.hasSource() will only pass entries that actually match T
268 // unmatched fields 263 @SuppressWarnings("unchecked")
269 updateHighlighted(obfUnmatchedEntries, deobfuscator, reader, unmatchedHighlightPainter); 264 T sourceEntry = (T) source;
270 } 265
271 266 T obfSourceEntry = sourceDeobfuscator.obfuscateEntry(sourceEntry);
272 private void updateHighlighted(Collection<T> entries, Deobfuscator deobfuscator, CodeReader reader, HighlightPainter painter) 267 if (memberMatches.hasSource(obfSourceEntry)) {
273 { 268 setSource(obfSourceEntry);
274 SourceIndex index = reader.getSourceIndex(); 269
275 for (T obfT : entries) { 270 // look for a matched dest too
276 T deobfT = deobfuscator.deobfuscateEntry(obfT); 271 T obfDestEntry = memberMatches.matches().get(obfSourceEntry);
277 Token token = index.getDeclarationToken(deobfT); 272 if (obfDestEntry != null) {
278 if (token != null) { 273 setDest(obfDestEntry);
279 reader.setHighlightedToken(token, painter); 274 }
280 } 275 }
281 } 276 }
282 } 277
283 278 updateButtons();
284 private boolean isSelectionMatched() { 279 }
285 return obfSourceEntry != null && obfDestEntry != null 280
286 && memberMatches.isMatched(obfSourceEntry, obfDestEntry); 281 protected void onSelectDest(Entry dest) {
287 } 282
288 283 // start with no selection
289 protected void onSelectSource(Entry source) { 284 if (isSelectionMatched()) {
290 285 setSource(null);
291 // start with no selection 286 }
292 if (isSelectionMatched()) { 287 setDest(null);
293 setDest(null); 288
294 } 289 // then look for a valid dest selection
295 setSource(null); 290 if (dest != null) {
296 291
297 // then look for a valid source selection 292 // this looks really scary, but it's actually ok
298 if (source != null) { 293 // Deobfuscator.obfuscateEntry can handle all implementations of Entry
299 294 // and MemberMatches.hasSource() will only pass entries that actually match T
300 // this looks really scary, but it's actually ok 295 @SuppressWarnings("unchecked")
301 // Deobfuscator.obfuscateEntry can handle all implementations of Entry 296 T destEntry = (T) dest;
302 // and MemberMatches.hasSource() will only pass entries that actually match T 297
303 @SuppressWarnings("unchecked") 298 T obfDestEntry = destDeobfuscator.obfuscateEntry(destEntry);
304 T sourceEntry = (T) source; 299 if (memberMatches.hasDest(obfDestEntry)) {
305 300 setDest(obfDestEntry);
306 T obfSourceEntry = sourceDeobfuscator.obfuscateEntry(sourceEntry); 301
307 if (memberMatches.hasSource(obfSourceEntry)) { 302 // look for a matched source too
308 setSource(obfSourceEntry); 303 T obfSourceEntry = memberMatches.matches().inverse().get(obfDestEntry);
309 304 if (obfSourceEntry != null) {
310 // look for a matched dest too 305 setSource(obfSourceEntry);
311 T obfDestEntry = memberMatches.matches().get(obfSourceEntry); 306 }
312 if (obfDestEntry != null) { 307 }
313 setDest(obfDestEntry); 308 }
314 } 309
315 } 310 updateButtons();
316 } 311 }
317 312
318 updateButtons(); 313 private void setSource(T obfEntry) {
319 } 314 if (obfEntry == null) {
320 315 obfSourceEntry = null;
321 protected void onSelectDest(Entry dest) { 316 sourceLabel.setText("");
322 317 } else {
323 // start with no selection 318 obfSourceEntry = obfEntry;
324 if (isSelectionMatched()) { 319 sourceLabel.setText(getEntryLabel(obfEntry, sourceDeobfuscator));
325 setSource(null); 320 }
326 } 321 }
327 setDest(null); 322
328 323 private void setDest(T obfEntry) {
329 // then look for a valid dest selection 324 if (obfEntry == null) {
330 if (dest != null) { 325 obfDestEntry = null;
331 326 destLabel.setText("");
332 // this looks really scary, but it's actually ok 327 } else {
333 // Deobfuscator.obfuscateEntry can handle all implementations of Entry 328 obfDestEntry = obfEntry;
334 // and MemberMatches.hasSource() will only pass entries that actually match T 329 destLabel.setText(getEntryLabel(obfEntry, destDeobfuscator));
335 @SuppressWarnings("unchecked") 330 }
336 T destEntry = (T) dest; 331 }
337 332
338 T obfDestEntry = destDeobfuscator.obfuscateEntry(destEntry); 333 private String getEntryLabel(T obfEntry, Deobfuscator deobfuscator) {
339 if (memberMatches.hasDest(obfDestEntry)) { 334 // show obfuscated and deobfuscated names, but no types/signatures
340 setDest(obfDestEntry); 335 T deobfEntry = deobfuscator.deobfuscateEntry(obfEntry);
341 336 return String.format("%s (%s)", deobfEntry.getName(), obfEntry.getName());
342 // look for a matched source too 337 }
343 T obfSourceEntry = memberMatches.matches().inverse().get(obfDestEntry); 338
344 if (obfSourceEntry != null) { 339 private void updateButtons() {
345 setSource(obfSourceEntry); 340
346 } 341 GuiTricks.deactivateButton(matchButton);
347 } 342 GuiTricks.deactivateButton(unmatchableButton);
348 } 343
349 344 if (obfSourceEntry != null && obfDestEntry != null) {
350 updateButtons(); 345 if (memberMatches.isMatched(obfSourceEntry, obfDestEntry))
351 } 346 GuiTricks.activateButton(matchButton, "Unmatch", event -> unmatch());
352 347 else if (!memberMatches.isMatchedSourceEntry(obfSourceEntry) && !memberMatches.isMatchedDestEntry(
353 private void setSource(T obfEntry) { 348 obfDestEntry))
354 if (obfEntry == null) { 349 GuiTricks.activateButton(matchButton, "Match", event -> match());
355 obfSourceEntry = null; 350 } else if (obfSourceEntry != null)
356 sourceLabel.setText(""); 351 GuiTricks.activateButton(unmatchableButton, "Set Unmatchable", event -> unmatchable());
357 } else { 352 }
358 obfSourceEntry = obfEntry; 353
359 sourceLabel.setText(getEntryLabel(obfEntry, sourceDeobfuscator)); 354 protected void match() {
360 } 355
361 } 356 // update the field matches
362 357 memberMatches.makeMatch(obfSourceEntry, obfDestEntry, sourceDeobfuscator, destDeobfuscator);
363 private void setDest(T obfEntry) { 358 save();
364 if (obfEntry == null) { 359
365 obfDestEntry = null; 360 // update the ui
366 destLabel.setText(""); 361 onSelectSource(null);
367 } else { 362 onSelectDest(null);
368 obfDestEntry = obfEntry; 363 updateSourceHighlights();
369 destLabel.setText(getEntryLabel(obfEntry, destDeobfuscator)); 364 updateDestHighlights();
370 } 365 updateSourceClasses();
371 } 366 }
372 367
373 private String getEntryLabel(T obfEntry, Deobfuscator deobfuscator) { 368 protected void unmatch() {
374 // show obfuscated and deobfuscated names, but no types/signatures 369
375 T deobfEntry = deobfuscator.deobfuscateEntry(obfEntry); 370 // update the field matches
376 return String.format("%s (%s)", deobfEntry.getName(), obfEntry.getName()); 371 memberMatches.unmakeMatch(obfSourceEntry, obfDestEntry, sourceDeobfuscator, destDeobfuscator);
377 } 372 save();
378 373
379 private void updateButtons() { 374 // update the ui
380 375 onSelectSource(null);
381 GuiTricks.deactivateButton(matchButton); 376 onSelectDest(null);
382 GuiTricks.deactivateButton(unmatchableButton); 377 updateSourceHighlights();
383 378 updateDestHighlights();
384 if (obfSourceEntry != null && obfDestEntry != null) { 379 updateSourceClasses();
385 if (memberMatches.isMatched(obfSourceEntry, obfDestEntry)) 380 }
386 GuiTricks.activateButton(matchButton, "Unmatch", event -> unmatch()); 381
387 else if (!memberMatches.isMatchedSourceEntry(obfSourceEntry) && !memberMatches.isMatchedDestEntry( 382 protected void unmatchable() {
388 obfDestEntry)) 383
389 GuiTricks.activateButton(matchButton, "Match", event -> match()); 384 // update the field matches
390 } else if (obfSourceEntry != null) 385 memberMatches.makeSourceUnmatchable(obfSourceEntry, sourceDeobfuscator);
391 GuiTricks.activateButton(unmatchableButton, "Set Unmatchable", event -> unmatchable()); 386 save();
392 } 387
393 388 // update the ui
394 protected void match() { 389 onSelectSource(null);
395 390 onSelectDest(null);
396 // update the field matches 391 updateSourceHighlights();
397 memberMatches.makeMatch(obfSourceEntry, obfDestEntry, sourceDeobfuscator, destDeobfuscator); 392 updateDestHighlights();
398 save(); 393 updateSourceClasses();
399 394 }
400 // update the ui 395
401 onSelectSource(null); 396 private void save() {
402 onSelectDest(null); 397 if (saveListener != null) {
403 updateSourceHighlights(); 398 saveListener.save(memberMatches);
404 updateDestHighlights(); 399 }
405 updateSourceClasses(); 400 }
406 } 401
407 402 private enum SourceType {
408 protected void unmatch() { 403 Matched {
409 404 @Override
410 // update the field matches 405 public <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches) {
411 memberMatches.unmakeMatch(obfSourceEntry, obfDestEntry, sourceDeobfuscator, destDeobfuscator); 406 return matches.getSourceClassesWithoutUnmatchedEntries();
412 save(); 407 }
413 408 },
414 // update the ui 409 Unmatched {
415 onSelectSource(null); 410 @Override
416 onSelectDest(null); 411 public <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches) {
417 updateSourceHighlights(); 412 return matches.getSourceClassesWithUnmatchedEntries();
418 updateDestHighlights(); 413 }
419 updateSourceClasses(); 414 };
420 } 415
421 416 public static SourceType getDefault() {
422 protected void unmatchable() { 417 return values()[0];
423 418 }
424 // update the field matches 419
425 memberMatches.makeSourceUnmatchable(obfSourceEntry, sourceDeobfuscator); 420 public JRadioButton newRadio(ActionListener listener, ButtonGroup group) {
426 save(); 421 JRadioButton button = new JRadioButton(name(), this == getDefault());
427 422 button.setActionCommand(name());
428 // update the ui 423 button.addActionListener(listener);
429 onSelectSource(null); 424 group.add(button);
430 onSelectDest(null); 425 return button;
431 updateSourceHighlights(); 426 }
432 updateDestHighlights(); 427
433 updateSourceClasses(); 428 public abstract <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches);
434 } 429 }
435 430
436 private void save() { 431 public interface SaveListener<T extends Entry> {
437 if (saveListener != null) { 432 void save(MemberMatches<T> matches);
438 saveListener.save(memberMatches); 433 }
439 }
440 }
441} 434}
diff --git a/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java b/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java
index bd9fe3d4..1fd2fa85 100644
--- a/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java
+++ b/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java
@@ -8,37 +8,37 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.gui; 12package cuchaz.enigma.gui;
12 13
13import cuchaz.enigma.mapping.ClassEntry; 14import cuchaz.enigma.mapping.ClassEntry;
14 15
15
16public class ScoredClassEntry extends ClassEntry { 16public class ScoredClassEntry extends ClassEntry {
17 17
18 private static final long serialVersionUID = -8798725308554217105L; 18 private static final long serialVersionUID = -8798725308554217105L;
19 19
20 private float score; 20 private float score;
21 21
22 public ScoredClassEntry(ClassEntry other, float score) { 22 public ScoredClassEntry(ClassEntry other, float score) {
23 super(other); 23 super(other);
24 this.score = score; 24 this.score = score;
25 } 25 }
26 26
27 public float getScore() { 27 public float getScore() {
28 return score; 28 return score;
29 } 29 }
30 30
31 @Override 31 @Override
32 public int hashCode() { 32 public int hashCode() {
33 return Float.hashCode(score) + super.hashCode(); 33 return Float.hashCode(score) + super.hashCode();
34 } 34 }
35 35
36 @Override 36 @Override
37 public boolean equals(Object other) { 37 public boolean equals(Object other) {
38 return super.equals(other) && other instanceof ScoredClassEntry && equals((ScoredClassEntry) other); 38 return super.equals(other) && other instanceof ScoredClassEntry && equals((ScoredClassEntry) other);
39 } 39 }
40 40
41 public boolean equals(ScoredClassEntry other) { 41 public boolean equals(ScoredClassEntry other) {
42 return other != null && score == other.score; 42 return other != null && Float.compare(score, other.score) == 0;
43 } 43 }
44} 44}
diff --git a/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java b/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java
index 518055fd..7375111e 100644
--- a/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java
+++ b/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java
@@ -8,31 +8,28 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui;
12
13import java.awt.Component;
14 11
15import javax.swing.DefaultListCellRenderer; 12package cuchaz.enigma.gui;
16import javax.swing.JLabel;
17import javax.swing.JList;
18import javax.swing.ListCellRenderer;
19 13
20import cuchaz.enigma.analysis.Token; 14import cuchaz.enigma.analysis.Token;
21 15
16import javax.swing.*;
17import java.awt.*;
18
22public class TokenListCellRenderer implements ListCellRenderer<Token> { 19public class TokenListCellRenderer implements ListCellRenderer<Token> {
23 20
24 private GuiController controller; 21 private GuiController controller;
25 private DefaultListCellRenderer defaultRenderer; 22 private DefaultListCellRenderer defaultRenderer;
26 23
27 public TokenListCellRenderer(GuiController controller) { 24 public TokenListCellRenderer(GuiController controller) {
28 this.controller = controller; 25 this.controller = controller;
29 this.defaultRenderer = new DefaultListCellRenderer(); 26 this.defaultRenderer = new DefaultListCellRenderer();
30 } 27 }
31 28
32 @Override 29 @Override
33 public Component getListCellRendererComponent(JList<? extends Token> list, Token token, int index, boolean isSelected, boolean hasFocus) { 30 public Component getListCellRendererComponent(JList<? extends Token> list, Token token, int index, boolean isSelected, boolean hasFocus) {
34 JLabel label = (JLabel) this.defaultRenderer.getListCellRendererComponent(list, token, index, isSelected, hasFocus); 31 JLabel label = (JLabel) this.defaultRenderer.getListCellRendererComponent(list, token, index, isSelected, hasFocus);
35 label.setText(this.controller.getReadableToken(token).toString()); 32 label.setText(this.controller.getReadableToken(token).toString());
36 return label; 33 return label;
37 } 34 }
38} 35}
diff --git a/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java b/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java
index f690b159..7b3234d8 100644
--- a/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java
+++ b/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java
@@ -8,63 +8,60 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui.dialog;
12
13import java.awt.Color;
14import java.awt.Container;
15import java.awt.Cursor;
16import java.awt.FlowLayout;
17import java.io.IOException;
18 11
19import javax.swing.*; 12package cuchaz.enigma.gui.dialog;
20 13
21import cuchaz.enigma.Constants; 14import cuchaz.enigma.Constants;
22import cuchaz.enigma.utils.Utils; 15import cuchaz.enigma.utils.Utils;
23 16
17import javax.swing.*;
18import java.awt.*;
19import java.io.IOException;
20
24public class AboutDialog { 21public class AboutDialog {
25 22
26 public static void show(JFrame parent) { 23 public static void show(JFrame parent) {
27 // init frame 24 // init frame
28 final JFrame frame = new JFrame(Constants.NAME + " - About"); 25 final JFrame frame = new JFrame(Constants.NAME + " - About");
29 final Container pane = frame.getContentPane(); 26 final Container pane = frame.getContentPane();
30 pane.setLayout(new FlowLayout()); 27 pane.setLayout(new FlowLayout());
31 28
32 // load the content 29 // load the content
33 try { 30 try {
34 String html = Utils.readResourceToString("/about.html"); 31 String html = Utils.readResourceToString("/about.html");
35 html = String.format(html, Constants.NAME, Constants.VERSION); 32 html = String.format(html, Constants.NAME, Constants.VERSION);
36 JLabel label = new JLabel(html); 33 JLabel label = new JLabel(html);
37 label.setHorizontalAlignment(JLabel.CENTER); 34 label.setHorizontalAlignment(JLabel.CENTER);
38 pane.add(label); 35 pane.add(label);
39 } catch (IOException ex) { 36 } catch (IOException ex) {
40 throw new Error(ex); 37 throw new Error(ex);
41 } 38 }
42 39
43 // show the link 40 // show the link
44 String html = "<html><a href=\"%s\">%s</a></html>"; 41 String html = "<html><a href=\"%s\">%s</a></html>";
45 html = String.format(html, Constants.URL, Constants.URL); 42 html = String.format(html, Constants.URL, Constants.URL);
46 JButton link = new JButton(html); 43 JButton link = new JButton(html);
47 link.addActionListener(event -> Utils.openUrl(Constants.URL)); 44 link.addActionListener(event -> Utils.openUrl(Constants.URL));
48 link.setBorderPainted(false); 45 link.setBorderPainted(false);
49 link.setOpaque(false); 46 link.setOpaque(false);
50 link.setBackground(Color.WHITE); 47 link.setBackground(Color.WHITE);
51 link.setCursor(new Cursor(Cursor.HAND_CURSOR)); 48 link.setCursor(new Cursor(Cursor.HAND_CURSOR));
52 link.setFocusable(false); 49 link.setFocusable(false);
53 JPanel linkPanel = new JPanel(); 50 JPanel linkPanel = new JPanel();
54 linkPanel.add(link); 51 linkPanel.add(link);
55 pane.add(linkPanel); 52 pane.add(linkPanel);
56 53
57 // show ok button 54 // show ok button
58 JButton okButton = new JButton("Ok"); 55 JButton okButton = new JButton("Ok");
59 pane.add(okButton); 56 pane.add(okButton);
60 okButton.addActionListener(arg0 -> frame.dispose()); 57 okButton.addActionListener(arg0 -> frame.dispose());
61 58
62 // show the frame 59 // show the frame
63 pane.doLayout(); 60 pane.doLayout();
64 frame.setSize(400, 220); 61 frame.setSize(400, 220);
65 frame.setResizable(false); 62 frame.setResizable(false);
66 frame.setLocationRelativeTo(parent); 63 frame.setLocationRelativeTo(parent);
67 frame.setVisible(true); 64 frame.setVisible(true);
68 frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 65 frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
69 } 66 }
70} 67}
diff --git a/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java b/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java
index 5c1d9ff8..04dd5d7b 100644
--- a/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java
+++ b/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java
@@ -8,80 +8,78 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui.dialog;
12 11
13import java.awt.BorderLayout; 12package cuchaz.enigma.gui.dialog;
14import java.awt.Container;
15import java.awt.FlowLayout;
16import java.io.PrintWriter;
17import java.io.StringWriter;
18
19import javax.swing.*;
20 13
21import cuchaz.enigma.Constants; 14import cuchaz.enigma.Constants;
22import cuchaz.enigma.utils.Utils; 15import cuchaz.enigma.utils.Utils;
23 16
17import javax.swing.*;
18import java.awt.*;
19import java.io.PrintWriter;
20import java.io.StringWriter;
21
24public class CrashDialog { 22public class CrashDialog {
25 23
26 private static CrashDialog instance = null; 24 private static CrashDialog instance = null;
27 25
28 private JFrame frame; 26 private JFrame frame;
29 private JTextArea text; 27 private JTextArea text;
30 28
31 private CrashDialog(JFrame parent) { 29 private CrashDialog(JFrame parent) {
32 // init frame 30 // init frame
33 frame = new JFrame(Constants.NAME + " - Crash Report"); 31 frame = new JFrame(Constants.NAME + " - Crash Report");
34 final Container pane = frame.getContentPane(); 32 final Container pane = frame.getContentPane();
35 pane.setLayout(new BorderLayout()); 33 pane.setLayout(new BorderLayout());
36 34
37 JLabel label = new JLabel(Constants.NAME + " has crashed! =("); 35 JLabel label = new JLabel(Constants.NAME + " has crashed! =(");
38 label.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); 36 label.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
39 pane.add(label, BorderLayout.NORTH); 37 pane.add(label, BorderLayout.NORTH);
40 38
41 // report panel 39 // report panel
42 text = new JTextArea(); 40 text = new JTextArea();
43 text.setTabSize(2); 41 text.setTabSize(2);
44 pane.add(new JScrollPane(text), BorderLayout.CENTER); 42 pane.add(new JScrollPane(text), BorderLayout.CENTER);
45 43
46 // buttons panel 44 // buttons panel
47 JPanel buttonsPanel = new JPanel(); 45 JPanel buttonsPanel = new JPanel();
48 FlowLayout buttonsLayout = new FlowLayout(); 46 FlowLayout buttonsLayout = new FlowLayout();
49 buttonsLayout.setAlignment(FlowLayout.RIGHT); 47 buttonsLayout.setAlignment(FlowLayout.RIGHT);
50 buttonsPanel.setLayout(buttonsLayout); 48 buttonsPanel.setLayout(buttonsLayout);
51 buttonsPanel.add(Utils.unboldLabel(new JLabel("If you choose exit, you will lose any unsaved work."))); 49 buttonsPanel.add(Utils.unboldLabel(new JLabel("If you choose exit, you will lose any unsaved work.")));
52 JButton ignoreButton = new JButton("Ignore"); 50 JButton ignoreButton = new JButton("Ignore");
53 ignoreButton.addActionListener(event -> { 51 ignoreButton.addActionListener(event -> {
54 // close (hide) the dialog 52 // close (hide) the dialog
55 frame.setVisible(false); 53 frame.setVisible(false);
56 }); 54 });
57 buttonsPanel.add(ignoreButton); 55 buttonsPanel.add(ignoreButton);
58 JButton exitButton = new JButton("Exit"); 56 JButton exitButton = new JButton("Exit");
59 exitButton.addActionListener(event -> { 57 exitButton.addActionListener(event -> {
60 // exit enigma 58 // exit enigma
61 System.exit(1); 59 System.exit(1);
62 }); 60 });
63 buttonsPanel.add(exitButton); 61 buttonsPanel.add(exitButton);
64 pane.add(buttonsPanel, BorderLayout.SOUTH); 62 pane.add(buttonsPanel, BorderLayout.SOUTH);
65 63
66 // show the frame 64 // show the frame
67 frame.setSize(600, 400); 65 frame.setSize(600, 400);
68 frame.setLocationRelativeTo(parent); 66 frame.setLocationRelativeTo(parent);
69 frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); 67 frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
70 } 68 }
71 69
72 public static void init(JFrame parent) { 70 public static void init(JFrame parent) {
73 instance = new CrashDialog(parent); 71 instance = new CrashDialog(parent);
74 } 72 }
75 73
76 public static void show(Throwable ex) { 74 public static void show(Throwable ex) {
77 // get the error report 75 // get the error report
78 StringWriter buf = new StringWriter(); 76 StringWriter buf = new StringWriter();
79 ex.printStackTrace(new PrintWriter(buf)); 77 ex.printStackTrace(new PrintWriter(buf));
80 String report = buf.toString(); 78 String report = buf.toString();
81 79
82 // show it! 80 // show it!
83 instance.text.setText(report); 81 instance.text.setText(report);
84 instance.frame.doLayout(); 82 instance.frame.doLayout();
85 instance.frame.setVisible(true); 83 instance.frame.setVisible(true);
86 } 84 }
87} 85}
diff --git a/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java b/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java
index 8df22a7f..5f048331 100644
--- a/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java
+++ b/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java
@@ -8,92 +8,89 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui.dialog;
12
13import java.awt.BorderLayout;
14import java.awt.Container;
15import java.awt.Dimension;
16import java.awt.FlowLayout;
17 11
18import javax.swing.*; 12package cuchaz.enigma.gui.dialog;
19 13
20import cuchaz.enigma.Constants; 14import cuchaz.enigma.Constants;
21import cuchaz.enigma.Deobfuscator.ProgressListener; 15import cuchaz.enigma.Deobfuscator.ProgressListener;
22import cuchaz.enigma.utils.Utils; 16import cuchaz.enigma.utils.Utils;
23 17
18import javax.swing.*;
19import java.awt.*;
20
24public class ProgressDialog implements ProgressListener, AutoCloseable { 21public class ProgressDialog implements ProgressListener, AutoCloseable {
25 22
26 private JFrame frame; 23 private JFrame frame;
27 private JLabel labelTitle; 24 private JLabel labelTitle;
28 private JLabel labelText; 25 private JLabel labelText;
29 private JProgressBar progress; 26 private JProgressBar progress;
30 27
31 public ProgressDialog(JFrame parent) { 28 public ProgressDialog(JFrame parent) {
32 29
33 // init frame 30 // init frame
34 this.frame = new JFrame(Constants.NAME + " - Operation in progress"); 31 this.frame = new JFrame(Constants.NAME + " - Operation in progress");
35 final Container pane = this.frame.getContentPane(); 32 final Container pane = this.frame.getContentPane();
36 FlowLayout layout = new FlowLayout(); 33 FlowLayout layout = new FlowLayout();
37 layout.setAlignment(FlowLayout.LEFT); 34 layout.setAlignment(FlowLayout.LEFT);
38 pane.setLayout(layout); 35 pane.setLayout(layout);
39 36
40 this.labelTitle = new JLabel(); 37 this.labelTitle = new JLabel();
41 pane.add(this.labelTitle); 38 pane.add(this.labelTitle);
42 39
43 // set up the progress bar 40 // set up the progress bar
44 JPanel panel = new JPanel(); 41 JPanel panel = new JPanel();
45 pane.add(panel); 42 pane.add(panel);
46 panel.setLayout(new BorderLayout()); 43 panel.setLayout(new BorderLayout());
47 this.labelText = Utils.unboldLabel(new JLabel()); 44 this.labelText = Utils.unboldLabel(new JLabel());
48 this.progress = new JProgressBar(); 45 this.progress = new JProgressBar();
49 this.labelText.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); 46 this.labelText.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
50 panel.add(this.labelText, BorderLayout.NORTH); 47 panel.add(this.labelText, BorderLayout.NORTH);
51 panel.add(this.progress, BorderLayout.CENTER); 48 panel.add(this.progress, BorderLayout.CENTER);
52 panel.setPreferredSize(new Dimension(360, 50)); 49 panel.setPreferredSize(new Dimension(360, 50));
53 50
54 // show the frame 51 // show the frame
55 pane.doLayout(); 52 pane.doLayout();
56 this.frame.setSize(400, 120); 53 this.frame.setSize(400, 120);
57 this.frame.setResizable(false); 54 this.frame.setResizable(false);
58 this.frame.setLocationRelativeTo(parent); 55 this.frame.setLocationRelativeTo(parent);
59 this.frame.setVisible(true); 56 this.frame.setVisible(true);
60 this.frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); 57 this.frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
61 } 58 }
62 59
63 public void close() { 60 public static void runInThread(final JFrame parent, final ProgressRunnable runnable) {
64 this.frame.dispose(); 61 new Thread(() ->
65 } 62 {
66 63 try (ProgressDialog progress = new ProgressDialog(parent)) {
67 @Override 64 runnable.run(progress);
68 public void init(int totalWork, String title) { 65 } catch (Exception ex) {
69 this.labelTitle.setText(title); 66 throw new Error(ex);
70 this.progress.setMinimum(0); 67 }
71 this.progress.setMaximum(totalWork); 68 }).start();
72 this.progress.setValue(0); 69 }
73 } 70
74 71 public void close() {
75 @Override 72 this.frame.dispose();
76 public void onProgress(int numDone, String message) { 73 }
77 this.labelText.setText(message); 74
78 this.progress.setValue(numDone); 75 @Override
79 76 public void init(int totalWork, String title) {
80 // update the frame 77 this.labelTitle.setText(title);
81 this.frame.validate(); 78 this.progress.setMinimum(0);
82 this.frame.repaint(); 79 this.progress.setMaximum(totalWork);
83 } 80 this.progress.setValue(0);
84 81 }
85 public interface ProgressRunnable { 82
86 void run(ProgressListener listener) throws Exception; 83 @Override
87 } 84 public void onProgress(int numDone, String message) {
88 85 this.labelText.setText(message);
89 public static void runInThread(final JFrame parent, final ProgressRunnable runnable) { 86 this.progress.setValue(numDone);
90 new Thread(() -> 87
91 { 88 // update the frame
92 try (ProgressDialog progress = new ProgressDialog(parent)) { 89 this.frame.validate();
93 runnable.run(progress); 90 this.frame.repaint();
94 } catch (Exception ex) { 91 }
95 throw new Error(ex); 92
96 } 93 public interface ProgressRunnable {
97 }).start(); 94 void run(ProgressListener listener) throws Exception;
98 } 95 }
99} 96}
diff --git a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
index 0ccd5372..cd11acaf 100644
--- a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
+++ b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
@@ -1,219 +1,207 @@
1package cuchaz.enigma.gui.elements; 1package cuchaz.enigma.gui.elements;
2 2
3import cuchaz.enigma.gui.Gui;
4import cuchaz.enigma.gui.dialog.AboutDialog;
5import cuchaz.enigma.throwables.MappingParseException;
6
7import javax.swing.*;
3import java.awt.event.InputEvent; 8import java.awt.event.InputEvent;
4import java.awt.event.KeyEvent; 9import java.awt.event.KeyEvent;
5import java.io.IOException; 10import java.io.IOException;
6import java.util.jar.JarFile; 11import java.util.jar.JarFile;
7 12
8import javax.swing.*;
9
10import cuchaz.enigma.gui.Gui;
11import cuchaz.enigma.gui.dialog.AboutDialog;
12import cuchaz.enigma.throwables.MappingParseException;
13
14public class MenuBar extends JMenuBar { 13public class MenuBar extends JMenuBar {
15 14
16 private final Gui gui; 15 public final JMenuItem closeJarMenu;
17 16 public final JMenuItem openEnigmaMappingsMenu;
18 public final JMenuItem closeJarMenu; 17 public final JMenuItem saveMappingsMenu;
19 18 public final JMenuItem saveMappingEnigmaFileMenu;
20 public final JMenuItem openEnigmaMappingsMenu; 19 public final JMenuItem saveMappingEnigmaDirectoryMenu;
21 20 public final JMenuItem saveMappingsSrgMenu;
22 public final JMenuItem saveMappingsMenu; 21 public final JMenuItem closeMappingsMenu;
23 public final JMenuItem saveMappingEnigmaFileMenu; 22 public final JMenuItem rebuildMethodNamesMenu;
24 public final JMenuItem saveMappingEnigmaDirectoryMenu; 23 public final JMenuItem exportSourceMenu;
25 public final JMenuItem saveMappingsSrgMenu; 24 public final JMenuItem exportJarMenu;
26 public final JMenuItem closeMappingsMenu; 25 private final Gui gui;
27
28 public final JMenuItem rebuildMethodNamesMenu;
29
30 public final JMenuItem exportSourceMenu;
31 public final JMenuItem exportJarMenu;
32 26
33 public MenuBar(Gui gui) { 27 public MenuBar(Gui gui) {
34 this.gui = gui; 28 this.gui = gui;
35 29
36 { 30 {
37 JMenu menu = new JMenu("File"); 31 JMenu menu = new JMenu("File");
38 this.add(menu); 32 this.add(menu);
39 { 33 {
40 JMenuItem item = new JMenuItem("Open Jar..."); 34 JMenuItem item = new JMenuItem("Open Jar...");
41 menu.add(item); 35 menu.add(item);
42 item.addActionListener(event -> { 36 item.addActionListener(event -> {
43 if (this.gui.jarFileChooser.showOpenDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { 37 if (this.gui.jarFileChooser.showOpenDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) {
44 // load the jar in a separate thread 38 // load the jar in a separate thread
45 new Thread(() -> 39 new Thread(() ->
46 { 40 {
47 try { 41 try {
48 gui.getController().openJar(new JarFile(gui.jarFileChooser.getSelectedFile())); 42 gui.getController().openJar(new JarFile(gui.jarFileChooser.getSelectedFile()));
49 } catch (IOException ex) { 43 } catch (IOException ex) {
50 throw new Error(ex); 44 throw new Error(ex);
51 } 45 }
52 }).start(); 46 }).start();
53 } 47 }
54 }); 48 });
55 } 49 }
56 { 50 {
57 JMenuItem item = new JMenuItem("Close Jar"); 51 JMenuItem item = new JMenuItem("Close Jar");
58 menu.add(item); 52 menu.add(item);
59 item.addActionListener(event -> this.gui.getController().closeJar()); 53 item.addActionListener(event -> this.gui.getController().closeJar());
60 this.closeJarMenu = item; 54 this.closeJarMenu = item;
61 } 55 }
62 menu.addSeparator(); 56 menu.addSeparator();
63 JMenu openMenu = new JMenu("Open Mappings..."); 57 JMenu openMenu = new JMenu("Open Mappings...");
64 menu.add(openMenu); 58 menu.add(openMenu);
65 { 59 {
66 JMenuItem item = new JMenuItem("Enigma"); 60 JMenuItem item = new JMenuItem("Enigma");
67 openMenu.add(item); 61 openMenu.add(item);
68 item.addActionListener(event -> { 62 item.addActionListener(event -> {
69 if (this.gui.enigmaMappingsFileChooser.showOpenDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { 63 if (this.gui.enigmaMappingsFileChooser.showOpenDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) {
70 try { 64 try {
71 this.gui.getController().openEnigmaMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile()); 65 this.gui.getController().openEnigmaMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile());
72 } catch (IOException ex) { 66 } catch (IOException ex) {
73 throw new Error(ex); 67 throw new Error(ex);
74 } catch (MappingParseException ex) { 68 } catch (MappingParseException ex) {
75 JOptionPane.showMessageDialog(this.gui.getFrame(), ex.getMessage()); 69 JOptionPane.showMessageDialog(this.gui.getFrame(), ex.getMessage());
76 } 70 }
77 } 71 }
78 }); 72 });
79 this.openEnigmaMappingsMenu = item; 73 this.openEnigmaMappingsMenu = item;
80 } 74 }
81 { 75 {
82 JMenuItem item = new JMenuItem("Save Mappings"); 76 JMenuItem item = new JMenuItem("Save Mappings");
83 menu.add(item); 77 menu.add(item);
84 item.addActionListener(event -> { 78 item.addActionListener(event -> {
85 try { 79 try {
86 this.gui.getController().saveMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile()); 80 this.gui.getController().saveMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile());
87 } catch (IOException ex) { 81 } catch (IOException ex) {
88 throw new Error(ex); 82 throw new Error(ex);
89 } 83 }
90 }); 84 });
91 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK)); 85 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK));
92 this.saveMappingsMenu = item; 86 this.saveMappingsMenu = item;
93 } 87 }
94 JMenu saveMenu = new JMenu("Save Mappings As..."); 88 JMenu saveMenu = new JMenu("Save Mappings As...");
95 menu.add(saveMenu); 89 menu.add(saveMenu);
96 { 90 {
97 JMenuItem item = new JMenuItem("Enigma (single file)"); 91 JMenuItem item = new JMenuItem("Enigma (single file)");
98 saveMenu.add(item); 92 saveMenu.add(item);
99 item.addActionListener(event -> { 93 item.addActionListener(event -> {
100 // TODO: Use a specific file chooser for it 94 // TODO: Use a specific file chooser for it
101 if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { 95 if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) {
102 try { 96 try {
103 this.gui.getController().saveEnigmaMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile(), false); 97 this.gui.getController().saveEnigmaMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile(), false);
104 this.saveMappingsMenu.setEnabled(true); 98 this.saveMappingsMenu.setEnabled(true);
105 } catch (IOException ex) { 99 } catch (IOException ex) {
106 throw new Error(ex); 100 throw new Error(ex);
107 } 101 }
108 } 102 }
109 }); 103 });
110 this.saveMappingEnigmaFileMenu = item; 104 this.saveMappingEnigmaFileMenu = item;
111 } 105 }
112 { 106 {
113 JMenuItem item = new JMenuItem("Enigma (directory)"); 107 JMenuItem item = new JMenuItem("Enigma (directory)");
114 saveMenu.add(item); 108 saveMenu.add(item);
115 item.addActionListener(event -> { 109 item.addActionListener(event -> {
116 // TODO: Use a specific file chooser for it 110 // TODO: Use a specific file chooser for it
117 if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { 111 if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) {
118 try { 112 try {
119 this.gui.getController().saveEnigmaMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile(), true); 113 this.gui.getController().saveEnigmaMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile(), true);
120 this.saveMappingsMenu.setEnabled(true); 114 this.saveMappingsMenu.setEnabled(true);
121 } catch (IOException ex) { 115 } catch (IOException ex) {
122 throw new Error(ex); 116 throw new Error(ex);
123 } 117 }
124 } 118 }
125 }); 119 });
126 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK)); 120 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK));
127 this.saveMappingEnigmaDirectoryMenu = item; 121 this.saveMappingEnigmaDirectoryMenu = item;
128 } 122 }
129 { 123 {
130 JMenuItem item = new JMenuItem("SRG (single file)"); 124 JMenuItem item = new JMenuItem("SRG (single file)");
131 saveMenu.add(item); 125 saveMenu.add(item);
132 item.addActionListener(event -> { 126 item.addActionListener(event -> {
133 // TODO: Use a specific file chooser for it 127 // TODO: Use a specific file chooser for it
134 if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { 128 if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) {
135 try { 129 try {
136 this.gui.getController().saveSRGMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile()); 130 this.gui.getController().saveSRGMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile());
137 this.saveMappingsMenu.setEnabled(true); 131 this.saveMappingsMenu.setEnabled(true);
138 } catch (IOException ex) { 132 } catch (IOException ex) {
139 throw new Error(ex); 133 throw new Error(ex);
140 } 134 }
141 } 135 }
142 }); 136 });
143 this.saveMappingsSrgMenu = item; 137 this.saveMappingsSrgMenu = item;
144 } 138 }
145 { 139 {
146 JMenuItem item = new JMenuItem("Close Mappings"); 140 JMenuItem item = new JMenuItem("Close Mappings");
147 menu.add(item); 141 menu.add(item);
148 item.addActionListener(event -> { 142 item.addActionListener(event -> {
149 if (this.gui.getController().isDirty()) 143 if (this.gui.getController().isDirty()) {
150 { 144 this.gui.showDiscardDiag((response -> {
151 this.gui.showDiscardDiag((response -> { 145 if (response == JOptionPane.YES_OPTION) {
152 if (response == JOptionPane.YES_OPTION) 146 try {
153 { 147 gui.saveMapping();
154 try 148 this.gui.getController().closeMappings();
155 { 149 } catch (IOException e) {
156 gui.saveMapping(); 150 throw new Error(e);
157 this.gui.getController().closeMappings(); 151 }
158 } catch (IOException e) 152 } else if (response == JOptionPane.NO_OPTION)
159 { 153 this.gui.getController().closeMappings();
160 throw new Error(e); 154 return null;
161 } 155 }), "Save and close", "Discard changes", "Cancel");
162 } 156 } else
163 else if (response == JOptionPane.NO_OPTION) 157 this.gui.getController().closeMappings();
164 this.gui.getController().closeMappings();
165 return null;
166 }), "Save and close", "Discard changes", "Cancel");
167 }
168 else
169 this.gui.getController().closeMappings();
170 158
171 }); 159 });
172 this.closeMappingsMenu = item; 160 this.closeMappingsMenu = item;
173 } 161 }
174 menu.addSeparator(); 162 menu.addSeparator();
175 { 163 {
176 JMenuItem item = new JMenuItem("Rebuild Method Names"); 164 JMenuItem item = new JMenuItem("Rebuild Method Names");
177 menu.add(item); 165 menu.add(item);
178 item.addActionListener(event -> this.gui.getController().rebuildMethodNames()); 166 item.addActionListener(event -> this.gui.getController().rebuildMethodNames());
179 this.rebuildMethodNamesMenu = item; 167 this.rebuildMethodNamesMenu = item;
180 } 168 }
181 menu.addSeparator(); 169 menu.addSeparator();
182 { 170 {
183 JMenuItem item = new JMenuItem("Export Source..."); 171 JMenuItem item = new JMenuItem("Export Source...");
184 menu.add(item); 172 menu.add(item);
185 item.addActionListener(event -> { 173 item.addActionListener(event -> {
186 if (this.gui.exportSourceFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { 174 if (this.gui.exportSourceFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) {
187 this.gui.getController().exportSource(this.gui.exportSourceFileChooser.getSelectedFile()); 175 this.gui.getController().exportSource(this.gui.exportSourceFileChooser.getSelectedFile());
188 } 176 }
189 }); 177 });
190 this.exportSourceMenu = item; 178 this.exportSourceMenu = item;
191 } 179 }
192 { 180 {
193 JMenuItem item = new JMenuItem("Export Jar..."); 181 JMenuItem item = new JMenuItem("Export Jar...");
194 menu.add(item); 182 menu.add(item);
195 item.addActionListener(event -> { 183 item.addActionListener(event -> {
196 if (this.gui.exportJarFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { 184 if (this.gui.exportJarFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) {
197 this.gui.getController().exportJar(this.gui.exportJarFileChooser.getSelectedFile()); 185 this.gui.getController().exportJar(this.gui.exportJarFileChooser.getSelectedFile());
198 } 186 }
199 }); 187 });
200 this.exportJarMenu = item; 188 this.exportJarMenu = item;
201 } 189 }
202 menu.addSeparator(); 190 menu.addSeparator();
203 { 191 {
204 JMenuItem item = new JMenuItem("Exit"); 192 JMenuItem item = new JMenuItem("Exit");
205 menu.add(item); 193 menu.add(item);
206 item.addActionListener(event -> this.gui.close()); 194 item.addActionListener(event -> this.gui.close());
207 } 195 }
208 } 196 }
209 { 197 {
210 JMenu menu = new JMenu("Help"); 198 JMenu menu = new JMenu("Help");
211 this.add(menu); 199 this.add(menu);
212 { 200 {
213 JMenuItem item = new JMenuItem("About"); 201 JMenuItem item = new JMenuItem("About");
214 menu.add(item); 202 menu.add(item);
215 item.addActionListener(event -> AboutDialog.show(this.gui.getFrame())); 203 item.addActionListener(event -> AboutDialog.show(this.gui.getFrame()));
216 } 204 }
217 } 205 }
218 } 206 }
219} 207}
diff --git a/src/main/java/cuchaz/enigma/gui/elements/PopupMenuBar.java b/src/main/java/cuchaz/enigma/gui/elements/PopupMenuBar.java
index e387ed31..2f6d96c4 100644
--- a/src/main/java/cuchaz/enigma/gui/elements/PopupMenuBar.java
+++ b/src/main/java/cuchaz/enigma/gui/elements/PopupMenuBar.java
@@ -1,79 +1,76 @@
1package cuchaz.enigma.gui.elements; 1package cuchaz.enigma.gui.elements;
2 2
3import java.awt.event.KeyEvent;
4
5import javax.swing.JMenuItem;
6import javax.swing.JPopupMenu;
7import javax.swing.KeyStroke;
8
9import cuchaz.enigma.gui.Gui; 3import cuchaz.enigma.gui.Gui;
10 4
5import javax.swing.*;
6import java.awt.event.KeyEvent;
7
11public class PopupMenuBar extends JPopupMenu { 8public class PopupMenuBar extends JPopupMenu {
12 9
13 public final JMenuItem renameMenu; 10 public final JMenuItem renameMenu;
14 public final JMenuItem showInheritanceMenu; 11 public final JMenuItem showInheritanceMenu;
15 public final JMenuItem showImplementationsMenu; 12 public final JMenuItem showImplementationsMenu;
16 public final JMenuItem showCallsMenu; 13 public final JMenuItem showCallsMenu;
17 public final JMenuItem openEntryMenu; 14 public final JMenuItem openEntryMenu;
18 public final JMenuItem openPreviousMenu; 15 public final JMenuItem openPreviousMenu;
19 public final JMenuItem toggleMappingMenu; 16 public final JMenuItem toggleMappingMenu;
20 17
21 public PopupMenuBar(Gui gui) { 18 public PopupMenuBar(Gui gui) {
22 { 19 {
23 JMenuItem menu = new JMenuItem("Rename"); 20 JMenuItem menu = new JMenuItem("Rename");
24 menu.addActionListener(event -> gui.startRename()); 21 menu.addActionListener(event -> gui.startRename());
25 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, 0)); 22 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, 0));
26 menu.setEnabled(false); 23 menu.setEnabled(false);
27 this.add(menu); 24 this.add(menu);
28 this.renameMenu = menu; 25 this.renameMenu = menu;
29 } 26 }
30 { 27 {
31 JMenuItem menu = new JMenuItem("Show Inheritance"); 28 JMenuItem menu = new JMenuItem("Show Inheritance");
32 menu.addActionListener(event -> gui.showInheritance()); 29 menu.addActionListener(event -> gui.showInheritance());
33 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, 0)); 30 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, 0));
34 menu.setEnabled(false); 31 menu.setEnabled(false);
35 this.add(menu); 32 this.add(menu);
36 this.showInheritanceMenu = menu; 33 this.showInheritanceMenu = menu;
37 } 34 }
38 { 35 {
39 JMenuItem menu = new JMenuItem("Show Implementations"); 36 JMenuItem menu = new JMenuItem("Show Implementations");
40 menu.addActionListener(event -> gui.showImplementations()); 37 menu.addActionListener(event -> gui.showImplementations());
41 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, 0)); 38 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, 0));
42 menu.setEnabled(false); 39 menu.setEnabled(false);
43 this.add(menu); 40 this.add(menu);
44 this.showImplementationsMenu = menu; 41 this.showImplementationsMenu = menu;
45 } 42 }
46 { 43 {
47 JMenuItem menu = new JMenuItem("Show Calls"); 44 JMenuItem menu = new JMenuItem("Show Calls");
48 menu.addActionListener(event -> gui.showCalls()); 45 menu.addActionListener(event -> gui.showCalls());
49 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, 0)); 46 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, 0));
50 menu.setEnabled(false); 47 menu.setEnabled(false);
51 this.add(menu); 48 this.add(menu);
52 this.showCallsMenu = menu; 49 this.showCallsMenu = menu;
53 } 50 }
54 { 51 {
55 JMenuItem menu = new JMenuItem("Go to Declaration"); 52 JMenuItem menu = new JMenuItem("Go to Declaration");
56 menu.addActionListener(event -> gui.navigateTo(gui.reference.entry)); 53 menu.addActionListener(event -> gui.navigateTo(gui.reference.entry));
57 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, 0)); 54 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, 0));
58 menu.setEnabled(false); 55 menu.setEnabled(false);
59 this.add(menu); 56 this.add(menu);
60 this.openEntryMenu = menu; 57 this.openEntryMenu = menu;
61 } 58 }
62 { 59 {
63 JMenuItem menu = new JMenuItem("Go to previous"); 60 JMenuItem menu = new JMenuItem("Go to previous");
64 menu.addActionListener(event -> gui.getController().openPreviousReference()); 61 menu.addActionListener(event -> gui.getController().openPreviousReference());
65 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, 0)); 62 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, 0));
66 menu.setEnabled(false); 63 menu.setEnabled(false);
67 this.add(menu); 64 this.add(menu);
68 this.openPreviousMenu = menu; 65 this.openPreviousMenu = menu;
69 } 66 }
70 { 67 {
71 JMenuItem menu = new JMenuItem("Mark as deobfuscated"); 68 JMenuItem menu = new JMenuItem("Mark as deobfuscated");
72 menu.addActionListener(event -> gui.toggleMapping()); 69 menu.addActionListener(event -> gui.toggleMapping());
73 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, 0)); 70 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, 0));
74 menu.setEnabled(false); 71 menu.setEnabled(false);
75 this.add(menu); 72 this.add(menu);
76 this.toggleMappingMenu = menu; 73 this.toggleMappingMenu = menu;
77 } 74 }
78 } 75 }
79} 76}
diff --git a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserAny.java b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserAny.java
index 23393347..f5f66287 100644
--- a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserAny.java
+++ b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserAny.java
@@ -2,10 +2,9 @@ package cuchaz.enigma.gui.filechooser;
2 2
3import javax.swing.*; 3import javax.swing.*;
4 4
5public class FileChooserAny extends JFileChooser 5public class FileChooserAny extends JFileChooser {
6{ 6 public FileChooserAny() {
7 public FileChooserAny() { 7 this.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
8 this.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); 8 this.setAcceptAllFileFilterUsed(false);
9 this.setAcceptAllFileFilterUsed(false); 9 }
10 }
11} \ No newline at end of file 10} \ No newline at end of file
diff --git a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFile.java b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFile.java
index 62a0f200..cea11a68 100644
--- a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFile.java
+++ b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFile.java
@@ -1,8 +1,8 @@
1package cuchaz.enigma.gui.filechooser; 1package cuchaz.enigma.gui.filechooser;
2 2
3import javax.swing.JFileChooser; 3import javax.swing.*;
4 4
5public class FileChooserFile extends JFileChooser { 5public class FileChooserFile extends JFileChooser {
6 public FileChooserFile() { 6 public FileChooserFile() {
7 } 7 }
8} 8}
diff --git a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java
index 93ca5d68..c16e0afc 100644
--- a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java
+++ b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java
@@ -1,11 +1,11 @@
1package cuchaz.enigma.gui.filechooser; 1package cuchaz.enigma.gui.filechooser;
2 2
3import javax.swing.JFileChooser; 3import javax.swing.*;
4 4
5public class FileChooserFolder extends JFileChooser { 5public class FileChooserFolder extends JFileChooser {
6 6
7 public FileChooserFolder() { 7 public FileChooserFolder() {
8 this.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); 8 this.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
9 this.setAcceptAllFileFilterUsed(false); 9 this.setAcceptAllFileFilterUsed(false);
10 } 10 }
11} 11}
diff --git a/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java
index 0a730880..0f649278 100644
--- a/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java
+++ b/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java
@@ -8,57 +8,54 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui.highlight;
12 11
13import java.awt.Color; 12package cuchaz.enigma.gui.highlight;
14import java.awt.Graphics;
15import java.awt.Rectangle;
16import java.awt.Shape;
17 13
18import javax.swing.text.BadLocationException; 14import javax.swing.text.BadLocationException;
19import javax.swing.text.Highlighter; 15import javax.swing.text.Highlighter;
20import javax.swing.text.JTextComponent; 16import javax.swing.text.JTextComponent;
17import java.awt.*;
21 18
22public abstract class BoxHighlightPainter implements Highlighter.HighlightPainter { 19public abstract class BoxHighlightPainter implements Highlighter.HighlightPainter {
23 20
24 private Color fillColor; 21 private Color fillColor;
25 private Color borderColor; 22 private Color borderColor;
26 23
27 protected BoxHighlightPainter(Color fillColor, Color borderColor) { 24 protected BoxHighlightPainter(Color fillColor, Color borderColor) {
28 this.fillColor = fillColor; 25 this.fillColor = fillColor;
29 this.borderColor = borderColor; 26 this.borderColor = borderColor;
30 } 27 }
31 28
32 @Override 29 public static Rectangle getBounds(JTextComponent text, int start, int end) {
33 public void paint(Graphics g, int start, int end, Shape shape, JTextComponent text) { 30 try {
34 Rectangle bounds = getBounds(text, start, end); 31 // determine the bounds of the text
35 32 Rectangle bounds = text.modelToView(start).union(text.modelToView(end));
36 // fill the area 33
37 if (this.fillColor != null) { 34 // adjust the box so it looks nice
38 g.setColor(this.fillColor); 35 bounds.x -= 2;
39 g.fillRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); 36 bounds.width += 2;
40 } 37 bounds.y += 1;
41 38 bounds.height -= 2;
42 // draw a box around the area 39
43 g.setColor(this.borderColor); 40 return bounds;
44 g.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); 41 } catch (BadLocationException ex) {
45 } 42 // don't care... just return something
46 43 return new Rectangle(0, 0, 0, 0);
47 public static Rectangle getBounds(JTextComponent text, int start, int end) { 44 }
48 try { 45 }
49 // determine the bounds of the text 46
50 Rectangle bounds = text.modelToView(start).union(text.modelToView(end)); 47 @Override
51 48 public void paint(Graphics g, int start, int end, Shape shape, JTextComponent text) {
52 // adjust the box so it looks nice 49 Rectangle bounds = getBounds(text, start, end);
53 bounds.x -= 2; 50
54 bounds.width += 2; 51 // fill the area
55 bounds.y += 1; 52 if (this.fillColor != null) {
56 bounds.height -= 2; 53 g.setColor(this.fillColor);
57 54 g.fillRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4);
58 return bounds; 55 }
59 } catch (BadLocationException ex) { 56
60 // don't care... just return something 57 // draw a box around the area
61 return new Rectangle(0, 0, 0, 0); 58 g.setColor(this.borderColor);
62 } 59 g.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4);
63 } 60 }
64} 61}
diff --git a/src/main/java/cuchaz/enigma/gui/highlight/DeobfuscatedHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/DeobfuscatedHighlightPainter.java
index 5d572030..a2d28844 100644
--- a/src/main/java/cuchaz/enigma/gui/highlight/DeobfuscatedHighlightPainter.java
+++ b/src/main/java/cuchaz/enigma/gui/highlight/DeobfuscatedHighlightPainter.java
@@ -8,13 +8,14 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.gui.highlight; 12package cuchaz.enigma.gui.highlight;
12 13
13import java.awt.Color; 14import java.awt.*;
14 15
15public class DeobfuscatedHighlightPainter extends BoxHighlightPainter { 16public class DeobfuscatedHighlightPainter extends BoxHighlightPainter {
16 17
17 public DeobfuscatedHighlightPainter() { 18 public DeobfuscatedHighlightPainter() {
18 super(new Color(220, 255, 220), new Color(80, 160, 80)); 19 super(new Color(220, 255, 220), new Color(80, 160, 80));
19 } 20 }
20} 21}
diff --git a/src/main/java/cuchaz/enigma/gui/highlight/ObfuscatedHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/ObfuscatedHighlightPainter.java
index ee64d86a..0947d4b7 100644
--- a/src/main/java/cuchaz/enigma/gui/highlight/ObfuscatedHighlightPainter.java
+++ b/src/main/java/cuchaz/enigma/gui/highlight/ObfuscatedHighlightPainter.java
@@ -8,13 +8,14 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.gui.highlight; 12package cuchaz.enigma.gui.highlight;
12 13
13import java.awt.Color; 14import java.awt.*;
14 15
15public class ObfuscatedHighlightPainter extends BoxHighlightPainter { 16public class ObfuscatedHighlightPainter extends BoxHighlightPainter {
16 17
17 public ObfuscatedHighlightPainter() { 18 public ObfuscatedHighlightPainter() {
18 super(new Color(255, 220, 220), new Color(160, 80, 80)); 19 super(new Color(255, 220, 220), new Color(160, 80, 80));
19 } 20 }
20} 21}
diff --git a/src/main/java/cuchaz/enigma/gui/highlight/OtherHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/OtherHighlightPainter.java
index 43d8352e..74e7273d 100644
--- a/src/main/java/cuchaz/enigma/gui/highlight/OtherHighlightPainter.java
+++ b/src/main/java/cuchaz/enigma/gui/highlight/OtherHighlightPainter.java
@@ -8,13 +8,14 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.gui.highlight; 12package cuchaz.enigma.gui.highlight;
12 13
13import java.awt.Color; 14import java.awt.*;
14 15
15public class OtherHighlightPainter extends BoxHighlightPainter { 16public class OtherHighlightPainter extends BoxHighlightPainter {
16 17
17 public OtherHighlightPainter() { 18 public OtherHighlightPainter() {
18 super(null, new Color(180, 180, 180)); 19 super(null, new Color(180, 180, 180));
19 } 20 }
20} 21}
diff --git a/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java
index f772284f..5cbeabc0 100644
--- a/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java
+++ b/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java
@@ -8,22 +8,22 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui.highlight;
12 11
13import java.awt.*; 12package cuchaz.enigma.gui.highlight;
14 13
15import javax.swing.text.Highlighter; 14import javax.swing.text.Highlighter;
16import javax.swing.text.JTextComponent; 15import javax.swing.text.JTextComponent;
16import java.awt.*;
17 17
18public class SelectionHighlightPainter implements Highlighter.HighlightPainter { 18public class SelectionHighlightPainter implements Highlighter.HighlightPainter {
19 19
20 @Override 20 @Override
21 public void paint(Graphics g, int start, int end, Shape shape, JTextComponent text) { 21 public void paint(Graphics g, int start, int end, Shape shape, JTextComponent text) {
22 // draw a thick border 22 // draw a thick border
23 Graphics2D g2d = (Graphics2D) g; 23 Graphics2D g2d = (Graphics2D) g;
24 Rectangle bounds = BoxHighlightPainter.getBounds(text, start, end); 24 Rectangle bounds = BoxHighlightPainter.getBounds(text, start, end);
25 g2d.setColor(Color.black); 25 g2d.setColor(Color.black);
26 g2d.setStroke(new BasicStroke(2.0f)); 26 g2d.setStroke(new BasicStroke(2.0f));
27 g2d.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); 27 g2d.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4);
28 } 28 }
29} 29}
diff --git a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
index 9f391f2e..dc933cd5 100644
--- a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
+++ b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
@@ -8,58 +8,59 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui.node;
12 11
13import javax.swing.tree.DefaultMutableTreeNode; 12package cuchaz.enigma.gui.node;
14 13
15import cuchaz.enigma.mapping.ClassEntry; 14import cuchaz.enigma.mapping.ClassEntry;
16 15
16import javax.swing.tree.DefaultMutableTreeNode;
17
17public class ClassSelectorClassNode extends DefaultMutableTreeNode { 18public class ClassSelectorClassNode extends DefaultMutableTreeNode {
18 19
19 private ClassEntry classEntry; 20 private ClassEntry classEntry;
20 21
21 public ClassSelectorClassNode(ClassEntry classEntry) { 22 public ClassSelectorClassNode(ClassEntry classEntry) {
22 this.classEntry = classEntry; 23 this.classEntry = classEntry;
23 this.setUserObject(classEntry); 24 this.setUserObject(classEntry);
24 } 25 }
25 26
26 public ClassEntry getClassEntry() { 27 public ClassEntry getClassEntry() {
27 return this.classEntry; 28 return this.classEntry;
28 } 29 }
29 30
30 @Override 31 @Override
31 public String toString() { 32 public String toString() {
32 return this.classEntry.getSimpleName(); 33 return this.classEntry.getSimpleName();
33 } 34 }
34 35
35 @Override 36 @Override
36 public boolean equals(Object other) { 37 public boolean equals(Object other) {
37 return other instanceof ClassSelectorClassNode && equals((ClassSelectorClassNode) other); 38 return other instanceof ClassSelectorClassNode && equals((ClassSelectorClassNode) other);
38 } 39 }
39 40
40 @Override public int hashCode() 41 @Override
41 { 42 public int hashCode() {
42 return 17 + (classEntry != null ? classEntry.hashCode() : 0); 43 return 17 + (classEntry != null ? classEntry.hashCode() : 0);
43 } 44 }
44 45
45 @Override public void setUserObject(Object userObject) 46 @Override
46 { 47 public Object getUserObject() {
47 String packageName = ""; 48 return classEntry;
48 if (classEntry.getPackageName() != null) 49 }
49 packageName = classEntry.getPackageName() + "/";
50 if (userObject instanceof String)
51 this.classEntry = new ClassEntry(packageName + userObject);
52 else if (userObject instanceof ClassEntry)
53 this.classEntry = (ClassEntry) userObject;
54 super.setUserObject(classEntry);
55 }
56 50
57 @Override public Object getUserObject() 51 @Override
58 { 52 public void setUserObject(Object userObject) {
59 return classEntry; 53 String packageName = "";
60 } 54 if (classEntry.getPackageName() != null)
55 packageName = classEntry.getPackageName() + "/";
56 if (userObject instanceof String)
57 this.classEntry = new ClassEntry(packageName + userObject);
58 else if (userObject instanceof ClassEntry)
59 this.classEntry = (ClassEntry) userObject;
60 super.setUserObject(classEntry);
61 }
61 62
62 public boolean equals(ClassSelectorClassNode other) { 63 public boolean equals(ClassSelectorClassNode other) {
63 return this.classEntry.equals(other.classEntry); 64 return this.classEntry.equals(other.classEntry);
64 } 65 }
65} 66}
diff --git a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java
index b3eb90ef..f80abba6 100644
--- a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java
+++ b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.gui.node; 12package cuchaz.enigma.gui.node;
12 13
13import javassist.bytecode.Descriptor; 14import javassist.bytecode.Descriptor;
@@ -16,44 +17,44 @@ import javax.swing.tree.DefaultMutableTreeNode;
16 17
17public class ClassSelectorPackageNode extends DefaultMutableTreeNode { 18public class ClassSelectorPackageNode extends DefaultMutableTreeNode {
18 19
19 private String packageName; 20 private String packageName;
20 21
21 public ClassSelectorPackageNode(String packageName) { 22 public ClassSelectorPackageNode(String packageName) {
22 this.packageName = packageName != null ? packageName : "(none)"; 23 this.packageName = packageName != null ? packageName : "(none)";
23 } 24 }
24 25
25 public String getPackageName() { 26 public String getPackageName() {
26 return packageName; 27 return packageName;
27 } 28 }
28 29
29 @Override public void setUserObject(Object userObject) 30 @Override
30 { 31 public Object getUserObject() {
31 if (userObject instanceof String) 32 return packageName;
32 this.packageName = (String) userObject; 33 }
33 super.setUserObject(userObject); 34
34 } 35 @Override
35 36 public void setUserObject(Object userObject) {
36 @Override public Object getUserObject() 37 if (userObject instanceof String)
37 { 38 this.packageName = (String) userObject;
38 return packageName; 39 super.setUserObject(userObject);
39 } 40 }
40 41
41 @Override 42 @Override
42 public String toString() { 43 public String toString() {
43 return !packageName.equals("(none)") ? Descriptor.toJavaName(this.packageName) : "(none)"; 44 return !packageName.equals("(none)") ? Descriptor.toJavaName(this.packageName) : "(none)";
44 } 45 }
45 46
46 @Override 47 @Override
47 public boolean equals(Object other) { 48 public boolean equals(Object other) {
48 return other instanceof ClassSelectorPackageNode && equals((ClassSelectorPackageNode) other); 49 return other instanceof ClassSelectorPackageNode && equals((ClassSelectorPackageNode) other);
49 } 50 }
50 51
51 @Override public int hashCode() 52 @Override
52 { 53 public int hashCode() {
53 return packageName.hashCode(); 54 return packageName.hashCode();
54 } 55 }
55 56
56 public boolean equals(ClassSelectorPackageNode other) { 57 public boolean equals(ClassSelectorPackageNode other) {
57 return other != null && this.packageName.equals(other.packageName); 58 return other != null && this.packageName.equals(other.packageName);
58 } 59 }
59} 60}
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java b/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java
index 4f551756..68cc8e14 100644
--- a/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java
+++ b/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java
@@ -1,28 +1,25 @@
1package cuchaz.enigma.gui.panels; 1package cuchaz.enigma.gui.panels;
2 2
3import java.awt.BorderLayout;
4
5import javax.swing.JLabel;
6import javax.swing.JPanel;
7import javax.swing.JScrollPane;
8
9import cuchaz.enigma.gui.ClassSelector; 3import cuchaz.enigma.gui.ClassSelector;
10import cuchaz.enigma.gui.Gui; 4import cuchaz.enigma.gui.Gui;
11 5
6import javax.swing.*;
7import java.awt.*;
8
12public class PanelDeobf extends JPanel { 9public class PanelDeobf extends JPanel {
13 10
14 public final ClassSelector deobfClasses; 11 public final ClassSelector deobfClasses;
15 private final Gui gui; 12 private final Gui gui;
16 13
17 public PanelDeobf(Gui gui) { 14 public PanelDeobf(Gui gui) {
18 this.gui = gui; 15 this.gui = gui;
19 16
20 this.deobfClasses = new ClassSelector(gui, ClassSelector.DEOBF_CLASS_COMPARATOR, true); 17 this.deobfClasses = new ClassSelector(gui, ClassSelector.DEOBF_CLASS_COMPARATOR, true);
21 this.deobfClasses.setSelectionListener(gui::navigateTo); 18 this.deobfClasses.setSelectionListener(gui::navigateTo);
22 this.deobfClasses.setRenameSelectionListener(gui::onPanelRename); 19 this.deobfClasses.setRenameSelectionListener(gui::onPanelRename);
23 20
24 this.setLayout(new BorderLayout()); 21 this.setLayout(new BorderLayout());
25 this.add(new JLabel("De-obfuscated Classes"), BorderLayout.NORTH); 22 this.add(new JLabel("De-obfuscated Classes"), BorderLayout.NORTH);
26 this.add(new JScrollPane(this.deobfClasses), BorderLayout.CENTER); 23 this.add(new JScrollPane(this.deobfClasses), BorderLayout.CENTER);
27 } 24 }
28} 25}
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java b/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java
index 914952b9..fd30e389 100644
--- a/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java
+++ b/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java
@@ -1,73 +1,71 @@
1package cuchaz.enigma.gui.panels; 1package cuchaz.enigma.gui.panels;
2 2
3import cuchaz.enigma.gui.BrowserCaret;
4import cuchaz.enigma.gui.Gui;
5
6import javax.swing.*;
3import java.awt.*; 7import java.awt.*;
4import java.awt.event.KeyAdapter; 8import java.awt.event.KeyAdapter;
5import java.awt.event.KeyEvent; 9import java.awt.event.KeyEvent;
6import java.awt.event.MouseAdapter; 10import java.awt.event.MouseAdapter;
7import java.awt.event.MouseEvent; 11import java.awt.event.MouseEvent;
8 12
9import javax.swing.JEditorPane;
10
11import cuchaz.enigma.gui.BrowserCaret;
12import cuchaz.enigma.gui.Gui;
13
14public class PanelEditor extends JEditorPane { 13public class PanelEditor extends JEditorPane {
15 private final Gui gui; 14 private final Gui gui;
16 15
17 public PanelEditor(Gui gui) { 16 public PanelEditor(Gui gui) {
18 this.gui = gui; 17 this.gui = gui;
19 18
20 this.setEditable(false); 19 this.setEditable(false);
21 this.setSelectionColor(new Color(31, 46, 90)); 20 this.setSelectionColor(new Color(31, 46, 90));
22 this.setCaret(new BrowserCaret()); 21 this.setCaret(new BrowserCaret());
23 this.addCaretListener(event -> gui.onCaretMove(event.getDot())); 22 this.addCaretListener(event -> gui.onCaretMove(event.getDot()));
24 final PanelEditor self = this; 23 final PanelEditor self = this;
25 this.addMouseListener(new MouseAdapter() 24 this.addMouseListener(new MouseAdapter() {
26 { 25 @Override
27 @Override public void mouseReleased(MouseEvent e) 26 public void mouseReleased(MouseEvent e) {
28 { 27 if (e.getButton() == MouseEvent.BUTTON3)
29 if (e.getButton() == MouseEvent.BUTTON3) 28 self.setCaretPosition(self.viewToModel(e.getPoint()));
30 self.setCaretPosition(self.viewToModel(e.getPoint())); 29 }
31 } 30 });
32 }); 31 this.addKeyListener(new KeyAdapter() {
33 this.addKeyListener(new KeyAdapter() { 32 @Override
34 @Override 33 public void keyPressed(KeyEvent event) {
35 public void keyPressed(KeyEvent event) { 34 switch (event.getKeyCode()) {
36 switch (event.getKeyCode()) { 35 case KeyEvent.VK_R:
37 case KeyEvent.VK_R: 36 gui.popupMenu.renameMenu.doClick();
38 gui.popupMenu.renameMenu.doClick(); 37 break;
39 break;
40 38
41 case KeyEvent.VK_I: 39 case KeyEvent.VK_I:
42 gui.popupMenu.showInheritanceMenu.doClick(); 40 gui.popupMenu.showInheritanceMenu.doClick();
43 break; 41 break;
44 42
45 case KeyEvent.VK_M: 43 case KeyEvent.VK_M:
46 gui.popupMenu.showImplementationsMenu.doClick(); 44 gui.popupMenu.showImplementationsMenu.doClick();
47 break; 45 break;
48 46
49 case KeyEvent.VK_N: 47 case KeyEvent.VK_N:
50 gui.popupMenu.openEntryMenu.doClick(); 48 gui.popupMenu.openEntryMenu.doClick();
51 break; 49 break;
52 50
53 case KeyEvent.VK_P: 51 case KeyEvent.VK_P:
54 gui.popupMenu.openPreviousMenu.doClick(); 52 gui.popupMenu.openPreviousMenu.doClick();
55 break; 53 break;
56 54
57 case KeyEvent.VK_C: 55 case KeyEvent.VK_C:
58 gui.popupMenu.showCallsMenu.doClick(); 56 gui.popupMenu.showCallsMenu.doClick();
59 break; 57 break;
60 58
61 case KeyEvent.VK_T: 59 case KeyEvent.VK_T:
62 gui.popupMenu.toggleMappingMenu.doClick(); 60 gui.popupMenu.toggleMappingMenu.doClick();
63 break; 61 break;
64 case KeyEvent.VK_F5: 62 case KeyEvent.VK_F5:
65 gui.getController().refreshCurrentClass(); 63 gui.getController().refreshCurrentClass();
66 break; 64 break;
67 default: 65 default:
68 break; 66 break;
69 } 67 }
70 } 68 }
71 }); 69 });
72 } 70 }
73} 71}
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java b/src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java
index faa023bd..1bf68879 100644
--- a/src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java
+++ b/src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java
@@ -1,34 +1,30 @@
1package cuchaz.enigma.gui.panels; 1package cuchaz.enigma.gui.panels;
2 2
3import java.awt.Dimension;
4import java.awt.GridLayout;
5
6import javax.swing.BorderFactory;
7import javax.swing.JLabel;
8import javax.swing.JPanel;
9
10import cuchaz.enigma.gui.Gui; 3import cuchaz.enigma.gui.Gui;
11import cuchaz.enigma.utils.Utils; 4import cuchaz.enigma.utils.Utils;
12 5
6import javax.swing.*;
7import java.awt.*;
8
13public class PanelIdentifier extends JPanel { 9public class PanelIdentifier extends JPanel {
14 10
15 private final Gui gui; 11 private final Gui gui;
16 12
17 public PanelIdentifier(Gui gui) { 13 public PanelIdentifier(Gui gui) {
18 this.gui = gui; 14 this.gui = gui;
19 15
20 this.setLayout(new GridLayout(4, 1, 0, 0)); 16 this.setLayout(new GridLayout(4, 1, 0, 0));
21 this.setPreferredSize(new Dimension(0, 100)); 17 this.setPreferredSize(new Dimension(0, 100));
22 this.setBorder(BorderFactory.createTitledBorder("Identifier Info")); 18 this.setBorder(BorderFactory.createTitledBorder("Identifier Info"));
23 } 19 }
24 20
25 public void clearReference() { 21 public void clearReference() {
26 this.removeAll(); 22 this.removeAll();
27 JLabel label = new JLabel("No identifier selected"); 23 JLabel label = new JLabel("No identifier selected");
28 Utils.unboldLabel(label); 24 Utils.unboldLabel(label);
29 label.setHorizontalAlignment(JLabel.CENTER); 25 label.setHorizontalAlignment(JLabel.CENTER);
30 this.add(label); 26 this.add(label);
31 27
32 gui.redraw(); 28 gui.redraw();
33 } 29 }
34} 30}
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java b/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java
index 27bb70b4..4bbd32bd 100644
--- a/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java
+++ b/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java
@@ -1,39 +1,36 @@
1package cuchaz.enigma.gui.panels; 1package cuchaz.enigma.gui.panels;
2 2
3import java.awt.BorderLayout;
4import java.util.Comparator;
5
6import javax.swing.JLabel;
7import javax.swing.JPanel;
8import javax.swing.JScrollPane;
9
10import cuchaz.enigma.gui.ClassSelector; 3import cuchaz.enigma.gui.ClassSelector;
11import cuchaz.enigma.gui.Gui; 4import cuchaz.enigma.gui.Gui;
12import cuchaz.enigma.mapping.ClassEntry; 5import cuchaz.enigma.mapping.ClassEntry;
13 6
7import javax.swing.*;
8import java.awt.*;
9import java.util.Comparator;
10
14public class PanelObf extends JPanel { 11public class PanelObf extends JPanel {
15 12
16 private final Gui gui; 13 public final ClassSelector obfClasses;
17 public final ClassSelector obfClasses; 14 private final Gui gui;
18 15
19 public PanelObf(Gui gui) { 16 public PanelObf(Gui gui) {
20 this.gui = gui; 17 this.gui = gui;
21 18
22 Comparator<ClassEntry> obfClassComparator = (a, b) -> { 19 Comparator<ClassEntry> obfClassComparator = (a, b) -> {
23 String aname = a.getName(); 20 String aname = a.getName();
24 String bname = b.getName(); 21 String bname = b.getName();
25 if (aname.length() != bname.length()) { 22 if (aname.length() != bname.length()) {
26 return aname.length() - bname.length(); 23 return aname.length() - bname.length();
27 } 24 }
28 return aname.compareTo(bname); 25 return aname.compareTo(bname);
29 }; 26 };
30 27
31 this.obfClasses = new ClassSelector(gui, obfClassComparator, false); 28 this.obfClasses = new ClassSelector(gui, obfClassComparator, false);
32 this.obfClasses.setSelectionListener(gui::navigateTo); 29 this.obfClasses.setSelectionListener(gui::navigateTo);
33 this.obfClasses.setRenameSelectionListener(gui::onPanelRename); 30 this.obfClasses.setRenameSelectionListener(gui::onPanelRename);
34 31
35 this.setLayout(new BorderLayout()); 32 this.setLayout(new BorderLayout());
36 this.add(new JLabel("Obfuscated Classes"), BorderLayout.NORTH); 33 this.add(new JLabel("Obfuscated Classes"), BorderLayout.NORTH);
37 this.add(new JScrollPane(this.obfClasses), BorderLayout.CENTER); 34 this.add(new JScrollPane(this.obfClasses), BorderLayout.CENTER);
38 } 35 }
39} 36}
diff --git a/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java b/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java
index 662516da..9154cc22 100644
--- a/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java
@@ -8,102 +8,103 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13import cuchaz.enigma.utils.Utils; 14import cuchaz.enigma.utils.Utils;
14 15
15public class ArgumentEntry implements Entry { 16public class ArgumentEntry implements Entry {
16 17
17 private BehaviorEntry behaviorEntry; 18 private BehaviorEntry behaviorEntry;
18 private int index; 19 private int index;
19 private String name; 20 private String name;
20 21
21 public ArgumentEntry(BehaviorEntry behaviorEntry, int index, String name) { 22 public ArgumentEntry(BehaviorEntry behaviorEntry, int index, String name) {
22 if (behaviorEntry == null) { 23 if (behaviorEntry == null) {
23 throw new IllegalArgumentException("Behavior cannot be null!"); 24 throw new IllegalArgumentException("Behavior cannot be null!");
24 } 25 }
25 if (index < 0) { 26 if (index < 0) {
26 throw new IllegalArgumentException("Index must be non-negative!"); 27 throw new IllegalArgumentException("Index must be non-negative!");
27 } 28 }
28 if (name == null) { 29 if (name == null) {
29 throw new IllegalArgumentException("Argument name cannot be null!"); 30 throw new IllegalArgumentException("Argument name cannot be null!");
30 } 31 }
31 32
32 this.behaviorEntry = behaviorEntry; 33 this.behaviorEntry = behaviorEntry;
33 this.index = index; 34 this.index = index;
34 this.name = name; 35 this.name = name;
35 } 36 }
36 37
37 public ArgumentEntry(ArgumentEntry other) { 38 public ArgumentEntry(ArgumentEntry other) {
38 this.behaviorEntry = other.getBehaviorEntry(); 39 this.behaviorEntry = other.getBehaviorEntry();
39 this.index = other.index; 40 this.index = other.index;
40 this.name = other.name; 41 this.name = other.name;
41 } 42 }
42 43
43 public ArgumentEntry(ArgumentEntry other, String newClassName) { 44 public ArgumentEntry(ArgumentEntry other, String newClassName) {
44 this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(new ClassEntry(newClassName)); 45 this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(new ClassEntry(newClassName));
45 this.index = other.index; 46 this.index = other.index;
46 this.name = other.name; 47 this.name = other.name;
47 } 48 }
48 49
49 public ArgumentEntry(ArgumentEntry other, BehaviorEntry entry) { 50 public ArgumentEntry(ArgumentEntry other, BehaviorEntry entry) {
50 this.behaviorEntry = entry; 51 this.behaviorEntry = entry;
51 this.index = other.index; 52 this.index = other.index;
52 this.name = other.name; 53 this.name = other.name;
53 } 54 }
54 55
55 public BehaviorEntry getBehaviorEntry() { 56 public BehaviorEntry getBehaviorEntry() {
56 return this.behaviorEntry; 57 return this.behaviorEntry;
57 } 58 }
58 59
59 public int getIndex() { 60 public int getIndex() {
60 return this.index; 61 return this.index;
61 } 62 }
62 63
63 @Override 64 @Override
64 public String getName() { 65 public String getName() {
65 return this.name; 66 return this.name;
66 } 67 }
67 68
68 @Override 69 @Override
69 public ClassEntry getClassEntry() { 70 public ClassEntry getClassEntry() {
70 return this.behaviorEntry.getClassEntry(); 71 return this.behaviorEntry.getClassEntry();
71 } 72 }
72 73
73 @Override 74 @Override
74 public String getClassName() { 75 public String getClassName() {
75 return this.behaviorEntry.getClassName(); 76 return this.behaviorEntry.getClassName();
76 } 77 }
77 78
78 @Override 79 @Override
79 public ArgumentEntry cloneToNewClass(ClassEntry classEntry) { 80 public ArgumentEntry cloneToNewClass(ClassEntry classEntry) {
80 return new ArgumentEntry(this, classEntry.getName()); 81 return new ArgumentEntry(this, classEntry.getName());
81 } 82 }
82 83
83 public String getMethodName() { 84 public String getMethodName() {
84 return this.behaviorEntry.getName(); 85 return this.behaviorEntry.getName();
85 } 86 }
86 87
87 public Signature getMethodSignature() { 88 public Signature getMethodSignature() {
88 return this.behaviorEntry.getSignature(); 89 return this.behaviorEntry.getSignature();
89 } 90 }
90 91
91 @Override 92 @Override
92 public int hashCode() { 93 public int hashCode() {
93 return Utils.combineHashesOrdered(this.behaviorEntry, Integer.valueOf(this.index).hashCode(), this.name.hashCode()); 94 return Utils.combineHashesOrdered(this.behaviorEntry, Integer.valueOf(this.index).hashCode(), this.name.hashCode());
94 } 95 }
95 96
96 @Override 97 @Override
97 public boolean equals(Object other) { 98 public boolean equals(Object other) {
98 return other instanceof ArgumentEntry && equals((ArgumentEntry) other); 99 return other instanceof ArgumentEntry && equals((ArgumentEntry) other);
99 } 100 }
100 101
101 public boolean equals(ArgumentEntry other) { 102 public boolean equals(ArgumentEntry other) {
102 return this.behaviorEntry.equals(other.behaviorEntry) && this.index == other.index && this.name.equals(other.name); 103 return this.behaviorEntry.equals(other.behaviorEntry) && this.index == other.index && this.name.equals(other.name);
103 } 104 }
104 105
105 @Override 106 @Override
106 public String toString() { 107 public String toString() {
107 return this.behaviorEntry.toString() + "(" + this.index + ":" + this.name + ")"; 108 return this.behaviorEntry + "(" + this.index + ":" + this.name + ")";
108 } 109 }
109} 110}
diff --git a/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java b/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java
index e3f89272..91ecd106 100644
--- a/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java
@@ -8,42 +8,43 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13public class ArgumentMapping implements Comparable<ArgumentMapping> { 14public class ArgumentMapping implements Comparable<ArgumentMapping> {
14 15
15 private int index; 16 private int index;
16 private String name; 17 private String name;
17 18
18 // NOTE: this argument order is important for the MethodReader/MethodWriter 19 // NOTE: this argument order is important for the MethodReader/MethodWriter
19 public ArgumentMapping(int index, String name) { 20 public ArgumentMapping(int index, String name) {
20 this.index = index; 21 this.index = index;
21 this.name = NameValidator.validateArgumentName(name); 22 this.name = NameValidator.validateArgumentName(name);
22 } 23 }
23 24
24 public ArgumentMapping(ArgumentMapping other) { 25 public ArgumentMapping(ArgumentMapping other) {
25 this.index = other.index; 26 this.index = other.index;
26 this.name = other.name; 27 this.name = other.name;
27 } 28 }
28 29
29 public int getIndex() { 30 public int getIndex() {
30 return this.index; 31 return this.index;
31 } 32 }
32 33
33 public String getName() { 34 public String getName() {
34 return this.name; 35 return this.name;
35 } 36 }
36 37
37 public void setName(String val) { 38 public void setName(String val) {
38 this.name = NameValidator.validateArgumentName(val); 39 this.name = NameValidator.validateArgumentName(val);
39 } 40 }
40 41
41 public ArgumentEntry getObfEntry(BehaviorEntry behaviorEntry) { 42 public ArgumentEntry getObfEntry(BehaviorEntry behaviorEntry) {
42 return new ArgumentEntry(behaviorEntry, index, name); 43 return new ArgumentEntry(behaviorEntry, index, name);
43 } 44 }
44 45
45 @Override 46 @Override
46 public int compareTo(ArgumentMapping other) { 47 public int compareTo(ArgumentMapping other) {
47 return Integer.compare(this.index, other.index); 48 return Integer.compare(this.index, other.index);
48 } 49 }
49} 50}
diff --git a/src/main/java/cuchaz/enigma/mapping/BehaviorEntry.java b/src/main/java/cuchaz/enigma/mapping/BehaviorEntry.java
index f5c6c051..04b4ebc7 100644
--- a/src/main/java/cuchaz/enigma/mapping/BehaviorEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/BehaviorEntry.java
@@ -8,8 +8,9 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13public interface BehaviorEntry extends Entry { 14public interface BehaviorEntry extends Entry {
14 Signature getSignature(); 15 Signature getSignature();
15} 16}
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassEntry.java b/src/main/java/cuchaz/enigma/mapping/ClassEntry.java
index 398b1355..788811ff 100644
--- a/src/main/java/cuchaz/enigma/mapping/ClassEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/ClassEntry.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
@@ -16,151 +17,151 @@ import java.util.List;
16 17
17public class ClassEntry implements Entry { 18public class ClassEntry implements Entry {
18 19
19 private String name; 20 private String name;
20 21
21 public ClassEntry(String className) { 22 public ClassEntry(String className) {
22 if (className == null) { 23 if (className == null) {
23 throw new IllegalArgumentException("Class name cannot be null!"); 24 throw new IllegalArgumentException("Class name cannot be null!");
24 } 25 }
25 if (className.indexOf('.') >= 0) { 26 if (className.indexOf('.') >= 0) {
26 throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className); 27 throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className);
27 } 28 }
28 29
29 this.name = className; 30 this.name = className;
30 31
31 if (isInnerClass() && getInnermostClassName().indexOf('/') >= 0) { 32 if (isInnerClass() && getInnermostClassName().indexOf('/') >= 0) {
32 throw new IllegalArgumentException("Inner class must not have a package: " + className); 33 throw new IllegalArgumentException("Inner class must not have a package: " + className);
33 } 34 }
34 } 35 }
35 36
36 public ClassEntry(ClassEntry other) { 37 public ClassEntry(ClassEntry other) {
37 this.name = other.name; 38 this.name = other.name;
38 } 39 }
39 40
40 @Override 41 @Override
41 public String getName() { 42 public String getName() {
42 return this.name; 43 return this.name;
43 } 44 }
44 45
45 @Override 46 @Override
46 public String getClassName() { 47 public String getClassName() {
47 return this.name; 48 return this.name;
48 } 49 }
49 50
50 @Override 51 @Override
51 public ClassEntry getClassEntry() { 52 public ClassEntry getClassEntry() {
52 return this; 53 return this;
53 } 54 }
54 55
55 @Override 56 @Override
56 public ClassEntry cloneToNewClass(ClassEntry classEntry) { 57 public ClassEntry cloneToNewClass(ClassEntry classEntry) {
57 return classEntry; 58 return classEntry;
58 } 59 }
59 60
60 @Override 61 @Override
61 public int hashCode() { 62 public int hashCode() {
62 return this.name.hashCode(); 63 return this.name.hashCode();
63 } 64 }
64 65
65 @Override 66 @Override
66 public boolean equals(Object other) { 67 public boolean equals(Object other) {
67 return other instanceof ClassEntry && equals((ClassEntry) other); 68 return other instanceof ClassEntry && equals((ClassEntry) other);
68 } 69 }
69 70
70 public boolean equals(ClassEntry other) { 71 public boolean equals(ClassEntry other) {
71 return other != null && this.name.equals(other.name); 72 return other != null && this.name.equals(other.name);
72 } 73 }
73 74
74 @Override 75 @Override
75 public String toString() { 76 public String toString() {
76 return this.name; 77 return this.name;
77 } 78 }
78 79
79 public boolean isInnerClass() { 80 public boolean isInnerClass() {
80 return this.name.lastIndexOf('$') >= 0; 81 return this.name.lastIndexOf('$') >= 0;
81 } 82 }
82 83
83 public List<String> getClassChainNames() { 84 public List<String> getClassChainNames() {
84 return Lists.newArrayList(this.name.split("\\$")); 85 return Lists.newArrayList(this.name.split("\\$"));
85 } 86 }
86 87
87 public List<ClassEntry> getClassChain() { 88 public List<ClassEntry> getClassChain() {
88 List<ClassEntry> entries = Lists.newArrayList(); 89 List<ClassEntry> entries = Lists.newArrayList();
89 StringBuilder buf = new StringBuilder(); 90 StringBuilder buf = new StringBuilder();
90 for (String name : getClassChainNames()) { 91 for (String name : getClassChainNames()) {
91 if (buf.length() > 0) { 92 if (buf.length() > 0) {
92 buf.append("$"); 93 buf.append("$");
93 } 94 }
94 buf.append(name); 95 buf.append(name);
95 entries.add(new ClassEntry(buf.toString())); 96 entries.add(new ClassEntry(buf.toString()));
96 } 97 }
97 return entries; 98 return entries;
98 } 99 }
99 100
100 public String getOutermostClassName() { 101 public String getOutermostClassName() {
101 if (isInnerClass()) { 102 if (isInnerClass()) {
102 return this.name.substring(0, this.name.indexOf('$')); 103 return this.name.substring(0, this.name.indexOf('$'));
103 } 104 }
104 return this.name; 105 return this.name;
105 } 106 }
106 107
107 public ClassEntry getOutermostClassEntry() { 108 public ClassEntry getOutermostClassEntry() {
108 return new ClassEntry(getOutermostClassName()); 109 return new ClassEntry(getOutermostClassName());
109 } 110 }
110 111
111 public String getOuterClassName() { 112 public String getOuterClassName() {
112 if (!isInnerClass()) { 113 if (!isInnerClass()) {
113 throw new Error("This is not an inner class!"); 114 throw new Error("This is not an inner class!");
114 } 115 }
115 return this.name.substring(0, this.name.lastIndexOf('$')); 116 return this.name.substring(0, this.name.lastIndexOf('$'));
116 } 117 }
117 118
118 public ClassEntry getOuterClassEntry() { 119 public ClassEntry getOuterClassEntry() {
119 return new ClassEntry(getOuterClassName()); 120 return new ClassEntry(getOuterClassName());
120 } 121 }
121 122
122 public String getInnermostClassName() { 123 public String getInnermostClassName() {
123 if (!isInnerClass()) { 124 if (!isInnerClass()) {
124 throw new Error("This is not an inner class!"); 125 throw new Error("This is not an inner class!");
125 } 126 }
126 return this.name.substring(this.name.lastIndexOf('$') + 1); 127 return this.name.substring(this.name.lastIndexOf('$') + 1);
127 } 128 }
128 129
129 public boolean isInDefaultPackage() { 130 public boolean isInDefaultPackage() {
130 return this.name.indexOf('/') < 0; 131 return this.name.indexOf('/') < 0;
131 } 132 }
132 133
133 public String getPackageName() { 134 public String getPackageName() {
134 int pos = this.name.lastIndexOf('/'); 135 int pos = this.name.lastIndexOf('/');
135 if (pos > 0) { 136 if (pos > 0) {
136 return this.name.substring(0, pos); 137 return this.name.substring(0, pos);
137 } 138 }
138 return null; 139 return null;
139 } 140 }
140 141
141 public String getSimpleName() { 142 public String getSimpleName() {
142 int pos = this.name.lastIndexOf('/'); 143 int pos = this.name.lastIndexOf('/');
143 if (pos > 0) { 144 if (pos > 0) {
144 return this.name.substring(pos + 1); 145 return this.name.substring(pos + 1);
145 } 146 }
146 return this.name; 147 return this.name;
147 } 148 }
148 149
149 public ClassEntry buildClassEntry(List<ClassEntry> classChain) { 150 public ClassEntry buildClassEntry(List<ClassEntry> classChain) {
150 assert (classChain.contains(this)); 151 assert (classChain.contains(this));
151 StringBuilder buf = new StringBuilder(); 152 StringBuilder buf = new StringBuilder();
152 for (ClassEntry chainEntry : classChain) { 153 for (ClassEntry chainEntry : classChain) {
153 if (buf.length() == 0) { 154 if (buf.length() == 0) {
154 buf.append(chainEntry.getName()); 155 buf.append(chainEntry.getName());
155 } else { 156 } else {
156 buf.append("$"); 157 buf.append("$");
157 buf.append(chainEntry.isInnerClass() ? chainEntry.getInnermostClassName() : chainEntry.getSimpleName()); 158 buf.append(chainEntry.isInnerClass() ? chainEntry.getInnermostClassName() : chainEntry.getSimpleName());
158 } 159 }
159 160
160 if (chainEntry == this) { 161 if (chainEntry == this) {
161 break; 162 break;
162 } 163 }
163 } 164 }
164 return new ClassEntry(buf.toString()); 165 return new ClassEntry(buf.toString());
165 } 166 }
166} 167}
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
index a261c912..178dd3c6 100644
--- a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
@@ -8,540 +8,530 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13import com.google.common.collect.Maps; 14import com.google.common.collect.Maps;
15import cuchaz.enigma.throwables.MappingConflict;
14 16
15import java.util.ArrayList; 17import java.util.ArrayList;
16import java.util.Map; 18import java.util.Map;
17 19
18import cuchaz.enigma.throwables.MappingConflict;
19
20// FIXME: Enigma doesn't support inner classes of inner class????! 20// FIXME: Enigma doesn't support inner classes of inner class????!
21public class ClassMapping implements Comparable<ClassMapping> { 21public class ClassMapping implements Comparable<ClassMapping> {
22 22
23 private String obfFullName; 23 private String obfFullName;
24 private String obfSimpleName; 24 private String obfSimpleName;
25 private String deobfName; 25 private String deobfName;
26 private String previousDeobfName; 26 private String previousDeobfName;
27 private Map<String, ClassMapping> innerClassesByObfSimple; 27 private Map<String, ClassMapping> innerClassesByObfSimple;
28 private Map<String, ClassMapping> innerClassesByObfFull; 28 private Map<String, ClassMapping> innerClassesByObfFull;
29 private Map<String, ClassMapping> innerClassesByDeobf; 29 private Map<String, ClassMapping> innerClassesByDeobf;
30 private Map<String, FieldMapping> fieldsByObf; 30 private Map<String, FieldMapping> fieldsByObf;
31 private Map<String, FieldMapping> fieldsByDeobf; 31 private Map<String, FieldMapping> fieldsByDeobf;
32 private Map<String, MethodMapping> methodsByObf; 32 private Map<String, MethodMapping> methodsByObf;
33 private Map<String, MethodMapping> methodsByDeobf; 33 private Map<String, MethodMapping> methodsByDeobf;
34 private boolean isDirty; 34 private boolean isDirty;
35 private Mappings.EntryModifier modifier; 35 private Mappings.EntryModifier modifier;
36 36
37 public ClassMapping(String obfFullName) 37 public ClassMapping(String obfFullName) {
38 { 38 this(obfFullName, null, Mappings.EntryModifier.UNCHANGED);
39 this(obfFullName, null, Mappings.EntryModifier.UNCHANGED); 39 }
40 } 40
41 41 public ClassMapping(String obfFullName, String deobfName) {
42 public ClassMapping(String obfFullName, String deobfName) 42 this(obfFullName, deobfName, Mappings.EntryModifier.UNCHANGED);
43 { 43 }
44 this(obfFullName, deobfName, Mappings.EntryModifier.UNCHANGED); 44
45 } 45 public ClassMapping(String obfFullName, String deobfName, Mappings.EntryModifier modifier) {
46 46 this.obfFullName = obfFullName;
47 public ClassMapping(String obfFullName, String deobfName, Mappings.EntryModifier modifier) 47 ClassEntry classEntry = new ClassEntry(obfFullName);
48 { 48 obfSimpleName = classEntry.isInnerClass() ? classEntry.getInnermostClassName() : classEntry.getSimpleName();
49 this.obfFullName = obfFullName; 49 previousDeobfName = null;
50 ClassEntry classEntry = new ClassEntry(obfFullName); 50 this.deobfName = NameValidator.validateClassName(deobfName, false);
51 obfSimpleName = classEntry.isInnerClass() ? classEntry.getInnermostClassName() : classEntry.getSimpleName(); 51 innerClassesByObfSimple = Maps.newHashMap();
52 previousDeobfName = null; 52 innerClassesByObfFull = Maps.newHashMap();
53 this.deobfName = NameValidator.validateClassName(deobfName, false); 53 innerClassesByDeobf = Maps.newHashMap();
54 innerClassesByObfSimple = Maps.newHashMap(); 54 fieldsByObf = Maps.newHashMap();
55 innerClassesByObfFull = Maps.newHashMap(); 55 fieldsByDeobf = Maps.newHashMap();
56 innerClassesByDeobf = Maps.newHashMap(); 56 methodsByObf = Maps.newHashMap();
57 fieldsByObf = Maps.newHashMap(); 57 methodsByDeobf = Maps.newHashMap();
58 fieldsByDeobf = Maps.newHashMap(); 58 isDirty = true;
59 methodsByObf = Maps.newHashMap(); 59 this.modifier = modifier;
60 methodsByDeobf = Maps.newHashMap(); 60 }
61 isDirty = true; 61
62 this.modifier = modifier; 62 public static boolean isSimpleClassName(String name) {
63 } 63 return name.indexOf('/') < 0 && name.indexOf('$') < 0;
64 64 }
65 public String getObfFullName() { 65
66 return obfFullName; 66 public String getObfFullName() {
67 } 67 return obfFullName;
68 68 }
69 public String getObfSimpleName() { 69
70 return obfSimpleName; 70 public String getObfSimpleName() {
71 } 71 return obfSimpleName;
72 72 }
73 public String getPreviousDeobfName() { 73
74 return previousDeobfName; 74 public String getPreviousDeobfName() {
75 } 75 return previousDeobfName;
76 76 }
77 public String getDeobfName() { 77
78 return deobfName; 78 public String getDeobfName() {
79 } 79 return deobfName;
80 80 }
81 public void setDeobfName(String val) { 81
82 previousDeobfName = deobfName; 82 //// INNER CLASSES ////////
83 deobfName = NameValidator.validateClassName(val, false); 83
84 this.isDirty = true; 84 public void setDeobfName(String val) {
85 } 85 previousDeobfName = deobfName;
86 86 deobfName = NameValidator.validateClassName(val, false);
87 //// INNER CLASSES //////// 87 this.isDirty = true;
88 88 }
89 public Iterable<ClassMapping> innerClasses() { 89
90 assert (innerClassesByObfSimple.size() >= innerClassesByDeobf.size()); 90 public Iterable<ClassMapping> innerClasses() {
91 return innerClassesByObfSimple.values(); 91 assert (innerClassesByObfSimple.size() >= innerClassesByDeobf.size());
92 } 92 return innerClassesByObfSimple.values();
93 93 }
94 public void addInnerClassMapping(ClassMapping classMapping) throws MappingConflict { 94
95 // FIXME: dirty hack, that can get into issues, but it's a temp fix! 95 public void addInnerClassMapping(ClassMapping classMapping) throws MappingConflict {
96 if (this.innerClassesByObfFull.containsKey(classMapping.getObfSimpleName())) { 96 // FIXME: dirty hack, that can get into issues, but it's a temp fix!
97 throw new MappingConflict("classes", classMapping.getObfSimpleName(), this.innerClassesByObfSimple.get(classMapping.getObfSimpleName()).getObfSimpleName()); 97 if (this.innerClassesByObfFull.containsKey(classMapping.getObfSimpleName())) {
98 } 98 throw new MappingConflict("classes", classMapping.getObfSimpleName(), this.innerClassesByObfSimple.get(classMapping.getObfSimpleName()).getObfSimpleName());
99 innerClassesByObfFull.put(classMapping.getObfFullName(), classMapping); 99 }
100 innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping); 100 innerClassesByObfFull.put(classMapping.getObfFullName(), classMapping);
101 101 innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping);
102 if (classMapping.getDeobfName() != null) { 102
103 if (this.innerClassesByDeobf.containsKey(classMapping.getDeobfName())) { 103 if (classMapping.getDeobfName() != null) {
104 throw new MappingConflict("classes", classMapping.getDeobfName(), this.innerClassesByDeobf.get(classMapping.getDeobfName()).getDeobfName()); 104 if (this.innerClassesByDeobf.containsKey(classMapping.getDeobfName())) {
105 } 105 throw new MappingConflict("classes", classMapping.getDeobfName(), this.innerClassesByDeobf.get(classMapping.getDeobfName()).getDeobfName());
106 innerClassesByDeobf.put(classMapping.getDeobfName(), classMapping); 106 }
107 } 107 innerClassesByDeobf.put(classMapping.getDeobfName(), classMapping);
108 this.isDirty = true; 108 }
109 } 109 this.isDirty = true;
110 110 }
111 public void removeInnerClassMapping(ClassMapping classMapping) { 111
112 innerClassesByObfFull.remove(classMapping.getObfFullName()); 112 public void removeInnerClassMapping(ClassMapping classMapping) {
113 boolean obfWasRemoved = innerClassesByObfSimple.remove(classMapping.getObfSimpleName()) != null; 113 innerClassesByObfFull.remove(classMapping.getObfFullName());
114 assert (obfWasRemoved); 114 boolean obfWasRemoved = innerClassesByObfSimple.remove(classMapping.getObfSimpleName()) != null;
115 if (classMapping.getDeobfName() != null) { 115 assert (obfWasRemoved);
116 boolean deobfWasRemoved = innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; 116 if (classMapping.getDeobfName() != null) {
117 assert (deobfWasRemoved); 117 boolean deobfWasRemoved = innerClassesByDeobf.remove(classMapping.getDeobfName()) != null;
118 } 118 assert (deobfWasRemoved);
119 this.isDirty = true; 119 }
120 } 120 this.isDirty = true;
121 121 }
122 public ClassMapping getOrCreateInnerClass(ClassEntry obfInnerClass) { 122
123 ClassMapping classMapping = innerClassesByObfSimple.get(obfInnerClass.getInnermostClassName()); 123 public ClassMapping getOrCreateInnerClass(ClassEntry obfInnerClass) {
124 if (classMapping == null) { 124 ClassMapping classMapping = innerClassesByObfSimple.get(obfInnerClass.getInnermostClassName());
125 classMapping = new ClassMapping(obfInnerClass.getName()); 125 if (classMapping == null) {
126 innerClassesByObfFull.put(classMapping.getObfFullName(), classMapping); 126 classMapping = new ClassMapping(obfInnerClass.getName());
127 boolean wasAdded = innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping) == null; 127 innerClassesByObfFull.put(classMapping.getObfFullName(), classMapping);
128 assert (wasAdded); 128 boolean wasAdded = innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping) == null;
129 this.isDirty = true; 129 assert (wasAdded);
130 } 130 this.isDirty = true;
131 return classMapping; 131 }
132 } 132 return classMapping;
133 133 }
134 public ClassMapping getInnerClassByObfSimple(String obfSimpleName) { 134
135 assert (isSimpleClassName(obfSimpleName)); 135 public ClassMapping getInnerClassByObfSimple(String obfSimpleName) {
136 return innerClassesByObfSimple.get(obfSimpleName); 136 assert (isSimpleClassName(obfSimpleName));
137 } 137 return innerClassesByObfSimple.get(obfSimpleName);
138 138 }
139 public ClassMapping getInnerClassByDeobf(String deobfName) { 139
140 assert (isSimpleClassName(deobfName)); 140 public ClassMapping getInnerClassByDeobf(String deobfName) {
141 return innerClassesByDeobf.get(deobfName); 141 assert (isSimpleClassName(deobfName));
142 } 142 return innerClassesByDeobf.get(deobfName);
143 143 }
144 public ClassMapping getInnerClassByDeobfThenObfSimple(String name) { 144
145 ClassMapping classMapping = getInnerClassByDeobf(name); 145 public ClassMapping getInnerClassByDeobfThenObfSimple(String name) {
146 if (classMapping == null) { 146 ClassMapping classMapping = getInnerClassByDeobf(name);
147 classMapping = getInnerClassByObfSimple(name); 147 if (classMapping == null) {
148 } 148 classMapping = getInnerClassByObfSimple(name);
149 return classMapping; 149 }
150 } 150 return classMapping;
151 151 }
152 public String getDeobfInnerClassName(String obfSimpleName) { 152
153 assert (isSimpleClassName(obfSimpleName)); 153 public String getDeobfInnerClassName(String obfSimpleName) {
154 ClassMapping classMapping = innerClassesByObfSimple.get(obfSimpleName); 154 assert (isSimpleClassName(obfSimpleName));
155 if (classMapping != null) { 155 ClassMapping classMapping = innerClassesByObfSimple.get(obfSimpleName);
156 return classMapping.getDeobfName(); 156 if (classMapping != null) {
157 } 157 return classMapping.getDeobfName();
158 return null; 158 }
159 } 159 return null;
160 160 }
161 public void setInnerClassName(ClassEntry obfInnerClass, String deobfName) { 161
162 ClassMapping classMapping = getOrCreateInnerClass(obfInnerClass); 162 public void setInnerClassName(ClassEntry obfInnerClass, String deobfName) {
163 if (classMapping.getDeobfName() != null) { 163 ClassMapping classMapping = getOrCreateInnerClass(obfInnerClass);
164 boolean wasRemoved = innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; 164 if (classMapping.getDeobfName() != null) {
165 assert (wasRemoved); 165 boolean wasRemoved = innerClassesByDeobf.remove(classMapping.getDeobfName()) != null;
166 } 166 assert (wasRemoved);
167 classMapping.setDeobfName(deobfName); 167 }
168 if (deobfName != null) { 168 classMapping.setDeobfName(deobfName);
169 assert (isSimpleClassName(deobfName)); 169 if (deobfName != null) {
170 boolean wasAdded = innerClassesByDeobf.put(deobfName, classMapping) == null; 170 assert (isSimpleClassName(deobfName));
171 assert (wasAdded); 171 boolean wasAdded = innerClassesByDeobf.put(deobfName, classMapping) == null;
172 } 172 assert (wasAdded);
173 this.isDirty = true; 173 }
174 } 174 this.isDirty = true;
175 175 }
176 public boolean hasInnerClassByObfSimple(String obfSimpleName) { 176
177 return innerClassesByObfSimple.containsKey(obfSimpleName); 177 public boolean hasInnerClassByObfSimple(String obfSimpleName) {
178 } 178 return innerClassesByObfSimple.containsKey(obfSimpleName);
179 179 }
180 public boolean hasInnerClassByDeobf(String deobfName) { 180
181 return innerClassesByDeobf.containsKey(deobfName); 181 //// FIELDS ////////
182 } 182
183 183 public boolean hasInnerClassByDeobf(String deobfName) {
184 184 return innerClassesByDeobf.containsKey(deobfName);
185 //// FIELDS //////// 185 }
186 186
187 public Iterable<FieldMapping> fields() { 187 public Iterable<FieldMapping> fields() {
188 assert (fieldsByObf.size() == fieldsByDeobf.size()); 188 assert (fieldsByObf.size() == fieldsByDeobf.size());
189 return fieldsByObf.values(); 189 return fieldsByObf.values();
190 } 190 }
191 191
192 public boolean containsObfField(String obfName, Type obfType) { 192 public boolean containsObfField(String obfName, Type obfType) {
193 return fieldsByObf.containsKey(getFieldKey(obfName, obfType)); 193 return fieldsByObf.containsKey(getFieldKey(obfName, obfType));
194 } 194 }
195 195
196 public boolean containsDeobfField(String deobfName, Type deobfType) { 196 public boolean containsDeobfField(String deobfName, Type deobfType) {
197 return fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfType)); 197 return fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfType));
198 } 198 }
199 199
200 public void addFieldMapping(FieldMapping fieldMapping) { 200 public void addFieldMapping(FieldMapping fieldMapping) {
201 String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); 201 String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType());
202 if (fieldsByObf.containsKey(obfKey)) { 202 if (fieldsByObf.containsKey(obfKey)) {
203 throw new Error("Already have mapping for " + obfFullName + "." + obfKey); 203 throw new Error("Already have mapping for " + obfFullName + "." + obfKey);
204 } 204 }
205 if (fieldMapping.getDeobfName() != null) { 205 if (fieldMapping.getDeobfName() != null) {
206 String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType()); 206 String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType());
207 if (fieldsByDeobf.containsKey(deobfKey)) { 207 if (fieldsByDeobf.containsKey(deobfKey)) {
208 throw new Error("Already have mapping for " + deobfName + "." + deobfKey); 208 throw new Error("Already have mapping for " + deobfName + "." + deobfKey);
209 } 209 }
210 boolean deobfWasAdded = fieldsByDeobf.put(deobfKey, fieldMapping) == null; 210 boolean deobfWasAdded = fieldsByDeobf.put(deobfKey, fieldMapping) == null;
211 assert (deobfWasAdded); 211 assert (deobfWasAdded);
212 } 212 }
213 boolean obfWasAdded = fieldsByObf.put(obfKey, fieldMapping) == null; 213 boolean obfWasAdded = fieldsByObf.put(obfKey, fieldMapping) == null;
214 assert (obfWasAdded); 214 assert (obfWasAdded);
215 this.isDirty = true; 215 this.isDirty = true;
216 } 216 }
217 217
218 public void removeFieldMapping(FieldMapping fieldMapping) { 218 public void removeFieldMapping(FieldMapping fieldMapping) {
219 boolean obfWasRemoved = fieldsByObf.remove(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType())) != null; 219 boolean obfWasRemoved = fieldsByObf.remove(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType())) != null;
220 assert (obfWasRemoved); 220 assert (obfWasRemoved);
221 if (fieldMapping.getDeobfName() != null) { 221 if (fieldMapping.getDeobfName() != null) {
222 boolean deobfWasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType())) != null; 222 boolean deobfWasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType())) != null;
223 assert (deobfWasRemoved); 223 assert (deobfWasRemoved);
224 } 224 }
225 this.isDirty = true; 225 this.isDirty = true;
226 } 226 }
227 227
228 public FieldMapping getFieldByObf(String obfName, Type obfType) { 228 public FieldMapping getFieldByObf(String obfName, Type obfType) {
229 return fieldsByObf.get(getFieldKey(obfName, obfType)); 229 return fieldsByObf.get(getFieldKey(obfName, obfType));
230 } 230 }
231 231
232 public FieldMapping getFieldByDeobf(String deobfName, Type obfType) { 232 public FieldMapping getFieldByDeobf(String deobfName, Type obfType) {
233 return fieldsByDeobf.get(getFieldKey(deobfName, obfType)); 233 return fieldsByDeobf.get(getFieldKey(deobfName, obfType));
234 } 234 }
235 235
236 public String getObfFieldName(String deobfName, Type obfType) { 236 public String getObfFieldName(String deobfName, Type obfType) {
237 FieldMapping fieldMapping = fieldsByDeobf.get(getFieldKey(deobfName, obfType)); 237 FieldMapping fieldMapping = fieldsByDeobf.get(getFieldKey(deobfName, obfType));
238 if (fieldMapping != null) { 238 if (fieldMapping != null) {
239 return fieldMapping.getObfName(); 239 return fieldMapping.getObfName();
240 } 240 }
241 return null; 241 return null;
242 } 242 }
243 243
244 public String getDeobfFieldName(String obfName, Type obfType) { 244 public String getDeobfFieldName(String obfName, Type obfType) {
245 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType)); 245 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType));
246 if (fieldMapping != null) { 246 if (fieldMapping != null) {
247 return fieldMapping.getDeobfName(); 247 return fieldMapping.getDeobfName();
248 } 248 }
249 return null; 249 return null;
250 } 250 }
251 251
252 private String getFieldKey(String name, Type type) { 252 private String getFieldKey(String name, Type type) {
253 if (name == null) { 253 if (name == null) {
254 throw new IllegalArgumentException("name cannot be null!"); 254 throw new IllegalArgumentException("name cannot be null!");
255 } 255 }
256 if (type == null) { 256 if (type == null) {
257 throw new IllegalArgumentException("type cannot be null!"); 257 throw new IllegalArgumentException("type cannot be null!");
258 } 258 }
259 return name + ":" + type; 259 return name + ":" + type;
260 } 260 }
261 261
262 public void setFieldName(String obfName, Type obfType, String deobfName) { 262 public void setFieldName(String obfName, Type obfType, String deobfName) {
263 assert (deobfName != null); 263 assert (deobfName != null);
264 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType)); 264 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType));
265 if (fieldMapping == null) { 265 if (fieldMapping == null) {
266 fieldMapping = new FieldMapping(obfName, obfType, deobfName, Mappings.EntryModifier.UNCHANGED); 266 fieldMapping = new FieldMapping(obfName, obfType, deobfName, Mappings.EntryModifier.UNCHANGED);
267 boolean obfWasAdded = fieldsByObf.put(getFieldKey(obfName, obfType), fieldMapping) == null; 267 boolean obfWasAdded = fieldsByObf.put(getFieldKey(obfName, obfType), fieldMapping) == null;
268 assert (obfWasAdded); 268 assert (obfWasAdded);
269 } else { 269 } else {
270 boolean wasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), obfType)) != null; 270 boolean wasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), obfType)) != null;
271 assert (wasRemoved); 271 assert (wasRemoved);
272 } 272 }
273 fieldMapping.setDeobfName(deobfName); 273 fieldMapping.setDeobfName(deobfName);
274 if (deobfName != null) { 274 if (deobfName != null) {
275 boolean wasAdded = fieldsByDeobf.put(getFieldKey(deobfName, obfType), fieldMapping) == null; 275 boolean wasAdded = fieldsByDeobf.put(getFieldKey(deobfName, obfType), fieldMapping) == null;
276 assert (wasAdded); 276 assert (wasAdded);
277 } 277 }
278 this.isDirty = true; 278 this.isDirty = true;
279 } 279 }
280 280
281 public void setFieldObfNameAndType(String oldObfName, Type obfType, String newObfName, Type newObfType) { 281 //// METHODS ////////
282 assert(newObfName != null); 282
283 FieldMapping fieldMapping = fieldsByObf.remove(getFieldKey(oldObfName, obfType)); 283 public void setFieldObfNameAndType(String oldObfName, Type obfType, String newObfName, Type newObfType) {
284 assert(fieldMapping != null); 284 assert (newObfName != null);
285 fieldMapping.setObfName(newObfName); 285 FieldMapping fieldMapping = fieldsByObf.remove(getFieldKey(oldObfName, obfType));
286 fieldMapping.setObfType(newObfType); 286 assert (fieldMapping != null);
287 boolean obfWasAdded = fieldsByObf.put(getFieldKey(newObfName, newObfType), fieldMapping) == null; 287 fieldMapping.setObfName(newObfName);
288 assert(obfWasAdded); 288 fieldMapping.setObfType(newObfType);
289 this.isDirty = true; 289 boolean obfWasAdded = fieldsByObf.put(getFieldKey(newObfName, newObfType), fieldMapping) == null;
290 } 290 assert (obfWasAdded);
291 291 this.isDirty = true;
292 //// METHODS //////// 292 }
293 293
294 public Iterable<MethodMapping> methods() { 294 public Iterable<MethodMapping> methods() {
295 assert (methodsByObf.size() >= methodsByDeobf.size()); 295 assert (methodsByObf.size() >= methodsByDeobf.size());
296 return methodsByObf.values(); 296 return methodsByObf.values();
297 } 297 }
298 298
299 public boolean containsObfMethod(String obfName, Signature obfSignature) { 299 public boolean containsObfMethod(String obfName, Signature obfSignature) {
300 return methodsByObf.containsKey(getMethodKey(obfName, obfSignature)); 300 return methodsByObf.containsKey(getMethodKey(obfName, obfSignature));
301 } 301 }
302 302
303 public boolean containsDeobfMethod(String deobfName, Signature obfSignature) { 303 public boolean containsDeobfMethod(String deobfName, Signature obfSignature) {
304 return methodsByDeobf.containsKey(getMethodKey(deobfName, obfSignature)); 304 return methodsByDeobf.containsKey(getMethodKey(deobfName, obfSignature));
305 } 305 }
306 306
307 public void addMethodMapping(MethodMapping methodMapping) { 307 public void addMethodMapping(MethodMapping methodMapping) {
308 String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); 308 String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature());
309 if (methodsByObf.containsKey(obfKey)) { 309 if (methodsByObf.containsKey(obfKey)) {
310 throw new Error("Already have mapping for " + obfFullName + "." + obfKey); 310 throw new Error("Already have mapping for " + obfFullName + "." + obfKey);
311 } 311 }
312 boolean wasAdded = methodsByObf.put(obfKey, methodMapping) == null; 312 boolean wasAdded = methodsByObf.put(obfKey, methodMapping) == null;
313 assert (wasAdded); 313 assert (wasAdded);
314 if (methodMapping.getDeobfName() != null) { 314 if (methodMapping.getDeobfName() != null) {
315 String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature()); 315 String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature());
316 if (methodsByDeobf.containsKey(deobfKey)) { 316 if (methodsByDeobf.containsKey(deobfKey)) {
317 throw new Error("Already have mapping for " + deobfName + "." + deobfKey); 317 throw new Error("Already have mapping for " + deobfName + "." + deobfKey);
318 } 318 }
319 boolean deobfWasAdded = methodsByDeobf.put(deobfKey, methodMapping) == null; 319 boolean deobfWasAdded = methodsByDeobf.put(deobfKey, methodMapping) == null;
320 assert (deobfWasAdded); 320 assert (deobfWasAdded);
321 } 321 }
322 this.isDirty = true; 322 this.isDirty = true;
323 assert (methodsByObf.size() >= methodsByDeobf.size()); 323 assert (methodsByObf.size() >= methodsByDeobf.size());
324 } 324 }
325 325
326 public void removeMethodMapping(MethodMapping methodMapping) { 326 public void removeMethodMapping(MethodMapping methodMapping) {
327 boolean obfWasRemoved = methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature())) != null; 327 boolean obfWasRemoved = methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature())) != null;
328 assert (obfWasRemoved); 328 assert (obfWasRemoved);
329 if (methodMapping.getDeobfName() != null) { 329 if (methodMapping.getDeobfName() != null) {
330 boolean deobfWasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; 330 boolean deobfWasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null;
331 assert (deobfWasRemoved); 331 assert (deobfWasRemoved);
332 } 332 }
333 this.isDirty = true; 333 this.isDirty = true;
334 } 334 }
335 335
336 public MethodMapping getMethodByObf(String obfName, Signature obfSignature) { 336 public MethodMapping getMethodByObf(String obfName, Signature obfSignature) {
337 return methodsByObf.get(getMethodKey(obfName, obfSignature)); 337 return methodsByObf.get(getMethodKey(obfName, obfSignature));
338 } 338 }
339 339
340 public MethodMapping getMethodByDeobf(String deobfName, Signature obfSignature) { 340 public MethodMapping getMethodByDeobf(String deobfName, Signature obfSignature) {
341 return methodsByDeobf.get(getMethodKey(deobfName, obfSignature)); 341 return methodsByDeobf.get(getMethodKey(deobfName, obfSignature));
342 } 342 }
343 343
344 private String getMethodKey(String name, Signature signature) { 344 private String getMethodKey(String name, Signature signature) {
345 if (name == null) { 345 if (name == null) {
346 throw new IllegalArgumentException("name cannot be null!"); 346 throw new IllegalArgumentException("name cannot be null!");
347 } 347 }
348 if (signature == null) { 348 if (signature == null) {
349 throw new IllegalArgumentException("signature cannot be null!"); 349 throw new IllegalArgumentException("signature cannot be null!");
350 } 350 }
351 return name + signature; 351 return name + signature;
352 } 352 }
353 353
354 public void setMethodName(String obfName, Signature obfSignature, String deobfName) { 354 public void setMethodName(String obfName, Signature obfSignature, String deobfName) {
355 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfName, obfSignature)); 355 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfName, obfSignature));
356 if (methodMapping == null) { 356 if (methodMapping == null) {
357 methodMapping = createMethodMapping(obfName, obfSignature); 357 methodMapping = createMethodMapping(obfName, obfSignature);
358 } else if (methodMapping.getDeobfName() != null) { 358 } else if (methodMapping.getDeobfName() != null) {
359 boolean wasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; 359 boolean wasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null;
360 assert (wasRemoved); 360 assert (wasRemoved);
361 } 361 }
362 methodMapping.setDeobfName(deobfName); 362 methodMapping.setDeobfName(deobfName);
363 if (deobfName != null) { 363 if (deobfName != null) {
364 boolean wasAdded = methodsByDeobf.put(getMethodKey(deobfName, obfSignature), methodMapping) == null; 364 boolean wasAdded = methodsByDeobf.put(getMethodKey(deobfName, obfSignature), methodMapping) == null;
365 assert (wasAdded); 365 assert (wasAdded);
366 } 366 }
367 this.isDirty = true; 367 this.isDirty = true;
368 } 368 }
369 369
370 public void setMethodObfNameAndSignature(String oldObfName, Signature obfSignature, String newObfName, Signature newObfSignature) { 370 //// ARGUMENTS ////////
371 assert(newObfName != null); 371
372 MethodMapping methodMapping = methodsByObf.remove(getMethodKey(oldObfName, obfSignature)); 372 public void setMethodObfNameAndSignature(String oldObfName, Signature obfSignature, String newObfName, Signature newObfSignature) {
373 assert(methodMapping != null); 373 assert (newObfName != null);
374 methodMapping.setObfName(newObfName); 374 MethodMapping methodMapping = methodsByObf.remove(getMethodKey(oldObfName, obfSignature));
375 methodMapping.setObfSignature(newObfSignature); 375 assert (methodMapping != null);
376 boolean obfWasAdded = methodsByObf.put(getMethodKey(newObfName, newObfSignature), methodMapping) == null; 376 methodMapping.setObfName(newObfName);
377 assert(obfWasAdded); 377 methodMapping.setObfSignature(newObfSignature);
378 this.isDirty = true; 378 boolean obfWasAdded = methodsByObf.put(getMethodKey(newObfName, newObfSignature), methodMapping) == null;
379 } 379 assert (obfWasAdded);
380 380 this.isDirty = true;
381 //// ARGUMENTS //////// 381 }
382 382
383 public void setArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex, String argumentName) { 383 public void setArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex, String argumentName) {
384 assert (argumentName != null); 384 assert (argumentName != null);
385 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)); 385 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature));
386 if (methodMapping == null) { 386 if (methodMapping == null) {
387 methodMapping = createMethodMapping(obfMethodName, obfMethodSignature); 387 methodMapping = createMethodMapping(obfMethodName, obfMethodSignature);
388 } 388 }
389 methodMapping.setArgumentName(argumentIndex, argumentName); 389 methodMapping.setArgumentName(argumentIndex, argumentName);
390 this.isDirty = true; 390 this.isDirty = true;
391 } 391 }
392 392
393 public void removeArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex) { 393 public void removeArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex) {
394 methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)).removeArgumentName(argumentIndex); 394 methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)).removeArgumentName(argumentIndex);
395 this.isDirty = true; 395 this.isDirty = true;
396 } 396 }
397 397
398 private MethodMapping createMethodMapping(String obfName, Signature obfSignature) { 398 private MethodMapping createMethodMapping(String obfName, Signature obfSignature) {
399 MethodMapping methodMapping = new MethodMapping(obfName, obfSignature); 399 MethodMapping methodMapping = new MethodMapping(obfName, obfSignature);
400 boolean wasAdded = methodsByObf.put(getMethodKey(obfName, obfSignature), methodMapping) == null; 400 boolean wasAdded = methodsByObf.put(getMethodKey(obfName, obfSignature), methodMapping) == null;
401 assert (wasAdded); 401 assert (wasAdded);
402 this.isDirty = true; 402 this.isDirty = true;
403 return methodMapping; 403 return methodMapping;
404 } 404 }
405 405
406 @Override 406 @Override
407 public String toString() { 407 public String toString() {
408 StringBuilder buf = new StringBuilder(); 408 StringBuilder buf = new StringBuilder();
409 buf.append(obfFullName); 409 buf.append(obfFullName);
410 buf.append(" <-> "); 410 buf.append(" <-> ");
411 buf.append(deobfName); 411 buf.append(deobfName);
412 buf.append("\n"); 412 buf.append("\n");
413 buf.append("Fields:\n"); 413 buf.append("Fields:\n");
414 for (FieldMapping fieldMapping : fields()) { 414 for (FieldMapping fieldMapping : fields()) {
415 buf.append("\t"); 415 buf.append("\t");
416 buf.append(fieldMapping.getObfName()); 416 buf.append(fieldMapping.getObfName());
417 buf.append(" <-> "); 417 buf.append(" <-> ");
418 buf.append(fieldMapping.getDeobfName()); 418 buf.append(fieldMapping.getDeobfName());
419 buf.append("\n"); 419 buf.append("\n");
420 } 420 }
421 buf.append("Methods:\n"); 421 buf.append("Methods:\n");
422 for (MethodMapping methodMapping : methodsByObf.values()) { 422 for (MethodMapping methodMapping : methodsByObf.values()) {
423 buf.append(methodMapping.toString()); 423 buf.append(methodMapping);
424 buf.append("\n"); 424 buf.append("\n");
425 } 425 }
426 buf.append("Inner Classes:\n"); 426 buf.append("Inner Classes:\n");
427 for (ClassMapping classMapping : innerClassesByObfSimple.values()) { 427 for (ClassMapping classMapping : innerClassesByObfSimple.values()) {
428 buf.append("\t"); 428 buf.append("\t");
429 buf.append(classMapping.getObfSimpleName()); 429 buf.append(classMapping.getObfSimpleName());
430 buf.append(" <-> "); 430 buf.append(" <-> ");
431 buf.append(classMapping.getDeobfName()); 431 buf.append(classMapping.getDeobfName());
432 buf.append("\n"); 432 buf.append("\n");
433 } 433 }
434 return buf.toString(); 434 return buf.toString();
435 } 435 }
436 436
437 @Override 437 @Override
438 public int compareTo(ClassMapping other) { 438 public int compareTo(ClassMapping other) {
439 // sort by a, b, c, ... aa, ab, etc 439 // sort by a, b, c, ... aa, ab, etc
440 if (obfFullName.length() != other.obfFullName.length()) { 440 if (obfFullName.length() != other.obfFullName.length()) {
441 return obfFullName.length() - other.obfFullName.length(); 441 return obfFullName.length() - other.obfFullName.length();
442 } 442 }
443 return obfFullName.compareTo(other.obfFullName); 443 return obfFullName.compareTo(other.obfFullName);
444 } 444 }
445 445
446 public boolean renameObfClass(String oldObfClassName, String newObfClassName) { 446 public boolean renameObfClass(String oldObfClassName, String newObfClassName) {
447 447
448 // rename inner classes 448 // rename inner classes
449 for (ClassMapping innerClassMapping : new ArrayList<>(innerClassesByObfSimple.values())) { 449 for (ClassMapping innerClassMapping : new ArrayList<>(innerClassesByObfSimple.values())) {
450 if (innerClassMapping.renameObfClass(oldObfClassName, newObfClassName)) { 450 if (innerClassMapping.renameObfClass(oldObfClassName, newObfClassName)) {
451 boolean wasRemoved = innerClassesByObfSimple.remove(oldObfClassName) != null; 451 boolean wasRemoved = innerClassesByObfSimple.remove(oldObfClassName) != null;
452 assert (wasRemoved); 452 assert (wasRemoved);
453 boolean wasAdded = innerClassesByObfSimple.put(newObfClassName, innerClassMapping) == null; 453 boolean wasAdded = innerClassesByObfSimple.put(newObfClassName, innerClassMapping) == null;
454 assert (wasAdded); 454 assert (wasAdded);
455 } 455 }
456 } 456 }
457 457
458 // rename field types 458 // rename field types
459 for (FieldMapping fieldMapping : new ArrayList<>(fieldsByObf.values())) { 459 for (FieldMapping fieldMapping : new ArrayList<>(fieldsByObf.values())) {
460 String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); 460 String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType());
461 if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) { 461 if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) {
462 boolean wasRemoved = fieldsByObf.remove(oldFieldKey) != null; 462 boolean wasRemoved = fieldsByObf.remove(oldFieldKey) != null;
463 assert (wasRemoved); 463 assert (wasRemoved);
464 boolean wasAdded = fieldsByObf 464 boolean wasAdded = fieldsByObf
465 .put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()), fieldMapping) == null; 465 .put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()), fieldMapping) == null;
466 assert (wasAdded); 466 assert (wasAdded);
467 } 467 }
468 } 468 }
469 469
470 // rename method signatures 470 // rename method signatures
471 for (MethodMapping methodMapping : new ArrayList<>(methodsByObf.values())) { 471 for (MethodMapping methodMapping : new ArrayList<>(methodsByObf.values())) {
472 String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); 472 String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature());
473 if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) { 473 if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) {
474 boolean wasRemoved = methodsByObf.remove(oldMethodKey) != null; 474 boolean wasRemoved = methodsByObf.remove(oldMethodKey) != null;
475 assert (wasRemoved); 475 assert (wasRemoved);
476 boolean wasAdded = methodsByObf 476 boolean wasAdded = methodsByObf
477 .put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()), methodMapping) == null; 477 .put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()), methodMapping) == null;
478 assert (wasAdded); 478 assert (wasAdded);
479 } 479 }
480 } 480 }
481 481
482 if (obfFullName.equals(oldObfClassName)) { 482 if (obfFullName.equals(oldObfClassName)) {
483 // rename this class 483 // rename this class
484 obfFullName = newObfClassName; 484 obfFullName = newObfClassName;
485 return true; 485 return true;
486 } 486 }
487 this.isDirty = true; 487 this.isDirty = true;
488 return false; 488 return false;
489 } 489 }
490 490
491 public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { 491 public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) {
492 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfBehaviorEntry.getName(), obfBehaviorEntry.getSignature())); 492 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfBehaviorEntry.getName(), obfBehaviorEntry.getSignature()));
493 return methodMapping != null && methodMapping.containsArgument(name); 493 return methodMapping != null && methodMapping.containsArgument(name);
494 } 494 }
495 495
496 public static boolean isSimpleClassName(String name) { 496 public ClassEntry getObfEntry() {
497 return name.indexOf('/') < 0 && name.indexOf('$') < 0; 497 return new ClassEntry(obfFullName);
498 } 498 }
499 499
500 public ClassEntry getObfEntry() { 500 public boolean isDirty() {
501 return new ClassEntry(obfFullName); 501 return isDirty;
502 } 502 }
503 503
504 public boolean isDirty() 504 public void resetDirty() {
505 { 505 this.isDirty = false;
506 return isDirty; 506 }
507 } 507
508 508 public Mappings.EntryModifier getModifier() {
509 public void resetDirty() 509 return modifier;
510 { 510 }
511 this.isDirty = false; 511
512 } 512 public void setModifier(Mappings.EntryModifier modifier) {
513 513 if (this.modifier != modifier)
514 public void setModifier(Mappings.EntryModifier modifier) 514 this.isDirty = true;
515 { 515 this.modifier = modifier;
516 if (this.modifier != modifier) 516 }
517 this.isDirty = true; 517
518 this.modifier = modifier; 518 public void setFieldModifier(String obfName, Type obfType, Mappings.EntryModifier modifier) {
519 } 519 FieldMapping fieldMapping = fieldsByObf.computeIfAbsent(getFieldKey(obfName, obfType),
520 520 k -> new FieldMapping(obfName, obfType, null, Mappings.EntryModifier.UNCHANGED));
521 public Mappings.EntryModifier getModifier() 521
522 { 522 if (fieldMapping.getModifier() != modifier) {
523 return modifier; 523 fieldMapping.setModifier(modifier);
524 } 524 this.isDirty = true;
525 525 }
526 public void setFieldModifier(String obfName, Type obfType, Mappings.EntryModifier modifier) { 526 }
527 FieldMapping fieldMapping = fieldsByObf.computeIfAbsent(getFieldKey(obfName, obfType), 527
528 k -> new FieldMapping(obfName, obfType, null, Mappings.EntryModifier.UNCHANGED)); 528 public void setMethodModifier(String obfName, Signature sig, Mappings.EntryModifier modifier) {
529 529 MethodMapping methodMapping = methodsByObf.computeIfAbsent(getMethodKey(obfName, sig),
530 if (fieldMapping.getModifier() != modifier) 530 k -> new MethodMapping(obfName, sig, null, Mappings.EntryModifier.UNCHANGED));
531 { 531
532 fieldMapping.setModifier(modifier); 532 if (methodMapping.getModifier() != modifier) {
533 this.isDirty = true; 533 methodMapping.setModifier(modifier);
534 } 534 this.isDirty = true;
535 } 535 }
536 536 }
537 public void setMethodModifier(String obfName, Signature sig, Mappings.EntryModifier modifier) {
538 MethodMapping methodMapping = methodsByObf.computeIfAbsent(getMethodKey(obfName, sig),
539 k -> new MethodMapping(obfName, sig, null, Mappings.EntryModifier.UNCHANGED));
540
541 if (methodMapping.getModifier() != modifier)
542 {
543 methodMapping.setModifier(modifier);
544 this.isDirty = true;
545 }
546 }
547} 537}
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java b/src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java
index dc833bb8..801c4104 100644
--- a/src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java
+++ b/src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java
@@ -8,8 +8,9 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13public interface ClassNameReplacer { 14public interface ClassNameReplacer {
14 String replace(String className); 15 String replace(String className);
15} 16}
diff --git a/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java b/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java
index 4c798204..20e51138 100644
--- a/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java
@@ -8,97 +8,98 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13import cuchaz.enigma.utils.Utils; 14import cuchaz.enigma.utils.Utils;
14 15
15public class ConstructorEntry implements BehaviorEntry { 16public class ConstructorEntry implements BehaviorEntry {
16 17
17 private ClassEntry classEntry; 18 private ClassEntry classEntry;
18 private Signature signature; 19 private Signature signature;
19 20
20 public ConstructorEntry(ClassEntry classEntry) { 21 public ConstructorEntry(ClassEntry classEntry) {
21 this(classEntry, null); 22 this(classEntry, null);
22 } 23 }
23 24
24 public ConstructorEntry(ClassEntry classEntry, Signature signature) { 25 public ConstructorEntry(ClassEntry classEntry, Signature signature) {
25 if (classEntry == null) { 26 if (classEntry == null) {
26 throw new IllegalArgumentException("Class cannot be null!"); 27 throw new IllegalArgumentException("Class cannot be null!");
27 } 28 }
28 29
29 this.classEntry = classEntry; 30 this.classEntry = classEntry;
30 this.signature = signature; 31 this.signature = signature;
31 } 32 }
32 33
33 public ConstructorEntry(ConstructorEntry other, String newClassName) { 34 public ConstructorEntry(ConstructorEntry other, String newClassName) {
34 this.classEntry = new ClassEntry(newClassName); 35 this.classEntry = new ClassEntry(newClassName);
35 this.signature = other.signature; 36 this.signature = other.signature;
36 } 37 }
37 38
38 @Override 39 @Override
39 public ClassEntry getClassEntry() { 40 public ClassEntry getClassEntry() {
40 return this.classEntry; 41 return this.classEntry;
41 } 42 }
42 43
43 @Override 44 @Override
44 public String getName() { 45 public String getName() {
45 if (isStatic()) { 46 if (isStatic()) {
46 return "<clinit>"; 47 return "<clinit>";
47 } 48 }
48 return "<init>"; 49 return "<init>";
49 } 50 }
50 51
51 public boolean isStatic() { 52 public boolean isStatic() {
52 return this.signature == null; 53 return this.signature == null;
53 } 54 }
54 55
55 @Override 56 @Override
56 public Signature getSignature() { 57 public Signature getSignature() {
57 return this.signature; 58 return this.signature;
58 } 59 }
59 60
60 @Override 61 @Override
61 public String getClassName() { 62 public String getClassName() {
62 return this.classEntry.getName(); 63 return this.classEntry.getName();
63 } 64 }
64 65
65 @Override 66 @Override
66 public ConstructorEntry cloneToNewClass(ClassEntry classEntry) { 67 public ConstructorEntry cloneToNewClass(ClassEntry classEntry) {
67 return new ConstructorEntry(this, classEntry.getName()); 68 return new ConstructorEntry(this, classEntry.getName());
68 } 69 }
69 70
70 @Override 71 @Override
71 public int hashCode() { 72 public int hashCode() {
72 if (isStatic()) { 73 if (isStatic()) {
73 return Utils.combineHashesOrdered(this.classEntry); 74 return Utils.combineHashesOrdered(this.classEntry);
74 } else { 75 } else {
75 return Utils.combineHashesOrdered(this.classEntry, this.signature); 76 return Utils.combineHashesOrdered(this.classEntry, this.signature);
76 } 77 }
77 } 78 }
78 79
79 @Override 80 @Override
80 public boolean equals(Object other) { 81 public boolean equals(Object other) {
81 return other instanceof ConstructorEntry && equals((ConstructorEntry) other); 82 return other instanceof ConstructorEntry && equals((ConstructorEntry) other);
82 } 83 }
83 84
84 public boolean equals(ConstructorEntry other) { 85 public boolean equals(ConstructorEntry other) {
85 if (isStatic() != other.isStatic()) { 86 if (isStatic() != other.isStatic()) {
86 return false; 87 return false;
87 } 88 }
88 89
89 if (isStatic()) { 90 if (isStatic()) {
90 return this.classEntry.equals(other.classEntry); 91 return this.classEntry.equals(other.classEntry);
91 } else { 92 } else {
92 return this.classEntry.equals(other.classEntry) && this.signature.equals(other.signature); 93 return this.classEntry.equals(other.classEntry) && this.signature.equals(other.signature);
93 } 94 }
94 } 95 }
95 96
96 @Override 97 @Override
97 public String toString() { 98 public String toString() {
98 if (isStatic()) { 99 if (isStatic()) {
99 return this.classEntry.getName() + "." + getName(); 100 return this.classEntry.getName() + "." + getName();
100 } else { 101 } else {
101 return this.classEntry.getName() + "." + getName() + this.signature; 102 return this.classEntry.getName() + "." + getName() + this.signature;
102 } 103 }
103 } 104 }
104} 105}
diff --git a/src/main/java/cuchaz/enigma/mapping/Entry.java b/src/main/java/cuchaz/enigma/mapping/Entry.java
index 95747d55..c79510b9 100644
--- a/src/main/java/cuchaz/enigma/mapping/Entry.java
+++ b/src/main/java/cuchaz/enigma/mapping/Entry.java
@@ -8,14 +8,15 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13public interface Entry { 14public interface Entry {
14 String getName(); 15 String getName();
15 16
16 String getClassName(); 17 String getClassName();
17 18
18 ClassEntry getClassEntry(); 19 ClassEntry getClassEntry();
19 20
20 Entry cloneToNewClass(ClassEntry classEntry); 21 Entry cloneToNewClass(ClassEntry classEntry);
21} 22}
diff --git a/src/main/java/cuchaz/enigma/mapping/EntryFactory.java b/src/main/java/cuchaz/enigma/mapping/EntryFactory.java
index ce4b948a..993bb64b 100644
--- a/src/main/java/cuchaz/enigma/mapping/EntryFactory.java
+++ b/src/main/java/cuchaz/enigma/mapping/EntryFactory.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13import cuchaz.enigma.analysis.JarIndex; 14import cuchaz.enigma.analysis.JarIndex;
@@ -20,112 +21,112 @@ import javassist.expr.NewExpr;
20 21
21public class EntryFactory { 22public class EntryFactory {
22 23
23 public static ClassEntry getClassEntry(CtClass c) { 24 public static ClassEntry getClassEntry(CtClass c) {
24 return new ClassEntry(Descriptor.toJvmName(c.getName())); 25 return new ClassEntry(Descriptor.toJvmName(c.getName()));
25 } 26 }
26 27
27 public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) { 28 public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) {
28 ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfFullName()); 29 ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfFullName());
29 return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry)); 30 return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry));
30 } 31 }
31 32
32 private static ClassEntry getObfClassEntry(ClassMapping classMapping) { 33 private static ClassEntry getObfClassEntry(ClassMapping classMapping) {
33 return new ClassEntry(classMapping.getObfFullName()); 34 return new ClassEntry(classMapping.getObfFullName());
34 } 35 }
35 36
36 public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) { 37 public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) {
37 return new ClassEntry(classMapping.getDeobfName()); 38 return new ClassEntry(classMapping.getDeobfName());
38 } 39 }
39 40
40 public static ClassEntry getSuperclassEntry(CtClass c) { 41 public static ClassEntry getSuperclassEntry(CtClass c) {
41 return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass())); 42 return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass()));
42 } 43 }
43 44
44 public static FieldEntry getFieldEntry(CtField field) { 45 public static FieldEntry getFieldEntry(CtField field) {
45 return new FieldEntry(getClassEntry(field.getDeclaringClass()), field.getName(), new Type(field.getFieldInfo().getDescriptor())); 46 return new FieldEntry(getClassEntry(field.getDeclaringClass()), field.getName(), new Type(field.getFieldInfo().getDescriptor()));
46 } 47 }
47 48
48 public static FieldEntry getFieldEntry(FieldAccess call) { 49 public static FieldEntry getFieldEntry(FieldAccess call) {
49 return new FieldEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getFieldName(), new Type(call.getSignature())); 50 return new FieldEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getFieldName(), new Type(call.getSignature()));
50 } 51 }
51 52
52 public static FieldEntry getFieldEntry(String className, String name, String type) { 53 public static FieldEntry getFieldEntry(String className, String name, String type) {
53 return new FieldEntry(new ClassEntry(className), name, new Type(type)); 54 return new FieldEntry(new ClassEntry(className), name, new Type(type));
54 } 55 }
55 56
56 public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) { 57 public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) {
57 return new FieldEntry(getObfClassEntry(classMapping), fieldMapping.getObfName(), fieldMapping.getObfType()); 58 return new FieldEntry(getObfClassEntry(classMapping), fieldMapping.getObfName(), fieldMapping.getObfType());
58 } 59 }
59 60
60 public static MethodEntry getMethodEntry(CtMethod method) { 61 public static MethodEntry getMethodEntry(CtMethod method) {
61 return new MethodEntry(getClassEntry(method.getDeclaringClass()), method.getName(), new Signature(method.getMethodInfo().getDescriptor())); 62 return new MethodEntry(getClassEntry(method.getDeclaringClass()), method.getName(), new Signature(method.getMethodInfo().getDescriptor()));
62 } 63 }
63 64
64 public static MethodEntry getMethodEntry(MethodCall call) { 65 public static MethodEntry getMethodEntry(MethodCall call) {
65 return new MethodEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getMethodName(), new Signature(call.getSignature())); 66 return new MethodEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getMethodName(), new Signature(call.getSignature()));
66 } 67 }
67 68
68 public static ConstructorEntry getConstructorEntry(CtConstructor constructor) { 69 public static ConstructorEntry getConstructorEntry(CtConstructor constructor) {
69 if (constructor.isClassInitializer()) { 70 if (constructor.isClassInitializer()) {
70 return new ConstructorEntry(getClassEntry(constructor.getDeclaringClass())); 71 return new ConstructorEntry(getClassEntry(constructor.getDeclaringClass()));
71 } else { 72 } else {
72 return new ConstructorEntry(getClassEntry(constructor.getDeclaringClass()), new Signature(constructor.getMethodInfo().getDescriptor())); 73 return new ConstructorEntry(getClassEntry(constructor.getDeclaringClass()), new Signature(constructor.getMethodInfo().getDescriptor()));
73 } 74 }
74 } 75 }
75 76
76 public static ConstructorEntry getConstructorEntry(ConstructorCall call) { 77 public static ConstructorEntry getConstructorEntry(ConstructorCall call) {
77 return new ConstructorEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), new Signature(call.getSignature())); 78 return new ConstructorEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), new Signature(call.getSignature()));
78 } 79 }
79 80
80 public static ConstructorEntry getConstructorEntry(NewExpr call) { 81 public static ConstructorEntry getConstructorEntry(NewExpr call) {
81 return new ConstructorEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), new Signature(call.getSignature())); 82 return new ConstructorEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), new Signature(call.getSignature()));
82 } 83 }
83 84
84 public static BehaviorEntry getBehaviorEntry(CtBehavior behavior) { 85 public static BehaviorEntry getBehaviorEntry(CtBehavior behavior) {
85 if (behavior instanceof CtMethod) { 86 if (behavior instanceof CtMethod) {
86 return getMethodEntry((CtMethod) behavior); 87 return getMethodEntry((CtMethod) behavior);
87 } else if (behavior instanceof CtConstructor) { 88 } else if (behavior instanceof CtConstructor) {
88 return getConstructorEntry((CtConstructor) behavior); 89 return getConstructorEntry((CtConstructor) behavior);
89 } 90 }
90 throw new Error("behavior is neither Method nor Constructor!"); 91 throw new Error("behavior is neither Method nor Constructor!");
91 } 92 }
92 93
93 public static BehaviorEntry getBehaviorEntry(String className, String behaviorName, String behaviorSignature) { 94 public static BehaviorEntry getBehaviorEntry(String className, String behaviorName, String behaviorSignature) {
94 return getBehaviorEntry(new ClassEntry(className), behaviorName, new Signature(behaviorSignature)); 95 return getBehaviorEntry(new ClassEntry(className), behaviorName, new Signature(behaviorSignature));
95 } 96 }
96 97
97 public static BehaviorEntry getBehaviorEntry(String className, String behaviorName) { 98 public static BehaviorEntry getBehaviorEntry(String className, String behaviorName) {
98 return getBehaviorEntry(new ClassEntry(className), behaviorName); 99 return getBehaviorEntry(new ClassEntry(className), behaviorName);
99 } 100 }
100 101
101 public static BehaviorEntry getBehaviorEntry(String className) { 102 public static BehaviorEntry getBehaviorEntry(String className) {
102 return new ConstructorEntry(new ClassEntry(className)); 103 return new ConstructorEntry(new ClassEntry(className));
103 } 104 }
104 105
105 public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName, Signature behaviorSignature) { 106 public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName, Signature behaviorSignature) {
106 switch (behaviorName) { 107 switch (behaviorName) {
107 case "<init>": 108 case "<init>":
108 return new ConstructorEntry(classEntry, behaviorSignature); 109 return new ConstructorEntry(classEntry, behaviorSignature);
109 case "<clinit>": 110 case "<clinit>":
110 return new ConstructorEntry(classEntry); 111 return new ConstructorEntry(classEntry);
111 default: 112 default:
112 return new MethodEntry(classEntry, behaviorName, behaviorSignature); 113 return new MethodEntry(classEntry, behaviorName, behaviorSignature);
113 } 114 }
114 } 115 }
115 116
116 public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName) { 117 public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName) {
117 if(behaviorName.equals("<clinit>")) { 118 if (behaviorName.equals("<clinit>")) {
118 return new ConstructorEntry(classEntry); 119 return new ConstructorEntry(classEntry);
119 } else { 120 } else {
120 throw new IllegalArgumentException("Only class initializers don't have signatures"); 121 throw new IllegalArgumentException("Only class initializers don't have signatures");
121 } 122 }
122 } 123 }
123 124
124 public static BehaviorEntry getObfBehaviorEntry(ClassEntry classEntry, MethodMapping methodMapping) { 125 public static BehaviorEntry getObfBehaviorEntry(ClassEntry classEntry, MethodMapping methodMapping) {
125 return getBehaviorEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature()); 126 return getBehaviorEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature());
126 } 127 }
127 128
128 public static BehaviorEntry getObfBehaviorEntry(ClassMapping classMapping, MethodMapping methodMapping) { 129 public static BehaviorEntry getObfBehaviorEntry(ClassMapping classMapping, MethodMapping methodMapping) {
129 return getObfBehaviorEntry(getObfClassEntry(classMapping), methodMapping); 130 return getObfBehaviorEntry(getObfClassEntry(classMapping), methodMapping);
130 } 131 }
131} 132}
diff --git a/src/main/java/cuchaz/enigma/mapping/FieldEntry.java b/src/main/java/cuchaz/enigma/mapping/FieldEntry.java
index 9980e8e6..0f1f5065 100644
--- a/src/main/java/cuchaz/enigma/mapping/FieldEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/FieldEntry.java
@@ -8,79 +8,80 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13import cuchaz.enigma.utils.Utils; 14import cuchaz.enigma.utils.Utils;
14 15
15public class FieldEntry implements Entry { 16public class FieldEntry implements Entry {
16 17
17 private ClassEntry classEntry; 18 private ClassEntry classEntry;
18 private String name; 19 private String name;
19 private Type type; 20 private Type type;
20 21
21 // NOTE: this argument order is important for the MethodReader/MethodWriter 22 // NOTE: this argument order is important for the MethodReader/MethodWriter
22 public FieldEntry(ClassEntry classEntry, String name, Type type) { 23 public FieldEntry(ClassEntry classEntry, String name, Type type) {
23 if (classEntry == null) { 24 if (classEntry == null) {
24 throw new IllegalArgumentException("Class cannot be null!"); 25 throw new IllegalArgumentException("Class cannot be null!");
25 } 26 }
26 if (name == null) { 27 if (name == null) {
27 throw new IllegalArgumentException("Field name cannot be null!"); 28 throw new IllegalArgumentException("Field name cannot be null!");
28 } 29 }
29 if (type == null) { 30 if (type == null) {
30 throw new IllegalArgumentException("Field type cannot be null!"); 31 throw new IllegalArgumentException("Field type cannot be null!");
31 } 32 }
32 33
33 this.classEntry = classEntry; 34 this.classEntry = classEntry;
34 this.name = name; 35 this.name = name;
35 this.type = type; 36 this.type = type;
36 } 37 }
37 38
38 public FieldEntry(FieldEntry other, ClassEntry newClassEntry) { 39 public FieldEntry(FieldEntry other, ClassEntry newClassEntry) {
39 this.classEntry = newClassEntry; 40 this.classEntry = newClassEntry;
40 this.name = other.name; 41 this.name = other.name;
41 this.type = other.type; 42 this.type = other.type;
42 } 43 }
43 44
44 @Override 45 @Override
45 public ClassEntry getClassEntry() { 46 public ClassEntry getClassEntry() {
46 return this.classEntry; 47 return this.classEntry;
47 } 48 }
48 49
49 @Override 50 @Override
50 public String getName() { 51 public String getName() {
51 return this.name; 52 return this.name;
52 } 53 }
53 54
54 @Override 55 @Override
55 public String getClassName() { 56 public String getClassName() {
56 return this.classEntry.getName(); 57 return this.classEntry.getName();
57 } 58 }
58 59
59 public Type getType() { 60 public Type getType() {
60 return this.type; 61 return this.type;
61 } 62 }
62 63
63 @Override 64 @Override
64 public FieldEntry cloneToNewClass(ClassEntry classEntry) { 65 public FieldEntry cloneToNewClass(ClassEntry classEntry) {
65 return new FieldEntry(this, classEntry); 66 return new FieldEntry(this, classEntry);
66 } 67 }
67 68
68 @Override 69 @Override
69 public int hashCode() { 70 public int hashCode() {
70 return Utils.combineHashesOrdered(this.classEntry, this.name, this.type); 71 return Utils.combineHashesOrdered(this.classEntry, this.name, this.type);
71 } 72 }
72 73
73 @Override 74 @Override
74 public boolean equals(Object other) { 75 public boolean equals(Object other) {
75 return other instanceof FieldEntry && equals((FieldEntry) other); 76 return other instanceof FieldEntry && equals((FieldEntry) other);
76 } 77 }
77 78
78 public boolean equals(FieldEntry other) { 79 public boolean equals(FieldEntry other) {
79 return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.type.equals(other.type); 80 return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.type.equals(other.type);
80 } 81 }
81 82
82 @Override 83 @Override
83 public String toString() { 84 public String toString() {
84 return this.classEntry.getName() + "." + this.name + ":" + this.type; 85 return this.classEntry.getName() + "." + this.name + ":" + this.type;
85 } 86 }
86} 87}
diff --git a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
index 22ba307e..cd761b47 100644
--- a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
@@ -8,103 +8,99 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13import cuchaz.enigma.throwables.IllegalNameException; 14import cuchaz.enigma.throwables.IllegalNameException;
14 15
15public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<FieldEntry> { 16public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<FieldEntry> {
16 17
17 private String obfName; 18 private String obfName;
18 private String deobfName; 19 private String deobfName;
19 private Type obfType; 20 private Type obfType;
20 private Mappings.EntryModifier modifier; 21 private Mappings.EntryModifier modifier;
21 22
22 public FieldMapping(String obfName, Type obfType, String deobfName, Mappings.EntryModifier modifier) { 23 public FieldMapping(String obfName, Type obfType, String deobfName, Mappings.EntryModifier modifier) {
23 this.obfName = obfName; 24 this.obfName = obfName;
24 this.deobfName = NameValidator.validateFieldName(deobfName); 25 this.deobfName = NameValidator.validateFieldName(deobfName);
25 this.obfType = obfType; 26 this.obfType = obfType;
26 this.modifier = modifier; 27 this.modifier = modifier;
27 } 28 }
28 29
29 public FieldMapping(FieldMapping other, ClassNameReplacer obfClassNameReplacer) { 30 public FieldMapping(FieldMapping other, ClassNameReplacer obfClassNameReplacer) {
30 this.obfName = other.obfName; 31 this.obfName = other.obfName;
31 this.deobfName = other.deobfName; 32 this.deobfName = other.deobfName;
32 this.modifier = other.modifier; 33 this.modifier = other.modifier;
33 this.obfType = new Type(other.obfType, obfClassNameReplacer); 34 this.obfType = new Type(other.obfType, obfClassNameReplacer);
34 } 35 }
35 36
36 @Override 37 @Override
37 public FieldEntry getObfEntry(ClassEntry classEntry) { 38 public FieldEntry getObfEntry(ClassEntry classEntry) {
38 return new FieldEntry(classEntry, this.obfName, this.obfType); 39 return new FieldEntry(classEntry, this.obfName, this.obfType);
39 } 40 }
40 41
41 @Override 42 @Override
42 public String getObfName() { 43 public String getObfName() {
43 return this.obfName; 44 return this.obfName;
44 } 45 }
45 46
46 public String getDeobfName() { 47 public void setObfName(String name) {
47 return this.deobfName; 48 try {
48 } 49 NameValidator.validateMethodName(name);
49 50 } catch (IllegalNameException ex) {
50 public void setDeobfName(String val) { 51 // Invalid name, damn obfuscation! Map to a deob name with another name to avoid issues
51 this.deobfName = NameValidator.validateFieldName(val); 52 if (this.deobfName == null) {
52 } 53 System.err.println("WARNING: " + name + " is conflicting, auto deobfuscate to " + (name + "_auto_deob"));
53 54 setDeobfName(name + "_auto_deob");
54 public void setObfName(String name) { 55 }
55 try 56 }
56 { 57 this.obfName = name;
57 NameValidator.validateMethodName(name); 58 }
58 } catch (IllegalNameException ex) 59
59 { 60 public String getDeobfName() {
60 // Invalid name, damn obfuscation! Map to a deob name with another name to avoid issues 61 return this.deobfName;
61 if (this.deobfName == null) 62 }
62 { 63
63 System.err.println("WARNING: " + name + " is conflicting, auto deobfuscate to " + (name + "_auto_deob")); 64 public void setDeobfName(String val) {
64 setDeobfName(name + "_auto_deob"); 65 this.deobfName = NameValidator.validateFieldName(val);
65 } 66 }
66 } 67
67 this.obfName = name; 68 public Type getObfType() {
68 } 69 return this.obfType;
69 70 }
70 public Type getObfType() { 71
71 return this.obfType; 72 public void setObfType(Type val) {
72 } 73 this.obfType = val;
73 74 }
74 public void setObfType(Type val) { 75
75 this.obfType = val; 76 public Mappings.EntryModifier getModifier() {
76 } 77 return modifier;
77 78 }
78 public void setModifier(Mappings.EntryModifier modifier) 79
79 { 80 public void setModifier(Mappings.EntryModifier modifier) {
80 this.modifier = modifier; 81 this.modifier = modifier;
81 } 82 }
82 83
83 public Mappings.EntryModifier getModifier() 84 @Override
84 { 85 public int compareTo(FieldMapping other) {
85 return modifier; 86 return (this.obfName + this.obfType).compareTo(other.obfName + other.obfType);
86 } 87 }
87 88
88 @Override 89 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) {
89 public int compareTo(FieldMapping other) { 90 // rename obf classes in the type
90 return (this.obfName + this.obfType).compareTo(other.obfName + other.obfType); 91 Type newType = new Type(this.obfType, className ->
91 } 92 {
92 93 if (className.equals(oldObfClassName)) {
93 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { 94 return newObfClassName;
94 // rename obf classes in the type 95 }
95 Type newType = new Type(this.obfType, className -> 96 return null;
96 { 97 });
97 if (className.equals(oldObfClassName)) { 98
98 return newObfClassName; 99 if (!newType.equals(this.obfType)) {
99 } 100 this.obfType = newType;
100 return null; 101 return true;
101 }); 102 }
102 103 return false;
103 if (!newType.equals(this.obfType)) { 104 }
104 this.obfType = newType;
105 return true;
106 }
107 return false;
108 }
109 105
110} 106}
diff --git a/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java b/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java
index 8bbaaaf6..2bb5e3f7 100644
--- a/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java
@@ -7,98 +7,96 @@ import cuchaz.enigma.utils.Utils;
7 * Created by Thog 7 * Created by Thog
8 * 19/10/2016 8 * 19/10/2016
9 */ 9 */
10public class LocalVariableEntry implements Entry 10public class LocalVariableEntry implements Entry {
11{ 11
12 12 protected final BehaviorEntry behaviorEntry;
13 protected final BehaviorEntry behaviorEntry; 13 protected final String name;
14 protected final String name; 14 protected final Type type;
15 protected final Type type; 15 protected final int index;
16 protected final int index; 16
17 17 public LocalVariableEntry(BehaviorEntry behaviorEntry, int index, String name, Type type) {
18 public LocalVariableEntry(BehaviorEntry behaviorEntry, int index, String name, Type type) { 18 if (behaviorEntry == null) {
19 if (behaviorEntry == null) { 19 throw new IllegalArgumentException("Behavior cannot be null!");
20 throw new IllegalArgumentException("Behavior cannot be null!"); 20 }
21 } 21 if (index < 0) {
22 if (index < 0) { 22 throw new IllegalArgumentException("Index must be non-negative!");
23 throw new IllegalArgumentException("Index must be non-negative!"); 23 }
24 } 24 if (name == null) {
25 if (name == null) { 25 throw new IllegalArgumentException("Variable name cannot be null!");
26 throw new IllegalArgumentException("Variable name cannot be null!"); 26 }
27 } 27 if (type == null) {
28 if (type == null) { 28 throw new IllegalArgumentException("Variable type cannot be null!");
29 throw new IllegalArgumentException("Variable type cannot be null!"); 29 }
30 } 30
31 31 this.behaviorEntry = behaviorEntry;
32 this.behaviorEntry = behaviorEntry; 32 this.name = name;
33 this.name = name; 33 this.type = type;
34 this.type = type; 34 this.index = index;
35 this.index = index; 35 }
36 } 36
37 37 public LocalVariableEntry(LocalVariableEntry other, ClassEntry newClassEntry) {
38 38 this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(newClassEntry);
39 public LocalVariableEntry(LocalVariableEntry other, ClassEntry newClassEntry) { 39 this.name = other.name;
40 this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(newClassEntry); 40 this.type = other.type;
41 this.name = other.name; 41 this.index = other.index;
42 this.type = other.type; 42 }
43 this.index = other.index; 43
44 } 44 public BehaviorEntry getBehaviorEntry() {
45 45 return this.behaviorEntry;
46 public BehaviorEntry getBehaviorEntry() { 46 }
47 return this.behaviorEntry; 47
48 } 48 public Type getType() {
49 49 return type;
50 public Type getType() { 50 }
51 return type; 51
52 } 52 public int getIndex() {
53 53 return index;
54 public int getIndex() { 54 }
55 return index; 55
56 } 56 @Override
57 57 public String getName() {
58 @Override 58 return this.name;
59 public String getName() { 59 }
60 return this.name; 60
61 } 61 @Override
62 62 public ClassEntry getClassEntry() {
63 @Override 63 return this.behaviorEntry.getClassEntry();
64 public ClassEntry getClassEntry() { 64 }
65 return this.behaviorEntry.getClassEntry(); 65
66 } 66 @Override
67 67 public String getClassName() {
68 @Override 68 return this.behaviorEntry.getClassName();
69 public String getClassName() { 69 }
70 return this.behaviorEntry.getClassName(); 70
71 } 71 @Override
72 72 public LocalVariableEntry cloneToNewClass(ClassEntry classEntry) {
73 @Override 73 return new LocalVariableEntry(this, classEntry);
74 public LocalVariableEntry cloneToNewClass(ClassEntry classEntry) { 74 }
75 return new LocalVariableEntry(this, classEntry); 75
76 } 76 public String getMethodName() {
77 77 return this.behaviorEntry.getName();
78 public String getMethodName() { 78 }
79 return this.behaviorEntry.getName(); 79
80 } 80 public Signature getMethodSignature() {
81 81 return this.behaviorEntry.getSignature();
82 public Signature getMethodSignature() { 82 }
83 return this.behaviorEntry.getSignature(); 83
84 } 84 @Override
85 85 public int hashCode() {
86 @Override 86 return Utils.combineHashesOrdered(this.behaviorEntry, this.type.hashCode(), this.name.hashCode(), Integer.hashCode(this.index));
87 public int hashCode() { 87 }
88 return Utils.combineHashesOrdered(this.behaviorEntry, this.type.hashCode(), this.name.hashCode(), Integer.hashCode(this.index)); 88
89 } 89 @Override
90 90 public boolean equals(Object other) {
91 @Override 91 return other instanceof LocalVariableEntry && equals((LocalVariableEntry) other);
92 public boolean equals(Object other) { 92 }
93 return other instanceof LocalVariableEntry && equals((LocalVariableEntry) other); 93
94 } 94 public boolean equals(LocalVariableEntry other) {
95 95 return this.behaviorEntry.equals(other.behaviorEntry) && this.type.equals(other.type) && this.name.equals(other.name) && this.index == other.index;
96 public boolean equals(LocalVariableEntry other) { 96 }
97 return this.behaviorEntry.equals(other.behaviorEntry) && this.type.equals(other.type) && this.name.equals(other.name) && this.index == other.index; 97
98 } 98 @Override
99 99 public String toString() {
100 @Override 100 return this.behaviorEntry + "(" + this.index + ":" + this.name + ":" + this.type + ")";
101 public String toString() { 101 }
102 return this.behaviorEntry.toString() + "(" + this.index + ":" + this.name + ":" + this.type + ")";
103 }
104} 102}
diff --git a/src/main/java/cuchaz/enigma/mapping/Mappings.java b/src/main/java/cuchaz/enigma/mapping/Mappings.java
index d493dcfa..33dd3c54 100644
--- a/src/main/java/cuchaz/enigma/mapping/Mappings.java
+++ b/src/main/java/cuchaz/enigma/mapping/Mappings.java
@@ -8,246 +8,237 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
14import com.google.common.collect.Maps; 15import com.google.common.collect.Maps;
16import com.google.common.collect.Sets;
17import cuchaz.enigma.analysis.TranslationIndex;
18import cuchaz.enigma.throwables.MappingConflict;
15 19
16import java.io.File; 20import java.io.File;
17import java.io.IOException; 21import java.io.IOException;
18import java.util.*; 22import java.util.*;
19 23
20import com.google.common.collect.Sets;
21import cuchaz.enigma.analysis.TranslationIndex;
22import cuchaz.enigma.throwables.MappingConflict;
23
24public class Mappings { 24public class Mappings {
25 25
26 protected Map<String, ClassMapping> classesByObf; 26 private final FormatType originMapping;
27 protected Map<String, ClassMapping> classesByDeobf; 27 protected Map<String, ClassMapping> classesByObf;
28 private final FormatType originMapping; 28 protected Map<String, ClassMapping> classesByDeobf;
29 private Mappings previousState; 29 private Mappings previousState;
30 30
31 public Mappings() 31 public Mappings() {
32 { 32 this(FormatType.ENIGMA_DIRECTORY);
33 this(FormatType.ENIGMA_DIRECTORY); 33 }
34 } 34
35 35 public Mappings(FormatType originMapping) {
36 public Mappings(FormatType originMapping) { 36 this.originMapping = originMapping;
37 this.originMapping = originMapping; 37 this.classesByObf = Maps.newHashMap();
38 this.classesByObf = Maps.newHashMap(); 38 this.classesByDeobf = Maps.newHashMap();
39 this.classesByDeobf = Maps.newHashMap(); 39 this.previousState = null;
40 this.previousState = null; 40 }
41 } 41
42 42 public Collection<ClassMapping> classes() {
43 public Collection<ClassMapping> classes() { 43 assert (this.classesByObf.size() >= this.classesByDeobf.size());
44 assert (this.classesByObf.size() >= this.classesByDeobf.size()); 44 return this.classesByObf.values();
45 return this.classesByObf.values(); 45 }
46 } 46
47 47 public void addClassMapping(ClassMapping classMapping) throws MappingConflict {
48 public void addClassMapping(ClassMapping classMapping) throws MappingConflict { 48 if (this.classesByObf.containsKey(classMapping.getObfFullName())) {
49 if (this.classesByObf.containsKey(classMapping.getObfFullName())) { 49 throw new MappingConflict("class", classMapping.getObfFullName(), this.classesByObf.get(classMapping.getObfFullName()).getObfFullName());
50 throw new MappingConflict("class", classMapping.getObfFullName(), this.classesByObf.get(classMapping.getObfFullName()).getObfFullName()); 50 }
51 } 51 this.classesByObf.put(classMapping.getObfFullName(), classMapping);
52 this.classesByObf.put(classMapping.getObfFullName(), classMapping); 52
53 53 if (classMapping.getDeobfName() != null) {
54 if (classMapping.getDeobfName() != null) { 54 if (this.classesByDeobf.containsKey(classMapping.getDeobfName())) {
55 if (this.classesByDeobf.containsKey(classMapping.getDeobfName())) { 55 throw new MappingConflict("class", classMapping.getDeobfName(), this.classesByDeobf.get(classMapping.getDeobfName()).getDeobfName());
56 throw new MappingConflict("class", classMapping.getDeobfName(), this.classesByDeobf.get(classMapping.getDeobfName()).getDeobfName()); 56 }
57 } 57 this.classesByDeobf.put(classMapping.getDeobfName(), classMapping);
58 this.classesByDeobf.put(classMapping.getDeobfName(), classMapping); 58 }
59 } 59 }
60 } 60
61 61 public void removeClassMapping(ClassMapping classMapping) {
62 public void removeClassMapping(ClassMapping classMapping) { 62 boolean obfWasRemoved = this.classesByObf.remove(classMapping.getObfFullName()) != null;
63 boolean obfWasRemoved = this.classesByObf.remove(classMapping.getObfFullName()) != null; 63 assert (obfWasRemoved);
64 assert (obfWasRemoved); 64 if (classMapping.getDeobfName() != null) {
65 if (classMapping.getDeobfName() != null) { 65 boolean deobfWasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null;
66 boolean deobfWasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null; 66 assert (deobfWasRemoved);
67 assert (deobfWasRemoved); 67 }
68 } 68 }
69 } 69
70 70 public ClassMapping getClassByObf(ClassEntry entry) {
71 71 return getClassByObf(entry.getName());
72 public ClassMapping getClassByObf(ClassEntry entry) { 72 }
73 return getClassByObf(entry.getName()); 73
74 } 74 public ClassMapping getClassByObf(String obfName) {
75 75 return this.classesByObf.get(obfName);
76 public ClassMapping getClassByObf(String obfName) { 76 }
77 return this.classesByObf.get(obfName); 77
78 } 78 public ClassMapping getClassByDeobf(ClassEntry entry) {
79 79 return getClassByDeobf(entry.getName());
80 public ClassMapping getClassByDeobf(ClassEntry entry) { 80 }
81 return getClassByDeobf(entry.getName()); 81
82 } 82 public ClassMapping getClassByDeobf(String deobfName) {
83 83 return this.classesByDeobf.get(deobfName);
84 public ClassMapping getClassByDeobf(String deobfName) { 84 }
85 return this.classesByDeobf.get(deobfName); 85
86 } 86 public void setClassDeobfName(ClassMapping classMapping, String deobfName) {
87 87 if (classMapping.getDeobfName() != null) {
88 public void setClassDeobfName(ClassMapping classMapping, String deobfName) { 88 boolean wasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null;
89 if (classMapping.getDeobfName() != null) { 89 assert (wasRemoved);
90 boolean wasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null; 90 }
91 assert (wasRemoved); 91 classMapping.setDeobfName(deobfName);
92 } 92 if (deobfName != null) {
93 classMapping.setDeobfName(deobfName); 93 boolean wasAdded = this.classesByDeobf.put(deobfName, classMapping) == null;
94 if (deobfName != null) { 94 assert (wasAdded);
95 boolean wasAdded = this.classesByDeobf.put(deobfName, classMapping) == null; 95 }
96 assert (wasAdded); 96 }
97 } 97
98 } 98 public Translator getTranslator(TranslationDirection direction, TranslationIndex index) {
99 99 switch (direction) {
100 public Translator getTranslator(TranslationDirection direction, TranslationIndex index) { 100 case Deobfuscating:
101 switch (direction) { 101
102 case Deobfuscating: 102 return new Translator(direction, this.classesByObf, index);
103 103
104 return new Translator(direction, this.classesByObf, index); 104 case Obfuscating:
105 105
106 case Obfuscating: 106 // fill in the missing deobf class entries with obf entries
107 107 Map<String, ClassMapping> classes = Maps.newHashMap();
108 // fill in the missing deobf class entries with obf entries 108 for (ClassMapping classMapping : classes()) {
109 Map<String, ClassMapping> classes = Maps.newHashMap(); 109 if (classMapping.getDeobfName() != null) {
110 for (ClassMapping classMapping : classes()) { 110 classes.put(classMapping.getDeobfName(), classMapping);
111 if (classMapping.getDeobfName() != null) { 111 } else {
112 classes.put(classMapping.getDeobfName(), classMapping); 112 classes.put(classMapping.getObfFullName(), classMapping);
113 } else { 113 }
114 classes.put(classMapping.getObfFullName(), classMapping); 114 }
115 } 115
116 } 116 // translate the translation index
117 117 // NOTE: this isn't actually recursive
118 // translate the translation index 118 TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.Deobfuscating, index));
119 // NOTE: this isn't actually recursive 119
120 TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.Deobfuscating, index)); 120 return new Translator(direction, classes, deobfIndex);
121 121
122 return new Translator(direction, classes, deobfIndex); 122 default:
123 123 throw new Error("Invalid translation direction!");
124 default: 124 }
125 throw new Error("Invalid translation direction!"); 125 }
126 } 126
127 } 127 @Override
128 128 public String toString() {
129 @Override 129 StringBuilder buf = new StringBuilder();
130 public String toString() { 130 for (ClassMapping classMapping : this.classesByObf.values()) {
131 StringBuilder buf = new StringBuilder(); 131 buf.append(classMapping);
132 for (ClassMapping classMapping : this.classesByObf.values()) { 132 buf.append("\n");
133 buf.append(classMapping.toString()); 133 }
134 buf.append("\n"); 134 return buf.toString();
135 } 135 }
136 return buf.toString(); 136
137 } 137 public void renameObfClass(String oldObfName, String newObfName) {
138 138 new ArrayList<>(classes()).stream().filter(classMapping -> classMapping.renameObfClass(oldObfName, newObfName)).forEach(classMapping -> {
139 public void renameObfClass(String oldObfName, String newObfName) { 139 boolean wasRemoved = this.classesByObf.remove(oldObfName) != null;
140 new ArrayList<>(classes()).stream().filter(classMapping -> classMapping.renameObfClass(oldObfName, newObfName)).forEach(classMapping -> { 140 assert (wasRemoved);
141 boolean wasRemoved = this.classesByObf.remove(oldObfName) != null; 141 boolean wasAdded = this.classesByObf.put(newObfName, classMapping) == null;
142 assert (wasRemoved); 142 assert (wasAdded);
143 boolean wasAdded = this.classesByObf.put(newObfName, classMapping) == null; 143 });
144 assert (wasAdded); 144 }
145 }); 145
146 } 146 public Set<String> getAllObfClassNames() {
147 147 final Set<String> classNames = Sets.newHashSet();
148 public Set<String> getAllObfClassNames() { 148 for (ClassMapping classMapping : classes()) {
149 final Set<String> classNames = Sets.newHashSet(); 149
150 for (ClassMapping classMapping : classes()) { 150 // add the class name
151 151 classNames.add(classMapping.getObfFullName());
152 // add the class name 152
153 classNames.add(classMapping.getObfFullName()); 153 // add classes from method signatures
154 154 for (MethodMapping methodMapping : classMapping.methods()) {
155 // add classes from method signatures 155 for (Type type : methodMapping.getObfSignature().types()) {
156 for (MethodMapping methodMapping : classMapping.methods()) { 156 if (type.hasClass()) {
157 for (Type type : methodMapping.getObfSignature().types()) { 157 classNames.add(type.getClassEntry().getClassName());
158 if (type.hasClass()) { 158 }
159 classNames.add(type.getClassEntry().getClassName()); 159 }
160 } 160 }
161 } 161 }
162 } 162 return classNames;
163 } 163 }
164 return classNames; 164
165 } 165 public boolean containsDeobfClass(String deobfName) {
166 166 return this.classesByDeobf.containsKey(deobfName);
167 public boolean containsDeobfClass(String deobfName) { 167 }
168 return this.classesByDeobf.containsKey(deobfName); 168
169 } 169 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, Type obfType) {
170 170 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
171 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, Type obfType) { 171 return classMapping != null && classMapping.containsDeobfField(deobfName, obfType);
172 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); 172 }
173 return classMapping != null && classMapping.containsDeobfField(deobfName, obfType); 173
174 } 174 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) {
175 175 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
176 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) { 176 if (classMapping != null)
177 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); 177 for (FieldMapping fieldMapping : classMapping.fields())
178 if (classMapping != null) 178 if (deobfName.equals(fieldMapping.getDeobfName()) || deobfName.equals(fieldMapping.getObfName()))
179 for (FieldMapping fieldMapping : classMapping.fields()) 179 return true;
180 if (deobfName.equals(fieldMapping.getDeobfName()) || deobfName.equals(fieldMapping.getObfName())) 180
181 return true; 181 return false;
182 182 }
183 return false; 183
184 } 184 public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, Signature obfSignature) {
185 185 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
186 public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, Signature obfSignature) { 186 return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfSignature);
187 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); 187 }
188 return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfSignature); 188
189 } 189 public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) {
190 190 ClassMapping classMapping = this.classesByObf.get(obfBehaviorEntry.getClassName());
191 public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { 191 return classMapping != null && classMapping.containsArgument(obfBehaviorEntry, name);
192 ClassMapping classMapping = this.classesByObf.get(obfBehaviorEntry.getClassName()); 192 }
193 return classMapping != null && classMapping.containsArgument(obfBehaviorEntry, name); 193
194 } 194 public List<ClassMapping> getClassMappingChain(ClassEntry obfClass) {
195 195 List<ClassMapping> mappingChain = Lists.newArrayList();
196 public List<ClassMapping> getClassMappingChain(ClassEntry obfClass) { 196 ClassMapping classMapping = null;
197 List<ClassMapping> mappingChain = Lists.newArrayList(); 197 for (ClassEntry obfClassEntry : obfClass.getClassChain()) {
198 ClassMapping classMapping = null; 198 if (mappingChain.isEmpty()) {
199 for (ClassEntry obfClassEntry : obfClass.getClassChain()) { 199 classMapping = this.classesByObf.get(obfClassEntry.getName());
200 if (mappingChain.isEmpty()) { 200 } else if (classMapping != null) {
201 classMapping = this.classesByObf.get(obfClassEntry.getName()); 201 classMapping = classMapping.getInnerClassByObfSimple(obfClassEntry.getInnermostClassName());
202 } else if (classMapping != null) { 202 }
203 classMapping = classMapping.getInnerClassByObfSimple(obfClassEntry.getInnermostClassName()); 203 mappingChain.add(classMapping);
204 } 204 }
205 mappingChain.add(classMapping); 205 return mappingChain;
206 } 206 }
207 return mappingChain; 207
208 } 208 public FormatType getOriginMappingFormat() {
209 209 return originMapping;
210 public FormatType getOriginMappingFormat() 210 }
211 { 211
212 return originMapping; 212 public void savePreviousState() {
213 } 213 this.previousState = new Mappings(this.originMapping);
214 214 this.previousState.classesByDeobf = Maps.newHashMap(this.classesByDeobf);
215 public void savePreviousState() 215 this.previousState.classesByObf = Maps.newHashMap(this.classesByObf);
216 { 216 classesByDeobf.values().forEach(ClassMapping::resetDirty);
217 this.previousState = new Mappings(this.originMapping); 217 classesByObf.values().forEach(ClassMapping::resetDirty);
218 this.previousState.classesByDeobf = Maps.newHashMap(this.classesByDeobf); 218 }
219 this.previousState.classesByObf = Maps.newHashMap(this.classesByObf); 219
220 classesByDeobf.values().forEach(ClassMapping::resetDirty); 220 public void saveEnigmaMappings(File file, boolean isDirectoryFormat) throws IOException {
221 classesByObf.values().forEach(ClassMapping::resetDirty); 221 new MappingsEnigmaWriter().write(file, this, isDirectoryFormat);
222 } 222 this.savePreviousState();
223 223 }
224 public void saveEnigmaMappings(File file, boolean isDirectoryFormat) throws IOException 224
225 { 225 public void saveSRGMappings(File file) throws IOException {
226 new MappingsEnigmaWriter().write(file, this, isDirectoryFormat); 226 new MappingsSRGWriter().write(file, this);
227 this.savePreviousState(); 227 }
228 } 228
229 229 public Mappings getPreviousState() {
230 public void saveSRGMappings(File file) throws IOException 230 return previousState;
231 { 231 }
232 new MappingsSRGWriter().write(file, this); 232
233 } 233 public enum FormatType {
234 234 ENIGMA_FILE, ENIGMA_DIRECTORY, SRG_FILE
235 public Mappings getPreviousState() { 235 }
236 return previousState; 236
237 } 237 public enum EntryModifier {
238 238 UNCHANGED, PUBLIC, PROTECTED, PRIVATE;
239 public enum FormatType 239
240 { 240 public String getFormattedName() {
241 ENIGMA_FILE, ENIGMA_DIRECTORY, SRG_FILE 241 return " ACC:" + super.toString();
242 } 242 }
243 243 }
244 public enum EntryModifier
245 {
246 UNCHANGED, PUBLIC, PROTECTED, PRIVATE;
247
248 public String getFormattedName()
249 {
250 return " ACC:" + super.toString();
251 }
252 }
253} 244}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java b/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
index 6cf279d1..172641bd 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
@@ -8,91 +8,90 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
14import com.google.common.collect.Maps; 15import com.google.common.collect.Maps;
15
16import java.util.Map;
17
18import cuchaz.enigma.analysis.JarIndex; 16import cuchaz.enigma.analysis.JarIndex;
19 17
18import java.util.Map;
20 19
21public class MappingsChecker { 20public class MappingsChecker {
22 21
23 private JarIndex index; 22 private JarIndex index;
24 private Map<ClassEntry, ClassMapping> droppedClassMappings; 23 private Map<ClassEntry, ClassMapping> droppedClassMappings;
25 private Map<ClassEntry, ClassMapping> droppedInnerClassMappings; 24 private Map<ClassEntry, ClassMapping> droppedInnerClassMappings;
26 private Map<FieldEntry, FieldMapping> droppedFieldMappings; 25 private Map<FieldEntry, FieldMapping> droppedFieldMappings;
27 private Map<BehaviorEntry, MethodMapping> droppedMethodMappings; 26 private Map<BehaviorEntry, MethodMapping> droppedMethodMappings;
28 27
29 public MappingsChecker(JarIndex index) { 28 public MappingsChecker(JarIndex index) {
30 this.index = index; 29 this.index = index;
31 this.droppedClassMappings = Maps.newHashMap(); 30 this.droppedClassMappings = Maps.newHashMap();
32 this.droppedInnerClassMappings = Maps.newHashMap(); 31 this.droppedInnerClassMappings = Maps.newHashMap();
33 this.droppedFieldMappings = Maps.newHashMap(); 32 this.droppedFieldMappings = Maps.newHashMap();
34 this.droppedMethodMappings = Maps.newHashMap(); 33 this.droppedMethodMappings = Maps.newHashMap();
35 } 34 }
36 35
37 public Map<ClassEntry, ClassMapping> getDroppedClassMappings() { 36 public Map<ClassEntry, ClassMapping> getDroppedClassMappings() {
38 return this.droppedClassMappings; 37 return this.droppedClassMappings;
39 } 38 }
40 39
41 public Map<ClassEntry, ClassMapping> getDroppedInnerClassMappings() { 40 public Map<ClassEntry, ClassMapping> getDroppedInnerClassMappings() {
42 return this.droppedInnerClassMappings; 41 return this.droppedInnerClassMappings;
43 } 42 }
44 43
45 public Map<FieldEntry, FieldMapping> getDroppedFieldMappings() { 44 public Map<FieldEntry, FieldMapping> getDroppedFieldMappings() {
46 return this.droppedFieldMappings; 45 return this.droppedFieldMappings;
47 } 46 }
48 47
49 public Map<BehaviorEntry, MethodMapping> getDroppedMethodMappings() { 48 public Map<BehaviorEntry, MethodMapping> getDroppedMethodMappings() {
50 return this.droppedMethodMappings; 49 return this.droppedMethodMappings;
51 } 50 }
52 51
53 public void dropBrokenMappings(Mappings mappings) { 52 public void dropBrokenMappings(Mappings mappings) {
54 for (ClassMapping classMapping : Lists.newArrayList(mappings.classes())) { 53 for (ClassMapping classMapping : Lists.newArrayList(mappings.classes())) {
55 if (!checkClassMapping(classMapping)) { 54 if (!checkClassMapping(classMapping)) {
56 mappings.removeClassMapping(classMapping); 55 mappings.removeClassMapping(classMapping);
57 this.droppedClassMappings.put(EntryFactory.getObfClassEntry(this.index, classMapping), classMapping); 56 this.droppedClassMappings.put(EntryFactory.getObfClassEntry(this.index, classMapping), classMapping);
58 } 57 }
59 } 58 }
60 } 59 }
61 60
62 private boolean checkClassMapping(ClassMapping classMapping) { 61 private boolean checkClassMapping(ClassMapping classMapping) {
63 62
64 // check the class 63 // check the class
65 ClassEntry classEntry = EntryFactory.getObfClassEntry(this.index, classMapping); 64 ClassEntry classEntry = EntryFactory.getObfClassEntry(this.index, classMapping);
66 if (!this.index.getObfClassEntries().contains(classEntry)) { 65 if (!this.index.getObfClassEntries().contains(classEntry)) {
67 return false; 66 return false;
68 } 67 }
69 68
70 // check the fields 69 // check the fields
71 for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { 70 for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) {
72 FieldEntry obfFieldEntry = EntryFactory.getObfFieldEntry(classMapping, fieldMapping); 71 FieldEntry obfFieldEntry = EntryFactory.getObfFieldEntry(classMapping, fieldMapping);
73 if (!this.index.containsObfField(obfFieldEntry)) { 72 if (!this.index.containsObfField(obfFieldEntry)) {
74 classMapping.removeFieldMapping(fieldMapping); 73 classMapping.removeFieldMapping(fieldMapping);
75 this.droppedFieldMappings.put(obfFieldEntry, fieldMapping); 74 this.droppedFieldMappings.put(obfFieldEntry, fieldMapping);
76 } 75 }
77 } 76 }
78 77
79 // check methods 78 // check methods
80 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { 79 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) {
81 BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping); 80 BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping);
82 if (!this.index.containsObfBehavior(obfBehaviorEntry)) { 81 if (!this.index.containsObfBehavior(obfBehaviorEntry)) {
83 classMapping.removeMethodMapping(methodMapping); 82 classMapping.removeMethodMapping(methodMapping);
84 this.droppedMethodMappings.put(obfBehaviorEntry, methodMapping); 83 this.droppedMethodMappings.put(obfBehaviorEntry, methodMapping);
85 } 84 }
86 } 85 }
87 86
88 // check inner classes 87 // check inner classes
89 for (ClassMapping innerClassMapping : Lists.newArrayList(classMapping.innerClasses())) { 88 for (ClassMapping innerClassMapping : Lists.newArrayList(classMapping.innerClasses())) {
90 if (!checkClassMapping(innerClassMapping)) { 89 if (!checkClassMapping(innerClassMapping)) {
91 classMapping.removeInnerClassMapping(innerClassMapping); 90 classMapping.removeInnerClassMapping(innerClassMapping);
92 this.droppedInnerClassMappings.put(EntryFactory.getObfClassEntry(this.index, innerClassMapping), innerClassMapping); 91 this.droppedInnerClassMappings.put(EntryFactory.getObfClassEntry(this.index, innerClassMapping), innerClassMapping);
93 } 92 }
94 } 93 }
95 94
96 return true; 95 return true;
97 } 96 }
98} 97}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
index cdfed726..a0d43133 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
@@ -8,178 +8,170 @@ import cuchaz.enigma.throwables.MappingParseException;
8import java.io.*; 8import java.io.*;
9import java.util.Deque; 9import java.util.Deque;
10 10
11public class MappingsEnigmaReader 11public class MappingsEnigmaReader {
12{ 12
13 13 public Mappings read(File file) throws IOException, MappingParseException {
14 public Mappings read(File file) throws IOException, MappingParseException { 14 Mappings mappings;
15 Mappings mappings; 15
16 16 // Multiple file
17 // Multiple file 17 if (file.isDirectory()) {
18 if (file.isDirectory()) 18 mappings = new Mappings(Mappings.FormatType.ENIGMA_DIRECTORY);
19 { 19 readDirectory(mappings, file);
20 mappings = new Mappings(Mappings.FormatType.ENIGMA_DIRECTORY); 20 } else {
21 readDirectory(mappings, file); 21 mappings = new Mappings();
22 } 22 readFile(mappings, file);
23 else 23 }
24 { 24 return mappings;
25 mappings = new Mappings(); 25 }
26 readFile(mappings, file); 26
27 } 27 public void readDirectory(Mappings mappings, File directory) throws IOException, MappingParseException {
28 return mappings; 28 File[] files = directory.listFiles();
29 } 29 if (files != null) {
30 30 for (File file : files) {
31 public void readDirectory(Mappings mappings, File directory) throws IOException, MappingParseException { 31 if (file.isFile() && !file.getName().startsWith(".") && file.getName().endsWith(".mapping"))
32 File[] files = directory.listFiles(); 32 readFile(mappings, file);
33 if (files != null) { 33 else if (file.isDirectory())
34 for (File file : files) { 34 readDirectory(mappings, file.getAbsoluteFile());
35 if (file.isFile() && !file.getName().startsWith(".") && file.getName().endsWith(".mapping")) 35 }
36 readFile(mappings, file); 36 mappings.savePreviousState();
37 else if (file.isDirectory()) 37 } else
38 readDirectory(mappings, file.getAbsoluteFile()); 38 throw new IOException("Cannot access directory" + directory.getAbsolutePath());
39 } 39 }
40 mappings.savePreviousState(); 40
41 } 41 public Mappings readFile(Mappings mappings, File file) throws IOException, MappingParseException {
42 else 42
43 throw new IOException("Cannot access directory" + directory.getAbsolutePath()); 43 BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charsets.UTF_8));
44 } 44 Deque<Object> mappingStack = Queues.newArrayDeque();
45 45
46 public Mappings readFile(Mappings mappings, File file) throws IOException, MappingParseException { 46 int lineNumber = 0;
47 47 String line;
48 BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charsets.UTF_8)); 48 while ((line = in.readLine()) != null) {
49 Deque<Object> mappingStack = Queues.newArrayDeque(); 49 lineNumber++;
50 50
51 int lineNumber = 0; 51 // strip comments
52 String line; 52 int commentPos = line.indexOf('#');
53 while ((line = in.readLine()) != null) { 53 if (commentPos >= 0) {
54 lineNumber++; 54 line = line.substring(0, commentPos);
55 55 }
56 // strip comments 56
57 int commentPos = line.indexOf('#'); 57 // skip blank lines
58 if (commentPos >= 0) { 58 if (line.trim().length() <= 0) {
59 line = line.substring(0, commentPos); 59 continue;
60 } 60 }
61 61
62 // skip blank lines 62 // get the indent of this line
63 if (line.trim().length() <= 0) { 63 int indent = 0;
64 continue; 64 for (int i = 0; i < line.length(); i++) {
65 } 65 if (line.charAt(i) != '\t') {
66 66 break;
67 // get the indent of this line 67 }
68 int indent = 0; 68 indent++;
69 for (int i = 0; i < line.length(); i++) { 69 }
70 if (line.charAt(i) != '\t') { 70
71 break; 71 // handle stack pops
72 } 72 while (indent < mappingStack.size()) {
73 indent++; 73 mappingStack.pop();
74 } 74 }
75 75
76 // handle stack pops 76 String[] parts = line.trim().split("\\s");
77 while (indent < mappingStack.size()) { 77 try {
78 mappingStack.pop(); 78 // read the first token
79 } 79 String token = parts[0];
80 80
81 String[] parts = line.trim().split("\\s"); 81 if (token.equalsIgnoreCase("CLASS")) {
82 try { 82 ClassMapping classMapping;
83 // read the first token 83 if (indent <= 0) {
84 String token = parts[0]; 84 // outer class
85 85 classMapping = readClass(parts, false);
86 if (token.equalsIgnoreCase("CLASS")) { 86 mappings.addClassMapping(classMapping);
87 ClassMapping classMapping; 87 } else {
88 if (indent <= 0) { 88
89 // outer class 89 // inner class
90 classMapping = readClass(parts, false); 90 if (!(mappingStack.peek() instanceof ClassMapping)) {
91 mappings.addClassMapping(classMapping); 91 throw new MappingParseException(file, lineNumber, "Unexpected CLASS entry here!");
92 } else { 92 }
93 93
94 // inner class 94 classMapping = readClass(parts, true);
95 if (!(mappingStack.peek() instanceof ClassMapping)) { 95 ((ClassMapping) mappingStack.peek()).addInnerClassMapping(classMapping);
96 throw new MappingParseException(file, lineNumber, "Unexpected CLASS entry here!"); 96 }
97 } 97 mappingStack.push(classMapping);
98 98 } else if (token.equalsIgnoreCase("FIELD")) {
99 classMapping = readClass(parts, true); 99 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) {
100 ((ClassMapping) mappingStack.peek()).addInnerClassMapping(classMapping); 100 throw new MappingParseException(file, lineNumber, "Unexpected FIELD entry here!");
101 } 101 }
102 mappingStack.push(classMapping); 102 ((ClassMapping) mappingStack.peek()).addFieldMapping(readField(parts));
103 } else if (token.equalsIgnoreCase("FIELD")) { 103 } else if (token.equalsIgnoreCase("METHOD")) {
104 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) { 104 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) {
105 throw new MappingParseException(file, lineNumber, "Unexpected FIELD entry here!"); 105 throw new MappingParseException(file, lineNumber, "Unexpected METHOD entry here!");
106 } 106 }
107 ((ClassMapping) mappingStack.peek()).addFieldMapping(readField(parts)); 107 MethodMapping methodMapping = readMethod(parts);
108 } else if (token.equalsIgnoreCase("METHOD")) { 108 ((ClassMapping) mappingStack.peek()).addMethodMapping(methodMapping);
109 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) { 109 mappingStack.push(methodMapping);
110 throw new MappingParseException(file, lineNumber, "Unexpected METHOD entry here!"); 110 } else if (token.equalsIgnoreCase("ARG")) {
111 } 111 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof MethodMapping)) {
112 MethodMapping methodMapping = readMethod(parts); 112 throw new MappingParseException(file, lineNumber, "Unexpected ARG entry here!");
113 ((ClassMapping) mappingStack.peek()).addMethodMapping(methodMapping); 113 }
114 mappingStack.push(methodMapping); 114 ((MethodMapping) mappingStack.peek()).addArgumentMapping(readArgument(parts));
115 } else if (token.equalsIgnoreCase("ARG")) { 115 }
116 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof MethodMapping)) { 116 } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) {
117 throw new MappingParseException(file, lineNumber, "Unexpected ARG entry here!"); 117 throw new MappingParseException(file, lineNumber, "Malformed line:\n" + line);
118 } 118 } catch (MappingConflict e) {
119 ((MethodMapping) mappingStack.peek()).addArgumentMapping(readArgument(parts)); 119 throw new MappingParseException(file, lineNumber, e.getMessage());
120 } 120 }
121 } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) { 121 }
122 throw new MappingParseException(file, lineNumber, "Malformed line:\n" + line); 122 in.close();
123 } catch (MappingConflict e) { 123 return mappings;
124 throw new MappingParseException(file, lineNumber, e.getMessage()); 124 }
125 } 125
126 } 126 private ArgumentMapping readArgument(String[] parts) {
127 in.close(); 127 return new ArgumentMapping(Integer.parseInt(parts[1]), parts[2]);
128 return mappings; 128 }
129 } 129
130 130 private ClassMapping readClass(String[] parts, boolean makeSimple) {
131 private ArgumentMapping readArgument(String[] parts) { 131 if (parts.length == 2) {
132 return new ArgumentMapping(Integer.parseInt(parts[1]), parts[2]); 132 return new ClassMapping(parts[1]);
133 } 133 } else if (parts.length == 3) {
134 134 boolean access = parts[2].startsWith("ACC:");
135 private ClassMapping readClass(String[] parts, boolean makeSimple) { 135 ClassMapping mapping;
136 if (parts.length == 2) { 136 if (access)
137 return new ClassMapping(parts[1]); 137 mapping = new ClassMapping(parts[1], null, Mappings.EntryModifier.valueOf(parts[2].substring(4)));
138 } else if (parts.length == 3) { 138 else
139 boolean access = parts[2].startsWith("ACC:"); 139 mapping = new ClassMapping(parts[1], parts[2]);
140 ClassMapping mapping; 140
141 if (access) 141 return mapping;
142 mapping = new ClassMapping(parts[1], null, Mappings.EntryModifier.valueOf(parts[2].substring(4))); 142 } else if (parts.length == 4)
143 else 143 return new ClassMapping(parts[1], parts[2], Mappings.EntryModifier.valueOf(parts[3].substring(4)));
144 mapping = new ClassMapping(parts[1], parts[2]); 144 return null;
145 145 }
146 return mapping; 146
147 } else if (parts.length == 4) 147 /* TEMP */
148 return new ClassMapping(parts[1], parts[2], Mappings.EntryModifier.valueOf(parts[3].substring(4))); 148 protected FieldMapping readField(String[] parts) {
149 return null; 149 FieldMapping mapping = null;
150 } 150 if (parts.length == 4) {
151 151 boolean access = parts[3].startsWith("ACC:");
152 /* TEMP */ 152 if (access)
153 protected FieldMapping readField(String[] parts) { 153 mapping = new FieldMapping(parts[1], new Type(parts[2]), null,
154 FieldMapping mapping = null; 154 Mappings.EntryModifier.valueOf(parts[3].substring(4)));
155 if (parts.length == 4) 155 else
156 { 156 mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.UNCHANGED);
157 boolean access = parts[3].startsWith("ACC:"); 157 } else if (parts.length == 5)
158 if (access) 158 mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.valueOf(parts[4].substring(4)));
159 mapping = new FieldMapping(parts[1], new Type(parts[2]), null, 159 return mapping;
160 Mappings.EntryModifier.valueOf(parts[3].substring(4))); 160 }
161 else 161
162 mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.UNCHANGED); 162 private MethodMapping readMethod(String[] parts) {
163 } 163 MethodMapping mapping = null;
164 else if (parts.length == 5) 164 if (parts.length == 3)
165 mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.valueOf(parts[4].substring(4))); 165 mapping = new MethodMapping(parts[1], new Signature(parts[2]));
166 return mapping; 166 else if (parts.length == 4) {
167 } 167 boolean access = parts[3].startsWith("ACC:");
168 168 if (access)
169 private MethodMapping readMethod(String[] parts) { 169 mapping = new MethodMapping(parts[1], new Signature(parts[2]), null, Mappings.EntryModifier.valueOf(parts[3].substring(4)));
170 MethodMapping mapping = null; 170 else
171 if (parts.length == 3) 171 mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2]);
172 mapping = new MethodMapping(parts[1], new Signature(parts[2])); 172 } else if (parts.length == 5)
173 else if (parts.length == 4){ 173 mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2],
174 boolean access = parts[3].startsWith("ACC:"); 174 Mappings.EntryModifier.valueOf(parts[4].substring(4)));
175 if (access) 175 return mapping;
176 mapping = new MethodMapping(parts[1], new Signature(parts[2]), null, Mappings.EntryModifier.valueOf(parts[3].substring(4))); 176 }
177 else
178 mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2]);
179 }
180 else if (parts.length == 5)
181 mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2],
182 Mappings.EntryModifier.valueOf(parts[4].substring(4)));
183 return mapping;
184 }
185} 177}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
index 6c57200f..ba1b258b 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
@@ -4,10 +4,11 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13import com.google.common.base.Charsets; 14import com.google.common.base.Charsets;
@@ -18,15 +19,13 @@ import java.util.Collections;
18import java.util.List; 19import java.util.List;
19 20
20public class MappingsEnigmaWriter { 21public class MappingsEnigmaWriter {
21 22
22 public void write(File out, Mappings mappings, boolean isDirectoryFormat) throws IOException { 23 public void write(File out, Mappings mappings, boolean isDirectoryFormat) throws IOException {
23 if (!isDirectoryFormat) 24 if (!isDirectoryFormat) {
24 {
25 PrintWriter outputWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(out), Charsets.UTF_8)); 25 PrintWriter outputWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(out), Charsets.UTF_8));
26 write(outputWriter, mappings); 26 write(outputWriter, mappings);
27 outputWriter.close(); 27 outputWriter.close();
28 } 28 } else
29 else
30 writeAsDirectory(out, mappings); 29 writeAsDirectory(out, mappings);
31 } 30 }
32 31
@@ -42,8 +41,7 @@ public class MappingsEnigmaWriter {
42 File result; 41 File result;
43 if (classMapping.getDeobfName() == null) 42 if (classMapping.getDeobfName() == null)
44 result = obFile; 43 result = obFile;
45 else 44 else {
46 {
47 // Make sure that old version of the file doesn't exist 45 // Make sure that old version of the file doesn't exist
48 if (obFile.exists()) 46 if (obFile.exists())
49 obFile.delete(); 47 obFile.delete();
@@ -59,19 +57,16 @@ public class MappingsEnigmaWriter {
59 } 57 }
60 58
61 // Remove dropped mappings 59 // Remove dropped mappings
62 if (mappings.getPreviousState() != null) 60 if (mappings.getPreviousState() != null) {
63 {
64 List<ClassMapping> droppedClassMappings = new ArrayList<>(mappings.getPreviousState().classes()); 61 List<ClassMapping> droppedClassMappings = new ArrayList<>(mappings.getPreviousState().classes());
65 List<ClassMapping> classMappings = new ArrayList<>(mappings.classes()); 62 List<ClassMapping> classMappings = new ArrayList<>(mappings.classes());
66 droppedClassMappings.removeAll(classMappings); 63 droppedClassMappings.removeAll(classMappings);
67 for (ClassMapping classMapping : droppedClassMappings) 64 for (ClassMapping classMapping : droppedClassMappings) {
68 {
69 File obFile = new File(target, classMapping.getObfFullName() + ".mapping"); 65 File obFile = new File(target, classMapping.getObfFullName() + ".mapping");
70 File result; 66 File result;
71 if (classMapping.getDeobfName() == null) 67 if (classMapping.getDeobfName() == null)
72 result = obFile; 68 result = obFile;
73 else 69 else {
74 {
75 // Make sure that old version of the file doesn't exist 70 // Make sure that old version of the file doesn't exist
76 if (obFile.exists()) 71 if (obFile.exists())
77 obFile.delete(); 72 obFile.delete();
@@ -86,18 +81,15 @@ public class MappingsEnigmaWriter {
86 private void deletePreviousClassMapping(File target, ClassMapping classMapping) { 81 private void deletePreviousClassMapping(File target, ClassMapping classMapping) {
87 File prevFile = null; 82 File prevFile = null;
88 // Deob rename 83 // Deob rename
89 if (classMapping.getDeobfName() != null && classMapping.getPreviousDeobfName() != null && !classMapping.getPreviousDeobfName().equals(classMapping.getDeobfName())) 84 if (classMapping.getDeobfName() != null && classMapping.getPreviousDeobfName() != null && !classMapping.getPreviousDeobfName().equals(classMapping.getDeobfName())) {
90 {
91 prevFile = new File(target, classMapping.getPreviousDeobfName() + ".mapping"); 85 prevFile = new File(target, classMapping.getPreviousDeobfName() + ".mapping");
92 } 86 }
93 // Deob to ob rename 87 // Deob to ob rename
94 else if (classMapping.getDeobfName() == null && classMapping.getPreviousDeobfName() != null) 88 else if (classMapping.getDeobfName() == null && classMapping.getPreviousDeobfName() != null) {
95 {
96 prevFile = new File(target, classMapping.getPreviousDeobfName() + ".mapping"); 89 prevFile = new File(target, classMapping.getPreviousDeobfName() + ".mapping");
97 } 90 }
98 // Ob to Deob rename 91 // Ob to Deob rename
99 else if (classMapping.getDeobfName() != null && classMapping.getPreviousDeobfName() == null) 92 else if (classMapping.getDeobfName() != null && classMapping.getPreviousDeobfName() == null) {
100 {
101 prevFile = new File(target, classMapping.getObfFullName() + ".mapping"); 93 prevFile = new File(target, classMapping.getObfFullName() + ".mapping");
102 } 94 }
103 95
@@ -110,50 +102,56 @@ public class MappingsEnigmaWriter {
110 write(out, classMapping, 0); 102 write(out, classMapping, 0);
111 } 103 }
112 } 104 }
113 105
114 private void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException { 106 private void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException {
115 if (classMapping.getDeobfName() == null) { 107 if (classMapping.getDeobfName() == null) {
116 out.format("%sCLASS %s%s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName()); 108 out.format("%sCLASS %s%s\n", getIndent(depth), classMapping.getObfFullName(),
109 classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName());
117 } else { 110 } else {
118 out.format("%sCLASS %s %s%s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getDeobfName(), classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName()); 111 out.format("%sCLASS %s %s%s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getDeobfName(),
112 classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName());
119 } 113 }
120 114
121 for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { 115 for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) {
122 write(out, innerClassMapping, depth + 1); 116 write(out, innerClassMapping, depth + 1);
123 } 117 }
124 118
125 for (FieldMapping fieldMapping : sorted(classMapping.fields())) { 119 for (FieldMapping fieldMapping : sorted(classMapping.fields())) {
126 write(out, fieldMapping, depth + 1); 120 write(out, fieldMapping, depth + 1);
127 } 121 }
128 122
129 for (MethodMapping methodMapping : sorted(classMapping.methods())) { 123 for (MethodMapping methodMapping : sorted(classMapping.methods())) {
130 write(out, methodMapping, depth + 1); 124 write(out, methodMapping, depth + 1);
131 } 125 }
132 } 126 }
133 127
134 private void write(PrintWriter out, FieldMapping fieldMapping, int depth) { 128 private void write(PrintWriter out, FieldMapping fieldMapping, int depth) {
135 if (fieldMapping.getDeobfName() == null) 129 if (fieldMapping.getDeobfName() == null)
136 out.format("%sFIELD %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getObfType().toString(), fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName()); 130 out.format("%sFIELD %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getObfType().toString(),
131 fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName());
137 else 132 else
138 out.format("%sFIELD %s %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName(), fieldMapping.getObfType().toString(), fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName()); 133 out.format("%sFIELD %s %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName(), fieldMapping.getObfType().toString(),
134 fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName());
139 } 135 }
140 136
141 private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException { 137 private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException {
142 if (methodMapping.getDeobfName() == null) { 138 if (methodMapping.getDeobfName() == null) {
143 out.format("%sMETHOD %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfSignature(), methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" :methodMapping.getModifier().getFormattedName()); 139 out.format("%sMETHOD %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfSignature(),
140 methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName());
144 } else { 141 } else {
145 out.format("%sMETHOD %s %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfSignature(), methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName()); 142 out.format("%sMETHOD %s %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfSignature(),
143 methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName());
146 } 144 }
147 145
148 for (ArgumentMapping argumentMapping : sorted(methodMapping.arguments())) { 146 for (ArgumentMapping argumentMapping : sorted(methodMapping.arguments())) {
149 write(out, argumentMapping, depth + 1); 147 write(out, argumentMapping, depth + 1);
150 } 148 }
151 } 149 }
152 150
153 private void write(PrintWriter out, ArgumentMapping argumentMapping, int depth) { 151 private void write(PrintWriter out, ArgumentMapping argumentMapping, int depth) {
154 out.format("%sARG %d %s\n", getIndent(depth), argumentMapping.getIndex(), argumentMapping.getName()); 152 out.format("%sARG %d %s\n", getIndent(depth), argumentMapping.getIndex(), argumentMapping.getName());
155 } 153 }
156 154
157 private <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) { 155 private <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) {
158 List<T> out = new ArrayList<>(); 156 List<T> out = new ArrayList<>();
159 for (T t : classes) { 157 for (T t : classes) {
@@ -162,7 +160,7 @@ public class MappingsEnigmaWriter {
162 Collections.sort(out); 160 Collections.sort(out);
163 return out; 161 return out;
164 } 162 }
165 163
166 private String getIndent(int depth) { 164 private String getIndent(int depth) {
167 StringBuilder buf = new StringBuilder(); 165 StringBuilder buf = new StringBuilder();
168 for (int i = 0; i < depth; i++) { 166 for (int i = 0; i < depth; i++) {
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
index e1428ea0..7126d2b6 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
@@ -8,8 +8,14 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
14import com.google.common.collect.Lists;
15import cuchaz.enigma.analysis.JarIndex;
16import cuchaz.enigma.throwables.IllegalNameException;
17import cuchaz.enigma.throwables.MappingConflict;
18
13import java.io.IOException; 19import java.io.IOException;
14import java.io.ObjectOutputStream; 20import java.io.ObjectOutputStream;
15import java.io.OutputStream; 21import java.io.OutputStream;
@@ -17,324 +23,315 @@ import java.util.List;
17import java.util.Set; 23import java.util.Set;
18import java.util.zip.GZIPOutputStream; 24import java.util.zip.GZIPOutputStream;
19 25
20import com.google.common.collect.Lists;
21import cuchaz.enigma.analysis.JarIndex;
22import cuchaz.enigma.throwables.IllegalNameException;
23import cuchaz.enigma.throwables.MappingConflict;
24
25public class MappingsRenamer { 26public class MappingsRenamer {
26 27
27 private JarIndex index; 28 private JarIndex index;
28 private Mappings mappings; 29 private Mappings mappings;
29 30
30 public MappingsRenamer(JarIndex index, Mappings mappings) { 31 public MappingsRenamer(JarIndex index, Mappings mappings) {
31 this.index = index; 32 this.index = index;
32 this.mappings = mappings; 33 this.mappings = mappings;
33 } 34 }
34 35
35 public void setMappings(Mappings mappings) 36 public void setMappings(Mappings mappings) {
36 { 37 this.mappings = mappings;
37 this.mappings = mappings; 38 }
38 } 39
39 40 public void setClassName(ClassEntry obf, String deobfName) {
40 public void setClassName(ClassEntry obf, String deobfName) { 41
41 42 deobfName = NameValidator.validateClassName(deobfName, !obf.isInnerClass());
42 deobfName = NameValidator.validateClassName(deobfName, !obf.isInnerClass()); 43
43 44 List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obf);
44 List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obf); 45 if (mappingChain.size() == 1) {
45 if (mappingChain.size() == 1) { 46
46 47 if (deobfName != null) {
47 if (deobfName != null) { 48 // make sure we don't rename to an existing obf or deobf class
48 // make sure we don't rename to an existing obf or deobf class 49 if (mappings.containsDeobfClass(deobfName) || index.containsObfClass(new ClassEntry(deobfName))) {
49 if (mappings.containsDeobfClass(deobfName) || index.containsObfClass(new ClassEntry(deobfName))) { 50 throw new IllegalNameException(deobfName, "There is already a class with that name");
50 throw new IllegalNameException(deobfName, "There is already a class with that name"); 51 }
51 } 52 }
52 } 53
53 54 ClassMapping classMapping = mappingChain.get(0);
54 ClassMapping classMapping = mappingChain.get(0); 55 mappings.setClassDeobfName(classMapping, deobfName);
55 mappings.setClassDeobfName(classMapping, deobfName); 56
56 57 } else {
57 } else { 58
58 59 ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2);
59 ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2); 60
60 61 if (deobfName != null) {
61 if (deobfName != null) { 62 // make sure we don't rename to an existing obf or deobf inner class
62 // make sure we don't rename to an existing obf or deobf inner class 63 if (outerClassMapping.hasInnerClassByDeobf(deobfName) || outerClassMapping.hasInnerClassByObfSimple(deobfName)) {
63 if (outerClassMapping.hasInnerClassByDeobf(deobfName) || outerClassMapping.hasInnerClassByObfSimple(deobfName)) { 64 throw new IllegalNameException(deobfName, "There is already a class with that name");
64 throw new IllegalNameException(deobfName, "There is already a class with that name"); 65 }
65 } 66 }
66 } 67
67 68 outerClassMapping.setInnerClassName(obf, deobfName);
68 outerClassMapping.setInnerClassName(obf, deobfName); 69 }
69 } 70 }
70 } 71
71 72 public void removeClassMapping(ClassEntry obf) {
72 public void removeClassMapping(ClassEntry obf) { 73 setClassName(obf, null);
73 setClassName(obf, null); 74 }
74 } 75
75 76 public void markClassAsDeobfuscated(ClassEntry obf) {
76 public void markClassAsDeobfuscated(ClassEntry obf) { 77 String deobfName = obf.isInnerClass() ? obf.getInnermostClassName() : obf.getName();
77 String deobfName = obf.isInnerClass() ? obf.getInnermostClassName() : obf.getName(); 78 List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obf);
78 List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obf); 79 if (mappingChain.size() == 1) {
79 if (mappingChain.size() == 1) { 80 ClassMapping classMapping = mappingChain.get(0);
80 ClassMapping classMapping = mappingChain.get(0); 81 mappings.setClassDeobfName(classMapping, deobfName);
81 mappings.setClassDeobfName(classMapping, deobfName); 82 } else {
82 } else { 83 ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2);
83 ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2); 84 outerClassMapping.setInnerClassName(obf, deobfName);
84 outerClassMapping.setInnerClassName(obf, deobfName); 85 }
85 } 86 }
86 } 87
87 88 public void setFieldName(FieldEntry obf, String deobfName) {
88 public void setFieldName(FieldEntry obf, String deobfName) { 89 deobfName = NameValidator.validateFieldName(deobfName);
89 deobfName = NameValidator.validateFieldName(deobfName); 90 FieldEntry targetEntry = new FieldEntry(obf.getClassEntry(), deobfName, obf.getType());
90 FieldEntry targetEntry = new FieldEntry(obf.getClassEntry(), deobfName, obf.getType()); 91 ClassEntry definedClass = null;
91 ClassEntry definedClass = null; 92 if (mappings.containsDeobfField(obf.getClassEntry(), deobfName) || index.containsEntryWithSameName(targetEntry))
92 if (mappings.containsDeobfField(obf.getClassEntry(), deobfName) || index.containsEntryWithSameName(targetEntry)) 93 definedClass = obf.getClassEntry();
93 definedClass = obf.getClassEntry(); 94 else {
94 else { 95 for (ClassEntry ancestorEntry : this.index.getTranslationIndex().getAncestry(obf.getClassEntry())) {
95 for (ClassEntry ancestorEntry : this.index.getTranslationIndex().getAncestry(obf.getClassEntry())) { 96 if (mappings.containsDeobfField(ancestorEntry, deobfName) || index.containsEntryWithSameName(targetEntry.cloneToNewClass(ancestorEntry))) {
96 if (mappings.containsDeobfField(ancestorEntry, deobfName) || index.containsEntryWithSameName(targetEntry.cloneToNewClass(ancestorEntry))) { 97 definedClass = ancestorEntry;
97 definedClass = ancestorEntry; 98 break;
98 break; 99 }
99 } 100 }
100 } 101 }
101 } 102
102 103 if (definedClass != null) {
103 if (definedClass != null) { 104 String className = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(definedClass.getClassName());
104 String className = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(definedClass.getClassName()); 105 if (className == null)
105 if (className == null) 106 className = definedClass.getClassName();
106 className = definedClass.getClassName(); 107 throw new IllegalNameException(deobfName, "There is already a field with that name in " + className);
107 throw new IllegalNameException(deobfName, "There is already a field with that name in " + className); 108 }
108 } 109
109 110 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry());
110 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 111 classMapping.setFieldName(obf.getName(), obf.getType(), deobfName);
111 classMapping.setFieldName(obf.getName(), obf.getType(), deobfName); 112 }
112 } 113
113 114 public void removeFieldMapping(FieldEntry obf) {
114 public void removeFieldMapping(FieldEntry obf) { 115 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry());
115 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 116 classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getType()));
116 classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getType())); 117 }
117 } 118
118 119 public void markFieldAsDeobfuscated(FieldEntry obf) {
119 public void markFieldAsDeobfuscated(FieldEntry obf) { 120 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry());
120 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 121 classMapping.setFieldName(obf.getName(), obf.getType(), obf.getName());
121 classMapping.setFieldName(obf.getName(), obf.getType(), obf.getName()); 122 }
122 } 123
123 124 private void validateMethodTreeName(MethodEntry entry, String deobfName) {
124 private void validateMethodTreeName(MethodEntry entry, String deobfName) { 125 MethodEntry targetEntry = new MethodEntry(entry.getClassEntry(), deobfName, entry.getSignature());
125 MethodEntry targetEntry = new MethodEntry(entry.getClassEntry(), deobfName, entry.getSignature()); 126
126 127 // TODO: Verify if I don't break things
127 // TODO: Verify if I don't break things 128 ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry());
128 ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry()); 129 if ((classMapping != null && classMapping.containsDeobfMethod(deobfName, entry.getSignature()) && classMapping.getMethodByObf(entry.getName(), entry.getSignature()) != classMapping.getMethodByDeobf(deobfName, entry.getSignature()))
129 if ((classMapping != null && classMapping.containsDeobfMethod(deobfName, entry.getSignature()) && classMapping.getMethodByObf(entry.getName(), entry.getSignature()) != classMapping.getMethodByDeobf(deobfName, entry.getSignature())) 130 || index.containsObfBehavior(targetEntry)) {
130 || index.containsObfBehavior(targetEntry)) { 131 String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(entry.getClassName());
131 String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(entry.getClassName()); 132 if (deobfClassName == null) {
132 if (deobfClassName == null) { 133 deobfClassName = entry.getClassName();
133 deobfClassName = entry.getClassName(); 134 }
134 } 135 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName);
135 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); 136 }
136 } 137
137 138 for (ClassEntry child : index.getTranslationIndex().getSubclass(entry.getClassEntry())) {
138 for (ClassEntry child : index.getTranslationIndex().getSubclass(entry.getClassEntry())) { 139 validateMethodTreeName(entry.cloneToNewClass(child), deobfName);
139 validateMethodTreeName(entry.cloneToNewClass(child), deobfName); 140 }
140 } 141 }
141 } 142
142 143 public void setMethodTreeName(MethodEntry obf, String deobfName) {
143 public void setMethodTreeName(MethodEntry obf, String deobfName) { 144 Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obf);
144 Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obf); 145
145 146 deobfName = NameValidator.validateMethodName(deobfName);
146 deobfName = NameValidator.validateMethodName(deobfName); 147 for (MethodEntry entry : implementations) {
147 for (MethodEntry entry : implementations) { 148 validateMethodTreeName(entry, deobfName);
148 validateMethodTreeName(entry, deobfName); 149 }
149 } 150
150 151 for (MethodEntry entry : implementations) {
151 for (MethodEntry entry : implementations) { 152 setMethodName(entry, deobfName);
152 setMethodName(entry, deobfName); 153 }
153 } 154 }
154 } 155
155 156 public void setMethodName(MethodEntry obf, String deobfName) {
156 public void setMethodName(MethodEntry obf, String deobfName) { 157 deobfName = NameValidator.validateMethodName(deobfName);
157 deobfName = NameValidator.validateMethodName(deobfName); 158 MethodEntry targetEntry = new MethodEntry(obf.getClassEntry(), deobfName, obf.getSignature());
158 MethodEntry targetEntry = new MethodEntry(obf.getClassEntry(), deobfName, obf.getSignature()); 159 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry());
159 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 160
160 161 // TODO: Verify if I don't break things
161 // TODO: Verify if I don't break things 162 if ((mappings.containsDeobfMethod(obf.getClassEntry(), deobfName, obf.getSignature()) && classMapping.getMethodByObf(obf.getName(), obf.getSignature()) != classMapping.getMethodByDeobf(deobfName, obf.getSignature()))
162 if ((mappings.containsDeobfMethod(obf.getClassEntry(), deobfName, obf.getSignature()) && classMapping.getMethodByObf(obf.getName(), obf.getSignature()) != classMapping.getMethodByDeobf(deobfName, obf.getSignature())) 163 || index.containsObfBehavior(targetEntry)) {
163 || index.containsObfBehavior(targetEntry)) { 164 String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(obf.getClassName());
164 String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(obf.getClassName()); 165 if (deobfClassName == null) {
165 if (deobfClassName == null) { 166 deobfClassName = obf.getClassName();
166 deobfClassName = obf.getClassName(); 167 }
167 } 168 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName);
168 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); 169 }
169 } 170
170 171 classMapping.setMethodName(obf.getName(), obf.getSignature(), deobfName);
171 classMapping.setMethodName(obf.getName(), obf.getSignature(), deobfName); 172 }
172 } 173
173 174 public void removeMethodTreeMapping(MethodEntry obf) {
174 public void removeMethodTreeMapping(MethodEntry obf) { 175 index.getRelatedMethodImplementations(obf).forEach(this::removeMethodMapping);
175 index.getRelatedMethodImplementations(obf).forEach(this::removeMethodMapping); 176 }
176 } 177
177 178 public void removeMethodMapping(MethodEntry obf) {
178 public void removeMethodMapping(MethodEntry obf) { 179 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry());
179 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 180 classMapping.setMethodName(obf.getName(), obf.getSignature(), null);
180 classMapping.setMethodName(obf.getName(), obf.getSignature(), null); 181 }
181 } 182
182 183 public void markMethodTreeAsDeobfuscated(MethodEntry obf) {
183 public void markMethodTreeAsDeobfuscated(MethodEntry obf) { 184 index.getRelatedMethodImplementations(obf).forEach(this::markMethodAsDeobfuscated);
184 index.getRelatedMethodImplementations(obf).forEach(this::markMethodAsDeobfuscated); 185 }
185 } 186
186 187 public void markMethodAsDeobfuscated(MethodEntry obf) {
187 public void markMethodAsDeobfuscated(MethodEntry obf) { 188 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry());
188 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 189 classMapping.setMethodName(obf.getName(), obf.getSignature(), obf.getName());
189 classMapping.setMethodName(obf.getName(), obf.getSignature(), obf.getName()); 190 }
190 } 191
191 192 public void setArgumentTreeName(ArgumentEntry obf, String deobfName) {
192 public void setArgumentTreeName(ArgumentEntry obf, String deobfName) { 193 if (!(obf.getBehaviorEntry() instanceof MethodEntry)) {
193 if (!(obf.getBehaviorEntry() instanceof MethodEntry)) { 194 setArgumentName(obf, deobfName);
194 setArgumentName(obf, deobfName); 195 return;
195 return; 196 }
196 } 197
197 198 MethodEntry obfMethod = (MethodEntry) obf.getBehaviorEntry();
198 MethodEntry obfMethod = (MethodEntry) obf.getBehaviorEntry(); 199
199 200 Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obfMethod);
200 Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obfMethod); 201 for (MethodEntry entry : implementations) {
201 for (MethodEntry entry : implementations) { 202 ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry());
202 ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry()); 203 if (classMapping != null) {
203 if (classMapping != null) { 204 MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getSignature());
204 MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getSignature()); 205 // NOTE: don't need to check arguments for name collisions with names determined by Procyon
205 // NOTE: don't need to check arguments for name collisions with names determined by Procyon 206 // TODO: Verify if I don't break things
206 // TODO: Verify if I don't break things 207 if (mapping != null) {
207 if (mapping != null) { 208 for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) {
208 for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) { 209 if (argumentMapping.getIndex() != obf.getIndex()) {
209 if (argumentMapping.getIndex() != obf.getIndex()) { 210 if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName)
210 if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName) 211 || argumentMapping.getName().equals(deobfName)) {
211 || argumentMapping.getName().equals(deobfName)) { 212 throw new IllegalNameException(deobfName, "There is already an argument with that name");
212 throw new IllegalNameException(deobfName, "There is already an argument with that name"); 213 }
213 } 214 }
214 } 215 }
215 } 216 }
216 } 217 }
217 } 218 }
218 } 219
219 220 for (MethodEntry entry : implementations) {
220 for (MethodEntry entry : implementations) { 221 setArgumentName(new ArgumentEntry(obf, entry), deobfName);
221 setArgumentName(new ArgumentEntry(obf, entry), deobfName); 222 }
222 } 223 }
223 } 224
224 225 public void setArgumentName(ArgumentEntry obf, String deobfName) {
225 public void setArgumentName(ArgumentEntry obf, String deobfName) { 226 deobfName = NameValidator.validateArgumentName(deobfName);
226 deobfName = NameValidator.validateArgumentName(deobfName); 227 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry());
227 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 228 MethodMapping mapping = classMapping.getMethodByObf(obf.getMethodName(), obf.getMethodSignature());
228 MethodMapping mapping = classMapping.getMethodByObf(obf.getMethodName(), obf.getMethodSignature()); 229 // NOTE: don't need to check arguments for name collisions with names determined by Procyon
229 // NOTE: don't need to check arguments for name collisions with names determined by Procyon 230 // TODO: Verify if I don't break things
230 // TODO: Verify if I don't break things 231 if (mapping != null) {
231 if (mapping != null) { 232 for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) {
232 for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) { 233 if (argumentMapping.getIndex() != obf.getIndex()) {
233 if (argumentMapping.getIndex() != obf.getIndex()) { 234 if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName)
234 if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName) 235 || argumentMapping.getName().equals(deobfName)) {
235 || argumentMapping.getName().equals(deobfName)) { 236 throw new IllegalNameException(deobfName, "There is already an argument with that name");
236 throw new IllegalNameException(deobfName, "There is already an argument with that name"); 237 }
237 } 238 }
238 } 239 }
239 } 240 }
240 } 241
241 242 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName);
242 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName); 243 }
243 } 244
244 245 public void removeArgumentMapping(ArgumentEntry obf) {
245 public void removeArgumentMapping(ArgumentEntry obf) { 246 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry());
246 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 247 classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex());
247 classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex()); 248 }
248 } 249
249 250 public void markArgumentAsDeobfuscated(ArgumentEntry obf) {
250 public void markArgumentAsDeobfuscated(ArgumentEntry obf) { 251 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry());
251 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 252 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName());
252 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName()); 253 }
253 } 254
254 255 public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) {
255 public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) { 256 classMapping.removeFieldMapping(fieldMapping);
256 classMapping.removeFieldMapping(fieldMapping); 257 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass);
257 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); 258 if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfType())) {
258 if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfType())) { 259 if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfType())) {
259 if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfType())) { 260 targetClassMapping.addFieldMapping(fieldMapping);
260 targetClassMapping.addFieldMapping(fieldMapping); 261 return true;
261 return true; 262 } else {
262 } else { 263 System.err.println("WARNING: deobf field was already there: " + obfClass + "." + fieldMapping.getDeobfName());
263 System.err.println("WARNING: deobf field was already there: " + obfClass + "." + fieldMapping.getDeobfName()); 264 }
264 } 265 }
265 } 266 return false;
266 return false; 267 }
267 } 268
268 269 public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) {
269 public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) { 270 classMapping.removeMethodMapping(methodMapping);
270 classMapping.removeMethodMapping(methodMapping); 271 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass);
271 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); 272 if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfSignature())) {
272 if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfSignature())) { 273 if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfSignature())) {
273 if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfSignature())) { 274 targetClassMapping.addMethodMapping(methodMapping);
274 targetClassMapping.addMethodMapping(methodMapping); 275 return true;
275 return true; 276 } else {
276 } else { 277 System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfSignature());
277 System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfSignature()); 278 }
278 } 279 }
279 } 280 return false;
280 return false; 281 }
281 } 282
282 283 public void write(OutputStream out) throws IOException {
283 public void write(OutputStream out) throws IOException { 284 // TEMP: just use the object output for now. We can find a more efficient storage format later
284 // TEMP: just use the object output for now. We can find a more efficient storage format later 285 GZIPOutputStream gzipout = new GZIPOutputStream(out);
285 GZIPOutputStream gzipout = new GZIPOutputStream(out); 286 ObjectOutputStream oout = new ObjectOutputStream(gzipout);
286 ObjectOutputStream oout = new ObjectOutputStream(gzipout); 287 oout.writeObject(this);
287 oout.writeObject(this); 288 gzipout.finish();
288 gzipout.finish(); 289 }
289 } 290
290 291 private ClassMapping getOrCreateClassMapping(ClassEntry obfClassEntry) {
291 private ClassMapping getOrCreateClassMapping(ClassEntry obfClassEntry) { 292 List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obfClassEntry);
292 List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obfClassEntry); 293 return mappingChain.get(mappingChain.size() - 1);
293 return mappingChain.get(mappingChain.size() - 1); 294 }
294 } 295
295 296 private List<ClassMapping> getOrCreateClassMappingChain(ClassEntry obfClassEntry) {
296 private List<ClassMapping> getOrCreateClassMappingChain(ClassEntry obfClassEntry) { 297 List<ClassEntry> classChain = obfClassEntry.getClassChain();
297 List<ClassEntry> classChain = obfClassEntry.getClassChain(); 298 List<ClassMapping> mappingChain = mappings.getClassMappingChain(obfClassEntry);
298 List<ClassMapping> mappingChain = mappings.getClassMappingChain(obfClassEntry); 299 for (int i = 0; i < classChain.size(); i++) {
299 for (int i = 0; i < classChain.size(); i++) { 300 ClassEntry classEntry = classChain.get(i);
300 ClassEntry classEntry = classChain.get(i); 301 ClassMapping classMapping = mappingChain.get(i);
301 ClassMapping classMapping = mappingChain.get(i); 302 if (classMapping == null) {
302 if (classMapping == null) { 303
303 304 // create it
304 // create it 305 classMapping = new ClassMapping(classEntry.getName());
305 classMapping = new ClassMapping(classEntry.getName()); 306 mappingChain.set(i, classMapping);
306 mappingChain.set(i, classMapping); 307
307 308 // add it to the right parent
308 // add it to the right parent 309 try {
309 try { 310 if (i == 0) {
310 if (i == 0) { 311 mappings.addClassMapping(classMapping);
311 mappings.addClassMapping(classMapping); 312 } else {
312 } else { 313 mappingChain.get(i - 1).addInnerClassMapping(classMapping);
313 mappingChain.get(i - 1).addInnerClassMapping(classMapping); 314 }
314 } 315 } catch (MappingConflict mappingConflict) {
315 } catch (MappingConflict mappingConflict) { 316 mappingConflict.printStackTrace();
316 mappingConflict.printStackTrace(); 317 }
317 } 318 }
318 } 319 }
319 } 320 return mappingChain;
320 return mappingChain; 321 }
321 } 322
322 323 public void setClassModifier(ClassEntry obEntry, Mappings.EntryModifier modifier) {
323 public void setClassModifier(ClassEntry obEntry, Mappings.EntryModifier modifier) 324 ClassMapping classMapping = getOrCreateClassMapping(obEntry);
324 { 325 classMapping.setModifier(modifier);
325 ClassMapping classMapping = getOrCreateClassMapping(obEntry); 326 }
326 classMapping.setModifier(modifier); 327
327 } 328 public void setFieldModifier(FieldEntry obEntry, Mappings.EntryModifier modifier) {
328 329 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry());
329 public void setFieldModifier(FieldEntry obEntry, Mappings.EntryModifier modifier) 330 classMapping.setFieldModifier(obEntry.getName(), obEntry.getType(), modifier);
330 { 331 }
331 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry()); 332
332 classMapping.setFieldModifier(obEntry.getName(), obEntry.getType(), modifier); 333 public void setMethodModifier(BehaviorEntry obEntry, Mappings.EntryModifier modifier) {
333 } 334 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry());
334 335 classMapping.setMethodModifier(obEntry.getName(), obEntry.getSignature(), modifier);
335 public void setMethodModifier(BehaviorEntry obEntry, Mappings.EntryModifier modifier) 336 }
336 {
337 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry());
338 classMapping.setMethodModifier(obEntry.getName(), obEntry.getSignature(), modifier);
339 }
340} 337}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
index a3f0cc87..b0eb826e 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
@@ -13,69 +13,67 @@ import java.util.List;
13 */ 13 */
14public class MappingsSRGWriter { 14public class MappingsSRGWriter {
15 15
16 public void write(File file, Mappings mappings) throws IOException { 16 public void write(File file, Mappings mappings) throws IOException {
17 if(file.exists()){ 17 if (file.exists()) {
18 file.delete(); 18 file.delete();
19 } 19 }
20 file.createNewFile(); 20 file.createNewFile();
21 21
22 TranslationIndex index = new TranslationIndex(); 22 TranslationIndex index = new TranslationIndex();
23 23
24 PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8)); 24 PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8));
25 List<String> fieldMappings = new ArrayList<>(); 25 List<String> fieldMappings = new ArrayList<>();
26 List<String> methodMappings = new ArrayList<>(); 26 List<String> methodMappings = new ArrayList<>();
27 for (ClassMapping classMapping : sorted(mappings.classes())) { 27 for (ClassMapping classMapping : sorted(mappings.classes())) {
28 if(classMapping.getDeobfName() == null || classMapping.getObfSimpleName() == null || classMapping.getDeobfName() == null){ 28 if (classMapping.getDeobfName() == null || classMapping.getObfSimpleName() == null || classMapping.getDeobfName() == null) {
29 continue; 29 continue;
30 } 30 }
31 writer.write("CL: " + classMapping.getObfSimpleName() + " " + classMapping.getDeobfName()); 31 writer.write("CL: " + classMapping.getObfSimpleName() + " " + classMapping.getDeobfName());
32 writer.write(System.lineSeparator()); 32 writer.write(System.lineSeparator());
33 for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { 33 for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) {
34 if(innerClassMapping.getDeobfName() == null || innerClassMapping.getObfSimpleName() == null || innerClassMapping.getDeobfName() == null){ 34 if (innerClassMapping.getDeobfName() == null || innerClassMapping.getObfSimpleName() == null || innerClassMapping.getDeobfName() == null) {
35 continue; 35 continue;
36 } 36 }
37 String innerClassName = classMapping.getObfSimpleName() + "$" + innerClassMapping.getObfSimpleName(); 37 String innerClassName = classMapping.getObfSimpleName() + "$" + innerClassMapping.getObfSimpleName();
38 String innerDeobfClassName = classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName(); 38 String innerDeobfClassName = classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName();
39 writer.write("CL: " + innerClassName + " " + classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName()); 39 writer.write("CL: " + innerClassName + " " + classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName());
40 writer.write(System.lineSeparator()); 40 writer.write(System.lineSeparator());
41 for (FieldMapping fieldMapping : sorted(innerClassMapping.fields())) { 41 for (FieldMapping fieldMapping : sorted(innerClassMapping.fields())) {
42 fieldMappings.add("FD: " + innerClassName + "/" + fieldMapping.getObfName() + " " + innerDeobfClassName + "/" + fieldMapping.getDeobfName()); 42 fieldMappings.add("FD: " + innerClassName + "/" + fieldMapping.getObfName() + " " + innerDeobfClassName + "/" + fieldMapping.getDeobfName());
43 } 43 }
44 44
45 for (MethodMapping methodMapping : sorted(innerClassMapping.methods())) { 45 for (MethodMapping methodMapping : sorted(innerClassMapping.methods())) {
46 methodMappings.add("MD: " + innerClassName + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature().toString() + " " + innerDeobfClassName + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature())); 46 methodMappings.add("MD: " + innerClassName + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature() + " " + innerDeobfClassName + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature()));
47 } 47 }
48 } 48 }
49 49
50 for (FieldMapping fieldMapping : sorted(classMapping.fields())) { 50 for (FieldMapping fieldMapping : sorted(classMapping.fields())) {
51 fieldMappings.add("FD: " + classMapping.getObfFullName() + "/" + fieldMapping.getObfName() + " " + classMapping.getDeobfName() + "/" + fieldMapping.getDeobfName()); 51 fieldMappings.add("FD: " + classMapping.getObfFullName() + "/" + fieldMapping.getObfName() + " " + classMapping.getDeobfName() + "/" + fieldMapping.getDeobfName());
52 } 52 }
53 53
54 for (MethodMapping methodMapping : sorted(classMapping.methods())) { 54 for (MethodMapping methodMapping : sorted(classMapping.methods())) {
55 methodMappings.add("MD: " + classMapping.getObfFullName() + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature().toString() + " " + classMapping.getDeobfName() + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature())); 55 methodMappings.add("MD: " + classMapping.getObfFullName() + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature() + " " + classMapping.getDeobfName() + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature()));
56 } 56 }
57 } 57 }
58 for(String fd : fieldMappings){ 58 for (String fd : fieldMappings) {
59 writer.write(fd); 59 writer.write(fd);
60 writer.write(System.lineSeparator()); 60 writer.write(System.lineSeparator());
61 } 61 }
62 62
63 for(String md : methodMappings){ 63 for (String md : methodMappings) {
64 writer.write(md); 64 writer.write(md);
65 writer.write(System.lineSeparator()); 65 writer.write(System.lineSeparator());
66 } 66 }
67 67
68 writer.close();
69 }
68 70
69 writer.close(); 71 private <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) {
70 } 72 List<T> out = new ArrayList<>();
71 73 for (T t : classes) {
72 74 out.add(t);
73 private <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) { 75 }
74 List<T> out = new ArrayList<>(); 76 Collections.sort(out);
75 for (T t : classes) { 77 return out;
76 out.add(t); 78 }
77 }
78 Collections.sort(out);
79 return out;
80 }
81} 79}
diff --git a/src/main/java/cuchaz/enigma/mapping/MemberMapping.java b/src/main/java/cuchaz/enigma/mapping/MemberMapping.java
index 90c096fa..d4514d42 100644
--- a/src/main/java/cuchaz/enigma/mapping/MemberMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/MemberMapping.java
@@ -8,11 +8,11 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12 11
12package cuchaz.enigma.mapping;
13 13
14public interface MemberMapping<T extends Entry> { 14public interface MemberMapping<T extends Entry> {
15 T getObfEntry(ClassEntry classEntry); 15 T getObfEntry(ClassEntry classEntry);
16 16
17 String getObfName(); 17 String getObfName();
18} 18}
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodEntry.java b/src/main/java/cuchaz/enigma/mapping/MethodEntry.java
index 4d7ed8f0..9c3058c4 100644
--- a/src/main/java/cuchaz/enigma/mapping/MethodEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/MethodEntry.java
@@ -8,82 +8,83 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13import cuchaz.enigma.utils.Utils; 14import cuchaz.enigma.utils.Utils;
14 15
15public class MethodEntry implements BehaviorEntry { 16public class MethodEntry implements BehaviorEntry {
16 17
17 private ClassEntry classEntry; 18 private ClassEntry classEntry;
18 private String name; 19 private String name;
19 private Signature signature; 20 private Signature signature;
20 21
21 public MethodEntry(ClassEntry classEntry, String name, Signature signature) { 22 public MethodEntry(ClassEntry classEntry, String name, Signature signature) {
22 if (classEntry == null) { 23 if (classEntry == null) {
23 throw new IllegalArgumentException("Class cannot be null!"); 24 throw new IllegalArgumentException("Class cannot be null!");
24 } 25 }
25 if (name == null) { 26 if (name == null) {
26 throw new IllegalArgumentException("Method name cannot be null!"); 27 throw new IllegalArgumentException("Method name cannot be null!");
27 } 28 }
28 if (signature == null) { 29 if (signature == null) {
29 throw new IllegalArgumentException("Method signature cannot be null!"); 30 throw new IllegalArgumentException("Method signature cannot be null!");
30 } 31 }
31 if (name.startsWith("<")) { 32 if (name.startsWith("<")) {
32 throw new IllegalArgumentException("Don't use MethodEntry for a constructor!"); 33 throw new IllegalArgumentException("Don't use MethodEntry for a constructor!");
33 } 34 }
34 35
35 this.classEntry = classEntry; 36 this.classEntry = classEntry;
36 this.name = name; 37 this.name = name;
37 this.signature = signature; 38 this.signature = signature;
38 } 39 }
39 40
40 public MethodEntry(MethodEntry other, String newClassName) { 41 public MethodEntry(MethodEntry other, String newClassName) {
41 this.classEntry = new ClassEntry(newClassName); 42 this.classEntry = new ClassEntry(newClassName);
42 this.name = other.name; 43 this.name = other.name;
43 this.signature = other.signature; 44 this.signature = other.signature;
44 } 45 }
45 46
46 @Override 47 @Override
47 public ClassEntry getClassEntry() { 48 public ClassEntry getClassEntry() {
48 return this.classEntry; 49 return this.classEntry;
49 } 50 }
50 51
51 @Override 52 @Override
52 public String getName() { 53 public String getName() {
53 return this.name; 54 return this.name;
54 } 55 }
55 56
56 @Override 57 @Override
57 public Signature getSignature() { 58 public Signature getSignature() {
58 return this.signature; 59 return this.signature;
59 } 60 }
60 61
61 @Override 62 @Override
62 public String getClassName() { 63 public String getClassName() {
63 return this.classEntry.getName(); 64 return this.classEntry.getName();
64 } 65 }
65 66
66 @Override 67 @Override
67 public MethodEntry cloneToNewClass(ClassEntry classEntry) { 68 public MethodEntry cloneToNewClass(ClassEntry classEntry) {
68 return new MethodEntry(this, classEntry.getName()); 69 return new MethodEntry(this, classEntry.getName());
69 } 70 }
70 71
71 @Override 72 @Override
72 public int hashCode() { 73 public int hashCode() {
73 return Utils.combineHashesOrdered(this.classEntry, this.name, this.signature); 74 return Utils.combineHashesOrdered(this.classEntry, this.name, this.signature);
74 } 75 }
75 76
76 @Override 77 @Override
77 public boolean equals(Object other) { 78 public boolean equals(Object other) {
78 return other instanceof MethodEntry && equals((MethodEntry) other); 79 return other instanceof MethodEntry && equals((MethodEntry) other);
79 } 80 }
80 81
81 public boolean equals(MethodEntry other) { 82 public boolean equals(MethodEntry other) {
82 return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.signature.equals(other.signature); 83 return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.signature.equals(other.signature);
83 } 84 }
84 85
85 @Override 86 @Override
86 public String toString() { 87 public String toString() {
87 return this.classEntry.getName() + "." + this.name + this.signature; 88 return this.classEntry.getName() + "." + this.name + this.signature;
88 } 89 }
89} 90}
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
index e0aeea2d..1524ce63 100644
--- a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
@@ -8,211 +8,206 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13import com.google.common.collect.Maps; 14import com.google.common.collect.Maps;
14
15import java.util.Map;
16
17import cuchaz.enigma.throwables.IllegalNameException; 15import cuchaz.enigma.throwables.IllegalNameException;
18import cuchaz.enigma.throwables.MappingConflict; 16import cuchaz.enigma.throwables.MappingConflict;
19 17
18import java.util.Map;
19
20public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<BehaviorEntry> { 20public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<BehaviorEntry> {
21 21
22 private String obfName; 22 private String obfName;
23 private String deobfName; 23 private String deobfName;
24 private Signature obfSignature; 24 private Signature obfSignature;
25 private Map<Integer, ArgumentMapping> arguments; 25 private Map<Integer, ArgumentMapping> arguments;
26 private Mappings.EntryModifier modifier; 26 private Mappings.EntryModifier modifier;
27 27
28 public MethodMapping(String obfName, Signature obfSignature) { 28 public MethodMapping(String obfName, Signature obfSignature) {
29 this(obfName, obfSignature, null,Mappings.EntryModifier.UNCHANGED); 29 this(obfName, obfSignature, null, Mappings.EntryModifier.UNCHANGED);
30 } 30 }
31 31
32 public MethodMapping(String obfName, Signature obfSignature, String deobfName) { 32 public MethodMapping(String obfName, Signature obfSignature, String deobfName) {
33 this(obfName, obfSignature, deobfName, Mappings.EntryModifier.UNCHANGED); 33 this(obfName, obfSignature, deobfName, Mappings.EntryModifier.UNCHANGED);
34 } 34 }
35 35
36 public MethodMapping(String obfName, Signature obfSignature, String deobfName, Mappings.EntryModifier modifier) { 36 public MethodMapping(String obfName, Signature obfSignature, String deobfName, Mappings.EntryModifier modifier) {
37 if (obfName == null) { 37 if (obfName == null) {
38 throw new IllegalArgumentException("obf name cannot be null!"); 38 throw new IllegalArgumentException("obf name cannot be null!");
39 } 39 }
40 if (obfSignature == null) { 40 if (obfSignature == null) {
41 throw new IllegalArgumentException("obf signature cannot be null!"); 41 throw new IllegalArgumentException("obf signature cannot be null!");
42 } 42 }
43 this.obfName = obfName; 43 this.obfName = obfName;
44 this.deobfName = NameValidator.validateMethodName(deobfName); 44 this.deobfName = NameValidator.validateMethodName(deobfName);
45 this.obfSignature = obfSignature; 45 this.obfSignature = obfSignature;
46 this.arguments = Maps.newTreeMap(); 46 this.arguments = Maps.newTreeMap();
47 this.modifier = modifier; 47 this.modifier = modifier;
48 } 48 }
49 49
50 public MethodMapping(MethodMapping other, ClassNameReplacer obfClassNameReplacer) { 50 public MethodMapping(MethodMapping other, ClassNameReplacer obfClassNameReplacer) {
51 this.obfName = other.obfName; 51 this.obfName = other.obfName;
52 this.deobfName = other.deobfName; 52 this.deobfName = other.deobfName;
53 this.modifier = other.modifier; 53 this.modifier = other.modifier;
54 this.obfSignature = new Signature(other.obfSignature, obfClassNameReplacer); 54 this.obfSignature = new Signature(other.obfSignature, obfClassNameReplacer);
55 this.arguments = Maps.newTreeMap(); 55 this.arguments = Maps.newTreeMap();
56 for (Map.Entry<Integer,ArgumentMapping> entry : other.arguments.entrySet()) { 56 for (Map.Entry<Integer, ArgumentMapping> entry : other.arguments.entrySet()) {
57 this.arguments.put(entry.getKey(), new ArgumentMapping(entry.getValue())); 57 this.arguments.put(entry.getKey(), new ArgumentMapping(entry.getValue()));
58 } 58 }
59 } 59 }
60 60
61 @Override 61 @Override
62 public String getObfName() { 62 public String getObfName() {
63 return this.obfName; 63 return this.obfName;
64 } 64 }
65 65
66 public String getDeobfName() { 66 public void setObfName(String name) {
67 return this.deobfName; 67 try {
68 } 68 NameValidator.validateMethodName(name);
69 69 } catch (IllegalNameException ex) {
70 public void setDeobfName(String val) { 70 // Invalid name, damn obfuscation! Map to a deob name with another name to avoid issues
71 this.deobfName = NameValidator.validateMethodName(val); 71 if (this.deobfName == null) {
72 } 72 System.err.println("WARNING: " + name + " is conflicting, auto deobfuscate to " + (name + "_auto_deob"));
73 73 setDeobfName(name + "_auto_deob");
74 public Signature getObfSignature() { 74 }
75 return this.obfSignature; 75 }
76 } 76 this.obfName = name;
77 77 }
78 public void setObfName(String name) { 78
79 try 79 public String getDeobfName() {
80 { 80 return this.deobfName;
81 NameValidator.validateMethodName(name); 81 }
82 } catch (IllegalNameException ex) 82
83 { 83 public void setDeobfName(String val) {
84 // Invalid name, damn obfuscation! Map to a deob name with another name to avoid issues 84 this.deobfName = NameValidator.validateMethodName(val);
85 if (this.deobfName == null) 85 }
86 { 86
87 System.err.println("WARNING: " + name + " is conflicting, auto deobfuscate to " + (name + "_auto_deob")); 87 public Signature getObfSignature() {
88 setDeobfName(name + "_auto_deob"); 88 return this.obfSignature;
89 } 89 }
90 } 90
91 this.obfName = name; 91 public void setObfSignature(Signature val) {
92 } 92 this.obfSignature = val;
93 93 }
94 public void setObfSignature(Signature val) { 94
95 this.obfSignature = val; 95 public Iterable<ArgumentMapping> arguments() {
96 } 96 return this.arguments.values();
97 97 }
98 public Iterable<ArgumentMapping> arguments() { 98
99 return this.arguments.values(); 99 public void addArgumentMapping(ArgumentMapping argumentMapping) throws MappingConflict {
100 } 100 if (this.arguments.containsKey(argumentMapping.getIndex())) {
101 101 throw new MappingConflict("argument", argumentMapping.getName(), this.arguments.get(argumentMapping.getIndex()).getName());
102 public void addArgumentMapping(ArgumentMapping argumentMapping) throws MappingConflict { 102 }
103 if (this.arguments.containsKey(argumentMapping.getIndex())) { 103 this.arguments.put(argumentMapping.getIndex(), argumentMapping);
104 throw new MappingConflict("argument", argumentMapping.getName(), this.arguments.get(argumentMapping.getIndex()).getName()); 104 }
105 } 105
106 this.arguments.put(argumentMapping.getIndex(), argumentMapping); 106 public String getObfArgumentName(int index) {
107 } 107 ArgumentMapping argumentMapping = this.arguments.get(index);
108 108 if (argumentMapping != null) {
109 public String getObfArgumentName(int index) { 109 return argumentMapping.getName();
110 ArgumentMapping argumentMapping = this.arguments.get(index); 110 }
111 if (argumentMapping != null) { 111
112 return argumentMapping.getName(); 112 return null;
113 } 113 }
114 114
115 return null; 115 public String getDeobfArgumentName(int index) {
116 } 116 ArgumentMapping argumentMapping = this.arguments.get(index);
117 117 if (argumentMapping != null) {
118 public String getDeobfArgumentName(int index) { 118 return argumentMapping.getName();
119 ArgumentMapping argumentMapping = this.arguments.get(index); 119 }
120 if (argumentMapping != null) { 120
121 return argumentMapping.getName(); 121 return null;
122 } 122 }
123 123
124 return null; 124 public void setArgumentName(int index, String name) {
125 } 125 ArgumentMapping argumentMapping = this.arguments.get(index);
126 126 if (argumentMapping == null) {
127 public void setArgumentName(int index, String name) { 127 argumentMapping = new ArgumentMapping(index, name);
128 ArgumentMapping argumentMapping = this.arguments.get(index); 128 boolean wasAdded = this.arguments.put(index, argumentMapping) == null;
129 if (argumentMapping == null) { 129 assert (wasAdded);
130 argumentMapping = new ArgumentMapping(index, name); 130 } else {
131 boolean wasAdded = this.arguments.put(index, argumentMapping) == null; 131 argumentMapping.setName(name);
132 assert (wasAdded); 132 }
133 } else { 133 }
134 argumentMapping.setName(name); 134
135 } 135 public void removeArgumentName(int index) {
136 } 136 boolean wasRemoved = this.arguments.remove(index) != null;
137 137 assert (wasRemoved);
138 public void removeArgumentName(int index) { 138 }
139 boolean wasRemoved = this.arguments.remove(index) != null; 139
140 assert (wasRemoved); 140 @Override
141 } 141 public String toString() {
142 142 StringBuilder buf = new StringBuilder();
143 @Override 143 buf.append("\t");
144 public String toString() { 144 buf.append(this.obfName);
145 StringBuilder buf = new StringBuilder(); 145 buf.append(" <-> ");
146 buf.append("\t"); 146 buf.append(this.deobfName);
147 buf.append(this.obfName); 147 buf.append("\n");
148 buf.append(" <-> "); 148 buf.append("\t");
149 buf.append(this.deobfName); 149 buf.append(this.obfSignature);
150 buf.append("\n"); 150 buf.append("\n");
151 buf.append("\t"); 151 buf.append("\tArguments:\n");
152 buf.append(this.obfSignature); 152 for (ArgumentMapping argumentMapping : this.arguments.values()) {
153 buf.append("\n"); 153 buf.append("\t\t");
154 buf.append("\tArguments:\n"); 154 buf.append(argumentMapping.getIndex());
155 for (ArgumentMapping argumentMapping : this.arguments.values()) { 155 buf.append(" -> ");
156 buf.append("\t\t"); 156 buf.append(argumentMapping.getName());
157 buf.append(argumentMapping.getIndex()); 157 buf.append("\n");
158 buf.append(" -> "); 158 }
159 buf.append(argumentMapping.getName()); 159 return buf.toString();
160 buf.append("\n"); 160 }
161 } 161
162 return buf.toString(); 162 @Override
163 } 163 public int compareTo(MethodMapping other) {
164 164 return (this.obfName + this.obfSignature).compareTo(other.obfName + other.obfSignature);
165 @Override 165 }
166 public int compareTo(MethodMapping other) { 166
167 return (this.obfName + this.obfSignature).compareTo(other.obfName + other.obfSignature); 167 public boolean containsArgument(String name) {
168 } 168 for (ArgumentMapping argumentMapping : this.arguments.values()) {
169 169 if (argumentMapping.getName().equals(name)) {
170 public boolean containsArgument(String name) { 170 return true;
171 for (ArgumentMapping argumentMapping : this.arguments.values()) { 171 }
172 if (argumentMapping.getName().equals(name)) { 172 }
173 return true; 173 return false;
174 } 174 }
175 } 175
176 return false; 176 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) {
177 } 177 // rename obf classes in the signature
178 178 Signature newSignature = new Signature(this.obfSignature, className ->
179 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { 179 {
180 // rename obf classes in the signature 180 if (className.equals(oldObfClassName)) {
181 Signature newSignature = new Signature(this.obfSignature, className -> 181 return newObfClassName;
182 { 182 }
183 if (className.equals(oldObfClassName)) { 183 return null;
184 return newObfClassName; 184 });
185 } 185
186 return null; 186 if (!newSignature.equals(this.obfSignature)) {
187 }); 187 this.obfSignature = newSignature;
188 188 return true;
189 if (!newSignature.equals(this.obfSignature)) { 189 }
190 this.obfSignature = newSignature; 190 return false;
191 return true; 191 }
192 } 192
193 return false; 193 public boolean isConstructor() {
194 } 194 return this.obfName.startsWith("<");
195 195 }
196 public boolean isConstructor() { 196
197 return this.obfName.startsWith("<"); 197 @Override
198 } 198 public BehaviorEntry getObfEntry(ClassEntry classEntry) {
199 199 if (isConstructor()) {
200 @Override 200 return new ConstructorEntry(classEntry, this.obfSignature);
201 public BehaviorEntry getObfEntry(ClassEntry classEntry) { 201 } else {
202 if (isConstructor()) { 202 return new MethodEntry(classEntry, this.obfName, this.obfSignature);
203 return new ConstructorEntry(classEntry, this.obfSignature); 203 }
204 } else { 204 }
205 return new MethodEntry(classEntry, this.obfName, this.obfSignature); 205
206 } 206 public Mappings.EntryModifier getModifier() {
207 } 207 return modifier;
208 208 }
209 public Mappings.EntryModifier getModifier() 209
210 { 210 public void setModifier(Mappings.EntryModifier modifier) {
211 return modifier; 211 this.modifier = modifier;
212 } 212 }
213
214 public void setModifier(Mappings.EntryModifier modifier)
215 {
216 this.modifier = modifier;
217 }
218} 213}
diff --git a/src/main/java/cuchaz/enigma/mapping/NameValidator.java b/src/main/java/cuchaz/enigma/mapping/NameValidator.java
index 6925b72a..aa3dc4de 100644
--- a/src/main/java/cuchaz/enigma/mapping/NameValidator.java
+++ b/src/main/java/cuchaz/enigma/mapping/NameValidator.java
@@ -8,61 +8,62 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
14import cuchaz.enigma.throwables.IllegalNameException;
15import javassist.bytecode.Descriptor;
16
13import java.util.Arrays; 17import java.util.Arrays;
14import java.util.List; 18import java.util.List;
15import java.util.regex.Pattern; 19import java.util.regex.Pattern;
16 20
17import cuchaz.enigma.throwables.IllegalNameException;
18import javassist.bytecode.Descriptor;
19
20public class NameValidator { 21public class NameValidator {
21 22
22 private static final Pattern IdentifierPattern; 23 private static final Pattern IdentifierPattern;
23 private static final Pattern ClassPattern; 24 private static final Pattern ClassPattern;
24 private static final List<String> ReservedWords = Arrays.asList( 25 private static final List<String> ReservedWords = Arrays.asList(
25 "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized", 26 "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized",
26 "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte", 27 "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte",
27 "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch", 28 "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch",
28 "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally", 29 "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally",
29 "long", "strictfp", "volatile", "const", "float", "native", "super", "while" 30 "long", "strictfp", "volatile", "const", "float", "native", "super", "while"
30 ); 31 );
31 32
32 static { 33 static {
33 String identifierRegex = "[A-Za-z_<][A-Za-z0-9_>]*"; 34 String identifierRegex = "[A-Za-z_<][A-Za-z0-9_>]*";
34 IdentifierPattern = Pattern.compile(identifierRegex); 35 IdentifierPattern = Pattern.compile(identifierRegex);
35 ClassPattern = Pattern.compile(String.format("^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex)); 36 ClassPattern = Pattern.compile(String.format("^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex));
36 } 37 }
37 38
38 public static String validateClassName(String name, boolean packageRequired) { 39 public static String validateClassName(String name, boolean packageRequired) {
39 if (name == null) { 40 if (name == null) {
40 return null; 41 return null;
41 } 42 }
42 if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) { 43 if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) {
43 throw new IllegalNameException(name, "This doesn't look like a legal class name"); 44 throw new IllegalNameException(name, "This doesn't look like a legal class name");
44 } 45 }
45 if (packageRequired && new ClassEntry(name).getPackageName() == null) { 46 if (packageRequired && new ClassEntry(name).getPackageName() == null) {
46 throw new IllegalNameException(name, "Class must be in a package"); 47 throw new IllegalNameException(name, "Class must be in a package");
47 } 48 }
48 return Descriptor.toJvmName(name); 49 return Descriptor.toJvmName(name);
49 } 50 }
50 51
51 public static String validateFieldName(String name) { 52 public static String validateFieldName(String name) {
52 if (name == null) { 53 if (name == null) {
53 return null; 54 return null;
54 } 55 }
55 if (!IdentifierPattern.matcher(name).matches() || ReservedWords.contains(name)) { 56 if (!IdentifierPattern.matcher(name).matches() || ReservedWords.contains(name)) {
56 throw new IllegalNameException(name, "This doesn't look like a legal identifier"); 57 throw new IllegalNameException(name, "This doesn't look like a legal identifier");
57 } 58 }
58 return name; 59 return name;
59 } 60 }
60 61
61 public static String validateMethodName(String name) { 62 public static String validateMethodName(String name) {
62 return validateFieldName(name); 63 return validateFieldName(name);
63 } 64 }
64 65
65 public static String validateArgumentName(String name) { 66 public static String validateArgumentName(String name) {
66 return validateFieldName(name); 67 return validateFieldName(name);
67 } 68 }
68} 69}
diff --git a/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java b/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java
index 51fed83e..33d930dc 100644
--- a/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java
+++ b/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13import com.strobel.assembler.metadata.*; 14import com.strobel.assembler.metadata.*;
@@ -16,57 +17,51 @@ import java.util.List;
16 17
17public class ProcyonEntryFactory { 18public class ProcyonEntryFactory {
18 19
19 private static String getErasedSignature(MemberReference def) 20 private static String getErasedSignature(MemberReference def) {
20 { 21 if (!(def instanceof MethodReference))
21 if (!(def instanceof MethodReference)) 22 return def.getErasedSignature();
22 return def.getErasedSignature(); 23 MethodReference methodReference = (MethodReference) def;
23 MethodReference methodReference = (MethodReference) def; 24 StringBuilder builder = new StringBuilder("(");
24 StringBuilder builder = new StringBuilder("("); 25 for (ParameterDefinition param : methodReference.getParameters()) {
25 for (ParameterDefinition param : methodReference.getParameters()) 26 TypeReference paramType = param.getParameterType();
26 { 27 if (paramType.getErasedSignature().equals("Ljava/lang/Object;") && paramType.hasExtendsBound() && paramType.getExtendsBound() instanceof CompoundTypeReference) {
27 TypeReference paramType = param.getParameterType(); 28 List<TypeReference> interfaces = ((CompoundTypeReference) paramType.getExtendsBound()).getInterfaces();
28 if (paramType.getErasedSignature().equals("Ljava/lang/Object;") && paramType.hasExtendsBound() && paramType.getExtendsBound() instanceof CompoundTypeReference) 29 interfaces.forEach((inter) -> builder.append(inter.getErasedSignature()));
29 { 30 } else
30 List<TypeReference> interfaces = ((CompoundTypeReference) paramType.getExtendsBound()).getInterfaces(); 31 builder.append(paramType.getErasedSignature());
31 interfaces.forEach((inter) -> builder.append(inter.getErasedSignature())); 32 }
32 } 33 builder.append(")");
33 else
34 builder.append(paramType.getErasedSignature());
35 }
36 builder.append(")");
37 34
38 TypeReference returnType = methodReference.getReturnType(); 35 TypeReference returnType = methodReference.getReturnType();
39 if (returnType.getErasedSignature().equals("Ljava/lang/Object;") && returnType.hasExtendsBound() && returnType.getExtendsBound() instanceof CompoundTypeReference) 36 if (returnType.getErasedSignature().equals("Ljava/lang/Object;") && returnType.hasExtendsBound() && returnType.getExtendsBound() instanceof CompoundTypeReference) {
40 { 37 List<TypeReference> interfaces = ((CompoundTypeReference) returnType.getExtendsBound()).getInterfaces();
41 List<TypeReference> interfaces = ((CompoundTypeReference) returnType.getExtendsBound()).getInterfaces(); 38 interfaces.forEach((inter) -> builder.append(inter.getErasedSignature()));
42 interfaces.forEach((inter) -> builder.append(inter.getErasedSignature())); 39 } else
43 } 40 builder.append(returnType.getErasedSignature());
44 else 41 return builder.toString();
45 builder.append(returnType.getErasedSignature()); 42 }
46 return builder.toString();
47 }
48 43
49 public static FieldEntry getFieldEntry(MemberReference def) { 44 public static FieldEntry getFieldEntry(MemberReference def) {
50 return new FieldEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Type(def.getErasedSignature())); 45 return new FieldEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Type(def.getErasedSignature()));
51 } 46 }
52 47
53 public static MethodEntry getMethodEntry(MemberReference def) { 48 public static MethodEntry getMethodEntry(MemberReference def) {
54 return new MethodEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Signature(getErasedSignature(def))); 49 return new MethodEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Signature(getErasedSignature(def)));
55 } 50 }
56 51
57 public static ConstructorEntry getConstructorEntry(MethodReference def) { 52 public static ConstructorEntry getConstructorEntry(MethodReference def) {
58 if (def.isTypeInitializer()) { 53 if (def.isTypeInitializer()) {
59 return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName())); 54 return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName()));
60 } else { 55 } else {
61 return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName()), new Signature(def.getErasedSignature())); 56 return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName()), new Signature(def.getErasedSignature()));
62 } 57 }
63 } 58 }
64 59
65 public static BehaviorEntry getBehaviorEntry(MethodReference def) { 60 public static BehaviorEntry getBehaviorEntry(MethodReference def) {
66 if (def.isConstructor() || def.isTypeInitializer()) { 61 if (def.isConstructor() || def.isTypeInitializer()) {
67 return getConstructorEntry(def); 62 return getConstructorEntry(def);
68 } else { 63 } else {
69 return getMethodEntry(def); 64 return getMethodEntry(def);
70 } 65 }
71 } 66 }
72} 67}
diff --git a/src/main/java/cuchaz/enigma/mapping/Signature.java b/src/main/java/cuchaz/enigma/mapping/Signature.java
index f30b6069..78130d6b 100644
--- a/src/main/java/cuchaz/enigma/mapping/Signature.java
+++ b/src/main/java/cuchaz/enigma/mapping/Signature.java
@@ -8,99 +8,99 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import cuchaz.enigma.utils.Utils;
14 16
15import java.util.List; 17import java.util.List;
16 18
17import cuchaz.enigma.utils.Utils;
18
19public class Signature { 19public class Signature {
20 20
21 private List<Type> argumentTypes; 21 private List<Type> argumentTypes;
22 private Type returnType; 22 private Type returnType;
23 23
24 public Signature(String signature) { 24 public Signature(String signature) {
25 try { 25 try {
26 this.argumentTypes = Lists.newArrayList(); 26 this.argumentTypes = Lists.newArrayList();
27 int i = 0; 27 int i = 0;
28 while (i < signature.length()) { 28 while (i < signature.length()) {
29 char c = signature.charAt(i); 29 char c = signature.charAt(i);
30 if (c == '(') { 30 if (c == '(') {
31 assert (this.argumentTypes.isEmpty()); 31 assert (this.argumentTypes.isEmpty());
32 assert (this.returnType == null); 32 assert (this.returnType == null);
33 i++; 33 i++;
34 } else if (c == ')') { 34 } else if (c == ')') {
35 i++; 35 i++;
36 break; 36 break;
37 } else { 37 } else {
38 String type = Type.parseFirst(signature.substring(i)); 38 String type = Type.parseFirst(signature.substring(i));
39 this.argumentTypes.add(new Type(type)); 39 this.argumentTypes.add(new Type(type));
40 i += type.length(); 40 i += type.length();
41 } 41 }
42 } 42 }
43 this.returnType = new Type(Type.parseFirst(signature.substring(i))); 43 this.returnType = new Type(Type.parseFirst(signature.substring(i)));
44 } catch (Exception ex) { 44 } catch (Exception ex) {
45 throw new IllegalArgumentException("Unable to parse signature: " + signature, ex); 45 throw new IllegalArgumentException("Unable to parse signature: " + signature, ex);
46 } 46 }
47 } 47 }
48 48
49 public Signature(Signature other, ClassNameReplacer replacer) { 49 public Signature(Signature other, ClassNameReplacer replacer) {
50 this.argumentTypes = Lists.newArrayList(other.argumentTypes); 50 this.argumentTypes = Lists.newArrayList(other.argumentTypes);
51 for (int i = 0; i < this.argumentTypes.size(); i++) { 51 for (int i = 0; i < this.argumentTypes.size(); i++) {
52 this.argumentTypes.set(i, new Type(this.argumentTypes.get(i), replacer)); 52 this.argumentTypes.set(i, new Type(this.argumentTypes.get(i), replacer));
53 } 53 }
54 this.returnType = new Type(other.returnType, replacer); 54 this.returnType = new Type(other.returnType, replacer);
55 } 55 }
56 56
57 public List<Type> getArgumentTypes() { 57 public List<Type> getArgumentTypes() {
58 return this.argumentTypes; 58 return this.argumentTypes;
59 } 59 }
60 60
61 public Type getReturnType() { 61 public Type getReturnType() {
62 return this.returnType; 62 return this.returnType;
63 } 63 }
64 64
65 @Override 65 @Override
66 public String toString() { 66 public String toString() {
67 StringBuilder buf = new StringBuilder(); 67 StringBuilder buf = new StringBuilder();
68 buf.append("("); 68 buf.append("(");
69 for (Type type : this.argumentTypes) { 69 for (Type type : this.argumentTypes) {
70 buf.append(type.toString()); 70 buf.append(type);
71 } 71 }
72 buf.append(")"); 72 buf.append(")");
73 buf.append(this.returnType.toString()); 73 buf.append(this.returnType);
74 return buf.toString(); 74 return buf.toString();
75 } 75 }
76 76
77 public Iterable<Type> types() { 77 public Iterable<Type> types() {
78 List<Type> types = Lists.newArrayList(); 78 List<Type> types = Lists.newArrayList();
79 types.addAll(this.argumentTypes); 79 types.addAll(this.argumentTypes);
80 types.add(this.returnType); 80 types.add(this.returnType);
81 return types; 81 return types;
82 } 82 }
83 83
84 @Override 84 @Override
85 public boolean equals(Object other) { 85 public boolean equals(Object other) {
86 return other instanceof Signature && equals((Signature) other); 86 return other instanceof Signature && equals((Signature) other);
87 } 87 }
88 88
89 public boolean equals(Signature other) { 89 public boolean equals(Signature other) {
90 return this.argumentTypes.equals(other.argumentTypes) && this.returnType.equals(other.returnType); 90 return this.argumentTypes.equals(other.argumentTypes) && this.returnType.equals(other.returnType);
91 } 91 }
92 92
93 @Override 93 @Override
94 public int hashCode() { 94 public int hashCode() {
95 return Utils.combineHashesOrdered(this.argumentTypes.hashCode(), this.returnType.hashCode()); 95 return Utils.combineHashesOrdered(this.argumentTypes.hashCode(), this.returnType.hashCode());
96 } 96 }
97 97
98 public boolean hasClass(ClassEntry classEntry) { 98 public boolean hasClass(ClassEntry classEntry) {
99 for (Type type : types()) { 99 for (Type type : types()) {
100 if (type.hasClass() && type.getClassEntry().equals(classEntry)) { 100 if (type.hasClass() && type.getClassEntry().equals(classEntry)) {
101 return true; 101 return true;
102 } 102 }
103 } 103 }
104 return false; 104 return false;
105 } 105 }
106} 106}
diff --git a/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java b/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java
index 98643330..ddc5af4b 100644
--- a/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java
+++ b/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
@@ -18,74 +19,74 @@ import java.util.List;
18 19
19public class SignatureUpdater { 20public class SignatureUpdater {
20 21
21 public interface ClassNameUpdater { 22 public static String update(String signature, ClassNameUpdater updater) {
22 String update(String className); 23 try {
23 } 24 StringBuilder buf = new StringBuilder();
24 25
25 public static String update(String signature, ClassNameUpdater updater) { 26 // read the signature character-by-character
26 try { 27 StringReader reader = new StringReader(signature);
27 StringBuilder buf = new StringBuilder(); 28 int i;
29 while ((i = reader.read()) != -1) {
30 char c = (char) i;
28 31
29 // read the signature character-by-character 32 // does this character start a class name?
30 StringReader reader = new StringReader(signature); 33 if (c == 'L') {
31 int i; 34 // update the class name and add it to the buffer
32 while ((i = reader.read()) != -1) { 35 buf.append('L');
33 char c = (char) i; 36 String className = readClass(reader);
37 if (className == null) {
38 throw new IllegalArgumentException("Malformed signature: " + signature);
39 }
40 buf.append(updater.update(className));
41 buf.append(';');
42 } else {
43 // copy the character into the buffer
44 buf.append(c);
45 }
46 }
34 47
35 // does this character start a class name? 48 return buf.toString();
36 if (c == 'L') { 49 } catch (IOException ex) {
37 // update the class name and add it to the buffer 50 // I'm pretty sure a StringReader will never throw one of these
38 buf.append('L'); 51 throw new Error(ex);
39 String className = readClass(reader); 52 }
40 if (className == null) { 53 }
41 throw new IllegalArgumentException("Malformed signature: " + signature);
42 }
43 buf.append(updater.update(className));
44 buf.append(';');
45 } else {
46 // copy the character into the buffer
47 buf.append(c);
48 }
49 }
50 54
51 return buf.toString(); 55 private static String readClass(StringReader reader) throws IOException {
52 } catch (IOException ex) { 56 // read all the characters in the buffer until we hit a ';'
53 // I'm pretty sure a StringReader will never throw one of these 57 // remember to treat generics correctly
54 throw new Error(ex); 58 StringBuilder buf = new StringBuilder();
55 } 59 int depth = 0;
56 } 60 int i;
61 while ((i = reader.read()) != -1) {
62 char c = (char) i;
57 63
58 private static String readClass(StringReader reader) throws IOException { 64 if (c == '<') {
59 // read all the characters in the buffer until we hit a ';' 65 depth++;
60 // remember to treat generics correctly 66 } else if (c == '>') {
61 StringBuilder buf = new StringBuilder(); 67 depth--;
62 int depth = 0; 68 } else if (depth == 0) {
63 int i; 69 if (c == ';') {
64 while ((i = reader.read()) != -1) { 70 return buf.toString();
65 char c = (char) i; 71 } else {
72 buf.append(c);
73 }
74 }
75 }
66 76
67 if (c == '<') { 77 return null;
68 depth++; 78 }
69 } else if (c == '>') {
70 depth--;
71 } else if (depth == 0) {
72 if (c == ';') {
73 return buf.toString();
74 } else {
75 buf.append(c);
76 }
77 }
78 }
79 79
80 return null; 80 public static List<String> getClasses(String signature) {
81 } 81 final List<String> classNames = Lists.newArrayList();
82 update(signature, className -> {
83 classNames.add(className);
84 return className;
85 });
86 return classNames;
87 }
82 88
83 public static List<String> getClasses(String signature) { 89 public interface ClassNameUpdater {
84 final List<String> classNames = Lists.newArrayList(); 90 String update(String className);
85 update(signature, className -> { 91 }
86 classNames.add(className);
87 return className;
88 });
89 return classNames;
90 }
91} 92}
diff --git a/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java b/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java
index 8329d0d2..17e31876 100644
--- a/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java
+++ b/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java
@@ -8,22 +8,23 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13public enum TranslationDirection { 14public enum TranslationDirection {
14 15
15 Deobfuscating { 16 Deobfuscating {
16 @Override 17 @Override
17 public <T> T choose(T deobfChoice, T obfChoice) { 18 public <T> T choose(T deobfChoice, T obfChoice) {
18 return deobfChoice; 19 return deobfChoice;
19 } 20 }
20 }, 21 },
21 Obfuscating { 22 Obfuscating {
22 @Override 23 @Override
23 public <T> T choose(T deobfChoice, T obfChoice) { 24 public <T> T choose(T deobfChoice, T obfChoice) {
24 return obfChoice; 25 return obfChoice;
25 } 26 }
26 }; 27 };
27 28
28 public abstract <T> T choose(T deobfChoice, T obfChoice); 29 public abstract <T> T choose(T deobfChoice, T obfChoice);
29} 30}
diff --git a/src/main/java/cuchaz/enigma/mapping/Translator.java b/src/main/java/cuchaz/enigma/mapping/Translator.java
index e94009ee..8d464fc4 100644
--- a/src/main/java/cuchaz/enigma/mapping/Translator.java
+++ b/src/main/java/cuchaz/enigma/mapping/Translator.java
@@ -8,348 +8,335 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
14import com.google.common.collect.Maps; 15import com.google.common.collect.Maps;
16import cuchaz.enigma.analysis.TranslationIndex;
15 17
16import java.util.List; 18import java.util.List;
17import java.util.Map; 19import java.util.Map;
18 20
19import cuchaz.enigma.analysis.TranslationIndex;
20
21public class Translator { 21public class Translator {
22 22
23 private TranslationDirection direction; 23 private TranslationDirection direction;
24 private Map<String, ClassMapping> classes; 24 private Map<String, ClassMapping> classes;
25 private TranslationIndex index; 25 private TranslationIndex index;
26 26
27 private ClassNameReplacer classNameReplacer = className -> translateEntry(new ClassEntry(className)).getName(); 27 private ClassNameReplacer classNameReplacer = className -> translateEntry(new ClassEntry(className)).getName();
28 28
29 public Translator() { 29 public Translator() {
30 this.direction = null; 30 this.direction = null;
31 this.classes = Maps.newHashMap(); 31 this.classes = Maps.newHashMap();
32 this.index = new TranslationIndex(); 32 this.index = new TranslationIndex();
33 } 33 }
34 34
35 public Translator(TranslationDirection direction, Map<String, ClassMapping> classes, TranslationIndex index) { 35 public Translator(TranslationDirection direction, Map<String, ClassMapping> classes, TranslationIndex index) {
36 this.direction = direction; 36 this.direction = direction;
37 this.classes = classes; 37 this.classes = classes;
38 this.index = index; 38 this.index = index;
39 } 39 }
40 40
41 public TranslationDirection getDirection() { 41 public TranslationDirection getDirection() {
42 return direction; 42 return direction;
43 } 43 }
44 44
45 public TranslationIndex getTranslationIndex() { 45 public TranslationIndex getTranslationIndex() {
46 return index; 46 return index;
47 } 47 }
48 48
49 @SuppressWarnings("unchecked") 49 @SuppressWarnings("unchecked")
50 public <T extends Entry> T translateEntry(T entry) { 50 public <T extends Entry> T translateEntry(T entry) {
51 if (entry instanceof ClassEntry) { 51 if (entry instanceof ClassEntry) {
52 return (T) translateEntry((ClassEntry) entry); 52 return (T) translateEntry((ClassEntry) entry);
53 } else if (entry instanceof FieldEntry) { 53 } else if (entry instanceof FieldEntry) {
54 return (T) translateEntry((FieldEntry) entry); 54 return (T) translateEntry((FieldEntry) entry);
55 } else if (entry instanceof MethodEntry) { 55 } else if (entry instanceof MethodEntry) {
56 return (T) translateEntry((MethodEntry) entry); 56 return (T) translateEntry((MethodEntry) entry);
57 } else if (entry instanceof ConstructorEntry) { 57 } else if (entry instanceof ConstructorEntry) {
58 return (T) translateEntry((ConstructorEntry) entry); 58 return (T) translateEntry((ConstructorEntry) entry);
59 } else if (entry instanceof ArgumentEntry) { 59 } else if (entry instanceof ArgumentEntry) {
60 return (T) translateEntry((ArgumentEntry) entry); 60 return (T) translateEntry((ArgumentEntry) entry);
61 } else if (entry instanceof LocalVariableEntry) { 61 } else if (entry instanceof LocalVariableEntry) {
62 return (T) translateEntry((LocalVariableEntry) entry); 62 return (T) translateEntry((LocalVariableEntry) entry);
63 } else { 63 } else {
64 throw new Error("Unknown entry type: " + entry.getClass().getName()); 64 throw new Error("Unknown entry type: " + entry.getClass().getName());
65 } 65 }
66 } 66 }
67 67
68 public <T extends Entry> String translate(T entry) { 68 public <T extends Entry> String translate(T entry) {
69 if (entry instanceof ClassEntry) { 69 if (entry instanceof ClassEntry) {
70 return translate((ClassEntry) entry); 70 return translate((ClassEntry) entry);
71 } else if (entry instanceof FieldEntry) { 71 } else if (entry instanceof FieldEntry) {
72 return translate((FieldEntry) entry); 72 return translate((FieldEntry) entry);
73 } else if (entry instanceof MethodEntry) { 73 } else if (entry instanceof MethodEntry) {
74 return translate((MethodEntry) entry); 74 return translate((MethodEntry) entry);
75 } else if (entry instanceof ConstructorEntry) { 75 } else if (entry instanceof ConstructorEntry) {
76 return translate(entry); 76 return translate(entry);
77 } else if (entry instanceof ArgumentEntry) { 77 } else if (entry instanceof ArgumentEntry) {
78 return translate((ArgumentEntry) entry); 78 return translate((ArgumentEntry) entry);
79 } else if (entry instanceof LocalVariableEntry) { 79 } else if (entry instanceof LocalVariableEntry) {
80 return translate((LocalVariableEntry) entry); 80 return translate((LocalVariableEntry) entry);
81 } else { 81 } else {
82 throw new Error("Unknown entry type: " + entry.getClass().getName()); 82 throw new Error("Unknown entry type: " + entry.getClass().getName());
83 } 83 }
84 } 84 }
85 85
86 public String translate(LocalVariableEntry in) 86 public String translate(LocalVariableEntry in) {
87 { 87 LocalVariableEntry translated = translateEntry(in);
88 LocalVariableEntry translated = translateEntry(in); 88 if (translated.equals(in)) {
89 if (translated.equals(in)) { 89 return null;
90 return null; 90 }
91 } 91 return translated.getName();
92 return translated.getName(); 92 }
93 } 93
94 94 public LocalVariableEntry translateEntry(LocalVariableEntry in) {
95 public LocalVariableEntry translateEntry(LocalVariableEntry in) 95 // TODO: Implement it
96 { 96 return in;
97 // TODO: Implement it 97 }
98 return in; 98
99 } 99 public String translate(ClassEntry in) {
100 100 ClassEntry translated = translateEntry(in);
101 public String translate(ClassEntry in) { 101 if (translated.equals(in)) {
102 ClassEntry translated = translateEntry(in); 102 return null;
103 if (translated.equals(in)) { 103 }
104 return null; 104 return translated.getName();
105 } 105 }
106 return translated.getName(); 106
107 } 107 public String translateClass(String className) {
108 108 return translate(new ClassEntry(className));
109 public String translateClass(String className) { 109 }
110 return translate(new ClassEntry(className)); 110
111 } 111 public ClassEntry translateEntry(ClassEntry in) {
112 112
113 public ClassEntry translateEntry(ClassEntry in) { 113 if (in.isInnerClass()) {
114 114
115 if (in.isInnerClass()) { 115 // translate as much of the class chain as we can
116 116 List<ClassMapping> mappingsChain = getClassMappingChain(in);
117 // translate as much of the class chain as we can 117 String[] obfClassNames = in.getName().split("\\$");
118 List<ClassMapping> mappingsChain = getClassMappingChain(in); 118 StringBuilder buf = new StringBuilder();
119 String[] obfClassNames = in.getName().split("\\$"); 119 for (int i = 0; i < obfClassNames.length; i++) {
120 StringBuilder buf = new StringBuilder(); 120 boolean isFirstClass = buf.length() == 0;
121 for (int i = 0; i < obfClassNames.length; i++) { 121 String className = null;
122 boolean isFirstClass = buf.length() == 0; 122 ClassMapping classMapping = mappingsChain.get(i);
123 String className = null; 123 if (classMapping != null) {
124 ClassMapping classMapping = mappingsChain.get(i); 124 className = this.direction.choose(
125 if (classMapping != null) { 125 classMapping.getDeobfName(),
126 className = this.direction.choose( 126 isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName()
127 classMapping.getDeobfName(), 127 );
128 isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName() 128 }
129 ); 129 if (className == null) {
130 } 130 className = obfClassNames[i];
131 if (className == null) { 131 }
132 className = obfClassNames[i]; 132 if (!isFirstClass) {
133 } 133 buf.append("$");
134 if (!isFirstClass) { 134 }
135 buf.append("$"); 135 buf.append(className);
136 } 136 }
137 buf.append(className); 137 return new ClassEntry(buf.toString());
138 } 138
139 return new ClassEntry(buf.toString()); 139 } else {
140 140
141 } else { 141 // normal classes are easy
142 142 ClassMapping classMapping = this.classes.get(in.getName());
143 // normal classes are easy 143 if (classMapping == null) {
144 ClassMapping classMapping = this.classes.get(in.getName()); 144 return in;
145 if (classMapping == null) { 145 }
146 return in; 146 return this.direction.choose(
147 } 147 classMapping.getDeobfName() != null ? new ClassEntry(classMapping.getDeobfName()) : in,
148 return this.direction.choose( 148 new ClassEntry(classMapping.getObfFullName())
149 classMapping.getDeobfName() != null ? new ClassEntry(classMapping.getDeobfName()) : in, 149 );
150 new ClassEntry(classMapping.getObfFullName()) 150 }
151 ); 151 }
152 } 152
153 } 153 public String translate(FieldEntry in) {
154 154
155 public String translate(FieldEntry in) { 155 // resolve the class entry
156 156 ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in);
157 // resolve the class entry 157 if (resolvedClassEntry != null) {
158 ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in); 158
159 if (resolvedClassEntry != null) { 159 // look for the class
160 160 ClassMapping classMapping = findClassMapping(resolvedClassEntry);
161 // look for the class 161 if (classMapping != null) {
162 ClassMapping classMapping = findClassMapping(resolvedClassEntry); 162
163 if (classMapping != null) { 163 // look for the field
164 164 String translatedName = this.direction.choose(
165 // look for the field 165 classMapping.getDeobfFieldName(in.getName(), in.getType()),
166 String translatedName = this.direction.choose( 166 classMapping.getObfFieldName(in.getName(), translateType(in.getType()))
167 classMapping.getDeobfFieldName(in.getName(), in.getType()), 167 );
168 classMapping.getObfFieldName(in.getName(), translateType(in.getType())) 168 if (translatedName != null) {
169 ); 169 return translatedName;
170 if (translatedName != null) { 170 }
171 return translatedName; 171 }
172 } 172 }
173 } 173 return null;
174 } 174 }
175 return null; 175
176 } 176 public FieldEntry translateEntry(FieldEntry in) {
177 177 String name = translate(in);
178 public FieldEntry translateEntry(FieldEntry in) { 178 if (name == null) {
179 String name = translate(in); 179 name = in.getName();
180 if (name == null) { 180 }
181 name = in.getName(); 181 return new FieldEntry(translateEntry(in.getClassEntry()), name, translateType(in.getType()));
182 } 182 }
183 return new FieldEntry(translateEntry(in.getClassEntry()), name, translateType(in.getType())); 183
184 } 184 public String translate(MethodEntry in) {
185 185 // resolve the class entry
186 public String translate(MethodEntry in) { 186 ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in, true);
187 // resolve the class entry 187 if (resolvedClassEntry != null) {
188 ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in, true); 188
189 if (resolvedClassEntry != null) { 189 // look for class
190 190 ClassMapping classMapping = findClassMapping(resolvedClassEntry);
191 // look for class 191 if (classMapping != null) {
192 ClassMapping classMapping = findClassMapping(resolvedClassEntry); 192
193 if (classMapping != null) { 193 // look for the method
194 194 MethodMapping methodMapping = this.direction.choose(
195 // look for the method 195 classMapping.getMethodByObf(in.getName(), in.getSignature()),
196 MethodMapping methodMapping = this.direction.choose( 196 classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature()))
197 classMapping.getMethodByObf(in.getName(), in.getSignature()), 197 );
198 classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature())) 198 if (methodMapping != null) {
199 ); 199 return this.direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName());
200 if (methodMapping != null) { 200 }
201 return this.direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName()); 201 }
202 } 202 }
203 } 203 return null;
204 } 204 }
205 return null; 205
206 } 206 public MethodEntry translateEntry(MethodEntry in) {
207 207 String name = translate(in);
208 public MethodEntry translateEntry(MethodEntry in) { 208 if (name == null) {
209 String name = translate(in); 209 name = in.getName();
210 if (name == null) { 210 }
211 name = in.getName(); 211 return new MethodEntry(translateEntry(in.getClassEntry()), name, translateSignature(in.getSignature()));
212 } 212 }
213 return new MethodEntry(translateEntry(in.getClassEntry()), name, translateSignature(in.getSignature())); 213
214 } 214 public ConstructorEntry translateEntry(ConstructorEntry in) {
215 215 if (in.isStatic()) {
216 public ConstructorEntry translateEntry(ConstructorEntry in) { 216 return new ConstructorEntry(translateEntry(in.getClassEntry()));
217 if (in.isStatic()) { 217 } else {
218 return new ConstructorEntry(translateEntry(in.getClassEntry())); 218 return new ConstructorEntry(translateEntry(in.getClassEntry()), translateSignature(in.getSignature()));
219 } else { 219 }
220 return new ConstructorEntry(translateEntry(in.getClassEntry()), translateSignature(in.getSignature())); 220 }
221 } 221
222 } 222 public BehaviorEntry translateEntry(BehaviorEntry in) {
223 223 if (in instanceof MethodEntry) {
224 public BehaviorEntry translateEntry(BehaviorEntry in) { 224 return translateEntry((MethodEntry) in);
225 if (in instanceof MethodEntry) { 225 } else if (in instanceof ConstructorEntry) {
226 return translateEntry((MethodEntry) in); 226 return translateEntry((ConstructorEntry) in);
227 } else if (in instanceof ConstructorEntry) { 227 }
228 return translateEntry((ConstructorEntry) in); 228 throw new Error("Wrong entry type!");
229 } 229 }
230 throw new Error("Wrong entry type!"); 230
231 } 231 // TODO: support not identical behavior (specific to constructor)
232 232 public String translate(ArgumentEntry in) {
233 // TODO: support not identical behavior (specific to constructor) 233 String classTranslate = translateArgument(in);
234 public String translate(ArgumentEntry in) 234
235 { 235 // Not found in this class
236 String classTranslate = translateArgument(in); 236 if (classTranslate == null) {
237 237 List<ClassEntry> ancestry = this.index.getAncestry(in.getClassEntry());
238 // Not found in this class 238
239 if (classTranslate == null) 239 // Check in mother class for the arg
240 { 240 for (ClassEntry entry : ancestry) {
241 List<ClassEntry> ancestry = this.index.getAncestry(in.getClassEntry()); 241 ArgumentEntry motherArg = in.cloneToNewClass(entry);
242 242 if (this.index.entryExists(motherArg)) {
243 // Check in mother class for the arg 243 String result = translateArgument(motherArg);
244 for (ClassEntry entry : ancestry) 244 if (result != null)
245 { 245 return result;
246 ArgumentEntry motherArg = in.cloneToNewClass(entry); 246 }
247 if (this.index.entryExists(motherArg)) 247 }
248 { 248 }
249 String result = translateArgument(motherArg); 249 return classTranslate;
250 if (result != null) 250 }
251 return result; 251
252 } 252 public String translateArgument(ArgumentEntry in) {
253 } 253 // look for identical behavior in superclasses
254 } 254 ClassEntry entry = in.getClassEntry();
255 return classTranslate; 255
256 } 256 if (entry != null) {
257 257 // look for the class
258 public String translateArgument(ArgumentEntry in) { 258 ClassMapping classMapping = findClassMapping(entry);
259 // look for identical behavior in superclasses 259 if (classMapping != null) {
260 ClassEntry entry = in.getClassEntry(); 260
261 261 // look for the method
262 if (entry != null) 262 MethodMapping methodMapping = this.direction.choose(
263 { 263 classMapping.getMethodByObf(in.getMethodName(), in.getMethodSignature()),
264 // look for the class 264 classMapping.getMethodByDeobf(in.getMethodName(), translateSignature(in.getMethodSignature()))
265 ClassMapping classMapping = findClassMapping(entry); 265 );
266 if (classMapping != null) { 266 if (methodMapping != null) {
267 267 return this.direction.choose(
268 // look for the method 268 methodMapping.getDeobfArgumentName(in.getIndex()),
269 MethodMapping methodMapping = this.direction.choose( 269 methodMapping.getObfArgumentName(in.getIndex())
270 classMapping.getMethodByObf(in.getMethodName(), in.getMethodSignature()), 270 );
271 classMapping.getMethodByDeobf(in.getMethodName(), translateSignature(in.getMethodSignature())) 271 }
272 ); 272 }
273 if (methodMapping != null) { 273 }
274 return this.direction.choose( 274 return null;
275 methodMapping.getDeobfArgumentName(in.getIndex()), 275 }
276 methodMapping.getObfArgumentName(in.getIndex()) 276
277 ); 277 public ArgumentEntry translateEntry(ArgumentEntry in) {
278 } 278 String name = translate(in);
279 } 279 if (name == null) {
280 } 280 name = in.getName();
281 return null; 281 }
282 } 282 return new ArgumentEntry(translateEntry(in.getBehaviorEntry()), in.getIndex(), name);
283 283 }
284 public ArgumentEntry translateEntry(ArgumentEntry in) { 284
285 String name = translate(in); 285 public Type translateType(Type type) {
286 if (name == null) { 286 return new Type(type, this.classNameReplacer);
287 name = in.getName(); 287 }
288 } 288
289 return new ArgumentEntry(translateEntry(in.getBehaviorEntry()), in.getIndex(), name); 289 public Signature translateSignature(Signature signature) {
290 } 290 return new Signature(signature, this.classNameReplacer);
291 291 }
292 public Type translateType(Type type) { 292
293 return new Type(type, this.classNameReplacer); 293 private ClassMapping findClassMapping(ClassEntry in) {
294 } 294 List<ClassMapping> mappingChain = getClassMappingChain(in);
295 295 return mappingChain.get(mappingChain.size() - 1);
296 public Signature translateSignature(Signature signature) { 296 }
297 return new Signature(signature, this.classNameReplacer); 297
298 } 298 private List<ClassMapping> getClassMappingChain(ClassEntry in) {
299 299
300 private ClassMapping findClassMapping(ClassEntry in) { 300 // get a list of all the classes in the hierarchy
301 List<ClassMapping> mappingChain = getClassMappingChain(in); 301 String[] parts = in.getName().split("\\$");
302 return mappingChain.get(mappingChain.size() - 1); 302 List<ClassMapping> mappingsChain = Lists.newArrayList();
303 } 303
304 304 // get mappings for the outer class
305 private List<ClassMapping> getClassMappingChain(ClassEntry in) { 305 ClassMapping outerClassMapping = this.classes.get(parts[0]);
306 306 mappingsChain.add(outerClassMapping);
307 // get a list of all the classes in the hierarchy 307
308 String[] parts = in.getName().split("\\$"); 308 for (int i = 1; i < parts.length; i++) {
309 List<ClassMapping> mappingsChain = Lists.newArrayList(); 309
310 310 // get mappings for the inner class
311 // get mappings for the outer class 311 ClassMapping innerClassMapping = null;
312 ClassMapping outerClassMapping = this.classes.get(parts[0]); 312 if (outerClassMapping != null) {
313 mappingsChain.add(outerClassMapping); 313 innerClassMapping = this.direction.choose(
314 314 outerClassMapping.getInnerClassByObfSimple(parts[i]),
315 for (int i = 1; i < parts.length; i++) { 315 outerClassMapping.getInnerClassByDeobfThenObfSimple(parts[i])
316 316 );
317 // get mappings for the inner class 317 }
318 ClassMapping innerClassMapping = null; 318 mappingsChain.add(innerClassMapping);
319 if (outerClassMapping != null) { 319 outerClassMapping = innerClassMapping;
320 innerClassMapping = this.direction.choose( 320 }
321 outerClassMapping.getInnerClassByObfSimple(parts[i]), 321
322 outerClassMapping.getInnerClassByDeobfThenObfSimple(parts[i]) 322 assert (mappingsChain.size() == parts.length);
323 ); 323 return mappingsChain;
324 } 324 }
325 mappingsChain.add(innerClassMapping); 325
326 outerClassMapping = innerClassMapping; 326 public Mappings.EntryModifier getModifier(Entry entry) {
327 } 327 ClassMapping classMapping = findClassMapping(entry.getClassEntry());
328 328 if (classMapping != null && !entry.getName().equals("<clinit>")) {
329 assert (mappingsChain.size() == parts.length); 329 if (entry instanceof ClassEntry)
330 return mappingsChain; 330 return classMapping.getModifier();
331 } 331 else if (entry instanceof FieldEntry) {
332 332 FieldMapping fieldMapping = classMapping.getFieldByObf(entry.getName(), ((FieldEntry) entry).getType());
333 public Mappings.EntryModifier getModifier(Entry entry) 333 return fieldMapping != null ? fieldMapping.getModifier() : Mappings.EntryModifier.UNCHANGED;
334 { 334 } else if (entry instanceof BehaviorEntry) {
335 ClassMapping classMapping = findClassMapping(entry.getClassEntry()); 335 MethodMapping methodMapping = classMapping.getMethodByObf(entry.getName(), ((BehaviorEntry) entry).getSignature());
336 if (classMapping != null && !entry.getName().equals("<clinit>")) 336 return methodMapping != null ? methodMapping.getModifier() : Mappings.EntryModifier.UNCHANGED;
337 { 337 } else
338 if (entry instanceof ClassEntry) 338 throw new Error("Unknown entry type: " + entry.getClass().getName());
339 return classMapping.getModifier(); 339 }
340 else if (entry instanceof FieldEntry) 340 return Mappings.EntryModifier.UNCHANGED;
341 { 341 }
342 FieldMapping fieldMapping = classMapping.getFieldByObf(entry.getName(), ((FieldEntry) entry).getType());
343 return fieldMapping != null ? fieldMapping.getModifier() : Mappings.EntryModifier.UNCHANGED;
344 }
345 else if (entry instanceof BehaviorEntry)
346 {
347 MethodMapping methodMapping = classMapping.getMethodByObf(entry.getName(), ((BehaviorEntry) entry).getSignature());
348 return methodMapping != null ? methodMapping.getModifier() : Mappings.EntryModifier.UNCHANGED;
349 }
350 else
351 throw new Error("Unknown entry type: " + entry.getClass().getName());
352 }
353 return Mappings.EntryModifier.UNCHANGED;
354 }
355} 342}
diff --git a/src/main/java/cuchaz/enigma/mapping/Type.java b/src/main/java/cuchaz/enigma/mapping/Type.java
index 8136e13b..609bd64e 100644
--- a/src/main/java/cuchaz/enigma/mapping/Type.java
+++ b/src/main/java/cuchaz/enigma/mapping/Type.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13import com.google.common.collect.Maps; 14import com.google.common.collect.Maps;
@@ -16,219 +17,219 @@ import java.util.Map;
16 17
17public class Type { 18public class Type {
18 19
19 public enum Primitive { 20 protected String name;
20 Byte('B'), 21
21 Character('C'), 22 public Type(String name) {
22 Short('S'), 23
23 Integer('I'), 24 // don't deal with generics
24 Long('J'), 25 // this is just for raw jvm types
25 Float('F'), 26 if (name.charAt(0) == 'T' || name.indexOf('<') >= 0 || name.indexOf('>') >= 0) {
26 Double('D'), 27 throw new IllegalArgumentException("don't use with generic types or templates: " + name);
27 Boolean('Z'); 28 }
28 29
29 private static final Map<Character, Primitive> lookup; 30 this.name = name;
30 31 }
31 static { 32
32 lookup = Maps.newTreeMap(); 33 public Type(Type other, ClassNameReplacer replacer) {
33 for (Primitive val : values()) { 34 this.name = other.name;
34 lookup.put(val.getCode(), val); 35 if (other.isClass()) {
35 } 36 String replacedName = replacer.replace(other.getClassEntry().getClassName());
36 } 37 if (replacedName != null) {
37 38 this.name = "L" + replacedName + ";";
38 public static Primitive get(char code) { 39 }
39 return lookup.get(code); 40 } else if (other.isArray() && other.hasClass()) {
40 } 41 String replacedName = replacer.replace(other.getClassEntry().getClassName());
41 42 if (replacedName != null) {
42 private char code; 43 this.name = Type.getArrayPrefix(other.getArrayDimension()) + "L" + replacedName + ";";
43 44 }
44 Primitive(char code) { 45 }
45 this.code = code; 46 }
46 } 47
47 48 public static String parseFirst(String in) {
48 public char getCode() { 49
49 return this.code; 50 if (in == null || in.length() <= 0) {
50 } 51 throw new IllegalArgumentException("No type to parse, input is empty!");
51 } 52 }
52 53
53 public static String parseFirst(String in) { 54 // read one type from the input
54 55
55 if (in == null || in.length() <= 0) { 56 char c = in.charAt(0);
56 throw new IllegalArgumentException("No type to parse, input is empty!"); 57
57 } 58 // first check for void
58 59 if (c == 'V') {
59 // read one type from the input 60 return "V";
60 61 }
61 char c = in.charAt(0); 62
62 63 // then check for primitives
63 // first check for void 64 Primitive primitive = Primitive.get(c);
64 if (c == 'V') { 65 if (primitive != null) {
65 return "V"; 66 return in.substring(0, 1);
66 } 67 }
67 68
68 // then check for primitives 69 // then check for classes
69 Primitive primitive = Primitive.get(c); 70 if (c == 'L') {
70 if (primitive != null) { 71 return readClass(in);
71 return in.substring(0, 1); 72 }
72 } 73
73 74 // then check for templates
74 // then check for classes 75 if (c == 'T') {
75 if (c == 'L') { 76 return readClass(in);
76 return readClass(in); 77 }
77 } 78
78 79 // then check for arrays
79 // then check for templates 80 int dim = countArrayDimension(in);
80 if (c == 'T') { 81 if (dim > 0) {
81 return readClass(in); 82 String arrayType = Type.parseFirst(in.substring(dim));
82 } 83 return in.substring(0, dim + arrayType.length());
83 84 }
84 // then check for arrays 85
85 int dim = countArrayDimension(in); 86 throw new IllegalArgumentException("don't know how to parse: " + in);
86 if (dim > 0) { 87 }
87 String arrayType = Type.parseFirst(in.substring(dim)); 88
88 return in.substring(0, dim + arrayType.length()); 89 private static String getArrayPrefix(int dimension) {
89 } 90 StringBuilder buf = new StringBuilder();
90 91 for (int i = 0; i < dimension; i++) {
91 throw new IllegalArgumentException("don't know how to parse: " + in); 92 buf.append("[");
92 } 93 }
93 94 return buf.toString();
94 protected String name; 95 }
95 96
96 public Type(String name) { 97 private static int countArrayDimension(String in) {
97 98 int i = 0;
98 // don't deal with generics 99 while (i < in.length() && in.charAt(i) == '[')
99 // this is just for raw jvm types 100 i++;
100 if (name.charAt(0) == 'T' || name.indexOf('<') >= 0 || name.indexOf('>') >= 0) { 101 return i;
101 throw new IllegalArgumentException("don't use with generic types or templates: " + name); 102 }
102 } 103
103 104 private static String readClass(String in) {
104 this.name = name; 105 // read all the characters in the buffer until we hit a ';'
105 } 106 // include the parameters too
106 107 StringBuilder buf = new StringBuilder();
107 public Type(Type other, ClassNameReplacer replacer) { 108 int depth = 0;
108 this.name = other.name; 109 for (int i = 0; i < in.length(); i++) {
109 if (other.isClass()) { 110 char c = in.charAt(i);
110 String replacedName = replacer.replace(other.getClassEntry().getClassName()); 111 buf.append(c);
111 if (replacedName != null) { 112
112 this.name = "L" + replacedName + ";"; 113 if (c == '<') {
113 } 114 depth++;
114 } else if (other.isArray() && other.hasClass()) { 115 } else if (c == '>') {
115 String replacedName = replacer.replace(other.getClassEntry().getClassName()); 116 depth--;
116 if (replacedName != null) { 117 } else if (depth == 0 && c == ';') {
117 this.name = Type.getArrayPrefix(other.getArrayDimension()) + "L" + replacedName + ";"; 118 return buf.toString();
118 } 119 }
119 } 120 }
120 } 121 return null;
121 122 }
122 @Override 123
123 public String toString() { 124 @Override
124 return this.name; 125 public String toString() {
125 } 126 return this.name;
126 127 }
127 public boolean isVoid() { 128
128 return this.name.length() == 1 && this.name.charAt(0) == 'V'; 129 public boolean isVoid() {
129 } 130 return this.name.length() == 1 && this.name.charAt(0) == 'V';
130 131 }
131 public boolean isPrimitive() { 132
132 return this.name.length() == 1 && Primitive.get(this.name.charAt(0)) != null; 133 public boolean isPrimitive() {
133 } 134 return this.name.length() == 1 && Primitive.get(this.name.charAt(0)) != null;
134 135 }
135 public Primitive getPrimitive() { 136
136 if (!isPrimitive()) { 137 public Primitive getPrimitive() {
137 throw new IllegalStateException("not a primitive"); 138 if (!isPrimitive()) {
138 } 139 throw new IllegalStateException("not a primitive");
139 return Primitive.get(this.name.charAt(0)); 140 }
140 } 141 return Primitive.get(this.name.charAt(0));
141 142 }
142 public boolean isClass() { 143
143 return this.name.charAt(0) == 'L' && this.name.charAt(this.name.length() - 1) == ';'; 144 public boolean isClass() {
144 } 145 return this.name.charAt(0) == 'L' && this.name.charAt(this.name.length() - 1) == ';';
145 146 }
146 public ClassEntry getClassEntry() { 147
147 if (isClass()) { 148 public ClassEntry getClassEntry() {
148 String name = this.name.substring(1, this.name.length() - 1); 149 if (isClass()) {
149 150 String name = this.name.substring(1, this.name.length() - 1);
150 int pos = name.indexOf('<'); 151
151 if (pos >= 0) { 152 int pos = name.indexOf('<');
152 // remove the parameters from the class name 153 if (pos >= 0) {
153 name = name.substring(0, pos); 154 // remove the parameters from the class name
154 } 155 name = name.substring(0, pos);
155 156 }
156 return new ClassEntry(name); 157
157 158 return new ClassEntry(name);
158 } else if (isArray() && getArrayType().isClass()) { 159
159 return getArrayType().getClassEntry(); 160 } else if (isArray() && getArrayType().isClass()) {
160 } else { 161 return getArrayType().getClassEntry();
161 throw new IllegalStateException("type doesn't have a class"); 162 } else {
162 } 163 throw new IllegalStateException("type doesn't have a class");
163 } 164 }
164 165 }
165 public boolean isArray() { 166
166 return this.name.charAt(0) == '['; 167 public boolean isArray() {
167 } 168 return this.name.charAt(0) == '[';
168 169 }
169 public int getArrayDimension() { 170
170 if (!isArray()) { 171 public int getArrayDimension() {
171 throw new IllegalStateException("not an array"); 172 if (!isArray()) {
172 } 173 throw new IllegalStateException("not an array");
173 return countArrayDimension(this.name); 174 }
174 } 175 return countArrayDimension(this.name);
175 176 }
176 public Type getArrayType() { 177
177 if (!isArray()) { 178 public Type getArrayType() {
178 throw new IllegalStateException("not an array"); 179 if (!isArray()) {
179 } 180 throw new IllegalStateException("not an array");
180 return new Type(this.name.substring(getArrayDimension(), this.name.length())); 181 }
181 } 182 return new Type(this.name.substring(getArrayDimension(), this.name.length()));
182 183 }
183 private static String getArrayPrefix(int dimension) { 184
184 StringBuilder buf = new StringBuilder(); 185 public boolean hasClass() {
185 for (int i = 0; i < dimension; i++) { 186 return isClass() || (isArray() && getArrayType().hasClass());
186 buf.append("["); 187 }
187 } 188
188 return buf.toString(); 189 @Override
189 } 190 public boolean equals(Object other) {
190 191 return other instanceof Type && equals((Type) other);
191 public boolean hasClass() { 192 }
192 return isClass() || (isArray() && getArrayType().hasClass()); 193
193 } 194 public boolean equals(Type other) {
194 195 return this.name.equals(other.name);
195 @Override 196 }
196 public boolean equals(Object other) { 197
197 return other instanceof Type && equals((Type) other); 198 public int hashCode() {
198 } 199 return this.name.hashCode();
199 200 }
200 public boolean equals(Type other) { 201
201 return this.name.equals(other.name); 202 public enum Primitive {
202 } 203 Byte('B'),
203 204 Character('C'),
204 public int hashCode() { 205 Short('S'),
205 return this.name.hashCode(); 206 Integer('I'),
206 } 207 Long('J'),
207 208 Float('F'),
208 private static int countArrayDimension(String in) { 209 Double('D'),
209 int i = 0; 210 Boolean('Z');
210 while (i < in.length() && in.charAt(i) == '[') 211
211 i++; 212 private static final Map<Character, Primitive> lookup;
212 return i; 213
213 } 214 static {
214 215 lookup = Maps.newTreeMap();
215 private static String readClass(String in) { 216 for (Primitive val : values()) {
216 // read all the characters in the buffer until we hit a ';' 217 lookup.put(val.getCode(), val);
217 // include the parameters too 218 }
218 StringBuilder buf = new StringBuilder(); 219 }
219 int depth = 0; 220
220 for (int i = 0; i < in.length(); i++) { 221 private char code;
221 char c = in.charAt(i); 222
222 buf.append(c); 223 Primitive(char code) {
223 224 this.code = code;
224 if (c == '<') { 225 }
225 depth++; 226
226 } else if (c == '>') { 227 public static Primitive get(char code) {
227 depth--; 228 return lookup.get(code);
228 } else if (depth == 0 && c == ';') { 229 }
229 return buf.toString(); 230
230 } 231 public char getCode() {
231 } 232 return this.code;
232 return null; 233 }
233 } 234 }
234} 235}
diff --git a/src/main/java/cuchaz/enigma/throwables/IllegalNameException.java b/src/main/java/cuchaz/enigma/throwables/IllegalNameException.java
index fa21a9e5..0155ad27 100644
--- a/src/main/java/cuchaz/enigma/throwables/IllegalNameException.java
+++ b/src/main/java/cuchaz/enigma/throwables/IllegalNameException.java
@@ -8,31 +8,32 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.throwables; 12package cuchaz.enigma.throwables;
12 13
13public class IllegalNameException extends RuntimeException { 14public class IllegalNameException extends RuntimeException {
14 15
15 private String name; 16 private String name;
16 private String reason; 17 private String reason;
17 18
18 public IllegalNameException(String name, String reason) { 19 public IllegalNameException(String name, String reason) {
19 this.name = name; 20 this.name = name;
20 this.reason = reason; 21 this.reason = reason;
21 } 22 }
22 23
23 public String getReason() { 24 public String getReason() {
24 return this.reason; 25 return this.reason;
25 } 26 }
26 27
27 @Override 28 @Override
28 public String getMessage() { 29 public String getMessage() {
29 StringBuilder buf = new StringBuilder(); 30 StringBuilder buf = new StringBuilder();
30 buf.append("Illegal name: "); 31 buf.append("Illegal name: ");
31 buf.append(this.name); 32 buf.append(this.name);
32 if (this.reason != null) { 33 if (this.reason != null) {
33 buf.append(" because "); 34 buf.append(" because ");
34 buf.append(this.reason); 35 buf.append(this.reason);
35 } 36 }
36 return buf.toString(); 37 return buf.toString();
37 } 38 }
38} 39}
diff --git a/src/main/java/cuchaz/enigma/throwables/MappingConflict.java b/src/main/java/cuchaz/enigma/throwables/MappingConflict.java
index 5924f32a..95cd4494 100644
--- a/src/main/java/cuchaz/enigma/throwables/MappingConflict.java
+++ b/src/main/java/cuchaz/enigma/throwables/MappingConflict.java
@@ -1,7 +1,7 @@
1package cuchaz.enigma.throwables; 1package cuchaz.enigma.throwables;
2 2
3public class MappingConflict extends Exception { 3public class MappingConflict extends Exception {
4 public MappingConflict(String clazz, String name, String nameExisting) { 4 public MappingConflict(String clazz, String name, String nameExisting) {
5 super(String.format("Conflicting mappings found for %s. The mapping file is %s and the second is %s", clazz, name, nameExisting)); 5 super(String.format("Conflicting mappings found for %s. The mapping file is %s and the second is %s", clazz, name, nameExisting));
6 } 6 }
7} 7}
diff --git a/src/main/java/cuchaz/enigma/throwables/MappingParseException.java b/src/main/java/cuchaz/enigma/throwables/MappingParseException.java
index d4c66734..cc5f650a 100644
--- a/src/main/java/cuchaz/enigma/throwables/MappingParseException.java
+++ b/src/main/java/cuchaz/enigma/throwables/MappingParseException.java
@@ -8,24 +8,25 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.throwables; 12package cuchaz.enigma.throwables;
12 13
13import java.io.File; 14import java.io.File;
14 15
15public class MappingParseException extends Exception { 16public class MappingParseException extends Exception {
16 17
17 private int line; 18 private int line;
18 private String message; 19 private String message;
19 private String filePath; 20 private String filePath;
20 21
21 public MappingParseException(File file, int line, String message) { 22 public MappingParseException(File file, int line, String message) {
22 this.line = line; 23 this.line = line;
23 this.message = message; 24 this.message = message;
24 filePath = file.getAbsolutePath(); 25 filePath = file.getAbsolutePath();
25 } 26 }
26 27
27 @Override 28 @Override
28 public String getMessage() { 29 public String getMessage() {
29 return "Line " + line + ": " + message + " in file " + filePath; 30 return "Line " + line + ": " + message + " in file " + filePath;
30 } 31 }
31} 32}
diff --git a/src/main/java/cuchaz/enigma/utils/ReadableToken.java b/src/main/java/cuchaz/enigma/utils/ReadableToken.java
index 008e28cf..de152fe3 100644
--- a/src/main/java/cuchaz/enigma/utils/ReadableToken.java
+++ b/src/main/java/cuchaz/enigma/utils/ReadableToken.java
@@ -8,22 +8,23 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.utils; 12package cuchaz.enigma.utils;
12 13
13public class ReadableToken { 14public class ReadableToken {
14 15
15 public int line; 16 public int line;
16 public int startColumn; 17 public int startColumn;
17 public int endColumn; 18 public int endColumn;
18 19
19 public ReadableToken(int line, int startColumn, int endColumn) { 20 public ReadableToken(int line, int startColumn, int endColumn) {
20 this.line = line; 21 this.line = line;
21 this.startColumn = startColumn; 22 this.startColumn = startColumn;
22 this.endColumn = endColumn; 23 this.endColumn = endColumn;
23 } 24 }
24 25
25 @Override 26 @Override
26 public String toString() { 27 public String toString() {
27 return "line " + line + " columns " + startColumn + "-" + endColumn; 28 return "line " + line + " columns " + startColumn + "-" + endColumn;
28 } 29 }
29} 30}
diff --git a/src/main/java/cuchaz/enigma/utils/Utils.java b/src/main/java/cuchaz/enigma/utils/Utils.java
index 474f7edf..8e502d47 100644
--- a/src/main/java/cuchaz/enigma/utils/Utils.java
+++ b/src/main/java/cuchaz/enigma/utils/Utils.java
@@ -8,12 +8,13 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.utils; 12package cuchaz.enigma.utils;
12 13
13import com.google.common.io.CharStreams; 14import com.google.common.io.CharStreams;
14 15
15import java.awt.Desktop; 16import javax.swing.*;
16import java.awt.Font; 17import java.awt.*;
17import java.awt.event.MouseEvent; 18import java.awt.event.MouseEvent;
18import java.io.IOException; 19import java.io.IOException;
19import java.io.InputStream; 20import java.io.InputStream;
@@ -23,69 +24,66 @@ import java.net.URISyntaxException;
23import java.util.Arrays; 24import java.util.Arrays;
24import java.util.List; 25import java.util.List;
25 26
26import javax.swing.*;
27
28public class Utils { 27public class Utils {
29 28
30 public static int combineHashesOrdered(Object... objs) { 29 public static int combineHashesOrdered(Object... objs) {
31 return combineHashesOrdered(Arrays.asList(objs)); 30 return combineHashesOrdered(Arrays.asList(objs));
32 } 31 }
33 32
34 public static int combineHashesOrdered(List<Object> objs) { 33 public static int combineHashesOrdered(List<Object> objs) {
35 final int prime = 67; 34 final int prime = 67;
36 int result = 1; 35 int result = 1;
37 for (Object obj : objs) { 36 for (Object obj : objs) {
38 result *= prime; 37 result *= prime;
39 if (obj != null) { 38 if (obj != null) {
40 result += obj.hashCode(); 39 result += obj.hashCode();
41 } 40 }
42 } 41 }
43 return result; 42 return result;
44 } 43 }
45 44
46 public static String readStreamToString(InputStream in) throws IOException { 45 public static String readStreamToString(InputStream in) throws IOException {
47 return CharStreams.toString(new InputStreamReader(in, "UTF-8")); 46 return CharStreams.toString(new InputStreamReader(in, "UTF-8"));
48 } 47 }
49 48
50 public static String readResourceToString(String path) throws IOException { 49 public static String readResourceToString(String path) throws IOException {
51 InputStream in = Utils.class.getResourceAsStream(path); 50 InputStream in = Utils.class.getResourceAsStream(path);
52 if (in == null) { 51 if (in == null) {
53 throw new IllegalArgumentException("Resource not found! " + path); 52 throw new IllegalArgumentException("Resource not found! " + path);
54 } 53 }
55 return readStreamToString(in); 54 return readStreamToString(in);
56 } 55 }
57 56
58 public static void openUrl(String url) { 57 public static void openUrl(String url) {
59 if (Desktop.isDesktopSupported()) { 58 if (Desktop.isDesktopSupported()) {
60 Desktop desktop = Desktop.getDesktop(); 59 Desktop desktop = Desktop.getDesktop();
61 try { 60 try {
62 desktop.browse(new URI(url)); 61 desktop.browse(new URI(url));
63 } catch (IOException ex) { 62 } catch (IOException ex) {
64 throw new Error(ex); 63 throw new Error(ex);
65 } catch (URISyntaxException ex) { 64 } catch (URISyntaxException ex) {
66 throw new IllegalArgumentException(ex); 65 throw new IllegalArgumentException(ex);
67 } 66 }
68 } 67 }
69 } 68 }
70 69
71 public static JLabel unboldLabel(JLabel label) { 70 public static JLabel unboldLabel(JLabel label) {
72 Font font = label.getFont(); 71 Font font = label.getFont();
73 label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD)); 72 label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD));
74 return label; 73 return label;
75 } 74 }
76 75
77 public static void showToolTipNow(JComponent component) { 76 public static void showToolTipNow(JComponent component) {
78 // HACKHACK: trick the tooltip manager into showing the tooltip right now 77 // HACKHACK: trick the tooltip manager into showing the tooltip right now
79 ToolTipManager manager = ToolTipManager.sharedInstance(); 78 ToolTipManager manager = ToolTipManager.sharedInstance();
80 int oldDelay = manager.getInitialDelay(); 79 int oldDelay = manager.getInitialDelay();
81 manager.setInitialDelay(0); 80 manager.setInitialDelay(0);
82 manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false)); 81 manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false));
83 manager.setInitialDelay(oldDelay); 82 manager.setInitialDelay(oldDelay);
84 } 83 }
85 84
86 public static boolean getSystemPropertyAsBoolean(String property, boolean defValue) 85 public static boolean getSystemPropertyAsBoolean(String property, boolean defValue) {
87 { 86 String value = System.getProperty(property);
88 String value = System.getProperty(property); 87 return value == null ? defValue : Boolean.parseBoolean(value);
89 return value == null ? defValue : Boolean.parseBoolean(value); 88 }
90 }
91} 89}
diff --git a/src/test/java/cuchaz/enigma/TestDeobfed.java b/src/test/java/cuchaz/enigma/TestDeobfed.java
index 76a3d3b5..e6c1b746 100644
--- a/src/test/java/cuchaz/enigma/TestDeobfed.java
+++ b/src/test/java/cuchaz/enigma/TestDeobfed.java
@@ -4,38 +4,36 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma;
12
13
14import static cuchaz.enigma.TestEntryFactory.*;
15import static org.hamcrest.MatcherAssert.*;
16import static org.hamcrest.Matchers.*;
17 11
18import java.util.jar.JarFile; 12package cuchaz.enigma;
19 13
14import cuchaz.enigma.analysis.JarIndex;
20import org.junit.BeforeClass; 15import org.junit.BeforeClass;
21import org.junit.Test; 16import org.junit.Test;
22 17
23import cuchaz.enigma.analysis.JarIndex; 18import java.util.jar.JarFile;
24 19
20import static cuchaz.enigma.TestEntryFactory.newClass;
21import static org.hamcrest.MatcherAssert.assertThat;
22import static org.hamcrest.Matchers.containsInAnyOrder;
25 23
26public class TestDeobfed { 24public class TestDeobfed {
27 25
28 private static JarFile jar; 26 private static JarFile jar;
29 private static JarIndex index; 27 private static JarIndex index;
30 28
31 @BeforeClass 29 @BeforeClass
32 public static void beforeClass() 30 public static void beforeClass()
33 throws Exception { 31 throws Exception {
34 jar = new JarFile("build/test-deobf/translation.jar"); 32 jar = new JarFile("build/test-deobf/translation.jar");
35 index = new JarIndex(); 33 index = new JarIndex();
36 index.indexJar(jar, true); 34 index.indexJar(jar, true);
37 } 35 }
38 36
39 @Test 37 @Test
40 public void obfEntries() { 38 public void obfEntries() {
41 assertThat(index.getObfClassEntries(), containsInAnyOrder( 39 assertThat(index.getObfClassEntries(), containsInAnyOrder(
@@ -64,10 +62,10 @@ public class TestDeobfed {
64 newClass("i$b") 62 newClass("i$b")
65 )); 63 ));
66 } 64 }
67 65
68 @Test 66 @Test
69 public void decompile() 67 public void decompile()
70 throws Exception { 68 throws Exception {
71 Deobfuscator deobfuscator = new Deobfuscator(jar); 69 Deobfuscator deobfuscator = new Deobfuscator(jar);
72 deobfuscator.getSourceTree("a"); 70 deobfuscator.getSourceTree("a");
73 deobfuscator.getSourceTree("b"); 71 deobfuscator.getSourceTree("b");
diff --git a/src/test/java/cuchaz/enigma/TestDeobfuscator.java b/src/test/java/cuchaz/enigma/TestDeobfuscator.java
index 8c97ff34..62a52861 100644
--- a/src/test/java/cuchaz/enigma/TestDeobfuscator.java
+++ b/src/test/java/cuchaz/enigma/TestDeobfuscator.java
@@ -4,40 +4,39 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma; 12package cuchaz.enigma;
12 13
13import static org.junit.Assert.*; 14import com.google.common.collect.Lists;
15import cuchaz.enigma.mapping.ClassEntry;
16import org.junit.Test;
14 17
15import java.io.IOException; 18import java.io.IOException;
16import java.util.List; 19import java.util.List;
17import java.util.jar.JarFile; 20import java.util.jar.JarFile;
18 21
19import org.junit.Test; 22import static org.junit.Assert.assertEquals;
20
21import com.google.common.collect.Lists;
22
23import cuchaz.enigma.mapping.ClassEntry;
24 23
25public class TestDeobfuscator { 24public class TestDeobfuscator {
26 25
27 private Deobfuscator getDeobfuscator() 26 private Deobfuscator getDeobfuscator()
28 throws IOException { 27 throws IOException {
29 return new Deobfuscator(new JarFile("build/test-obf/loneClass.jar")); 28 return new Deobfuscator(new JarFile("build/test-obf/loneClass.jar"));
30 } 29 }
31 30
32 @Test 31 @Test
33 public void loadJar() 32 public void loadJar()
34 throws Exception { 33 throws Exception {
35 getDeobfuscator(); 34 getDeobfuscator();
36 } 35 }
37 36
38 @Test 37 @Test
39 public void getClasses() 38 public void getClasses()
40 throws Exception { 39 throws Exception {
41 Deobfuscator deobfuscator = getDeobfuscator(); 40 Deobfuscator deobfuscator = getDeobfuscator();
42 List<ClassEntry> obfClasses = Lists.newArrayList(); 41 List<ClassEntry> obfClasses = Lists.newArrayList();
43 List<ClassEntry> deobfClasses = Lists.newArrayList(); 42 List<ClassEntry> deobfClasses = Lists.newArrayList();
@@ -47,10 +46,10 @@ public class TestDeobfuscator {
47 assertEquals(1, deobfClasses.size()); 46 assertEquals(1, deobfClasses.size());
48 assertEquals("cuchaz/enigma/inputs/Keep", deobfClasses.get(0).getName()); 47 assertEquals("cuchaz/enigma/inputs/Keep", deobfClasses.get(0).getName());
49 } 48 }
50 49
51 @Test 50 @Test
52 public void decompileClass() 51 public void decompileClass()
53 throws Exception { 52 throws Exception {
54 Deobfuscator deobfuscator = getDeobfuscator(); 53 Deobfuscator deobfuscator = getDeobfuscator();
55 deobfuscator.getSource(deobfuscator.getSourceTree("a")); 54 deobfuscator.getSource(deobfuscator.getSourceTree("a"));
56 } 55 }
diff --git a/src/test/java/cuchaz/enigma/TestEntryFactory.java b/src/test/java/cuchaz/enigma/TestEntryFactory.java
index 4aa773b6..1c527f53 100644
--- a/src/test/java/cuchaz/enigma/TestEntryFactory.java
+++ b/src/test/java/cuchaz/enigma/TestEntryFactory.java
@@ -4,64 +4,59 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma; 12package cuchaz.enigma;
12 13
13import cuchaz.enigma.analysis.EntryReference; 14import cuchaz.enigma.analysis.EntryReference;
14import cuchaz.enigma.mapping.BehaviorEntry; 15import cuchaz.enigma.mapping.*;
15import cuchaz.enigma.mapping.ClassEntry;
16import cuchaz.enigma.mapping.ConstructorEntry;
17import cuchaz.enigma.mapping.FieldEntry;
18import cuchaz.enigma.mapping.MethodEntry;
19import cuchaz.enigma.mapping.Signature;
20import cuchaz.enigma.mapping.Type;
21 16
22public class TestEntryFactory { 17public class TestEntryFactory {
23 18
24 public static ClassEntry newClass(String name) { 19 public static ClassEntry newClass(String name) {
25 return new ClassEntry(name); 20 return new ClassEntry(name);
26 } 21 }
27 22
28 public static FieldEntry newField(String className, String fieldName, String fieldType) { 23 public static FieldEntry newField(String className, String fieldName, String fieldType) {
29 return newField(newClass(className), fieldName, fieldType); 24 return newField(newClass(className), fieldName, fieldType);
30 } 25 }
31 26
32 public static FieldEntry newField(ClassEntry classEntry, String fieldName, String fieldType) { 27 public static FieldEntry newField(ClassEntry classEntry, String fieldName, String fieldType) {
33 return new FieldEntry(classEntry, fieldName, new Type(fieldType)); 28 return new FieldEntry(classEntry, fieldName, new Type(fieldType));
34 } 29 }
35 30
36 public static MethodEntry newMethod(String className, String methodName, String methodSignature) { 31 public static MethodEntry newMethod(String className, String methodName, String methodSignature) {
37 return newMethod(newClass(className), methodName, methodSignature); 32 return newMethod(newClass(className), methodName, methodSignature);
38 } 33 }
39 34
40 public static MethodEntry newMethod(ClassEntry classEntry, String methodName, String methodSignature) { 35 public static MethodEntry newMethod(ClassEntry classEntry, String methodName, String methodSignature) {
41 return new MethodEntry(classEntry, methodName, new Signature(methodSignature)); 36 return new MethodEntry(classEntry, methodName, new Signature(methodSignature));
42 } 37 }
43 38
44 public static ConstructorEntry newConstructor(String className, String signature) { 39 public static ConstructorEntry newConstructor(String className, String signature) {
45 return newConstructor(newClass(className), signature); 40 return newConstructor(newClass(className), signature);
46 } 41 }
47 42
48 public static ConstructorEntry newConstructor(ClassEntry classEntry, String signature) { 43 public static ConstructorEntry newConstructor(ClassEntry classEntry, String signature) {
49 return new ConstructorEntry(classEntry, new Signature(signature)); 44 return new ConstructorEntry(classEntry, new Signature(signature));
50 } 45 }
51 46
52 public static EntryReference<FieldEntry,BehaviorEntry> newFieldReferenceByMethod(FieldEntry fieldEntry, String callerClassName, String callerName, String callerSignature) { 47 public static EntryReference<FieldEntry, BehaviorEntry> newFieldReferenceByMethod(FieldEntry fieldEntry, String callerClassName, String callerName, String callerSignature) {
53 return new EntryReference<FieldEntry,BehaviorEntry>(fieldEntry, "", newMethod(callerClassName, callerName, callerSignature)); 48 return new EntryReference<FieldEntry, BehaviorEntry>(fieldEntry, "", newMethod(callerClassName, callerName, callerSignature));
54 } 49 }
55 50
56 public static EntryReference<FieldEntry,BehaviorEntry> newFieldReferenceByConstructor(FieldEntry fieldEntry, String callerClassName, String callerSignature) { 51 public static EntryReference<FieldEntry, BehaviorEntry> newFieldReferenceByConstructor(FieldEntry fieldEntry, String callerClassName, String callerSignature) {
57 return new EntryReference<FieldEntry,BehaviorEntry>(fieldEntry, "", newConstructor(callerClassName, callerSignature)); 52 return new EntryReference<FieldEntry, BehaviorEntry>(fieldEntry, "", newConstructor(callerClassName, callerSignature));
58 } 53 }
59 54
60 public static EntryReference<BehaviorEntry,BehaviorEntry> newBehaviorReferenceByMethod(BehaviorEntry behaviorEntry, String callerClassName, String callerName, String callerSignature) { 55 public static EntryReference<BehaviorEntry, BehaviorEntry> newBehaviorReferenceByMethod(BehaviorEntry behaviorEntry, String callerClassName, String callerName, String callerSignature) {
61 return new EntryReference<BehaviorEntry,BehaviorEntry>(behaviorEntry, "", newMethod(callerClassName, callerName, callerSignature)); 56 return new EntryReference<BehaviorEntry, BehaviorEntry>(behaviorEntry, "", newMethod(callerClassName, callerName, callerSignature));
62 } 57 }
63 58
64 public static EntryReference<BehaviorEntry,BehaviorEntry> newBehaviorReferenceByConstructor(BehaviorEntry behaviorEntry, String callerClassName, String callerSignature) { 59 public static EntryReference<BehaviorEntry, BehaviorEntry> newBehaviorReferenceByConstructor(BehaviorEntry behaviorEntry, String callerClassName, String callerSignature) {
65 return new EntryReference<BehaviorEntry,BehaviorEntry>(behaviorEntry, "", newConstructor(callerClassName, callerSignature)); 60 return new EntryReference<BehaviorEntry, BehaviorEntry>(behaviorEntry, "", newConstructor(callerClassName, callerSignature));
66 } 61 }
67} 62}
diff --git a/src/test/java/cuchaz/enigma/TestInnerClasses.java b/src/test/java/cuchaz/enigma/TestInnerClasses.java
index 64a695a9..38db0df9 100644
--- a/src/test/java/cuchaz/enigma/TestInnerClasses.java
+++ b/src/test/java/cuchaz/enigma/TestInnerClasses.java
@@ -4,29 +4,28 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma;
12
13import static org.hamcrest.MatcherAssert.*;
14import static org.hamcrest.Matchers.*;
15 11
16import java.util.jar.JarFile; 12package cuchaz.enigma;
17 13
14import cuchaz.enigma.analysis.JarIndex;
15import cuchaz.enigma.mapping.ClassEntry;
18import org.junit.Test; 16import org.junit.Test;
19 17
20import static cuchaz.enigma.TestEntryFactory.*; 18import java.util.jar.JarFile;
21 19
22import cuchaz.enigma.analysis.JarIndex; 20import static cuchaz.enigma.TestEntryFactory.newClass;
23import cuchaz.enigma.mapping.ClassEntry; 21import static org.hamcrest.MatcherAssert.assertThat;
22import static org.hamcrest.Matchers.containsInAnyOrder;
23import static org.hamcrest.Matchers.empty;
24import static org.hamcrest.Matchers.is;
25import static org.hamcrest.Matchers.nullValue;
24 26
25public class TestInnerClasses { 27public class TestInnerClasses {
26 28
27 private JarIndex index;
28 private Deobfuscator deobfuscator;
29
30 private static final ClassEntry AnonymousOuter = newClass("a"); 29 private static final ClassEntry AnonymousOuter = newClass("a");
31 private static final ClassEntry AnonymousInner = newClass("a$1"); 30 private static final ClassEntry AnonymousInner = newClass("a$1");
32 private static final ClassEntry SimpleOuter = newClass("d"); 31 private static final ClassEntry SimpleOuter = newClass("d");
@@ -41,15 +40,17 @@ public class TestInnerClasses {
41 private static final ClassEntry ClassTreeLevel1 = newClass("f$a"); 40 private static final ClassEntry ClassTreeLevel1 = newClass("f$a");
42 private static final ClassEntry ClassTreeLevel2 = newClass("f$a$a"); 41 private static final ClassEntry ClassTreeLevel2 = newClass("f$a$a");
43 private static final ClassEntry ClassTreeLevel3 = newClass("f$a$a$a"); 42 private static final ClassEntry ClassTreeLevel3 = newClass("f$a$a$a");
44 43 private JarIndex index;
44 private Deobfuscator deobfuscator;
45
45 public TestInnerClasses() 46 public TestInnerClasses()
46 throws Exception { 47 throws Exception {
47 index = new JarIndex(); 48 index = new JarIndex();
48 JarFile jar = new JarFile("build/test-obf/innerClasses.jar"); 49 JarFile jar = new JarFile("build/test-obf/innerClasses.jar");
49 index.indexJar(jar, true); 50 index.indexJar(jar, true);
50 deobfuscator = new Deobfuscator(jar); 51 deobfuscator = new Deobfuscator(jar);
51 } 52 }
52 53
53 @Test 54 @Test
54 public void simple() { 55 public void simple() {
55 assertThat(index.getOuterClass(SimpleInner), is(SimpleOuter)); 56 assertThat(index.getOuterClass(SimpleInner), is(SimpleOuter));
@@ -57,7 +58,7 @@ public class TestInnerClasses {
57 assertThat(index.isAnonymousClass(SimpleInner), is(false)); 58 assertThat(index.isAnonymousClass(SimpleInner), is(false));
58 decompile(SimpleOuter); 59 decompile(SimpleOuter);
59 } 60 }
60 61
61 @Test 62 @Test
62 public void anonymous() { 63 public void anonymous() {
63 assertThat(index.getOuterClass(AnonymousInner), is(AnonymousOuter)); 64 assertThat(index.getOuterClass(AnonymousInner), is(AnonymousOuter));
@@ -65,7 +66,7 @@ public class TestInnerClasses {
65 assertThat(index.isAnonymousClass(AnonymousInner), is(true)); 66 assertThat(index.isAnonymousClass(AnonymousInner), is(true));
66 decompile(AnonymousOuter); 67 decompile(AnonymousOuter);
67 } 68 }
68 69
69 @Test 70 @Test
70 public void constructorArgs() { 71 public void constructorArgs() {
71 assertThat(index.getOuterClass(ConstructorArgsInner), is(ConstructorArgsOuter)); 72 assertThat(index.getOuterClass(ConstructorArgsInner), is(ConstructorArgsOuter));
@@ -73,7 +74,7 @@ public class TestInnerClasses {
73 assertThat(index.isAnonymousClass(ConstructorArgsInner), is(false)); 74 assertThat(index.isAnonymousClass(ConstructorArgsInner), is(false));
74 decompile(ConstructorArgsOuter); 75 decompile(ConstructorArgsOuter);
75 } 76 }
76 77
77 @Test 78 @Test
78 public void anonymousWithScopeArgs() { 79 public void anonymousWithScopeArgs() {
79 assertThat(index.getOuterClass(AnonymousWithScopeArgsInner), is(AnonymousWithScopeArgsOuter)); 80 assertThat(index.getOuterClass(AnonymousWithScopeArgsInner), is(AnonymousWithScopeArgsOuter));
@@ -81,7 +82,7 @@ public class TestInnerClasses {
81 assertThat(index.isAnonymousClass(AnonymousWithScopeArgsInner), is(true)); 82 assertThat(index.isAnonymousClass(AnonymousWithScopeArgsInner), is(true));
82 decompile(AnonymousWithScopeArgsOuter); 83 decompile(AnonymousWithScopeArgsOuter);
83 } 84 }
84 85
85 @Test 86 @Test
86 public void anonymousWithOuterAccess() { 87 public void anonymousWithOuterAccess() {
87 assertThat(index.getOuterClass(AnonymousWithOuterAccessInner), is(AnonymousWithOuterAccessOuter)); 88 assertThat(index.getOuterClass(AnonymousWithOuterAccessInner), is(AnonymousWithOuterAccessOuter));
@@ -89,15 +90,15 @@ public class TestInnerClasses {
89 assertThat(index.isAnonymousClass(AnonymousWithOuterAccessInner), is(true)); 90 assertThat(index.isAnonymousClass(AnonymousWithOuterAccessInner), is(true));
90 decompile(AnonymousWithOuterAccessOuter); 91 decompile(AnonymousWithOuterAccessOuter);
91 } 92 }
92 93
93 @Test 94 @Test
94 public void classTree() { 95 public void classTree() {
95 96
96 // root level 97 // root level
97 assertThat(index.containsObfClass(ClassTreeRoot), is(true)); 98 assertThat(index.containsObfClass(ClassTreeRoot), is(true));
98 assertThat(index.getOuterClass(ClassTreeRoot), is(nullValue())); 99 assertThat(index.getOuterClass(ClassTreeRoot), is(nullValue()));
99 assertThat(index.getInnerClasses(ClassTreeRoot), containsInAnyOrder(ClassTreeLevel1)); 100 assertThat(index.getInnerClasses(ClassTreeRoot), containsInAnyOrder(ClassTreeLevel1));
100 101
101 // level 1 102 // level 1
102 ClassEntry fullClassEntry = new ClassEntry(ClassTreeRoot.getName() 103 ClassEntry fullClassEntry = new ClassEntry(ClassTreeRoot.getName()
103 + "$" + ClassTreeLevel1.getInnermostClassName() 104 + "$" + ClassTreeLevel1.getInnermostClassName()
@@ -105,7 +106,7 @@ public class TestInnerClasses {
105 assertThat(index.containsObfClass(fullClassEntry), is(true)); 106 assertThat(index.containsObfClass(fullClassEntry), is(true));
106 assertThat(index.getOuterClass(ClassTreeLevel1), is(ClassTreeRoot)); 107 assertThat(index.getOuterClass(ClassTreeLevel1), is(ClassTreeRoot));
107 assertThat(index.getInnerClasses(ClassTreeLevel1), containsInAnyOrder(ClassTreeLevel2)); 108 assertThat(index.getInnerClasses(ClassTreeLevel1), containsInAnyOrder(ClassTreeLevel2));
108 109
109 // level 2 110 // level 2
110 fullClassEntry = new ClassEntry(ClassTreeRoot.getName() 111 fullClassEntry = new ClassEntry(ClassTreeRoot.getName()
111 + "$" + ClassTreeLevel1.getInnermostClassName() 112 + "$" + ClassTreeLevel1.getInnermostClassName()
@@ -114,7 +115,7 @@ public class TestInnerClasses {
114 assertThat(index.containsObfClass(fullClassEntry), is(true)); 115 assertThat(index.containsObfClass(fullClassEntry), is(true));
115 assertThat(index.getOuterClass(ClassTreeLevel2), is(ClassTreeLevel1)); 116 assertThat(index.getOuterClass(ClassTreeLevel2), is(ClassTreeLevel1));
116 assertThat(index.getInnerClasses(ClassTreeLevel2), containsInAnyOrder(ClassTreeLevel3)); 117 assertThat(index.getInnerClasses(ClassTreeLevel2), containsInAnyOrder(ClassTreeLevel3));
117 118
118 // level 3 119 // level 3
119 fullClassEntry = new ClassEntry(ClassTreeRoot.getName() 120 fullClassEntry = new ClassEntry(ClassTreeRoot.getName()
120 + "$" + ClassTreeLevel1.getInnermostClassName() 121 + "$" + ClassTreeLevel1.getInnermostClassName()
@@ -125,7 +126,7 @@ public class TestInnerClasses {
125 assertThat(index.getOuterClass(ClassTreeLevel3), is(ClassTreeLevel2)); 126 assertThat(index.getOuterClass(ClassTreeLevel3), is(ClassTreeLevel2));
126 assertThat(index.getInnerClasses(ClassTreeLevel3), is(empty())); 127 assertThat(index.getInnerClasses(ClassTreeLevel3), is(empty()));
127 } 128 }
128 129
129 private void decompile(ClassEntry classEntry) { 130 private void decompile(ClassEntry classEntry) {
130 deobfuscator.getSourceTree(classEntry.getName()); 131 deobfuscator.getSourceTree(classEntry.getName());
131 } 132 }
diff --git a/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java b/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java
index 01d4bab6..edb859a7 100644
--- a/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java
+++ b/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java
@@ -4,62 +4,67 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma; 12package cuchaz.enigma;
12 13
13import static cuchaz.enigma.TestEntryFactory.*; 14import cuchaz.enigma.analysis.EntryReference;
14import static org.hamcrest.MatcherAssert.*; 15import cuchaz.enigma.analysis.JarIndex;
15import static org.hamcrest.Matchers.*; 16import cuchaz.enigma.mapping.BehaviorEntry;
17import cuchaz.enigma.mapping.ClassEntry;
18import org.junit.Test;
16 19
17import java.io.File; 20import java.io.File;
18import java.util.Collection; 21import java.util.Collection;
19import java.util.jar.JarFile; 22import java.util.jar.JarFile;
20 23
21import org.junit.Test; 24import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByConstructor;
22 25import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByMethod;
23import cuchaz.enigma.analysis.EntryReference; 26import static cuchaz.enigma.TestEntryFactory.newClass;
24import cuchaz.enigma.analysis.JarIndex; 27import static cuchaz.enigma.TestEntryFactory.newConstructor;
25import cuchaz.enigma.mapping.BehaviorEntry; 28import static org.hamcrest.MatcherAssert.assertThat;
26import cuchaz.enigma.mapping.ClassEntry; 29import static org.hamcrest.Matchers.containsInAnyOrder;
30import static org.hamcrest.Matchers.empty;
31import static org.hamcrest.Matchers.is;
27 32
28public class TestJarIndexConstructorReferences { 33public class TestJarIndexConstructorReferences {
29 34
30 private JarIndex index; 35 private JarIndex index;
31 36
32 private ClassEntry baseClass = newClass("a"); 37 private ClassEntry baseClass = newClass("a");
33 private ClassEntry subClass = newClass("d"); 38 private ClassEntry subClass = newClass("d");
34 private ClassEntry subsubClass = newClass("e"); 39 private ClassEntry subsubClass = newClass("e");
35 private ClassEntry defaultClass = newClass("c"); 40 private ClassEntry defaultClass = newClass("c");
36 private ClassEntry callerClass = newClass("b"); 41 private ClassEntry callerClass = newClass("b");
37 42
38 public TestJarIndexConstructorReferences() 43 public TestJarIndexConstructorReferences()
39 throws Exception { 44 throws Exception {
40 File jarFile = new File("build/test-obf/constructors.jar"); 45 File jarFile = new File("build/test-obf/constructors.jar");
41 index = new JarIndex(); 46 index = new JarIndex();
42 index.indexJar(new JarFile(jarFile), false); 47 index.indexJar(new JarFile(jarFile), false);
43 } 48 }
44 49
45 @Test 50 @Test
46 public void obfEntries() { 51 public void obfEntries() {
47 assertThat(index.getObfClassEntries(), containsInAnyOrder(newClass("cuchaz/enigma/inputs/Keep"), baseClass, 52 assertThat(index.getObfClassEntries(), containsInAnyOrder(newClass("cuchaz/enigma/inputs/Keep"), baseClass,
48 subClass, subsubClass, defaultClass, callerClass)); 53 subClass, subsubClass, defaultClass, callerClass));
49 } 54 }
50 55
51 @Test 56 @Test
52 @SuppressWarnings("unchecked") 57 @SuppressWarnings("unchecked")
53 public void baseDefault() { 58 public void baseDefault() {
54 BehaviorEntry source = newConstructor(baseClass, "()V"); 59 BehaviorEntry source = newConstructor(baseClass, "()V");
55 Collection<EntryReference<BehaviorEntry,BehaviorEntry>> references = index.getBehaviorReferences(source); 60 Collection<EntryReference<BehaviorEntry, BehaviorEntry>> references = index.getBehaviorReferences(source);
56 assertThat(references, containsInAnyOrder( 61 assertThat(references, containsInAnyOrder(
57 newBehaviorReferenceByMethod(source, callerClass.getName(), "a", "()V"), 62 newBehaviorReferenceByMethod(source, callerClass.getName(), "a", "()V"),
58 newBehaviorReferenceByConstructor(source, subClass.getName(), "()V"), 63 newBehaviorReferenceByConstructor(source, subClass.getName(), "()V"),
59 newBehaviorReferenceByConstructor(source, subClass.getName(), "(III)V") 64 newBehaviorReferenceByConstructor(source, subClass.getName(), "(III)V")
60 )); 65 ));
61 } 66 }
62 67
63 @Test 68 @Test
64 @SuppressWarnings("unchecked") 69 @SuppressWarnings("unchecked")
65 public void baseInt() { 70 public void baseInt() {
@@ -68,7 +73,7 @@ public class TestJarIndexConstructorReferences {
68 newBehaviorReferenceByMethod(source, callerClass.getName(), "b", "()V") 73 newBehaviorReferenceByMethod(source, callerClass.getName(), "b", "()V")
69 )); 74 ));
70 } 75 }
71 76
72 @Test 77 @Test
73 @SuppressWarnings("unchecked") 78 @SuppressWarnings("unchecked")
74 public void subDefault() { 79 public void subDefault() {
@@ -78,7 +83,7 @@ public class TestJarIndexConstructorReferences {
78 newBehaviorReferenceByConstructor(source, subClass.getName(), "(I)V") 83 newBehaviorReferenceByConstructor(source, subClass.getName(), "(I)V")
79 )); 84 ));
80 } 85 }
81 86
82 @Test 87 @Test
83 @SuppressWarnings("unchecked") 88 @SuppressWarnings("unchecked")
84 public void subInt() { 89 public void subInt() {
@@ -89,7 +94,7 @@ public class TestJarIndexConstructorReferences {
89 newBehaviorReferenceByConstructor(source, subsubClass.getName(), "(I)V") 94 newBehaviorReferenceByConstructor(source, subsubClass.getName(), "(I)V")
90 )); 95 ));
91 } 96 }
92 97
93 @Test 98 @Test
94 @SuppressWarnings("unchecked") 99 @SuppressWarnings("unchecked")
95 public void subIntInt() { 100 public void subIntInt() {
@@ -98,13 +103,13 @@ public class TestJarIndexConstructorReferences {
98 newBehaviorReferenceByMethod(source, callerClass.getName(), "e", "()V") 103 newBehaviorReferenceByMethod(source, callerClass.getName(), "e", "()V")
99 )); 104 ));
100 } 105 }
101 106
102 @Test 107 @Test
103 public void subIntIntInt() { 108 public void subIntIntInt() {
104 BehaviorEntry source = newConstructor(subClass, "(III)V"); 109 BehaviorEntry source = newConstructor(subClass, "(III)V");
105 assertThat(index.getBehaviorReferences(source), is(empty())); 110 assertThat(index.getBehaviorReferences(source), is(empty()));
106 } 111 }
107 112
108 @Test 113 @Test
109 @SuppressWarnings("unchecked") 114 @SuppressWarnings("unchecked")
110 public void subsubInt() { 115 public void subsubInt() {
@@ -113,7 +118,7 @@ public class TestJarIndexConstructorReferences {
113 newBehaviorReferenceByMethod(source, callerClass.getName(), "f", "()V") 118 newBehaviorReferenceByMethod(source, callerClass.getName(), "f", "()V")
114 )); 119 ));
115 } 120 }
116 121
117 @Test 122 @Test
118 @SuppressWarnings("unchecked") 123 @SuppressWarnings("unchecked")
119 public void defaultConstructable() { 124 public void defaultConstructable() {
diff --git a/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java b/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java
index 4d9c8dc1..62469780 100644
--- a/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java
+++ b/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java
@@ -4,31 +4,12 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma;
12
13import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByConstructor;
14import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByMethod;
15import static cuchaz.enigma.TestEntryFactory.newClass;
16import static cuchaz.enigma.TestEntryFactory.newConstructor;
17import static cuchaz.enigma.TestEntryFactory.newField;
18import static cuchaz.enigma.TestEntryFactory.newFieldReferenceByConstructor;
19import static cuchaz.enigma.TestEntryFactory.newFieldReferenceByMethod;
20import static cuchaz.enigma.TestEntryFactory.newMethod;
21import static org.hamcrest.MatcherAssert.assertThat;
22import static org.hamcrest.Matchers.contains;
23import static org.hamcrest.Matchers.containsInAnyOrder;
24import static org.hamcrest.Matchers.empty;
25import static org.hamcrest.Matchers.is;
26 11
27import java.util.Collection; 12package cuchaz.enigma;
28import java.util.Set;
29import java.util.jar.JarFile;
30
31import org.junit.Test;
32 13
33import cuchaz.enigma.analysis.Access; 14import cuchaz.enigma.analysis.Access;
34import cuchaz.enigma.analysis.EntryReference; 15import cuchaz.enigma.analysis.EntryReference;
@@ -38,70 +19,82 @@ import cuchaz.enigma.mapping.BehaviorEntry;
38import cuchaz.enigma.mapping.ClassEntry; 19import cuchaz.enigma.mapping.ClassEntry;
39import cuchaz.enigma.mapping.FieldEntry; 20import cuchaz.enigma.mapping.FieldEntry;
40import cuchaz.enigma.mapping.MethodEntry; 21import cuchaz.enigma.mapping.MethodEntry;
22import org.junit.Test;
23
24import java.util.Collection;
25import java.util.Set;
26import java.util.jar.JarFile;
27
28import static cuchaz.enigma.TestEntryFactory.*;
29import static org.hamcrest.MatcherAssert.assertThat;
30import static org.hamcrest.Matchers.contains;
31import static org.hamcrest.Matchers.containsInAnyOrder;
32import static org.hamcrest.Matchers.empty;
33import static org.hamcrest.Matchers.is;
41 34
42public class TestJarIndexInheritanceTree { 35public class TestJarIndexInheritanceTree {
43 36
44 private JarIndex index; 37 private JarIndex index;
45 38
46 private ClassEntry objectClass = newClass("java/lang/Object"); 39 private ClassEntry objectClass = newClass("java/lang/Object");
47 private ClassEntry baseClass = newClass("a"); 40 private ClassEntry baseClass = newClass("a");
48 private ClassEntry subClassA = newClass("b"); 41 private ClassEntry subClassA = newClass("b");
49 private ClassEntry subClassAA = newClass("d"); 42 private ClassEntry subClassAA = newClass("d");
50 private ClassEntry subClassB = newClass("c"); 43 private ClassEntry subClassB = newClass("c");
51 private FieldEntry nameField = newField(baseClass, "a", "Ljava/lang/String;"); 44 private FieldEntry nameField = newField(baseClass, "a", "Ljava/lang/String;");
52 private FieldEntry numThingsField = newField(subClassB, "a", "I"); 45 private FieldEntry numThingsField = newField(subClassB, "a", "I");
53 46
54 public TestJarIndexInheritanceTree() 47 public TestJarIndexInheritanceTree()
55 throws Exception { 48 throws Exception {
56 index = new JarIndex(); 49 index = new JarIndex();
57 index.indexJar(new JarFile("build/test-obf/inheritanceTree.jar"), false); 50 index.indexJar(new JarFile("build/test-obf/inheritanceTree.jar"), false);
58 } 51 }
59 52
60 @Test 53 @Test
61 public void obfEntries() { 54 public void obfEntries() {
62 assertThat(index.getObfClassEntries(), containsInAnyOrder( 55 assertThat(index.getObfClassEntries(), containsInAnyOrder(
63 newClass("cuchaz/enigma/inputs/Keep"), baseClass, subClassA, subClassAA, subClassB 56 newClass("cuchaz/enigma/inputs/Keep"), baseClass, subClassA, subClassAA, subClassB
64 )); 57 ));
65 } 58 }
66 59
67 @Test 60 @Test
68 public void translationIndex() { 61 public void translationIndex() {
69 62
70 TranslationIndex index = this.index.getTranslationIndex(); 63 TranslationIndex index = this.index.getTranslationIndex();
71 64
72 // base class 65 // base class
73 assertThat(index.getSuperclass(baseClass), is(objectClass)); 66 assertThat(index.getSuperclass(baseClass), is(objectClass));
74 assertThat(index.getAncestry(baseClass), contains(objectClass)); 67 assertThat(index.getAncestry(baseClass), contains(objectClass));
75 assertThat(index.getSubclass(baseClass), containsInAnyOrder(subClassA, subClassB 68 assertThat(index.getSubclass(baseClass), containsInAnyOrder(subClassA, subClassB
76 )); 69 ));
77 70
78 // subclass a 71 // subclass a
79 assertThat(index.getSuperclass(subClassA), is(baseClass)); 72 assertThat(index.getSuperclass(subClassA), is(baseClass));
80 assertThat(index.getAncestry(subClassA), contains(baseClass, objectClass)); 73 assertThat(index.getAncestry(subClassA), contains(baseClass, objectClass));
81 assertThat(index.getSubclass(subClassA), contains(subClassAA)); 74 assertThat(index.getSubclass(subClassA), contains(subClassAA));
82 75
83 // subclass aa 76 // subclass aa
84 assertThat(index.getSuperclass(subClassAA), is(subClassA)); 77 assertThat(index.getSuperclass(subClassAA), is(subClassA));
85 assertThat(index.getAncestry(subClassAA), contains(subClassA, baseClass, objectClass)); 78 assertThat(index.getAncestry(subClassAA), contains(subClassA, baseClass, objectClass));
86 assertThat(index.getSubclass(subClassAA), is(empty())); 79 assertThat(index.getSubclass(subClassAA), is(empty()));
87 80
88 // subclass b 81 // subclass b
89 assertThat(index.getSuperclass(subClassB), is(baseClass)); 82 assertThat(index.getSuperclass(subClassB), is(baseClass));
90 assertThat(index.getAncestry(subClassB), contains(baseClass, objectClass)); 83 assertThat(index.getAncestry(subClassB), contains(baseClass, objectClass));
91 assertThat(index.getSubclass(subClassB), is(empty())); 84 assertThat(index.getSubclass(subClassB), is(empty()));
92 } 85 }
93 86
94 @Test 87 @Test
95 public void access() { 88 public void access() {
96 assertThat(index.getAccess(nameField), is(Access.PRIVATE)); 89 assertThat(index.getAccess(nameField), is(Access.PRIVATE));
97 assertThat(index.getAccess(numThingsField), is(Access.PRIVATE)); 90 assertThat(index.getAccess(numThingsField), is(Access.PRIVATE));
98 } 91 }
99 92
100 @Test 93 @Test
101 public void relatedMethodImplementations() { 94 public void relatedMethodImplementations() {
102 95
103 Set<MethodEntry> entries; 96 Set<MethodEntry> entries;
104 97
105 // getName() 98 // getName()
106 entries = index.getRelatedMethodImplementations(newMethod(baseClass, "a", "()Ljava/lang/String;")); 99 entries = index.getRelatedMethodImplementations(newMethod(baseClass, "a", "()Ljava/lang/String;"));
107 assertThat(entries, containsInAnyOrder( 100 assertThat(entries, containsInAnyOrder(
@@ -113,7 +106,7 @@ public class TestJarIndexInheritanceTree {
113 newMethod(baseClass, "a", "()Ljava/lang/String;"), 106 newMethod(baseClass, "a", "()Ljava/lang/String;"),
114 newMethod(subClassAA, "a", "()Ljava/lang/String;") 107 newMethod(subClassAA, "a", "()Ljava/lang/String;")
115 )); 108 ));
116 109
117 // doBaseThings() 110 // doBaseThings()
118 entries = index.getRelatedMethodImplementations(newMethod(baseClass, "a", "()V")); 111 entries = index.getRelatedMethodImplementations(newMethod(baseClass, "a", "()V"));
119 assertThat(entries, containsInAnyOrder( 112 assertThat(entries, containsInAnyOrder(
@@ -133,24 +126,24 @@ public class TestJarIndexInheritanceTree {
133 newMethod(subClassAA, "a", "()V"), 126 newMethod(subClassAA, "a", "()V"),
134 newMethod(subClassB, "a", "()V") 127 newMethod(subClassB, "a", "()V")
135 )); 128 ));
136 129
137 // doBThings 130 // doBThings
138 entries = index.getRelatedMethodImplementations(newMethod(subClassB, "b", "()V")); 131 entries = index.getRelatedMethodImplementations(newMethod(subClassB, "b", "()V"));
139 assertThat(entries, containsInAnyOrder(newMethod(subClassB, "b", "()V"))); 132 assertThat(entries, containsInAnyOrder(newMethod(subClassB, "b", "()V")));
140 } 133 }
141 134
142 @Test 135 @Test
143 @SuppressWarnings("unchecked") 136 @SuppressWarnings("unchecked")
144 public void fieldReferences() { 137 public void fieldReferences() {
145 Collection<EntryReference<FieldEntry,BehaviorEntry>> references; 138 Collection<EntryReference<FieldEntry, BehaviorEntry>> references;
146 139
147 // name 140 // name
148 references = index.getFieldReferences(nameField); 141 references = index.getFieldReferences(nameField);
149 assertThat(references, containsInAnyOrder( 142 assertThat(references, containsInAnyOrder(
150 newFieldReferenceByConstructor(nameField, baseClass.getName(), "(Ljava/lang/String;)V"), 143 newFieldReferenceByConstructor(nameField, baseClass.getName(), "(Ljava/lang/String;)V"),
151 newFieldReferenceByMethod(nameField, baseClass.getName(), "a", "()Ljava/lang/String;") 144 newFieldReferenceByMethod(nameField, baseClass.getName(), "a", "()Ljava/lang/String;")
152 )); 145 ));
153 146
154 // numThings 147 // numThings
155 references = index.getFieldReferences(numThingsField); 148 references = index.getFieldReferences(numThingsField);
156 assertThat(references, containsInAnyOrder( 149 assertThat(references, containsInAnyOrder(
@@ -158,14 +151,14 @@ public class TestJarIndexInheritanceTree {
158 newFieldReferenceByMethod(numThingsField, subClassB.getName(), "b", "()V") 151 newFieldReferenceByMethod(numThingsField, subClassB.getName(), "b", "()V")
159 )); 152 ));
160 } 153 }
161 154
162 @Test 155 @Test
163 @SuppressWarnings("unchecked") 156 @SuppressWarnings("unchecked")
164 public void behaviorReferences() { 157 public void behaviorReferences() {
165 158
166 BehaviorEntry source; 159 BehaviorEntry source;
167 Collection<EntryReference<BehaviorEntry,BehaviorEntry>> references; 160 Collection<EntryReference<BehaviorEntry, BehaviorEntry>> references;
168 161
169 // baseClass constructor 162 // baseClass constructor
170 source = newConstructor(baseClass, "(Ljava/lang/String;)V"); 163 source = newConstructor(baseClass, "(Ljava/lang/String;)V");
171 references = index.getBehaviorReferences(source); 164 references = index.getBehaviorReferences(source);
@@ -173,14 +166,14 @@ public class TestJarIndexInheritanceTree {
173 newBehaviorReferenceByConstructor(source, subClassA.getName(), "(Ljava/lang/String;)V"), 166 newBehaviorReferenceByConstructor(source, subClassA.getName(), "(Ljava/lang/String;)V"),
174 newBehaviorReferenceByConstructor(source, subClassB.getName(), "()V") 167 newBehaviorReferenceByConstructor(source, subClassB.getName(), "()V")
175 )); 168 ));
176 169
177 // subClassA constructor 170 // subClassA constructor
178 source = newConstructor(subClassA, "(Ljava/lang/String;)V"); 171 source = newConstructor(subClassA, "(Ljava/lang/String;)V");
179 references = index.getBehaviorReferences(source); 172 references = index.getBehaviorReferences(source);
180 assertThat(references, containsInAnyOrder( 173 assertThat(references, containsInAnyOrder(
181 newBehaviorReferenceByConstructor(source, subClassAA.getName(), "()V") 174 newBehaviorReferenceByConstructor(source, subClassAA.getName(), "()V")
182 )); 175 ));
183 176
184 // baseClass.getName() 177 // baseClass.getName()
185 source = newMethod(baseClass, "a", "()Ljava/lang/String;"); 178 source = newMethod(baseClass, "a", "()Ljava/lang/String;");
186 references = index.getBehaviorReferences(source); 179 references = index.getBehaviorReferences(source);
@@ -188,7 +181,7 @@ public class TestJarIndexInheritanceTree {
188 newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()Ljava/lang/String;"), 181 newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()Ljava/lang/String;"),
189 newBehaviorReferenceByMethod(source, subClassB.getName(), "a", "()V") 182 newBehaviorReferenceByMethod(source, subClassB.getName(), "a", "()V")
190 )); 183 ));
191 184
192 // subclassAA.getName() 185 // subclassAA.getName()
193 source = newMethod(subClassAA, "a", "()Ljava/lang/String;"); 186 source = newMethod(subClassAA, "a", "()Ljava/lang/String;");
194 references = index.getBehaviorReferences(source); 187 references = index.getBehaviorReferences(source);
@@ -196,38 +189,38 @@ public class TestJarIndexInheritanceTree {
196 newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()V") 189 newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()V")
197 )); 190 ));
198 } 191 }
199 192
200 @Test 193 @Test
201 public void containsEntries() { 194 public void containsEntries() {
202 195
203 // classes 196 // classes
204 assertThat(index.containsObfClass(baseClass), is(true)); 197 assertThat(index.containsObfClass(baseClass), is(true));
205 assertThat(index.containsObfClass(subClassA), is(true)); 198 assertThat(index.containsObfClass(subClassA), is(true));
206 assertThat(index.containsObfClass(subClassAA), is(true)); 199 assertThat(index.containsObfClass(subClassAA), is(true));
207 assertThat(index.containsObfClass(subClassB), is(true)); 200 assertThat(index.containsObfClass(subClassB), is(true));
208 201
209 // fields 202 // fields
210 assertThat(index.containsObfField(nameField), is(true)); 203 assertThat(index.containsObfField(nameField), is(true));
211 assertThat(index.containsObfField(numThingsField), is(true)); 204 assertThat(index.containsObfField(numThingsField), is(true));
212 205
213 // methods 206 // methods
214 // getName() 207 // getName()
215 assertThat(index.containsObfBehavior(newMethod(baseClass, "a", "()Ljava/lang/String;")), is(true)); 208 assertThat(index.containsObfBehavior(newMethod(baseClass, "a", "()Ljava/lang/String;")), is(true));
216 assertThat(index.containsObfBehavior(newMethod(subClassA, "a", "()Ljava/lang/String;")), is(false)); 209 assertThat(index.containsObfBehavior(newMethod(subClassA, "a", "()Ljava/lang/String;")), is(false));
217 assertThat(index.containsObfBehavior(newMethod(subClassAA, "a", "()Ljava/lang/String;")), is(true)); 210 assertThat(index.containsObfBehavior(newMethod(subClassAA, "a", "()Ljava/lang/String;")), is(true));
218 assertThat(index.containsObfBehavior(newMethod(subClassB, "a", "()Ljava/lang/String;")), is(false)); 211 assertThat(index.containsObfBehavior(newMethod(subClassB, "a", "()Ljava/lang/String;")), is(false));
219 212
220 // doBaseThings() 213 // doBaseThings()
221 assertThat(index.containsObfBehavior(newMethod(baseClass, "a", "()V")), is(true)); 214 assertThat(index.containsObfBehavior(newMethod(baseClass, "a", "()V")), is(true));
222 assertThat(index.containsObfBehavior(newMethod(subClassA, "a", "()V")), is(false)); 215 assertThat(index.containsObfBehavior(newMethod(subClassA, "a", "()V")), is(false));
223 assertThat(index.containsObfBehavior(newMethod(subClassAA, "a", "()V")), is(true)); 216 assertThat(index.containsObfBehavior(newMethod(subClassAA, "a", "()V")), is(true));
224 assertThat(index.containsObfBehavior(newMethod(subClassB, "a", "()V")), is(true)); 217 assertThat(index.containsObfBehavior(newMethod(subClassB, "a", "()V")), is(true));
225 218
226 // doBThings() 219 // doBThings()
227 assertThat(index.containsObfBehavior(newMethod(baseClass, "b", "()V")), is(false)); 220 assertThat(index.containsObfBehavior(newMethod(baseClass, "b", "()V")), is(false));
228 assertThat(index.containsObfBehavior(newMethod(subClassA, "b", "()V")), is(false)); 221 assertThat(index.containsObfBehavior(newMethod(subClassA, "b", "()V")), is(false));
229 assertThat(index.containsObfBehavior(newMethod(subClassAA, "b", "()V")), is(false)); 222 assertThat(index.containsObfBehavior(newMethod(subClassAA, "b", "()V")), is(false));
230 assertThat(index.containsObfBehavior(newMethod(subClassB, "b", "()V")), is(true)); 223 assertThat(index.containsObfBehavior(newMethod(subClassB, "b", "()V")), is(true));
231 224
232 } 225 }
233} 226}
diff --git a/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java b/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java
index 8efa57c6..6cab1c84 100644
--- a/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java
+++ b/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java
@@ -4,44 +4,35 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma; 12package cuchaz.enigma;
12 13
13import static cuchaz.enigma.TestEntryFactory.*; 14import cuchaz.enigma.analysis.*;
14import static org.hamcrest.MatcherAssert.*; 15import cuchaz.enigma.mapping.*;
15import static org.hamcrest.Matchers.*; 16import org.junit.Test;
16 17
17import java.util.Collection; 18import java.util.Collection;
18import java.util.Set; 19import java.util.Set;
19import java.util.jar.JarFile; 20import java.util.jar.JarFile;
20 21
21import org.junit.Test; 22import static cuchaz.enigma.TestEntryFactory.*;
22 23import static org.hamcrest.MatcherAssert.assertThat;
23import cuchaz.enigma.analysis.Access; 24import static org.hamcrest.Matchers.*;
24import cuchaz.enigma.analysis.ClassImplementationsTreeNode;
25import cuchaz.enigma.analysis.ClassInheritanceTreeNode;
26import cuchaz.enigma.analysis.EntryReference;
27import cuchaz.enigma.analysis.JarIndex;
28import cuchaz.enigma.analysis.MethodInheritanceTreeNode;
29import cuchaz.enigma.mapping.BehaviorEntry;
30import cuchaz.enigma.mapping.ClassEntry;
31import cuchaz.enigma.mapping.FieldEntry;
32import cuchaz.enigma.mapping.MethodEntry;
33import cuchaz.enigma.mapping.Translator;
34 25
35public class TestJarIndexLoneClass { 26public class TestJarIndexLoneClass {
36 27
37 private JarIndex index; 28 private JarIndex index;
38 29
39 public TestJarIndexLoneClass() 30 public TestJarIndexLoneClass()
40 throws Exception { 31 throws Exception {
41 index = new JarIndex(); 32 index = new JarIndex();
42 index.indexJar(new JarFile("build/test-obf/loneClass.jar"), false); 33 index.indexJar(new JarFile("build/test-obf/loneClass.jar"), false);
43 } 34 }
44 35
45 @Test 36 @Test
46 public void obfEntries() { 37 public void obfEntries() {
47 assertThat(index.getObfClassEntries(), containsInAnyOrder( 38 assertThat(index.getObfClassEntries(), containsInAnyOrder(
@@ -49,7 +40,7 @@ public class TestJarIndexLoneClass {
49 newClass("a") 40 newClass("a")
50 )); 41 ));
51 } 42 }
52 43
53 @Test 44 @Test
54 public void translationIndex() { 45 public void translationIndex() {
55 assertThat(index.getTranslationIndex().getSuperclass(new ClassEntry("a")), is(new ClassEntry("java/lang/Object"))); 46 assertThat(index.getTranslationIndex().getSuperclass(new ClassEntry("a")), is(new ClassEntry("java/lang/Object")));
@@ -59,7 +50,7 @@ public class TestJarIndexLoneClass {
59 assertThat(index.getTranslationIndex().getSubclass(new ClassEntry("a")), is(empty())); 50 assertThat(index.getTranslationIndex().getSubclass(new ClassEntry("a")), is(empty()));
60 assertThat(index.getTranslationIndex().getSubclass(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(empty())); 51 assertThat(index.getTranslationIndex().getSubclass(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(empty()));
61 } 52 }
62 53
63 @Test 54 @Test
64 public void access() { 55 public void access() {
65 assertThat(index.getAccess(newField("a", "a", "Ljava/lang/String;")), is(Access.PRIVATE)); 56 assertThat(index.getAccess(newField("a", "a", "Ljava/lang/String;")), is(Access.PRIVATE));
@@ -67,7 +58,7 @@ public class TestJarIndexLoneClass {
67 assertThat(index.getAccess(newField("a", "b", "Ljava/lang/String;")), is(nullValue())); 58 assertThat(index.getAccess(newField("a", "b", "Ljava/lang/String;")), is(nullValue()));
68 assertThat(index.getAccess(newField("a", "a", "LFoo;")), is(nullValue())); 59 assertThat(index.getAccess(newField("a", "a", "LFoo;")), is(nullValue()));
69 } 60 }
70 61
71 @Test 62 @Test
72 public void classInheritance() { 63 public void classInheritance() {
73 ClassInheritanceTreeNode node = index.getClassInheritance(new Translator(), newClass("a")); 64 ClassInheritanceTreeNode node = index.getClassInheritance(new Translator(), newClass("a"));
@@ -75,7 +66,7 @@ public class TestJarIndexLoneClass {
75 assertThat(node.getObfClassName(), is("a")); 66 assertThat(node.getObfClassName(), is("a"));
76 assertThat(node.getChildCount(), is(0)); 67 assertThat(node.getChildCount(), is(0));
77 } 68 }
78 69
79 @Test 70 @Test
80 public void methodInheritance() { 71 public void methodInheritance() {
81 MethodEntry source = newMethod("a", "a", "()Ljava/lang/String;"); 72 MethodEntry source = newMethod("a", "a", "()Ljava/lang/String;");
@@ -84,19 +75,19 @@ public class TestJarIndexLoneClass {
84 assertThat(node.getMethodEntry(), is(source)); 75 assertThat(node.getMethodEntry(), is(source));
85 assertThat(node.getChildCount(), is(0)); 76 assertThat(node.getChildCount(), is(0));
86 } 77 }
87 78
88 @Test 79 @Test
89 public void classImplementations() { 80 public void classImplementations() {
90 ClassImplementationsTreeNode node = index.getClassImplementations(new Translator(), newClass("a")); 81 ClassImplementationsTreeNode node = index.getClassImplementations(new Translator(), newClass("a"));
91 assertThat(node, is(nullValue())); 82 assertThat(node, is(nullValue()));
92 } 83 }
93 84
94 @Test 85 @Test
95 public void methodImplementations() { 86 public void methodImplementations() {
96 MethodEntry source = newMethod("a", "a", "()Ljava/lang/String;"); 87 MethodEntry source = newMethod("a", "a", "()Ljava/lang/String;");
97 assertThat(index.getMethodImplementations(new Translator(), source), is(empty())); 88 assertThat(index.getMethodImplementations(new Translator(), source), is(empty()));
98 } 89 }
99 90
100 @Test 91 @Test
101 public void relatedMethodImplementations() { 92 public void relatedMethodImplementations() {
102 Set<MethodEntry> entries = index.getRelatedMethodImplementations(newMethod("a", "a", "()Ljava/lang/String;")); 93 Set<MethodEntry> entries = index.getRelatedMethodImplementations(newMethod("a", "a", "()Ljava/lang/String;"));
@@ -104,53 +95,53 @@ public class TestJarIndexLoneClass {
104 newMethod("a", "a", "()Ljava/lang/String;") 95 newMethod("a", "a", "()Ljava/lang/String;")
105 )); 96 ));
106 } 97 }
107 98
108 @Test 99 @Test
109 @SuppressWarnings("unchecked") 100 @SuppressWarnings("unchecked")
110 public void fieldReferences() { 101 public void fieldReferences() {
111 FieldEntry source = newField("a", "a", "Ljava/lang/String;"); 102 FieldEntry source = newField("a", "a", "Ljava/lang/String;");
112 Collection<EntryReference<FieldEntry,BehaviorEntry>> references = index.getFieldReferences(source); 103 Collection<EntryReference<FieldEntry, BehaviorEntry>> references = index.getFieldReferences(source);
113 assertThat(references, containsInAnyOrder( 104 assertThat(references, containsInAnyOrder(
114 newFieldReferenceByConstructor(source, "a", "(Ljava/lang/String;)V"), 105 newFieldReferenceByConstructor(source, "a", "(Ljava/lang/String;)V"),
115 newFieldReferenceByMethod(source, "a", "a", "()Ljava/lang/String;") 106 newFieldReferenceByMethod(source, "a", "a", "()Ljava/lang/String;")
116 )); 107 ));
117 } 108 }
118 109
119 @Test 110 @Test
120 public void behaviorReferences() { 111 public void behaviorReferences() {
121 assertThat(index.getBehaviorReferences(newMethod("a", "a", "()Ljava/lang/String;")), is(empty())); 112 assertThat(index.getBehaviorReferences(newMethod("a", "a", "()Ljava/lang/String;")), is(empty()));
122 } 113 }
123 114
124 @Test 115 @Test
125 public void innerClasses() { 116 public void innerClasses() {
126 assertThat(index.getInnerClasses(newClass("a")), is(empty())); 117 assertThat(index.getInnerClasses(newClass("a")), is(empty()));
127 } 118 }
128 119
129 @Test 120 @Test
130 public void outerClass() { 121 public void outerClass() {
131 assertThat(index.getOuterClass(newClass("a")), is(nullValue())); 122 assertThat(index.getOuterClass(newClass("a")), is(nullValue()));
132 } 123 }
133 124
134 @Test 125 @Test
135 public void isAnonymousClass() { 126 public void isAnonymousClass() {
136 assertThat(index.isAnonymousClass(newClass("a")), is(false)); 127 assertThat(index.isAnonymousClass(newClass("a")), is(false));
137 } 128 }
138 129
139 @Test 130 @Test
140 public void interfaces() { 131 public void interfaces() {
141 assertThat(index.getInterfaces("a"), is(empty())); 132 assertThat(index.getInterfaces("a"), is(empty()));
142 } 133 }
143 134
144 @Test 135 @Test
145 public void implementingClasses() { 136 public void implementingClasses() {
146 assertThat(index.getImplementingClasses("a"), is(empty())); 137 assertThat(index.getImplementingClasses("a"), is(empty()));
147 } 138 }
148 139
149 @Test 140 @Test
150 public void isInterface() { 141 public void isInterface() {
151 assertThat(index.isInterface("a"), is(false)); 142 assertThat(index.isInterface("a"), is(false));
152 } 143 }
153 144
154 @Test 145 @Test
155 public void testContains() { 146 public void testContains() {
156 assertThat(index.containsObfClass(newClass("a")), is(true)); 147 assertThat(index.containsObfClass(newClass("a")), is(true));
diff --git a/src/test/java/cuchaz/enigma/TestSignature.java b/src/test/java/cuchaz/enigma/TestSignature.java
index 8537adfb..534b43ae 100644
--- a/src/test/java/cuchaz/enigma/TestSignature.java
+++ b/src/test/java/cuchaz/enigma/TestSignature.java
@@ -4,31 +4,33 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma;
12
13import static org.hamcrest.MatcherAssert.*;
14import static org.hamcrest.Matchers.*;
15 11
16import org.junit.Test; 12package cuchaz.enigma;
17 13
18import cuchaz.enigma.mapping.ClassNameReplacer; 14import cuchaz.enigma.mapping.ClassNameReplacer;
19import cuchaz.enigma.mapping.Signature; 15import cuchaz.enigma.mapping.Signature;
20import cuchaz.enigma.mapping.Type; 16import cuchaz.enigma.mapping.Type;
17import org.junit.Test;
21 18
19import static org.hamcrest.MatcherAssert.assertThat;
20import static org.hamcrest.Matchers.contains;
21import static org.hamcrest.Matchers.empty;
22import static org.hamcrest.Matchers.is;
23import static org.hamcrest.Matchers.not;
22 24
23public class TestSignature { 25public class TestSignature {
24 26
25 @Test 27 @Test
26 public void easiest() { 28 public void easiest() {
27 final Signature sig = new Signature("()V"); 29 final Signature sig = new Signature("()V");
28 assertThat(sig.getArgumentTypes(), is(empty())); 30 assertThat(sig.getArgumentTypes(), is(empty()));
29 assertThat(sig.getReturnType(), is(new Type("V"))); 31 assertThat(sig.getReturnType(), is(new Type("V")));
30 } 32 }
31 33
32 @Test 34 @Test
33 public void primitives() { 35 public void primitives() {
34 { 36 {
@@ -56,7 +58,7 @@ public class TestSignature {
56 assertThat(sig.getReturnType(), is(new Type("Z"))); 58 assertThat(sig.getReturnType(), is(new Type("Z")));
57 } 59 }
58 } 60 }
59 61
60 @Test 62 @Test
61 public void classes() { 63 public void classes() {
62 { 64 {
@@ -82,7 +84,7 @@ public class TestSignature {
82 assertThat(sig.getReturnType(), is(new Type("LBar;"))); 84 assertThat(sig.getReturnType(), is(new Type("LBar;")));
83 } 85 }
84 } 86 }
85 87
86 @Test 88 @Test
87 public void arrays() { 89 public void arrays() {
88 { 90 {
@@ -109,7 +111,7 @@ public class TestSignature {
109 assertThat(sig.getReturnType(), is(new Type("[D"))); 111 assertThat(sig.getReturnType(), is(new Type("[D")));
110 } 112 }
111 } 113 }
112 114
113 @Test 115 @Test
114 public void mixed() { 116 public void mixed() {
115 { 117 {
@@ -131,7 +133,7 @@ public class TestSignature {
131 assertThat(sig.getReturnType(), is(new Type("[LFoo;"))); 133 assertThat(sig.getReturnType(), is(new Type("[LFoo;")));
132 } 134 }
133 } 135 }
134 136
135 @Test 137 @Test
136 public void replaceClasses() { 138 public void replaceClasses() {
137 { 139 {
@@ -195,7 +197,7 @@ public class TestSignature {
195 assertThat(sig.getReturnType(), is(new Type("LCow;"))); 197 assertThat(sig.getReturnType(), is(new Type("LCow;")));
196 } 198 }
197 } 199 }
198 200
199 @Test 201 @Test
200 public void replaceArrayClasses() { 202 public void replaceArrayClasses() {
201 { 203 {
@@ -217,13 +219,13 @@ public class TestSignature {
217 assertThat(sig.getReturnType(), is(new Type("[[[LBeer;"))); 219 assertThat(sig.getReturnType(), is(new Type("[[[LBeer;")));
218 } 220 }
219 } 221 }
220 222
221 @Test 223 @Test
222 public void equals() { 224 public void equals() {
223 225
224 // base 226 // base
225 assertThat(new Signature("()V"), is(new Signature("()V"))); 227 assertThat(new Signature("()V"), is(new Signature("()V")));
226 228
227 // arguments 229 // arguments
228 assertThat(new Signature("(I)V"), is(new Signature("(I)V"))); 230 assertThat(new Signature("(I)V"), is(new Signature("(I)V")));
229 assertThat(new Signature("(ZIZ)V"), is(new Signature("(ZIZ)V"))); 231 assertThat(new Signature("(ZIZ)V"), is(new Signature("(ZIZ)V")));
@@ -238,7 +240,7 @@ public class TestSignature {
238 assertThat(new Signature("([[Z)V"), is(not(new Signature("([[LFoo;)V")))); 240 assertThat(new Signature("([[Z)V"), is(not(new Signature("([[LFoo;)V"))));
239 assertThat(new Signature("(LFoo;LBar;)V"), is(not(new Signature("(LFoo;LCow;)V")))); 241 assertThat(new Signature("(LFoo;LBar;)V"), is(not(new Signature("(LFoo;LCow;)V"))));
240 assertThat(new Signature("([LFoo;LBar;)V"), is(not(new Signature("(LFoo;LCow;)V")))); 242 assertThat(new Signature("([LFoo;LBar;)V"), is(not(new Signature("(LFoo;LCow;)V"))));
241 243
242 // return type 244 // return type
243 assertThat(new Signature("()I"), is(new Signature("()I"))); 245 assertThat(new Signature("()I"), is(new Signature("()I")));
244 assertThat(new Signature("()Z"), is(new Signature("()Z"))); 246 assertThat(new Signature("()Z"), is(new Signature("()Z")));
@@ -246,7 +248,7 @@ public class TestSignature {
246 assertThat(new Signature("()[[[Z"), is(new Signature("()[[[Z"))); 248 assertThat(new Signature("()[[[Z"), is(new Signature("()[[[Z")));
247 assertThat(new Signature("()LFoo;"), is(new Signature("()LFoo;"))); 249 assertThat(new Signature("()LFoo;"), is(new Signature("()LFoo;")));
248 assertThat(new Signature("()[LFoo;"), is(new Signature("()[LFoo;"))); 250 assertThat(new Signature("()[LFoo;"), is(new Signature("()[LFoo;")));
249 251
250 assertThat(new Signature("()I"), is(not(new Signature("()Z")))); 252 assertThat(new Signature("()I"), is(not(new Signature("()Z"))));
251 assertThat(new Signature("()Z"), is(not(new Signature("()I")))); 253 assertThat(new Signature("()Z"), is(not(new Signature("()I"))));
252 assertThat(new Signature("()[D"), is(not(new Signature("()[J")))); 254 assertThat(new Signature("()[D"), is(not(new Signature("()[J"))));
@@ -254,7 +256,7 @@ public class TestSignature {
254 assertThat(new Signature("()LFoo;"), is(not(new Signature("()LBar;")))); 256 assertThat(new Signature("()LFoo;"), is(not(new Signature("()LBar;"))));
255 assertThat(new Signature("()[LFoo;"), is(not(new Signature("()[LBar;")))); 257 assertThat(new Signature("()[LFoo;"), is(not(new Signature("()[LBar;"))));
256 } 258 }
257 259
258 @Test 260 @Test
259 public void testToString() { 261 public void testToString() {
260 assertThat(new Signature("()V").toString(), is("()V")); 262 assertThat(new Signature("()V").toString(), is("()V"));
diff --git a/src/test/java/cuchaz/enigma/TestSourceIndex.java b/src/test/java/cuchaz/enigma/TestSourceIndex.java
index 58d9ca91..6e9e5aec 100644
--- a/src/test/java/cuchaz/enigma/TestSourceIndex.java
+++ b/src/test/java/cuchaz/enigma/TestSourceIndex.java
@@ -4,27 +4,26 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma;
12
13import java.io.File;
14import java.util.Set;
15import java.util.jar.JarFile;
16 11
17import org.junit.Test; 12package cuchaz.enigma;
18 13
19import com.google.common.collect.Sets; 14import com.google.common.collect.Sets;
20import com.strobel.decompiler.languages.java.ast.CompilationUnit; 15import com.strobel.decompiler.languages.java.ast.CompilationUnit;
21
22import cuchaz.enigma.mapping.ClassEntry; 16import cuchaz.enigma.mapping.ClassEntry;
17import org.junit.Test;
18
19import java.io.File;
20import java.util.Set;
21import java.util.jar.JarFile;
23 22
24public class TestSourceIndex { 23public class TestSourceIndex {
25 @Test 24 @Test
26 public void indexEverything() 25 public void indexEverything()
27 throws Exception { 26 throws Exception {
28 // Figure out where Minecraft is... 27 // Figure out where Minecraft is...
29 final String mcDir = System.getProperty("enigma.test.minecraftdir"); 28 final String mcDir = System.getProperty("enigma.test.minecraftdir");
30 File mcJar = null; 29 File mcJar = null;
@@ -32,20 +31,17 @@ public class TestSourceIndex {
32 String osname = System.getProperty("os.name").toLowerCase(); 31 String osname = System.getProperty("os.name").toLowerCase();
33 if (osname.contains("nix") || osname.contains("nux") || osname.contains("solaris")) { 32 if (osname.contains("nix") || osname.contains("nux") || osname.contains("solaris")) {
34 mcJar = new File(System.getProperty("user.home"), ".minecraft/versions/1.8.3/1.8.3.jar"); 33 mcJar = new File(System.getProperty("user.home"), ".minecraft/versions/1.8.3/1.8.3.jar");
35 } 34 } else if (osname.contains("mac") || osname.contains("darwin")) {
36 else if (osname.contains("mac") || osname.contains("darwin")) {
37 mcJar = new File(System.getProperty("user.home"), "Library/Application Support/minecraft/versions/1.8.3/1.8.3.jar"); 35 mcJar = new File(System.getProperty("user.home"), "Library/Application Support/minecraft/versions/1.8.3/1.8.3.jar");
38 } 36 } else if (osname.contains("win")) {
39 else if (osname.contains("win")) {
40 mcJar = new File(System.getenv("AppData"), ".minecraft/versions/1.8.3/1.8.3.jar"); 37 mcJar = new File(System.getenv("AppData"), ".minecraft/versions/1.8.3/1.8.3.jar");
41 } 38 }
42 } 39 } else {
43 else {
44 mcJar = new File(mcDir, "versions/1.8.3/1.8.3.jar"); 40 mcJar = new File(mcDir, "versions/1.8.3/1.8.3.jar");
45 } 41 }
46 42
47 Deobfuscator deobfuscator = new Deobfuscator(new JarFile(mcJar)); 43 Deobfuscator deobfuscator = new Deobfuscator(new JarFile(mcJar));
48 44
49 // get all classes that aren't inner classes 45 // get all classes that aren't inner classes
50 Set<ClassEntry> classEntries = Sets.newHashSet(); 46 Set<ClassEntry> classEntries = Sets.newHashSet();
51 for (ClassEntry obfClassEntry : deobfuscator.getJarIndex().getObfClassEntries()) { 47 for (ClassEntry obfClassEntry : deobfuscator.getJarIndex().getObfClassEntries()) {
@@ -53,7 +49,7 @@ public class TestSourceIndex {
53 classEntries.add(obfClassEntry); 49 classEntries.add(obfClassEntry);
54 } 50 }
55 } 51 }
56 52
57 for (ClassEntry obfClassEntry : classEntries) { 53 for (ClassEntry obfClassEntry : classEntries) {
58 try { 54 try {
59 CompilationUnit tree = deobfuscator.getSourceTree(obfClassEntry.getName()); 55 CompilationUnit tree = deobfuscator.getSourceTree(obfClassEntry.getName());
diff --git a/src/test/java/cuchaz/enigma/TestTokensConstructors.java b/src/test/java/cuchaz/enigma/TestTokensConstructors.java
index 890a4fd3..e40d5fdc 100644
--- a/src/test/java/cuchaz/enigma/TestTokensConstructors.java
+++ b/src/test/java/cuchaz/enigma/TestTokensConstructors.java
@@ -4,35 +4,40 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma; 12package cuchaz.enigma;
12 13
13import static cuchaz.enigma.TestEntryFactory.*; 14import cuchaz.enigma.mapping.BehaviorEntry;
14import static org.hamcrest.MatcherAssert.*; 15import org.junit.Test;
15import static org.hamcrest.Matchers.*;
16 16
17import java.util.jar.JarFile; 17import java.util.jar.JarFile;
18 18
19import org.junit.Test; 19import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByConstructor;
20 20import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByMethod;
21import cuchaz.enigma.mapping.BehaviorEntry; 21import static cuchaz.enigma.TestEntryFactory.newConstructor;
22import static org.hamcrest.MatcherAssert.assertThat;
23import static org.hamcrest.Matchers.containsInAnyOrder;
24import static org.hamcrest.Matchers.empty;
25import static org.hamcrest.Matchers.is;
26import static org.hamcrest.Matchers.nullValue;
22 27
23public class TestTokensConstructors extends TokenChecker { 28public class TestTokensConstructors extends TokenChecker {
24 29
25 public TestTokensConstructors() 30 public TestTokensConstructors()
26 throws Exception { 31 throws Exception {
27 super(new JarFile("build/test-obf/constructors.jar")); 32 super(new JarFile("build/test-obf/constructors.jar"));
28 } 33 }
29 34
30 @Test 35 @Test
31 public void baseDeclarations() { 36 public void baseDeclarations() {
32 assertThat(getDeclarationToken(newConstructor("a", "()V")), is("a")); 37 assertThat(getDeclarationToken(newConstructor("a", "()V")), is("a"));
33 assertThat(getDeclarationToken(newConstructor("a", "(I)V")), is("a")); 38 assertThat(getDeclarationToken(newConstructor("a", "(I)V")), is("a"));
34 } 39 }
35 40
36 @Test 41 @Test
37 public void subDeclarations() { 42 public void subDeclarations() {
38 assertThat(getDeclarationToken(newConstructor("d", "()V")), is("d")); 43 assertThat(getDeclarationToken(newConstructor("d", "()V")), is("d"));
@@ -40,17 +45,17 @@ public class TestTokensConstructors extends TokenChecker {
40 assertThat(getDeclarationToken(newConstructor("d", "(II)V")), is("d")); 45 assertThat(getDeclarationToken(newConstructor("d", "(II)V")), is("d"));
41 assertThat(getDeclarationToken(newConstructor("d", "(III)V")), is("d")); 46 assertThat(getDeclarationToken(newConstructor("d", "(III)V")), is("d"));
42 } 47 }
43 48
44 @Test 49 @Test
45 public void subsubDeclarations() { 50 public void subsubDeclarations() {
46 assertThat(getDeclarationToken(newConstructor("e", "(I)V")), is("e")); 51 assertThat(getDeclarationToken(newConstructor("e", "(I)V")), is("e"));
47 } 52 }
48 53
49 @Test 54 @Test
50 public void defaultDeclarations() { 55 public void defaultDeclarations() {
51 assertThat(getDeclarationToken(newConstructor("c", "()V")), nullValue()); 56 assertThat(getDeclarationToken(newConstructor("c", "()V")), nullValue());
52 } 57 }
53 58
54 @Test 59 @Test
55 public void baseDefaultReferences() { 60 public void baseDefaultReferences() {
56 BehaviorEntry source = newConstructor("a", "()V"); 61 BehaviorEntry source = newConstructor("a", "()V");
@@ -67,7 +72,7 @@ public class TestTokensConstructors extends TokenChecker {
67 is(empty()) // implicit call, not decompiled to token 72 is(empty()) // implicit call, not decompiled to token
68 ); 73 );
69 } 74 }
70 75
71 @Test 76 @Test
72 public void baseIntReferences() { 77 public void baseIntReferences() {
73 BehaviorEntry source = newConstructor("a", "(I)V"); 78 BehaviorEntry source = newConstructor("a", "(I)V");
@@ -76,7 +81,7 @@ public class TestTokensConstructors extends TokenChecker {
76 containsInAnyOrder("a") 81 containsInAnyOrder("a")
77 ); 82 );
78 } 83 }
79 84
80 @Test 85 @Test
81 public void subDefaultReferences() { 86 public void subDefaultReferences() {
82 BehaviorEntry source = newConstructor("d", "()V"); 87 BehaviorEntry source = newConstructor("d", "()V");
@@ -89,7 +94,7 @@ public class TestTokensConstructors extends TokenChecker {
89 containsInAnyOrder("this") 94 containsInAnyOrder("this")
90 ); 95 );
91 } 96 }
92 97
93 @Test 98 @Test
94 public void subIntReferences() { 99 public void subIntReferences() {
95 BehaviorEntry source = newConstructor("d", "(I)V"); 100 BehaviorEntry source = newConstructor("d", "(I)V");
@@ -106,7 +111,7 @@ public class TestTokensConstructors extends TokenChecker {
106 containsInAnyOrder("super") 111 containsInAnyOrder("super")
107 ); 112 );
108 } 113 }
109 114
110 @Test 115 @Test
111 public void subIntIntReferences() { 116 public void subIntIntReferences() {
112 BehaviorEntry source = newConstructor("d", "(II)V"); 117 BehaviorEntry source = newConstructor("d", "(II)V");
@@ -115,7 +120,7 @@ public class TestTokensConstructors extends TokenChecker {
115 containsInAnyOrder("d") 120 containsInAnyOrder("d")
116 ); 121 );
117 } 122 }
118 123
119 @Test 124 @Test
120 public void subsubIntReferences() { 125 public void subsubIntReferences() {
121 BehaviorEntry source = newConstructor("e", "(I)V"); 126 BehaviorEntry source = newConstructor("e", "(I)V");
@@ -124,7 +129,7 @@ public class TestTokensConstructors extends TokenChecker {
124 containsInAnyOrder("e") 129 containsInAnyOrder("e")
125 ); 130 );
126 } 131 }
127 132
128 @Test 133 @Test
129 public void defaultConstructableReferences() { 134 public void defaultConstructableReferences() {
130 BehaviorEntry source = newConstructor("c", "()V"); 135 BehaviorEntry source = newConstructor("c", "()V");
diff --git a/src/test/java/cuchaz/enigma/TestTranslator.java b/src/test/java/cuchaz/enigma/TestTranslator.java
index 2c54603b..b63dff86 100644
--- a/src/test/java/cuchaz/enigma/TestTranslator.java
+++ b/src/test/java/cuchaz/enigma/TestTranslator.java
@@ -8,28 +8,29 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma;
12
13import static cuchaz.enigma.TestEntryFactory.*;
14 11
15import org.junit.BeforeClass; 12package cuchaz.enigma;
16import org.junit.Test;
17 13
18import cuchaz.enigma.mapping.Entry; 14import cuchaz.enigma.mapping.Entry;
19import cuchaz.enigma.mapping.Mappings; 15import cuchaz.enigma.mapping.Mappings;
20import cuchaz.enigma.mapping.Translator; 16import cuchaz.enigma.mapping.Translator;
17import org.junit.BeforeClass;
18import org.junit.Test;
21 19
20import static cuchaz.enigma.TestEntryFactory.newClass;
21import static cuchaz.enigma.TestEntryFactory.newField;
22import static cuchaz.enigma.TestEntryFactory.newMethod;
22 23
23public class TestTranslator { 24public class TestTranslator {
24 25
25 private static Deobfuscator deobfuscator; 26 private static Deobfuscator deobfuscator;
26 private static Mappings mappings; 27 private static Mappings mappings;
27 private static Translator deobfTranslator; 28 private static Translator deobfTranslator;
28 private static Translator obfTranslator; 29 private static Translator obfTranslator;
29 30
30 @BeforeClass 31 @BeforeClass
31 public static void beforeClass() 32 public static void beforeClass()
32 throws Exception { 33 throws Exception {
33 //TODO FIx 34 //TODO FIx
34 //deobfuscator = new Deobfuscator(new JarFile("build/test-obf/translation.jar")); 35 //deobfuscator = new Deobfuscator(new JarFile("build/test-obf/translation.jar"));
35 //try (InputStream in = TestTranslator.class.getResourceAsStream("/cuchaz/enigma/resources/translation.mappings")) { 36 //try (InputStream in = TestTranslator.class.getResourceAsStream("/cuchaz/enigma/resources/translation.mappings")) {
diff --git a/src/test/java/cuchaz/enigma/TestType.java b/src/test/java/cuchaz/enigma/TestType.java
index 01c235b8..43dacb0c 100644
--- a/src/test/java/cuchaz/enigma/TestType.java
+++ b/src/test/java/cuchaz/enigma/TestType.java
@@ -4,23 +4,23 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma;
12
13import static cuchaz.enigma.TestEntryFactory.*;
14import static org.hamcrest.MatcherAssert.*;
15import static org.hamcrest.Matchers.*;
16 11
17import org.junit.Test; 12package cuchaz.enigma;
18 13
19import cuchaz.enigma.mapping.Type; 14import cuchaz.enigma.mapping.Type;
15import org.junit.Test;
20 16
17import static cuchaz.enigma.TestEntryFactory.newClass;
18import static org.hamcrest.MatcherAssert.assertThat;
19import static org.hamcrest.Matchers.is;
20import static org.hamcrest.Matchers.not;
21 21
22public class TestType { 22public class TestType {
23 23
24 @Test 24 @Test
25 public void isVoid() { 25 public void isVoid() {
26 assertThat(new Type("V").isVoid(), is(true)); 26 assertThat(new Type("V").isVoid(), is(true));
@@ -34,7 +34,7 @@ public class TestType {
34 assertThat(new Type("LFoo;").isVoid(), is(false)); 34 assertThat(new Type("LFoo;").isVoid(), is(false));
35 assertThat(new Type("[I").isVoid(), is(false)); 35 assertThat(new Type("[I").isVoid(), is(false));
36 } 36 }
37 37
38 @Test 38 @Test
39 public void isPrimitive() { 39 public void isPrimitive() {
40 assertThat(new Type("V").isPrimitive(), is(false)); 40 assertThat(new Type("V").isPrimitive(), is(false));
@@ -48,7 +48,7 @@ public class TestType {
48 assertThat(new Type("LFoo;").isPrimitive(), is(false)); 48 assertThat(new Type("LFoo;").isPrimitive(), is(false));
49 assertThat(new Type("[I").isPrimitive(), is(false)); 49 assertThat(new Type("[I").isPrimitive(), is(false));
50 } 50 }
51 51
52 @Test 52 @Test
53 public void getPrimitive() { 53 public void getPrimitive() {
54 assertThat(new Type("Z").getPrimitive(), is(Type.Primitive.Boolean)); 54 assertThat(new Type("Z").getPrimitive(), is(Type.Primitive.Boolean));
@@ -59,7 +59,7 @@ public class TestType {
59 assertThat(new Type("F").getPrimitive(), is(Type.Primitive.Float)); 59 assertThat(new Type("F").getPrimitive(), is(Type.Primitive.Float));
60 assertThat(new Type("D").getPrimitive(), is(Type.Primitive.Double)); 60 assertThat(new Type("D").getPrimitive(), is(Type.Primitive.Double));
61 } 61 }
62 62
63 @Test 63 @Test
64 public void isClass() { 64 public void isClass() {
65 assertThat(new Type("V").isClass(), is(false)); 65 assertThat(new Type("V").isClass(), is(false));
@@ -73,19 +73,19 @@ public class TestType {
73 assertThat(new Type("LFoo;").isClass(), is(true)); 73 assertThat(new Type("LFoo;").isClass(), is(true));
74 assertThat(new Type("[I").isClass(), is(false)); 74 assertThat(new Type("[I").isClass(), is(false));
75 } 75 }
76 76
77 @Test 77 @Test
78 public void getClassEntry() { 78 public void getClassEntry() {
79 assertThat(new Type("LFoo;").getClassEntry(), is(newClass("Foo"))); 79 assertThat(new Type("LFoo;").getClassEntry(), is(newClass("Foo")));
80 assertThat(new Type("Ljava/lang/String;").getClassEntry(), is(newClass("java/lang/String"))); 80 assertThat(new Type("Ljava/lang/String;").getClassEntry(), is(newClass("java/lang/String")));
81 } 81 }
82 82
83 @Test 83 @Test
84 public void getArrayClassEntry() { 84 public void getArrayClassEntry() {
85 assertThat(new Type("[LFoo;").getClassEntry(), is(newClass("Foo"))); 85 assertThat(new Type("[LFoo;").getClassEntry(), is(newClass("Foo")));
86 assertThat(new Type("[[[Ljava/lang/String;").getClassEntry(), is(newClass("java/lang/String"))); 86 assertThat(new Type("[[[Ljava/lang/String;").getClassEntry(), is(newClass("java/lang/String")));
87 } 87 }
88 88
89 @Test 89 @Test
90 public void isArray() { 90 public void isArray() {
91 assertThat(new Type("V").isArray(), is(false)); 91 assertThat(new Type("V").isArray(), is(false));
@@ -99,14 +99,14 @@ public class TestType {
99 assertThat(new Type("LFoo;").isArray(), is(false)); 99 assertThat(new Type("LFoo;").isArray(), is(false));
100 assertThat(new Type("[I").isArray(), is(true)); 100 assertThat(new Type("[I").isArray(), is(true));
101 } 101 }
102 102
103 @Test 103 @Test
104 public void getArrayDimension() { 104 public void getArrayDimension() {
105 assertThat(new Type("[I").getArrayDimension(), is(1)); 105 assertThat(new Type("[I").getArrayDimension(), is(1));
106 assertThat(new Type("[[I").getArrayDimension(), is(2)); 106 assertThat(new Type("[[I").getArrayDimension(), is(2));
107 assertThat(new Type("[[[I").getArrayDimension(), is(3)); 107 assertThat(new Type("[[[I").getArrayDimension(), is(3));
108 } 108 }
109 109
110 @Test 110 @Test
111 public void getArrayType() { 111 public void getArrayType() {
112 assertThat(new Type("[I").getArrayType(), is(new Type("I"))); 112 assertThat(new Type("[I").getArrayType(), is(new Type("I")));
@@ -114,7 +114,7 @@ public class TestType {
114 assertThat(new Type("[[[I").getArrayType(), is(new Type("I"))); 114 assertThat(new Type("[[[I").getArrayType(), is(new Type("I")));
115 assertThat(new Type("[Ljava/lang/String;").getArrayType(), is(new Type("Ljava/lang/String;"))); 115 assertThat(new Type("[Ljava/lang/String;").getArrayType(), is(new Type("Ljava/lang/String;")));
116 } 116 }
117 117
118 @Test 118 @Test
119 public void hasClass() { 119 public void hasClass() {
120 assertThat(new Type("LFoo;").hasClass(), is(true)); 120 assertThat(new Type("LFoo;").hasClass(), is(true));
@@ -127,7 +127,7 @@ public class TestType {
127 assertThat(new Type("[[[I").hasClass(), is(false)); 127 assertThat(new Type("[[[I").hasClass(), is(false));
128 assertThat(new Type("Z").hasClass(), is(false)); 128 assertThat(new Type("Z").hasClass(), is(false));
129 } 129 }
130 130
131 @Test 131 @Test
132 public void parseVoid() { 132 public void parseVoid() {
133 final String answer = "V"; 133 final String answer = "V";
@@ -138,7 +138,7 @@ public class TestType {
138 assertThat(Type.parseFirst("VLFoo;"), is(answer)); 138 assertThat(Type.parseFirst("VLFoo;"), is(answer));
139 assertThat(Type.parseFirst("V[LFoo;"), is(answer)); 139 assertThat(Type.parseFirst("V[LFoo;"), is(answer));
140 } 140 }
141 141
142 @Test 142 @Test
143 public void parsePrimitive() { 143 public void parsePrimitive() {
144 final String answer = "I"; 144 final String answer = "I";
@@ -149,7 +149,7 @@ public class TestType {
149 assertThat(Type.parseFirst("ILFoo;"), is(answer)); 149 assertThat(Type.parseFirst("ILFoo;"), is(answer));
150 assertThat(Type.parseFirst("I[LFoo;"), is(answer)); 150 assertThat(Type.parseFirst("I[LFoo;"), is(answer));
151 } 151 }
152 152
153 @Test 153 @Test
154 public void parseClass() { 154 public void parseClass() {
155 { 155 {
@@ -199,7 +199,7 @@ public class TestType {
199 assertThat(Type.parseFirst("[LFoo;LFoo;"), is(answer)); 199 assertThat(Type.parseFirst("[LFoo;LFoo;"), is(answer));
200 } 200 }
201 } 201 }
202 202
203 @Test 203 @Test
204 public void equals() { 204 public void equals() {
205 assertThat(new Type("V"), is(new Type("V"))); 205 assertThat(new Type("V"), is(new Type("V")));
@@ -214,7 +214,7 @@ public class TestType {
214 assertThat(new Type("[I"), is(new Type("[I"))); 214 assertThat(new Type("[I"), is(new Type("[I")));
215 assertThat(new Type("[[[I"), is(new Type("[[[I"))); 215 assertThat(new Type("[[[I"), is(new Type("[[[I")));
216 assertThat(new Type("[LFoo;"), is(new Type("[LFoo;"))); 216 assertThat(new Type("[LFoo;"), is(new Type("[LFoo;")));
217 217
218 assertThat(new Type("V"), is(not(new Type("I")))); 218 assertThat(new Type("V"), is(not(new Type("I"))));
219 assertThat(new Type("I"), is(not(new Type("J")))); 219 assertThat(new Type("I"), is(not(new Type("J"))));
220 assertThat(new Type("I"), is(not(new Type("LBar;")))); 220 assertThat(new Type("I"), is(not(new Type("LBar;"))));
@@ -224,7 +224,7 @@ public class TestType {
224 assertThat(new Type("[[[I"), is(not(new Type("[I")))); 224 assertThat(new Type("[[[I"), is(not(new Type("[I"))));
225 assertThat(new Type("[LFoo;"), is(not(new Type("[LBar;")))); 225 assertThat(new Type("[LFoo;"), is(not(new Type("[LBar;"))));
226 } 226 }
227 227
228 @Test 228 @Test
229 public void testToString() { 229 public void testToString() {
230 assertThat(new Type("V").toString(), is("V")); 230 assertThat(new Type("V").toString(), is("V"));
diff --git a/src/test/java/cuchaz/enigma/TokenChecker.java b/src/test/java/cuchaz/enigma/TokenChecker.java
index 07463206..c6ced488 100644
--- a/src/test/java/cuchaz/enigma/TokenChecker.java
+++ b/src/test/java/cuchaz/enigma/TokenChecker.java
@@ -4,34 +4,34 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma;
12 11
13import java.io.IOException; 12package cuchaz.enigma;
14import java.util.Collection;
15import java.util.List;
16import java.util.jar.JarFile;
17 13
18import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
19import com.strobel.decompiler.languages.java.ast.CompilationUnit; 15import com.strobel.decompiler.languages.java.ast.CompilationUnit;
20
21import cuchaz.enigma.analysis.EntryReference; 16import cuchaz.enigma.analysis.EntryReference;
22import cuchaz.enigma.analysis.SourceIndex; 17import cuchaz.enigma.analysis.SourceIndex;
23import cuchaz.enigma.analysis.Token; 18import cuchaz.enigma.analysis.Token;
24import cuchaz.enigma.mapping.Entry; 19import cuchaz.enigma.mapping.Entry;
25 20
21import java.io.IOException;
22import java.util.Collection;
23import java.util.List;
24import java.util.jar.JarFile;
25
26public class TokenChecker { 26public class TokenChecker {
27 27
28 private Deobfuscator deobfuscator; 28 private Deobfuscator deobfuscator;
29 29
30 protected TokenChecker(JarFile jarFile) 30 protected TokenChecker(JarFile jarFile)
31 throws IOException { 31 throws IOException {
32 deobfuscator = new Deobfuscator(jarFile); 32 deobfuscator = new Deobfuscator(jarFile);
33 } 33 }
34 34
35 protected String getDeclarationToken(Entry entry) { 35 protected String getDeclarationToken(Entry entry) {
36 // decompile the class 36 // decompile the class
37 CompilationUnit tree = deobfuscator.getSourceTree(entry.getClassName()); 37 CompilationUnit tree = deobfuscator.getSourceTree(entry.getClassName());
@@ -39,7 +39,7 @@ public class TokenChecker {
39 // tree.acceptVisitor( new TreeDumpVisitor( new File( "tree." + entry.getClassName().replace( '/', '.' ) + ".txt" ) ), null ); 39 // tree.acceptVisitor( new TreeDumpVisitor( new File( "tree." + entry.getClassName().replace( '/', '.' ) + ".txt" ) ), null );
40 String source = deobfuscator.getSource(tree); 40 String source = deobfuscator.getSource(tree);
41 SourceIndex index = deobfuscator.getSourceIndex(tree, source); 41 SourceIndex index = deobfuscator.getSourceIndex(tree, source);
42 42
43 // get the token value 43 // get the token value
44 Token token = index.getDeclarationToken(entry); 44 Token token = index.getDeclarationToken(entry);
45 if (token == null) { 45 if (token == null) {
@@ -47,17 +47,17 @@ public class TokenChecker {
47 } 47 }
48 return source.substring(token.start, token.end); 48 return source.substring(token.start, token.end);
49 } 49 }
50 50
51 @SuppressWarnings("unchecked") 51 @SuppressWarnings("unchecked")
52 protected Collection<String> getReferenceTokens(EntryReference<? extends Entry,? extends Entry> reference) { 52 protected Collection<String> getReferenceTokens(EntryReference<? extends Entry, ? extends Entry> reference) {
53 // decompile the class 53 // decompile the class
54 CompilationUnit tree = deobfuscator.getSourceTree(reference.context.getClassName()); 54 CompilationUnit tree = deobfuscator.getSourceTree(reference.context.getClassName());
55 String source = deobfuscator.getSource(tree); 55 String source = deobfuscator.getSource(tree);
56 SourceIndex index = deobfuscator.getSourceIndex(tree, source); 56 SourceIndex index = deobfuscator.getSourceIndex(tree, source);
57 57
58 // get the token values 58 // get the token values
59 List<String> values = Lists.newArrayList(); 59 List<String> values = Lists.newArrayList();
60 for (Token token : index.getReferenceTokens((EntryReference<Entry,Entry>)reference)) { 60 for (Token token : index.getReferenceTokens((EntryReference<Entry, Entry>) reference)) {
61 values.add(source.substring(token.start, token.end)); 61 values.add(source.substring(token.start, token.end));
62 } 62 }
63 return values; 63 return values;
diff --git a/src/test/java/cuchaz/enigma/inputs/Keep.java b/src/test/java/cuchaz/enigma/inputs/Keep.java
index f04875f5..4dbe8e2f 100644
--- a/src/test/java/cuchaz/enigma/inputs/Keep.java
+++ b/src/test/java/cuchaz/enigma/inputs/Keep.java
@@ -4,10 +4,11 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs; 12package cuchaz.enigma.inputs;
12 13
13public class Keep { 14public class Keep {
diff --git a/src/test/java/cuchaz/enigma/inputs/constructors/BaseClass.java b/src/test/java/cuchaz/enigma/inputs/constructors/BaseClass.java
index ad5e950e..f07e1f8b 100644
--- a/src/test/java/cuchaz/enigma/inputs/constructors/BaseClass.java
+++ b/src/test/java/cuchaz/enigma/inputs/constructors/BaseClass.java
@@ -4,20 +4,21 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.constructors; 12package cuchaz.enigma.inputs.constructors;
12 13
13// a 14// a
14public class BaseClass { 15public class BaseClass {
15 16
16 // <init>()V 17 // <init>()V
17 public BaseClass() { 18 public BaseClass() {
18 System.out.println("Default constructor"); 19 System.out.println("Default constructor");
19 } 20 }
20 21
21 // <init>(I)V 22 // <init>(I)V
22 public BaseClass(int i) { 23 public BaseClass(int i) {
23 System.out.println("Int constructor " + i); 24 System.out.println("Int constructor " + i);
diff --git a/src/test/java/cuchaz/enigma/inputs/constructors/Caller.java b/src/test/java/cuchaz/enigma/inputs/constructors/Caller.java
index dcd96173..71439fd1 100644
--- a/src/test/java/cuchaz/enigma/inputs/constructors/Caller.java
+++ b/src/test/java/cuchaz/enigma/inputs/constructors/Caller.java
@@ -4,51 +4,52 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.constructors; 12package cuchaz.enigma.inputs.constructors;
12 13
13// b 14// b
14public class Caller { 15public class Caller {
15 16
16 // a()V 17 // a()V
17 public void callBaseDefault() { 18 public void callBaseDefault() {
18 // a.<init>()V 19 // a.<init>()V
19 System.out.println(new BaseClass()); 20 System.out.println(new BaseClass());
20 } 21 }
21 22
22 // b()V 23 // b()V
23 public void callBaseInt() { 24 public void callBaseInt() {
24 // a.<init>(I)V 25 // a.<init>(I)V
25 System.out.println(new BaseClass(5)); 26 System.out.println(new BaseClass(5));
26 } 27 }
27 28
28 // c()V 29 // c()V
29 public void callSubDefault() { 30 public void callSubDefault() {
30 // d.<init>()V 31 // d.<init>()V
31 System.out.println(new SubClass()); 32 System.out.println(new SubClass());
32 } 33 }
33 34
34 // d()V 35 // d()V
35 public void callSubInt() { 36 public void callSubInt() {
36 // d.<init>(I)V 37 // d.<init>(I)V
37 System.out.println(new SubClass(6)); 38 System.out.println(new SubClass(6));
38 } 39 }
39 40
40 // e()V 41 // e()V
41 public void callSubIntInt() { 42 public void callSubIntInt() {
42 // d.<init>(II)V 43 // d.<init>(II)V
43 System.out.println(new SubClass(4, 2)); 44 System.out.println(new SubClass(4, 2));
44 } 45 }
45 46
46 // f()V 47 // f()V
47 public void callSubSubInt() { 48 public void callSubSubInt() {
48 // e.<init>(I)V 49 // e.<init>(I)V
49 System.out.println(new SubSubClass(3)); 50 System.out.println(new SubSubClass(3));
50 } 51 }
51 52
52 // g()V 53 // g()V
53 public void callDefaultConstructable() { 54 public void callDefaultConstructable() {
54 // c.<init>()V 55 // c.<init>()V
diff --git a/src/test/java/cuchaz/enigma/inputs/constructors/DefaultConstructable.java b/src/test/java/cuchaz/enigma/inputs/constructors/DefaultConstructable.java
index 655f4da3..c3d41705 100644
--- a/src/test/java/cuchaz/enigma/inputs/constructors/DefaultConstructable.java
+++ b/src/test/java/cuchaz/enigma/inputs/constructors/DefaultConstructable.java
@@ -4,10 +4,11 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.constructors; 12package cuchaz.enigma.inputs.constructors;
12 13
13public class DefaultConstructable { 14public class DefaultConstructable {
diff --git a/src/test/java/cuchaz/enigma/inputs/constructors/SubClass.java b/src/test/java/cuchaz/enigma/inputs/constructors/SubClass.java
index b2934a27..bc56b3b2 100644
--- a/src/test/java/cuchaz/enigma/inputs/constructors/SubClass.java
+++ b/src/test/java/cuchaz/enigma/inputs/constructors/SubClass.java
@@ -4,33 +4,34 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.constructors; 12package cuchaz.enigma.inputs.constructors;
12 13
13// d extends a 14// d extends a
14public class SubClass extends BaseClass { 15public class SubClass extends BaseClass {
15 16
16 // <init>()V 17 // <init>()V
17 public SubClass() { 18 public SubClass() {
18 // a.<init>()V 19 // a.<init>()V
19 } 20 }
20 21
21 // <init>(I)V 22 // <init>(I)V
22 public SubClass(int num) { 23 public SubClass(int num) {
23 // <init>()V 24 // <init>()V
24 this(); 25 this();
25 System.out.println("SubClass " + num); 26 System.out.println("SubClass " + num);
26 } 27 }
27 28
28 // <init>(II)V 29 // <init>(II)V
29 public SubClass(int a, int b) { 30 public SubClass(int a, int b) {
30 // <init>(I)V 31 // <init>(I)V
31 this(a + b); 32 this(a + b);
32 } 33 }
33 34
34 // <init>(III)V 35 // <init>(III)V
35 public SubClass(int a, int b, int c) { 36 public SubClass(int a, int b, int c) {
36 // a.<init>()V 37 // a.<init>()V
diff --git a/src/test/java/cuchaz/enigma/inputs/constructors/SubSubClass.java b/src/test/java/cuchaz/enigma/inputs/constructors/SubSubClass.java
index c1725fea..87b69d32 100644
--- a/src/test/java/cuchaz/enigma/inputs/constructors/SubSubClass.java
+++ b/src/test/java/cuchaz/enigma/inputs/constructors/SubSubClass.java
@@ -4,15 +4,16 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.constructors; 12package cuchaz.enigma.inputs.constructors;
12 13
13// e extends d 14// e extends d
14public class SubSubClass extends SubClass { 15public class SubSubClass extends SubClass {
15 16
16 // <init>(I)V 17 // <init>(I)V
17 public SubSubClass(int i) { 18 public SubSubClass(int i) {
18 // c.<init>(I)V 19 // c.<init>(I)V
diff --git a/src/test/java/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java b/src/test/java/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java
index 1b1f3694..b9c4929c 100644
--- a/src/test/java/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java
+++ b/src/test/java/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java
@@ -4,28 +4,29 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.inheritanceTree; 12package cuchaz.enigma.inputs.inheritanceTree;
12 13
13// a 14// a
14public abstract class BaseClass { 15public abstract class BaseClass {
15 16
16 // a 17 // a
17 private String name; 18 private String name;
18 19
19 // <init>(Ljava/lang/String;)V 20 // <init>(Ljava/lang/String;)V
20 protected BaseClass(String name) { 21 protected BaseClass(String name) {
21 this.name = name; 22 this.name = name;
22 } 23 }
23 24
24 // a()Ljava/lang/String; 25 // a()Ljava/lang/String;
25 public String getName() { 26 public String getName() {
26 return name; 27 return name;
27 } 28 }
28 29
29 // a()V 30 // a()V
30 public abstract void doBaseThings(); 31 public abstract void doBaseThings();
31} 32}
diff --git a/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java b/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java
index d0213a37..50e963c0 100644
--- a/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java
+++ b/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java
@@ -4,15 +4,16 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.inheritanceTree; 12package cuchaz.enigma.inputs.inheritanceTree;
12 13
13// b extends a 14// b extends a
14public abstract class SubclassA extends BaseClass { 15public abstract class SubclassA extends BaseClass {
15 16
16 // <init>(Ljava/lang/String;)V 17 // <init>(Ljava/lang/String;)V
17 protected SubclassA(String name) { 18 protected SubclassA(String name) {
18 // call to a.<init>(Ljava/lang/String)V 19 // call to a.<init>(Ljava/lang/String)V
diff --git a/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java b/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java
index 6d3b0d0f..d0dd664d 100644
--- a/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java
+++ b/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java
@@ -4,34 +4,35 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.inheritanceTree; 12package cuchaz.enigma.inputs.inheritanceTree;
12 13
13// c extends a 14// c extends a
14public class SubclassB extends BaseClass { 15public class SubclassB extends BaseClass {
15 16
16 // a 17 // a
17 private int numThings; 18 private int numThings;
18 19
19 // <init>()V 20 // <init>()V
20 protected SubclassB() { 21 protected SubclassB() {
21 // a.<init>(Ljava/lang/String;)V 22 // a.<init>(Ljava/lang/String;)V
22 super("B"); 23 super("B");
23 24
24 // access to a 25 // access to a
25 numThings = 4; 26 numThings = 4;
26 } 27 }
27 28
28 @Override 29 @Override
29 // a()V 30 // a()V
30 public void doBaseThings() { 31 public void doBaseThings() {
31 // call to a.a()Ljava/lang/String; 32 // call to a.a()Ljava/lang/String;
32 System.out.println("Base things by B! " + getName()); 33 System.out.println("Base things by B! " + getName());
33 } 34 }
34 35
35 // b()V 36 // b()V
36 public void doBThings() { 37 public void doBThings() {
37 // access to a 38 // access to a
diff --git a/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java b/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java
index a5b25fd5..c5845702 100644
--- a/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java
+++ b/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java
@@ -4,27 +4,28 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.inheritanceTree; 12package cuchaz.enigma.inputs.inheritanceTree;
12 13
13// d extends b 14// d extends b
14public class SubsubclassAA extends SubclassA { 15public class SubsubclassAA extends SubclassA {
15 16
16 protected SubsubclassAA() { 17 protected SubsubclassAA() {
17 // call to b.<init>(Ljava/lang/String;)V 18 // call to b.<init>(Ljava/lang/String;)V
18 super("AA"); 19 super("AA");
19 } 20 }
20 21
21 @Override 22 @Override
22 // a()Ljava/lang/String; 23 // a()Ljava/lang/String;
23 public String getName() { 24 public String getName() {
24 // call to b.a()Ljava/lang/String; 25 // call to b.a()Ljava/lang/String;
25 return "subsub" + super.getName(); 26 return "subsub" + super.getName();
26 } 27 }
27 28
28 @Override 29 @Override
29 // a()V 30 // a()V
30 public void doBaseThings() { 31 public void doBaseThings() {
diff --git a/src/test/java/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java b/src/test/java/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java
index f6444396..f652d875 100644
--- a/src/test/java/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java
+++ b/src/test/java/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java
@@ -4,14 +4,15 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.innerClasses; 12package cuchaz.enigma.inputs.innerClasses;
12 13
13public class A_Anonymous { 14public class A_Anonymous {
14 15
15 public void foo() { 16 public void foo() {
16 Runnable runnable = new Runnable() { 17 Runnable runnable = new Runnable() {
17 @Override 18 @Override
diff --git a/src/test/java/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java b/src/test/java/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java
index d78be847..d1b7601f 100644
--- a/src/test/java/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java
+++ b/src/test/java/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java
@@ -4,14 +4,15 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.innerClasses; 12package cuchaz.enigma.inputs.innerClasses;
12 13
13public class B_AnonymousWithScopeArgs { 14public class B_AnonymousWithScopeArgs {
14 15
15 public static void foo(final D_Simple arg) { 16 public static void foo(final D_Simple arg) {
16 System.out.println(new Object() { 17 System.out.println(new Object() {
17 @Override 18 @Override
diff --git a/src/test/java/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java b/src/test/java/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java
index eb03489d..94061faa 100644
--- a/src/test/java/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java
+++ b/src/test/java/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java
@@ -4,27 +4,28 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.innerClasses; 12package cuchaz.enigma.inputs.innerClasses;
12 13
13@SuppressWarnings("unused") 14@SuppressWarnings("unused")
14public class C_ConstructorArgs { 15public class C_ConstructorArgs {
15 16
17 Inner i;
18
19 public void foo() {
20 i = new Inner(5);
21 }
22
16 class Inner { 23 class Inner {
17 24
18 private int a; 25 private int a;
19 26
20 public Inner(int a) { 27 public Inner(int a) {
21 this.a = a; 28 this.a = a;
22 } 29 }
23 } 30 }
24
25 Inner i;
26
27 public void foo() {
28 i = new Inner(5);
29 }
30} 31}
diff --git a/src/test/java/cuchaz/enigma/inputs/innerClasses/D_Simple.java b/src/test/java/cuchaz/enigma/inputs/innerClasses/D_Simple.java
index 0e9bf827..71b3a6d8 100644
--- a/src/test/java/cuchaz/enigma/inputs/innerClasses/D_Simple.java
+++ b/src/test/java/cuchaz/enigma/inputs/innerClasses/D_Simple.java
@@ -4,14 +4,15 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.innerClasses; 12package cuchaz.enigma.inputs.innerClasses;
12 13
13public class D_Simple { 14public class D_Simple {
14 15
15 class Inner { 16 class Inner {
16 // nothing to do 17 // nothing to do
17 } 18 }
diff --git a/src/test/java/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java b/src/test/java/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java
index 255434d1..976ec426 100644
--- a/src/test/java/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java
+++ b/src/test/java/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java
@@ -4,17 +4,18 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.innerClasses; 12package cuchaz.enigma.inputs.innerClasses;
12 13
13public class E_AnonymousWithOuterAccess { 14public class E_AnonymousWithOuterAccess {
14 15
15 // reproduction of error case documented at: 16 // reproduction of error case documented at:
16 // https://bitbucket.org/cuchaz/enigma/issue/61/stackoverflowerror-when-deobfuscating 17 // https://bitbucket.org/cuchaz/enigma/issue/61/stackoverflowerror-when-deobfuscating
17 18
18 public Object makeInner() { 19 public Object makeInner() {
19 outerMethod(); 20 outerMethod();
20 return new Object() { 21 return new Object() {
@@ -24,7 +25,7 @@ public class E_AnonymousWithOuterAccess {
24 } 25 }
25 }; 26 };
26 } 27 }
27 28
28 private String outerMethod() { 29 private String outerMethod() {
29 return "foo"; 30 return "foo";
30 } 31 }
diff --git a/src/test/java/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java b/src/test/java/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java
index 7d1dab41..b1de3c9a 100644
--- a/src/test/java/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java
+++ b/src/test/java/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java
@@ -4,25 +4,25 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.inputs.innerClasses;
12 11
12package cuchaz.enigma.inputs.innerClasses;
13 13
14public class F_ClassTree { 14public class F_ClassTree {
15 15
16 public class Level1 { 16 public class Level1 {
17 17
18 public int f1; 18 public int f1;
19 19
20 public class Level2 { 20 public class Level2 {
21 21
22 public int f2; 22 public int f2;
23 23
24 public class Level3 { 24 public class Level3 {
25 25
26 public int f3; 26 public int f3;
27 } 27 }
28 } 28 }
diff --git a/src/test/java/cuchaz/enigma/inputs/loneClass/LoneClass.java b/src/test/java/cuchaz/enigma/inputs/loneClass/LoneClass.java
index d28ae97c..ddc4e319 100644
--- a/src/test/java/cuchaz/enigma/inputs/loneClass/LoneClass.java
+++ b/src/test/java/cuchaz/enigma/inputs/loneClass/LoneClass.java
@@ -4,20 +4,21 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.loneClass; 12package cuchaz.enigma.inputs.loneClass;
12 13
13public class LoneClass { 14public class LoneClass {
14 15
15 private String name; 16 private String name;
16 17
17 public LoneClass(String name) { 18 public LoneClass(String name) {
18 this.name = name; 19 this.name = name;
19 } 20 }
20 21
21 public String getName() { 22 public String getName() {
22 return name; 23 return name;
23 } 24 }
diff --git a/src/test/java/cuchaz/enigma/inputs/translation/A_Basic.java b/src/test/java/cuchaz/enigma/inputs/translation/A_Basic.java
index 26acac8a..26f3718c 100644
--- a/src/test/java/cuchaz/enigma/inputs/translation/A_Basic.java
+++ b/src/test/java/cuchaz/enigma/inputs/translation/A_Basic.java
@@ -4,28 +4,29 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.translation; 12package cuchaz.enigma.inputs.translation;
12 13
13public class A_Basic { 14public class A_Basic {
14 15
15 public int one; 16 public int one;
16 public float two; 17 public float two;
17 public String three; 18 public String three;
18 19
19 public void m1() { 20 public void m1() {
20 } 21 }
21 22
22 public int m2() { 23 public int m2() {
23 return 42; 24 return 42;
24 } 25 }
25 26
26 public void m3(int a1) { 27 public void m3(int a1) {
27 } 28 }
28 29
29 public int m4(int a1) { 30 public int m4(int a1) {
30 return 5; // chosen by fair die roll, guaranteed to be random 31 return 5; // chosen by fair die roll, guaranteed to be random
31 } 32 }
diff --git a/src/test/java/cuchaz/enigma/inputs/translation/B_BaseClass.java b/src/test/java/cuchaz/enigma/inputs/translation/B_BaseClass.java
index 035e3299..fd7f6e7e 100644
--- a/src/test/java/cuchaz/enigma/inputs/translation/B_BaseClass.java
+++ b/src/test/java/cuchaz/enigma/inputs/translation/B_BaseClass.java
@@ -4,21 +4,22 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.translation; 12package cuchaz.enigma.inputs.translation;
12 13
13public class B_BaseClass { 14public class B_BaseClass {
14 15
15 public int f1; 16 public int f1;
16 public char f2; 17 public char f2;
17 18
18 public int m1() { 19 public int m1() {
19 return 5; 20 return 5;
20 } 21 }
21 22
22 public int m2() { 23 public int m2() {
23 return 42; 24 return 42;
24 } 25 }
diff --git a/src/test/java/cuchaz/enigma/inputs/translation/C_SubClass.java b/src/test/java/cuchaz/enigma/inputs/translation/C_SubClass.java
index 6026a8d5..9d74e443 100644
--- a/src/test/java/cuchaz/enigma/inputs/translation/C_SubClass.java
+++ b/src/test/java/cuchaz/enigma/inputs/translation/C_SubClass.java
@@ -4,23 +4,24 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.translation; 12package cuchaz.enigma.inputs.translation;
12 13
13public class C_SubClass extends B_BaseClass { 14public class C_SubClass extends B_BaseClass {
14 15
15 public char f2; // shadows B_BaseClass.f2 16 public char f2; // shadows B_BaseClass.f2
16 public int f3; 17 public int f3;
17 public int f4; 18 public int f4;
18 19
19 @Override 20 @Override
20 public int m1() { 21 public int m1() {
21 return 32; 22 return 32;
22 } 23 }
23 24
24 public int m3() { 25 public int m3() {
25 return 7; 26 return 7;
26 } 27 }
diff --git a/src/test/java/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java b/src/test/java/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java
index a1827f98..99c83bbf 100644
--- a/src/test/java/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java
+++ b/src/test/java/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java
@@ -4,17 +4,18 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.translation; 12package cuchaz.enigma.inputs.translation;
12 13
13import java.util.ArrayList; 14import java.util.ArrayList;
14import java.util.List; 15import java.util.List;
15 16
16public class D_AnonymousTesting { 17public class D_AnonymousTesting {
17 18
18 public List<Object> getObjs() { 19 public List<Object> getObjs() {
19 List<Object> objs = new ArrayList<Object>(); 20 List<Object> objs = new ArrayList<Object>();
20 objs.add(new Object() { 21 objs.add(new Object() {
diff --git a/src/test/java/cuchaz/enigma/inputs/translation/E_Bridges.java b/src/test/java/cuchaz/enigma/inputs/translation/E_Bridges.java
index 769eb70e..0b8cf2a5 100644
--- a/src/test/java/cuchaz/enigma/inputs/translation/E_Bridges.java
+++ b/src/test/java/cuchaz/enigma/inputs/translation/E_Bridges.java
@@ -4,15 +4,15 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.translation; 12package cuchaz.enigma.inputs.translation;
12 13
13import java.util.Iterator; 14import java.util.Iterator;
14 15
15
16public class E_Bridges implements Iterator<Object> { 16public class E_Bridges implements Iterator<Object> {
17 17
18 @Override 18 @Override
diff --git a/src/test/java/cuchaz/enigma/inputs/translation/F_ObjectMethods.java b/src/test/java/cuchaz/enigma/inputs/translation/F_ObjectMethods.java
index 845d62b0..8a92792a 100644
--- a/src/test/java/cuchaz/enigma/inputs/translation/F_ObjectMethods.java
+++ b/src/test/java/cuchaz/enigma/inputs/translation/F_ObjectMethods.java
@@ -4,17 +4,18 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.translation; 12package cuchaz.enigma.inputs.translation;
12 13
13@SuppressWarnings("FinalizeCalledExplicitly") 14@SuppressWarnings("FinalizeCalledExplicitly")
14public class F_ObjectMethods { 15public class F_ObjectMethods {
15 16
16 public void callEmAll() 17 public void callEmAll()
17 throws Throwable { 18 throws Throwable {
18 clone(); 19 clone();
19 equals(this); 20 equals(this);
20 finalize(); 21 finalize();
diff --git a/src/test/java/cuchaz/enigma/inputs/translation/G_OuterClass.java b/src/test/java/cuchaz/enigma/inputs/translation/G_OuterClass.java
index a2e0dafb..a1e6a85c 100644
--- a/src/test/java/cuchaz/enigma/inputs/translation/G_OuterClass.java
+++ b/src/test/java/cuchaz/enigma/inputs/translation/G_OuterClass.java
@@ -4,30 +4,30 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.inputs.translation;
12 11
12package cuchaz.enigma.inputs.translation;
13 13
14public class G_OuterClass { 14public class G_OuterClass {
15 15
16 public class A_InnerClass { 16 public class A_InnerClass {
17 17
18 public int f1; 18 public int f1;
19 public String f2; 19 public String f2;
20 20
21 public void m1() {} 21 public void m1() {}
22 22
23 public class A_InnerInnerClass { 23 public class A_InnerInnerClass {
24 24
25 public int f3; 25 public int f3;
26 26
27 public void m2() {} 27 public void m2() {}
28 } 28 }
29 } 29 }
30 30
31 public class B_NamelessClass { 31 public class B_NamelessClass {
32 public class A_NamedInnerClass { 32 public class A_NamedInnerClass {
33 public int f4; 33 public int f4;
diff --git a/src/test/java/cuchaz/enigma/inputs/translation/H_NamelessClass.java b/src/test/java/cuchaz/enigma/inputs/translation/H_NamelessClass.java
index 1b718a54..013c55ae 100644
--- a/src/test/java/cuchaz/enigma/inputs/translation/H_NamelessClass.java
+++ b/src/test/java/cuchaz/enigma/inputs/translation/H_NamelessClass.java
@@ -4,34 +4,36 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.inputs.translation;
12 11
12package cuchaz.enigma.inputs.translation;
13 13
14public class H_NamelessClass { 14public class H_NamelessClass {
15 15
16 public class A_InnerClass { 16 public class A_InnerClass {
17 17
18 public int f1; 18 public int f1;
19 public String f2; 19 public String f2;
20 20
21 public void m1() {} 21 public void m1() {}
22 22
23 public class A_InnerInnerClass { 23 public class A_InnerInnerClass {
24 24
25 public int f3; 25 public int f3;
26 26
27 public void m2() {} 27 public void m2() {}
28 } 28 }
29 } 29 }
30 30
31 public class B_NamelessClass { 31 public class B_NamelessClass {
32 public class A_NamedInnerClass { 32 public class A_NamedInnerClass {
33 public int f4; 33 public int f4;
34
34 public class A_AnotherInnerClass {} 35 public class A_AnotherInnerClass {}
36
35 public class B_YetAnotherInnerClass {} 37 public class B_YetAnotherInnerClass {}
36 } 38 }
37 } 39 }
diff --git a/src/test/java/cuchaz/enigma/inputs/translation/I_Generics.java b/src/test/java/cuchaz/enigma/inputs/translation/I_Generics.java
index 3490f9d9..fd2ebdd5 100644
--- a/src/test/java/cuchaz/enigma/inputs/translation/I_Generics.java
+++ b/src/test/java/cuchaz/enigma/inputs/translation/I_Generics.java
@@ -4,32 +4,32 @@
4 * are made available under the terms of the GNU Lesser General Public 4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at 5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html 6 * http://www.gnu.org/licenses/lgpl.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.inputs.translation; 12package cuchaz.enigma.inputs.translation;
12 13
13import java.util.List; 14import java.util.List;
14import java.util.Map; 15import java.util.Map;
15 16
16
17public class I_Generics { 17public class I_Generics {
18 18
19 public class A_Type {
20 }
21
22 public List<Integer> f1; 19 public List<Integer> f1;
23 public List<A_Type> f2; 20 public List<A_Type> f2;
24 public Map<A_Type,A_Type> f3; 21 public Map<A_Type, A_Type> f3;
25 22 public B_Generic<Integer> f5;
23 public B_Generic<A_Type> f6;
24
25 public class A_Type {
26 }
27
26 public class B_Generic<T> { 28 public class B_Generic<T> {
27 public T f4; 29 public T f4;
30
28 public T m1() { 31 public T m1() {
29 return null; 32 return null;
30 } 33 }
31 } 34 }
32
33 public B_Generic<Integer> f5;
34 public B_Generic<A_Type> f6;
35} 35}