summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar modmuss502022-09-14 13:12:55 +0100
committerGravatar GitHub2022-09-14 13:12:55 +0100
commit9c736848fb7aa82d295b3aa2946e6cd132ee998f (patch)
treeb982613cfa7201b2db25cb64a5950f9a2c34a5b3
parentNested packages in Swing UI (#458) (diff)
downloadenigma-fork-9c736848fb7aa82d295b3aa2946e6cd132ee998f.tar.gz
enigma-fork-9c736848fb7aa82d295b3aa2946e6cd132ee998f.tar.xz
enigma-fork-9c736848fb7aa82d295b3aa2946e6cd132ee998f.zip
Add checkstyle (#460)
-rw-r--r--.editorconfig5
-rw-r--r--.github/workflows/build.yml9
-rw-r--r--.github/workflows/release.yml2
-rw-r--r--README.md15
-rw-r--r--build.gradle156
-rw-r--r--checkstyle.xml170
-rw-r--r--enigma-cli/build.gradle16
-rw-r--r--enigma-cli/src/main/java/cuchaz/enigma/command/CheckMappingsCommand.java25
-rw-r--r--enigma-cli/src/main/java/cuchaz/enigma/command/Command.java32
-rw-r--r--enigma-cli/src/main/java/cuchaz/enigma/command/ComposeMappingsCommand.java64
-rw-r--r--enigma-cli/src/main/java/cuchaz/enigma/command/ConvertMappingsCommand.java58
-rw-r--r--enigma-cli/src/main/java/cuchaz/enigma/command/DecompileCommand.java11
-rw-r--r--enigma-cli/src/main/java/cuchaz/enigma/command/DeobfuscateCommand.java5
-rw-r--r--enigma-cli/src/main/java/cuchaz/enigma/command/InvertMappingsCommand.java62
-rw-r--r--enigma-cli/src/main/java/cuchaz/enigma/command/Main.java33
-rw-r--r--enigma-cli/src/main/java/cuchaz/enigma/command/MapSpecializedMethodsCommand.java92
-rw-r--r--enigma-cli/src/main/java/cuchaz/enigma/command/MappingCommandsUtil.java151
-rw-r--r--enigma-cli/src/test/java/cuchaz/enigma/command/CheckMappingsCommandTest.java10
-rw-r--r--enigma-server/build.gradle6
-rw-r--r--enigma-server/docs/protocol.md50
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/ClientPacketHandler.java18
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/DedicatedEnigmaServer.java74
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/EnigmaClient.java25
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/EnigmaServer.java47
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/IntegratedEnigmaServer.java4
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/Message.java155
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/ServerAddress.java44
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/ServerPacketHandler.java1
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/ConfirmChangeC2SPacket.java4
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeC2SPacket.java2
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeS2CPacket.java2
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/KickS2CPacket.java4
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/LoginC2SPacket.java13
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageC2SPacket.java5
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageS2CPacket.java2
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/Packet.java2
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketHelper.java109
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketRegistry.java2
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/SyncMappingsS2CPacket.java9
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/UserListS2CPacket.java8
-rw-r--r--enigma-server/src/test/java/cuchaz/enigma/network/ServerAddressTest.java6
-rw-r--r--enigma-swing/build.gradle30
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/BrowserCaret.java20
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java69
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/EditableType.java9
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaQuickFindDialog.java26
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaSyntaxKit.java55
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/ExceptionIgnorer.java24
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java127
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java149
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/Main.java106
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/NestedPackages.java37
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/QuickFindAction.java9
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/ReadableToken.java19
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java30
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/config/LookAndFeel.java11
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/config/NetConfig.java2
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/config/OldConfigImporter.java4
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/config/Themes.java9
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java15
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/config/legacy/Config.java20
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java31
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AbstractDialog.java12
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ChangeDialog.java2
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ConnectToServerDialog.java27
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java50
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CreateServerDialog.java24
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/FontDialog.java30
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/JavadocDialog.java74
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java33
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java92
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/StatsDialog.java23
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/elements/AbstractInheritanceTree.java9
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CallsTree.java16
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CollapsibleTabbedPane.java11
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ConvertingTextField.java43
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/elements/DeobfPanelPopupMenu.java101
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorPopupMenu.java66
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabPopupMenu.java1
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabbedPane.java11
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/elements/JMultiLineToolTip.java19
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java99
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/elements/StatusBar.java2
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatablePasswordField.java2
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextArea.java2
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextField.java2
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableUi.java43
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/events/ConvertingTextFieldListener.java2
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/events/EditorActionListener.java2
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/events/ThemeChangeListener.java2
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java19
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java26
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/newabstraction/EntryValidation.java3
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java34
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java27
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ClosableTabTitlePane.java28
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/panels/DeobfPanel.java3
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java223
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/panels/IdentifierPanel.java48
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ObfPanel.java4
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/panels/StructurePanel.java291
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/CallsTreeCellRenderer.java78
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/ImplementationsTreeCellRenderer.java39
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/InheritanceTreeCellRenderer.java19
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/MessageListCellRenderer.java4
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/StructureOptionListCellRenderer.java25
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchEntry.java2
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchUtil.java71
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java212
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsMember.java8
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java6
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/util/AbstractListCellRenderer.java17
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/util/GridBagConstraintsBuilder.java2
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java334
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/util/History.java4
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageChangeListener.java2
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageUtil.java2
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleChangeListener.java2
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleUtil.java5
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/util/SingleTreeSelectionModel.java7
-rw-r--r--enigma/build.gradle74
-rw-r--r--enigma/src/main/java/cuchaz/enigma/Enigma.java25
-rw-r--r--enigma/src/main/java/cuchaz/enigma/EnigmaProfile.java35
-rw-r--r--enigma/src/main/java/cuchaz/enigma/EnigmaProject.java97
-rw-r--r--enigma/src/main/java/cuchaz/enigma/EnigmaServices.java5
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/Access.java29
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java56
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java31
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java29
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java37
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/EntryReference.java31
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java24
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/IndexSimpleVerifier.java292
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java15
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/InterpreterPair.java213
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java34
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java26
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/MethodNodeWithAction.java22
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java39
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/ReferenceTargetType.java136
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java18
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeNode.java354
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeOptions.java108
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java16
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java16
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java7
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java65
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java39
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java42
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java6
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/index/PackageVisibilityIndex.java27
-rw-r--r--enigma/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java25
-rw-r--r--enigma/src/main/java/cuchaz/enigma/api/service/EnigmaServiceType.java4
-rw-r--r--enigma/src/main/java/cuchaz/enigma/api/service/JarIndexerService.java9
-rw-r--r--enigma/src/main/java/cuchaz/enigma/api/service/NameProposalService.java4
-rw-r--r--enigma/src/main/java/cuchaz/enigma/bytecode/translators/AsmObjectTranslator.java33
-rw-r--r--enigma/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableFixVisitor.java17
-rw-r--r--enigma/src/main/java/cuchaz/enigma/bytecode/translators/SourceFixVisitor.java8
-rw-r--r--enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationAnnotationVisitor.java5
-rw-r--r--enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java39
-rw-r--r--enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java7
-rw-r--r--enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java21
-rw-r--r--enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationRecordComponentVisitor.java5
-rw-r--r--enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationSignatureVisitor.java22
-rw-r--r--enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandle.java2
-rw-r--r--enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleError.java9
-rw-r--r--enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java75
-rw-r--r--enigma/src/main/java/cuchaz/enigma/classprovider/CachingClassProvider.java43
-rw-r--r--enigma/src/main/java/cuchaz/enigma/classprovider/ClassProvider.java22
-rw-r--r--enigma/src/main/java/cuchaz/enigma/classprovider/ClasspathClassProvider.java35
-rw-r--r--enigma/src/main/java/cuchaz/enigma/classprovider/CombiningClassProvider.java34
-rw-r--r--enigma/src/main/java/cuchaz/enigma/classprovider/JarClassProvider.java88
-rw-r--r--enigma/src/main/java/cuchaz/enigma/classprovider/ObfuscationFixClassProvider.java110
-rw-r--r--enigma/src/main/java/cuchaz/enigma/config/ConfigContainer.java8
-rw-r--r--enigma/src/main/java/cuchaz/enigma/config/ConfigPaths.java30
-rw-r--r--enigma/src/main/java/cuchaz/enigma/config/ConfigSection.java24
-rw-r--r--enigma/src/main/java/cuchaz/enigma/config/ConfigSerializer.java136
-rw-r--r--enigma/src/main/java/cuchaz/enigma/config/ConfigStructureVisitor.java2
-rw-r--r--enigma/src/main/java/cuchaz/enigma/events/ClassHandleListener.java2
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java16
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/Decompiler.java3
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/DecompilerService.java6
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/Decompilers.java6
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/Source.java6
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/SourceIndex.java316
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/SourceRemapper.java2
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/SourceSettings.java12
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/Token.java19
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/TokenStore.java25
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/bytecode/BytecodeDecompiler.java19
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/bytecode/BytecodeSource.java73
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/bytecode/EnigmaTextifier.java13
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java93
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java142
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java781
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/procyon/EntryParser.java57
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java28
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java71
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexClassVisitor.java38
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexMethodVisitor.java93
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexVisitor.java20
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/procyon/index/TokenFactory.java68
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java54
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/DropVarModifiersAstTransform.java11
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/InvalidIdentifierFix.java6
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/Java8Generics.java53
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/ObfuscatedEnumSwitchRewriterTransform.java542
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/RemoveObjectCasts.java6
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/VarargsFixer.java50
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/LocalNameGenerator.java2
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/MappingTranslator.java2
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/ProposingTranslator.java17
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/SignatureUpdater.java27
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/Translatable.java2
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/TranslateResult.java15
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/TranslationDirection.java21
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/Translator.java32
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/VoidTranslator.java1
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/AccessModifier.java13
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryChange.java17
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMap.java5
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java6
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java12
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryResolver.java15
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryUtil.java2
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/IdentifierValidation.java50
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/IndexEntryResolver.java25
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java5
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingOperations.java121
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingPair.java4
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java11
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java33
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/VoidEntryResolver.java6
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/LfPrintWriter.java16
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java16
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingHelper.java8
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingParseException.java19
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsReader.java6
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java4
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsReader.java76
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsWriter.java81
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/proguard/ProguardMappingsReader.java238
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/recaf/RecafMappingsReader.java19
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/recaf/RecafMappingsWriter.java27
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/srg/SrgMappingsWriter.java29
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsReader.java35
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsWriter.java253
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Reader.java232
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Writer.java36
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java11
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTreeNode.java13
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java46
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashTreeNode.java9
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java9
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/Lambda.java39
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/MethodDescriptor.java29
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/Signature.java12
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java56
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java26
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java51
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java31
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java28
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java23
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java5
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java6
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java23
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java25
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java22
-rw-r--r--enigma/src/main/java/cuchaz/enigma/utils/AsmUtil.java22
-rw-r--r--enigma/src/main/java/cuchaz/enigma/utils/I18n.java16
-rw-r--r--enigma/src/main/java/cuchaz/enigma/utils/Os.java8
-rw-r--r--enigma/src/main/java/cuchaz/enigma/utils/Pair.java31
-rw-r--r--enigma/src/main/java/cuchaz/enigma/utils/Result.java55
-rw-r--r--enigma/src/main/java/cuchaz/enigma/utils/TristateChange.java20
-rw-r--r--enigma/src/main/java/cuchaz/enigma/utils/Utils.java160
-rw-r--r--enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java2
-rw-r--r--enigma/src/main/java/cuchaz/enigma/utils/validation/ParameterizedMessage.java16
-rw-r--r--enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java8
-rw-r--r--enigma/src/main/java/cuchaz/enigma/utils/validation/StandardValidation.java15
-rw-r--r--enigma/src/main/java/cuchaz/enigma/utils/validation/Validatable.java2
-rw-r--r--enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java14
-rw-r--r--enigma/src/test/java/cuchaz/enigma/ConfigTest.java32
-rw-r--r--enigma/src/test/java/cuchaz/enigma/PackageVisibilityIndexTest.java43
-rw-r--r--enigma/src/test/java/cuchaz/enigma/TestDeobfed.java54
-rw-r--r--enigma/src/test/java/cuchaz/enigma/TestDeobfuscator.java30
-rw-r--r--enigma/src/test/java/cuchaz/enigma/TestEntryFactory.java22
-rw-r--r--enigma/src/test/java/cuchaz/enigma/TestInnerClasses.java49
-rw-r--r--enigma/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java79
-rw-r--r--enigma/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java114
-rw-r--r--enigma/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java69
-rw-r--r--enigma/src/test/java/cuchaz/enigma/TestMethodDescriptor.java116
-rw-r--r--enigma/src/test/java/cuchaz/enigma/TestTokensConstructors.java98
-rw-r--r--enigma/src/test/java/cuchaz/enigma/TestTranslator.java31
-rw-r--r--enigma/src/test/java/cuchaz/enigma/TestTypeDescriptor.java29
-rw-r--r--enigma/src/test/java/cuchaz/enigma/TokenChecker.java40
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/Keep.java18
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/constructors/BaseClass.java19
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/constructors/Caller.java19
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/constructors/DefaultConstructable.java18
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/constructors/SubClass.java19
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/constructors/SubSubClass.java19
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java19
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java19
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java19
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java19
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java19
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java19
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java22
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/D_Simple.java19
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java19
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java22
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/loneClass/LoneClass.java19
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/packageAccess/SamePackageChild.java1
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/packageAccess/sub/OtherPackageChild.java1
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/translation/A_Basic.java19
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/translation/B_BaseClass.java19
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/translation/C_SubClass.java19
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java19
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/translation/E_Bridges.java19
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/translation/F_ObjectMethods.java22
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/translation/G_OuterClass.java27
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/translation/H_NamelessClass.java33
-rw-r--r--enigma/src/test/java/cuchaz/enigma/inputs/translation/I_Generics.java19
-rw-r--r--enigma/src/test/java/cuchaz/enigma/translation/mapping/TestComments.java38
-rw-r--r--enigma/src/test/java/cuchaz/enigma/translation/mapping/TestReadWriteCycle.java43
-rw-r--r--enigma/src/test/java/cuchaz/enigma/translation/mapping/TestTinyV2InnerClasses.java27
-rw-r--r--enigma/src/test/java/cuchaz/enigma/translation/mapping/TestV2Main.java6
-rw-r--r--enigma/src/test/java/cuchaz/enigma/translation/mapping/serde/recaf/TestRecaf.java52
328 files changed, 7514 insertions, 6406 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..308508d
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,5 @@
1[*.{gradle,java,kotlin}]
2indent_style = tab
3ij_continuation_indent_size = 8
4ij_java_imports_layout = $*, |, java.**, |, javax.**, |, *, |, cuchaz.enigma.**
5ij_java_class_count_to_use_import_on_demand = 999
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 93a2e1f..0abdf58 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,10 +1,10 @@
1name: Build 1name: Build
2on: [push, pull_request] 2on: [ push, pull_request ]
3jobs: 3jobs:
4 build: 4 build:
5 strategy: 5 strategy:
6 matrix: 6 matrix:
7 java: [17-jdk] 7 java: [ 17-jdk ]
8 runs-on: ubuntu-20.04 8 runs-on: ubuntu-20.04
9 container: 9 container:
10 image: openjdk:${{ matrix.java }} 10 image: openjdk:${{ matrix.java }}
@@ -13,3 +13,8 @@ jobs:
13 - uses: actions/checkout@v1 13 - uses: actions/checkout@v1
14 - uses: gradle/wrapper-validation-action@v1 14 - uses: gradle/wrapper-validation-action@v1
15 - run: ./gradlew build --stacktrace --warning-mode fail 15 - run: ./gradlew build --stacktrace --warning-mode fail
16 - uses: Juuxel/publish-checkstyle-report@v1
17 if: ${{ failure() }}
18 with:
19 reports: |
20 **/build/reports/checkstyle/*.xml
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 48aacde..9a4dc97 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,5 +1,5 @@
1name: Release 1name: Release
2on: [workflow_dispatch] # Manual trigger 2on: [ workflow_dispatch ] # Manual trigger
3jobs: 3jobs:
4 build: 4 build:
5 runs-on: ubuntu-20.04 5 runs-on: ubuntu-20.04
diff --git a/README.md b/README.md
index f3ab4c7..b156364 100644
--- a/README.md
+++ b/README.md
@@ -7,13 +7,14 @@ A tool for deobfuscation of Java bytecode. Forked from <https://bitbucket.org/cu
7Enigma is distributed under the [LGPL-3.0](LICENSE). 7Enigma is distributed under the [LGPL-3.0](LICENSE).
8 8
9Enigma includes the following open-source libraries: 9Enigma includes the following open-source libraries:
10 - A [modified version](https://github.com/FabricMC/procyon) of [Procyon](https://bitbucket.org/mstrobel/procyon) (Apache-2.0) 10
11 - A [modified version](https://github.com/FabricMC/cfr) of [CFR](https://github.com/leibnitz27/cfr) (MIT) 11- A [modified version](https://github.com/FabricMC/procyon) of [Procyon](https://bitbucket.org/mstrobel/procyon) (Apache-2.0)
12 - [Guava](https://github.com/google/guava) (Apache-2.0) 12- A [modified version](https://github.com/FabricMC/cfr) of [CFR](https://github.com/leibnitz27/cfr) (MIT)
13 - [SyntaxPane](https://github.com/Sciss/SyntaxPane) (Apache-2.0) 13- [Guava](https://github.com/google/guava) (Apache-2.0)
14 - [FlatLaf](https://github.com/JFormDesigner/FlatLaf) (Apache-2.0) 14- [SyntaxPane](https://github.com/Sciss/SyntaxPane) (Apache-2.0)
15 - [jopt-simple](https://github.com/jopt-simple/jopt-simple) (MIT) 15- [FlatLaf](https://github.com/JFormDesigner/FlatLaf) (Apache-2.0)
16 - [ASM](https://asm.ow2.io/) (BSD-3-Clause) 16- [jopt-simple](https://github.com/jopt-simple/jopt-simple) (MIT)
17- [ASM](https://asm.ow2.io/) (BSD-3-Clause)
17 18
18## Usage 19## Usage
19 20
diff --git a/build.gradle b/build.gradle
index 80cbd09..782d7ec 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,89 +1,95 @@
1plugins { 1plugins {
2 id 'maven-publish' 2 id 'maven-publish'
3} 3}
4 4
5subprojects { 5subprojects {
6 apply plugin: 'java' 6 apply plugin: 'java'
7 apply plugin: 'maven-publish' 7 apply plugin: 'maven-publish'
8 8 apply plugin: 'checkstyle'
9 sourceCompatibility = JavaVersion.VERSION_17 9
10 targetCompatibility = JavaVersion.VERSION_17 10 sourceCompatibility = JavaVersion.VERSION_17
11 11 targetCompatibility = JavaVersion.VERSION_17
12 repositories { 12
13 mavenLocal() 13 repositories {
14 mavenCentral() 14 mavenLocal()
15 maven { url 'https://maven.fabricmc.net/' } 15 mavenCentral()
16 } 16 maven { url 'https://maven.fabricmc.net/' }
17 17 }
18 dependencies { 18
19 implementation 'com.google.guava:guava:30.1.1-jre' 19 dependencies {
20 implementation 'com.google.code.gson:gson:2.8.7' 20 implementation 'com.google.guava:guava:30.1.1-jre'
21 21 implementation 'com.google.code.gson:gson:2.8.7'
22 testImplementation 'junit:junit:4.13.2' 22
23 testImplementation 'org.hamcrest:hamcrest:2.2' 23 testImplementation 'junit:junit:4.13.2'
24 } 24 testImplementation 'org.hamcrest:hamcrest:2.2'
25 25 }
26 group = 'cuchaz' 26
27 version = '2.1.0' 27 group = 'cuchaz'
28 28 version = '2.1.1'
29 version = version + (System.getenv("GITHUB_ACTIONS") ? "" : "+local") 29
30 30 version = version + (System.getenv("GITHUB_ACTIONS") ? "" : "+local")
31 task sourcesJar(type: Jar, dependsOn: classes) { 31
32 classifier = 'sources' 32 task sourcesJar(type: Jar, dependsOn: classes) {
33 from sourceSets.main.allSource 33 classifier = 'sources'
34 } 34 from sourceSets.main.allSource
35 35 }
36 java { 36
37 withSourcesJar() 37 java {
38 } 38 withSourcesJar()
39 39 }
40 tasks.withType(JavaCompile).configureEach { 40
41 it.options.encoding = "UTF-8" 41 tasks.withType(JavaCompile).configureEach {
42 42 it.options.encoding = "UTF-8"
43 it.options.release = 17 43
44 } 44 it.options.release = 17
45 45 }
46 publishing { 46
47 publications { 47 checkstyle {
48 "$project.name"(MavenPublication) { 48 configFile = rootProject.file('checkstyle.xml')
49 groupId project.group 49 toolVersion = '10.3.3'
50 artifactId project.name 50 }
51 version project.version 51
52 from components.java 52 publishing {
53 } 53 publications {
54 } 54 "$project.name"(MavenPublication) {
55 } 55 groupId project.group
56 artifactId project.name
57 version project.version
58 from components.java
59 }
60 }
61 }
56} 62}
57 63
58allprojects { 64allprojects {
59 publishing { 65 publishing {
60 repositories { 66 repositories {
61 mavenLocal() 67 mavenLocal()
62 68
63 def ENV = System.getenv() 69 def ENV = System.getenv()
64 if (ENV.MAVEN_URL) { 70 if (ENV.MAVEN_URL) {
65 maven { 71 maven {
66 url ENV.MAVEN_URL 72 url ENV.MAVEN_URL
67 credentials { 73 credentials {
68 username ENV.MAVEN_USERNAME 74 username ENV.MAVEN_USERNAME
69 password ENV.MAVEN_PASSWORD 75 password ENV.MAVEN_PASSWORD
70 } 76 }
71 } 77 }
72 } 78 }
73 } 79 }
74 } 80 }
75} 81}
76 82
77// A task to ensure that the version being released has not already been released. 83// A task to ensure that the version being released has not already been released.
78task checkVersion { 84task checkVersion {
79 doFirst { 85 doFirst {
80 def xml = new URL("https://maven.fabricmc.net/cuchaz/enigma/maven-metadata.xml").text 86 def xml = new URL("https://maven.fabricmc.net/cuchaz/enigma/maven-metadata.xml").text
81 def metadata = new XmlSlurper().parseText(xml) 87 def metadata = new XmlSlurper().parseText(xml)
82 def versions = metadata.versioning.versions.version*.text(); 88 def versions = metadata.versioning.versions.version*.text();
83 if (versions.contains(version)) { 89 if (versions.contains(version)) {
84 throw new RuntimeException("${version} has already been released!") 90 throw new RuntimeException("${version} has already been released!")
85 } 91 }
86 } 92 }
87} 93}
88 94
89publish.mustRunAfter checkVersion 95publish.mustRunAfter checkVersion
diff --git a/checkstyle.xml b/checkstyle.xml
new file mode 100644
index 0000000..5256fb0
--- /dev/null
+++ b/checkstyle.xml
@@ -0,0 +1,170 @@
1<?xml version="1.0"?>
2<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.2//EN" "http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
3<module name="Checker">
4 <property name="charset" value="UTF-8"/>
5 <property name="fileExtensions" value="java"/>
6 <property name="localeLanguage" value="en"/>
7 <property name="localeCountry" value="US"/>
8
9 <module name="NewlineAtEndOfFile"/>
10
11 <!-- disallow trailing whitespace -->
12 <module name="RegexpSingleline">
13 <property name="format" value="\s+$"/>
14 <property name="message" value="trailing whitespace"/>
15 </module>
16
17 <!-- note: RegexpMultiline shows nicer messages than Regexp, but has to be outside TreeWalker -->
18 <!-- disallow multiple consecutive blank lines -->
19 <module name="RegexpMultiline">
20 <property name="format" value="\n[\t ]*\r?\n[\t ]*\r?\n"/>
21 <property name="message" value="adjacent blank lines"/>
22 </module>
23
24 <!-- disallow blank after { -->
25 <module name="RegexpMultiline">
26 <property name="format" value="\{[\t ]*\r?\n[\t ]*\r?\n"/>
27 <property name="message" value="blank line after '{'"/>
28 </module>
29
30 <!-- disallow blank before } -->
31 <module name="RegexpMultiline">
32 <property name="format" value="\n[\t ]*\r?\n[\t ]*\}"/>
33 <property name="message" value="blank line before '}'"/>
34 </module>
35
36 <!-- require blank before { in the same indentation level -->
37 <module name="RegexpMultiline">
38 <!-- the regex works as follows:
39 It matches (=fails) for \n<indentation><something>\n<same indentation><control statement>[...]{\n
40 while <something> is a single line comment, it'll look for a blank line one line earlier
41 if <something> is a space, indicating a formatting error or ' */', it'll ignore the instance
42 if <something> is a tab, indicating a continued line, it'll ignore the instance
43 <control statement> is 'if', 'do', 'while', 'for', 'try' or nothing (instance initializer block)
44
45 - first \n: with positive lookbehind (?<=\n) to move the error marker to a more reasonable place
46 - capture tabs for <indentation>, later referenced via \1
47 - remaining preceding line as a non-comment (doesn't start with '/', '//', ' ' or '\t') or multiple lines where all but the first are a single line comment with the same indentation
48 - new line
49 - <indentation> as captured earlier
50 - <control statement> as specified above
51 - { before the next new line -->
52 <property name="format" value="(?&lt;=\n)([\t]+)(?:[^/\r\n \t][^\r\n]*|/[^/\r\n][^\r\n]*|[^/\r\n][^\r\n]*(\r?\n\1//[^\r\n]*)+)\r?\n\1(|(if|do|while|for|try)[^\r\n]+)\{[\t ]*\r?\n"/>
53 <property name="message" value="missing blank line before block at same indentation level"/>
54 </module>
55
56 <!-- require blank after } in the same indentation level -->
57 <module name="RegexpMultiline">
58 <!-- \n<indentation>}\n<same indentation><whatever unless newline, '}' or starting with cas(e) or def(ault)> -->
59 <property name="format" value="(?&lt;=\n)([\t]+)\}\r?\n\1(?:[^\r\n\}cd]|c[^\r\na]|ca[^\r\ns]|d[^\r\ne]|de[^\r\nf])"/>
60 <property name="message" value="missing blank line after block at same indentation level"/>
61 </module>
62
63 <module name="TreeWalker">
64 <!-- Ensure all imports are ship shape -->
65 <module name="AvoidStarImport"/>
66 <module name="IllegalImport"/>
67 <module name="RedundantImport"/>
68 <module name="UnusedImports"/>
69
70 <module name="ImportOrder">
71 <property name="groups" value="java,javax,*,cuchaz.enigma"/>
72 <property name="ordered" value="false"/><!-- the plugin orders alphabetically without considering separators.. -->
73 <property name="separated" value="true"/>
74 <property name="option" value="top"/>
75 <property name="sortStaticImportsAlphabetically" value="true"/>
76 </module>
77
78 <!-- Ensures braces are at the end of a line -->
79 <module name="LeftCurly"/>
80 <module name="RightCurly"/>
81
82 <!-- single line statements on one line, -->
83 <module name="NeedBraces">
84 <property name="tokens" value="LITERAL_IF,LITERAL_FOR,LITERAL_WHILE"/>
85 <property name="allowSingleLineStatement" value="true"/>
86 </module>
87 <module name="NeedBraces">
88 <property name="tokens" value="LITERAL_ELSE,LITERAL_DO"/>
89 <property name="allowSingleLineStatement" value="false"/>
90 </module>
91
92 <module name="EmptyLineSeparator">
93 <property name="allowNoEmptyLineBetweenFields" value="true"/>
94 <property name="allowMultipleEmptyLines" value="false"/>
95 <!-- exclude METHOD_DEF and VARIABLE_DEF -->
96 <property name="tokens" value="PACKAGE_DEF,IMPORT,STATIC_IMPORT,CLASS_DEF,INTERFACE_DEF,ENUM_DEF,STATIC_INIT,INSTANCE_INIT,CTOR_DEF"/>
97 </module>
98
99 <module name="OperatorWrap"/>
100 <module name="SeparatorWrap">
101 <property name="tokens" value="DOT,ELLIPSIS,AT"/>
102 <property name="option" value="nl"/>
103 </module>
104 <module name="SeparatorWrap">
105 <property name="tokens" value="COMMA,SEMI"/>
106 <property name="option" value="eol"/>
107 </module>
108
109 <module name="Indentation">
110 <property name="basicOffset" value="8"/>
111 <property name="caseIndent" value="0"/>
112 <property name="throwsIndent" value="8"/>
113 <property name="arrayInitIndent" value="8"/>
114 <property name="lineWrappingIndentation" value="16"/>
115 </module>
116
117 <module name="ParenPad"/>
118 <module name="NoWhitespaceBefore"/>
119 <module name="NoWhitespaceAfter">
120 <!-- allow ARRAY_INIT -->
121 <property name="tokens" value="AT,INC,DEC,UNARY_MINUS,UNARY_PLUS,BNOT,LNOT,DOT,ARRAY_DECLARATOR,INDEX_OP"/>
122 </module>
123 <module name="WhitespaceAfter"/>
124 <module name="WhitespaceAround">
125 <!-- Allow PLUS, MINUS, MUL, DIV as they may be more readable without spaces in some cases -->
126 <property name="tokens"
127 value="ASSIGN,BAND,BAND_ASSIGN,BOR,BOR_ASSIGN,BSR,BSR_ASSIGN,BXOR,BXOR_ASSIGN,COLON,DIV_ASSIGN,DO_WHILE,EQUAL,GE,GT,LAMBDA,LAND,LCURLY,LE,LITERAL_CATCH,LITERAL_DO,LITERAL_ELSE,LITERAL_FINALLY,LITERAL_FOR,LITERAL_IF,LITERAL_RETURN,LITERAL_SWITCH,LITERAL_SYNCHRONIZED,LITERAL_TRY,LITERAL_WHILE,LOR,LT,MINUS_ASSIGN,MOD,MOD_ASSIGN,NOT_EQUAL,PLUS_ASSIGN,QUESTION,RCURLY,SL,SLIST,SL_ASSIGN,SR,SR_ASSIGN,STAR,STAR_ASSIGN,LITERAL_ASSERT,TYPE_EXTENSION_AND"/>
128 </module>
129 <module name="SingleSpaceSeparator"/>
130 <module name="GenericWhitespace"/>
131 <module name="CommentsIndentation"/>
132
133 <module name="ArrayTypeStyle"/>
134 <module name="DefaultComesLast">
135 <property name="skipIfLastAndSharedWithCase" value="true"/>
136 </module>
137 <module name="SimplifyBooleanExpression"/>
138 <module name="SimplifyBooleanReturn"/>
139 <module name="StringLiteralEquality"/>
140
141 <module name="ModifierOrder"/>
142 <module name="RedundantModifier"/>
143
144 <module name="AnnotationLocation"/>
145 <module name="MissingOverride"/>
146
147 <!-- By default this allows catch blocks with only comments -->
148 <module name="EmptyCatchBlock"/>
149
150 <!-- Enforce tabs -->
151 <module name="RegexpSinglelineJava">
152 <property name="format" value="^\t* ([^*]|\*[^ /])"/>
153 <property name="message" value="non-tab indentation"/>
154 </module>
155
156 <module name="OuterTypeFilename"/>
157
158 <!--<module name="InvalidJavadocPosition"/>-->
159 <module name="JavadocParagraph"/>
160 <module name="JavadocStyle"/>
161 <module name="AtclauseOrder">
162 <property name="tagOrder" value="@param,@return,@throws,@deprecated"/>
163 </module>
164
165 <!-- Prevent var for all cases other than new instance creation -->
166 <module name="MatchXpath">
167 <property name="query" value="//VARIABLE_DEF[./TYPE/IDENT[@text='var'] and not(./ASSIGN/EXPR/LITERAL_NEW)]"/>
168 </module>
169 </module>
170</module> \ No newline at end of file
diff --git a/enigma-cli/build.gradle b/enigma-cli/build.gradle
index 5b84196..5281e9e 100644
--- a/enigma-cli/build.gradle
+++ b/enigma-cli/build.gradle
@@ -1,10 +1,10 @@
1plugins { 1plugins {
2 id 'application' 2 id 'application'
3 id 'com.github.johnrengelman.shadow' version '7.0.0' 3 id 'com.github.johnrengelman.shadow' version '7.0.0'
4} 4}
5 5
6dependencies { 6dependencies {
7 implementation project(':enigma') 7 implementation project(':enigma')
8} 8}
9 9
10mainClassName = 'cuchaz.enigma.command.Main' 10mainClassName = 'cuchaz.enigma.command.Main'
@@ -12,9 +12,9 @@ mainClassName = 'cuchaz.enigma.command.Main'
12jar.manifest.attributes 'Main-Class': mainClassName 12jar.manifest.attributes 'Main-Class': mainClassName
13 13
14publishing { 14publishing {
15 publications { 15 publications {
16 shadow(MavenPublication) { publication -> 16 shadow(MavenPublication) { publication ->
17 project.shadow.component publication 17 project.shadow.component publication
18 } 18 }
19 } 19 }
20} 20}
diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/CheckMappingsCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/CheckMappingsCommand.java
index 75ef225..922d668 100644
--- a/enigma-cli/src/main/java/cuchaz/enigma/command/CheckMappingsCommand.java
+++ b/enigma-cli/src/main/java/cuchaz/enigma/command/CheckMappingsCommand.java
@@ -1,22 +1,21 @@
1package cuchaz.enigma.command; 1package cuchaz.enigma.command;
2 2
3import java.nio.file.Path;
4import java.util.Set;
5import java.util.stream.Collectors;
6
3import cuchaz.enigma.Enigma; 7import cuchaz.enigma.Enigma;
4import cuchaz.enigma.EnigmaProject; 8import cuchaz.enigma.EnigmaProject;
5import cuchaz.enigma.ProgressListener; 9import cuchaz.enigma.ProgressListener;
6import cuchaz.enigma.analysis.index.JarIndex; 10import cuchaz.enigma.analysis.index.JarIndex;
7import cuchaz.enigma.classprovider.ClasspathClassProvider; 11import cuchaz.enigma.classprovider.ClasspathClassProvider;
8import cuchaz.enigma.translation.mapping.EntryMapping; 12import cuchaz.enigma.translation.mapping.EntryMapping;
9import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
10import cuchaz.enigma.translation.mapping.serde.MappingFormat; 13import cuchaz.enigma.translation.mapping.serde.MappingFormat;
14import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
11import cuchaz.enigma.translation.mapping.tree.EntryTree; 15import cuchaz.enigma.translation.mapping.tree.EntryTree;
12import cuchaz.enigma.translation.representation.entry.ClassEntry; 16import cuchaz.enigma.translation.representation.entry.ClassEntry;
13 17
14import java.nio.file.Path;
15import java.util.Set;
16import java.util.stream.Collectors;
17
18public class CheckMappingsCommand extends Command { 18public class CheckMappingsCommand extends Command {
19
20 public CheckMappingsCommand() { 19 public CheckMappingsCommand() {
21 super("checkmappings"); 20 super("checkmappings");
22 } 21 }
@@ -55,19 +54,11 @@ public class CheckMappingsCommand extends Command {
55 boolean error = false; 54 boolean error = false;
56 55
57 for (Set<ClassEntry> partition : idx.getPackageVisibilityIndex().getPartitions()) { 56 for (Set<ClassEntry> partition : idx.getPackageVisibilityIndex().getPartitions()) {
58 long packages = partition.stream() 57 long packages = partition.stream().map(project.getMapper()::deobfuscate).map(ClassEntry::getPackageName).distinct().count();
59 .map(project.getMapper()::deobfuscate) 58
60 .map(ClassEntry::getPackageName)
61 .distinct()
62 .count();
63 if (packages > 1) { 59 if (packages > 1) {
64 error = true; 60 error = true;
65 System.err.println("ERROR: Must be in one package:\n" + partition.stream() 61 System.err.println("ERROR: Must be in one package:\n" + partition.stream().map(project.getMapper()::deobfuscate).map(ClassEntry::toString).sorted().collect(Collectors.joining("\n")));
66 .map(project.getMapper()::deobfuscate)
67 .map(ClassEntry::toString)
68 .sorted()
69 .collect(Collectors.joining("\n"))
70 );
71 } 62 }
72 } 63 }
73 64
diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/Command.java b/enigma-cli/src/main/java/cuchaz/enigma/command/Command.java
index 0d71f02..04d49f2 100644
--- a/enigma-cli/src/main/java/cuchaz/enigma/command/Command.java
+++ b/enigma-cli/src/main/java/cuchaz/enigma/command/Command.java
@@ -1,21 +1,21 @@
1package cuchaz.enigma.command; 1package cuchaz.enigma.command;
2 2
3import java.io.File;
4import java.nio.file.Files;
5import java.nio.file.Path;
6import java.nio.file.Paths;
7
8import com.google.common.io.MoreFiles;
9
3import cuchaz.enigma.Enigma; 10import cuchaz.enigma.Enigma;
4import cuchaz.enigma.EnigmaProject; 11import cuchaz.enigma.EnigmaProject;
5import cuchaz.enigma.ProgressListener; 12import cuchaz.enigma.ProgressListener;
6import cuchaz.enigma.classprovider.ClasspathClassProvider; 13import cuchaz.enigma.classprovider.ClasspathClassProvider;
7import cuchaz.enigma.translation.mapping.EntryMapping; 14import cuchaz.enigma.translation.mapping.EntryMapping;
8import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
9import cuchaz.enigma.translation.mapping.serde.MappingFormat; 15import cuchaz.enigma.translation.mapping.serde.MappingFormat;
16import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
10import cuchaz.enigma.translation.mapping.tree.EntryTree; 17import cuchaz.enigma.translation.mapping.tree.EntryTree;
11 18
12import java.io.File;
13import java.nio.file.Files;
14import java.nio.file.Path;
15import java.nio.file.Paths;
16
17import com.google.common.io.MoreFiles;
18
19public abstract class Command { 19public abstract class Command {
20 public final String name; 20 public final String name;
21 21
@@ -63,15 +63,19 @@ public abstract class Command {
63 if (path == null) { 63 if (path == null) {
64 return null; 64 return null;
65 } 65 }
66
66 File file = new File(path).getAbsoluteFile(); 67 File file = new File(path).getAbsoluteFile();
67 File dir = file.getParentFile(); 68 File dir = file.getParentFile();
69
68 if (dir == null) { 70 if (dir == null) {
69 throw new IllegalArgumentException("Cannot write file: " + path); 71 throw new IllegalArgumentException("Cannot write file: " + path);
70 } 72 }
73
71 // quick fix to avoid stupid stuff in Gradle code 74 // quick fix to avoid stupid stuff in Gradle code
72 if (!dir.isDirectory()) { 75 if (!dir.isDirectory()) {
73 dir.mkdirs(); 76 dir.mkdirs();
74 } 77 }
78
75 return file; 79 return file;
76 } 80 }
77 81
@@ -79,10 +83,13 @@ public abstract class Command {
79 if (path == null) { 83 if (path == null) {
80 return null; 84 return null;
81 } 85 }
86
82 File dir = new File(path).getAbsoluteFile(); 87 File dir = new File(path).getAbsoluteFile();
88
83 if (!dir.exists()) { 89 if (!dir.exists()) {
84 throw new IllegalArgumentException("Cannot write to folder: " + dir); 90 throw new IllegalArgumentException("Cannot write to folder: " + dir);
85 } 91 }
92
86 return dir; 93 return dir;
87 } 94 }
88 95
@@ -90,10 +97,13 @@ public abstract class Command {
90 if (path == null) { 97 if (path == null) {
91 return null; 98 return null;
92 } 99 }
100
93 File file = new File(path).getAbsoluteFile(); 101 File file = new File(path).getAbsoluteFile();
102
94 if (!file.exists()) { 103 if (!file.exists()) {
95 throw new IllegalArgumentException("Cannot find file: " + file.getAbsolutePath()); 104 throw new IllegalArgumentException("Cannot find file: " + file.getAbsolutePath());
96 } 105 }
106
97 return file; 107 return file;
98 } 108 }
99 109
@@ -101,10 +111,13 @@ public abstract class Command {
101 if (path == null) { 111 if (path == null) {
102 return null; 112 return null;
103 } 113 }
114
104 Path file = Paths.get(path).toAbsolutePath(); 115 Path file = Paths.get(path).toAbsolutePath();
116
105 if (!Files.exists(file)) { 117 if (!Files.exists(file)) {
106 throw new IllegalArgumentException("Cannot find file: " + file.toString()); 118 throw new IllegalArgumentException("Cannot find file: " + file.toString());
107 } 119 }
120
108 return file; 121 return file;
109 } 122 }
110 123
@@ -116,11 +129,11 @@ public abstract class Command {
116 return null; 129 return null;
117 } 130 }
118 } 131 }
132
119 return args[i]; 133 return args[i];
120 } 134 }
121 135
122 public static class ConsoleProgressListener implements ProgressListener { 136 public static class ConsoleProgressListener implements ProgressListener {
123
124 private static final int ReportTime = 5000; // 5s 137 private static final int ReportTime = 5000; // 5s
125 138
126 private int totalWork; 139 private int totalWork;
@@ -146,6 +159,7 @@ public abstract class Command {
146 System.out.println(String.format("\tProgress: %3d%%", percent)); 159 System.out.println(String.format("\tProgress: %3d%%", percent));
147 this.lastReportTime = now; 160 this.lastReportTime = now;
148 } 161 }
162
149 if (isLastUpdate) { 163 if (isLastUpdate) {
150 double elapsedSeconds = (now - this.startTime) / 1000.0; 164 double elapsedSeconds = (now - this.startTime) / 1000.0;
151 System.out.println(String.format("Finished in %.1f seconds", elapsedSeconds)); 165 System.out.println(String.format("Finished in %.1f seconds", elapsedSeconds));
diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/ComposeMappingsCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/ComposeMappingsCommand.java
index e10fd47..7e9002d 100644
--- a/enigma-cli/src/main/java/cuchaz/enigma/command/ComposeMappingsCommand.java
+++ b/enigma-cli/src/main/java/cuchaz/enigma/command/ComposeMappingsCommand.java
@@ -1,42 +1,42 @@
1package cuchaz.enigma.command; 1package cuchaz.enigma.command;
2 2
3import cuchaz.enigma.translation.mapping.MappingOperations; 3import java.io.IOException;
4import cuchaz.enigma.translation.mapping.serde.MappingParseException; 4import java.nio.file.Path;
5import java.nio.file.Paths;
6
5import cuchaz.enigma.translation.mapping.EntryMapping; 7import cuchaz.enigma.translation.mapping.EntryMapping;
8import cuchaz.enigma.translation.mapping.MappingOperations;
6import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; 9import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat;
10import cuchaz.enigma.translation.mapping.serde.MappingParseException;
7import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; 11import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
8import cuchaz.enigma.translation.mapping.tree.EntryTree; 12import cuchaz.enigma.translation.mapping.tree.EntryTree;
9import cuchaz.enigma.utils.Utils; 13import cuchaz.enigma.utils.Utils;
10 14
11import java.io.IOException;
12import java.nio.file.Path;
13import java.nio.file.Paths;
14
15public class ComposeMappingsCommand extends Command { 15public class ComposeMappingsCommand extends Command {
16 public ComposeMappingsCommand() { 16 public ComposeMappingsCommand() {
17 super("compose-mappings"); 17 super("compose-mappings");
18 } 18 }
19 19
20 @Override 20 @Override
21 public String getUsage() { 21 public String getUsage() {
22 return "<left-format> <left> <right-format> <right> <result-format> <result> <keep-mode>"; 22 return "<left-format> <left> <right-format> <right> <result-format> <result> <keep-mode>";
23 } 23 }
24 24
25 @Override 25 @Override
26 public boolean isValidArgument(int length) { 26 public boolean isValidArgument(int length) {
27 return length == 7; 27 return length == 7;
28 } 28 }
29 29
30 @Override 30 @Override
31 public void run(String... args) throws IOException, MappingParseException { 31 public void run(String... args) throws IOException, MappingParseException {
32 MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); 32 MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF);
33 33
34 EntryTree<EntryMapping> left = MappingCommandsUtil.read(args[0], Paths.get(args[1]), saveParameters); 34 EntryTree<EntryMapping> left = MappingCommandsUtil.read(args[0], Paths.get(args[1]), saveParameters);
35 EntryTree<EntryMapping> right = MappingCommandsUtil.read(args[2], Paths.get(args[3]), saveParameters); 35 EntryTree<EntryMapping> right = MappingCommandsUtil.read(args[2], Paths.get(args[3]), saveParameters);
36 EntryTree<EntryMapping> result = MappingOperations.compose(left, right, args[6].equals("left") || args[6].equals("both"), args[6].equals("right") || args[6].equals("both")); 36 EntryTree<EntryMapping> result = MappingOperations.compose(left, right, args[6].equals("left") || args[6].equals("both"), args[6].equals("right") || args[6].equals("both"));
37 37
38 Path output = Paths.get(args[5]); 38 Path output = Paths.get(args[5]);
39 Utils.delete(output); 39 Utils.delete(output);
40 MappingCommandsUtil.write(result, args[4], output, saveParameters); 40 MappingCommandsUtil.write(result, args[4], output, saveParameters);
41 } 41 }
42} 42}
diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/ConvertMappingsCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/ConvertMappingsCommand.java
index 144d89c..99b27e1 100644
--- a/enigma-cli/src/main/java/cuchaz/enigma/command/ConvertMappingsCommand.java
+++ b/enigma-cli/src/main/java/cuchaz/enigma/command/ConvertMappingsCommand.java
@@ -1,39 +1,39 @@
1package cuchaz.enigma.command; 1package cuchaz.enigma.command;
2 2
3import cuchaz.enigma.translation.mapping.serde.MappingParseException; 3import java.io.IOException;
4import java.nio.file.Path;
5import java.nio.file.Paths;
6
4import cuchaz.enigma.translation.mapping.EntryMapping; 7import cuchaz.enigma.translation.mapping.EntryMapping;
5import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; 8import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat;
9import cuchaz.enigma.translation.mapping.serde.MappingParseException;
6import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; 10import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
7import cuchaz.enigma.translation.mapping.tree.EntryTree; 11import cuchaz.enigma.translation.mapping.tree.EntryTree;
8import cuchaz.enigma.utils.Utils; 12import cuchaz.enigma.utils.Utils;
9 13
10import java.io.IOException;
11import java.nio.file.Path;
12import java.nio.file.Paths;
13
14public class ConvertMappingsCommand extends Command { 14public class ConvertMappingsCommand extends Command {
15 public ConvertMappingsCommand() { 15 public ConvertMappingsCommand() {
16 super("convert-mappings"); 16 super("convert-mappings");
17 } 17 }
18 18
19 @Override 19 @Override
20 public String getUsage() { 20 public String getUsage() {
21 return "<source-format> <source> <result-format> <result>"; 21 return "<source-format> <source> <result-format> <result>";
22 } 22 }
23 23
24 @Override 24 @Override
25 public boolean isValidArgument(int length) { 25 public boolean isValidArgument(int length) {
26 return length == 4; 26 return length == 4;
27 } 27 }
28 28
29 @Override 29 @Override
30 public void run(String... args) throws IOException, MappingParseException { 30 public void run(String... args) throws IOException, MappingParseException {
31 MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); 31 MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF);
32 32
33 EntryTree<EntryMapping> mappings = MappingCommandsUtil.read(args[0], Paths.get(args[1]), saveParameters); 33 EntryTree<EntryMapping> mappings = MappingCommandsUtil.read(args[0], Paths.get(args[1]), saveParameters);
34 34
35 Path output = Paths.get(args[3]); 35 Path output = Paths.get(args[3]);
36 Utils.delete(output); 36 Utils.delete(output);
37 MappingCommandsUtil.write(mappings, args[2], output, saveParameters); 37 MappingCommandsUtil.write(mappings, args[2], output, saveParameters);
38 } 38 }
39} 39}
diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/DecompileCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/DecompileCommand.java
index 12a4e88..020bd97 100644
--- a/enigma-cli/src/main/java/cuchaz/enigma/command/DecompileCommand.java
+++ b/enigma-cli/src/main/java/cuchaz/enigma/command/DecompileCommand.java
@@ -1,17 +1,16 @@
1package cuchaz.enigma.command; 1package cuchaz.enigma.command;
2 2
3import java.lang.reflect.Field;
4import java.nio.file.Path;
5import java.util.Locale;
6
3import cuchaz.enigma.EnigmaProject; 7import cuchaz.enigma.EnigmaProject;
4import cuchaz.enigma.ProgressListener;
5import cuchaz.enigma.EnigmaProject.DecompileErrorStrategy; 8import cuchaz.enigma.EnigmaProject.DecompileErrorStrategy;
9import cuchaz.enigma.ProgressListener;
6import cuchaz.enigma.source.DecompilerService; 10import cuchaz.enigma.source.DecompilerService;
7import cuchaz.enigma.source.Decompilers; 11import cuchaz.enigma.source.Decompilers;
8 12
9import java.lang.reflect.Field;
10import java.nio.file.Path;
11import java.util.Locale;
12
13public class DecompileCommand extends Command { 13public class DecompileCommand extends Command {
14
15 public DecompileCommand() { 14 public DecompileCommand() {
16 super("decompile"); 15 super("decompile");
17 } 16 }
diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/DeobfuscateCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/DeobfuscateCommand.java
index b0d2a7d..c8e6200 100644
--- a/enigma-cli/src/main/java/cuchaz/enigma/command/DeobfuscateCommand.java
+++ b/enigma-cli/src/main/java/cuchaz/enigma/command/DeobfuscateCommand.java
@@ -1,12 +1,11 @@
1package cuchaz.enigma.command; 1package cuchaz.enigma.command;
2 2
3import java.nio.file.Path;
4
3import cuchaz.enigma.EnigmaProject; 5import cuchaz.enigma.EnigmaProject;
4import cuchaz.enigma.ProgressListener; 6import cuchaz.enigma.ProgressListener;
5 7
6import java.nio.file.Path;
7
8public class DeobfuscateCommand extends Command { 8public class DeobfuscateCommand extends Command {
9
10 public DeobfuscateCommand() { 9 public DeobfuscateCommand() {
11 super("deobfuscate"); 10 super("deobfuscate");
12 } 11 }
diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/InvertMappingsCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/InvertMappingsCommand.java
index 0780a96..af24978 100644
--- a/enigma-cli/src/main/java/cuchaz/enigma/command/InvertMappingsCommand.java
+++ b/enigma-cli/src/main/java/cuchaz/enigma/command/InvertMappingsCommand.java
@@ -1,41 +1,41 @@
1package cuchaz.enigma.command; 1package cuchaz.enigma.command;
2 2
3import cuchaz.enigma.translation.mapping.MappingOperations; 3import java.io.IOException;
4import cuchaz.enigma.translation.mapping.serde.MappingParseException; 4import java.nio.file.Path;
5import java.nio.file.Paths;
6
5import cuchaz.enigma.translation.mapping.EntryMapping; 7import cuchaz.enigma.translation.mapping.EntryMapping;
8import cuchaz.enigma.translation.mapping.MappingOperations;
6import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; 9import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat;
10import cuchaz.enigma.translation.mapping.serde.MappingParseException;
7import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; 11import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
8import cuchaz.enigma.translation.mapping.tree.EntryTree; 12import cuchaz.enigma.translation.mapping.tree.EntryTree;
9import cuchaz.enigma.utils.Utils; 13import cuchaz.enigma.utils.Utils;
10 14
11import java.io.IOException;
12import java.nio.file.Path;
13import java.nio.file.Paths;
14
15public class InvertMappingsCommand extends Command { 15public class InvertMappingsCommand extends Command {
16 public InvertMappingsCommand() { 16 public InvertMappingsCommand() {
17 super("invert-mappings"); 17 super("invert-mappings");
18 } 18 }
19 19
20 @Override 20 @Override
21 public String getUsage() { 21 public String getUsage() {
22 return "<source-format> <source> <result-format> <result>"; 22 return "<source-format> <source> <result-format> <result>";
23 } 23 }
24 24
25 @Override 25 @Override
26 public boolean isValidArgument(int length) { 26 public boolean isValidArgument(int length) {
27 return length == 4; 27 return length == 4;
28 } 28 }
29 29
30 @Override 30 @Override
31 public void run(String... args) throws IOException, MappingParseException { 31 public void run(String... args) throws IOException, MappingParseException {
32 MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); 32 MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF);
33 33
34 EntryTree<EntryMapping> source = MappingCommandsUtil.read(args[0], Paths.get(args[1]), saveParameters); 34 EntryTree<EntryMapping> source = MappingCommandsUtil.read(args[0], Paths.get(args[1]), saveParameters);
35 EntryTree<EntryMapping> result = MappingOperations.invert(source); 35 EntryTree<EntryMapping> result = MappingOperations.invert(source);
36 36
37 Path output = Paths.get(args[3]); 37 Path output = Paths.get(args[3]);
38 Utils.delete(output); 38 Utils.delete(output);
39 MappingCommandsUtil.write(result, args[2], output, saveParameters); 39 MappingCommandsUtil.write(result, args[2], output, saveParameters);
40 } 40 }
41} 41}
diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/Main.java b/enigma-cli/src/main/java/cuchaz/enigma/command/Main.java
index 0a4c1b9..9021ff1 100644
--- a/enigma-cli/src/main/java/cuchaz/enigma/command/Main.java
+++ b/enigma-cli/src/main/java/cuchaz/enigma/command/Main.java
@@ -1,36 +1,39 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.command; 12package cuchaz.enigma.command;
13 13
14import cuchaz.enigma.Enigma;
15
16import java.util.LinkedHashMap; 14import java.util.LinkedHashMap;
17import java.util.Locale; 15import java.util.Locale;
18import java.util.Map; 16import java.util.Map;
19 17
20public class Main { 18import cuchaz.enigma.Enigma;
21 19
20public class Main {
22 private static final Map<String, Command> COMMANDS = new LinkedHashMap<>(); 21 private static final Map<String, Command> COMMANDS = new LinkedHashMap<>();
23 22
24 public static void main(String... args) throws Exception { 23 public static void main(String... args) throws Exception {
25 try { 24 try {
26 // process the command 25 // process the command
27 if (args.length < 1) 26 if (args.length < 1) {
28 throw new IllegalArgumentException("Requires a command"); 27 throw new IllegalArgumentException("Requires a command");
28 }
29
29 String command = args[0].toLowerCase(Locale.ROOT); 30 String command = args[0].toLowerCase(Locale.ROOT);
30 31
31 Command cmd = COMMANDS.get(command); 32 Command cmd = COMMANDS.get(command);
32 if (cmd == null) 33
34 if (cmd == null) {
33 throw new IllegalArgumentException("Command not recognized: " + command); 35 throw new IllegalArgumentException("Command not recognized: " + command);
36 }
34 37
35 if (!cmd.isValidArgument(args.length - 1)) { 38 if (!cmd.isValidArgument(args.length - 1)) {
36 throw new CommandHelpException(cmd); 39 throw new CommandHelpException(cmd);
@@ -74,6 +77,7 @@ public class Main {
74 77
75 private static void register(Command command) { 78 private static void register(Command command) {
76 Command old = COMMANDS.put(command.name, command); 79 Command old = COMMANDS.put(command.name, command);
80
77 if (old != null) { 81 if (old != null) {
78 System.err.println("Command " + old + " with name " + command.name + " has been substituted by " + command); 82 System.err.println("Command " + old + " with name " + command.name + " has been substituted by " + command);
79 } 83 }
@@ -90,7 +94,6 @@ public class Main {
90 } 94 }
91 95
92 private static final class CommandHelpException extends IllegalArgumentException { 96 private static final class CommandHelpException extends IllegalArgumentException {
93
94 final Command command; 97 final Command command;
95 98
96 CommandHelpException(Command command) { 99 CommandHelpException(Command command) {
diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/MapSpecializedMethodsCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/MapSpecializedMethodsCommand.java
index 46da89a..644b08d 100644
--- a/enigma-cli/src/main/java/cuchaz/enigma/command/MapSpecializedMethodsCommand.java
+++ b/enigma-cli/src/main/java/cuchaz/enigma/command/MapSpecializedMethodsCommand.java
@@ -1,5 +1,10 @@
1package cuchaz.enigma.command; 1package cuchaz.enigma.command;
2 2
3import java.io.IOException;
4import java.nio.file.Path;
5import java.nio.file.Paths;
6import java.util.Map;
7
3import cuchaz.enigma.ProgressListener; 8import cuchaz.enigma.ProgressListener;
4import cuchaz.enigma.analysis.IndexTreeBuilder; 9import cuchaz.enigma.analysis.IndexTreeBuilder;
5import cuchaz.enigma.analysis.index.BridgeMethodIndex; 10import cuchaz.enigma.analysis.index.BridgeMethodIndex;
@@ -18,60 +23,55 @@ import cuchaz.enigma.translation.mapping.tree.HashEntryTree;
18import cuchaz.enigma.translation.representation.entry.MethodEntry; 23import cuchaz.enigma.translation.representation.entry.MethodEntry;
19import cuchaz.enigma.utils.Utils; 24import cuchaz.enigma.utils.Utils;
20 25
21import java.io.IOException;
22import java.nio.file.Path;
23import java.nio.file.Paths;
24import java.util.Map;
25
26public class MapSpecializedMethodsCommand extends Command { 26public class MapSpecializedMethodsCommand extends Command {
27 public MapSpecializedMethodsCommand() { 27 public MapSpecializedMethodsCommand() {
28 super("map-specialized-methods"); 28 super("map-specialized-methods");
29 } 29 }
30 30
31 @Override 31 @Override
32 public String getUsage() { 32 public String getUsage() {
33 return "<jar> <source-format> <source> <result-format> <result>"; 33 return "<jar> <source-format> <source> <result-format> <result>";
34 } 34 }
35 35
36 @Override 36 @Override
37 public boolean isValidArgument(int length) { 37 public boolean isValidArgument(int length) {
38 return length == 5; 38 return length == 5;
39 } 39 }
40 40
41 @Override 41 @Override
42 public void run(String... args) throws IOException, MappingParseException { 42 public void run(String... args) throws IOException, MappingParseException {
43 run(Paths.get(args[0]), args[1], Paths.get(args[2]), args[3], Paths.get(args[4])); 43 run(Paths.get(args[0]), args[1], Paths.get(args[2]), args[3], Paths.get(args[4]));
44 } 44 }
45 45
46 public static void run(Path jar, String sourceFormat, Path sourcePath, String resultFormat, Path output) throws IOException, MappingParseException { 46 public static void run(Path jar, String sourceFormat, Path sourcePath, String resultFormat, Path output) throws IOException, MappingParseException {
47 MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); 47 MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF);
48 EntryTree<EntryMapping> source = MappingCommandsUtil.read(sourceFormat, sourcePath, saveParameters); 48 EntryTree<EntryMapping> source = MappingCommandsUtil.read(sourceFormat, sourcePath, saveParameters);
49 EntryTree<EntryMapping> result = new HashEntryTree<>(); 49 EntryTree<EntryMapping> result = new HashEntryTree<>();
50 50
51 JarClassProvider jcp = new JarClassProvider(jar); 51 JarClassProvider jcp = new JarClassProvider(jar);
52 JarIndex jarIndex = JarIndex.empty(); 52 JarIndex jarIndex = JarIndex.empty();
53 jarIndex.indexJar(jcp.getClassNames(), new CachingClassProvider(jcp), ProgressListener.none()); 53 jarIndex.indexJar(jcp.getClassNames(), new CachingClassProvider(jcp), ProgressListener.none());
54 54
55 BridgeMethodIndex bridgeMethodIndex = jarIndex.getBridgeMethodIndex(); 55 BridgeMethodIndex bridgeMethodIndex = jarIndex.getBridgeMethodIndex();
56 Translator translator = new MappingTranslator(source, jarIndex.getEntryResolver()); 56 Translator translator = new MappingTranslator(source, jarIndex.getEntryResolver());
57 IndexTreeBuilder indexTreeBuilder = new IndexTreeBuilder(jarIndex); 57 IndexTreeBuilder indexTreeBuilder = new IndexTreeBuilder(jarIndex);
58 58
59 // Copy all non-specialized methods 59 // Copy all non-specialized methods
60 for (EntryTreeNode<EntryMapping> node : source) { 60 for (EntryTreeNode<EntryMapping> node : source) {
61 if (!(node.getEntry() instanceof MethodEntry) || !bridgeMethodIndex.isSpecializedMethod((MethodEntry) node.getEntry())) { 61 if (!(node.getEntry() instanceof MethodEntry) || !bridgeMethodIndex.isSpecializedMethod((MethodEntry) node.getEntry())) {
62 result.insert(node.getEntry(), node.getValue()); 62 result.insert(node.getEntry(), node.getValue());
63 } 63 }
64 } 64 }
65 65
66 // Add correct mappings for specialized methods 66 // Add correct mappings for specialized methods
67 for (Map.Entry<MethodEntry, MethodEntry> entry : bridgeMethodIndex.getBridgeToSpecialized().entrySet()) { 67 for (Map.Entry<MethodEntry, MethodEntry> entry : bridgeMethodIndex.getBridgeToSpecialized().entrySet()) {
68 MethodEntry bridge = entry.getKey(); 68 MethodEntry bridge = entry.getKey();
69 MethodEntry specialized = indexTreeBuilder.buildMethodInheritance(translator, entry.getValue()).getMethodEntry(); 69 MethodEntry specialized = indexTreeBuilder.buildMethodInheritance(translator, entry.getValue()).getMethodEntry();
70 String name = translator.translate(bridge).getName(); 70 String name = translator.translate(bridge).getName();
71 result.insert(specialized, new EntryMapping(name)); 71 result.insert(specialized, new EntryMapping(name));
72 } 72 }
73 73
74 Utils.delete(output); 74 Utils.delete(output);
75 MappingCommandsUtil.write(result, resultFormat, output, saveParameters); 75 MappingCommandsUtil.write(result, resultFormat, output, saveParameters);
76 } 76 }
77} 77}
diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/MappingCommandsUtil.java b/enigma-cli/src/main/java/cuchaz/enigma/command/MappingCommandsUtil.java
index d365129..787625b 100644
--- a/enigma-cli/src/main/java/cuchaz/enigma/command/MappingCommandsUtil.java
+++ b/enigma-cli/src/main/java/cuchaz/enigma/command/MappingCommandsUtil.java
@@ -1,10 +1,14 @@
1package cuchaz.enigma.command; 1package cuchaz.enigma.command;
2 2
3import java.io.IOException;
4import java.nio.file.Files;
5import java.nio.file.Path;
6
3import cuchaz.enigma.ProgressListener; 7import cuchaz.enigma.ProgressListener;
4import cuchaz.enigma.translation.mapping.serde.MappingParseException;
5import cuchaz.enigma.translation.mapping.EntryMapping; 8import cuchaz.enigma.translation.mapping.EntryMapping;
9import cuchaz.enigma.translation.mapping.serde.MappingFormat;
10import cuchaz.enigma.translation.mapping.serde.MappingParseException;
6import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; 11import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
7import cuchaz.enigma.translation.mapping.serde.*;
8import cuchaz.enigma.translation.mapping.serde.enigma.EnigmaMappingsReader; 12import cuchaz.enigma.translation.mapping.serde.enigma.EnigmaMappingsReader;
9import cuchaz.enigma.translation.mapping.serde.enigma.EnigmaMappingsWriter; 13import cuchaz.enigma.translation.mapping.serde.enigma.EnigmaMappingsWriter;
10import cuchaz.enigma.translation.mapping.serde.tiny.TinyMappingsReader; 14import cuchaz.enigma.translation.mapping.serde.tiny.TinyMappingsReader;
@@ -12,76 +16,77 @@ import cuchaz.enigma.translation.mapping.serde.tiny.TinyMappingsWriter;
12import cuchaz.enigma.translation.mapping.serde.tinyv2.TinyV2Writer; 16import cuchaz.enigma.translation.mapping.serde.tinyv2.TinyV2Writer;
13import cuchaz.enigma.translation.mapping.tree.EntryTree; 17import cuchaz.enigma.translation.mapping.tree.EntryTree;
14 18
15import java.io.IOException;
16import java.nio.file.Files;
17import java.nio.file.Path;
18
19public final class MappingCommandsUtil { 19public final class MappingCommandsUtil {
20 private MappingCommandsUtil() {} 20 private MappingCommandsUtil() {
21 21 }
22 public static EntryTree<EntryMapping> read(String type, Path path, MappingSaveParameters saveParameters) throws MappingParseException, IOException { 22
23 if (type.equals("enigma")) { 23 public static EntryTree<EntryMapping> read(String type, Path path, MappingSaveParameters saveParameters) throws MappingParseException, IOException {
24 return (Files.isDirectory(path) ? EnigmaMappingsReader.DIRECTORY : EnigmaMappingsReader.ZIP).read(path, ProgressListener.none(), saveParameters); 24 if (type.equals("enigma")) {
25 } 25 return (Files.isDirectory(path) ? EnigmaMappingsReader.DIRECTORY : EnigmaMappingsReader.ZIP).read(path, ProgressListener.none(), saveParameters);
26 26 }
27 if (type.equals("tiny")) { 27
28 return TinyMappingsReader.INSTANCE.read(path, ProgressListener.none(), saveParameters); 28 if (type.equals("tiny")) {
29 } 29 return TinyMappingsReader.INSTANCE.read(path, ProgressListener.none(), saveParameters);
30 30 }
31 MappingFormat format = null; 31
32 try { 32 MappingFormat format = null;
33 format = MappingFormat.valueOf(type.toUpperCase()); 33
34 } catch (IllegalArgumentException ignored) { 34 try {
35 if (type.equals("tinyv2")) { 35 format = MappingFormat.valueOf(type.toUpperCase());
36 format = MappingFormat.TINY_V2; 36 } catch (IllegalArgumentException ignored) {
37 } 37 if (type.equals("tinyv2")) {
38 } 38 format = MappingFormat.TINY_V2;
39 39 }
40 if (format != null) { 40 }
41 return format.getReader().read(path, ProgressListener.none(), saveParameters); 41
42 } 42 if (format != null) {
43 43 return format.getReader().read(path, ProgressListener.none(), saveParameters);
44 throw new IllegalArgumentException("no reader for " + type); 44 }
45 } 45
46 46 throw new IllegalArgumentException("no reader for " + type);
47 public static void write(EntryTree<EntryMapping> mappings, String type, Path path, MappingSaveParameters saveParameters) { 47 }
48 if (type.equals("enigma")) { 48
49 EnigmaMappingsWriter.DIRECTORY.write(mappings, path, ProgressListener.none(), saveParameters); 49 public static void write(EntryTree<EntryMapping> mappings, String type, Path path, MappingSaveParameters saveParameters) {
50 return; 50 if (type.equals("enigma")) {
51 } 51 EnigmaMappingsWriter.DIRECTORY.write(mappings, path, ProgressListener.none(), saveParameters);
52 52 return;
53 if (type.startsWith("tinyv2:") || type.startsWith("tiny_v2:")) { 53 }
54 String[] split = type.split(":"); 54
55 55 if (type.startsWith("tinyv2:") || type.startsWith("tiny_v2:")) {
56 if (split.length != 3) { 56 String[] split = type.split(":");
57 throw new IllegalArgumentException("specify column names as 'tinyv2:from_namespace:to_namespace'"); 57
58 } 58 if (split.length != 3) {
59 59 throw new IllegalArgumentException("specify column names as 'tinyv2:from_namespace:to_namespace'");
60 new TinyV2Writer(split[1], split[2]).write(mappings, path, ProgressListener.none(), saveParameters); 60 }
61 return; 61
62 } 62 new TinyV2Writer(split[1], split[2]).write(mappings, path, ProgressListener.none(), saveParameters);
63 63 return;
64 if (type.startsWith("tiny:")) { 64 }
65 String[] split = type.split(":"); 65
66 66 if (type.startsWith("tiny:")) {
67 if (split.length != 3) { 67 String[] split = type.split(":");
68 throw new IllegalArgumentException("specify column names as 'tiny:from_column:to_column'"); 68
69 } 69 if (split.length != 3) {
70 70 throw new IllegalArgumentException("specify column names as 'tiny:from_column:to_column'");
71 new TinyMappingsWriter(split[1], split[2]).write(mappings, path, ProgressListener.none(), saveParameters); 71 }
72 return; 72
73 } 73 new TinyMappingsWriter(split[1], split[2]).write(mappings, path, ProgressListener.none(), saveParameters);
74 74 return;
75 MappingFormat format = null; 75 }
76 try { 76
77 format = MappingFormat.valueOf(type.toUpperCase()); 77 MappingFormat format = null;
78 } catch (IllegalArgumentException ignored) {} 78
79 79 try {
80 if (format != null) { 80 format = MappingFormat.valueOf(type.toUpperCase());
81 format.getWriter().write(mappings, path, ProgressListener.none(), saveParameters); 81 } catch (IllegalArgumentException ignored) {
82 return; 82 // ignored
83 } 83 }
84 84
85 throw new IllegalArgumentException("no writer for " + type); 85 if (format != null) {
86 } 86 format.getWriter().write(mappings, path, ProgressListener.none(), saveParameters);
87 return;
88 }
89
90 throw new IllegalArgumentException("no writer for " + type);
91 }
87} 92}
diff --git a/enigma-cli/src/test/java/cuchaz/enigma/command/CheckMappingsCommandTest.java b/enigma-cli/src/test/java/cuchaz/enigma/command/CheckMappingsCommandTest.java
index a29bba4..8cfa49e 100644
--- a/enigma-cli/src/test/java/cuchaz/enigma/command/CheckMappingsCommandTest.java
+++ b/enigma-cli/src/test/java/cuchaz/enigma/command/CheckMappingsCommandTest.java
@@ -1,21 +1,19 @@
1package cuchaz.enigma.command; 1package cuchaz.enigma.command;
2 2
3import org.junit.Test;
4
5import java.io.File; 3import java.io.File;
6 4
5import org.junit.Test;
6
7public class CheckMappingsCommandTest { 7public class CheckMappingsCommandTest {
8 private static final String PACKAGE_ACCESS = "../enigma/build/test-obf/packageAccess.jar"; 8 private static final String PACKAGE_ACCESS = "../enigma/build/test-obf/packageAccess.jar";
9 9
10 @Test(expected = IllegalStateException.class) 10 @Test(expected = IllegalStateException.class)
11 public void testWrong() throws Exception { 11 public void testWrong() throws Exception {
12 new CheckMappingsCommand().run(new File(PACKAGE_ACCESS).getAbsolutePath(), new File("src/test/resources" + 12 new CheckMappingsCommand().run(new File(PACKAGE_ACCESS).getAbsolutePath(), new File("src/test/resources" + "/packageAccess/wrongMappings").getAbsolutePath());
13 "/packageAccess/wrongMappings").getAbsolutePath());
14 } 13 }
15 14
16 @Test 15 @Test
17 public void testRight() throws Exception { 16 public void testRight() throws Exception {
18 new CheckMappingsCommand().run(new File(PACKAGE_ACCESS).getAbsolutePath(), new File("src/test/resources" + 17 new CheckMappingsCommand().run(new File(PACKAGE_ACCESS).getAbsolutePath(), new File("src/test/resources" + "/packageAccess/correctMappings").getAbsolutePath());
19 "/packageAccess/correctMappings").getAbsolutePath());
20 } 18 }
21} 19}
diff --git a/enigma-server/build.gradle b/enigma-server/build.gradle
index 2764558..873adb9 100644
--- a/enigma-server/build.gradle
+++ b/enigma-server/build.gradle
@@ -1,10 +1,10 @@
1plugins { 1plugins {
2 id 'application' 2 id 'application'
3} 3}
4 4
5dependencies { 5dependencies {
6 implementation project(':enigma') 6 implementation project(':enigma')
7 implementation 'net.sf.jopt-simple:jopt-simple:6.0-alpha-3' 7 implementation 'net.sf.jopt-simple:jopt-simple:6.0-alpha-3'
8} 8}
9 9
10mainClassName = 'cuchaz.enigma.network.DedicatedEnigmaServer' 10mainClassName = 'cuchaz.enigma.network.DedicatedEnigmaServer'
diff --git a/enigma-server/docs/protocol.md b/enigma-server/docs/protocol.md
index 83ef4c0..f642e13 100644
--- a/enigma-server/docs/protocol.md
+++ b/enigma-server/docs/protocol.md
@@ -1,4 +1,5 @@
1# Enigma protocol 1# Enigma protocol
2
2Enigma uses TCP sockets for communication. Data is sent in each direction as a continuous stream, with packets being 3Enigma uses TCP sockets for communication. Data is sent in each direction as a continuous stream, with packets being
3concatenated one after the other. 4concatenated one after the other.
4 5
@@ -10,6 +11,7 @@ use the same modified UTF format as in `DataOutputStream`, I repeat, the normal
10Strings, see below. 11Strings, see below.
11 12
12## Login protocol 13## Login protocol
14
13``` 15```
14Client Server 16Client Server
15| | 17| |
@@ -22,6 +24,7 @@ Client Server
22| ConfirmChange | 24| ConfirmChange |
23| >>>>>>>>>>>>> | 25| >>>>>>>>>>>>> |
24``` 26```
27
251. On connect, the client sends a login packet to the server. This allows the server to test the validity of the client, 281. On connect, the client sends a login packet to the server. This allows the server to test the validity of the client,
26 as well as allowing the client to declare metadata about itself, such as the username. 29 as well as allowing the client to declare metadata about itself, such as the username.
271. After validating the login packet, the server sends all its mappings to the client, and the client will apply them. 301. After validating the login packet, the server sends all its mappings to the client, and the client will apply them.
@@ -29,15 +32,18 @@ Client Server
29 has received the mappings and is in sync with the server. Once the server receives this packet, the client will be 32 has received the mappings and is in sync with the server. Once the server receives this packet, the client will be
30 allowed to modify mappings. 33 allowed to modify mappings.
31 34
32The server will not accept any other packets from the client until this entire exchange has been completed. 35The server will not accept any other packets from the client until this entire exchange has been completed.
33 36
34## Kicking clients 37## Kicking clients
38
35When the server kicks a client, it may optionally send a `Kick` packet immediately before closing the connection, which 39When the server kicks a client, it may optionally send a `Kick` packet immediately before closing the connection, which
36contains the reason why the client was kicked (so the client can display it to the user). This is not required though - 40contains the reason why the client was kicked (so the client can display it to the user). This is not required though -
37the server may simply terminate the connection. 41the server may simply terminate the connection.
38 42
39## Changing mappings 43## Changing mappings
44
40This section uses the example of renaming, but the same pattern applies to all mapping changes. 45This section uses the example of renaming, but the same pattern applies to all mapping changes.
46
41``` 47```
42Client A Server Client B 48Client A Server Client B
43| | | 49| | |
@@ -66,19 +72,23 @@ Client A Server Client B
66 server will unlock that mapping for that client and allow them to make changes again. 72 server will unlock that mapping for that client and allow them to make changes again.
67 73
68## Packets 74## Packets
75
69```c 76```c
70struct Packet { 77struct Packet {
71 unsigned short packet_id; 78 unsigned short packet_id;
72 data[]; // depends on packet_id 79 data[]; // depends on packet_id
73} 80}
74``` 81```
82
75The IDs for client-to-server packets are as follows: 83The IDs for client-to-server packets are as follows:
84
76- 0: `Login` 85- 0: `Login`
77- 1: `ConfirmChange` 86- 1: `ConfirmChange`
78- 6: `Message` 87- 6: `Message`
79- 7: `EntryChange` 88- 7: `EntryChange`
80 89
81The IDs for server-to-client packets are as follows: 90The IDs for server-to-client packets are as follows:
91
82- 0: `Kick` 92- 0: `Kick`
83- 1: `SyncMappings` 93- 1: `SyncMappings`
84- 6: `Message` 94- 6: `Message`
@@ -86,17 +96,20 @@ The IDs for server-to-client packets are as follows:
86- 8: `EntryChange` 96- 8: `EntryChange`
87 97
88### The utf struct 98### The utf struct
99
89```c 100```c
90struct utf { 101struct utf {
91 unsigned short length; 102 unsigned short length;
92 byte data[length]; 103 byte data[length];
93} 104}
94``` 105```
106
95- `length`: The number of bytes in the UTF-8 encoding of the string. Note, this may not be the same as the number of 107- `length`: The number of bytes in the UTF-8 encoding of the string. Note, this may not be the same as the number of
96 Unicode characters in the string. 108 Unicode characters in the string.
97- `data`: A standard UTF-8 encoded byte array representing the string. 109- `data`: A standard UTF-8 encoded byte array representing the string.
98 110
99### The Entry struct 111### The Entry struct
112
100```c 113```c
101enum EntryType { 114enum EntryType {
102 ENTRY_CLASS = 0, ENTRY_FIELD = 1, ENTRY_METHOD = 2, ENTRY_LOCAL_VAR = 3; 115 ENTRY_CLASS = 0, ENTRY_FIELD = 1, ENTRY_METHOD = 2, ENTRY_LOCAL_VAR = 3;
@@ -121,9 +134,10 @@ struct Entry {
121 } 134 }
122} 135}
123``` 136```
137
124- `type`: The type of entry this is. One of `ENTRY_CLASS`, `ENTRY_FIELD`, `ENTRY_METHOD` or `ENTRY_LOCAL_VAR`. 138- `type`: The type of entry this is. One of `ENTRY_CLASS`, `ENTRY_FIELD`, `ENTRY_METHOD` or `ENTRY_LOCAL_VAR`.
125- `parent`: The parent entry. Only class entries may have no parent. fields, methods and inner classes must have their 139- `parent`: The parent entry. Only class entries may have no parent. fields, methods and inner classes must have their
126 containing class as their parent. Local variables have a method as a parent. 140 containing class as their parent. Local variables have a method as a parent.
127- `name`: The class/field/method/variable name. 141- `name`: The class/field/method/variable name.
128- `javadoc`: The javadoc of an entry, if present. 142- `javadoc`: The javadoc of an entry, if present.
129- `descriptor`: The field/method descriptor. 143- `descriptor`: The field/method descriptor.
@@ -131,6 +145,7 @@ struct Entry {
131- `parameter`: Whether the local variable is a parameter. 145- `parameter`: Whether the local variable is a parameter.
132 146
133### The Message struct 147### The Message struct
148
134```c 149```c
135enum MessageType { 150enum MessageType {
136 MESSAGE_CHAT = 0, 151 MESSAGE_CHAT = 0,
@@ -176,8 +191,9 @@ struct Message {
176 } data; 191 } data;
177}; 192};
178``` 193```
194
179- `type`: The type of message this is. One of `MESSAGE_CHAT`, `MESSAGE_CONNECT`, `MESSAGE_DISCONNECT`, 195- `type`: The type of message this is. One of `MESSAGE_CHAT`, `MESSAGE_CONNECT`, `MESSAGE_DISCONNECT`,
180 `MESSAGE_EDIT_DOCS`, `MESSAGE_MARK_DEOBF`, `MESSAGE_REMOVE_MAPPING`, `MESSAGE_RENAME`. 196 `MESSAGE_EDIT_DOCS`, `MESSAGE_MARK_DEOBF`, `MESSAGE_REMOVE_MAPPING`, `MESSAGE_RENAME`.
181- `chat`: Chat message. Use in case `type` is `MESSAGE_CHAT` 197- `chat`: Chat message. Use in case `type` is `MESSAGE_CHAT`
182- `connect`: Sent when a user connects. Use in case `type` is `MESSAGE_CONNECT` 198- `connect`: Sent when a user connects. Use in case `type` is `MESSAGE_CONNECT`
183- `disconnect`: Sent when a user disconnects. Use in case `type` is `MESSAGE_DISCONNECT` 199- `disconnect`: Sent when a user disconnects. Use in case `type` is `MESSAGE_DISCONNECT`
@@ -191,6 +207,7 @@ struct Message {
191- `new_name`: The new name for the entry. 207- `new_name`: The new name for the entry.
192 208
193### The entry_change struct 209### The entry_change struct
210
194```c 211```c
195typedef enum tristate_change { 212typedef enum tristate_change {
196 TRISTATE_CHANGE_UNCHANGED = 0, 213 TRISTATE_CHANGE_UNCHANGED = 0,
@@ -224,6 +241,7 @@ struct entry_change {
224 } 241 }
225} 242}
226``` 243```
244
227- `entry`: The entry this change gets applied to. 245- `entry`: The entry this change gets applied to.
228- `flags`: See definition of `entry_change_flags`. 246- `flags`: See definition of `entry_change_flags`.
229- `deobf_name`: The new deobfuscated name, if deobf_name_change == TRISTATE_CHANGE_SET 247- `deobf_name`: The new deobfuscated name, if deobf_name_change == TRISTATE_CHANGE_SET
@@ -231,6 +249,7 @@ struct entry_change {
231- `access_modifiers`: The new access modifier, if access_change == TRISTATE_CHANGE_SET (otherwise 0) 249- `access_modifiers`: The new access modifier, if access_change == TRISTATE_CHANGE_SET (otherwise 0)
232 250
233### Login (client-to-server) 251### Login (client-to-server)
252
234```c 253```c
235struct LoginC2SPacket { 254struct LoginC2SPacket {
236 unsigned short protocol_version; 255 unsigned short protocol_version;
@@ -240,47 +259,57 @@ struct LoginC2SPacket {
240 utf username; 259 utf username;
241} 260}
242``` 261```
262
243- `protocol_version`: the version of the protocol. If the version does not match on the server, then the client will be 263- `protocol_version`: the version of the protocol. If the version does not match on the server, then the client will be
244 kicked immediately. Currently always equal to 0. 264 kicked immediately. Currently always equal to 0.
245- `checksum`: the SHA-1 hash of the JAR file the client has open. If this does not match the SHA-1 hash of the JAR file 265- `checksum`: the SHA-1 hash of the JAR file the client has open. If this does not match the SHA-1 hash of the JAR file
246 the server has open, the client will be kicked. 266 the server has open, the client will be kicked.
247- `password`: the password needed to log into the server. Note that each `char` is 2 bytes, as per the Java data type. 267- `password`: the password needed to log into the server. Note that each `char` is 2 bytes, as per the Java data type.
248 If this password is incorrect, the client will be kicked. 268 If this password is incorrect, the client will be kicked.
249- `username`: the username of the user logging in. If the username is not unique, the client will be kicked. 269- `username`: the username of the user logging in. If the username is not unique, the client will be kicked.
250 270
251### ConfirmChange (client-to-server) 271### ConfirmChange (client-to-server)
272
252```c 273```c
253struct ConfirmChangeC2SPacket { 274struct ConfirmChangeC2SPacket {
254 unsigned short sync_id; 275 unsigned short sync_id;
255} 276}
256``` 277```
278
257- `sync_id`: the sync ID to confirm. 279- `sync_id`: the sync ID to confirm.
258 280
259### Message (client-to-server) 281### Message (client-to-server)
282
260```c 283```c
261struct MessageC2SPacket { 284struct MessageC2SPacket {
262 utf message; 285 utf message;
263} 286}
264``` 287```
288
265- `message`: The text message the user sent. 289- `message`: The text message the user sent.
266 290
267### EntryChange (client-to-server) 291### EntryChange (client-to-server)
292
268```c 293```c
269struct EntryChangeC2SPacket { 294struct EntryChangeC2SPacket {
270 entry_change change; 295 entry_change change;
271} 296}
272``` 297```
298
273- `change`: The change to apply. 299- `change`: The change to apply.
274 300
275### Kick (server-to-client) 301### Kick (server-to-client)
302
276```c 303```c
277struct KickS2CPacket { 304struct KickS2CPacket {
278 utf reason; 305 utf reason;
279} 306}
280``` 307```
308
281- `reason`: the reason for the kick, may or may not be a translation key for the client to display to the user. 309- `reason`: the reason for the kick, may or may not be a translation key for the client to display to the user.
282 310
283### SyncMappings (server-to-client) 311### SyncMappings (server-to-client)
312
284```c 313```c
285struct SyncMappingsS2CPacket { 314struct SyncMappingsS2CPacket {
286 int num_roots; 315 int num_roots;
@@ -296,6 +325,7 @@ struct MappingNode {
296} 325}
297typedef { Entry but without the has_parent or parent fields } NoParentEntry; 326typedef { Entry but without the has_parent or parent fields } NoParentEntry;
298``` 327```
328
299- `roots`: The root mapping nodes, containing all the entries without parents. 329- `roots`: The root mapping nodes, containing all the entries without parents.
300- `obf_entry`: The value of a node, containing the obfuscated name and descriptor of the entry. 330- `obf_entry`: The value of a node, containing the obfuscated name and descriptor of the entry.
301- `name`: The deobfuscated name of the entry, if it exists, otherwise the empty string. 331- `name`: The deobfuscated name of the entry, if it exists, otherwise the empty string.
@@ -303,6 +333,7 @@ typedef { Entry but without the has_parent or parent fields } NoParentEntry;
303- `children`: The children of this node 333- `children`: The children of this node
304 334
305### Message (server-to-client) 335### Message (server-to-client)
336
306```c 337```c
307struct MessageS2CPacket { 338struct MessageS2CPacket {
308 Message message; 339 Message message;
@@ -310,6 +341,7 @@ struct MessageS2CPacket {
310``` 341```
311 342
312### UserList (server-to-client) 343### UserList (server-to-client)
344
313```c 345```c
314struct UserListS2CPacket { 346struct UserListS2CPacket {
315 unsigned short len; 347 unsigned short len;
@@ -318,11 +350,13 @@ struct UserListS2CPacket {
318``` 350```
319 351
320### EntryChange (server-to-client) 352### EntryChange (server-to-client)
353
321```c 354```c
322struct EntryChangeS2CPacket { 355struct EntryChangeS2CPacket {
323 uint16_t sync_id; 356 uint16_t sync_id;
324 entry_change change; 357 entry_change change;
325} 358}
326``` 359```
360
327- `sync_id`: The sync ID of the change for locking purposes. 361- `sync_id`: The sync ID of the change for locking purposes.
328- `change`: The change to apply. \ No newline at end of file 362- `change`: The change to apply. \ No newline at end of file
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/ClientPacketHandler.java b/enigma-server/src/main/java/cuchaz/enigma/network/ClientPacketHandler.java
index a651fe8..8fcd437 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/ClientPacketHandler.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/ClientPacketHandler.java
@@ -1,22 +1,22 @@
1package cuchaz.enigma.network; 1package cuchaz.enigma.network;
2 2
3import java.util.List;
4
5import cuchaz.enigma.network.packet.Packet;
3import cuchaz.enigma.translation.mapping.EntryChange; 6import cuchaz.enigma.translation.mapping.EntryChange;
4import cuchaz.enigma.translation.mapping.EntryMapping; 7import cuchaz.enigma.translation.mapping.EntryMapping;
5import cuchaz.enigma.translation.mapping.tree.EntryTree; 8import cuchaz.enigma.translation.mapping.tree.EntryTree;
6import cuchaz.enigma.network.packet.Packet;
7
8import java.util.List;
9 9
10public interface ClientPacketHandler { 10public interface ClientPacketHandler {
11 void openMappings(EntryTree<EntryMapping> mappings); 11 void openMappings(EntryTree<EntryMapping> mappings);
12 12
13 boolean applyChangeFromServer(EntryChange<?> change); 13 boolean applyChangeFromServer(EntryChange<?> change);
14 14
15 void disconnectIfConnected(String reason); 15 void disconnectIfConnected(String reason);
16 16
17 void sendPacket(Packet<ServerPacketHandler> packet); 17 void sendPacket(Packet<ServerPacketHandler> packet);
18 18
19 void addMessage(Message message); 19 void addMessage(Message message);
20 20
21 void updateUserList(List<String> users); 21 void updateUserList(List<String> users);
22} 22}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/DedicatedEnigmaServer.java b/enigma-server/src/main/java/cuchaz/enigma/network/DedicatedEnigmaServer.java
index 41f0834..eb22a50 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/DedicatedEnigmaServer.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/DedicatedEnigmaServer.java
@@ -1,17 +1,5 @@
1package cuchaz.enigma.network; 1package cuchaz.enigma.network;
2 2
3import com.google.common.io.MoreFiles;
4import cuchaz.enigma.*;
5import cuchaz.enigma.classprovider.ClasspathClassProvider;
6import cuchaz.enigma.translation.mapping.serde.MappingParseException;
7import cuchaz.enigma.translation.mapping.EntryRemapper;
8import cuchaz.enigma.translation.mapping.serde.MappingFormat;
9import cuchaz.enigma.utils.Utils;
10import joptsimple.OptionParser;
11import joptsimple.OptionSet;
12import joptsimple.OptionSpec;
13import joptsimple.ValueConverter;
14
15import java.io.IOException; 3import java.io.IOException;
16import java.io.PrintWriter; 4import java.io.PrintWriter;
17import java.nio.file.Files; 5import java.nio.file.Files;
@@ -22,24 +10,30 @@ import java.util.concurrent.Executors;
22import java.util.concurrent.LinkedBlockingDeque; 10import java.util.concurrent.LinkedBlockingDeque;
23import java.util.concurrent.TimeUnit; 11import java.util.concurrent.TimeUnit;
24 12
25public class DedicatedEnigmaServer extends EnigmaServer { 13import com.google.common.io.MoreFiles;
14import joptsimple.OptionParser;
15import joptsimple.OptionSet;
16import joptsimple.OptionSpec;
17import joptsimple.ValueConverter;
18
19import cuchaz.enigma.Enigma;
20import cuchaz.enigma.EnigmaProfile;
21import cuchaz.enigma.EnigmaProject;
22import cuchaz.enigma.ProgressListener;
23import cuchaz.enigma.classprovider.ClasspathClassProvider;
24import cuchaz.enigma.translation.mapping.EntryRemapper;
25import cuchaz.enigma.translation.mapping.serde.MappingFormat;
26import cuchaz.enigma.translation.mapping.serde.MappingParseException;
27import cuchaz.enigma.utils.Utils;
26 28
29public class DedicatedEnigmaServer extends EnigmaServer {
27 private final EnigmaProfile profile; 30 private final EnigmaProfile profile;
28 private final MappingFormat mappingFormat; 31 private final MappingFormat mappingFormat;
29 private final Path mappingsFile; 32 private final Path mappingsFile;
30 private final PrintWriter log; 33 private final PrintWriter log;
31 private BlockingQueue<Runnable> tasks = new LinkedBlockingDeque<>(); 34 private BlockingQueue<Runnable> tasks = new LinkedBlockingDeque<>();
32 35
33 public DedicatedEnigmaServer( 36 public DedicatedEnigmaServer(byte[] jarChecksum, char[] password, EnigmaProfile profile, MappingFormat mappingFormat, Path mappingsFile, PrintWriter log, EntryRemapper mappings, int port) {
34 byte[] jarChecksum,
35 char[] password,
36 EnigmaProfile profile,
37 MappingFormat mappingFormat,
38 Path mappingsFile,
39 PrintWriter log,
40 EntryRemapper mappings,
41 int port
42 ) {
43 super(jarChecksum, password, mappings, port); 37 super(jarChecksum, password, mappings, port);
44 this.profile = profile; 38 this.profile = profile;
45 this.mappingFormat = mappingFormat; 39 this.mappingFormat = mappingFormat;
@@ -61,33 +55,17 @@ public class DedicatedEnigmaServer extends EnigmaServer {
61 public static void main(String[] args) { 55 public static void main(String[] args) {
62 OptionParser parser = new OptionParser(); 56 OptionParser parser = new OptionParser();
63 57
64 OptionSpec<Path> jarOpt = parser.accepts("jar", "Jar file to open at startup") 58 OptionSpec<Path> jarOpt = parser.accepts("jar", "Jar file to open at startup").withRequiredArg().required().withValuesConvertedBy(PathConverter.INSTANCE);
65 .withRequiredArg()
66 .required()
67 .withValuesConvertedBy(PathConverter.INSTANCE);
68 59
69 OptionSpec<Path> mappingsOpt = parser.accepts("mappings", "Mappings file to open at startup") 60 OptionSpec<Path> mappingsOpt = parser.accepts("mappings", "Mappings file to open at startup").withRequiredArg().required().withValuesConvertedBy(PathConverter.INSTANCE);
70 .withRequiredArg()
71 .required()
72 .withValuesConvertedBy(PathConverter.INSTANCE);
73 61
74 OptionSpec<Path> profileOpt = parser.accepts("profile", "Profile json to apply at startup") 62 OptionSpec<Path> profileOpt = parser.accepts("profile", "Profile json to apply at startup").withRequiredArg().withValuesConvertedBy(PathConverter.INSTANCE);
75 .withRequiredArg()
76 .withValuesConvertedBy(PathConverter.INSTANCE);
77 63
78 OptionSpec<Integer> portOpt = parser.accepts("port", "Port to run the server on") 64 OptionSpec<Integer> portOpt = parser.accepts("port", "Port to run the server on").withOptionalArg().ofType(Integer.class).defaultsTo(EnigmaServer.DEFAULT_PORT);
79 .withOptionalArg()
80 .ofType(Integer.class)
81 .defaultsTo(EnigmaServer.DEFAULT_PORT);
82 65
83 OptionSpec<String> passwordOpt = parser.accepts("password", "The password to join the server") 66 OptionSpec<String> passwordOpt = parser.accepts("password", "The password to join the server").withRequiredArg().defaultsTo("");
84 .withRequiredArg()
85 .defaultsTo("");
86 67
87 OptionSpec<Path> logFileOpt = parser.accepts("log", "The log file to write to") 68 OptionSpec<Path> logFileOpt = parser.accepts("log", "The log file to write to").withRequiredArg().withValuesConvertedBy(PathConverter.INSTANCE).defaultsTo(Paths.get("log.txt"));
88 .withRequiredArg()
89 .withValuesConvertedBy(PathConverter.INSTANCE)
90 .defaultsTo(Paths.get("log.txt"));
91 69
92 OptionSet parsedArgs = parser.parse(args); 70 OptionSet parsedArgs = parser.parse(args);
93 Path jar = parsedArgs.valueOf(jarOpt); 71 Path jar = parsedArgs.valueOf(jarOpt);
@@ -95,14 +73,17 @@ public class DedicatedEnigmaServer extends EnigmaServer {
95 Path profileFile = parsedArgs.valueOf(profileOpt); 73 Path profileFile = parsedArgs.valueOf(profileOpt);
96 int port = parsedArgs.valueOf(portOpt); 74 int port = parsedArgs.valueOf(portOpt);
97 char[] password = parsedArgs.valueOf(passwordOpt).toCharArray(); 75 char[] password = parsedArgs.valueOf(passwordOpt).toCharArray();
76
98 if (password.length > EnigmaServer.MAX_PASSWORD_LENGTH) { 77 if (password.length > EnigmaServer.MAX_PASSWORD_LENGTH) {
99 System.err.println("Password too long, must be at most " + EnigmaServer.MAX_PASSWORD_LENGTH + " characters"); 78 System.err.println("Password too long, must be at most " + EnigmaServer.MAX_PASSWORD_LENGTH + " characters");
100 System.exit(1); 79 System.exit(1);
101 } 80 }
81
102 Path logFile = parsedArgs.valueOf(logFileOpt); 82 Path logFile = parsedArgs.valueOf(logFileOpt);
103 83
104 System.out.println("Starting Enigma server"); 84 System.out.println("Starting Enigma server");
105 DedicatedEnigmaServer server; 85 DedicatedEnigmaServer server;
86
106 try { 87 try {
107 byte[] checksum = Utils.zipSha1(parsedArgs.valueOf(jarOpt)); 88 byte[] checksum = Utils.zipSha1(parsedArgs.valueOf(jarOpt));
108 89
@@ -113,10 +94,12 @@ public class DedicatedEnigmaServer extends EnigmaServer {
113 94
114 MappingFormat mappingFormat = MappingFormat.ENIGMA_DIRECTORY; 95 MappingFormat mappingFormat = MappingFormat.ENIGMA_DIRECTORY;
115 EntryRemapper mappings; 96 EntryRemapper mappings;
97
116 if (!Files.exists(mappingsFile)) { 98 if (!Files.exists(mappingsFile)) {
117 mappings = EntryRemapper.empty(project.getJarIndex()); 99 mappings = EntryRemapper.empty(project.getJarIndex());
118 } else { 100 } else {
119 System.out.println("Reading mappings..."); 101 System.out.println("Reading mappings...");
102
120 if (Files.isDirectory(mappingsFile)) { 103 if (Files.isDirectory(mappingsFile)) {
121 mappingFormat = MappingFormat.ENIGMA_DIRECTORY; 104 mappingFormat = MappingFormat.ENIGMA_DIRECTORY;
122 } else if ("zip".equalsIgnoreCase(MoreFiles.getFileExtension(mappingsFile))) { 105 } else if ("zip".equalsIgnoreCase(MoreFiles.getFileExtension(mappingsFile))) {
@@ -124,6 +107,7 @@ public class DedicatedEnigmaServer extends EnigmaServer {
124 } else { 107 } else {
125 mappingFormat = MappingFormat.ENIGMA_FILE; 108 mappingFormat = MappingFormat.ENIGMA_FILE;
126 } 109 }
110
127 mappings = EntryRemapper.mapped(project.getJarIndex(), mappingFormat.read(mappingsFile, ProgressListener.none(), profile.getMappingSaveParameters())); 111 mappings = EntryRemapper.mapped(project.getJarIndex(), mappingFormat.read(mappingsFile, ProgressListener.none(), profile.getMappingSaveParameters()));
128 } 112 }
129 113
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaClient.java b/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaClient.java
index 71bd011..69e4f5e 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaClient.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaClient.java
@@ -1,15 +1,20 @@
1package cuchaz.enigma.network; 1package cuchaz.enigma.network;
2 2
3import cuchaz.enigma.network.packet.Packet; 3import java.io.DataInput;
4import cuchaz.enigma.network.packet.PacketRegistry; 4import java.io.DataInputStream;
5 5import java.io.DataOutput;
6import javax.swing.*; 6import java.io.DataOutputStream;
7import java.io.*; 7import java.io.EOFException;
8import java.io.IOException;
8import java.net.Socket; 9import java.net.Socket;
9import java.net.SocketException; 10import java.net.SocketException;
10 11
11public class EnigmaClient { 12import javax.swing.SwingUtilities;
12 13
14import cuchaz.enigma.network.packet.Packet;
15import cuchaz.enigma.network.packet.PacketRegistry;
16
17public class EnigmaClient {
13 private final ClientPacketHandler controller; 18 private final ClientPacketHandler controller;
14 19
15 private final String ip; 20 private final String ip;
@@ -29,17 +34,22 @@ public class EnigmaClient {
29 Thread thread = new Thread(() -> { 34 Thread thread = new Thread(() -> {
30 try { 35 try {
31 DataInput input = new DataInputStream(socket.getInputStream()); 36 DataInput input = new DataInputStream(socket.getInputStream());
37
32 while (true) { 38 while (true) {
33 int packetId; 39 int packetId;
40
34 try { 41 try {
35 packetId = input.readUnsignedByte(); 42 packetId = input.readUnsignedByte();
36 } catch (EOFException | SocketException e) { 43 } catch (EOFException | SocketException e) {
37 break; 44 break;
38 } 45 }
46
39 Packet<ClientPacketHandler> packet = PacketRegistry.createS2CPacket(packetId); 47 Packet<ClientPacketHandler> packet = PacketRegistry.createS2CPacket(packetId);
48
40 if (packet == null) { 49 if (packet == null) {
41 throw new IOException("Received invalid packet id " + packetId); 50 throw new IOException("Received invalid packet id " + packetId);
42 } 51 }
52
43 packet.read(input); 53 packet.read(input);
44 SwingUtilities.invokeLater(() -> packet.handle(controller)); 54 SwingUtilities.invokeLater(() -> packet.handle(controller));
45 } 55 }
@@ -47,6 +57,7 @@ public class EnigmaClient {
47 controller.disconnectIfConnected(e.toString()); 57 controller.disconnectIfConnected(e.toString());
48 return; 58 return;
49 } 59 }
60
50 controller.disconnectIfConnected("Disconnected"); 61 controller.disconnectIfConnected("Disconnected");
51 }); 62 });
52 thread.setName("Client I/O thread"); 63 thread.setName("Client I/O thread");
@@ -65,7 +76,6 @@ public class EnigmaClient {
65 } 76 }
66 } 77 }
67 78
68
69 public void sendPacket(Packet<ServerPacketHandler> packet) { 79 public void sendPacket(Packet<ServerPacketHandler> packet) {
70 try { 80 try {
71 output.writeByte(PacketRegistry.getC2SId(packet)); 81 output.writeByte(PacketRegistry.getC2SId(packet));
@@ -74,5 +84,4 @@ public class EnigmaClient {
74 controller.disconnectIfConnected(e.toString()); 84 controller.disconnectIfConnected(e.toString());
75 } 85 }
76 } 86 }
77
78} 87}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaServer.java b/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaServer.java
index 1ce359b..8872735 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaServer.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaServer.java
@@ -1,20 +1,35 @@
1package cuchaz.enigma.network; 1package cuchaz.enigma.network;
2 2
3import java.io.*; 3import java.io.DataInput;
4import java.io.DataInputStream;
5import java.io.DataOutput;
6import java.io.DataOutputStream;
7import java.io.EOFException;
8import java.io.IOException;
4import java.net.ServerSocket; 9import java.net.ServerSocket;
5import java.net.Socket; 10import java.net.Socket;
6import java.net.SocketException; 11import java.net.SocketException;
7import java.util.*; 12import java.util.ArrayList;
13import java.util.Collections;
14import java.util.HashMap;
15import java.util.HashSet;
16import java.util.List;
17import java.util.Map;
18import java.util.Set;
8import java.util.concurrent.CopyOnWriteArrayList; 19import java.util.concurrent.CopyOnWriteArrayList;
9 20
10import cuchaz.enigma.network.packet.*; 21import cuchaz.enigma.network.packet.EntryChangeS2CPacket;
22import cuchaz.enigma.network.packet.KickS2CPacket;
23import cuchaz.enigma.network.packet.MessageS2CPacket;
24import cuchaz.enigma.network.packet.Packet;
25import cuchaz.enigma.network.packet.PacketRegistry;
26import cuchaz.enigma.network.packet.UserListS2CPacket;
11import cuchaz.enigma.translation.mapping.EntryChange; 27import cuchaz.enigma.translation.mapping.EntryChange;
12import cuchaz.enigma.translation.mapping.EntryMapping; 28import cuchaz.enigma.translation.mapping.EntryMapping;
13import cuchaz.enigma.translation.mapping.EntryRemapper; 29import cuchaz.enigma.translation.mapping.EntryRemapper;
14import cuchaz.enigma.translation.representation.entry.Entry; 30import cuchaz.enigma.translation.representation.entry.Entry;
15 31
16public abstract class EnigmaServer { 32public abstract class EnigmaServer {
17
18 // https://discordapp.com/channels/507304429255393322/566418023372816394/700292322918793347 33 // https://discordapp.com/channels/507304429255393322/566418023372816394/700292322918793347
19 public static final int DEFAULT_PORT = 34712; 34 public static final int DEFAULT_PORT = 34712;
20 public static final int PROTOCOL_VERSION = 1; 35 public static final int PROTOCOL_VERSION = 1;
@@ -71,17 +86,22 @@ public abstract class EnigmaServer {
71 Thread thread = new Thread(() -> { 86 Thread thread = new Thread(() -> {
72 try { 87 try {
73 DataInput input = new DataInputStream(client.getInputStream()); 88 DataInput input = new DataInputStream(client.getInputStream());
89
74 while (true) { 90 while (true) {
75 int packetId; 91 int packetId;
92
76 try { 93 try {
77 packetId = input.readUnsignedByte(); 94 packetId = input.readUnsignedByte();
78 } catch (EOFException | SocketException e) { 95 } catch (EOFException | SocketException e) {
79 break; 96 break;
80 } 97 }
98
81 Packet<ServerPacketHandler> packet = PacketRegistry.createC2SPacket(packetId); 99 Packet<ServerPacketHandler> packet = PacketRegistry.createC2SPacket(packetId);
100
82 if (packet == null) { 101 if (packet == null) {
83 throw new IOException("Received invalid packet id " + packetId); 102 throw new IOException("Received invalid packet id " + packetId);
84 } 103 }
104
85 packet.read(input); 105 packet.read(input);
86 runOnThread(() -> packet.handle(new ServerPacketHandler(client, this))); 106 runOnThread(() -> packet.handle(new ServerPacketHandler(client, this)));
87 } 107 }
@@ -90,6 +110,7 @@ public abstract class EnigmaServer {
90 e.printStackTrace(); 110 e.printStackTrace();
91 return; 111 return;
92 } 112 }
113
93 kick(client, "disconnect.disconnected"); 114 kick(client, "disconnect.disconnected");
94 }); 115 });
95 thread.setName("Server I/O thread #" + (nextIoId++)); 116 thread.setName("Server I/O thread #" + (nextIoId++));
@@ -103,6 +124,7 @@ public abstract class EnigmaServer {
103 for (Socket client : clients) { 124 for (Socket client : clients) {
104 kick(client, "disconnect.server_closed"); 125 kick(client, "disconnect.server_closed");
105 } 126 }
127
106 try { 128 try {
107 socket.close(); 129 socket.close();
108 } catch (IOException e) { 130 } catch (IOException e) {
@@ -114,7 +136,9 @@ public abstract class EnigmaServer {
114 } 136 }
115 137
116 public void kick(Socket client, String reason) { 138 public void kick(Socket client, String reason) {
117 if (!clients.remove(client)) return; 139 if (!clients.remove(client)) {
140 return;
141 }
118 142
119 sendPacket(client, new KickS2CPacket(reason)); 143 sendPacket(client, new KickS2CPacket(reason));
120 144
@@ -123,6 +147,7 @@ public abstract class EnigmaServer {
123 return list.isEmpty(); 147 return list.isEmpty();
124 }); 148 });
125 String username = usernames.remove(client); 149 String username = usernames.remove(client);
150
126 try { 151 try {
127 client.close(); 152 client.close();
128 } catch (IOException e) { 153 } catch (IOException e) {
@@ -134,6 +159,7 @@ public abstract class EnigmaServer {
134 System.out.println("Kicked " + username + " because " + reason); 159 System.out.println("Kicked " + username + " because " + reason);
135 sendMessage(Message.disconnect(username)); 160 sendMessage(Message.disconnect(username));
136 } 161 }
162
137 sendUsernamePacket(); 163 sendUsernamePacket();
138 } 164 }
139 165
@@ -159,6 +185,7 @@ public abstract class EnigmaServer {
159 public void sendPacket(Socket client, Packet<ClientPacketHandler> packet) { 185 public void sendPacket(Socket client, Packet<ClientPacketHandler> packet) {
160 if (!client.isClosed()) { 186 if (!client.isClosed()) {
161 int packetId = PacketRegistry.getS2CId(packet); 187 int packetId = PacketRegistry.getS2CId(packet);
188
162 try { 189 try {
163 DataOutput output = new DataOutputStream(client.getOutputStream()); 190 DataOutput output = new DataOutputStream(client.getOutputStream());
164 output.writeByte(packetId); 191 output.writeByte(packetId);
@@ -192,9 +219,11 @@ public abstract class EnigmaServer {
192 } 219 }
193 220
194 Integer syncId = syncIds.get(entry); 221 Integer syncId = syncIds.get(entry);
222
195 if (syncId == null) { 223 if (syncId == null) {
196 return true; 224 return true;
197 } 225 }
226
198 Set<Socket> clients = clientsNeedingConfirmation.get(syncId); 227 Set<Socket> clients = clientsNeedingConfirmation.get(syncId);
199 return clients == null || !clients.contains(client); 228 return clients == null || !clients.contains(client);
200 } 229 }
@@ -202,14 +231,18 @@ public abstract class EnigmaServer {
202 public int lockEntry(Socket exception, Entry<?> entry) { 231 public int lockEntry(Socket exception, Entry<?> entry) {
203 int syncId = nextSyncId; 232 int syncId = nextSyncId;
204 nextSyncId++; 233 nextSyncId++;
234
205 // sync id is sent as an unsigned short, can't have more than 65536 235 // sync id is sent as an unsigned short, can't have more than 65536
206 if (nextSyncId == 65536) { 236 if (nextSyncId == 65536) {
207 nextSyncId = DUMMY_SYNC_ID + 1; 237 nextSyncId = DUMMY_SYNC_ID + 1;
208 } 238 }
239
209 Integer oldSyncId = syncIds.get(entry); 240 Integer oldSyncId = syncIds.get(entry);
241
210 if (oldSyncId != null) { 242 if (oldSyncId != null) {
211 clientsNeedingConfirmation.remove(oldSyncId); 243 clientsNeedingConfirmation.remove(oldSyncId);
212 } 244 }
245
213 syncIds.put(entry, syncId); 246 syncIds.put(entry, syncId);
214 inverseSyncIds.put(syncId, entry); 247 inverseSyncIds.put(syncId, entry);
215 Set<Socket> clients = new HashSet<>(this.clients); 248 Set<Socket> clients = new HashSet<>(this.clients);
@@ -224,8 +257,10 @@ public abstract class EnigmaServer {
224 } 257 }
225 258
226 Set<Socket> clients = clientsNeedingConfirmation.get(syncId); 259 Set<Socket> clients = clientsNeedingConfirmation.get(syncId);
260
227 if (clients != null) { 261 if (clients != null) {
228 clients.remove(client); 262 clients.remove(client);
263
229 if (clients.isEmpty()) { 264 if (clients.isEmpty()) {
230 clientsNeedingConfirmation.remove(syncId); 265 clientsNeedingConfirmation.remove(syncId);
231 syncIds.remove(inverseSyncIds.remove(syncId)); 266 syncIds.remove(inverseSyncIds.remove(syncId));
@@ -236,6 +271,7 @@ public abstract class EnigmaServer {
236 public void sendCorrectMapping(Socket client, Entry<?> entry, boolean refreshClassTree) { 271 public void sendCorrectMapping(Socket client, Entry<?> entry, boolean refreshClassTree) {
237 EntryMapping oldMapping = mappings.getDeobfMapping(entry); 272 EntryMapping oldMapping = mappings.getDeobfMapping(entry);
238 String oldName = oldMapping.targetName(); 273 String oldName = oldMapping.targetName();
274
239 if (oldName == null) { 275 if (oldName == null) {
240 sendPacket(client, new EntryChangeS2CPacket(DUMMY_SYNC_ID, EntryChange.modify(entry).clearDeobfName())); 276 sendPacket(client, new EntryChangeS2CPacket(DUMMY_SYNC_ID, EntryChange.modify(entry).clearDeobfName()));
241 } else { 277 } else {
@@ -269,5 +305,4 @@ public abstract class EnigmaServer {
269 log(String.format("[MSG] %s", message.translate())); 305 log(String.format("[MSG] %s", message.translate()));
270 sendToAll(new MessageS2CPacket(message)); 306 sendToAll(new MessageS2CPacket(message));
271 } 307 }
272
273} 308}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/IntegratedEnigmaServer.java b/enigma-server/src/main/java/cuchaz/enigma/network/IntegratedEnigmaServer.java
index 21c6825..99e4e99 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/IntegratedEnigmaServer.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/IntegratedEnigmaServer.java
@@ -1,8 +1,8 @@
1package cuchaz.enigma.network; 1package cuchaz.enigma.network;
2 2
3import cuchaz.enigma.translation.mapping.EntryRemapper; 3import javax.swing.SwingUtilities;
4 4
5import javax.swing.*; 5import cuchaz.enigma.translation.mapping.EntryRemapper;
6 6
7public class IntegratedEnigmaServer extends EnigmaServer { 7public class IntegratedEnigmaServer extends EnigmaServer {
8 public IntegratedEnigmaServer(byte[] jarChecksum, char[] password, EntryRemapper mappings, int port) { 8 public IntegratedEnigmaServer(byte[] jarChecksum, char[] password, EntryRemapper mappings, int port) {
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/Message.java b/enigma-server/src/main/java/cuchaz/enigma/network/Message.java
index c157838..d49e60c 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/Message.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/Message.java
@@ -10,9 +10,8 @@ import cuchaz.enigma.translation.representation.entry.Entry;
10import cuchaz.enigma.utils.I18n; 10import cuchaz.enigma.utils.I18n;
11 11
12public abstract class Message { 12public abstract class Message {
13
14 public final String user; 13 public final String user;
15 14
16 public static Chat chat(String user, String message) { 15 public static Chat chat(String user, String message) {
17 return new Chat(user, message); 16 return new Chat(user, message);
18 } 17 }
@@ -47,34 +46,37 @@ public abstract class Message {
47 46
48 public static Message read(DataInput input) throws IOException { 47 public static Message read(DataInput input) throws IOException {
49 byte typeId = input.readByte(); 48 byte typeId = input.readByte();
49
50 if (typeId < 0 || typeId >= Type.values().length) { 50 if (typeId < 0 || typeId >= Type.values().length) {
51 throw new IOException(String.format("Invalid message type ID %d", typeId)); 51 throw new IOException(String.format("Invalid message type ID %d", typeId));
52 } 52 }
53
53 Type type = Type.values()[typeId]; 54 Type type = Type.values()[typeId];
54 String user = input.readUTF(); 55 String user = input.readUTF();
56
55 switch (type) { 57 switch (type) {
56 case CHAT: 58 case CHAT:
57 String message = input.readUTF(); 59 String message = input.readUTF();
58 return chat(user, message); 60 return chat(user, message);
59 case CONNECT: 61 case CONNECT:
60 return connect(user); 62 return connect(user);
61 case DISCONNECT: 63 case DISCONNECT:
62 return disconnect(user); 64 return disconnect(user);
63 case EDIT_DOCS: 65 case EDIT_DOCS:
64 Entry<?> entry = PacketHelper.readEntry(input); 66 Entry<?> entry = PacketHelper.readEntry(input);
65 return editDocs(user, entry); 67 return editDocs(user, entry);
66 case MARK_DEOBF: 68 case MARK_DEOBF:
67 entry = PacketHelper.readEntry(input); 69 entry = PacketHelper.readEntry(input);
68 return markDeobf(user, entry); 70 return markDeobf(user, entry);
69 case REMOVE_MAPPING: 71 case REMOVE_MAPPING:
70 entry = PacketHelper.readEntry(input); 72 entry = PacketHelper.readEntry(input);
71 return removeMapping(user, entry); 73 return removeMapping(user, entry);
72 case RENAME: 74 case RENAME:
73 entry = PacketHelper.readEntry(input); 75 entry = PacketHelper.readEntry(input);
74 String newName = input.readUTF(); 76 String newName = input.readUTF();
75 return rename(user, entry, newName); 77 return rename(user, entry, newName);
76 default: 78 default:
77 throw new IllegalStateException("unreachable"); 79 throw new IllegalStateException("unreachable");
78 } 80 }
79 } 81 }
80 82
@@ -89,8 +91,14 @@ public abstract class Message {
89 91
90 @Override 92 @Override
91 public boolean equals(Object o) { 93 public boolean equals(Object o) {
92 if (this == o) return true; 94 if (this == o) {
93 if (o == null || getClass() != o.getClass()) return false; 95 return true;
96 }
97
98 if (o == null || getClass() != o.getClass()) {
99 return false;
100 }
101
94 Message message = (Message) o; 102 Message message = (Message) o;
95 return Objects.equals(user, message.user); 103 return Objects.equals(user, message.user);
96 } 104 }
@@ -111,7 +119,6 @@ public abstract class Message {
111 } 119 }
112 120
113 public static final class Chat extends Message { 121 public static final class Chat extends Message {
114
115 public final String message; 122 public final String message;
116 123
117 private Chat(String user, String message) { 124 private Chat(String user, String message) {
@@ -137,9 +144,18 @@ public abstract class Message {
137 144
138 @Override 145 @Override
139 public boolean equals(Object o) { 146 public boolean equals(Object o) {
140 if (this == o) return true; 147 if (this == o) {
141 if (o == null || getClass() != o.getClass()) return false; 148 return true;
142 if (!super.equals(o)) return false; 149 }
150
151 if (o == null || getClass() != o.getClass()) {
152 return false;
153 }
154
155 if (!super.equals(o)) {
156 return false;
157 }
158
143 Chat chat = (Chat) o; 159 Chat chat = (Chat) o;
144 return Objects.equals(message, chat.message); 160 return Objects.equals(message, chat.message);
145 } 161 }
@@ -153,11 +169,9 @@ public abstract class Message {
153 public String toString() { 169 public String toString() {
154 return String.format("Message.Chat { user: '%s', message: '%s' }", user, message); 170 return String.format("Message.Chat { user: '%s', message: '%s' }", user, message);
155 } 171 }
156
157 } 172 }
158 173
159 public static final class Connect extends Message { 174 public static final class Connect extends Message {
160
161 private Connect(String user) { 175 private Connect(String user) {
162 super(user); 176 super(user);
163 } 177 }
@@ -176,11 +190,9 @@ public abstract class Message {
176 public String toString() { 190 public String toString() {
177 return String.format("Message.Connect { user: '%s' }", user); 191 return String.format("Message.Connect { user: '%s' }", user);
178 } 192 }
179
180 } 193 }
181 194
182 public static final class Disconnect extends Message { 195 public static final class Disconnect extends Message {
183
184 private Disconnect(String user) { 196 private Disconnect(String user) {
185 super(user); 197 super(user);
186 } 198 }
@@ -199,11 +211,9 @@ public abstract class Message {
199 public String toString() { 211 public String toString() {
200 return String.format("Message.Disconnect { user: '%s' }", user); 212 return String.format("Message.Disconnect { user: '%s' }", user);
201 } 213 }
202
203 } 214 }
204 215
205 public static final class EditDocs extends Message { 216 public static final class EditDocs extends Message {
206
207 public final Entry<?> entry; 217 public final Entry<?> entry;
208 218
209 private EditDocs(String user, Entry<?> entry) { 219 private EditDocs(String user, Entry<?> entry) {
@@ -229,9 +239,18 @@ public abstract class Message {
229 239
230 @Override 240 @Override
231 public boolean equals(Object o) { 241 public boolean equals(Object o) {
232 if (this == o) return true; 242 if (this == o) {
233 if (o == null || getClass() != o.getClass()) return false; 243 return true;
234 if (!super.equals(o)) return false; 244 }
245
246 if (o == null || getClass() != o.getClass()) {
247 return false;
248 }
249
250 if (!super.equals(o)) {
251 return false;
252 }
253
235 EditDocs editDocs = (EditDocs) o; 254 EditDocs editDocs = (EditDocs) o;
236 return Objects.equals(entry, editDocs.entry); 255 return Objects.equals(entry, editDocs.entry);
237 } 256 }
@@ -245,11 +264,9 @@ public abstract class Message {
245 public String toString() { 264 public String toString() {
246 return String.format("Message.EditDocs { user: '%s', entry: %s }", user, entry); 265 return String.format("Message.EditDocs { user: '%s', entry: %s }", user, entry);
247 } 266 }
248
249 } 267 }
250 268
251 public static final class MarkDeobf extends Message { 269 public static final class MarkDeobf extends Message {
252
253 public final Entry<?> entry; 270 public final Entry<?> entry;
254 271
255 private MarkDeobf(String user, Entry<?> entry) { 272 private MarkDeobf(String user, Entry<?> entry) {
@@ -275,9 +292,18 @@ public abstract class Message {
275 292
276 @Override 293 @Override
277 public boolean equals(Object o) { 294 public boolean equals(Object o) {
278 if (this == o) return true; 295 if (this == o) {
279 if (o == null || getClass() != o.getClass()) return false; 296 return true;
280 if (!super.equals(o)) return false; 297 }
298
299 if (o == null || getClass() != o.getClass()) {
300 return false;
301 }
302
303 if (!super.equals(o)) {
304 return false;
305 }
306
281 MarkDeobf markDeobf = (MarkDeobf) o; 307 MarkDeobf markDeobf = (MarkDeobf) o;
282 return Objects.equals(entry, markDeobf.entry); 308 return Objects.equals(entry, markDeobf.entry);
283 } 309 }
@@ -291,11 +317,9 @@ public abstract class Message {
291 public String toString() { 317 public String toString() {
292 return String.format("Message.MarkDeobf { user: '%s', entry: %s }", user, entry); 318 return String.format("Message.MarkDeobf { user: '%s', entry: %s }", user, entry);
293 } 319 }
294
295 } 320 }
296 321
297 public static final class RemoveMapping extends Message { 322 public static final class RemoveMapping extends Message {
298
299 public final Entry<?> entry; 323 public final Entry<?> entry;
300 324
301 private RemoveMapping(String user, Entry<?> entry) { 325 private RemoveMapping(String user, Entry<?> entry) {
@@ -321,9 +345,18 @@ public abstract class Message {
321 345
322 @Override 346 @Override
323 public boolean equals(Object o) { 347 public boolean equals(Object o) {
324 if (this == o) return true; 348 if (this == o) {
325 if (o == null || getClass() != o.getClass()) return false; 349 return true;
326 if (!super.equals(o)) return false; 350 }
351
352 if (o == null || getClass() != o.getClass()) {
353 return false;
354 }
355
356 if (!super.equals(o)) {
357 return false;
358 }
359
327 RemoveMapping that = (RemoveMapping) o; 360 RemoveMapping that = (RemoveMapping) o;
328 return Objects.equals(entry, that.entry); 361 return Objects.equals(entry, that.entry);
329 } 362 }
@@ -337,11 +370,9 @@ public abstract class Message {
337 public String toString() { 370 public String toString() {
338 return String.format("Message.RemoveMapping { user: '%s', entry: %s }", user, entry); 371 return String.format("Message.RemoveMapping { user: '%s', entry: %s }", user, entry);
339 } 372 }
340
341 } 373 }
342 374
343 public static final class Rename extends Message { 375 public static final class Rename extends Message {
344
345 public final Entry<?> entry; 376 public final Entry<?> entry;
346 public final String newName; 377 public final String newName;
347 378
@@ -370,12 +401,20 @@ public abstract class Message {
370 401
371 @Override 402 @Override
372 public boolean equals(Object o) { 403 public boolean equals(Object o) {
373 if (this == o) return true; 404 if (this == o) {
374 if (o == null || getClass() != o.getClass()) return false; 405 return true;
375 if (!super.equals(o)) return false; 406 }
407
408 if (o == null || getClass() != o.getClass()) {
409 return false;
410 }
411
412 if (!super.equals(o)) {
413 return false;
414 }
415
376 Rename rename = (Rename) o; 416 Rename rename = (Rename) o;
377 return Objects.equals(entry, rename.entry) && 417 return Objects.equals(entry, rename.entry) && Objects.equals(newName, rename.newName);
378 Objects.equals(newName, rename.newName);
379 } 418 }
380 419
381 @Override 420 @Override
@@ -387,7 +426,5 @@ public abstract class Message {
387 public String toString() { 426 public String toString() {
388 return String.format("Message.Rename { user: '%s', entry: %s, newName: '%s' }", user, entry, newName); 427 return String.format("Message.Rename { user: '%s', entry: %s, newName: '%s' }", user, entry, newName);
389 } 428 }
390
391 } 429 }
392
393} 430}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/ServerAddress.java b/enigma-server/src/main/java/cuchaz/enigma/network/ServerAddress.java
index 45e0750..a8a1029 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/ServerAddress.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/ServerAddress.java
@@ -5,7 +5,6 @@ import java.util.Objects;
5import javax.annotation.Nullable; 5import javax.annotation.Nullable;
6 6
7public class ServerAddress { 7public class ServerAddress {
8
9 public final String address; 8 public final String address;
10 public final int port; 9 public final int port;
11 10
@@ -16,11 +15,26 @@ public class ServerAddress {
16 15
17 @Nullable 16 @Nullable
18 public static ServerAddress of(String address, int port) { 17 public static ServerAddress of(String address, int port) {
19 if (port < 0 || port > 65535) return null; 18 if (port < 0 || port > 65535) {
20 if (address == null) return null; 19 return null;
21 if (address.equals("")) return null; 20 }
22 if (!address.matches("[a-zA-Z0-9.:-]+")) return null; 21
23 if (address.startsWith("-") || address.endsWith("-")) return null; 22 if (address == null) {
23 return null;
24 }
25
26 if (address.equals("")) {
27 return null;
28 }
29
30 if (!address.matches("[a-zA-Z0-9.:-]+")) {
31 return null;
32 }
33
34 if (address.startsWith("-") || address.endsWith("-")) {
35 return null;
36 }
37
24 return new ServerAddress(address, port); 38 return new ServerAddress(address, port);
25 } 39 }
26 40
@@ -28,6 +42,7 @@ public class ServerAddress {
28 public static ServerAddress from(String s, int defaultPort) { 42 public static ServerAddress from(String s, int defaultPort) {
29 String address; 43 String address;
30 int idx = s.indexOf(']'); 44 int idx = s.indexOf(']');
45
31 if (s.startsWith("[") && idx != -1) { 46 if (s.startsWith("[") && idx != -1) {
32 address = s.substring(1, idx); 47 address = s.substring(1, idx);
33 s = s.substring(idx + 1); 48 s = s.substring(idx + 1);
@@ -41,10 +56,12 @@ public class ServerAddress {
41 } 56 }
42 57
43 int port; 58 int port;
59
44 if (s.isEmpty()) { 60 if (s.isEmpty()) {
45 port = defaultPort; 61 port = defaultPort;
46 } else if (s.startsWith(":")) { 62 } else if (s.startsWith(":")) {
47 s = s.substring(1); 63 s = s.substring(1);
64
48 try { 65 try {
49 port = Integer.parseInt(s); 66 port = Integer.parseInt(s);
50 } catch (NumberFormatException e) { 67 } catch (NumberFormatException e) {
@@ -53,16 +70,22 @@ public class ServerAddress {
53 } else { 70 } else {
54 return null; 71 return null;
55 } 72 }
73
56 return ServerAddress.of(address, port); 74 return ServerAddress.of(address, port);
57 } 75 }
58 76
59 @Override 77 @Override
60 public boolean equals(Object o) { 78 public boolean equals(Object o) {
61 if (this == o) return true; 79 if (this == o) {
62 if (o == null || getClass() != o.getClass()) return false; 80 return true;
81 }
82
83 if (o == null || getClass() != o.getClass()) {
84 return false;
85 }
86
63 ServerAddress that = (ServerAddress) o; 87 ServerAddress that = (ServerAddress) o;
64 return port == that.port && 88 return port == that.port && Objects.equals(address, that.address);
65 Objects.equals(address, that.address);
66 } 89 }
67 90
68 @Override 91 @Override
@@ -74,5 +97,4 @@ public class ServerAddress {
74 public String toString() { 97 public String toString() {
75 return String.format("ServerAddress { address: '%s', port: %d }", address, port); 98 return String.format("ServerAddress { address: '%s', port: %d }", address, port);
76 } 99 }
77
78} 100}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/ServerPacketHandler.java b/enigma-server/src/main/java/cuchaz/enigma/network/ServerPacketHandler.java
index 8618553..5b5b0e6 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/ServerPacketHandler.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/ServerPacketHandler.java
@@ -3,7 +3,6 @@ package cuchaz.enigma.network;
3import java.net.Socket; 3import java.net.Socket;
4 4
5public class ServerPacketHandler { 5public class ServerPacketHandler {
6
7 private final Socket client; 6 private final Socket client;
8 private final EnigmaServer server; 7 private final EnigmaServer server;
9 8
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/ConfirmChangeC2SPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/ConfirmChangeC2SPacket.java
index 78ef964..ab4f5a1 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/ConfirmChangeC2SPacket.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/ConfirmChangeC2SPacket.java
@@ -1,11 +1,11 @@
1package cuchaz.enigma.network.packet; 1package cuchaz.enigma.network.packet;
2 2
3import cuchaz.enigma.network.ServerPacketHandler;
4
5import java.io.DataInput; 3import java.io.DataInput;
6import java.io.DataOutput; 4import java.io.DataOutput;
7import java.io.IOException; 5import java.io.IOException;
8 6
7import cuchaz.enigma.network.ServerPacketHandler;
8
9public class ConfirmChangeC2SPacket implements Packet<ServerPacketHandler> { 9public class ConfirmChangeC2SPacket implements Packet<ServerPacketHandler> {
10 private int syncId; 10 private int syncId;
11 11
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeC2SPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeC2SPacket.java
index b97877c..6a7ffe9 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeC2SPacket.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeC2SPacket.java
@@ -12,7 +12,6 @@ import cuchaz.enigma.utils.validation.PrintValidatable;
12import cuchaz.enigma.utils.validation.ValidationContext; 12import cuchaz.enigma.utils.validation.ValidationContext;
13 13
14public class EntryChangeC2SPacket implements Packet<ServerPacketHandler> { 14public class EntryChangeC2SPacket implements Packet<ServerPacketHandler> {
15
16 private EntryChange<?> change; 15 private EntryChange<?> change;
17 16
18 EntryChangeC2SPacket() { 17 EntryChangeC2SPacket() {
@@ -62,5 +61,4 @@ public class EntryChangeC2SPacket implements Packet<ServerPacketHandler> {
62 handler.getServer().sendMessage(Message.editDocs(handler.getServer().getUsername(handler.getClient()), this.change.getTarget())); 61 handler.getServer().sendMessage(Message.editDocs(handler.getServer().getUsername(handler.getClient()), this.change.getTarget()));
63 } 62 }
64 } 63 }
65
66} 64}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeS2CPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeS2CPacket.java
index a237b91..8e4688e 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeS2CPacket.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeS2CPacket.java
@@ -8,7 +8,6 @@ import cuchaz.enigma.network.ClientPacketHandler;
8import cuchaz.enigma.translation.mapping.EntryChange; 8import cuchaz.enigma.translation.mapping.EntryChange;
9 9
10public class EntryChangeS2CPacket implements Packet<ClientPacketHandler> { 10public class EntryChangeS2CPacket implements Packet<ClientPacketHandler> {
11
12 private int syncId; 11 private int syncId;
13 private EntryChange<?> change; 12 private EntryChange<?> change;
14 13
@@ -38,5 +37,4 @@ public class EntryChangeS2CPacket implements Packet<ClientPacketHandler> {
38 handler.sendPacket(new ConfirmChangeC2SPacket(this.syncId)); 37 handler.sendPacket(new ConfirmChangeC2SPacket(this.syncId));
39 } 38 }
40 } 39 }
41
42} 40}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/KickS2CPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/KickS2CPacket.java
index 9a112a8..bd238dc 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/KickS2CPacket.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/KickS2CPacket.java
@@ -1,11 +1,11 @@
1package cuchaz.enigma.network.packet; 1package cuchaz.enigma.network.packet;
2 2
3import cuchaz.enigma.network.ClientPacketHandler;
4
5import java.io.DataInput; 3import java.io.DataInput;
6import java.io.DataOutput; 4import java.io.DataOutput;
7import java.io.IOException; 5import java.io.IOException;
8 6
7import cuchaz.enigma.network.ClientPacketHandler;
8
9public class KickS2CPacket implements Packet<ClientPacketHandler> { 9public class KickS2CPacket implements Packet<ClientPacketHandler> {
10 private String reason; 10 private String reason;
11 11
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/LoginC2SPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/LoginC2SPacket.java
index da0f44a..e93c1d4 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/LoginC2SPacket.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/LoginC2SPacket.java
@@ -1,14 +1,14 @@
1package cuchaz.enigma.network.packet; 1package cuchaz.enigma.network.packet;
2 2
3import cuchaz.enigma.network.EnigmaServer;
4import cuchaz.enigma.network.ServerPacketHandler;
5import cuchaz.enigma.network.Message;
6
7import java.io.DataInput; 3import java.io.DataInput;
8import java.io.DataOutput; 4import java.io.DataOutput;
9import java.io.IOException; 5import java.io.IOException;
10import java.util.Arrays; 6import java.util.Arrays;
11 7
8import cuchaz.enigma.network.EnigmaServer;
9import cuchaz.enigma.network.Message;
10import cuchaz.enigma.network.ServerPacketHandler;
11
12public class LoginC2SPacket implements Packet<ServerPacketHandler> { 12public class LoginC2SPacket implements Packet<ServerPacketHandler> {
13 private byte[] jarChecksum; 13 private byte[] jarChecksum;
14 private char[] password; 14 private char[] password;
@@ -28,12 +28,15 @@ public class LoginC2SPacket implements Packet<ServerPacketHandler> {
28 if (input.readUnsignedShort() != EnigmaServer.PROTOCOL_VERSION) { 28 if (input.readUnsignedShort() != EnigmaServer.PROTOCOL_VERSION) {
29 throw new IOException("Mismatching protocol"); 29 throw new IOException("Mismatching protocol");
30 } 30 }
31
31 this.jarChecksum = new byte[EnigmaServer.CHECKSUM_SIZE]; 32 this.jarChecksum = new byte[EnigmaServer.CHECKSUM_SIZE];
32 input.readFully(jarChecksum); 33 input.readFully(jarChecksum);
33 this.password = new char[input.readUnsignedByte()]; 34 this.password = new char[input.readUnsignedByte()];
35
34 for (int i = 0; i < password.length; i++) { 36 for (int i = 0; i < password.length; i++) {
35 password[i] = input.readChar(); 37 password[i] = input.readChar();
36 } 38 }
39
37 this.username = PacketHelper.readString(input); 40 this.username = PacketHelper.readString(input);
38 } 41 }
39 42
@@ -42,9 +45,11 @@ public class LoginC2SPacket implements Packet<ServerPacketHandler> {
42 output.writeShort(EnigmaServer.PROTOCOL_VERSION); 45 output.writeShort(EnigmaServer.PROTOCOL_VERSION);
43 output.write(jarChecksum); 46 output.write(jarChecksum);
44 output.writeByte(password.length); 47 output.writeByte(password.length);
48
45 for (char c : password) { 49 for (char c : password) {
46 output.writeChar(c); 50 output.writeChar(c);
47 } 51 }
52
48 PacketHelper.writeString(output, username); 53 PacketHelper.writeString(output, username);
49 } 54 }
50 55
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageC2SPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageC2SPacket.java
index 3bc09e7..b0610b0 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageC2SPacket.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageC2SPacket.java
@@ -4,11 +4,10 @@ import java.io.DataInput;
4import java.io.DataOutput; 4import java.io.DataOutput;
5import java.io.IOException; 5import java.io.IOException;
6 6
7import cuchaz.enigma.network.ServerPacketHandler;
8import cuchaz.enigma.network.Message; 7import cuchaz.enigma.network.Message;
8import cuchaz.enigma.network.ServerPacketHandler;
9 9
10public class MessageC2SPacket implements Packet<ServerPacketHandler> { 10public class MessageC2SPacket implements Packet<ServerPacketHandler> {
11
12 private String message; 11 private String message;
13 12
14 MessageC2SPacket() { 13 MessageC2SPacket() {
@@ -31,9 +30,9 @@ public class MessageC2SPacket implements Packet<ServerPacketHandler> {
31 @Override 30 @Override
32 public void handle(ServerPacketHandler handler) { 31 public void handle(ServerPacketHandler handler) {
33 String message = this.message.trim(); 32 String message = this.message.trim();
33
34 if (!message.isEmpty()) { 34 if (!message.isEmpty()) {
35 handler.getServer().sendMessage(Message.chat(handler.getServer().getUsername(handler.getClient()), message)); 35 handler.getServer().sendMessage(Message.chat(handler.getServer().getUsername(handler.getClient()), message));
36 } 36 }
37 } 37 }
38
39} 38}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageS2CPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageS2CPacket.java
index 2b07968..9833eeb 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageS2CPacket.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageS2CPacket.java
@@ -8,7 +8,6 @@ import cuchaz.enigma.network.ClientPacketHandler;
8import cuchaz.enigma.network.Message; 8import cuchaz.enigma.network.Message;
9 9
10public class MessageS2CPacket implements Packet<ClientPacketHandler> { 10public class MessageS2CPacket implements Packet<ClientPacketHandler> {
11
12 private Message message; 11 private Message message;
13 12
14 MessageS2CPacket() { 13 MessageS2CPacket() {
@@ -32,5 +31,4 @@ public class MessageS2CPacket implements Packet<ClientPacketHandler> {
32 public void handle(ClientPacketHandler handler) { 31 public void handle(ClientPacketHandler handler) {
33 handler.addMessage(message); 32 handler.addMessage(message);
34 } 33 }
35
36} 34}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/Packet.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/Packet.java
index 2f16dfb..15054e7 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/Packet.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/Packet.java
@@ -5,11 +5,9 @@ import java.io.DataOutput;
5import java.io.IOException; 5import java.io.IOException;
6 6
7public interface Packet<H> { 7public interface Packet<H> {
8
9 void read(DataInput input) throws IOException; 8 void read(DataInput input) throws IOException;
10 9
11 void write(DataOutput output) throws IOException; 10 void write(DataOutput output) throws IOException;
12 11
13 void handle(H handler); 12 void handle(H handler);
14
15} 13}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketHelper.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketHelper.java
index 2649cdc..ce767c3 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketHelper.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketHelper.java
@@ -9,11 +9,14 @@ import cuchaz.enigma.translation.mapping.AccessModifier;
9import cuchaz.enigma.translation.mapping.EntryChange; 9import cuchaz.enigma.translation.mapping.EntryChange;
10import cuchaz.enigma.translation.representation.MethodDescriptor; 10import cuchaz.enigma.translation.representation.MethodDescriptor;
11import cuchaz.enigma.translation.representation.TypeDescriptor; 11import cuchaz.enigma.translation.representation.TypeDescriptor;
12import cuchaz.enigma.translation.representation.entry.*; 12import cuchaz.enigma.translation.representation.entry.ClassEntry;
13import cuchaz.enigma.translation.representation.entry.Entry;
14import cuchaz.enigma.translation.representation.entry.FieldEntry;
15import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
16import cuchaz.enigma.translation.representation.entry.MethodEntry;
13import cuchaz.enigma.utils.TristateChange; 17import cuchaz.enigma.utils.TristateChange;
14 18
15public class PacketHelper { 19public class PacketHelper {
16
17 private static final int ENTRY_CLASS = 0, ENTRY_FIELD = 1, ENTRY_METHOD = 2, ENTRY_LOCAL_VAR = 3; 20 private static final int ENTRY_CLASS = 0, ENTRY_FIELD = 1, ENTRY_METHOD = 2, ENTRY_LOCAL_VAR = 3;
18 private static final int MAX_STRING_LENGTH = 65535; 21 private static final int MAX_STRING_LENGTH = 65535;
19 22
@@ -31,45 +34,46 @@ public class PacketHelper {
31 String name = readString(input); 34 String name = readString(input);
32 35
33 String javadocs = null; 36 String javadocs = null;
37
34 if (input.readBoolean()) { 38 if (input.readBoolean()) {
35 javadocs = readString(input); 39 javadocs = readString(input);
36 } 40 }
37 41
38 switch (type) { 42 switch (type) {
39 case ENTRY_CLASS: { 43 case ENTRY_CLASS: {
40 if (parent != null && !(parent instanceof ClassEntry)) { 44 if (parent != null && !(parent instanceof ClassEntry)) {
41 throw new IOException("Class requires class parent"); 45 throw new IOException("Class requires class parent");
42 }
43
44 return new ClassEntry((ClassEntry) parent, name, javadocs);
45 } 46 }
46 case ENTRY_FIELD: {
47 if (!(parent instanceof ClassEntry parentClass)) {
48 throw new IOException("Field requires class parent");
49 }
50 47
51 TypeDescriptor desc = new TypeDescriptor(readString(input)); 48 return new ClassEntry((ClassEntry) parent, name, javadocs);
52 return new FieldEntry(parentClass, name, desc, javadocs); 49 }
50 case ENTRY_FIELD: {
51 if (!(parent instanceof ClassEntry parentClass)) {
52 throw new IOException("Field requires class parent");
53 } 53 }
54 case ENTRY_METHOD: {
55 if (!(parent instanceof ClassEntry parentClass)) {
56 throw new IOException("Method requires class parent");
57 }
58 54
59 MethodDescriptor desc = new MethodDescriptor(readString(input)); 55 TypeDescriptor desc = new TypeDescriptor(readString(input));
60 return new MethodEntry(parentClass, name, desc, javadocs); 56 return new FieldEntry(parentClass, name, desc, javadocs);
57 }
58 case ENTRY_METHOD: {
59 if (!(parent instanceof ClassEntry parentClass)) {
60 throw new IOException("Method requires class parent");
61 } 61 }
62 case ENTRY_LOCAL_VAR: { 62
63 if (!(parent instanceof MethodEntry parentMethod)) { 63 MethodDescriptor desc = new MethodDescriptor(readString(input));
64 throw new IOException("Local variable requires method parent"); 64 return new MethodEntry(parentClass, name, desc, javadocs);
65 } 65 }
66 66 case ENTRY_LOCAL_VAR: {
67 int index = input.readUnsignedShort(); 67 if (!(parent instanceof MethodEntry parentMethod)) {
68 boolean parameter = input.readBoolean(); 68 throw new IOException("Local variable requires method parent");
69 return new LocalVariableEntry(parentMethod, index, name, parameter, javadocs);
70 } 69 }
71 default: 70
72 throw new IOException("Received unknown entry type " + type); 71 int index = input.readUnsignedShort();
72 boolean parameter = input.readBoolean();
73 return new LocalVariableEntry(parentMethod, index, name, parameter, javadocs);
74 }
75 default:
76 throw new IOException("Received unknown entry type " + type);
73 } 77 }
74 } 78 }
75 79
@@ -94,6 +98,7 @@ public class PacketHelper {
94 // parent 98 // parent
95 if (includeParent) { 99 if (includeParent) {
96 output.writeBoolean(entry.getParent() != null); 100 output.writeBoolean(entry.getParent() != null);
101
97 if (entry.getParent() != null) { 102 if (entry.getParent() != null) {
98 writeEntry(output, entry.getParent(), true); 103 writeEntry(output, entry.getParent(), true);
99 } 104 }
@@ -104,6 +109,7 @@ public class PacketHelper {
104 109
105 // javadocs 110 // javadocs
106 output.writeBoolean(entry.getJavadocs() != null); 111 output.writeBoolean(entry.getJavadocs() != null);
112
107 if (entry.getJavadocs() != null) { 113 if (entry.getJavadocs() != null) {
108 writeString(output, entry.getJavadocs()); 114 writeString(output, entry.getJavadocs());
109 } 115 }
@@ -129,9 +135,11 @@ public class PacketHelper {
129 135
130 public static void writeString(DataOutput output, String str) throws IOException { 136 public static void writeString(DataOutput output, String str) throws IOException {
131 byte[] bytes = str.getBytes(StandardCharsets.UTF_8); 137 byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
138
132 if (bytes.length > MAX_STRING_LENGTH) { 139 if (bytes.length > MAX_STRING_LENGTH) {
133 throw new IOException("String too long, was " + bytes.length + " bytes, max " + MAX_STRING_LENGTH + " allowed"); 140 throw new IOException("String too long, was " + bytes.length + " bytes, max " + MAX_STRING_LENGTH + " allowed");
134 } 141 }
142
135 output.writeShort(bytes.length); 143 output.writeShort(bytes.length);
136 output.write(bytes); 144 output.write(bytes);
137 } 145 }
@@ -146,30 +154,30 @@ public class PacketHelper {
146 TristateChange.Type javadocT = TristateChange.Type.values()[flags >> 4 & 0x3]; 154 TristateChange.Type javadocT = TristateChange.Type.values()[flags >> 4 & 0x3];
147 155
148 switch (deobfNameT) { 156 switch (deobfNameT) {
149 case RESET: 157 case RESET:
150 change = change.clearDeobfName(); 158 change = change.clearDeobfName();
151 break; 159 break;
152 case SET: 160 case SET:
153 change = change.withDeobfName(readString(input)); 161 change = change.withDeobfName(readString(input));
154 break; 162 break;
155 } 163 }
156 164
157 switch (accessT) { 165 switch (accessT) {
158 case RESET: 166 case RESET:
159 change = change.clearAccess(); 167 change = change.clearAccess();
160 break; 168 break;
161 case SET: 169 case SET:
162 change = change.withAccess(AccessModifier.values()[flags >> 6 & 0x3]); 170 change = change.withAccess(AccessModifier.values()[flags >> 6 & 0x3]);
163 break; 171 break;
164 } 172 }
165 173
166 switch (javadocT) { 174 switch (javadocT) {
167 case RESET: 175 case RESET:
168 change = change.clearJavadoc(); 176 change = change.clearJavadoc();
169 break; 177 break;
170 case SET: 178 case SET:
171 change = change.withJavadoc(readString(input)); 179 change = change.withJavadoc(readString(input));
172 break; 180 break;
173 } 181 }
174 182
175 return change; 183 return change;
@@ -177,9 +185,7 @@ public class PacketHelper {
177 185
178 public static void writeEntryChange(DataOutput output, EntryChange<?> change) throws IOException { 186 public static void writeEntryChange(DataOutput output, EntryChange<?> change) throws IOException {
179 writeEntry(output, change.getTarget()); 187 writeEntry(output, change.getTarget());
180 int flags = change.getDeobfName().getType().ordinal() | 188 int flags = change.getDeobfName().getType().ordinal() | change.getAccess().getType().ordinal() << 2 | change.getJavadoc().getType().ordinal() << 4;
181 change.getAccess().getType().ordinal() << 2 |
182 change.getJavadoc().getType().ordinal() << 4;
183 189
184 if (change.getAccess().isSet()) { 190 if (change.getAccess().isSet()) {
185 flags |= change.getAccess().getNewValue().ordinal() << 6; 191 flags |= change.getAccess().getNewValue().ordinal() << 6;
@@ -195,5 +201,4 @@ public class PacketHelper {
195 writeString(output, change.getJavadoc().getNewValue()); 201 writeString(output, change.getJavadoc().getNewValue());
196 } 202 }
197 } 203 }
198
199} 204}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketRegistry.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketRegistry.java
index 59999cc..49d56fd 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketRegistry.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketRegistry.java
@@ -8,7 +8,6 @@ import cuchaz.enigma.network.ClientPacketHandler;
8import cuchaz.enigma.network.ServerPacketHandler; 8import cuchaz.enigma.network.ServerPacketHandler;
9 9
10public class PacketRegistry { 10public class PacketRegistry {
11
12 private static final Map<Class<? extends Packet<ServerPacketHandler>>, Integer> c2sPacketIds = new HashMap<>(); 11 private static final Map<Class<? extends Packet<ServerPacketHandler>>, Integer> c2sPacketIds = new HashMap<>();
13 private static final Map<Integer, Supplier<? extends Packet<ServerPacketHandler>>> c2sPacketCreators = new HashMap<>(); 12 private static final Map<Integer, Supplier<? extends Packet<ServerPacketHandler>>> c2sPacketCreators = new HashMap<>();
14 private static final Map<Class<? extends Packet<ClientPacketHandler>>, Integer> s2cPacketIds = new HashMap<>(); 13 private static final Map<Class<? extends Packet<ClientPacketHandler>>, Integer> s2cPacketIds = new HashMap<>();
@@ -54,5 +53,4 @@ public class PacketRegistry {
54 Supplier<? extends Packet<ClientPacketHandler>> creator = s2cPacketCreators.get(id); 53 Supplier<? extends Packet<ClientPacketHandler>> creator = s2cPacketCreators.get(id);
55 return creator == null ? null : creator.get(); 54 return creator == null ? null : creator.get();
56 } 55 }
57
58} 56}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/SyncMappingsS2CPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/SyncMappingsS2CPacket.java
index 6d9c0bc..7e50938 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/SyncMappingsS2CPacket.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/SyncMappingsS2CPacket.java
@@ -28,6 +28,7 @@ public class SyncMappingsS2CPacket implements Packet<ClientPacketHandler> {
28 public void read(DataInput input) throws IOException { 28 public void read(DataInput input) throws IOException {
29 mappings = new HashEntryTree<>(); 29 mappings = new HashEntryTree<>();
30 int size = input.readInt(); 30 int size = input.readInt();
31
31 for (int i = 0; i < size; i++) { 32 for (int i = 0; i < size; i++) {
32 readEntryTreeNode(input, null); 33 readEntryTreeNode(input, null);
33 } 34 }
@@ -40,6 +41,7 @@ public class SyncMappingsS2CPacket implements Packet<ClientPacketHandler> {
40 EntryMapping mapping = new EntryMapping(!name.isEmpty() ? name : null, !javadoc.isEmpty() ? javadoc : null); 41 EntryMapping mapping = new EntryMapping(!name.isEmpty() ? name : null, !javadoc.isEmpty() ? javadoc : null);
41 mappings.insert(entry, mapping); 42 mappings.insert(entry, mapping);
42 int size = input.readUnsignedShort(); 43 int size = input.readUnsignedShort();
44
43 for (int i = 0; i < size; i++) { 45 for (int i = 0; i < size; i++) {
44 readEntryTreeNode(input, entry); 46 readEntryTreeNode(input, entry);
45 } 47 }
@@ -49,6 +51,7 @@ public class SyncMappingsS2CPacket implements Packet<ClientPacketHandler> {
49 public void write(DataOutput output) throws IOException { 51 public void write(DataOutput output) throws IOException {
50 List<EntryTreeNode<EntryMapping>> roots = mappings.getRootNodes().toList(); 52 List<EntryTreeNode<EntryMapping>> roots = mappings.getRootNodes().toList();
51 output.writeInt(roots.size()); 53 output.writeInt(roots.size());
54
52 for (EntryTreeNode<EntryMapping> node : roots) { 55 for (EntryTreeNode<EntryMapping> node : roots) {
53 writeEntryTreeNode(output, node); 56 writeEntryTreeNode(output, node);
54 } 57 }
@@ -57,12 +60,16 @@ public class SyncMappingsS2CPacket implements Packet<ClientPacketHandler> {
57 private static void writeEntryTreeNode(DataOutput output, EntryTreeNode<EntryMapping> node) throws IOException { 60 private static void writeEntryTreeNode(DataOutput output, EntryTreeNode<EntryMapping> node) throws IOException {
58 PacketHelper.writeEntry(output, node.getEntry(), false); 61 PacketHelper.writeEntry(output, node.getEntry(), false);
59 EntryMapping value = node.getValue(); 62 EntryMapping value = node.getValue();
60 if (value == null) value = EntryMapping.DEFAULT; 63
64 if (value == null) {
65 value = EntryMapping.DEFAULT;
66 }
61 67
62 PacketHelper.writeString(output, value.targetName() != null ? value.targetName() : ""); 68 PacketHelper.writeString(output, value.targetName() != null ? value.targetName() : "");
63 PacketHelper.writeString(output, value.javadoc() != null ? value.javadoc() : ""); 69 PacketHelper.writeString(output, value.javadoc() != null ? value.javadoc() : "");
64 Collection<? extends EntryTreeNode<EntryMapping>> children = node.getChildNodes(); 70 Collection<? extends EntryTreeNode<EntryMapping>> children = node.getChildNodes();
65 output.writeShort(children.size()); 71 output.writeShort(children.size());
72
66 for (EntryTreeNode<EntryMapping> child : children) { 73 for (EntryTreeNode<EntryMapping> child : children) {
67 writeEntryTreeNode(output, child); 74 writeEntryTreeNode(output, child);
68 } 75 }
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/UserListS2CPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/UserListS2CPacket.java
index b4a277a..baac2b3 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/UserListS2CPacket.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/UserListS2CPacket.java
@@ -1,15 +1,14 @@
1package cuchaz.enigma.network.packet; 1package cuchaz.enigma.network.packet;
2 2
3import cuchaz.enigma.network.ClientPacketHandler;
4
5import java.io.DataInput; 3import java.io.DataInput;
6import java.io.DataOutput; 4import java.io.DataOutput;
7import java.io.IOException; 5import java.io.IOException;
8import java.util.ArrayList; 6import java.util.ArrayList;
9import java.util.List; 7import java.util.List;
10 8
11public class UserListS2CPacket implements Packet<ClientPacketHandler> { 9import cuchaz.enigma.network.ClientPacketHandler;
12 10
11public class UserListS2CPacket implements Packet<ClientPacketHandler> {
13 private List<String> users; 12 private List<String> users;
14 13
15 UserListS2CPacket() { 14 UserListS2CPacket() {
@@ -23,6 +22,7 @@ public class UserListS2CPacket implements Packet<ClientPacketHandler> {
23 public void read(DataInput input) throws IOException { 22 public void read(DataInput input) throws IOException {
24 int len = input.readUnsignedShort(); 23 int len = input.readUnsignedShort();
25 users = new ArrayList<>(len); 24 users = new ArrayList<>(len);
25
26 for (int i = 0; i < len; i++) { 26 for (int i = 0; i < len; i++) {
27 users.add(input.readUTF()); 27 users.add(input.readUTF());
28 } 28 }
@@ -31,6 +31,7 @@ public class UserListS2CPacket implements Packet<ClientPacketHandler> {
31 @Override 31 @Override
32 public void write(DataOutput output) throws IOException { 32 public void write(DataOutput output) throws IOException {
33 output.writeShort(users.size()); 33 output.writeShort(users.size());
34
34 for (String user : users) { 35 for (String user : users) {
35 PacketHelper.writeString(output, user); 36 PacketHelper.writeString(output, user);
36 } 37 }
@@ -40,5 +41,4 @@ public class UserListS2CPacket implements Packet<ClientPacketHandler> {
40 public void handle(ClientPacketHandler handler) { 41 public void handle(ClientPacketHandler handler) {
41 handler.updateUserList(users); 42 handler.updateUserList(users);
42 } 43 }
43
44} 44}
diff --git a/enigma-server/src/test/java/cuchaz/enigma/network/ServerAddressTest.java b/enigma-server/src/test/java/cuchaz/enigma/network/ServerAddressTest.java
index 3765f7a..c2920e0 100644
--- a/enigma-server/src/test/java/cuchaz/enigma/network/ServerAddressTest.java
+++ b/enigma-server/src/test/java/cuchaz/enigma/network/ServerAddressTest.java
@@ -1,12 +1,11 @@
1package cuchaz.enigma.network; 1package cuchaz.enigma.network;
2 2
3import org.junit.Test;
4
5import static org.junit.Assert.assertEquals; 3import static org.junit.Assert.assertEquals;
6import static org.junit.Assert.assertNull; 4import static org.junit.Assert.assertNull;
7 5
8public class ServerAddressTest { 6import org.junit.Test;
9 7
8public class ServerAddressTest {
10 @Test 9 @Test
11 public void validAddresses() { 10 public void validAddresses() {
12 assertEquals(ServerAddress.of("127.0.0.1", 22), ServerAddress.from("127.0.0.1", 22)); 11 assertEquals(ServerAddress.of("127.0.0.1", 22), ServerAddress.from("127.0.0.1", 22));
@@ -24,5 +23,4 @@ public class ServerAddressTest {
24 assertNull(ServerAddress.from("127.0.0.1:100000000", 22)); 23 assertNull(ServerAddress.from("127.0.0.1:100000000", 22));
25 assertNull(ServerAddress.from("127.0.0.1:lmao", 22)); 24 assertNull(ServerAddress.from("127.0.0.1:lmao", 22));
26 } 25 }
27
28} 26}
diff --git a/enigma-swing/build.gradle b/enigma-swing/build.gradle
index d783e95..a4a13d8 100644
--- a/enigma-swing/build.gradle
+++ b/enigma-swing/build.gradle
@@ -1,18 +1,18 @@
1plugins { 1plugins {
2 id 'application' 2 id 'application'
3 id 'com.github.johnrengelman.shadow' version '7.0.0' 3 id 'com.github.johnrengelman.shadow' version '7.0.0'
4} 4}
5 5
6dependencies { 6dependencies {
7 implementation project(':enigma') 7 implementation project(':enigma')
8 implementation project(':enigma-server') 8 implementation project(':enigma-server')
9 9
10 implementation 'net.sf.jopt-simple:jopt-simple:6.0-alpha-3' 10 implementation 'net.sf.jopt-simple:jopt-simple:6.0-alpha-3'
11 implementation 'com.formdev:flatlaf:1.6.1' 11 implementation 'com.formdev:flatlaf:1.6.1'
12 implementation 'com.formdev:flatlaf-extras:1.6.1' // for SVG icons 12 implementation 'com.formdev:flatlaf-extras:1.6.1' // for SVG icons
13 implementation 'de.sciss:syntaxpane:1.2.0' 13 implementation 'de.sciss:syntaxpane:1.2.0'
14 implementation 'com.github.lukeu:swing-dpi:0.9' 14 implementation 'com.github.lukeu:swing-dpi:0.9'
15 implementation 'org.drjekyll:fontchooser:2.5.2' 15 implementation 'org.drjekyll:fontchooser:2.5.2'
16} 16}
17 17
18mainClassName = 'cuchaz.enigma.gui.Main' 18mainClassName = 'cuchaz.enigma.gui.Main'
@@ -20,9 +20,9 @@ mainClassName = 'cuchaz.enigma.gui.Main'
20jar.manifest.attributes 'Main-Class': mainClassName 20jar.manifest.attributes 'Main-Class': mainClassName
21 21
22publishing { 22publishing {
23 publications { 23 publications {
24 shadow(MavenPublication) { publication -> 24 shadow(MavenPublication) { publication ->
25 publication.from components.java 25 publication.from components.java
26 } 26 }
27 } 27 }
28} 28}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/BrowserCaret.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/BrowserCaret.java
index af105db..a213a59 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/BrowserCaret.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/BrowserCaret.java
@@ -1,20 +1,19 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.gui; 12package cuchaz.enigma.gui;
13 13
14import javax.swing.text.DefaultCaret; 14import javax.swing.text.DefaultCaret;
15 15
16public class BrowserCaret extends DefaultCaret { 16public class BrowserCaret extends DefaultCaret {
17
18 @Override 17 @Override
19 public boolean isSelectionVisible() { 18 public boolean isSelectionVisible() {
20 return true; 19 return true;
@@ -24,5 +23,4 @@ public class BrowserCaret extends DefaultCaret {
24 public boolean isVisible() { 23 public boolean isVisible() {
25 return true; 24 return true;
26 } 25 }
27
28} 26}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java
index b7d3c4e..4b9fa59 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java
@@ -1,35 +1,43 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.gui; 12package cuchaz.enigma.gui;
13 13
14import cuchaz.enigma.gui.node.ClassSelectorClassNode; 14import java.awt.Component;
15import cuchaz.enigma.gui.util.GuiUtil;
16import cuchaz.enigma.translation.representation.entry.ClassEntry;
17import cuchaz.enigma.utils.validation.ValidationContext;
18
19import javax.swing.*;
20import javax.swing.event.CellEditorListener;
21import javax.swing.event.ChangeEvent;
22import javax.swing.tree.*;
23import java.awt.*;
24import java.awt.event.KeyAdapter; 15import java.awt.event.KeyAdapter;
25import java.awt.event.KeyEvent; 16import java.awt.event.KeyEvent;
26import java.awt.event.MouseAdapter; 17import java.awt.event.MouseAdapter;
27import java.awt.event.MouseEvent; 18import java.awt.event.MouseEvent;
19import java.util.ArrayList;
20import java.util.Collection;
21import java.util.Comparator;
22import java.util.EventObject;
28import java.util.List; 23import java.util.List;
29import java.util.*;
30 24
31public class ClassSelector extends JTree { 25import javax.swing.JTree;
26import javax.swing.event.CellEditorListener;
27import javax.swing.event.ChangeEvent;
28import javax.swing.tree.DefaultMutableTreeNode;
29import javax.swing.tree.DefaultTreeCellEditor;
30import javax.swing.tree.DefaultTreeCellRenderer;
31import javax.swing.tree.DefaultTreeModel;
32import javax.swing.tree.TreeNode;
33import javax.swing.tree.TreePath;
34
35import cuchaz.enigma.gui.node.ClassSelectorClassNode;
36import cuchaz.enigma.gui.util.GuiUtil;
37import cuchaz.enigma.translation.representation.entry.ClassEntry;
38import cuchaz.enigma.utils.validation.ValidationContext;
32 39
40public class ClassSelector extends JTree {
33 public static final Comparator<ClassEntry> DEOBF_CLASS_COMPARATOR = Comparator.comparing(ClassEntry::getFullName); 41 public static final Comparator<ClassEntry> DEOBF_CLASS_COMPARATOR = Comparator.comparing(ClassEntry::getFullName);
34 42
35 private final Comparator<ClassEntry> comparator; 43 private final Comparator<ClassEntry> comparator;
@@ -56,6 +64,7 @@ public class ClassSelector extends JTree {
56 if (selectionListener != null && event.getClickCount() == 2) { 64 if (selectionListener != null && event.getClickCount() == 2) {
57 // get the selected node 65 // get the selected node
58 TreePath path = getSelectionPath(); 66 TreePath path = getSelectionPath();
67
59 if (path != null && path.getLastPathComponent() instanceof ClassSelectorClassNode node) { 68 if (path != null && path.getLastPathComponent() instanceof ClassSelectorClassNode node) {
60 selectionListener.onSelectClass(node.getObfEntry()); 69 selectionListener.onSelectClass(node.getObfEntry());
61 } 70 }
@@ -121,23 +130,31 @@ public class ClassSelector extends JTree {
121 TreePath path = getSelectionPath(); 130 TreePath path = getSelectionPath();
122 131
123 Object realPath = path.getLastPathComponent(); 132 Object realPath = path.getLastPathComponent();
133
124 if (realPath instanceof DefaultMutableTreeNode node && data != null) { 134 if (realPath instanceof DefaultMutableTreeNode node && data != null) {
125 TreeNode parentNode = node.getParent(); 135 TreeNode parentNode = node.getParent();
126 if (parentNode == null) 136
137 if (parentNode == null) {
127 return; 138 return;
139 }
140
128 boolean allowEdit = true; 141 boolean allowEdit = true;
142
129 for (int i = 0; i < parentNode.getChildCount(); i++) { 143 for (int i = 0; i < parentNode.getChildCount(); i++) {
130 TreeNode childNode = parentNode.getChildAt(i); 144 TreeNode childNode = parentNode.getChildAt(i);
145
131 if (childNode != null && childNode.toString().equals(data) && childNode != node) { 146 if (childNode != null && childNode.toString().equals(data) && childNode != node) {
132 allowEdit = false; 147 allowEdit = false;
133 break; 148 break;
134 } 149 }
135 } 150 }
151
136 if (allowEdit && renameSelectionListener != null) { 152 if (allowEdit && renameSelectionListener != null) {
137 Object prevData = node.getUserObject(); 153 Object prevData = node.getUserObject();
138 Object objectData = node.getUserObject() instanceof ClassEntry ? new ClassEntry(((ClassEntry) prevData).getPackageName() + "/" + data) : data; 154 Object objectData = node.getUserObject() instanceof ClassEntry ? new ClassEntry(((ClassEntry) prevData).getPackageName() + "/" + data) : data;
139 gui.validateImmediateAction(vc -> { 155 gui.validateImmediateAction(vc -> {
140 renameSelectionListener.onSelectionRename(vc, node.getUserObject(), objectData, node); 156 renameSelectionListener.onSelectionRename(vc, node.getUserObject(), objectData, node);
157
141 if (vc.canProceed()) { 158 if (vc.canProceed()) {
142 node.setUserObject(objectData); // Make sure that it's modified 159 node.setUserObject(objectData); // Make sure that it's modified
143 } else { 160 } else {
@@ -206,15 +223,19 @@ public class ClassSelector extends JTree {
206 public List<StateEntry> getExpansionState() { 223 public List<StateEntry> getExpansionState() {
207 List<StateEntry> state = new ArrayList<>(); 224 List<StateEntry> state = new ArrayList<>();
208 int rowCount = getRowCount(); 225 int rowCount = getRowCount();
226
209 for (int i = 0; i < rowCount; i++) { 227 for (int i = 0; i < rowCount; i++) {
210 TreePath path = getPathForRow(i); 228 TreePath path = getPathForRow(i);
229
211 if (isPathSelected(path)) { 230 if (isPathSelected(path)) {
212 state.add(new StateEntry(State.SELECTED, path)); 231 state.add(new StateEntry(State.SELECTED, path));
213 } 232 }
233
214 if (isExpanded(path)) { 234 if (isExpanded(path)) {
215 state.add(new StateEntry(State.EXPANDED, path)); 235 state.add(new StateEntry(State.EXPANDED, path));
216 } 236 }
217 } 237 }
238
218 return state; 239 return state;
219 } 240 }
220 241
@@ -223,8 +244,8 @@ public class ClassSelector extends JTree {
223 244
224 for (StateEntry entry : expansionState) { 245 for (StateEntry entry : expansionState) {
225 switch (entry.state) { 246 switch (entry.state) {
226 case SELECTED -> addSelectionPath(entry.path); 247 case SELECTED -> addSelectionPath(entry.path);
227 case EXPANDED -> expandPath(entry.path); 248 case EXPANDED -> expandPath(entry.path);
228 } 249 }
229 } 250 }
230 } 251 }
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/EditableType.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/EditableType.java
index 1e8d6cf..6028609 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/EditableType.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/EditableType.java
@@ -2,7 +2,11 @@ package cuchaz.enigma.gui;
2 2
3import javax.annotation.Nullable; 3import javax.annotation.Nullable;
4 4
5import cuchaz.enigma.translation.representation.entry.*; 5import cuchaz.enigma.translation.representation.entry.ClassEntry;
6import cuchaz.enigma.translation.representation.entry.Entry;
7import cuchaz.enigma.translation.representation.entry.FieldEntry;
8import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
9import cuchaz.enigma.translation.representation.entry.MethodEntry;
6 10
7public enum EditableType { 11public enum EditableType {
8 CLASS, 12 CLASS,
@@ -10,8 +14,7 @@ public enum EditableType {
10 FIELD, 14 FIELD,
11 PARAMETER, 15 PARAMETER,
12 LOCAL_VARIABLE, 16 LOCAL_VARIABLE,
13 JAVADOC, 17 JAVADOC;
14 ;
15 18
16 @Nullable 19 @Nullable
17 public static EditableType fromEntry(Entry<?> entry) { 20 public static EditableType fromEntry(Entry<?> entry) {
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaQuickFindDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaQuickFindDialog.java
index c912be3..0af8f9c 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaQuickFindDialog.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaQuickFindDialog.java
@@ -1,16 +1,22 @@
1package cuchaz.enigma.gui; 1package cuchaz.enigma.gui;
2 2
3import de.sciss.syntaxpane.actions.DocumentSearchData; 3import java.awt.Component;
4import de.sciss.syntaxpane.actions.gui.QuickFindDialog; 4import java.awt.Container;
5 5import java.awt.Point;
6import javax.swing.*;
7import javax.swing.text.JTextComponent;
8import java.awt.*;
9import java.awt.event.KeyAdapter; 6import java.awt.event.KeyAdapter;
10import java.awt.event.KeyEvent; 7import java.awt.event.KeyEvent;
11import java.util.stream.IntStream; 8import java.util.stream.IntStream;
12import java.util.stream.Stream; 9import java.util.stream.Stream;
13 10
11import javax.swing.JButton;
12import javax.swing.JTextField;
13import javax.swing.JToolBar;
14import javax.swing.SwingUtilities;
15import javax.swing.text.JTextComponent;
16
17import de.sciss.syntaxpane.actions.DocumentSearchData;
18import de.sciss.syntaxpane.actions.gui.QuickFindDialog;
19
14public class EnigmaQuickFindDialog extends QuickFindDialog { 20public class EnigmaQuickFindDialog extends QuickFindDialog {
15 public EnigmaQuickFindDialog(JTextComponent target) { 21 public EnigmaQuickFindDialog(JTextComponent target) {
16 super(target, DocumentSearchData.getFromEditor(target)); 22 super(target, DocumentSearchData.getFromEditor(target));
@@ -22,6 +28,7 @@ public class EnigmaQuickFindDialog extends QuickFindDialog {
22 @Override 28 @Override
23 public void keyPressed(KeyEvent e) { 29 public void keyPressed(KeyEvent e) {
24 super.keyPressed(e); 30 super.keyPressed(e);
31
25 if (e.getKeyCode() == KeyEvent.VK_ENTER) { 32 if (e.getKeyCode() == KeyEvent.VK_ENTER) {
26 JToolBar toolBar = getToolBar(); 33 JToolBar toolBar = getToolBar();
27 boolean next = !e.isShiftDown(); 34 boolean next = !e.isShiftDown();
@@ -78,13 +85,10 @@ public class EnigmaQuickFindDialog extends QuickFindDialog {
78 } 85 }
79 86
80 private static Stream<Component> components(Container container) { 87 private static Stream<Component> components(Container container) {
81 return IntStream.range(0, container.getComponentCount()) 88 return IntStream.range(0, container.getComponentCount()).mapToObj(container::getComponent);
82 .mapToObj(container::getComponent);
83 } 89 }
84 90
85 private static <T extends Component> Stream<T> components(Container container, Class<T> type) { 91 private static <T extends Component> Stream<T> components(Container container, Class<T> type) {
86 return components(container) 92 return components(container).filter(type::isInstance).map(type::cast);
87 .filter(type::isInstance)
88 .map(type::cast);
89 } 93 }
90} 94}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaSyntaxKit.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaSyntaxKit.java
index 43745dd..b81782e 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaSyntaxKit.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaSyntaxKit.java
@@ -10,7 +10,6 @@ import de.sciss.syntaxpane.util.Configuration;
10import cuchaz.enigma.gui.config.UiConfig; 10import cuchaz.enigma.gui.config.UiConfig;
11 11
12public class EnigmaSyntaxKit extends JavaSyntaxKit { 12public class EnigmaSyntaxKit extends JavaSyntaxKit {
13
14 private static Configuration configuration = null; 13 private static Configuration configuration = null;
15 14
16 @Override 15 @Override
@@ -18,23 +17,20 @@ public class EnigmaSyntaxKit extends JavaSyntaxKit {
18 if (configuration == null) { 17 if (configuration == null) {
19 initConfig(DefaultSyntaxKit.getConfig(JavaSyntaxKit.class)); 18 initConfig(DefaultSyntaxKit.getConfig(JavaSyntaxKit.class));
20 } 19 }
20
21 return configuration; 21 return configuration;
22 } 22 }
23 23
24 public void initConfig(Configuration baseConfig) { 24 public void initConfig(Configuration baseConfig) {
25 configuration = flattenConfiguration(baseConfig, EnigmaSyntaxKit.class); 25 configuration = flattenConfiguration(baseConfig, EnigmaSyntaxKit.class);
26 26
27 // Remove all actions except a select few because they disregard the 27 // Remove all actions except a select few because they disregard the
28 // editable state of the editor, or at least are useless anyway because 28 // editable state of the editor, or at least are useless anyway because
29 // they would try editing the file. 29 // they would try editing the file.
30 // Also includes the Action.insert-date action which is written in 30 // Also includes the Action.insert-date action which is written in
31 // Javascript and causes the editor to freeze on first load for a short 31 // Javascript and causes the editor to freeze on first load for a short
32 // time. 32 // time.
33 configuration.keySet().removeIf(s -> s.startsWith("Action.") && 33 configuration.keySet().removeIf(s -> s.startsWith("Action.") && !(s.startsWith("Action.find") || s.startsWith("Action.goto-line") || s.startsWith("Action.jump-to-pair") || s.startsWith("Action.quick-find")));
34 !(s.startsWith("Action.find") ||
35 s.startsWith("Action.goto-line") ||
36 s.startsWith("Action.jump-to-pair") ||
37 s.startsWith("Action.quick-find")));
38 34
39 // See de.sciss.syntaxpane.TokenType 35 // See de.sciss.syntaxpane.TokenType
40 configuration.put("Style.KEYWORD", String.format("%d, 0", UiConfig.getHighlightColor().getRGB())); 36 configuration.put("Style.KEYWORD", String.format("%d, 0", UiConfig.getHighlightColor().getRGB()));
@@ -59,27 +55,28 @@ public class EnigmaSyntaxKit extends JavaSyntaxKit {
59 55
60 Font editorFont = UiConfig.activeUseCustomFonts() ? UiConfig.getEditorFont() : UiConfig.getFallbackEditorFont(); 56 Font editorFont = UiConfig.activeUseCustomFonts() ? UiConfig.getEditorFont() : UiConfig.getFallbackEditorFont();
61 configuration.put("DefaultFont", UiConfig.encodeFont(editorFont)); 57 configuration.put("DefaultFont", UiConfig.encodeFont(editorFont));
62 } 58 }
59
60 /**
61 * Creates a new configuration from the passed configuration so that it has
62 * no parents and all its values are on the same level. This is needed since
63 * there is no way to remove map entries from parent configurations.
64 *
65 * @param source the configuration to flatten
66 * @param configClass the class for the new configuration
67 * @return a new configuration
68 */
69 private static Configuration flattenConfiguration(Configuration source, Class<?> configClass) {
70 Configuration config = new Configuration(configClass, null);
63 71
64 /** 72 for (String p : source.stringPropertyNames()) {
65 * Creates a new configuration from the passed configuration so that it has 73 config.put(p, source.getString(p));
66 * no parents and all its values are on the same level. This is needed since 74 }
67 * there is no way to remove map entries from parent configurations. 75
68 * 76 return config;
69 * @param source the configuration to flatten
70 * @param configClass the class for the new configuration
71 * @return a new configuration
72 */
73 private static Configuration flattenConfiguration(Configuration source, Class<?> configClass) {
74 Configuration config = new Configuration(configClass, null);
75 for (String p : source.stringPropertyNames()) {
76 config.put(p, source.getString(p));
77 }
78 return config;
79 } 77 }
80 78
81 public static void invalidate() { 79 public static void invalidate() {
82 configuration = null; 80 configuration = null;
83 } 81 }
84
85} 82}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/ExceptionIgnorer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/ExceptionIgnorer.java
index 6246192..76d1859 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/ExceptionIgnorer.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/ExceptionIgnorer.java
@@ -1,28 +1,27 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.gui; 12package cuchaz.enigma.gui;
13 13
14public class ExceptionIgnorer { 14public class ExceptionIgnorer {
15
16 public static boolean shouldIgnore(Throwable t) { 15 public static boolean shouldIgnore(Throwable t) {
17
18 // is this that pesky concurrent access bug in the highlight painter system? 16 // is this that pesky concurrent access bug in the highlight painter system?
19 // (ancient ui code is ancient) 17 // (ancient ui code is ancient)
20 if (t instanceof ArrayIndexOutOfBoundsException) { 18 if (t instanceof ArrayIndexOutOfBoundsException) {
21 StackTraceElement[] stackTrace = t.getStackTrace(); 19 StackTraceElement[] stackTrace = t.getStackTrace();
22 if (stackTrace.length > 1) {
23 20
21 if (stackTrace.length > 1) {
24 // does this stack frame match javax.swing.text.DefaultHighlighter.paint*() ? 22 // does this stack frame match javax.swing.text.DefaultHighlighter.paint*() ?
25 StackTraceElement frame = stackTrace[1]; 23 StackTraceElement frame = stackTrace[1];
24
26 if (frame.getClassName().equals("javax.swing.text.DefaultHighlighter") && frame.getMethodName().startsWith("paint")) { 25 if (frame.getClassName().equals("javax.swing.text.DefaultHighlighter") && frame.getMethodName().startsWith("paint")) {
27 return true; 26 return true;
28 } 27 }
@@ -31,5 +30,4 @@ public class ExceptionIgnorer {
31 30
32 return false; 31 return false;
33 } 32 }
34
35} 33}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java
index 5a1e3d8..b3117ce 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.gui; 12package cuchaz.enigma.gui;
13 13
@@ -17,7 +17,6 @@ import java.awt.Point;
17import java.awt.event.ActionEvent; 17import java.awt.event.ActionEvent;
18import java.nio.file.Path; 18import java.nio.file.Path;
19import java.util.Collection; 19import java.util.Collection;
20import java.util.Collections;
21import java.util.List; 20import java.util.List;
22import java.util.Set; 21import java.util.Set;
23import java.util.concurrent.CompletableFuture; 22import java.util.concurrent.CompletableFuture;
@@ -25,12 +24,23 @@ import java.util.function.Consumer;
25import java.util.function.Function; 24import java.util.function.Function;
26 25
27import javax.annotation.Nullable; 26import javax.annotation.Nullable;
28import javax.swing.*; 27import javax.swing.AbstractAction;
28import javax.swing.DefaultListModel;
29import javax.swing.JButton;
30import javax.swing.JFileChooser;
31import javax.swing.JFrame;
32import javax.swing.JLabel;
33import javax.swing.JList;
34import javax.swing.JOptionPane;
35import javax.swing.JPanel;
36import javax.swing.JScrollBar;
37import javax.swing.JScrollPane;
38import javax.swing.JSplitPane;
39import javax.swing.JTabbedPane;
40import javax.swing.JTextField;
41import javax.swing.SwingUtilities;
42import javax.swing.WindowConstants;
29import javax.swing.tree.DefaultMutableTreeNode; 43import javax.swing.tree.DefaultMutableTreeNode;
30import javax.swing.tree.TreeNode;
31import javax.swing.tree.TreePath;
32
33import com.google.common.collect.Lists;
34 44
35import cuchaz.enigma.Enigma; 45import cuchaz.enigma.Enigma;
36import cuchaz.enigma.EnigmaProfile; 46import cuchaz.enigma.EnigmaProfile;
@@ -39,8 +49,19 @@ import cuchaz.enigma.gui.config.Themes;
39import cuchaz.enigma.gui.config.UiConfig; 49import cuchaz.enigma.gui.config.UiConfig;
40import cuchaz.enigma.gui.dialog.JavadocDialog; 50import cuchaz.enigma.gui.dialog.JavadocDialog;
41import cuchaz.enigma.gui.dialog.SearchDialog; 51import cuchaz.enigma.gui.dialog.SearchDialog;
42import cuchaz.enigma.gui.elements.*; 52import cuchaz.enigma.gui.elements.CallsTree;
43import cuchaz.enigma.gui.panels.*; 53import cuchaz.enigma.gui.elements.CollapsibleTabbedPane;
54import cuchaz.enigma.gui.elements.EditorTabbedPane;
55import cuchaz.enigma.gui.elements.ImplementationsTree;
56import cuchaz.enigma.gui.elements.InheritanceTree;
57import cuchaz.enigma.gui.elements.MainWindow;
58import cuchaz.enigma.gui.elements.MenuBar;
59import cuchaz.enigma.gui.elements.ValidatableUi;
60import cuchaz.enigma.gui.panels.DeobfPanel;
61import cuchaz.enigma.gui.panels.EditorPanel;
62import cuchaz.enigma.gui.panels.IdentifierPanel;
63import cuchaz.enigma.gui.panels.ObfPanel;
64import cuchaz.enigma.gui.panels.StructurePanel;
44import cuchaz.enigma.gui.renderer.MessageListCellRenderer; 65import cuchaz.enigma.gui.renderer.MessageListCellRenderer;
45import cuchaz.enigma.gui.util.GuiUtil; 66import cuchaz.enigma.gui.util.GuiUtil;
46import cuchaz.enigma.gui.util.LanguageUtil; 67import cuchaz.enigma.gui.util.LanguageUtil;
@@ -57,7 +78,6 @@ import cuchaz.enigma.utils.validation.ParameterizedMessage;
57import cuchaz.enigma.utils.validation.ValidationContext; 78import cuchaz.enigma.utils.validation.ValidationContext;
58 79
59public class Gui { 80public class Gui {
60
61 private final MainWindow mainWindow = new MainWindow(Enigma.NAME); 81 private final MainWindow mainWindow = new MainWindow(Enigma.NAME);
62 private final GuiController controller; 82 private final GuiController controller;
63 83
@@ -179,6 +199,7 @@ public class Gui {
179 199
180 // restore state 200 // restore state
181 int[] layout = UiConfig.getLayout(); 201 int[] layout = UiConfig.getLayout();
202
182 if (layout.length >= 4) { 203 if (layout.length >= 4) {
183 this.splitClasses.setDividerLocation(layout[0]); 204 this.splitClasses.setDividerLocation(layout[0]);
184 this.splitCenter.setDividerLocation(layout[1]); 205 this.splitCenter.setDividerLocation(layout[1]);
@@ -200,6 +221,7 @@ public class Gui {
200 frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); 221 frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
201 222
202 Point windowPos = UiConfig.getWindowPos("Main Window", null); 223 Point windowPos = UiConfig.getWindowPos("Main Window", null);
224
203 if (windowPos != null) { 225 if (windowPos != null) {
204 frame.setLocation(windowPos); 226 frame.setLocation(windowPos);
205 } else { 227 } else {
@@ -327,18 +349,26 @@ public class Gui {
327 349
328 public void startDocChange(EditorPanel editor) { 350 public void startDocChange(EditorPanel editor) {
329 EntryReference<Entry<?>, Entry<?>> cursorReference = editor.getCursorReference(); 351 EntryReference<Entry<?>, Entry<?>> cursorReference = editor.getCursorReference();
330 if (cursorReference == null || !this.isEditable(EditableType.JAVADOC)) return; 352
353 if (cursorReference == null || !this.isEditable(EditableType.JAVADOC)) {
354 return;
355 }
356
331 JavadocDialog.show(mainWindow.frame(), getController(), cursorReference); 357 JavadocDialog.show(mainWindow.frame(), getController(), cursorReference);
332 } 358 }
333 359
334 public void startRename(EditorPanel editor, String text) { 360 public void startRename(EditorPanel editor, String text) {
335 if (editor != this.editorTabbedPane.getActiveEditor()) return; 361 if (editor != this.editorTabbedPane.getActiveEditor()) {
362 return;
363 }
336 364
337 infoPanel.startRenaming(text); 365 infoPanel.startRenaming(text);
338 } 366 }
339 367
340 public void startRename(EditorPanel editor) { 368 public void startRename(EditorPanel editor) {
341 if (editor != this.editorTabbedPane.getActiveEditor()) return; 369 if (editor != this.editorTabbedPane.getActiveEditor()) {
370 return;
371 }
342 372
343 infoPanel.startRenaming(); 373 infoPanel.startRenaming();
344 } 374 }
@@ -349,7 +379,10 @@ public class Gui {
349 379
350 public void showInheritance(EditorPanel editor) { 380 public void showInheritance(EditorPanel editor) {
351 EntryReference<Entry<?>, Entry<?>> cursorReference = editor.getCursorReference(); 381 EntryReference<Entry<?>, Entry<?>> cursorReference = editor.getCursorReference();
352 if (cursorReference == null) return; 382
383 if (cursorReference == null) {
384 return;
385 }
353 386
354 this.inheritanceTree.display(cursorReference.entry); 387 this.inheritanceTree.display(cursorReference.entry);
355 tabs.setSelectedIndex(1); 388 tabs.setSelectedIndex(1);
@@ -357,7 +390,10 @@ public class Gui {
357 390
358 public void showImplementations(EditorPanel editor) { 391 public void showImplementations(EditorPanel editor) {
359 EntryReference<Entry<?>, Entry<?>> cursorReference = editor.getCursorReference(); 392 EntryReference<Entry<?>, Entry<?>> cursorReference = editor.getCursorReference();
360 if (cursorReference == null) return; 393
394 if (cursorReference == null) {
395 return;
396 }
361 397
362 this.implementationsTree.display(cursorReference.entry); 398 this.implementationsTree.display(cursorReference.entry);
363 tabs.setSelectedIndex(2); 399 tabs.setSelectedIndex(2);
@@ -365,7 +401,10 @@ public class Gui {
365 401
366 public void showCalls(EditorPanel editor, boolean recurse) { 402 public void showCalls(EditorPanel editor, boolean recurse) {
367 EntryReference<Entry<?>, Entry<?>> cursorReference = editor.getCursorReference(); 403 EntryReference<Entry<?>, Entry<?>> cursorReference = editor.getCursorReference();
368 if (cursorReference == null) return; 404
405 if (cursorReference == null) {
406 return;
407 }
369 408
370 this.callsTree.showCalls(cursorReference.entry, recurse); 409 this.callsTree.showCalls(cursorReference.entry, recurse);
371 tabs.setSelectedIndex(3); 410 tabs.setSelectedIndex(3);
@@ -373,7 +412,10 @@ public class Gui {
373 412
374 public void toggleMapping(EditorPanel editor) { 413 public void toggleMapping(EditorPanel editor) {
375 EntryReference<Entry<?>, Entry<?>> cursorReference = editor.getCursorReference(); 414 EntryReference<Entry<?>, Entry<?>> cursorReference = editor.getCursorReference();
376 if (cursorReference == null) return; 415
416 if (cursorReference == null) {
417 return;
418 }
377 419
378 Entry<?> obfEntry = cursorReference.entry; 420 Entry<?> obfEntry = cursorReference.entry;
379 toggleMappingFromEntry(obfEntry); 421 toggleMappingFromEntry(obfEntry);
@@ -388,14 +430,15 @@ public class Gui {
388 } 430 }
389 431
390 public void showDiscardDiag(Function<Integer, Void> callback, String... options) { 432 public void showDiscardDiag(Function<Integer, Void> callback, String... options) {
391 int response = JOptionPane.showOptionDialog(this.mainWindow.frame(), I18n.translate("prompt.close.summary"), I18n.translate("prompt.close.title"), JOptionPane.YES_NO_CANCEL_OPTION, 433 int response = JOptionPane.showOptionDialog(this.mainWindow.frame(), I18n.translate("prompt.close.summary"), I18n.translate("prompt.close.title"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[2]);
392 JOptionPane.QUESTION_MESSAGE, null, options, options[2]);
393 callback.apply(response); 434 callback.apply(response);
394 } 435 }
395 436
396 public CompletableFuture<Void> saveMapping() { 437 public CompletableFuture<Void> saveMapping() {
397 if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.mainWindow.frame()) == JFileChooser.APPROVE_OPTION) 438 if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.mainWindow.frame()) == JFileChooser.APPROVE_OPTION) {
398 return this.controller.saveMappings(this.enigmaMappingsFileChooser.getSelectedFile().toPath()); 439 return this.controller.saveMappings(this.enigmaMappingsFileChooser.getSelectedFile().toPath());
440 }
441
399 return CompletableFuture.completedFuture(null); 442 return CompletableFuture.completedFuture(null);
400 } 443 }
401 444
@@ -421,16 +464,13 @@ public class Gui {
421 private void exit() { 464 private void exit() {
422 UiConfig.setWindowPos("Main Window", this.mainWindow.frame().getLocationOnScreen()); 465 UiConfig.setWindowPos("Main Window", this.mainWindow.frame().getLocationOnScreen());
423 UiConfig.setWindowSize("Main Window", this.mainWindow.frame().getSize()); 466 UiConfig.setWindowSize("Main Window", this.mainWindow.frame().getSize());
424 UiConfig.setLayout( 467 UiConfig.setLayout(this.splitClasses.getDividerLocation(), this.splitCenter.getDividerLocation(), this.splitRight.getDividerLocation(), this.logSplit.getDividerLocation());
425 this.splitClasses.getDividerLocation(),
426 this.splitCenter.getDividerLocation(),
427 this.splitRight.getDividerLocation(),
428 this.logSplit.getDividerLocation());
429 UiConfig.save(); 468 UiConfig.save();
430 469
431 if (searchDialog != null) { 470 if (searchDialog != null) {
432 searchDialog.dispose(); 471 searchDialog.dispose();
433 } 472 }
473
434 this.mainWindow.frame().dispose(); 474 this.mainWindow.frame().dispose();
435 System.exit(0); 475 System.exit(0);
436 } 476 }
@@ -452,6 +492,7 @@ public class Gui {
452 492
453 onRenameFromClassTree(vc, prevDataChild, dataChild, node); 493 onRenameFromClassTree(vc, prevDataChild, dataChild, node);
454 } 494 }
495
455 node.setUserObject(data); 496 node.setUserObject(data);
456 // Ob package will never be modified, just reload deob view 497 // Ob package will never be modified, just reload deob view
457 this.deobfPanel.deobfClasses.reload(); 498 this.deobfPanel.deobfClasses.reload();
@@ -462,11 +503,7 @@ public class Gui {
462 // fast enough for now 503 // fast enough for now
463 EntryRemapper mapper = this.controller.project.getMapper(); 504 EntryRemapper mapper = this.controller.project.getMapper();
464 ClassEntry deobf = (ClassEntry) prevData; 505 ClassEntry deobf = (ClassEntry) prevData;
465 ClassEntry obf = mapper.getObfToDeobf().getAllEntries() 506 ClassEntry obf = mapper.getObfToDeobf().getAllEntries().filter(e -> e instanceof ClassEntry).map(e -> (ClassEntry) e).filter(e -> mapper.deobfuscate(e).equals(deobf)).findAny().orElse(deobf);
466 .filter(e -> e instanceof ClassEntry)
467 .map(e -> (ClassEntry) e)
468 .filter(e -> mapper.deobfuscate(e).equals(deobf))
469 .findAny().orElse(deobf);
470 507
471 this.controller.applyChange(vc, EntryChange.modify(obf).withDeobfName(((ClassEntry) data).getFullName())); 508 this.controller.applyChange(vc, EntryChange.modify(obf).withDeobfName(((ClassEntry) data).getFullName()));
472 } else { 509 } else {
@@ -493,16 +530,12 @@ public class Gui {
493 this.obfPanel.obfClasses.removeEntry(classEntry); 530 this.obfPanel.obfClasses.removeEntry(classEntry);
494 this.deobfPanel.deobfClasses.reload(); 531 this.deobfPanel.deobfClasses.reload();
495 this.obfPanel.obfClasses.reload(); 532 this.obfPanel.obfClasses.reload();
496 } 533 } else if (!isOldOb) { // Deob -> ob
497 // Deob -> ob
498 else if (!isOldOb) {
499 this.obfPanel.obfClasses.moveClassIn(classEntry); 534 this.obfPanel.obfClasses.moveClassIn(classEntry);
500 this.deobfPanel.deobfClasses.removeEntry(classEntry); 535 this.deobfPanel.deobfClasses.removeEntry(classEntry);
501 this.deobfPanel.deobfClasses.reload(); 536 this.deobfPanel.deobfClasses.reload();
502 this.obfPanel.obfClasses.reload(); 537 this.obfPanel.obfClasses.reload();
503 } 538 } else if (isOldOb) { // Local move
504 // Local move
505 else if (isOldOb) {
506 this.obfPanel.obfClasses.moveClassIn(classEntry); 539 this.obfPanel.obfClasses.moveClassIn(classEntry);
507 this.obfPanel.obfClasses.reload(); 540 this.obfPanel.obfClasses.reload();
508 } else { 541 } else {
@@ -526,6 +559,7 @@ public class Gui {
526 if (searchDialog == null) { 559 if (searchDialog == null) {
527 searchDialog = new SearchDialog(this); 560 searchDialog = new SearchDialog(this);
528 } 561 }
562
529 return searchDialog; 563 return searchDialog;
530 } 564 }
531 565
@@ -549,9 +583,11 @@ public class Gui {
549 583
550 private void sendMessage() { 584 private void sendMessage() {
551 String text = chatBox.getText().trim(); 585 String text = chatBox.getText().trim();
586
552 if (!text.isEmpty()) { 587 if (!text.isEmpty()) {
553 getController().sendPacket(new MessageC2SPacket(text)); 588 getController().sendPacket(new MessageC2SPacket(text));
554 } 589 }
590
555 chatBox.setText(""); 591 chatBox.setText("");
556 } 592 }
557 593
@@ -617,16 +653,17 @@ public class Gui {
617 public boolean validateImmediateAction(Consumer<ValidationContext> op) { 653 public boolean validateImmediateAction(Consumer<ValidationContext> op) {
618 ValidationContext vc = new ValidationContext(); 654 ValidationContext vc = new ValidationContext();
619 op.accept(vc); 655 op.accept(vc);
656
620 if (!vc.canProceed()) { 657 if (!vc.canProceed()) {
621 List<ParameterizedMessage> messages = vc.getMessages(); 658 List<ParameterizedMessage> messages = vc.getMessages();
622 String text = ValidatableUi.formatMessages(messages); 659 String text = ValidatableUi.formatMessages(messages);
623 JOptionPane.showMessageDialog(this.getFrame(), text, String.format("%d message(s)", messages.size()), JOptionPane.ERROR_MESSAGE); 660 JOptionPane.showMessageDialog(this.getFrame(), text, String.format("%d message(s)", messages.size()), JOptionPane.ERROR_MESSAGE);
624 } 661 }
662
625 return vc.canProceed(); 663 return vc.canProceed();
626 } 664 }
627 665
628 public boolean isEditable(EditableType t) { 666 public boolean isEditable(EditableType t) {
629 return this.editableTypes.contains(t); 667 return this.editableTypes.contains(t);
630 } 668 }
631
632} 669}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java
index 47a854f..0eb9a16 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.gui; 12package cuchaz.enigma.gui;
13 13
@@ -32,7 +32,17 @@ import com.google.common.collect.Lists;
32import cuchaz.enigma.Enigma; 32import cuchaz.enigma.Enigma;
33import cuchaz.enigma.EnigmaProfile; 33import cuchaz.enigma.EnigmaProfile;
34import cuchaz.enigma.EnigmaProject; 34import cuchaz.enigma.EnigmaProject;
35import cuchaz.enigma.analysis.*; 35import cuchaz.enigma.analysis.ClassImplementationsTreeNode;
36import cuchaz.enigma.analysis.ClassInheritanceTreeNode;
37import cuchaz.enigma.analysis.ClassReferenceTreeNode;
38import cuchaz.enigma.analysis.EntryReference;
39import cuchaz.enigma.analysis.FieldReferenceTreeNode;
40import cuchaz.enigma.analysis.IndexTreeBuilder;
41import cuchaz.enigma.analysis.MethodImplementationsTreeNode;
42import cuchaz.enigma.analysis.MethodInheritanceTreeNode;
43import cuchaz.enigma.analysis.MethodReferenceTreeNode;
44import cuchaz.enigma.analysis.StructureTreeNode;
45import cuchaz.enigma.analysis.StructureTreeOptions;
36import cuchaz.enigma.api.service.ObfuscationTestService; 46import cuchaz.enigma.api.service.ObfuscationTestService;
37import cuchaz.enigma.classhandle.ClassHandle; 47import cuchaz.enigma.classhandle.ClassHandle;
38import cuchaz.enigma.classhandle.ClassHandleProvider; 48import cuchaz.enigma.classhandle.ClassHandleProvider;
@@ -44,7 +54,12 @@ import cuchaz.enigma.gui.newabstraction.EntryValidation;
44import cuchaz.enigma.gui.stats.StatsGenerator; 54import cuchaz.enigma.gui.stats.StatsGenerator;
45import cuchaz.enigma.gui.stats.StatsMember; 55import cuchaz.enigma.gui.stats.StatsMember;
46import cuchaz.enigma.gui.util.History; 56import cuchaz.enigma.gui.util.History;
47import cuchaz.enigma.network.*; 57import cuchaz.enigma.network.ClientPacketHandler;
58import cuchaz.enigma.network.EnigmaClient;
59import cuchaz.enigma.network.EnigmaServer;
60import cuchaz.enigma.network.IntegratedEnigmaServer;
61import cuchaz.enigma.network.Message;
62import cuchaz.enigma.network.ServerPacketHandler;
48import cuchaz.enigma.network.packet.EntryChangeC2SPacket; 63import cuchaz.enigma.network.packet.EntryChangeC2SPacket;
49import cuchaz.enigma.network.packet.LoginC2SPacket; 64import cuchaz.enigma.network.packet.LoginC2SPacket;
50import cuchaz.enigma.network.packet.Packet; 65import cuchaz.enigma.network.packet.Packet;
@@ -54,7 +69,12 @@ import cuchaz.enigma.source.SourceIndex;
54import cuchaz.enigma.source.Token; 69import cuchaz.enigma.source.Token;
55import cuchaz.enigma.translation.TranslateResult; 70import cuchaz.enigma.translation.TranslateResult;
56import cuchaz.enigma.translation.Translator; 71import cuchaz.enigma.translation.Translator;
57import cuchaz.enigma.translation.mapping.*; 72import cuchaz.enigma.translation.mapping.EntryChange;
73import cuchaz.enigma.translation.mapping.EntryMapping;
74import cuchaz.enigma.translation.mapping.EntryRemapper;
75import cuchaz.enigma.translation.mapping.EntryUtil;
76import cuchaz.enigma.translation.mapping.MappingDelta;
77import cuchaz.enigma.translation.mapping.ResolutionStrategy;
58import cuchaz.enigma.translation.mapping.serde.MappingFormat; 78import cuchaz.enigma.translation.mapping.serde.MappingFormat;
59import cuchaz.enigma.translation.mapping.serde.MappingParseException; 79import cuchaz.enigma.translation.mapping.serde.MappingParseException;
60import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; 80import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
@@ -90,9 +110,7 @@ public class GuiController implements ClientPacketHandler {
90 110
91 public GuiController(Gui gui, EnigmaProfile profile) { 111 public GuiController(Gui gui, EnigmaProfile profile) {
92 this.gui = gui; 112 this.gui = gui;
93 this.enigma = Enigma.builder() 113 this.enigma = Enigma.builder().setProfile(profile).build();
94 .setProfile(profile)
95 .build();
96 } 114 }
97 115
98 public boolean isDirty() { 116 public boolean isDirty() {
@@ -121,7 +139,9 @@ public class GuiController implements ClientPacketHandler {
121 } 139 }
122 140
123 public CompletableFuture<Void> openMappings(MappingFormat format, Path path) { 141 public CompletableFuture<Void> openMappings(MappingFormat format, Path path) {
124 if (project == null) return CompletableFuture.completedFuture(null); 142 if (project == null) {
143 return CompletableFuture.completedFuture(null);
144 }
125 145
126 gui.setMappingsFile(path); 146 gui.setMappingsFile(path);
127 147
@@ -145,7 +165,9 @@ public class GuiController implements ClientPacketHandler {
145 165
146 @Override 166 @Override
147 public void openMappings(EntryTree<EntryMapping> mappings) { 167 public void openMappings(EntryTree<EntryMapping> mappings) {
148 if (project == null) return; 168 if (project == null) {
169 return;
170 }
149 171
150 project.setMappings(mappings); 172 project.setMappings(mappings);
151 refreshClasses(); 173 refreshClasses();
@@ -168,7 +190,9 @@ public class GuiController implements ClientPacketHandler {
168 * @return the future of saving 190 * @return the future of saving
169 */ 191 */
170 public CompletableFuture<Void> saveMappings(Path path, MappingFormat format) { 192 public CompletableFuture<Void> saveMappings(Path path, MappingFormat format) {
171 if (project == null) return CompletableFuture.completedFuture(null); 193 if (project == null) {
194 return CompletableFuture.completedFuture(null);
195 }
172 196
173 return ProgressDialog.runOffThread(this.gui.getFrame(), progress -> { 197 return ProgressDialog.runOffThread(this.gui.getFrame(), progress -> {
174 EntryRemapper mapper = project.getMapper(); 198 EntryRemapper mapper = project.getMapper();
@@ -189,7 +213,9 @@ public class GuiController implements ClientPacketHandler {
189 } 213 }
190 214
191 public void closeMappings() { 215 public void closeMappings() {
192 if (project == null) return; 216 if (project == null) {
217 return;
218 }
193 219
194 project.setMappings(null); 220 project.setMappings(null);
195 221
@@ -202,9 +228,11 @@ public class GuiController implements ClientPacketHandler {
202 Path jarPath = this.project.getJarPath(); 228 Path jarPath = this.project.getJarPath();
203 MappingFormat loadedMappingFormat = this.loadedMappingFormat; 229 MappingFormat loadedMappingFormat = this.loadedMappingFormat;
204 Path loadedMappingPath = this.loadedMappingPath; 230 Path loadedMappingPath = this.loadedMappingPath;
231
205 if (jarPath != null) { 232 if (jarPath != null) {
206 this.closeJar(); 233 this.closeJar();
207 CompletableFuture<Void> f = this.openJar(jarPath); 234 CompletableFuture<Void> f = this.openJar(jarPath);
235
208 if (loadedMappingFormat != null && loadedMappingPath != null) { 236 if (loadedMappingFormat != null && loadedMappingPath != null) {
209 f.whenComplete((v, t) -> this.openMappings(loadedMappingFormat, loadedMappingPath)); 237 f.whenComplete((v, t) -> this.openMappings(loadedMappingFormat, loadedMappingPath));
210 } 238 }
@@ -214,6 +242,7 @@ public class GuiController implements ClientPacketHandler {
214 public void reloadMappings() { 242 public void reloadMappings() {
215 MappingFormat loadedMappingFormat = this.loadedMappingFormat; 243 MappingFormat loadedMappingFormat = this.loadedMappingFormat;
216 Path loadedMappingPath = this.loadedMappingPath; 244 Path loadedMappingPath = this.loadedMappingPath;
245
217 if (loadedMappingFormat != null && loadedMappingPath != null) { 246 if (loadedMappingFormat != null && loadedMappingPath != null) {
218 this.closeMappings(); 247 this.closeMappings();
219 this.openMappings(loadedMappingFormat, loadedMappingPath); 248 this.openMappings(loadedMappingFormat, loadedMappingPath);
@@ -221,29 +250,34 @@ public class GuiController implements ClientPacketHandler {
221 } 250 }
222 251
223 public CompletableFuture<Void> dropMappings() { 252 public CompletableFuture<Void> dropMappings() {
224 if (project == null) return CompletableFuture.completedFuture(null); 253 if (project == null) {
254 return CompletableFuture.completedFuture(null);
255 }
225 256
226 return ProgressDialog.runOffThread(this.gui.getFrame(), progress -> project.dropMappings(progress)); 257 return ProgressDialog.runOffThread(this.gui.getFrame(), progress -> project.dropMappings(progress));
227 } 258 }
228 259
229 public CompletableFuture<Void> exportSource(final Path path) { 260 public CompletableFuture<Void> exportSource(final Path path) {
230 if (project == null) return CompletableFuture.completedFuture(null); 261 if (project == null) {
262 return CompletableFuture.completedFuture(null);
263 }
231 264
232 return ProgressDialog.runOffThread(this.gui.getFrame(), progress -> { 265 return ProgressDialog.runOffThread(this.gui.getFrame(), progress -> {
233 EnigmaProject.JarExport jar = project.exportRemappedJar(progress); 266 EnigmaProject.JarExport jar = project.exportRemappedJar(progress);
234 jar.decompileStream(progress, chp.getDecompilerService(), EnigmaProject.DecompileErrorStrategy.TRACE_AS_SOURCE) 267 jar.decompileStream(progress, chp.getDecompilerService(), EnigmaProject.DecompileErrorStrategy.TRACE_AS_SOURCE).forEach(source -> {
235 .forEach(source -> { 268 try {
236 try { 269 source.writeTo(source.resolvePath(path));
237 source.writeTo(source.resolvePath(path)); 270 } catch (IOException e) {
238 } catch (IOException e) { 271 e.printStackTrace();
239 e.printStackTrace(); 272 }
240 } 273 });
241 });
242 }); 274 });
243 } 275 }
244 276
245 public CompletableFuture<Void> exportJar(final Path path) { 277 public CompletableFuture<Void> exportJar(final Path path) {
246 if (project == null) return CompletableFuture.completedFuture(null); 278 if (project == null) {
279 return CompletableFuture.completedFuture(null);
280 }
247 281
248 return ProgressDialog.runOffThread(this.gui.getFrame(), progress -> { 282 return ProgressDialog.runOffThread(this.gui.getFrame(), progress -> {
249 EnigmaProject.JarExport jar = project.exportRemappedJar(progress); 283 EnigmaProject.JarExport jar = project.exportRemappedJar(progress);
@@ -269,20 +303,14 @@ public class GuiController implements ClientPacketHandler {
269 } 303 }
270 304
271 try { 305 try {
272 return tokenHandle.getSource().get() 306 return tokenHandle.getSource().get().map(DecompiledClassSource::getIndex).map(index -> new ReadableToken(index.getLineNumber(token.start), index.getColumnNumber(token.start), index.getColumnNumber(token.end))).unwrapOr(null);
273 .map(DecompiledClassSource::getIndex)
274 .map(index -> new ReadableToken(
275 index.getLineNumber(token.start),
276 index.getColumnNumber(token.start),
277 index.getColumnNumber(token.end)))
278 .unwrapOr(null);
279 } catch (InterruptedException | ExecutionException e) { 307 } catch (InterruptedException | ExecutionException e) {
280 throw new RuntimeException(e); 308 throw new RuntimeException(e);
281 } 309 }
282 } 310 }
283 311
284 /** 312 /**
285 * Navigates to the declaration with respect to navigation history 313 * Navigates to the declaration with respect to navigation history.
286 * 314 *
287 * @param entry the entry whose declaration will be navigated to 315 * @param entry the entry whose declaration will be navigated to
288 */ 316 */
@@ -290,11 +318,12 @@ public class GuiController implements ClientPacketHandler {
290 if (entry == null) { 318 if (entry == null) {
291 throw new IllegalArgumentException("Entry cannot be null!"); 319 throw new IllegalArgumentException("Entry cannot be null!");
292 } 320 }
321
293 openReference(EntryReference.declaration(entry, entry.getName())); 322 openReference(EntryReference.declaration(entry, entry.getName()));
294 } 323 }
295 324
296 /** 325 /**
297 * Navigates to the reference with respect to navigation history 326 * Navigates to the reference with respect to navigation history.
298 * 327 *
299 * @param reference the reference 328 * @param reference the reference
300 */ 329 */
@@ -302,6 +331,7 @@ public class GuiController implements ClientPacketHandler {
302 if (reference == null) { 331 if (reference == null) {
303 throw new IllegalArgumentException("Reference cannot be null!"); 332 throw new IllegalArgumentException("Reference cannot be null!");
304 } 333 }
334
305 if (this.referenceHistory == null) { 335 if (this.referenceHistory == null) {
306 this.referenceHistory = new History<>(reference); 336 this.referenceHistory = new History<>(reference);
307 } else { 337 } else {
@@ -317,11 +347,7 @@ public class GuiController implements ClientPacketHandler {
317 EntryRemapper mapper = this.project.getMapper(); 347 EntryRemapper mapper = this.project.getMapper();
318 348
319 SourceIndex index = source.getIndex(); 349 SourceIndex index = source.getIndex();
320 return mapper.getObfResolver().resolveReference(reference, ResolutionStrategy.RESOLVE_CLOSEST) 350 return mapper.getObfResolver().resolveReference(reference, ResolutionStrategy.RESOLVE_CLOSEST).stream().flatMap(r -> index.getReferenceTokens(r).stream()).sorted().toList();
321 .stream()
322 .flatMap(r -> index.getReferenceTokens(r).stream())
323 .sorted()
324 .toList();
325 } 351 }
326 352
327 public void openPreviousReference() { 353 public void openPreviousReference() {
@@ -349,6 +375,7 @@ public class GuiController implements ClientPacketHandler {
349 // entry is not in the jar. Ignore it 375 // entry is not in the jar. Ignore it
350 return; 376 return;
351 } 377 }
378
352 openDeclaration(entry); 379 openDeclaration(entry);
353 } 380 }
354 381
@@ -356,12 +383,15 @@ public class GuiController implements ClientPacketHandler {
356 if (!project.isRenamable(reference.getLocationClassEntry())) { 383 if (!project.isRenamable(reference.getLocationClassEntry())) {
357 return; 384 return;
358 } 385 }
386
359 openReference(reference); 387 openReference(reference);
360 } 388 }
361 389
362 public void refreshClasses() { 390 public void refreshClasses() {
363 if (project == null) return; 391 if (project == null) {
364 392 return;
393 }
394
365 List<ClassEntry> obfClasses = Lists.newArrayList(); 395 List<ClassEntry> obfClasses = Lists.newArrayList();
366 List<ClassEntry> deobfClasses = Lists.newArrayList(); 396 List<ClassEntry> deobfClasses = Lists.newArrayList();
367 this.addSeparatedClasses(obfClasses, deobfClasses); 397 this.addSeparatedClasses(obfClasses, deobfClasses);
@@ -373,8 +403,7 @@ public class GuiController implements ClientPacketHandler {
373 EntryRemapper mapper = project.getMapper(); 403 EntryRemapper mapper = project.getMapper();
374 404
375 Collection<ClassEntry> classes = project.getJarIndex().getEntryIndex().getClasses(); 405 Collection<ClassEntry> classes = project.getJarIndex().getEntryIndex().getClasses();
376 Stream<ClassEntry> visibleClasses = classes.stream() 406 Stream<ClassEntry> visibleClasses = classes.stream().filter(entry -> !entry.isInnerClass());
377 .filter(entry -> !entry.isInnerClass());
378 407
379 visibleClasses.forEach(entry -> { 408 visibleClasses.forEach(entry -> {
380 if (gui.isSingleClassTree()) { 409 if (gui.isSingleClassTree()) {
@@ -428,12 +457,15 @@ public class GuiController implements ClientPacketHandler {
428 public MethodImplementationsTreeNode getMethodImplementations(MethodEntry entry) { 457 public MethodImplementationsTreeNode getMethodImplementations(MethodEntry entry) {
429 Translator translator = project.getMapper().getDeobfuscator(); 458 Translator translator = project.getMapper().getDeobfuscator();
430 List<MethodImplementationsTreeNode> rootNodes = indexTreeBuilder.buildMethodImplementations(translator, entry); 459 List<MethodImplementationsTreeNode> rootNodes = indexTreeBuilder.buildMethodImplementations(translator, entry);
460
431 if (rootNodes.isEmpty()) { 461 if (rootNodes.isEmpty()) {
432 return null; 462 return null;
433 } 463 }
464
434 if (rootNodes.size() > 1) { 465 if (rootNodes.size() > 1) {
435 System.err.println("WARNING: Method " + entry + " implements multiple interfaces. Only showing first one."); 466 System.err.println("WARNING: Method " + entry + " implements multiple interfaces. Only showing first one.");
436 } 467 }
468
437 return MethodImplementationsTreeNode.findNode(rootNodes.get(0), entry); 469 return MethodImplementationsTreeNode.findNode(rootNodes.get(0), entry);
438 } 470 }
439 471
@@ -481,13 +513,20 @@ public class GuiController implements ClientPacketHandler {
481 public void applyChange(ValidationContext vc, EntryChange<?> change) { 513 public void applyChange(ValidationContext vc, EntryChange<?> change) {
482 this.applyChange0(vc, change); 514 this.applyChange0(vc, change);
483 gui.showStructure(gui.getActiveEditor()); 515 gui.showStructure(gui.getActiveEditor());
484 if (!vc.canProceed()) return; 516
517 if (!vc.canProceed()) {
518 return;
519 }
520
485 this.sendPacket(new EntryChangeC2SPacket(change)); 521 this.sendPacket(new EntryChangeC2SPacket(change));
486 } 522 }
487 523
488 private void applyChange0(ValidationContext vc, EntryChange<?> change) { 524 private void applyChange0(ValidationContext vc, EntryChange<?> change) {
489 validateChange(vc, change); 525 validateChange(vc, change);
490 if (!vc.canProceed()) return; 526
527 if (!vc.canProceed()) {
528 return;
529 }
491 530
492 Entry<?> target = change.getTarget(); 531 Entry<?> target = change.getTarget();
493 EntryMapping prev = this.project.getMapper().getDeobfMapping(target); 532 EntryMapping prev = this.project.getMapper().getDeobfMapping(target);
@@ -506,6 +545,7 @@ public class GuiController implements ClientPacketHandler {
506 if (!Objects.equals(prev.javadoc(), mapping.javadoc())) { 545 if (!Objects.equals(prev.javadoc(), mapping.javadoc())) {
507 this.chp.invalidateJavadoc(target.getTopLevelClass()); 546 this.chp.invalidateJavadoc(target.getTopLevelClass());
508 } 547 }
548
509 gui.showStructure(gui.getActiveEditor()); 549 gui.showStructure(gui.getActiveEditor());
510 } 550 }
511 551
@@ -517,10 +557,7 @@ public class GuiController implements ClientPacketHandler {
517 File statsFile = File.createTempFile("stats", ".html"); 557 File statsFile = File.createTempFile("stats", ".html");
518 558
519 try (FileWriter w = new FileWriter(statsFile)) { 559 try (FileWriter w = new FileWriter(statsFile)) {
520 w.write( 560 w.write(Utils.readResourceToString("/stats.html").replace("/*data*/", data));
521 Utils.readResourceToString("/stats.html")
522 .replace("/*data*/", data)
523 );
524 } 561 }
525 562
526 Desktop.getDesktop().open(statsFile); 563 Desktop.getDesktop().open(statsFile);
@@ -573,15 +610,18 @@ public class GuiController implements ClientPacketHandler {
573 if (client != null) { 610 if (client != null) {
574 client.disconnect(); 611 client.disconnect();
575 } 612 }
613
576 if (server != null) { 614 if (server != null) {
577 server.stop(); 615 server.stop();
578 } 616 }
617
579 client = null; 618 client = null;
580 server = null; 619 server = null;
581 SwingUtilities.invokeLater(() -> { 620 SwingUtilities.invokeLater(() -> {
582 if (reason != null) { 621 if (reason != null) {
583 JOptionPane.showMessageDialog(gui.getFrame(), I18n.translate(reason), I18n.translate("disconnect.disconnected"), JOptionPane.INFORMATION_MESSAGE); 622 JOptionPane.showMessageDialog(gui.getFrame(), I18n.translate(reason), I18n.translate("disconnect.disconnected"), JOptionPane.INFORMATION_MESSAGE);
584 } 623 }
624
585 gui.setConnectionState(ConnectionState.NOT_CONNECTED); 625 gui.setConnectionState(ConnectionState.NOT_CONNECTED);
586 }); 626 });
587 } 627 }
@@ -602,5 +642,4 @@ public class GuiController implements ClientPacketHandler {
602 public void updateUserList(List<String> users) { 642 public void updateUserList(List<String> users) {
603 gui.setUserList(users); 643 gui.setUserList(users);
604 } 644 }
605
606} 645}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/Main.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/Main.java
index 1172a39..56f4385 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Main.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Main.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.gui; 12package cuchaz.enigma.gui;
13 13
@@ -20,7 +20,11 @@ import java.util.List;
20import java.util.Set; 20import java.util.Set;
21 21
22import com.google.common.io.MoreFiles; 22import com.google.common.io.MoreFiles;
23import joptsimple.*; 23import joptsimple.OptionException;
24import joptsimple.OptionParser;
25import joptsimple.OptionSet;
26import joptsimple.OptionSpec;
27import joptsimple.ValueConverter;
24 28
25import cuchaz.enigma.EnigmaProfile; 29import cuchaz.enigma.EnigmaProfile;
26import cuchaz.enigma.gui.config.Themes; 30import cuchaz.enigma.gui.config.Themes;
@@ -30,21 +34,14 @@ import cuchaz.enigma.translation.mapping.serde.MappingFormat;
30import cuchaz.enigma.utils.I18n; 34import cuchaz.enigma.utils.I18n;
31 35
32public class Main { 36public class Main {
33
34 public static void main(String[] args) throws IOException { 37 public static void main(String[] args) throws IOException {
35 OptionParser parser = new OptionParser(); 38 OptionParser parser = new OptionParser();
36 39
37 OptionSpec<Path> jar = parser.accepts("jar", "Jar file to open at startup") 40 OptionSpec<Path> jar = parser.accepts("jar", "Jar file to open at startup").withRequiredArg().withValuesConvertedBy(PathConverter.INSTANCE);
38 .withRequiredArg()
39 .withValuesConvertedBy(PathConverter.INSTANCE);
40 41
41 OptionSpec<Path> mappings = parser.accepts("mappings", "Mappings file to open at startup") 42 OptionSpec<Path> mappings = parser.accepts("mappings", "Mappings file to open at startup").withRequiredArg().withValuesConvertedBy(PathConverter.INSTANCE);
42 .withRequiredArg()
43 .withValuesConvertedBy(PathConverter.INSTANCE);
44 43
45 OptionSpec<Path> profile = parser.accepts("profile", "Profile json to apply at startup") 44 OptionSpec<Path> profile = parser.accepts("profile", "Profile json to apply at startup").withRequiredArg().withValuesConvertedBy(PathConverter.INSTANCE);
46 .withRequiredArg()
47 .withValuesConvertedBy(PathConverter.INSTANCE);
48 45
49 parser.acceptsAll(List.of("edit-all", "e"), "Enable editing everything"); 46 parser.acceptsAll(List.of("edit-all", "e"), "Enable editing everything");
50 parser.acceptsAll(List.of("no-edit-all", "E"), "Disable editing everything"); 47 parser.acceptsAll(List.of("no-edit-all", "E"), "Disable editing everything");
@@ -78,26 +75,26 @@ public class Main {
78 for (OptionSpec<?> spec : options.specs()) { 75 for (OptionSpec<?> spec : options.specs()) {
79 for (String s : spec.options()) { 76 for (String s : spec.options()) {
80 switch (s) { 77 switch (s) {
81 case "edit-all" -> editables.addAll(List.of(EditableType.values())); 78 case "edit-all" -> editables.addAll(List.of(EditableType.values()));
82 case "no-edit-all" -> editables.clear(); 79 case "no-edit-all" -> editables.clear();
83 case "edit-classes" -> editables.add(EditableType.CLASS); 80 case "edit-classes" -> editables.add(EditableType.CLASS);
84 case "no-edit-classes" -> editables.remove(EditableType.CLASS); 81 case "no-edit-classes" -> editables.remove(EditableType.CLASS);
85 case "edit-methods" -> editables.add(EditableType.METHOD); 82 case "edit-methods" -> editables.add(EditableType.METHOD);
86 case "no-edit-methods" -> editables.remove(EditableType.METHOD); 83 case "no-edit-methods" -> editables.remove(EditableType.METHOD);
87 case "edit-fields" -> editables.add(EditableType.FIELD); 84 case "edit-fields" -> editables.add(EditableType.FIELD);
88 case "no-edit-fields" -> editables.remove(EditableType.FIELD); 85 case "no-edit-fields" -> editables.remove(EditableType.FIELD);
89 case "edit-parameters" -> editables.add(EditableType.PARAMETER); 86 case "edit-parameters" -> editables.add(EditableType.PARAMETER);
90 case "no-edit-parameters" -> editables.remove(EditableType.PARAMETER); 87 case "no-edit-parameters" -> editables.remove(EditableType.PARAMETER);
91 case "edit-locals" -> { 88 case "edit-locals" -> {
92 editables.add(EditableType.LOCAL_VARIABLE); 89 editables.add(EditableType.LOCAL_VARIABLE);
93 System.err.println("warning: --edit-locals has no effect as local variables are currently not editable"); 90 System.err.println("warning: --edit-locals has no effect as local variables are currently not editable");
94 } 91 }
95 case "no-edit-locals" -> { 92 case "no-edit-locals" -> {
96 editables.remove(EditableType.LOCAL_VARIABLE); 93 editables.remove(EditableType.LOCAL_VARIABLE);
97 System.err.println("warning: --no-edit-locals has no effect as local variables are currently not editable"); 94 System.err.println("warning: --no-edit-locals has no effect as local variables are currently not editable");
98 } 95 }
99 case "edit-javadocs" -> editables.add(EditableType.JAVADOC); 96 case "edit-javadocs" -> editables.add(EditableType.JAVADOC);
100 case "no-edit-javadocs" -> editables.remove(EditableType.JAVADOC); 97 case "no-edit-javadocs" -> editables.remove(EditableType.JAVADOC);
101 } 98 }
102 } 99 }
103 } 100 }
@@ -110,7 +107,7 @@ public class Main {
110 107
111 Gui gui = new Gui(parsedProfile, editables); 108 Gui gui = new Gui(parsedProfile, editables);
112 GuiController controller = gui.getController(); 109 GuiController controller = gui.getController();
113 110
114 if (options.has("single-class-tree")) { 111 if (options.has("single-class-tree")) {
115 gui.setSingleClassTree(true); 112 gui.setSingleClassTree(true);
116 } 113 }
@@ -120,6 +117,7 @@ public class Main {
120 CrashDialog.init(gui.getFrame()); 117 CrashDialog.init(gui.getFrame());
121 Thread.setDefaultUncaughtExceptionHandler((thread, t) -> { 118 Thread.setDefaultUncaughtExceptionHandler((thread, t) -> {
122 t.printStackTrace(System.err); 119 t.printStackTrace(System.err);
120
123 if (!ExceptionIgnorer.shouldIgnore(t)) { 121 if (!ExceptionIgnorer.shouldIgnore(t)) {
124 CrashDialog.show(t); 122 CrashDialog.show(t);
125 } 123 }
@@ -128,19 +126,19 @@ public class Main {
128 126
129 if (options.has(jar)) { 127 if (options.has(jar)) {
130 Path jarPath = options.valueOf(jar); 128 Path jarPath = options.valueOf(jar);
131 controller.openJar(jarPath) 129 controller.openJar(jarPath).whenComplete((v, t) -> {
132 .whenComplete((v, t) -> { 130 if (options.has(mappings)) {
133 if (options.has(mappings)) { 131 Path mappingsPath = options.valueOf(mappings);
134 Path mappingsPath = options.valueOf(mappings); 132
135 if (Files.isDirectory(mappingsPath)) { 133 if (Files.isDirectory(mappingsPath)) {
136 controller.openMappings(MappingFormat.ENIGMA_DIRECTORY, mappingsPath); 134 controller.openMappings(MappingFormat.ENIGMA_DIRECTORY, mappingsPath);
137 } else if ("zip".equalsIgnoreCase(MoreFiles.getFileExtension(mappingsPath))) { 135 } else if ("zip".equalsIgnoreCase(MoreFiles.getFileExtension(mappingsPath))) {
138 controller.openMappings(MappingFormat.ENIGMA_ZIP, mappingsPath); 136 controller.openMappings(MappingFormat.ENIGMA_ZIP, mappingsPath);
139 } else { 137 } else {
140 controller.openMappings(MappingFormat.ENIGMA_FILE, mappingsPath); 138 controller.openMappings(MappingFormat.ENIGMA_FILE, mappingsPath);
141 } 139 }
142 } 140 }
143 }); 141 });
144 } 142 }
145 } catch (OptionException e) { 143 } catch (OptionException e) {
146 System.out.println("Invalid arguments: " + e.getMessage()); 144 System.out.println("Invalid arguments: " + e.getMessage());
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/NestedPackages.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/NestedPackages.java
index 309f910..c4541fc 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/NestedPackages.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/NestedPackages.java
@@ -1,21 +1,22 @@
1package cuchaz.enigma.gui; 1package cuchaz.enigma.gui;
2 2
3import cuchaz.enigma.gui.node.ClassSelectorClassNode; 3import java.util.Collection;
4import cuchaz.enigma.gui.node.ClassSelectorPackageNode; 4import java.util.Comparator;
5import cuchaz.enigma.translation.mapping.EntryRemapper; 5import java.util.Enumeration;
6import cuchaz.enigma.translation.representation.entry.ClassEntry; 6import java.util.HashMap;
7import java.util.Map;
7 8
8import javax.swing.tree.DefaultMutableTreeNode; 9import javax.swing.tree.DefaultMutableTreeNode;
9import javax.swing.tree.MutableTreeNode; 10import javax.swing.tree.MutableTreeNode;
10import javax.swing.tree.TreeNode; 11import javax.swing.tree.TreeNode;
11import javax.swing.tree.TreePath; 12import javax.swing.tree.TreePath;
12import java.util.Collection;
13import java.util.Comparator;
14import java.util.HashMap;
15import java.util.Map;
16 13
17public class NestedPackages { 14import cuchaz.enigma.gui.node.ClassSelectorClassNode;
15import cuchaz.enigma.gui.node.ClassSelectorPackageNode;
16import cuchaz.enigma.translation.mapping.EntryRemapper;
17import cuchaz.enigma.translation.representation.entry.ClassEntry;
18 18
19public class NestedPackages {
19 private final DefaultMutableTreeNode root = new DefaultMutableTreeNode(); 20 private final DefaultMutableTreeNode root = new DefaultMutableTreeNode();
20 private final Map<String, DefaultMutableTreeNode> packageToNode = new HashMap<>(); 21 private final Map<String, DefaultMutableTreeNode> packageToNode = new HashMap<>();
21 private final Map<ClassEntry, ClassSelectorClassNode> classToNode = new HashMap<>(); 22 private final Map<ClassEntry, ClassSelectorClassNode> classToNode = new HashMap<>();
@@ -42,20 +43,20 @@ public class NestedPackages {
42 return 0; 43 return 0;
43 }; 44 };
44 45
45 for (var entry : entries) { 46 for (ClassEntry entry : entries) {
46 addEntry(entry); 47 addEntry(entry);
47 } 48 }
48 } 49 }
49 50
50 public void addEntry(ClassEntry entry) { 51 public void addEntry(ClassEntry entry) {
51 var translated = remapper.deobfuscate(entry); 52 ClassEntry translated = remapper.deobfuscate(entry);
52 var me = new ClassSelectorClassNode(entry, translated); 53 var me = new ClassSelectorClassNode(entry, translated);
53 classToNode.put(entry, me); 54 classToNode.put(entry, me);
54 insert(getPackage(translated.getPackageName()), me); 55 insert(getPackage(translated.getPackageName()), me);
55 } 56 }
56 57
57 public DefaultMutableTreeNode getPackage(String packageName) { 58 public DefaultMutableTreeNode getPackage(String packageName) {
58 var node = packageToNode.get(packageName); 59 DefaultMutableTreeNode node = packageToNode.get(packageName);
59 60
60 if (packageName == null) { 61 if (packageName == null) {
61 return root; 62 return root;
@@ -75,7 +76,7 @@ public class NestedPackages {
75 } 76 }
76 77
77 public TreePath getPackagePath(String packageName) { 78 public TreePath getPackagePath(String packageName) {
78 var node = packageToNode.getOrDefault(packageName, root); 79 DefaultMutableTreeNode node = packageToNode.getOrDefault(packageName, root);
79 return new TreePath(node.getPath()); 80 return new TreePath(node.getPath());
80 } 81 }
81 82
@@ -84,15 +85,15 @@ public class NestedPackages {
84 } 85 }
85 86
86 public void removeClassNode(ClassEntry entry) { 87 public void removeClassNode(ClassEntry entry) {
87 var node = classToNode.remove(entry); 88 ClassSelectorClassNode node = classToNode.remove(entry);
88 89
89 if (node != null) { 90 if (node != null) {
90 node.removeFromParent(); 91 node.removeFromParent();
91 // remove dangling packages 92 // remove dangling packages
92 var packageNode = packageToNode.get(entry.getPackageName()); 93 DefaultMutableTreeNode packageNode = packageToNode.get(entry.getPackageName());
93 94
94 while (packageNode != null && packageNode.getChildCount() == 0) { 95 while (packageNode != null && packageNode.getChildCount() == 0) {
95 var theNode = packageNode; 96 DefaultMutableTreeNode theNode = packageNode;
96 packageNode = (DefaultMutableTreeNode) packageNode.getParent(); 97 packageNode = (DefaultMutableTreeNode) packageNode.getParent();
97 theNode.removeFromParent(); 98 theNode.removeFromParent();
98 99
@@ -108,8 +109,8 @@ public class NestedPackages {
108 } 109 }
109 110
110 private void insert(DefaultMutableTreeNode parent, MutableTreeNode child) { 111 private void insert(DefaultMutableTreeNode parent, MutableTreeNode child) {
111 var index = 0; 112 int index = 0;
112 var children = parent.children(); 113 Enumeration<TreeNode> children = parent.children();
113 114
114 while (children.hasMoreElements()) { 115 while (children.hasMoreElements()) {
115 if (comparator.compare(children.nextElement(), child) < 0) { 116 if (comparator.compare(children.nextElement(), child) < 0) {
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/QuickFindAction.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/QuickFindAction.java
index b7fa2eb..ff80e17 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/QuickFindAction.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/QuickFindAction.java
@@ -1,10 +1,11 @@
1package cuchaz.enigma.gui; 1package cuchaz.enigma.gui;
2 2
3import de.sciss.syntaxpane.SyntaxDocument; 3import java.awt.event.ActionEvent;
4import de.sciss.syntaxpane.actions.DefaultSyntaxAction;
5 4
6import javax.swing.text.JTextComponent; 5import javax.swing.text.JTextComponent;
7import java.awt.event.ActionEvent; 6
7import de.sciss.syntaxpane.SyntaxDocument;
8import de.sciss.syntaxpane.actions.DefaultSyntaxAction;
8 9
9public final class QuickFindAction extends DefaultSyntaxAction { 10public final class QuickFindAction extends DefaultSyntaxAction {
10 public QuickFindAction() { 11 public QuickFindAction() {
@@ -26,6 +27,7 @@ public final class QuickFindAction extends DefaultSyntaxAction {
26 27
27 public static Data get(JTextComponent target) { 28 public static Data get(JTextComponent target) {
28 Object o = target.getDocument().getProperty(KEY); 29 Object o = target.getDocument().getProperty(KEY);
30
29 if (o instanceof Data) { 31 if (o instanceof Data) {
30 return (Data) o; 32 return (Data) o;
31 } 33 }
@@ -39,6 +41,7 @@ public final class QuickFindAction extends DefaultSyntaxAction {
39 if (findDialog == null) { 41 if (findDialog == null) {
40 findDialog = new EnigmaQuickFindDialog(target); 42 findDialog = new EnigmaQuickFindDialog(target);
41 } 43 }
44
42 findDialog.showFor(target); 45 findDialog.showFor(target);
43 } 46 }
44 } 47 }
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/ReadableToken.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/ReadableToken.java
index 3e4b30c..eac11ed 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/ReadableToken.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/ReadableToken.java
@@ -1,18 +1,17 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.gui; 12package cuchaz.enigma.gui;
13 13
14public class ReadableToken { 14public class ReadableToken {
15
16 public int line; 15 public int line;
17 public int startColumn; 16 public int startColumn;
18 public int endColumn; 17 public int endColumn;
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java
index 4ef0442..000793e 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java
@@ -1,23 +1,26 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.gui; 12package cuchaz.enigma.gui;
13 13
14import cuchaz.enigma.source.Token; 14import java.awt.Component;
15 15
16import javax.swing.*; 16import javax.swing.DefaultListCellRenderer;
17import java.awt.*; 17import javax.swing.JLabel;
18import javax.swing.JList;
19import javax.swing.ListCellRenderer;
18 20
19public class TokenListCellRenderer implements ListCellRenderer<Token> { 21import cuchaz.enigma.source.Token;
20 22
23public class TokenListCellRenderer implements ListCellRenderer<Token> {
21 private GuiController controller; 24 private GuiController controller;
22 private DefaultListCellRenderer defaultRenderer; 25 private DefaultListCellRenderer defaultRenderer;
23 26
@@ -32,5 +35,4 @@ public class TokenListCellRenderer implements ListCellRenderer<Token> {
32 label.setText(this.controller.getReadableToken(token).toString()); 35 label.setText(this.controller.getReadableToken(token).toString());
33 return label; 36 return label;
34 } 37 }
35
36} 38}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/LookAndFeel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/LookAndFeel.java
index cec3fa1..2088aac 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/LookAndFeel.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/LookAndFeel.java
@@ -40,11 +40,11 @@ public enum LookAndFeel {
40 40
41 try { 41 try {
42 switch (this) { 42 switch (this) {
43 case NONE -> UIManager.setLookAndFeel(NONE_LAF); 43 case NONE -> UIManager.setLookAndFeel(NONE_LAF);
44 case DEFAULT -> UIManager.setLookAndFeel(new FlatLightLaf()); 44 case DEFAULT -> UIManager.setLookAndFeel(new FlatLightLaf());
45 case METAL -> UIManager.setLookAndFeel(new MetalLookAndFeel()); 45 case METAL -> UIManager.setLookAndFeel(new MetalLookAndFeel());
46 case DARCULA -> UIManager.setLookAndFeel(new FlatDarkLaf()); 46 case DARCULA -> UIManager.setLookAndFeel(new FlatDarkLaf());
47 case SYSTEM -> UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 47 case SYSTEM -> UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
48 } 48 }
49 } catch (Exception e) { 49 } catch (Exception e) {
50 throw new Error("Failed to set global look and feel", e); 50 throw new Error("Failed to set global look and feel", e);
@@ -66,5 +66,4 @@ public enum LookAndFeel {
66 int b = (int) (0.3 * c.getRed() + 0.59 * c.getGreen() + 0.11 * c.getBlue()); 66 int b = (int) (0.3 * c.getRed() + 0.59 * c.getGreen() + 0.11 * c.getBlue());
67 return b < 85; 67 return b < 85;
68 } 68 }
69
70} 69}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/NetConfig.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/NetConfig.java
index 4439cb8..eaf20e7 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/NetConfig.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/NetConfig.java
@@ -4,7 +4,6 @@ import cuchaz.enigma.config.ConfigContainer;
4import cuchaz.enigma.network.EnigmaServer; 4import cuchaz.enigma.network.EnigmaServer;
5 5
6public final class NetConfig { 6public final class NetConfig {
7
8 private NetConfig() { 7 private NetConfig() {
9 } 8 }
10 9
@@ -53,5 +52,4 @@ public final class NetConfig {
53 public static void setServerPort(int port) { 52 public static void setServerPort(int port) {
54 cfg.data().section("Server").setInt("Port", port); 53 cfg.data().section("Server").setInt("Port", port);
55 } 54 }
56
57} 55}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/OldConfigImporter.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/OldConfigImporter.java
index 660d231..2e84991 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/OldConfigImporter.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/OldConfigImporter.java
@@ -5,7 +5,6 @@ import java.awt.Font;
5import cuchaz.enigma.gui.config.legacy.Config; 5import cuchaz.enigma.gui.config.legacy.Config;
6 6
7public final class OldConfigImporter { 7public final class OldConfigImporter {
8
9 private OldConfigImporter() { 8 private OldConfigImporter() {
10 } 9 }
11 10
@@ -13,14 +12,15 @@ public final class OldConfigImporter {
13 public static void doImport() { 12 public static void doImport() {
14 if (Config.CONFIG_FILE.exists()) { 13 if (Config.CONFIG_FILE.exists()) {
15 Config config = new Config(); 14 Config config = new Config();
15
16 if (config.editorFont != null) { 16 if (config.editorFont != null) {
17 UiConfig.setEditorFont(Font.decode(config.editorFont)); 17 UiConfig.setEditorFont(Font.decode(config.editorFont));
18 } 18 }
19
19 UiConfig.setLanguage(config.language); 20 UiConfig.setLanguage(config.language);
20 UiConfig.setLookAndFeel(config.lookAndFeel); 21 UiConfig.setLookAndFeel(config.lookAndFeel);
21 UiConfig.setScaleFactor(config.scaleFactor); 22 UiConfig.setScaleFactor(config.scaleFactor);
22 UiConfig.setDecompiler(config.decompiler); 23 UiConfig.setDecompiler(config.decompiler);
23 } 24 }
24 } 25 }
25
26} 26}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Themes.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Themes.java
index 839a5cb..e2db968 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Themes.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Themes.java
@@ -16,7 +16,6 @@ import cuchaz.enigma.gui.util.ScaleUtil;
16import cuchaz.enigma.source.RenamableTokenType; 16import cuchaz.enigma.source.RenamableTokenType;
17 17
18public class Themes { 18public class Themes {
19
20 private static final Set<ThemeChangeListener> listeners = new HashSet<>(); 19 private static final Set<ThemeChangeListener> listeners = new HashSet<>();
21 20
22 // Calling this after the UI is initialized (e.g. when the user changes 21 // Calling this after the UI is initialized (e.g. when the user changes
@@ -87,11 +86,8 @@ public class Themes {
87 } 86 }
88 87
89 public static ImmutableMap<RenamableTokenType, BoxHighlightPainter> getBoxHighlightPainters() { 88 public static ImmutableMap<RenamableTokenType, BoxHighlightPainter> getBoxHighlightPainters() {
90 return ImmutableMap.of( 89 return ImmutableMap.of(RenamableTokenType.OBFUSCATED, BoxHighlightPainter.create(UiConfig.getObfuscatedColor(), UiConfig.getObfuscatedOutlineColor()), RenamableTokenType.PROPOSED, BoxHighlightPainter.create(UiConfig.getProposedColor(), UiConfig.getProposedOutlineColor()),
91 RenamableTokenType.OBFUSCATED, BoxHighlightPainter.create(UiConfig.getObfuscatedColor(), UiConfig.getObfuscatedOutlineColor()), 90 RenamableTokenType.DEOBFUSCATED, BoxHighlightPainter.create(UiConfig.getDeobfuscatedColor(), UiConfig.getDeobfuscatedOutlineColor()));
92 RenamableTokenType.PROPOSED, BoxHighlightPainter.create(UiConfig.getProposedColor(), UiConfig.getProposedOutlineColor()),
93 RenamableTokenType.DEOBFUSCATED, BoxHighlightPainter.create(UiConfig.getDeobfuscatedColor(), UiConfig.getDeobfuscatedOutlineColor())
94 );
95 } 91 }
96 92
97 public static void addListener(ThemeChangeListener listener) { 93 public static void addListener(ThemeChangeListener listener) {
@@ -101,5 +97,4 @@ public class Themes {
101 public static void removeListener(ThemeChangeListener listener) { 97 public static void removeListener(ThemeChangeListener listener) {
102 listeners.remove(listener); 98 listeners.remove(listener);
103 } 99 }
104
105} 100}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java
index 8a10ace..cdf27ca 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java
@@ -1,6 +1,10 @@
1package cuchaz.enigma.gui.config; 1package cuchaz.enigma.gui.config;
2 2
3import java.awt.*; 3import java.awt.Color;
4import java.awt.Dimension;
5import java.awt.Font;
6import java.awt.Point;
7import java.awt.Toolkit;
4import java.util.Optional; 8import java.util.Optional;
5import java.util.OptionalInt; 9import java.util.OptionalInt;
6 10
@@ -10,7 +14,6 @@ import cuchaz.enigma.gui.util.ScaleUtil;
10import cuchaz.enigma.utils.I18n; 14import cuchaz.enigma.utils.I18n;
11 15
12public final class UiConfig { 16public final class UiConfig {
13
14 private UiConfig() { 17 private UiConfig() {
15 } 18 }
16 19
@@ -82,11 +85,11 @@ public final class UiConfig {
82 * @return an integer array composed of these 4 dimensions 85 * @return an integer array composed of these 4 dimensions
83 */ 86 */
84 public static int[] getLayout() { 87 public static int[] getLayout() {
85 return swing.data().section("Main Window").getIntArray("Layout").orElseGet(() -> new int[] { -1, -1, -1, -1 }); 88 return swing.data().section("Main Window").getIntArray("Layout").orElseGet(() -> new int[]{-1, -1, -1, -1});
86 } 89 }
87 90
88 public static void setLayout(int leftV, int left, int right, int rightH) { 91 public static void setLayout(int leftV, int left, int right, int rightH) {
89 swing.data().section("Main Window").setIntArray("Layout", new int[] { leftV, left, right, rightH }); 92 swing.data().section("Main Window").setIntArray("Layout", new int[]{leftV, left, right, rightH});
90 } 93 }
91 94
92 public static LookAndFeel getLookAndFeel() { 95 public static LookAndFeel getLookAndFeel() {
@@ -287,6 +290,7 @@ public final class UiConfig {
287 ConfigSection section = swing.data().section(window); 290 ConfigSection section = swing.data().section(window);
288 OptionalInt width = section.getInt(String.format("Width %s", screenSize.width)); 291 OptionalInt width = section.getInt(String.format("Width %s", screenSize.width));
289 OptionalInt height = section.getInt(String.format("Height %s", screenSize.height)); 292 OptionalInt height = section.getInt(String.format("Height %s", screenSize.height));
293
290 if (width.isPresent() && height.isPresent()) { 294 if (width.isPresent() && height.isPresent()) {
291 return new Dimension(width.getAsInt(), height.getAsInt()); 295 return new Dimension(width.getAsInt(), height.getAsInt());
292 } else { 296 } else {
@@ -306,6 +310,7 @@ public final class UiConfig {
306 ConfigSection section = swing.data().section(window); 310 ConfigSection section = swing.data().section(window);
307 OptionalInt x = section.getInt(String.format("X %s", screenSize.width)); 311 OptionalInt x = section.getInt(String.format("X %s", screenSize.width));
308 OptionalInt y = section.getInt(String.format("Y %s", screenSize.height)); 312 OptionalInt y = section.getInt(String.format("Y %s", screenSize.height));
313
309 if (x.isPresent() && y.isPresent()) { 314 if (x.isPresent() && y.isPresent()) {
310 int ix = x.getAsInt(); 315 int ix = x.getAsInt();
311 int iy = y.getAsInt(); 316 int iy = y.getAsInt();
@@ -354,6 +359,7 @@ public final class UiConfig {
354 359
355 public static void setLookAndFeelDefaults(LookAndFeel laf, boolean isDark) { 360 public static void setLookAndFeelDefaults(LookAndFeel laf, boolean isDark) {
356 ConfigSection s = swing.data().section("Themes").section(laf.name()).section("Colors"); 361 ConfigSection s = swing.data().section("Themes").section(laf.name()).section("Colors");
362
357 if (!isDark) { 363 if (!isDark) {
358 // Defaults found here: https://github.com/Sciss/SyntaxPane/blob/122da367ff7a5d31627a70c62a48a9f0f4f85a0a/src/main/resources/de/sciss/syntaxpane/defaultsyntaxkit/config.properties#L139 364 // Defaults found here: https://github.com/Sciss/SyntaxPane/blob/122da367ff7a5d31627a70c62a48a9f0f4f85a0a/src/main/resources/de/sciss/syntaxpane/defaultsyntaxkit/config.properties#L139
359 s.setIfAbsentRgbColor("Line Numbers Foreground", 0x333300); 365 s.setIfAbsentRgbColor("Line Numbers Foreground", 0x333300);
@@ -412,5 +418,4 @@ public final class UiConfig {
412 s.setIfAbsentRgbColor("Text", 0xF8F8F2); 418 s.setIfAbsentRgbColor("Text", 0xF8F8F2);
413 } 419 }
414 } 420 }
415
416} 421}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/legacy/Config.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/legacy/Config.java
index 1265750..0e8f7da 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/legacy/Config.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/legacy/Config.java
@@ -6,7 +6,15 @@ import java.lang.reflect.Type;
6import java.nio.charset.Charset; 6import java.nio.charset.Charset;
7 7
8import com.google.common.io.Files; 8import com.google.common.io.Files;
9import com.google.gson.*; 9import com.google.gson.Gson;
10import com.google.gson.GsonBuilder;
11import com.google.gson.InstanceCreator;
12import com.google.gson.JsonDeserializationContext;
13import com.google.gson.JsonDeserializer;
14import com.google.gson.JsonElement;
15import com.google.gson.JsonPrimitive;
16import com.google.gson.JsonSerializationContext;
17import com.google.gson.JsonSerializer;
10 18
11import cuchaz.enigma.gui.config.Decompiler; 19import cuchaz.enigma.gui.config.Decompiler;
12import cuchaz.enigma.utils.I18n; 20import cuchaz.enigma.utils.I18n;
@@ -28,7 +36,7 @@ public class Config {
28 } 36 }
29 37
30 Color baseColor = new Color(rgb); 38 Color baseColor = new Color(rgb);
31 return new Color(baseColor.getRed(), baseColor.getGreen(), baseColor.getBlue(), (int)(255 * alpha)); 39 return new Color(baseColor.getRed(), baseColor.getGreen(), baseColor.getBlue(), (int) (255 * alpha));
32 } 40 }
33 } 41 }
34 42
@@ -73,12 +81,7 @@ public class Config {
73 public Decompiler decompiler = Decompiler.CFR; 81 public Decompiler decompiler = Decompiler.CFR;
74 82
75 public Config() { 83 public Config() {
76 gson = new GsonBuilder() 84 gson = new GsonBuilder().registerTypeAdapter(Integer.class, new IntSerializer()).registerTypeAdapter(Integer.class, new IntDeserializer()).registerTypeAdapter(Config.class, (InstanceCreator<Config>) type -> this).setPrettyPrinting().create();
77 .registerTypeAdapter(Integer.class, new IntSerializer())
78 .registerTypeAdapter(Integer.class, new IntDeserializer())
79 .registerTypeAdapter(Config.class, (InstanceCreator<Config>) type -> this)
80 .setPrettyPrinting()
81 .create();
82 this.loadConfig(); 85 this.loadConfig();
83 } 86 }
84 87
@@ -105,5 +108,4 @@ public class Config {
105 return (int) Long.parseLong(json.getAsString().replace("#", ""), 16); 108 return (int) Long.parseLong(json.getAsString().replace("#", ""), 16);
106 } 109 }
107 } 110 }
108
109} 111}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java
index f8922e6..c221120 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.gui.dialog; 12package cuchaz.enigma.gui.dialog;
13 13
@@ -15,7 +15,11 @@ import java.awt.Container;
15import java.awt.GridBagConstraints; 15import java.awt.GridBagConstraints;
16import java.awt.GridBagLayout; 16import java.awt.GridBagLayout;
17 17
18import javax.swing.*; 18import javax.swing.JButton;
19import javax.swing.JDialog;
20import javax.swing.JFrame;
21import javax.swing.JLabel;
22import javax.swing.WindowConstants;
19 23
20import cuchaz.enigma.Enigma; 24import cuchaz.enigma.Enigma;
21import cuchaz.enigma.gui.util.GridBagConstraintsBuilder; 25import cuchaz.enigma.gui.util.GridBagConstraintsBuilder;
@@ -23,16 +27,12 @@ import cuchaz.enigma.gui.util.GuiUtil;
23import cuchaz.enigma.utils.I18n; 27import cuchaz.enigma.utils.I18n;
24 28
25public class AboutDialog { 29public class AboutDialog {
26
27 public static void show(JFrame parent) { 30 public static void show(JFrame parent) {
28 JDialog frame = new JDialog(parent, String.format(I18n.translate("menu.help.about.title"), Enigma.NAME), true); 31 JDialog frame = new JDialog(parent, String.format(I18n.translate("menu.help.about.title"), Enigma.NAME), true);
29 Container pane = frame.getContentPane(); 32 Container pane = frame.getContentPane();
30 pane.setLayout(new GridBagLayout()); 33 pane.setLayout(new GridBagLayout());
31 34
32 GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create() 35 GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2).weight(1.0, 0.0).anchor(GridBagConstraints.WEST);
33 .insets(2)
34 .weight(1.0, 0.0)
35 .anchor(GridBagConstraints.WEST);
36 36
37 JLabel title = new JLabel(Enigma.NAME); 37 JLabel title = new JLabel(Enigma.NAME);
38 title.setFont(title.getFont().deriveFont(title.getFont().getSize2D() * 1.5f)); 38 title.setFont(title.getFont().deriveFont(title.getFont().getSize2D() * 1.5f));
@@ -52,5 +52,4 @@ public class AboutDialog {
52 frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 52 frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
53 frame.setVisible(true); 53 frame.setVisible(true);
54 } 54 }
55
56} 55}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AbstractDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AbstractDialog.java
index c9ca809..76232c4 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AbstractDialog.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AbstractDialog.java
@@ -1,6 +1,12 @@
1package cuchaz.enigma.gui.dialog; 1package cuchaz.enigma.gui.dialog;
2 2
3import java.awt.*; 3import java.awt.BorderLayout;
4import java.awt.Component;
5import java.awt.Container;
6import java.awt.FlowLayout;
7import java.awt.Frame;
8import java.awt.GridBagConstraints;
9import java.awt.GridBagLayout;
4import java.util.List; 10import java.util.List;
5 11
6import javax.swing.JButton; 12import javax.swing.JButton;
@@ -15,7 +21,6 @@ import cuchaz.enigma.utils.Pair;
15import cuchaz.enigma.utils.validation.ValidationContext; 21import cuchaz.enigma.utils.validation.ValidationContext;
16 22
17public abstract class AbstractDialog extends JDialog { 23public abstract class AbstractDialog extends JDialog {
18
19 protected final ValidationContext vc = new ValidationContext(); 24 protected final ValidationContext vc = new ValidationContext();
20 25
21 private boolean actionConfirm = false; 26 private boolean actionConfirm = false;
@@ -38,6 +43,7 @@ public abstract class AbstractDialog extends JDialog {
38 inputContainer.add(label, cb.pos(0, i).weightX(0.0).anchor(GridBagConstraints.LINE_END).fill(GridBagConstraints.NONE).build()); 43 inputContainer.add(label, cb.pos(0, i).weightX(0.0).anchor(GridBagConstraints.LINE_END).fill(GridBagConstraints.NONE).build());
39 inputContainer.add(component, cb.pos(1, i).weightX(1.0).anchor(GridBagConstraints.LINE_END).fill(GridBagConstraints.HORIZONTAL).build()); 44 inputContainer.add(component, cb.pos(1, i).weightX(1.0).anchor(GridBagConstraints.LINE_END).fill(GridBagConstraints.HORIZONTAL).build());
40 } 45 }
46
41 contentPane.add(inputContainer, BorderLayout.CENTER); 47 contentPane.add(inputContainer, BorderLayout.CENTER);
42 Container buttonContainer = new JPanel(new FlowLayout(FlowLayout.RIGHT, ScaleUtil.scale(4), ScaleUtil.scale(4))); 48 Container buttonContainer = new JPanel(new FlowLayout(FlowLayout.RIGHT, ScaleUtil.scale(4), ScaleUtil.scale(4)));
43 JButton connectButton = new JButton(I18n.translate(confirmAction)); 49 JButton connectButton = new JButton(I18n.translate(confirmAction));
@@ -57,6 +63,7 @@ public abstract class AbstractDialog extends JDialog {
57 protected void confirm() { 63 protected void confirm() {
58 vc.reset(); 64 vc.reset();
59 validateInputs(); 65 validateInputs();
66
60 if (vc.canProceed()) { 67 if (vc.canProceed()) {
61 actionConfirm = true; 68 actionConfirm = true;
62 setVisible(false); 69 setVisible(false);
@@ -74,5 +81,4 @@ public abstract class AbstractDialog extends JDialog {
74 81
75 public void validateInputs() { 82 public void validateInputs() {
76 } 83 }
77
78} 84}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ChangeDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ChangeDialog.java
index df65473..51948b5 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ChangeDialog.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ChangeDialog.java
@@ -14,7 +14,6 @@ import javax.swing.JPanel;
14import cuchaz.enigma.utils.I18n; 14import cuchaz.enigma.utils.I18n;
15 15
16public class ChangeDialog { 16public class ChangeDialog {
17
18 public static void show(Window parent) { 17 public static void show(Window parent) {
19 // init frame 18 // init frame
20 JDialog frame = new JDialog(parent, I18n.translate("menu.view.change.title"), Dialog.DEFAULT_MODALITY_TYPE); 19 JDialog frame = new JDialog(parent, I18n.translate("menu.view.change.title"), Dialog.DEFAULT_MODALITY_TYPE);
@@ -48,5 +47,4 @@ public class ChangeDialog {
48 frame.setLocationRelativeTo(parent); 47 frame.setLocationRelativeTo(parent);
49 frame.setVisible(true); 48 frame.setVisible(true);
50 } 49 }
51
52} 50}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ConnectToServerDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ConnectToServerDialog.java
index 2486dfe..1c2bd4c 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ConnectToServerDialog.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ConnectToServerDialog.java
@@ -20,7 +20,6 @@ import cuchaz.enigma.utils.validation.Message;
20import cuchaz.enigma.utils.validation.StandardValidation; 20import cuchaz.enigma.utils.validation.StandardValidation;
21 21
22public class ConnectToServerDialog extends AbstractDialog { 22public class ConnectToServerDialog extends AbstractDialog {
23
24 private JTextField usernameField; 23 private JTextField usernameField;
25 private ValidatableTextField ipField; 24 private ValidatableTextField ipField;
26 private JPasswordField passwordField; 25 private JPasswordField passwordField;
@@ -45,16 +44,13 @@ public class ConnectToServerDialog extends AbstractDialog {
45 ipField.addActionListener(event -> confirm()); 44 ipField.addActionListener(event -> confirm());
46 passwordField.addActionListener(event -> confirm()); 45 passwordField.addActionListener(event -> confirm());
47 46
48 return Arrays.asList( 47 return Arrays.asList(new Pair<>("prompt.connect.username", usernameField), new Pair<>("prompt.connect.address", ipField), new Pair<>("prompt.password", passwordField));
49 new Pair<>("prompt.connect.username", usernameField),
50 new Pair<>("prompt.connect.address", ipField),
51 new Pair<>("prompt.password", passwordField)
52 );
53 } 48 }
54 49
55 @Override 50 @Override
56 public void validateInputs() { 51 public void validateInputs() {
57 vc.setActiveElement(ipField); 52 vc.setActiveElement(ipField);
53
58 if (StandardValidation.notBlank(vc, ipField.getText())) { 54 if (StandardValidation.notBlank(vc, ipField.getText())) {
59 if (ServerAddress.from(ipField.getText(), EnigmaServer.DEFAULT_PORT) == null) { 55 if (ServerAddress.from(ipField.getText(), EnigmaServer.DEFAULT_PORT) == null) {
60 vc.raise(Message.INVALID_IP); 56 vc.raise(Message.INVALID_IP);
@@ -63,16 +59,18 @@ public class ConnectToServerDialog extends AbstractDialog {
63 } 59 }
64 60
65 public Result getResult() { 61 public Result getResult() {
66 if (!isActionConfirm()) return null; 62 if (!isActionConfirm()) {
63 return null;
64 }
65
67 vc.reset(); 66 vc.reset();
68 validateInputs(); 67 validateInputs();
69 if (!vc.canProceed()) return null; 68
70 return new Result( 69 if (!vc.canProceed()) {
71 usernameField.getText(), 70 return null;
72 ipField.getText(), 71 }
73 Objects.requireNonNull(ServerAddress.from(ipField.getText(), EnigmaServer.DEFAULT_PORT)), 72
74 passwordField.getPassword() 73 return new Result(usernameField.getText(), ipField.getText(), Objects.requireNonNull(ServerAddress.from(ipField.getText(), EnigmaServer.DEFAULT_PORT)), passwordField.getPassword());
75 );
76 } 74 }
77 75
78 public static Result show(Frame parent) { 76 public static Result show(Frame parent) {
@@ -114,5 +112,4 @@ public class ConnectToServerDialog extends AbstractDialog {
114 return password; 112 return password;
115 } 113 }
116 } 114 }
117
118} 115}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java
index c2a93fa..a84e977 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java
@@ -1,31 +1,42 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.gui.dialog; 12package cuchaz.enigma.gui.dialog;
13 13
14import java.awt.BorderLayout;
15import java.awt.Container;
16import java.io.File;
17import java.io.FileWriter;
18import java.io.IOException;
19import java.io.PrintWriter;
20import java.io.StringWriter;
21
22import javax.swing.BorderFactory;
23import javax.swing.Box;
24import javax.swing.BoxLayout;
25import javax.swing.JButton;
26import javax.swing.JFileChooser;
27import javax.swing.JFrame;
28import javax.swing.JLabel;
29import javax.swing.JPanel;
30import javax.swing.JScrollPane;
31import javax.swing.JTextArea;
32import javax.swing.WindowConstants;
33
14import cuchaz.enigma.Enigma; 34import cuchaz.enigma.Enigma;
15import cuchaz.enigma.gui.util.GuiUtil; 35import cuchaz.enigma.gui.util.GuiUtil;
16import cuchaz.enigma.utils.I18n;
17import cuchaz.enigma.gui.util.ScaleUtil; 36import cuchaz.enigma.gui.util.ScaleUtil;
18 37import cuchaz.enigma.utils.I18n;
19import javax.swing.*;
20import java.awt.*;
21import java.io.PrintWriter;
22import java.io.StringWriter;
23import java.io.FileWriter;
24import java.io.File;
25import java.io.IOException;
26 38
27public class CrashDialog { 39public class CrashDialog {
28
29 private static CrashDialog instance = null; 40 private static CrashDialog instance = null;
30 41
31 private JFrame frame; 42 private JFrame frame;
@@ -53,6 +64,7 @@ public class CrashDialog {
53 exportButton.addActionListener(event -> { 64 exportButton.addActionListener(event -> {
54 JFileChooser chooser = new JFileChooser(); 65 JFileChooser chooser = new JFileChooser();
55 chooser.setSelectedFile(new File("enigma_crash.log")); 66 chooser.setSelectedFile(new File("enigma_crash.log"));
67
56 if (chooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) { 68 if (chooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) {
57 try { 69 try {
58 File file = chooser.getSelectedFile(); 70 File file = chooser.getSelectedFile();
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CreateServerDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CreateServerDialog.java
index 07daf6d..35999e2 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CreateServerDialog.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CreateServerDialog.java
@@ -16,7 +16,6 @@ import cuchaz.enigma.utils.validation.Message;
16import cuchaz.enigma.utils.validation.StandardValidation; 16import cuchaz.enigma.utils.validation.StandardValidation;
17 17
18public class CreateServerDialog extends AbstractDialog { 18public class CreateServerDialog extends AbstractDialog {
19
20 private ValidatableTextField portField; 19 private ValidatableTextField portField;
21 private ValidatablePasswordField passwordField; 20 private ValidatablePasswordField passwordField;
22 21
@@ -38,10 +37,7 @@ public class CreateServerDialog extends AbstractDialog {
38 portField.addActionListener(event -> confirm()); 37 portField.addActionListener(event -> confirm());
39 passwordField.addActionListener(event -> confirm()); 38 passwordField.addActionListener(event -> confirm());
40 39
41 return Arrays.asList( 40 return Arrays.asList(new Pair<>("prompt.create_server.port", portField), new Pair<>("prompt.password", passwordField));
42 new Pair<>("prompt.create_server.port", portField),
43 new Pair<>("prompt.password", passwordField)
44 );
45 } 41 }
46 42
47 @Override 43 @Override
@@ -49,20 +45,25 @@ public class CreateServerDialog extends AbstractDialog {
49 vc.setActiveElement(portField); 45 vc.setActiveElement(portField);
50 StandardValidation.isIntInRange(vc, portField.getText(), 0, 65535); 46 StandardValidation.isIntInRange(vc, portField.getText(), 0, 65535);
51 vc.setActiveElement(passwordField); 47 vc.setActiveElement(passwordField);
48
52 if (passwordField.getPassword().length > EnigmaServer.MAX_PASSWORD_LENGTH) { 49 if (passwordField.getPassword().length > EnigmaServer.MAX_PASSWORD_LENGTH) {
53 vc.raise(Message.FIELD_LENGTH_OUT_OF_RANGE, EnigmaServer.MAX_PASSWORD_LENGTH); 50 vc.raise(Message.FIELD_LENGTH_OUT_OF_RANGE, EnigmaServer.MAX_PASSWORD_LENGTH);
54 } 51 }
55 } 52 }
56 53
57 public Result getResult() { 54 public Result getResult() {
58 if (!isActionConfirm()) return null; 55 if (!isActionConfirm()) {
56 return null;
57 }
58
59 vc.reset(); 59 vc.reset();
60 validateInputs(); 60 validateInputs();
61 if (!vc.canProceed()) return null; 61
62 return new Result( 62 if (!vc.canProceed()) {
63 Integer.parseInt(portField.getText()), 63 return null;
64 passwordField.getPassword() 64 }
65 ); 65
66 return new Result(Integer.parseInt(portField.getText()), passwordField.getPassword());
66 } 67 }
67 68
68 public static Result show(Frame parent) { 69 public static Result show(Frame parent) {
@@ -92,5 +93,4 @@ public class CreateServerDialog extends AbstractDialog {
92 return password; 93 return password;
93 } 94 }
94 } 95 }
95
96} 96}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/FontDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/FontDialog.java
index 4e02a66..f0bae17 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/FontDialog.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/FontDialog.java
@@ -1,6 +1,11 @@
1package cuchaz.enigma.gui.dialog; 1package cuchaz.enigma.gui.dialog;
2 2
3import java.awt.*; 3import java.awt.Component;
4import java.awt.Container;
5import java.awt.Font;
6import java.awt.Frame;
7import java.awt.GridBagConstraints;
8import java.awt.GridBagLayout;
4import java.util.List; 9import java.util.List;
5 10
6import javax.swing.JButton; 11import javax.swing.JButton;
@@ -16,20 +21,9 @@ import cuchaz.enigma.gui.util.ScaleUtil;
16import cuchaz.enigma.utils.I18n; 21import cuchaz.enigma.utils.I18n;
17 22
18public class FontDialog extends JDialog { 23public class FontDialog extends JDialog {
24 private static final List<String> CATEGORIES = List.of("Default", "Default 2", "Small", "Editor");
19 25
20 private static final List<String> CATEGORIES = List.of( 26 private static final List<String> CATEGORY_TEXTS = List.of("fonts.cat.default", "fonts.cat.default2", "fonts.cat.small", "fonts.cat.editor");
21 "Default",
22 "Default 2",
23 "Small",
24 "Editor"
25 );
26
27 private static final List<String> CATEGORY_TEXTS = List.of(
28 "fonts.cat.default",
29 "fonts.cat.default2",
30 "fonts.cat.small",
31 "fonts.cat.editor"
32 );
33 27
34 private final JList<String> entries = new JList<>(CATEGORY_TEXTS.stream().map(I18n::translate).toArray(String[]::new)); 28 private final JList<String> entries = new JList<>(CATEGORY_TEXTS.stream().map(I18n::translate).toArray(String[]::new));
35 private final FontChooser chooser = new FontChooser(Font.decode(Font.DIALOG)); 29 private final FontChooser chooser = new FontChooser(Font.decode(Font.DIALOG));
@@ -55,8 +49,7 @@ public class FontDialog extends JDialog {
55 Container contentPane = this.getContentPane(); 49 Container contentPane = this.getContentPane();
56 contentPane.setLayout(new GridBagLayout()); 50 contentPane.setLayout(new GridBagLayout());
57 51
58 GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create() 52 GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2);
59 .insets(2);
60 53
61 contentPane.add(this.entries, cb.pos(0, 0).weight(0.0, 1.0).fill(GridBagConstraints.BOTH).build()); 54 contentPane.add(this.entries, cb.pos(0, 0).weight(0.0, 1.0).fill(GridBagConstraints.BOTH).build());
62 contentPane.add(this.chooser, cb.pos(1, 0).weight(1.0, 1.0).fill(GridBagConstraints.BOTH).size(2, 1).build()); 55 contentPane.add(this.chooser, cb.pos(1, 0).weight(1.0, 1.0).fill(GridBagConstraints.BOTH).size(2, 1).build());
@@ -77,6 +70,7 @@ public class FontDialog extends JDialog {
77 private void categoryChanged() { 70 private void categoryChanged() {
78 this.updateUiState(); 71 this.updateUiState();
79 int selectedIndex = this.entries.getSelectedIndex(); 72 int selectedIndex = this.entries.getSelectedIndex();
73
80 if (selectedIndex != -1) { 74 if (selectedIndex != -1) {
81 this.chooser.setSelectedFont(this.fonts[selectedIndex]); 75 this.chooser.setSelectedFont(this.fonts[selectedIndex]);
82 } 76 }
@@ -84,6 +78,7 @@ public class FontDialog extends JDialog {
84 78
85 private void selectedFontChanged() { 79 private void selectedFontChanged() {
86 int selectedIndex = this.entries.getSelectedIndex(); 80 int selectedIndex = this.entries.getSelectedIndex();
81
87 if (selectedIndex != -1) { 82 if (selectedIndex != -1) {
88 this.fonts[selectedIndex] = this.chooser.getSelectedFont(); 83 this.fonts[selectedIndex] = this.chooser.getSelectedFont();
89 } 84 }
@@ -98,6 +93,7 @@ public class FontDialog extends JDialog {
98 for (int i = 0; i < CATEGORIES.size(); i++) { 93 for (int i = 0; i < CATEGORIES.size(); i++) {
99 UiConfig.setFont(CATEGORIES.get(i), this.fonts[i]); 94 UiConfig.setFont(CATEGORIES.get(i), this.fonts[i]);
100 } 95 }
96
101 UiConfig.setUseCustomFonts(this.customCheckBox.isSelected()); 97 UiConfig.setUseCustomFonts(this.customCheckBox.isSelected());
102 UiConfig.save(); 98 UiConfig.save();
103 ChangeDialog.show(this); 99 ChangeDialog.show(this);
@@ -118,8 +114,8 @@ public class FontDialog extends JDialog {
118 for (Component component : ((Container) self).getComponents()) { 114 for (Component component : ((Container) self).getComponents()) {
119 recursiveSetEnabled(component, enabled); 115 recursiveSetEnabled(component, enabled);
120 } 116 }
117
121 self.setEnabled(enabled); 118 self.setEnabled(enabled);
122 } 119 }
123 } 120 }
124
125} 121}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/JavadocDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/JavadocDialog.java
index 9470e11..d6e544d 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/JavadocDialog.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/JavadocDialog.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.gui.dialog; 12package cuchaz.enigma.gui.dialog;
13 13
@@ -18,7 +18,15 @@ import java.awt.FlowLayout;
18import java.awt.event.KeyAdapter; 18import java.awt.event.KeyAdapter;
19import java.awt.event.KeyEvent; 19import java.awt.event.KeyEvent;
20 20
21import javax.swing.*; 21import javax.swing.JButton;
22import javax.swing.JComboBox;
23import javax.swing.JDialog;
24import javax.swing.JFrame;
25import javax.swing.JLabel;
26import javax.swing.JMenuBar;
27import javax.swing.JPanel;
28import javax.swing.JScrollPane;
29import javax.swing.WindowConstants;
22import javax.swing.text.html.HTML; 30import javax.swing.text.html.HTML;
23 31
24import com.google.common.base.Strings; 32import com.google.common.base.Strings;
@@ -35,7 +43,6 @@ import cuchaz.enigma.utils.I18n;
35import cuchaz.enigma.utils.validation.ValidationContext; 43import cuchaz.enigma.utils.validation.ValidationContext;
36 44
37public class JavadocDialog { 45public class JavadocDialog {
38
39 private final JDialog ui; 46 private final JDialog ui;
40 private final GuiController controller; 47 private final GuiController controller;
41 private final Entry<?> entry; 48 private final Entry<?> entry;
@@ -62,19 +69,21 @@ public class JavadocDialog {
62 @Override 69 @Override
63 public void keyPressed(KeyEvent event) { 70 public void keyPressed(KeyEvent event) {
64 switch (event.getKeyCode()) { 71 switch (event.getKeyCode()) {
65 case KeyEvent.VK_ENTER: 72 case KeyEvent.VK_ENTER:
66 if (event.isControlDown()) { 73 if (event.isControlDown()) {
67 doSave(); 74 doSave();
68 if (vc.canProceed()) { 75
69 close(); 76 if (vc.canProceed()) {
70 } 77 close();
71 } 78 }
72 break; 79 }
73 case KeyEvent.VK_ESCAPE: 80
74 close(); 81 break;
75 break; 82 case KeyEvent.VK_ESCAPE:
76 default: 83 close();
77 break; 84 break;
85 default:
86 break;
78 } 87 }
79 } 88 }
80 }); 89 });
@@ -108,6 +117,7 @@ public class JavadocDialog {
108 } else { 117 } else {
109 tagText = tag.getText() + " " + text.getSelectedText(); 118 tagText = tag.getText() + " " + text.getSelectedText();
110 } 119 }
120
111 text.replaceSelection(tagText); 121 text.replaceSelection(tagText);
112 } else { 122 } else {
113 text.insert(tagText, text.getCaretPosition()); 123 text.insert(tagText, text.getCaretPosition());
@@ -116,6 +126,7 @@ public class JavadocDialog {
116 if (tag.isInline()) { 126 if (tag.isInline()) {
117 text.setCaretPosition(text.getCaretPosition() - 1); 127 text.setCaretPosition(text.getCaretPosition() - 1);
118 } 128 }
129
119 text.grabFocus(); 130 text.grabFocus();
120 }); 131 });
121 tagsMenu.add(tagButton); 132 tagsMenu.add(tagButton);
@@ -124,9 +135,11 @@ public class JavadocDialog {
124 // add html tags 135 // add html tags
125 JComboBox<String> htmlList = new JComboBox<String>(); 136 JComboBox<String> htmlList = new JComboBox<String>();
126 htmlList.setPreferredSize(new Dimension()); 137 htmlList.setPreferredSize(new Dimension());
138
127 for (HTML.Tag htmlTag : HTML.getAllTags()) { 139 for (HTML.Tag htmlTag : HTML.getAllTags()) {
128 htmlList.addItem(htmlTag.toString()); 140 htmlList.addItem(htmlTag.toString());
129 } 141 }
142
130 htmlList.addActionListener(action -> { 143 htmlList.addActionListener(action -> {
131 String tagText = "<" + htmlList.getSelectedItem().toString() + ">"; 144 String tagText = "<" + htmlList.getSelectedItem().toString() + ">";
132 text.insert(tagText, text.getCaretPosition()); 145 text.insert(tagText, text.getCaretPosition());
@@ -146,9 +159,17 @@ public class JavadocDialog {
146 public void doSave() { 159 public void doSave() {
147 vc.reset(); 160 vc.reset();
148 validate(); 161 validate();
149 if (!vc.canProceed()) return; 162
163 if (!vc.canProceed()) {
164 return;
165 }
166
150 save(); 167 save();
151 if (!vc.canProceed()) return; 168
169 if (!vc.canProceed()) {
170 return;
171 }
172
152 close(); 173 close();
153 } 174 }
154 175
@@ -189,7 +210,7 @@ public class JavadocDialog {
189 210
190 private boolean inline; 211 private boolean inline;
191 212
192 private JavadocTag(boolean inline) { 213 JavadocTag(boolean inline) {
193 this.inline = inline; 214 this.inline = inline;
194 } 215 }
195 216
@@ -201,5 +222,4 @@ public class JavadocDialog {
201 return this.inline; 222 return this.inline;
202 } 223 }
203 } 224 }
204
205} 225}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java
index d76ddea..3beae21 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.gui.dialog; 12package cuchaz.enigma.gui.dialog;
13 13
@@ -17,7 +17,12 @@ import java.awt.GridBagConstraints;
17import java.awt.GridBagLayout; 17import java.awt.GridBagLayout;
18import java.util.concurrent.CompletableFuture; 18import java.util.concurrent.CompletableFuture;
19 19
20import javax.swing.*; 20import javax.swing.JDialog;
21import javax.swing.JFrame;
22import javax.swing.JLabel;
23import javax.swing.JProgressBar;
24import javax.swing.SwingUtilities;
25import javax.swing.WindowConstants;
21 26
22import cuchaz.enigma.Enigma; 27import cuchaz.enigma.Enigma;
23import cuchaz.enigma.ProgressListener; 28import cuchaz.enigma.ProgressListener;
@@ -27,7 +32,6 @@ import cuchaz.enigma.gui.util.ScaleUtil;
27import cuchaz.enigma.utils.I18n; 32import cuchaz.enigma.utils.I18n;
28 33
29public class ProgressDialog implements ProgressListener, AutoCloseable { 34public class ProgressDialog implements ProgressListener, AutoCloseable {
30
31 private final JDialog dialog; 35 private final JDialog dialog;
32 private final JLabel labelTitle = new JLabel(); 36 private final JLabel labelTitle = new JLabel();
33 private final JLabel labelText = GuiUtil.unboldLabel(new JLabel()); 37 private final JLabel labelText = GuiUtil.unboldLabel(new JLabel());
@@ -39,11 +43,7 @@ public class ProgressDialog implements ProgressListener, AutoCloseable {
39 Container pane = this.dialog.getContentPane(); 43 Container pane = this.dialog.getContentPane();
40 pane.setLayout(new GridBagLayout()); 44 pane.setLayout(new GridBagLayout());
41 45
42 GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create() 46 GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2).anchor(GridBagConstraints.WEST).fill(GridBagConstraints.BOTH).weight(1.0, 0.0);
43 .insets(2)
44 .anchor(GridBagConstraints.WEST)
45 .fill(GridBagConstraints.BOTH)
46 .weight(1.0, 0.0);
47 47
48 pane.add(this.labelTitle, cb.pos(0, 0).build()); 48 pane.add(this.labelTitle, cb.pos(0, 0).build());
49 pane.add(this.labelText, cb.pos(0, 1).build()); 49 pane.add(this.labelText, cb.pos(0, 1).build());
@@ -121,6 +121,7 @@ public class ProgressDialog implements ProgressListener, AutoCloseable {
121 public void step(int numDone, String message) { 121 public void step(int numDone, String message) {
122 SwingUtilities.invokeLater(() -> { 122 SwingUtilities.invokeLater(() -> {
123 this.labelText.setText(message); 123 this.labelText.setText(message);
124
124 if (numDone != -1) { 125 if (numDone != -1) {
125 this.progress.setValue(numDone); 126 this.progress.setValue(numDone);
126 this.progress.setIndeterminate(false); 127 this.progress.setIndeterminate(false);
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java
index e65b661..7814dd8 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.gui.dialog; 12package cuchaz.enigma.gui.dialog;
13 13
@@ -15,17 +15,36 @@ import java.awt.BorderLayout;
15import java.awt.Color; 15import java.awt.Color;
16import java.awt.FlowLayout; 16import java.awt.FlowLayout;
17import java.awt.Font; 17import java.awt.Font;
18import java.awt.event.*; 18import java.awt.event.ComponentAdapter;
19import java.util.*; 19import java.awt.event.ComponentEvent;
20import java.awt.event.KeyAdapter;
21import java.awt.event.KeyEvent;
22import java.awt.event.MouseAdapter;
23import java.awt.event.MouseEvent;
24import java.util.Arrays;
25import java.util.Collections;
26import java.util.List;
27import java.util.Queue;
20import java.util.concurrent.ConcurrentLinkedQueue; 28import java.util.concurrent.ConcurrentLinkedQueue;
21 29
22import javax.swing.*; 30import javax.swing.DefaultListModel;
31import javax.swing.JButton;
32import javax.swing.JDialog;
33import javax.swing.JLabel;
34import javax.swing.JList;
35import javax.swing.JPanel;
36import javax.swing.JScrollPane;
37import javax.swing.JTextField;
38import javax.swing.ListSelectionModel;
39import javax.swing.SwingUtilities;
23import javax.swing.event.DocumentEvent; 40import javax.swing.event.DocumentEvent;
24import javax.swing.event.DocumentListener; 41import javax.swing.event.DocumentListener;
25 42
26import cuchaz.enigma.analysis.index.EntryIndex; 43import cuchaz.enigma.analysis.index.EntryIndex;
27import cuchaz.enigma.gui.Gui; 44import cuchaz.enigma.gui.Gui;
28import cuchaz.enigma.gui.GuiController; 45import cuchaz.enigma.gui.GuiController;
46import cuchaz.enigma.gui.search.SearchEntry;
47import cuchaz.enigma.gui.search.SearchUtil;
29import cuchaz.enigma.gui.util.AbstractListCellRenderer; 48import cuchaz.enigma.gui.util.AbstractListCellRenderer;
30import cuchaz.enigma.gui.util.GuiUtil; 49import cuchaz.enigma.gui.util.GuiUtil;
31import cuchaz.enigma.gui.util.ScaleUtil; 50import cuchaz.enigma.gui.util.ScaleUtil;
@@ -34,11 +53,8 @@ import cuchaz.enigma.translation.representation.entry.FieldEntry;
34import cuchaz.enigma.translation.representation.entry.MethodEntry; 53import cuchaz.enigma.translation.representation.entry.MethodEntry;
35import cuchaz.enigma.translation.representation.entry.ParentedEntry; 54import cuchaz.enigma.translation.representation.entry.ParentedEntry;
36import cuchaz.enigma.utils.I18n; 55import cuchaz.enigma.utils.I18n;
37import cuchaz.enigma.gui.search.SearchEntry;
38import cuchaz.enigma.gui.search.SearchUtil;
39 56
40public class SearchDialog { 57public class SearchDialog {
41
42 private final JTextField searchField; 58 private final JTextField searchField;
43 private DefaultListModel<SearchEntryImpl> classListModel; 59 private DefaultListModel<SearchEntryImpl> classListModel;
44 private final JList<SearchEntryImpl> classList; 60 private final JList<SearchEntryImpl> classList;
@@ -60,7 +76,6 @@ public class SearchDialog {
60 76
61 searchField = new JTextField(); 77 searchField = new JTextField();
62 searchField.getDocument().addDocumentListener(new DocumentListener() { 78 searchField.getDocument().addDocumentListener(new DocumentListener() {
63
64 @Override 79 @Override
65 public void insertUpdate(DocumentEvent e) { 80 public void insertUpdate(DocumentEvent e) {
66 updateList(); 81 updateList();
@@ -75,7 +90,6 @@ public class SearchDialog {
75 public void changedUpdate(DocumentEvent e) { 90 public void changedUpdate(DocumentEvent e) {
76 updateList(); 91 updateList();
77 } 92 }
78
79 }); 93 });
80 searchField.addKeyListener(new KeyAdapter() { 94 searchField.addKeyListener(new KeyAdapter() {
81 @Override 95 @Override
@@ -143,23 +157,9 @@ public class SearchDialog {
143 final EntryIndex entryIndex = parent.getController().project.getJarIndex().getEntryIndex(); 157 final EntryIndex entryIndex = parent.getController().project.getJarIndex().getEntryIndex();
144 158
145 switch (type) { 159 switch (type) {
146 case CLASS -> entryIndex.getClasses().parallelStream() 160 case CLASS -> entryIndex.getClasses().parallelStream().filter(e -> !e.isInnerClass()).map(e -> SearchEntryImpl.from(e, parent.getController())).map(SearchUtil.Entry::from).sequential().forEach(su::add);
147 .filter(e -> !e.isInnerClass()) 161 case METHOD -> entryIndex.getMethods().parallelStream().filter(e -> !e.isConstructor() && !entryIndex.getMethodAccess(e).isSynthetic()).map(e -> SearchEntryImpl.from(e, parent.getController())).map(SearchUtil.Entry::from).sequential().forEach(su::add);
148 .map(e -> SearchEntryImpl.from(e, parent.getController())) 162 case FIELD -> entryIndex.getFields().parallelStream().map(e -> SearchEntryImpl.from(e, parent.getController())).map(SearchUtil.Entry::from).sequential().forEach(su::add);
149 .map(SearchUtil.Entry::from)
150 .sequential()
151 .forEach(su::add);
152 case METHOD -> entryIndex.getMethods().parallelStream()
153 .filter(e -> !e.isConstructor() && !entryIndex.getMethodAccess(e).isSynthetic())
154 .map(e -> SearchEntryImpl.from(e, parent.getController()))
155 .map(SearchUtil.Entry::from)
156 .sequential()
157 .forEach(su::add);
158 case FIELD -> entryIndex.getFields().parallelStream()
159 .map(e -> SearchEntryImpl.from(e, parent.getController()))
160 .map(SearchUtil.Entry::from)
161 .sequential()
162 .forEach(su::add);
163 } 163 }
164 164
165 updateList(); 165 updateList();
@@ -172,6 +172,7 @@ public class SearchDialog {
172 172
173 private void openSelected() { 173 private void openSelected() {
174 SearchEntryImpl selectedValue = classList.getSelectedValue(); 174 SearchEntryImpl selectedValue = classList.getSelectedValue();
175
175 if (selectedValue != null) { 176 if (selectedValue != null) {
176 openEntry(selectedValue); 177 openEntry(selectedValue);
177 } 178 }
@@ -181,6 +182,7 @@ public class SearchDialog {
181 close(); 182 close();
182 su.hit(e); 183 su.hit(e);
183 parent.getController().navigateTo(e.obf); 184 parent.getController().navigateTo(e.obf);
185
184 if (e.obf instanceof ClassEntry) { 186 if (e.obf instanceof ClassEntry) {
185 if (e.deobf != null) { 187 if (e.deobf != null) {
186 parent.getDeobfPanel().deobfClasses.setSelectionClass((ClassEntry) e.deobf); 188 parent.getDeobfPanel().deobfClasses.setSelectionClass((ClassEntry) e.deobf);
@@ -202,7 +204,9 @@ public class SearchDialog {
202 204
203 // Updates the list of class names 205 // Updates the list of class names
204 private void updateList() { 206 private void updateList() {
205 if (currentSearch != null) currentSearch.stop(); 207 if (currentSearch != null) {
208 currentSearch.stop();
209 }
206 210
207 DefaultListModel<SearchEntryImpl> classListModel = new DefaultListModel<>(); 211 DefaultListModel<SearchEntryImpl> classListModel = new DefaultListModel<>();
208 this.classListModel = classListModel; 212 this.classListModel = classListModel;
@@ -210,7 +214,9 @@ public class SearchDialog {
210 214
211 // handle these search result like minecraft scheduled tasks to prevent 215 // handle these search result like minecraft scheduled tasks to prevent
212 // flooding swing buttons inputs etc with tons of (possibly outdated) invocations 216 // flooding swing buttons inputs etc with tons of (possibly outdated) invocations
213 record Order(int idx, SearchEntryImpl e) {} 217 record Order(int idx, SearchEntryImpl e) {
218 }
219
214 Queue<Order> queue = new ConcurrentLinkedQueue<>(); 220 Queue<Order> queue = new ConcurrentLinkedQueue<>();
215 Runnable updater = new Runnable() { 221 Runnable updater = new Runnable() {
216 @Override 222 @Override
@@ -221,8 +227,9 @@ public class SearchDialog {
221 227
222 // too large count may increase delay for key and input handling, etc. 228 // too large count may increase delay for key and input handling, etc.
223 int count = 100; 229 int count = 100;
230
224 while (count > 0 && !queue.isEmpty()) { 231 while (count > 0 && !queue.isEmpty()) {
225 var o = queue.remove(); 232 Order o = queue.remove();
226 classListModel.insertElementAt(o.e, o.idx); 233 classListModel.insertElementAt(o.e, o.idx);
227 count--; 234 count--;
228 } 235 }
@@ -240,7 +247,6 @@ public class SearchDialog {
240 } 247 }
241 248
242 private static final class SearchEntryImpl implements SearchEntry { 249 private static final class SearchEntryImpl implements SearchEntry {
243
244 public final ParentedEntry<?> obf; 250 public final ParentedEntry<?> obf;
245 public final ParentedEntry<?> deobf; 251 public final ParentedEntry<?> deobf;
246 252
@@ -270,10 +276,13 @@ public class SearchDialog {
270 276
271 public static SearchEntryImpl from(ParentedEntry<?> e, GuiController controller) { 277 public static SearchEntryImpl from(ParentedEntry<?> e, GuiController controller) {
272 ParentedEntry<?> deobf = controller.project.getMapper().deobfuscate(e); 278 ParentedEntry<?> deobf = controller.project.getMapper().deobfuscate(e);
273 if (deobf.equals(e)) deobf = null; 279
280 if (deobf.equals(e)) {
281 deobf = null;
282 }
283
274 return new SearchEntryImpl(e, deobf); 284 return new SearchEntryImpl(e, deobf);
275 } 285 }
276
277 } 286 }
278 287
279 private static final class ListCellRendererImpl extends AbstractListCellRenderer<SearchEntryImpl> { 288 private static final class ListCellRendererImpl extends AbstractListCellRenderer<SearchEntryImpl> {
@@ -281,7 +290,7 @@ public class SearchDialog {
281 private final JLabel mainName; 290 private final JLabel mainName;
282 private final JLabel secondaryName; 291 private final JLabel secondaryName;
283 292
284 public ListCellRendererImpl(Gui gui) { 293 ListCellRendererImpl(Gui gui) {
285 this.setLayout(new BorderLayout()); 294 this.setLayout(new BorderLayout());
286 this.gui = gui; 295 this.gui = gui;
287 296
@@ -316,7 +325,6 @@ public class SearchDialog {
316 mainName.setIcon(GuiUtil.FIELD_ICON); 325 mainName.setIcon(GuiUtil.FIELD_ICON);
317 } 326 }
318 } 327 }
319
320 } 328 }
321 329
322 public enum Type { 330 public enum Type {
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/StatsDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/StatsDialog.java
index 0398093..1ab66ef 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/StatsDialog.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/StatsDialog.java
@@ -4,10 +4,19 @@ import java.awt.Container;
4import java.awt.Dimension; 4import java.awt.Dimension;
5import java.awt.GridBagConstraints; 5import java.awt.GridBagConstraints;
6import java.awt.GridBagLayout; 6import java.awt.GridBagLayout;
7import java.util.*; 7import java.util.Collections;
8import java.util.HashMap;
9import java.util.Locale;
10import java.util.Map;
11import java.util.Set;
8import java.util.stream.Collectors; 12import java.util.stream.Collectors;
9 13
10import javax.swing.*; 14import javax.swing.JButton;
15import javax.swing.JCheckBox;
16import javax.swing.JDialog;
17import javax.swing.JLabel;
18import javax.swing.JTextField;
19import javax.swing.SwingUtilities;
11 20
12import cuchaz.enigma.gui.Gui; 21import cuchaz.enigma.gui.Gui;
13import cuchaz.enigma.gui.config.UiConfig; 22import cuchaz.enigma.gui.config.UiConfig;
@@ -19,14 +28,15 @@ import cuchaz.enigma.gui.util.ScaleUtil;
19import cuchaz.enigma.utils.I18n; 28import cuchaz.enigma.utils.I18n;
20 29
21public class StatsDialog { 30public class StatsDialog {
22
23 public static void show(Gui gui) { 31 public static void show(Gui gui) {
24 ProgressDialog.runOffThread(gui.getFrame(), listener -> { 32 ProgressDialog.runOffThread(gui.getFrame(), listener -> {
25 final StatsGenerator statsGenerator = new StatsGenerator(gui.getController().project); 33 final StatsGenerator statsGenerator = new StatsGenerator(gui.getController().project);
26 final Map<StatsMember, StatsResult> results = new HashMap<>(); 34 final Map<StatsMember, StatsResult> results = new HashMap<>();
35
27 for (StatsMember member : StatsMember.values()) { 36 for (StatsMember member : StatsMember.values()) {
28 results.put(member, statsGenerator.generate(listener, Collections.singleton(member), "", false)); 37 results.put(member, statsGenerator.generate(listener, Collections.singleton(member), "", false));
29 } 38 }
39
30 SwingUtilities.invokeLater(() -> show(gui, results)); 40 SwingUtilities.invokeLater(() -> show(gui, results));
31 }); 41 });
32 } 42 }
@@ -111,12 +121,7 @@ public class StatsDialog {
111 121
112 private static void generateStats(Gui gui, Map<StatsMember, JCheckBox> checkboxes, String topLevelPackage, boolean includeSynthetic) { 122 private static void generateStats(Gui gui, Map<StatsMember, JCheckBox> checkboxes, String topLevelPackage, boolean includeSynthetic) {
113 // get members from selected checkboxes 123 // get members from selected checkboxes
114 Set<StatsMember> includedMembers = checkboxes 124 Set<StatsMember> includedMembers = checkboxes.entrySet().stream().filter(entry -> entry.getValue().isSelected()).map(Map.Entry::getKey).collect(Collectors.toSet());
115 .entrySet()
116 .stream()
117 .filter(entry -> entry.getValue().isSelected())
118 .map(Map.Entry::getKey)
119 .collect(Collectors.toSet());
120 125
121 // checks if a project is open 126 // checks if a project is open
122 if (gui.getController().project != null) { 127 if (gui.getController().project != null) {
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/AbstractInheritanceTree.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/AbstractInheritanceTree.java
index 39aa212..3f1625d 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/AbstractInheritanceTree.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/AbstractInheritanceTree.java
@@ -7,7 +7,11 @@ import javax.annotation.Nullable;
7import javax.swing.JPanel; 7import javax.swing.JPanel;
8import javax.swing.JScrollPane; 8import javax.swing.JScrollPane;
9import javax.swing.JTree; 9import javax.swing.JTree;
10import javax.swing.tree.*; 10import javax.swing.tree.DefaultMutableTreeNode;
11import javax.swing.tree.DefaultTreeModel;
12import javax.swing.tree.TreeCellRenderer;
13import javax.swing.tree.TreeNode;
14import javax.swing.tree.TreePath;
11 15
12import cuchaz.enigma.analysis.ClassInheritanceTreeNode; 16import cuchaz.enigma.analysis.ClassInheritanceTreeNode;
13import cuchaz.enigma.analysis.MethodInheritanceTreeNode; 17import cuchaz.enigma.analysis.MethodInheritanceTreeNode;
@@ -40,11 +44,13 @@ public abstract class AbstractInheritanceTree {
40 if (event.getClickCount() >= 2 && event.getButton() == MouseEvent.BUTTON1) { 44 if (event.getClickCount() >= 2 && event.getButton() == MouseEvent.BUTTON1) {
41 // get the selected node 45 // get the selected node
42 TreePath path = tree.getSelectionPath(); 46 TreePath path = tree.getSelectionPath();
47
43 if (path == null) { 48 if (path == null) {
44 return; 49 return;
45 } 50 }
46 51
47 Object node = path.getLastPathComponent(); 52 Object node = path.getLastPathComponent();
53
48 if (node instanceof ClassInheritanceTreeNode classNode) { 54 if (node instanceof ClassInheritanceTreeNode classNode) {
49 gui.getController().navigateTo(new ClassEntry(classNode.getObfClassName())); 55 gui.getController().navigateTo(new ClassEntry(classNode.getObfClassName()));
50 } else if (node instanceof MethodInheritanceTreeNode methodNode) { 56 } else if (node instanceof MethodInheritanceTreeNode methodNode) {
@@ -72,7 +78,6 @@ public abstract class AbstractInheritanceTree {
72 } 78 }
73 79
74 public void retranslateUi() { 80 public void retranslateUi() {
75
76 } 81 }
77 82
78 @Nullable 83 @Nullable
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CallsTree.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CallsTree.java
index c92534f..5671188 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CallsTree.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CallsTree.java
@@ -5,7 +5,12 @@ import java.awt.event.MouseEvent;
5import java.util.Collection; 5import java.util.Collection;
6import java.util.Vector; 6import java.util.Vector;
7 7
8import javax.swing.*; 8import javax.swing.JList;
9import javax.swing.JPanel;
10import javax.swing.JScrollPane;
11import javax.swing.JSplitPane;
12import javax.swing.JTree;
13import javax.swing.ListSelectionModel;
9import javax.swing.tree.DefaultTreeModel; 14import javax.swing.tree.DefaultTreeModel;
10import javax.swing.tree.TreeNode; 15import javax.swing.tree.TreeNode;
11import javax.swing.tree.TreePath; 16import javax.swing.tree.TreePath;
@@ -47,12 +52,7 @@ public class CallsTree {
47 this.tokens.setPreferredSize(ScaleUtil.getDimension(0, 200)); 52 this.tokens.setPreferredSize(ScaleUtil.getDimension(0, 200));
48 this.tokens.setMinimumSize(ScaleUtil.getDimension(0, 200)); 53 this.tokens.setMinimumSize(ScaleUtil.getDimension(0, 200));
49 54
50 JSplitPane contentPane = new JSplitPane( 55 JSplitPane contentPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, new JScrollPane(this.callsTree), new JScrollPane(this.tokens));
51 JSplitPane.VERTICAL_SPLIT,
52 true,
53 new JScrollPane(this.callsTree),
54 new JScrollPane(this.tokens)
55 );
56 56
57 contentPane.setResizeWeight(1); // let the top side take all the slack 57 contentPane.setResizeWeight(1); // let the top side take all the slack
58 contentPane.resetToPreferredSizes(); 58 contentPane.resetToPreferredSizes();
@@ -109,6 +109,7 @@ public class CallsTree {
109 private void onTokenClicked(MouseEvent event) { 109 private void onTokenClicked(MouseEvent event) {
110 if (event.getClickCount() == 2) { 110 if (event.getClickCount() == 2) {
111 Token selected = this.tokens.getSelectedValue(); 111 Token selected = this.tokens.getSelectedValue();
112
112 if (selected != null) { 113 if (selected != null) {
113 this.gui.openClass(this.gui.getController().getTokenHandle().getRef()).navigateToToken(selected); 114 this.gui.openClass(this.gui.getController().getTokenHandle().getRef()).navigateToToken(selected);
114 } 115 }
@@ -116,7 +117,6 @@ public class CallsTree {
116 } 117 }
117 118
118 public void retranslateUi() { 119 public void retranslateUi() {
119
120 } 120 }
121 121
122 public JPanel getPanel() { 122 public JPanel getPanel() {
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CollapsibleTabbedPane.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CollapsibleTabbedPane.java
index fb497b1..e05ab45 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CollapsibleTabbedPane.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CollapsibleTabbedPane.java
@@ -5,7 +5,6 @@ import java.awt.event.MouseEvent;
5import javax.swing.JTabbedPane; 5import javax.swing.JTabbedPane;
6 6
7public class CollapsibleTabbedPane extends JTabbedPane { 7public class CollapsibleTabbedPane extends JTabbedPane {
8
9 public CollapsibleTabbedPane() { 8 public CollapsibleTabbedPane() {
10 } 9 }
11 10
@@ -20,9 +19,14 @@ public class CollapsibleTabbedPane extends JTabbedPane {
20 @Override 19 @Override
21 protected void processMouseEvent(MouseEvent e) { 20 protected void processMouseEvent(MouseEvent e) {
22 int id = e.getID(); 21 int id = e.getID();
22
23 if (id == MouseEvent.MOUSE_PRESSED) { 23 if (id == MouseEvent.MOUSE_PRESSED) {
24 if (!isEnabled()) return; 24 if (!isEnabled()) {
25 return;
26 }
27
25 int tabIndex = getUI().tabForCoordinate(this, e.getX(), e.getY()); 28 int tabIndex = getUI().tabForCoordinate(this, e.getX(), e.getY());
29
26 if (tabIndex >= 0 && isEnabledAt(tabIndex)) { 30 if (tabIndex >= 0 && isEnabledAt(tabIndex)) {
27 if (tabIndex == getSelectedIndex()) { 31 if (tabIndex == getSelectedIndex()) {
28 if (isFocusOwner() && isRequestFocusEnabled()) { 32 if (isFocusOwner() && isRequestFocusEnabled()) {
@@ -30,11 +34,12 @@ public class CollapsibleTabbedPane extends JTabbedPane {
30 } else { 34 } else {
31 setSelectedIndex(-1); 35 setSelectedIndex(-1);
32 } 36 }
37
33 return; 38 return;
34 } 39 }
35 } 40 }
36 } 41 }
42
37 super.processMouseEvent(e); 43 super.processMouseEvent(e);
38 } 44 }
39
40} 45}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ConvertingTextField.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ConvertingTextField.java
index 9a6ea09..301ae7f 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ConvertingTextField.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ConvertingTextField.java
@@ -1,7 +1,12 @@
1package cuchaz.enigma.gui.elements; 1package cuchaz.enigma.gui.elements;
2 2
3import java.awt.GridLayout; 3import java.awt.GridLayout;
4import java.awt.event.*; 4import java.awt.event.FocusAdapter;
5import java.awt.event.FocusEvent;
6import java.awt.event.KeyAdapter;
7import java.awt.event.KeyEvent;
8import java.awt.event.MouseAdapter;
9import java.awt.event.MouseEvent;
5import java.util.HashSet; 10import java.util.HashSet;
6import java.util.Set; 11import java.util.Set;
7 12
@@ -21,7 +26,6 @@ import cuchaz.enigma.utils.validation.Validatable;
21 * A label that converts into an editable text field when you click it. 26 * A label that converts into an editable text field when you click it.
22 */ 27 */
23public class ConvertingTextField implements Validatable { 28public class ConvertingTextField implements Validatable {
24
25 private final JPanel ui; 29 private final JPanel ui;
26 private final ValidatableTextField textField; 30 private final ValidatableTextField textField;
27 private final JLabel label; 31 private final JLabel label;
@@ -69,7 +73,9 @@ public class ConvertingTextField implements Validatable {
69 } 73 }
70 74
71 public void startEditing() { 75 public void startEditing() {
72 if (this.editing || !this.editable) return; 76 if (this.editing || !this.editable) {
77 return;
78 }
73 79
74 this.ui.removeAll(); 80 this.ui.removeAll();
75 this.ui.add(this.textField); 81 this.ui.add(this.textField);
@@ -82,9 +88,13 @@ public class ConvertingTextField implements Validatable {
82 } 88 }
83 89
84 public void stopEditing(boolean abort) { 90 public void stopEditing(boolean abort) {
85 if (!editing) return; 91 if (!editing) {
92 return;
93 }
86 94
87 if (!listeners.stream().allMatch(l -> l.tryStopEditing(this, abort))) return; 95 if (!listeners.stream().allMatch(l -> l.tryStopEditing(this, abort))) {
96 return;
97 }
88 98
89 if (abort) { 99 if (abort) {
90 this.textField.setText(this.label.getText()); 100 this.textField.setText(this.label.getText());
@@ -107,7 +117,9 @@ public class ConvertingTextField implements Validatable {
107 } 117 }
108 118
109 public void setEditText(String text) { 119 public void setEditText(String text) {
110 if (!editing) return; 120 if (!editing) {
121 return;
122 }
111 123
112 this.textField.setText(text); 124 this.textField.setText(text);
113 } 125 }
@@ -122,22 +134,29 @@ public class ConvertingTextField implements Validatable {
122 } 134 }
123 135
124 public void selectAll() { 136 public void selectAll() {
125 if (!editing) return; 137 if (!editing) {
138 return;
139 }
126 140
127 this.textField.selectAll(); 141 this.textField.selectAll();
128 } 142 }
129 143
130 public void selectSubstring(int startIndex) { 144 public void selectSubstring(int startIndex) {
131 if (!editing) return; 145 if (!editing) {
146 return;
147 }
132 148
133 Document doc = this.textField.getDocument(); 149 Document doc = this.textField.getDocument();
150
134 if (doc != null) { 151 if (doc != null) {
135 this.selectSubstring(startIndex, doc.getLength()); 152 this.selectSubstring(startIndex, doc.getLength());
136 } 153 }
137 } 154 }
138 155
139 public void selectSubstring(int startIndex, int endIndex) { 156 public void selectSubstring(int startIndex, int endIndex) {
140 if (!editing) return; 157 if (!editing) {
158 return;
159 }
141 160
142 this.textField.select(startIndex, endIndex); 161 this.textField.select(startIndex, endIndex);
143 } 162 }
@@ -155,7 +174,10 @@ public class ConvertingTextField implements Validatable {
155 } 174 }
156 175
157 public boolean hasChanges() { 176 public boolean hasChanges() {
158 if (!editing) return false; 177 if (!editing) {
178 return false;
179 }
180
159 return !this.textField.getText().equals(this.label.getText()); 181 return !this.textField.getText().equals(this.label.getText());
160 } 182 }
161 183
@@ -180,5 +202,4 @@ public class ConvertingTextField implements Validatable {
180 public JPanel getUi() { 202 public JPanel getUi() {
181 return ui; 203 return ui;
182 } 204 }
183
184} 205}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/DeobfPanelPopupMenu.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/DeobfPanelPopupMenu.java
index 0b44881..bcc6dc6 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/DeobfPanelPopupMenu.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/DeobfPanelPopupMenu.java
@@ -9,55 +9,54 @@ import cuchaz.enigma.gui.panels.DeobfPanel;
9import cuchaz.enigma.utils.I18n; 9import cuchaz.enigma.utils.I18n;
10 10
11public class DeobfPanelPopupMenu { 11public class DeobfPanelPopupMenu {
12 12 private final JPopupMenu ui;
13 private final JPopupMenu ui; 13 private final JMenuItem renamePackage = new JMenuItem();
14 private final JMenuItem renamePackage = new JMenuItem(); 14 private final JMenuItem renameClass = new JMenuItem();
15 private final JMenuItem renameClass = new JMenuItem(); 15 private final JMenuItem expandAll = new JMenuItem();
16 private final JMenuItem expandAll = new JMenuItem(); 16 private final JMenuItem collapseAll = new JMenuItem();
17 private final JMenuItem collapseAll = new JMenuItem(); 17
18 18 public DeobfPanelPopupMenu(DeobfPanel panel) {
19 public DeobfPanelPopupMenu(DeobfPanel panel) { 19 this.ui = new JPopupMenu();
20 this.ui = new JPopupMenu(); 20
21 21 this.ui.add(this.renamePackage);
22 this.ui.add(this.renamePackage); 22 this.ui.add(this.renameClass);
23 this.ui.add(this.renameClass); 23 this.ui.addSeparator();
24 this.ui.addSeparator(); 24 this.ui.add(this.expandAll);
25 this.ui.add(this.expandAll); 25 this.ui.add(this.collapseAll);
26 this.ui.add(this.collapseAll); 26
27 27 ClassSelector deobfClasses = panel.deobfClasses;
28 ClassSelector deobfClasses = panel.deobfClasses; 28
29 29 this.renamePackage.addActionListener(a -> {
30 this.renamePackage.addActionListener(a -> { 30 TreePath path;
31 TreePath path; 31
32 32 if (deobfClasses.getSelectedClass() != null) {
33 if (deobfClasses.getSelectedClass() != null) { 33 // Rename parent package if selected path is a class
34 // Rename parent package if selected path is a class 34 path = deobfClasses.getSelectionPath().getParentPath();
35 path = deobfClasses.getSelectionPath().getParentPath(); 35 } else {
36 } else { 36 // Rename selected path if it's already a package
37 // Rename selected path if it's already a package 37 path = deobfClasses.getSelectionPath();
38 path = deobfClasses.getSelectionPath(); 38 }
39 } 39
40 40 deobfClasses.getUI().startEditingAtPath(deobfClasses, path);
41 deobfClasses.getUI().startEditingAtPath(deobfClasses, path); 41 });
42 }); 42 this.renameClass.addActionListener(a -> deobfClasses.getUI().startEditingAtPath(deobfClasses, deobfClasses.getSelectionPath()));
43 this.renameClass.addActionListener(a -> deobfClasses.getUI().startEditingAtPath(deobfClasses, deobfClasses.getSelectionPath())); 43 this.expandAll.addActionListener(a -> deobfClasses.expandAll());
44 this.expandAll.addActionListener(a -> deobfClasses.expandAll()); 44 this.collapseAll.addActionListener(a -> deobfClasses.collapseAll());
45 this.collapseAll.addActionListener(a -> deobfClasses.collapseAll()); 45
46 46 this.retranslateUi();
47 this.retranslateUi(); 47 }
48 } 48
49 49 public void show(ClassSelector deobfClasses, int x, int y) {
50 public void show(ClassSelector deobfClasses, int x, int y) { 50 // Only enable rename class if selected path is a class
51 // Only enable rename class if selected path is a class 51 this.renameClass.setEnabled(deobfClasses.getSelectedClass() != null);
52 this.renameClass.setEnabled(deobfClasses.getSelectedClass() != null); 52
53 53 this.ui.show(deobfClasses, x, y);
54 this.ui.show(deobfClasses, x, y); 54 }
55 } 55
56 56 public void retranslateUi() {
57 public void retranslateUi() { 57 this.renamePackage.setText(I18n.translate("popup_menu.deobf_panel.rename_package"));
58 this.renamePackage.setText(I18n.translate("popup_menu.deobf_panel.rename_package")); 58 this.renameClass.setText(I18n.translate("popup_menu.deobf_panel.rename_class"));
59 this.renameClass.setText(I18n.translate("popup_menu.deobf_panel.rename_class")); 59 this.expandAll.setText(I18n.translate("popup_menu.deobf_panel.expand_all"));
60 this.expandAll.setText(I18n.translate("popup_menu.deobf_panel.expand_all")); 60 this.collapseAll.setText(I18n.translate("popup_menu.deobf_panel.collapse_all"));
61 this.collapseAll.setText(I18n.translate("popup_menu.deobf_panel.collapse_all")); 61 }
62 }
63} 62}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorPopupMenu.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorPopupMenu.java
index 2ce6ed9..d128bf5 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorPopupMenu.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorPopupMenu.java
@@ -19,7 +19,6 @@ import cuchaz.enigma.translation.representation.entry.MethodEntry;
19import cuchaz.enigma.utils.I18n; 19import cuchaz.enigma.utils.I18n;
20 20
21public class EditorPopupMenu { 21public class EditorPopupMenu {
22
23 private final JPopupMenu ui = new JPopupMenu(); 22 private final JPopupMenu ui = new JPopupMenu();
24 23
25 private final JMenuItem renameItem = new JMenuItem(); 24 private final JMenuItem renameItem = new JMenuItem();
@@ -105,39 +104,41 @@ public class EditorPopupMenu {
105 public boolean handleKeyEvent(KeyEvent event) { 104 public boolean handleKeyEvent(KeyEvent event) {
106 if (event.isControlDown()) { 105 if (event.isControlDown()) {
107 switch (event.getKeyCode()) { 106 switch (event.getKeyCode()) {
108 case KeyEvent.VK_I: 107 case KeyEvent.VK_I:
109 this.showInheritanceItem.doClick(); 108 this.showInheritanceItem.doClick();
110 return true; 109 return true;
111 case KeyEvent.VK_M: 110 case KeyEvent.VK_M:
112 this.showImplementationsItem.doClick(); 111 this.showImplementationsItem.doClick();
113 return true; 112 return true;
114 case KeyEvent.VK_N: 113 case KeyEvent.VK_N:
115 this.openEntryItem.doClick(); 114 this.openEntryItem.doClick();
116 return true; 115 return true;
117 case KeyEvent.VK_P: 116 case KeyEvent.VK_P:
118 this.openPreviousItem.doClick(); 117 this.openPreviousItem.doClick();
119 return true; 118 return true;
120 case KeyEvent.VK_E: 119 case KeyEvent.VK_E:
121 this.openNextItem.doClick(); 120 this.openNextItem.doClick();
122 return true; 121 return true;
123 case KeyEvent.VK_C: 122 case KeyEvent.VK_C:
124 if (event.isShiftDown()) { 123 if (event.isShiftDown()) {
125 this.showCallsSpecificItem.doClick(); 124 this.showCallsSpecificItem.doClick();
126 } else { 125 } else {
127 this.showCallsItem.doClick(); 126 this.showCallsItem.doClick();
128 } 127 }
129 return true; 128
130 case KeyEvent.VK_O: 129 return true;
131 this.toggleMappingItem.doClick(); 130 case KeyEvent.VK_O:
132 return true; 131 this.toggleMappingItem.doClick();
133 case KeyEvent.VK_R: 132 return true;
134 this.renameItem.doClick(); 133 case KeyEvent.VK_R:
135 return true; 134 this.renameItem.doClick();
136 case KeyEvent.VK_D: 135 return true;
137 this.editJavadocItem.doClick(); 136 case KeyEvent.VK_D:
138 return true; 137 this.editJavadocItem.doClick();
138 return true;
139 } 139 }
140 } 140 }
141
141 return false; 142 return false;
142 } 143 }
143 144
@@ -191,5 +192,4 @@ public class EditorPopupMenu {
191 public JPopupMenu getUi() { 192 public JPopupMenu getUi() {
192 return ui; 193 return ui;
193 } 194 }
194
195} 195}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabPopupMenu.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabPopupMenu.java
index 0b4926e..9385481 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabPopupMenu.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabPopupMenu.java
@@ -11,7 +11,6 @@ import cuchaz.enigma.gui.panels.EditorPanel;
11import cuchaz.enigma.utils.I18n; 11import cuchaz.enigma.utils.I18n;
12 12
13public class EditorTabPopupMenu { 13public class EditorTabPopupMenu {
14
15 private final JPopupMenu ui; 14 private final JPopupMenu ui;
16 private final JMenuItem close; 15 private final JMenuItem close;
17 private final JMenuItem closeAll; 16 private final JMenuItem closeAll;
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabbedPane.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabbedPane.java
index ff0bba3..7a6290e 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabbedPane.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabbedPane.java
@@ -39,7 +39,11 @@ public class EditorTabbedPane {
39 public EditorPanel openClass(ClassEntry entry) { 39 public EditorPanel openClass(ClassEntry entry) {
40 EditorPanel editorPanel = this.editors.computeIfAbsent(entry, e -> { 40 EditorPanel editorPanel = this.editors.computeIfAbsent(entry, e -> {
41 ClassHandle ch = this.gui.getController().getClassHandleProvider().openClass(entry); 41 ClassHandle ch = this.gui.getController().getClassHandleProvider().openClass(entry);
42 if (ch == null) return null; 42
43 if (ch == null) {
44 return null;
45 }
46
43 EditorPanel ed = new EditorPanel(this.gui); 47 EditorPanel ed = new EditorPanel(this.gui);
44 ed.setup(); 48 ed.setup();
45 ed.setClassHandle(ch); 49 ed.setClassHandle(ch);
@@ -125,7 +129,10 @@ public class EditorTabbedPane {
125 int index = this.openFiles.indexOfComponent(ed.getUi()); 129 int index = this.openFiles.indexOfComponent(ed.getUi());
126 130
127 for (int i = this.openFiles.getTabCount() - 1; i >= 0; i--) { 131 for (int i = this.openFiles.getTabCount() - 1; i >= 0; i--) {
128 if (i == index) continue; 132 if (i == index) {
133 continue;
134 }
135
129 closeEditor(EditorPanel.byUi(this.openFiles.getComponentAt(i))); 136 closeEditor(EditorPanel.byUi(this.openFiles.getComponentAt(i)));
130 } 137 }
131 } 138 }
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/JMultiLineToolTip.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/JMultiLineToolTip.java
index 533d1b3..9e632d9 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/JMultiLineToolTip.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/JMultiLineToolTip.java
@@ -12,13 +12,12 @@ import javax.swing.plaf.ComponentUI;
12import javax.swing.plaf.basic.BasicToolTipUI; 12import javax.swing.plaf.basic.BasicToolTipUI;
13 13
14/** 14/**
15 * Implements a multi line tooltip for GUI components 15 * Implements a multi line tooltip for GUI components.
16 * Copied from http://www.codeguru.com/java/articles/122.shtml 16 * Copied from http://www.codeguru.com/java/articles/122.shtml
17 * 17 *
18 * @author Zafir Anjum 18 * @author Zafir Anjum
19 */ 19 */
20public class JMultiLineToolTip extends JToolTip { 20public class JMultiLineToolTip extends JToolTip {
21
22 private static final long serialVersionUID = 7813662474312183098L; 21 private static final long serialVersionUID = 7813662474312183098L;
23 22
24 public JMultiLineToolTip() { 23 public JMultiLineToolTip() {
@@ -52,10 +51,9 @@ public class JMultiLineToolTip extends JToolTip {
52} 51}
53 52
54/** 53/**
55 * UI for multi line tool tip 54 * UI for multi line tool tip.
56 */ 55 */
57class MultiLineToolTipUI extends BasicToolTipUI { 56class MultiLineToolTipUI extends BasicToolTipUI {
58
59 static MultiLineToolTipUI sharedInstance = new MultiLineToolTipUI(); 57 static MultiLineToolTipUI sharedInstance = new MultiLineToolTipUI();
60 Font smallFont; 58 Font smallFont;
61 static JToolTip tip; 59 static JToolTip tip;
@@ -67,7 +65,7 @@ class MultiLineToolTipUI extends BasicToolTipUI {
67 return sharedInstance; 65 return sharedInstance;
68 } 66 }
69 67
70 public MultiLineToolTipUI() { 68 MultiLineToolTipUI() {
71 super(); 69 super();
72 } 70 }
73 71
@@ -93,7 +91,11 @@ class MultiLineToolTipUI extends BasicToolTipUI {
93 91
94 public Dimension getPreferredSize(JComponent c) { 92 public Dimension getPreferredSize(JComponent c) {
95 String tipText = ((JToolTip) c).getTipText(); 93 String tipText = ((JToolTip) c).getTipText();
96 if (tipText == null) return new Dimension(0, 0); 94
95 if (tipText == null) {
96 return new Dimension(0, 0);
97 }
98
97 textArea = new JTextArea(tipText); 99 textArea = new JTextArea(tipText);
98 rendererPane.removeAll(); 100 rendererPane.removeAll();
99 rendererPane.add(textArea); 101 rendererPane.add(textArea);
@@ -112,8 +114,9 @@ class MultiLineToolTipUI extends BasicToolTipUI {
112 d.width = width; 114 d.width = width;
113 d.height++; 115 d.height++;
114 textArea.setSize(d); 116 textArea.setSize(d);
115 } else 117 } else {
116 textArea.setLineWrap(false); 118 textArea.setLineWrap(false);
119 }
117 120
118 Dimension dim = textArea.getPreferredSize(); 121 Dimension dim = textArea.getPreferredSize();
119 122
@@ -129,4 +132,4 @@ class MultiLineToolTipUI extends BasicToolTipUI {
129 public Dimension getMaximumSize(JComponent c) { 132 public Dimension getMaximumSize(JComponent c) {
130 return getPreferredSize(c); 133 return getPreferredSize(c);
131 } 134 }
132} \ No newline at end of file 135}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
index eeb52cc..24a69b6 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
@@ -12,7 +12,14 @@ import java.util.Map;
12import java.util.stream.Collectors; 12import java.util.stream.Collectors;
13import java.util.stream.IntStream; 13import java.util.stream.IntStream;
14 14
15import javax.swing.*; 15import javax.swing.ButtonGroup;
16import javax.swing.JFileChooser;
17import javax.swing.JMenu;
18import javax.swing.JMenuBar;
19import javax.swing.JMenuItem;
20import javax.swing.JOptionPane;
21import javax.swing.JRadioButtonMenuItem;
22import javax.swing.KeyStroke;
16 23
17import cuchaz.enigma.gui.ConnectionState; 24import cuchaz.enigma.gui.ConnectionState;
18import cuchaz.enigma.gui.Gui; 25import cuchaz.enigma.gui.Gui;
@@ -20,7 +27,13 @@ import cuchaz.enigma.gui.config.Decompiler;
20import cuchaz.enigma.gui.config.LookAndFeel; 27import cuchaz.enigma.gui.config.LookAndFeel;
21import cuchaz.enigma.gui.config.NetConfig; 28import cuchaz.enigma.gui.config.NetConfig;
22import cuchaz.enigma.gui.config.UiConfig; 29import cuchaz.enigma.gui.config.UiConfig;
23import cuchaz.enigma.gui.dialog.*; 30import cuchaz.enigma.gui.dialog.AboutDialog;
31import cuchaz.enigma.gui.dialog.ChangeDialog;
32import cuchaz.enigma.gui.dialog.ConnectToServerDialog;
33import cuchaz.enigma.gui.dialog.CreateServerDialog;
34import cuchaz.enigma.gui.dialog.FontDialog;
35import cuchaz.enigma.gui.dialog.SearchDialog;
36import cuchaz.enigma.gui.dialog.StatsDialog;
24import cuchaz.enigma.gui.util.GuiUtil; 37import cuchaz.enigma.gui.util.GuiUtil;
25import cuchaz.enigma.gui.util.LanguageUtil; 38import cuchaz.enigma.gui.util.LanguageUtil;
26import cuchaz.enigma.gui.util.ScaleUtil; 39import cuchaz.enigma.gui.util.ScaleUtil;
@@ -29,7 +42,6 @@ import cuchaz.enigma.utils.I18n;
29import cuchaz.enigma.utils.Pair; 42import cuchaz.enigma.utils.Pair;
30 43
31public class MenuBar { 44public class MenuBar {
32
33 private final JMenu fileMenu = new JMenu(); 45 private final JMenu fileMenu = new JMenu();
34 private final JMenuItem jarOpenItem = new JMenuItem(); 46 private final JMenuItem jarOpenItem = new JMenuItem();
35 private final JMenuItem jarCloseItem = new JMenuItem(); 47 private final JMenuItem jarCloseItem = new JMenuItem();
@@ -221,13 +233,16 @@ public class MenuBar {
221 } 233 }
222 234
223 File file = d.getSelectedFile(); 235 File file = d.getSelectedFile();
236
224 // checks if the file name is not empty 237 // checks if the file name is not empty
225 if (file != null) { 238 if (file != null) {
226 Path path = file.toPath(); 239 Path path = file.toPath();
240
227 // checks if the file name corresponds to an existing file 241 // checks if the file name corresponds to an existing file
228 if (Files.exists(path)) { 242 if (Files.exists(path)) {
229 this.gui.getController().openJar(path); 243 this.gui.getController().openJar(path);
230 } 244 }
245
231 UiConfig.setLastSelectedDir(d.getCurrentDirectory().getAbsolutePath()); 246 UiConfig.setLastSelectedDir(d.getCurrentDirectory().getAbsolutePath());
232 } 247 }
233 } 248 }
@@ -241,8 +256,10 @@ public class MenuBar {
241 this.gui.showDiscardDiag((response -> { 256 this.gui.showDiscardDiag((response -> {
242 if (response == JOptionPane.YES_OPTION) { 257 if (response == JOptionPane.YES_OPTION) {
243 this.gui.saveMapping().thenRun(then); 258 this.gui.saveMapping().thenRun(then);
244 } else if (response == JOptionPane.NO_OPTION) 259 } else if (response == JOptionPane.NO_OPTION) {
245 then.run(); 260 then.run();
261 }
262
246 return null; 263 return null;
247 }), I18n.translate("prompt.close.save"), I18n.translate("prompt.close.discard"), I18n.translate("prompt.cancel")); 264 }), I18n.translate("prompt.close.save"), I18n.translate("prompt.close.discard"), I18n.translate("prompt.cancel"));
248 } else { 265 } else {
@@ -264,6 +281,7 @@ public class MenuBar {
264 281
265 private void onExportSourceClicked() { 282 private void onExportSourceClicked() {
266 this.gui.exportSourceFileChooser.setCurrentDirectory(new File(UiConfig.getLastSelectedDir())); 283 this.gui.exportSourceFileChooser.setCurrentDirectory(new File(UiConfig.getLastSelectedDir()));
284
267 if (this.gui.exportSourceFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { 285 if (this.gui.exportSourceFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) {
268 UiConfig.setLastSelectedDir(this.gui.exportSourceFileChooser.getCurrentDirectory().toString()); 286 UiConfig.setLastSelectedDir(this.gui.exportSourceFileChooser.getCurrentDirectory().toString());
269 this.gui.getController().exportSource(this.gui.exportSourceFileChooser.getSelectedFile().toPath()); 287 this.gui.getController().exportSource(this.gui.exportSourceFileChooser.getSelectedFile().toPath());
@@ -287,29 +305,35 @@ public class MenuBar {
287 } 305 }
288 306
289 private void onCustomScaleClicked() { 307 private void onCustomScaleClicked() {
290 String answer = (String) JOptionPane.showInputDialog(this.gui.getFrame(), I18n.translate("menu.view.scale.custom.title"), I18n.translate("menu.view.scale.custom.title"), 308 String answer = (String) JOptionPane.showInputDialog(this.gui.getFrame(), I18n.translate("menu.view.scale.custom.title"), I18n.translate("menu.view.scale.custom.title"), JOptionPane.QUESTION_MESSAGE, null, null, Float.toString(UiConfig.getScaleFactor() * 100));
291 JOptionPane.QUESTION_MESSAGE, null, null, Float.toString(UiConfig.getScaleFactor() * 100)); 309
292 if (answer == null) return; 310 if (answer == null) {
311 return;
312 }
313
293 float newScale = 1.0f; 314 float newScale = 1.0f;
315
294 try { 316 try {
295 newScale = Float.parseFloat(answer) / 100f; 317 newScale = Float.parseFloat(answer) / 100f;
296 } catch (NumberFormatException ignored) { 318 } catch (NumberFormatException ignored) {
319 // ignored
297 } 320 }
321
298 ScaleUtil.setScaleFactor(newScale); 322 ScaleUtil.setScaleFactor(newScale);
299 ChangeDialog.show(this.gui.getFrame()); 323 ChangeDialog.show(this.gui.getFrame());
300 } 324 }
301 325
302 private void onFontClicked(Gui gui) { 326 private void onFontClicked(Gui gui) {
303// FontDialog fd = new FontDialog(gui.getFrame(), "Choose Font", true); 327 // FontDialog fd = new FontDialog(gui.getFrame(), "Choose Font", true);
304// fd.setLocationRelativeTo(gui.getFrame()); 328 // fd.setLocationRelativeTo(gui.getFrame());
305// fd.setSelectedFont(UiConfig.getEditorFont()); 329 // fd.setSelectedFont(UiConfig.getEditorFont());
306// fd.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 330 // fd.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
307// fd.setVisible(true); 331 // fd.setVisible(true);
308// 332 //
309// if (!fd.isCancelSelected()) { 333 // if (!fd.isCancelSelected()) {
310// UiConfig.setEditorFont(fd.getSelectedFont()); 334 // UiConfig.setEditorFont(fd.getSelectedFont());
311// UiConfig.save(); 335 // UiConfig.save();
312// } 336 // }
313 FontDialog.display(gui.getFrame()); 337 FontDialog.display(gui.getFrame());
314 } 338 }
315 339
@@ -324,11 +348,15 @@ public class MenuBar {
324 this.gui.getController().disconnectIfConnected(null); 348 this.gui.getController().disconnectIfConnected(null);
325 return; 349 return;
326 } 350 }
351
327 ConnectToServerDialog.Result result = ConnectToServerDialog.show(this.gui.getFrame()); 352 ConnectToServerDialog.Result result = ConnectToServerDialog.show(this.gui.getFrame());
353
328 if (result == null) { 354 if (result == null) {
329 return; 355 return;
330 } 356 }
357
331 this.gui.getController().disconnectIfConnected(null); 358 this.gui.getController().disconnectIfConnected(null);
359
332 try { 360 try {
333 this.gui.getController().createClient(result.getUsername(), result.getAddress().address, result.getAddress().port, result.getPassword()); 361 this.gui.getController().createClient(result.getUsername(), result.getAddress().address, result.getAddress().port, result.getPassword());
334 NetConfig.setUsername(result.getUsername()); 362 NetConfig.setUsername(result.getUsername());
@@ -339,6 +367,7 @@ public class MenuBar {
339 JOptionPane.showMessageDialog(this.gui.getFrame(), e.toString(), I18n.translate("menu.collab.connect.error"), JOptionPane.ERROR_MESSAGE); 367 JOptionPane.showMessageDialog(this.gui.getFrame(), e.toString(), I18n.translate("menu.collab.connect.error"), JOptionPane.ERROR_MESSAGE);
340 this.gui.getController().disconnectIfConnected(null); 368 this.gui.getController().disconnectIfConnected(null);
341 } 369 }
370
342 Arrays.fill(result.getPassword(), (char) 0); 371 Arrays.fill(result.getPassword(), (char) 0);
343 } 372 }
344 373
@@ -347,11 +376,15 @@ public class MenuBar {
347 this.gui.getController().disconnectIfConnected(null); 376 this.gui.getController().disconnectIfConnected(null);
348 return; 377 return;
349 } 378 }
379
350 CreateServerDialog.Result result = CreateServerDialog.show(this.gui.getFrame()); 380 CreateServerDialog.Result result = CreateServerDialog.show(this.gui.getFrame());
381
351 if (result == null) { 382 if (result == null) {
352 return; 383 return;
353 } 384 }
385
354 this.gui.getController().disconnectIfConnected(null); 386 this.gui.getController().disconnectIfConnected(null);
387
355 try { 388 try {
356 this.gui.getController().createServer(result.getPort(), result.getPassword()); 389 this.gui.getController().createServer(result.getPort(), result.getPassword());
357 NetConfig.setServerPort(result.getPort()); 390 NetConfig.setServerPort(result.getPort());
@@ -373,6 +406,7 @@ public class MenuBar {
373 JMenuItem item = new JMenuItem(I18n.translate("mapping_format." + format.name().toLowerCase(Locale.ROOT))); 406 JMenuItem item = new JMenuItem(I18n.translate("mapping_format." + format.name().toLowerCase(Locale.ROOT)));
374 item.addActionListener(event -> { 407 item.addActionListener(event -> {
375 gui.enigmaMappingsFileChooser.setCurrentDirectory(new File(UiConfig.getLastSelectedDir())); 408 gui.enigmaMappingsFileChooser.setCurrentDirectory(new File(UiConfig.getLastSelectedDir()));
409
376 if (gui.enigmaMappingsFileChooser.showOpenDialog(gui.getFrame()) == JFileChooser.APPROVE_OPTION) { 410 if (gui.enigmaMappingsFileChooser.showOpenDialog(gui.getFrame()) == JFileChooser.APPROVE_OPTION) {
377 File selectedFile = gui.enigmaMappingsFileChooser.getSelectedFile(); 411 File selectedFile = gui.enigmaMappingsFileChooser.getSelectedFile();
378 gui.getController().openMappings(format, selectedFile.toPath()); 412 gui.getController().openMappings(format, selectedFile.toPath());
@@ -411,9 +445,11 @@ public class MenuBar {
411 for (Decompiler decompiler : Decompiler.values()) { 445 for (Decompiler decompiler : Decompiler.values()) {
412 JRadioButtonMenuItem decompilerButton = new JRadioButtonMenuItem(decompiler.name); 446 JRadioButtonMenuItem decompilerButton = new JRadioButtonMenuItem(decompiler.name);
413 decompilerGroup.add(decompilerButton); 447 decompilerGroup.add(decompilerButton);
448
414 if (decompiler.equals(UiConfig.getDecompiler())) { 449 if (decompiler.equals(UiConfig.getDecompiler())) {
415 decompilerButton.setSelected(true); 450 decompilerButton.setSelected(true);
416 } 451 }
452
417 decompilerButton.addActionListener(event -> { 453 decompilerButton.addActionListener(event -> {
418 gui.getController().setDecompiler(decompiler.service); 454 gui.getController().setDecompiler(decompiler.service);
419 455
@@ -426,12 +462,15 @@ public class MenuBar {
426 462
427 private static void prepareThemesMenu(JMenu themesMenu, Gui gui) { 463 private static void prepareThemesMenu(JMenu themesMenu, Gui gui) {
428 ButtonGroup themeGroup = new ButtonGroup(); 464 ButtonGroup themeGroup = new ButtonGroup();
465
429 for (LookAndFeel lookAndFeel : LookAndFeel.values()) { 466 for (LookAndFeel lookAndFeel : LookAndFeel.values()) {
430 JRadioButtonMenuItem themeButton = new JRadioButtonMenuItem(I18n.translate("menu.view.themes." + lookAndFeel.name().toLowerCase(Locale.ROOT))); 467 JRadioButtonMenuItem themeButton = new JRadioButtonMenuItem(I18n.translate("menu.view.themes." + lookAndFeel.name().toLowerCase(Locale.ROOT)));
431 themeGroup.add(themeButton); 468 themeGroup.add(themeButton);
469
432 if (lookAndFeel.equals(UiConfig.getLookAndFeel())) { 470 if (lookAndFeel.equals(UiConfig.getLookAndFeel())) {
433 themeButton.setSelected(true); 471 themeButton.setSelected(true);
434 } 472 }
473
435 themeButton.addActionListener(_e -> { 474 themeButton.addActionListener(_e -> {
436 UiConfig.setLookAndFeel(lookAndFeel); 475 UiConfig.setLookAndFeel(lookAndFeel);
437 UiConfig.save(); 476 UiConfig.save();
@@ -443,12 +482,15 @@ public class MenuBar {
443 482
444 private static void prepareLanguagesMenu(JMenu languagesMenu) { 483 private static void prepareLanguagesMenu(JMenu languagesMenu) {
445 ButtonGroup languageGroup = new ButtonGroup(); 484 ButtonGroup languageGroup = new ButtonGroup();
485
446 for (String lang : I18n.getAvailableLanguages()) { 486 for (String lang : I18n.getAvailableLanguages()) {
447 JRadioButtonMenuItem languageButton = new JRadioButtonMenuItem(I18n.getLanguageName(lang)); 487 JRadioButtonMenuItem languageButton = new JRadioButtonMenuItem(I18n.getLanguageName(lang));
448 languageGroup.add(languageButton); 488 languageGroup.add(languageButton);
489
449 if (lang.equals(UiConfig.getLanguage())) { 490 if (lang.equals(UiConfig.getLanguage())) {
450 languageButton.setSelected(true); 491 languageButton.setSelected(true);
451 } 492 }
493
452 languageButton.addActionListener(event -> { 494 languageButton.addActionListener(event -> {
453 UiConfig.setLanguage(lang); 495 UiConfig.setLanguage(lang);
454 I18n.setLanguage(lang); 496 I18n.setLanguage(lang);
@@ -461,25 +503,25 @@ public class MenuBar {
461 503
462 private static void prepareScaleMenu(JMenu scaleMenu, Gui gui) { 504 private static void prepareScaleMenu(JMenu scaleMenu, Gui gui) {
463 ButtonGroup scaleGroup = new ButtonGroup(); 505 ButtonGroup scaleGroup = new ButtonGroup();
464 Map<Float, JRadioButtonMenuItem> scaleButtons = IntStream.of(100, 125, 150, 175, 200) 506 Map<Float, JRadioButtonMenuItem> scaleButtons = IntStream.of(100, 125, 150, 175, 200).mapToObj(scaleFactor -> {
465 .mapToObj(scaleFactor -> { 507 float realScaleFactor = scaleFactor / 100f;
466 float realScaleFactor = scaleFactor / 100f; 508 JRadioButtonMenuItem menuItem = new JRadioButtonMenuItem(String.format("%d%%", scaleFactor));
467 JRadioButtonMenuItem menuItem = new JRadioButtonMenuItem(String.format("%d%%", scaleFactor)); 509 menuItem.addActionListener(event -> ScaleUtil.setScaleFactor(realScaleFactor));
468 menuItem.addActionListener(event -> ScaleUtil.setScaleFactor(realScaleFactor)); 510 menuItem.addActionListener(event -> ChangeDialog.show(gui.getFrame()));
469 menuItem.addActionListener(event -> ChangeDialog.show(gui.getFrame())); 511 scaleGroup.add(menuItem);
470 scaleGroup.add(menuItem); 512 scaleMenu.add(menuItem);
471 scaleMenu.add(menuItem); 513 return new Pair<>(realScaleFactor, menuItem);
472 return new Pair<>(realScaleFactor, menuItem); 514 }).collect(Collectors.toMap(x -> x.a, x -> x.b));
473 })
474 .collect(Collectors.toMap(x -> x.a, x -> x.b));
475 515
476 JRadioButtonMenuItem currentScaleButton = scaleButtons.get(UiConfig.getScaleFactor()); 516 JRadioButtonMenuItem currentScaleButton = scaleButtons.get(UiConfig.getScaleFactor());
517
477 if (currentScaleButton != null) { 518 if (currentScaleButton != null) {
478 currentScaleButton.setSelected(true); 519 currentScaleButton.setSelected(true);
479 } 520 }
480 521
481 ScaleUtil.addListener((newScale, _oldScale) -> { 522 ScaleUtil.addListener((newScale, _oldScale) -> {
482 JRadioButtonMenuItem mi = scaleButtons.get(newScale); 523 JRadioButtonMenuItem mi = scaleButtons.get(newScale);
524
483 if (mi != null) { 525 if (mi != null) {
484 mi.setSelected(true); 526 mi.setSelected(true);
485 } else { 527 } else {
@@ -487,5 +529,4 @@ public class MenuBar {
487 } 529 }
488 }); 530 });
489 } 531 }
490
491} 532}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/StatusBar.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/StatusBar.java
index 0c667c0..48404a1 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/StatusBar.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/StatusBar.java
@@ -54,7 +54,7 @@ public class StatusBar {
54 * 54 *
55 * @param message the message to display 55 * @param message the message to display
56 * @param timeout the timeout in milliseconds to wait until clearing the 56 * @param timeout the timeout in milliseconds to wait until clearing the
57 * message; if 0, the message is not automatically cleared 57 * message; if 0, the message is not automatically cleared
58 */ 58 */
59 public void showMessage(String message, int timeout) { 59 public void showMessage(String message, int timeout) {
60 this.timer.stop(); 60 this.timer.stop();
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatablePasswordField.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatablePasswordField.java
index 02e1bc3..4329cae 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatablePasswordField.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatablePasswordField.java
@@ -14,7 +14,6 @@ import cuchaz.enigma.utils.validation.ParameterizedMessage;
14import cuchaz.enigma.utils.validation.Validatable; 14import cuchaz.enigma.utils.validation.Validatable;
15 15
16public class ValidatablePasswordField extends JPasswordField implements Validatable { 16public class ValidatablePasswordField extends JPasswordField implements Validatable {
17
18 private List<ParameterizedMessage> messages = new ArrayList<>(); 17 private List<ParameterizedMessage> messages = new ArrayList<>();
19 private String tooltipText = null; 18 private String tooltipText = null;
20 19
@@ -92,5 +91,4 @@ public class ValidatablePasswordField extends JPasswordField implements Validata
92 super.paint(g); 91 super.paint(g);
93 ValidatableUi.drawMarker(this, g, messages); 92 ValidatableUi.drawMarker(this, g, messages);
94 } 93 }
95
96} 94}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextArea.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextArea.java
index 7d1f866..2d5e229 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextArea.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextArea.java
@@ -14,7 +14,6 @@ import cuchaz.enigma.utils.validation.ParameterizedMessage;
14import cuchaz.enigma.utils.validation.Validatable; 14import cuchaz.enigma.utils.validation.Validatable;
15 15
16public class ValidatableTextArea extends JTextArea implements Validatable { 16public class ValidatableTextArea extends JTextArea implements Validatable {
17
18 private List<ParameterizedMessage> messages = new ArrayList<>(); 17 private List<ParameterizedMessage> messages = new ArrayList<>();
19 private String tooltipText = null; 18 private String tooltipText = null;
20 19
@@ -96,5 +95,4 @@ public class ValidatableTextArea extends JTextArea implements Validatable {
96 super.paint(g); 95 super.paint(g);
97 ValidatableUi.drawMarker(this, g, messages); 96 ValidatableUi.drawMarker(this, g, messages);
98 } 97 }
99
100} 98}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextField.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextField.java
index c114dc1..be658d5 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextField.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextField.java
@@ -14,7 +14,6 @@ import cuchaz.enigma.utils.validation.ParameterizedMessage;
14import cuchaz.enigma.utils.validation.Validatable; 14import cuchaz.enigma.utils.validation.Validatable;
15 15
16public class ValidatableTextField extends JTextField implements Validatable { 16public class ValidatableTextField extends JTextField implements Validatable {
17
18 private List<ParameterizedMessage> messages = new ArrayList<>(); 17 private List<ParameterizedMessage> messages = new ArrayList<>();
19 private String tooltipText = null; 18 private String tooltipText = null;
20 19
@@ -92,5 +91,4 @@ public class ValidatableTextField extends JTextField implements Validatable {
92 super.paint(g); 91 super.paint(g);
93 ValidatableUi.drawMarker(this, g, messages); 92 ValidatableUi.drawMarker(this, g, messages);
94 } 93 }
95
96} 94}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableUi.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableUi.java
index 5df6348..b8b8431 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableUi.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableUi.java
@@ -13,26 +13,29 @@ import cuchaz.enigma.gui.util.ScaleUtil;
13import cuchaz.enigma.utils.validation.ParameterizedMessage; 13import cuchaz.enigma.utils.validation.ParameterizedMessage;
14 14
15public final class ValidatableUi { 15public final class ValidatableUi {
16
17 private ValidatableUi() { 16 private ValidatableUi() {
18 } 17 }
19 18
20 public static String getTooltipText(String tooltipText, List<ParameterizedMessage> messages) { 19 public static String getTooltipText(String tooltipText, List<ParameterizedMessage> messages) {
21 List<String> strings = new ArrayList<>(); 20 List<String> strings = new ArrayList<>();
21
22 if (tooltipText != null) { 22 if (tooltipText != null) {
23 strings.add(tooltipText); 23 strings.add(tooltipText);
24 } 24 }
25
25 if (!messages.isEmpty()) { 26 if (!messages.isEmpty()) {
26 strings.add("Error(s): "); 27 strings.add("Error(s): ");
27 28
28 messages.forEach(msg -> { 29 messages.forEach(msg -> {
29 strings.add(String.format(" - %s", msg.getText())); 30 strings.add(String.format(" - %s", msg.getText()));
30 String longDesc = msg.getLongText(); 31 String longDesc = msg.getLongText();
32
31 if (!longDesc.isEmpty()) { 33 if (!longDesc.isEmpty()) {
32 Arrays.stream(longDesc.split("\n")).map(s -> String.format(" %s", s)).forEach(strings::add); 34 Arrays.stream(longDesc.split("\n")).map(s -> String.format(" %s", s)).forEach(strings::add);
33 } 35 }
34 }); 36 });
35 } 37 }
38
36 if (strings.isEmpty()) { 39 if (strings.isEmpty()) {
37 return null; 40 return null;
38 } else { 41 } else {
@@ -49,11 +52,13 @@ public final class ValidatableUi {
49 messages.forEach(msg -> { 52 messages.forEach(msg -> {
50 strings.add(String.format(" - %s", msg.getText())); 53 strings.add(String.format(" - %s", msg.getText()));
51 String longDesc = msg.getLongText(); 54 String longDesc = msg.getLongText();
55
52 if (!longDesc.isEmpty()) { 56 if (!longDesc.isEmpty()) {
53 Arrays.stream(longDesc.split("\n")).map(s -> String.format(" %s", s)).forEach(strings::add); 57 Arrays.stream(longDesc.split("\n")).map(s -> String.format(" %s", s)).forEach(strings::add);
54 } 58 }
55 }); 59 });
56 } 60 }
61
57 if (strings.isEmpty()) { 62 if (strings.isEmpty()) {
58 return null; 63 return null;
59 } else { 64 } else {
@@ -63,6 +68,7 @@ public final class ValidatableUi {
63 68
64 public static void drawMarker(Component self, Graphics g, List<ParameterizedMessage> messages) { 69 public static void drawMarker(Component self, Graphics g, List<ParameterizedMessage> messages) {
65 Color color = ValidatableUi.getMarkerColor(messages); 70 Color color = ValidatableUi.getMarkerColor(messages);
71
66 if (color != null) { 72 if (color != null) {
67 g.setColor(color); 73 g.setColor(color);
68 int x1 = self.getWidth() - ScaleUtil.scale(8) - 1; 74 int x1 = self.getWidth() - ScaleUtil.scale(8) - 1;
@@ -75,33 +81,32 @@ public final class ValidatableUi {
75 81
76 @Nullable 82 @Nullable
77 public static Color getMarkerColor(List<ParameterizedMessage> messages) { 83 public static Color getMarkerColor(List<ParameterizedMessage> messages) {
78 int level = messages.stream() 84 int level = messages.stream().mapToInt(ValidatableUi::getMessageLevel).max().orElse(0);
79 .mapToInt(ValidatableUi::getMessageLevel)
80 .max().orElse(0);
81 85
82 switch (level) { 86 switch (level) {
83 case 0: 87 case 0:
84 return null; 88 return null;
85 case 1: 89 case 1:
86 return Color.BLUE; 90 return Color.BLUE;
87 case 2: 91 case 2:
88 return Color.ORANGE; 92 return Color.ORANGE;
89 case 3: 93 case 3:
90 return Color.RED; 94 return Color.RED;
91 } 95 }
96
92 throw new IllegalStateException("unreachable"); 97 throw new IllegalStateException("unreachable");
93 } 98 }
94 99
95 private static int getMessageLevel(ParameterizedMessage message) { 100 private static int getMessageLevel(ParameterizedMessage message) {
96 switch (message.message.type) { 101 switch (message.message.type) {
97 case INFO: 102 case INFO:
98 return 1; 103 return 1;
99 case WARNING: 104 case WARNING:
100 return 2; 105 return 2;
101 case ERROR: 106 case ERROR:
102 return 3; 107 return 3;
103 } 108 }
109
104 throw new IllegalStateException("unreachable"); 110 throw new IllegalStateException("unreachable");
105 } 111 }
106
107} 112}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ConvertingTextFieldListener.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ConvertingTextFieldListener.java
index 6e17fec..d9ec95c 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ConvertingTextFieldListener.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ConvertingTextFieldListener.java
@@ -3,7 +3,6 @@ package cuchaz.enigma.gui.events;
3import cuchaz.enigma.gui.elements.ConvertingTextField; 3import cuchaz.enigma.gui.elements.ConvertingTextField;
4 4
5public interface ConvertingTextFieldListener { 5public interface ConvertingTextFieldListener {
6
7 default void onStartEditing(ConvertingTextField field) { 6 default void onStartEditing(ConvertingTextField field) {
8 } 7 }
9 8
@@ -13,5 +12,4 @@ public interface ConvertingTextFieldListener {
13 12
14 default void onStopEditing(ConvertingTextField field, boolean abort) { 13 default void onStopEditing(ConvertingTextField field, boolean abort) {
15 } 14 }
16
17} 15}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/events/EditorActionListener.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/events/EditorActionListener.java
index 48c9ec4..1651abf 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/events/EditorActionListener.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/events/EditorActionListener.java
@@ -7,7 +7,6 @@ import cuchaz.enigma.translation.representation.entry.ClassEntry;
7import cuchaz.enigma.translation.representation.entry.Entry; 7import cuchaz.enigma.translation.representation.entry.Entry;
8 8
9public interface EditorActionListener { 9public interface EditorActionListener {
10
11 default void onCursorReferenceChanged(EditorPanel editor, EntryReference<Entry<?>, Entry<?>> ref) { 10 default void onCursorReferenceChanged(EditorPanel editor, EntryReference<Entry<?>, Entry<?>> ref) {
12 } 11 }
13 12
@@ -16,5 +15,4 @@ public interface EditorActionListener {
16 15
17 default void onTitleChanged(EditorPanel editor, String title) { 16 default void onTitleChanged(EditorPanel editor, String title) {
18 } 17 }
19
20} 18}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ThemeChangeListener.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ThemeChangeListener.java
index 10d7ce1..e2054b2 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ThemeChangeListener.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ThemeChangeListener.java
@@ -7,7 +7,5 @@ import cuchaz.enigma.gui.highlight.BoxHighlightPainter;
7import cuchaz.enigma.source.RenamableTokenType; 7import cuchaz.enigma.source.RenamableTokenType;
8 8
9public interface ThemeChangeListener { 9public interface ThemeChangeListener {
10
11 void onThemeChanged(LookAndFeel lookAndFeel, Map<RenamableTokenType, BoxHighlightPainter> boxHighlightPainters); 10 void onThemeChanged(LookAndFeel lookAndFeel, Map<RenamableTokenType, BoxHighlightPainter> boxHighlightPainters);
12
13} 11}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java
index 2d8d76a..a97b377 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.gui.highlight; 12package cuchaz.enigma.gui.highlight;
13 13
@@ -67,5 +67,4 @@ public class BoxHighlightPainter implements Highlighter.HighlightPainter {
67 g.setColor(this.borderColor); 67 g.setColor(this.borderColor);
68 g.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); 68 g.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4);
69 } 69 }
70
71} 70}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java
index 22d6420..a807802 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java
@@ -1,17 +1,21 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.gui.highlight; 12package cuchaz.enigma.gui.highlight;
13 13
14import java.awt.*; 14import java.awt.BasicStroke;
15import java.awt.Graphics;
16import java.awt.Graphics2D;
17import java.awt.Rectangle;
18import java.awt.Shape;
15 19
16import javax.swing.text.Highlighter; 20import javax.swing.text.Highlighter;
17import javax.swing.text.JTextComponent; 21import javax.swing.text.JTextComponent;
@@ -19,7 +23,6 @@ import javax.swing.text.JTextComponent;
19import cuchaz.enigma.gui.config.UiConfig; 23import cuchaz.enigma.gui.config.UiConfig;
20 24
21public class SelectionHighlightPainter implements Highlighter.HighlightPainter { 25public class SelectionHighlightPainter implements Highlighter.HighlightPainter {
22
23 public static final SelectionHighlightPainter INSTANCE = new SelectionHighlightPainter(); 26 public static final SelectionHighlightPainter INSTANCE = new SelectionHighlightPainter();
24 27
25 @Override 28 @Override
@@ -31,5 +34,4 @@ public class SelectionHighlightPainter implements Highlighter.HighlightPainter {
31 g2d.setStroke(new BasicStroke(2.0f)); 34 g2d.setStroke(new BasicStroke(2.0f));
32 g2d.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); 35 g2d.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4);
33 } 36 }
34
35} 37}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/newabstraction/EntryValidation.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/newabstraction/EntryValidation.java
index 898529a..1dcdeab 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/newabstraction/EntryValidation.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/newabstraction/EntryValidation.java
@@ -6,17 +6,16 @@ import cuchaz.enigma.utils.validation.Message;
6import cuchaz.enigma.utils.validation.ValidationContext; 6import cuchaz.enigma.utils.validation.ValidationContext;
7 7
8public class EntryValidation { 8public class EntryValidation {
9
10 public static boolean validateJavadoc(ValidationContext vc, String javadoc) { 9 public static boolean validateJavadoc(ValidationContext vc, String javadoc) {
11 if (javadoc.contains("*/")) { 10 if (javadoc.contains("*/")) {
12 vc.raise(Message.ILLEGAL_DOC_COMMENT_END); 11 vc.raise(Message.ILLEGAL_DOC_COMMENT_END);
13 return false; 12 return false;
14 } 13 }
14
15 return true; 15 return true;
16 } 16 }
17 17
18 public static boolean validateRename(ValidationContext vc, EnigmaProject p, Entry<?> entry, String newName) { 18 public static boolean validateRename(ValidationContext vc, EnigmaProject p, Entry<?> entry, String newName) {
19 return p.getMapper().getValidator().validateRename(vc, entry, newName); 19 return p.getMapper().getValidator().validateRename(vc, entry, newName);
20 } 20 }
21
22} 21}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
index 922f8f2..f931a93 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
@@ -1,22 +1,21 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.gui.node; 12package cuchaz.enigma.gui.node;
13 13
14import cuchaz.enigma.translation.representation.entry.ClassEntry;
15
16import javax.swing.tree.DefaultMutableTreeNode; 14import javax.swing.tree.DefaultMutableTreeNode;
17 15
18public class ClassSelectorClassNode extends DefaultMutableTreeNode { 16import cuchaz.enigma.translation.representation.entry.ClassEntry;
19 17
18public class ClassSelectorClassNode extends DefaultMutableTreeNode {
20 private final ClassEntry obfEntry; 19 private final ClassEntry obfEntry;
21 private ClassEntry classEntry; 20 private ClassEntry classEntry;
22 21
@@ -57,12 +56,17 @@ public class ClassSelectorClassNode extends DefaultMutableTreeNode {
57 @Override 56 @Override
58 public void setUserObject(Object userObject) { 57 public void setUserObject(Object userObject) {
59 String packageName = ""; 58 String packageName = "";
60 if (classEntry.getPackageName() != null) 59
60 if (classEntry.getPackageName() != null) {
61 packageName = classEntry.getPackageName() + "/"; 61 packageName = classEntry.getPackageName() + "/";
62 if (userObject instanceof String) 62 }
63
64 if (userObject instanceof String) {
63 this.classEntry = new ClassEntry(packageName + userObject); 65 this.classEntry = new ClassEntry(packageName + userObject);
64 else if (userObject instanceof ClassEntry) 66 } else if (userObject instanceof ClassEntry) {
65 this.classEntry = (ClassEntry) userObject; 67 this.classEntry = (ClassEntry) userObject;
68 }
69
66 super.setUserObject(classEntry); 70 super.setUserObject(classEntry);
67 } 71 }
68 72
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java
index c1c7d38..dfcbd8c 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java
@@ -1,22 +1,21 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.gui.node; 12package cuchaz.enigma.gui.node;
13 13
14import cuchaz.enigma.translation.representation.entry.ClassEntry;
15
16import javax.swing.tree.DefaultMutableTreeNode; 14import javax.swing.tree.DefaultMutableTreeNode;
17 15
18public class ClassSelectorPackageNode extends DefaultMutableTreeNode { 16import cuchaz.enigma.translation.representation.entry.ClassEntry;
19 17
18public class ClassSelectorPackageNode extends DefaultMutableTreeNode {
20 private String packageName; 19 private String packageName;
21 20
22 public ClassSelectorPackageNode(String packageName) { 21 public ClassSelectorPackageNode(String packageName) {
@@ -34,8 +33,10 @@ public class ClassSelectorPackageNode extends DefaultMutableTreeNode {
34 33
35 @Override 34 @Override
36 public void setUserObject(Object userObject) { 35 public void setUserObject(Object userObject) {
37 if (userObject instanceof String) 36 if (userObject instanceof String) {
38 this.packageName = (String) userObject; 37 this.packageName = (String) userObject;
38 }
39
39 super.setUserObject(userObject); 40 super.setUserObject(userObject);
40 } 41 }
41 42
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ClosableTabTitlePane.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ClosableTabTitlePane.java
index fe5c857..dca714d 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ClosableTabTitlePane.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ClosableTabTitlePane.java
@@ -9,12 +9,17 @@ import java.awt.event.MouseEvent;
9 9
10import javax.accessibility.AccessibleContext; 10import javax.accessibility.AccessibleContext;
11import javax.annotation.Nullable; 11import javax.annotation.Nullable;
12import javax.swing.*; 12import javax.swing.JButton;
13import javax.swing.JComponent;
14import javax.swing.JLabel;
15import javax.swing.JPanel;
16import javax.swing.JTabbedPane;
17import javax.swing.SwingUtilities;
18import javax.swing.UIManager;
13import javax.swing.border.EmptyBorder; 19import javax.swing.border.EmptyBorder;
14import javax.swing.event.ChangeListener; 20import javax.swing.event.ChangeListener;
15 21
16public class ClosableTabTitlePane { 22public class ClosableTabTitlePane {
17
18 private final JPanel ui; 23 private final JPanel ui;
19 private final JButton closeButton; 24 private final JButton closeButton;
20 private final JLabel label; 25 private final JLabel label;
@@ -66,19 +71,7 @@ public class ClosableTabTitlePane {
66 if (parent != null) { 71 if (parent != null) {
67 Point pt = new Point(e.getXOnScreen(), e.getYOnScreen()); 72 Point pt = new Point(e.getXOnScreen(), e.getYOnScreen());
68 SwingUtilities.convertPointFromScreen(pt, parent); 73 SwingUtilities.convertPointFromScreen(pt, parent);
69 MouseEvent e1 = new MouseEvent( 74 MouseEvent e1 = new MouseEvent(parent, e.getID(), e.getWhen(), e.getModifiersEx(), (int) pt.getX(), (int) pt.getY(), e.getXOnScreen(), e.getYOnScreen(), e.getClickCount(), e.isPopupTrigger(), e.getButton());
70 parent,
71 e.getID(),
72 e.getWhen(),
73 e.getModifiersEx(),
74 (int) pt.getX(),
75 (int) pt.getY(),
76 e.getXOnScreen(),
77 e.getYOnScreen(),
78 e.getClickCount(),
79 e.isPopupTrigger(),
80 e.getButton()
81 );
82 parent.dispatchEvent(e1); 75 parent.dispatchEvent(e1);
83 } 76 }
84 } 77 }
@@ -91,11 +84,13 @@ public class ClosableTabTitlePane {
91 if (this.parent != null) { 84 if (this.parent != null) {
92 pane.removeChangeListener(cachedChangeListener); 85 pane.removeChangeListener(cachedChangeListener);
93 } 86 }
87
94 if (pane != null) { 88 if (pane != null) {
95 updateState(pane); 89 updateState(pane);
96 cachedChangeListener = e -> updateState(pane); 90 cachedChangeListener = e -> updateState(pane);
97 pane.addChangeListener(cachedChangeListener); 91 pane.addChangeListener(cachedChangeListener);
98 } 92 }
93
99 this.parent = pane; 94 this.parent = pane;
100 } 95 }
101 96
@@ -123,11 +118,12 @@ public class ClosableTabTitlePane {
123 public static ClosableTabTitlePane byUi(Component c) { 118 public static ClosableTabTitlePane byUi(Component c) {
124 if (c instanceof JComponent) { 119 if (c instanceof JComponent) {
125 Object prop = ((JComponent) c).getClientProperty(ClosableTabTitlePane.class); 120 Object prop = ((JComponent) c).getClientProperty(ClosableTabTitlePane.class);
121
126 if (prop instanceof ClosableTabTitlePane) { 122 if (prop instanceof ClosableTabTitlePane) {
127 return (ClosableTabTitlePane) prop; 123 return (ClosableTabTitlePane) prop;
128 } 124 }
129 } 125 }
126
130 return null; 127 return null;
131 } 128 }
132
133} 129}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/DeobfPanel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/DeobfPanel.java
index 10fc5e1..5d1d0f2 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/DeobfPanel.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/DeobfPanel.java
@@ -15,7 +15,6 @@ import cuchaz.enigma.gui.util.GuiUtil;
15import cuchaz.enigma.utils.I18n; 15import cuchaz.enigma.utils.I18n;
16 16
17public class DeobfPanel extends JPanel { 17public class DeobfPanel extends JPanel {
18
19 public final ClassSelector deobfClasses; 18 public final ClassSelector deobfClasses;
20 private final JLabel title = new JLabel(); 19 private final JLabel title = new JLabel();
21 20
@@ -44,6 +43,7 @@ public class DeobfPanel extends JPanel {
44 if (SwingUtilities.isRightMouseButton(e)) { 43 if (SwingUtilities.isRightMouseButton(e)) {
45 deobfClasses.setSelectionRow(deobfClasses.getClosestRowForLocation(e.getX(), e.getY())); 44 deobfClasses.setSelectionRow(deobfClasses.getClosestRowForLocation(e.getX(), e.getY()));
46 int i = deobfClasses.getRowForPath(deobfClasses.getSelectionPath()); 45 int i = deobfClasses.getRowForPath(deobfClasses.getSelectionPath());
46
47 if (i != -1) { 47 if (i != -1) {
48 deobfPanelPopupMenu.show(deobfClasses, e.getX(), e.getY()); 48 deobfPanelPopupMenu.show(deobfClasses, e.getX(), e.getY());
49 } 49 }
@@ -54,5 +54,4 @@ public class DeobfPanel extends JPanel {
54 this.title.setText(I18n.translate(gui.isSingleClassTree() ? "info_panel.classes" : "info_panel.classes.deobfuscated")); 54 this.title.setText(I18n.translate(gui.isSingleClassTree() ? "info_panel.classes" : "info_panel.classes.deobfuscated"));
55 this.deobfPanelPopupMenu.retranslateUi(); 55 this.deobfPanelPopupMenu.retranslateUi();
56 } 56 }
57
58} 57}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java
index f4b190b..cb74cec 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java
@@ -1,14 +1,35 @@
1package cuchaz.enigma.gui.panels; 1package cuchaz.enigma.gui.panels;
2 2
3import java.awt.*; 3import java.awt.Color;
4import java.awt.event.*; 4import java.awt.Component;
5import java.awt.Font;
6import java.awt.GridBagConstraints;
7import java.awt.GridBagLayout;
8import java.awt.GridLayout;
9import java.awt.Rectangle;
10import java.awt.event.ActionEvent;
11import java.awt.event.ActionListener;
12import java.awt.event.KeyAdapter;
13import java.awt.event.KeyEvent;
14import java.awt.event.MouseAdapter;
15import java.awt.event.MouseEvent;
5import java.util.ArrayList; 16import java.util.ArrayList;
6import java.util.Collection; 17import java.util.Collection;
7import java.util.List; 18import java.util.List;
8import java.util.Map; 19import java.util.Map;
9 20
10import javax.annotation.Nullable; 21import javax.annotation.Nullable;
11import javax.swing.*; 22import javax.swing.JButton;
23import javax.swing.JComponent;
24import javax.swing.JEditorPane;
25import javax.swing.JLabel;
26import javax.swing.JPanel;
27import javax.swing.JProgressBar;
28import javax.swing.JScrollPane;
29import javax.swing.JSeparator;
30import javax.swing.JTextArea;
31import javax.swing.SwingUtilities;
32import javax.swing.Timer;
12import javax.swing.text.BadLocationException; 33import javax.swing.text.BadLocationException;
13import javax.swing.text.Document; 34import javax.swing.text.Document;
14import javax.swing.text.Highlighter.HighlightPainter; 35import javax.swing.text.Highlighter.HighlightPainter;
@@ -46,7 +67,6 @@ import cuchaz.enigma.utils.I18n;
46import cuchaz.enigma.utils.Result; 67import cuchaz.enigma.utils.Result;
47 68
48public class EditorPanel { 69public class EditorPanel {
49
50 private final JPanel ui = new JPanel(); 70 private final JPanel ui = new JPanel();
51 private final JEditorPane editor = new JEditorPane(); 71 private final JEditorPane editor = new JEditorPane();
52 private final JScrollPane editorScrollPane = new JScrollPane(this.editor); 72 private final JScrollPane editorScrollPane = new JScrollPane(this.editor);
@@ -123,18 +143,19 @@ public class EditorPanel {
123 @Override 143 @Override
124 public void mouseReleased(MouseEvent e) { 144 public void mouseReleased(MouseEvent e) {
125 switch (e.getButton()) { 145 switch (e.getButton()) {
126 case MouseEvent.BUTTON3: // Right click 146 case MouseEvent.BUTTON3: // Right click
127 EditorPanel.this.editor.setCaretPosition(EditorPanel.this.editor.viewToModel(e.getPoint())); 147 EditorPanel.this.editor.setCaretPosition(EditorPanel.this.editor.viewToModel(e.getPoint()));
128 break; 148 break;
129 149
130 case 4: // Back navigation 150 case 4: // Back navigation
131 gui.getController().openPreviousReference(); 151 gui.getController().openPreviousReference();
132 break; 152 break;
133 153
134 case 5: // Forward navigation 154 case 5: // Forward navigation
135 gui.getController().openNextReference(); 155 gui.getController().openNextReference();
136 break; 156 break;
137 } 157 }
158
138 EditorPanel.this.mouseIsPressed = false; 159 EditorPanel.this.mouseIsPressed = false;
139 } 160 }
140 }); 161 });
@@ -143,31 +164,36 @@ public class EditorPanel {
143 public void keyPressed(KeyEvent event) { 164 public void keyPressed(KeyEvent event) {
144 if (event.isControlDown()) { 165 if (event.isControlDown()) {
145 EditorPanel.this.shouldNavigateOnClick = false; 166 EditorPanel.this.shouldNavigateOnClick = false;
146 if (EditorPanel.this.popupMenu.handleKeyEvent(event)) return; 167
168 if (EditorPanel.this.popupMenu.handleKeyEvent(event)) {
169 return;
170 }
171
147 switch (event.getKeyCode()) { 172 switch (event.getKeyCode()) {
148 case KeyEvent.VK_F5: 173 case KeyEvent.VK_F5:
149 if (EditorPanel.this.classHandle != null) { 174 if (EditorPanel.this.classHandle != null) {
150 EditorPanel.this.classHandle.invalidate(); 175 EditorPanel.this.classHandle.invalidate();
151 } 176 }
152 break; 177
153 178 break;
154 case KeyEvent.VK_F: 179
155 // prevent navigating on click when quick find activated 180 case KeyEvent.VK_F:
156 break; 181 // prevent navigating on click when quick find activated
157 182 break;
158 case KeyEvent.VK_ADD: 183
159 case KeyEvent.VK_EQUALS: 184 case KeyEvent.VK_ADD:
160 case KeyEvent.VK_PLUS: 185 case KeyEvent.VK_EQUALS:
161 offsetEditorZoom(2); 186 case KeyEvent.VK_PLUS:
162 break; 187 offsetEditorZoom(2);
163 case KeyEvent.VK_SUBTRACT: 188 break;
164 case KeyEvent.VK_MINUS: 189 case KeyEvent.VK_SUBTRACT:
165 offsetEditorZoom(-2); 190 case KeyEvent.VK_MINUS:
166 break; 191 offsetEditorZoom(-2);
167 192 break;
168 default: 193
169 EditorPanel.this.shouldNavigateOnClick = true; // CTRL 194 default:
170 break; 195 EditorPanel.this.shouldNavigateOnClick = true; // CTRL
196 break;
171 } 197 }
172 } 198 }
173 } 199 }
@@ -175,8 +201,14 @@ public class EditorPanel {
175 @Override 201 @Override
176 public void keyTyped(KeyEvent event) { 202 public void keyTyped(KeyEvent event) {
177 EntryReference<Entry<?>, Entry<?>> ref = EditorPanel.this.getCursorReference(); 203 EntryReference<Entry<?>, Entry<?>> ref = EditorPanel.this.getCursorReference();
178 if (ref == null) return; 204
179 if (!EditorPanel.this.controller.project.isRenamable(ref)) return; 205 if (ref == null) {
206 return;
207 }
208
209 if (!EditorPanel.this.controller.project.isRenamable(ref)) {
210 return;
211 }
180 212
181 if (!event.isControlDown() && !event.isAltDown() && Character.isJavaIdentifierPart(event.getKeyChar())) { 213 if (!event.isControlDown() && !event.isAltDown() && Character.isJavaIdentifierPart(event.getKeyChar())) {
182 EnigmaProject project = gui.getController().project; 214 EnigmaProject project = gui.getController().project;
@@ -184,8 +216,10 @@ public class EditorPanel {
184 Entry<?> entry = reference.getNameableEntry(); 216 Entry<?> entry = reference.getNameableEntry();
185 217
186 String name = String.valueOf(event.getKeyChar()); 218 String name = String.valueOf(event.getKeyChar());
219
187 if (entry instanceof ClassEntry && ((ClassEntry) entry).getParent() == null) { 220 if (entry instanceof ClassEntry && ((ClassEntry) entry).getParent() == null) {
188 String packageName = ((ClassEntry) entry).getPackageName(); 221 String packageName = ((ClassEntry) entry).getPackageName();
222
189 if (packageName != null) { 223 if (packageName != null) {
190 name = packageName + "/" + name; 224 name = packageName + "/" + name;
191 } 225 }
@@ -207,12 +241,14 @@ public class EditorPanel {
207 if ((this.editorLaf == null || this.editorLaf != laf)) { 241 if ((this.editorLaf == null || this.editorLaf != laf)) {
208 this.editor.updateUI(); 242 this.editor.updateUI();
209 this.editor.setBackground(UiConfig.getEditorBackgroundColor()); 243 this.editor.setBackground(UiConfig.getEditorBackgroundColor());
244
210 if (this.editorLaf != null) { 245 if (this.editorLaf != null) {
211 this.classHandle.invalidateMapped(); 246 this.classHandle.invalidateMapped();
212 } 247 }
213 248
214 this.editorLaf = laf; 249 this.editorLaf = laf;
215 } 250 }
251
216 this.boxHighlightPainters = boxHighlightPainters; 252 this.boxHighlightPainters = boxHighlightPainters;
217 }; 253 };
218 254
@@ -223,19 +259,23 @@ public class EditorPanel {
223 public static EditorPanel byUi(Component ui) { 259 public static EditorPanel byUi(Component ui) {
224 if (ui instanceof JComponent) { 260 if (ui instanceof JComponent) {
225 Object prop = ((JComponent) ui).getClientProperty(EditorPanel.class); 261 Object prop = ((JComponent) ui).getClientProperty(EditorPanel.class);
262
226 if (prop instanceof EditorPanel) { 263 if (prop instanceof EditorPanel) {
227 return (EditorPanel) prop; 264 return (EditorPanel) prop;
228 } 265 }
229 } 266 }
267
230 return null; 268 return null;
231 } 269 }
232 270
233 public void setClassHandle(ClassHandle handle) { 271 public void setClassHandle(ClassHandle handle) {
234 ClassEntry old = null; 272 ClassEntry old = null;
273
235 if (this.classHandle != null) { 274 if (this.classHandle != null) {
236 old = this.classHandle.getRef(); 275 old = this.classHandle.getRef();
237 this.classHandle.close(); 276 this.classHandle.close();
238 } 277 }
278
239 setClassHandle0(old, handle); 279 setClassHandle0(old, handle);
240 } 280 }
241 281
@@ -299,53 +339,61 @@ public class EditorPanel {
299 } else { 339 } else {
300 this.displayError(res.unwrapErr()); 340 this.displayError(res.unwrapErr());
301 } 341 }
342
302 this.nextReference = null; 343 this.nextReference = null;
303 }); 344 });
304 } 345 }
305 346
306 public void displayError(ClassHandleError t) { 347 public void displayError(ClassHandleError t) {
307 this.setDisplayMode(DisplayMode.ERRORED); 348 this.setDisplayMode(DisplayMode.ERRORED);
349
308 String str = switch (t.type) { 350 String str = switch (t.type) {
309 case DECOMPILE -> "editor.decompile_error"; 351 case DECOMPILE -> "editor.decompile_error";
310 case REMAP -> "editor.remap_error"; 352 case REMAP -> "editor.remap_error";
311 }; 353 };
354
312 this.errorLabel.setText(I18n.translate(str)); 355 this.errorLabel.setText(I18n.translate(str));
313 this.errorTextArea.setText(t.getStackTrace()); 356 this.errorTextArea.setText(t.getStackTrace());
314 this.errorTextArea.setCaretPosition(0); 357 this.errorTextArea.setCaretPosition(0);
315 } 358 }
316 359
317 public void setDisplayMode(DisplayMode mode) { 360 public void setDisplayMode(DisplayMode mode) {
318 if (this.mode == mode) return; 361 if (this.mode == mode) {
362 return;
363 }
364
319 this.ui.removeAll(); 365 this.ui.removeAll();
366
320 switch (mode) { 367 switch (mode) {
321 case INACTIVE: 368 case INACTIVE:
322 break; 369 break;
323 case IN_PROGRESS: { 370 case IN_PROGRESS: {
324 // make progress bar start from the left every time 371 // make progress bar start from the left every time
325 this.decompilingProgressBar.setIndeterminate(false); 372 this.decompilingProgressBar.setIndeterminate(false);
326 this.decompilingProgressBar.setIndeterminate(true); 373 this.decompilingProgressBar.setIndeterminate(true);
327 374
328 this.ui.setLayout(new GridBagLayout()); 375 this.ui.setLayout(new GridBagLayout());
329 GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2); 376 GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2);
330 this.ui.add(this.decompilingLabel, cb.pos(0, 0).anchor(GridBagConstraints.SOUTH).build()); 377 this.ui.add(this.decompilingLabel, cb.pos(0, 0).anchor(GridBagConstraints.SOUTH).build());
331 this.ui.add(this.decompilingProgressBar, cb.pos(0, 1).anchor(GridBagConstraints.NORTH).build()); 378 this.ui.add(this.decompilingProgressBar, cb.pos(0, 1).anchor(GridBagConstraints.NORTH).build());
332 break; 379 break;
333 } 380 }
334 case SUCCESS: { 381 case SUCCESS: {
335 this.ui.setLayout(new GridLayout(1, 1, 0, 0)); 382 this.ui.setLayout(new GridLayout(1, 1, 0, 0));
336 this.ui.add(this.editorScrollPane); 383 this.ui.add(this.editorScrollPane);
337 break; 384 break;
338 } 385 }
339 case ERRORED: { 386 case ERRORED: {
340 this.ui.setLayout(new GridBagLayout()); 387 this.ui.setLayout(new GridBagLayout());
341 GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2).weight(1.0, 0.0).anchor(GridBagConstraints.WEST); 388 GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2).weight(1.0, 0.0).anchor(GridBagConstraints.WEST);
342 this.ui.add(this.errorLabel, cb.pos(0, 0).build()); 389 this.ui.add(this.errorLabel, cb.pos(0, 0).build());
343 this.ui.add(new JSeparator(JSeparator.HORIZONTAL), cb.pos(0, 1).fill(GridBagConstraints.HORIZONTAL).build()); 390 this.ui.add(new JSeparator(JSeparator.HORIZONTAL), cb.pos(0, 1).fill(GridBagConstraints.HORIZONTAL).build());
344 this.ui.add(this.errorScrollPane, cb.pos(0, 2).weight(1.0, 1.0).fill(GridBagConstraints.BOTH).build()); 391 this.ui.add(this.errorScrollPane, cb.pos(0, 2).weight(1.0, 1.0).fill(GridBagConstraints.BOTH).build());
345 this.ui.add(this.retryButton, cb.pos(0, 3).weight(0.0, 0.0).anchor(GridBagConstraints.EAST).build()); 392 this.ui.add(this.retryButton, cb.pos(0, 3).weight(0.0, 0.0).anchor(GridBagConstraints.EAST).build());
346 break; 393 break;
347 } 394 }
348 } 395 }
396
349 this.ui.validate(); 397 this.ui.validate();
350 this.ui.repaint(); 398 this.ui.repaint();
351 this.mode = mode; 399 this.mode = mode;
@@ -353,6 +401,7 @@ public class EditorPanel {
353 401
354 public void offsetEditorZoom(int zoomAmount) { 402 public void offsetEditorZoom(int zoomAmount) {
355 int newResult = this.fontSize + zoomAmount; 403 int newResult = this.fontSize + zoomAmount;
404
356 if (newResult > 8 && newResult < 72) { 405 if (newResult > 8 && newResult < 72) {
357 this.fontSize = newResult; 406 this.fontSize = newResult;
358 this.editor.setFont(ScaleUtil.getFont(this.editor.getFont().getFontName(), Font.PLAIN, this.fontSize)); 407 this.editor.setFont(ScaleUtil.getFont(this.editor.getFont().getFontName(), Font.PLAIN, this.fontSize));
@@ -365,8 +414,13 @@ public class EditorPanel {
365 } 414 }
366 415
367 public void onCaretMove(int pos, boolean fromClick) { 416 public void onCaretMove(int pos, boolean fromClick) {
368 if (this.settingSource) return; 417 if (this.settingSource) {
369 if (this.controller.project == null) return; 418 return;
419 }
420
421 if (this.controller.project == null) {
422 return;
423 }
370 424
371 EntryRemapper mapper = this.controller.project.getMapper(); 425 EntryRemapper mapper = this.controller.project.getMapper();
372 Token token = getToken(pos); 426 Token token = getToken(pos);
@@ -378,10 +432,12 @@ public class EditorPanel {
378 if (referenceEntry != null && this.shouldNavigateOnClick && fromClick) { 432 if (referenceEntry != null && this.shouldNavigateOnClick && fromClick) {
379 this.shouldNavigateOnClick = false; 433 this.shouldNavigateOnClick = false;
380 Entry<?> navigationEntry = referenceEntry; 434 Entry<?> navigationEntry = referenceEntry;
435
381 if (this.cursorReference.context == null) { 436 if (this.cursorReference.context == null) {
382 EntryResolver resolver = mapper.getObfResolver(); 437 EntryResolver resolver = mapper.getObfResolver();
383 navigationEntry = resolver.resolveFirstEntry(referenceEntry, ResolutionStrategy.RESOLVE_ROOT); 438 navigationEntry = resolver.resolveFirstEntry(referenceEntry, ResolutionStrategy.RESOLVE_ROOT);
384 } 439 }
440
385 this.controller.navigateTo(navigationEntry); 441 this.controller.navigateTo(navigationEntry);
386 } 442 }
387 } 443 }
@@ -398,6 +454,7 @@ public class EditorPanel {
398 if (this.source == null) { 454 if (this.source == null) {
399 return null; 455 return null;
400 } 456 }
457
401 return this.source.getIndex().getReferenceToken(pos); 458 return this.source.getIndex().getReferenceToken(pos);
402 } 459 }
403 460
@@ -406,16 +463,22 @@ public class EditorPanel {
406 if (this.source == null) { 463 if (this.source == null) {
407 return null; 464 return null;
408 } 465 }
466
409 return this.source.getIndex().getReference(token); 467 return this.source.getIndex().getReference(token);
410 } 468 }
411 469
412 public void setSource(DecompiledClassSource source) { 470 public void setSource(DecompiledClassSource source) {
413 this.setDisplayMode(DisplayMode.SUCCESS); 471 this.setDisplayMode(DisplayMode.SUCCESS);
414 if (source == null) return; 472
473 if (source == null) {
474 return;
475 }
476
415 try { 477 try {
416 this.settingSource = true; 478 this.settingSource = true;
417 479
418 int newCaretPos = 0; 480 int newCaretPos = 0;
481
419 if (this.source != null && this.source.getEntry().equals(source.getEntry())) { 482 if (this.source != null && this.source.getEntry().equals(source.getEntry())) {
420 int caretPos = this.editor.getCaretPosition(); 483 int caretPos = this.editor.getCaretPosition();
421 484
@@ -441,9 +504,11 @@ public class EditorPanel {
441 this.source = source; 504 this.source = source;
442 this.editor.getHighlighter().removeAllHighlights(); 505 this.editor.getHighlighter().removeAllHighlights();
443 this.editor.setText(source.toString()); 506 this.editor.setText(source.toString());
507
444 if (this.source != null) { 508 if (this.source != null) {
445 this.editor.setCaretPosition(newCaretPos); 509 this.editor.setCaretPosition(newCaretPos);
446 } 510 }
511
447 setHighlightedTokens(source.getHighlightedTokens()); 512 setHighlightedTokens(source.getHighlightedTokens());
448 setCursorReference(getReference(getToken(this.editor.getCaretPosition()))); 513 setCursorReference(getReference(getToken(this.editor.getCaretPosition())));
449 } finally { 514 } finally {
@@ -515,10 +580,16 @@ public class EditorPanel {
515 * @param reference 580 * @param reference
516 */ 581 */
517 private void showReference0(EntryReference<Entry<?>, Entry<?>> reference) { 582 private void showReference0(EntryReference<Entry<?>, Entry<?>> reference) {
518 if (this.source == null) return; 583 if (this.source == null) {
519 if (reference == null) return; 584 return;
585 }
586
587 if (reference == null) {
588 return;
589 }
520 590
521 List<Token> tokens = this.controller.getTokensForReference(this.source, reference); 591 List<Token> tokens = this.controller.getTokensForReference(this.source, reference);
592
522 if (tokens.isEmpty()) { 593 if (tokens.isEmpty()) {
523 // DEBUG 594 // DEBUG
524 System.err.println(String.format("WARNING: no tokens found for %s in %s", reference, this.classHandle.getRef())); 595 System.err.println(String.format("WARNING: no tokens found for %s in %s", reference, this.classHandle.getRef()));
@@ -531,6 +602,7 @@ public class EditorPanel {
531 if (token == null) { 602 if (token == null) {
532 throw new IllegalArgumentException("Token cannot be null!"); 603 throw new IllegalArgumentException("Token cannot be null!");
533 } 604 }
605
534 navigateToToken(token, SelectionHighlightPainter.INSTANCE); 606 navigateToToken(token, SelectionHighlightPainter.INSTANCE);
535 } 607 }
536 608
@@ -546,9 +618,11 @@ public class EditorPanel {
546 // make sure the token is visible in the scroll window 618 // make sure the token is visible in the scroll window
547 Rectangle start = this.editor.modelToView(token.start); 619 Rectangle start = this.editor.modelToView(token.start);
548 Rectangle end = this.editor.modelToView(token.end); 620 Rectangle end = this.editor.modelToView(token.end);
621
549 if (start == null || end == null) { 622 if (start == null || end == null) {
550 return; 623 return;
551 } 624 }
625
552 Rectangle show = start.union(end); 626 Rectangle show = start.union(end);
553 show.grow(start.width * 10, start.height * 6); 627 show.grow(start.width * 10, start.height * 6);
554 SwingUtilities.invokeLater(() -> this.editor.scrollRectToVisible(show)); 628 SwingUtilities.invokeLater(() -> this.editor.scrollRectToVisible(show));
@@ -625,5 +699,4 @@ public class EditorPanel {
625 SUCCESS, 699 SUCCESS,
626 ERRORED, 700 ERRORED,
627 } 701 }
628
629} 702}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/IdentifierPanel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/IdentifierPanel.java
index e71894d..7b75f1a 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/IdentifierPanel.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/IdentifierPanel.java
@@ -25,12 +25,15 @@ import cuchaz.enigma.gui.util.ScaleUtil;
25import cuchaz.enigma.translation.mapping.AccessModifier; 25import cuchaz.enigma.translation.mapping.AccessModifier;
26import cuchaz.enigma.translation.mapping.EntryChange; 26import cuchaz.enigma.translation.mapping.EntryChange;
27import cuchaz.enigma.translation.mapping.EntryMapping; 27import cuchaz.enigma.translation.mapping.EntryMapping;
28import cuchaz.enigma.translation.representation.entry.*; 28import cuchaz.enigma.translation.representation.entry.ClassEntry;
29import cuchaz.enigma.translation.representation.entry.Entry;
30import cuchaz.enigma.translation.representation.entry.FieldEntry;
31import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
32import cuchaz.enigma.translation.representation.entry.MethodEntry;
29import cuchaz.enigma.utils.I18n; 33import cuchaz.enigma.utils.I18n;
30import cuchaz.enigma.utils.validation.ValidationContext; 34import cuchaz.enigma.utils.validation.ValidationContext;
31 35
32public class IdentifierPanel { 36public class IdentifierPanel {
33
34 private final Gui gui; 37 private final Gui gui;
35 38
36 private final JPanel ui = new JPanel(); 39 private final JPanel ui = new JPanel();
@@ -57,7 +60,9 @@ public class IdentifierPanel {
57 } 60 }
58 61
59 public boolean startRenaming() { 62 public boolean startRenaming() {
60 if (this.nameField == null) return false; 63 if (this.nameField == null) {
64 return false;
65 }
61 66
62 this.nameField.startEditing(); 67 this.nameField.startEditing();
63 68
@@ -65,7 +70,9 @@ public class IdentifierPanel {
65 } 70 }
66 71
67 public boolean startRenaming(String text) { 72 public boolean startRenaming(String text) {
68 if (this.nameField == null) return false; 73 if (this.nameField == null) {
74 return false;
75 }
69 76
70 this.nameField.startEditing(); 77 this.nameField.startEditing();
71 this.nameField.setEditText(text); 78 this.nameField.setEditText(text);
@@ -84,6 +91,7 @@ public class IdentifierPanel {
84 91
85 TableHelper th = new TableHelper(this.ui, this.entry, this.gui); 92 TableHelper th = new TableHelper(this.ui, this.entry, this.gui);
86 th.begin(); 93 th.begin();
94
87 if (this.entry == null) { 95 if (this.entry == null) {
88 this.ui.setEnabled(false); 96 this.ui.setEnabled(false);
89 } else { 97 } else {
@@ -102,8 +110,10 @@ public class IdentifierPanel {
102 th.addModifierRow(I18n.translate("info_panel.identifier.modifier"), EditableType.FIELD, this::onModifierChanged); 110 th.addModifierRow(I18n.translate("info_panel.identifier.modifier"), EditableType.FIELD, this::onModifierChanged);
103 } else if (deobfEntry instanceof MethodEntry) { 111 } else if (deobfEntry instanceof MethodEntry) {
104 MethodEntry me = (MethodEntry) deobfEntry; 112 MethodEntry me = (MethodEntry) deobfEntry;
113
105 if (me.isConstructor()) { 114 if (me.isConstructor()) {
106 ClassEntry ce = me.getParent(); 115 ClassEntry ce = me.getParent();
116
107 if (ce != null) { 117 if (ce != null) {
108 String name = ce.isInnerClass() ? ce.getName() : ce.getFullName(); 118 String name = ce.isInnerClass() ? ce.getName() : ce.getFullName();
109 this.nameField = th.addRenameTextField(EditableType.CLASS, name); 119 this.nameField = th.addRenameTextField(EditableType.CLASS, name);
@@ -112,6 +122,7 @@ public class IdentifierPanel {
112 this.nameField = th.addRenameTextField(EditableType.METHOD, me.getName()); 122 this.nameField = th.addRenameTextField(EditableType.METHOD, me.getName());
113 th.addStringRow(I18n.translate("info_panel.identifier.class"), me.getParent().getFullName()); 123 th.addStringRow(I18n.translate("info_panel.identifier.class"), me.getParent().getFullName());
114 } 124 }
125
115 th.addCopiableStringRow(I18n.translate("info_panel.identifier.method_descriptor"), me.getDesc().toString()); 126 th.addCopiableStringRow(I18n.translate("info_panel.identifier.method_descriptor"), me.getDesc().toString());
116 th.addModifierRow(I18n.translate("info_panel.identifier.modifier"), EditableType.METHOD, this::onModifierChanged); 127 th.addModifierRow(I18n.translate("info_panel.identifier.modifier"), EditableType.METHOD, this::onModifierChanged);
117 } else if (deobfEntry instanceof LocalVariableEntry) { 128 } else if (deobfEntry instanceof LocalVariableEntry) {
@@ -132,6 +143,7 @@ public class IdentifierPanel {
132 throw new IllegalStateException("unreachable"); 143 throw new IllegalStateException("unreachable");
133 } 144 }
134 } 145 }
146
135 th.end(); 147 th.end();
136 148
137 if (this.nameField != null) { 149 if (this.nameField != null) {
@@ -139,6 +151,7 @@ public class IdentifierPanel {
139 @Override 151 @Override
140 public void onStartEditing(ConvertingTextField field) { 152 public void onStartEditing(ConvertingTextField field) {
141 int i = field.getText().lastIndexOf('/'); 153 int i = field.getText().lastIndexOf('/');
154
142 if (i != -1) { 155 if (i != -1) {
143 field.selectSubstring(i + 1); 156 field.selectSubstring(i + 1);
144 } 157 }
@@ -146,7 +159,10 @@ public class IdentifierPanel {
146 159
147 @Override 160 @Override
148 public boolean tryStopEditing(ConvertingTextField field, boolean abort) { 161 public boolean tryStopEditing(ConvertingTextField field, boolean abort) {
149 if (abort) return true; 162 if (abort) {
163 return true;
164 }
165
150 vc.reset(); 166 vc.reset();
151 vc.setActiveElement(field); 167 vc.setActiveElement(field);
152 validateRename(field.getText()); 168 validateRename(field.getText());
@@ -162,6 +178,7 @@ public class IdentifierPanel {
162 } 178 }
163 179
164 EditorPanel e = gui.getActiveEditor(); 180 EditorPanel e = gui.getActiveEditor();
181
165 if (e != null) { 182 if (e != null) {
166 e.getEditor().requestFocusInWindow(); 183 e.getEditor().requestFocusInWindow();
167 } 184 }
@@ -192,13 +209,12 @@ public class IdentifierPanel {
192 } 209 }
193 210
194 private static final class TableHelper { 211 private static final class TableHelper {
195
196 private final Container c; 212 private final Container c;
197 private final Entry<?> e; 213 private final Entry<?> e;
198 private final Gui gui; 214 private final Gui gui;
199 private int row; 215 private int row;
200 216
201 public TableHelper(Container c, Entry<?> e, Gui gui) { 217 TableHelper(Container c, Entry<?> e, Gui gui) {
202 this.c = c; 218 this.c = c;
203 this.e = e; 219 this.e = e;
204 this.gui = gui; 220 this.gui = gui;
@@ -210,9 +226,7 @@ public class IdentifierPanel {
210 } 226 }
211 227
212 public void addRow(Component c1, Component c2) { 228 public void addRow(Component c1, Component c2) {
213 GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create() 229 GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2).anchor(GridBagConstraints.WEST);
214 .insets(2)
215 .anchor(GridBagConstraints.WEST);
216 c.add(c1, cb.pos(0, this.row).build()); 230 c.add(c1, cb.pos(0, this.row).build());
217 c.add(c2, cb.pos(1, this.row).weightX(1.0).fill(GridBagConstraints.HORIZONTAL).build()); 231 c.add(c2, cb.pos(1, this.row).weightX(1.0).fill(GridBagConstraints.HORIZONTAL).build());
218 232
@@ -239,12 +253,12 @@ public class IdentifierPanel {
239 } 253 }
240 254
241 public ConvertingTextField addRenameTextField(EditableType type, String c2) { 255 public ConvertingTextField addRenameTextField(EditableType type, String c2) {
242 String description = switch(type) { 256 String description = switch (type) {
243 case CLASS -> I18n.translate("info_panel.identifier.class"); 257 case CLASS -> I18n.translate("info_panel.identifier.class");
244 case METHOD -> I18n.translate("info_panel.identifier.method"); 258 case METHOD -> I18n.translate("info_panel.identifier.method");
245 case FIELD -> I18n.translate("info_panel.identifier.field"); 259 case FIELD -> I18n.translate("info_panel.identifier.field");
246 case PARAMETER, LOCAL_VARIABLE -> I18n.translate("info_panel.identifier.variable"); 260 case PARAMETER, LOCAL_VARIABLE -> I18n.translate("info_panel.identifier.variable");
247 default -> throw new IllegalStateException("Unexpected value: " + type); 261 default -> throw new IllegalStateException("Unexpected value: " + type);
248 }; 262 };
249 263
250 if (this.gui.getController().project.isRenamable(e)) { 264 if (this.gui.getController().project.isRenamable(e)) {
@@ -296,7 +310,5 @@ public class IdentifierPanel {
296 // Add an empty panel with y-weight=1 so that all the other elements get placed at the top edge 310 // Add an empty panel with y-weight=1 so that all the other elements get placed at the top edge
297 c.add(new JPanel(), GridBagConstraintsBuilder.create().pos(0, row).weight(0.0, 1.0).build()); 311 c.add(new JPanel(), GridBagConstraintsBuilder.create().pos(0, row).weight(0.0, 1.0).build());
298 } 312 }
299
300 } 313 }
301
302} 314}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ObfPanel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ObfPanel.java
index 7783843..f82e666 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ObfPanel.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ObfPanel.java
@@ -13,7 +13,6 @@ import cuchaz.enigma.translation.representation.entry.ClassEntry;
13import cuchaz.enigma.utils.I18n; 13import cuchaz.enigma.utils.I18n;
14 14
15public class ObfPanel extends JPanel { 15public class ObfPanel extends JPanel {
16
17 public final ClassSelector obfClasses; 16 public final ClassSelector obfClasses;
18 private final JLabel title = new JLabel(); 17 private final JLabel title = new JLabel();
19 18
@@ -25,9 +24,11 @@ public class ObfPanel extends JPanel {
25 Comparator<ClassEntry> obfClassComparator = (a, b) -> { 24 Comparator<ClassEntry> obfClassComparator = (a, b) -> {
26 String aname = a.getFullName(); 25 String aname = a.getFullName();
27 String bname = b.getFullName(); 26 String bname = b.getFullName();
27
28 if (aname.length() != bname.length()) { 28 if (aname.length() != bname.length()) {
29 return aname.length() - bname.length(); 29 return aname.length() - bname.length();
30 } 30 }
31
31 return aname.compareTo(bname); 32 return aname.compareTo(bname);
32 }; 33 };
33 34
@@ -45,5 +46,4 @@ public class ObfPanel extends JPanel {
45 public void retranslateUi() { 46 public void retranslateUi() {
46 this.title.setText(I18n.translate("info_panel.classes.obfuscated")); 47 this.title.setText(I18n.translate("info_panel.classes.obfuscated"));
47 } 48 }
48
49} 49}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/StructurePanel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/StructurePanel.java
index ccded45..571c638 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/StructurePanel.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/StructurePanel.java
@@ -1,9 +1,16 @@
1package cuchaz.enigma.gui.panels; 1package cuchaz.enigma.gui.panels;
2 2
3import java.awt.*; 3import java.awt.BorderLayout;
4import java.awt.Component;
5import java.awt.GridBagConstraints;
6import java.awt.GridBagLayout;
4import java.awt.event.MouseEvent; 7import java.awt.event.MouseEvent;
5 8
6import javax.swing.*; 9import javax.swing.JComboBox;
10import javax.swing.JLabel;
11import javax.swing.JPanel;
12import javax.swing.JScrollPane;
13import javax.swing.JTree;
7import javax.swing.tree.DefaultTreeCellRenderer; 14import javax.swing.tree.DefaultTreeCellRenderer;
8import javax.swing.tree.DefaultTreeModel; 15import javax.swing.tree.DefaultTreeModel;
9import javax.swing.tree.TreeNode; 16import javax.swing.tree.TreeNode;
@@ -23,144 +30,144 @@ import cuchaz.enigma.translation.representation.entry.ParentedEntry;
23import cuchaz.enigma.utils.I18n; 30import cuchaz.enigma.utils.I18n;
24 31
25public class StructurePanel { 32public class StructurePanel {
26 private final Gui gui; 33 private final Gui gui;
27 34
28 private final JPanel panel = new JPanel(new BorderLayout()); 35 private final JPanel panel = new JPanel(new BorderLayout());
29 36
30 private final JPanel optionsPanel; 37 private final JPanel optionsPanel;
31 38
32 private final JLabel obfuscationVisibilityLabel = new JLabel(); 39 private final JLabel obfuscationVisibilityLabel = new JLabel();
33 private final JLabel documentationVisibilityLabel = new JLabel(); 40 private final JLabel documentationVisibilityLabel = new JLabel();
34 private final JLabel sortingOrderLabel = new JLabel(); 41 private final JLabel sortingOrderLabel = new JLabel();
35 42
36 private final JComboBox<StructureTreeOptions.ObfuscationVisibility> obfuscationVisibility; 43 private final JComboBox<StructureTreeOptions.ObfuscationVisibility> obfuscationVisibility;
37 private final JComboBox<StructureTreeOptions.DocumentationVisibility> documentationVisibility; 44 private final JComboBox<StructureTreeOptions.DocumentationVisibility> documentationVisibility;
38 private final JComboBox<StructureTreeOptions.SortingOrder> sortingOrder; 45 private final JComboBox<StructureTreeOptions.SortingOrder> sortingOrder;
39 46
40 private final JTree structureTree; 47 private final JTree structureTree;
41 48
42 public StructurePanel(Gui gui) { 49 public StructurePanel(Gui gui) {
43 this.gui = gui; 50 this.gui = gui;
44 51
45 this.optionsPanel = new JPanel(new GridBagLayout()); 52 this.optionsPanel = new JPanel(new GridBagLayout());
46 this.optionsPanel.setVisible(false); 53 this.optionsPanel.setVisible(false);
47 54
48 GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(5).fill(GridBagConstraints.HORIZONTAL); 55 GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(5).fill(GridBagConstraints.HORIZONTAL);
49 56
50 this.optionsPanel.add(this.obfuscationVisibilityLabel, cb.pos(0, 0).build()); 57 this.optionsPanel.add(this.obfuscationVisibilityLabel, cb.pos(0, 0).build());
51 this.obfuscationVisibility = new JComboBox<>(StructureTreeOptions.ObfuscationVisibility.values()); 58 this.obfuscationVisibility = new JComboBox<>(StructureTreeOptions.ObfuscationVisibility.values());
52 this.obfuscationVisibility.setRenderer(new StructureOptionListCellRenderer()); 59 this.obfuscationVisibility.setRenderer(new StructureOptionListCellRenderer());
53 this.obfuscationVisibility.addActionListener(event -> this.showStructure(gui.getActiveEditor())); 60 this.obfuscationVisibility.addActionListener(event -> this.showStructure(gui.getActiveEditor()));
54 this.optionsPanel.add(this.obfuscationVisibility, cb.pos(1, 0).build()); 61 this.optionsPanel.add(this.obfuscationVisibility, cb.pos(1, 0).build());
55 62
56 this.optionsPanel.add(this.documentationVisibilityLabel, cb.pos(0, 1).build()); 63 this.optionsPanel.add(this.documentationVisibilityLabel, cb.pos(0, 1).build());
57 this.documentationVisibility = new JComboBox<>(StructureTreeOptions.DocumentationVisibility.values()); 64 this.documentationVisibility = new JComboBox<>(StructureTreeOptions.DocumentationVisibility.values());
58 this.documentationVisibility.setRenderer(new StructureOptionListCellRenderer()); 65 this.documentationVisibility.setRenderer(new StructureOptionListCellRenderer());
59 this.documentationVisibility.addActionListener(event -> this.showStructure(gui.getActiveEditor())); 66 this.documentationVisibility.addActionListener(event -> this.showStructure(gui.getActiveEditor()));
60 this.optionsPanel.add(this.documentationVisibility, cb.pos(1, 1).build()); 67 this.optionsPanel.add(this.documentationVisibility, cb.pos(1, 1).build());
61 68
62 this.optionsPanel.add(this.sortingOrderLabel, cb.pos(0, 2).build()); 69 this.optionsPanel.add(this.sortingOrderLabel, cb.pos(0, 2).build());
63 this.sortingOrder = new JComboBox<>(StructureTreeOptions.SortingOrder.values()); 70 this.sortingOrder = new JComboBox<>(StructureTreeOptions.SortingOrder.values());
64 this.sortingOrder.setRenderer(new StructureOptionListCellRenderer()); 71 this.sortingOrder.setRenderer(new StructureOptionListCellRenderer());
65 this.sortingOrder.addActionListener(event -> this.showStructure(gui.getActiveEditor())); 72 this.sortingOrder.addActionListener(event -> this.showStructure(gui.getActiveEditor()));
66 this.optionsPanel.add(this.sortingOrder, cb.pos(1, 2).build()); 73 this.optionsPanel.add(this.sortingOrder, cb.pos(1, 2).build());
67 74
68 this.structureTree = new JTree(); 75 this.structureTree = new JTree();
69 this.structureTree.setModel(null); 76 this.structureTree.setModel(null);
70 this.structureTree.setCellRenderer(new StructureTreeCellRenderer(gui)); 77 this.structureTree.setCellRenderer(new StructureTreeCellRenderer(gui));
71 this.structureTree.setSelectionModel(new SingleTreeSelectionModel()); 78 this.structureTree.setSelectionModel(new SingleTreeSelectionModel());
72 this.structureTree.setShowsRootHandles(true); 79 this.structureTree.setShowsRootHandles(true);
73 this.structureTree.addMouseListener(GuiUtil.onMouseClick(this::onClick)); 80 this.structureTree.addMouseListener(GuiUtil.onMouseClick(this::onClick));
74 81
75 this.retranslateUi(); 82 this.retranslateUi();
76 83
77 this.panel.add(this.optionsPanel, BorderLayout.NORTH); 84 this.panel.add(this.optionsPanel, BorderLayout.NORTH);
78 this.panel.add(new JScrollPane(this.structureTree)); 85 this.panel.add(new JScrollPane(this.structureTree));
79 } 86 }
80 87
81 public void showStructure(EditorPanel editor) { 88 public void showStructure(EditorPanel editor) {
82 structureTree.setModel(null); 89 structureTree.setModel(null);
83 90
84 if (editor == null) { 91 if (editor == null) {
85 this.optionsPanel.setVisible(false); 92 this.optionsPanel.setVisible(false);
86 return; 93 return;
87 } 94 }
88 95
89 ClassEntry classEntry = editor.getClassHandle().getRef(); 96 ClassEntry classEntry = editor.getClassHandle().getRef();
90 if (classEntry == null) return; 97
91 98 if (classEntry == null) {
92 this.optionsPanel.setVisible(true); 99 return;
93 100 }
94 // get the class structure 101
95 StructureTreeNode node = this.gui.getController().getClassStructure(classEntry, this.getOptions()); 102 this.optionsPanel.setVisible(true);
96 103
97 // show the tree at the root 104 // get the class structure
98 TreePath path = GuiUtil.getPathToRoot(node); 105 StructureTreeNode node = this.gui.getController().getClassStructure(classEntry, this.getOptions());
99 structureTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); 106
100 structureTree.expandPath(path); 107 // show the tree at the root
101 structureTree.setSelectionRow(structureTree.getRowForPath(path)); 108 TreePath path = GuiUtil.getPathToRoot(node);
102 } 109 structureTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0)));
103 110 structureTree.expandPath(path);
104 private void onClick(MouseEvent event) { 111 structureTree.setSelectionRow(structureTree.getRowForPath(path));
105 if (event.getClickCount() >= 2 && event.getButton() == MouseEvent.BUTTON1) { 112 }
106 // get the selected node 113
107 TreePath path = structureTree.getSelectionPath(); 114 private void onClick(MouseEvent event) {
108 if (path == null) { 115 if (event.getClickCount() >= 2 && event.getButton() == MouseEvent.BUTTON1) {
109 return; 116 // get the selected node
110 } 117 TreePath path = structureTree.getSelectionPath();
111 118
112 Object node = path.getLastPathComponent(); 119 if (path == null) {
113 120 return;
114 if (node instanceof StructureTreeNode) { 121 }
115 this.gui.getController().navigateTo(((StructureTreeNode) node).getEntry()); 122
116 } 123 Object node = path.getLastPathComponent();
117 } 124
118 } 125 if (node instanceof StructureTreeNode) {
119 126 this.gui.getController().navigateTo(((StructureTreeNode) node).getEntry());
120 /** 127 }
121 * Creates and returns the options of this structure panel. 128 }
122 */ 129 }
123 private StructureTreeOptions getOptions() { 130
124 return new StructureTreeOptions( 131 /**
125 (StructureTreeOptions.ObfuscationVisibility) this.obfuscationVisibility.getSelectedItem(), 132 * Creates and returns the options of this structure panel.
126 (StructureTreeOptions.DocumentationVisibility) this.documentationVisibility.getSelectedItem(), 133 */
127 (StructureTreeOptions.SortingOrder) this.sortingOrder.getSelectedItem() 134 private StructureTreeOptions getOptions() {
128 ); 135 return new StructureTreeOptions((StructureTreeOptions.ObfuscationVisibility) this.obfuscationVisibility.getSelectedItem(), (StructureTreeOptions.DocumentationVisibility) this.documentationVisibility.getSelectedItem(), (StructureTreeOptions.SortingOrder) this.sortingOrder.getSelectedItem());
129 } 136 }
130 137
131 public void retranslateUi() { 138 public void retranslateUi() {
132 this.obfuscationVisibilityLabel.setText(I18n.translate("structure.options.obfuscation")); 139 this.obfuscationVisibilityLabel.setText(I18n.translate("structure.options.obfuscation"));
133 this.documentationVisibilityLabel.setText(I18n.translate("structure.options.documentation")); 140 this.documentationVisibilityLabel.setText(I18n.translate("structure.options.documentation"));
134 this.sortingOrderLabel.setText(I18n.translate("structure.options.sorting")); 141 this.sortingOrderLabel.setText(I18n.translate("structure.options.sorting"));
135 } 142 }
136 143
137 public JPanel getPanel() { 144 public JPanel getPanel() {
138 return this.panel; 145 return this.panel;
139 } 146 }
140 147
141 private static class StructureTreeCellRenderer extends DefaultTreeCellRenderer { 148 private static class StructureTreeCellRenderer extends DefaultTreeCellRenderer {
142 private final Gui gui; 149 private final Gui gui;
143 150
144 StructureTreeCellRenderer(Gui gui) { 151 StructureTreeCellRenderer(Gui gui) {
145 this.gui = gui; 152 this.gui = gui;
146 } 153 }
147 154
148 @Override 155 @Override
149 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { 156 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
150 Component c = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); 157 Component c = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
151 ParentedEntry<?> entry = ((StructureTreeNode) value).getEntry(); 158 ParentedEntry<?> entry = ((StructureTreeNode) value).getEntry();
152 159
153 if (entry instanceof ClassEntry classEntry) { 160 if (entry instanceof ClassEntry classEntry) {
154 this.setIcon(GuiUtil.getClassIcon(gui, classEntry)); 161 this.setIcon(GuiUtil.getClassIcon(gui, classEntry));
155 } else if (entry instanceof MethodEntry methodEntry) { 162 } else if (entry instanceof MethodEntry methodEntry) {
156 this.setIcon(GuiUtil.getMethodIcon(methodEntry)); 163 this.setIcon(GuiUtil.getMethodIcon(methodEntry));
157 } else if (entry instanceof FieldEntry) { 164 } else if (entry instanceof FieldEntry) {
158 this.setIcon(GuiUtil.FIELD_ICON); 165 this.setIcon(GuiUtil.FIELD_ICON);
159 } 166 }
160 167
161 this.setText("<html>" + ((StructureTreeNode) value).toHtml()); 168 this.setText("<html>" + ((StructureTreeNode) value).toHtml());
162 169
163 return c; 170 return c;
164 } 171 }
165 } 172 }
166} 173}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/CallsTreeCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/CallsTreeCellRenderer.java
index 0aa6510..3791a1e 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/CallsTreeCellRenderer.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/CallsTreeCellRenderer.java
@@ -1,45 +1,51 @@
1package cuchaz.enigma.gui.renderer; 1package cuchaz.enigma.gui.renderer;
2 2
3import cuchaz.enigma.analysis.*; 3import java.awt.Component;
4
5import javax.swing.JTree;
6import javax.swing.tree.DefaultTreeCellRenderer;
7
8import cuchaz.enigma.analysis.ClassReferenceTreeNode;
9import cuchaz.enigma.analysis.EntryReference;
10import cuchaz.enigma.analysis.FieldReferenceTreeNode;
11import cuchaz.enigma.analysis.MethodReferenceTreeNode;
12import cuchaz.enigma.analysis.ReferenceTreeNode;
4import cuchaz.enigma.gui.Gui; 13import cuchaz.enigma.gui.Gui;
5import cuchaz.enigma.gui.config.UiConfig; 14import cuchaz.enigma.gui.config.UiConfig;
6import cuchaz.enigma.gui.util.GuiUtil; 15import cuchaz.enigma.gui.util.GuiUtil;
7import cuchaz.enigma.translation.representation.entry.MethodEntry; 16import cuchaz.enigma.translation.representation.entry.MethodEntry;
8 17
9import javax.swing.*;
10import javax.swing.tree.DefaultTreeCellRenderer;
11import java.awt.*;
12
13public class CallsTreeCellRenderer extends DefaultTreeCellRenderer { 18public class CallsTreeCellRenderer extends DefaultTreeCellRenderer {
14 private final Gui gui; 19 private final Gui gui;
15 20
16 public CallsTreeCellRenderer(Gui gui) { 21 public CallsTreeCellRenderer(Gui gui) {
17 this.gui = gui; 22 this.gui = gui;
18 } 23 }
19 24
20 @Override 25 @Override
21 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { 26 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
22 Component c = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); 27 Component c = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
23 EntryReference<?, ?> reference = ((ReferenceTreeNode<?, ?>) value).getReference(); 28 EntryReference<?, ?> reference = ((ReferenceTreeNode<?, ?>) value).getReference();
24 29
25 this.setForeground(UiConfig.getTextColor()); 30 this.setForeground(UiConfig.getTextColor());
26 31
27 // if the node represents the method calling the entry 32 // if the node represents the method calling the entry
28 if (reference != null) { 33 if (reference != null) {
29 if (reference.context instanceof MethodEntry) { 34 if (reference.context instanceof MethodEntry) {
30 this.setIcon(GuiUtil.getMethodIcon((MethodEntry) reference.context)); 35 this.setIcon(GuiUtil.getMethodIcon((MethodEntry) reference.context));
31 } 36 }
32 // if the node represents the called entry 37
33 } else { 38 // if the node represents the called entry
34 if (value instanceof ClassReferenceTreeNode node) { 39 } else {
35 this.setIcon(GuiUtil.getClassIcon(this.gui, node.getEntry())); 40 if (value instanceof ClassReferenceTreeNode node) {
36 } else if (value instanceof MethodReferenceTreeNode node) { 41 this.setIcon(GuiUtil.getClassIcon(this.gui, node.getEntry()));
37 this.setIcon(GuiUtil.getMethodIcon(node.getEntry())); 42 } else if (value instanceof MethodReferenceTreeNode node) {
38 } else if (value instanceof FieldReferenceTreeNode) { 43 this.setIcon(GuiUtil.getMethodIcon(node.getEntry()));
39 this.setIcon(GuiUtil.FIELD_ICON); 44 } else if (value instanceof FieldReferenceTreeNode) {
40 } 45 this.setIcon(GuiUtil.FIELD_ICON);
41 } 46 }
42 47 }
43 return c; 48
44 } 49 return c;
50 }
45} 51}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/ImplementationsTreeCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/ImplementationsTreeCellRenderer.java
index 7bf3900..b4126c0 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/ImplementationsTreeCellRenderer.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/ImplementationsTreeCellRenderer.java
@@ -1,34 +1,35 @@
1package cuchaz.enigma.gui.renderer; 1package cuchaz.enigma.gui.renderer;
2 2
3import java.awt.Component;
4
5import javax.swing.JTree;
6import javax.swing.tree.DefaultTreeCellRenderer;
7
3import cuchaz.enigma.analysis.ClassImplementationsTreeNode; 8import cuchaz.enigma.analysis.ClassImplementationsTreeNode;
4import cuchaz.enigma.analysis.MethodImplementationsTreeNode; 9import cuchaz.enigma.analysis.MethodImplementationsTreeNode;
5import cuchaz.enigma.gui.Gui; 10import cuchaz.enigma.gui.Gui;
6import cuchaz.enigma.gui.config.UiConfig; 11import cuchaz.enigma.gui.config.UiConfig;
7import cuchaz.enigma.gui.util.GuiUtil; 12import cuchaz.enigma.gui.util.GuiUtil;
8 13
9import javax.swing.*;
10import javax.swing.tree.DefaultTreeCellRenderer;
11import java.awt.*;
12
13public class ImplementationsTreeCellRenderer extends DefaultTreeCellRenderer { 14public class ImplementationsTreeCellRenderer extends DefaultTreeCellRenderer {
14 private final Gui gui; 15 private final Gui gui;
15 16
16 public ImplementationsTreeCellRenderer(Gui gui) { 17 public ImplementationsTreeCellRenderer(Gui gui) {
17 this.gui = gui; 18 this.gui = gui;
18 } 19 }
19 20
20 @Override 21 @Override
21 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { 22 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
22 Component c = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); 23 Component c = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
23 24
24 this.setForeground(UiConfig.getTextColor()); 25 this.setForeground(UiConfig.getTextColor());
25 26
26 if (value instanceof ClassImplementationsTreeNode node) { 27 if (value instanceof ClassImplementationsTreeNode node) {
27 this.setIcon(GuiUtil.getClassIcon(this.gui, node.getClassEntry())); 28 this.setIcon(GuiUtil.getClassIcon(this.gui, node.getClassEntry()));
28 } else if (value instanceof MethodImplementationsTreeNode node) { 29 } else if (value instanceof MethodImplementationsTreeNode node) {
29 this.setIcon(GuiUtil.getMethodIcon(node.getMethodEntry())); 30 this.setIcon(GuiUtil.getMethodIcon(node.getMethodEntry()));
30 } 31 }
31 32
32 return c; 33 return c;
33 } 34 }
34} 35}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/InheritanceTreeCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/InheritanceTreeCellRenderer.java
index a102553..04bf0f9 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/InheritanceTreeCellRenderer.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/InheritanceTreeCellRenderer.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.gui.renderer; 12package cuchaz.enigma.gui.renderer;
13 13
@@ -37,6 +37,7 @@ public class InheritanceTreeCellRenderer extends DefaultTreeCellRenderer {
37 if (!(value instanceof MethodInheritanceTreeNode node) || node.isImplemented()) { 37 if (!(value instanceof MethodInheritanceTreeNode node) || node.isImplemented()) {
38 ret.setForeground(UiConfig.getTextColor()); 38 ret.setForeground(UiConfig.getTextColor());
39 ret.setFont(ret.getFont().deriveFont(Font.PLAIN)); 39 ret.setFont(ret.getFont().deriveFont(Font.PLAIN));
40
40 if (value instanceof ClassInheritanceTreeNode) { 41 if (value instanceof ClassInheritanceTreeNode) {
41 this.setIcon(GuiUtil.getClassIcon(this.gui, ((ClassInheritanceTreeNode) value).getClassEntry())); 42 this.setIcon(GuiUtil.getClassIcon(this.gui, ((ClassInheritanceTreeNode) value).getClassEntry()));
42 } else if (value instanceof MethodInheritanceTreeNode) { 43 } else if (value instanceof MethodInheritanceTreeNode) {
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/MessageListCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/MessageListCellRenderer.java
index b6ae0c5..123990e 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/MessageListCellRenderer.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/MessageListCellRenderer.java
@@ -10,15 +10,15 @@ import cuchaz.enigma.network.Message;
10// For now, just render the translated text. 10// For now, just render the translated text.
11// TODO: Icons or something later? 11// TODO: Icons or something later?
12public class MessageListCellRenderer extends DefaultListCellRenderer { 12public class MessageListCellRenderer extends DefaultListCellRenderer {
13
14 @Override 13 @Override
15 public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) { 14 public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
16 super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 15 super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
17 Message message = (Message) value; 16 Message message = (Message) value;
17
18 if (message != null) { 18 if (message != null) {
19 setText(message.translate()); 19 setText(message.translate());
20 } 20 }
21
21 return this; 22 return this;
22 } 23 }
23
24} 24}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/StructureOptionListCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/StructureOptionListCellRenderer.java
index f9a1cae..09cdc9b 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/StructureOptionListCellRenderer.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/StructureOptionListCellRenderer.java
@@ -1,21 +1,22 @@
1package cuchaz.enigma.gui.renderer; 1package cuchaz.enigma.gui.renderer;
2 2
3import java.awt.Component;
4
5import javax.swing.DefaultListCellRenderer;
6import javax.swing.JList;
7
3import cuchaz.enigma.analysis.StructureTreeOptions; 8import cuchaz.enigma.analysis.StructureTreeOptions;
4import cuchaz.enigma.utils.I18n; 9import cuchaz.enigma.utils.I18n;
5 10
6import javax.swing.*;
7import java.awt.*;
8
9public class StructureOptionListCellRenderer extends DefaultListCellRenderer { 11public class StructureOptionListCellRenderer extends DefaultListCellRenderer {
12 @Override
13 public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
14 Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
10 15
11 @Override 16 if (value instanceof StructureTreeOptions.Option option) {
12 public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) { 17 this.setText(I18n.translate(option.getTranslationKey()));
13 Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 18 }
14
15 if (value instanceof StructureTreeOptions.Option option) {
16 this.setText(I18n.translate(option.getTranslationKey()));
17 }
18 19
19 return c; 20 return c;
20 } 21 }
21} 22}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchEntry.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchEntry.java
index 91727c3..93507bc 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchEntry.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchEntry.java
@@ -3,7 +3,6 @@ package cuchaz.enigma.gui.search;
3import java.util.List; 3import java.util.List;
4 4
5public interface SearchEntry { 5public interface SearchEntry {
6
7 List<String> getSearchableNames(); 6 List<String> getSearchableNames();
8 7
9 /** 8 /**
@@ -13,5 +12,4 @@ public interface SearchEntry {
13 * @return a unique identifier for this search entry 12 * @return a unique identifier for this search entry
14 */ 13 */
15 String getIdentifier(); 14 String getIdentifier();
16
17} 15}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchUtil.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchUtil.java
index a3b35fa..c8212ce 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchUtil.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchUtil.java
@@ -1,6 +1,14 @@
1package cuchaz.enigma.gui.search; 1package cuchaz.enigma.gui.search;
2 2
3import java.util.*; 3import java.util.ArrayList;
4import java.util.Arrays;
5import java.util.Collection;
6import java.util.Collections;
7import java.util.Comparator;
8import java.util.HashMap;
9import java.util.List;
10import java.util.Locale;
11import java.util.Map;
4import java.util.concurrent.Executor; 12import java.util.concurrent.Executor;
5import java.util.concurrent.Executors; 13import java.util.concurrent.Executors;
6import java.util.concurrent.atomic.AtomicBoolean; 14import java.util.concurrent.atomic.AtomicBoolean;
@@ -14,7 +22,6 @@ import java.util.stream.Stream;
14import cuchaz.enigma.utils.Pair; 22import cuchaz.enigma.utils.Pair;
15 23
16public class SearchUtil<T extends SearchEntry> { 24public class SearchUtil<T extends SearchEntry> {
17
18 private final Map<T, Entry<T>> entries = new HashMap<>(); 25 private final Map<T, Entry<T>> entries = new HashMap<>();
19 private final Map<String, Integer> hitCount = new HashMap<>(); 26 private final Map<String, Integer> hitCount = new HashMap<>();
20 private final Executor searchExecutor = Executors.newWorkStealingPool(); 27 private final Executor searchExecutor = Executors.newWorkStealingPool();
@@ -45,12 +52,7 @@ public class SearchUtil<T extends SearchEntry> {
45 } 52 }
46 53
47 public Stream<T> search(String term) { 54 public Stream<T> search(String term) {
48 return entries.values().parallelStream() 55 return entries.values().parallelStream().map(e -> new Pair<>(e, e.getScore(term, hitCount.getOrDefault(e.searchEntry.getIdentifier(), 0)))).filter(e -> e.b > 0).sorted(Comparator.comparingDouble(o -> -o.b)).map(e -> e.a.searchEntry).sequential();
49 .map(e -> new Pair<>(e, e.getScore(term, hitCount.getOrDefault(e.searchEntry.getIdentifier(), 0))))
50 .filter(e -> e.b > 0)
51 .sorted(Comparator.comparingDouble(o -> -o.b))
52 .map(e -> e.a.searchEntry)
53 .sequential();
54 } 56 }
55 57
56 public SearchControl asyncSearch(String term, SearchResultConsumer<T> consumer) { 58 public SearchControl asyncSearch(String term, SearchResultConsumer<T> consumer) {
@@ -61,21 +63,36 @@ public class SearchUtil<T extends SearchEntry> {
61 AtomicInteger size = new AtomicInteger(); 63 AtomicInteger size = new AtomicInteger();
62 AtomicBoolean control = new AtomicBoolean(false); 64 AtomicBoolean control = new AtomicBoolean(false);
63 AtomicInteger elapsed = new AtomicInteger(); 65 AtomicInteger elapsed = new AtomicInteger();
66
64 for (Entry<T> value : entries.values()) { 67 for (Entry<T> value : entries.values()) {
65 searchExecutor.execute(() -> { 68 searchExecutor.execute(() -> {
66 try { 69 try {
67 if (control.get()) return; 70 if (control.get()) {
71 return;
72 }
73
68 float score = value.getScore(term, hitCount.getOrDefault(value.searchEntry.getIdentifier(), 0)); 74 float score = value.getScore(term, hitCount.getOrDefault(value.searchEntry.getIdentifier(), 0));
69 if (score <= 0) return; 75
76 if (score <= 0) {
77 return;
78 }
79
70 score = -score; // sort descending 80 score = -score; // sort descending
81
71 try { 82 try {
72 scoresLock.lock(); 83 scoresLock.lock();
73 if (control.get()) return; 84
85 if (control.get()) {
86 return;
87 }
88
74 int dataSize = size.getAndIncrement(); 89 int dataSize = size.getAndIncrement();
75 int index = Arrays.binarySearch(scores, 0, dataSize, score); 90 int index = Arrays.binarySearch(scores, 0, dataSize, score);
91
76 if (index < 0) { 92 if (index < 0) {
77 index = ~index; 93 index = ~index;
78 } 94 }
95
79 System.arraycopy(scores, index, scores, index + 1, dataSize - index); 96 System.arraycopy(scores, index, scores, index + 1, dataSize - index);
80 scores[index] = score; 97 scores[index] = score;
81 consumer.add(index, value.searchEntry); 98 consumer.add(index, value.searchEntry);
@@ -113,7 +130,6 @@ public class SearchUtil<T extends SearchEntry> {
113 } 130 }
114 131
115 public static final class Entry<T extends SearchEntry> { 132 public static final class Entry<T extends SearchEntry> {
116
117 public final T searchEntry; 133 public final T searchEntry;
118 private final String[][] components; 134 private final String[][] components;
119 135
@@ -124,9 +140,7 @@ public class SearchUtil<T extends SearchEntry> {
124 140
125 public float getScore(String term, int hits) { 141 public float getScore(String term, int hits) {
126 String ucTerm = term.toUpperCase(Locale.ROOT); 142 String ucTerm = term.toUpperCase(Locale.ROOT);
127 float maxScore = (float) Arrays.stream(components) 143 float maxScore = (float) Arrays.stream(components).mapToDouble(name -> getScoreFor(ucTerm, name)).max().orElse(0.0);
128 .mapToDouble(name -> getScoreFor(ucTerm, name))
129 .max().orElse(0.0);
130 return maxScore * (hits + 1); 144 return maxScore * (hits + 1);
131 } 145 }
132 146
@@ -156,17 +170,20 @@ public class SearchUtil<T extends SearchEntry> {
156 String component = name[componentIndex]; 170 String component = name[componentIndex];
157 float posMultiplier = (name.length - componentIndex) * 0.3f; 171 float posMultiplier = (name.length - componentIndex) * 0.3f;
158 Map<String, Float> newSnapshots = new HashMap<>(); 172 Map<String, Float> newSnapshots = new HashMap<>();
173
159 for (Map.Entry<String, Float> snapshot : snapshots.entrySet()) { 174 for (Map.Entry<String, Float> snapshot : snapshots.entrySet()) {
160 String remaining = snapshot.getKey(); 175 String remaining = snapshot.getKey();
161 float score = snapshot.getValue(); 176 float score = snapshot.getValue();
162 component = component.toUpperCase(Locale.ROOT); 177 component = component.toUpperCase(Locale.ROOT);
163 int l = compareEqualLength(remaining, component); 178 int l = compareEqualLength(remaining, component);
179
164 for (int i = 1; i <= l; i++) { 180 for (int i = 1; i <= l; i++) {
165 float baseScore = scorePerChar * i; 181 float baseScore = scorePerChar * i;
166 float chainBonus = (i - 1) * 0.5f; 182 float chainBonus = (i - 1) * 0.5f;
167 merge(newSnapshots, Collections.singletonMap(remaining.substring(i), score + baseScore * posMultiplier + chainBonus), Math::max); 183 merge(newSnapshots, Collections.singletonMap(remaining.substring(i), score + baseScore * posMultiplier + chainBonus), Math::max);
168 } 184 }
169 } 185 }
186
170 merge(snapshots, newSnapshots, Math::max); 187 merge(snapshots, newSnapshots, Math::max);
171 } 188 }
172 189
@@ -180,24 +197,24 @@ public class SearchUtil<T extends SearchEntry> {
180 } 197 }
181 198
182 public static <T extends SearchEntry> Entry<T> from(T e) { 199 public static <T extends SearchEntry> Entry<T> from(T e) {
183 String[][] components = e.getSearchableNames().parallelStream() 200 String[][] components = e.getSearchableNames().parallelStream().map(Entry::wordwiseSplit).toArray(String[][]::new);
184 .map(Entry::wordwiseSplit)
185 .toArray(String[][]::new);
186 return new Entry<>(e, components); 201 return new Entry<>(e, components);
187 } 202 }
188 203
189 private static int compareEqualLength(String s1, String s2) { 204 private static int compareEqualLength(String s1, String s2) {
190 int len = 0; 205 int len = 0;
206
191 while (len < s1.length() && len < s2.length() && s1.charAt(len) == s2.charAt(len)) { 207 while (len < s1.length() && len < s2.length() && s1.charAt(len) == s2.charAt(len)) {
192 len += 1; 208 len += 1;
193 } 209 }
210
194 return len; 211 return len;
195 } 212 }
196 213
197 /** 214 /**
198 * Splits the given input into components, trying to detect word parts. 215 * Splits the given input into components, trying to detect word parts.
199 * <p> 216 *
200 * Example of how words get split (using <code>|</code> as seperator): 217 * <p>Example of how words get split (using <code>|</code> as seperator):
201 * <p><code>MinecraftClientGame -> Minecraft|Client|Game</code></p> 218 * <p><code>MinecraftClientGame -> Minecraft|Client|Game</code></p>
202 * <p><code>HTTPInputStream -> HTTP|Input|Stream</code></p> 219 * <p><code>HTTPInputStream -> HTTP|Input|Stream</code></p>
203 * <p><code>class_932 -> class|_|932</code></p> 220 * <p><code>class_932 -> class|_|932</code></p>
@@ -210,46 +227,57 @@ public class SearchUtil<T extends SearchEntry> {
210 */ 227 */
211 private static String[] wordwiseSplit(String input) { 228 private static String[] wordwiseSplit(String input) {
212 List<String> list = new ArrayList<>(); 229 List<String> list = new ArrayList<>();
230
213 while (!input.isEmpty()) { 231 while (!input.isEmpty()) {
214 int take; 232 int take;
233
215 if (Character.isLetter(input.charAt(0))) { 234 if (Character.isLetter(input.charAt(0))) {
216 if (input.length() == 1) { 235 if (input.length() == 1) {
217 take = 1; 236 take = 1;
218 } else { 237 } else {
219 boolean nextSegmentIsUppercase = Character.isUpperCase(input.charAt(0)) && Character.isUpperCase(input.charAt(1)); 238 boolean nextSegmentIsUppercase = Character.isUpperCase(input.charAt(0)) && Character.isUpperCase(input.charAt(1));
239
220 if (nextSegmentIsUppercase) { 240 if (nextSegmentIsUppercase) {
221 int nextLowercase = 1; 241 int nextLowercase = 1;
242
222 while (Character.isUpperCase(input.charAt(nextLowercase))) { 243 while (Character.isUpperCase(input.charAt(nextLowercase))) {
223 nextLowercase += 1; 244 nextLowercase += 1;
245
224 if (nextLowercase == input.length()) { 246 if (nextLowercase == input.length()) {
225 nextLowercase += 1; 247 nextLowercase += 1;
226 break; 248 break;
227 } 249 }
228 } 250 }
251
229 take = nextLowercase - 1; 252 take = nextLowercase - 1;
230 } else { 253 } else {
231 int nextUppercase = 1; 254 int nextUppercase = 1;
255
232 while (nextUppercase < input.length() && Character.isLowerCase(input.charAt(nextUppercase))) { 256 while (nextUppercase < input.length() && Character.isLowerCase(input.charAt(nextUppercase))) {
233 nextUppercase += 1; 257 nextUppercase += 1;
234 } 258 }
259
235 take = nextUppercase; 260 take = nextUppercase;
236 } 261 }
237 } 262 }
238 } else if (Character.isDigit(input.charAt(0))) { 263 } else if (Character.isDigit(input.charAt(0))) {
239 int nextNonNum = 1; 264 int nextNonNum = 1;
265
240 while (nextNonNum < input.length() && Character.isLetter(input.charAt(nextNonNum)) && !Character.isLowerCase(input.charAt(nextNonNum))) { 266 while (nextNonNum < input.length() && Character.isLetter(input.charAt(nextNonNum)) && !Character.isLowerCase(input.charAt(nextNonNum))) {
241 nextNonNum += 1; 267 nextNonNum += 1;
242 } 268 }
269
243 take = nextNonNum; 270 take = nextNonNum;
244 } else { 271 } else {
245 take = 1; 272 take = 1;
246 } 273 }
274
247 list.add(input.substring(0, take)); 275 list.add(input.substring(0, take));
248 input = input.substring(take); 276 input = input.substring(take);
249 } 277 }
278
250 return list.toArray(new String[0]); 279 return list.toArray(new String[0]);
251 } 280 }
252
253 } 281 }
254 282
255 @FunctionalInterface 283 @FunctionalInterface
@@ -264,5 +292,4 @@ public class SearchUtil<T extends SearchEntry> {
264 292
265 float getProgress(); 293 float getProgress();
266 } 294 }
267
268} 295}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java
index 20d6a0e..99b5572 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java
@@ -1,5 +1,10 @@
1package cuchaz.enigma.gui.stats; 1package cuchaz.enigma.gui.stats;
2 2
3import java.util.EnumSet;
4import java.util.HashMap;
5import java.util.Map;
6import java.util.Set;
7
3import cuchaz.enigma.EnigmaProject; 8import cuchaz.enigma.EnigmaProject;
4import cuchaz.enigma.ProgressListener; 9import cuchaz.enigma.ProgressListener;
5import cuchaz.enigma.analysis.index.EntryIndex; 10import cuchaz.enigma.analysis.index.EntryIndex;
@@ -7,109 +12,112 @@ import cuchaz.enigma.translation.mapping.EntryRemapper;
7import cuchaz.enigma.translation.mapping.EntryResolver; 12import cuchaz.enigma.translation.mapping.EntryResolver;
8import cuchaz.enigma.translation.mapping.ResolutionStrategy; 13import cuchaz.enigma.translation.mapping.ResolutionStrategy;
9import cuchaz.enigma.translation.representation.TypeDescriptor; 14import cuchaz.enigma.translation.representation.TypeDescriptor;
10import cuchaz.enigma.translation.representation.entry.*; 15import cuchaz.enigma.translation.representation.entry.ClassEntry;
16import cuchaz.enigma.translation.representation.entry.Entry;
17import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
18import cuchaz.enigma.translation.representation.entry.FieldEntry;
19import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
20import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
21import cuchaz.enigma.translation.representation.entry.MethodEntry;
11import cuchaz.enigma.utils.I18n; 22import cuchaz.enigma.utils.I18n;
12 23
13import java.util.*;
14
15public class StatsGenerator { 24public class StatsGenerator {
16 private final EnigmaProject project; 25 private final EnigmaProject project;
17 private final EntryIndex entryIndex; 26 private final EntryIndex entryIndex;
18 private final EntryRemapper mapper; 27 private final EntryRemapper mapper;
19 private final EntryResolver entryResolver; 28 private final EntryResolver entryResolver;
20 29
21 public StatsGenerator(EnigmaProject project) { 30 public StatsGenerator(EnigmaProject project) {
22 this.project = project; 31 this.project = project;
23 this.entryIndex = project.getJarIndex().getEntryIndex(); 32 this.entryIndex = project.getJarIndex().getEntryIndex();
24 this.mapper = project.getMapper(); 33 this.mapper = project.getMapper();
25 this.entryResolver = project.getJarIndex().getEntryResolver(); 34 this.entryResolver = project.getJarIndex().getEntryResolver();
26 } 35 }
27 36
28 public StatsResult generate(ProgressListener progress, Set<StatsMember> includedMembers, String topLevelPackage, boolean includeSynthetic) { 37 public StatsResult generate(ProgressListener progress, Set<StatsMember> includedMembers, String topLevelPackage, boolean includeSynthetic) {
29 includedMembers = EnumSet.copyOf(includedMembers); 38 includedMembers = EnumSet.copyOf(includedMembers);
30 int totalWork = 0; 39 int totalWork = 0;
31 int totalMappable = 0; 40 int totalMappable = 0;
32 41
33 if (includedMembers.contains(StatsMember.METHODS) || includedMembers.contains(StatsMember.PARAMETERS)) { 42 if (includedMembers.contains(StatsMember.METHODS) || includedMembers.contains(StatsMember.PARAMETERS)) {
34 totalWork += entryIndex.getMethods().size(); 43 totalWork += entryIndex.getMethods().size();
35 } 44 }
36 45
37 if (includedMembers.contains(StatsMember.FIELDS)) { 46 if (includedMembers.contains(StatsMember.FIELDS)) {
38 totalWork += entryIndex.getFields().size(); 47 totalWork += entryIndex.getFields().size();
39 } 48 }
40 49
41 if (includedMembers.contains(StatsMember.CLASSES)) { 50 if (includedMembers.contains(StatsMember.CLASSES)) {
42 totalWork += entryIndex.getClasses().size(); 51 totalWork += entryIndex.getClasses().size();
43 } 52 }
44 53
45 progress.init(totalWork, I18n.translate("progress.stats")); 54 progress.init(totalWork, I18n.translate("progress.stats"));
46 55
47 Map<String, Integer> counts = new HashMap<>(); 56 Map<String, Integer> counts = new HashMap<>();
48 57
49 int numDone = 0; 58 int numDone = 0;
50 if (includedMembers.contains(StatsMember.METHODS) || includedMembers.contains(StatsMember.PARAMETERS)) { 59
51 for (MethodEntry method : entryIndex.getMethods()) { 60 if (includedMembers.contains(StatsMember.METHODS) || includedMembers.contains(StatsMember.PARAMETERS)) {
52 progress.step(numDone++, I18n.translate("type.methods")); 61 for (MethodEntry method : entryIndex.getMethods()) {
53 MethodEntry root = entryResolver 62 progress.step(numDone++, I18n.translate("type.methods"));
54 .resolveEntry(method, ResolutionStrategy.RESOLVE_ROOT) 63 MethodEntry root = entryResolver.resolveEntry(method, ResolutionStrategy.RESOLVE_ROOT).stream().findFirst().orElseThrow(AssertionError::new);
55 .stream() 64
56 .findFirst() 65 if (root == method) {
57 .orElseThrow(AssertionError::new); 66 if (includedMembers.contains(StatsMember.METHODS) && !((MethodDefEntry) method).getAccess().isSynthetic()) {
58 67 update(counts, method);
59 if (root == method) { 68 totalMappable++;
60 if (includedMembers.contains(StatsMember.METHODS) && !((MethodDefEntry) method).getAccess().isSynthetic()) { 69 }
61 update(counts, method); 70
62 totalMappable ++; 71 if (includedMembers.contains(StatsMember.PARAMETERS) && (!((MethodDefEntry) method).getAccess().isSynthetic() || includeSynthetic)) {
63 } 72 int index = ((MethodDefEntry) method).getAccess().isStatic() ? 0 : 1;
64 73
65 if (includedMembers.contains(StatsMember.PARAMETERS) && (!((MethodDefEntry) method).getAccess().isSynthetic() || includeSynthetic)) { 74 for (TypeDescriptor argument : method.getDesc().getArgumentDescs()) {
66 int index = ((MethodDefEntry) method).getAccess().isStatic() ? 0 : 1; 75 update(counts, new LocalVariableEntry(method, index, "", true, null));
67 for (TypeDescriptor argument : method.getDesc().getArgumentDescs()) { 76 index += argument.getSize();
68 update(counts, new LocalVariableEntry(method, index, "", true,null)); 77 totalMappable++;
69 index += argument.getSize(); 78 }
70 totalMappable ++; 79 }
71 } 80 }
72 } 81 }
73 } 82 }
74 } 83
75 } 84 if (includedMembers.contains(StatsMember.FIELDS)) {
76 85 for (FieldEntry field : entryIndex.getFields()) {
77 if (includedMembers.contains(StatsMember.FIELDS)) { 86 progress.step(numDone++, I18n.translate("type.fields"));
78 for (FieldEntry field : entryIndex.getFields()) { 87
79 progress.step(numDone++, I18n.translate("type.fields")); 88 if (!((FieldDefEntry) field).getAccess().isSynthetic()) {
80 if (!((FieldDefEntry)field).getAccess().isSynthetic()) { 89 update(counts, field);
81 update(counts, field); 90 totalMappable++;
82 totalMappable ++; 91 }
83 } 92 }
84 } 93 }
85 } 94
86 95 if (includedMembers.contains(StatsMember.CLASSES)) {
87 if (includedMembers.contains(StatsMember.CLASSES)) { 96 for (ClassEntry clazz : entryIndex.getClasses()) {
88 for (ClassEntry clazz : entryIndex.getClasses()) { 97 progress.step(numDone++, I18n.translate("type.classes"));
89 progress.step(numDone++, I18n.translate("type.classes")); 98 update(counts, clazz);
90 update(counts, clazz); 99 totalMappable++;
91 totalMappable ++; 100 }
92 } 101 }
93 } 102
94 103 progress.step(-1, I18n.translate("progress.stats.data"));
95 progress.step(-1, I18n.translate("progress.stats.data")); 104
96 105 StatsResult.Tree<Integer> tree = new StatsResult.Tree<>();
97 StatsResult.Tree<Integer> tree = new StatsResult.Tree<>(); 106
98 107 for (Map.Entry<String, Integer> entry : counts.entrySet()) {
99 for (Map.Entry<String, Integer> entry : counts.entrySet()) { 108 if (entry.getKey().startsWith(topLevelPackage)) {
100 if (entry.getKey().startsWith(topLevelPackage)) { 109 tree.getNode(entry.getKey()).value = entry.getValue();
101 tree.getNode(entry.getKey()).value = entry.getValue(); 110 }
102 } 111 }
103 } 112
104 113 tree.collapse(tree.root);
105 tree.collapse(tree.root); 114 return new StatsResult(totalMappable, counts.values().stream().mapToInt(i -> i).sum(), tree);
106 return new StatsResult(totalMappable, counts.values().stream().mapToInt(i -> i).sum(), tree); 115 }
107 } 116
108 117 private void update(Map<String, Integer> counts, Entry<?> entry) {
109 private void update(Map<String, Integer> counts, Entry<?> entry) { 118 if (project.isObfuscated(entry)) {
110 if (project.isObfuscated(entry)) { 119 String parent = mapper.deobfuscate(entry.getAncestry().get(0)).getName().replace('/', '.');
111 String parent = mapper.deobfuscate(entry.getAncestry().get(0)).getName().replace('/', '.'); 120 counts.put(parent, counts.getOrDefault(parent, 0) + 1);
112 counts.put(parent, counts.getOrDefault(parent, 0) + 1); 121 }
113 } 122 }
114 }
115} 123}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsMember.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsMember.java
index 0e2452f..0037ab5 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsMember.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsMember.java
@@ -1,8 +1,8 @@
1package cuchaz.enigma.gui.stats; 1package cuchaz.enigma.gui.stats;
2 2
3public enum StatsMember { 3public enum StatsMember {
4 CLASSES, 4 CLASSES,
5 METHODS, 5 METHODS,
6 FIELDS, 6 FIELDS,
7 PARAMETERS 7 PARAMETERS
8} 8}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java
index 0a71a64..12726c0 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java
@@ -1,14 +1,13 @@
1package cuchaz.enigma.gui.stats; 1package cuchaz.enigma.gui.stats;
2 2
3import com.google.gson.GsonBuilder;
4
5import java.util.ArrayList; 3import java.util.ArrayList;
6import java.util.HashMap; 4import java.util.HashMap;
7import java.util.List; 5import java.util.List;
8import java.util.Map; 6import java.util.Map;
9 7
10public final class StatsResult { 8import com.google.gson.GsonBuilder;
11 9
10public final class StatsResult {
12 private final int total; 11 private final int total;
13 private final int unmapped; 12 private final int unmapped;
14 private final Tree<Integer> tree; 13 private final Tree<Integer> tree;
@@ -101,5 +100,4 @@ public final class StatsResult {
101 } 100 }
102 } 101 }
103 } 102 }
104
105} 103}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/AbstractListCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/AbstractListCellRenderer.java
index 612e3e9..f8ce36d 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/AbstractListCellRenderer.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/AbstractListCellRenderer.java
@@ -3,11 +3,16 @@ package cuchaz.enigma.gui.util;
3import java.awt.Component; 3import java.awt.Component;
4import java.awt.event.MouseEvent; 4import java.awt.event.MouseEvent;
5 5
6import javax.swing.*; 6import javax.swing.BorderFactory;
7import javax.swing.JComponent;
8import javax.swing.JList;
9import javax.swing.JPanel;
10import javax.swing.ListCellRenderer;
11import javax.swing.UIDefaults;
12import javax.swing.UIManager;
7import javax.swing.border.Border; 13import javax.swing.border.Border;
8 14
9public abstract class AbstractListCellRenderer<E> extends JPanel implements ListCellRenderer<E> { 15public abstract class AbstractListCellRenderer<E> extends JPanel implements ListCellRenderer<E> {
10
11 private static final Border NO_FOCUS_BORDER = BorderFactory.createEmptyBorder(1, 1, 1, 1); 16 private static final Border NO_FOCUS_BORDER = BorderFactory.createEmptyBorder(1, 1, 1, 1);
12 17
13 private Border noFocusBorder; 18 private Border noFocusBorder;
@@ -21,22 +26,27 @@ public abstract class AbstractListCellRenderer<E> extends JPanel implements List
21 Border border = UIManager.getLookAndFeel().getDefaults().getBorder("List.List.cellNoFocusBorder"); 26 Border border = UIManager.getLookAndFeel().getDefaults().getBorder("List.List.cellNoFocusBorder");
22 noFocusBorder = border != null ? border : NO_FOCUS_BORDER; 27 noFocusBorder = border != null ? border : NO_FOCUS_BORDER;
23 } 28 }
29
24 return noFocusBorder; 30 return noFocusBorder;
25 } 31 }
26 32
27 protected Border getBorder(boolean isSelected, boolean cellHasFocus) { 33 protected Border getBorder(boolean isSelected, boolean cellHasFocus) {
28 Border b = null; 34 Border b = null;
35
29 if (cellHasFocus) { 36 if (cellHasFocus) {
30 UIDefaults defaults = UIManager.getLookAndFeel().getDefaults(); 37 UIDefaults defaults = UIManager.getLookAndFeel().getDefaults();
38
31 if (isSelected) { 39 if (isSelected) {
32 b = defaults.getBorder("List.focusSelectedCellHighlightBorder"); 40 b = defaults.getBorder("List.focusSelectedCellHighlightBorder");
33 } 41 }
42
34 if (b == null) { 43 if (b == null) {
35 b = defaults.getBorder("List.focusCellHighlightBorder"); 44 b = defaults.getBorder("List.focusCellHighlightBorder");
36 } 45 }
37 } else { 46 } else {
38 b = getNoFocusBorder(); 47 b = getNoFocusBorder();
39 } 48 }
49
40 return b; 50 return b;
41 } 51 }
42 52
@@ -68,10 +78,11 @@ public abstract class AbstractListCellRenderer<E> extends JPanel implements List
68 @Override 78 @Override
69 public String getToolTipText(MouseEvent event) { 79 public String getToolTipText(MouseEvent event) {
70 Component c = getComponentAt(event.getPoint()); 80 Component c = getComponentAt(event.getPoint());
81
71 if (c instanceof JComponent) { 82 if (c instanceof JComponent) {
72 return ((JComponent) c).getToolTipText(); 83 return ((JComponent) c).getToolTipText();
73 } 84 }
85
74 return getToolTipText(); 86 return getToolTipText();
75 } 87 }
76
77} 88}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GridBagConstraintsBuilder.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GridBagConstraintsBuilder.java
index 6a75686..2d8aa73 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GridBagConstraintsBuilder.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GridBagConstraintsBuilder.java
@@ -3,7 +3,6 @@ package cuchaz.enigma.gui.util;
3import java.awt.GridBagConstraints; 3import java.awt.GridBagConstraints;
4 4
5public final class GridBagConstraintsBuilder { 5public final class GridBagConstraintsBuilder {
6
7 private final GridBagConstraints inner; 6 private final GridBagConstraints inner;
8 7
9 private GridBagConstraintsBuilder(GridBagConstraints inner) { 8 private GridBagConstraintsBuilder(GridBagConstraints inner) {
@@ -136,5 +135,4 @@ public final class GridBagConstraintsBuilder {
136 public GridBagConstraints build() { 135 public GridBagConstraints build() {
137 return (GridBagConstraints) this.inner.clone(); 136 return (GridBagConstraints) this.inner.clone();
138 } 137 }
139
140} 138}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java
index 3b8ecbc..d078424 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java
@@ -1,8 +1,17 @@
1package cuchaz.enigma.gui.util; 1package cuchaz.enigma.gui.util;
2 2
3import java.awt.*; 3import java.awt.Color;
4import java.awt.Cursor;
5import java.awt.Desktop;
6import java.awt.Font;
7import java.awt.Toolkit;
4import java.awt.datatransfer.StringSelection; 8import java.awt.datatransfer.StringSelection;
5import java.awt.event.*; 9import java.awt.event.MouseAdapter;
10import java.awt.event.MouseEvent;
11import java.awt.event.MouseListener;
12import java.awt.event.WindowAdapter;
13import java.awt.event.WindowEvent;
14import java.awt.event.WindowListener;
6import java.awt.font.TextAttribute; 15import java.awt.font.TextAttribute;
7import java.io.IOException; 16import java.io.IOException;
8import java.net.URI; 17import java.net.URI;
@@ -13,7 +22,14 @@ import java.util.Map;
13import java.util.NoSuchElementException; 22import java.util.NoSuchElementException;
14import java.util.function.Consumer; 23import java.util.function.Consumer;
15 24
16import javax.swing.*; 25import javax.swing.Icon;
26import javax.swing.JComponent;
27import javax.swing.JLabel;
28import javax.swing.JToolTip;
29import javax.swing.Popup;
30import javax.swing.PopupFactory;
31import javax.swing.Timer;
32import javax.swing.ToolTipManager;
17import javax.swing.tree.TreeNode; 33import javax.swing.tree.TreeNode;
18import javax.swing.tree.TreePath; 34import javax.swing.tree.TreePath;
19 35
@@ -28,160 +44,160 @@ import cuchaz.enigma.translation.representation.entry.MethodEntry;
28import cuchaz.enigma.utils.Os; 44import cuchaz.enigma.utils.Os;
29 45
30public class GuiUtil { 46public class GuiUtil {
31 public static final Icon CLASS_ICON = loadIcon("class"); 47 public static final Icon CLASS_ICON = loadIcon("class");
32 public static final Icon INTERFACE_ICON = loadIcon("interface"); 48 public static final Icon INTERFACE_ICON = loadIcon("interface");
33 public static final Icon ENUM_ICON = loadIcon("enum"); 49 public static final Icon ENUM_ICON = loadIcon("enum");
34 public static final Icon ANNOTATION_ICON = loadIcon("annotation"); 50 public static final Icon ANNOTATION_ICON = loadIcon("annotation");
35 public static final Icon RECORD_ICON = loadIcon("record"); 51 public static final Icon RECORD_ICON = loadIcon("record");
36 public static final Icon METHOD_ICON = loadIcon("method"); 52 public static final Icon METHOD_ICON = loadIcon("method");
37 public static final Icon FIELD_ICON = loadIcon("field"); 53 public static final Icon FIELD_ICON = loadIcon("field");
38 public static final Icon CONSTRUCTOR_ICON = loadIcon("constructor"); 54 public static final Icon CONSTRUCTOR_ICON = loadIcon("constructor");
39 55
40 public static void openUrl(String url) { 56 public static void openUrl(String url) {
41 try { 57 try {
42 switch (Os.getOs()) { 58 switch (Os.getOs()) {
43 case LINUX: 59 case LINUX:
44 new ProcessBuilder("/usr/bin/env", "xdg-open", url).start(); 60 new ProcessBuilder("/usr/bin/env", "xdg-open", url).start();
45 break; 61 break;
46 default: 62 default:
47 if (Desktop.isDesktopSupported()) { 63 if (Desktop.isDesktopSupported()) {
48 Desktop desktop = Desktop.getDesktop(); 64 Desktop desktop = Desktop.getDesktop();
49 desktop.browse(new URI(url)); 65 desktop.browse(new URI(url));
50 } 66 }
51 } 67 }
52 } catch (IOException ex) { 68 } catch (IOException ex) {
53 throw new RuntimeException(ex); 69 throw new RuntimeException(ex);
54 } catch (URISyntaxException ex) { 70 } catch (URISyntaxException ex) {
55 throw new IllegalArgumentException(ex); 71 throw new IllegalArgumentException(ex);
56 } 72 }
57 } 73 }
58 74
59 public static JLabel unboldLabel(JLabel label) { 75 public static JLabel unboldLabel(JLabel label) {
60 Font font = label.getFont(); 76 Font font = label.getFont();
61 label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD)); 77 label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD));
62 return label; 78 return label;
63 } 79 }
64 80
65 /** 81 /**
66 * Puts the provided {@code text} in the system clipboard. 82 * Puts the provided {@code text} in the system clipboard.
67 */ 83 */
68 public static void copyToClipboard(String text) { 84 public static void copyToClipboard(String text) {
69 Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(text), null); 85 Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(text), null);
70 } 86 }
71 87
72 public static void showPopup(JComponent component, String text, int x, int y) { 88 public static void showPopup(JComponent component, String text, int x, int y) {
73 // from https://stackoverflow.com/questions/39955015/java-swing-show-tooltip-as-a-message-dialog 89 // from https://stackoverflow.com/questions/39955015/java-swing-show-tooltip-as-a-message-dialog
74 JToolTip tooltip = new JToolTip(); 90 JToolTip tooltip = new JToolTip();
75 tooltip.setTipText(text); 91 tooltip.setTipText(text);
76 Popup p = PopupFactory.getSharedInstance().getPopup(component, tooltip, x + 10, y); 92 Popup p = PopupFactory.getSharedInstance().getPopup(component, tooltip, x + 10, y);
77 p.show(); 93 p.show();
78 Timer t = new Timer(1000, e -> p.hide()); 94 Timer t = new Timer(1000, e -> p.hide());
79 t.setRepeats(false); 95 t.setRepeats(false);
80 t.start(); 96 t.start();
81 } 97 }
82 98
83 public static void showToolTipNow(JComponent component) { 99 public static void showToolTipNow(JComponent component) {
84 // HACKHACK: trick the tooltip manager into showing the tooltip right now 100 // HACKHACK: trick the tooltip manager into showing the tooltip right now
85 ToolTipManager manager = ToolTipManager.sharedInstance(); 101 ToolTipManager manager = ToolTipManager.sharedInstance();
86 int oldDelay = manager.getInitialDelay(); 102 int oldDelay = manager.getInitialDelay();
87 manager.setInitialDelay(0); 103 manager.setInitialDelay(0);
88 manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false)); 104 manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false));
89 manager.setInitialDelay(oldDelay); 105 manager.setInitialDelay(oldDelay);
90 } 106 }
91 107
92 public static JLabel createLink(String text, Runnable action) { 108 public static JLabel createLink(String text, Runnable action) {
93 JLabel link = new JLabel(text); 109 JLabel link = new JLabel(text);
94 link.setForeground(Color.BLUE.darker()); 110 link.setForeground(Color.BLUE.darker());
95 link.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 111 link.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
96 @SuppressWarnings("unchecked") 112 @SuppressWarnings("unchecked") Map<TextAttribute, Object> attributes = (Map<TextAttribute, Object>) link.getFont().getAttributes();
97 Map<TextAttribute, Object> attributes = (Map<TextAttribute, Object>) link.getFont().getAttributes(); 113 attributes.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
98 attributes.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); 114 link.setFont(link.getFont().deriveFont(attributes));
99 link.setFont(link.getFont().deriveFont(attributes)); 115 link.addMouseListener(new MouseAdapter() {
100 link.addMouseListener(new MouseAdapter() { 116 @Override
101 @Override 117 public void mousePressed(MouseEvent e) {
102 public void mousePressed(MouseEvent e) { 118 action.run();
103 action.run(); 119 }
104 } 120 });
105 }); 121 return link;
106 return link; 122 }
107 } 123
108 124 public static Icon loadIcon(String name) {
109 public static Icon loadIcon(String name) { 125 String path = "icons/" + name + ".svg";
110 String path = "icons/" + name + ".svg"; 126
111 127 // Do an eager check for a missing icon since FlatSVGIcon does it later at render time
112 // Do an eager check for a missing icon since FlatSVGIcon does it later at render time 128 if (GuiUtil.class.getResource('/' + path) == null) {
113 if (GuiUtil.class.getResource('/' + path) == null) { 129 throw new NoSuchElementException("Missing icon: '" + name + "' at " + path);
114 throw new NoSuchElementException("Missing icon: '" + name + "' at " + path); 130 }
115 } 131
116 132 // Note: the width and height are scaled automatically because the FlatLaf UI scale
117 // Note: the width and height are scaled automatically because the FlatLaf UI scale 133 // is set in LookAndFeel.setGlobalLAF()
118 // is set in LookAndFeel.setGlobalLAF() 134 return new FlatSVGIcon(path, 16, 16, GuiUtil.class.getClassLoader());
119 return new FlatSVGIcon(path, 16, 16, GuiUtil.class.getClassLoader()); 135 }
120 } 136
121 137 public static Icon getClassIcon(Gui gui, ClassEntry entry) {
122 public static Icon getClassIcon(Gui gui, ClassEntry entry) { 138 EntryIndex entryIndex = gui.getController().project.getJarIndex().getEntryIndex();
123 EntryIndex entryIndex = gui.getController().project.getJarIndex().getEntryIndex(); 139 AccessFlags access = entryIndex.getClassAccess(entry);
124 AccessFlags access = entryIndex.getClassAccess(entry); 140
125 141 if (access != null) {
126 if (access != null) { 142 if (access.isAnnotation()) {
127 if (access.isAnnotation()) { 143 return ANNOTATION_ICON;
128 return ANNOTATION_ICON; 144 } else if (access.isInterface()) {
129 } else if (access.isInterface()) { 145 return INTERFACE_ICON;
130 return INTERFACE_ICON; 146 } else if (access.isEnum()) {
131 } else if (access.isEnum()) { 147 return ENUM_ICON;
132 return ENUM_ICON; 148 } else if (entryIndex.getDefinition(entry).isRecord()) {
133 } else if (entryIndex.getDefinition(entry).isRecord()) { 149 return RECORD_ICON;
134 return RECORD_ICON; 150 }
135 } 151 }
136 } 152
137 153 return CLASS_ICON;
138 return CLASS_ICON; 154 }
139 } 155
140 156 public static Icon getMethodIcon(MethodEntry entry) {
141 public static Icon getMethodIcon(MethodEntry entry) { 157 if (entry.isConstructor()) {
142 if (entry.isConstructor()) { 158 return CONSTRUCTOR_ICON;
143 return CONSTRUCTOR_ICON; 159 }
144 } 160
145 return METHOD_ICON; 161 return METHOD_ICON;
146 } 162 }
147 163
148 public static TreePath getPathToRoot(TreeNode node) { 164 public static TreePath getPathToRoot(TreeNode node) {
149 List<TreeNode> nodes = Lists.newArrayList(); 165 List<TreeNode> nodes = Lists.newArrayList();
150 TreeNode n = node; 166 TreeNode n = node;
151 167
152 do { 168 do {
153 nodes.add(n); 169 nodes.add(n);
154 n = n.getParent(); 170 n = n.getParent();
155 } while (n != null); 171 } while (n != null);
156 172
157 Collections.reverse(nodes); 173 Collections.reverse(nodes);
158 return new TreePath(nodes.toArray()); 174 return new TreePath(nodes.toArray());
159 } 175 }
160 176
161 public static MouseListener onMouseClick(Consumer<MouseEvent> op) { 177 public static MouseListener onMouseClick(Consumer<MouseEvent> op) {
162 return new MouseAdapter() { 178 return new MouseAdapter() {
163 @Override 179 @Override
164 public void mouseClicked(MouseEvent e) { 180 public void mouseClicked(MouseEvent e) {
165 op.accept(e); 181 op.accept(e);
166 } 182 }
167 }; 183 };
168 } 184 }
169 185
170 public static MouseListener onMousePress(Consumer<MouseEvent> op) { 186 public static MouseListener onMousePress(Consumer<MouseEvent> op) {
171 return new MouseAdapter() { 187 return new MouseAdapter() {
172 @Override 188 @Override
173 public void mousePressed(MouseEvent e) { 189 public void mousePressed(MouseEvent e) {
174 op.accept(e); 190 op.accept(e);
175 } 191 }
176 }; 192 };
177 } 193 }
178 194
179 public static WindowListener onWindowClose(Consumer<WindowEvent> op) { 195 public static WindowListener onWindowClose(Consumer<WindowEvent> op) {
180 return new WindowAdapter() { 196 return new WindowAdapter() {
181 @Override 197 @Override
182 public void windowClosing(WindowEvent e) { 198 public void windowClosing(WindowEvent e) {
183 op.accept(e); 199 op.accept(e);
184 } 200 }
185 }; 201 };
186 } 202 }
187} 203}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/History.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/History.java
index b128699..f1a8a7a 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/History.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/History.java
@@ -1,9 +1,9 @@
1package cuchaz.enigma.gui.util; 1package cuchaz.enigma.gui.util;
2 2
3import com.google.common.collect.Queues;
4
5import java.util.Deque; 3import java.util.Deque;
6 4
5import com.google.common.collect.Queues;
6
7public class History<T> { 7public class History<T> {
8 private final Deque<T> previous = Queues.newArrayDeque(); 8 private final Deque<T> previous = Queues.newArrayDeque();
9 private final Deque<T> next = Queues.newArrayDeque(); 9 private final Deque<T> next = Queues.newArrayDeque();
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageChangeListener.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageChangeListener.java
index 9f53a44..818e112 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageChangeListener.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageChangeListener.java
@@ -1,11 +1,9 @@
1package cuchaz.enigma.gui.util; 1package cuchaz.enigma.gui.util;
2 2
3public interface LanguageChangeListener { 3public interface LanguageChangeListener {
4
5 void retranslateUi(); 4 void retranslateUi();
6 5
7 default boolean isValid() { 6 default boolean isValid() {
8 return true; 7 return true;
9 } 8 }
10
11} 9}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageUtil.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageUtil.java
index d3e6376..30a9180 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageUtil.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageUtil.java
@@ -4,7 +4,6 @@ import java.util.ArrayList;
4import java.util.List; 4import java.util.List;
5 5
6public final class LanguageUtil { 6public final class LanguageUtil {
7
8 private static final List<LanguageChangeListener> listeners = new ArrayList<>(); 7 private static final List<LanguageChangeListener> listeners = new ArrayList<>();
9 8
10 public LanguageUtil() { 9 public LanguageUtil() {
@@ -21,5 +20,4 @@ public final class LanguageUtil {
21 public static void dispatchLanguageChange() { 20 public static void dispatchLanguageChange() {
22 listeners.forEach(LanguageChangeListener::retranslateUi); 21 listeners.forEach(LanguageChangeListener::retranslateUi);
23 } 22 }
24
25} 23}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleChangeListener.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleChangeListener.java
index d045c6d..243f26f 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleChangeListener.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleChangeListener.java
@@ -2,7 +2,5 @@ package cuchaz.enigma.gui.util;
2 2
3@FunctionalInterface 3@FunctionalInterface
4public interface ScaleChangeListener { 4public interface ScaleChangeListener {
5
6 void onScaleChanged(float scale, float oldScale); 5 void onScaleChanged(float scale, float oldScale);
7
8} 6}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleUtil.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleUtil.java
index 28e3769..bc587fa 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleUtil.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleUtil.java
@@ -21,7 +21,6 @@ import de.sciss.syntaxpane.DefaultSyntaxKit;
21import cuchaz.enigma.gui.config.UiConfig; 21import cuchaz.enigma.gui.config.UiConfig;
22 22
23public class ScaleUtil { 23public class ScaleUtil {
24
25 private static List<ScaleChangeListener> listeners = new ArrayList<>(); 24 private static List<ScaleChangeListener> listeners = new ArrayList<>();
26 25
27 public static void setScaleFactor(float scaleFactor) { 26 public static void setScaleFactor(float scaleFactor) {
@@ -110,15 +109,19 @@ public class ScaleUtil {
110 109
111 private static BasicTweaker createTweakerForCurrentLook(float dpiScaling) { 110 private static BasicTweaker createTweakerForCurrentLook(float dpiScaling) {
112 String testString = UIManager.getLookAndFeel().getName().toLowerCase(); 111 String testString = UIManager.getLookAndFeel().getName().toLowerCase();
112
113 if (testString.contains("windows")) { 113 if (testString.contains("windows")) {
114 return new WindowsTweaker(dpiScaling, testString.contains("classic")); 114 return new WindowsTweaker(dpiScaling, testString.contains("classic"));
115 } 115 }
116
116 if (testString.contains("metal")) { 117 if (testString.contains("metal")) {
117 return new MetalTweaker(dpiScaling); 118 return new MetalTweaker(dpiScaling);
118 } 119 }
120
119 if (testString.contains("nimbus")) { 121 if (testString.contains("nimbus")) {
120 return new NimbusTweaker(dpiScaling); 122 return new NimbusTweaker(dpiScaling);
121 } 123 }
124
122 return new BasicTweaker(dpiScaling); 125 return new BasicTweaker(dpiScaling);
123 } 126 }
124} 127}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/SingleTreeSelectionModel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/SingleTreeSelectionModel.java
index 8915264..9d967b8 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/SingleTreeSelectionModel.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/SingleTreeSelectionModel.java
@@ -4,8 +4,7 @@ import javax.swing.tree.DefaultTreeSelectionModel;
4import javax.swing.tree.TreeSelectionModel; 4import javax.swing.tree.TreeSelectionModel;
5 5
6public class SingleTreeSelectionModel extends DefaultTreeSelectionModel { 6public class SingleTreeSelectionModel extends DefaultTreeSelectionModel {
7 7 public SingleTreeSelectionModel() {
8 public SingleTreeSelectionModel() { 8 this.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
9 this.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); 9 }
10 }
11} 10}
diff --git a/enigma/build.gradle b/enigma/build.gradle
index b4a4062..13fb6b5 100644
--- a/enigma/build.gradle
+++ b/enigma/build.gradle
@@ -1,19 +1,19 @@
1configurations { 1configurations {
2 proGuard 2 proGuard
3} 3}
4 4
5dependencies { 5dependencies {
6 implementation 'org.ow2.asm:asm:9.3' 6 implementation 'org.ow2.asm:asm:9.3'
7 implementation 'org.ow2.asm:asm-commons:9.3' 7 implementation 'org.ow2.asm:asm-commons:9.3'
8 implementation 'org.ow2.asm:asm-tree:9.3' 8 implementation 'org.ow2.asm:asm-tree:9.3'
9 implementation 'org.ow2.asm:asm-util:9.3' 9 implementation 'org.ow2.asm:asm-util:9.3'
10 10
11 implementation 'net.fabricmc:procyon-fabric-compilertools:0.5.35.13' 11 implementation 'net.fabricmc:procyon-fabric-compilertools:0.5.35.13'
12 implementation 'net.fabricmc:cfr:0.1.0' 12 implementation 'net.fabricmc:cfr:0.1.0'
13 13
14 proGuard 'com.guardsquare:proguard-base:7.2.0-beta2' 14 proGuard 'com.guardsquare:proguard-base:7.2.0-beta2'
15 15
16 testImplementation 'com.google.jimfs:jimfs:1.2' 16 testImplementation 'com.google.jimfs:jimfs:1.2'
17} 17}
18 18
19// Generate "version.txt" file 19// Generate "version.txt" file
@@ -21,11 +21,11 @@ dependencies {
21ext.genOutputDir = file("$buildDir/generated-resources") 21ext.genOutputDir = file("$buildDir/generated-resources")
22 22
23task generateVersionFile { 23task generateVersionFile {
24 ext.outputFile = file("$genOutputDir/version.txt") 24 ext.outputFile = file("$genOutputDir/version.txt")
25 outputs.file(outputFile) 25 outputs.file(outputFile)
26 doLast { 26 doLast {
27 outputFile.text = "${project.version}" 27 outputFile.text = "${project.version}"
28 } 28 }
29} 29}
30 30
31sourceSets.main.output.dir genOutputDir, builtBy: generateVersionFile 31sourceSets.main.output.dir genOutputDir, builtBy: generateVersionFile
@@ -36,29 +36,29 @@ def libraryJarsArg = "<java.home>/jmods"
36 36
37// If your test fails for class file version problem with proguard, run gradle with -Dorg.gradle.java.home="<older jdk>" flag 37// If your test fails for class file version problem with proguard, run gradle with -Dorg.gradle.java.home="<older jdk>" flag
38file('src/test/java/cuchaz/enigma/inputs').listFiles().each { theFile -> 38file('src/test/java/cuchaz/enigma/inputs').listFiles().each { theFile ->
39 if (theFile.directory) { 39 if (theFile.directory) {
40 task("${theFile.name}TestJar", type: Jar) { 40 task("${theFile.name}TestJar", type: Jar) {
41 from(sourceSets.test.output) { 41 from(sourceSets.test.output) {
42 include "cuchaz/enigma/inputs/$theFile.name/**/*.class" 42 include "cuchaz/enigma/inputs/$theFile.name/**/*.class"
43 include 'cuchaz/enigma/inputs/Keep.class' 43 include 'cuchaz/enigma/inputs/Keep.class'
44 } 44 }
45 45
46 archiveFileName = theFile.name + '.jar' 46 archiveFileName = theFile.name + '.jar'
47 destinationDirectory = file('build/test-inputs') 47 destinationDirectory = file('build/test-inputs')
48 } 48 }
49 49
50 task("${theFile.name}TestObf", type: JavaExec, 50 task("${theFile.name}TestObf", type: JavaExec,
51 dependsOn: "${theFile.name}TestJar") { 51 dependsOn: "${theFile.name}TestJar") {
52 mainClass = 'proguard.ProGuard' 52 mainClass = 'proguard.ProGuard'
53 classpath configurations.proGuard 53 classpath configurations.proGuard
54 54
55 args '@src/test/resources/proguard-test.conf', '-injars', file('build/test-inputs/' + 55 args '@src/test/resources/proguard-test.conf', '-injars', file('build/test-inputs/' +
56 "${theFile.name}.jar"), '-libraryjars', libraryJarsArg, 56 "${theFile.name}.jar"), '-libraryjars', libraryJarsArg,
57 '-outjars', file('build/test-obf/' + "${theFile.name}.jar") 57 '-outjars', file('build/test-obf/' + "${theFile.name}.jar")
58 } 58 }
59 59
60 test.dependsOn "${theFile.name}TestObf" 60 test.dependsOn "${theFile.name}TestObf"
61 } 61 }
62} 62}
63 63
64test.dependsOn 'translationTestObf' 64test.dependsOn 'translationTestObf'
diff --git a/enigma/src/main/java/cuchaz/enigma/Enigma.java b/enigma/src/main/java/cuchaz/enigma/Enigma.java
index a37f074..696a848 100644
--- a/enigma/src/main/java/cuchaz/enigma/Enigma.java
+++ b/enigma/src/main/java/cuchaz/enigma/Enigma.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
@@ -35,12 +35,12 @@ import cuchaz.enigma.classprovider.JarClassProvider;
35import cuchaz.enigma.utils.Utils; 35import cuchaz.enigma.utils.Utils;
36 36
37public class Enigma { 37public class Enigma {
38 public static final String NAME = "Enigma"; 38 public static final String NAME = "Enigma";
39 public static final String VERSION; 39 public static final String VERSION;
40 public static final String URL = "https://fabricmc.net"; 40 public static final String URL = "https://fabricmc.net";
41 public static final int ASM_VERSION = Opcodes.ASM9; 41 public static final int ASM_VERSION = Opcodes.ASM9;
42 42
43 private final EnigmaProfile profile; 43 private final EnigmaProfile profile;
44 private final EnigmaServices services; 44 private final EnigmaServices services;
45 45
46 private Enigma(EnigmaProfile profile, EnigmaServices services) { 46 private Enigma(EnigmaProfile profile, EnigmaServices services) {
@@ -97,6 +97,7 @@ public class Enigma {
97 97
98 public Enigma build() { 98 public Enigma build() {
99 PluginContext pluginContext = new PluginContext(profile); 99 PluginContext pluginContext = new PluginContext(profile);
100
100 for (EnigmaPlugin plugin : plugins) { 101 for (EnigmaPlugin plugin : plugins) {
101 plugin.init(pluginContext); 102 plugin.init(pluginContext);
102 } 103 }
diff --git a/enigma/src/main/java/cuchaz/enigma/EnigmaProfile.java b/enigma/src/main/java/cuchaz/enigma/EnigmaProfile.java
index daf2727..f95bf1e 100644
--- a/enigma/src/main/java/cuchaz/enigma/EnigmaProfile.java
+++ b/enigma/src/main/java/cuchaz/enigma/EnigmaProfile.java
@@ -1,5 +1,20 @@
1package cuchaz.enigma; 1package cuchaz.enigma;
2 2
3import java.io.BufferedReader;
4import java.io.IOException;
5import java.io.InputStreamReader;
6import java.io.Reader;
7import java.lang.reflect.Type;
8import java.nio.charset.StandardCharsets;
9import java.nio.file.Files;
10import java.nio.file.Path;
11import java.util.Collections;
12import java.util.List;
13import java.util.Map;
14import java.util.Optional;
15
16import javax.annotation.Nullable;
17
3import com.google.common.collect.ImmutableMap; 18import com.google.common.collect.ImmutableMap;
4import com.google.gson.Gson; 19import com.google.gson.Gson;
5import com.google.gson.GsonBuilder; 20import com.google.gson.GsonBuilder;
@@ -10,31 +25,16 @@ import com.google.gson.JsonObject;
10import com.google.gson.JsonParseException; 25import com.google.gson.JsonParseException;
11import com.google.gson.annotations.SerializedName; 26import com.google.gson.annotations.SerializedName;
12import com.google.gson.reflect.TypeToken; 27import com.google.gson.reflect.TypeToken;
28
13import cuchaz.enigma.api.service.EnigmaServiceType; 29import cuchaz.enigma.api.service.EnigmaServiceType;
14import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; 30import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat;
15import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; 31import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
16 32
17import javax.annotation.Nullable;
18import java.io.BufferedReader;
19import java.io.IOException;
20import java.io.InputStreamReader;
21import java.io.Reader;
22import java.lang.reflect.Type;
23import java.nio.charset.StandardCharsets;
24import java.nio.file.Files;
25import java.nio.file.Path;
26import java.util.Collections;
27import java.util.List;
28import java.util.Map;
29import java.util.Optional;
30
31public final class EnigmaProfile { 33public final class EnigmaProfile {
32 public static final EnigmaProfile EMPTY = new EnigmaProfile(new ServiceContainer(ImmutableMap.of())); 34 public static final EnigmaProfile EMPTY = new EnigmaProfile(new ServiceContainer(ImmutableMap.of()));
33 35
34 private static final MappingSaveParameters DEFAULT_MAPPING_SAVE_PARAMETERS = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); 36 private static final MappingSaveParameters DEFAULT_MAPPING_SAVE_PARAMETERS = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF);
35 private static final Gson GSON = new GsonBuilder() 37 private static final Gson GSON = new GsonBuilder().registerTypeAdapter(ServiceContainer.class, (JsonDeserializer<ServiceContainer>) EnigmaProfile::loadServiceContainer).create();
36 .registerTypeAdapter(ServiceContainer.class, (JsonDeserializer<ServiceContainer>) EnigmaProfile::loadServiceContainer)
37 .create();
38 private static final Type SERVICE_LIST_TYPE = new TypeToken<List<Service>>() { 38 private static final Type SERVICE_LIST_TYPE = new TypeToken<List<Service>>() {
39 }.getType(); 39 }.getType();
40 40
@@ -78,6 +78,7 @@ public final class EnigmaProfile {
78 78
79 for (Map.Entry<String, JsonElement> entry : object.entrySet()) { 79 for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
80 JsonElement value = entry.getValue(); 80 JsonElement value = entry.getValue();
81
81 if (value.isJsonObject()) { 82 if (value.isJsonObject()) {
82 builder.put(entry.getKey(), Collections.singletonList(GSON.fromJson(value, Service.class))); 83 builder.put(entry.getKey(), Collections.singletonList(GSON.fromJson(value, Service.class)));
83 } else if (value.isJsonArray()) { 84 } else if (value.isJsonArray()) {
diff --git a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java
index 4f50f2f..15d5e98 100644
--- a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java
+++ b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java
@@ -18,16 +18,16 @@ import java.util.stream.Stream;
18 18
19import com.google.common.base.Functions; 19import com.google.common.base.Functions;
20import com.google.common.base.Preconditions; 20import com.google.common.base.Preconditions;
21import cuchaz.enigma.api.service.ObfuscationTestService;
22import cuchaz.enigma.classprovider.ObfuscationFixClassProvider;
23import org.objectweb.asm.ClassWriter; 21import org.objectweb.asm.ClassWriter;
24import org.objectweb.asm.tree.ClassNode; 22import org.objectweb.asm.tree.ClassNode;
25 23
26import cuchaz.enigma.analysis.EntryReference; 24import cuchaz.enigma.analysis.EntryReference;
27import cuchaz.enigma.analysis.index.JarIndex; 25import cuchaz.enigma.analysis.index.JarIndex;
28import cuchaz.enigma.api.service.NameProposalService; 26import cuchaz.enigma.api.service.NameProposalService;
27import cuchaz.enigma.api.service.ObfuscationTestService;
29import cuchaz.enigma.bytecode.translators.TranslationClassVisitor; 28import cuchaz.enigma.bytecode.translators.TranslationClassVisitor;
30import cuchaz.enigma.classprovider.ClassProvider; 29import cuchaz.enigma.classprovider.ClassProvider;
30import cuchaz.enigma.classprovider.ObfuscationFixClassProvider;
31import cuchaz.enigma.source.Decompiler; 31import cuchaz.enigma.source.Decompiler;
32import cuchaz.enigma.source.DecompilerService; 32import cuchaz.enigma.source.DecompilerService;
33import cuchaz.enigma.source.SourceSettings; 33import cuchaz.enigma.source.SourceSettings;
@@ -101,6 +101,7 @@ public class EnigmaProject {
101 DeltaTrackingTree<EntryMapping> mappings = mapper.getObfToDeobf(); 101 DeltaTrackingTree<EntryMapping> mappings = mapper.getObfToDeobf();
102 102
103 Collection<Entry<?>> dropped = dropMappings(mappings, progress); 103 Collection<Entry<?>> dropped = dropMappings(mappings, progress);
104
104 for (Entry<?> entry : dropped) { 105 for (Entry<?> entry : dropped) {
105 mappings.trackChange(entry); 106 mappings.trackChange(entry);
106 } 107 }
@@ -112,6 +113,7 @@ public class EnigmaProject {
112 MappingsChecker.Dropped dropped = checker.dropBrokenMappings(progress); 113 MappingsChecker.Dropped dropped = checker.dropBrokenMappings(progress);
113 114
114 Map<Entry<?>, String> droppedMappings = dropped.getDroppedMappings(); 115 Map<Entry<?>, String> droppedMappings = dropped.getDroppedMappings();
116
115 for (Map.Entry<Entry<?>, String> mapping : droppedMappings.entrySet()) { 117 for (Map.Entry<Entry<?>, String> mapping : droppedMappings.entrySet()) {
116 System.out.println("WARNING: Couldn't find " + mapping.getKey() + " (" + mapping.getValue() + ") in jar. Mapping was dropped."); 118 System.out.println("WARNING: Couldn't find " + mapping.getKey() + " (" + mapping.getValue() + ") in jar. Mapping was dropped.");
117 } 119 }
@@ -124,6 +126,7 @@ public class EnigmaProject {
124 // HACKHACK: Object methods are not obfuscated identifiers 126 // HACKHACK: Object methods are not obfuscated identifiers
125 String name = obfMethodEntry.getName(); 127 String name = obfMethodEntry.getName();
126 String sig = obfMethodEntry.getDesc().toString(); 128 String sig = obfMethodEntry.getDesc().toString();
129
127 //TODO replace with a map or check if declaring class is java.lang.Object 130 //TODO replace with a map or check if declaring class is java.lang.Object
128 if (name.equals("clone") && sig.equals("()Ljava/lang/Object;")) { 131 if (name.equals("clone") && sig.equals("()Ljava/lang/Object;")) {
129 return false; 132 return false;
@@ -163,6 +166,7 @@ public class EnigmaProject {
163 String name = entry.getName(); 166 String name = entry.getName();
164 167
165 List<ObfuscationTestService> obfuscationTestServices = this.getEnigma().getServices().get(ObfuscationTestService.TYPE); 168 List<ObfuscationTestService> obfuscationTestServices = this.getEnigma().getServices().get(ObfuscationTestService.TYPE);
169
166 if (!obfuscationTestServices.isEmpty()) { 170 if (!obfuscationTestServices.isEmpty()) {
167 for (ObfuscationTestService service : obfuscationTestServices) { 171 for (ObfuscationTestService service : obfuscationTestServices) {
168 if (service.testDeobfuscated(entry)) { 172 if (service.testDeobfuscated(entry)) {
@@ -172,6 +176,7 @@ public class EnigmaProject {
172 } 176 }
173 177
174 List<NameProposalService> nameProposalServices = this.getEnigma().getServices().get(NameProposalService.TYPE); 178 List<NameProposalService> nameProposalServices = this.getEnigma().getServices().get(NameProposalService.TYPE);
179
175 if (!nameProposalServices.isEmpty()) { 180 if (!nameProposalServices.isEmpty()) {
176 for (NameProposalService service : nameProposalServices) { 181 for (NameProposalService service : nameProposalServices) {
177 if (service.proposeName(entry, mapper).isPresent()) { 182 if (service.proposeName(entry, mapper).isPresent()) {
@@ -181,6 +186,7 @@ public class EnigmaProject {
181 } 186 }
182 187
183 String mappedName = mapper.deobfuscate(entry).getName(); 188 String mappedName = mapper.deobfuscate(entry).getName();
189
184 if (mappedName != null && !mappedName.isEmpty() && !mappedName.equals(name)) { 190 if (mappedName != null && !mappedName.isEmpty() && !mappedName.equals(name)) {
185 return false; 191 return false;
186 } 192 }
@@ -198,22 +204,20 @@ public class EnigmaProject {
198 AtomicInteger count = new AtomicInteger(); 204 AtomicInteger count = new AtomicInteger();
199 progress.init(classEntries.size(), I18n.translate("progress.classes.deobfuscating")); 205 progress.init(classEntries.size(), I18n.translate("progress.classes.deobfuscating"));
200 206
201 Map<String, ClassNode> compiled = classEntries.parallelStream() 207 Map<String, ClassNode> compiled = classEntries.parallelStream().map(entry -> {
202 .map(entry -> { 208 ClassEntry translatedEntry = deobfuscator.translate(entry);
203 ClassEntry translatedEntry = deobfuscator.translate(entry); 209 progress.step(count.getAndIncrement(), translatedEntry.toString());
204 progress.step(count.getAndIncrement(), translatedEntry.toString());
205 210
206 ClassNode node = fixingClassProvider.get(entry.getFullName()); 211 ClassNode node = fixingClassProvider.get(entry.getFullName());
207 if (node != null) {
208 ClassNode translatedNode = new ClassNode();
209 node.accept(new TranslationClassVisitor(deobfuscator, Enigma.ASM_VERSION, translatedNode));
210 return translatedNode;
211 }
212 212
213 return null; 213 if (node != null) {
214 }) 214 ClassNode translatedNode = new ClassNode();
215 .filter(Objects::nonNull) 215 node.accept(new TranslationClassVisitor(deobfuscator, Enigma.ASM_VERSION, translatedNode));
216 .collect(Collectors.toMap(n -> n.name, Functions.identity())); 216 return translatedNode;
217 }
218
219 return null;
220 }).filter(Objects::nonNull).collect(Collectors.toMap(n -> n.name, Functions.identity()));
217 221
218 return new JarExport(mapper, compiled); 222 return new JarExport(mapper, compiled);
219 } 223 }
@@ -254,9 +258,7 @@ public class EnigmaProject {
254 } 258 }
255 259
256 public Stream<ClassSource> decompileStream(ProgressListener progress, DecompilerService decompilerService, DecompileErrorStrategy errorStrategy) { 260 public Stream<ClassSource> decompileStream(ProgressListener progress, DecompilerService decompilerService, DecompileErrorStrategy errorStrategy) {
257 Collection<ClassNode> classes = this.compiled.values().stream() 261 Collection<ClassNode> classes = this.compiled.values().stream().filter(classNode -> classNode.name.indexOf('$') == -1).toList();
258 .filter(classNode -> classNode.name.indexOf('$') == -1)
259 .toList();
260 262
261 progress.init(classes.size(), I18n.translate("progress.classes.decompiling")); 263 progress.init(classes.size(), I18n.translate("progress.classes.decompiling"));
262 264
@@ -265,33 +267,34 @@ public class EnigmaProject {
265 267
266 AtomicInteger count = new AtomicInteger(); 268 AtomicInteger count = new AtomicInteger();
267 269
268 return classes.parallelStream() 270 return classes.parallelStream().map(translatedNode -> {
269 .map(translatedNode -> { 271 progress.step(count.getAndIncrement(), translatedNode.name);
270 progress.step(count.getAndIncrement(), translatedNode.name); 272
271 273 String source = null;
272 String source = null; 274
273 try { 275 try {
274 source = decompileClass(translatedNode, decompiler); 276 source = decompileClass(translatedNode, decompiler);
275 } catch (Throwable throwable) { 277 } catch (Throwable throwable) {
276 switch (errorStrategy) { 278 switch (errorStrategy) {
277 case PROPAGATE: throw throwable; 279 case PROPAGATE:
278 case IGNORE: break; 280 throw throwable;
279 case TRACE_AS_SOURCE: { 281 case IGNORE:
280 StringWriter writer = new StringWriter(); 282 break;
281 throwable.printStackTrace(new PrintWriter(writer)); 283 case TRACE_AS_SOURCE: {
282 source = writer.toString(); 284 StringWriter writer = new StringWriter();
283 break; 285 throwable.printStackTrace(new PrintWriter(writer));
284 } 286 source = writer.toString();
285 } 287 break;
286 } 288 }
287 289 }
288 if (source == null) { 290 }
289 return null; 291
290 } 292 if (source == null) {
291 293 return null;
292 return new ClassSource(translatedNode.name, source); 294 }
293 }) 295
294 .filter(Objects::nonNull); 296 return new ClassSource(translatedNode.name, source);
297 }).filter(Objects::nonNull);
295 } 298 }
296 299
297 private String decompileClass(ClassNode translatedNode, Decompiler decompiler) { 300 private String decompileClass(ClassNode translatedNode, Decompiler decompiler) {
@@ -310,6 +313,7 @@ public class EnigmaProject {
310 progress.init(decompiled.size(), I18n.translate("progress.sources.writing")); 313 progress.init(decompiled.size(), I18n.translate("progress.sources.writing"));
311 314
312 int count = 0; 315 int count = 0;
316
313 for (ClassSource source : decompiled) { 317 for (ClassSource source : decompiled) {
314 progress.step(count++, source.name); 318 progress.step(count++, source.name);
315 319
@@ -329,7 +333,6 @@ public class EnigmaProject {
329 } 333 }
330 334
331 public void writeTo(Path path) throws IOException { 335 public void writeTo(Path path) throws IOException {
332 Files.createDirectories(path.getParent());
333 try (BufferedWriter writer = Files.newBufferedWriter(path)) { 336 try (BufferedWriter writer = Files.newBufferedWriter(path)) {
334 writer.write(source); 337 writer.write(source);
335 } 338 }
diff --git a/enigma/src/main/java/cuchaz/enigma/EnigmaServices.java b/enigma/src/main/java/cuchaz/enigma/EnigmaServices.java
index df3b7bb..bbdc684 100644
--- a/enigma/src/main/java/cuchaz/enigma/EnigmaServices.java
+++ b/enigma/src/main/java/cuchaz/enigma/EnigmaServices.java
@@ -1,11 +1,12 @@
1package cuchaz.enigma; 1package cuchaz.enigma;
2 2
3import java.util.List;
4
3import com.google.common.collect.ImmutableListMultimap; 5import com.google.common.collect.ImmutableListMultimap;
6
4import cuchaz.enigma.api.service.EnigmaService; 7import cuchaz.enigma.api.service.EnigmaService;
5import cuchaz.enigma.api.service.EnigmaServiceType; 8import cuchaz.enigma.api.service.EnigmaServiceType;
6 9
7import java.util.List;
8
9public final class EnigmaServices { 10public final class EnigmaServices {
10 private final ImmutableListMultimap<EnigmaServiceType<?>, EnigmaService> services; 11 private final ImmutableListMultimap<EnigmaServiceType<?>, EnigmaService> services;
11 12
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/Access.java b/enigma/src/main/java/cuchaz/enigma/analysis/Access.java
index 82ca669..cc7d121 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/Access.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/Access.java
@@ -1,23 +1,25 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import cuchaz.enigma.translation.representation.AccessFlags;
15
16import java.lang.reflect.Modifier; 14import java.lang.reflect.Modifier;
17 15
18public enum Access { 16import cuchaz.enigma.translation.representation.AccessFlags;
19 17
20 PUBLIC, PROTECTED, PACKAGE, PRIVATE; 18public enum Access {
19 PUBLIC,
20 PROTECTED,
21 PACKAGE,
22 PRIVATE;
21 23
22 public static Access get(AccessFlags flags) { 24 public static Access get(AccessFlags flags) {
23 return get(flags.getFlags()); 25 return get(flags.getFlags());
@@ -37,6 +39,7 @@ public enum Access {
37 } else if (!isPublic && !isProtected && !isPrivate) { 39 } else if (!isPublic && !isProtected && !isPrivate) {
38 return PACKAGE; 40 return PACKAGE;
39 } 41 }
42
40 // assume public by default 43 // assume public by default
41 return PUBLIC; 44 return PUBLIC;
42 } 45 }
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java b/enigma/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java
index 013c52f..45dac2c 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java
@@ -1,17 +1,13 @@
1package cuchaz.enigma.analysis; 1package cuchaz.enigma.analysis;
2 2
3import cuchaz.enigma.Enigma; 3import java.util.ArrayList;
4import cuchaz.enigma.api.EnigmaPlugin; 4import java.util.HashMap;
5import cuchaz.enigma.api.EnigmaPluginContext; 5import java.util.HashSet;
6import cuchaz.enigma.api.service.JarIndexerService; 6import java.util.List;
7import cuchaz.enigma.api.service.NameProposalService; 7import java.util.Map;
8import cuchaz.enigma.source.DecompilerService; 8import java.util.Optional;
9import cuchaz.enigma.source.Decompilers; 9import java.util.Set;
10import cuchaz.enigma.translation.representation.TypeDescriptor; 10
11import cuchaz.enigma.translation.representation.entry.ClassEntry;
12import cuchaz.enigma.translation.representation.entry.Entry;
13import cuchaz.enigma.translation.representation.entry.FieldEntry;
14import cuchaz.enigma.utils.Pair;
15import org.objectweb.asm.ClassVisitor; 11import org.objectweb.asm.ClassVisitor;
16import org.objectweb.asm.FieldVisitor; 12import org.objectweb.asm.FieldVisitor;
17import org.objectweb.asm.MethodVisitor; 13import org.objectweb.asm.MethodVisitor;
@@ -27,16 +23,20 @@ import org.objectweb.asm.tree.analysis.Frame;
27import org.objectweb.asm.tree.analysis.SourceInterpreter; 23import org.objectweb.asm.tree.analysis.SourceInterpreter;
28import org.objectweb.asm.tree.analysis.SourceValue; 24import org.objectweb.asm.tree.analysis.SourceValue;
29 25
30import java.util.ArrayList; 26import cuchaz.enigma.Enigma;
31import java.util.HashMap; 27import cuchaz.enigma.api.EnigmaPlugin;
32import java.util.HashSet; 28import cuchaz.enigma.api.EnigmaPluginContext;
33import java.util.List; 29import cuchaz.enigma.api.service.JarIndexerService;
34import java.util.Map; 30import cuchaz.enigma.api.service.NameProposalService;
35import java.util.Optional; 31import cuchaz.enigma.source.DecompilerService;
36import java.util.Set; 32import cuchaz.enigma.source.Decompilers;
33import cuchaz.enigma.translation.representation.TypeDescriptor;
34import cuchaz.enigma.translation.representation.entry.ClassEntry;
35import cuchaz.enigma.translation.representation.entry.Entry;
36import cuchaz.enigma.translation.representation.entry.FieldEntry;
37import cuchaz.enigma.utils.Pair;
37 38
38public final class BuiltinPlugin implements EnigmaPlugin { 39public final class BuiltinPlugin implements EnigmaPlugin {
39
40 public BuiltinPlugin() { 40 public BuiltinPlugin() {
41 } 41 }
42 42
@@ -61,7 +61,6 @@ public final class BuiltinPlugin implements EnigmaPlugin {
61 } 61 }
62 62
63 private static final class EnumFieldNameFindingVisitor extends ClassVisitor { 63 private static final class EnumFieldNameFindingVisitor extends ClassVisitor {
64
65 private ClassEntry clazz; 64 private ClassEntry clazz;
66 private String className; 65 private String className;
67 private final Map<Entry<?>, String> mappings; 66 private final Map<Entry<?>, String> mappings;
@@ -89,6 +88,7 @@ public final class BuiltinPlugin implements EnigmaPlugin {
89 throw new IllegalArgumentException("Found two enum fields with the same name \"" + name + "\" and desc \"" + descriptor + "\"!"); 88 throw new IllegalArgumentException("Found two enum fields with the same name \"" + name + "\" and desc \"" + descriptor + "\"!");
90 } 89 }
91 } 90 }
91
92 return super.visitField(access, name, descriptor, signature, value); 92 return super.visitField(access, name, descriptor, signature, value);
93 } 93 }
94 94
@@ -99,12 +99,14 @@ public final class BuiltinPlugin implements EnigmaPlugin {
99 classInits.add(node); 99 classInits.add(node);
100 return node; 100 return node;
101 } 101 }
102
102 return super.visitMethod(access, name, descriptor, signature, exceptions); 103 return super.visitMethod(access, name, descriptor, signature, exceptions);
103 } 104 }
104 105
105 @Override 106 @Override
106 public void visitEnd() { 107 public void visitEnd() {
107 super.visitEnd(); 108 super.visitEnd();
109
108 try { 110 try {
109 collectResults(); 111 collectResults();
110 } catch (Exception ex) { 112 } catch (Exception ex) {
@@ -118,21 +120,18 @@ public final class BuiltinPlugin implements EnigmaPlugin {
118 120
119 for (MethodNode mn : classInits) { 121 for (MethodNode mn : classInits) {
120 Frame<SourceValue>[] frames = analyzer.analyze(className, mn); 122 Frame<SourceValue>[] frames = analyzer.analyze(className, mn);
121
122 InsnList instrs = mn.instructions; 123 InsnList instrs = mn.instructions;
124
123 for (int i = 1; i < instrs.size(); i++) { 125 for (int i = 1; i < instrs.size(); i++) {
124 AbstractInsnNode instr1 = instrs.get(i - 1); 126 AbstractInsnNode instr1 = instrs.get(i - 1);
125 AbstractInsnNode instr2 = instrs.get(i); 127 AbstractInsnNode instr2 = instrs.get(i);
126 String s = null; 128 String s = null;
127 129
128 if (instr2.getOpcode() == Opcodes.PUTSTATIC 130 if (instr2.getOpcode() == Opcodes.PUTSTATIC && ((FieldInsnNode) instr2).owner.equals(owner) && enumFields.contains(new Pair<>(((FieldInsnNode) instr2).name, ((FieldInsnNode) instr2).desc)) && instr1.getOpcode() == Opcodes.INVOKESPECIAL && "<init>".equals(
129 && ((FieldInsnNode) instr2).owner.equals(owner) 131 ((MethodInsnNode) instr1).name)) {
130 && enumFields.contains(new Pair<>(((FieldInsnNode) instr2).name, ((FieldInsnNode) instr2).desc))
131 && instr1.getOpcode() == Opcodes.INVOKESPECIAL
132 && "<init>".equals(((MethodInsnNode) instr1).name)) {
133
134 for (int j = 0; j < frames[i - 1].getStackSize(); j++) { 132 for (int j = 0; j < frames[i - 1].getStackSize(); j++) {
135 SourceValue sv = frames[i - 1].getStack(j); 133 SourceValue sv = frames[i - 1].getStack(j);
134
136 for (AbstractInsnNode ci : sv.insns) { 135 for (AbstractInsnNode ci : sv.insns) {
137 if (ci instanceof LdcInsnNode && ((LdcInsnNode) ci).cst instanceof String) { 136 if (ci instanceof LdcInsnNode && ((LdcInsnNode) ci).cst instanceof String) {
138 //if (s == null || !s.equals(((LdcInsnNode) ci).cst)) { 137 //if (s == null || !s.equals(((LdcInsnNode) ci).cst)) {
@@ -148,6 +147,7 @@ public final class BuiltinPlugin implements EnigmaPlugin {
148 if (s != null) { 147 if (s != null) {
149 mappings.put(new FieldEntry(clazz, ((FieldInsnNode) instr2).name, new TypeDescriptor(((FieldInsnNode) instr2).desc)), s); 148 mappings.put(new FieldEntry(clazz, ((FieldInsnNode) instr2).name, new TypeDescriptor(((FieldInsnNode) instr2).desc)), s);
150 } 149 }
150
151 // report otherwise? 151 // report otherwise?
152 } 152 }
153 } 153 }
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/enigma/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
index 0fc44ca..8ef28d9 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
@@ -1,27 +1,29 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import java.util.Collection;
15import java.util.List;
16
17import javax.swing.tree.DefaultMutableTreeNode;
18
14import com.google.common.collect.Lists; 19import com.google.common.collect.Lists;
20
15import cuchaz.enigma.analysis.index.InheritanceIndex; 21import cuchaz.enigma.analysis.index.InheritanceIndex;
16import cuchaz.enigma.analysis.index.JarIndex; 22import cuchaz.enigma.analysis.index.JarIndex;
17import cuchaz.enigma.translation.Translator; 23import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.translation.representation.entry.ClassEntry; 24import cuchaz.enigma.translation.representation.entry.ClassEntry;
19import cuchaz.enigma.translation.representation.entry.MethodEntry; 25import cuchaz.enigma.translation.representation.entry.MethodEntry;
20 26
21import javax.swing.tree.DefaultMutableTreeNode;
22import java.util.Collection;
23import java.util.List;
24
25public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { 27public class ClassImplementationsTreeNode extends DefaultMutableTreeNode {
26 private final Translator translator; 28 private final Translator translator;
27 private final ClassEntry entry; 29 private final ClassEntry entry;
@@ -40,10 +42,12 @@ public class ClassImplementationsTreeNode extends DefaultMutableTreeNode {
40 // recurse 42 // recurse
41 for (int i = 0; i < node.getChildCount(); i++) { 43 for (int i = 0; i < node.getChildCount(); i++) {
42 ClassImplementationsTreeNode foundNode = findNode((ClassImplementationsTreeNode) node.getChildAt(i), entry); 44 ClassImplementationsTreeNode foundNode = findNode((ClassImplementationsTreeNode) node.getChildAt(i), entry);
45
43 if (foundNode != null) { 46 if (foundNode != null) {
44 return foundNode; 47 return foundNode;
45 } 48 }
46 } 49 }
50
47 return null; 51 return null;
48 } 52 }
49 53
@@ -62,6 +66,7 @@ public class ClassImplementationsTreeNode extends DefaultMutableTreeNode {
62 InheritanceIndex inheritanceIndex = index.getInheritanceIndex(); 66 InheritanceIndex inheritanceIndex = index.getInheritanceIndex();
63 67
64 Collection<ClassEntry> inheritors = inheritanceIndex.getChildren(entry); 68 Collection<ClassEntry> inheritors = inheritanceIndex.getChildren(entry);
69
65 for (ClassEntry inheritor : inheritors) { 70 for (ClassEntry inheritor : inheritors) {
66 nodes.add(new ClassImplementationsTreeNode(translator, inheritor)); 71 nodes.add(new ClassImplementationsTreeNode(translator, inheritor));
67 } 72 }
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/enigma/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
index 788c534..24da23c 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
@@ -1,24 +1,26 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import java.util.List;
15
16import javax.swing.tree.DefaultMutableTreeNode;
17
14import com.google.common.collect.Lists; 18import com.google.common.collect.Lists;
19
15import cuchaz.enigma.analysis.index.InheritanceIndex; 20import cuchaz.enigma.analysis.index.InheritanceIndex;
16import cuchaz.enigma.translation.Translator; 21import cuchaz.enigma.translation.Translator;
17import cuchaz.enigma.translation.representation.entry.ClassEntry; 22import cuchaz.enigma.translation.representation.entry.ClassEntry;
18 23
19import javax.swing.tree.DefaultMutableTreeNode;
20import java.util.List;
21
22public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { 24public class ClassInheritanceTreeNode extends DefaultMutableTreeNode {
23 private final Translator translator; 25 private final Translator translator;
24 private final ClassEntry obfClassEntry; 26 private final ClassEntry obfClassEntry;
@@ -37,10 +39,12 @@ public class ClassInheritanceTreeNode extends DefaultMutableTreeNode {
37 // recurse 39 // recurse
38 for (int i = 0; i < node.getChildCount(); i++) { 40 for (int i = 0; i < node.getChildCount(); i++) {
39 ClassInheritanceTreeNode foundNode = findNode((ClassInheritanceTreeNode) node.getChildAt(i), entry); 41 ClassInheritanceTreeNode foundNode = findNode((ClassInheritanceTreeNode) node.getChildAt(i), entry);
42
40 if (foundNode != null) { 43 if (foundNode != null) {
41 return foundNode; 44 return foundNode;
42 } 45 }
43 } 46 }
47
44 return null; 48 return null;
45 } 49 }
46 50
@@ -63,6 +67,7 @@ public class ClassInheritanceTreeNode extends DefaultMutableTreeNode {
63 public void load(InheritanceIndex ancestries, boolean recurse) { 67 public void load(InheritanceIndex ancestries, boolean recurse) {
64 // get all the child nodes 68 // get all the child nodes
65 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList(); 69 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList();
70
66 for (ClassEntry inheritor : ancestries.getChildren(this.obfClassEntry)) { 71 for (ClassEntry inheritor : ancestries.getChildren(this.obfClassEntry)) {
67 nodes.add(new ClassInheritanceTreeNode(translator, inheritor.getFullName())); 72 nodes.add(new ClassInheritanceTreeNode(translator, inheritor.getFullName()));
68 } 73 }
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java b/enigma/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java
index 0142412..c76dca7 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java
@@ -1,17 +1,23 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import java.util.Set;
15
16import javax.swing.tree.DefaultMutableTreeNode;
17import javax.swing.tree.TreeNode;
18
14import com.google.common.collect.Sets; 19import com.google.common.collect.Sets;
20
15import cuchaz.enigma.analysis.index.JarIndex; 21import cuchaz.enigma.analysis.index.JarIndex;
16import cuchaz.enigma.analysis.index.ReferenceIndex; 22import cuchaz.enigma.analysis.index.ReferenceIndex;
17import cuchaz.enigma.translation.Translator; 23import cuchaz.enigma.translation.Translator;
@@ -19,13 +25,7 @@ import cuchaz.enigma.translation.representation.entry.ClassEntry;
19import cuchaz.enigma.translation.representation.entry.Entry; 25import cuchaz.enigma.translation.representation.entry.Entry;
20import cuchaz.enigma.translation.representation.entry.MethodDefEntry; 26import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
21 27
22import javax.swing.tree.DefaultMutableTreeNode; 28public class ClassReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<ClassEntry, MethodDefEntry> {
23import javax.swing.tree.TreeNode;
24import java.util.Set;
25
26public class ClassReferenceTreeNode extends DefaultMutableTreeNode
27 implements ReferenceTreeNode<ClassEntry, MethodDefEntry> {
28
29 private Translator deobfuscatingTranslator; 29 private Translator deobfuscatingTranslator;
30 private ClassEntry entry; 30 private ClassEntry entry;
31 private EntryReference<ClassEntry, MethodDefEntry> reference; 31 private EntryReference<ClassEntry, MethodDefEntry> reference;
@@ -57,6 +57,7 @@ public class ClassReferenceTreeNode extends DefaultMutableTreeNode
57 if (this.reference != null) { 57 if (this.reference != null) {
58 return String.format("%s", this.deobfuscatingTranslator.translate(this.reference.context)); 58 return String.format("%s", this.deobfuscatingTranslator.translate(this.reference.context));
59 } 59 }
60
60 return this.deobfuscatingTranslator.translate(this.entry).getFullName(); 61 return this.deobfuscatingTranslator.translate(this.entry).getFullName();
61 } 62 }
62 63
@@ -71,16 +72,18 @@ public class ClassReferenceTreeNode extends DefaultMutableTreeNode
71 if (recurse && this.children != null) { 72 if (recurse && this.children != null) {
72 for (Object child : this.children) { 73 for (Object child : this.children) {
73 if (child instanceof ClassReferenceTreeNode node) { 74 if (child instanceof ClassReferenceTreeNode node) {
74
75 // don't recurse into ancestor 75 // don't recurse into ancestor
76 Set<Entry<?>> ancestors = Sets.newHashSet(); 76 Set<Entry<?>> ancestors = Sets.newHashSet();
77 TreeNode n = node; 77 TreeNode n = node;
78
78 while (n.getParent() != null) { 79 while (n.getParent() != null) {
79 n = n.getParent(); 80 n = n.getParent();
81
80 if (n instanceof ClassReferenceTreeNode) { 82 if (n instanceof ClassReferenceTreeNode) {
81 ancestors.add(((ClassReferenceTreeNode) n).getEntry()); 83 ancestors.add(((ClassReferenceTreeNode) n).getEntry());
82 } 84 }
83 } 85 }
86
84 if (ancestors.contains(node.getEntry())) { 87 if (ancestors.contains(node.getEntry())) {
85 continue; 88 continue;
86 } 89 }
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/EntryReference.java b/enigma/src/main/java/cuchaz/enigma/analysis/EntryReference.java
index 281b05f..9c54281 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/EntryReference.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/EntryReference.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
@@ -26,7 +26,6 @@ import cuchaz.enigma.translation.representation.entry.Entry;
26import cuchaz.enigma.translation.representation.entry.MethodEntry; 26import cuchaz.enigma.translation.representation.entry.MethodEntry;
27 27
28public class EntryReference<E extends Entry<?>, C extends Entry<?>> implements Translatable { 28public class EntryReference<E extends Entry<?>, C extends Entry<?>> implements Translatable {
29
30 private static final List<String> CONSTRUCTOR_NON_NAMES = Arrays.asList("this", "super", "static"); 29 private static final List<String> CONSTRUCTOR_NON_NAMES = Arrays.asList("this", "super", "static");
31 public final E entry; 30 public final E entry;
32 public final C context; 31 public final C context;
@@ -60,8 +59,7 @@ public class EntryReference<E extends Entry<?>, C extends Entry<?>> implements T
60 this.targetType = targetType; 59 this.targetType = targetType;
61 this.declaration = declaration; 60 this.declaration = declaration;
62 61
63 this.sourceName = sourceName != null && !sourceName.isEmpty() && 62 this.sourceName = sourceName != null && !sourceName.isEmpty() && !(entry instanceof MethodEntry && ((MethodEntry) entry).isConstructor() && CONSTRUCTOR_NON_NAMES.contains(sourceName));
64 !(entry instanceof MethodEntry && ((MethodEntry) entry).isConstructor() && CONSTRUCTOR_NON_NAMES.contains(sourceName));
65 } 63 }
66 64
67 public EntryReference(E entry, C context, EntryReference<E, C> other) { 65 public EntryReference(E entry, C context, EntryReference<E, C> other) {
@@ -76,6 +74,7 @@ public class EntryReference<E extends Entry<?>, C extends Entry<?>> implements T
76 if (context != null) { 74 if (context != null) {
77 return context.getContainingClass(); 75 return context.getContainingClass();
78 } 76 }
77
79 return entry.getContainingClass(); 78 return entry.getContainingClass();
80 } 79 }
81 80
@@ -95,6 +94,7 @@ public class EntryReference<E extends Entry<?>, C extends Entry<?>> implements T
95 // renaming a constructor really means renaming the class 94 // renaming a constructor really means renaming the class
96 return entry.getContainingClass(); 95 return entry.getContainingClass();
97 } 96 }
97
98 return entry; 98 return entry;
99 } 99 }
100 100
@@ -107,6 +107,7 @@ public class EntryReference<E extends Entry<?>, C extends Entry<?>> implements T
107 if (context != null) { 107 if (context != null) {
108 return Objects.hash(entry.hashCode(), context.hashCode()); 108 return Objects.hash(entry.hashCode(), context.hashCode());
109 } 109 }
110
110 return entry.hashCode() ^ Boolean.hashCode(this.declaration); 111 return entry.hashCode() ^ Boolean.hashCode(this.declaration);
111 } 112 }
112 113
@@ -116,10 +117,7 @@ public class EntryReference<E extends Entry<?>, C extends Entry<?>> implements T
116 } 117 }
117 118
118 public boolean equals(EntryReference<?, ?> other) { 119 public boolean equals(EntryReference<?, ?> other) {
119 return other != null 120 return other != null && Objects.equals(entry, other.entry) && Objects.equals(context, other.context) && declaration == other.declaration;
120 && Objects.equals(entry, other.entry)
121 && Objects.equals(context, other.context)
122 && declaration == other.declaration;
123 } 121 }
124 122
125 @Override 123 @Override
@@ -149,5 +147,4 @@ public class EntryReference<E extends Entry<?>, C extends Entry<?>> implements T
149 public TranslateResult<EntryReference<E, C>> extendedTranslate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) { 147 public TranslateResult<EntryReference<E, C>> extendedTranslate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
150 return translator.extendedTranslate(this.entry).map(e -> new EntryReference<>(e, translator.translate(context), this)); 148 return translator.extendedTranslate(this.entry).map(e -> new EntryReference<>(e, translator.translate(context), this));
151 } 149 }
152
153} 150}
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/enigma/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
index c93ac53..cc511f3 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
@@ -1,16 +1,18 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import javax.swing.tree.DefaultMutableTreeNode;
15
14import cuchaz.enigma.analysis.index.JarIndex; 16import cuchaz.enigma.analysis.index.JarIndex;
15import cuchaz.enigma.analysis.index.ReferenceIndex; 17import cuchaz.enigma.analysis.index.ReferenceIndex;
16import cuchaz.enigma.translation.Translator; 18import cuchaz.enigma.translation.Translator;
@@ -18,10 +20,7 @@ import cuchaz.enigma.translation.representation.entry.FieldEntry;
18import cuchaz.enigma.translation.representation.entry.MethodDefEntry; 20import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
19import cuchaz.enigma.translation.representation.entry.MethodEntry; 21import cuchaz.enigma.translation.representation.entry.MethodEntry;
20 22
21import javax.swing.tree.DefaultMutableTreeNode;
22
23public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, MethodDefEntry> { 23public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, MethodDefEntry> {
24
25 private final Translator translator; 24 private final Translator translator;
26 private FieldEntry entry; 25 private FieldEntry entry;
27 private EntryReference<FieldEntry, MethodDefEntry> reference; 26 private EntryReference<FieldEntry, MethodDefEntry> reference;
@@ -53,6 +52,7 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re
53 if (this.reference != null) { 52 if (this.reference != null) {
54 return String.format("%s", translator.translate(this.reference.context)); 53 return String.format("%s", translator.translate(this.reference.context));
55 } 54 }
55
56 return translator.translate(entry).toString(); 56 return translator.translate(entry).toString();
57 } 57 }
58 58
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/IndexSimpleVerifier.java b/enigma/src/main/java/cuchaz/enigma/analysis/IndexSimpleVerifier.java
index ec8f323..44a768e 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/IndexSimpleVerifier.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/IndexSimpleVerifier.java
@@ -1,154 +1,160 @@
1package cuchaz.enigma.analysis; 1package cuchaz.enigma.analysis;
2 2
3import java.util.Set;
4
5import org.objectweb.asm.Type;
6import org.objectweb.asm.tree.analysis.BasicValue;
7import org.objectweb.asm.tree.analysis.SimpleVerifier;
8
3import cuchaz.enigma.Enigma; 9import cuchaz.enigma.Enigma;
4import cuchaz.enigma.analysis.index.EntryIndex; 10import cuchaz.enigma.analysis.index.EntryIndex;
5import cuchaz.enigma.analysis.index.InheritanceIndex; 11import cuchaz.enigma.analysis.index.InheritanceIndex;
6import cuchaz.enigma.translation.representation.AccessFlags; 12import cuchaz.enigma.translation.representation.AccessFlags;
7import cuchaz.enigma.translation.representation.entry.ClassDefEntry; 13import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
8import cuchaz.enigma.translation.representation.entry.ClassEntry; 14import cuchaz.enigma.translation.representation.entry.ClassEntry;
9import org.objectweb.asm.Type;
10import org.objectweb.asm.tree.analysis.BasicValue;
11import org.objectweb.asm.tree.analysis.SimpleVerifier;
12
13import java.util.Set;
14 15
15public class IndexSimpleVerifier extends SimpleVerifier { 16public class IndexSimpleVerifier extends SimpleVerifier {
16 private static final Type OBJECT_TYPE = Type.getType("Ljava/lang/Object;"); 17 private static final Type OBJECT_TYPE = Type.getType("Ljava/lang/Object;");
17 private final EntryIndex entryIndex; 18 private final EntryIndex entryIndex;
18 private final InheritanceIndex inheritanceIndex; 19 private final InheritanceIndex inheritanceIndex;
19 20
20 public IndexSimpleVerifier(EntryIndex entryIndex, InheritanceIndex inheritanceIndex) { 21 public IndexSimpleVerifier(EntryIndex entryIndex, InheritanceIndex inheritanceIndex) {
21 super(Enigma.ASM_VERSION, null, null, null, false); 22 super(Enigma.ASM_VERSION, null, null, null, false);
22 this.entryIndex = entryIndex; 23 this.entryIndex = entryIndex;
23 this.inheritanceIndex = inheritanceIndex; 24 this.inheritanceIndex = inheritanceIndex;
24 } 25 }
25 26
26 @Override 27 @Override
27 protected boolean isSubTypeOf(BasicValue value, BasicValue expected) { 28 protected boolean isSubTypeOf(BasicValue value, BasicValue expected) {
28 Type expectedType = expected.getType(); 29 Type expectedType = expected.getType();
29 Type type = value.getType(); 30 Type type = value.getType();
30 switch (expectedType.getSort()) { 31 switch (expectedType.getSort()) {
31 case Type.INT: 32 case Type.INT:
32 case Type.FLOAT: 33 case Type.FLOAT:
33 case Type.LONG: 34 case Type.LONG:
34 case Type.DOUBLE: 35 case Type.DOUBLE:
35 return type.equals(expectedType); 36 return type.equals(expectedType);
36 case Type.ARRAY: 37 case Type.ARRAY:
37 case Type.OBJECT: 38 case Type.OBJECT:
38 if (type.equals(NULL_TYPE)) { 39 if (type.equals(NULL_TYPE)) {
39 return true; 40 return true;
40 } else if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { 41 } else if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
41 if (isAssignableFrom(expectedType, type)) { 42 if (isAssignableFrom(expectedType, type)) {
42 return true; 43 return true;
43 } else if (isInterface(expectedType)) { 44 } else if (isInterface(expectedType)) {
44 return isAssignableFrom(OBJECT_TYPE, type); 45 return isAssignableFrom(OBJECT_TYPE, type);
45 } else { 46 } else {
46 return false; 47 return false;
47 } 48 }
48 } else { 49 } else {
49 return false; 50 return false;
50 } 51 }
51 default: 52 default:
52 throw new AssertionError(); 53 throw new AssertionError();
53 } 54 }
54 } 55 }
55 56
56 @Override 57 @Override
57 protected boolean isInterface(Type type) { 58 protected boolean isInterface(Type type) {
58 AccessFlags classAccess = entryIndex.getClassAccess(new ClassEntry(type.getInternalName())); 59 AccessFlags classAccess = entryIndex.getClassAccess(new ClassEntry(type.getInternalName()));
59 if (classAccess != null) { 60
60 return classAccess.isInterface(); 61 if (classAccess != null) {
61 } 62 return classAccess.isInterface();
62 63 }
63 Class<?> clazz = getClass(type); 64
64 if (clazz != null) { 65 Class<?> clazz = getClass(type);
65 return clazz.isInterface(); 66
66 } 67 if (clazz != null) {
67 68 return clazz.isInterface();
68 return false; 69 }
69 } 70
70 71 return false;
71 @Override 72 }
72 protected Type getSuperClass(Type type) { 73
73 ClassDefEntry definition = entryIndex.getDefinition(new ClassEntry(type.getInternalName())); 74 @Override
74 if (definition != null) { 75 protected Type getSuperClass(Type type) {
75 return Type.getType('L' + definition.getSuperClass().getFullName() + ';'); 76 ClassDefEntry definition = entryIndex.getDefinition(new ClassEntry(type.getInternalName()));
76 } 77
77 78 if (definition != null) {
78 Class<?> clazz = getClass(type); 79 return Type.getType('L' + definition.getSuperClass().getFullName() + ';');
79 if (clazz != null) { 80 }
80 return Type.getType(clazz.getSuperclass()); 81
81 } 82 Class<?> clazz = getClass(type);
82 83
83 return OBJECT_TYPE; 84 if (clazz != null) {
84 } 85 return Type.getType(clazz.getSuperclass());
85 86 }
86 @Override 87
87 protected boolean isAssignableFrom(Type type1, Type type2) { 88 return OBJECT_TYPE;
88 if (type1.equals(type2)) { 89 }
89 return true; 90
90 } 91 @Override
91 92 protected boolean isAssignableFrom(Type type1, Type type2) {
92 if (type2.equals(NULL_TYPE)) { 93 if (type1.equals(type2)) {
93 return true; 94 return true;
94 } 95 }
95 96
96 if (type1.getSort() == Type.ARRAY) { 97 if (type2.equals(NULL_TYPE)) {
97 return type2.getSort() == Type.ARRAY && isAssignableFrom(Type.getType(type1.getDescriptor().substring(1)), Type.getType(type2.getDescriptor().substring(1))); 98 return true;
98 } 99 }
99 100
100 if (type2.getSort() == Type.ARRAY) { 101 if (type1.getSort() == Type.ARRAY) {
101 return type1.equals(OBJECT_TYPE); 102 return type2.getSort() == Type.ARRAY && isAssignableFrom(Type.getType(type1.getDescriptor().substring(1)), Type.getType(type2.getDescriptor().substring(1)));
102 } 103 }
103 104
104 if (type1.getSort() == Type.OBJECT && type2.getSort() == Type.OBJECT) { 105 if (type2.getSort() == Type.ARRAY) {
105 if (type1.equals(OBJECT_TYPE)) { 106 return type1.equals(OBJECT_TYPE);
106 return true; 107 }
107 } 108
108 109 if (type1.getSort() == Type.OBJECT && type2.getSort() == Type.OBJECT) {
109 ClassEntry class1 = new ClassEntry(type1.getInternalName()); 110 if (type1.equals(OBJECT_TYPE)) {
110 ClassEntry class2 = new ClassEntry(type2.getInternalName()); 111 return true;
111 112 }
112 if (entryIndex.hasClass(class1) && entryIndex.hasClass(class2)) { 113
113 return inheritanceIndex.getAncestors(class2).contains(class1); 114 ClassEntry class1 = new ClassEntry(type1.getInternalName());
114 } 115 ClassEntry class2 = new ClassEntry(type2.getInternalName());
115 116
116 Class<?> class1Class = getClass(Type.getType('L' + class1.getFullName() + ';')); 117 if (entryIndex.hasClass(class1) && entryIndex.hasClass(class2)) {
117 Class<?> class2Class = getClass(Type.getType('L' + class2.getFullName() + ';')); 118 return inheritanceIndex.getAncestors(class2).contains(class1);
118 119 }
119 if (class1Class == null) { 120
120 return true; // missing classes to find out 121 Class<?> class1Class = getClass(Type.getType('L' + class1.getFullName() + ';'));
121 } 122 Class<?> class2Class = getClass(Type.getType('L' + class2.getFullName() + ';'));
122 123
123 if (class2Class != null) { 124 if (class1Class == null) {
124 return class1Class.isAssignableFrom(class2Class); 125 return true; // missing classes to find out
125 } 126 }
126 127
127 if (entryIndex.hasClass(class2)) { 128 if (class2Class != null) {
128 Set<ClassEntry> ancestors = inheritanceIndex.getAncestors(class2); 129 return class1Class.isAssignableFrom(class2Class);
129 130 }
130 for (ClassEntry ancestorEntry : ancestors) { 131
131 Class<?> ancestor = getClass(Type.getType('L' + ancestorEntry.getFullName() + ';')); 132 if (entryIndex.hasClass(class2)) {
132 if (ancestor == null || class1Class.isAssignableFrom(ancestor)) { 133 Set<ClassEntry> ancestors = inheritanceIndex.getAncestors(class2);
133 return true; // assignable, or missing classes to find out 134
134 } 135 for (ClassEntry ancestorEntry : ancestors) {
135 } 136 Class<?> ancestor = getClass(Type.getType('L' + ancestorEntry.getFullName() + ';'));
136 137
137 return false; 138 if (ancestor == null || class1Class.isAssignableFrom(ancestor)) {
138 } 139 return true; // assignable, or missing classes to find out
139 140 }
140 return true; // missing classes to find out 141 }
141 } 142
142 143 return false;
143 return false; 144 }
144 } 145
145 146 return true; // missing classes to find out
146 @Override 147 }
147 protected final Class<?> getClass(Type type) { 148
148 try { 149 return false;
149 return Class.forName(type.getSort() == Type.ARRAY ? type.getDescriptor().replace('/', '.') : type.getClassName(), false, null); 150 }
150 } catch (ClassNotFoundException e) { 151
151 return null; 152 @Override
152 } 153 protected final Class<?> getClass(Type type) {
153 } 154 try {
155 return Class.forName(type.getSort() == Type.ARRAY ? type.getDescriptor().replace('/', '.') : type.getClassName(), false, null);
156 } catch (ClassNotFoundException e) {
157 return null;
158 }
159 }
154} 160}
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java b/enigma/src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java
index 0c2dfd7..3043577 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java
@@ -1,6 +1,10 @@
1package cuchaz.enigma.analysis; 1package cuchaz.enigma.analysis;
2 2
3import java.util.Collection;
4import java.util.List;
5
3import com.google.common.collect.Lists; 6import com.google.common.collect.Lists;
7
4import cuchaz.enigma.analysis.index.JarIndex; 8import cuchaz.enigma.analysis.index.JarIndex;
5import cuchaz.enigma.translation.Translator; 9import cuchaz.enigma.translation.Translator;
6import cuchaz.enigma.translation.mapping.EntryResolver; 10import cuchaz.enigma.translation.mapping.EntryResolver;
@@ -8,9 +12,6 @@ import cuchaz.enigma.translation.mapping.ResolutionStrategy;
8import cuchaz.enigma.translation.representation.entry.ClassEntry; 12import cuchaz.enigma.translation.representation.entry.ClassEntry;
9import cuchaz.enigma.translation.representation.entry.MethodEntry; 13import cuchaz.enigma.translation.representation.entry.MethodEntry;
10 14
11import java.util.Collection;
12import java.util.List;
13
14public class IndexTreeBuilder { 15public class IndexTreeBuilder {
15 private final JarIndex index; 16 private final JarIndex index;
16 17
@@ -22,6 +23,7 @@ public class IndexTreeBuilder {
22 // get the root node 23 // get the root node
23 List<String> ancestry = Lists.newArrayList(); 24 List<String> ancestry = Lists.newArrayList();
24 ancestry.add(obfClassEntry.getFullName()); 25 ancestry.add(obfClassEntry.getFullName());
26
25 for (ClassEntry classEntry : index.getInheritanceIndex().getAncestors(obfClassEntry)) { 27 for (ClassEntry classEntry : index.getInheritanceIndex().getAncestors(obfClassEntry)) {
26 ancestry.add(classEntry.getFullName()); 28 ancestry.add(classEntry.getFullName());
27 } 29 }
@@ -40,6 +42,7 @@ public class IndexTreeBuilder {
40 node.load(index); 42 node.load(index);
41 return node; 43 return node;
42 } 44 }
45
43 return null; 46 return null;
44 } 47 }
45 48
@@ -47,10 +50,7 @@ public class IndexTreeBuilder {
47 MethodEntry resolvedEntry = index.getEntryResolver().resolveFirstEntry(obfMethodEntry, ResolutionStrategy.RESOLVE_ROOT); 50 MethodEntry resolvedEntry = index.getEntryResolver().resolveFirstEntry(obfMethodEntry, ResolutionStrategy.RESOLVE_ROOT);
48 51
49 // make a root node at the base 52 // make a root node at the base
50 MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( 53 MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode(translator, resolvedEntry, index.getEntryIndex().hasMethod(resolvedEntry));
51 translator, resolvedEntry,
52 index.getEntryIndex().hasMethod(resolvedEntry)
53 );
54 54
55 // expand the full tree 55 // expand the full tree
56 rootNode.load(index); 56 rootNode.load(index);
@@ -63,6 +63,7 @@ public class IndexTreeBuilder {
63 Collection<MethodEntry> resolvedEntries = resolver.resolveEntry(obfMethodEntry, ResolutionStrategy.RESOLVE_ROOT); 63 Collection<MethodEntry> resolvedEntries = resolver.resolveEntry(obfMethodEntry, ResolutionStrategy.RESOLVE_ROOT);
64 64
65 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList(); 65 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList();
66
66 for (MethodEntry resolvedEntry : resolvedEntries) { 67 for (MethodEntry resolvedEntry : resolvedEntries) {
67 MethodImplementationsTreeNode node = new MethodImplementationsTreeNode(translator, resolvedEntry); 68 MethodImplementationsTreeNode node = new MethodImplementationsTreeNode(translator, resolvedEntry);
68 node.load(index); 69 node.load(index);
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/InterpreterPair.java b/enigma/src/main/java/cuchaz/enigma/analysis/InterpreterPair.java
index a624b7c..2ca1dfd 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/InterpreterPair.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/InterpreterPair.java
@@ -1,129 +1,106 @@
1package cuchaz.enigma.analysis; 1package cuchaz.enigma.analysis;
2 2
3import cuchaz.enigma.Enigma; 3import java.util.List;
4import java.util.Objects;
5
4import org.objectweb.asm.Type; 6import org.objectweb.asm.Type;
5import org.objectweb.asm.tree.AbstractInsnNode; 7import org.objectweb.asm.tree.AbstractInsnNode;
6import org.objectweb.asm.tree.analysis.AnalyzerException; 8import org.objectweb.asm.tree.analysis.AnalyzerException;
7import org.objectweb.asm.tree.analysis.Interpreter; 9import org.objectweb.asm.tree.analysis.Interpreter;
8import org.objectweb.asm.tree.analysis.Value; 10import org.objectweb.asm.tree.analysis.Value;
9 11
10import java.util.List; 12import cuchaz.enigma.Enigma;
11import java.util.Objects;
12 13
13public class InterpreterPair<V extends Value, W extends Value> extends Interpreter<InterpreterPair.PairValue<V, W>> { 14public class InterpreterPair<V extends Value, W extends Value> extends Interpreter<InterpreterPair.PairValue<V, W>> {
14 private final Interpreter<V> left; 15 private final Interpreter<V> left;
15 private final Interpreter<W> right; 16 private final Interpreter<W> right;
16 17
17 public InterpreterPair(Interpreter<V> left, Interpreter<W> right) { 18 public InterpreterPair(Interpreter<V> left, Interpreter<W> right) {
18 super(Enigma.ASM_VERSION); 19 super(Enigma.ASM_VERSION);
19 this.left = left; 20 this.left = left;
20 this.right = right; 21 this.right = right;
21 } 22 }
22 23
23 @Override 24 @Override
24 public PairValue<V, W> newValue(Type type) { 25 public PairValue<V, W> newValue(Type type) {
25 return pair( 26 return pair(left.newValue(type), right.newValue(type));
26 left.newValue(type), 27 }
27 right.newValue(type) 28
28 ); 29 @Override
29 } 30 public PairValue<V, W> newOperation(AbstractInsnNode insn) throws AnalyzerException {
30 31 return pair(left.newOperation(insn), right.newOperation(insn));
31 @Override 32 }
32 public PairValue<V, W> newOperation(AbstractInsnNode insn) throws AnalyzerException { 33
33 return pair( 34 @Override
34 left.newOperation(insn), 35 public PairValue<V, W> copyOperation(AbstractInsnNode insn, PairValue<V, W> value) throws AnalyzerException {
35 right.newOperation(insn) 36 return pair(left.copyOperation(insn, value.left), right.copyOperation(insn, value.right));
36 ); 37 }
37 } 38
38 39 @Override
39 @Override 40 public PairValue<V, W> unaryOperation(AbstractInsnNode insn, PairValue<V, W> value) throws AnalyzerException {
40 public PairValue<V, W> copyOperation(AbstractInsnNode insn, PairValue<V, W> value) throws AnalyzerException { 41 return pair(left.unaryOperation(insn, value.left), right.unaryOperation(insn, value.right));
41 return pair( 42 }
42 left.copyOperation(insn, value.left), 43
43 right.copyOperation(insn, value.right) 44 @Override
44 ); 45 public PairValue<V, W> binaryOperation(AbstractInsnNode insn, PairValue<V, W> value1, PairValue<V, W> value2) throws AnalyzerException {
45 } 46 return pair(left.binaryOperation(insn, value1.left, value2.left), right.binaryOperation(insn, value1.right, value2.right));
46 47 }
47 @Override 48
48 public PairValue<V, W> unaryOperation(AbstractInsnNode insn, PairValue<V, W> value) throws AnalyzerException { 49 @Override
49 return pair( 50 public PairValue<V, W> ternaryOperation(AbstractInsnNode insn, PairValue<V, W> value1, PairValue<V, W> value2, PairValue<V, W> value3) throws AnalyzerException {
50 left.unaryOperation(insn, value.left), 51 return pair(left.ternaryOperation(insn, value1.left, value2.left, value3.left), right.ternaryOperation(insn, value1.right, value2.right, value3.right));
51 right.unaryOperation(insn, value.right) 52 }
52 ); 53
53 } 54 @Override
54 55 public PairValue<V, W> naryOperation(AbstractInsnNode insn, List<? extends PairValue<V, W>> values) throws AnalyzerException {
55 @Override 56 return pair(left.naryOperation(insn, values.stream().map(v -> v.left).toList()), right.naryOperation(insn, values.stream().map(v -> v.right).toList()));
56 public PairValue<V, W> binaryOperation(AbstractInsnNode insn, PairValue<V, W> value1, PairValue<V, W> value2) throws AnalyzerException { 57 }
57 return pair( 58
58 left.binaryOperation(insn, value1.left, value2.left), 59 @Override
59 right.binaryOperation(insn, value1.right, value2.right) 60 public void returnOperation(AbstractInsnNode insn, PairValue<V, W> value, PairValue<V, W> expected) throws AnalyzerException {
60 ); 61 left.returnOperation(insn, value.left, expected.left);
61 } 62 right.returnOperation(insn, value.right, expected.right);
62 63 }
63 @Override 64
64 public PairValue<V, W> ternaryOperation(AbstractInsnNode insn, PairValue<V, W> value1, PairValue<V, W> value2, PairValue<V, W> value3) throws AnalyzerException { 65 @Override
65 return pair( 66 public PairValue<V, W> merge(PairValue<V, W> value1, PairValue<V, W> value2) {
66 left.ternaryOperation(insn, value1.left, value2.left, value3.left), 67 return pair(left.merge(value1.left, value2.left), right.merge(value1.right, value2.right));
67 right.ternaryOperation(insn, value1.right, value2.right, value3.right) 68 }
68 ); 69
69 } 70 private PairValue<V, W> pair(V left, W right) {
70 71 if (left == null && right == null) {
71 @Override 72 return null;
72 public PairValue<V, W> naryOperation(AbstractInsnNode insn, List<? extends PairValue<V, W>> values) throws AnalyzerException { 73 }
73 return pair( 74
74 left.naryOperation(insn, values.stream().map(v -> v.left).toList()), 75 return new PairValue<>(left, right);
75 right.naryOperation(insn, values.stream().map(v -> v.right).toList()) 76 }
76 ); 77
77 } 78 public static final class PairValue<V extends Value, W extends Value> implements Value {
78 79 public final V left;
79 @Override 80 public final W right;
80 public void returnOperation(AbstractInsnNode insn, PairValue<V, W> value, PairValue<V, W> expected) throws AnalyzerException { 81
81 left.returnOperation(insn, value.left, expected.left); 82 public PairValue(V left, W right) {
82 right.returnOperation(insn, value.right, expected.right); 83 if (left == null && right == null) {
83 } 84 throw new IllegalArgumentException("should use null rather than pair of nulls");
84 85 }
85 @Override 86
86 public PairValue<V, W> merge(PairValue<V, W> value1, PairValue<V, W> value2) { 87 this.left = left;
87 return pair( 88 this.right = right;
88 left.merge(value1.left, value2.left), 89 }
89 right.merge(value1.right, value2.right) 90
90 ); 91 @Override
91 } 92 public boolean equals(Object o) {
92 93 return o instanceof InterpreterPair.PairValue pairValue && Objects.equals(left, pairValue.left) && Objects.equals(right, pairValue.right);
93 private PairValue<V, W> pair(V left, W right) { 94 }
94 if (left == null && right == null) { 95
95 return null; 96 @Override
96 } 97 public int hashCode() {
97 98 return left.hashCode() * 31 + right.hashCode();
98 return new PairValue<>(left, right); 99 }
99 } 100
100 101 @Override
101 public static final class PairValue<V extends Value, W extends Value> implements Value { 102 public int getSize() {
102 public final V left; 103 return (left == null ? right : left).getSize();
103 public final W right; 104 }
104 105 }
105 public PairValue(V left, W right) {
106 if (left == null && right == null) {
107 throw new IllegalArgumentException("should use null rather than pair of nulls");
108 }
109
110 this.left = left;
111 this.right = right;
112 }
113
114 @Override
115 public boolean equals(Object o) {
116 return o instanceof InterpreterPair.PairValue pairValue && Objects.equals(left, pairValue.left) && Objects.equals(right, pairValue.right);
117 }
118
119 @Override
120 public int hashCode() {
121 return left.hashCode() * 31 + right.hashCode();
122 }
123
124 @Override
125 public int getSize() {
126 return (left == null ? right : left).getSize();
127 }
128 }
129} 106}
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/enigma/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
index 4633ace..83275da 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
@@ -1,17 +1,23 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import java.util.Collection;
15import java.util.List;
16
17import javax.swing.tree.DefaultMutableTreeNode;
18
14import com.google.common.collect.Lists; 19import com.google.common.collect.Lists;
20
15import cuchaz.enigma.analysis.index.EntryIndex; 21import cuchaz.enigma.analysis.index.EntryIndex;
16import cuchaz.enigma.analysis.index.InheritanceIndex; 22import cuchaz.enigma.analysis.index.InheritanceIndex;
17import cuchaz.enigma.analysis.index.JarIndex; 23import cuchaz.enigma.analysis.index.JarIndex;
@@ -19,17 +25,13 @@ import cuchaz.enigma.translation.Translator;
19import cuchaz.enigma.translation.representation.entry.ClassEntry; 25import cuchaz.enigma.translation.representation.entry.ClassEntry;
20import cuchaz.enigma.translation.representation.entry.MethodEntry; 26import cuchaz.enigma.translation.representation.entry.MethodEntry;
21 27
22import javax.swing.tree.DefaultMutableTreeNode;
23import java.util.Collection;
24import java.util.List;
25
26public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { 28public class MethodImplementationsTreeNode extends DefaultMutableTreeNode {
27
28 private final Translator translator; 29 private final Translator translator;
29 private MethodEntry entry; 30 private MethodEntry entry;
30 31
31 public MethodImplementationsTreeNode(Translator translator, MethodEntry entry) { 32 public MethodImplementationsTreeNode(Translator translator, MethodEntry entry) {
32 this.translator = translator; 33 this.translator = translator;
34
33 if (entry == null) { 35 if (entry == null) {
34 throw new IllegalArgumentException("Entry cannot be null!"); 36 throw new IllegalArgumentException("Entry cannot be null!");
35 } 37 }
@@ -46,10 +48,12 @@ public class MethodImplementationsTreeNode extends DefaultMutableTreeNode {
46 // recurse 48 // recurse
47 for (int i = 0; i < node.getChildCount(); i++) { 49 for (int i = 0; i < node.getChildCount(); i++) {
48 MethodImplementationsTreeNode foundNode = findNode((MethodImplementationsTreeNode) node.getChildAt(i), entry); 50 MethodImplementationsTreeNode foundNode = findNode((MethodImplementationsTreeNode) node.getChildAt(i), entry);
51
49 if (foundNode != null) { 52 if (foundNode != null) {
50 return foundNode; 53 return foundNode;
51 } 54 }
52 } 55 }
56
53 return null; 57 return null;
54 } 58 }
55 59
@@ -70,8 +74,10 @@ public class MethodImplementationsTreeNode extends DefaultMutableTreeNode {
70 InheritanceIndex inheritanceIndex = index.getInheritanceIndex(); 74 InheritanceIndex inheritanceIndex = index.getInheritanceIndex();
71 75
72 Collection<ClassEntry> descendants = inheritanceIndex.getDescendants(entry.getParent()); 76 Collection<ClassEntry> descendants = inheritanceIndex.getDescendants(entry.getParent());
77
73 for (ClassEntry inheritor : descendants) { 78 for (ClassEntry inheritor : descendants) {
74 MethodEntry methodEntry = entry.withParent(inheritor); 79 MethodEntry methodEntry = entry.withParent(inheritor);
80
75 if (entryIndex.hasMethod(methodEntry)) { 81 if (entryIndex.hasMethod(methodEntry)) {
76 nodes.add(new MethodImplementationsTreeNode(translator, methodEntry)); 82 nodes.add(new MethodImplementationsTreeNode(translator, methodEntry));
77 } 83 }
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/enigma/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
index 455456f..2afeed9 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
@@ -1,16 +1,18 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import javax.swing.tree.DefaultMutableTreeNode;
15
14import cuchaz.enigma.analysis.index.EntryIndex; 16import cuchaz.enigma.analysis.index.EntryIndex;
15import cuchaz.enigma.analysis.index.InheritanceIndex; 17import cuchaz.enigma.analysis.index.InheritanceIndex;
16import cuchaz.enigma.analysis.index.JarIndex; 18import cuchaz.enigma.analysis.index.JarIndex;
@@ -18,10 +20,7 @@ import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.translation.representation.entry.ClassEntry; 20import cuchaz.enigma.translation.representation.entry.ClassEntry;
19import cuchaz.enigma.translation.representation.entry.MethodEntry; 21import cuchaz.enigma.translation.representation.entry.MethodEntry;
20 22
21import javax.swing.tree.DefaultMutableTreeNode;
22
23public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { 23public class MethodInheritanceTreeNode extends DefaultMutableTreeNode {
24
25 private final Translator translator; 24 private final Translator translator;
26 private MethodEntry entry; 25 private MethodEntry entry;
27 private boolean implemented; 26 private boolean implemented;
@@ -41,10 +40,12 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode {
41 // recurse 40 // recurse
42 for (int i = 0; i < node.getChildCount(); i++) { 41 for (int i = 0; i < node.getChildCount(); i++) {
43 MethodInheritanceTreeNode foundNode = findNode((MethodInheritanceTreeNode) node.getChildAt(i), entry); 42 MethodInheritanceTreeNode foundNode = findNode((MethodInheritanceTreeNode) node.getChildAt(i), entry);
43
44 if (foundNode != null) { 44 if (foundNode != null) {
45 return foundNode; 45 return foundNode;
46 } 46 }
47 } 47 }
48
48 return null; 49 return null;
49 } 50 }
50 51
@@ -79,6 +80,7 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode {
79 InheritanceIndex inheritanceIndex = index.getInheritanceIndex(); 80 InheritanceIndex inheritanceIndex = index.getInheritanceIndex();
80 81
81 boolean ret = false; 82 boolean ret = false;
83
82 for (ClassEntry inheritorEntry : inheritanceIndex.getChildren(this.entry.getParent())) { 84 for (ClassEntry inheritorEntry : inheritanceIndex.getChildren(this.entry.getParent())) {
83 MethodEntry methodEntry = new MethodEntry(inheritorEntry, this.entry.getName(), this.entry.getDesc()); 85 MethodEntry methodEntry = new MethodEntry(inheritorEntry, this.entry.getName(), this.entry.getDesc());
84 86
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/MethodNodeWithAction.java b/enigma/src/main/java/cuchaz/enigma/analysis/MethodNodeWithAction.java
index 8117103..8dc7fe6 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/MethodNodeWithAction.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/MethodNodeWithAction.java
@@ -1,19 +1,19 @@
1package cuchaz.enigma.analysis; 1package cuchaz.enigma.analysis;
2 2
3import org.objectweb.asm.tree.MethodNode;
4
5import java.util.function.Consumer; 3import java.util.function.Consumer;
6 4
5import org.objectweb.asm.tree.MethodNode;
6
7public class MethodNodeWithAction extends MethodNode { 7public class MethodNodeWithAction extends MethodNode {
8 private final Consumer<MethodNode> action; 8 private final Consumer<MethodNode> action;
9 9
10 public MethodNodeWithAction(int api, int access, String name, String descriptor, String signature, String[] exceptions, Consumer<MethodNode> action) { 10 public MethodNodeWithAction(int api, int access, String name, String descriptor, String signature, String[] exceptions, Consumer<MethodNode> action) {
11 super(api, access, name, descriptor, signature, exceptions); 11 super(api, access, name, descriptor, signature, exceptions);
12 this.action = action; 12 this.action = action;
13 } 13 }
14 14
15 @Override 15 @Override
16 public void visitEnd() { 16 public void visitEnd() {
17 action.accept(this); 17 action.accept(this);
18 } 18 }
19} 19}
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java b/enigma/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java
index 6803861..fc58c6d 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java
@@ -1,17 +1,25 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import java.util.ArrayList;
15import java.util.Collection;
16import java.util.Set;
17
18import javax.swing.tree.DefaultMutableTreeNode;
19import javax.swing.tree.TreeNode;
20
14import com.google.common.collect.Sets; 21import com.google.common.collect.Sets;
22
15import cuchaz.enigma.analysis.index.JarIndex; 23import cuchaz.enigma.analysis.index.JarIndex;
16import cuchaz.enigma.analysis.index.ReferenceIndex; 24import cuchaz.enigma.analysis.index.ReferenceIndex;
17import cuchaz.enigma.translation.Translator; 25import cuchaz.enigma.translation.Translator;
@@ -20,14 +28,7 @@ import cuchaz.enigma.translation.representation.entry.Entry;
20import cuchaz.enigma.translation.representation.entry.MethodDefEntry; 28import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
21import cuchaz.enigma.translation.representation.entry.MethodEntry; 29import cuchaz.enigma.translation.representation.entry.MethodEntry;
22 30
23import javax.swing.tree.DefaultMutableTreeNode;
24import javax.swing.tree.TreeNode;
25import java.util.ArrayList;
26import java.util.Collection;
27import java.util.Set;
28
29public class MethodReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<MethodEntry, MethodDefEntry> { 31public class MethodReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<MethodEntry, MethodDefEntry> {
30
31 private final Translator translator; 32 private final Translator translator;
32 private MethodEntry entry; 33 private MethodEntry entry;
33 private EntryReference<MethodEntry, MethodDefEntry> reference; 34 private EntryReference<MethodEntry, MethodDefEntry> reference;
@@ -59,6 +60,7 @@ public class MethodReferenceTreeNode extends DefaultMutableTreeNode implements R
59 if (this.reference != null) { 60 if (this.reference != null) {
60 return String.format("%s", translator.translate(this.reference.context)); 61 return String.format("%s", translator.translate(this.reference.context));
61 } 62 }
63
62 return translator.translate(this.entry).getName(); 64 return translator.translate(this.entry).getName();
63 } 65 }
64 66
@@ -73,16 +75,18 @@ public class MethodReferenceTreeNode extends DefaultMutableTreeNode implements R
73 if (recurse && this.children != null) { 75 if (recurse && this.children != null) {
74 for (Object child : this.children) { 76 for (Object child : this.children) {
75 if (child instanceof MethodReferenceTreeNode node) { 77 if (child instanceof MethodReferenceTreeNode node) {
76
77 // don't recurse into ancestor 78 // don't recurse into ancestor
78 Set<Entry<?>> ancestors = Sets.newHashSet(); 79 Set<Entry<?>> ancestors = Sets.newHashSet();
79 TreeNode n = node; 80 TreeNode n = node;
81
80 while (n.getParent() != null) { 82 while (n.getParent() != null) {
81 n = n.getParent(); 83 n = n.getParent();
84
82 if (n instanceof MethodReferenceTreeNode) { 85 if (n instanceof MethodReferenceTreeNode) {
83 ancestors.add(((MethodReferenceTreeNode) n).getEntry()); 86 ancestors.add(((MethodReferenceTreeNode) n).getEntry());
84 } 87 }
85 } 88 }
89
86 if (ancestors.contains(node.getEntry())) { 90 if (ancestors.contains(node.getEntry())) {
87 continue; 91 continue;
88 } 92 }
@@ -100,6 +104,7 @@ public class MethodReferenceTreeNode extends DefaultMutableTreeNode implements R
100 Collection<EntryReference<MethodEntry, MethodDefEntry>> references = new ArrayList<>(); 104 Collection<EntryReference<MethodEntry, MethodDefEntry>> references = new ArrayList<>();
101 105
102 EntryResolver entryResolver = index.getEntryResolver(); 106 EntryResolver entryResolver = index.getEntryResolver();
107
103 for (MethodEntry methodEntry : entryResolver.resolveEquivalentMethods(entry)) { 108 for (MethodEntry methodEntry : entryResolver.resolveEquivalentMethods(entry)) {
104 references.addAll(referenceIndex.getReferencesToMethod(methodEntry)); 109 references.addAll(referenceIndex.getReferencesToMethod(methodEntry));
105 } 110 }
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/ReferenceTargetType.java b/enigma/src/main/java/cuchaz/enigma/analysis/ReferenceTargetType.java
index 5b19d18..4dcb834 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/ReferenceTargetType.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/ReferenceTargetType.java
@@ -3,72 +3,72 @@ package cuchaz.enigma.analysis;
3import cuchaz.enigma.translation.representation.entry.ClassEntry; 3import cuchaz.enigma.translation.representation.entry.ClassEntry;
4 4
5public abstract class ReferenceTargetType { 5public abstract class ReferenceTargetType {
6 private static final None NONE = new None(); 6 private static final None NONE = new None();
7 private static final Uninitialized UNINITIALIZED = new Uninitialized(); 7 private static final Uninitialized UNINITIALIZED = new Uninitialized();
8 8
9 public abstract Kind getKind(); 9 public abstract Kind getKind();
10 10
11 public static None none() { 11 public static None none() {
12 return NONE; 12 return NONE;
13 } 13 }
14 14
15 public static Uninitialized uninitialized() { 15 public static Uninitialized uninitialized() {
16 return UNINITIALIZED; 16 return UNINITIALIZED;
17 } 17 }
18 18
19 public static ClassType classType(ClassEntry name) { 19 public static ClassType classType(ClassEntry name) {
20 return new ClassType(name); 20 return new ClassType(name);
21 } 21 }
22 22
23 public enum Kind { 23 public enum Kind {
24 NONE, 24 NONE,
25 UNINITIALIZED, 25 UNINITIALIZED,
26 CLASS_TYPE 26 CLASS_TYPE
27 } 27 }
28 28
29 public static class None extends ReferenceTargetType { 29 public static class None extends ReferenceTargetType {
30 @Override 30 @Override
31 public Kind getKind() { 31 public Kind getKind() {
32 return Kind.NONE; 32 return Kind.NONE;
33 } 33 }
34 34
35 @Override 35 @Override
36 public String toString() { 36 public String toString() {
37 return "(none)"; 37 return "(none)";
38 } 38 }
39 } 39 }
40 40
41 public static class Uninitialized extends ReferenceTargetType { 41 public static class Uninitialized extends ReferenceTargetType {
42 @Override 42 @Override
43 public Kind getKind() { 43 public Kind getKind() {
44 return Kind.UNINITIALIZED; 44 return Kind.UNINITIALIZED;
45 } 45 }
46 46
47 @Override 47 @Override
48 public String toString() { 48 public String toString() {
49 return "(uninitialized)"; 49 return "(uninitialized)";
50 } 50 }
51 } 51 }
52 52
53 public static class ClassType extends ReferenceTargetType { 53 public static class ClassType extends ReferenceTargetType {
54 private final ClassEntry entry; 54 private final ClassEntry entry;
55 55
56 private ClassType(ClassEntry entry) { 56 private ClassType(ClassEntry entry) {
57 this.entry = entry; 57 this.entry = entry;
58 } 58 }
59 59
60 public ClassEntry getEntry() { 60 public ClassEntry getEntry() {
61 return entry; 61 return entry;
62 } 62 }
63 63
64 @Override 64 @Override
65 public Kind getKind() { 65 public Kind getKind() {
66 return Kind.CLASS_TYPE; 66 return Kind.CLASS_TYPE;
67 } 67 }
68 68
69 @Override 69 @Override
70 public String toString() { 70 public String toString() {
71 return entry.toString(); 71 return entry.toString();
72 } 72 }
73 } 73 }
74} 74}
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java b/enigma/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java
index ce23cb6..8e0afd5 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeNode.java b/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeNode.java
index aea7618..b3ba896 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeNode.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeNode.java
@@ -1,180 +1,196 @@
1package cuchaz.enigma.analysis; 1package cuchaz.enigma.analysis;
2 2
3import java.util.ArrayList;
4import java.util.Comparator;
5import java.util.List;
6import java.util.stream.Stream;
7
8import javax.swing.tree.DefaultMutableTreeNode;
9
3import cuchaz.enigma.EnigmaProject; 10import cuchaz.enigma.EnigmaProject;
4import cuchaz.enigma.api.service.NameProposalService; 11import cuchaz.enigma.api.service.NameProposalService;
5import cuchaz.enigma.translation.TranslateResult; 12import cuchaz.enigma.translation.TranslateResult;
6import cuchaz.enigma.translation.mapping.EntryRemapper; 13import cuchaz.enigma.translation.mapping.EntryRemapper;
7import cuchaz.enigma.translation.representation.AccessFlags; 14import cuchaz.enigma.translation.representation.AccessFlags;
8import cuchaz.enigma.translation.representation.TypeDescriptor; 15import cuchaz.enigma.translation.representation.TypeDescriptor;
9import cuchaz.enigma.translation.representation.entry.*; 16import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
10 17import cuchaz.enigma.translation.representation.entry.ClassEntry;
11import javax.swing.tree.DefaultMutableTreeNode; 18import cuchaz.enigma.translation.representation.entry.DefEntry;
12import java.util.ArrayList; 19import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
13import java.util.Comparator; 20import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
14import java.util.List; 21import cuchaz.enigma.translation.representation.entry.MethodEntry;
15import java.util.stream.Stream; 22import cuchaz.enigma.translation.representation.entry.ParentedEntry;
16 23
17public class StructureTreeNode extends DefaultMutableTreeNode { 24public class StructureTreeNode extends DefaultMutableTreeNode {
18 private final List<NameProposalService> nameProposalServices; 25 private final List<NameProposalService> nameProposalServices;
19 private final EntryRemapper mapper; 26 private final EntryRemapper mapper;
20 private final ClassEntry parentEntry; 27 private final ClassEntry parentEntry;
21 private final ParentedEntry entry; 28 private final ParentedEntry entry;
22 29
23 public StructureTreeNode(EnigmaProject project, ClassEntry parentEntry, ParentedEntry entry) { 30 public StructureTreeNode(EnigmaProject project, ClassEntry parentEntry, ParentedEntry entry) {
24 this.nameProposalServices = project.getEnigma().getServices().get(NameProposalService.TYPE); 31 this.nameProposalServices = project.getEnigma().getServices().get(NameProposalService.TYPE);
25 this.mapper = project.getMapper(); 32 this.mapper = project.getMapper();
26 this.parentEntry = parentEntry; 33 this.parentEntry = parentEntry;
27 this.entry = entry; 34 this.entry = entry;
28 } 35 }
29 36
30 /** 37 /**
31 * Returns the parented entry represented by this tree node. 38 * Returns the parented entry represented by this tree node.
32 */ 39 */
33 public ParentedEntry getEntry() { 40 public ParentedEntry getEntry() {
34 return this.entry; 41 return this.entry;
35 } 42 }
36 43
37 public void load(EnigmaProject project, StructureTreeOptions options) { 44 public void load(EnigmaProject project, StructureTreeOptions options) {
38 Stream<ParentedEntry> children = project.getJarIndex().getChildrenByClass().get(this.parentEntry).stream(); 45 Stream<ParentedEntry> children = project.getJarIndex().getChildrenByClass().get(this.parentEntry).stream();
39 46
40 children = switch (options.obfuscationVisibility()) { 47 children = switch (options.obfuscationVisibility()) {
41 case ALL -> children; 48 case ALL -> children;
42 case OBFUSCATED -> children 49 case OBFUSCATED -> children
43 // remove deobfuscated members if only obfuscated, unless it's an inner class 50 // remove deobfuscated members if only obfuscated, unless it's an inner class
44 .filter(e -> (e instanceof ClassEntry) || (project.isObfuscated(e) && project.isRenamable(e))) 51 .filter(e -> (e instanceof ClassEntry) || (project.isObfuscated(e) && project.isRenamable(e)))
45 // keep constructor methods if the class is obfuscated 52 // keep constructor methods if the class is obfuscated
46 .filter(e -> !(e instanceof MethodEntry m && m.isConstructor()) || project.isObfuscated(e.getParent())); 53 .filter(e -> !(e instanceof MethodEntry m && m.isConstructor()) || project.isObfuscated(e.getParent()));
47 case DEOBFUSCATED -> children.filter(e -> (e instanceof ClassEntry) 54 case DEOBFUSCATED -> children.filter(e -> (e instanceof ClassEntry) || (!project.isObfuscated(e) && project.isRenamable(e))
48 || (!project.isObfuscated(e) && project.isRenamable(e)) 55 // keep constructor methods if the class is deobfuscated
49 // keep constructor methods if the class is deobfuscated 56 || (e instanceof MethodEntry m && m.isConstructor()) && !project.isObfuscated(e.getParent()));
50 || (e instanceof MethodEntry m && m.isConstructor()) && !project.isObfuscated(e.getParent())); 57 };
51 }; 58
52 59 children = switch (options.documentationVisibility()) {
53 children = switch (options.documentationVisibility()) { 60 case ALL -> children;
54 case ALL -> children; 61 // TODO remove EntryRemapper.deobfuscate() calls when javadocs will no longer be tied to deobfuscation
55 // TODO remove EntryRemapper.deobfuscate() calls when javadocs will no longer be tied to deobfuscation 62 case DOCUMENTED -> children.filter(e -> (e instanceof ClassEntry) || (project.getMapper().deobfuscate(e).getJavadocs() != null && !project.getMapper().deobfuscate(e).getJavadocs().isBlank()));
56 case DOCUMENTED -> children.filter(e -> (e instanceof ClassEntry) || (project.getMapper().deobfuscate(e).getJavadocs() != null && !project.getMapper().deobfuscate(e).getJavadocs().isBlank())); 63 case NON_DOCUMENTED -> children.filter(e -> (e instanceof ClassEntry) || (project.getMapper().deobfuscate(e).getJavadocs() == null || project.getMapper().deobfuscate(e).getJavadocs().isBlank()));
57 case NON_DOCUMENTED -> children.filter(e -> (e instanceof ClassEntry) || (project.getMapper().deobfuscate(e).getJavadocs() == null || project.getMapper().deobfuscate(e).getJavadocs().isBlank())); 64 };
58 }; 65
59 66 children = switch (options.sortingOrder()) {
60 children = switch (options.sortingOrder()) { 67 case DEFAULT -> children;
61 case DEFAULT -> children; 68 case A_Z -> children.sorted(Comparator.comparing(e -> (e instanceof MethodEntry m && m.isConstructor())
62 case A_Z -> children.sorted(Comparator.comparing(e -> (e instanceof MethodEntry m && m.isConstructor()) 69 // compare the class name when the entry is a constructor
63 // compare the class name when the entry is a constructor 70 ? project.getMapper().deobfuscate(e.getParent()).getSimpleName().toLowerCase() : project.getMapper().deobfuscate(e).getSimpleName().toLowerCase()));
64 ? project.getMapper().deobfuscate(e.getParent()).getSimpleName().toLowerCase() 71 case Z_A -> children.sorted(
65 : project.getMapper().deobfuscate(e).getSimpleName().toLowerCase())); 72 Comparator.comparing(e -> (e instanceof MethodEntry m && m.isConstructor()) ? project.getMapper().deobfuscate(((ParentedEntry<?>) e).getParent()).getSimpleName().toLowerCase() : project.getMapper().deobfuscate((ParentedEntry<?>) e).getSimpleName().toLowerCase()).reversed());
66 case Z_A -> children.sorted(Comparator.comparing(e -> (e instanceof MethodEntry m && m.isConstructor()) 73 };
67 ? project.getMapper().deobfuscate(((ParentedEntry<?>) e).getParent()).getSimpleName().toLowerCase() 74
68 : project.getMapper().deobfuscate((ParentedEntry<?>) e).getSimpleName().toLowerCase()) 75 for (ParentedEntry<?> child : children.toList()) {
69 .reversed()); 76 StructureTreeNode childNode = new StructureTreeNode(project, this.parentEntry, child);
70 }; 77
71 78 if (child instanceof ClassEntry) {
72 for (ParentedEntry<?> child : children.toList()) { 79 childNode = new StructureTreeNode(project, (ClassEntry) child, child);
73 StructureTreeNode childNode = new StructureTreeNode(project, this.parentEntry, child); 80 childNode.load(project, options);
74 81 }
75 if (child instanceof ClassEntry) { 82
76 childNode = new StructureTreeNode(project, (ClassEntry) child, child); 83 this.add(childNode);
77 childNode.load(project, options); 84 }
78 } 85 }
79 86
80 this.add(childNode); 87 @Override
81 } 88 public String toString() {
82 } 89 TranslateResult<ParentedEntry> translateResult = this.mapper.extendedDeobfuscate(this.entry);
83 90 String result = translateResult.getValue().getName();
84 @Override 91
85 public String toString() { 92 if (translateResult.isObfuscated()) {
86 TranslateResult<ParentedEntry> translateResult = this.mapper.extendedDeobfuscate(this.entry); 93 if (!this.nameProposalServices.isEmpty()) {
87 String result = translateResult.getValue().getName(); 94 for (NameProposalService service : this.nameProposalServices) {
88 95 if (service.proposeName(this.entry, this.mapper).isPresent()) {
89 if (translateResult.isObfuscated()) { 96 result = service.proposeName(this.entry, this.mapper).get();
90 if (!this.nameProposalServices.isEmpty()) { 97 }
91 for (NameProposalService service : this.nameProposalServices) { 98 }
92 if (service.proposeName(this.entry, this.mapper).isPresent()) { 99 }
93 result = service.proposeName(this.entry, this.mapper).get(); 100 }
94 } 101
95 } 102 if (this.entry instanceof FieldDefEntry) {
96 } 103 FieldDefEntry field = (FieldDefEntry) translateResult.getValue();
97 } 104 String returnType = this.parseDesc(field.getDesc());
98 105
99 if (this.entry instanceof FieldDefEntry) { 106 result = result + ": " + returnType;
100 FieldDefEntry field = (FieldDefEntry) translateResult.getValue(); 107 } else if (this.entry instanceof MethodDefEntry) {
101 String returnType = this.parseDesc(field.getDesc()); 108 MethodDefEntry method = (MethodDefEntry) translateResult.getValue();
102 109 String args = this.parseArgs(method.getDesc().getArgumentDescs());
103 result = result + ": " + returnType; 110 String returnType = this.parseDesc(method.getDesc().getReturnDesc());
104 } else if (this.entry instanceof MethodDefEntry) { 111
105 MethodDefEntry method = (MethodDefEntry) translateResult.getValue(); 112 if (method.isConstructor()) {
106 String args = this.parseArgs(method.getDesc().getArgumentDescs()); 113 result = method.getParent().getSimpleName() + args;
107 String returnType = this.parseDesc(method.getDesc().getReturnDesc()); 114 } else {
108 115 result = result + args + ": " + returnType;
109 if (method.isConstructor()) { 116 }
110 result = method.getParent().getSimpleName() + args; 117 }
111 } else { 118
112 result = result + args + ": " + returnType; 119 return result;
113 } 120 }
114 } 121
115 122 public String toHtml() {
116 return result; 123 List<String> modifiers = new ArrayList<>();
117 } 124
118 125 if (this.entry instanceof DefEntry<?> defEntry) {
119 public String toHtml() { 126 AccessFlags access = defEntry.getAccess();
120 List<String> modifiers = new ArrayList<>(); 127 boolean isInterfaceMethod = false;
121 128
122 if (this.entry instanceof DefEntry<?> defEntry) { 129 if (this.entry instanceof MethodEntry && this.entry.getParent() instanceof ClassDefEntry parent) {
123 AccessFlags access = defEntry.getAccess(); 130 isInterfaceMethod = parent.getAccess().isInterface();
124 boolean isInterfaceMethod = false; 131 }
125 132
126 if (this.entry instanceof MethodEntry && this.entry.getParent() instanceof ClassDefEntry parent) { 133 if (access.isStatic() && !access.isEnum()) {
127 isInterfaceMethod = parent.getAccess().isInterface(); 134 // Static member, but not an enum constant
128 } 135 modifiers.add("static");
129 136 } else if (isInterfaceMethod && !access.isAbstract()) {
130 if (access.isStatic() && !access.isEnum()) { 137 // Non-static default interface method
131 // Static member, but not an enum constant 138 modifiers.add("default");
132 modifiers.add("static"); 139 }
133 } else if (isInterfaceMethod && !access.isAbstract()) { 140
134 // Non-static default interface method 141 if (access.isAbstract() && !access.isInterface() && !isInterfaceMethod && !access.isEnum()) {
135 modifiers.add("default"); 142 // Abstract, but not an interface, an interface method or an enum class (abstract is the default or meaningless)
136 } 143 modifiers.add("abstract");
137 144 } else if (access.isFinal() && !access.isEnum()) {
138 if (access.isAbstract() && !access.isInterface() && !isInterfaceMethod && !access.isEnum()) { 145 // Final, but not an enum or an enum constant (they're always final)
139 // Abstract, but not an interface, an interface method or an enum class (abstract is the default or meaningless) 146 modifiers.add("final");
140 modifiers.add("abstract"); 147 }
141 } else if (access.isFinal() && !access.isEnum()) { 148 }
142 // Final, but not an enum or an enum constant (they're always final) 149
143 modifiers.add("final"); 150 return "<i>" + String.join(" ", modifiers) + "</i> " + toString();
144 } 151 }
145 } 152
146 153 private String parseArgs(List<TypeDescriptor> args) {
147 return "<i>" + String.join(" ", modifiers) + "</i> " + toString(); 154 if (args.size() > 0) {
148 } 155 String result = "(";
149 156
150 private String parseArgs(List<TypeDescriptor> args) { 157 for (int i = 0; i < args.size(); i++) {
151 if (args.size() > 0) { 158 if (i > 0) {
152 String result = "("; 159 result += ", ";
153 160 }
154 for (int i = 0; i < args.size(); i++) { 161
155 if (i > 0) { 162 result += this.parseDesc(args.get(i));
156 result += ", "; 163 }
157 } 164
158 165 return result + ")";
159 result += this.parseDesc(args.get(i)); 166 }
160 } 167
161 168 return "()";
162 return result + ")"; 169 }
163 } 170
164 171 private String parseDesc(TypeDescriptor desc) {
165 return "()"; 172 if (desc.isVoid()) {
166 } 173 return "void";
167 174 }
168 private String parseDesc(TypeDescriptor desc) { 175
169 if (desc.isVoid()) return "void"; 176 if (desc.isPrimitive()) {
170 if (desc.isPrimitive()) return desc.getPrimitive().getKeyword(); 177 return desc.getPrimitive().getKeyword();
171 if (desc.isType()) return desc.getTypeEntry().getSimpleName(); 178 }
172 179
173 if (desc.isArray()) { 180 if (desc.isType()) {
174 if (desc.getArrayType().isPrimitive()) return desc.getArrayType().getPrimitive().getKeyword() + "[]"; 181 return desc.getTypeEntry().getSimpleName();
175 if (desc.getArrayType().isType()) return desc.getArrayType().getTypeEntry().getSimpleName() + "[]"; 182 }
176 } 183
177 184 if (desc.isArray()) {
178 return null; 185 if (desc.getArrayType().isPrimitive()) {
179 } 186 return desc.getArrayType().getPrimitive().getKeyword() + "[]";
187 }
188
189 if (desc.getArrayType().isType()) {
190 return desc.getArrayType().getTypeEntry().getSimpleName() + "[]";
191 }
192 }
193
194 return null;
195 }
180} 196}
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeOptions.java b/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeOptions.java
index cfc80b4..e2e5084 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeOptions.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeOptions.java
@@ -1,59 +1,55 @@
1package cuchaz.enigma.analysis; 1package cuchaz.enigma.analysis;
2 2
3public record StructureTreeOptions( 3public record StructureTreeOptions(ObfuscationVisibility obfuscationVisibility, DocumentationVisibility documentationVisibility, SortingOrder sortingOrder) {
4 ObfuscationVisibility obfuscationVisibility, 4 public enum ObfuscationVisibility implements Option {
5 DocumentationVisibility documentationVisibility, 5 ALL("structure.options.obfuscation.all"),
6 SortingOrder sortingOrder) { 6 OBFUSCATED("structure.options.obfuscation.obfuscated"),
7 7 DEOBFUSCATED("structure.options.obfuscation.deobfuscated");
8 public enum ObfuscationVisibility implements Option { 8
9 ALL("structure.options.obfuscation.all"), 9 private final String translationKey;
10 OBFUSCATED("structure.options.obfuscation.obfuscated"), 10
11 DEOBFUSCATED("structure.options.obfuscation.deobfuscated"); 11 ObfuscationVisibility(String translationKey) {
12 12 this.translationKey = translationKey;
13 private final String translationKey; 13 }
14 14
15 ObfuscationVisibility(String translationKey) { 15 public String getTranslationKey() {
16 this.translationKey = translationKey; 16 return this.translationKey;
17 } 17 }
18 18 }
19 public String getTranslationKey() { 19
20 return this.translationKey; 20 public enum DocumentationVisibility implements Option {
21 } 21 ALL("structure.options.documentation.all"),
22 } 22 DOCUMENTED("structure.options.documentation.documented"),
23 23 NON_DOCUMENTED("structure.options.documentation.non_documented");
24 public enum DocumentationVisibility implements Option { 24
25 ALL("structure.options.documentation.all"), 25 private final String translationKey;
26 DOCUMENTED("structure.options.documentation.documented"), 26
27 NON_DOCUMENTED("structure.options.documentation.non_documented"); 27 DocumentationVisibility(String translationKey) {
28 28 this.translationKey = translationKey;
29 private final String translationKey; 29 }
30 30
31 DocumentationVisibility(String translationKey) { 31 public String getTranslationKey() {
32 this.translationKey = translationKey; 32 return this.translationKey;
33 } 33 }
34 34 }
35 public String getTranslationKey() { 35
36 return this.translationKey; 36 public enum SortingOrder implements Option {
37 } 37 DEFAULT("structure.options.sorting.default"),
38 } 38 A_Z("structure.options.sorting.a_z"),
39 39 Z_A("structure.options.sorting.z_a");
40 public enum SortingOrder implements Option { 40
41 DEFAULT("structure.options.sorting.default"), 41 private final String translationKey;
42 A_Z("structure.options.sorting.a_z"), 42
43 Z_A("structure.options.sorting.z_a"); 43 SortingOrder(String translationKey) {
44 44 this.translationKey = translationKey;
45 private final String translationKey; 45 }
46 46
47 SortingOrder(String translationKey) { 47 public String getTranslationKey() {
48 this.translationKey = translationKey; 48 return this.translationKey;
49 } 49 }
50 50 }
51 public String getTranslationKey() { 51
52 return this.translationKey; 52 public interface Option {
53 } 53 String getTranslationKey();
54 } 54 }
55
56 public interface Option {
57 String getTranslationKey();
58 }
59} 55}
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java b/enigma/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java
index a4b1aac..26093c3 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java
@@ -1,6 +1,15 @@
1package cuchaz.enigma.analysis.index; 1package cuchaz.enigma.analysis.index;
2 2
3import java.util.Collection;
4import java.util.Collections;
5import java.util.HashMap;
6import java.util.List;
7import java.util.Map;
8
9import javax.annotation.Nullable;
10
3import com.google.common.collect.Maps; 11import com.google.common.collect.Maps;
12
4import cuchaz.enigma.translation.representation.AccessFlags; 13import cuchaz.enigma.translation.representation.AccessFlags;
5import cuchaz.enigma.translation.representation.MethodDescriptor; 14import cuchaz.enigma.translation.representation.MethodDescriptor;
6import cuchaz.enigma.translation.representation.TypeDescriptor; 15import cuchaz.enigma.translation.representation.TypeDescriptor;
@@ -8,9 +17,6 @@ import cuchaz.enigma.translation.representation.entry.ClassEntry;
8import cuchaz.enigma.translation.representation.entry.MethodDefEntry; 17import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
9import cuchaz.enigma.translation.representation.entry.MethodEntry; 18import cuchaz.enigma.translation.representation.entry.MethodEntry;
10 19
11import javax.annotation.Nullable;
12import java.util.*;
13
14public class BridgeMethodIndex implements JarIndexer { 20public class BridgeMethodIndex implements JarIndexer {
15 private final EntryIndex entryIndex; 21 private final EntryIndex entryIndex;
16 private final InheritanceIndex inheritanceIndex; 22 private final InheritanceIndex inheritanceIndex;
@@ -31,6 +37,7 @@ public class BridgeMethodIndex implements JarIndexer {
31 MethodDefEntry methodDefEntry = (MethodDefEntry) methodEntry; 37 MethodDefEntry methodDefEntry = (MethodDefEntry) methodEntry;
32 38
33 AccessFlags access = methodDefEntry.getAccess(); 39 AccessFlags access = methodDefEntry.getAccess();
40
34 if (access == null || !access.isSynthetic()) { 41 if (access == null || !access.isSynthetic()) {
35 continue; 42 continue;
36 } 43 }
@@ -46,6 +53,7 @@ public class BridgeMethodIndex implements JarIndexer {
46 for (Map.Entry<MethodEntry, MethodEntry> entry : copiedAccessToBridge.entrySet()) { 53 for (Map.Entry<MethodEntry, MethodEntry> entry : copiedAccessToBridge.entrySet()) {
47 MethodEntry specializedEntry = entry.getKey(); 54 MethodEntry specializedEntry = entry.getKey();
48 MethodEntry bridgeEntry = entry.getValue(); 55 MethodEntry bridgeEntry = entry.getValue();
56
49 if (bridgeEntry.getName().equals(specializedEntry.getName())) { 57 if (bridgeEntry.getName().equals(specializedEntry.getName())) {
50 continue; 58 continue;
51 } 59 }
@@ -57,6 +65,7 @@ public class BridgeMethodIndex implements JarIndexer {
57 65
58 private void indexSyntheticMethod(MethodDefEntry syntheticMethod, AccessFlags access) { 66 private void indexSyntheticMethod(MethodDefEntry syntheticMethod, AccessFlags access) {
59 MethodEntry specializedMethod = findSpecializedMethod(syntheticMethod); 67 MethodEntry specializedMethod = findSpecializedMethod(syntheticMethod);
68
60 if (specializedMethod == null) { 69 if (specializedMethod == null) {
61 return; 70 return;
62 } 71 }
@@ -84,6 +93,7 @@ public class BridgeMethodIndex implements JarIndexer {
84 private boolean isPotentialBridge(MethodDefEntry bridgeMethod, MethodEntry specializedMethod) { 93 private boolean isPotentialBridge(MethodDefEntry bridgeMethod, MethodEntry specializedMethod) {
85 // Bridge methods only exist for inheritance purposes, if we're private, final, or static, we cannot be inherited 94 // Bridge methods only exist for inheritance purposes, if we're private, final, or static, we cannot be inherited
86 AccessFlags bridgeAccess = bridgeMethod.getAccess(); 95 AccessFlags bridgeAccess = bridgeMethod.getAccess();
96
87 if (bridgeAccess.isPrivate() || bridgeAccess.isFinal() || bridgeAccess.isStatic()) { 97 if (bridgeAccess.isPrivate() || bridgeAccess.isFinal() || bridgeAccess.isStatic()) {
88 return false; 98 return false;
89 } 99 }
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java b/enigma/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java
index bb992b7..0e4cdcf 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java
@@ -1,13 +1,21 @@
1package cuchaz.enigma.analysis.index; 1package cuchaz.enigma.analysis.index;
2 2
3import cuchaz.enigma.translation.representation.AccessFlags;
4import cuchaz.enigma.translation.representation.entry.*;
5
6import javax.annotation.Nullable;
7import java.util.Collection; 3import java.util.Collection;
8import java.util.HashMap; 4import java.util.HashMap;
9import java.util.Map; 5import java.util.Map;
10 6
7import javax.annotation.Nullable;
8
9import cuchaz.enigma.translation.representation.AccessFlags;
10import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
11import cuchaz.enigma.translation.representation.entry.ClassEntry;
12import cuchaz.enigma.translation.representation.entry.Entry;
13import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
14import cuchaz.enigma.translation.representation.entry.FieldEntry;
15import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
16import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
17import cuchaz.enigma.translation.representation.entry.MethodEntry;
18
11public class EntryIndex implements JarIndexer { 19public class EntryIndex implements JarIndexer {
12 private Map<ClassEntry, AccessFlags> classes = new HashMap<>(); 20 private Map<ClassEntry, AccessFlags> classes = new HashMap<>();
13 private Map<FieldEntry, AccessFlags> fields = new HashMap<>(); 21 private Map<FieldEntry, AccessFlags> fields = new HashMap<>();
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java b/enigma/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java
index f9cb23c..e697182 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java
@@ -1,12 +1,13 @@
1package cuchaz.enigma.analysis.index; 1package cuchaz.enigma.analysis.index;
2 2
3import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
4import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
5import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
6import org.objectweb.asm.ClassVisitor; 3import org.objectweb.asm.ClassVisitor;
7import org.objectweb.asm.FieldVisitor; 4import org.objectweb.asm.FieldVisitor;
8import org.objectweb.asm.MethodVisitor; 5import org.objectweb.asm.MethodVisitor;
9 6
7import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
8import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
9import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
10
10public class IndexClassVisitor extends ClassVisitor { 11public class IndexClassVisitor extends ClassVisitor {
11 private final JarIndexer indexer; 12 private final JarIndexer indexer;
12 private ClassDefEntry classEntry; 13 private ClassDefEntry classEntry;
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java b/enigma/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java
index efea83d..97fec47 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java
@@ -1,5 +1,22 @@
1package cuchaz.enigma.analysis.index; 1package cuchaz.enigma.analysis.index;
2 2
3import java.util.List;
4
5import org.objectweb.asm.ClassVisitor;
6import org.objectweb.asm.Handle;
7import org.objectweb.asm.MethodVisitor;
8import org.objectweb.asm.Opcodes;
9import org.objectweb.asm.Type;
10import org.objectweb.asm.tree.AbstractInsnNode;
11import org.objectweb.asm.tree.FieldInsnNode;
12import org.objectweb.asm.tree.InvokeDynamicInsnNode;
13import org.objectweb.asm.tree.MethodInsnNode;
14import org.objectweb.asm.tree.analysis.Analyzer;
15import org.objectweb.asm.tree.analysis.AnalyzerException;
16import org.objectweb.asm.tree.analysis.BasicValue;
17import org.objectweb.asm.tree.analysis.SourceInterpreter;
18import org.objectweb.asm.tree.analysis.SourceValue;
19
3import cuchaz.enigma.analysis.IndexSimpleVerifier; 20import cuchaz.enigma.analysis.IndexSimpleVerifier;
4import cuchaz.enigma.analysis.InterpreterPair; 21import cuchaz.enigma.analysis.InterpreterPair;
5import cuchaz.enigma.analysis.MethodNodeWithAction; 22import cuchaz.enigma.analysis.MethodNodeWithAction;
@@ -8,15 +25,11 @@ import cuchaz.enigma.translation.representation.AccessFlags;
8import cuchaz.enigma.translation.representation.Lambda; 25import cuchaz.enigma.translation.representation.Lambda;
9import cuchaz.enigma.translation.representation.MethodDescriptor; 26import cuchaz.enigma.translation.representation.MethodDescriptor;
10import cuchaz.enigma.translation.representation.Signature; 27import cuchaz.enigma.translation.representation.Signature;
11import cuchaz.enigma.translation.representation.entry.*; 28import cuchaz.enigma.translation.representation.entry.ClassEntry;
12import org.objectweb.asm.*; 29import cuchaz.enigma.translation.representation.entry.FieldEntry;
13import org.objectweb.asm.tree.AbstractInsnNode; 30import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
14import org.objectweb.asm.tree.FieldInsnNode; 31import cuchaz.enigma.translation.representation.entry.MethodEntry;
15import org.objectweb.asm.tree.InvokeDynamicInsnNode; 32import cuchaz.enigma.translation.representation.entry.ParentedEntry;
16import org.objectweb.asm.tree.MethodInsnNode;
17import org.objectweb.asm.tree.analysis.*;
18
19import java.util.List;
20 33
21public class IndexReferenceVisitor extends ClassVisitor { 34public class IndexReferenceVisitor extends ClassVisitor {
22 private final JarIndexer indexer; 35 private final JarIndexer indexer;
@@ -54,7 +67,7 @@ public class IndexReferenceVisitor extends ClassVisitor {
54 private final MethodDefEntry callerEntry; 67 private final MethodDefEntry callerEntry;
55 private JarIndexer indexer; 68 private JarIndexer indexer;
56 69
57 public MethodInterpreter(MethodDefEntry callerEntry, JarIndexer indexer, EntryIndex entryIndex, InheritanceIndex inheritanceIndex) { 70 MethodInterpreter(MethodDefEntry callerEntry, JarIndexer indexer, EntryIndex entryIndex, InheritanceIndex inheritanceIndex) {
58 super(new IndexSimpleVerifier(entryIndex, inheritanceIndex), new SourceInterpreter()); 71 super(new IndexSimpleVerifier(entryIndex, inheritanceIndex), new SourceInterpreter());
59 this.callerEntry = callerEntry; 72 this.callerEntry = callerEntry;
60 this.indexer = indexer; 73 this.indexer = indexer;
@@ -85,7 +98,6 @@ public class IndexReferenceVisitor extends ClassVisitor {
85 return super.unaryOperation(insn, value); 98 return super.unaryOperation(insn, value);
86 } 99 }
87 100
88
89 @Override 101 @Override
90 public PairValue<BasicValue, SourceValue> binaryOperation(AbstractInsnNode insn, PairValue<BasicValue, SourceValue> value1, PairValue<BasicValue, SourceValue> value2) throws AnalyzerException { 102 public PairValue<BasicValue, SourceValue> binaryOperation(AbstractInsnNode insn, PairValue<BasicValue, SourceValue> value1, PairValue<BasicValue, SourceValue> value2) throws AnalyzerException {
91 if (insn.getOpcode() == Opcodes.PUTFIELD) { 103 if (insn.getOpcode() == Opcodes.PUTFIELD) {
@@ -119,6 +131,7 @@ public class IndexReferenceVisitor extends ClassVisitor {
119 Type instantiatedMethodType = (Type) invokeDynamicInsn.bsmArgs[2]; 131 Type instantiatedMethodType = (Type) invokeDynamicInsn.bsmArgs[2];
120 132
121 ReferenceTargetType targetType; 133 ReferenceTargetType targetType;
134
122 if (implMethod.getTag() != Opcodes.H_GETSTATIC && implMethod.getTag() != Opcodes.H_PUTFIELD && implMethod.getTag() != Opcodes.H_INVOKESTATIC) { 135 if (implMethod.getTag() != Opcodes.H_GETSTATIC && implMethod.getTag() != Opcodes.H_PUTFIELD && implMethod.getTag() != Opcodes.H_INVOKESTATIC) {
123 if (instantiatedMethodType.getArgumentTypes().length < Type.getArgumentTypes(implMethod.getDesc()).length) { 136 if (instantiatedMethodType.getArgumentTypes().length < Type.getArgumentTypes(implMethod.getDesc()).length) {
124 targetType = getReferenceTargetType(values.get(0), insn); 137 targetType = getReferenceTargetType(values.get(0), insn);
@@ -129,13 +142,7 @@ public class IndexReferenceVisitor extends ClassVisitor {
129 targetType = ReferenceTargetType.none(); 142 targetType = ReferenceTargetType.none();
130 } 143 }
131 144
132 indexer.indexLambda(callerEntry, new Lambda( 145 indexer.indexLambda(callerEntry, new Lambda(invokeDynamicInsn.name, new MethodDescriptor(invokeDynamicInsn.desc), new MethodDescriptor(samMethodType.getDescriptor()), getHandleEntry(implMethod), new MethodDescriptor(instantiatedMethodType.getDescriptor())), targetType);
133 invokeDynamicInsn.name,
134 new MethodDescriptor(invokeDynamicInsn.desc),
135 new MethodDescriptor(samMethodType.getDescriptor()),
136 getHandleEntry(implMethod),
137 new MethodDescriptor(instantiatedMethodType.getDescriptor())
138 ), targetType);
139 } 146 }
140 } 147 }
141 148
@@ -160,17 +167,17 @@ public class IndexReferenceVisitor extends ClassVisitor {
160 167
161 private static ParentedEntry<?> getHandleEntry(Handle handle) { 168 private static ParentedEntry<?> getHandleEntry(Handle handle) {
162 switch (handle.getTag()) { 169 switch (handle.getTag()) {
163 case Opcodes.H_GETFIELD: 170 case Opcodes.H_GETFIELD:
164 case Opcodes.H_GETSTATIC: 171 case Opcodes.H_GETSTATIC:
165 case Opcodes.H_PUTFIELD: 172 case Opcodes.H_PUTFIELD:
166 case Opcodes.H_PUTSTATIC: 173 case Opcodes.H_PUTSTATIC:
167 return FieldEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc()); 174 return FieldEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc());
168 case Opcodes.H_INVOKEINTERFACE: 175 case Opcodes.H_INVOKEINTERFACE:
169 case Opcodes.H_INVOKESPECIAL: 176 case Opcodes.H_INVOKESPECIAL:
170 case Opcodes.H_INVOKESTATIC: 177 case Opcodes.H_INVOKESTATIC:
171 case Opcodes.H_INVOKEVIRTUAL: 178 case Opcodes.H_INVOKEVIRTUAL:
172 case Opcodes.H_NEWINVOKESPECIAL: 179 case Opcodes.H_NEWINVOKESPECIAL:
173 return MethodEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc()); 180 return MethodEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc());
174 } 181 }
175 182
176 throw new RuntimeException("Invalid handle tag " + handle.getTag()); 183 throw new RuntimeException("Invalid handle tag " + handle.getTag());
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java b/enigma/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java
index 1ab2abd..1c60db9 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java
@@ -1,27 +1,28 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.analysis.index; 12package cuchaz.enigma.analysis.index;
13 13
14import java.util.Collection;
15import java.util.HashSet;
16import java.util.LinkedList;
17import java.util.Set;
18
14import com.google.common.collect.HashMultimap; 19import com.google.common.collect.HashMultimap;
15import com.google.common.collect.Multimap; 20import com.google.common.collect.Multimap;
16import com.google.common.collect.Sets; 21import com.google.common.collect.Sets;
22
17import cuchaz.enigma.translation.representation.entry.ClassDefEntry; 23import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
18import cuchaz.enigma.translation.representation.entry.ClassEntry; 24import cuchaz.enigma.translation.representation.entry.ClassEntry;
19 25
20import java.util.Collection;
21import java.util.HashSet;
22import java.util.LinkedList;
23import java.util.Set;
24
25public class InheritanceIndex implements JarIndexer { 26public class InheritanceIndex implements JarIndexer {
26 private final EntryIndex entryIndex; 27 private final EntryIndex entryIndex;
27 28
@@ -39,6 +40,7 @@ public class InheritanceIndex implements JarIndexer {
39 } 40 }
40 41
41 ClassEntry superClass = classEntry.getSuperClass(); 42 ClassEntry superClass = classEntry.getSuperClass();
43
42 if (superClass != null && !superClass.getName().equals("java/lang/Object")) { 44 if (superClass != null && !superClass.getName().equals("java/lang/Object")) {
43 indexParent(classEntry, superClass); 45 indexParent(classEntry, superClass);
44 } 46 }
@@ -96,8 +98,13 @@ public class InheritanceIndex implements JarIndexer {
96 } 98 }
97 99
98 public Relation computeClassRelation(ClassEntry classEntry, ClassEntry potentialAncestor) { 100 public Relation computeClassRelation(ClassEntry classEntry, ClassEntry potentialAncestor) {
99 if (potentialAncestor.getName().equals("java/lang/Object")) return Relation.RELATED; 101 if (potentialAncestor.getName().equals("java/lang/Object")) {
100 if (!entryIndex.hasClass(classEntry)) return Relation.UNKNOWN; 102 return Relation.RELATED;
103 }
104
105 if (!entryIndex.hasClass(classEntry)) {
106 return Relation.UNKNOWN;
107 }
101 108
102 for (ClassEntry ancestor : getAncestors(classEntry)) { 109 for (ClassEntry ancestor : getAncestors(classEntry)) {
103 if (potentialAncestor.equals(ancestor)) { 110 if (potentialAncestor.equals(ancestor)) {
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java b/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java
index 6c26282..60864ba 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java
@@ -1,17 +1,26 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.analysis.index; 12package cuchaz.enigma.analysis.index;
13 13
14import com.google.common.collect.*; 14import java.util.Collection;
15import java.util.HashSet;
16import java.util.List;
17import java.util.Set;
18
19import com.google.common.collect.ArrayListMultimap;
20import com.google.common.collect.HashMultimap;
21import com.google.common.collect.ListMultimap;
22import com.google.common.collect.Multimap;
23
15import cuchaz.enigma.Enigma; 24import cuchaz.enigma.Enigma;
16import cuchaz.enigma.ProgressListener; 25import cuchaz.enigma.ProgressListener;
17import cuchaz.enigma.analysis.ReferenceTargetType; 26import cuchaz.enigma.analysis.ReferenceTargetType;
@@ -19,11 +28,15 @@ import cuchaz.enigma.classprovider.ClassProvider;
19import cuchaz.enigma.translation.mapping.EntryResolver; 28import cuchaz.enigma.translation.mapping.EntryResolver;
20import cuchaz.enigma.translation.mapping.IndexEntryResolver; 29import cuchaz.enigma.translation.mapping.IndexEntryResolver;
21import cuchaz.enigma.translation.representation.Lambda; 30import cuchaz.enigma.translation.representation.Lambda;
22import cuchaz.enigma.translation.representation.entry.*; 31import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
32import cuchaz.enigma.translation.representation.entry.ClassEntry;
33import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
34import cuchaz.enigma.translation.representation.entry.FieldEntry;
35import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
36import cuchaz.enigma.translation.representation.entry.MethodEntry;
37import cuchaz.enigma.translation.representation.entry.ParentedEntry;
23import cuchaz.enigma.utils.I18n; 38import cuchaz.enigma.utils.I18n;
24 39
25import java.util.*;
26
27public class JarIndex implements JarIndexer { 40public class JarIndex implements JarIndexer {
28 private final Set<String> indexedClasses = new HashSet<>(); 41 private final Set<String> indexedClasses = new HashSet<>();
29 private final EntryIndex entryIndex; 42 private final EntryIndex entryIndex;
@@ -99,6 +112,7 @@ public class JarIndex implements JarIndexer {
99 } 112 }
100 113
101 indexers.forEach(indexer -> indexer.indexClass(classEntry)); 114 indexers.forEach(indexer -> indexer.indexClass(classEntry));
115
102 if (classEntry.isInnerClass() && !classEntry.getAccess().isSynthetic()) { 116 if (classEntry.isInnerClass() && !classEntry.getAccess().isSynthetic()) {
103 childrenByClass.put(classEntry.getParent(), classEntry); 117 childrenByClass.put(classEntry.getParent(), classEntry);
104 } 118 }
@@ -111,6 +125,7 @@ public class JarIndex implements JarIndexer {
111 } 125 }
112 126
113 indexers.forEach(indexer -> indexer.indexField(fieldEntry)); 127 indexers.forEach(indexer -> indexer.indexField(fieldEntry));
128
114 if (!fieldEntry.getAccess().isSynthetic()) { 129 if (!fieldEntry.getAccess().isSynthetic()) {
115 childrenByClass.put(fieldEntry.getParent(), fieldEntry); 130 childrenByClass.put(fieldEntry.getParent(), fieldEntry);
116 } 131 }
@@ -123,6 +138,7 @@ public class JarIndex implements JarIndexer {
123 } 138 }
124 139
125 indexers.forEach(indexer -> indexer.indexMethod(methodEntry)); 140 indexers.forEach(indexer -> indexer.indexMethod(methodEntry));
141
126 if (!methodEntry.getAccess().isSynthetic() && !methodEntry.getName().equals("<clinit>")) { 142 if (!methodEntry.getAccess().isSynthetic() && !methodEntry.getName().equals("<clinit>")) {
127 childrenByClass.put(methodEntry.getParent(), methodEntry); 143 childrenByClass.put(methodEntry.getParent(), methodEntry);
128 } 144 }
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java b/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java
index f17e7c9..8726fb5 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java
@@ -2,7 +2,11 @@ package cuchaz.enigma.analysis.index;
2 2
3import cuchaz.enigma.analysis.ReferenceTargetType; 3import cuchaz.enigma.analysis.ReferenceTargetType;
4import cuchaz.enigma.translation.representation.Lambda; 4import cuchaz.enigma.translation.representation.Lambda;
5import cuchaz.enigma.translation.representation.entry.*; 5import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
6import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
7import cuchaz.enigma.translation.representation.entry.FieldEntry;
8import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
9import cuchaz.enigma.translation.representation.entry.MethodEntry;
6 10
7public interface JarIndexer { 11public interface JarIndexer {
8 default void indexClass(ClassDefEntry classEntry) { 12 default void indexClass(ClassDefEntry classEntry) {
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/index/PackageVisibilityIndex.java b/enigma/src/main/java/cuchaz/enigma/analysis/index/PackageVisibilityIndex.java
index 64de5f3..b400a66 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/index/PackageVisibilityIndex.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/index/PackageVisibilityIndex.java
@@ -1,15 +1,25 @@
1package cuchaz.enigma.analysis.index; 1package cuchaz.enigma.analysis.index;
2 2
3import java.util.Collection;
4import java.util.HashSet;
5import java.util.Iterator;
6import java.util.List;
7import java.util.Map;
8import java.util.Set;
9
3import com.google.common.collect.HashMultimap; 10import com.google.common.collect.HashMultimap;
4import com.google.common.collect.Lists; 11import com.google.common.collect.Lists;
5import com.google.common.collect.Maps; 12import com.google.common.collect.Maps;
6import com.google.common.collect.Sets; 13import com.google.common.collect.Sets;
14
7import cuchaz.enigma.analysis.EntryReference; 15import cuchaz.enigma.analysis.EntryReference;
8import cuchaz.enigma.analysis.ReferenceTargetType; 16import cuchaz.enigma.analysis.ReferenceTargetType;
9import cuchaz.enigma.translation.representation.AccessFlags; 17import cuchaz.enigma.translation.representation.AccessFlags;
10import cuchaz.enigma.translation.representation.entry.*; 18import cuchaz.enigma.translation.representation.entry.ClassEntry;
11 19import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
12import java.util.*; 20import cuchaz.enigma.translation.representation.entry.FieldEntry;
21import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
22import cuchaz.enigma.translation.representation.entry.MethodEntry;
13 23
14public class PackageVisibilityIndex implements JarIndexer { 24public class PackageVisibilityIndex implements JarIndexer {
15 private static boolean requiresSamePackage(AccessFlags entryAcc, EntryReference ref, InheritanceIndex inheritanceIndex) { 25 private static boolean requiresSamePackage(AccessFlags entryAcc, EntryReference ref, InheritanceIndex inheritanceIndex) {
@@ -30,9 +40,7 @@ public class PackageVisibilityIndex implements JarIndexer {
30 } 40 }
31 41
32 // access to instance member only valid if target's class assignable to context class 42 // access to instance member only valid if target's class assignable to context class
33 return !(ref.targetType.getKind() == ReferenceTargetType.Kind.UNINITIALIZED || 43 return !(ref.targetType.getKind() == ReferenceTargetType.Kind.UNINITIALIZED || ((ReferenceTargetType.ClassType) ref.targetType).getEntry().equals(contextClass) || inheritanceIndex.getAncestors(((ReferenceTargetType.ClassType) ref.targetType).getEntry()).contains(contextClass));
34 ((ReferenceTargetType.ClassType) ref.targetType).getEntry().equals(contextClass) ||
35 inheritanceIndex.getAncestors(((ReferenceTargetType.ClassType) ref.targetType).getEntry()).contains(contextClass));
36 } 44 }
37 45
38 return true; 46 return true;
@@ -61,6 +69,7 @@ public class PackageVisibilityIndex implements JarIndexer {
61 private void addConnections(EntryIndex entryIndex, ReferenceIndex referenceIndex, InheritanceIndex inheritanceIndex) { 69 private void addConnections(EntryIndex entryIndex, ReferenceIndex referenceIndex, InheritanceIndex inheritanceIndex) {
62 for (FieldEntry entry : entryIndex.getFields()) { 70 for (FieldEntry entry : entryIndex.getFields()) {
63 AccessFlags entryAcc = entryIndex.getFieldAccess(entry); 71 AccessFlags entryAcc = entryIndex.getFieldAccess(entry);
72
64 if (!entryAcc.isPublic() && !entryAcc.isPrivate()) { 73 if (!entryAcc.isPublic() && !entryAcc.isPrivate()) {
65 for (EntryReference<FieldEntry, MethodDefEntry> ref : referenceIndex.getReferencesToField(entry)) { 74 for (EntryReference<FieldEntry, MethodDefEntry> ref : referenceIndex.getReferencesToField(entry)) {
66 if (requiresSamePackage(entryAcc, ref, inheritanceIndex)) { 75 if (requiresSamePackage(entryAcc, ref, inheritanceIndex)) {
@@ -72,6 +81,7 @@ public class PackageVisibilityIndex implements JarIndexer {
72 81
73 for (MethodEntry entry : entryIndex.getMethods()) { 82 for (MethodEntry entry : entryIndex.getMethods()) {
74 AccessFlags entryAcc = entryIndex.getMethodAccess(entry); 83 AccessFlags entryAcc = entryIndex.getMethodAccess(entry);
84
75 if (!entryAcc.isPublic() && !entryAcc.isPrivate()) { 85 if (!entryAcc.isPublic() && !entryAcc.isPrivate()) {
76 for (EntryReference<MethodEntry, MethodDefEntry> ref : referenceIndex.getReferencesToMethod(entry)) { 86 for (EntryReference<MethodEntry, MethodDefEntry> ref : referenceIndex.getReferencesToMethod(entry)) {
77 if (requiresSamePackage(entryAcc, ref, inheritanceIndex)) { 87 if (requiresSamePackage(entryAcc, ref, inheritanceIndex)) {
@@ -83,6 +93,7 @@ public class PackageVisibilityIndex implements JarIndexer {
83 93
84 for (ClassEntry entry : entryIndex.getClasses()) { 94 for (ClassEntry entry : entryIndex.getClasses()) {
85 AccessFlags entryAcc = entryIndex.getClassAccess(entry); 95 AccessFlags entryAcc = entryIndex.getClassAccess(entry);
96
86 if (!entryAcc.isPublic() && !entryAcc.isPrivate()) { 97 if (!entryAcc.isPublic() && !entryAcc.isPrivate()) {
87 for (EntryReference<ClassEntry, FieldDefEntry> ref : referenceIndex.getFieldTypeReferencesToClass(entry)) { 98 for (EntryReference<ClassEntry, FieldDefEntry> ref : referenceIndex.getFieldTypeReferencesToClass(entry)) {
88 if (requiresSamePackage(entryAcc, ref, inheritanceIndex)) { 99 if (requiresSamePackage(entryAcc, ref, inheritanceIndex)) {
@@ -99,12 +110,14 @@ public class PackageVisibilityIndex implements JarIndexer {
99 110
100 for (ClassEntry parent : inheritanceIndex.getParents(entry)) { 111 for (ClassEntry parent : inheritanceIndex.getParents(entry)) {
101 AccessFlags parentAcc = entryIndex.getClassAccess(parent); 112 AccessFlags parentAcc = entryIndex.getClassAccess(parent);
113
102 if (parentAcc != null && !parentAcc.isPublic() && !parentAcc.isPrivate()) { 114 if (parentAcc != null && !parentAcc.isPublic() && !parentAcc.isPrivate()) {
103 addConnection(entry, parent); 115 addConnection(entry, parent);
104 } 116 }
105 } 117 }
106 118
107 ClassEntry outerClass = entry.getOuterClass(); 119 ClassEntry outerClass = entry.getOuterClass();
120
108 if (outerClass != null) { 121 if (outerClass != null) {
109 addConnection(entry, outerClass); 122 addConnection(entry, outerClass);
110 } 123 }
@@ -113,6 +126,7 @@ public class PackageVisibilityIndex implements JarIndexer {
113 126
114 private void addPartitions(EntryIndex entryIndex) { 127 private void addPartitions(EntryIndex entryIndex) {
115 Set<ClassEntry> unassignedClasses = Sets.newHashSet(entryIndex.getClasses()); 128 Set<ClassEntry> unassignedClasses = Sets.newHashSet(entryIndex.getClasses());
129
116 while (!unassignedClasses.isEmpty()) { 130 while (!unassignedClasses.isEmpty()) {
117 Iterator<ClassEntry> iterator = unassignedClasses.iterator(); 131 Iterator<ClassEntry> iterator = unassignedClasses.iterator();
118 ClassEntry initialEntry = iterator.next(); 132 ClassEntry initialEntry = iterator.next();
@@ -122,6 +136,7 @@ public class PackageVisibilityIndex implements JarIndexer {
122 partition.add(initialEntry); 136 partition.add(initialEntry);
123 buildPartition(unassignedClasses, partition, initialEntry); 137 buildPartition(unassignedClasses, partition, initialEntry);
124 partitions.add(partition); 138 partitions.add(partition);
139
125 for (ClassEntry entry : partition) { 140 for (ClassEntry entry : partition) {
126 classPartitions.put(entry, partition); 141 classPartitions.put(entry, partition);
127 } 142 }
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java b/enigma/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java
index b6797c2..332a967 100644
--- a/enigma/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java
+++ b/enigma/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java
@@ -1,17 +1,23 @@
1package cuchaz.enigma.analysis.index; 1package cuchaz.enigma.analysis.index;
2 2
3import java.util.Collection;
4import java.util.Map;
5
3import com.google.common.collect.HashMultimap; 6import com.google.common.collect.HashMultimap;
4import com.google.common.collect.Multimap; 7import com.google.common.collect.Multimap;
8
5import cuchaz.enigma.analysis.EntryReference; 9import cuchaz.enigma.analysis.EntryReference;
6import cuchaz.enigma.analysis.ReferenceTargetType; 10import cuchaz.enigma.analysis.ReferenceTargetType;
7import cuchaz.enigma.translation.mapping.ResolutionStrategy; 11import cuchaz.enigma.translation.mapping.ResolutionStrategy;
8import cuchaz.enigma.translation.representation.Lambda; 12import cuchaz.enigma.translation.representation.Lambda;
9import cuchaz.enigma.translation.representation.MethodDescriptor; 13import cuchaz.enigma.translation.representation.MethodDescriptor;
10import cuchaz.enigma.translation.representation.TypeDescriptor; 14import cuchaz.enigma.translation.representation.TypeDescriptor;
11import cuchaz.enigma.translation.representation.entry.*; 15import cuchaz.enigma.translation.representation.entry.ClassEntry;
12 16import cuchaz.enigma.translation.representation.entry.Entry;
13import java.util.Collection; 17import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
14import java.util.Map; 18import cuchaz.enigma.translation.representation.entry.FieldEntry;
19import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
20import cuchaz.enigma.translation.representation.entry.MethodEntry;
15 21
16public class ReferenceIndex implements JarIndexer { 22public class ReferenceIndex implements JarIndexer {
17 private Multimap<MethodEntry, MethodEntry> methodReferences = HashMultimap.create(); 23 private Multimap<MethodEntry, MethodEntry> methodReferences = HashMultimap.create();
@@ -24,13 +30,14 @@ public class ReferenceIndex implements JarIndexer {
24 30
25 @Override 31 @Override
26 public void indexMethod(MethodDefEntry methodEntry) { 32 public void indexMethod(MethodDefEntry methodEntry) {
27 indexMethodDescriptor(methodEntry, methodEntry.getDesc()); 33 indexMethodDescriptor(methodEntry, methodEntry.getDesc());
28 } 34 }
29 35
30 private void indexMethodDescriptor(MethodDefEntry entry, MethodDescriptor descriptor) { 36 private void indexMethodDescriptor(MethodDefEntry entry, MethodDescriptor descriptor) {
31 for (TypeDescriptor typeDescriptor : descriptor.getArgumentDescs()) { 37 for (TypeDescriptor typeDescriptor : descriptor.getArgumentDescs()) {
32 indexMethodTypeDescriptor(entry, typeDescriptor); 38 indexMethodTypeDescriptor(entry, typeDescriptor);
33 } 39 }
40
34 indexMethodTypeDescriptor(entry, descriptor.getReturnDesc()); 41 indexMethodTypeDescriptor(entry, descriptor.getReturnDesc());
35 } 42 }
36 43
@@ -45,7 +52,7 @@ public class ReferenceIndex implements JarIndexer {
45 52
46 @Override 53 @Override
47 public void indexField(FieldDefEntry fieldEntry) { 54 public void indexField(FieldDefEntry fieldEntry) {
48 indexFieldTypeDescriptor(fieldEntry, fieldEntry.getDesc()); 55 indexFieldTypeDescriptor(fieldEntry, fieldEntry.getDesc());
49 } 56 }
50 57
51 private void indexFieldTypeDescriptor(FieldDefEntry field, TypeDescriptor typeDescriptor) { 58 private void indexFieldTypeDescriptor(FieldDefEntry field, TypeDescriptor typeDescriptor) {
@@ -53,7 +60,7 @@ public class ReferenceIndex implements JarIndexer {
53 ClassEntry referencedClass = typeDescriptor.getTypeEntry(); 60 ClassEntry referencedClass = typeDescriptor.getTypeEntry();
54 fieldTypeReferences.put(referencedClass, new EntryReference<>(referencedClass, referencedClass.getName(), field)); 61 fieldTypeReferences.put(referencedClass, new EntryReference<>(referencedClass, referencedClass.getName(), field));
55 } else if (typeDescriptor.isArray()) { 62 } else if (typeDescriptor.isArray()) {
56 indexFieldTypeDescriptor(field, typeDescriptor.getArrayType()); 63 indexFieldTypeDescriptor(field, typeDescriptor.getArrayType());
57 } 64 }
58 } 65 }
59 66
@@ -99,18 +106,22 @@ public class ReferenceIndex implements JarIndexer {
99 private <K extends Entry<?>, V extends Entry<?>> Multimap<K, V> remapReferences(JarIndex index, Multimap<K, V> multimap) { 106 private <K extends Entry<?>, V extends Entry<?>> Multimap<K, V> remapReferences(JarIndex index, Multimap<K, V> multimap) {
100 final int keySetSize = multimap.keySet().size(); 107 final int keySetSize = multimap.keySet().size();
101 Multimap<K, V> resolved = HashMultimap.create(multimap.keySet().size(), keySetSize == 0 ? 0 : multimap.size() / keySetSize); 108 Multimap<K, V> resolved = HashMultimap.create(multimap.keySet().size(), keySetSize == 0 ? 0 : multimap.size() / keySetSize);
109
102 for (Map.Entry<K, V> entry : multimap.entries()) { 110 for (Map.Entry<K, V> entry : multimap.entries()) {
103 resolved.put(remap(index, entry.getKey()), remap(index, entry.getValue())); 111 resolved.put(remap(index, entry.getKey()), remap(index, entry.getValue()));
104 } 112 }
113
105 return resolved; 114 return resolved;
106 } 115 }
107 116
108 private <E extends Entry<?>, C extends Entry<?>> Multimap<E, EntryReference<E, C>> remapReferencesTo(JarIndex index, Multimap<E, EntryReference<E, C>> multimap) { 117 private <E extends Entry<?>, C extends Entry<?>> Multimap<E, EntryReference<E, C>> remapReferencesTo(JarIndex index, Multimap<E, EntryReference<E, C>> multimap) {
109 final int keySetSize = multimap.keySet().size(); 118 final int keySetSize = multimap.keySet().size();
110 Multimap<E, EntryReference<E, C>> resolved = HashMultimap.create(keySetSize, keySetSize == 0 ? 0 : multimap.size() / keySetSize); 119 Multimap<E, EntryReference<E, C>> resolved = HashMultimap.create(keySetSize, keySetSize == 0 ? 0 : multimap.size() / keySetSize);
120
111 for (Map.Entry<E, EntryReference<E, C>> entry : multimap.entries()) { 121 for (Map.Entry<E, EntryReference<E, C>> entry : multimap.entries()) {
112 resolved.put(remap(index, entry.getKey()), remap(index, entry.getValue())); 122 resolved.put(remap(index, entry.getKey()), remap(index, entry.getValue()));
113 } 123 }
124
114 return resolved; 125 return resolved;
115 } 126 }
116 127
diff --git a/enigma/src/main/java/cuchaz/enigma/api/service/EnigmaServiceType.java b/enigma/src/main/java/cuchaz/enigma/api/service/EnigmaServiceType.java
index 358828f..e2cb6b1 100644
--- a/enigma/src/main/java/cuchaz/enigma/api/service/EnigmaServiceType.java
+++ b/enigma/src/main/java/cuchaz/enigma/api/service/EnigmaServiceType.java
@@ -18,7 +18,9 @@ public final class EnigmaServiceType<T extends EnigmaService> {
18 18
19 @Override 19 @Override
20 public boolean equals(Object obj) { 20 public boolean equals(Object obj) {
21 if (obj == this) return true; 21 if (obj == this) {
22 return true;
23 }
22 24
23 if (obj instanceof EnigmaServiceType) { 25 if (obj instanceof EnigmaServiceType) {
24 return ((EnigmaServiceType) obj).key.equals(key); 26 return ((EnigmaServiceType) obj).key.equals(key);
diff --git a/enigma/src/main/java/cuchaz/enigma/api/service/JarIndexerService.java b/enigma/src/main/java/cuchaz/enigma/api/service/JarIndexerService.java
index 5417531..3ed6d33 100644
--- a/enigma/src/main/java/cuchaz/enigma/api/service/JarIndexerService.java
+++ b/enigma/src/main/java/cuchaz/enigma/api/service/JarIndexerService.java
@@ -1,10 +1,11 @@
1package cuchaz.enigma.api.service; 1package cuchaz.enigma.api.service;
2 2
3import cuchaz.enigma.analysis.index.JarIndex; 3import java.util.Set;
4import cuchaz.enigma.classprovider.ClassProvider; 4
5import org.objectweb.asm.ClassVisitor; 5import org.objectweb.asm.ClassVisitor;
6 6
7import java.util.Set; 7import cuchaz.enigma.analysis.index.JarIndex;
8import cuchaz.enigma.classprovider.ClassProvider;
8 9
9public interface JarIndexerService extends EnigmaService { 10public interface JarIndexerService extends EnigmaService {
10 EnigmaServiceType<JarIndexerService> TYPE = EnigmaServiceType.create("jar_indexer"); 11 EnigmaServiceType<JarIndexerService> TYPE = EnigmaServiceType.create("jar_indexer");
@@ -12,7 +13,7 @@ public interface JarIndexerService extends EnigmaService {
12 void acceptJar(Set<String> scope, ClassProvider classProvider, JarIndex jarIndex); 13 void acceptJar(Set<String> scope, ClassProvider classProvider, JarIndex jarIndex);
13 14
14 static JarIndexerService fromVisitor(ClassVisitor visitor) { 15 static JarIndexerService fromVisitor(ClassVisitor visitor) {
15 return (scope, classProvider, jarIndex) -> { 16 return (scope, classProvider, jarIndex) -> {
16 for (String className : scope) { 17 for (String className : scope) {
17 classProvider.get(className).accept(visitor); 18 classProvider.get(className).accept(visitor);
18 } 19 }
diff --git a/enigma/src/main/java/cuchaz/enigma/api/service/NameProposalService.java b/enigma/src/main/java/cuchaz/enigma/api/service/NameProposalService.java
index 4c357db..4c40868 100644
--- a/enigma/src/main/java/cuchaz/enigma/api/service/NameProposalService.java
+++ b/enigma/src/main/java/cuchaz/enigma/api/service/NameProposalService.java
@@ -1,10 +1,10 @@
1package cuchaz.enigma.api.service; 1package cuchaz.enigma.api.service;
2 2
3import java.util.Optional;
4
3import cuchaz.enigma.translation.mapping.EntryRemapper; 5import cuchaz.enigma.translation.mapping.EntryRemapper;
4import cuchaz.enigma.translation.representation.entry.Entry; 6import cuchaz.enigma.translation.representation.entry.Entry;
5 7
6import java.util.Optional;
7
8public interface NameProposalService extends EnigmaService { 8public interface NameProposalService extends EnigmaService {
9 EnigmaServiceType<NameProposalService> TYPE = EnigmaServiceType.create("name_proposal"); 9 EnigmaServiceType<NameProposalService> TYPE = EnigmaServiceType.create("name_proposal");
10 10
diff --git a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/AsmObjectTranslator.java b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/AsmObjectTranslator.java
index 341cfce..891fe9d 100644
--- a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/AsmObjectTranslator.java
+++ b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/AsmObjectTranslator.java
@@ -1,32 +1,34 @@
1package cuchaz.enigma.bytecode.translators; 1package cuchaz.enigma.bytecode.translators;
2 2
3import org.objectweb.asm.Handle;
4import org.objectweb.asm.Opcodes;
5import org.objectweb.asm.Type;
6
3import cuchaz.enigma.translation.Translator; 7import cuchaz.enigma.translation.Translator;
4import cuchaz.enigma.translation.representation.MethodDescriptor; 8import cuchaz.enigma.translation.representation.MethodDescriptor;
5import cuchaz.enigma.translation.representation.TypeDescriptor; 9import cuchaz.enigma.translation.representation.TypeDescriptor;
6import cuchaz.enigma.translation.representation.entry.ClassEntry; 10import cuchaz.enigma.translation.representation.entry.ClassEntry;
7import cuchaz.enigma.translation.representation.entry.FieldEntry; 11import cuchaz.enigma.translation.representation.entry.FieldEntry;
8import cuchaz.enigma.translation.representation.entry.MethodEntry; 12import cuchaz.enigma.translation.representation.entry.MethodEntry;
9import org.objectweb.asm.Handle;
10import org.objectweb.asm.Opcodes;
11import org.objectweb.asm.Type;
12 13
13public class AsmObjectTranslator { 14public class AsmObjectTranslator {
14 public static Type translateType(Translator translator, Type type) { 15 public static Type translateType(Translator translator, Type type) {
15 String descString = type.getDescriptor(); 16 String descString = type.getDescriptor();
16 switch (type.getSort()) { 17 switch (type.getSort()) {
17 case Type.OBJECT: { 18 case Type.OBJECT: {
18 ClassEntry classEntry = new ClassEntry(type.getInternalName()); 19 ClassEntry classEntry = new ClassEntry(type.getInternalName());
19 return Type.getObjectType(translator.translate(classEntry).getFullName()); 20 return Type.getObjectType(translator.translate(classEntry).getFullName());
20 } 21 }
21 case Type.ARRAY: { 22 case Type.ARRAY: {
22 TypeDescriptor descriptor = new TypeDescriptor(descString); 23 TypeDescriptor descriptor = new TypeDescriptor(descString);
23 return Type.getType(translator.translate(descriptor).toString()); 24 return Type.getType(translator.translate(descriptor).toString());
24 } 25 }
25 case Type.METHOD: { 26 case Type.METHOD: {
26 MethodDescriptor descriptor = new MethodDescriptor(descString); 27 MethodDescriptor descriptor = new MethodDescriptor(descString);
27 return Type.getMethodType(translator.translate(descriptor).toString()); 28 return Type.getMethodType(translator.translate(descriptor).toString());
28 }
29 } 29 }
30 }
31
30 return type; 32 return type;
31 } 33 }
32 34
@@ -55,6 +57,7 @@ public class AsmObjectTranslator {
55 } else if (value instanceof Handle) { 57 } else if (value instanceof Handle) {
56 return translateHandle(translator, (Handle) value); 58 return translateHandle(translator, (Handle) value);
57 } 59 }
60
58 return value; 61 return value;
59 } 62 }
60} 63}
diff --git a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableFixVisitor.java b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableFixVisitor.java
index cfd8fbe..dc399e5 100644
--- a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableFixVisitor.java
+++ b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableFixVisitor.java
@@ -1,18 +1,19 @@
1package cuchaz.enigma.bytecode.translators; 1package cuchaz.enigma.bytecode.translators;
2 2
3import java.util.HashMap;
4import java.util.List;
5import java.util.Map;
6
3import com.google.common.base.CharMatcher; 7import com.google.common.base.CharMatcher;
4import cuchaz.enigma.translation.LocalNameGenerator;
5import cuchaz.enigma.translation.representation.TypeDescriptor;
6import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
7import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
8import org.objectweb.asm.ClassVisitor; 8import org.objectweb.asm.ClassVisitor;
9import org.objectweb.asm.Label; 9import org.objectweb.asm.Label;
10import org.objectweb.asm.MethodVisitor; 10import org.objectweb.asm.MethodVisitor;
11import org.objectweb.asm.Opcodes; 11import org.objectweb.asm.Opcodes;
12 12
13import java.util.HashMap; 13import cuchaz.enigma.translation.LocalNameGenerator;
14import java.util.List; 14import cuchaz.enigma.translation.representation.TypeDescriptor;
15import java.util.Map; 15import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
16import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
16 17
17public class LocalVariableFixVisitor extends ClassVisitor { 18public class LocalVariableFixVisitor extends ClassVisitor {
18 private ClassDefEntry ownerEntry; 19 private ClassDefEntry ownerEntry;
@@ -46,6 +47,7 @@ public class LocalVariableFixVisitor extends ClassVisitor {
46 47
47 int lvIndex = methodEntry.getAccess().isStatic() ? 0 : 1; 48 int lvIndex = methodEntry.getAccess().isStatic() ? 0 : 1;
48 List<TypeDescriptor> parameters = methodEntry.getDesc().getArgumentDescs(); 49 List<TypeDescriptor> parameters = methodEntry.getDesc().getArgumentDescs();
50
49 for (int parameterIndex = 0; parameterIndex < parameters.size(); parameterIndex++) { 51 for (int parameterIndex = 0; parameterIndex < parameters.size(); parameterIndex++) {
50 TypeDescriptor param = parameters.get(parameterIndex); 52 TypeDescriptor param = parameters.get(parameterIndex);
51 parameterIndices.put(lvIndex, parameterIndex); 53 parameterIndices.put(lvIndex, parameterIndex);
@@ -81,6 +83,7 @@ public class LocalVariableFixVisitor extends ClassVisitor {
81 public void visitEnd() { 83 public void visitEnd() {
82 if (!hasParameterTable) { 84 if (!hasParameterTable) {
83 List<TypeDescriptor> arguments = methodEntry.getDesc().getArgumentDescs(); 85 List<TypeDescriptor> arguments = methodEntry.getDesc().getArgumentDescs();
86
84 for (int argumentIndex = 0; argumentIndex < arguments.size(); argumentIndex++) { 87 for (int argumentIndex = 0; argumentIndex < arguments.size(); argumentIndex++) {
85 super.visitParameter(fixParameterName(argumentIndex, null), fixParameterAccess(argumentIndex, 0)); 88 super.visitParameter(fixParameterName(argumentIndex, null), fixParameterAccess(argumentIndex, 0));
86 } 89 }
diff --git a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/SourceFixVisitor.java b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/SourceFixVisitor.java
index 2b750ea..51b21a6 100644
--- a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/SourceFixVisitor.java
+++ b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/SourceFixVisitor.java
@@ -1,12 +1,13 @@
1package cuchaz.enigma.bytecode.translators; 1package cuchaz.enigma.bytecode.translators;
2 2
3import org.objectweb.asm.ClassVisitor;
4import org.objectweb.asm.MethodVisitor;
5import org.objectweb.asm.Opcodes;
6
3import cuchaz.enigma.analysis.index.BridgeMethodIndex; 7import cuchaz.enigma.analysis.index.BridgeMethodIndex;
4import cuchaz.enigma.analysis.index.JarIndex; 8import cuchaz.enigma.analysis.index.JarIndex;
5import cuchaz.enigma.translation.representation.entry.ClassDefEntry; 9import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
6import cuchaz.enigma.translation.representation.entry.MethodDefEntry; 10import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
7import org.objectweb.asm.ClassVisitor;
8import org.objectweb.asm.MethodVisitor;
9import org.objectweb.asm.Opcodes;
10 11
11public class SourceFixVisitor extends ClassVisitor { 12public class SourceFixVisitor extends ClassVisitor {
12 private final JarIndex index; 13 private final JarIndex index;
@@ -28,6 +29,7 @@ public class SourceFixVisitor extends ClassVisitor {
28 MethodDefEntry methodEntry = MethodDefEntry.parse(ownerEntry, access, name, descriptor, signature); 29 MethodDefEntry methodEntry = MethodDefEntry.parse(ownerEntry, access, name, descriptor, signature);
29 30
30 BridgeMethodIndex bridgeIndex = index.getBridgeMethodIndex(); 31 BridgeMethodIndex bridgeIndex = index.getBridgeMethodIndex();
32
31 if (bridgeIndex.isBridgeMethod(methodEntry)) { 33 if (bridgeIndex.isBridgeMethod(methodEntry)) {
32 access |= Opcodes.ACC_BRIDGE; 34 access |= Opcodes.ACC_BRIDGE;
33 } else if (bridgeIndex.isSpecializedMethod(methodEntry)) { 35 } else if (bridgeIndex.isSpecializedMethod(methodEntry)) {
diff --git a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationAnnotationVisitor.java b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationAnnotationVisitor.java
index cb843ad..d105e4c 100644
--- a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationAnnotationVisitor.java
+++ b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationAnnotationVisitor.java
@@ -1,10 +1,11 @@
1package cuchaz.enigma.bytecode.translators; 1package cuchaz.enigma.bytecode.translators;
2 2
3import org.objectweb.asm.AnnotationVisitor;
4
3import cuchaz.enigma.translation.Translator; 5import cuchaz.enigma.translation.Translator;
4import cuchaz.enigma.translation.representation.TypeDescriptor; 6import cuchaz.enigma.translation.representation.TypeDescriptor;
5import cuchaz.enigma.translation.representation.entry.ClassEntry; 7import cuchaz.enigma.translation.representation.entry.ClassEntry;
6import cuchaz.enigma.translation.representation.entry.FieldEntry; 8import cuchaz.enigma.translation.representation.entry.FieldEntry;
7import org.objectweb.asm.AnnotationVisitor;
8 9
9public class TranslationAnnotationVisitor extends AnnotationVisitor { 10public class TranslationAnnotationVisitor extends AnnotationVisitor {
10 private final Translator translator; 11 private final Translator translator;
@@ -29,6 +30,7 @@ public class TranslationAnnotationVisitor extends AnnotationVisitor {
29 @Override 30 @Override
30 public AnnotationVisitor visitAnnotation(String name, String desc) { 31 public AnnotationVisitor visitAnnotation(String name, String desc) {
31 TypeDescriptor type = new TypeDescriptor(desc); 32 TypeDescriptor type = new TypeDescriptor(desc);
33
32 if (name != null) { 34 if (name != null) {
33 FieldEntry annotationField = translator.translate(new FieldEntry(annotationEntry, name, type)); 35 FieldEntry annotationField = translator.translate(new FieldEntry(annotationEntry, name, type));
34 return super.visitAnnotation(annotationField.getName(), annotationField.getDesc().toString()); 36 return super.visitAnnotation(annotationField.getName(), annotationField.getDesc().toString());
@@ -41,6 +43,7 @@ public class TranslationAnnotationVisitor extends AnnotationVisitor {
41 public void visitEnum(String name, String desc, String value) { 43 public void visitEnum(String name, String desc, String value) {
42 TypeDescriptor type = new TypeDescriptor(desc); 44 TypeDescriptor type = new TypeDescriptor(desc);
43 FieldEntry enumField = translator.translate(new FieldEntry(type.getTypeEntry(), value, type)); 45 FieldEntry enumField = translator.translate(new FieldEntry(type.getTypeEntry(), value, type));
46
44 if (name != null) { 47 if (name != null) {
45 FieldEntry annotationField = translator.translate(new FieldEntry(annotationEntry, name, type)); 48 FieldEntry annotationField = translator.translate(new FieldEntry(annotationEntry, name, type));
46 super.visitEnum(annotationField.getName(), annotationField.getDesc().toString(), enumField.getName()); 49 super.visitEnum(annotationField.getName(), annotationField.getDesc().toString(), enumField.getName());
diff --git a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java
index 0b2ca9a..66c8490 100644
--- a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java
+++ b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java
@@ -1,23 +1,33 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.bytecode.translators; 12package cuchaz.enigma.bytecode.translators;
13 13
14import java.util.Arrays;
15
16import org.objectweb.asm.AnnotationVisitor;
17import org.objectweb.asm.ClassVisitor;
18import org.objectweb.asm.FieldVisitor;
19import org.objectweb.asm.MethodVisitor;
20import org.objectweb.asm.RecordComponentVisitor;
21import org.objectweb.asm.TypePath;
22
14import cuchaz.enigma.translation.Translator; 23import cuchaz.enigma.translation.Translator;
15import cuchaz.enigma.translation.representation.MethodDescriptor; 24import cuchaz.enigma.translation.representation.MethodDescriptor;
16import cuchaz.enigma.translation.representation.TypeDescriptor; 25import cuchaz.enigma.translation.representation.TypeDescriptor;
17import cuchaz.enigma.translation.representation.entry.*; 26import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
18import org.objectweb.asm.*; 27import cuchaz.enigma.translation.representation.entry.ClassEntry;
19 28import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
20import java.util.Arrays; 29import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
30import cuchaz.enigma.translation.representation.entry.MethodEntry;
21 31
22public class TranslationClassVisitor extends ClassVisitor { 32public class TranslationClassVisitor extends ClassVisitor {
23 private final Translator translator; 33 private final Translator translator;
@@ -53,9 +63,11 @@ public class TranslationClassVisitor extends ClassVisitor {
53 MethodDefEntry entry = MethodDefEntry.parse(obfClassEntry, access, name, desc, signature); 63 MethodDefEntry entry = MethodDefEntry.parse(obfClassEntry, access, name, desc, signature);
54 MethodDefEntry translatedEntry = translator.translate(entry); 64 MethodDefEntry translatedEntry = translator.translate(entry);
55 String[] translatedExceptions = new String[exceptions.length]; 65 String[] translatedExceptions = new String[exceptions.length];
66
56 for (int i = 0; i < exceptions.length; i++) { 67 for (int i = 0; i < exceptions.length; i++) {
57 translatedExceptions[i] = translator.translate(new ClassEntry(exceptions[i])).getFullName(); 68 translatedExceptions[i] = translator.translate(new ClassEntry(exceptions[i])).getFullName();
58 } 69 }
70
59 MethodVisitor mv = super.visitMethod(translatedEntry.getAccess().getFlags(), translatedEntry.getName(), translatedEntry.getDesc().toString(), translatedEntry.getSignature().toString(), translatedExceptions); 71 MethodVisitor mv = super.visitMethod(translatedEntry.getAccess().getFlags(), translatedEntry.getName(), translatedEntry.getDesc().toString(), translatedEntry.getSignature().toString(), translatedExceptions);
60 return new TranslationMethodVisitor(translator, obfClassEntry, entry, api, mv); 72 return new TranslationMethodVisitor(translator, obfClassEntry, entry, api, mv);
61 } 73 }
@@ -65,6 +77,7 @@ public class TranslationClassVisitor extends ClassVisitor {
65 ClassDefEntry classEntry = ClassDefEntry.parse(access, name, obfClassEntry.getSignature().toString(), null, new String[0]); 77 ClassDefEntry classEntry = ClassDefEntry.parse(access, name, obfClassEntry.getSignature().toString(), null, new String[0]);
66 ClassDefEntry translatedEntry = translator.translate(classEntry); 78 ClassDefEntry translatedEntry = translator.translate(classEntry);
67 ClassEntry translatedOuterClass = translatedEntry.getOuterClass(); 79 ClassEntry translatedOuterClass = translatedEntry.getOuterClass();
80
68 if (translatedOuterClass == null) { 81 if (translatedOuterClass == null) {
69 throw new IllegalStateException("Translated inner class did not have outer class"); 82 throw new IllegalStateException("Translated inner class did not have outer class");
70 } 83 }
diff --git a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java
index 28fc199..d026f15 100644
--- a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java
+++ b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java
@@ -1,12 +1,13 @@
1package cuchaz.enigma.bytecode.translators; 1package cuchaz.enigma.bytecode.translators;
2 2
3import cuchaz.enigma.translation.Translator;
4import cuchaz.enigma.translation.representation.TypeDescriptor;
5import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
6import org.objectweb.asm.AnnotationVisitor; 3import org.objectweb.asm.AnnotationVisitor;
7import org.objectweb.asm.FieldVisitor; 4import org.objectweb.asm.FieldVisitor;
8import org.objectweb.asm.TypePath; 5import org.objectweb.asm.TypePath;
9 6
7import cuchaz.enigma.translation.Translator;
8import cuchaz.enigma.translation.representation.TypeDescriptor;
9import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
10
10public class TranslationFieldVisitor extends FieldVisitor { 11public class TranslationFieldVisitor extends FieldVisitor {
11 private final FieldDefEntry fieldEntry; 12 private final FieldDefEntry fieldEntry;
12 private final Translator translator; 13 private final Translator translator;
diff --git a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java
index a82df1b..932c123 100644
--- a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java
+++ b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java
@@ -1,11 +1,21 @@
1package cuchaz.enigma.bytecode.translators; 1package cuchaz.enigma.bytecode.translators;
2 2
3import org.objectweb.asm.AnnotationVisitor;
4import org.objectweb.asm.Handle;
5import org.objectweb.asm.Label;
6import org.objectweb.asm.MethodVisitor;
7import org.objectweb.asm.TypePath;
8
3import cuchaz.enigma.translation.Translator; 9import cuchaz.enigma.translation.Translator;
4import cuchaz.enigma.translation.representation.MethodDescriptor; 10import cuchaz.enigma.translation.representation.MethodDescriptor;
5import cuchaz.enigma.translation.representation.Signature; 11import cuchaz.enigma.translation.representation.Signature;
6import cuchaz.enigma.translation.representation.TypeDescriptor; 12import cuchaz.enigma.translation.representation.TypeDescriptor;
7import cuchaz.enigma.translation.representation.entry.*; 13import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
8import org.objectweb.asm.*; 14import cuchaz.enigma.translation.representation.entry.ClassEntry;
15import cuchaz.enigma.translation.representation.entry.FieldEntry;
16import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
17import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
18import cuchaz.enigma.translation.representation.entry.MethodEntry;
9 19
10public class TranslationMethodVisitor extends MethodVisitor { 20public class TranslationMethodVisitor extends MethodVisitor {
11 private final MethodDefEntry methodEntry; 21 private final MethodDefEntry methodEntry;
@@ -55,13 +65,16 @@ public class TranslationMethodVisitor extends MethodVisitor {
55 if (array == null) { 65 if (array == null) {
56 return null; 66 return null;
57 } 67 }
68
58 for (int i = 0; i < count; i++) { 69 for (int i = 0; i < count; i++) {
59 Object object = array[i]; 70 Object object = array[i];
71
60 if (object instanceof String) { 72 if (object instanceof String) {
61 String type = (String) object; 73 String type = (String) object;
62 array[i] = translator.translate(new ClassEntry(type)).getFullName(); 74 array[i] = translator.translate(new ClassEntry(type)).getFullName();
63 } 75 }
64 } 76 }
77
65 return array; 78 return array;
66 } 79 }
67 80
@@ -96,9 +109,11 @@ public class TranslationMethodVisitor extends MethodVisitor {
96 public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { 109 public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
97 MethodDescriptor translatedMethodDesc = translator.translate(new MethodDescriptor(desc)); 110 MethodDescriptor translatedMethodDesc = translator.translate(new MethodDescriptor(desc));
98 Object[] translatedBsmArgs = new Object[bsmArgs.length]; 111 Object[] translatedBsmArgs = new Object[bsmArgs.length];
112
99 for (int i = 0; i < bsmArgs.length; i++) { 113 for (int i = 0; i < bsmArgs.length; i++) {
100 translatedBsmArgs[i] = AsmObjectTranslator.translateValue(translator, bsmArgs[i]); 114 translatedBsmArgs[i] = AsmObjectTranslator.translateValue(translator, bsmArgs[i]);
101 } 115 }
116
102 super.visitInvokeDynamicInsn(name, translatedMethodDesc.toString(), AsmObjectTranslator.translateHandle(translator, bsm), translatedBsmArgs); 117 super.visitInvokeDynamicInsn(name, translatedMethodDesc.toString(), AsmObjectTranslator.translateHandle(translator, bsm), translatedBsmArgs);
103 } 118 }
104 119
@@ -132,7 +147,7 @@ public class TranslationMethodVisitor extends MethodVisitor {
132 } 147 }
133 148
134 private String translateVariableName(int index, String name) { 149 private String translateVariableName(int index, String name) {
135 LocalVariableEntry entry = new LocalVariableEntry(methodEntry, index, "", true,null); 150 LocalVariableEntry entry = new LocalVariableEntry(methodEntry, index, "", true, null);
136 LocalVariableEntry translatedEntry = translator.translate(entry); 151 LocalVariableEntry translatedEntry = translator.translate(entry);
137 String translatedName = translatedEntry.getName(); 152 String translatedName = translatedEntry.getName();
138 153
diff --git a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationRecordComponentVisitor.java b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationRecordComponentVisitor.java
index 06fd22b..f7de0f8 100644
--- a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationRecordComponentVisitor.java
+++ b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationRecordComponentVisitor.java
@@ -1,11 +1,12 @@
1package cuchaz.enigma.bytecode.translators; 1package cuchaz.enigma.bytecode.translators;
2 2
3import cuchaz.enigma.translation.Translator;
4import cuchaz.enigma.translation.representation.TypeDescriptor;
5import org.objectweb.asm.AnnotationVisitor; 3import org.objectweb.asm.AnnotationVisitor;
6import org.objectweb.asm.RecordComponentVisitor; 4import org.objectweb.asm.RecordComponentVisitor;
7import org.objectweb.asm.TypePath; 5import org.objectweb.asm.TypePath;
8 6
7import cuchaz.enigma.translation.Translator;
8import cuchaz.enigma.translation.representation.TypeDescriptor;
9
9public class TranslationRecordComponentVisitor extends RecordComponentVisitor { 10public class TranslationRecordComponentVisitor extends RecordComponentVisitor {
10 private final Translator translator; 11 private final Translator translator;
11 12
diff --git a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationSignatureVisitor.java b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationSignatureVisitor.java
index 6cab22c..49350f6 100644
--- a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationSignatureVisitor.java
+++ b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationSignatureVisitor.java
@@ -29,17 +29,23 @@ public class TranslationSignatureVisitor extends SignatureVisitor {
29 @Override 29 @Override
30 public void visitInnerClassType(String name) { 30 public void visitInnerClassType(String name) {
31 String lastClass = classStack.pop(); 31 String lastClass = classStack.pop();
32 if (!name.startsWith(lastClass+"$")){//todo see if there's a way to base this on whether there were type params or not 32
33 name = lastClass+"$"+name; 33 if (!name.startsWith(lastClass + "$")) {
34 //todo see if there's a way to base this on whether there were type params or not
35 name = lastClass + "$" + name;
34 } 36 }
37
35 classStack.push(name); 38 classStack.push(name);
36 String translatedEntry = this.remapper.apply(name); 39 String translatedEntry = this.remapper.apply(name);
37 if (translatedEntry.contains("/")){ 40
38 translatedEntry = translatedEntry.substring(translatedEntry.lastIndexOf("/")+1); 41 if (translatedEntry.contains("/")) {
42 translatedEntry = translatedEntry.substring(translatedEntry.lastIndexOf("/") + 1);
39 } 43 }
40 if (translatedEntry.contains("$")){ 44
41 translatedEntry = translatedEntry.substring(translatedEntry.lastIndexOf("$")+1); 45 if (translatedEntry.contains("$")) {
46 translatedEntry = translatedEntry.substring(translatedEntry.lastIndexOf("$") + 1);
42 } 47 }
48
43 this.sv.visitInnerClassType(translatedEntry); 49 this.sv.visitInnerClassType(translatedEntry);
44 } 50 }
45 51
@@ -120,8 +126,10 @@ public class TranslationSignatureVisitor extends SignatureVisitor {
120 @Override 126 @Override
121 public void visitEnd() { 127 public void visitEnd() {
122 this.sv.visitEnd(); 128 this.sv.visitEnd();
123 if (!classStack.empty()) 129
130 if (!classStack.empty()) {
124 classStack.pop(); 131 classStack.pop();
132 }
125 } 133 }
126 134
127 @Override 135 @Override
diff --git a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandle.java b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandle.java
index 326197d..2a1643e 100644
--- a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandle.java
+++ b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandle.java
@@ -18,7 +18,6 @@ import cuchaz.enigma.utils.Result;
18 * @see ClassHandleProvider 18 * @see ClassHandleProvider
19 */ 19 */
20public interface ClassHandle extends AutoCloseable { 20public interface ClassHandle extends AutoCloseable {
21
22 /** 21 /**
23 * Gets the reference to this class. This is always obfuscated, for example 22 * Gets the reference to this class. This is always obfuscated, for example
24 * {@code net/minecraft/class_1000}. 23 * {@code net/minecraft/class_1000}.
@@ -104,5 +103,4 @@ public interface ClassHandle extends AutoCloseable {
104 */ 103 */
105 @Override 104 @Override
106 void close(); 105 void close();
107
108} 106}
diff --git a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleError.java b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleError.java
index 20f847a..ce6b23f 100644
--- a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleError.java
+++ b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleError.java
@@ -6,7 +6,6 @@ import java.io.PrintStream;
6import javax.annotation.Nullable; 6import javax.annotation.Nullable;
7 7
8public final class ClassHandleError { 8public final class ClassHandleError {
9
10 public final Type type; 9 public final Type type;
11 public final Throwable cause; 10 public final Throwable cause;
12 11
@@ -17,7 +16,10 @@ public final class ClassHandleError {
17 16
18 @Nullable 17 @Nullable
19 public String getStackTrace() { 18 public String getStackTrace() {
20 if (cause == null) return null; 19 if (cause == null) {
20 return null;
21 }
22
21 ByteArrayOutputStream os = new ByteArrayOutputStream(); 23 ByteArrayOutputStream os = new ByteArrayOutputStream();
22 PrintStream ps = new PrintStream(os); 24 PrintStream ps = new PrintStream(os);
23 cause.printStackTrace(ps); 25 cause.printStackTrace(ps);
@@ -36,5 +38,4 @@ public final class ClassHandleError {
36 DECOMPILE, 38 DECOMPILE,
37 REMAP, 39 REMAP,
38 } 40 }
39 41}
40} \ No newline at end of file
diff --git a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java
index 229d18a..f18be67 100644
--- a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java
+++ b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java
@@ -1,6 +1,15 @@
1package cuchaz.enigma.classhandle; 1package cuchaz.enigma.classhandle;
2 2
3import java.util.*; 3import static cuchaz.enigma.utils.Utils.withLock;
4
5import java.util.ArrayList;
6import java.util.Collections;
7import java.util.HashMap;
8import java.util.HashSet;
9import java.util.List;
10import java.util.Map;
11import java.util.Objects;
12import java.util.Set;
4import java.util.concurrent.CompletableFuture; 13import java.util.concurrent.CompletableFuture;
5import java.util.concurrent.ExecutorService; 14import java.util.concurrent.ExecutorService;
6import java.util.concurrent.Executors; 15import java.util.concurrent.Executors;
@@ -16,14 +25,16 @@ import cuchaz.enigma.classprovider.CachingClassProvider;
16import cuchaz.enigma.classprovider.ObfuscationFixClassProvider; 25import cuchaz.enigma.classprovider.ObfuscationFixClassProvider;
17import cuchaz.enigma.events.ClassHandleListener; 26import cuchaz.enigma.events.ClassHandleListener;
18import cuchaz.enigma.events.ClassHandleListener.InvalidationType; 27import cuchaz.enigma.events.ClassHandleListener.InvalidationType;
19import cuchaz.enigma.source.*; 28import cuchaz.enigma.source.DecompiledClassSource;
29import cuchaz.enigma.source.Decompiler;
30import cuchaz.enigma.source.DecompilerService;
31import cuchaz.enigma.source.Source;
32import cuchaz.enigma.source.SourceIndex;
33import cuchaz.enigma.source.SourceSettings;
20import cuchaz.enigma.translation.representation.entry.ClassEntry; 34import cuchaz.enigma.translation.representation.entry.ClassEntry;
21import cuchaz.enigma.utils.Result; 35import cuchaz.enigma.utils.Result;
22 36
23import static cuchaz.enigma.utils.Utils.withLock;
24
25public final class ClassHandleProvider { 37public final class ClassHandleProvider {
26
27 private final EnigmaProject project; 38 private final EnigmaProject project;
28 39
29 private final ExecutorService pool = Executors.newWorkStealingPool(); 40 private final ExecutorService pool = Executors.newWorkStealingPool();
@@ -50,7 +61,9 @@ public final class ClassHandleProvider {
50 */ 61 */
51 @Nullable 62 @Nullable
52 public ClassHandle openClass(ClassEntry entry) { 63 public ClassHandle openClass(ClassEntry entry) {
53 if (!project.getJarIndex().getEntryIndex().hasClass(entry)) return null; 64 if (!project.getJarIndex().getEntryIndex().hasClass(entry)) {
65 return null;
66 }
54 67
55 return withLock(lock.writeLock(), () -> { 68 return withLock(lock.writeLock(), () -> {
56 Entry e = handles.computeIfAbsent(entry, entry1 -> new Entry(this, entry1)); 69 Entry e = handles.computeIfAbsent(entry, entry1 -> new Entry(this, entry1));
@@ -68,7 +81,9 @@ public final class ClassHandleProvider {
68 * @param ds the decompiler service to use 81 * @param ds the decompiler service to use
69 */ 82 */
70 public void setDecompilerService(DecompilerService ds) { 83 public void setDecompilerService(DecompilerService ds) {
71 if (this.ds.equals(ds)) return; 84 if (this.ds.equals(ds)) {
85 return;
86 }
72 87
73 this.ds = ds; 88 this.ds = ds;
74 this.decompiler = createDecompiler(); 89 this.decompiler = createDecompiler();
@@ -111,6 +126,7 @@ public final class ClassHandleProvider {
111 public void invalidateMapped(ClassEntry entry) { 126 public void invalidateMapped(ClassEntry entry) {
112 withLock(lock.readLock(), () -> { 127 withLock(lock.readLock(), () -> {
113 Entry e = handles.get(entry); 128 Entry e = handles.get(entry);
129
114 if (e != null) { 130 if (e != null) {
115 e.invalidateMapped(); 131 e.invalidateMapped();
116 } 132 }
@@ -136,6 +152,7 @@ public final class ClassHandleProvider {
136 public void invalidateJavadoc(ClassEntry entry) { 152 public void invalidateJavadoc(ClassEntry entry) {
137 withLock(lock.readLock(), () -> { 153 withLock(lock.readLock(), () -> {
138 Entry e = handles.get(entry); 154 Entry e = handles.get(entry);
155
139 if (e != null) { 156 if (e != null) {
140 e.invalidateJavadoc(); 157 e.invalidateJavadoc();
141 } 158 }
@@ -163,6 +180,7 @@ public final class ClassHandleProvider {
163 */ 180 */
164 public void destroy() { 181 public void destroy() {
165 pool.shutdown(); 182 pool.shutdown();
183
166 try { 184 try {
167 pool.awaitTermination(30, TimeUnit.SECONDS); 185 pool.awaitTermination(30, TimeUnit.SECONDS);
168 } catch (InterruptedException e) { 186 } catch (InterruptedException e) {
@@ -176,7 +194,6 @@ public final class ClassHandleProvider {
176 } 194 }
177 195
178 private static final class Entry { 196 private static final class Entry {
179
180 private final ClassHandleProvider p; 197 private final ClassHandleProvider p;
181 private final ClassEntry entry; 198 private final ClassEntry entry;
182 private ClassEntry deobfRef; 199 private ClassEntry deobfRef;
@@ -216,6 +233,7 @@ public final class ClassHandleProvider {
216 233
217 private void checkDeobfRefForUpdate() { 234 private void checkDeobfRefForUpdate() {
218 ClassEntry newDeobf = p.project.getMapper().deobfuscate(entry); 235 ClassEntry newDeobf = p.project.getMapper().deobfuscate(entry);
236
219 if (!Objects.equals(deobfRef, newDeobf)) { 237 if (!Objects.equals(deobfRef, newDeobf)) {
220 deobfRef = newDeobf; 238 deobfRef = newDeobf;
221 // copy the list so we don't call event listener code with the lock active 239 // copy the list so we don't call event listener code with the lock active
@@ -244,7 +262,9 @@ public final class ClassHandleProvider {
244 private CompletableFuture<Result<Source, ClassHandleError>> decompile() { 262 private CompletableFuture<Result<Source, ClassHandleError>> decompile() {
245 int v = decompileVersion.incrementAndGet(); 263 int v = decompileVersion.incrementAndGet();
246 return CompletableFuture.supplyAsync(() -> { 264 return CompletableFuture.supplyAsync(() -> {
247 if (decompileVersion.get() != v) return null; 265 if (decompileVersion.get() != v) {
266 return null;
267 }
248 268
249 Result<Source, ClassHandleError> uncommentedSource = Result.ok(p.decompiler.getSource(entry.getFullName())); 269 Result<Source, ClassHandleError> uncommentedSource = Result.ok(p.decompiler.getSource(entry.getFullName()));
250 Entry.this.uncommentedSource = uncommentedSource; 270 Entry.this.uncommentedSource = uncommentedSource;
@@ -258,7 +278,10 @@ public final class ClassHandleProvider {
258 private CompletableFuture<Result<Source, ClassHandleError>> continueInsertJavadoc(CompletableFuture<Result<Source, ClassHandleError>> f) { 278 private CompletableFuture<Result<Source, ClassHandleError>> continueInsertJavadoc(CompletableFuture<Result<Source, ClassHandleError>> f) {
259 int v = javadocVersion.incrementAndGet(); 279 int v = javadocVersion.incrementAndGet();
260 return f.thenApplyAsync(res -> { 280 return f.thenApplyAsync(res -> {
261 if (res == null || javadocVersion.get() != v) return null; 281 if (res == null || javadocVersion.get() != v) {
282 return null;
283 }
284
262 Result<Source, ClassHandleError> jdSource = res.map(s -> s.withJavadocs(p.project.getMapper())); 285 Result<Source, ClassHandleError> jdSource = res.map(s -> s.withJavadocs(p.project.getMapper()));
263 withLock(lock.readLock(), () -> new ArrayList<>(handles)).forEach(h -> h.onDocsChanged(jdSource)); 286 withLock(lock.readLock(), () -> new ArrayList<>(handles)).forEach(h -> h.onDocsChanged(jdSource));
264 return jdSource; 287 return jdSource;
@@ -268,7 +291,10 @@ public final class ClassHandleProvider {
268 private CompletableFuture<Result<DecompiledClassSource, ClassHandleError>> continueIndexSource(CompletableFuture<Result<Source, ClassHandleError>> f) { 291 private CompletableFuture<Result<DecompiledClassSource, ClassHandleError>> continueIndexSource(CompletableFuture<Result<Source, ClassHandleError>> f) {
269 int v = indexVersion.incrementAndGet(); 292 int v = indexVersion.incrementAndGet();
270 return f.thenApplyAsync(res -> { 293 return f.thenApplyAsync(res -> {
271 if (res == null || indexVersion.get() != v) return null; 294 if (res == null || indexVersion.get() != v) {
295 return null;
296 }
297
272 return res.andThen(jdSource -> { 298 return res.andThen(jdSource -> {
273 SourceIndex index = jdSource.index(); 299 SourceIndex index = jdSource.index();
274 index.resolveReferences(p.project.getMapper().getObfResolver()); 300 index.resolveReferences(p.project.getMapper().getObfResolver());
@@ -281,11 +307,20 @@ public final class ClassHandleProvider {
281 private void continueMapSource(CompletableFuture<Result<DecompiledClassSource, ClassHandleError>> f) { 307 private void continueMapSource(CompletableFuture<Result<DecompiledClassSource, ClassHandleError>> f) {
282 int v = mappedVersion.incrementAndGet(); 308 int v = mappedVersion.incrementAndGet();
283 f.thenApplyAsync(res -> { 309 f.thenApplyAsync(res -> {
284 if (res == null || mappedVersion.get() != v) return null; 310 if (res == null || mappedVersion.get() != v) {
311 return null;
312 }
313
285 return res.andThen(source -> Result.ok(source.remapSource(p.project, p.project.getMapper().getDeobfuscator()))); 314 return res.andThen(source -> Result.ok(source.remapSource(p.project, p.project.getMapper().getDeobfuscator())));
286 }, p.pool).whenComplete((res, e) -> { 315 }, p.pool).whenComplete((res, e) -> {
287 if (e != null) res = Result.err(ClassHandleError.remap(e)); 316 if (e != null) {
288 if (res == null) return; 317 res = Result.err(ClassHandleError.remap(e));
318 }
319
320 if (res == null) {
321 return;
322 }
323
289 Entry.this.source = res; 324 Entry.this.source = res;
290 Entry.this.waitingSources.forEach(s -> s.complete(source)); 325 Entry.this.waitingSources.forEach(s -> s.complete(source));
291 Entry.this.waitingSources.clear(); 326 Entry.this.waitingSources.clear();
@@ -297,6 +332,7 @@ public final class ClassHandleProvider {
297 classHandle.destroy(); 332 classHandle.destroy();
298 withLock(lock.writeLock(), () -> { 333 withLock(lock.writeLock(), () -> {
299 handles.remove(classHandle); 334 handles.remove(classHandle);
335
300 if (handles.isEmpty()) { 336 if (handles.isEmpty()) {
301 p.deleteEntry(this); 337 p.deleteEntry(this);
302 } 338 }
@@ -332,7 +368,6 @@ public final class ClassHandleProvider {
332 } 368 }
333 369
334 private static final class ClassHandleImpl implements ClassHandle { 370 private static final class ClassHandleImpl implements ClassHandle {
335
336 private final Entry entry; 371 private final Entry entry;
337 372
338 private boolean valid = true; 373 private boolean valid = true;
@@ -425,18 +460,20 @@ public final class ClassHandleProvider {
425 460
426 @Override 461 @Override
427 public void close() { 462 public void close() {
428 if (valid) entry.closeHandle(this); 463 if (valid) {
464 entry.closeHandle(this);
465 }
429 } 466 }
430 467
431 private void checkValid() { 468 private void checkValid() {
432 if (!valid) throw new IllegalStateException("Class handle no longer valid"); 469 if (!valid) {
470 throw new IllegalStateException("Class handle no longer valid");
471 }
433 } 472 }
434 473
435 public void destroy() { 474 public void destroy() {
436 listeners.forEach(l -> l.onDeleted(this)); 475 listeners.forEach(l -> l.onDeleted(this));
437 valid = false; 476 valid = false;
438 } 477 }
439
440 } 478 }
441
442} 479}
diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/CachingClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/CachingClassProvider.java
index 47f5eb8..eaba6df 100644
--- a/enigma/src/main/java/cuchaz/enigma/classprovider/CachingClassProvider.java
+++ b/enigma/src/main/java/cuchaz/enigma/classprovider/CachingClassProvider.java
@@ -1,36 +1,33 @@
1package cuchaz.enigma.classprovider; 1package cuchaz.enigma.classprovider;
2 2
3import com.google.common.cache.Cache;
4import com.google.common.cache.CacheBuilder;
5import org.objectweb.asm.tree.ClassNode;
6
7import javax.annotation.Nullable;
8import java.util.Optional; 3import java.util.Optional;
9import java.util.concurrent.ExecutionException; 4import java.util.concurrent.ExecutionException;
10import java.util.concurrent.TimeUnit; 5import java.util.concurrent.TimeUnit;
11 6
7import javax.annotation.Nullable;
8
9import com.google.common.cache.Cache;
10import com.google.common.cache.CacheBuilder;
11import org.objectweb.asm.tree.ClassNode;
12
12/** 13/**
13 * Wraps a ClassProvider to provide caching and synchronization. 14 * Wraps a ClassProvider to provide caching and synchronization.
14 */ 15 */
15public class CachingClassProvider implements ClassProvider { 16public class CachingClassProvider implements ClassProvider {
16 private final ClassProvider classProvider; 17 private final ClassProvider classProvider;
17 private final Cache<String, Optional<ClassNode>> cache = CacheBuilder.newBuilder() 18 private final Cache<String, Optional<ClassNode>> cache = CacheBuilder.newBuilder().maximumSize(128).expireAfterAccess(1, TimeUnit.MINUTES).concurrencyLevel(1).build();
18 .maximumSize(128)
19 .expireAfterAccess(1, TimeUnit.MINUTES)
20 .concurrencyLevel(1)
21 .build();
22 19
23 public CachingClassProvider(ClassProvider classProvider) { 20 public CachingClassProvider(ClassProvider classProvider) {
24 this.classProvider = classProvider; 21 this.classProvider = classProvider;
25 } 22 }
26 23
27 @Override 24 @Override
28 @Nullable 25 @Nullable
29 public ClassNode get(String name) { 26 public ClassNode get(String name) {
30 try { 27 try {
31 return cache.get(name, () -> Optional.ofNullable(classProvider.get(name))).orElse(null); 28 return cache.get(name, () -> Optional.ofNullable(classProvider.get(name))).orElse(null);
32 } catch (ExecutionException e) { 29 } catch (ExecutionException e) {
33 throw new RuntimeException(e); 30 throw new RuntimeException(e);
34 } 31 }
35 } 32 }
36} 33}
diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/ClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/ClassProvider.java
index 6e4a665..6eec0f3 100644
--- a/enigma/src/main/java/cuchaz/enigma/classprovider/ClassProvider.java
+++ b/enigma/src/main/java/cuchaz/enigma/classprovider/ClassProvider.java
@@ -1,17 +1,17 @@
1package cuchaz.enigma.classprovider; 1package cuchaz.enigma.classprovider;
2 2
3import org.objectweb.asm.tree.ClassNode;
4
5import javax.annotation.Nullable; 3import javax.annotation.Nullable;
6 4
5import org.objectweb.asm.tree.ClassNode;
6
7public interface ClassProvider { 7public interface ClassProvider {
8 /** 8 /**
9 * Gets the {@linkplain ClassNode} for a class. The class provider may return a cached result, 9 * Gets the {@linkplain ClassNode} for a class. The class provider may return a cached result,
10 * so it's important to not mutate it. 10 * so it's important to not mutate it.
11 * 11 *
12 * @param name the internal name of the class 12 * @param name the internal name of the class
13 * @return the {@linkplain ClassNode} for that class, or {@code null} if it was not found 13 * @return the {@linkplain ClassNode} for that class, or {@code null} if it was not found
14 */ 14 */
15 @Nullable 15 @Nullable
16 ClassNode get(String name); 16 ClassNode get(String name);
17} 17}
diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/ClasspathClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/ClasspathClassProvider.java
index e9472fa..224093f 100644
--- a/enigma/src/main/java/cuchaz/enigma/classprovider/ClasspathClassProvider.java
+++ b/enigma/src/main/java/cuchaz/enigma/classprovider/ClasspathClassProvider.java
@@ -1,27 +1,30 @@
1package cuchaz.enigma.classprovider; 1package cuchaz.enigma.classprovider;
2 2
3import org.objectweb.asm.ClassReader;
4import org.objectweb.asm.tree.ClassNode;
5
6import javax.annotation.Nullable;
7import java.io.IOException; 3import java.io.IOException;
8import java.io.InputStream; 4import java.io.InputStream;
9 5
6import javax.annotation.Nullable;
7
8import org.objectweb.asm.ClassReader;
9import org.objectweb.asm.tree.ClassNode;
10
10/** 11/**
11 * Provides classes by loading them from the classpath. 12 * Provides classes by loading them from the classpath.
12 */ 13 */
13public class ClasspathClassProvider implements ClassProvider { 14public class ClasspathClassProvider implements ClassProvider {
14 @Nullable @Override public ClassNode get(String name) { 15 @Nullable
15 try (InputStream in = ClasspathClassProvider.class.getResourceAsStream("/" + name + ".class")) { 16 @Override
16 if (in == null) { 17 public ClassNode get(String name) {
17 return null; 18 try (InputStream in = ClasspathClassProvider.class.getResourceAsStream("/" + name + ".class")) {
18 } 19 if (in == null) {
20 return null;
21 }
19 22
20 ClassNode node = new ClassNode(); 23 ClassNode node = new ClassNode();
21 new ClassReader(in).accept(node, 0); 24 new ClassReader(in).accept(node, 0);
22 return node; 25 return node;
23 } catch (IOException e) { 26 } catch (IOException e) {
24 return null; 27 return null;
25 } 28 }
26 } 29 }
27} 30}
diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/CombiningClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/CombiningClassProvider.java
index 865464c..6856540 100644
--- a/enigma/src/main/java/cuchaz/enigma/classprovider/CombiningClassProvider.java
+++ b/enigma/src/main/java/cuchaz/enigma/classprovider/CombiningClassProvider.java
@@ -1,31 +1,31 @@
1package cuchaz.enigma.classprovider; 1package cuchaz.enigma.classprovider;
2 2
3import org.objectweb.asm.tree.ClassNode;
4
5import javax.annotation.Nullable; 3import javax.annotation.Nullable;
6 4
5import org.objectweb.asm.tree.ClassNode;
6
7/** 7/**
8 * Combines a list of {@link ClassProvider}s into one, calling each one in a row 8 * Combines a list of {@link ClassProvider}s into one, calling each one in a row
9 * until one can provide the class. 9 * until one can provide the class.
10 */ 10 */
11public class CombiningClassProvider implements ClassProvider { 11public class CombiningClassProvider implements ClassProvider {
12 private final ClassProvider[] classProviders; 12 private final ClassProvider[] classProviders;
13 13
14 public CombiningClassProvider(ClassProvider... classProviders) { 14 public CombiningClassProvider(ClassProvider... classProviders) {
15 this.classProviders = classProviders; 15 this.classProviders = classProviders;
16 } 16 }
17 17
18 @Override 18 @Override
19 @Nullable 19 @Nullable
20 public ClassNode get(String name) { 20 public ClassNode get(String name) {
21 for (ClassProvider cp : classProviders) { 21 for (ClassProvider cp : classProviders) {
22 ClassNode node = cp.get(name); 22 ClassNode node = cp.get(name);
23 23
24 if (node != null) { 24 if (node != null) {
25 return node; 25 return node;
26 } 26 }
27 } 27 }
28 28
29 return null; 29 return null;
30 } 30 }
31} 31}
diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/JarClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/JarClassProvider.java
index c614b0a..900a0c8 100644
--- a/enigma/src/main/java/cuchaz/enigma/classprovider/JarClassProvider.java
+++ b/enigma/src/main/java/cuchaz/enigma/classprovider/JarClassProvider.java
@@ -1,10 +1,5 @@
1package cuchaz.enigma.classprovider; 1package cuchaz.enigma.classprovider;
2 2
3import com.google.common.collect.ImmutableSet;
4import cuchaz.enigma.utils.AsmUtil;
5import org.objectweb.asm.tree.ClassNode;
6
7import javax.annotation.Nullable;
8import java.io.IOException; 3import java.io.IOException;
9import java.nio.file.FileSystem; 4import java.nio.file.FileSystem;
10import java.nio.file.FileSystems; 5import java.nio.file.FileSystems;
@@ -12,53 +7,60 @@ import java.nio.file.Files;
12import java.nio.file.Path; 7import java.nio.file.Path;
13import java.util.Set; 8import java.util.Set;
14 9
10import javax.annotation.Nullable;
11
12import com.google.common.collect.ImmutableSet;
13import org.objectweb.asm.tree.ClassNode;
14
15import cuchaz.enigma.utils.AsmUtil;
16
15/** 17/**
16 * Provides classes by loading them from a JAR file. 18 * Provides classes by loading them from a JAR file.
17 */ 19 */
18public class JarClassProvider implements AutoCloseable, ClassProvider { 20public class JarClassProvider implements AutoCloseable, ClassProvider {
19 private final FileSystem fileSystem; 21 private final FileSystem fileSystem;
20 private final Set<String> classNames; 22 private final Set<String> classNames;
23
24 public JarClassProvider(Path jarPath) throws IOException {
25 this.fileSystem = FileSystems.newFileSystem(jarPath, (ClassLoader) null);
26 this.classNames = collectClassNames(fileSystem);
27 }
21 28
22 public JarClassProvider(Path jarPath) throws IOException { 29 private static ImmutableSet<String> collectClassNames(FileSystem fileSystem) throws IOException {
23 this.fileSystem = FileSystems.newFileSystem(jarPath, (ClassLoader) null); 30 ImmutableSet.Builder<String> classNames = ImmutableSet.builder();
24 this.classNames = collectClassNames(fileSystem);
25 }
26 31
27 private static ImmutableSet<String> collectClassNames(FileSystem fileSystem) throws IOException { 32 for (Path root : fileSystem.getRootDirectories()) {
28 ImmutableSet.Builder<String> classNames = ImmutableSet.builder(); 33 Files.walk(root).map(Path::toString).forEach(path -> {
29 for (Path root : fileSystem.getRootDirectories()) { 34 if (path.endsWith(".class")) {
30 Files.walk(root).map(Path::toString) 35 String name = path.substring(1, path.length() - ".class".length());
31 .forEach(path -> { 36 classNames.add(name);
32 if (path.endsWith(".class")) { 37 }
33 String name = path.substring(1, path.length() - ".class".length()); 38 });
34 classNames.add(name); 39 }
35 }
36 });
37 }
38 40
39 return classNames.build(); 41 return classNames.build();
40 } 42 }
41 43
42 public Set<String> getClassNames() { 44 public Set<String> getClassNames() {
43 return classNames; 45 return classNames;
44 } 46 }
45 47
46 @Nullable 48 @Nullable
47 @Override 49 @Override
48 public ClassNode get(String name) { 50 public ClassNode get(String name) {
49 if (!classNames.contains(name)) { 51 if (!classNames.contains(name)) {
50 return null; 52 return null;
51 } 53 }
52 54
53 try { 55 try {
54 return AsmUtil.bytesToNode(Files.readAllBytes(fileSystem.getPath(name + ".class"))); 56 return AsmUtil.bytesToNode(Files.readAllBytes(fileSystem.getPath(name + ".class")));
55 } catch (IOException e) { 57 } catch (IOException e) {
56 throw new RuntimeException(e); 58 throw new RuntimeException(e);
57 } 59 }
58 } 60 }
59 61
60 @Override 62 @Override
61 public void close() throws Exception { 63 public void close() throws Exception {
62 fileSystem.close(); 64 fileSystem.close();
63 } 65 }
64} 66}
diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/ObfuscationFixClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/ObfuscationFixClassProvider.java
index 36236a8..604bf49 100644
--- a/enigma/src/main/java/cuchaz/enigma/classprovider/ObfuscationFixClassProvider.java
+++ b/enigma/src/main/java/cuchaz/enigma/classprovider/ObfuscationFixClassProvider.java
@@ -1,10 +1,7 @@
1package cuchaz.enigma.classprovider; 1package cuchaz.enigma.classprovider;
2 2
3import cuchaz.enigma.Enigma; 3import javax.annotation.Nullable;
4import cuchaz.enigma.analysis.index.JarIndex; 4
5import cuchaz.enigma.bytecode.translators.LocalVariableFixVisitor;
6import cuchaz.enigma.bytecode.translators.SourceFixVisitor;
7import cuchaz.enigma.classprovider.ClassProvider;
8import org.objectweb.asm.ClassVisitor; 5import org.objectweb.asm.ClassVisitor;
9import org.objectweb.asm.Opcodes; 6import org.objectweb.asm.Opcodes;
10import org.objectweb.asm.tree.AbstractInsnNode; 7import org.objectweb.asm.tree.AbstractInsnNode;
@@ -12,7 +9,10 @@ import org.objectweb.asm.tree.ClassNode;
12import org.objectweb.asm.tree.MethodInsnNode; 9import org.objectweb.asm.tree.MethodInsnNode;
13import org.objectweb.asm.tree.MethodNode; 10import org.objectweb.asm.tree.MethodNode;
14 11
15import javax.annotation.Nullable; 12import cuchaz.enigma.Enigma;
13import cuchaz.enigma.analysis.index.JarIndex;
14import cuchaz.enigma.bytecode.translators.LocalVariableFixVisitor;
15import cuchaz.enigma.bytecode.translators.SourceFixVisitor;
16 16
17/** 17/**
18 * Wraps a ClassProvider to apply fixes to the following problems introduced by the obfuscator, 18 * Wraps a ClassProvider to apply fixes to the following problems introduced by the obfuscator,
@@ -26,59 +26,63 @@ import javax.annotation.Nullable;
26 * <li>Enum constructor parameters that are incorrectly named or missing the "synthetic" access modifier 26 * <li>Enum constructor parameters that are incorrectly named or missing the "synthetic" access modifier
27 * <li>"this" parameter which is incorrectly named 27 * <li>"this" parameter which is incorrectly named
28 * </ul> 28 * </ul>
29 * <p> 29 *
30 * These fixes are only applied to classes that were indexed by the JarIndex provided, and not library classes. 30 * <p>These fixes are only applied to classes that were indexed by the JarIndex provided, and not library classes.
31 */ 31 */
32public class ObfuscationFixClassProvider implements ClassProvider { 32public class ObfuscationFixClassProvider implements ClassProvider {
33 private final ClassProvider classProvider; 33 private final ClassProvider classProvider;
34 private final JarIndex jarIndex; 34 private final JarIndex jarIndex;
35
36 public ObfuscationFixClassProvider(ClassProvider classProvider, JarIndex jarIndex) {
37 this.classProvider = classProvider;
38 this.jarIndex = jarIndex;
39 }
40
41 @Override
42 @Nullable
43 public ClassNode get(String name) {
44 ClassNode node = classProvider.get(name);
45
46 if (!jarIndex.isIndexed(name)) {
47 return node;
48 }
35 49
36 public ObfuscationFixClassProvider(ClassProvider classProvider, JarIndex jarIndex) { 50 ClassNode fixedNode = new ClassNode();
37 this.classProvider = classProvider; 51 ClassVisitor visitor = fixedNode;
38 this.jarIndex = jarIndex; 52 visitor = new LocalVariableFixVisitor(Enigma.ASM_VERSION, visitor);
39 } 53 visitor = new SourceFixVisitor(Enigma.ASM_VERSION, visitor, jarIndex);
54 node.accept(visitor);
55 removeRedundantClassCalls(fixedNode);
40 56
41 @Override 57 return fixedNode;
42 @Nullable 58 }
43 public ClassNode get(String name) {
44 ClassNode node = classProvider.get(name);
45 59
46 if (!jarIndex.isIndexed(name)) { 60 private void removeRedundantClassCalls(ClassNode node) {
47 return node; 61 // Removes .getClass() calls added by Proguard:
48 } 62 // DUP
63 // INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
64 // POP
65 for (MethodNode methodNode : node.methods) {
66 AbstractInsnNode insnNode = methodNode.instructions.getFirst();
49 67
50 ClassNode fixedNode = new ClassNode(); 68 while (insnNode != null) {
51 ClassVisitor visitor = fixedNode; 69 if (insnNode instanceof MethodInsnNode methodInsnNode && insnNode.getOpcode() == Opcodes.INVOKEVIRTUAL) {
52 visitor = new LocalVariableFixVisitor(Enigma.ASM_VERSION, visitor); 70 if (methodInsnNode.name.equals("getClass") && methodInsnNode.owner.equals("java/lang/Object") && methodInsnNode.desc.equals("()Ljava/lang/Class;")) {
53 visitor = new SourceFixVisitor(Enigma.ASM_VERSION, visitor, jarIndex); 71 AbstractInsnNode previous = methodInsnNode.getPrevious();
54 node.accept(visitor); 72 AbstractInsnNode next = methodInsnNode.getNext();
55 removeRedundantClassCalls(fixedNode);
56 73
57 return fixedNode; 74 if (previous.getOpcode() == Opcodes.DUP && next.getOpcode() == Opcodes.POP) {
58 } 75 //reset the iterator so it gets the new next instruction
76 insnNode = previous.getPrevious();
77 methodNode.instructions.remove(previous);
78 methodNode.instructions.remove(methodInsnNode);
79 methodNode.instructions.remove(next);
80 }
81 }
82 }
59 83
60 private void removeRedundantClassCalls(ClassNode node) { 84 insnNode = insnNode.getNext();
61 // Removes .getClass() calls added by Proguard: 85 }
62 // DUP 86 }
63 // INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class; 87 }
64 // POP
65 for (MethodNode methodNode : node.methods) {
66 AbstractInsnNode insnNode = methodNode.instructions.getFirst();
67 while (insnNode != null) {
68 if (insnNode instanceof MethodInsnNode methodInsnNode && insnNode.getOpcode() == Opcodes.INVOKEVIRTUAL) {
69 if (methodInsnNode.name.equals("getClass") && methodInsnNode.owner.equals("java/lang/Object") && methodInsnNode.desc.equals("()Ljava/lang/Class;")) {
70 AbstractInsnNode previous = methodInsnNode.getPrevious();
71 AbstractInsnNode next = methodInsnNode.getNext();
72 if (previous.getOpcode() == Opcodes.DUP && next.getOpcode() == Opcodes.POP) {
73 insnNode = previous.getPrevious();//reset the iterator so it gets the new next instruction
74 methodNode.instructions.remove(previous);
75 methodNode.instructions.remove(methodInsnNode);
76 methodNode.instructions.remove(next);
77 }
78 }
79 }
80 insnNode = insnNode.getNext();
81 }
82 }
83 }
84} 88}
diff --git a/enigma/src/main/java/cuchaz/enigma/config/ConfigContainer.java b/enigma/src/main/java/cuchaz/enigma/config/ConfigContainer.java
index cb9cbc2..fd078a2 100644
--- a/enigma/src/main/java/cuchaz/enigma/config/ConfigContainer.java
+++ b/enigma/src/main/java/cuchaz/enigma/config/ConfigContainer.java
@@ -8,7 +8,6 @@ import java.util.Deque;
8import java.util.LinkedList; 8import java.util.LinkedList;
9 9
10public class ConfigContainer { 10public class ConfigContainer {
11
12 private Path configPath; 11 private Path configPath;
13 private boolean existsOnDisk; 12 private boolean existsOnDisk;
14 13
@@ -19,7 +18,10 @@ public class ConfigContainer {
19 } 18 }
20 19
21 public void save() { 20 public void save() {
22 if (this.configPath == null) throw new IllegalStateException("File has no config path set!"); 21 if (this.configPath == null) {
22 throw new IllegalStateException("File has no config path set!");
23 }
24
23 try { 25 try {
24 Files.createDirectories(this.configPath.getParent()); 26 Files.createDirectories(this.configPath.getParent());
25 Files.write(this.configPath, this.serialize().getBytes(StandardCharsets.UTF_8)); 27 Files.write(this.configPath, this.serialize().getBytes(StandardCharsets.UTF_8));
@@ -52,6 +54,7 @@ public class ConfigContainer {
52 54
53 public static ConfigContainer getOrCreate(Path path) { 55 public static ConfigContainer getOrCreate(Path path) {
54 ConfigContainer cc = null; 56 ConfigContainer cc = null;
57
55 try { 58 try {
56 if (Files.exists(path)) { 59 if (Files.exists(path)) {
57 String s = String.join("\n", Files.readAllLines(path)); 60 String s = String.join("\n", Files.readAllLines(path));
@@ -93,5 +96,4 @@ public class ConfigContainer {
93 }); 96 });
94 return cc; 97 return cc;
95 } 98 }
96
97} 99}
diff --git a/enigma/src/main/java/cuchaz/enigma/config/ConfigPaths.java b/enigma/src/main/java/cuchaz/enigma/config/ConfigPaths.java
index b3f3d0c..6d9d304 100644
--- a/enigma/src/main/java/cuchaz/enigma/config/ConfigPaths.java
+++ b/enigma/src/main/java/cuchaz/enigma/config/ConfigPaths.java
@@ -6,7 +6,6 @@ import java.nio.file.Paths;
6import cuchaz.enigma.utils.Os; 6import cuchaz.enigma.utils.Os;
7 7
8public class ConfigPaths { 8public class ConfigPaths {
9
10 public static Path getConfigFilePath(String name) { 9 public static Path getConfigFilePath(String name) {
11 String fileName = Os.getOs() == Os.LINUX ? String.format("%src", name) : String.format("%s.ini", name); 10 String fileName = Os.getOs() == Os.LINUX ? String.format("%src", name) : String.format("%s.ini", name);
12 return getConfigPathRoot().resolve(fileName); 11 return getConfigPathRoot().resolve(fileName);
@@ -14,27 +13,30 @@ public class ConfigPaths {
14 13
15 public static Path getConfigPathRoot() { 14 public static Path getConfigPathRoot() {
16 switch (Os.getOs()) { 15 switch (Os.getOs()) {
17 case LINUX: 16 case LINUX:
18 String configHome = System.getenv("XDG_CONFIG_HOME"); 17 String configHome = System.getenv("XDG_CONFIG_HOME");
19 if (configHome == null) { 18
20 return getUserHomeUnix().resolve(".config"); 19 if (configHome == null) {
21 } 20 return getUserHomeUnix().resolve(".config");
22 return Paths.get(configHome); 21 }
23 case MAC: 22
24 return getUserHomeUnix().resolve("Library").resolve("Application Support"); 23 return Paths.get(configHome);
25 case WINDOWS: 24 case MAC:
26 return Paths.get(System.getenv("LOCALAPPDATA")); 25 return getUserHomeUnix().resolve("Library").resolve("Application Support");
27 default: 26 case WINDOWS:
28 return Paths.get(System.getProperty("user.dir")); 27 return Paths.get(System.getenv("LOCALAPPDATA"));
28 default:
29 return Paths.get(System.getProperty("user.dir"));
29 } 30 }
30 } 31 }
31 32
32 private static Path getUserHomeUnix() { 33 private static Path getUserHomeUnix() {
33 String userHome = System.getenv("HOME"); 34 String userHome = System.getenv("HOME");
35
34 if (userHome == null) { 36 if (userHome == null) {
35 userHome = System.getProperty("user.dir"); 37 userHome = System.getProperty("user.dir");
36 } 38 }
39
37 return Paths.get(userHome); 40 return Paths.get(userHome);
38 } 41 }
39
40} 42}
diff --git a/enigma/src/main/java/cuchaz/enigma/config/ConfigSection.java b/enigma/src/main/java/cuchaz/enigma/config/ConfigSection.java
index 3e7bf6d..fba7da3 100644
--- a/enigma/src/main/java/cuchaz/enigma/config/ConfigSection.java
+++ b/enigma/src/main/java/cuchaz/enigma/config/ConfigSection.java
@@ -1,10 +1,16 @@
1package cuchaz.enigma.config; 1package cuchaz.enigma.config;
2 2
3import java.util.*; 3import java.util.Arrays;
4import java.util.Collections;
5import java.util.HashMap;
6import java.util.Map;
7import java.util.Objects;
8import java.util.Optional;
9import java.util.OptionalDouble;
10import java.util.OptionalInt;
4import java.util.function.Function; 11import java.util.function.Function;
5 12
6public class ConfigSection { 13public class ConfigSection {
7
8 private final Map<String, String> values; 14 private final Map<String, String> values;
9 private final Map<String, ConfigSection> sections; 15 private final Map<String, ConfigSection> sections;
10 16
@@ -163,11 +169,16 @@ public class ConfigSection {
163 169
164 @Override 170 @Override
165 public boolean equals(Object o) { 171 public boolean equals(Object o) {
166 if (this == o) return true; 172 if (this == o) {
167 if (!(o instanceof ConfigSection)) return false; 173 return true;
174 }
175
176 if (!(o instanceof ConfigSection)) {
177 return false;
178 }
179
168 ConfigSection that = (ConfigSection) o; 180 ConfigSection that = (ConfigSection) o;
169 return values.equals(that.values) && 181 return values.equals(that.values) && sections.equals(that.sections);
170 sections.equals(that.sections);
171 } 182 }
172 183
173 @Override 184 @Override
@@ -179,5 +190,4 @@ public class ConfigSection {
179 public String toString() { 190 public String toString() {
180 return String.format("ConfigSection { values: %s, sections: %s }", values, sections); 191 return String.format("ConfigSection { values: %s, sections: %s }", values, sections);
181 } 192 }
182
183} 193}
diff --git a/enigma/src/main/java/cuchaz/enigma/config/ConfigSerializer.java b/enigma/src/main/java/cuchaz/enigma/config/ConfigSerializer.java
index dccb585..a1e3e55 100644
--- a/enigma/src/main/java/cuchaz/enigma/config/ConfigSerializer.java
+++ b/enigma/src/main/java/cuchaz/enigma/config/ConfigSerializer.java
@@ -1,13 +1,17 @@
1package cuchaz.enigma.config; 1package cuchaz.enigma.config;
2 2
3import java.util.*; 3import java.util.ArrayList;
4import java.util.Arrays;
5import java.util.List;
4import java.util.Map.Entry; 6import java.util.Map.Entry;
7import java.util.Optional;
8import java.util.OptionalDouble;
9import java.util.OptionalInt;
5import java.util.function.Function; 10import java.util.function.Function;
6import java.util.regex.Pattern; 11import java.util.regex.Pattern;
7import java.util.stream.Collectors; 12import java.util.stream.Collectors;
8 13
9public final class ConfigSerializer { 14public final class ConfigSerializer {
10
11 private static final Pattern FULL_RGB_COLOR = Pattern.compile("#[0-9A-Fa-f]{6}"); 15 private static final Pattern FULL_RGB_COLOR = Pattern.compile("#[0-9A-Fa-f]{6}");
12 private static final Pattern MIN_RGB_COLOR = Pattern.compile("#[0-9A-Fa-f]{3}"); 16 private static final Pattern MIN_RGB_COLOR = Pattern.compile("#[0-9A-Fa-f]{3}");
13 17
@@ -19,6 +23,7 @@ public final class ConfigSerializer {
19 23
20 // join escaped newlines 24 // join escaped newlines
21 int len = lines.length; 25 int len = lines.length;
26
22 for (int i = len - 2; i >= 0; i--) { 27 for (int i = len - 2; i >= 0; i--) {
23 if (lines[i].endsWith("\\")) { 28 if (lines[i].endsWith("\\")) {
24 lines[i] = String.format("%s\n%s", lines[i], lines[i + 1]); 29 lines[i] = String.format("%s\n%s", lines[i], lines[i + 1]);
@@ -31,24 +36,30 @@ public final class ConfigSerializer {
31 String line = lines[i]; 36 String line = lines[i];
32 37
33 // skip empty lines and comment lines 38 // skip empty lines and comment lines
34 if (line.trim().isEmpty() || line.trim().startsWith(";")) continue; 39 if (line.trim().isEmpty() || line.trim().startsWith(";")) {
40 continue;
41 }
35 42
36 int r; 43 int r;
37 boolean fail = (r = parseSectionLine(line, 0, visitor)) == NO_MATCH && 44 boolean fail = (r = parseSectionLine(line, 0, visitor)) == NO_MATCH && (r = parseKeyValue(line, 0, visitor)) == NO_MATCH;
38 (r = parseKeyValue(line, 0, visitor)) == NO_MATCH;
39 } 45 }
40 } 46 }
41 47
42 private static int parseSectionLine(String v, int idx, ConfigStructureVisitor visitor) { 48 private static int parseSectionLine(String v, int idx, ConfigStructureVisitor visitor) {
43 if (v.startsWith("[")) { 49 if (v.startsWith("[")) {
44 List<String> path = new ArrayList<>(); 50 List<String> path = new ArrayList<>();
51
45 while (idx < v.length() && v.charAt(idx) == '[') { 52 while (idx < v.length() && v.charAt(idx) == '[') {
46 idx = parseSection(v, idx, path); 53 idx = parseSection(v, idx, path);
47 if (idx == UNEXPECTED_TOKEN) return UNEXPECTED_TOKEN; 54
55 if (idx == UNEXPECTED_TOKEN) {
56 return UNEXPECTED_TOKEN;
57 }
48 } 58 }
49 59
50 if (!path.isEmpty()) { 60 if (!path.isEmpty()) {
51 visitor.jumpToRootSection(); 61 visitor.jumpToRootSection();
62
52 for (String s : path) { 63 for (String s : path) {
53 visitor.visitSection(s); 64 visitor.visitSection(s);
54 } 65 }
@@ -63,10 +74,12 @@ public final class ConfigSerializer {
63 private static int parseSection(String v, int idx, List<String> path) { 74 private static int parseSection(String v, int idx, List<String> path) {
64 idx += 1; // skip leading [ 75 idx += 1; // skip leading [
65 StringBuilder sb = new StringBuilder(); 76 StringBuilder sb = new StringBuilder();
77
66 while (idx < v.length()) { 78 while (idx < v.length()) {
67 int nextCloseBracket = v.indexOf(']', idx); 79 int nextCloseBracket = v.indexOf(']', idx);
68 int nextEscape = v.indexOf('\\', idx); 80 int nextEscape = v.indexOf('\\', idx);
69 int next = optMin(nextCloseBracket, nextEscape); 81 int next = optMin(nextCloseBracket, nextEscape);
82
70 if (next == -1) { 83 if (next == -1) {
71 // unexpected 84 // unexpected
72 return UNEXPECTED_TOKEN; 85 return UNEXPECTED_TOKEN;
@@ -79,16 +92,19 @@ public final class ConfigSerializer {
79 idx = parseEscape(v, nextEscape, sb); 92 idx = parseEscape(v, nextEscape, sb);
80 } 93 }
81 } 94 }
95
82 return idx; 96 return idx;
83 } 97 }
84 98
85 private static int parseKeyValue(String v, int idx, ConfigStructureVisitor visitor) { 99 private static int parseKeyValue(String v, int idx, ConfigStructureVisitor visitor) {
86 StringBuilder sb = new StringBuilder(); 100 StringBuilder sb = new StringBuilder();
87 String k = null; 101 String k = null;
102
88 while (idx < v.length()) { 103 while (idx < v.length()) {
89 int nextEq = v.indexOf('=', idx); 104 int nextEq = v.indexOf('=', idx);
90 int nextEscape = v.indexOf('\\', idx); 105 int nextEscape = v.indexOf('\\', idx);
91 int next = optMin(nextEq, nextEscape); 106 int next = optMin(nextEq, nextEscape);
107
92 if (next == -1) { 108 if (next == -1) {
93 break; 109 break;
94 } else if (next == nextEq) { 110 } else if (next == nextEq) {
@@ -102,8 +118,10 @@ public final class ConfigSerializer {
102 idx = parseEscape(v, nextEscape, sb); 118 idx = parseEscape(v, nextEscape, sb);
103 } 119 }
104 } 120 }
121
105 while (idx < v.length()) { 122 while (idx < v.length()) {
106 int nextEscape = v.indexOf('\\', idx); 123 int nextEscape = v.indexOf('\\', idx);
124
107 if (nextEscape != -1) { 125 if (nextEscape != -1) {
108 sb.append(v, idx, nextEscape); 126 sb.append(v, idx, nextEscape);
109 idx = parseEscape(v, nextEscape, sb); 127 idx = parseEscape(v, nextEscape, sb);
@@ -111,8 +129,13 @@ public final class ConfigSerializer {
111 break; 129 break;
112 } 130 }
113 } 131 }
132
114 sb.append(v, idx, v.length()); 133 sb.append(v, idx, v.length());
115 if (k == null) return NO_MATCH; 134
135 if (k == null) {
136 return NO_MATCH;
137 }
138
116 visitor.visitKeyValue(k, sb.toString()); 139 visitor.visitKeyValue(k, sb.toString());
117 return idx; 140 return idx;
118 } 141 }
@@ -122,11 +145,14 @@ public final class ConfigSerializer {
122 if (v.charAt(idx + 1) == 'u') { 145 if (v.charAt(idx + 1) == 'u') {
123 if (idx + 5 < v.length()) { 146 if (idx + 5 < v.length()) {
124 String codePoint = v.substring(idx + 2, idx + 6); 147 String codePoint = v.substring(idx + 2, idx + 6);
148
125 try { 149 try {
126 int c = Integer.parseUnsignedInt(codePoint, 16); 150 int c = Integer.parseUnsignedInt(codePoint, 16);
127 sb.append((char) c); 151 sb.append((char) c);
128 } catch (NumberFormatException ignored) { 152 } catch (NumberFormatException ignored) {
153 // ignored
129 } 154 }
155
130 idx = idx + 6; 156 idx = idx + 6;
131 } 157 }
132 } else if (v.charAt(idx + 1) == 'n') { 158 } else if (v.charAt(idx + 1) == 'n') {
@@ -139,6 +165,7 @@ public final class ConfigSerializer {
139 } else { 165 } else {
140 idx = idx + 1; 166 idx = idx + 1;
141 } 167 }
168
142 return idx; 169 return idx;
143 } 170 }
144 171
@@ -150,12 +177,17 @@ public final class ConfigSerializer {
150 177
151 private static void structureToString(ConfigSection section, StringBuilder sb, List<String> pathStack) { 178 private static void structureToString(ConfigSection section, StringBuilder sb, List<String> pathStack) {
152 if (!section.values().isEmpty()) { 179 if (!section.values().isEmpty()) {
153 if (sb.length() > 0) sb.append('\n'); 180 if (sb.length() > 0) {
181 sb.append('\n');
182 }
183
154 pathStack.forEach(n -> sb.append('[').append(escapeSection(n)).append(']')); 184 pathStack.forEach(n -> sb.append('[').append(escapeSection(n)).append(']'));
155 if (!pathStack.isEmpty()) sb.append('\n'); 185
156 section.values().entrySet().stream() 186 if (!pathStack.isEmpty()) {
157 .sorted(Entry.comparingByKey()) 187 sb.append('\n');
158 .forEach(e -> sb.append(escapeKey(e.getKey())).append('=').append(escapeValue(e.getValue())).append('\n')); 188 }
189
190 section.values().entrySet().stream().sorted(Entry.comparingByKey()).forEach(e -> sb.append(escapeKey(e.getKey())).append('=').append(escapeValue(e.getValue())).append('\n'));
159 } 191 }
160 192
161 section.sections().entrySet().stream().sorted(Entry.comparingByKey()).forEach(e -> { 193 section.sections().entrySet().stream().sorted(Entry.comparingByKey()).forEach(e -> {
@@ -166,43 +198,37 @@ public final class ConfigSerializer {
166 } 198 }
167 199
168 private static String escapeSection(String s) { 200 private static String escapeSection(String s) {
169 return s 201 return s.replace("\\", "\\\\").replace("\n", "\\n").replace("]", "\\]").chars().mapToObj(c -> c >= 32 && c < 127 ? Character.toString((char) c) : String.format("\\u%04x", c)).collect(Collectors.joining());
170 .replace("\\", "\\\\")
171 .replace("\n", "\\n")
172 .replace("]", "\\]")
173 .chars().mapToObj(c -> c >= 32 && c < 127 ? Character.toString((char) c) : String.format("\\u%04x", c)).collect(Collectors.joining());
174 } 202 }
175 203
176 private static String escapeKey(String s) { 204 private static String escapeKey(String s) {
177 return s 205 return s.replace("\\", "\\\\").replace("[", "\\[").replace("\n", "\\n").replace("=", "\\=").chars().mapToObj(c -> c >= 32 && c < 127 ? Character.toString((char) c) : String.format("\\u%04x", c)).collect(Collectors.joining());
178 .replace("\\", "\\\\")
179 .replace("[", "\\[")
180 .replace("\n", "\\n")
181 .replace("=", "\\=")
182 .chars().mapToObj(c -> c >= 32 && c < 127 ? Character.toString((char) c) : String.format("\\u%04x", c)).collect(Collectors.joining());
183 } 206 }
184 207
185 private static String escapeValue(String s) { 208 private static String escapeValue(String s) {
186 return s 209 return s.replace("\\", "\\\\").replace("\n", "\\n").chars().mapToObj(c -> c >= 32 && c < 127 ? Character.toString((char) c) : String.format("\\u%04x", c)).collect(Collectors.joining());
187 .replace("\\", "\\\\")
188 .replace("\n", "\\n")
189 .chars().mapToObj(c -> c >= 32 && c < 127 ? Character.toString((char) c) : String.format("\\u%04x", c)).collect(Collectors.joining());
190 } 210 }
191 211
192 public static Optional<Boolean> parseBool(String v) { 212 public static Optional<Boolean> parseBool(String v) {
193 if (v == null) return Optional.empty(); 213 if (v == null) {
214 return Optional.empty();
215 }
216
194 switch (v) { 217 switch (v) {
195 case "true": 218 case "true":
196 return Optional.of(true); 219 return Optional.of(true);
197 case "false": 220 case "false":
198 return Optional.of(false); 221 return Optional.of(false);
199 default: 222 default:
200 return Optional.empty(); 223 return Optional.empty();
201 } 224 }
202 } 225 }
203 226
204 public static OptionalInt parseInt(String v) { 227 public static OptionalInt parseInt(String v) {
205 if (v == null) return OptionalInt.empty(); 228 if (v == null) {
229 return OptionalInt.empty();
230 }
231
206 try { 232 try {
207 return OptionalInt.of(Integer.parseInt(v)); 233 return OptionalInt.of(Integer.parseInt(v));
208 } catch (NumberFormatException e) { 234 } catch (NumberFormatException e) {
@@ -211,7 +237,10 @@ public final class ConfigSerializer {
211 } 237 }
212 238
213 public static OptionalDouble parseDouble(String v) { 239 public static OptionalDouble parseDouble(String v) {
214 if (v == null) return OptionalDouble.empty(); 240 if (v == null) {
241 return OptionalDouble.empty();
242 }
243
215 try { 244 try {
216 return OptionalDouble.of(Double.parseDouble(v)); 245 return OptionalDouble.of(Double.parseDouble(v));
217 } catch (NumberFormatException e) { 246 } catch (NumberFormatException e) {
@@ -220,7 +249,10 @@ public final class ConfigSerializer {
220 } 249 }
221 250
222 public static OptionalInt parseRgbColor(String v) { 251 public static OptionalInt parseRgbColor(String v) {
223 if (v == null) return OptionalInt.empty(); 252 if (v == null) {
253 return OptionalInt.empty();
254 }
255
224 try { 256 try {
225 if (FULL_RGB_COLOR.matcher(v).matches()) { 257 if (FULL_RGB_COLOR.matcher(v).matches()) {
226 return OptionalInt.of(Integer.parseUnsignedInt(v.substring(1), 16)); 258 return OptionalInt.of(Integer.parseUnsignedInt(v.substring(1), 16));
@@ -241,6 +273,7 @@ public final class ConfigSerializer {
241 public static String rgbColorToString(int color) { 273 public static String rgbColorToString(int color) {
242 color = color & 0xFFFFFF; 274 color = color & 0xFFFFFF;
243 boolean isShort = ((color & 0xF0F0F0) >> 4 ^ color & 0x0F0F0F) == 0; 275 boolean isShort = ((color & 0xF0F0F0) >> 4 ^ color & 0x0F0F0F) == 0;
276
244 if (isShort) { 277 if (isShort) {
245 int packed = color & 0x0F0F0F; 278 int packed = color & 0x0F0F0F;
246 packed = packed & 0xF | packed >> 4; 279 packed = packed & 0xF | packed >> 4;
@@ -252,14 +285,19 @@ public final class ConfigSerializer {
252 } 285 }
253 286
254 public static Optional<String[]> parseArray(String v) { 287 public static Optional<String[]> parseArray(String v) {
255 if (v == null) return Optional.empty(); 288 if (v == null) {
289 return Optional.empty();
290 }
291
256 List<String> l = new ArrayList<>(); 292 List<String> l = new ArrayList<>();
257 int idx = 0; 293 int idx = 0;
258 StringBuilder cur = new StringBuilder(); 294 StringBuilder cur = new StringBuilder();
295
259 while (true) { 296 while (true) {
260 int nextSep = v.indexOf(',', idx); 297 int nextSep = v.indexOf(',', idx);
261 int nextEsc = v.indexOf('\\', idx); 298 int nextEsc = v.indexOf('\\', idx);
262 int next = optMin(nextSep, nextEsc); 299 int next = optMin(nextSep, nextEsc);
300
263 if (next == -1) { 301 if (next == -1) {
264 cur.append(v, idx, v.length()); 302 cur.append(v, idx, v.length());
265 l.add(cur.toString()); 303 l.add(cur.toString());
@@ -271,22 +309,25 @@ public final class ConfigSerializer {
271 idx = nextSep + 1; 309 idx = nextSep + 1;
272 } else if (next == nextEsc) { 310 } else if (next == nextEsc) {
273 cur.append(v, idx, nextEsc); 311 cur.append(v, idx, nextEsc);
312
274 if (nextEsc + 1 < v.length()) { 313 if (nextEsc + 1 < v.length()) {
275 cur.append(v.charAt(nextEsc + 1)); 314 cur.append(v.charAt(nextEsc + 1));
276 } 315 }
316
277 idx = nextEsc + 2; 317 idx = nextEsc + 2;
278 } 318 }
279 } 319 }
280 } 320 }
281 321
282 public static String arrayToString(String[] values) { 322 public static String arrayToString(String[] values) {
283 return Arrays.stream(values) 323 return Arrays.stream(values).map(s -> s.replace("\\", "\\\\").replace(",", "\\,")).collect(Collectors.joining(","));
284 .map(s -> s.replace("\\", "\\\\").replace(",", "\\,"))
285 .collect(Collectors.joining(","));
286 } 324 }
287 325
288 public static <T extends Enum<T>> Optional<T> parseEnum(Function<String, T> byName, String v) { 326 public static <T extends Enum<T>> Optional<T> parseEnum(Function<String, T> byName, String v) {
289 if (v == null) return Optional.empty(); 327 if (v == null) {
328 return Optional.empty();
329 }
330
290 try { 331 try {
291 return Optional.of(byName.apply(v)); 332 return Optional.of(byName.apply(v));
292 } catch (IllegalArgumentException e) { 333 } catch (IllegalArgumentException e) {
@@ -295,9 +336,14 @@ public final class ConfigSerializer {
295 } 336 }
296 337
297 private static int optMin(int v1, int v2) { 338 private static int optMin(int v1, int v2) {
298 if (v1 == -1) return v2; 339 if (v1 == -1) {
299 if (v2 == -1) return v1; 340 return v2;
341 }
342
343 if (v2 == -1) {
344 return v1;
345 }
346
300 return Math.min(v1, v2); 347 return Math.min(v1, v2);
301 } 348 }
302
303} 349}
diff --git a/enigma/src/main/java/cuchaz/enigma/config/ConfigStructureVisitor.java b/enigma/src/main/java/cuchaz/enigma/config/ConfigStructureVisitor.java
index 12d7ec4..5374314 100644
--- a/enigma/src/main/java/cuchaz/enigma/config/ConfigStructureVisitor.java
+++ b/enigma/src/main/java/cuchaz/enigma/config/ConfigStructureVisitor.java
@@ -1,11 +1,9 @@
1package cuchaz.enigma.config; 1package cuchaz.enigma.config;
2 2
3public interface ConfigStructureVisitor { 3public interface ConfigStructureVisitor {
4
5 void visitKeyValue(String key, String value); 4 void visitKeyValue(String key, String value);
6 5
7 void visitSection(String section); 6 void visitSection(String section);
8 7
9 void jumpToRootSection(); 8 void jumpToRootSection();
10
11} 9}
diff --git a/enigma/src/main/java/cuchaz/enigma/events/ClassHandleListener.java b/enigma/src/main/java/cuchaz/enigma/events/ClassHandleListener.java
index 61fea4e..22be2e0 100644
--- a/enigma/src/main/java/cuchaz/enigma/events/ClassHandleListener.java
+++ b/enigma/src/main/java/cuchaz/enigma/events/ClassHandleListener.java
@@ -8,7 +8,6 @@ import cuchaz.enigma.translation.representation.entry.ClassEntry;
8import cuchaz.enigma.utils.Result; 8import cuchaz.enigma.utils.Result;
9 9
10public interface ClassHandleListener { 10public interface ClassHandleListener {
11
12 default void onDeobfRefChanged(ClassHandle h, ClassEntry deobfRef) { 11 default void onDeobfRefChanged(ClassHandle h, ClassEntry deobfRef) {
13 } 12 }
14 13
@@ -32,5 +31,4 @@ public interface ClassHandleListener {
32 JAVADOC, 31 JAVADOC,
33 MAPPINGS, 32 MAPPINGS,
34 } 33 }
35
36} 34}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java b/enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java
index 5f371a5..9475d74 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java
@@ -1,6 +1,10 @@
1package cuchaz.enigma.source; 1package cuchaz.enigma.source;
2 2
3import java.util.*; 3import java.util.Collection;
4import java.util.Iterator;
5import java.util.List;
6import java.util.Map;
7import java.util.Optional;
4 8
5import javax.annotation.Nullable; 9import javax.annotation.Nullable;
6 10
@@ -62,6 +66,7 @@ public class DecompiledClassSource {
62 return translatedEntry.getValue().getSourceRemapName(); 66 return translatedEntry.getValue().getSourceRemapName();
63 } else { 67 } else {
64 Optional<String> proposedName = proposeName(project, entry); 68 Optional<String> proposedName = proposeName(project, entry);
69
65 if (proposedName.isPresent()) { 70 if (proposedName.isPresent()) {
66 target.add(RenamableTokenType.PROPOSED, movedToken); 71 target.add(RenamableTokenType.PROPOSED, movedToken);
67 return proposedName.get(); 72 return proposedName.get();
@@ -72,6 +77,7 @@ public class DecompiledClassSource {
72 } 77 }
73 78
74 String defaultName = generateDefaultName(translatedEntry.getValue()); 79 String defaultName = generateDefaultName(translatedEntry.getValue());
80
75 if (defaultName != null) { 81 if (defaultName != null) {
76 return defaultName; 82 return defaultName;
77 } 83 }
@@ -86,10 +92,7 @@ public class DecompiledClassSource {
86 EntryRemapper mapper = project.getMapper(); 92 EntryRemapper mapper = project.getMapper();
87 Collection<Entry<?>> resolved = mapper.getObfResolver().resolveEntry(entry, ResolutionStrategy.RESOLVE_ROOT); 93 Collection<Entry<?>> resolved = mapper.getObfResolver().resolveEntry(entry, ResolutionStrategy.RESOLVE_ROOT);
88 94
89 return resolved.stream() 95 return resolved.stream().map(e -> nameProposalService.proposeName(e, mapper)).filter(Optional::isPresent).map(Optional::get);
90 .map(e -> nameProposalService.proposeName(e, mapper))
91 .filter(Optional::isPresent)
92 .map(Optional::get);
93 }).findFirst(); 96 }).findFirst();
94 } 97 }
95 98
@@ -99,6 +102,7 @@ public class DecompiledClassSource {
99 LocalVariableDefEntry localVariable = (LocalVariableDefEntry) entry; 102 LocalVariableDefEntry localVariable = (LocalVariableDefEntry) entry;
100 103
101 int index = localVariable.getIndex(); 104 int index = localVariable.getIndex();
105
102 if (localVariable.isArgument()) { 106 if (localVariable.isArgument()) {
103 List<TypeDescriptor> arguments = localVariable.getParent().getDesc().getArgumentDescs(); 107 List<TypeDescriptor> arguments = localVariable.getParent().getDesc().getArgumentDescs();
104 return LocalNameGenerator.generateArgumentName(index, localVariable.getDesc(), arguments); 108 return LocalNameGenerator.generateArgumentName(index, localVariable.getDesc(), arguments);
@@ -139,9 +143,11 @@ public class DecompiledClassSource {
139 143
140 Iterator<Token> fromTokenItr = fromIndex.referenceTokens().iterator(); 144 Iterator<Token> fromTokenItr = fromIndex.referenceTokens().iterator();
141 Iterator<Token> toTokenItr = toIndex.referenceTokens().iterator(); 145 Iterator<Token> toTokenItr = toIndex.referenceTokens().iterator();
146
142 while (fromTokenItr.hasNext() && toTokenItr.hasNext()) { 147 while (fromTokenItr.hasNext() && toTokenItr.hasNext()) {
143 Token fromToken = fromTokenItr.next(); 148 Token fromToken = fromTokenItr.next();
144 Token toToken = toTokenItr.next(); 149 Token toToken = toTokenItr.next();
150
145 if (fromToken.end > fromOffset) { 151 if (fromToken.end > fromOffset) {
146 break; 152 break;
147 } 153 }
diff --git a/enigma/src/main/java/cuchaz/enigma/source/Decompiler.java b/enigma/src/main/java/cuchaz/enigma/source/Decompiler.java
index 938a736..b31dcc4 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/Decompiler.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/Decompiler.java
@@ -1,8 +1,9 @@
1package cuchaz.enigma.source; 1package cuchaz.enigma.source;
2 2
3import cuchaz.enigma.translation.mapping.EntryRemapper;
4import org.checkerframework.checker.nullness.qual.Nullable; 3import org.checkerframework.checker.nullness.qual.Nullable;
5 4
5import cuchaz.enigma.translation.mapping.EntryRemapper;
6
6public interface Decompiler { 7public interface Decompiler {
7 @Deprecated // use remapper specific one for easy doc inclusion 8 @Deprecated // use remapper specific one for easy doc inclusion
8 default Source getSource(String className) { 9 default Source getSource(String className) {
diff --git a/enigma/src/main/java/cuchaz/enigma/source/DecompilerService.java b/enigma/src/main/java/cuchaz/enigma/source/DecompilerService.java
index 638498f..a87fc98 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/DecompilerService.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/DecompilerService.java
@@ -1,11 +1,11 @@
1package cuchaz.enigma.source; 1package cuchaz.enigma.source;
2 2
3import cuchaz.enigma.classprovider.ClassProvider;
4import cuchaz.enigma.api.service.EnigmaService; 3import cuchaz.enigma.api.service.EnigmaService;
5import cuchaz.enigma.api.service.EnigmaServiceType; 4import cuchaz.enigma.api.service.EnigmaServiceType;
5import cuchaz.enigma.classprovider.ClassProvider;
6 6
7public interface DecompilerService extends EnigmaService { 7public interface DecompilerService extends EnigmaService {
8 EnigmaServiceType<DecompilerService> TYPE = EnigmaServiceType.create("decompiler"); 8 EnigmaServiceType<DecompilerService> TYPE = EnigmaServiceType.create("decompiler");
9 9
10 Decompiler create(ClassProvider classProvider, SourceSettings settings); 10 Decompiler create(ClassProvider classProvider, SourceSettings settings);
11} 11}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/Decompilers.java b/enigma/src/main/java/cuchaz/enigma/source/Decompilers.java
index 643ea7a..0e3244d 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/Decompilers.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/Decompilers.java
@@ -5,7 +5,7 @@ import cuchaz.enigma.source.cfr.CfrDecompiler;
5import cuchaz.enigma.source.procyon.ProcyonDecompiler; 5import cuchaz.enigma.source.procyon.ProcyonDecompiler;
6 6
7public class Decompilers { 7public class Decompilers {
8 public static final DecompilerService PROCYON = ProcyonDecompiler::new; 8 public static final DecompilerService PROCYON = ProcyonDecompiler::new;
9 public static final DecompilerService CFR = CfrDecompiler::new; 9 public static final DecompilerService CFR = CfrDecompiler::new;
10 public static final DecompilerService BYTECODE = BytecodeDecompiler::new; 10 public static final DecompilerService BYTECODE = BytecodeDecompiler::new;
11} 11}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/Source.java b/enigma/src/main/java/cuchaz/enigma/source/Source.java
index 6ecce7c..fe45805 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/Source.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/Source.java
@@ -3,9 +3,9 @@ package cuchaz.enigma.source;
3import cuchaz.enigma.translation.mapping.EntryRemapper; 3import cuchaz.enigma.translation.mapping.EntryRemapper;
4 4
5public interface Source { 5public interface Source {
6 String asString(); 6 String asString();
7 7
8 Source withJavadocs(EntryRemapper remapper); 8 Source withJavadocs(EntryRemapper remapper);
9 9
10 SourceIndex index(); 10 SourceIndex index();
11} 11}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/SourceIndex.java b/enigma/src/main/java/cuchaz/enigma/source/SourceIndex.java
index 971252e..e9d928e 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/SourceIndex.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/SourceIndex.java
@@ -1,172 +1,170 @@
1package cuchaz.enigma.source; 1package cuchaz.enigma.source;
2 2
3import java.util.Collection;
4import java.util.List;
5import java.util.Map;
6import java.util.TreeMap;
7
3import com.google.common.collect.HashMultimap; 8import com.google.common.collect.HashMultimap;
4import com.google.common.collect.Lists; 9import com.google.common.collect.Lists;
5import com.google.common.collect.Maps; 10import com.google.common.collect.Maps;
6import com.google.common.collect.Multimap; 11import com.google.common.collect.Multimap;
12
7import cuchaz.enigma.analysis.EntryReference; 13import cuchaz.enigma.analysis.EntryReference;
8import cuchaz.enigma.translation.mapping.EntryResolver; 14import cuchaz.enigma.translation.mapping.EntryResolver;
9import cuchaz.enigma.translation.mapping.ResolutionStrategy; 15import cuchaz.enigma.translation.mapping.ResolutionStrategy;
10import cuchaz.enigma.translation.representation.entry.Entry; 16import cuchaz.enigma.translation.representation.entry.Entry;
11 17
12import java.util.Collection;
13import java.util.List;
14import java.util.Map;
15import java.util.TreeMap;
16
17public class SourceIndex { 18public class SourceIndex {
18 private String source; 19 private String source;
19 private List<Integer> lineOffsets; 20 private List<Integer> lineOffsets;
20 private final TreeMap<Token, EntryReference<Entry<?>, Entry<?>>> tokenToReference; 21 private final TreeMap<Token, EntryReference<Entry<?>, Entry<?>>> tokenToReference;
21 private final Multimap<EntryReference<Entry<?>, Entry<?>>, Token> referenceToTokens; 22 private final Multimap<EntryReference<Entry<?>, Entry<?>>, Token> referenceToTokens;
22 private final Map<Entry<?>, Token> declarationToToken; 23 private final Map<Entry<?>, Token> declarationToToken;
23 24
24 public SourceIndex() { 25 public SourceIndex() {
25 tokenToReference = new TreeMap<>(); 26 tokenToReference = new TreeMap<>();
26 referenceToTokens = HashMultimap.create(); 27 referenceToTokens = HashMultimap.create();
27 declarationToToken = Maps.newHashMap(); 28 declarationToToken = Maps.newHashMap();
28 } 29 }
29 30
30 public SourceIndex(String source) { 31 public SourceIndex(String source) {
31 this(); 32 this();
32 setSource(source); 33 setSource(source);
33 } 34 }
34 35
35 public void setSource(String source) { 36 public void setSource(String source) {
36 this.source = source; 37 this.source = source;
37 lineOffsets = Lists.newArrayList(); 38 lineOffsets = Lists.newArrayList();
38 lineOffsets.add(0); 39 lineOffsets.add(0);
39 40
40 for (int i = 0; i < this.source.length(); i++) { 41 for (int i = 0; i < this.source.length(); i++) {
41 if (this.source.charAt(i) == '\n') { 42 if (this.source.charAt(i) == '\n') {
42 lineOffsets.add(i + 1); 43 lineOffsets.add(i + 1);
43 } 44 }
44 } 45 }
45 } 46 }
46 47
47 public String getSource() { 48 public String getSource() {
48 return source; 49 return source;
49 } 50 }
50 51
51 public int getLineNumber(int position) { 52 public int getLineNumber(int position) {
52 int line = 0; 53 int line = 0;
53 54
54 for (int offset : lineOffsets) { 55 for (int offset : lineOffsets) {
55 if (offset > position) { 56 if (offset > position) {
56 break; 57 break;
57 } 58 }
58 59
59 line++; 60 line++;
60 } 61 }
61 62
62 return line; 63 return line;
63 } 64 }
64 65
65 public int getColumnNumber(int position) { 66 public int getColumnNumber(int position) {
66 return position - lineOffsets.get(getLineNumber(position) - 1) + 1; 67 return position - lineOffsets.get(getLineNumber(position) - 1) + 1;
67 } 68 }
68 69
69 public int getPosition(int line, int column) { 70 public int getPosition(int line, int column) {
70 return lineOffsets.get(line - 1) + column - 1; 71 return lineOffsets.get(line - 1) + column - 1;
71 } 72 }
72 73
73 public Iterable<Entry<?>> declarations() { 74 public Iterable<Entry<?>> declarations() {
74 return declarationToToken.keySet(); 75 return declarationToToken.keySet();
75 } 76 }
76 77
77 public Iterable<Token> declarationTokens() { 78 public Iterable<Token> declarationTokens() {
78 return declarationToToken.values(); 79 return declarationToToken.values();
79 } 80 }
80 81
81 public Token getDeclarationToken(Entry<?> entry) { 82 public Token getDeclarationToken(Entry<?> entry) {
82 return declarationToToken.get(entry); 83 return declarationToToken.get(entry);
83 } 84 }
84 85
85 public void addDeclaration(Token token, Entry<?> deobfEntry) { 86 public void addDeclaration(Token token, Entry<?> deobfEntry) {
86 if (token != null) { 87 if (token != null) {
87 EntryReference<Entry<?>, Entry<?>> reference = new EntryReference<>(deobfEntry, token.text); 88 EntryReference<Entry<?>, Entry<?>> reference = new EntryReference<>(deobfEntry, token.text);
88 tokenToReference.put(token, reference); 89 tokenToReference.put(token, reference);
89 referenceToTokens.put(reference, token); 90 referenceToTokens.put(reference, token);
90 referenceToTokens.put(EntryReference.declaration(deobfEntry, token.text), token); 91 referenceToTokens.put(EntryReference.declaration(deobfEntry, token.text), token);
91 declarationToToken.put(deobfEntry, token); 92 declarationToToken.put(deobfEntry, token);
92 } 93 }
93 } 94 }
94 95
95 public Iterable<EntryReference<Entry<?>, Entry<?>>> references() { 96 public Iterable<EntryReference<Entry<?>, Entry<?>>> references() {
96 return referenceToTokens.keySet(); 97 return referenceToTokens.keySet();
97 } 98 }
98 99
99 public EntryReference<Entry<?>, Entry<?>> getReference(Token token) { 100 public EntryReference<Entry<?>, Entry<?>> getReference(Token token) {
100 if (token == null) { 101 if (token == null) {
101 return null; 102 return null;
102 } 103 }
103 104
104 return tokenToReference.get(token); 105 return tokenToReference.get(token);
105 } 106 }
106 107
107 public Iterable<Token> referenceTokens() { 108 public Iterable<Token> referenceTokens() {
108 return tokenToReference.keySet(); 109 return tokenToReference.keySet();
109 } 110 }
110 111
111 public Token getReferenceToken(int pos) { 112 public Token getReferenceToken(int pos) {
112 Token token = tokenToReference.floorKey(new Token(pos, pos, null)); 113 Token token = tokenToReference.floorKey(new Token(pos, pos, null));
113 114
114 if (token != null && token.contains(pos)) { 115 if (token != null && token.contains(pos)) {
115 return token; 116 return token;
116 } 117 }
117 118
118 return null; 119 return null;
119 } 120 }
120 121
121 public Collection<Token> getReferenceTokens(EntryReference<Entry<?>, Entry<?>> deobfReference) { 122 public Collection<Token> getReferenceTokens(EntryReference<Entry<?>, Entry<?>> deobfReference) {
122 return referenceToTokens.get(deobfReference); 123 return referenceToTokens.get(deobfReference);
123 } 124 }
124 125
125 public void addReference(Token token, Entry<?> deobfEntry, Entry<?> deobfContext) { 126 public void addReference(Token token, Entry<?> deobfEntry, Entry<?> deobfContext) {
126 if (token != null) { 127 if (token != null) {
127 EntryReference<Entry<?>, Entry<?>> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext); 128 EntryReference<Entry<?>, Entry<?>> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext);
128 tokenToReference.put(token, deobfReference); 129 tokenToReference.put(token, deobfReference);
129 referenceToTokens.put(deobfReference, token); 130 referenceToTokens.put(deobfReference, token);
130 } 131 }
131 } 132 }
132 133
133 public void resolveReferences(EntryResolver resolver) { 134 public void resolveReferences(EntryResolver resolver) {
134 // resolve all the classes in the source references 135 // resolve all the classes in the source references
135 for (Token token : Lists.newArrayList(referenceToTokens.values())) { 136 for (Token token : Lists.newArrayList(referenceToTokens.values())) {
136 EntryReference<Entry<?>, Entry<?>> reference = tokenToReference.get(token); 137 EntryReference<Entry<?>, Entry<?>> reference = tokenToReference.get(token);
137 EntryReference<Entry<?>, Entry<?>> resolvedReference = resolver.resolveFirstReference(reference, ResolutionStrategy.RESOLVE_CLOSEST); 138 EntryReference<Entry<?>, Entry<?>> resolvedReference = resolver.resolveFirstReference(reference, ResolutionStrategy.RESOLVE_CLOSEST);
138 139
139 // replace the reference 140 // replace the reference
140 tokenToReference.replace(token, resolvedReference); 141 tokenToReference.replace(token, resolvedReference);
141 142
142 Collection<Token> tokens = referenceToTokens.removeAll(reference); 143 Collection<Token> tokens = referenceToTokens.removeAll(reference);
143 referenceToTokens.putAll(resolvedReference, tokens); 144 referenceToTokens.putAll(resolvedReference, tokens);
144 } 145 }
145 } 146 }
146 147
147 public SourceIndex remapTo(SourceRemapper.Result result) { 148 public SourceIndex remapTo(SourceRemapper.Result result) {
148 SourceIndex remapped = new SourceIndex(result.getSource()); 149 SourceIndex remapped = new SourceIndex(result.getSource());
149 150
150 for (Map.Entry<Entry<?>, Token> entry : declarationToToken.entrySet()) { 151 for (Map.Entry<Entry<?>, Token> entry : declarationToToken.entrySet()) {
151 remapped.declarationToToken.put(entry.getKey(), result.getRemappedToken(entry.getValue())); 152 remapped.declarationToToken.put(entry.getKey(), result.getRemappedToken(entry.getValue()));
152 } 153 }
153 154
154 for (Map.Entry<EntryReference<Entry<?>, Entry<?>>, Collection<Token>> entry : referenceToTokens.asMap().entrySet()) { 155 for (Map.Entry<EntryReference<Entry<?>, Entry<?>>, Collection<Token>> entry : referenceToTokens.asMap().entrySet()) {
155 EntryReference<Entry<?>, Entry<?>> reference = entry.getKey(); 156 EntryReference<Entry<?>, Entry<?>> reference = entry.getKey();
156 Collection<Token> oldTokens = entry.getValue(); 157 Collection<Token> oldTokens = entry.getValue();
157 158
158 Collection<Token> newTokens = oldTokens 159 Collection<Token> newTokens = oldTokens.stream().map(result::getRemappedToken).toList();
159 .stream() 160
160 .map(result::getRemappedToken) 161 remapped.referenceToTokens.putAll(reference, newTokens);
161 .toList(); 162 }
162 163
163 remapped.referenceToTokens.putAll(reference, newTokens); 164 for (Map.Entry<Token, EntryReference<Entry<?>, Entry<?>>> entry : tokenToReference.entrySet()) {
164 } 165 remapped.tokenToReference.put(result.getRemappedToken(entry.getKey()), entry.getValue());
165 166 }
166 for (Map.Entry<Token, EntryReference<Entry<?>, Entry<?>>> entry : tokenToReference.entrySet()) { 167
167 remapped.tokenToReference.put(result.getRemappedToken(entry.getKey()), entry.getValue()); 168 return remapped;
168 } 169 }
169
170 return remapped;
171 }
172} 170}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/SourceRemapper.java b/enigma/src/main/java/cuchaz/enigma/source/SourceRemapper.java
index b5f8006..f12c387 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/SourceRemapper.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/SourceRemapper.java
@@ -17,10 +17,12 @@ public class SourceRemapper {
17 Map<Token, Token> remappedTokens = new HashMap<>(); 17 Map<Token, Token> remappedTokens = new HashMap<>();
18 18
19 int accumulatedOffset = 0; 19 int accumulatedOffset = 0;
20
20 for (Token token : tokens) { 21 for (Token token : tokens) {
21 Token movedToken = token.move(accumulatedOffset); 22 Token movedToken = token.move(accumulatedOffset);
22 23
23 String remappedName = remapper.remap(token, movedToken); 24 String remappedName = remapper.remap(token, movedToken);
25
24 if (remappedName != null) { 26 if (remappedName != null) {
25 accumulatedOffset += movedToken.getRenameOffset(remappedName); 27 accumulatedOffset += movedToken.getRenameOffset(remappedName);
26 movedToken.rename(remappedSource, remappedName); 28 movedToken.rename(remappedSource, remappedName);
diff --git a/enigma/src/main/java/cuchaz/enigma/source/SourceSettings.java b/enigma/src/main/java/cuchaz/enigma/source/SourceSettings.java
index f6c68e9..1754770 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/SourceSettings.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/SourceSettings.java
@@ -1,11 +1,11 @@
1package cuchaz.enigma.source; 1package cuchaz.enigma.source;
2 2
3public class SourceSettings { 3public class SourceSettings {
4 public final boolean removeImports; 4 public final boolean removeImports;
5 public final boolean removeVariableFinal; 5 public final boolean removeVariableFinal;
6 6
7 public SourceSettings(boolean removeImports, boolean removeVariableFinal) { 7 public SourceSettings(boolean removeImports, boolean removeVariableFinal) {
8 this.removeImports = removeImports; 8 this.removeImports = removeImports;
9 this.removeVariableFinal = removeVariableFinal; 9 this.removeVariableFinal = removeVariableFinal;
10 } 10 }
11} 11}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/Token.java b/enigma/src/main/java/cuchaz/enigma/source/Token.java
index 7d729ab..5c14234 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/Token.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/Token.java
@@ -1,18 +1,17 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.source; 12package cuchaz.enigma.source;
13 13
14public class Token implements Comparable<Token> { 14public class Token implements Comparable<Token> {
15
16 public int start; 15 public int start;
17 public int end; 16 public int end;
18 public String text; 17 public String text;
diff --git a/enigma/src/main/java/cuchaz/enigma/source/TokenStore.java b/enigma/src/main/java/cuchaz/enigma/source/TokenStore.java
index f32d918..179eece 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/TokenStore.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/TokenStore.java
@@ -1,9 +1,14 @@
1package cuchaz.enigma.source; 1package cuchaz.enigma.source;
2 2
3import java.util.*; 3import java.util.Collections;
4import java.util.Comparator;
5import java.util.EnumMap;
6import java.util.Iterator;
7import java.util.Map;
8import java.util.NavigableSet;
9import java.util.TreeSet;
4 10
5public final class TokenStore { 11public final class TokenStore {
6
7 private static final TokenStore EMPTY = new TokenStore(Collections.emptyNavigableSet(), Collections.emptyMap(), null); 12 private static final TokenStore EMPTY = new TokenStore(Collections.emptyNavigableSet(), Collections.emptyMap(), null);
8 13
9 private final NavigableSet<Token> tokens; 14 private final NavigableSet<Token> tokens;
@@ -18,9 +23,11 @@ public final class TokenStore {
18 23
19 public static TokenStore create(SourceIndex obfuscatedIndex) { 24 public static TokenStore create(SourceIndex obfuscatedIndex) {
20 EnumMap<RenamableTokenType, NavigableSet<Token>> map = new EnumMap<>(RenamableTokenType.class); 25 EnumMap<RenamableTokenType, NavigableSet<Token>> map = new EnumMap<>(RenamableTokenType.class);
26
21 for (RenamableTokenType value : RenamableTokenType.values()) { 27 for (RenamableTokenType value : RenamableTokenType.values()) {
22 map.put(value, new TreeSet<>(Comparator.comparing(t -> t.start))); 28 map.put(value, new TreeSet<>(Comparator.comparing(t -> t.start)));
23 } 29 }
30
24 return new TokenStore(new TreeSet<>(Comparator.comparing(t -> t.start)), Collections.unmodifiableMap(map), obfuscatedIndex.getSource()); 31 return new TokenStore(new TreeSet<>(Comparator.comparing(t -> t.start)), Collections.unmodifiableMap(map), obfuscatedIndex.getSource());
25 } 32 }
26 33
@@ -34,22 +41,25 @@ public final class TokenStore {
34 } 41 }
35 42
36 public boolean isCompatible(TokenStore other) { 43 public boolean isCompatible(TokenStore other) {
37 return this.obfSource != null && other.obfSource != null && 44 return this.obfSource != null && other.obfSource != null && this.obfSource.equals(other.obfSource) && this.tokens.size() == other.tokens.size();
38 this.obfSource.equals(other.obfSource) &&
39 this.tokens.size() == other.tokens.size();
40 } 45 }
41 46
42 public int mapPosition(TokenStore to, int position) { 47 public int mapPosition(TokenStore to, int position) {
43 if (!this.isCompatible(to)) return 0; 48 if (!this.isCompatible(to)) {
49 return 0;
50 }
44 51
45 int newPos = position; 52 int newPos = position;
46 Iterator<Token> thisIter = this.tokens.iterator(); 53 Iterator<Token> thisIter = this.tokens.iterator();
47 Iterator<Token> toIter = to.tokens.iterator(); 54 Iterator<Token> toIter = to.tokens.iterator();
55
48 while (thisIter.hasNext()) { 56 while (thisIter.hasNext()) {
49 Token token = thisIter.next(); 57 Token token = thisIter.next();
50 Token newToken = toIter.next(); 58 Token newToken = toIter.next();
51 59
52 if (position < token.start) break; 60 if (position < token.start) {
61 break;
62 }
53 63
54 // if we're inside the token and the text changed, 64 // if we're inside the token and the text changed,
55 // snap the cursor to the beginning 65 // snap the cursor to the beginning
@@ -67,5 +77,4 @@ public final class TokenStore {
67 public Map<RenamableTokenType, NavigableSet<Token>> getByType() { 77 public Map<RenamableTokenType, NavigableSet<Token>> getByType() {
68 return byType; 78 return byType;
69 } 79 }
70
71} 80}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/bytecode/BytecodeDecompiler.java b/enigma/src/main/java/cuchaz/enigma/source/bytecode/BytecodeDecompiler.java
index 97d2969..6461d20 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/bytecode/BytecodeDecompiler.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/bytecode/BytecodeDecompiler.java
@@ -1,21 +1,22 @@
1package cuchaz.enigma.source.bytecode; 1package cuchaz.enigma.source.bytecode;
2 2
3import org.checkerframework.checker.nullness.qual.Nullable;
4
3import cuchaz.enigma.classprovider.ClassProvider; 5import cuchaz.enigma.classprovider.ClassProvider;
4import cuchaz.enigma.source.Decompiler; 6import cuchaz.enigma.source.Decompiler;
5import cuchaz.enigma.source.Source; 7import cuchaz.enigma.source.Source;
6import cuchaz.enigma.source.SourceSettings; 8import cuchaz.enigma.source.SourceSettings;
7import cuchaz.enigma.translation.mapping.EntryRemapper; 9import cuchaz.enigma.translation.mapping.EntryRemapper;
8import org.checkerframework.checker.nullness.qual.Nullable;
9 10
10public class BytecodeDecompiler implements Decompiler { 11public class BytecodeDecompiler implements Decompiler {
11 private final ClassProvider classProvider; 12 private final ClassProvider classProvider;
12 13
13 public BytecodeDecompiler(ClassProvider classProvider, SourceSettings settings) { 14 public BytecodeDecompiler(ClassProvider classProvider, SourceSettings settings) {
14 this.classProvider = classProvider; 15 this.classProvider = classProvider;
15 } 16 }
16 17
17 @Override 18 @Override
18 public Source getSource(String className, @Nullable EntryRemapper remapper) { 19 public Source getSource(String className, @Nullable EntryRemapper remapper) {
19 return new BytecodeSource(classProvider.get(className), remapper); 20 return new BytecodeSource(classProvider.get(className), remapper);
20 } 21 }
21} 22}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/bytecode/BytecodeSource.java b/enigma/src/main/java/cuchaz/enigma/source/bytecode/BytecodeSource.java
index 4364b40..bcb5455 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/bytecode/BytecodeSource.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/bytecode/BytecodeSource.java
@@ -1,56 +1,57 @@
1package cuchaz.enigma.source.bytecode; 1package cuchaz.enigma.source.bytecode;
2 2
3import java.io.PrintWriter;
4import java.io.StringWriter;
5
6import org.objectweb.asm.tree.ClassNode;
7import org.objectweb.asm.util.TraceClassVisitor;
8
3import cuchaz.enigma.Enigma; 9import cuchaz.enigma.Enigma;
4import cuchaz.enigma.bytecode.translators.TranslationClassVisitor; 10import cuchaz.enigma.bytecode.translators.TranslationClassVisitor;
5import cuchaz.enigma.source.Source; 11import cuchaz.enigma.source.Source;
6import cuchaz.enigma.source.SourceIndex; 12import cuchaz.enigma.source.SourceIndex;
7import cuchaz.enigma.translation.mapping.EntryRemapper; 13import cuchaz.enigma.translation.mapping.EntryRemapper;
8import org.objectweb.asm.tree.ClassNode;
9import org.objectweb.asm.util.TraceClassVisitor;
10
11import java.io.PrintWriter;
12import java.io.StringWriter;
13 14
14public class BytecodeSource implements Source { 15public class BytecodeSource implements Source {
15 private final ClassNode classNode; 16 private final ClassNode classNode;
16 private final EntryRemapper remapper; 17 private final EntryRemapper remapper;
17 18
18 public BytecodeSource(ClassNode classNode, EntryRemapper remapper) { 19 public BytecodeSource(ClassNode classNode, EntryRemapper remapper) {
19 this.classNode = classNode; 20 this.classNode = classNode;
20 this.remapper = remapper; 21 this.remapper = remapper;
21 } 22 }
22 23
23 @Override 24 @Override
24 public String asString() { 25 public String asString() {
25 return index().getSource(); 26 return index().getSource();
26 } 27 }
27 28
28 @Override 29 @Override
29 public Source withJavadocs(EntryRemapper remapper) { 30 public Source withJavadocs(EntryRemapper remapper) {
30 return new BytecodeSource(classNode, remapper); 31 return new BytecodeSource(classNode, remapper);
31 } 32 }
32 33
33 @Override 34 @Override
34 public SourceIndex index() { 35 public SourceIndex index() {
35 SourceIndex index = new SourceIndex(); 36 SourceIndex index = new SourceIndex();
36 37
37 EnigmaTextifier textifier = new EnigmaTextifier(index); 38 EnigmaTextifier textifier = new EnigmaTextifier(index);
38 StringWriter out = new StringWriter(); 39 StringWriter out = new StringWriter();
39 PrintWriter writer = new PrintWriter(out); 40 PrintWriter writer = new PrintWriter(out);
40 41
41 TraceClassVisitor traceClassVisitor = new TraceClassVisitor(null, textifier, writer); 42 TraceClassVisitor traceClassVisitor = new TraceClassVisitor(null, textifier, writer);
42 43
43 ClassNode node = this.classNode; 44 ClassNode node = this.classNode;
44 45
45 if (remapper != null) { 46 if (remapper != null) {
46 ClassNode translatedNode = new ClassNode(); 47 ClassNode translatedNode = new ClassNode();
47 node.accept(new TranslationClassVisitor(remapper.getDeobfuscator(), Enigma.ASM_VERSION, translatedNode)); 48 node.accept(new TranslationClassVisitor(remapper.getDeobfuscator(), Enigma.ASM_VERSION, translatedNode));
48 node = translatedNode; 49 node = translatedNode;
49 } 50 }
50 51
51 node.accept(traceClassVisitor); 52 node.accept(traceClassVisitor);
52 index.setSource(out.toString()); 53 index.setSource(out.toString());
53 54
54 return index; 55 return index;
55 } 56 }
56} 57}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/bytecode/EnigmaTextifier.java b/enigma/src/main/java/cuchaz/enigma/source/bytecode/EnigmaTextifier.java
index 2f3fcf2..f1586ee 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/bytecode/EnigmaTextifier.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/bytecode/EnigmaTextifier.java
@@ -1,14 +1,15 @@
1package cuchaz.enigma.source.bytecode; 1package cuchaz.enigma.source.bytecode;
2 2
3import org.objectweb.asm.util.Textifier;
4
3import cuchaz.enigma.Enigma; 5import cuchaz.enigma.Enigma;
4import cuchaz.enigma.source.SourceIndex; 6import cuchaz.enigma.source.SourceIndex;
5import org.objectweb.asm.util.Textifier;
6 7
7public class EnigmaTextifier extends Textifier { 8public class EnigmaTextifier extends Textifier {
8 private final SourceIndex sourceIndex; 9 private final SourceIndex sourceIndex;
9 10
10 public EnigmaTextifier(SourceIndex sourceIndex) { 11 public EnigmaTextifier(SourceIndex sourceIndex) {
11 super(Enigma.ASM_VERSION); 12 super(Enigma.ASM_VERSION);
12 this.sourceIndex = sourceIndex; 13 this.sourceIndex = sourceIndex;
13 } 14 }
14} 15}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java b/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java
index cd7b2aa..5531236 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java
@@ -1,11 +1,8 @@
1package cuchaz.enigma.source.cfr; 1package cuchaz.enigma.source.cfr;
2 2
3import cuchaz.enigma.classprovider.ClassProvider; 3import java.util.Collection;
4import cuchaz.enigma.source.Decompiler; 4import java.util.Map;
5import cuchaz.enigma.source.Source; 5
6import cuchaz.enigma.source.SourceSettings;
7import cuchaz.enigma.translation.mapping.EntryRemapper;
8import cuchaz.enigma.utils.AsmUtil;
9import org.benf.cfr.reader.apiunreleased.ClassFileSource2; 6import org.benf.cfr.reader.apiunreleased.ClassFileSource2;
10import org.benf.cfr.reader.apiunreleased.JarContent; 7import org.benf.cfr.reader.apiunreleased.JarContent;
11import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair; 8import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair;
@@ -15,55 +12,59 @@ import org.benf.cfr.reader.util.getopt.OptionsImpl;
15import org.checkerframework.checker.nullness.qual.Nullable; 12import org.checkerframework.checker.nullness.qual.Nullable;
16import org.objectweb.asm.tree.ClassNode; 13import org.objectweb.asm.tree.ClassNode;
17 14
18import java.util.Collection; 15import cuchaz.enigma.classprovider.ClassProvider;
19import java.util.Map; 16import cuchaz.enigma.source.Decompiler;
17import cuchaz.enigma.source.Source;
18import cuchaz.enigma.source.SourceSettings;
19import cuchaz.enigma.translation.mapping.EntryRemapper;
20import cuchaz.enigma.utils.AsmUtil;
20 21
21public class CfrDecompiler implements Decompiler { 22public class CfrDecompiler implements Decompiler {
22 // cfr doesn't add final on params so final setting is ignored 23 // cfr doesn't add final on params so final setting is ignored
23 private final SourceSettings settings; 24 private final SourceSettings settings;
24 private final Options options; 25 private final Options options;
25 private final ClassFileSource2 classFileSource; 26 private final ClassFileSource2 classFileSource;
26 27
27 public CfrDecompiler(ClassProvider classProvider, SourceSettings sourceSettings) { 28 public CfrDecompiler(ClassProvider classProvider, SourceSettings sourceSettings) {
28 this.options = OptionsImpl.getFactory().create( Map.of("trackbytecodeloc", "true")); 29 this.options = OptionsImpl.getFactory().create(Map.of("trackbytecodeloc", "true"));
29 this.settings = sourceSettings; 30 this.settings = sourceSettings;
30 this.classFileSource = new ClassFileSource(classProvider); 31 this.classFileSource = new ClassFileSource(classProvider);
31 } 32 }
32 33
33 @Override 34 @Override
34 public Source getSource(String className, @Nullable EntryRemapper mapper) { 35 public Source getSource(String className, @Nullable EntryRemapper mapper) {
35 return new CfrSource(className, settings, this.options, this.classFileSource, mapper); 36 return new CfrSource(className, settings, this.options, this.classFileSource, mapper);
36 } 37 }
37 38
38 private record ClassFileSource(ClassProvider classProvider) implements ClassFileSource2 { 39 private record ClassFileSource(ClassProvider classProvider) implements ClassFileSource2 {
39 @Override 40 @Override
40 public JarContent addJarContent(String s, AnalysisType analysisType) { 41 public JarContent addJarContent(String s, AnalysisType analysisType) {
41 return null; 42 return null;
42 } 43 }
43 44
44 @Override 45 @Override
45 public void informAnalysisRelativePathDetail(String usePath, String classFilePath) { 46 public void informAnalysisRelativePathDetail(String usePath, String classFilePath) {
46 } 47 }
47 48
48 @Override 49 @Override
49 public Collection<String> addJar(String jarPath) { 50 public Collection<String> addJar(String jarPath) {
50 return null; 51 return null;
51 } 52 }
52 53
53 @Override 54 @Override
54 public String getPossiblyRenamedPath(String path) { 55 public String getPossiblyRenamedPath(String path) {
55 return path; 56 return path;
56 } 57 }
57 58
58 @Override 59 @Override
59 public Pair<byte[], String> getClassFileContent(String path) { 60 public Pair<byte[], String> getClassFileContent(String path) {
60 ClassNode node = classProvider.get(path.substring(0, path.lastIndexOf('.'))); 61 ClassNode node = classProvider.get(path.substring(0, path.lastIndexOf('.')));
61 62
62 if (node == null) { 63 if (node == null) {
63 return null; 64 return null;
64 } 65 }
65 66
66 return new Pair<>(AsmUtil.nodeToBytes(node), path); 67 return new Pair<>(AsmUtil.nodeToBytes(node), path);
67 } 68 }
68 } 69 }
69} 70}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java b/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java
index fb44ef9..70b43ac 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java
@@ -1,9 +1,5 @@
1package cuchaz.enigma.source.cfr; 1package cuchaz.enigma.source.cfr;
2 2
3import cuchaz.enigma.source.Source;
4import cuchaz.enigma.source.SourceIndex;
5import cuchaz.enigma.source.SourceSettings;
6import cuchaz.enigma.translation.mapping.EntryRemapper;
7import org.benf.cfr.reader.apiunreleased.ClassFileSource2; 3import org.benf.cfr.reader.apiunreleased.ClassFileSource2;
8import org.benf.cfr.reader.entities.ClassFile; 4import org.benf.cfr.reader.entities.ClassFile;
9import org.benf.cfr.reader.mapping.MappingFactory; 5import org.benf.cfr.reader.mapping.MappingFactory;
@@ -17,71 +13,77 @@ import org.benf.cfr.reader.util.getopt.Options;
17import org.benf.cfr.reader.util.getopt.OptionsImpl; 13import org.benf.cfr.reader.util.getopt.OptionsImpl;
18import org.checkerframework.checker.nullness.qual.Nullable; 14import org.checkerframework.checker.nullness.qual.Nullable;
19 15
16import cuchaz.enigma.source.Source;
17import cuchaz.enigma.source.SourceIndex;
18import cuchaz.enigma.source.SourceSettings;
19import cuchaz.enigma.translation.mapping.EntryRemapper;
20
20public class CfrSource implements Source { 21public class CfrSource implements Source {
21 private final String className; 22 private final String className;
22 private final SourceSettings settings; 23 private final SourceSettings settings;
23 private final Options options; 24 private final Options options;
24 private final ClassFileSource2 classFileSource; 25 private final ClassFileSource2 classFileSource;
25 private final EntryRemapper mapper; 26 private final EntryRemapper mapper;
26 27
27 private SourceIndex index; 28 private SourceIndex index;
28 29
29 public CfrSource(String className, SourceSettings settings, Options options, ClassFileSource2 classFileSource, @Nullable EntryRemapper mapper) { 30 public CfrSource(String className, SourceSettings settings, Options options, ClassFileSource2 classFileSource, @Nullable EntryRemapper mapper) {
30 this.className = className; 31 this.className = className;
31 this.settings = settings; 32 this.settings = settings;
32 this.options = options; 33 this.options = options;
33 this.classFileSource = classFileSource; 34 this.classFileSource = classFileSource;
34 this.mapper = mapper; 35 this.mapper = mapper;
35 } 36 }
36 37
37 @Override 38 @Override
38 public Source withJavadocs(EntryRemapper mapper) { 39 public Source withJavadocs(EntryRemapper mapper) {
39 return new CfrSource(className, settings, options, classFileSource, mapper); 40 return new CfrSource(className, settings, options, classFileSource, mapper);
40 } 41 }
41 42
42 @Override 43 @Override
43 public SourceIndex index() { 44 public SourceIndex index() {
44 ensureDecompiled(); 45 ensureDecompiled();
45 return index; 46 return index;
46 } 47 }
47 48
48 @Override 49 @Override
49 public String asString() { 50 public String asString() {
50 ensureDecompiled(); 51 ensureDecompiled();
51 return index.getSource(); 52 return index.getSource();
52 } 53 }
53 54
54 private void ensureDecompiled() { 55 private void ensureDecompiled() {
55 if (index != null) { 56 if (index != null) {
56 return; 57 return;
57 } 58 }
58 59
59 DCCommonState commonState = new DCCommonState(options, classFileSource); 60 DCCommonState commonState = new DCCommonState(options, classFileSource);
60 ObfuscationMapping mapping = MappingFactory.get(options, commonState); 61 ObfuscationMapping mapping = MappingFactory.get(options, commonState);
61 DCCommonState state = new DCCommonState(commonState, mapping); 62 DCCommonState state = new DCCommonState(commonState, mapping);
62 ClassFile tree = state.getClassFileMaybePath(className); 63 ClassFile tree = state.getClassFileMaybePath(className);
63 64
64 state.configureWith(tree); 65 state.configureWith(tree);
65 66
66 // To make sure we're analysing the cached version 67 // To make sure we're analysing the cached version
67 try { 68 try {
68 tree = state.getClassFile(tree.getClassType()); 69 tree = state.getClassFile(tree.getClassType());
69 } catch (CannotLoadClassException ignored) { 70 } catch (CannotLoadClassException ignored) {
70 } 71 // ignored
71 72 }
72 if (options.getOption(OptionsImpl.DECOMPILE_INNER_CLASSES)) { 73
73 tree.loadInnerClasses(state); 74 if (options.getOption(OptionsImpl.DECOMPILE_INNER_CLASSES)) {
74 } 75 tree.loadInnerClasses(state);
75 76 }
76 if (options.getOption(OptionsImpl.RENAME_DUP_MEMBERS)) { 77
77 MemberNameResolver.resolveNames(state, ListFactory.newList(state.getClassCache().getLoadedTypes())); 78 if (options.getOption(OptionsImpl.RENAME_DUP_MEMBERS)) {
78 } 79 MemberNameResolver.resolveNames(state, ListFactory.newList(state.getClassCache().getLoadedTypes()));
79 80 }
80 TypeUsageCollectingDumper typeUsageCollector = new TypeUsageCollectingDumper(options, tree); 81
81 tree.analyseTop(state, typeUsageCollector); 82 TypeUsageCollectingDumper typeUsageCollector = new TypeUsageCollectingDumper(options, tree);
82 83 tree.analyseTop(state, typeUsageCollector);
83 EnigmaDumper dumper = new EnigmaDumper(new StringBuilder(), settings, typeUsageCollector.getRealTypeUsageInformation(), options, mapper); 84
84 tree.dump(state.getObfuscationMapping().wrap(dumper)); 85 EnigmaDumper dumper = new EnigmaDumper(new StringBuilder(), settings, typeUsageCollector.getRealTypeUsageInformation(), options, mapper);
85 index = dumper.getIndex(); 86 tree.dump(state.getObfuscationMapping().wrap(dumper));
86 } 87 index = dumper.getIndex();
88 }
87} 89}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java b/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java
index 14fd168..93cc64f 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java
@@ -1,17 +1,13 @@
1package cuchaz.enigma.source.cfr; 1package cuchaz.enigma.source.cfr;
2 2
3import cuchaz.enigma.source.SourceIndex; 3import java.util.ArrayList;
4import cuchaz.enigma.source.SourceSettings; 4import java.util.Arrays;
5import cuchaz.enigma.source.Token; 5import java.util.Collection;
6import cuchaz.enigma.translation.mapping.EntryMapping; 6import java.util.HashMap;
7import cuchaz.enigma.translation.mapping.EntryRemapper; 7import java.util.LinkedList;
8import cuchaz.enigma.translation.representation.MethodDescriptor; 8import java.util.List;
9import cuchaz.enigma.translation.representation.TypeDescriptor; 9import java.util.Map;
10import cuchaz.enigma.translation.representation.entry.ClassEntry; 10
11import cuchaz.enigma.translation.representation.entry.Entry;
12import cuchaz.enigma.translation.representation.entry.FieldEntry;
13import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
14import cuchaz.enigma.translation.representation.entry.MethodEntry;
15import org.benf.cfr.reader.bytecode.analysis.loc.HasByteCodeLoc; 11import org.benf.cfr.reader.bytecode.analysis.loc.HasByteCodeLoc;
16import org.benf.cfr.reader.bytecode.analysis.types.JavaRefTypeInstance; 12import org.benf.cfr.reader.bytecode.analysis.types.JavaRefTypeInstance;
17import org.benf.cfr.reader.bytecode.analysis.types.JavaTypeInstance; 13import org.benf.cfr.reader.bytecode.analysis.types.JavaTypeInstance;
@@ -31,371 +27,404 @@ import org.benf.cfr.reader.util.output.StringStreamDumper;
31import org.benf.cfr.reader.util.output.TypeContext; 27import org.benf.cfr.reader.util.output.TypeContext;
32import org.checkerframework.checker.nullness.qual.Nullable; 28import org.checkerframework.checker.nullness.qual.Nullable;
33 29
34import java.util.ArrayList; 30import cuchaz.enigma.source.SourceIndex;
35import java.util.Arrays; 31import cuchaz.enigma.source.SourceSettings;
36import java.util.Collection; 32import cuchaz.enigma.source.Token;
37import java.util.HashMap; 33import cuchaz.enigma.translation.mapping.EntryMapping;
38import java.util.LinkedList; 34import cuchaz.enigma.translation.mapping.EntryRemapper;
39import java.util.List; 35import cuchaz.enigma.translation.representation.MethodDescriptor;
40import java.util.Map; 36import cuchaz.enigma.translation.representation.TypeDescriptor;
37import cuchaz.enigma.translation.representation.entry.ClassEntry;
38import cuchaz.enigma.translation.representation.entry.Entry;
39import cuchaz.enigma.translation.representation.entry.FieldEntry;
40import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
41import cuchaz.enigma.translation.representation.entry.MethodEntry;
41 42
42public class EnigmaDumper extends StringStreamDumper { 43public class EnigmaDumper extends StringStreamDumper {
43 private final StringBuilder sb; 44 private final StringBuilder sb;
44 private final SourceSettings sourceSettings; 45 private final SourceSettings sourceSettings;
45 private final SourceIndex index; 46 private final SourceIndex index;
46 private final @Nullable EntryRemapper mapper; 47 private final @Nullable EntryRemapper mapper;
47 private final Map<Object, Entry<?>> refs = new HashMap<>(); 48 private final Map<Object, Entry<?>> refs = new HashMap<>();
48 private final TypeUsageInformation typeUsage; 49 private final TypeUsageInformation typeUsage;
49 private final MovableDumperContext dumperContext; 50 private final MovableDumperContext dumperContext;
50 private boolean muteLine = false; 51 private boolean muteLine = false;
51 private MethodEntry contextMethod = null; 52 private MethodEntry contextMethod = null;
52 53
53 public EnigmaDumper(StringBuilder sb, SourceSettings sourceSettings, TypeUsageInformation typeUsage, Options options, 54 public EnigmaDumper(StringBuilder sb, SourceSettings sourceSettings, TypeUsageInformation typeUsage, Options options, @Nullable EntryRemapper mapper) {
54 @Nullable EntryRemapper mapper) { 55 this(sb, sourceSettings, typeUsage, options, mapper, new SourceIndex(), new MovableDumperContext());
55 this(sb, sourceSettings, typeUsage, options, mapper, new SourceIndex(), new MovableDumperContext()); 56 }
56 } 57
57 58 protected EnigmaDumper(StringBuilder sb, SourceSettings sourceSettings, TypeUsageInformation typeUsage, Options options, @Nullable EntryRemapper mapper, SourceIndex index, MovableDumperContext context) {
58 protected EnigmaDumper(StringBuilder sb, SourceSettings sourceSettings, TypeUsageInformation typeUsage, Options options, 59 super((m, e) -> {
59 @Nullable EntryRemapper mapper, SourceIndex index, MovableDumperContext context) { 60 }, sb, typeUsage, options, IllegalIdentifierDump.Nop.getInstance(), context);
60 super((m, e) -> { 61 this.sb = sb;
61 }, sb, typeUsage, options, IllegalIdentifierDump.Nop.getInstance(), context); 62 this.sourceSettings = sourceSettings;
62 this.sb = sb; 63 this.typeUsage = typeUsage;
63 this.sourceSettings = sourceSettings; 64 this.mapper = mapper;
64 this.typeUsage = typeUsage; 65 this.dumperContext = context;
65 this.mapper = mapper; 66 this.index = index;
66 this.dumperContext = context; 67 }
67 this.index = index; 68
68 } 69 private MethodEntry getMethodEntry(MethodPrototype method) {
69 70 if (method == null || method.getOwner() == null) {
70 private MethodEntry getMethodEntry(MethodPrototype method) { 71 return null;
71 if (method == null || method.getOwner() == null) { 72 }
72 return null; 73
73 } 74 MethodDescriptor desc = new MethodDescriptor(method.getOriginalDescriptor());
74 75
75 MethodDescriptor desc = new MethodDescriptor(method.getOriginalDescriptor()); 76 return new MethodEntry(getClassEntry(method.getOwner()), method.getName(), desc);
76 77 }
77 return new MethodEntry(getClassEntry(method.getOwner()), method.getName(), desc); 78
78 } 79 private LocalVariableEntry getParameterEntry(MethodPrototype method, int parameterIndex, String name) {
79 80 MethodEntry owner = getMethodEntry(method);
80 private LocalVariableEntry getParameterEntry(MethodPrototype method, int parameterIndex, String name) { 81
81 MethodEntry owner = getMethodEntry(method); 82 // params may be not computed if cfr creates a lambda expression fallback, e.g. in PointOfInterestSet
82 // params may be not computed if cfr creates a lambda expression fallback, e.g. in PointOfInterestSet 83 if (owner == null || !method.parametersComputed()) {
83 if (owner == null || !method.parametersComputed()) { 84 return null;
84 return null; 85 }
85 } 86
86 87 int variableIndex = method.getParameterLValues().get(parameterIndex).localVariable.getIdx();
87 int variableIndex = method.getParameterLValues().get(parameterIndex).localVariable.getIdx(); 88
88 89 return new LocalVariableEntry(owner, variableIndex, name, true, null);
89 return new LocalVariableEntry(owner, variableIndex, name, true, null); 90 }
90 } 91
91 92 private FieldEntry getFieldEntry(JavaTypeInstance owner, String name, String desc) {
92 private FieldEntry getFieldEntry(JavaTypeInstance owner, String name, String desc) { 93 return new FieldEntry(getClassEntry(owner), name, new TypeDescriptor(desc));
93 return new FieldEntry(getClassEntry(owner), name, new TypeDescriptor(desc)); 94 }
94 } 95
95 96 private ClassEntry getClassEntry(JavaTypeInstance type) {
96 private ClassEntry getClassEntry(JavaTypeInstance type) { 97 return new ClassEntry(type.getRawName().replace('.', '/'));
97 return new ClassEntry(type.getRawName().replace('.', '/')); 98 }
98 } 99
99 100 @Override
100 @Override 101 public Dumper packageName(JavaRefTypeInstance t) {
101 public Dumper packageName(JavaRefTypeInstance t) { 102 if (sourceSettings.removeImports) {
102 if (sourceSettings.removeImports) { 103 return this;
103 return this; 104 }
104 } 105
105 return super.packageName(t); 106 return super.packageName(t);
106 } 107 }
107 108
108 @Override 109 @Override
109 public Dumper keyword(String s) { 110 public Dumper keyword(String s) {
110 if (sourceSettings.removeImports && s.startsWith("import")) { 111 if (sourceSettings.removeImports && s.startsWith("import")) {
111 muteLine = true; 112 muteLine = true;
112 return this; 113 return this;
113 } 114 }
114 return super.keyword(s); 115
115 } 116 return super.keyword(s);
116 117 }
117 @Override 118
118 public Dumper endCodeln() { 119 @Override
119 if (muteLine) { 120 public Dumper endCodeln() {
120 muteLine = false; 121 if (muteLine) {
121 return this; 122 muteLine = false;
122 } 123 return this;
123 return super.endCodeln(); 124 }
124 } 125
125 126 return super.endCodeln();
126 @Override 127 }
127 public Dumper print(String s) { 128
128 if (muteLine) { 129 @Override
129 return this; 130 public Dumper print(String s) {
130 } 131 if (muteLine) {
131 return super.print(s); 132 return this;
132 } 133 }
133 134
134 @Override 135 return super.print(s);
135 public Dumper dumpClassDoc(JavaTypeInstance owner) { 136 }
136 if (mapper != null) { 137
137 List<String> recordComponentDocs = new LinkedList<>(); 138 @Override
138 139 public Dumper dumpClassDoc(JavaTypeInstance owner) {
139 if (isRecord(owner)) { 140 if (mapper != null) {
140 ClassFile classFile = ((JavaRefTypeInstance) owner).getClassFile(); 141 List<String> recordComponentDocs = new LinkedList<>();
141 for (ClassFileField field : classFile.getFields()) { 142
142 if (field.getField().testAccessFlag(AccessFlag.ACC_STATIC)) { 143 if (isRecord(owner)) {
143 continue; 144 ClassFile classFile = ((JavaRefTypeInstance) owner).getClassFile();
144 } 145
145 146 for (ClassFileField field : classFile.getFields()) {
146 EntryMapping mapping = mapper.getDeobfMapping(getFieldEntry(owner, field.getFieldName(), field.getField().getDescriptor())); 147 if (field.getField().testAccessFlag(AccessFlag.ACC_STATIC)) {
147 if (mapping == null) { 148 continue;
148 continue; 149 }
149 } 150
150 151 EntryMapping mapping = mapper.getDeobfMapping(getFieldEntry(owner, field.getFieldName(), field.getField().getDescriptor()));
151 String javaDoc = mapping.javadoc(); 152
152 if (javaDoc != null) { 153 if (mapping == null) {
153 recordComponentDocs.add(String.format("@param %s %s", mapping.targetName(), javaDoc)); 154 continue;
154 } 155 }
155 } 156
156 } 157 String javaDoc = mapping.javadoc();
157 158
158 EntryMapping mapping = mapper.getDeobfMapping(getClassEntry(owner)); 159 if (javaDoc != null) {
159 160 recordComponentDocs.add(String.format("@param %s %s", mapping.targetName(), javaDoc));
160 String javadoc = null; 161 }
161 if (mapping != null) { 162 }
162 javadoc = mapping.javadoc(); 163 }
163 } 164
164 165 EntryMapping mapping = mapper.getDeobfMapping(getClassEntry(owner));
165 if (javadoc != null || !recordComponentDocs.isEmpty()) { 166
166 print("/**").newln(); 167 String javadoc = null;
167 if (javadoc != null) { 168
168 for (String line : javadoc.split("\\R")) { 169 if (mapping != null) {
169 print(" * ").print(line).newln(); 170 javadoc = mapping.javadoc();
170 } 171 }
171 172
172 if (!recordComponentDocs.isEmpty()) { 173 if (javadoc != null || !recordComponentDocs.isEmpty()) {
173 print(" * ").newln(); 174 print("/**").newln();
174 }
175 }
176
177 for (String componentDoc : recordComponentDocs) {
178 print(" * ").print(componentDoc).newln();
179 }
180
181 print(" */").newln();
182 }
183 }
184 return this;
185 }
186
187 @Override
188 public Dumper dumpMethodDoc(MethodPrototype method) {
189 if (mapper != null) {
190 List<String> lines = new ArrayList<>();
191 MethodEntry methodEntry = getMethodEntry(method);
192 EntryMapping mapping = mapper.getDeobfMapping(methodEntry);
193 if (mapping != null) {
194 String javadoc = mapping.javadoc();
195 if (javadoc != null) {
196 lines.addAll(Arrays.asList(javadoc.split("\\R")));
197 }
198 }
199
200 Collection<Entry<?>> children = mapper.getObfChildren(methodEntry);
201
202 if (children != null && !children.isEmpty()) {
203 for (Entry<?> each : children) {
204 if (each instanceof LocalVariableEntry) {
205 EntryMapping paramMapping = mapper.getDeobfMapping(each);
206 if (paramMapping != null) {
207 String javadoc = paramMapping.javadoc();
208 if (javadoc != null) {
209 lines.addAll(Arrays.asList(("@param " + paramMapping.targetName() + " " + javadoc).split("\\R")));
210 }
211 }
212 }
213 }
214 }
215
216 if (!lines.isEmpty()) {
217 print("/**").newln();
218 for (String line : lines) {
219 print(" * ").print(line).newln();
220 }
221 print(" */").newln();
222 }
223 }
224 return this;
225 }
226
227 @Override
228 public Dumper dumpFieldDoc(Field field, JavaTypeInstance owner) {
229 boolean recordComponent = isRecord(owner) && !field.testAccessFlag(AccessFlag.ACC_STATIC);
230 if (mapper != null && !recordComponent) {
231 EntryMapping mapping = mapper.getDeobfMapping(getFieldEntry(owner, field.getFieldName(), field.getDescriptor()));
232 if (mapping != null) {
233 String javadoc = mapping.javadoc();
234 if (javadoc != null) {
235 print("/**").newln();
236 for (String line : javadoc.split("\\R")) {
237 print(" * ").print(line).newln();
238 }
239 print(" */").newln();
240 }
241 }
242 }
243 return this;
244 }
245
246 @Override
247 public Dumper methodName(String name, MethodPrototype method, boolean special, boolean defines) {
248 Entry<?> entry = getMethodEntry(method);
249 super.methodName(name, method, special, defines);
250 int now = sb.length();
251 Token token = new Token(now - name.length(), now, name);
252
253 if (entry != null) {
254 if (defines) {
255 index.addDeclaration(token, entry); // override as cfr reuses local vars
256 } else {
257 index.addReference(token, entry, contextMethod);
258 }
259 }
260
261 return this;
262 }
263
264 @Override
265 public Dumper parameterName(String name, Object ref, MethodPrototype method, int index, boolean defines) {
266 super.parameterName(name, ref, method, index, defines);
267 int now = sb.length();
268 Token token = new Token(now - name.length(), now, name);
269 Entry<?> entry;
270 if (defines) {
271 refs.put(ref, entry = getParameterEntry(method, index, name));
272 } else {
273 entry = refs.get(ref);
274 }
275
276 if (entry != null) {
277 if (defines) {
278 this.index.addDeclaration(token, entry);
279 } else {
280 this.index.addReference(token, entry, contextMethod);
281 }
282 }
283
284 return this;
285 }
286
287 @Override
288 public Dumper variableName(String name, NamedVariable variable, boolean defines) {
289 // todo catch var declarations in the future
290 return super.variableName(name, variable, defines);
291 }
292
293 @Override
294 public Dumper identifier(String name, Object ref, boolean defines) {
295 super.identifier(name, ref, defines);
296 Entry<?> entry;
297 if (defines) {
298 refs.remove(ref);
299 return this;
300 }
301 if ((entry = refs.get(ref)) == null) {
302 return this;
303 }
304 int now = sb.length();
305 Token token = new Token(now - name.length(), now, name);
306 index.addReference(token, entry, contextMethod);
307 return this;
308 }
309
310 @Override
311 public Dumper fieldName(String name, String descriptor, JavaTypeInstance owner, boolean hiddenDeclaration, boolean isStatic, boolean defines) {
312 super.fieldName(name, descriptor, owner, hiddenDeclaration, isStatic, defines);
313 int now = sb.length();
314 Token token = new Token(now - name.length(), now, name);
315 if (descriptor != null) {
316 Entry<?> entry = getFieldEntry(owner, name, descriptor);
317
318 if (defines) {
319 index.addDeclaration(token, entry);
320 } else {
321 index.addReference(token, entry, contextMethod);
322 }
323 }
324
325 return this;
326 }
327
328 @Override
329 public Dumper dump(JavaTypeInstance type) {
330 dumpClass(TypeContext.None, type, false);
331 return this;
332 }
333
334 @Override
335 public Dumper dump(JavaTypeInstance type, boolean defines) {
336 dumpClass(TypeContext.None, type, defines);
337 return this;
338 }
339
340 @Override
341 public Dumper dump(JavaTypeInstance type, TypeContext context) {
342 dumpClass(context, type, false);
343 return this;
344 }
345
346 private void dumpClass(TypeContext context, JavaTypeInstance type, boolean defines) {
347 if (type instanceof JavaRefTypeInstance) {
348 type.dumpInto(this, typeUsage, context);
349 String name = typeUsage.getName(type, context); // the actually used name, dump will indent
350 int now = sb.length();
351 Token token = new Token(now - name.length(), now, name);
352
353 if (defines) {
354 index.addDeclaration(token, getClassEntry(type));
355 } else {
356 index.addReference(token, getClassEntry(type), contextMethod);
357 }
358 return;
359 }
360
361 type.dumpInto(this, typeUsage, context);
362 }
363
364 /**
365 * {@inheritDoc}
366 *
367 * <p>Otherwise the type usage override dumper will not go through the type instance dump
368 * we have here.
369 */
370 @Override
371 public Dumper withTypeUsageInformation(TypeUsageInformation innerclassTypeUsageInformation) {
372 return new EnigmaDumper(this.sb, sourceSettings, innerclassTypeUsageInformation, options, mapper, index, dumperContext);
373 }
374
375 @Override
376 public void informBytecodeLoc(HasByteCodeLoc loc) {
377 Collection<Method> methods = loc.getLoc().getMethods();
378 if (!methods.isEmpty()) {
379 this.contextMethod = getMethodEntry(methods.iterator().next().getMethodPrototype());
380 }
381 }
382
383 public SourceIndex getIndex() {
384 index.setSource(getString());
385 return index;
386 }
387
388 public String getString() {
389 return sb.toString();
390 }
391
392 private boolean isRecord(JavaTypeInstance javaTypeInstance) {
393 if (javaTypeInstance instanceof JavaRefTypeInstance) {
394 ClassFile classFile = ((JavaRefTypeInstance) javaTypeInstance).getClassFile();
395 return classFile.getClassSignature().getSuperClass().getRawName().equals("java.lang.Record");
396 }
397
398 return false;
399 }
400 175
176 if (javadoc != null) {
177 for (String line : javadoc.split("\\R")) {
178 print(" * ").print(line).newln();
179 }
180
181 if (!recordComponentDocs.isEmpty()) {
182 print(" * ").newln();
183 }
184 }
185
186 for (String componentDoc : recordComponentDocs) {
187 print(" * ").print(componentDoc).newln();
188 }
189
190 print(" */").newln();
191 }
192 }
193
194 return this;
195 }
196
197 @Override
198 public Dumper dumpMethodDoc(MethodPrototype method) {
199 if (mapper != null) {
200 List<String> lines = new ArrayList<>();
201 MethodEntry methodEntry = getMethodEntry(method);
202 EntryMapping mapping = mapper.getDeobfMapping(methodEntry);
203
204 if (mapping != null) {
205 String javadoc = mapping.javadoc();
206
207 if (javadoc != null) {
208 lines.addAll(Arrays.asList(javadoc.split("\\R")));
209 }
210 }
211
212 Collection<Entry<?>> children = mapper.getObfChildren(methodEntry);
213
214 if (children != null && !children.isEmpty()) {
215 for (Entry<?> each : children) {
216 if (each instanceof LocalVariableEntry) {
217 EntryMapping paramMapping = mapper.getDeobfMapping(each);
218
219 if (paramMapping != null) {
220 String javadoc = paramMapping.javadoc();
221
222 if (javadoc != null) {
223 lines.addAll(Arrays.asList(("@param " + paramMapping.targetName() + " " + javadoc).split("\\R")));
224 }
225 }
226 }
227 }
228 }
229
230 if (!lines.isEmpty()) {
231 print("/**").newln();
232
233 for (String line : lines) {
234 print(" * ").print(line).newln();
235 }
236
237 print(" */").newln();
238 }
239 }
240
241 return this;
242 }
243
244 @Override
245 public Dumper dumpFieldDoc(Field field, JavaTypeInstance owner) {
246 boolean recordComponent = isRecord(owner) && !field.testAccessFlag(AccessFlag.ACC_STATIC);
247
248 if (mapper != null && !recordComponent) {
249 EntryMapping mapping = mapper.getDeobfMapping(getFieldEntry(owner, field.getFieldName(), field.getDescriptor()));
250
251 if (mapping != null) {
252 String javadoc = mapping.javadoc();
253
254 if (javadoc != null) {
255 print("/**").newln();
256
257 for (String line : javadoc.split("\\R")) {
258 print(" * ").print(line).newln();
259 }
260
261 print(" */").newln();
262 }
263 }
264 }
265
266 return this;
267 }
268
269 @Override
270 public Dumper methodName(String name, MethodPrototype method, boolean special, boolean defines) {
271 Entry<?> entry = getMethodEntry(method);
272 super.methodName(name, method, special, defines);
273 int now = sb.length();
274 Token token = new Token(now - name.length(), now, name);
275
276 if (entry != null) {
277 if (defines) {
278 index.addDeclaration(token, entry); // override as cfr reuses local vars
279 } else {
280 index.addReference(token, entry, contextMethod);
281 }
282 }
283
284 return this;
285 }
286
287 @Override
288 public Dumper parameterName(String name, Object ref, MethodPrototype method, int index, boolean defines) {
289 super.parameterName(name, ref, method, index, defines);
290 int now = sb.length();
291 Token token = new Token(now - name.length(), now, name);
292 Entry<?> entry;
293
294 if (defines) {
295 refs.put(ref, entry = getParameterEntry(method, index, name));
296 } else {
297 entry = refs.get(ref);
298 }
299
300 if (entry != null) {
301 if (defines) {
302 this.index.addDeclaration(token, entry);
303 } else {
304 this.index.addReference(token, entry, contextMethod);
305 }
306 }
307
308 return this;
309 }
310
311 @Override
312 public Dumper variableName(String name, NamedVariable variable, boolean defines) {
313 // todo catch var declarations in the future
314 return super.variableName(name, variable, defines);
315 }
316
317 @Override
318 public Dumper identifier(String name, Object ref, boolean defines) {
319 super.identifier(name, ref, defines);
320 Entry<?> entry;
321
322 if (defines) {
323 refs.remove(ref);
324 return this;
325 }
326
327 if ((entry = refs.get(ref)) == null) {
328 return this;
329 }
330
331 int now = sb.length();
332 Token token = new Token(now - name.length(), now, name);
333 index.addReference(token, entry, contextMethod);
334 return this;
335 }
336
337 @Override
338 public Dumper fieldName(String name, String descriptor, JavaTypeInstance owner, boolean hiddenDeclaration, boolean isStatic, boolean defines) {
339 super.fieldName(name, descriptor, owner, hiddenDeclaration, isStatic, defines);
340 int now = sb.length();
341 Token token = new Token(now - name.length(), now, name);
342
343 if (descriptor != null) {
344 Entry<?> entry = getFieldEntry(owner, name, descriptor);
345
346 if (defines) {
347 index.addDeclaration(token, entry);
348 } else {
349 index.addReference(token, entry, contextMethod);
350 }
351 }
352
353 return this;
354 }
355
356 @Override
357 public Dumper dump(JavaTypeInstance type) {
358 dumpClass(TypeContext.None, type, false);
359 return this;
360 }
361
362 @Override
363 public Dumper dump(JavaTypeInstance type, boolean defines) {
364 dumpClass(TypeContext.None, type, defines);
365 return this;
366 }
367
368 @Override
369 public Dumper dump(JavaTypeInstance type, TypeContext context) {
370 dumpClass(context, type, false);
371 return this;
372 }
373
374 private void dumpClass(TypeContext context, JavaTypeInstance type, boolean defines) {
375 if (type instanceof JavaRefTypeInstance) {
376 type.dumpInto(this, typeUsage, context);
377 String name = typeUsage.getName(type, context); // the actually used name, dump will indent
378 int now = sb.length();
379 Token token = new Token(now - name.length(), now, name);
380
381 if (defines) {
382 index.addDeclaration(token, getClassEntry(type));
383 } else {
384 index.addReference(token, getClassEntry(type), contextMethod);
385 }
386
387 return;
388 }
389
390 type.dumpInto(this, typeUsage, context);
391 }
392
393 /**
394 * {@inheritDoc}
395 *
396 * <p>Otherwise the type usage override dumper will not go through the type instance dump
397 * we have here.
398 */
399 @Override
400 public Dumper withTypeUsageInformation(TypeUsageInformation innerclassTypeUsageInformation) {
401 return new EnigmaDumper(this.sb, sourceSettings, innerclassTypeUsageInformation, options, mapper, index, dumperContext);
402 }
403
404 @Override
405 public void informBytecodeLoc(HasByteCodeLoc loc) {
406 Collection<Method> methods = loc.getLoc().getMethods();
407
408 if (!methods.isEmpty()) {
409 this.contextMethod = getMethodEntry(methods.iterator().next().getMethodPrototype());
410 }
411 }
412
413 public SourceIndex getIndex() {
414 index.setSource(getString());
415 return index;
416 }
417
418 public String getString() {
419 return sb.toString();
420 }
421
422 private boolean isRecord(JavaTypeInstance javaTypeInstance) {
423 if (javaTypeInstance instanceof JavaRefTypeInstance) {
424 ClassFile classFile = ((JavaRefTypeInstance) javaTypeInstance).getClassFile();
425 return classFile.getClassSignature().getSuperClass().getRawName().equals("java.lang.Record");
426 }
427
428 return false;
429 }
401} 430}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/EntryParser.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/EntryParser.java
index 2fae61a..dade7c6 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/procyon/EntryParser.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/EntryParser.java
@@ -4,6 +4,7 @@ import com.strobel.assembler.metadata.FieldDefinition;
4import com.strobel.assembler.metadata.MethodDefinition; 4import com.strobel.assembler.metadata.MethodDefinition;
5import com.strobel.assembler.metadata.TypeDefinition; 5import com.strobel.assembler.metadata.TypeDefinition;
6import com.strobel.assembler.metadata.TypeReference; 6import com.strobel.assembler.metadata.TypeReference;
7
7import cuchaz.enigma.translation.representation.AccessFlags; 8import cuchaz.enigma.translation.representation.AccessFlags;
8import cuchaz.enigma.translation.representation.MethodDescriptor; 9import cuchaz.enigma.translation.representation.MethodDescriptor;
9import cuchaz.enigma.translation.representation.Signature; 10import cuchaz.enigma.translation.representation.Signature;
@@ -14,36 +15,36 @@ import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
14import cuchaz.enigma.translation.representation.entry.MethodDefEntry; 15import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
15 16
16public class EntryParser { 17public class EntryParser {
17 public static FieldDefEntry parse(FieldDefinition definition) { 18 public static FieldDefEntry parse(FieldDefinition definition) {
18 ClassEntry owner = parse(definition.getDeclaringType()); 19 ClassEntry owner = parse(definition.getDeclaringType());
19 TypeDescriptor descriptor = new TypeDescriptor(definition.getErasedSignature()); 20 TypeDescriptor descriptor = new TypeDescriptor(definition.getErasedSignature());
20 Signature signature = Signature.createTypedSignature(definition.getSignature()); 21 Signature signature = Signature.createTypedSignature(definition.getSignature());
21 AccessFlags access = new AccessFlags(definition.getModifiers()); 22 AccessFlags access = new AccessFlags(definition.getModifiers());
22 return new FieldDefEntry(owner, definition.getName(), descriptor, signature, access, null); 23 return new FieldDefEntry(owner, definition.getName(), descriptor, signature, access, null);
23 } 24 }
24 25
25 public static ClassDefEntry parse(TypeDefinition def) { 26 public static ClassDefEntry parse(TypeDefinition def) {
26 String name = def.getInternalName(); 27 String name = def.getInternalName();
27 Signature signature = Signature.createSignature(def.getSignature()); 28 Signature signature = Signature.createSignature(def.getSignature());
28 AccessFlags access = new AccessFlags(def.getModifiers()); 29 AccessFlags access = new AccessFlags(def.getModifiers());
29 ClassEntry superClass = def.getBaseType() != null ? parse(def.getBaseType()) : null; 30 ClassEntry superClass = def.getBaseType() != null ? parse(def.getBaseType()) : null;
30 ClassEntry[] interfaces = def.getExplicitInterfaces().stream().map(EntryParser::parse).toArray(ClassEntry[]::new); 31 ClassEntry[] interfaces = def.getExplicitInterfaces().stream().map(EntryParser::parse).toArray(ClassEntry[]::new);
31 return new ClassDefEntry(name, signature, access, superClass, interfaces); 32 return new ClassDefEntry(name, signature, access, superClass, interfaces);
32 } 33 }
33 34
34 public static ClassEntry parse(TypeReference typeReference) { 35 public static ClassEntry parse(TypeReference typeReference) {
35 return new ClassEntry(typeReference.getInternalName()); 36 return new ClassEntry(typeReference.getInternalName());
36 } 37 }
37 38
38 public static MethodDefEntry parse(MethodDefinition definition) { 39 public static MethodDefEntry parse(MethodDefinition definition) {
39 ClassEntry classEntry = parse(definition.getDeclaringType()); 40 ClassEntry classEntry = parse(definition.getDeclaringType());
40 MethodDescriptor descriptor = new MethodDescriptor(definition.getErasedSignature()); 41 MethodDescriptor descriptor = new MethodDescriptor(definition.getErasedSignature());
41 Signature signature = Signature.createSignature(definition.getSignature()); 42 Signature signature = Signature.createSignature(definition.getSignature());
42 AccessFlags access = new AccessFlags(definition.getModifiers()); 43 AccessFlags access = new AccessFlags(definition.getModifiers());
43 return new MethodDefEntry(classEntry, definition.getName(), descriptor, signature, access, null); 44 return new MethodDefEntry(classEntry, definition.getName(), descriptor, signature, access, null);
44 } 45 }
45 46
46 public static TypeDescriptor parseTypeDescriptor(TypeReference type) { 47 public static TypeDescriptor parseTypeDescriptor(TypeReference type) {
47 return new TypeDescriptor(type.getErasedSignature()); 48 return new TypeDescriptor(type.getErasedSignature());
48 } 49 }
49} 50}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java
index bc3d072..3ba112f 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java
@@ -11,15 +11,23 @@ import com.strobel.decompiler.languages.java.JavaFormattingOptions;
11import com.strobel.decompiler.languages.java.ast.AstBuilder; 11import com.strobel.decompiler.languages.java.ast.AstBuilder;
12import com.strobel.decompiler.languages.java.ast.CompilationUnit; 12import com.strobel.decompiler.languages.java.ast.CompilationUnit;
13import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; 13import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor;
14import org.checkerframework.checker.nullness.qual.Nullable;
15import org.objectweb.asm.tree.ClassNode;
16
14import cuchaz.enigma.classprovider.ClassProvider; 17import cuchaz.enigma.classprovider.ClassProvider;
15import cuchaz.enigma.source.Source;
16import cuchaz.enigma.source.Decompiler; 18import cuchaz.enigma.source.Decompiler;
19import cuchaz.enigma.source.Source;
17import cuchaz.enigma.source.SourceSettings; 20import cuchaz.enigma.source.SourceSettings;
18import cuchaz.enigma.source.procyon.transformers.*; 21import cuchaz.enigma.source.procyon.transformers.AddJavadocsAstTransform;
22import cuchaz.enigma.source.procyon.transformers.DropImportAstTransform;
23import cuchaz.enigma.source.procyon.transformers.DropVarModifiersAstTransform;
24import cuchaz.enigma.source.procyon.transformers.InvalidIdentifierFix;
25import cuchaz.enigma.source.procyon.transformers.Java8Generics;
26import cuchaz.enigma.source.procyon.transformers.ObfuscatedEnumSwitchRewriterTransform;
27import cuchaz.enigma.source.procyon.transformers.RemoveObjectCasts;
28import cuchaz.enigma.source.procyon.transformers.VarargsFixer;
19import cuchaz.enigma.translation.mapping.EntryRemapper; 29import cuchaz.enigma.translation.mapping.EntryRemapper;
20import cuchaz.enigma.utils.AsmUtil; 30import cuchaz.enigma.utils.AsmUtil;
21import org.checkerframework.checker.nullness.qual.Nullable;
22import org.objectweb.asm.tree.ClassNode;
23 31
24public class ProcyonDecompiler implements Decompiler { 32public class ProcyonDecompiler implements Decompiler {
25 private final SourceSettings settings; 33 private final SourceSettings settings;
@@ -63,6 +71,7 @@ public class ProcyonDecompiler implements Decompiler {
63 @Override 71 @Override
64 public Source getSource(String className, @Nullable EntryRemapper remapper) { 72 public Source getSource(String className, @Nullable EntryRemapper remapper) {
65 TypeReference type = metadataSystem.lookupType(className); 73 TypeReference type = metadataSystem.lookupType(className);
74
66 if (type == null) { 75 if (type == null) {
67 throw new Error(String.format("Unable to find desc: %s", className)); 76 throw new Error(String.format("Unable to find desc: %s", className));
68 } 77 }
@@ -83,8 +92,15 @@ public class ProcyonDecompiler implements Decompiler {
83 new RemoveObjectCasts(context).run(source); 92 new RemoveObjectCasts(context).run(source);
84 new Java8Generics().run(source); 93 new Java8Generics().run(source);
85 new InvalidIdentifierFix().run(source); 94 new InvalidIdentifierFix().run(source);
86 if (settings.removeImports) DropImportAstTransform.INSTANCE.run(source); 95
87 if (settings.removeVariableFinal) DropVarModifiersAstTransform.INSTANCE.run(source); 96 if (settings.removeImports) {
97 DropImportAstTransform.INSTANCE.run(source);
98 }
99
100 if (settings.removeVariableFinal) {
101 DropVarModifiersAstTransform.INSTANCE.run(source);
102 }
103
88 source.acceptVisitor(new InsertParenthesesVisitor(), null); 104 source.acceptVisitor(new InsertParenthesesVisitor(), null);
89 105
90 if (remapper != null) { 106 if (remapper != null) {
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java
index 5b5b70e..a3f878b 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java
@@ -1,49 +1,50 @@
1package cuchaz.enigma.source.procyon; 1package cuchaz.enigma.source.procyon;
2 2
3import java.io.StringWriter;
4
3import com.strobel.decompiler.DecompilerSettings; 5import com.strobel.decompiler.DecompilerSettings;
4import com.strobel.decompiler.PlainTextOutput; 6import com.strobel.decompiler.PlainTextOutput;
5import com.strobel.decompiler.languages.java.JavaOutputVisitor; 7import com.strobel.decompiler.languages.java.JavaOutputVisitor;
6import com.strobel.decompiler.languages.java.ast.CompilationUnit; 8import com.strobel.decompiler.languages.java.ast.CompilationUnit;
9
7import cuchaz.enigma.source.Source; 10import cuchaz.enigma.source.Source;
8import cuchaz.enigma.source.SourceIndex; 11import cuchaz.enigma.source.SourceIndex;
9import cuchaz.enigma.source.procyon.index.SourceIndexVisitor; 12import cuchaz.enigma.source.procyon.index.SourceIndexVisitor;
10import cuchaz.enigma.source.procyon.transformers.AddJavadocsAstTransform; 13import cuchaz.enigma.source.procyon.transformers.AddJavadocsAstTransform;
11import cuchaz.enigma.translation.mapping.EntryRemapper; 14import cuchaz.enigma.translation.mapping.EntryRemapper;
12 15
13import java.io.StringWriter;
14
15public class ProcyonSource implements Source { 16public class ProcyonSource implements Source {
16 private final DecompilerSettings settings; 17 private final DecompilerSettings settings;
17 private final CompilationUnit tree; 18 private final CompilationUnit tree;
18 private String string; 19 private String string;
19 20
20 public ProcyonSource(CompilationUnit tree, DecompilerSettings settings) { 21 public ProcyonSource(CompilationUnit tree, DecompilerSettings settings) {
21 this.settings = settings; 22 this.settings = settings;
22 this.tree = tree; 23 this.tree = tree;
23 } 24 }
24 25
25 @Override 26 @Override
26 public SourceIndex index() { 27 public SourceIndex index() {
27 SourceIndex index = new SourceIndex(asString()); 28 SourceIndex index = new SourceIndex(asString());
28 tree.acceptVisitor(new SourceIndexVisitor(), index); 29 tree.acceptVisitor(new SourceIndexVisitor(), index);
29 return index; 30 return index;
30 } 31 }
31 32
32 @Override 33 @Override
33 public String asString() { 34 public String asString() {
34 if (string == null) { 35 if (string == null) {
35 StringWriter writer = new StringWriter(); 36 StringWriter writer = new StringWriter();
36 tree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(writer), settings), null); 37 tree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(writer), settings), null);
37 string = writer.toString(); 38 string = writer.toString();
38 } 39 }
39 40
40 return string; 41 return string;
41 } 42 }
42 43
43 @Override 44 @Override
44 public Source withJavadocs(EntryRemapper remapper) { 45 public Source withJavadocs(EntryRemapper remapper) {
45 CompilationUnit remappedTree = (CompilationUnit) tree.clone(); 46 CompilationUnit remappedTree = (CompilationUnit) tree.clone();
46 new AddJavadocsAstTransform(remapper).run(remappedTree); 47 new AddJavadocsAstTransform(remapper).run(remappedTree);
47 return new ProcyonSource(remappedTree, settings); 48 return new ProcyonSource(remappedTree, settings);
48 } 49 }
49} 50}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexClassVisitor.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexClassVisitor.java
index f6eeb15..174431f 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexClassVisitor.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexClassVisitor.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.source.procyon.index; 12package cuchaz.enigma.source.procyon.index;
13 13
@@ -16,10 +16,22 @@ import com.strobel.assembler.metadata.MethodDefinition;
16import com.strobel.assembler.metadata.TypeDefinition; 16import com.strobel.assembler.metadata.TypeDefinition;
17import com.strobel.assembler.metadata.TypeReference; 17import com.strobel.assembler.metadata.TypeReference;
18import com.strobel.decompiler.languages.TextLocation; 18import com.strobel.decompiler.languages.TextLocation;
19import com.strobel.decompiler.languages.java.ast.*; 19import com.strobel.decompiler.languages.java.ast.AstNode;
20import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration;
21import com.strobel.decompiler.languages.java.ast.EnumValueDeclaration;
22import com.strobel.decompiler.languages.java.ast.FieldDeclaration;
23import com.strobel.decompiler.languages.java.ast.Keys;
24import com.strobel.decompiler.languages.java.ast.MethodDeclaration;
25import com.strobel.decompiler.languages.java.ast.SimpleType;
26import com.strobel.decompiler.languages.java.ast.TypeDeclaration;
27import com.strobel.decompiler.languages.java.ast.VariableInitializer;
28
20import cuchaz.enigma.source.SourceIndex; 29import cuchaz.enigma.source.SourceIndex;
21import cuchaz.enigma.source.procyon.EntryParser; 30import cuchaz.enigma.source.procyon.EntryParser;
22import cuchaz.enigma.translation.representation.entry.*; 31import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
32import cuchaz.enigma.translation.representation.entry.ClassEntry;
33import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
34import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
23 35
24public class SourceIndexClassVisitor extends SourceIndexVisitor { 36public class SourceIndexClassVisitor extends SourceIndexVisitor {
25 private ClassDefEntry classEntry; 37 private ClassDefEntry classEntry;
@@ -33,6 +45,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
33 // is this this class, or a subtype? 45 // is this this class, or a subtype?
34 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); 46 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
35 ClassDefEntry classEntry = EntryParser.parse(def); 47 ClassDefEntry classEntry = EntryParser.parse(def);
48
36 if (!classEntry.equals(this.classEntry)) { 49 if (!classEntry.equals(this.classEntry)) {
37 // it's a subtype, recurse 50 // it's a subtype, recurse
38 index.addDeclaration(TokenFactory.createToken(index, node.getNameToken()), classEntry); 51 index.addDeclaration(TokenFactory.createToken(index, node.getNameToken()), classEntry);
@@ -45,6 +58,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
45 @Override 58 @Override
46 public Void visitSimpleType(SimpleType node, SourceIndex index) { 59 public Void visitSimpleType(SimpleType node, SourceIndex index) {
47 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); 60 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE);
61
48 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { 62 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) {
49 ClassEntry classEntry = new ClassEntry(ref.getInternalName()); 63 ClassEntry classEntry = new ClassEntry(ref.getInternalName());
50 index.addReference(TokenFactory.createToken(index, node.getIdentifierToken()), classEntry, this.classEntry); 64 index.addReference(TokenFactory.createToken(index, node.getIdentifierToken()), classEntry, this.classEntry);
@@ -58,10 +72,12 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
58 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); 72 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
59 MethodDefEntry methodEntry = EntryParser.parse(def); 73 MethodDefEntry methodEntry = EntryParser.parse(def);
60 AstNode tokenNode = node.getNameToken(); 74 AstNode tokenNode = node.getNameToken();
75
61 if (methodEntry.isConstructor() && methodEntry.getName().equals("<clinit>")) { 76 if (methodEntry.isConstructor() && methodEntry.getName().equals("<clinit>")) {
62 // for static initializers, check elsewhere for the token node 77 // for static initializers, check elsewhere for the token node
63 tokenNode = node.getModifiers().firstOrNullObject(); 78 tokenNode = node.getModifiers().firstOrNullObject();
64 } 79 }
80
65 index.addDeclaration(TokenFactory.createToken(index, tokenNode), methodEntry); 81 index.addDeclaration(TokenFactory.createToken(index, tokenNode), methodEntry);
66 return node.acceptVisitor(new SourceIndexMethodVisitor(methodEntry), index); 82 return node.acceptVisitor(new SourceIndexMethodVisitor(methodEntry), index);
67 } 83 }
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexMethodVisitor.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexMethodVisitor.java
index 8fc8e82..12dca73 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexMethodVisitor.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexMethodVisitor.java
@@ -1,31 +1,56 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.source.procyon.index; 12package cuchaz.enigma.source.procyon.index;
13 13
14import java.util.HashMap;
15import java.util.Map;
16
14import com.google.common.collect.HashMultimap; 17import com.google.common.collect.HashMultimap;
15import com.google.common.collect.Multimap; 18import com.google.common.collect.Multimap;
16import com.strobel.assembler.metadata.*; 19import com.strobel.assembler.metadata.FieldReference;
20import com.strobel.assembler.metadata.MemberReference;
21import com.strobel.assembler.metadata.MethodDefinition;
22import com.strobel.assembler.metadata.MethodReference;
23import com.strobel.assembler.metadata.ParameterDefinition;
24import com.strobel.assembler.metadata.TypeReference;
25import com.strobel.assembler.metadata.VariableDefinition;
17import com.strobel.decompiler.ast.Variable; 26import com.strobel.decompiler.ast.Variable;
18import com.strobel.decompiler.languages.TextLocation; 27import com.strobel.decompiler.languages.TextLocation;
19import com.strobel.decompiler.languages.java.ast.*; 28import com.strobel.decompiler.languages.java.ast.AstNode;
29import com.strobel.decompiler.languages.java.ast.AstNodeCollection;
30import com.strobel.decompiler.languages.java.ast.Identifier;
31import com.strobel.decompiler.languages.java.ast.IdentifierExpression;
32import com.strobel.decompiler.languages.java.ast.InvocationExpression;
33import com.strobel.decompiler.languages.java.ast.Keys;
34import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression;
35import com.strobel.decompiler.languages.java.ast.MethodGroupExpression;
36import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression;
37import com.strobel.decompiler.languages.java.ast.ParameterDeclaration;
38import com.strobel.decompiler.languages.java.ast.SimpleType;
39import com.strobel.decompiler.languages.java.ast.SuperReferenceExpression;
40import com.strobel.decompiler.languages.java.ast.ThisReferenceExpression;
41import com.strobel.decompiler.languages.java.ast.VariableDeclarationStatement;
42import com.strobel.decompiler.languages.java.ast.VariableInitializer;
43
20import cuchaz.enigma.source.SourceIndex; 44import cuchaz.enigma.source.SourceIndex;
21import cuchaz.enigma.source.procyon.EntryParser; 45import cuchaz.enigma.source.procyon.EntryParser;
22import cuchaz.enigma.translation.representation.MethodDescriptor; 46import cuchaz.enigma.translation.representation.MethodDescriptor;
23import cuchaz.enigma.translation.representation.TypeDescriptor; 47import cuchaz.enigma.translation.representation.TypeDescriptor;
24import cuchaz.enigma.translation.representation.entry.*; 48import cuchaz.enigma.translation.representation.entry.ClassEntry;
25 49import cuchaz.enigma.translation.representation.entry.Entry;
26import java.lang.Error; 50import cuchaz.enigma.translation.representation.entry.FieldEntry;
27import java.util.HashMap; 51import cuchaz.enigma.translation.representation.entry.LocalVariableDefEntry;
28import java.util.Map; 52import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
53import cuchaz.enigma.translation.representation.entry.MethodEntry;
29 54
30public class SourceIndexMethodVisitor extends SourceIndexVisitor { 55public class SourceIndexMethodVisitor extends SourceIndexVisitor {
31 private final MethodDefEntry methodEntry; 56 private final MethodDefEntry methodEntry;
@@ -45,12 +70,15 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
45 // get the behavior entry 70 // get the behavior entry
46 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 71 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
47 MethodEntry methodEntry = null; 72 MethodEntry methodEntry = null;
73
48 if (ref instanceof MethodReference) { 74 if (ref instanceof MethodReference) {
49 methodEntry = new MethodEntry(classEntry, ref.getName(), new MethodDescriptor(ref.getErasedSignature())); 75 methodEntry = new MethodEntry(classEntry, ref.getName(), new MethodDescriptor(ref.getErasedSignature()));
50 } 76 }
77
51 if (methodEntry != null) { 78 if (methodEntry != null) {
52 // get the node for the token 79 // get the node for the token
53 AstNode tokenNode = null; 80 AstNode tokenNode = null;
81
54 if (node.getTarget() instanceof MemberReferenceExpression) { 82 if (node.getTarget() instanceof MemberReferenceExpression) {
55 tokenNode = ((MemberReferenceExpression) node.getTarget()).getMemberNameToken(); 83 tokenNode = ((MemberReferenceExpression) node.getTarget()).getMemberNameToken();
56 } else if (node.getTarget() instanceof SuperReferenceExpression) { 84 } else if (node.getTarget() instanceof SuperReferenceExpression) {
@@ -58,6 +86,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
58 } else if (node.getTarget() instanceof ThisReferenceExpression) { 86 } else if (node.getTarget() instanceof ThisReferenceExpression) {
59 tokenNode = node.getTarget(); 87 tokenNode = node.getTarget();
60 } 88 }
89
61 if (tokenNode != null) { 90 if (tokenNode != null) {
62 index.addReference(TokenFactory.createToken(index, tokenNode), methodEntry, this.methodEntry); 91 index.addReference(TokenFactory.createToken(index, tokenNode), methodEntry, this.methodEntry);
63 } 92 }
@@ -65,17 +94,18 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
65 } 94 }
66 95
67 // Check for identifier 96 // Check for identifier
68 node.getArguments().stream().filter(expression -> expression instanceof IdentifierExpression) 97 node.getArguments().stream().filter(expression -> expression instanceof IdentifierExpression).forEach(expression -> this.checkIdentifier((IdentifierExpression) expression, index));
69 .forEach(expression -> this.checkIdentifier((IdentifierExpression) expression, index));
70 return visitChildren(node, index); 98 return visitChildren(node, index);
71 } 99 }
72 100
73 @Override 101 @Override
74 public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { 102 public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) {
75 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 103 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
104
76 if (ref instanceof FieldReference) { 105 if (ref instanceof FieldReference) {
77 // make sure this is actually a field 106 // make sure this is actually a field
78 String erasedSignature = ref.getErasedSignature(); 107 String erasedSignature = ref.getErasedSignature();
108
79 if (erasedSignature.indexOf('(') >= 0) { 109 if (erasedSignature.indexOf('(') >= 0) {
80 throw new Error("Expected a field here! got " + ref); 110 throw new Error("Expected a field here! got " + ref);
81 } 111 }
@@ -91,6 +121,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
91 @Override 121 @Override
92 public Void visitSimpleType(SimpleType node, SourceIndex index) { 122 public Void visitSimpleType(SimpleType node, SourceIndex index) {
93 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); 123 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE);
124
94 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { 125 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) {
95 ClassEntry classEntry = new ClassEntry(ref.getInternalName()); 126 ClassEntry classEntry = new ClassEntry(ref.getInternalName());
96 index.addReference(TokenFactory.createToken(index, node.getIdentifierToken()), classEntry, this.methodEntry); 127 index.addReference(TokenFactory.createToken(index, node.getIdentifierToken()), classEntry, this.methodEntry);
@@ -106,6 +137,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
106 137
107 if (parameterIndex >= 0) { 138 if (parameterIndex >= 0) {
108 MethodDefEntry ownerMethod = methodEntry; 139 MethodDefEntry ownerMethod = methodEntry;
140
109 if (def.getMethod() instanceof MethodDefinition) { 141 if (def.getMethod() instanceof MethodDefinition) {
110 ownerMethod = EntryParser.parse((MethodDefinition) def.getMethod()); 142 ownerMethod = EntryParser.parse((MethodDefinition) def.getMethod());
111 } 143 }
@@ -124,36 +156,46 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
124 @Override 156 @Override
125 public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { 157 public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) {
126 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 158 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
159
127 if (ref != null) { 160 if (ref != null) {
128 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 161 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
129 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new TypeDescriptor(ref.getErasedSignature())); 162 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new TypeDescriptor(ref.getErasedSignature()));
130 index.addReference(TokenFactory.createToken(index, node.getIdentifierToken()), fieldEntry, this.methodEntry); 163 index.addReference(TokenFactory.createToken(index, node.getIdentifierToken()), fieldEntry, this.methodEntry);
131 } else 164 } else {
132 this.checkIdentifier(node, index); 165 this.checkIdentifier(node, index);
166 }
167
133 return visitChildren(node, index); 168 return visitChildren(node, index);
134 } 169 }
135 170
136 private void checkIdentifier(IdentifierExpression node, SourceIndex index) { 171 private void checkIdentifier(IdentifierExpression node, SourceIndex index) {
137 if (identifierEntryCache.containsKey(node.getIdentifier())) // If it's in the argument cache, create a token! 172 if (identifierEntryCache.containsKey(node.getIdentifier())) {
173 // If it's in the argument cache, create a token!
138 index.addDeclaration(TokenFactory.createToken(index, node.getIdentifierToken()), identifierEntryCache.get(node.getIdentifier())); 174 index.addDeclaration(TokenFactory.createToken(index, node.getIdentifierToken()), identifierEntryCache.get(node.getIdentifier()));
139 else 175 } else {
140 unmatchedIdentifier.put(node.getIdentifier(), node.getIdentifierToken()); // Not matched actually, put it! 176 unmatchedIdentifier.put(node.getIdentifier(), node.getIdentifierToken()); // Not matched actually, put it!
177 }
141 } 178 }
142 179
143 private void addDeclarationToUnmatched(String key, SourceIndex index) { 180 private void addDeclarationToUnmatched(String key, SourceIndex index) {
144 Entry<?> entry = identifierEntryCache.get(key); 181 Entry<?> entry = identifierEntryCache.get(key);
145 182
146 // This cannot happened in theory 183 // This cannot happened in theory
147 if (entry == null) 184 if (entry == null) {
148 return; 185 return;
149 for (Identifier identifier : unmatchedIdentifier.get(key)) 186 }
187
188 for (Identifier identifier : unmatchedIdentifier.get(key)) {
150 index.addDeclaration(TokenFactory.createToken(index, identifier), entry); 189 index.addDeclaration(TokenFactory.createToken(index, identifier), entry);
190 }
191
151 unmatchedIdentifier.removeAll(key); 192 unmatchedIdentifier.removeAll(key);
152 } 193 }
153 194
154 @Override 195 @Override
155 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { 196 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) {
156 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 197 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
198
157 if (ref != null && node.getType() instanceof SimpleType) { 199 if (ref != null && node.getType() instanceof SimpleType) {
158 SimpleType simpleTypeNode = (SimpleType) node.getType(); 200 SimpleType simpleTypeNode = (SimpleType) node.getType();
159 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 201 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
@@ -171,13 +213,17 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
171 // Single assignation 213 // Single assignation
172 if (variables.size() == 1) { 214 if (variables.size() == 1) {
173 VariableInitializer initializer = variables.firstOrNullObject(); 215 VariableInitializer initializer = variables.firstOrNullObject();
216
174 if (initializer != null && node.getType() instanceof SimpleType) { 217 if (initializer != null && node.getType() instanceof SimpleType) {
175 Identifier identifier = initializer.getNameToken(); 218 Identifier identifier = initializer.getNameToken();
176 Variable variable = initializer.getUserData(Keys.VARIABLE); 219 Variable variable = initializer.getUserData(Keys.VARIABLE);
220
177 if (variable != null) { 221 if (variable != null) {
178 VariableDefinition originalVariable = variable.getOriginalVariable(); 222 VariableDefinition originalVariable = variable.getOriginalVariable();
223
179 if (originalVariable != null) { 224 if (originalVariable != null) {
180 int variableIndex = originalVariable.getSlot(); 225 int variableIndex = originalVariable.getSlot();
226
181 if (variableIndex >= 0) { 227 if (variableIndex >= 0) {
182 MethodDefEntry ownerMethod = EntryParser.parse(originalVariable.getDeclaringMethod()); 228 MethodDefEntry ownerMethod = EntryParser.parse(originalVariable.getDeclaringMethod());
183 TypeDescriptor variableType = EntryParser.parseTypeDescriptor(originalVariable.getVariableType()); 229 TypeDescriptor variableType = EntryParser.parseTypeDescriptor(originalVariable.getVariableType());
@@ -190,6 +236,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
190 } 236 }
191 } 237 }
192 } 238 }
239
193 return visitChildren(node, index); 240 return visitChildren(node, index);
194 } 241 }
195 242
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexVisitor.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexVisitor.java
index dad505f..56450c7 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexVisitor.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexVisitor.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.source.procyon.index; 12package cuchaz.enigma.source.procyon.index;
13 13
@@ -16,6 +16,7 @@ import com.strobel.decompiler.languages.java.ast.AstNode;
16import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; 16import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor;
17import com.strobel.decompiler.languages.java.ast.Keys; 17import com.strobel.decompiler.languages.java.ast.Keys;
18import com.strobel.decompiler.languages.java.ast.TypeDeclaration; 18import com.strobel.decompiler.languages.java.ast.TypeDeclaration;
19
19import cuchaz.enigma.source.SourceIndex; 20import cuchaz.enigma.source.SourceIndex;
20import cuchaz.enigma.source.procyon.EntryParser; 21import cuchaz.enigma.source.procyon.EntryParser;
21import cuchaz.enigma.translation.representation.entry.ClassDefEntry; 22import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
@@ -35,6 +36,7 @@ public class SourceIndexVisitor extends DepthFirstAstVisitor<SourceIndex, Void>
35 for (final AstNode child : node.getChildren()) { 36 for (final AstNode child : node.getChildren()) {
36 child.acceptVisitor(this, index); 37 child.acceptVisitor(this, index);
37 } 38 }
39
38 return null; 40 return null;
39 } 41 }
40} 42}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/index/TokenFactory.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/index/TokenFactory.java
index dc36865..6f87895 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/procyon/index/TokenFactory.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/index/TokenFactory.java
@@ -1,46 +1,44 @@
1package cuchaz.enigma.source.procyon.index; 1package cuchaz.enigma.source.procyon.index;
2 2
3import java.util.regex.Pattern;
4
3import com.strobel.decompiler.languages.Region; 5import com.strobel.decompiler.languages.Region;
4import com.strobel.decompiler.languages.java.ast.AstNode; 6import com.strobel.decompiler.languages.java.ast.AstNode;
5import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; 7import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration;
6import com.strobel.decompiler.languages.java.ast.Identifier; 8import com.strobel.decompiler.languages.java.ast.Identifier;
7import com.strobel.decompiler.languages.java.ast.TypeDeclaration; 9import com.strobel.decompiler.languages.java.ast.TypeDeclaration;
8import cuchaz.enigma.source.Token;
9import cuchaz.enigma.source.SourceIndex;
10 10
11import java.util.regex.Pattern; 11import cuchaz.enigma.source.SourceIndex;
12import cuchaz.enigma.source.Token;
12 13
13public class TokenFactory { 14public class TokenFactory {
14 private static final Pattern ANONYMOUS_INNER = Pattern.compile("\\$\\d+$"); 15 private static final Pattern ANONYMOUS_INNER = Pattern.compile("\\$\\d+$");
15 16
16 public static Token createToken(SourceIndex index, AstNode node) { 17 public static Token createToken(SourceIndex index, AstNode node) {
17 String name = node instanceof Identifier ? ((Identifier) node).getName() : ""; 18 String name = node instanceof Identifier ? ((Identifier) node).getName() : "";
18 Region region = node.getRegion(); 19 Region region = node.getRegion();
19 20
20 if (region.getBeginLine() == 0) { 21 if (region.getBeginLine() == 0) {
21 System.err.println("Got bad region from Procyon for node " + node); 22 System.err.println("Got bad region from Procyon for node " + node);
22 return null; 23 return null;
23 } 24 }
24 25
25 int start = index.getPosition(region.getBeginLine(), region.getBeginColumn()); 26 int start = index.getPosition(region.getBeginLine(), region.getBeginColumn());
26 int end = index.getPosition(region.getEndLine(), region.getEndColumn()); 27 int end = index.getPosition(region.getEndLine(), region.getEndColumn());
27 String text = index.getSource().substring(start, end); 28 String text = index.getSource().substring(start, end);
28 Token token = new Token(start, end, text); 29 Token token = new Token(start, end, text);
29 30
30 boolean isAnonymousInner = 31 boolean isAnonymousInner = node instanceof Identifier && name.indexOf('$') >= 0 && node.getParent() instanceof ConstructorDeclaration && name.lastIndexOf('$') >= 0 && !ANONYMOUS_INNER.matcher(name).matches();
31 node instanceof Identifier && 32
32 name.indexOf('$') >= 0 && node.getParent() instanceof ConstructorDeclaration && 33 if (isAnonymousInner) {
33 name.lastIndexOf('$') >= 0 && 34 TypeDeclaration type = node.getParent().getParent() instanceof TypeDeclaration ? (TypeDeclaration) node.getParent().getParent() : null;
34 !ANONYMOUS_INNER.matcher(name).matches(); 35
35 36 if (type != null) {
36 if (isAnonymousInner) { 37 name = type.getName();
37 TypeDeclaration type = node.getParent().getParent() instanceof TypeDeclaration ? (TypeDeclaration) node.getParent().getParent() : null; 38 token.end = token.start + name.length();
38 if (type != null) { 39 }
39 name = type.getName(); 40 }
40 token.end = token.start + name.length(); 41
41 } 42 return token;
42 } 43 }
43
44 return token;
45 }
46} 44}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java
index 1e5beb1..c48aba5 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java
@@ -1,24 +1,37 @@
1package cuchaz.enigma.source.procyon.transformers; 1package cuchaz.enigma.source.procyon.transformers;
2 2
3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.List;
6import java.util.Objects;
7import java.util.stream.Stream;
8
3import com.google.common.base.Function; 9import com.google.common.base.Function;
4import com.google.common.base.Strings; 10import com.google.common.base.Strings;
5import com.strobel.assembler.metadata.ParameterDefinition; 11import com.strobel.assembler.metadata.ParameterDefinition;
6import com.strobel.decompiler.languages.java.ast.*; 12import com.strobel.decompiler.languages.java.ast.AstNode;
13import com.strobel.decompiler.languages.java.ast.Comment;
14import com.strobel.decompiler.languages.java.ast.CommentType;
15import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration;
16import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor;
17import com.strobel.decompiler.languages.java.ast.EnumValueDeclaration;
18import com.strobel.decompiler.languages.java.ast.FieldDeclaration;
19import com.strobel.decompiler.languages.java.ast.Keys;
20import com.strobel.decompiler.languages.java.ast.MethodDeclaration;
21import com.strobel.decompiler.languages.java.ast.ParameterDeclaration;
22import com.strobel.decompiler.languages.java.ast.Roles;
23import com.strobel.decompiler.languages.java.ast.TypeDeclaration;
7import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; 24import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
25
8import cuchaz.enigma.source.procyon.EntryParser; 26import cuchaz.enigma.source.procyon.EntryParser;
9import cuchaz.enigma.translation.mapping.EntryMapping; 27import cuchaz.enigma.translation.mapping.EntryMapping;
10import cuchaz.enigma.translation.mapping.EntryRemapper; 28import cuchaz.enigma.translation.mapping.EntryRemapper;
11import cuchaz.enigma.translation.mapping.ResolutionStrategy; 29import cuchaz.enigma.translation.mapping.ResolutionStrategy;
12import cuchaz.enigma.translation.representation.entry.*; 30import cuchaz.enigma.translation.representation.entry.Entry;
13 31import cuchaz.enigma.translation.representation.entry.LocalVariableDefEntry;
14import java.util.ArrayList; 32import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
15import java.util.Collections;
16import java.util.List;
17import java.util.Objects;
18import java.util.stream.Stream;
19 33
20public final class AddJavadocsAstTransform implements IAstTransform { 34public final class AddJavadocsAstTransform implements IAstTransform {
21
22 private final EntryRemapper remapper; 35 private final EntryRemapper remapper;
23 36
24 public AddJavadocsAstTransform(EntryRemapper remapper) { 37 public AddJavadocsAstTransform(EntryRemapper remapper) {
@@ -31,7 +44,6 @@ public final class AddJavadocsAstTransform implements IAstTransform {
31 } 44 }
32 45
33 static class Visitor extends DepthFirstAstVisitor<Void, Void> { 46 static class Visitor extends DepthFirstAstVisitor<Void, Void> {
34
35 private final EntryRemapper remapper; 47 private final EntryRemapper remapper;
36 48
37 Visitor(EntryRemapper remapper) { 49 Visitor(EntryRemapper remapper) {
@@ -40,6 +52,7 @@ public final class AddJavadocsAstTransform implements IAstTransform {
40 52
41 private <T extends AstNode> void addDoc(T node, Function<T, Entry<?>> retriever) { 53 private <T extends AstNode> void addDoc(T node, Function<T, Entry<?>> retriever) {
42 final Comment[] comments = getComments(node, retriever); 54 final Comment[] comments = getComments(node, retriever);
55
43 if (comments != null) { 56 if (comments != null) {
44 node.insertChildrenBefore(node.getFirstChild(), Roles.COMMENT, comments); 57 node.insertChildrenBefore(node.getFirstChild(), Roles.COMMENT, comments);
45 } 58 }
@@ -48,22 +61,24 @@ public final class AddJavadocsAstTransform implements IAstTransform {
48 private <T extends AstNode> Comment[] getComments(T node, Function<T, Entry<?>> retriever) { 61 private <T extends AstNode> Comment[] getComments(T node, Function<T, Entry<?>> retriever) {
49 final EntryMapping mapping = remapper.getDeobfMapping(retriever.apply(node)); 62 final EntryMapping mapping = remapper.getDeobfMapping(retriever.apply(node));
50 final String docs = Strings.emptyToNull(mapping.javadoc()); 63 final String docs = Strings.emptyToNull(mapping.javadoc());
51 return docs == null ? null : Stream.of(docs.split("\\R")).map(st -> new Comment(st, 64 return docs == null ? null : Stream.of(docs.split("\\R")).map(st -> new Comment(st, CommentType.Documentation)).toArray(Comment[]::new);
52 CommentType.Documentation)).toArray(Comment[]::new);
53 } 65 }
54 66
55 private Comment[] getParameterComments(ParameterDeclaration node, Function<ParameterDeclaration, Entry<?>> retriever) { 67 private Comment[] getParameterComments(ParameterDeclaration node, Function<ParameterDeclaration, Entry<?>> retriever) {
56 Entry<?> entry = retriever.apply(node); 68 Entry<?> entry = retriever.apply(node);
57 final EntryMapping mapping = remapper.getDeobfMapping(entry); 69 final EntryMapping mapping = remapper.getDeobfMapping(entry);
58 final Comment[] ret = getComments(node, retriever); 70 final Comment[] ret = getComments(node, retriever);
71
59 if (ret != null) { 72 if (ret != null) {
60 final String paramPrefix = "@param " + (mapping.targetName() != null ? mapping.targetName() : entry.getName()) + " "; 73 final String paramPrefix = "@param " + (mapping.targetName() != null ? mapping.targetName() : entry.getName()) + " ";
61 final String indent = Strings.repeat(" ", paramPrefix.length()); 74 final String indent = Strings.repeat(" ", paramPrefix.length());
62 ret[0].setContent(paramPrefix + ret[0].getContent()); 75 ret[0].setContent(paramPrefix + ret[0].getContent());
76
63 for (int i = 1; i < ret.length; i++) { 77 for (int i = 1; i < ret.length; i++) {
64 ret[i].setContent(indent + ret[i].getContent()); 78 ret[i].setContent(indent + ret[i].getContent());
65 } 79 }
66 } 80 }
81
67 return ret; 82 return ret;
68 } 83 }
69 84
@@ -71,23 +86,27 @@ public final class AddJavadocsAstTransform implements IAstTransform {
71 final MethodDefEntry methodDefEntry = EntryParser.parse(node.getUserData(Keys.METHOD_DEFINITION)); 86 final MethodDefEntry methodDefEntry = EntryParser.parse(node.getUserData(Keys.METHOD_DEFINITION));
72 final Comment[] baseComments = getComments(node, $ -> methodDefEntry); 87 final Comment[] baseComments = getComments(node, $ -> methodDefEntry);
73 List<Comment> comments = new ArrayList<>(); 88 List<Comment> comments = new ArrayList<>();
74 if (baseComments != null) 89
90 if (baseComments != null) {
75 Collections.addAll(comments, baseComments); 91 Collections.addAll(comments, baseComments);
92 }
76 93
77 for (ParameterDeclaration dec : node.getChildrenByRole(Roles.PARAMETER)) { 94 for (ParameterDeclaration dec : node.getChildrenByRole(Roles.PARAMETER)) {
78 ParameterDefinition def = dec.getUserData(Keys.PARAMETER_DEFINITION); 95 ParameterDefinition def = dec.getUserData(Keys.PARAMETER_DEFINITION);
79 final Comment[] paramComments = getParameterComments(dec, $ -> new LocalVariableDefEntry(methodDefEntry, def.getSlot(), def.getName(), 96 final Comment[] paramComments = getParameterComments(dec, $ -> new LocalVariableDefEntry(methodDefEntry, def.getSlot(), def.getName(), true, EntryParser.parseTypeDescriptor(def.getParameterType()), null));
80 true, 97
81 EntryParser.parseTypeDescriptor(def.getParameterType()), null)); 98 if (paramComments != null) {
82 if (paramComments != null)
83 Collections.addAll(comments, paramComments); 99 Collections.addAll(comments, paramComments);
100 }
84 } 101 }
85 102
86 if (!comments.isEmpty()) { 103 if (!comments.isEmpty()) {
87 if (remapper.getObfResolver().resolveEntry(methodDefEntry, ResolutionStrategy.RESOLVE_ROOT).stream().noneMatch(e -> Objects.equals(e, methodDefEntry))) { 104 if (remapper.getObfResolver().resolveEntry(methodDefEntry, ResolutionStrategy.RESOLVE_ROOT).stream().noneMatch(e -> Objects.equals(e, methodDefEntry))) {
88 comments.add(0, new Comment("{@inheritDoc}", CommentType.Documentation)); 105 comments.add(0, new Comment("{@inheritDoc}", CommentType.Documentation));
89 } 106 }
107
90 final AstNode oldFirst = node.getFirstChild(); 108 final AstNode oldFirst = node.getFirstChild();
109
91 for (Comment comment : comments) { 110 for (Comment comment : comments) {
92 node.insertChildBefore(oldFirst, comment, Roles.COMMENT); 111 node.insertChildBefore(oldFirst, comment, Roles.COMMENT);
93 } 112 }
@@ -99,6 +118,7 @@ public final class AddJavadocsAstTransform implements IAstTransform {
99 for (final AstNode child : node.getChildren()) { 118 for (final AstNode child : node.getChildren()) {
100 child.acceptVisitor(this, data); 119 child.acceptVisitor(this, data);
101 } 120 }
121
102 return null; 122 return null;
103 } 123 }
104 124
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/DropVarModifiersAstTransform.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/DropVarModifiersAstTransform.java
index b8c087b..defd251 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/DropVarModifiersAstTransform.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/DropVarModifiersAstTransform.java
@@ -1,10 +1,15 @@
1package cuchaz.enigma.source.procyon.transformers; 1package cuchaz.enigma.source.procyon.transformers;
2 2
3import com.strobel.decompiler.languages.java.ast.*;
4import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
5
6import javax.lang.model.element.Modifier; 3import javax.lang.model.element.Modifier;
7 4
5import com.strobel.decompiler.languages.java.ast.AstNode;
6import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor;
7import com.strobel.decompiler.languages.java.ast.EntityDeclaration;
8import com.strobel.decompiler.languages.java.ast.JavaModifierToken;
9import com.strobel.decompiler.languages.java.ast.ParameterDeclaration;
10import com.strobel.decompiler.languages.java.ast.VariableDeclarationStatement;
11import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
12
8public final class DropVarModifiersAstTransform implements IAstTransform { 13public final class DropVarModifiersAstTransform implements IAstTransform {
9 public static final DropVarModifiersAstTransform INSTANCE = new DropVarModifiersAstTransform(); 14 public static final DropVarModifiersAstTransform INSTANCE = new DropVarModifiersAstTransform();
10 15
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/InvalidIdentifierFix.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/InvalidIdentifierFix.java
index 34d95fa..bc7d5a2 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/InvalidIdentifierFix.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/InvalidIdentifierFix.java
@@ -14,15 +14,17 @@ public class InvalidIdentifierFix implements IAstTransform {
14 compilationUnit.acceptVisitor(new Visitor(), null); 14 compilationUnit.acceptVisitor(new Visitor(), null);
15 } 15 }
16 16
17 class Visitor extends DepthFirstAstVisitor<Void,Void>{ 17 class Visitor extends DepthFirstAstVisitor<Void, Void> {
18 @Override 18 @Override
19 public Void visitIdentifier(Identifier node, Void data) { 19 public Void visitIdentifier(Identifier node, Void data) {
20 super.visitIdentifier(node, data); 20 super.visitIdentifier(node, data);
21 if (node.getName().equals("do") || node.getName().equals("if")){ 21
22 if (node.getName().equals("do") || node.getName().equals("if")) {
22 Identifier newIdentifier = Identifier.create(node.getName() + "_", node.getStartLocation()); 23 Identifier newIdentifier = Identifier.create(node.getName() + "_", node.getStartLocation());
23 newIdentifier.copyUserDataFrom(node); 24 newIdentifier.copyUserDataFrom(node);
24 node.replaceWith(newIdentifier); 25 node.replaceWith(newIdentifier);
25 } 26 }
27
26 return null; 28 return null;
27 } 29 }
28 } 30 }
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/Java8Generics.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/Java8Generics.java
index 8accfc7..3edc06c 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/Java8Generics.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/Java8Generics.java
@@ -1,66 +1,62 @@
1package cuchaz.enigma.source.procyon.transformers; 1package cuchaz.enigma.source.procyon.transformers;
2 2
3import com.strobel.assembler.metadata.BuiltinTypes;
4import com.strobel.assembler.metadata.CommonTypeReferences; 3import com.strobel.assembler.metadata.CommonTypeReferences;
5import com.strobel.assembler.metadata.Flags;
6import com.strobel.assembler.metadata.IGenericInstance; 4import com.strobel.assembler.metadata.IGenericInstance;
7import com.strobel.assembler.metadata.IMemberDefinition;
8import com.strobel.assembler.metadata.JvmType;
9import com.strobel.assembler.metadata.MemberReference;
10import com.strobel.assembler.metadata.MethodDefinition;
11import com.strobel.assembler.metadata.TypeDefinition; 5import com.strobel.assembler.metadata.TypeDefinition;
12import com.strobel.assembler.metadata.TypeReference; 6import com.strobel.assembler.metadata.TypeReference;
13import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression;
14import com.strobel.decompiler.languages.java.ast.AstNode; 7import com.strobel.decompiler.languages.java.ast.AstNode;
15import com.strobel.decompiler.languages.java.ast.AstNodeCollection; 8import com.strobel.decompiler.languages.java.ast.AstNodeCollection;
16import com.strobel.decompiler.languages.java.ast.AstType; 9import com.strobel.decompiler.languages.java.ast.AstType;
17import com.strobel.decompiler.languages.java.ast.CastExpression; 10import com.strobel.decompiler.languages.java.ast.CastExpression;
18import com.strobel.decompiler.languages.java.ast.ComposedType;
19import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; 11import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor;
20import com.strobel.decompiler.languages.java.ast.Expression; 12import com.strobel.decompiler.languages.java.ast.Expression;
21import com.strobel.decompiler.languages.java.ast.Identifier; 13import com.strobel.decompiler.languages.java.ast.Identifier;
22import com.strobel.decompiler.languages.java.ast.InvocationExpression; 14import com.strobel.decompiler.languages.java.ast.InvocationExpression;
23import com.strobel.decompiler.languages.java.ast.Keys;
24import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; 15import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression;
25import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; 16import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression;
26import com.strobel.decompiler.languages.java.ast.Roles; 17import com.strobel.decompiler.languages.java.ast.Roles;
27import com.strobel.decompiler.languages.java.ast.SimpleType; 18import com.strobel.decompiler.languages.java.ast.SimpleType;
28import com.strobel.decompiler.languages.java.ast.WildcardType;
29import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; 19import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
30 20
31/** 21/**
32 * Created by Thiakil on 12/07/2018. 22 * Created by Thiakil on 12/07/2018.
33 */ 23 */
34public class Java8Generics implements IAstTransform { 24public class Java8Generics implements IAstTransform {
35
36 @Override 25 @Override
37 public void run(AstNode compilationUnit) { 26 public void run(AstNode compilationUnit) {
38 compilationUnit.acceptVisitor(new Visitor(), null); 27 compilationUnit.acceptVisitor(new Visitor(), null);
39 } 28 }
40 29
41 static class Visitor extends DepthFirstAstVisitor<Void,Void>{ 30 static class Visitor extends DepthFirstAstVisitor<Void, Void> {
42
43 @Override 31 @Override
44 public Void visitInvocationExpression(InvocationExpression node, Void data) { 32 public Void visitInvocationExpression(InvocationExpression node, Void data) {
45 super.visitInvocationExpression(node, data); 33 super.visitInvocationExpression(node, data);
46 if (node.getTarget() instanceof MemberReferenceExpression){ 34
35 if (node.getTarget() instanceof MemberReferenceExpression) {
47 MemberReferenceExpression referenceExpression = (MemberReferenceExpression) node.getTarget(); 36 MemberReferenceExpression referenceExpression = (MemberReferenceExpression) node.getTarget();
48 if (referenceExpression.getTypeArguments().stream().map(t->{ 37
38 if (referenceExpression.getTypeArguments().stream().map(t -> {
49 TypeReference tr = t.toTypeReference(); 39 TypeReference tr = t.toTypeReference();
50 if (tr.getDeclaringType() != null){//ensure that inner types are resolved so we can get the TypeDefinition below 40
41 //ensure that inner types are resolved so we can get the TypeDefinition below
42 if (tr.getDeclaringType() != null) {
51 TypeReference resolved = tr.resolve(); 43 TypeReference resolved = tr.resolve();
52 if (resolved != null) 44
45 if (resolved != null) {
53 return resolved; 46 return resolved;
47 }
54 } 48 }
49
55 return tr; 50 return tr;
56 }).anyMatch(t -> t.isWildcardType() || (t instanceof TypeDefinition && ((TypeDefinition) t).isAnonymous()))) { 51 }).anyMatch(t -> t.isWildcardType() || (t instanceof TypeDefinition && ((TypeDefinition) t).isAnonymous()))) {
57 //these are invalid for invocations, let the compiler work it out 52 //these are invalid for invocations, let the compiler work it out
58 referenceExpression.getTypeArguments().clear(); 53 referenceExpression.getTypeArguments().clear();
59 } else if (referenceExpression.getTypeArguments().stream().allMatch(t->t.toTypeReference().equals(CommonTypeReferences.Object))){ 54 } else if (referenceExpression.getTypeArguments().stream().allMatch(t -> t.toTypeReference().equals(CommonTypeReferences.Object))) {
60 //all are <Object>, thereby redundant and/or bad 55 //all are <Object>, thereby redundant and/or bad
61 referenceExpression.getTypeArguments().clear(); 56 referenceExpression.getTypeArguments().clear();
62 } 57 }
63 } 58 }
59
64 return null; 60 return null;
65 } 61 }
66 62
@@ -68,14 +64,17 @@ public class Java8Generics implements IAstTransform {
68 public Void visitObjectCreationExpression(ObjectCreationExpression node, Void data) { 64 public Void visitObjectCreationExpression(ObjectCreationExpression node, Void data) {
69 super.visitObjectCreationExpression(node, data); 65 super.visitObjectCreationExpression(node, data);
70 AstType type = node.getType(); 66 AstType type = node.getType();
71 if (type instanceof SimpleType && !((SimpleType) type).getTypeArguments().isEmpty()){ 67
68 if (type instanceof SimpleType && !((SimpleType) type).getTypeArguments().isEmpty()) {
72 SimpleType simpleType = (SimpleType) type; 69 SimpleType simpleType = (SimpleType) type;
73 AstNodeCollection<AstType> typeArguments = simpleType.getTypeArguments(); 70 AstNodeCollection<AstType> typeArguments = simpleType.getTypeArguments();
74 if (typeArguments.size() == 1 && typeArguments.firstOrNullObject().toTypeReference().equals(CommonTypeReferences.Object)){ 71
72 if (typeArguments.size() == 1 && typeArguments.firstOrNullObject().toTypeReference().equals(CommonTypeReferences.Object)) {
75 //all are <Object>, thereby redundant and/or bad 73 //all are <Object>, thereby redundant and/or bad
76 typeArguments.firstOrNullObject().getChildByRole(Roles.IDENTIFIER).replaceWith(Identifier.create("")); 74 typeArguments.firstOrNullObject().getChildByRole(Roles.IDENTIFIER).replaceWith(Identifier.create(""));
77 } 75 }
78 } 76 }
77
79 return null; 78 return null;
80 } 79 }
81 80
@@ -83,24 +82,30 @@ public class Java8Generics implements IAstTransform {
83 public Void visitCastExpression(CastExpression node, Void data) { 82 public Void visitCastExpression(CastExpression node, Void data) {
84 boolean doReplace = false; 83 boolean doReplace = false;
85 TypeReference typeReference = node.getType().toTypeReference(); 84 TypeReference typeReference = node.getType().toTypeReference();
86 if (typeReference.isArray() && typeReference.getElementType().isGenericType()){ 85
86 if (typeReference.isArray() && typeReference.getElementType().isGenericType()) {
87 doReplace = true; 87 doReplace = true;
88 } else if (typeReference.isGenericType()) { 88 } else if (typeReference.isGenericType()) {
89 Expression target = node.getExpression(); 89 Expression target = node.getExpression();
90 if (typeReference instanceof IGenericInstance && ((IGenericInstance)typeReference).getTypeArguments().stream().anyMatch(t->t.isWildcardType())){ 90
91 if (typeReference instanceof IGenericInstance && ((IGenericInstance) typeReference).getTypeArguments().stream().anyMatch(t -> t.isWildcardType())) {
91 doReplace = true; 92 doReplace = true;
92 } else if (target instanceof InvocationExpression) { 93 } else if (target instanceof InvocationExpression) {
93 InvocationExpression invocationExpression = (InvocationExpression)target; 94 InvocationExpression invocationExpression = (InvocationExpression) target;
95
94 if (invocationExpression.getTarget() instanceof MemberReferenceExpression && !((MemberReferenceExpression) invocationExpression.getTarget()).getTypeArguments().isEmpty()) { 96 if (invocationExpression.getTarget() instanceof MemberReferenceExpression && !((MemberReferenceExpression) invocationExpression.getTarget()).getTypeArguments().isEmpty()) {
95 ((MemberReferenceExpression) invocationExpression.getTarget()).getTypeArguments().clear(); 97 ((MemberReferenceExpression) invocationExpression.getTarget()).getTypeArguments().clear();
96 doReplace = true; 98 doReplace = true;
97 } 99 }
98 } 100 }
99 } 101 }
102
100 super.visitCastExpression(node, data); 103 super.visitCastExpression(node, data);
101 if (doReplace){ 104
105 if (doReplace) {
102 node.replaceWith(node.getExpression()); 106 node.replaceWith(node.getExpression());
103 } 107 }
108
104 return null; 109 return null;
105 } 110 }
106 } 111 }
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/ObfuscatedEnumSwitchRewriterTransform.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/ObfuscatedEnumSwitchRewriterTransform.java
index 32bb72f..204351e 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/ObfuscatedEnumSwitchRewriterTransform.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/ObfuscatedEnumSwitchRewriterTransform.java
@@ -17,6 +17,12 @@
17 17
18package cuchaz.enigma.source.procyon.transformers; 18package cuchaz.enigma.source.procyon.transformers;
19 19
20import java.util.ArrayList;
21import java.util.IdentityHashMap;
22import java.util.LinkedHashMap;
23import java.util.List;
24import java.util.Map;
25
20import com.strobel.assembler.metadata.BuiltinTypes; 26import com.strobel.assembler.metadata.BuiltinTypes;
21import com.strobel.assembler.metadata.FieldDefinition; 27import com.strobel.assembler.metadata.FieldDefinition;
22import com.strobel.assembler.metadata.MethodDefinition; 28import com.strobel.assembler.metadata.MethodDefinition;
@@ -43,372 +49,356 @@ import com.strobel.decompiler.languages.java.ast.TypeDeclaration;
43import com.strobel.decompiler.languages.java.ast.TypeReferenceExpression; 49import com.strobel.decompiler.languages.java.ast.TypeReferenceExpression;
44import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; 50import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
45 51
46import java.util.ArrayList;
47import java.util.IdentityHashMap;
48import java.util.LinkedHashMap;
49import java.util.List;
50import java.util.Map;
51
52/** 52/**
53 * Copy of {@link com.strobel.decompiler.languages.java.ast.transforms.EnumSwitchRewriterTransform} modified to: 53 * Copy of {@link com.strobel.decompiler.languages.java.ast.transforms.EnumSwitchRewriterTransform} modified to.
54 * - Not rely on a field containing "$SwitchMap$" (Proguard strips it) 54 * - Not rely on a field containing "$SwitchMap$" (Proguard strips it)
55 * - Ignore classes *with* SwitchMap$ names (so the original can handle it) 55 * - Ignore classes *with* SwitchMap$ names (so the original can handle it)
56 * - Ignores inner synthetics that are not package private 56 * - Ignores inner synthetics that are not package private
57 */ 57 */
58@SuppressWarnings("Duplicates") 58@SuppressWarnings("Duplicates")
59public class ObfuscatedEnumSwitchRewriterTransform implements IAstTransform { 59public class ObfuscatedEnumSwitchRewriterTransform implements IAstTransform {
60 private final DecompilerContext _context; 60 private final DecompilerContext _context;
61 61
62 public ObfuscatedEnumSwitchRewriterTransform(final DecompilerContext context) { 62 public ObfuscatedEnumSwitchRewriterTransform(final DecompilerContext context) {
63 _context = VerifyArgument.notNull(context, "context"); 63 _context = VerifyArgument.notNull(context, "context");
64 } 64 }
65 65
66 @Override 66 @Override
67 public void run(final AstNode compilationUnit) { 67 public void run(final AstNode compilationUnit) {
68 compilationUnit.acceptVisitor(new Visitor(_context), null); 68 compilationUnit.acceptVisitor(new Visitor(_context), null);
69 } 69 }
70 70
71 private final static class Visitor extends ContextTrackingVisitor<Void> { 71 private static final class Visitor extends ContextTrackingVisitor<Void> {
72 private final static class SwitchMapInfo { 72 private static final class SwitchMapInfo {
73 final String enclosingType; 73 final String enclosingType;
74 final Map<String, List<SwitchStatement>> switches = new LinkedHashMap<>(); 74 final Map<String, List<SwitchStatement>> switches = new LinkedHashMap<>();
75 final Map<String, Map<Integer, Expression>> mappings = new LinkedHashMap<>(); 75 final Map<String, Map<Integer, Expression>> mappings = new LinkedHashMap<>();
76 76
77 TypeDeclaration enclosingTypeDeclaration; 77 TypeDeclaration enclosingTypeDeclaration;
78 78
79 SwitchMapInfo(final String enclosingType) { 79 SwitchMapInfo(final String enclosingType) {
80 this.enclosingType = enclosingType; 80 this.enclosingType = enclosingType;
81 } 81 }
82 } 82 }
83 83
84 private final Map<String, SwitchMapInfo> _switchMaps = new LinkedHashMap<>(); 84 private final Map<String, SwitchMapInfo> _switchMaps = new LinkedHashMap<>();
85 private boolean _isSwitchMapWrapper; 85 private boolean _isSwitchMapWrapper;
86 86
87 protected Visitor(final DecompilerContext context) { 87 protected Visitor(final DecompilerContext context) {
88 super(context); 88 super(context);
89 } 89 }
90 90
91 @Override 91 @Override
92 public Void visitTypeDeclaration(final TypeDeclaration typeDeclaration, final Void p) { 92 public Void visitTypeDeclaration(final TypeDeclaration typeDeclaration, final Void p) {
93 final boolean oldIsSwitchMapWrapper = _isSwitchMapWrapper; 93 final boolean oldIsSwitchMapWrapper = _isSwitchMapWrapper;
94 final TypeDefinition typeDefinition = typeDeclaration.getUserData(Keys.TYPE_DEFINITION); 94 final TypeDefinition typeDefinition = typeDeclaration.getUserData(Keys.TYPE_DEFINITION);
95 final boolean isSwitchMapWrapper = isSwitchMapWrapper(typeDefinition); 95 final boolean isSwitchMapWrapper = isSwitchMapWrapper(typeDefinition);
96 96
97 if (isSwitchMapWrapper) { 97 if (isSwitchMapWrapper) {
98 final String internalName = typeDefinition.getInternalName(); 98 final String internalName = typeDefinition.getInternalName();
99 99
100 SwitchMapInfo info = _switchMaps.get(internalName); 100 SwitchMapInfo info = _switchMaps.get(internalName);
101 101
102 if (info == null) { 102 if (info == null) {
103 _switchMaps.put(internalName, info = new SwitchMapInfo(internalName)); 103 _switchMaps.put(internalName, info = new SwitchMapInfo(internalName));
104 } 104 }
105 105
106 info.enclosingTypeDeclaration = typeDeclaration; 106 info.enclosingTypeDeclaration = typeDeclaration;
107 } 107 }
108 108
109 _isSwitchMapWrapper = isSwitchMapWrapper; 109 _isSwitchMapWrapper = isSwitchMapWrapper;
110 110
111 try { 111 try {
112 super.visitTypeDeclaration(typeDeclaration, p); 112 super.visitTypeDeclaration(typeDeclaration, p);
113 } 113 } finally {
114 finally { 114 _isSwitchMapWrapper = oldIsSwitchMapWrapper;
115 _isSwitchMapWrapper = oldIsSwitchMapWrapper; 115 }
116 }
117 116
118 rewrite(); 117 rewrite();
119 118
120 return null; 119 return null;
121 } 120 }
122 121
123 @Override 122 @Override
124 public Void visitSwitchStatement(final SwitchStatement node, final Void data) { 123 public Void visitSwitchStatement(final SwitchStatement node, final Void data) {
125 final Expression test = node.getExpression(); 124 final Expression test = node.getExpression();
126 125
127 if (test instanceof IndexerExpression) { 126 if (test instanceof IndexerExpression) {
128 final IndexerExpression indexer = (IndexerExpression) test; 127 final IndexerExpression indexer = (IndexerExpression) test;
129 final Expression array = indexer.getTarget(); 128 final Expression array = indexer.getTarget();
130 final Expression argument = indexer.getArgument(); 129 final Expression argument = indexer.getArgument();
131 130
132 if (!(array instanceof MemberReferenceExpression)) { 131 if (!(array instanceof MemberReferenceExpression)) {
133 return super.visitSwitchStatement(node, data); 132 return super.visitSwitchStatement(node, data);
134 } 133 }
135 134
136 final MemberReferenceExpression arrayAccess = (MemberReferenceExpression) array; 135 final MemberReferenceExpression arrayAccess = (MemberReferenceExpression) array;
137 final Expression arrayOwner = arrayAccess.getTarget(); 136 final Expression arrayOwner = arrayAccess.getTarget();
138 final String mapName = arrayAccess.getMemberName(); 137 final String mapName = arrayAccess.getMemberName();
139 138
140 if (mapName == null || mapName.startsWith("$SwitchMap$") || !(arrayOwner instanceof TypeReferenceExpression)) { 139 if (mapName == null || mapName.startsWith("$SwitchMap$") || !(arrayOwner instanceof TypeReferenceExpression)) {
141 return super.visitSwitchStatement(node, data); 140 return super.visitSwitchStatement(node, data);
142 } 141 }
143 142
144 final TypeReferenceExpression enclosingTypeExpression = (TypeReferenceExpression) arrayOwner; 143 final TypeReferenceExpression enclosingTypeExpression = (TypeReferenceExpression) arrayOwner;
145 final TypeReference enclosingType = enclosingTypeExpression.getType().getUserData(Keys.TYPE_REFERENCE); 144 final TypeReference enclosingType = enclosingTypeExpression.getType().getUserData(Keys.TYPE_REFERENCE);
146 145
147 if (!isSwitchMapWrapper(enclosingType) || !(argument instanceof InvocationExpression)) { 146 if (!isSwitchMapWrapper(enclosingType) || !(argument instanceof InvocationExpression)) {
148 return super.visitSwitchStatement(node, data); 147 return super.visitSwitchStatement(node, data);
149 } 148 }
150 149
151 final InvocationExpression invocation = (InvocationExpression) argument; 150 final InvocationExpression invocation = (InvocationExpression) argument;
152 final Expression invocationTarget = invocation.getTarget(); 151 final Expression invocationTarget = invocation.getTarget();
153 152
154 if (!(invocationTarget instanceof MemberReferenceExpression)) { 153 if (!(invocationTarget instanceof MemberReferenceExpression)) {
155 return super.visitSwitchStatement(node, data); 154 return super.visitSwitchStatement(node, data);
156 } 155 }
157 156
158 final MemberReferenceExpression memberReference = (MemberReferenceExpression) invocationTarget; 157 final MemberReferenceExpression memberReference = (MemberReferenceExpression) invocationTarget;
159 158
160 if (!"ordinal".equals(memberReference.getMemberName())) { 159 if (!"ordinal".equals(memberReference.getMemberName())) {
161 return super.visitSwitchStatement(node, data); 160 return super.visitSwitchStatement(node, data);
162 } 161 }
163 162
164 final String enclosingTypeName = enclosingType.getInternalName(); 163 final String enclosingTypeName = enclosingType.getInternalName();
165 164
166 SwitchMapInfo info = _switchMaps.get(enclosingTypeName); 165 SwitchMapInfo info = _switchMaps.get(enclosingTypeName);
167 166
168 if (info == null) { 167 if (info == null) {
169 _switchMaps.put(enclosingTypeName, info = new SwitchMapInfo(enclosingTypeName)); 168 _switchMaps.put(enclosingTypeName, info = new SwitchMapInfo(enclosingTypeName));
170 169
171 final TypeDefinition resolvedType = enclosingType.resolve(); 170 final TypeDefinition resolvedType = enclosingType.resolve();
172 171
173 if (resolvedType != null) { 172 if (resolvedType != null) {
174 AstBuilder astBuilder = context.getUserData(Keys.AST_BUILDER); 173 AstBuilder astBuilder = context.getUserData(Keys.AST_BUILDER);
175 174
176 if (astBuilder == null) { 175 if (astBuilder == null) {
177 astBuilder = new AstBuilder(context); 176 astBuilder = new AstBuilder(context);
178 } 177 }
179 178
180 try (final SafeCloseable importSuppression = astBuilder.suppressImports()) { 179 try (SafeCloseable importSuppression = astBuilder.suppressImports()) {
181 final TypeDeclaration declaration = astBuilder.createType(resolvedType); 180 final TypeDeclaration declaration = astBuilder.createType(resolvedType);
182 181
183 declaration.acceptVisitor(this, data); 182 declaration.acceptVisitor(this, data);
184 } 183 }
185 } 184 }
186 } 185 }
187 186
188 List<SwitchStatement> switches = info.switches.get(mapName); 187 List<SwitchStatement> switches = info.switches.get(mapName);
189 188
190 if (switches == null) { 189 if (switches == null) {
191 info.switches.put(mapName, switches = new ArrayList<>()); 190 info.switches.put(mapName, switches = new ArrayList<>());
192 } 191 }
193 192
194 switches.add(node); 193 switches.add(node);
195 } 194 }
196 195
197 return super.visitSwitchStatement(node, data); 196 return super.visitSwitchStatement(node, data);
198 } 197 }
199 198
200 @Override 199 @Override
201 public Void visitAssignmentExpression(final AssignmentExpression node, final Void data) { 200 public Void visitAssignmentExpression(final AssignmentExpression node, final Void data) {
202 final TypeDefinition currentType = context.getCurrentType(); 201 final TypeDefinition currentType = context.getCurrentType();
203 final MethodDefinition currentMethod = context.getCurrentMethod(); 202 final MethodDefinition currentMethod = context.getCurrentMethod();
204 203
205 if (_isSwitchMapWrapper && 204 if (_isSwitchMapWrapper && currentType != null && currentMethod != null && currentMethod.isTypeInitializer()) {
206 currentType != null && 205 final Expression left = node.getLeft();
207 currentMethod != null && 206 final Expression right = node.getRight();
208 currentMethod.isTypeInitializer()) {
209 207
210 final Expression left = node.getLeft(); 208 if (left instanceof IndexerExpression && right instanceof PrimitiveExpression) {
211 final Expression right = node.getRight(); 209 String mapName = null;
212 210
213 if (left instanceof IndexerExpression && 211 final Expression array = ((IndexerExpression) left).getTarget();
214 right instanceof PrimitiveExpression) { 212 final Expression argument = ((IndexerExpression) left).getArgument();
215 213
216 String mapName = null; 214 if (array instanceof MemberReferenceExpression) {
215 mapName = ((MemberReferenceExpression) array).getMemberName();
216 } else if (array instanceof IdentifierExpression) {
217 mapName = ((IdentifierExpression) array).getIdentifier();
218 }
217 219
218 final Expression array = ((IndexerExpression) left).getTarget(); 220 if (mapName == null || mapName.startsWith("$SwitchMap$")) {
219 final Expression argument = ((IndexerExpression) left).getArgument(); 221 return super.visitAssignmentExpression(node, data);
222 }
220 223
221 if (array instanceof MemberReferenceExpression) { 224 if (!(argument instanceof InvocationExpression)) {
222 mapName = ((MemberReferenceExpression) array).getMemberName(); 225 return super.visitAssignmentExpression(node, data);
223 } 226 }
224 else if (array instanceof IdentifierExpression) {
225 mapName = ((IdentifierExpression) array).getIdentifier();
226 }
227 227
228 if (mapName == null || mapName.startsWith("$SwitchMap$")) { 228 final InvocationExpression invocation = (InvocationExpression) argument;
229 return super.visitAssignmentExpression(node, data); 229 final Expression invocationTarget = invocation.getTarget();
230 }
231 230
232 if (!(argument instanceof InvocationExpression)) { 231 if (!(invocationTarget instanceof MemberReferenceExpression)) {
233 return super.visitAssignmentExpression(node, data); 232 return super.visitAssignmentExpression(node, data);
234 } 233 }
235 234
236 final InvocationExpression invocation = (InvocationExpression) argument; 235 final MemberReferenceExpression memberReference = (MemberReferenceExpression) invocationTarget;
237 final Expression invocationTarget = invocation.getTarget(); 236 final Expression memberTarget = memberReference.getTarget();
238 237
239 if (!(invocationTarget instanceof MemberReferenceExpression)) { 238 if (!(memberTarget instanceof MemberReferenceExpression) || !"ordinal".equals(memberReference.getMemberName())) {
240 return super.visitAssignmentExpression(node, data); 239 return super.visitAssignmentExpression(node, data);
241 } 240 }
242 241
243 final MemberReferenceExpression memberReference = (MemberReferenceExpression) invocationTarget; 242 final MemberReferenceExpression outerMemberReference = (MemberReferenceExpression) memberTarget;
244 final Expression memberTarget = memberReference.getTarget(); 243 final Expression outerMemberTarget = outerMemberReference.getTarget();
245 244
246 if (!(memberTarget instanceof MemberReferenceExpression) || !"ordinal".equals(memberReference.getMemberName())) { 245 if (!(outerMemberTarget instanceof TypeReferenceExpression)) {
247 return super.visitAssignmentExpression(node, data); 246 return super.visitAssignmentExpression(node, data);
248 } 247 }
249 248
250 final MemberReferenceExpression outerMemberReference = (MemberReferenceExpression) memberTarget; 249 final String enclosingType = currentType.getInternalName();
251 final Expression outerMemberTarget = outerMemberReference.getTarget();
252 250
253 if (!(outerMemberTarget instanceof TypeReferenceExpression)) { 251 SwitchMapInfo info = _switchMaps.get(enclosingType);
254 return super.visitAssignmentExpression(node, data);
255 }
256 252
257 final String enclosingType = currentType.getInternalName(); 253 if (info == null) {
254 _switchMaps.put(enclosingType, info = new SwitchMapInfo(enclosingType));
258 255
259 SwitchMapInfo info = _switchMaps.get(enclosingType); 256 AstBuilder astBuilder = context.getUserData(Keys.AST_BUILDER);
260 257
261 if (info == null) { 258 if (astBuilder == null) {
262 _switchMaps.put(enclosingType, info = new SwitchMapInfo(enclosingType)); 259 astBuilder = new AstBuilder(context);
260 }
263 261
264 AstBuilder astBuilder = context.getUserData(Keys.AST_BUILDER); 262 info.enclosingTypeDeclaration = astBuilder.createType(currentType);
263 }
265 264
266 if (astBuilder == null) { 265 final PrimitiveExpression value = (PrimitiveExpression) right;
267 astBuilder = new AstBuilder(context);
268 }
269 266
270 info.enclosingTypeDeclaration = astBuilder.createType(currentType); 267 assert value.getValue() instanceof Integer;
271 }
272 268
273 final PrimitiveExpression value = (PrimitiveExpression) right; 269 Map<Integer, Expression> mapping = info.mappings.get(mapName);
274 270
275 assert value.getValue() instanceof Integer; 271 if (mapping == null) {
272 info.mappings.put(mapName, mapping = new LinkedHashMap<>());
273 }
276 274
277 Map<Integer, Expression> mapping = info.mappings.get(mapName); 275 final IdentifierExpression enumValue = new IdentifierExpression(Expression.MYSTERY_OFFSET, outerMemberReference.getMemberName());
278 276
279 if (mapping == null) { 277 enumValue.putUserData(Keys.MEMBER_REFERENCE, outerMemberReference.getUserData(Keys.MEMBER_REFERENCE));
280 info.mappings.put(mapName, mapping = new LinkedHashMap<>());
281 }
282 278
283 final IdentifierExpression enumValue = new IdentifierExpression( Expression.MYSTERY_OFFSET, outerMemberReference.getMemberName()); 279 mapping.put(((Number) value.getValue()).intValue(), enumValue);
280 }
281 }
284 282
285 enumValue.putUserData(Keys.MEMBER_REFERENCE, outerMemberReference.getUserData(Keys.MEMBER_REFERENCE)); 283 return super.visitAssignmentExpression(node, data);
284 }
286 285
287 mapping.put(((Number) value.getValue()).intValue(), enumValue); 286 private void rewrite() {
288 } 287 if (_switchMaps.isEmpty()) {
289 } 288 return;
289 }
290 290
291 return super.visitAssignmentExpression(node, data); 291 for (final SwitchMapInfo info : _switchMaps.values()) {
292 } 292 rewrite(info);
293 }
293 294
294 private void rewrite() { 295 //
295 if (_switchMaps.isEmpty()) { 296 // Remove switch map type wrappers that are no longer referenced.
296 return; 297 //
297 }
298 298
299 for (final SwitchMapInfo info : _switchMaps.values()) { 299 outer:
300 rewrite(info);
301 }
302 300
303 // 301 for (final SwitchMapInfo info : _switchMaps.values()) {
304 // Remove switch map type wrappers that are no longer referenced. 302 for (final String mapName : info.switches.keySet()) {
305 // 303 final List<SwitchStatement> switches = info.switches.get(mapName);
306 304
307 outer: 305 if (switches != null && !switches.isEmpty()) {
308 for (final SwitchMapInfo info : _switchMaps.values()) { 306 continue outer;
309 for (final String mapName : info.switches.keySet()) { 307 }
310 final List<SwitchStatement> switches = info.switches.get(mapName); 308 }
311 309
312 if (switches != null && !switches.isEmpty()) { 310 final TypeDeclaration enclosingTypeDeclaration = info.enclosingTypeDeclaration;
313 continue outer;
314 }
315 }
316 311
317 final TypeDeclaration enclosingTypeDeclaration = info.enclosingTypeDeclaration; 312 if (enclosingTypeDeclaration != null) {
313 enclosingTypeDeclaration.remove();
314 }
315 }
316 }
318 317
319 if (enclosingTypeDeclaration != null) { 318 private void rewrite(final SwitchMapInfo info) {
320 enclosingTypeDeclaration.remove(); 319 if (info.switches.isEmpty()) {
321 } 320 return;
322 } 321 }
323 }
324 322
325 private void rewrite(final SwitchMapInfo info) { 323 for (final String mapName : info.switches.keySet()) {
326 if (info.switches.isEmpty()) { 324 final List<SwitchStatement> switches = info.switches.get(mapName);
327 return; 325 final Map<Integer, Expression> mappings = info.mappings.get(mapName);
328 }
329 326
330 for (final String mapName : info.switches.keySet()) { 327 if (switches != null && mappings != null) {
331 final List<SwitchStatement> switches = info.switches.get(mapName); 328 for (int i = 0; i < switches.size(); i++) {
332 final Map<Integer, Expression> mappings = info.mappings.get(mapName); 329 if (rewriteSwitch(switches.get(i), mappings)) {
330 switches.remove(i--);
331 }
332 }
333 }
334 }
335 }
333 336
334 if (switches != null && mappings != null) { 337 private boolean rewriteSwitch(final SwitchStatement s, final Map<Integer, Expression> mappings) {
335 for (int i = 0; i < switches.size(); i++) { 338 final Map<Expression, Expression> replacements = new IdentityHashMap<>();
336 if (rewriteSwitch(switches.get(i), mappings)) {
337 switches.remove(i--);
338 }
339 }
340 }
341 }
342 }
343 339
344 private boolean rewriteSwitch(final SwitchStatement s, final Map<Integer, Expression> mappings) { 340 for (final SwitchSection section : s.getSwitchSections()) {
345 final Map<Expression, Expression> replacements = new IdentityHashMap<>(); 341 for (final CaseLabel caseLabel : section.getCaseLabels()) {
342 final Expression expression = caseLabel.getExpression();
346 343
347 for (final SwitchSection section : s.getSwitchSections()) { 344 if (expression.isNull()) {
348 for (final CaseLabel caseLabel : section.getCaseLabels()) { 345 continue;
349 final Expression expression = caseLabel.getExpression(); 346 }
350 347
351 if (expression.isNull()) { 348 if (expression instanceof PrimitiveExpression) {
352 continue; 349 final Object value = ((PrimitiveExpression) expression).getValue();
353 }
354 350
355 if (expression instanceof PrimitiveExpression) { 351 if (value instanceof Integer) {
356 final Object value = ((PrimitiveExpression) expression).getValue(); 352 final Expression replacement = mappings.get(value);
357 353
358 if (value instanceof Integer) { 354 if (replacement != null) {
359 final Expression replacement = mappings.get(value); 355 replacements.put(expression, replacement);
356 continue;
357 }
358 }
359 }
360 360
361 if (replacement != null) { 361 //
362 replacements.put(expression, replacement); 362 // If we can't rewrite all cases, we abort.
363 continue; 363 //
364 }
365 }
366 }
367 364
368 // 365 return false;
369 // If we can't rewrite all cases, we abort. 366 }
370 // 367 }
371 368
372 return false; 369 final IndexerExpression indexer = (IndexerExpression) s.getExpression();
373 } 370 final InvocationExpression argument = (InvocationExpression) indexer.getArgument();
374 } 371 final MemberReferenceExpression memberReference = (MemberReferenceExpression) argument.getTarget();
375 372 final Expression newTest = memberReference.getTarget();
376 final IndexerExpression indexer = (IndexerExpression) s.getExpression(); 373
377 final InvocationExpression argument = (InvocationExpression) indexer.getArgument(); 374 newTest.remove();
378 final MemberReferenceExpression memberReference = (MemberReferenceExpression) argument.getTarget(); 375 indexer.replaceWith(newTest);
379 final Expression newTest = memberReference.getTarget(); 376
380 377 for (final Map.Entry<Expression, Expression> entry : replacements.entrySet()) {
381 newTest.remove(); 378 entry.getKey().replaceWith(entry.getValue().clone());
382 indexer.replaceWith(newTest); 379 }
383 380
384 for (final Map.Entry<Expression, Expression> entry : replacements.entrySet()) { 381 return true;
385 entry.getKey().replaceWith(entry.getValue().clone()); 382 }
386 } 383
387 384 private static boolean isSwitchMapWrapper(final TypeReference type) {
388 return true; 385 if (type == null) {
389 } 386 return false;
390 387 }
391 private static boolean isSwitchMapWrapper(final TypeReference type) { 388
392 if (type == null) { 389 final TypeDefinition definition = type instanceof TypeDefinition ? (TypeDefinition) type : type.resolve();
393 return false; 390
394 } 391 if (definition == null || !definition.isSynthetic() || !definition.isInnerClass() || !definition.isPackagePrivate()) {
395 392 return false;
396 final TypeDefinition definition = type instanceof TypeDefinition ? (TypeDefinition) type 393 }
397 : type.resolve(); 394
398 395 for (final FieldDefinition field : definition.getDeclaredFields()) {
399 if (definition == null || !definition.isSynthetic() || !definition.isInnerClass() || !definition.isPackagePrivate()) { 396 if (!field.getName().startsWith("$SwitchMap$") && BuiltinTypes.Integer.makeArrayType().equals(field.getFieldType())) {
400 return false; 397 return true;
401 } 398 }
402 399 }
403 for (final FieldDefinition field : definition.getDeclaredFields()) { 400
404 if (!field.getName().startsWith("$SwitchMap$") && 401 return false;
405 BuiltinTypes.Integer.makeArrayType().equals(field.getFieldType())) { 402 }
406 403 }
407 return true; 404}
408 }
409 }
410
411 return false;
412 }
413 }
414} \ No newline at end of file
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/RemoveObjectCasts.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/RemoveObjectCasts.java
index cf0376f..679b168 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/RemoveObjectCasts.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/RemoveObjectCasts.java
@@ -22,17 +22,17 @@ public class RemoveObjectCasts implements IAstTransform {
22 compilationUnit.acceptVisitor(new Visitor(_context), null); 22 compilationUnit.acceptVisitor(new Visitor(_context), null);
23 } 23 }
24 24
25 private final static class Visitor extends ContextTrackingVisitor<Void>{ 25 private static final class Visitor extends ContextTrackingVisitor<Void> {
26
27 protected Visitor(DecompilerContext context) { 26 protected Visitor(DecompilerContext context) {
28 super(context); 27 super(context);
29 } 28 }
30 29
31 @Override 30 @Override
32 public Void visitCastExpression(CastExpression node, Void data) { 31 public Void visitCastExpression(CastExpression node, Void data) {
33 if (node.getType().toTypeReference().equals(BuiltinTypes.Object)){ 32 if (node.getType().toTypeReference().equals(BuiltinTypes.Object)) {
34 node.replaceWith(node.getExpression()); 33 node.replaceWith(node.getExpression());
35 } 34 }
35
36 return super.visitCastExpression(node, data); 36 return super.visitCastExpression(node, data);
37 } 37 }
38 } 38 }
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/VarargsFixer.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/VarargsFixer.java
index d3ddaab..234834b 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/VarargsFixer.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/VarargsFixer.java
@@ -1,5 +1,8 @@
1package cuchaz.enigma.source.procyon.transformers; 1package cuchaz.enigma.source.procyon.transformers;
2 2
3import java.util.ArrayList;
4import java.util.List;
5
3import com.strobel.assembler.metadata.MemberReference; 6import com.strobel.assembler.metadata.MemberReference;
4import com.strobel.assembler.metadata.MetadataFilters; 7import com.strobel.assembler.metadata.MetadataFilters;
5import com.strobel.assembler.metadata.MetadataHelper; 8import com.strobel.assembler.metadata.MetadataHelper;
@@ -16,7 +19,6 @@ import com.strobel.decompiler.languages.java.ast.AstNode;
16import com.strobel.decompiler.languages.java.ast.AstNodeCollection; 19import com.strobel.decompiler.languages.java.ast.AstNodeCollection;
17import com.strobel.decompiler.languages.java.ast.CastExpression; 20import com.strobel.decompiler.languages.java.ast.CastExpression;
18import com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor; 21import com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor;
19import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor;
20import com.strobel.decompiler.languages.java.ast.Expression; 22import com.strobel.decompiler.languages.java.ast.Expression;
21import com.strobel.decompiler.languages.java.ast.InvocationExpression; 23import com.strobel.decompiler.languages.java.ast.InvocationExpression;
22import com.strobel.decompiler.languages.java.ast.JavaResolver; 24import com.strobel.decompiler.languages.java.ast.JavaResolver;
@@ -26,9 +28,6 @@ import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression;
26import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; 28import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
27import com.strobel.decompiler.semantics.ResolveResult; 29import com.strobel.decompiler.semantics.ResolveResult;
28 30
29import java.util.ArrayList;
30import java.util.List;
31
32/** 31/**
33 * Created by Thiakil on 12/07/2018. 32 * Created by Thiakil on 12/07/2018.
34 */ 33 */
@@ -46,6 +45,7 @@ public class VarargsFixer implements IAstTransform {
46 45
47 class Visitor extends ContextTrackingVisitor<Void> { 46 class Visitor extends ContextTrackingVisitor<Void> {
48 private final JavaResolver _resolver; 47 private final JavaResolver _resolver;
48
49 protected Visitor(DecompilerContext context) { 49 protected Visitor(DecompilerContext context) {
50 super(context); 50 super(context);
51 _resolver = new JavaResolver(context); 51 _resolver = new JavaResolver(context);
@@ -56,21 +56,26 @@ public class VarargsFixer implements IAstTransform {
56 public Void visitInvocationExpression(InvocationExpression node, Void data) { 56 public Void visitInvocationExpression(InvocationExpression node, Void data) {
57 super.visitInvocationExpression(node, data); 57 super.visitInvocationExpression(node, data);
58 MemberReference definition = node.getUserData(Keys.MEMBER_REFERENCE); 58 MemberReference definition = node.getUserData(Keys.MEMBER_REFERENCE);
59 if (definition instanceof MethodDefinition && ((MethodDefinition) definition).isVarArgs()){ 59
60 if (definition instanceof MethodDefinition && ((MethodDefinition) definition).isVarArgs()) {
60 AstNodeCollection<Expression> arguments = node.getArguments(); 61 AstNodeCollection<Expression> arguments = node.getArguments();
61 Expression lastParam = arguments.lastOrNullObject(); 62 Expression lastParam = arguments.lastOrNullObject();
62 if (!lastParam.isNull() && lastParam instanceof ArrayCreationExpression){ 63
63 ArrayCreationExpression varargArray = (ArrayCreationExpression)lastParam; 64 if (!lastParam.isNull() && lastParam instanceof ArrayCreationExpression) {
64 if (varargArray.getInitializer().isNull() || varargArray.getInitializer().getElements().isEmpty()){ 65 ArrayCreationExpression varargArray = (ArrayCreationExpression) lastParam;
66
67 if (varargArray.getInitializer().isNull() || varargArray.getInitializer().getElements().isEmpty()) {
65 lastParam.remove(); 68 lastParam.remove();
66 } else { 69 } else {
67 for (Expression e : varargArray.getInitializer().getElements()){ 70 for (Expression e : varargArray.getInitializer().getElements()) {
68 arguments.insertBefore(varargArray, e.clone()); 71 arguments.insertBefore(varargArray, e.clone());
69 } 72 }
73
70 varargArray.remove(); 74 varargArray.remove();
71 } 75 }
72 } 76 }
73 } 77 }
78
74 return null; 79 return null;
75 } 80 }
76 81
@@ -83,14 +88,11 @@ public class VarargsFixer implements IAstTransform {
83 88
84 Expression arrayArg = lastArgument; 89 Expression arrayArg = lastArgument;
85 90
86 if (arrayArg instanceof CastExpression) 91 if (arrayArg instanceof CastExpression) {
87 arrayArg = ((CastExpression) arrayArg).getExpression(); 92 arrayArg = ((CastExpression) arrayArg).getExpression();
93 }
88 94
89 if (arrayArg == null || 95 if (arrayArg == null || arrayArg.isNull() || !(arrayArg instanceof ArrayCreationExpression && node.getTarget() instanceof MemberReferenceExpression)) {
90 arrayArg.isNull() ||
91 !(arrayArg instanceof ArrayCreationExpression &&
92 node.getTarget() instanceof MemberReferenceExpression)) {
93
94 return null; 96 return null;
95 } 97 }
96 98
@@ -117,22 +119,15 @@ public class VarargsFixer implements IAstTransform {
117 final Expression invocationTarget = target.getTarget(); 119 final Expression invocationTarget = target.getTarget();
118 120
119 if (invocationTarget == null || invocationTarget.isNull()) { 121 if (invocationTarget == null || invocationTarget.isNull()) {
120 candidates = MetadataHelper.findMethods( 122 candidates = MetadataHelper.findMethods(context.getCurrentType(), MetadataFilters.matchName(resolved.getName()));
121 context.getCurrentType(), 123 } else {
122 MetadataFilters.matchName(resolved.getName())
123 );
124 }
125 else {
126 final ResolveResult targetResult = _resolver.apply(invocationTarget); 124 final ResolveResult targetResult = _resolver.apply(invocationTarget);
127 125
128 if (targetResult == null || targetResult.getType() == null) { 126 if (targetResult == null || targetResult.getType() == null) {
129 return null; 127 return null;
130 } 128 }
131 129
132 candidates = MetadataHelper.findMethods( 130 candidates = MetadataHelper.findMethods(targetResult.getType(), MetadataFilters.matchName(resolved.getName()));
133 targetResult.getType(),
134 MetadataFilters.matchName(resolved.getName())
135 );
136 } 131 }
137 132
138 final List<TypeReference> argTypes = new ArrayList<>(); 133 final List<TypeReference> argTypes = new ArrayList<>();
@@ -172,10 +167,7 @@ public class VarargsFixer implements IAstTransform {
172 167
173 final MethodBinder.BindResult c2 = MethodBinder.selectMethod(candidates, argTypes); 168 final MethodBinder.BindResult c2 = MethodBinder.selectMethod(candidates, argTypes);
174 169
175 if (c2.isFailure() || 170 if (c2.isFailure() || c2.isAmbiguous() || !StringUtilities.equals(c2.getMethod().getErasedSignature(), c1.getMethod().getErasedSignature())) {
176 c2.isAmbiguous() ||
177 !StringUtilities.equals(c2.getMethod().getErasedSignature(), c1.getMethod().getErasedSignature())) {
178
179 return null; 171 return null;
180 } 172 }
181 173
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/LocalNameGenerator.java b/enigma/src/main/java/cuchaz/enigma/translation/LocalNameGenerator.java
index dec75ff..92d5cfc 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/LocalNameGenerator.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/LocalNameGenerator.java
@@ -12,9 +12,11 @@ public class LocalNameGenerator {
12 String translatedName; 12 String translatedName;
13 int nameIndex = index + 1; 13 int nameIndex = index + 1;
14 StringBuilder nameBuilder = new StringBuilder(getTypeName(desc)); 14 StringBuilder nameBuilder = new StringBuilder(getTypeName(desc));
15
15 if (!uniqueType || IdentifierValidation.isReservedMethodName(nameBuilder.toString())) { 16 if (!uniqueType || IdentifierValidation.isReservedMethodName(nameBuilder.toString())) {
16 nameBuilder.append(nameIndex); 17 nameBuilder.append(nameIndex);
17 } 18 }
19
18 translatedName = nameBuilder.toString(); 20 translatedName = nameBuilder.toString();
19 return translatedName; 21 return translatedName;
20 } 22 }
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/MappingTranslator.java b/enigma/src/main/java/cuchaz/enigma/translation/MappingTranslator.java
index 51e5d86..833ea29 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/MappingTranslator.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/MappingTranslator.java
@@ -22,7 +22,7 @@ public class MappingTranslator implements Translator {
22 if (translatable == null) { 22 if (translatable == null) {
23 return null; 23 return null;
24 } 24 }
25
25 return (TranslateResult<T>) translatable.extendedTranslate(this, resolver, mappings); 26 return (TranslateResult<T>) translatable.extendedTranslate(this, resolver, mappings);
26 } 27 }
27
28} 28}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/ProposingTranslator.java b/enigma/src/main/java/cuchaz/enigma/translation/ProposingTranslator.java
index 5ab16c8..33a38fe 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/ProposingTranslator.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/ProposingTranslator.java
@@ -29,25 +29,14 @@ public class ProposingTranslator implements Translator {
29 TranslateResult<T> deobfuscated = mapper.extendedDeobfuscate(translatable); 29 TranslateResult<T> deobfuscated = mapper.extendedDeobfuscate(translatable);
30 30
31 if (translatable instanceof Entry && ((Entry) deobfuscated.getValue()).getName().equals(((Entry<?>) translatable).getName())) { 31 if (translatable instanceof Entry && ((Entry) deobfuscated.getValue()).getName().equals(((Entry<?>) translatable).getName())) {
32 return mapper.getObfResolver() 32 return mapper.getObfResolver().resolveEntry((Entry<?>) translatable, ResolutionStrategy.RESOLVE_ROOT).stream().map(this::proposeName).filter(Optional::isPresent).map(Optional::get).findFirst().map(
33 .resolveEntry((Entry<?>) translatable, ResolutionStrategy.RESOLVE_ROOT) 33 newName -> TranslateResult.proposed((T) ((Entry) deobfuscated.getValue()).withName(newName))).orElse(deobfuscated);
34 .stream()
35 .map(this::proposeName)
36 .filter(Optional::isPresent)
37 .map(Optional::get)
38 .findFirst()
39 .map(newName -> TranslateResult.proposed((T) ((Entry) deobfuscated.getValue()).withName(newName)))
40 .orElse(deobfuscated);
41 } 34 }
42 35
43 return deobfuscated; 36 return deobfuscated;
44 } 37 }
45 38
46 private Optional<String> proposeName(Entry<?> entry) { 39 private Optional<String> proposeName(Entry<?> entry) {
47 return Arrays.stream(nameProposalServices) 40 return Arrays.stream(nameProposalServices).map(service -> service.proposeName(entry, mapper)).filter(Optional::isPresent).map(Optional::get).findFirst();
48 .map(service -> service.proposeName(entry, mapper))
49 .filter(Optional::isPresent)
50 .map(Optional::get)
51 .findFirst();
52 } 41 }
53} 42}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/SignatureUpdater.java b/enigma/src/main/java/cuchaz/enigma/translation/SignatureUpdater.java
index 3783053..9966014 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/SignatureUpdater.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/SignatureUpdater.java
@@ -1,24 +1,23 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.translation; 12package cuchaz.enigma.translation;
13 13
14import com.google.common.collect.Lists;
15
16import java.io.IOException; 14import java.io.IOException;
17import java.io.StringReader; 15import java.io.StringReader;
18import java.util.List; 16import java.util.List;
19 17
20public class SignatureUpdater { 18import com.google.common.collect.Lists;
21 19
20public class SignatureUpdater {
22 public static String update(String signature, ClassNameUpdater updater) { 21 public static String update(String signature, ClassNameUpdater updater) {
23 try { 22 try {
24 StringBuilder buf = new StringBuilder(); 23 StringBuilder buf = new StringBuilder();
@@ -26,6 +25,7 @@ public class SignatureUpdater {
26 // read the signature character-by-character 25 // read the signature character-by-character
27 StringReader reader = new StringReader(signature); 26 StringReader reader = new StringReader(signature);
28 int i; 27 int i;
28
29 while ((i = reader.read()) != -1) { 29 while ((i = reader.read()) != -1) {
30 char c = (char) i; 30 char c = (char) i;
31 31
@@ -34,9 +34,11 @@ public class SignatureUpdater {
34 // update the class name and add it to the buffer 34 // update the class name and add it to the buffer
35 buf.append('L'); 35 buf.append('L');
36 String className = readClass(reader); 36 String className = readClass(reader);
37
37 if (className == null) { 38 if (className == null) {
38 throw new IllegalArgumentException("Malformed signature: " + signature); 39 throw new IllegalArgumentException("Malformed signature: " + signature);
39 } 40 }
41
40 buf.append(updater.update(className)); 42 buf.append(updater.update(className));
41 buf.append(';'); 43 buf.append(';');
42 } else { 44 } else {
@@ -58,6 +60,7 @@ public class SignatureUpdater {
58 StringBuilder buf = new StringBuilder(); 60 StringBuilder buf = new StringBuilder();
59 int depth = 0; 61 int depth = 0;
60 int i; 62 int i;
63
61 while ((i = reader.read()) != -1) { 64 while ((i = reader.read()) != -1) {
62 char c = (char) i; 65 char c = (char) i;
63 66
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/Translatable.java b/enigma/src/main/java/cuchaz/enigma/translation/Translatable.java
index 7061bfa..af6fbc9 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/Translatable.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/Translatable.java
@@ -5,12 +5,10 @@ import cuchaz.enigma.translation.mapping.EntryMapping;
5import cuchaz.enigma.translation.mapping.EntryResolver; 5import cuchaz.enigma.translation.mapping.EntryResolver;
6 6
7public interface Translatable { 7public interface Translatable {
8
9 TranslateResult<? extends Translatable> extendedTranslate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings); 8 TranslateResult<? extends Translatable> extendedTranslate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings);
10 9
11 @Deprecated 10 @Deprecated
12 default Translatable translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) { 11 default Translatable translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
13 return this.extendedTranslate(translator, resolver, mappings).getValue(); 12 return this.extendedTranslate(translator, resolver, mappings).getValue();
14 } 13 }
15
16} 14}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/TranslateResult.java b/enigma/src/main/java/cuchaz/enigma/translation/TranslateResult.java
index bb26235..0cf4acd 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/TranslateResult.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/TranslateResult.java
@@ -6,7 +6,6 @@ import java.util.function.Function;
6import cuchaz.enigma.source.RenamableTokenType; 6import cuchaz.enigma.source.RenamableTokenType;
7 7
8public final class TranslateResult<T> { 8public final class TranslateResult<T> {
9
10 private final RenamableTokenType type; 9 private final RenamableTokenType type;
11 private final T value; 10 private final T value;
12 11
@@ -65,11 +64,16 @@ public final class TranslateResult<T> {
65 64
66 @Override 65 @Override
67 public boolean equals(Object o) { 66 public boolean equals(Object o) {
68 if (this == o) return true; 67 if (this == o) {
69 if (o == null || getClass() != o.getClass()) return false; 68 return true;
69 }
70
71 if (o == null || getClass() != o.getClass()) {
72 return false;
73 }
74
70 TranslateResult<?> that = (TranslateResult<?>) o; 75 TranslateResult<?> that = (TranslateResult<?>) o;
71 return type == that.type && 76 return type == that.type && Objects.equals(value, that.value);
72 Objects.equals(value, that.value);
73 } 77 }
74 78
75 @Override 79 @Override
@@ -81,5 +85,4 @@ public final class TranslateResult<T> {
81 public String toString() { 85 public String toString() {
82 return String.format("TranslateResult { type: %s, value: %s }", type, value); 86 return String.format("TranslateResult { type: %s, value: %s }", type, value);
83 } 87 }
84
85} 88}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/TranslationDirection.java b/enigma/src/main/java/cuchaz/enigma/translation/TranslationDirection.java
index 2ecb30b..0725ebb 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/TranslationDirection.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/TranslationDirection.java
@@ -1,24 +1,24 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.translation; 12package cuchaz.enigma.translation;
13 13
14public enum TranslationDirection { 14public enum TranslationDirection {
15
16 DEOBFUSCATING { 15 DEOBFUSCATING {
17 @Override 16 @Override
18 public <T> T choose(T deobfChoice, T obfChoice) { 17 public <T> T choose(T deobfChoice, T obfChoice) {
19 if (deobfChoice == null) { 18 if (deobfChoice == null) {
20 return obfChoice; 19 return obfChoice;
21 } 20 }
21
22 return deobfChoice; 22 return deobfChoice;
23 } 23 }
24 }, 24 },
@@ -28,6 +28,7 @@ public enum TranslationDirection {
28 if (obfChoice == null) { 28 if (obfChoice == null) {
29 return deobfChoice; 29 return deobfChoice;
30 } 30 }
31
31 return obfChoice; 32 return obfChoice;
32 } 33 }
33 }; 34 };
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/Translator.java b/enigma/src/main/java/cuchaz/enigma/translation/Translator.java
index 66c2f9e..ed58197 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/Translator.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/Translator.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.translation; 12package cuchaz.enigma.translation;
13 13
@@ -34,38 +34,40 @@ public interface Translator {
34 } 34 }
35 35
36 default <T extends Translatable> Collection<T> translate(Collection<T> translatable) { 36 default <T extends Translatable> Collection<T> translate(Collection<T> translatable) {
37 return translatable.stream() 37 return translatable.stream().map(this::translate).toList();
38 .map(this::translate)
39 .toList();
40 } 38 }
41 39
42 default <T extends Translatable> Set<T> translate(Set<T> translatable) { 40 default <T extends Translatable> Set<T> translate(Set<T> translatable) {
43 return translatable.stream() 41 return translatable.stream().map(this::translate).collect(Collectors.toSet());
44 .map(this::translate)
45 .collect(Collectors.toSet());
46 } 42 }
47 43
48 default <T extends Translatable, V> Map<T, V> translateKeys(Map<T, V> translatable) { 44 default <T extends Translatable, V> Map<T, V> translateKeys(Map<T, V> translatable) {
49 Map<T, V> result = new HashMap<>(translatable.size()); 45 Map<T, V> result = new HashMap<>(translatable.size());
46
50 for (Map.Entry<T, V> entry : translatable.entrySet()) { 47 for (Map.Entry<T, V> entry : translatable.entrySet()) {
51 result.put(translate(entry.getKey()), entry.getValue()); 48 result.put(translate(entry.getKey()), entry.getValue());
52 } 49 }
50
53 return result; 51 return result;
54 } 52 }
55 53
56 default <K extends Translatable, V extends Translatable> Map<K, V> translate(Map<K, V> translatable) { 54 default <K extends Translatable, V extends Translatable> Map<K, V> translate(Map<K, V> translatable) {
57 Map<K, V> result = new HashMap<>(translatable.size()); 55 Map<K, V> result = new HashMap<>(translatable.size());
56
58 for (Map.Entry<K, V> entry : translatable.entrySet()) { 57 for (Map.Entry<K, V> entry : translatable.entrySet()) {
59 result.put(translate(entry.getKey()), translate(entry.getValue())); 58 result.put(translate(entry.getKey()), translate(entry.getValue()));
60 } 59 }
60
61 return result; 61 return result;
62 } 62 }
63 63
64 default <K extends Translatable, V extends Translatable> Multimap<K, V> translate(Multimap<K, V> translatable) { 64 default <K extends Translatable, V extends Translatable> Multimap<K, V> translate(Multimap<K, V> translatable) {
65 Multimap<K, V> result = HashMultimap.create(translatable.size(), 1); 65 Multimap<K, V> result = HashMultimap.create(translatable.size(), 1);
66
66 for (Map.Entry<K, Collection<V>> entry : translatable.asMap().entrySet()) { 67 for (Map.Entry<K, Collection<V>> entry : translatable.asMap().entrySet()) {
67 result.putAll(translate(entry.getKey()), translate(entry.getValue())); 68 result.putAll(translate(entry.getKey()), translate(entry.getValue()));
68 } 69 }
70
69 return result; 71 return result;
70 } 72 }
71} 73}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/VoidTranslator.java b/enigma/src/main/java/cuchaz/enigma/translation/VoidTranslator.java
index 28364f7..7d4755d 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/VoidTranslator.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/VoidTranslator.java
@@ -7,5 +7,4 @@ public enum VoidTranslator implements Translator {
7 public <T extends Translatable> TranslateResult<T> extendedTranslate(T translatable) { 7 public <T extends Translatable> TranslateResult<T> extendedTranslate(T translatable) {
8 return TranslateResult.obfuscated(translatable); 8 return TranslateResult.obfuscated(translatable);
9 } 9 }
10
11} 10}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/AccessModifier.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/AccessModifier.java
index f57dd90..6233784 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/AccessModifier.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/AccessModifier.java
@@ -3,7 +3,10 @@ package cuchaz.enigma.translation.mapping;
3import cuchaz.enigma.translation.representation.AccessFlags; 3import cuchaz.enigma.translation.representation.AccessFlags;
4 4
5public enum AccessModifier { 5public enum AccessModifier {
6 UNCHANGED, PUBLIC, PROTECTED, PRIVATE; 6 UNCHANGED,
7 PUBLIC,
8 PROTECTED,
9 PRIVATE;
7 10
8 public String getFormattedName() { 11 public String getFormattedName() {
9 return "ACC:" + super.toString(); 12 return "ACC:" + super.toString();
@@ -11,10 +14,10 @@ public enum AccessModifier {
11 14
12 public AccessFlags transform(AccessFlags access) { 15 public AccessFlags transform(AccessFlags access) {
13 return switch (this) { 16 return switch (this) {
14 case PUBLIC -> access.setPublic(); 17 case PUBLIC -> access.setPublic();
15 case PROTECTED -> access.setProtected(); 18 case PROTECTED -> access.setProtected();
16 case PRIVATE -> access.setPrivate(); 19 case PRIVATE -> access.setPrivate();
17 default -> access; 20 default -> access;
18 }; 21 };
19 } 22 }
20} 23}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryChange.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryChange.java
index b5ec855..4cd79b9 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryChange.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryChange.java
@@ -11,7 +11,6 @@ import cuchaz.enigma.translation.representation.entry.Entry;
11import cuchaz.enigma.utils.TristateChange; 11import cuchaz.enigma.utils.TristateChange;
12 12
13public final class EntryChange<E extends Entry<?>> { 13public final class EntryChange<E extends Entry<?>> {
14
15 private final E target; 14 private final E target;
16 private final TristateChange<String> deobfName; 15 private final TristateChange<String> deobfName;
17 private final TristateChange<String> javadoc; 16 private final TristateChange<String> javadoc;
@@ -75,13 +74,16 @@ public final class EntryChange<E extends Entry<?>> {
75 74
76 @Override 75 @Override
77 public boolean equals(Object o) { 76 public boolean equals(Object o) {
78 if (this == o) return true; 77 if (this == o) {
79 if (!(o instanceof EntryChange)) return false; 78 return true;
79 }
80
81 if (!(o instanceof EntryChange)) {
82 return false;
83 }
84
80 EntryChange<?> that = (EntryChange<?>) o; 85 EntryChange<?> that = (EntryChange<?>) o;
81 return Objects.equals(this.target, that.target) && 86 return Objects.equals(this.target, that.target) && Objects.equals(this.deobfName, that.deobfName) && Objects.equals(this.javadoc, that.javadoc) && Objects.equals(this.access, that.access);
82 Objects.equals(this.deobfName, that.deobfName) &&
83 Objects.equals(this.javadoc, that.javadoc) &&
84 Objects.equals(this.access, that.access);
85 } 87 }
86 88
87 @Override 89 @Override
@@ -93,5 +95,4 @@ public final class EntryChange<E extends Entry<?>> {
93 public String toString() { 95 public String toString() {
94 return String.format("EntryChange { target: %s, deobfName: %s, javadoc: %s, access: %s }", this.target, this.deobfName, this.javadoc, this.access); 96 return String.format("EntryChange { target: %s, deobfName: %s, javadoc: %s, access: %s }", this.target, this.deobfName, this.javadoc, this.access);
95 } 97 }
96
97} 98}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMap.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMap.java
index e1a3253..b398d41 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMap.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMap.java
@@ -1,9 +1,10 @@
1package cuchaz.enigma.translation.mapping; 1package cuchaz.enigma.translation.mapping;
2 2
3import cuchaz.enigma.translation.representation.entry.Entry; 3import java.util.stream.Stream;
4 4
5import javax.annotation.Nullable; 5import javax.annotation.Nullable;
6import java.util.stream.Stream; 6
7import cuchaz.enigma.translation.representation.entry.Entry;
7 8
8public interface EntryMap<T> { 9public interface EntryMap<T> {
9 void insert(Entry<?> entry, T value); 10 void insert(Entry<?> entry, T value);
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java
index e916bf3..72a8fd4 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java
@@ -5,11 +5,7 @@ import java.util.Arrays;
5import javax.annotation.Nonnull; 5import javax.annotation.Nonnull;
6import javax.annotation.Nullable; 6import javax.annotation.Nullable;
7 7
8public record EntryMapping( 8public record EntryMapping(@Nullable String targetName, @Nonnull AccessModifier accessModifier, @Nullable String javadoc) {
9 @Nullable String targetName,
10 @Nonnull AccessModifier accessModifier,
11 @Nullable String javadoc
12) {
13 public static final EntryMapping DEFAULT = new EntryMapping(null, AccessModifier.UNCHANGED, null); 9 public static final EntryMapping DEFAULT = new EntryMapping(null, AccessModifier.UNCHANGED, null);
14 10
15 public EntryMapping { 11 public EntryMapping {
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java
index 0268834..ed5dba5 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java
@@ -1,8 +1,8 @@
1package cuchaz.enigma.translation.mapping; 1package cuchaz.enigma.translation.mapping;
2 2
3import java.util.Collection; 3import java.util.Collection;
4import java.util.Objects;
5import java.util.List; 4import java.util.List;
5import java.util.Objects;
6import java.util.stream.Stream; 6import java.util.stream.Stream;
7 7
8import javax.annotation.Nonnull; 8import javax.annotation.Nonnull;
@@ -18,7 +18,6 @@ import cuchaz.enigma.translation.mapping.tree.HashEntryTree;
18import cuchaz.enigma.translation.representation.entry.ClassEntry; 18import cuchaz.enigma.translation.representation.entry.ClassEntry;
19import cuchaz.enigma.translation.representation.entry.Entry; 19import cuchaz.enigma.translation.representation.entry.Entry;
20import cuchaz.enigma.translation.representation.entry.FieldEntry; 20import cuchaz.enigma.translation.representation.entry.FieldEntry;
21
22import cuchaz.enigma.translation.representation.entry.MethodEntry; 21import cuchaz.enigma.translation.representation.entry.MethodEntry;
23import cuchaz.enigma.utils.validation.Message; 22import cuchaz.enigma.utils.validation.Message;
24import cuchaz.enigma.utils.validation.ValidationContext; 23import cuchaz.enigma.utils.validation.ValidationContext;
@@ -77,7 +76,9 @@ public class EntryRemapper {
77 } 76 }
78 } 77 }
79 78
80 if (validateOnly || !vc.canProceed()) return; 79 if (validateOnly || !vc.canProceed()) {
80 return;
81 }
81 82
82 for (Entry<?> resolvedEntry : resolvedEntries) { 83 for (Entry<?> resolvedEntry : resolvedEntries) {
83 if (deobfMapping.equals(EntryMapping.DEFAULT)) { 84 if (deobfMapping.equals(EntryMapping.DEFAULT)) {
@@ -95,9 +96,7 @@ public class EntryRemapper {
95 } 96 }
96 97
97 // Find all the methods in this record class 98 // Find all the methods in this record class
98 List<MethodEntry> classMethods = jarIndex.getEntryIndex().getMethods().stream() 99 List<MethodEntry> classMethods = jarIndex.getEntryIndex().getMethods().stream().filter(entry -> classEntry.equals(entry.getParent())).toList();
99 .filter(entry -> classEntry.equals(entry.getParent()))
100 .toList();
101 100
102 MethodEntry methodEntry = null; 101 MethodEntry methodEntry = null;
103 102
@@ -163,5 +162,4 @@ public class EntryRemapper {
163 public MappingValidator getValidator() { 162 public MappingValidator getValidator() {
164 return validator; 163 return validator;
165 } 164 }
166
167} 165}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryResolver.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryResolver.java
index 9dd0c3a..39e6825 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryResolver.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryResolver.java
@@ -1,13 +1,14 @@
1package cuchaz.enigma.translation.mapping; 1package cuchaz.enigma.translation.mapping;
2 2
3import java.util.Collection;
4import java.util.Set;
5
3import com.google.common.collect.Streams; 6import com.google.common.collect.Streams;
7
4import cuchaz.enigma.analysis.EntryReference; 8import cuchaz.enigma.analysis.EntryReference;
5import cuchaz.enigma.translation.representation.entry.Entry; 9import cuchaz.enigma.translation.representation.entry.Entry;
6import cuchaz.enigma.translation.representation.entry.MethodEntry; 10import cuchaz.enigma.translation.representation.entry.MethodEntry;
7 11
8import java.util.Collection;
9import java.util.Set;
10
11public interface EntryResolver { 12public interface EntryResolver {
12 <E extends Entry<?>> Collection<E> resolveEntry(E entry, ResolutionStrategy strategy); 13 <E extends Entry<?>> Collection<E> resolveEntry(E entry, ResolutionStrategy strategy);
13 14
@@ -17,14 +18,12 @@ public interface EntryResolver {
17 18
18 default <E extends Entry<?>, C extends Entry<?>> Collection<EntryReference<E, C>> resolveReference(EntryReference<E, C> reference, ResolutionStrategy strategy) { 19 default <E extends Entry<?>, C extends Entry<?>> Collection<EntryReference<E, C>> resolveReference(EntryReference<E, C> reference, ResolutionStrategy strategy) {
19 Collection<E> entry = resolveEntry(reference.entry, strategy); 20 Collection<E> entry = resolveEntry(reference.entry, strategy);
21
20 if (reference.context != null) { 22 if (reference.context != null) {
21 Collection<C> context = resolveEntry(reference.context, strategy); 23 Collection<C> context = resolveEntry(reference.context, strategy);
22 return Streams.zip(entry.stream(), context.stream(), (e, c) -> new EntryReference<>(e, c, reference)) 24 return Streams.zip(entry.stream(), context.stream(), (e, c) -> new EntryReference<>(e, c, reference)).toList();
23 .toList();
24 } else { 25 } else {
25 return entry.stream() 26 return entry.stream().map(e -> new EntryReference<>(e, null, reference)).toList();
26 .map(e -> new EntryReference<>(e, null, reference))
27 .toList();
28 } 27 }
29 } 28 }
30 29
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryUtil.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryUtil.java
index 582076c..f9a1060 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryUtil.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryUtil.java
@@ -6,7 +6,6 @@ import cuchaz.enigma.translation.representation.entry.Entry;
6import cuchaz.enigma.utils.validation.ValidationContext; 6import cuchaz.enigma.utils.validation.ValidationContext;
7 7
8public class EntryUtil { 8public class EntryUtil {
9
10 public static EntryMapping applyChange(ValidationContext vc, EntryRemapper remapper, EntryChange<?> change) { 9 public static EntryMapping applyChange(ValidationContext vc, EntryRemapper remapper, EntryChange<?> change) {
11 Entry<?> target = change.getTarget(); 10 Entry<?> target = change.getTarget();
12 EntryMapping prev = remapper.getDeobfMapping(target); 11 EntryMapping prev = remapper.getDeobfMapping(target);
@@ -38,5 +37,4 @@ public class EntryUtil {
38 37
39 return self; 38 return self;
40 } 39 }
41
42} 40}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/IdentifierValidation.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/IdentifierValidation.java
index e3aeb20..06e8d76 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/IdentifierValidation.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/IdentifierValidation.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.translation.mapping; 12package cuchaz.enigma.translation.mapping;
13 13
@@ -18,21 +18,17 @@ import cuchaz.enigma.utils.validation.StandardValidation;
18import cuchaz.enigma.utils.validation.ValidationContext; 18import cuchaz.enigma.utils.validation.ValidationContext;
19 19
20public final class IdentifierValidation { 20public final class IdentifierValidation {
21
22 private IdentifierValidation() { 21 private IdentifierValidation() {
23 } 22 }
24 23
25 private static final List<String> ILLEGAL_IDENTIFIERS = List.of( 24 private static final List<String> ILLEGAL_IDENTIFIERS = List.of("abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "default", "do", "double", "else", "enum", "extends", "false", "final", "finally", "float", "for", "goto", "if", "implements",
26 "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", 25 "import", "instanceof", "int", "interface", "long", "native", "new", "null", "package", "private", "protected", "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized", "this", "throw", "throws",
27 "continue", "default", "do", "double", "else", "enum", "extends", "false", "final", "finally", 26 "transient", "true", "try", "void", "volatile", "while", "_");
28 "float", "for", "goto", "if", "implements", "import", "instanceof", "int", "interface", "long",
29 "native", "new", "null", "package", "private", "protected", "public", "return", "short", "static",
30 "strictfp", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "true", "try",
31 "void", "volatile", "while", "_"
32 );
33 27
34 public static boolean validateClassName(ValidationContext vc, String name, boolean isInner) { 28 public static boolean validateClassName(ValidationContext vc, String name, boolean isInner) {
35 if (!StandardValidation.notBlank(vc, name)) return false; 29 if (!StandardValidation.notBlank(vc, name)) {
30 return false;
31 }
36 32
37 if (isInner) { 33 if (isInner) {
38 // When renaming, inner class names do not contain the package name, 34 // When renaming, inner class names do not contain the package name,
@@ -41,27 +37,37 @@ public final class IdentifierValidation {
41 } 37 }
42 38
43 String[] parts = name.split("/"); 39 String[] parts = name.split("/");
40
44 for (String part : parts) { 41 for (String part : parts) {
45 validateIdentifier(vc, part); 42 validateIdentifier(vc, part);
46 } 43 }
44
47 return true; 45 return true;
48 } 46 }
49 47
50 public static boolean validateIdentifier(ValidationContext vc, String name) { 48 public static boolean validateIdentifier(ValidationContext vc, String name) {
51 if (!StandardValidation.notBlank(vc, name)) return false; 49 if (!StandardValidation.notBlank(vc, name)) {
52 if (checkForReservedName(vc, name)) return false; 50 return false;
51 }
52
53 if (checkForReservedName(vc, name)) {
54 return false;
55 }
53 56
54 // Adapted from javax.lang.model.SourceVersion.isIdentifier 57 // Adapted from javax.lang.model.SourceVersion.isIdentifier
55 58
56 int cp = name.codePointAt(0); 59 int cp = name.codePointAt(0);
57 int position = 1; 60 int position = 1;
61
58 if (!Character.isJavaIdentifierStart(cp)) { 62 if (!Character.isJavaIdentifierStart(cp)) {
59 vc.raise(Message.ILLEGAL_IDENTIFIER, name, new String(Character.toChars(cp)), position); 63 vc.raise(Message.ILLEGAL_IDENTIFIER, name, new String(Character.toChars(cp)), position);
60 return false; 64 return false;
61 } 65 }
66
62 for (int i = Character.charCount(cp); i < name.length(); i += Character.charCount(cp)) { 67 for (int i = Character.charCount(cp); i < name.length(); i += Character.charCount(cp)) {
63 cp = name.codePointAt(i); 68 cp = name.codePointAt(i);
64 position += 1; 69 position += 1;
70
65 if (!Character.isJavaIdentifierPart(cp)) { 71 if (!Character.isJavaIdentifierPart(cp)) {
66 vc.raise(Message.ILLEGAL_IDENTIFIER, name, new String(Character.toChars(cp)), position); 72 vc.raise(Message.ILLEGAL_IDENTIFIER, name, new String(Character.toChars(cp)), position);
67 return false; 73 return false;
@@ -76,11 +82,11 @@ public final class IdentifierValidation {
76 vc.raise(Message.RESERVED_IDENTIFIER, name); 82 vc.raise(Message.RESERVED_IDENTIFIER, name);
77 return true; 83 return true;
78 } 84 }
85
79 return false; 86 return false;
80 } 87 }
81 88
82 public static boolean isReservedMethodName(String name) { 89 public static boolean isReservedMethodName(String name) {
83 return ILLEGAL_IDENTIFIERS.contains(name); 90 return ILLEGAL_IDENTIFIERS.contains(name);
84 } 91 }
85
86} 92}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/IndexEntryResolver.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/IndexEntryResolver.java
index 00168ba..8dc5659 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/IndexEntryResolver.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/IndexEntryResolver.java
@@ -1,6 +1,10 @@
1package cuchaz.enigma.translation.mapping; 1package cuchaz.enigma.translation.mapping;
2 2
3import java.util.*; 3import java.util.Collection;
4import java.util.Collections;
5import java.util.HashSet;
6import java.util.List;
7import java.util.Set;
4 8
5import javax.annotation.Nullable; 9import javax.annotation.Nullable;
6 10
@@ -40,6 +44,7 @@ public class IndexEntryResolver implements EntryResolver {
40 } 44 }
41 45
42 Entry<ClassEntry> classChild = getClassChild(entry); 46 Entry<ClassEntry> classChild = getClassChild(entry);
47
43 if (classChild != null && !(classChild instanceof ClassEntry)) { 48 if (classChild != null && !(classChild instanceof ClassEntry)) {
44 AccessFlags access = entryIndex.getEntryAccess(classChild); 49 AccessFlags access = entryIndex.getEntryAccess(classChild);
45 50
@@ -50,10 +55,9 @@ public class IndexEntryResolver implements EntryResolver {
50 55
51 if (access == null || !access.isPrivate()) { 56 if (access == null || !access.isPrivate()) {
52 Collection<Entry<ClassEntry>> resolvedChildren = resolveChildEntry(classChild, strategy); 57 Collection<Entry<ClassEntry>> resolvedChildren = resolveChildEntry(classChild, strategy);
58
53 if (!resolvedChildren.isEmpty()) { 59 if (!resolvedChildren.isEmpty()) {
54 return resolvedChildren.stream() 60 return resolvedChildren.stream().map(resolvedChild -> (E) entry.replaceAncestor(classChild, resolvedChild)).toList();
55 .map(resolvedChild -> (E) entry.replaceAncestor(classChild, resolvedChild))
56 .toList();
57 } 61 }
58 } 62 }
59 } 63 }
@@ -69,9 +73,11 @@ public class IndexEntryResolver implements EntryResolver {
69 73
70 // get the entry in the hierarchy that is the child of a class 74 // get the entry in the hierarchy that is the child of a class
71 List<Entry<?>> ancestry = entry.getAncestry(); 75 List<Entry<?>> ancestry = entry.getAncestry();
76
72 for (int i = ancestry.size() - 1; i > 0; i--) { 77 for (int i = ancestry.size() - 1; i > 0; i--) {
73 Entry<?> child = ancestry.get(i); 78 Entry<?> child = ancestry.get(i);
74 Entry<ClassEntry> cast = child.castParent(ClassEntry.class); 79 Entry<ClassEntry> cast = child.castParent(ClassEntry.class);
80
75 if (cast != null && !(cast instanceof ClassEntry)) { 81 if (cast != null && !(cast instanceof ClassEntry)) {
76 // we found the entry which is a child of a class, we are now able to resolve the owner of this entry 82 // we found the entry which is a child of a class, we are now able to resolve the owner of this entry
77 return cast; 83 return cast;
@@ -86,8 +92,10 @@ public class IndexEntryResolver implements EntryResolver {
86 92
87 if (entry instanceof MethodEntry) { 93 if (entry instanceof MethodEntry) {
88 MethodEntry bridgeMethod = bridgeMethodIndex.getBridgeFromSpecialized((MethodEntry) entry); 94 MethodEntry bridgeMethod = bridgeMethodIndex.getBridgeFromSpecialized((MethodEntry) entry);
95
89 if (bridgeMethod != null && ownerClass.equals(bridgeMethod.getParent())) { 96 if (bridgeMethod != null && ownerClass.equals(bridgeMethod.getParent())) {
90 Set<Entry<ClassEntry>> resolvedBridge = resolveChildEntry(bridgeMethod, strategy); 97 Set<Entry<ClassEntry>> resolvedBridge = resolveChildEntry(bridgeMethod, strategy);
98
91 if (!resolvedBridge.isEmpty()) { 99 if (!resolvedBridge.isEmpty()) {
92 return resolvedBridge; 100 return resolvedBridge;
93 } else { 101 } else {
@@ -117,6 +125,7 @@ public class IndexEntryResolver implements EntryResolver {
117 125
118 if (parentResolution.isEmpty()) { 126 if (parentResolution.isEmpty()) {
119 AccessFlags parentAccess = entryIndex.getEntryAccess(entry); 127 AccessFlags parentAccess = entryIndex.getEntryAccess(entry);
128
120 if (parentAccess != null && !parentAccess.isPrivate()) { 129 if (parentAccess != null && !parentAccess.isPrivate()) {
121 return Collections.singleton(entry); 130 return Collections.singleton(entry);
122 } 131 }
@@ -128,6 +137,7 @@ public class IndexEntryResolver implements EntryResolver {
128 private Collection<Entry<ClassEntry>> resolveClosest(Entry<ClassEntry> entry, ResolutionStrategy strategy) { 137 private Collection<Entry<ClassEntry>> resolveClosest(Entry<ClassEntry> entry, ResolutionStrategy strategy) {
129 // When resolving closest, we want to first check if we exist before looking further down 138 // When resolving closest, we want to first check if we exist before looking further down
130 AccessFlags parentAccess = entryIndex.getEntryAccess(entry); 139 AccessFlags parentAccess = entryIndex.getEntryAccess(entry);
140
131 if (parentAccess != null && !parentAccess.isPrivate()) { 141 if (parentAccess != null && !parentAccess.isPrivate()) {
132 return Collections.singleton(entry); 142 return Collections.singleton(entry);
133 } else { 143 } else {
@@ -138,6 +148,7 @@ public class IndexEntryResolver implements EntryResolver {
138 @Override 148 @Override
139 public Set<Entry<?>> resolveEquivalentEntries(Entry<?> entry) { 149 public Set<Entry<?>> resolveEquivalentEntries(Entry<?> entry) {
140 MethodEntry relevantMethod = entry.findAncestor(MethodEntry.class); 150 MethodEntry relevantMethod = entry.findAncestor(MethodEntry.class);
151
141 if (relevantMethod == null || !entryIndex.hasMethod(relevantMethod)) { 152 if (relevantMethod == null || !entryIndex.hasMethod(relevantMethod)) {
142 return Collections.singleton(entry); 153 return Collections.singleton(entry);
143 } 154 }
@@ -162,6 +173,7 @@ public class IndexEntryResolver implements EntryResolver {
162 173
163 private void resolveEquivalentMethods(Set<MethodEntry> methodEntries, MethodEntry methodEntry) { 174 private void resolveEquivalentMethods(Set<MethodEntry> methodEntries, MethodEntry methodEntry) {
164 AccessFlags access = entryIndex.getMethodAccess(methodEntry); 175 AccessFlags access = entryIndex.getMethodAccess(methodEntry);
176
165 if (access == null) { 177 if (access == null) {
166 throw new IllegalArgumentException("Could not find method " + methodEntry); 178 throw new IllegalArgumentException("Could not find method " + methodEntry);
167 } 179 }
@@ -176,11 +188,13 @@ public class IndexEntryResolver implements EntryResolver {
176 188
177 private void resolveEquivalentMethods(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) { 189 private void resolveEquivalentMethods(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) {
178 MethodEntry methodEntry = node.getMethodEntry(); 190 MethodEntry methodEntry = node.getMethodEntry();
191
179 if (methodEntries.contains(methodEntry)) { 192 if (methodEntries.contains(methodEntry)) {
180 return; 193 return;
181 } 194 }
182 195
183 AccessFlags flags = entryIndex.getMethodAccess(methodEntry); 196 AccessFlags flags = entryIndex.getMethodAccess(methodEntry);
197
184 if (flags != null && canInherit(methodEntry, flags)) { 198 if (flags != null && canInherit(methodEntry, flags)) {
185 // collect the entry 199 // collect the entry
186 methodEntries.add(methodEntry); 200 methodEntries.add(methodEntry);
@@ -188,6 +202,7 @@ public class IndexEntryResolver implements EntryResolver {
188 202
189 // look at bridge methods! 203 // look at bridge methods!
190 MethodEntry bridgedMethod = bridgeMethodIndex.getBridgeFromSpecialized(methodEntry); 204 MethodEntry bridgedMethod = bridgeMethodIndex.getBridgeFromSpecialized(methodEntry);
205
191 while (bridgedMethod != null) { 206 while (bridgedMethod != null) {
192 resolveEquivalentMethods(methodEntries, bridgedMethod); 207 resolveEquivalentMethods(methodEntries, bridgedMethod);
193 bridgedMethod = bridgeMethodIndex.getBridgeFromSpecialized(bridgedMethod); 208 bridgedMethod = bridgeMethodIndex.getBridgeFromSpecialized(bridgedMethod);
@@ -207,6 +222,7 @@ public class IndexEntryResolver implements EntryResolver {
207 private void resolveEquivalentMethods(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) { 222 private void resolveEquivalentMethods(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) {
208 MethodEntry methodEntry = node.getMethodEntry(); 223 MethodEntry methodEntry = node.getMethodEntry();
209 AccessFlags flags = entryIndex.getMethodAccess(methodEntry); 224 AccessFlags flags = entryIndex.getMethodAccess(methodEntry);
225
210 if (flags != null && !flags.isPrivate() && !flags.isStatic()) { 226 if (flags != null && !flags.isPrivate() && !flags.isStatic()) {
211 // collect the entry 227 // collect the entry
212 methodEntries.add(methodEntry); 228 methodEntries.add(methodEntry);
@@ -214,6 +230,7 @@ public class IndexEntryResolver implements EntryResolver {
214 230
215 // look at bridge methods! 231 // look at bridge methods!
216 MethodEntry bridgedMethod = bridgeMethodIndex.getBridgeFromSpecialized(methodEntry); 232 MethodEntry bridgedMethod = bridgeMethodIndex.getBridgeFromSpecialized(methodEntry);
233
217 while (bridgedMethod != null) { 234 while (bridgedMethod != null) {
218 resolveEquivalentMethods(methodEntries, bridgedMethod); 235 resolveEquivalentMethods(methodEntries, bridgedMethod);
219 bridgedMethod = bridgeMethodIndex.getBridgeFromSpecialized(bridgedMethod); 236 bridgedMethod = bridgeMethodIndex.getBridgeFromSpecialized(bridgedMethod);
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java
index 250851c..765c1f2 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java
@@ -49,9 +49,6 @@ public class MappingDelta<T> implements Translatable {
49 public TranslateResult<MappingDelta<T>> extendedTranslate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) { 49 public TranslateResult<MappingDelta<T>> extendedTranslate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
50 // there's no concept of deobfuscated for this as far as I can see, so 50 // there's no concept of deobfuscated for this as far as I can see, so
51 // it will always be marked as obfuscated 51 // it will always be marked as obfuscated
52 return TranslateResult.ungrouped(new MappingDelta<>( 52 return TranslateResult.ungrouped(new MappingDelta<>(translator.translate(baseMappings), translator.translate(changes)));
53 translator.translate(baseMappings),
54 translator.translate(changes)
55 ));
56 } 53 }
57} 54}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingOperations.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingOperations.java
index 2c03748..3b756c6 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingOperations.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingOperations.java
@@ -1,71 +1,74 @@
1package cuchaz.enigma.translation.mapping; 1package cuchaz.enigma.translation.mapping;
2 2
3import java.util.HashSet;
4import java.util.Set;
5
6import cuchaz.enigma.translation.MappingTranslator;
7import cuchaz.enigma.translation.Translator;
3import cuchaz.enigma.translation.mapping.tree.EntryTree; 8import cuchaz.enigma.translation.mapping.tree.EntryTree;
4import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; 9import cuchaz.enigma.translation.mapping.tree.EntryTreeNode;
5import cuchaz.enigma.translation.mapping.tree.HashEntryTree; 10import cuchaz.enigma.translation.mapping.tree.HashEntryTree;
6import cuchaz.enigma.translation.MappingTranslator;
7import cuchaz.enigma.translation.Translator;
8import cuchaz.enigma.translation.representation.entry.ClassEntry; 11import cuchaz.enigma.translation.representation.entry.ClassEntry;
9import cuchaz.enigma.translation.representation.entry.Entry; 12import cuchaz.enigma.translation.representation.entry.Entry;
10import cuchaz.enigma.translation.representation.entry.FieldEntry; 13import cuchaz.enigma.translation.representation.entry.FieldEntry;
11import cuchaz.enigma.translation.representation.entry.MethodEntry; 14import cuchaz.enigma.translation.representation.entry.MethodEntry;
12 15
13import java.util.HashSet;
14import java.util.Set;
15
16public class MappingOperations { 16public class MappingOperations {
17 public static EntryTree<EntryMapping> invert(EntryTree<EntryMapping> mappings) { 17 public static EntryTree<EntryMapping> invert(EntryTree<EntryMapping> mappings) {
18 Translator translator = new MappingTranslator(mappings, VoidEntryResolver.INSTANCE); 18 Translator translator = new MappingTranslator(mappings, VoidEntryResolver.INSTANCE);
19 EntryTree<EntryMapping> result = new HashEntryTree<>(); 19 EntryTree<EntryMapping> result = new HashEntryTree<>();
20 20
21 for (EntryTreeNode<EntryMapping> node : mappings) { 21 for (EntryTreeNode<EntryMapping> node : mappings) {
22 Entry<?> leftEntry = node.getEntry(); 22 Entry<?> leftEntry = node.getEntry();
23 EntryMapping leftMapping = node.getValue(); 23 EntryMapping leftMapping = node.getValue();
24 24
25 if (!(leftEntry instanceof ClassEntry || leftEntry instanceof MethodEntry || leftEntry instanceof FieldEntry)) { 25 if (!(leftEntry instanceof ClassEntry || leftEntry instanceof MethodEntry || leftEntry instanceof FieldEntry)) {
26 result.insert(translator.translate(leftEntry), leftMapping); 26 result.insert(translator.translate(leftEntry), leftMapping);
27 continue; 27 continue;
28 } 28 }
29 29
30 Entry<?> rightEntry = translator.translate(leftEntry); 30 Entry<?> rightEntry = translator.translate(leftEntry);
31 31
32 result.insert(rightEntry, leftMapping == null ? null : leftMapping.withName(leftEntry.getName())); 32 result.insert(rightEntry, leftMapping == null ? null : leftMapping.withName(leftEntry.getName()));
33 } 33 }
34 34
35 return result; 35 return result;
36 } 36 }
37 37
38 public static EntryTree<EntryMapping> compose(EntryTree<EntryMapping> left, EntryTree<EntryMapping> right, boolean keepLeftOnly, boolean keepRightOnly) { 38 public static EntryTree<EntryMapping> compose(EntryTree<EntryMapping> left, EntryTree<EntryMapping> right, boolean keepLeftOnly, boolean keepRightOnly) {
39 Translator leftTranslator = new MappingTranslator(left, VoidEntryResolver.INSTANCE); 39 Translator leftTranslator = new MappingTranslator(left, VoidEntryResolver.INSTANCE);
40 EntryTree<EntryMapping> result = new HashEntryTree<>(); 40 EntryTree<EntryMapping> result = new HashEntryTree<>();
41 Set<Entry<?>> addedMappings = new HashSet<>(); 41 Set<Entry<?>> addedMappings = new HashSet<>();
42 42
43 for (EntryTreeNode<EntryMapping> node : left) { 43 for (EntryTreeNode<EntryMapping> node : left) {
44 Entry<?> leftEntry = node.getEntry(); 44 Entry<?> leftEntry = node.getEntry();
45 EntryMapping leftMapping = node.getValue(); 45 EntryMapping leftMapping = node.getValue();
46 46
47 Entry<?> rightEntry = leftTranslator.translate(leftEntry); 47 Entry<?> rightEntry = leftTranslator.translate(leftEntry);
48 48
49 EntryMapping rightMapping = right.get(rightEntry); 49 EntryMapping rightMapping = right.get(rightEntry);
50 if (rightMapping != null) { 50
51 result.insert(leftEntry, rightMapping); 51 if (rightMapping != null) {
52 addedMappings.add(rightEntry); 52 result.insert(leftEntry, rightMapping);
53 } else if (keepLeftOnly) { 53 addedMappings.add(rightEntry);
54 result.insert(leftEntry, leftMapping); 54 } else if (keepLeftOnly) {
55 } 55 result.insert(leftEntry, leftMapping);
56 } 56 }
57 57 }
58 if (keepRightOnly) { 58
59 Translator leftInverseTranslator = new MappingTranslator(invert(left), VoidEntryResolver.INSTANCE); 59 if (keepRightOnly) {
60 for (EntryTreeNode<EntryMapping> node : right) { 60 Translator leftInverseTranslator = new MappingTranslator(invert(left), VoidEntryResolver.INSTANCE);
61 Entry<?> rightEntry = node.getEntry(); 61
62 EntryMapping rightMapping = node.getValue(); 62 for (EntryTreeNode<EntryMapping> node : right) {
63 63 Entry<?> rightEntry = node.getEntry();
64 if (!addedMappings.contains(rightEntry)) { 64 EntryMapping rightMapping = node.getValue();
65 result.insert(leftInverseTranslator.translate(rightEntry), rightMapping); 65
66 } 66 if (!addedMappings.contains(rightEntry)) {
67 } 67 result.insert(leftInverseTranslator.translate(rightEntry), rightMapping);
68 } 68 }
69 return result; 69 }
70 } 70 }
71
72 return result;
73 }
71} 74}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingPair.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingPair.java
index 5d39e3d..21c78cf 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingPair.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingPair.java
@@ -1,9 +1,9 @@
1package cuchaz.enigma.translation.mapping; 1package cuchaz.enigma.translation.mapping;
2 2
3import cuchaz.enigma.translation.representation.entry.Entry;
4
5import javax.annotation.Nullable; 3import javax.annotation.Nullable;
6 4
5import cuchaz.enigma.translation.representation.entry.Entry;
6
7public class MappingPair<E extends Entry<?>, M> { 7public class MappingPair<E extends Entry<?>, M> {
8 private final E entry; 8 private final E entry;
9 private M mapping; 9 private M mapping;
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java
index 065e5c3..5f42373 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java
@@ -14,7 +14,6 @@ import cuchaz.enigma.utils.validation.Message;
14import cuchaz.enigma.utils.validation.ValidationContext; 14import cuchaz.enigma.utils.validation.ValidationContext;
15 15
16public class MappingValidator { 16public class MappingValidator {
17
18 private final EntryTree<EntryMapping> obfToDeobf; 17 private final EntryTree<EntryMapping> obfToDeobf;
19 private final Translator deobfuscator; 18 private final Translator deobfuscator;
20 private final JarIndex index; 19 private final JarIndex index;
@@ -28,10 +27,12 @@ public class MappingValidator {
28 public boolean validateRename(ValidationContext vc, Entry<?> entry, String name) { 27 public boolean validateRename(ValidationContext vc, Entry<?> entry, String name) {
29 Collection<Entry<?>> equivalentEntries = index.getEntryResolver().resolveEquivalentEntries(entry); 28 Collection<Entry<?>> equivalentEntries = index.getEntryResolver().resolveEquivalentEntries(entry);
30 boolean error = false; 29 boolean error = false;
30
31 for (Entry<?> equivalentEntry : equivalentEntries) { 31 for (Entry<?> equivalentEntry : equivalentEntries) {
32 equivalentEntry.validateName(vc, name); 32 equivalentEntry.validateName(vc, name);
33 error |= validateUnique(vc, equivalentEntry, name); 33 error |= validateUnique(vc, equivalentEntry, name);
34 } 34 }
35
35 return error; 36 return error;
36 } 37 }
37 38
@@ -45,17 +46,17 @@ public class MappingValidator {
45 Entry<?> relatedEntry = entry.replaceAncestor(containingClass, relatedClass); 46 Entry<?> relatedEntry = entry.replaceAncestor(containingClass, relatedClass);
46 Entry<?> translatedEntry = deobfuscator.translate(relatedEntry); 47 Entry<?> translatedEntry = deobfuscator.translate(relatedEntry);
47 48
48 List<? extends Entry<?>> translatedSiblings = obfToDeobf.getSiblings(relatedEntry).stream() 49 List<? extends Entry<?>> translatedSiblings = obfToDeobf.getSiblings(relatedEntry).stream().map(deobfuscator::translate).toList();
49 .map(deobfuscator::translate)
50 .toList();
51 50
52 if (!isUnique(translatedEntry, translatedSiblings, name)) { 51 if (!isUnique(translatedEntry, translatedSiblings, name)) {
53 Entry<?> parent = translatedEntry.getParent(); 52 Entry<?> parent = translatedEntry.getParent();
53
54 if (parent != null) { 54 if (parent != null) {
55 vc.raise(Message.NONUNIQUE_NAME_CLASS, name, parent); 55 vc.raise(Message.NONUNIQUE_NAME_CLASS, name, parent);
56 } else { 56 } else {
57 vc.raise(Message.NONUNIQUE_NAME, name); 57 vc.raise(Message.NONUNIQUE_NAME, name);
58 } 58 }
59
59 error = true; 60 error = true;
60 } 61 }
61 } 62 }
@@ -80,7 +81,7 @@ public class MappingValidator {
80 return false; 81 return false;
81 } 82 }
82 } 83 }
84
83 return true; 85 return true;
84 } 86 }
85
86} 87}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java
index 33247fa..dc8055b 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java
@@ -1,16 +1,20 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.translation.mapping; 12package cuchaz.enigma.translation.mapping;
13 13
14import java.util.Collection;
15import java.util.HashMap;
16import java.util.Map;
17
14import cuchaz.enigma.ProgressListener; 18import cuchaz.enigma.ProgressListener;
15import cuchaz.enigma.analysis.index.JarIndex; 19import cuchaz.enigma.analysis.index.JarIndex;
16import cuchaz.enigma.translation.mapping.tree.EntryTree; 20import cuchaz.enigma.translation.mapping.tree.EntryTree;
@@ -21,10 +25,6 @@ import cuchaz.enigma.translation.representation.entry.FieldEntry;
21import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; 25import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
22import cuchaz.enigma.translation.representation.entry.MethodEntry; 26import cuchaz.enigma.translation.representation.entry.MethodEntry;
23 27
24import java.util.Collection;
25import java.util.HashMap;
26import java.util.Map;
27
28public class MappingsChecker { 28public class MappingsChecker {
29 private final JarIndex index; 29 private final JarIndex index;
30 private final EntryTree<EntryMapping> mappings; 30 private final EntryTree<EntryMapping> mappings;
@@ -37,13 +37,12 @@ public class MappingsChecker {
37 public Dropped dropBrokenMappings(ProgressListener progress) { 37 public Dropped dropBrokenMappings(ProgressListener progress) {
38 Dropped dropped = new Dropped(); 38 Dropped dropped = new Dropped();
39 39
40 Collection<Entry<?>> obfEntries = mappings.getAllEntries() 40 Collection<Entry<?>> obfEntries = mappings.getAllEntries().filter(e -> e instanceof ClassEntry || e instanceof MethodEntry || e instanceof FieldEntry || e instanceof LocalVariableEntry).toList();
41 .filter(e -> e instanceof ClassEntry || e instanceof MethodEntry || e instanceof FieldEntry || e instanceof LocalVariableEntry)
42 .toList();
43 41
44 progress.init(obfEntries.size(), "Checking for dropped mappings"); 42 progress.init(obfEntries.size(), "Checking for dropped mappings");
45 43
46 int steps = 0; 44 int steps = 0;
45
47 for (Entry<?> entry : obfEntries) { 46 for (Entry<?> entry : obfEntries) {
48 progress.step(steps++, entry.toString()); 47 progress.step(steps++, entry.toString());
49 tryDropEntry(dropped, entry); 48 tryDropEntry(dropped, entry);
@@ -57,6 +56,7 @@ public class MappingsChecker {
57 private void tryDropEntry(Dropped dropped, Entry<?> entry) { 56 private void tryDropEntry(Dropped dropped, Entry<?> entry) {
58 if (shouldDropEntry(entry)) { 57 if (shouldDropEntry(entry)) {
59 EntryMapping mapping = mappings.get(entry); 58 EntryMapping mapping = mappings.get(entry);
59
60 if (mapping != null) { 60 if (mapping != null) {
61 dropped.drop(entry, mapping); 61 dropped.drop(entry, mapping);
62 } 62 }
@@ -102,6 +102,7 @@ public class MappingsChecker {
102 void apply(EntryTree<EntryMapping> mappings) { 102 void apply(EntryTree<EntryMapping> mappings) {
103 for (Entry<?> entry : droppedMappings.keySet()) { 103 for (Entry<?> entry : droppedMappings.keySet()) {
104 EntryTreeNode<EntryMapping> node = mappings.findNode(entry); 104 EntryTreeNode<EntryMapping> node = mappings.findNode(entry);
105
105 if (node == null) { 106 if (node == null) {
106 continue; 107 continue;
107 } 108 }
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/VoidEntryResolver.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/VoidEntryResolver.java
index 2eab55f..1997572 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/VoidEntryResolver.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/VoidEntryResolver.java
@@ -1,12 +1,12 @@
1package cuchaz.enigma.translation.mapping; 1package cuchaz.enigma.translation.mapping;
2 2
3import cuchaz.enigma.translation.representation.entry.Entry;
4import cuchaz.enigma.translation.representation.entry.MethodEntry;
5
6import java.util.Collection; 3import java.util.Collection;
7import java.util.Collections; 4import java.util.Collections;
8import java.util.Set; 5import java.util.Set;
9 6
7import cuchaz.enigma.translation.representation.entry.Entry;
8import cuchaz.enigma.translation.representation.entry.MethodEntry;
9
10public enum VoidEntryResolver implements EntryResolver { 10public enum VoidEntryResolver implements EntryResolver {
11 INSTANCE; 11 INSTANCE;
12 12
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/LfPrintWriter.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/LfPrintWriter.java
index 441949c..f118e64 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/LfPrintWriter.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/LfPrintWriter.java
@@ -4,13 +4,13 @@ import java.io.PrintWriter;
4import java.io.Writer; 4import java.io.Writer;
5 5
6public class LfPrintWriter extends PrintWriter { 6public class LfPrintWriter extends PrintWriter {
7 public LfPrintWriter(Writer out) { 7 public LfPrintWriter(Writer out) {
8 super(out); 8 super(out);
9 } 9 }
10 10
11 @Override 11 @Override
12 public void println() { 12 public void println() {
13 // https://stackoverflow.com/a/14749004 13 // https://stackoverflow.com/a/14749004
14 write('\n'); 14 write('\n');
15 } 15 }
16} 16}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java
index 062c877..3be8048 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java
@@ -1,5 +1,10 @@
1package cuchaz.enigma.translation.mapping.serde; 1package cuchaz.enigma.translation.mapping.serde;
2 2
3import java.io.IOException;
4import java.nio.file.Path;
5
6import javax.annotation.Nullable;
7
3import cuchaz.enigma.ProgressListener; 8import cuchaz.enigma.ProgressListener;
4import cuchaz.enigma.translation.mapping.EntryMapping; 9import cuchaz.enigma.translation.mapping.EntryMapping;
5import cuchaz.enigma.translation.mapping.MappingDelta; 10import cuchaz.enigma.translation.mapping.MappingDelta;
@@ -14,10 +19,6 @@ import cuchaz.enigma.translation.mapping.serde.tinyv2.TinyV2Reader;
14import cuchaz.enigma.translation.mapping.serde.tinyv2.TinyV2Writer; 19import cuchaz.enigma.translation.mapping.serde.tinyv2.TinyV2Writer;
15import cuchaz.enigma.translation.mapping.tree.EntryTree; 20import cuchaz.enigma.translation.mapping.tree.EntryTree;
16 21
17import javax.annotation.Nullable;
18import java.io.IOException;
19import java.nio.file.Path;
20
21public enum MappingFormat { 22public enum MappingFormat {
22 ENIGMA_FILE(EnigmaMappingsWriter.FILE, EnigmaMappingsReader.FILE), 23 ENIGMA_FILE(EnigmaMappingsWriter.FILE, EnigmaMappingsReader.FILE),
23 ENIGMA_DIRECTORY(EnigmaMappingsWriter.DIRECTORY, EnigmaMappingsReader.DIRECTORY), 24 ENIGMA_DIRECTORY(EnigmaMappingsWriter.DIRECTORY, EnigmaMappingsReader.DIRECTORY),
@@ -28,7 +29,6 @@ public enum MappingFormat {
28 PROGUARD(null, ProguardMappingsReader.INSTANCE), 29 PROGUARD(null, ProguardMappingsReader.INSTANCE),
29 RECAF(null, RecafMappingsReader.INSTANCE); 30 RECAF(null, RecafMappingsReader.INSTANCE);
30 31
31
32 private final MappingsWriter writer; 32 private final MappingsWriter writer;
33 private final MappingsReader reader; 33 private final MappingsReader reader;
34 34
@@ -37,14 +37,15 @@ public enum MappingFormat {
37 this.reader = reader; 37 this.reader = reader;
38 } 38 }
39 39
40 public void write(EntryTree<EntryMapping> mappings, Path path, ProgressListener progressListener, MappingSaveParameters saveParameters) { 40 public void write(EntryTree<EntryMapping> mappings, Path path, ProgressListener progressListener, MappingSaveParameters saveParameters) {
41 write(mappings, MappingDelta.added(mappings), path, progressListener, saveParameters); 41 write(mappings, MappingDelta.added(mappings), path, progressListener, saveParameters);
42 } 42 }
43 43
44 public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progressListener, MappingSaveParameters saveParameters) { 44 public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progressListener, MappingSaveParameters saveParameters) {
45 if (writer == null) { 45 if (writer == null) {
46 throw new IllegalStateException(name() + " does not support writing"); 46 throw new IllegalStateException(name() + " does not support writing");
47 } 47 }
48
48 writer.write(mappings, delta, path, progressListener, saveParameters); 49 writer.write(mappings, delta, path, progressListener, saveParameters);
49 } 50 }
50 51
@@ -52,6 +53,7 @@ public enum MappingFormat {
52 if (reader == null) { 53 if (reader == null) {
53 throw new IllegalStateException(name() + " does not support reading"); 54 throw new IllegalStateException(name() + " does not support reading");
54 } 55 }
56
55 return reader.read(path, progressListener, saveParameters); 57 return reader.read(path, progressListener, saveParameters);
56 } 58 }
57 59
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingHelper.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingHelper.java
index 7c8f6cc..5f466bb 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingHelper.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingHelper.java
@@ -6,21 +6,27 @@ public final class MappingHelper {
6 6
7 public static String escape(String raw) { 7 public static String escape(String raw) {
8 StringBuilder builder = new StringBuilder(raw.length() + 1); 8 StringBuilder builder = new StringBuilder(raw.length() + 1);
9
9 for (int i = 0; i < raw.length(); i++) { 10 for (int i = 0; i < raw.length(); i++) {
10 final char c = raw.charAt(i); 11 final char c = raw.charAt(i);
11 final int r = TO_ESCAPE.indexOf(c); 12 final int r = TO_ESCAPE.indexOf(c);
13
12 if (r < 0) { 14 if (r < 0) {
13 builder.append(c); 15 builder.append(c);
14 } else { 16 } else {
15 builder.append('\\').append(ESCAPED.charAt(r)); 17 builder.append('\\').append(ESCAPED.charAt(r));
16 } 18 }
17 } 19 }
20
18 return builder.toString(); 21 return builder.toString();
19 } 22 }
20 23
21 public static String unescape(String str) { 24 public static String unescape(String str) {
22 int pos = str.indexOf('\\'); 25 int pos = str.indexOf('\\');
23 if (pos < 0) return str; 26
27 if (pos < 0) {
28 return str;
29 }
24 30
25 StringBuilder ret = new StringBuilder(str.length() - 1); 31 StringBuilder ret = new StringBuilder(str.length() - 1);
26 int start = 0; 32 int start = 0;
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingParseException.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingParseException.java
index 9d04b97..6f5d7d6 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingParseException.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingParseException.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.translation.mapping.serde; 12package cuchaz.enigma.translation.mapping.serde;
13 13
@@ -15,7 +15,6 @@ import java.io.File;
15import java.util.function.Supplier; 15import java.util.function.Supplier;
16 16
17public class MappingParseException extends Exception { 17public class MappingParseException extends Exception {
18
19 private int line; 18 private int line;
20 private String message; 19 private String message;
21 private String filePath; 20 private String filePath;
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsReader.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsReader.java
index 2f01375..4fdfdcb 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsReader.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsReader.java
@@ -1,12 +1,12 @@
1package cuchaz.enigma.translation.mapping.serde; 1package cuchaz.enigma.translation.mapping.serde;
2 2
3import java.io.IOException;
4import java.nio.file.Path;
5
3import cuchaz.enigma.ProgressListener; 6import cuchaz.enigma.ProgressListener;
4import cuchaz.enigma.translation.mapping.EntryMapping; 7import cuchaz.enigma.translation.mapping.EntryMapping;
5import cuchaz.enigma.translation.mapping.tree.EntryTree; 8import cuchaz.enigma.translation.mapping.tree.EntryTree;
6 9
7import java.io.IOException;
8import java.nio.file.Path;
9
10public interface MappingsReader { 10public interface MappingsReader {
11 EntryTree<EntryMapping> read(Path path, ProgressListener progress, MappingSaveParameters saveParameters) throws MappingParseException, IOException; 11 EntryTree<EntryMapping> read(Path path, ProgressListener progress, MappingSaveParameters saveParameters) throws MappingParseException, IOException;
12} 12}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java
index 68a8dbb..5c273ad 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java
@@ -1,12 +1,12 @@
1package cuchaz.enigma.translation.mapping.serde; 1package cuchaz.enigma.translation.mapping.serde;
2 2
3import java.nio.file.Path;
4
3import cuchaz.enigma.ProgressListener; 5import cuchaz.enigma.ProgressListener;
4import cuchaz.enigma.translation.mapping.EntryMapping; 6import cuchaz.enigma.translation.mapping.EntryMapping;
5import cuchaz.enigma.translation.mapping.MappingDelta; 7import cuchaz.enigma.translation.mapping.MappingDelta;
6import cuchaz.enigma.translation.mapping.tree.EntryTree; 8import cuchaz.enigma.translation.mapping.tree.EntryTree;
7 9
8import java.nio.file.Path;
9
10public interface MappingsWriter { 10public interface MappingsWriter {
11 void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress, MappingSaveParameters saveParameters); 11 void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress, MappingSaveParameters saveParameters);
12 12
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsReader.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsReader.java
index 61dbe93..1f8b755 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsReader.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsReader.java
@@ -5,7 +5,11 @@ import java.nio.file.FileSystem;
5import java.nio.file.FileSystems; 5import java.nio.file.FileSystems;
6import java.nio.file.Files; 6import java.nio.file.Files;
7import java.nio.file.Path; 7import java.nio.file.Path;
8import java.util.*; 8import java.util.ArrayDeque;
9import java.util.Arrays;
10import java.util.Deque;
11import java.util.List;
12import java.util.Locale;
9 13
10import javax.annotation.Nullable; 14import javax.annotation.Nullable;
11 15
@@ -15,12 +19,20 @@ import cuchaz.enigma.ProgressListener;
15import cuchaz.enigma.translation.mapping.AccessModifier; 19import cuchaz.enigma.translation.mapping.AccessModifier;
16import cuchaz.enigma.translation.mapping.EntryMapping; 20import cuchaz.enigma.translation.mapping.EntryMapping;
17import cuchaz.enigma.translation.mapping.MappingPair; 21import cuchaz.enigma.translation.mapping.MappingPair;
18import cuchaz.enigma.translation.mapping.serde.*; 22import cuchaz.enigma.translation.mapping.serde.MappingHelper;
23import cuchaz.enigma.translation.mapping.serde.MappingParseException;
24import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
25import cuchaz.enigma.translation.mapping.serde.MappingsReader;
26import cuchaz.enigma.translation.mapping.serde.RawEntryMapping;
19import cuchaz.enigma.translation.mapping.tree.EntryTree; 27import cuchaz.enigma.translation.mapping.tree.EntryTree;
20import cuchaz.enigma.translation.mapping.tree.HashEntryTree; 28import cuchaz.enigma.translation.mapping.tree.HashEntryTree;
21import cuchaz.enigma.translation.representation.MethodDescriptor; 29import cuchaz.enigma.translation.representation.MethodDescriptor;
22import cuchaz.enigma.translation.representation.TypeDescriptor; 30import cuchaz.enigma.translation.representation.TypeDescriptor;
23import cuchaz.enigma.translation.representation.entry.*; 31import cuchaz.enigma.translation.representation.entry.ClassEntry;
32import cuchaz.enigma.translation.representation.entry.Entry;
33import cuchaz.enigma.translation.representation.entry.FieldEntry;
34import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
35import cuchaz.enigma.translation.representation.entry.MethodEntry;
24import cuchaz.enigma.utils.I18n; 36import cuchaz.enigma.utils.I18n;
25 37
26public enum EnigmaMappingsReader implements MappingsReader { 38public enum EnigmaMappingsReader implements MappingsReader {
@@ -42,19 +54,18 @@ public enum EnigmaMappingsReader implements MappingsReader {
42 public EntryTree<EntryMapping> read(Path root, ProgressListener progress, MappingSaveParameters saveParameters) throws IOException, MappingParseException { 54 public EntryTree<EntryMapping> read(Path root, ProgressListener progress, MappingSaveParameters saveParameters) throws IOException, MappingParseException {
43 EntryTree<EntryMapping> mappings = new HashEntryTree<>(); 55 EntryTree<EntryMapping> mappings = new HashEntryTree<>();
44 56
45 List<Path> files = Files.walk(root) 57 List<Path> files = Files.walk(root).filter(f -> !Files.isDirectory(f)).filter(f -> f.toString().endsWith(".mapping")).toList();
46 .filter(f -> !Files.isDirectory(f))
47 .filter(f -> f.toString().endsWith(".mapping"))
48 .toList();
49 58
50 progress.init(files.size(), I18n.translate("progress.mappings.enigma_directory.loading")); 59 progress.init(files.size(), I18n.translate("progress.mappings.enigma_directory.loading"));
51 int step = 0; 60 int step = 0;
52 61
53 for (Path file : files) { 62 for (Path file : files) {
54 progress.step(step++, root.relativize(file).toString()); 63 progress.step(step++, root.relativize(file).toString());
64
55 if (Files.isHidden(file)) { 65 if (Files.isHidden(file)) {
56 continue; 66 continue;
57 } 67 }
68
58 readFile(file, mappings); 69 readFile(file, mappings);
59 } 70 }
60 71
@@ -74,10 +85,10 @@ public enum EnigmaMappingsReader implements MappingsReader {
74 * Reads multiple Enigma mapping files. 85 * Reads multiple Enigma mapping files.
75 * 86 *
76 * @param progress the progress listener 87 * @param progress the progress listener
77 * @param paths the Enigma files to read; cannot be empty 88 * @param paths the Enigma files to read; cannot be empty
78 * @return the parsed mappings 89 * @return the parsed mappings
79 * @throws MappingParseException if a mapping file cannot be parsed 90 * @throws MappingParseException if a mapping file cannot be parsed
80 * @throws IOException if an IO error occurs 91 * @throws IOException if an IO error occurs
81 * @throws IllegalArgumentException if there are no paths to read 92 * @throws IllegalArgumentException if there are no paths to read
82 */ 93 */
83 public static EntryTree<EntryMapping> readFiles(ProgressListener progress, Path... paths) throws MappingParseException, IOException { 94 public static EntryTree<EntryMapping> readFiles(ProgressListener progress, Path... paths) throws MappingParseException, IOException {
@@ -107,6 +118,7 @@ public enum EnigmaMappingsReader implements MappingsReader {
107 int indentation = countIndentation(line, path, lineNumber); 118 int indentation = countIndentation(line, path, lineNumber);
108 119
109 line = formatLine(line); 120 line = formatLine(line);
121
110 if (line == null) { 122 if (line == null) {
111 continue; 123 continue;
112 } 124 }
@@ -115,6 +127,7 @@ public enum EnigmaMappingsReader implements MappingsReader {
115 127
116 try { 128 try {
117 MappingPair<?, RawEntryMapping> pair = parseLine(mappingStack.peek(), line); 129 MappingPair<?, RawEntryMapping> pair = parseLine(mappingStack.peek(), line);
130
118 if (pair != null) { 131 if (pair != null) {
119 mappingStack.push(pair); 132 mappingStack.push(pair);
120 } 133 }
@@ -131,6 +144,7 @@ public enum EnigmaMappingsReader implements MappingsReader {
131 private static void cleanMappingStack(int indentation, Deque<MappingPair<?, RawEntryMapping>> mappingStack, EntryTree<EntryMapping> mappings) { 144 private static void cleanMappingStack(int indentation, Deque<MappingPair<?, RawEntryMapping>> mappingStack, EntryTree<EntryMapping> mappings) {
132 while (indentation < mappingStack.size()) { 145 while (indentation < mappingStack.size()) {
133 MappingPair<?, RawEntryMapping> pair = mappingStack.pop(); 146 MappingPair<?, RawEntryMapping> pair = mappingStack.pop();
147
134 if (pair.getMapping() != null) { 148 if (pair.getMapping() != null) {
135 mappings.insert(pair.getEntry(), pair.getMapping().bake()); 149 mappings.insert(pair.getEntry(), pair.getMapping().bake());
136 } 150 }
@@ -156,14 +170,17 @@ public enum EnigmaMappingsReader implements MappingsReader {
156 } 170 }
157 171
158 int commentPos = line.indexOf('#'); 172 int commentPos = line.indexOf('#');
173
159 if (commentPos >= 0) { 174 if (commentPos >= 0) {
160 return line.substring(0, commentPos); 175 return line.substring(0, commentPos);
161 } 176 }
177
162 return line; 178 return line;
163 } 179 }
164 180
165 private static int countIndentation(String line, Path path, int lineNumber) throws MappingParseException { 181 private static int countIndentation(String line, Path path, int lineNumber) throws MappingParseException {
166 int indent = 0; 182 int indent = 0;
183
167 for (int i = 0; i < line.length(); i++) { 184 for (int i = 0; i < line.length(); i++) {
168 if (line.charAt(i) == ' ') { 185 if (line.charAt(i) == ' ') {
169 throw new MappingParseException(path::toString, lineNumber + 1, "Spaces must not be used to indent lines!"); 186 throw new MappingParseException(path::toString, lineNumber + 1, "Spaces must not be used to indent lines!");
@@ -172,8 +189,10 @@ public enum EnigmaMappingsReader implements MappingsReader {
172 if (line.charAt(i) != '\t') { 189 if (line.charAt(i) != '\t') {
173 break; 190 break;
174 } 191 }
192
175 indent++; 193 indent++;
176 } 194 }
195
177 return indent; 196 return indent;
178 } 197 }
179 198
@@ -183,36 +202,41 @@ public enum EnigmaMappingsReader implements MappingsReader {
183 Entry<?> parentEntry = parent == null ? null : parent.getEntry(); 202 Entry<?> parentEntry = parent == null ? null : parent.getEntry();
184 203
185 switch (keyToken) { 204 switch (keyToken) {
186 case EnigmaFormat.CLASS: 205 case EnigmaFormat.CLASS:
187 return parseClass(parentEntry, tokens); 206 return parseClass(parentEntry, tokens);
188 case EnigmaFormat.FIELD: 207 case EnigmaFormat.FIELD:
189 return parseField(parentEntry, tokens); 208 return parseField(parentEntry, tokens);
190 case EnigmaFormat.METHOD: 209 case EnigmaFormat.METHOD:
191 return parseMethod(parentEntry, tokens); 210 return parseMethod(parentEntry, tokens);
192 case EnigmaFormat.PARAMETER: 211 case EnigmaFormat.PARAMETER:
193 return parseArgument(parentEntry, tokens); 212 return parseArgument(parentEntry, tokens);
194 case EnigmaFormat.COMMENT: 213 case EnigmaFormat.COMMENT:
195 readJavadoc(parent, tokens); 214 readJavadoc(parent, tokens);
196 return null; 215 return null;
197 default: 216 default:
198 throw new RuntimeException("Unknown token '" + keyToken + "'"); 217 throw new RuntimeException("Unknown token '" + keyToken + "'");
199 } 218 }
200 } 219 }
201 220
202 private static void readJavadoc(MappingPair<?, RawEntryMapping> parent, String[] tokens) { 221 private static void readJavadoc(MappingPair<?, RawEntryMapping> parent, String[] tokens) {
203 if (parent == null) 222 if (parent == null) {
204 throw new IllegalStateException("Javadoc has no parent!"); 223 throw new IllegalStateException("Javadoc has no parent!");
224 }
225
205 // Empty string to concat 226 // Empty string to concat
206 String jdLine = tokens.length > 1 ? String.join(" ", Arrays.copyOfRange(tokens, 1, tokens.length)) : ""; 227 String jdLine = tokens.length > 1 ? String.join(" ", Arrays.copyOfRange(tokens, 1, tokens.length)) : "";
228
207 if (parent.getMapping() == null) { 229 if (parent.getMapping() == null) {
208 parent.setMapping(new RawEntryMapping(parent.getEntry().getName(), AccessModifier.UNCHANGED)); 230 parent.setMapping(new RawEntryMapping(parent.getEntry().getName(), AccessModifier.UNCHANGED));
209 } 231 }
232
210 parent.getMapping().addJavadocLine(MappingHelper.unescape(jdLine)); 233 parent.getMapping().addJavadocLine(MappingHelper.unescape(jdLine));
211 } 234 }
212 235
213 private static MappingPair<ClassEntry, RawEntryMapping> parseClass(@Nullable Entry<?> parent, String[] tokens) { 236 private static MappingPair<ClassEntry, RawEntryMapping> parseClass(@Nullable Entry<?> parent, String[] tokens) {
214 String obfuscatedName = ClassEntry.getInnerName(tokens[1]); 237 String obfuscatedName = ClassEntry.getInnerName(tokens[1]);
215 ClassEntry obfuscatedEntry; 238 ClassEntry obfuscatedEntry;
239
216 if (parent instanceof ClassEntry) { 240 if (parent instanceof ClassEntry) {
217 obfuscatedEntry = new ClassEntry((ClassEntry) parent, obfuscatedName); 241 obfuscatedEntry = new ClassEntry((ClassEntry) parent, obfuscatedName);
218 } else { 242 } else {
@@ -224,6 +248,7 @@ public enum EnigmaMappingsReader implements MappingsReader {
224 248
225 if (tokens.length == 3) { 249 if (tokens.length == 3) {
226 AccessModifier parsedModifier = parseModifier(tokens[2]); 250 AccessModifier parsedModifier = parseModifier(tokens[2]);
251
227 if (parsedModifier != null) { 252 if (parsedModifier != null) {
228 modifier = parsedModifier; 253 modifier = parsedModifier;
229 mapping = obfuscatedName; 254 mapping = obfuscatedName;
@@ -254,6 +279,7 @@ public enum EnigmaMappingsReader implements MappingsReader {
254 descriptor = new TypeDescriptor(tokens[2]); 279 descriptor = new TypeDescriptor(tokens[2]);
255 } else if (tokens.length == 4) { 280 } else if (tokens.length == 4) {
256 AccessModifier parsedModifier = parseModifier(tokens[3]); 281 AccessModifier parsedModifier = parseModifier(tokens[3]);
282
257 if (parsedModifier != null) { 283 if (parsedModifier != null) {
258 descriptor = new TypeDescriptor(tokens[2]); 284 descriptor = new TypeDescriptor(tokens[2]);
259 modifier = parsedModifier; 285 modifier = parsedModifier;
@@ -289,6 +315,7 @@ public enum EnigmaMappingsReader implements MappingsReader {
289 descriptor = new MethodDescriptor(tokens[2]); 315 descriptor = new MethodDescriptor(tokens[2]);
290 } else if (tokens.length == 4) { 316 } else if (tokens.length == 4) {
291 AccessModifier parsedModifier = parseModifier(tokens[3]); 317 AccessModifier parsedModifier = parseModifier(tokens[3]);
318
292 if (parsedModifier != null) { 319 if (parsedModifier != null) {
293 modifier = parsedModifier; 320 modifier = parsedModifier;
294 mapping = obfuscatedName; 321 mapping = obfuscatedName;
@@ -326,6 +353,7 @@ public enum EnigmaMappingsReader implements MappingsReader {
326 if (token.startsWith("ACC:")) { 353 if (token.startsWith("ACC:")) {
327 return AccessModifier.valueOf(token.substring(4)); 354 return AccessModifier.valueOf(token.substring(4));
328 } 355 }
356
329 return null; 357 return null;
330 } 358 }
331} 359}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsWriter.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsWriter.java
index 5d7a789..cd00ef7 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsWriter.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsWriter.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.translation.mapping.serde.enigma; 12package cuchaz.enigma.translation.mapping.serde.enigma;
13 13
@@ -15,7 +15,12 @@ import java.io.IOException;
15import java.io.PrintWriter; 15import java.io.PrintWriter;
16import java.net.URI; 16import java.net.URI;
17import java.net.URISyntaxException; 17import java.net.URISyntaxException;
18import java.nio.file.*; 18import java.nio.file.DirectoryStream;
19import java.nio.file.FileSystem;
20import java.nio.file.FileSystems;
21import java.nio.file.Files;
22import java.nio.file.Path;
23import java.nio.file.Paths;
19import java.util.ArrayList; 24import java.util.ArrayList;
20import java.util.Collection; 25import java.util.Collection;
21import java.util.Collections; 26import java.util.Collections;
@@ -32,24 +37,30 @@ import cuchaz.enigma.translation.mapping.AccessModifier;
32import cuchaz.enigma.translation.mapping.EntryMapping; 37import cuchaz.enigma.translation.mapping.EntryMapping;
33import cuchaz.enigma.translation.mapping.MappingDelta; 38import cuchaz.enigma.translation.mapping.MappingDelta;
34import cuchaz.enigma.translation.mapping.VoidEntryResolver; 39import cuchaz.enigma.translation.mapping.VoidEntryResolver;
35import cuchaz.enigma.translation.mapping.serde.*; 40import cuchaz.enigma.translation.mapping.serde.LfPrintWriter;
41import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat;
42import cuchaz.enigma.translation.mapping.serde.MappingHelper;
43import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
44import cuchaz.enigma.translation.mapping.serde.MappingsWriter;
36import cuchaz.enigma.translation.mapping.tree.EntryTree; 45import cuchaz.enigma.translation.mapping.tree.EntryTree;
37import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; 46import cuchaz.enigma.translation.mapping.tree.EntryTreeNode;
38import cuchaz.enigma.translation.representation.entry.*; 47import cuchaz.enigma.translation.representation.entry.ClassEntry;
48import cuchaz.enigma.translation.representation.entry.Entry;
49import cuchaz.enigma.translation.representation.entry.FieldEntry;
50import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
51import cuchaz.enigma.translation.representation.entry.MethodEntry;
39import cuchaz.enigma.utils.I18n; 52import cuchaz.enigma.utils.I18n;
40 53
41public enum EnigmaMappingsWriter implements MappingsWriter { 54public enum EnigmaMappingsWriter implements MappingsWriter {
42 FILE { 55 FILE {
43 @Override 56 @Override
44 public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress, MappingSaveParameters saveParameters) { 57 public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress, MappingSaveParameters saveParameters) {
45 Collection<ClassEntry> classes = mappings.getRootNodes() 58 Collection<ClassEntry> classes = mappings.getRootNodes().filter(entry -> entry.getEntry() instanceof ClassEntry).map(entry -> (ClassEntry) entry.getEntry()).toList();
46 .filter(entry -> entry.getEntry() instanceof ClassEntry)
47 .map(entry -> (ClassEntry) entry.getEntry())
48 .toList();
49 59
50 progress.init(classes.size(), I18n.translate("progress.mappings.enigma_file.writing")); 60 progress.init(classes.size(), I18n.translate("progress.mappings.enigma_file.writing"));
51 61
52 int steps = 0; 62 int steps = 0;
63
53 try (PrintWriter writer = new LfPrintWriter(Files.newBufferedWriter(path))) { 64 try (PrintWriter writer = new LfPrintWriter(Files.newBufferedWriter(path))) {
54 for (ClassEntry classEntry : classes) { 65 for (ClassEntry classEntry : classes) {
55 progress.step(steps++, classEntry.getFullName()); 66 progress.step(steps++, classEntry.getFullName());
@@ -63,10 +74,7 @@ public enum EnigmaMappingsWriter implements MappingsWriter {
63 DIRECTORY { 74 DIRECTORY {
64 @Override 75 @Override
65 public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress, MappingSaveParameters saveParameters) { 76 public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress, MappingSaveParameters saveParameters) {
66 Collection<ClassEntry> changedClasses = delta.getChangedRoots() 77 Collection<ClassEntry> changedClasses = delta.getChangedRoots().filter(entry -> entry instanceof ClassEntry).map(entry -> (ClassEntry) entry).toList();
67 .filter(entry -> entry instanceof ClassEntry)
68 .map(entry -> (ClassEntry) entry)
69 .toList();
70 78
71 applyDeletions(path, changedClasses, mappings, delta.getBaseMappings(), saveParameters.getFileNameFormat()); 79 applyDeletions(path, changedClasses, mappings, delta.getBaseMappings(), saveParameters.getFileNameFormat());
72 80
@@ -80,6 +88,7 @@ public enum EnigmaMappingsWriter implements MappingsWriter {
80 88
81 try { 89 try {
82 ClassEntry fileEntry = classEntry; 90 ClassEntry fileEntry = classEntry;
91
83 if (saveParameters.getFileNameFormat() == MappingFileNameFormat.BY_DEOBF) { 92 if (saveParameters.getFileNameFormat() == MappingFileNameFormat.BY_DEOBF) {
84 fileEntry = translator.translate(fileEntry); 93 fileEntry = translator.translate(fileEntry);
85 } 94 }
@@ -101,8 +110,7 @@ public enum EnigmaMappingsWriter implements MappingsWriter {
101 private void applyDeletions(Path root, Collection<ClassEntry> changedClasses, EntryTree<EntryMapping> mappings, EntryTree<EntryMapping> oldMappings, MappingFileNameFormat fileNameFormat) { 110 private void applyDeletions(Path root, Collection<ClassEntry> changedClasses, EntryTree<EntryMapping> mappings, EntryTree<EntryMapping> oldMappings, MappingFileNameFormat fileNameFormat) {
102 Translator oldMappingTranslator = new MappingTranslator(oldMappings, VoidEntryResolver.INSTANCE); 111 Translator oldMappingTranslator = new MappingTranslator(oldMappings, VoidEntryResolver.INSTANCE);
103 112
104 Stream<ClassEntry> deletedClassStream = changedClasses.stream() 113 Stream<ClassEntry> deletedClassStream = changedClasses.stream().filter(e -> !Objects.equals(oldMappings.get(e), mappings.get(e)));
105 .filter(e -> !Objects.equals(oldMappings.get(e), mappings.get(e)));
106 114
107 if (fileNameFormat == MappingFileNameFormat.BY_DEOBF) { 115 if (fileNameFormat == MappingFileNameFormat.BY_DEOBF) {
108 deletedClassStream = deletedClassStream.map(oldMappingTranslator::translate); 116 deletedClassStream = deletedClassStream.map(oldMappingTranslator::translate);
@@ -121,8 +129,10 @@ public enum EnigmaMappingsWriter implements MappingsWriter {
121 129
122 for (ClassEntry classEntry : deletedClasses) { 130 for (ClassEntry classEntry : deletedClasses) {
123 String packageName = classEntry.getPackageName(); 131 String packageName = classEntry.getPackageName();
132
124 if (packageName != null) { 133 if (packageName != null) {
125 Path packagePath = Paths.get(packageName); 134 Path packagePath = Paths.get(packageName);
135
126 try { 136 try {
127 deleteDeadPackages(root, packagePath); 137 deleteDeadPackages(root, packagePath);
128 } catch (IOException e) { 138 } catch (IOException e) {
@@ -137,6 +147,7 @@ public enum EnigmaMappingsWriter implements MappingsWriter {
137 for (int i = packagePath.getNameCount() - 1; i >= 0; i--) { 147 for (int i = packagePath.getNameCount() - 1; i >= 0; i--) {
138 Path subPath = packagePath.subpath(0, i + 1); 148 Path subPath = packagePath.subpath(0, i + 1);
139 Path packagePart = root.resolve(subPath.toString()); 149 Path packagePart = root.resolve(subPath.toString());
150
140 if (isEmpty(packagePart)) { 151 if (isEmpty(packagePart)) {
141 Files.deleteIfExists(packagePart); 152 Files.deleteIfExists(packagePart);
142 } 153 }
@@ -178,6 +189,7 @@ public enum EnigmaMappingsWriter implements MappingsWriter {
178 } 189 }
179 190
180 writer.println(writeClass(classEntry, classEntryMapping).trim()); 191 writer.println(writeClass(classEntry, classEntryMapping).trim());
192
181 if (classEntryMapping.javadoc() != null) { 193 if (classEntryMapping.javadoc() != null) {
182 writeDocs(writer, classEntryMapping, 0); 194 writeDocs(writer, classEntryMapping, 0);
183 } 195 }
@@ -189,6 +201,7 @@ public enum EnigmaMappingsWriter implements MappingsWriter {
189 201
190 private void writeDocs(PrintWriter writer, EntryMapping mapping, int depth) { 202 private void writeDocs(PrintWriter writer, EntryMapping mapping, int depth) {
191 String jd = mapping.javadoc(); 203 String jd = mapping.javadoc();
204
192 if (jd != null) { 205 if (jd != null) {
193 for (String line : jd.split("\\R")) { 206 for (String line : jd.split("\\R")) {
194 writer.println(indent(EnigmaFormat.COMMENT + " " + MappingHelper.escape(line), depth + 1)); 207 writer.println(indent(EnigmaFormat.COMMENT + " " + MappingHelper.escape(line), depth + 1));
@@ -198,6 +211,7 @@ public enum EnigmaMappingsWriter implements MappingsWriter {
198 211
199 protected void writeEntry(PrintWriter writer, EntryTree<EntryMapping> mappings, Entry<?> entry, int depth) { 212 protected void writeEntry(PrintWriter writer, EntryTree<EntryMapping> mappings, Entry<?> entry, int depth) {
200 EntryTreeNode<EntryMapping> node = mappings.findNode(entry); 213 EntryTreeNode<EntryMapping> node = mappings.findNode(entry);
214
201 if (node == null) { 215 if (node == null) {
202 return; 216 return;
203 } 217 }
@@ -209,6 +223,7 @@ public enum EnigmaMappingsWriter implements MappingsWriter {
209 } 223 }
210 224
211 String line = null; 225 String line = null;
226
212 if (entry instanceof ClassEntry classEntry) { 227 if (entry instanceof ClassEntry classEntry) {
213 line = writeClass(classEntry, mapping); 228 line = writeClass(classEntry, mapping);
214 } else if (entry instanceof MethodEntry methodEntry) { 229 } else if (entry instanceof MethodEntry methodEntry) {
@@ -228,6 +243,7 @@ public enum EnigmaMappingsWriter implements MappingsWriter {
228 } 243 }
229 244
230 Collection<Entry<?>> children = groupChildren(node.getChildren()); 245 Collection<Entry<?>> children = groupChildren(node.getChildren());
246
231 for (Entry<?> child : children) { 247 for (Entry<?> child : children) {
232 writeEntry(writer, mappings, child, depth + 1); 248 writeEntry(writer, mappings, child, depth + 1);
233 } 249 }
@@ -236,25 +252,13 @@ public enum EnigmaMappingsWriter implements MappingsWriter {
236 private Collection<Entry<?>> groupChildren(Collection<Entry<?>> children) { 252 private Collection<Entry<?>> groupChildren(Collection<Entry<?>> children) {
237 Collection<Entry<?>> result = new ArrayList<>(children.size()); 253 Collection<Entry<?>> result = new ArrayList<>(children.size());
238 254
239 children.stream().filter(e -> e instanceof FieldEntry) 255 children.stream().filter(e -> e instanceof FieldEntry).map(e -> (FieldEntry) e).sorted().forEach(result::add);
240 .map(e -> (FieldEntry) e)
241 .sorted()
242 .forEach(result::add);
243 256
244 children.stream().filter(e -> e instanceof MethodEntry) 257 children.stream().filter(e -> e instanceof MethodEntry).map(e -> (MethodEntry) e).sorted().forEach(result::add);
245 .map(e -> (MethodEntry) e)
246 .sorted()
247 .forEach(result::add);
248 258
249 children.stream().filter(e -> e instanceof LocalVariableEntry) 259 children.stream().filter(e -> e instanceof LocalVariableEntry).map(e -> (LocalVariableEntry) e).sorted().forEach(result::add);
250 .map(e -> (LocalVariableEntry) e)
251 .sorted()
252 .forEach(result::add);
253 260
254 children.stream().filter(e -> e instanceof ClassEntry) 261 children.stream().filter(e -> e instanceof ClassEntry).map(e -> (ClassEntry) e).sorted().forEach(result::add);
255 .map(e -> (ClassEntry) e)
256 .sorted()
257 .forEach(result::add);
258 262
259 return result; 263 return result;
260 } 264 }
@@ -294,6 +298,7 @@ public enum EnigmaMappingsWriter implements MappingsWriter {
294 private void writeMapping(StringBuilder builder, EntryMapping mapping) { 298 private void writeMapping(StringBuilder builder, EntryMapping mapping) {
295 if (mapping.targetName() != null) { 299 if (mapping.targetName() != null) {
296 builder.append(mapping.targetName()).append(' '); 300 builder.append(mapping.targetName()).append(' ');
301
297 if (mapping.accessModifier() != AccessModifier.UNCHANGED) { 302 if (mapping.accessModifier() != AccessModifier.UNCHANGED) {
298 builder.append(mapping.accessModifier().getFormattedName()).append(' '); 303 builder.append(mapping.accessModifier().getFormattedName()).append(' ');
299 } 304 }
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/proguard/ProguardMappingsReader.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/proguard/ProguardMappingsReader.java
index 034fb41..fc550d4 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/proguard/ProguardMappingsReader.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/proguard/ProguardMappingsReader.java
@@ -1,9 +1,16 @@
1package cuchaz.enigma.translation.mapping.serde.proguard; 1package cuchaz.enigma.translation.mapping.serde.proguard;
2 2
3import java.io.IOException;
4import java.nio.charset.StandardCharsets;
5import java.nio.file.Files;
6import java.nio.file.Path;
7import java.util.regex.Matcher;
8import java.util.regex.Pattern;
9
3import cuchaz.enigma.ProgressListener; 10import cuchaz.enigma.ProgressListener;
11import cuchaz.enigma.translation.mapping.EntryMapping;
4import cuchaz.enigma.translation.mapping.MappingOperations; 12import cuchaz.enigma.translation.mapping.MappingOperations;
5import cuchaz.enigma.translation.mapping.serde.MappingParseException; 13import cuchaz.enigma.translation.mapping.serde.MappingParseException;
6import cuchaz.enigma.translation.mapping.EntryMapping;
7import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; 14import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
8import cuchaz.enigma.translation.mapping.serde.MappingsReader; 15import cuchaz.enigma.translation.mapping.serde.MappingsReader;
9import cuchaz.enigma.translation.mapping.tree.EntryTree; 16import cuchaz.enigma.translation.mapping.tree.EntryTree;
@@ -14,122 +21,117 @@ import cuchaz.enigma.translation.representation.entry.ClassEntry;
14import cuchaz.enigma.translation.representation.entry.FieldEntry; 21import cuchaz.enigma.translation.representation.entry.FieldEntry;
15import cuchaz.enigma.translation.representation.entry.MethodEntry; 22import cuchaz.enigma.translation.representation.entry.MethodEntry;
16 23
17import java.io.IOException;
18import java.nio.charset.StandardCharsets;
19import java.nio.file.Files;
20import java.nio.file.Path;
21import java.util.regex.Matcher;
22import java.util.regex.Pattern;
23
24public class ProguardMappingsReader implements MappingsReader { 24public class ProguardMappingsReader implements MappingsReader {
25 public static final ProguardMappingsReader INSTANCE = new ProguardMappingsReader(); 25 public static final ProguardMappingsReader INSTANCE = new ProguardMappingsReader();
26 private static final String NAME = "[a-zA-Z0-9_\\-.$<>]+"; 26 private static final String NAME = "[a-zA-Z0-9_\\-.$<>]+";
27 private static final String TYPE = NAME + "(?:\\[])*"; 27 private static final String TYPE = NAME + "(?:\\[])*";
28 private static final String TYPE_LIST = "|(?:(?:" + TYPE + ",)*" + TYPE + ")"; 28 private static final String TYPE_LIST = "|(?:(?:" + TYPE + ",)*" + TYPE + ")";
29 private static final Pattern CLASS = Pattern.compile("(" + NAME + ") -> (" + NAME + "):"); 29 private static final Pattern CLASS = Pattern.compile("(" + NAME + ") -> (" + NAME + "):");
30 private static final Pattern FIELD = Pattern.compile(" {4}(" + TYPE + ") (" + NAME + ") -> (" + NAME + ")"); 30 private static final Pattern FIELD = Pattern.compile(" {4}(" + TYPE + ") (" + NAME + ") -> (" + NAME + ")");
31 private static final Pattern METHOD = Pattern.compile(" {4}(?:[0-9]+:[0-9]+:)?(" + TYPE + ") (" + NAME + ")\\((" + TYPE_LIST + ")\\) -> (" + NAME + ")"); 31 private static final Pattern METHOD = Pattern.compile(" {4}(?:[0-9]+:[0-9]+:)?(" + TYPE + ") (" + NAME + ")\\((" + TYPE_LIST + ")\\) -> (" + NAME + ")");
32 32
33 public ProguardMappingsReader() {} 33 public ProguardMappingsReader() {
34 34 }
35 @Override 35
36 public EntryTree<EntryMapping> read(Path path, ProgressListener progress, MappingSaveParameters saveParameters) throws MappingParseException, IOException { 36 @Override
37 EntryTree<EntryMapping> mappings = new HashEntryTree<>(); 37 public EntryTree<EntryMapping> read(Path path, ProgressListener progress, MappingSaveParameters saveParameters) throws MappingParseException, IOException {
38 38 EntryTree<EntryMapping> mappings = new HashEntryTree<>();
39 int lineNumber = 0; 39
40 ClassEntry currentClass = null; 40 int lineNumber = 0;
41 for (String line : Files.readAllLines(path, StandardCharsets.UTF_8)) { 41 ClassEntry currentClass = null;
42 lineNumber++; 42
43 43 for (String line : Files.readAllLines(path, StandardCharsets.UTF_8)) {
44 if (line.startsWith("#") || line.isEmpty()) { 44 lineNumber++;
45 continue; 45
46 } 46 if (line.startsWith("#") || line.isEmpty()) {
47 47 continue;
48 Matcher classMatcher = CLASS.matcher(line); 48 }
49 Matcher fieldMatcher = FIELD.matcher(line); 49
50 Matcher methodMatcher = METHOD.matcher(line); 50 Matcher classMatcher = CLASS.matcher(line);
51 51 Matcher fieldMatcher = FIELD.matcher(line);
52 if (classMatcher.matches()) { 52 Matcher methodMatcher = METHOD.matcher(line);
53 String name = classMatcher.group(1); 53
54 String targetName = classMatcher.group(2); 54 if (classMatcher.matches()) {
55 55 String name = classMatcher.group(1);
56 mappings.insert(currentClass = new ClassEntry(name.replace('.', '/')), new EntryMapping(ClassEntry.getInnerName(targetName.replace('.', '/')))); 56 String targetName = classMatcher.group(2);
57 } else if (fieldMatcher.matches()) { 57
58 String type = fieldMatcher.group(1); 58 mappings.insert(currentClass = new ClassEntry(name.replace('.', '/')), new EntryMapping(ClassEntry.getInnerName(targetName.replace('.', '/'))));
59 String name = fieldMatcher.group(2); 59 } else if (fieldMatcher.matches()) {
60 String targetName = fieldMatcher.group(3); 60 String type = fieldMatcher.group(1);
61 61 String name = fieldMatcher.group(2);
62 if (currentClass == null) { 62 String targetName = fieldMatcher.group(3);
63 throw new MappingParseException(path::toString, lineNumber, "field mapping not inside class: " + line); 63
64 } 64 if (currentClass == null) {
65 65 throw new MappingParseException(path::toString, lineNumber, "field mapping not inside class: " + line);
66 mappings.insert(new FieldEntry(currentClass, name, new TypeDescriptor(getDescriptor(type))), new EntryMapping(targetName)); 66 }
67 } else if (methodMatcher.matches()) { 67
68 String returnType = methodMatcher.group(1); 68 mappings.insert(new FieldEntry(currentClass, name, new TypeDescriptor(getDescriptor(type))), new EntryMapping(targetName));
69 String name = methodMatcher.group(2); 69 } else if (methodMatcher.matches()) {
70 String[] parameterTypes = methodMatcher.group(3).isEmpty() ? new String[0] : methodMatcher.group(3).split(","); 70 String returnType = methodMatcher.group(1);
71 String targetName = methodMatcher.group(4); 71 String name = methodMatcher.group(2);
72 72 String[] parameterTypes = methodMatcher.group(3).isEmpty() ? new String[0] : methodMatcher.group(3).split(",");
73 if (currentClass == null) { 73 String targetName = methodMatcher.group(4);
74 throw new MappingParseException(path::toString, lineNumber, "method mapping not inside class: " + line); 74
75 } 75 if (currentClass == null) {
76 76 throw new MappingParseException(path::toString, lineNumber, "method mapping not inside class: " + line);
77 mappings.insert(new MethodEntry(currentClass, name, new MethodDescriptor(getDescriptor(returnType, parameterTypes))), new EntryMapping(targetName)); 77 }
78 } else { 78
79 throw new MappingParseException(path::toString, lineNumber, "invalid mapping line: " + line); 79 mappings.insert(new MethodEntry(currentClass, name, new MethodDescriptor(getDescriptor(returnType, parameterTypes))), new EntryMapping(targetName));
80 } 80 } else {
81 } 81 throw new MappingParseException(path::toString, lineNumber, "invalid mapping line: " + line);
82 82 }
83 return MappingOperations.invert(mappings); 83 }
84 } 84
85 85 return MappingOperations.invert(mappings);
86 private String getDescriptor(String type) { 86 }
87 StringBuilder descriptor = new StringBuilder(); 87
88 88 private String getDescriptor(String type) {
89 while (type.endsWith("[]")) { 89 StringBuilder descriptor = new StringBuilder();
90 descriptor.append("["); 90
91 type = type.substring(0, type.length() - 2); 91 while (type.endsWith("[]")) {
92 } 92 descriptor.append("[");
93 93 type = type.substring(0, type.length() - 2);
94 switch (type) { 94 }
95 case "byte": 95
96 return descriptor + "B"; 96 switch (type) {
97 case "char": 97 case "byte":
98 return descriptor + "C"; 98 return descriptor + "B";
99 case "short": 99 case "char":
100 return descriptor + "S"; 100 return descriptor + "C";
101 case "int": 101 case "short":
102 return descriptor + "I"; 102 return descriptor + "S";
103 case "long": 103 case "int":
104 return descriptor + "J"; 104 return descriptor + "I";
105 case "float": 105 case "long":
106 return descriptor + "F"; 106 return descriptor + "J";
107 case "double": 107 case "float":
108 return descriptor + "D"; 108 return descriptor + "F";
109 case "boolean": 109 case "double":
110 return descriptor + "Z"; 110 return descriptor + "D";
111 case "void": 111 case "boolean":
112 return descriptor + "V"; 112 return descriptor + "Z";
113 } 113 case "void":
114 114 return descriptor + "V";
115 descriptor.append("L"); 115 }
116 descriptor.append(type.replace('.', '/')); 116
117 descriptor.append(";"); 117 descriptor.append("L");
118 118 descriptor.append(type.replace('.', '/'));
119 return descriptor.toString(); 119 descriptor.append(";");
120 } 120
121 121 return descriptor.toString();
122 private String getDescriptor(String returnType, String[] parameterTypes) { 122 }
123 StringBuilder descriptor = new StringBuilder(); 123
124 descriptor.append('('); 124 private String getDescriptor(String returnType, String[] parameterTypes) {
125 125 StringBuilder descriptor = new StringBuilder();
126 for (String parameterType : parameterTypes) { 126 descriptor.append('(');
127 descriptor.append(getDescriptor(parameterType)); 127
128 } 128 for (String parameterType : parameterTypes) {
129 129 descriptor.append(getDescriptor(parameterType));
130 descriptor.append(')'); 130 }
131 descriptor.append(getDescriptor(returnType)); 131
132 132 descriptor.append(')');
133 return descriptor.toString(); 133 descriptor.append(getDescriptor(returnType));
134 } 134
135 return descriptor.toString();
136 }
135} 137}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/recaf/RecafMappingsReader.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/recaf/RecafMappingsReader.java
index 483e4e4..ce54fea 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/recaf/RecafMappingsReader.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/recaf/RecafMappingsReader.java
@@ -1,5 +1,12 @@
1package cuchaz.enigma.translation.mapping.serde.recaf; 1package cuchaz.enigma.translation.mapping.serde.recaf;
2 2
3import java.io.IOException;
4import java.nio.file.Files;
5import java.nio.file.Path;
6import java.util.List;
7import java.util.regex.Matcher;
8import java.util.regex.Pattern;
9
3import cuchaz.enigma.ProgressListener; 10import cuchaz.enigma.ProgressListener;
4import cuchaz.enigma.translation.mapping.EntryMapping; 11import cuchaz.enigma.translation.mapping.EntryMapping;
5import cuchaz.enigma.translation.mapping.serde.MappingParseException; 12import cuchaz.enigma.translation.mapping.serde.MappingParseException;
@@ -13,15 +20,7 @@ import cuchaz.enigma.translation.representation.entry.ClassEntry;
13import cuchaz.enigma.translation.representation.entry.FieldEntry; 20import cuchaz.enigma.translation.representation.entry.FieldEntry;
14import cuchaz.enigma.translation.representation.entry.MethodEntry; 21import cuchaz.enigma.translation.representation.entry.MethodEntry;
15 22
16import java.io.IOException;
17import java.nio.file.Files;
18import java.nio.file.Path;
19import java.util.List;
20import java.util.regex.Matcher;
21import java.util.regex.Pattern;
22
23public class RecafMappingsReader implements MappingsReader { 23public class RecafMappingsReader implements MappingsReader {
24
25 public static final RecafMappingsReader INSTANCE = new RecafMappingsReader(); 24 public static final RecafMappingsReader INSTANCE = new RecafMappingsReader();
26 private static final Pattern METHOD_PATTERN = Pattern.compile("(.*?)\\.(.*?)(\\(.*?) (.*)"); 25 private static final Pattern METHOD_PATTERN = Pattern.compile("(.*?)\\.(.*?)(\\(.*?) (.*)");
27 private static final Pattern FIELD_PATTERN = Pattern.compile("(.*?)\\.(.*?) (.*?) (.*)"); 26 private static final Pattern FIELD_PATTERN = Pattern.compile("(.*?)\\.(.*?) (.*?) (.*)");
@@ -34,6 +33,7 @@ public class RecafMappingsReader implements MappingsReader {
34 33
35 for (String line : lines) { 34 for (String line : lines) {
36 Matcher methodMatcher = METHOD_PATTERN.matcher(line); 35 Matcher methodMatcher = METHOD_PATTERN.matcher(line);
36
37 if (methodMatcher.find()) { 37 if (methodMatcher.find()) {
38 ClassEntry owner = new ClassEntry(methodMatcher.group(1)); 38 ClassEntry owner = new ClassEntry(methodMatcher.group(1));
39 String name = methodMatcher.group(2); 39 String name = methodMatcher.group(2);
@@ -43,6 +43,7 @@ public class RecafMappingsReader implements MappingsReader {
43 } 43 }
44 44
45 Matcher fieldMatcher = FIELD_PATTERN.matcher(line); 45 Matcher fieldMatcher = FIELD_PATTERN.matcher(line);
46
46 if (fieldMatcher.find()) { 47 if (fieldMatcher.find()) {
47 ClassEntry owner = new ClassEntry(fieldMatcher.group(1)); 48 ClassEntry owner = new ClassEntry(fieldMatcher.group(1));
48 String name = fieldMatcher.group(2); 49 String name = fieldMatcher.group(2);
@@ -52,10 +53,12 @@ public class RecafMappingsReader implements MappingsReader {
52 } 53 }
53 54
54 Matcher classMatcher = CLASS_PATTERN.matcher(line); 55 Matcher classMatcher = CLASS_PATTERN.matcher(line);
56
55 if (classMatcher.find()) { 57 if (classMatcher.find()) {
56 mappings.insert(new ClassEntry(classMatcher.group(1)), new EntryMapping(classMatcher.group(2))); 58 mappings.insert(new ClassEntry(classMatcher.group(1)), new EntryMapping(classMatcher.group(2)));
57 } 59 }
58 } 60 }
61
59 return mappings; 62 return mappings;
60 } 63 }
61} 64}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/recaf/RecafMappingsWriter.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/recaf/RecafMappingsWriter.java
index aa29ff6..df65b3c 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/recaf/RecafMappingsWriter.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/recaf/RecafMappingsWriter.java
@@ -1,6 +1,13 @@
1package cuchaz.enigma.translation.mapping.serde.recaf; 1package cuchaz.enigma.translation.mapping.serde.recaf;
2 2
3import java.io.BufferedWriter;
4import java.io.IOException;
5import java.io.Writer;
6import java.nio.file.Files;
7import java.nio.file.Path;
8
3import com.google.common.collect.Lists; 9import com.google.common.collect.Lists;
10
4import cuchaz.enigma.ProgressListener; 11import cuchaz.enigma.ProgressListener;
5import cuchaz.enigma.translation.mapping.EntryMapping; 12import cuchaz.enigma.translation.mapping.EntryMapping;
6import cuchaz.enigma.translation.mapping.MappingDelta; 13import cuchaz.enigma.translation.mapping.MappingDelta;
@@ -13,14 +20,7 @@ import cuchaz.enigma.translation.representation.entry.Entry;
13import cuchaz.enigma.translation.representation.entry.FieldEntry; 20import cuchaz.enigma.translation.representation.entry.FieldEntry;
14import cuchaz.enigma.translation.representation.entry.MethodEntry; 21import cuchaz.enigma.translation.representation.entry.MethodEntry;
15 22
16import java.io.BufferedWriter;
17import java.io.IOException;
18import java.io.Writer;
19import java.nio.file.Files;
20import java.nio.file.Path;
21
22public class RecafMappingsWriter implements MappingsWriter { 23public class RecafMappingsWriter implements MappingsWriter {
23
24 public static final RecafMappingsWriter INSTANCE = new RecafMappingsWriter(); 24 public static final RecafMappingsWriter INSTANCE = new RecafMappingsWriter();
25 25
26 @Override 26 @Override
@@ -33,10 +33,7 @@ public class RecafMappingsWriter implements MappingsWriter {
33 } 33 }
34 34
35 try (BufferedWriter writer = Files.newBufferedWriter(path)) { 35 try (BufferedWriter writer = Files.newBufferedWriter(path)) {
36 Lists.newArrayList(mappings) 36 Lists.newArrayList(mappings).stream().map(EntryTreeNode::getEntry).forEach(entry -> writeEntry(writer, mappings, entry));
37 .stream()
38 .map(EntryTreeNode::getEntry)
39 .forEach(entry -> writeEntry(writer, mappings, entry));
40 } catch (IOException e) { 37 } catch (IOException e) {
41 e.printStackTrace(); 38 e.printStackTrace();
42 } 39 }
@@ -44,6 +41,7 @@ public class RecafMappingsWriter implements MappingsWriter {
44 41
45 private void writeEntry(Writer writer, EntryTree<EntryMapping> mappings, Entry<?> entry) { 42 private void writeEntry(Writer writer, EntryTree<EntryMapping> mappings, Entry<?> entry) {
46 EntryTreeNode<EntryMapping> node = mappings.findNode(entry); 43 EntryTreeNode<EntryMapping> node = mappings.findNode(entry);
44
47 if (node == null) { 45 if (node == null) {
48 return; 46 return;
49 } 47 }
@@ -53,27 +51,22 @@ public class RecafMappingsWriter implements MappingsWriter {
53 try { 51 try {
54 if (mapping != null && mapping.targetName() != null) { 52 if (mapping != null && mapping.targetName() != null) {
55 if (entry instanceof ClassEntry classEntry) { 53 if (entry instanceof ClassEntry classEntry) {
56
57 writer.write(classEntry.getFullName()); 54 writer.write(classEntry.getFullName());
58 writer.write(" "); 55 writer.write(" ");
59 writer.write(mapping.targetName()); 56 writer.write(mapping.targetName());
60
61 } else if (entry instanceof FieldEntry fieldEntry) { 57 } else if (entry instanceof FieldEntry fieldEntry) {
62
63 writer.write(fieldEntry.getFullName()); 58 writer.write(fieldEntry.getFullName());
64 writer.write(" "); 59 writer.write(" ");
65 writer.write(fieldEntry.getDesc().toString()); 60 writer.write(fieldEntry.getDesc().toString());
66 writer.write(" "); 61 writer.write(" ");
67 writer.write(mapping.targetName()); 62 writer.write(mapping.targetName());
68
69 } else if (entry instanceof MethodEntry methodEntry) { 63 } else if (entry instanceof MethodEntry methodEntry) {
70
71 writer.write(methodEntry.getFullName()); 64 writer.write(methodEntry.getFullName());
72 writer.write(methodEntry.getDesc().toString()); 65 writer.write(methodEntry.getDesc().toString());
73 writer.write(" "); 66 writer.write(" ");
74 writer.write(mapping.targetName()); 67 writer.write(mapping.targetName());
75
76 } 68 }
69
77 writer.write("\n"); 70 writer.write("\n");
78 } 71 }
79 } catch (IOException e) { 72 } catch (IOException e) {
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/srg/SrgMappingsWriter.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/srg/SrgMappingsWriter.java
index 9275847..4621efe 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/srg/SrgMappingsWriter.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/srg/SrgMappingsWriter.java
@@ -1,14 +1,24 @@
1package cuchaz.enigma.translation.mapping.serde.srg; 1package cuchaz.enigma.translation.mapping.serde.srg;
2 2
3import java.io.IOException;
4import java.io.PrintWriter;
5import java.nio.file.Files;
6import java.nio.file.Path;
7import java.util.ArrayList;
8import java.util.Collection;
9import java.util.Comparator;
10import java.util.List;
11
3import com.google.common.collect.Lists; 12import com.google.common.collect.Lists;
13
4import cuchaz.enigma.ProgressListener; 14import cuchaz.enigma.ProgressListener;
5import cuchaz.enigma.translation.MappingTranslator; 15import cuchaz.enigma.translation.MappingTranslator;
6import cuchaz.enigma.translation.Translator; 16import cuchaz.enigma.translation.Translator;
7import cuchaz.enigma.translation.mapping.EntryMapping; 17import cuchaz.enigma.translation.mapping.EntryMapping;
8import cuchaz.enigma.translation.mapping.MappingDelta; 18import cuchaz.enigma.translation.mapping.MappingDelta;
9import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
10import cuchaz.enigma.translation.mapping.VoidEntryResolver; 19import cuchaz.enigma.translation.mapping.VoidEntryResolver;
11import cuchaz.enigma.translation.mapping.serde.LfPrintWriter; 20import cuchaz.enigma.translation.mapping.serde.LfPrintWriter;
21import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
12import cuchaz.enigma.translation.mapping.serde.MappingsWriter; 22import cuchaz.enigma.translation.mapping.serde.MappingsWriter;
13import cuchaz.enigma.translation.mapping.tree.EntryTree; 23import cuchaz.enigma.translation.mapping.tree.EntryTree;
14import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; 24import cuchaz.enigma.translation.mapping.tree.EntryTreeNode;
@@ -18,15 +28,6 @@ import cuchaz.enigma.translation.representation.entry.FieldEntry;
18import cuchaz.enigma.translation.representation.entry.MethodEntry; 28import cuchaz.enigma.translation.representation.entry.MethodEntry;
19import cuchaz.enigma.utils.I18n; 29import cuchaz.enigma.utils.I18n;
20 30
21import java.io.IOException;
22import java.io.PrintWriter;
23import java.nio.file.Files;
24import java.nio.file.Path;
25import java.util.ArrayList;
26import java.util.Collection;
27import java.util.Comparator;
28import java.util.List;
29
30public enum SrgMappingsWriter implements MappingsWriter { 31public enum SrgMappingsWriter implements MappingsWriter {
31 INSTANCE; 32 INSTANCE;
32 33
@@ -43,18 +44,18 @@ public enum SrgMappingsWriter implements MappingsWriter {
43 List<String> fieldLines = new ArrayList<>(); 44 List<String> fieldLines = new ArrayList<>();
44 List<String> methodLines = new ArrayList<>(); 45 List<String> methodLines = new ArrayList<>();
45 46
46 List<? extends Entry<?>> rootEntries = Lists.newArrayList(mappings).stream() 47 List<? extends Entry<?>> rootEntries = Lists.newArrayList(mappings).stream().map(EntryTreeNode::getEntry).toList();
47 .map(EntryTreeNode::getEntry)
48 .toList();
49 progress.init(rootEntries.size(), I18n.translate("progress.mappings.srg_file.generating")); 48 progress.init(rootEntries.size(), I18n.translate("progress.mappings.srg_file.generating"));
50 49
51 int steps = 0; 50 int steps = 0;
51
52 for (Entry<?> entry : sorted(rootEntries)) { 52 for (Entry<?> entry : sorted(rootEntries)) {
53 progress.step(steps++, entry.getName()); 53 progress.step(steps++, entry.getName());
54 writeEntry(classLines, fieldLines, methodLines, mappings, entry); 54 writeEntry(classLines, fieldLines, methodLines, mappings, entry);
55 } 55 }
56 56
57 progress.init(3, I18n.translate("progress.mappings.srg_file.writing")); 57 progress.init(3, I18n.translate("progress.mappings.srg_file.writing"));
58
58 try (PrintWriter writer = new LfPrintWriter(Files.newBufferedWriter(path))) { 59 try (PrintWriter writer = new LfPrintWriter(Files.newBufferedWriter(path))) {
59 progress.step(0, I18n.translate("type.classes")); 60 progress.step(0, I18n.translate("type.classes"));
60 classLines.forEach(writer::println); 61 classLines.forEach(writer::println);
@@ -69,11 +70,13 @@ public enum SrgMappingsWriter implements MappingsWriter {
69 70
70 private void writeEntry(List<String> classes, List<String> fields, List<String> methods, EntryTree<EntryMapping> mappings, Entry<?> entry) { 71 private void writeEntry(List<String> classes, List<String> fields, List<String> methods, EntryTree<EntryMapping> mappings, Entry<?> entry) {
71 EntryTreeNode<EntryMapping> node = mappings.findNode(entry); 72 EntryTreeNode<EntryMapping> node = mappings.findNode(entry);
73
72 if (node == null) { 74 if (node == null) {
73 return; 75 return;
74 } 76 }
75 77
76 Translator translator = new MappingTranslator(mappings, VoidEntryResolver.INSTANCE); 78 Translator translator = new MappingTranslator(mappings, VoidEntryResolver.INSTANCE);
79
77 if (entry instanceof ClassEntry) { 80 if (entry instanceof ClassEntry) {
78 classes.add(generateClassLine((ClassEntry) entry, translator)); 81 classes.add(generateClassLine((ClassEntry) entry, translator));
79 } else if (entry instanceof FieldEntry) { 82 } else if (entry instanceof FieldEntry) {
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsReader.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsReader.java
index f3c66df..e08c867 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsReader.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsReader.java
@@ -1,10 +1,16 @@
1package cuchaz.enigma.translation.mapping.serde.tiny; 1package cuchaz.enigma.translation.mapping.serde.tiny;
2 2
3import java.io.IOException;
4import java.nio.file.Files;
5import java.nio.file.Path;
6import java.util.List;
7
3import com.google.common.base.Charsets; 8import com.google.common.base.Charsets;
9
4import cuchaz.enigma.ProgressListener; 10import cuchaz.enigma.ProgressListener;
5import cuchaz.enigma.translation.mapping.serde.MappingParseException;
6import cuchaz.enigma.translation.mapping.EntryMapping; 11import cuchaz.enigma.translation.mapping.EntryMapping;
7import cuchaz.enigma.translation.mapping.MappingPair; 12import cuchaz.enigma.translation.mapping.MappingPair;
13import cuchaz.enigma.translation.mapping.serde.MappingParseException;
8import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; 14import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
9import cuchaz.enigma.translation.mapping.serde.MappingsReader; 15import cuchaz.enigma.translation.mapping.serde.MappingsReader;
10import cuchaz.enigma.translation.mapping.tree.EntryTree; 16import cuchaz.enigma.translation.mapping.tree.EntryTree;
@@ -17,11 +23,6 @@ import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
17import cuchaz.enigma.translation.representation.entry.MethodEntry; 23import cuchaz.enigma.translation.representation.entry.MethodEntry;
18import cuchaz.enigma.utils.I18n; 24import cuchaz.enigma.utils.I18n;
19 25
20import java.io.IOException;
21import java.nio.file.Files;
22import java.nio.file.Path;
23import java.util.List;
24
25public enum TinyMappingsReader implements MappingsReader { 26public enum TinyMappingsReader implements MappingsReader {
26 INSTANCE; 27 INSTANCE;
27 28
@@ -62,26 +63,28 @@ public enum TinyMappingsReader implements MappingsReader {
62 63
63 String key = tokens[0]; 64 String key = tokens[0];
64 switch (key) { 65 switch (key) {
65 case "CLASS": 66 case "CLASS":
66 return parseClass(tokens); 67 return parseClass(tokens);
67 case "FIELD": 68 case "FIELD":
68 return parseField(tokens); 69 return parseField(tokens);
69 case "METHOD": 70 case "METHOD":
70 return parseMethod(tokens); 71 return parseMethod(tokens);
71 case "MTH-ARG": 72 case "MTH-ARG":
72 return parseArgument(tokens); 73 return parseArgument(tokens);
73 default: 74 default:
74 throw new RuntimeException("Unknown token '" + key + "'!"); 75 throw new RuntimeException("Unknown token '" + key + "'!");
75 } 76 }
76 } 77 }
77 78
78 private MappingPair<ClassEntry, EntryMapping> parseClass(String[] tokens) { 79 private MappingPair<ClassEntry, EntryMapping> parseClass(String[] tokens) {
79 ClassEntry obfuscatedEntry = new ClassEntry(tokens[1]); 80 ClassEntry obfuscatedEntry = new ClassEntry(tokens[1]);
80 String mapping = tokens[2]; 81 String mapping = tokens[2];
82
81 if (mapping.indexOf('$') > 0) { 83 if (mapping.indexOf('$') > 0) {
82 // inner classes should map to only the final part 84 // inner classes should map to only the final part
83 mapping = mapping.substring(mapping.lastIndexOf('$') + 1); 85 mapping = mapping.substring(mapping.lastIndexOf('$') + 1);
84 } 86 }
87
85 return new MappingPair<>(obfuscatedEntry, new EntryMapping(mapping)); 88 return new MappingPair<>(obfuscatedEntry, new EntryMapping(mapping));
86 } 89 }
87 90
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsWriter.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsWriter.java
index 1f785e1..972d180 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsWriter.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsWriter.java
@@ -1,14 +1,25 @@
1package cuchaz.enigma.translation.mapping.serde.tiny; 1package cuchaz.enigma.translation.mapping.serde.tiny;
2 2
3import java.io.BufferedWriter;
4import java.io.IOException;
5import java.io.Writer;
6import java.nio.charset.StandardCharsets;
7import java.nio.file.Files;
8import java.nio.file.Path;
9import java.util.Comparator;
10import java.util.HashSet;
11import java.util.Set;
12
3import com.google.common.base.Joiner; 13import com.google.common.base.Joiner;
4import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15
5import cuchaz.enigma.ProgressListener; 16import cuchaz.enigma.ProgressListener;
6import cuchaz.enigma.translation.MappingTranslator; 17import cuchaz.enigma.translation.MappingTranslator;
7import cuchaz.enigma.translation.Translator; 18import cuchaz.enigma.translation.Translator;
8import cuchaz.enigma.translation.mapping.EntryMapping; 19import cuchaz.enigma.translation.mapping.EntryMapping;
9import cuchaz.enigma.translation.mapping.MappingDelta; 20import cuchaz.enigma.translation.mapping.MappingDelta;
10import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
11import cuchaz.enigma.translation.mapping.VoidEntryResolver; 21import cuchaz.enigma.translation.mapping.VoidEntryResolver;
22import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
12import cuchaz.enigma.translation.mapping.serde.MappingsWriter; 23import cuchaz.enigma.translation.mapping.serde.MappingsWriter;
13import cuchaz.enigma.translation.mapping.tree.EntryTree; 24import cuchaz.enigma.translation.mapping.tree.EntryTree;
14import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; 25import cuchaz.enigma.translation.mapping.tree.EntryTreeNode;
@@ -17,136 +28,120 @@ import cuchaz.enigma.translation.representation.entry.Entry;
17import cuchaz.enigma.translation.representation.entry.FieldEntry; 28import cuchaz.enigma.translation.representation.entry.FieldEntry;
18import cuchaz.enigma.translation.representation.entry.MethodEntry; 29import cuchaz.enigma.translation.representation.entry.MethodEntry;
19 30
20import java.io.BufferedWriter;
21import java.io.IOException;
22import java.io.Writer;
23import java.nio.charset.StandardCharsets;
24import java.nio.file.Files;
25import java.nio.file.Path;
26import java.util.Comparator;
27import java.util.HashSet;
28import java.util.Set;
29
30public class TinyMappingsWriter implements MappingsWriter { 31public class TinyMappingsWriter implements MappingsWriter {
31 private static final String VERSION_CONSTANT = "v1"; 32 private static final String VERSION_CONSTANT = "v1";
32 private static final Joiner TAB_JOINER = Joiner.on('\t'); 33 private static final Joiner TAB_JOINER = Joiner.on('\t');
33 34
34 //Possibly add a gui or a way to select the namespaces when exporting from the gui 35 //Possibly add a gui or a way to select the namespaces when exporting from the gui
35 public static final TinyMappingsWriter INSTANCE = new TinyMappingsWriter("intermediary", "named"); 36 public static final TinyMappingsWriter INSTANCE = new TinyMappingsWriter("intermediary", "named");
36 37
37 // HACK: as of enigma 0.13.1, some fields seem to appear duplicated? 38 // HACK: as of enigma 0.13.1, some fields seem to appear duplicated?
38 private final Set<String> writtenLines = new HashSet<>(); 39 private final Set<String> writtenLines = new HashSet<>();
39 private final String nameObf; 40 private final String nameObf;
40 private final String nameDeobf; 41 private final String nameDeobf;
41 42
42 public TinyMappingsWriter(String nameObf, String nameDeobf) { 43 public TinyMappingsWriter(String nameObf, String nameDeobf) {
43 this.nameObf = nameObf; 44 this.nameObf = nameObf;
44 this.nameDeobf = nameDeobf; 45 this.nameDeobf = nameDeobf;
45 } 46 }
46 47
47 @Override 48 @Override
48 public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress, MappingSaveParameters saveParameters) { 49 public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress, MappingSaveParameters saveParameters) {
49 try { 50 try {
50 Files.deleteIfExists(path); 51 Files.deleteIfExists(path);
51 Files.createFile(path); 52 Files.createFile(path);
52 } catch (IOException e) { 53 } catch (IOException e) {
53 e.printStackTrace(); 54 e.printStackTrace();
54 } 55 }
55 56
56 try (BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) { 57 try (BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
57 writeLine(writer, new String[]{VERSION_CONSTANT, nameObf, nameDeobf}); 58 writeLine(writer, new String[]{VERSION_CONSTANT, nameObf, nameDeobf});
58 59
59 Lists.newArrayList(mappings).stream() 60 Lists.newArrayList(mappings).stream().map(EntryTreeNode::getEntry).sorted(Comparator.comparing(Object::toString)).forEach(entry -> writeEntry(writer, mappings, entry));
60 .map(EntryTreeNode::getEntry).sorted(Comparator.comparing(Object::toString)) 61 } catch (IOException e) {
61 .forEach(entry -> writeEntry(writer, mappings, entry)); 62 e.printStackTrace();
62 } catch (IOException e) { 63 }
63 e.printStackTrace(); 64 }
64 } 65
65 } 66 private void writeEntry(Writer writer, EntryTree<EntryMapping> mappings, Entry<?> entry) {
66 67 EntryTreeNode<EntryMapping> node = mappings.findNode(entry);
67 private void writeEntry(Writer writer, EntryTree<EntryMapping> mappings, Entry<?> entry) { 68
68 EntryTreeNode<EntryMapping> node = mappings.findNode(entry); 69 if (node == null) {
69 if (node == null) { 70 return;
70 return; 71 }
71 } 72
72 73 Translator translator = new MappingTranslator(mappings, VoidEntryResolver.INSTANCE);
73 Translator translator = new MappingTranslator(mappings, VoidEntryResolver.INSTANCE); 74
74 75 EntryMapping mapping = mappings.get(entry);
75 EntryMapping mapping = mappings.get(entry); 76
76 77 // Do not write mappings without deobfuscated name since tiny v1 doesn't
77 // Do not write mappings without deobfuscated name since tiny v1 doesn't 78 // support comments anyway
78 // support comments anyway
79 if (mapping != null && mapping.targetName() != null) { 79 if (mapping != null && mapping.targetName() != null) {
80 if (entry instanceof ClassEntry) { 80 if (entry instanceof ClassEntry) {
81 writeClass(writer, (ClassEntry) entry, translator); 81 writeClass(writer, (ClassEntry) entry, translator);
82 } else if (entry instanceof FieldEntry) { 82 } else if (entry instanceof FieldEntry) {
83 writeLine(writer, serializeEntry(entry, mapping.targetName())); 83 writeLine(writer, serializeEntry(entry, mapping.targetName()));
84 } else if (entry instanceof MethodEntry) { 84 } else if (entry instanceof MethodEntry) {
85 writeLine(writer, serializeEntry(entry, mapping.targetName())); 85 writeLine(writer, serializeEntry(entry, mapping.targetName()));
86 } 86 }
87 } 87 }
88 88
89 writeChildren(writer, mappings, node); 89 writeChildren(writer, mappings, node);
90 } 90 }
91 91
92 private void writeChildren(Writer writer, EntryTree<EntryMapping> mappings, EntryTreeNode<EntryMapping> node) { 92 private void writeChildren(Writer writer, EntryTree<EntryMapping> mappings, EntryTreeNode<EntryMapping> node) {
93 node.getChildren().stream() 93 node.getChildren().stream().filter(e -> e instanceof FieldEntry).sorted().forEach(child -> writeEntry(writer, mappings, child));
94 .filter(e -> e instanceof FieldEntry).sorted() 94
95 .forEach(child -> writeEntry(writer, mappings, child)); 95 node.getChildren().stream().filter(e -> e instanceof MethodEntry).sorted().forEach(child -> writeEntry(writer, mappings, child));
96 96
97 node.getChildren().stream() 97 node.getChildren().stream().filter(e -> e instanceof ClassEntry).sorted().forEach(child -> writeEntry(writer, mappings, child));
98 .filter(e -> e instanceof MethodEntry).sorted() 98 }
99 .forEach(child -> writeEntry(writer, mappings, child)); 99
100 100 private void writeClass(Writer writer, ClassEntry entry, Translator translator) {
101 node.getChildren().stream() 101 ClassEntry translatedEntry = translator.translate(entry);
102 .filter(e -> e instanceof ClassEntry).sorted() 102
103 .forEach(child -> writeEntry(writer, mappings, child)); 103 String obfClassName = entry.getFullName();
104 } 104 String deobfClassName = translatedEntry.getFullName();
105 105 writeLine(writer, new String[]{"CLASS", obfClassName, deobfClassName});
106 private void writeClass(Writer writer, ClassEntry entry, Translator translator) { 106 }
107 ClassEntry translatedEntry = translator.translate(entry); 107
108 108 private void writeLine(Writer writer, String[] data) {
109 String obfClassName = entry.getFullName(); 109 try {
110 String deobfClassName = translatedEntry.getFullName(); 110 String line = TAB_JOINER.join(data) + "\n";
111 writeLine(writer, new String[]{"CLASS", obfClassName, deobfClassName}); 111
112 } 112 if (writtenLines.add(line)) {
113 113 writer.write(line);
114 private void writeLine(Writer writer, String[] data) { 114 }
115 try { 115 } catch (IOException e) {
116 String line = TAB_JOINER.join(data) + "\n"; 116 throw new RuntimeException(e);
117 if (writtenLines.add(line)) { 117 }
118 writer.write(line); 118 }
119 } 119
120 } catch (IOException e) { 120 private String[] serializeEntry(Entry<?> entry, String... extraFields) {
121 throw new RuntimeException(e); 121 String[] data = null;
122 } 122
123 } 123 if (entry instanceof FieldEntry) {
124 124 data = new String[4 + extraFields.length];
125 private String[] serializeEntry(Entry<?> entry, String... extraFields) { 125 data[0] = "FIELD";
126 String[] data = null; 126 data[1] = entry.getContainingClass().getFullName();
127 127 data[2] = ((FieldEntry) entry).getDesc().toString();
128 if (entry instanceof FieldEntry) { 128 data[3] = entry.getName();
129 data = new String[4 + extraFields.length]; 129 } else if (entry instanceof MethodEntry) {
130 data[0] = "FIELD"; 130 data = new String[4 + extraFields.length];
131 data[1] = entry.getContainingClass().getFullName(); 131 data[0] = "METHOD";
132 data[2] = ((FieldEntry) entry).getDesc().toString(); 132 data[1] = entry.getContainingClass().getFullName();
133 data[3] = entry.getName(); 133 data[2] = ((MethodEntry) entry).getDesc().toString();
134 } else if (entry instanceof MethodEntry) { 134 data[3] = entry.getName();
135 data = new String[4 + extraFields.length]; 135 } else if (entry instanceof ClassEntry) {
136 data[0] = "METHOD"; 136 data = new String[2 + extraFields.length];
137 data[1] = entry.getContainingClass().getFullName(); 137 data[0] = "CLASS";
138 data[2] = ((MethodEntry) entry).getDesc().toString(); 138 data[1] = ((ClassEntry) entry).getFullName();
139 data[3] = entry.getName(); 139 }
140 } else if (entry instanceof ClassEntry) { 140
141 data = new String[2 + extraFields.length]; 141 if (data != null) {
142 data[0] = "CLASS"; 142 System.arraycopy(extraFields, 0, data, data.length - extraFields.length, extraFields.length);
143 data[1] = ((ClassEntry) entry).getFullName(); 143 }
144 } 144
145 145 return data;
146 if (data != null) { 146 }
147 System.arraycopy(extraFields, 0, data, data.length - extraFields.length, extraFields.length);
148 }
149
150 return data;
151 }
152} 147}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Reader.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Reader.java
index dc3246d..61bc41d 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Reader.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Reader.java
@@ -1,9 +1,16 @@
1package cuchaz.enigma.translation.mapping.serde.tinyv2; 1package cuchaz.enigma.translation.mapping.serde.tinyv2;
2 2
3import java.io.IOException;
4import java.nio.charset.StandardCharsets;
5import java.nio.file.Files;
6import java.nio.file.Path;
7import java.util.BitSet;
8import java.util.List;
9
3import cuchaz.enigma.ProgressListener; 10import cuchaz.enigma.ProgressListener;
4import cuchaz.enigma.translation.mapping.serde.MappingParseException;
5import cuchaz.enigma.translation.mapping.EntryMapping; 11import cuchaz.enigma.translation.mapping.EntryMapping;
6import cuchaz.enigma.translation.mapping.MappingPair; 12import cuchaz.enigma.translation.mapping.MappingPair;
13import cuchaz.enigma.translation.mapping.serde.MappingParseException;
7import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; 14import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
8import cuchaz.enigma.translation.mapping.serde.MappingsReader; 15import cuchaz.enigma.translation.mapping.serde.MappingsReader;
9import cuchaz.enigma.translation.mapping.serde.RawEntryMapping; 16import cuchaz.enigma.translation.mapping.serde.RawEntryMapping;
@@ -17,15 +24,7 @@ import cuchaz.enigma.translation.representation.entry.FieldEntry;
17import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; 24import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
18import cuchaz.enigma.translation.representation.entry.MethodEntry; 25import cuchaz.enigma.translation.representation.entry.MethodEntry;
19 26
20import java.io.IOException;
21import java.nio.charset.StandardCharsets;
22import java.nio.file.Files;
23import java.nio.file.Path;
24import java.util.BitSet;
25import java.util.List;
26
27public final class TinyV2Reader implements MappingsReader { 27public final class TinyV2Reader implements MappingsReader {
28
29 private static final String MINOR_VERSION = "0"; 28 private static final String MINOR_VERSION = "0";
30 // 0 indent 29 // 0 indent
31 private static final int IN_HEADER = 0; 30 private static final int IN_HEADER = 0;
@@ -50,8 +49,7 @@ public final class TinyV2Reader implements MappingsReader {
50 progress.init(lines.size(), "progress.mappings.tiny_v2.loading"); 49 progress.init(lines.size(), "progress.mappings.tiny_v2.loading");
51 50
52 BitSet state = new BitSet(STATE_SIZE); 51 BitSet state = new BitSet(STATE_SIZE);
53 @SuppressWarnings({"unchecked", "rawtypes"}) 52 @SuppressWarnings({"unchecked", "rawtypes"}) MappingPair<? extends Entry<?>, RawEntryMapping>[] holds = new MappingPair[STATE_SIZE];
54 MappingPair<? extends Entry<?>, RawEntryMapping>[] holds = new MappingPair[STATE_SIZE];
55 boolean escapeNames = false; 53 boolean escapeNames = false;
56 54
57 for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) { 55 for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) {
@@ -60,16 +58,21 @@ public final class TinyV2Reader implements MappingsReader {
60 String line = lines.get(lineNumber); 58 String line = lines.get(lineNumber);
61 59
62 int indent = 0; 60 int indent = 0;
63 while (line.charAt(indent) == '\t') 61
62 while (line.charAt(indent) == '\t') {
64 indent++; 63 indent++;
64 }
65 65
66 String[] parts = line.substring(indent).split("\t", -1); 66 String[] parts = line.substring(indent).split("\t", -1);
67 if (parts.length == 0 || indent >= INDENT_CLEAR_START.length) 67
68 if (parts.length == 0 || indent >= INDENT_CLEAR_START.length) {
68 throw new IllegalArgumentException("Invalid format"); 69 throw new IllegalArgumentException("Invalid format");
70 }
69 71
70 // clean and register stuff in stack 72 // clean and register stuff in stack
71 for (int i = INDENT_CLEAR_START[indent]; i < STATE_SIZE; i++) { 73 for (int i = INDENT_CLEAR_START[indent]; i < STATE_SIZE; i++) {
72 state.clear(i); 74 state.clear(i);
75
73 if (holds[i] != null) { 76 if (holds[i] != null) {
74 bakeHeld(mappings, holds[i]); 77 bakeHeld(mappings, holds[i]);
75 holds[i] = null; 78 holds[i] = null;
@@ -77,104 +80,112 @@ public final class TinyV2Reader implements MappingsReader {
77 } 80 }
78 81
79 switch (indent) { 82 switch (indent) {
80 case 0: 83 case 0:
81 switch (parts[0]) { 84 switch (parts[0]) {
82 case "tiny": // header 85 case "tiny": // header
83 if (lineNumber != 0) { 86 if (lineNumber != 0) {
84 throw new IllegalArgumentException("Header can only be on the first line"); 87 throw new IllegalArgumentException("Header can only be on the first line");
85 } 88 }
86 if (parts.length < 5) { 89
87 throw new IllegalArgumentException("Not enough header columns, needs at least 5"); 90 if (parts.length < 5) {
88 } 91 throw new IllegalArgumentException("Not enough header columns, needs at least 5");
89 if (!"2".equals(parts[1]) || !MINOR_VERSION.equals(parts[2])) {
90 throw new IllegalArgumentException("Unsupported TinyV2 version, requires major " + "2" + " and minor " + MINOR_VERSION + "");
91 }
92 state.set(IN_HEADER);
93 break;
94 case "c": // class
95 state.set(IN_CLASS);
96 holds[IN_CLASS] = parseClass(parts, escapeNames);
97 break;
98 default:
99 unsupportKey(parts);
100 } 92 }
101 93
94 if (!"2".equals(parts[1]) || !MINOR_VERSION.equals(parts[2])) {
95 throw new IllegalArgumentException("Unsupported TinyV2 version, requires major " + "2" + " and minor " + MINOR_VERSION + "");
96 }
97
98 state.set(IN_HEADER);
99 break;
100 case "c": // class
101 state.set(IN_CLASS);
102 holds[IN_CLASS] = parseClass(parts, escapeNames);
102 break; 103 break;
103 case 1: 104 default:
104 if (state.get(IN_HEADER)) { 105 unsupportKey(parts);
105 if (parts[0].equals("esacpe-names")) { 106 }
106 escapeNames = true;
107 }
108 107
109 break; 108 break;
109 case 1:
110 if (state.get(IN_HEADER)) {
111 if (parts[0].equals("esacpe-names")) {
112 escapeNames = true;
110 } 113 }
111 114
112 if (state.get(IN_CLASS)) { 115 break;
113 switch (parts[0]) { 116 }
114 case "m": // method 117
115 state.set(IN_METHOD); 118 if (state.get(IN_CLASS)) {
116 holds[IN_METHOD] = parseMethod(holds[IN_CLASS], parts, escapeNames); 119 switch (parts[0]) {
117 break; 120 case "m": // method
118 case "f": // field 121 state.set(IN_METHOD);
119 state.set(IN_FIELD); 122 holds[IN_METHOD] = parseMethod(holds[IN_CLASS], parts, escapeNames);
120 holds[IN_FIELD] = parseField(holds[IN_CLASS], parts, escapeNames); 123 break;
121 break; 124 case "f": // field
122 case "c": // class javadoc 125 state.set(IN_FIELD);
123 addJavadoc(holds[IN_CLASS], parts); 126 holds[IN_FIELD] = parseField(holds[IN_CLASS], parts, escapeNames);
124 break; 127 break;
125 default: 128 case "c": // class javadoc
126 unsupportKey(parts); 129 addJavadoc(holds[IN_CLASS], parts);
127 }
128 break; 130 break;
131 default:
132 unsupportKey(parts);
129 } 133 }
130 134
131 unsupportKey(parts); 135 break;
132 case 2: 136 }
133 if (state.get(IN_METHOD)) { 137
134 switch (parts[0]) { 138 unsupportKey(parts);
135 case "p": // parameter 139 case 2:
136 state.set(IN_PARAMETER); 140 if (state.get(IN_METHOD)) {
137 holds[IN_PARAMETER] = parseArgument(holds[IN_METHOD], parts, escapeNames); 141 switch (parts[0]) {
138 break; 142 case "p": // parameter
139 case "v": // local variable 143 state.set(IN_PARAMETER);
140 // TODO add local var mapping 144 holds[IN_PARAMETER] = parseArgument(holds[IN_METHOD], parts, escapeNames);
141 break;
142 case "c": // method javadoc
143 addJavadoc(holds[IN_METHOD], parts);
144 break;
145 default:
146 unsupportKey(parts);
147 }
148 break; 145 break;
146 case "v": // local variable
147 // TODO add local var mapping
148 break;
149 case "c": // method javadoc
150 addJavadoc(holds[IN_METHOD], parts);
151 break;
152 default:
153 unsupportKey(parts);
149 } 154 }
150 155
151 if (state.get(IN_FIELD)) { 156 break;
152 switch (parts[0]) { 157 }
153 case "c": // field javadoc 158
154 addJavadoc(holds[IN_FIELD], parts); 159 if (state.get(IN_FIELD)) {
155 break; 160 switch (parts[0]) {
156 default: 161 case "c": // field javadoc
157 unsupportKey(parts); 162 addJavadoc(holds[IN_FIELD], parts);
158 }
159 break; 163 break;
164 default:
165 unsupportKey(parts);
160 } 166 }
161 unsupportKey(parts); 167
162 case 3: 168 break;
163 if (state.get(IN_PARAMETER)) { 169 }
164 switch (parts[0]) { 170
165 case "c": 171 unsupportKey(parts);
166 addJavadoc(holds[IN_PARAMETER], parts); 172 case 3:
167 break; 173 if (state.get(IN_PARAMETER)) {
168 default: 174 switch (parts[0]) {
169 unsupportKey(parts); 175 case "c":
170 } 176 addJavadoc(holds[IN_PARAMETER], parts);
171 break; 177 break;
178 default:
179 unsupportKey(parts);
172 } 180 }
173 unsupportKey(parts);
174 default:
175 unsupportKey(parts);
176 }
177 181
182 break;
183 }
184
185 unsupportKey(parts);
186 default:
187 unsupportKey(parts);
188 }
178 } catch (Throwable t) { 189 } catch (Throwable t) {
179 t.printStackTrace(); 190 t.printStackTrace();
180 throw new MappingParseException(path::toString, lineNumber + 1, t.toString()); 191 throw new MappingParseException(path::toString, lineNumber + 1, t.toString());
@@ -193,8 +204,10 @@ public final class TinyV2Reader implements MappingsReader {
193 204
194 private static void bakeHeld(EntryTree<EntryMapping> mappings, MappingPair<? extends Entry<?>, RawEntryMapping> hold2) { 205 private static void bakeHeld(EntryTree<EntryMapping> mappings, MappingPair<? extends Entry<?>, RawEntryMapping> hold2) {
195 RawEntryMapping mapping = hold2.getMapping(); 206 RawEntryMapping mapping = hold2.getMapping();
207
196 if (mapping != null) { 208 if (mapping != null) {
197 EntryMapping baked = mapping.bake(); 209 EntryMapping baked = mapping.bake();
210
198 if (baked != null) { 211 if (baked != null) {
199 mappings.insert(hold2.getEntry(), baked); 212 mappings.insert(hold2.getEntry(), baked);
200 } 213 }
@@ -215,8 +228,11 @@ public final class TinyV2Reader implements MappingsReader {
215 228
216 private MappingPair<ClassEntry, RawEntryMapping> parseClass(String[] tokens, boolean escapeNames) { 229 private MappingPair<ClassEntry, RawEntryMapping> parseClass(String[] tokens, boolean escapeNames) {
217 ClassEntry obfuscatedEntry = new ClassEntry(unescapeOpt(tokens[1], escapeNames)); 230 ClassEntry obfuscatedEntry = new ClassEntry(unescapeOpt(tokens[1], escapeNames));
218 if (tokens.length <= 2) 231
232 if (tokens.length <= 2) {
219 return new MappingPair<>(obfuscatedEntry); 233 return new MappingPair<>(obfuscatedEntry);
234 }
235
220 String token2 = unescapeOpt(tokens[2], escapeNames); 236 String token2 = unescapeOpt(tokens[2], escapeNames);
221 String mapping = token2.substring(token2.lastIndexOf('$') + 1); 237 String mapping = token2.substring(token2.lastIndexOf('$') + 1);
222 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping)); 238 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping));
@@ -227,8 +243,11 @@ public final class TinyV2Reader implements MappingsReader {
227 TypeDescriptor descriptor = new TypeDescriptor(unescapeOpt(tokens[1], escapeNames)); 243 TypeDescriptor descriptor = new TypeDescriptor(unescapeOpt(tokens[1], escapeNames));
228 244
229 FieldEntry obfuscatedEntry = new FieldEntry(ownerClass, unescapeOpt(tokens[2], escapeNames), descriptor); 245 FieldEntry obfuscatedEntry = new FieldEntry(ownerClass, unescapeOpt(tokens[2], escapeNames), descriptor);
230 if (tokens.length <= 3) 246
247 if (tokens.length <= 3) {
231 return new MappingPair<>(obfuscatedEntry); 248 return new MappingPair<>(obfuscatedEntry);
249 }
250
232 String mapping = unescapeOpt(tokens[3], escapeNames); 251 String mapping = unescapeOpt(tokens[3], escapeNames);
233 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping)); 252 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping));
234 } 253 }
@@ -238,24 +257,25 @@ public final class TinyV2Reader implements MappingsReader {
238 MethodDescriptor descriptor = new MethodDescriptor(unescapeOpt(tokens[1], escapeNames)); 257 MethodDescriptor descriptor = new MethodDescriptor(unescapeOpt(tokens[1], escapeNames));
239 258
240 MethodEntry obfuscatedEntry = new MethodEntry(ownerClass, unescapeOpt(tokens[2], escapeNames), descriptor); 259 MethodEntry obfuscatedEntry = new MethodEntry(ownerClass, unescapeOpt(tokens[2], escapeNames), descriptor);
241 if (tokens.length <= 3) 260
261 if (tokens.length <= 3) {
242 return new MappingPair<>(obfuscatedEntry); 262 return new MappingPair<>(obfuscatedEntry);
263 }
264
243 String mapping = unescapeOpt(tokens[3], escapeNames); 265 String mapping = unescapeOpt(tokens[3], escapeNames);
244 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping)); 266 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping));
245 } 267 }
246 268
247
248
249 private void addJavadoc(MappingPair<? extends Entry, RawEntryMapping> pair, String javadoc) { 269 private void addJavadoc(MappingPair<? extends Entry, RawEntryMapping> pair, String javadoc) {
250 RawEntryMapping mapping = pair.getMapping(); 270 RawEntryMapping mapping = pair.getMapping();
271
251 if (mapping == null) { 272 if (mapping == null) {
252 throw new IllegalArgumentException("Javadoc requires a mapping in enigma!"); 273 throw new IllegalArgumentException("Javadoc requires a mapping in enigma!");
253 } 274 }
275
254 mapping.addJavadocLine(unescape(javadoc)); 276 mapping.addJavadocLine(unescape(javadoc));
255 } 277 }
256 278
257
258
259 private MappingPair<LocalVariableEntry, RawEntryMapping> parseArgument(MappingPair<? extends Entry, RawEntryMapping> parent, String[] tokens, boolean escapeNames) { 279 private MappingPair<LocalVariableEntry, RawEntryMapping> parseArgument(MappingPair<? extends Entry, RawEntryMapping> parent, String[] tokens, boolean escapeNames) {
260 MethodEntry ownerMethod = (MethodEntry) parent.getEntry(); 280 MethodEntry ownerMethod = (MethodEntry) parent.getEntry();
261 int variableIndex = Integer.parseInt(tokens[1]); 281 int variableIndex = Integer.parseInt(tokens[1]);
@@ -263,8 +283,11 @@ public final class TinyV2Reader implements MappingsReader {
263 // tokens[2] is the useless obf name 283 // tokens[2] is the useless obf name
264 284
265 LocalVariableEntry obfuscatedEntry = new LocalVariableEntry(ownerMethod, variableIndex, "", true, null); 285 LocalVariableEntry obfuscatedEntry = new LocalVariableEntry(ownerMethod, variableIndex, "", true, null);
266 if (tokens.length <= 3) 286
287 if (tokens.length <= 3) {
267 return new MappingPair<>(obfuscatedEntry); 288 return new MappingPair<>(obfuscatedEntry);
289 }
290
268 String mapping = unescapeOpt(tokens[3], escapeNames); 291 String mapping = unescapeOpt(tokens[3], escapeNames);
269 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping)); 292 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping));
270 } 293 }
@@ -279,7 +302,10 @@ public final class TinyV2Reader implements MappingsReader {
279 private static String unescape(String str) { 302 private static String unescape(String str) {
280 // copied from matcher, lazy! 303 // copied from matcher, lazy!
281 int pos = str.indexOf('\\'); 304 int pos = str.indexOf('\\');
282 if (pos < 0) return str; 305
306 if (pos < 0) {
307 return str;
308 }
283 309
284 StringBuilder ret = new StringBuilder(str.length() - 1); 310 StringBuilder ret = new StringBuilder(str.length() - 1);
285 int start = 0; 311 int start = 0;
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Writer.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Writer.java
index c400568..959d2d8 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Writer.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Writer.java
@@ -1,13 +1,23 @@
1package cuchaz.enigma.translation.mapping.serde.tinyv2; 1package cuchaz.enigma.translation.mapping.serde.tinyv2;
2 2
3import java.io.IOException;
4import java.io.PrintWriter;
5import java.nio.file.Files;
6import java.nio.file.Path;
7import java.util.Deque;
8import java.util.LinkedList;
9import java.util.List;
10import java.util.stream.StreamSupport;
11
3import com.google.common.base.Strings; 12import com.google.common.base.Strings;
13
4import cuchaz.enigma.ProgressListener; 14import cuchaz.enigma.ProgressListener;
5import cuchaz.enigma.translation.mapping.EntryMap; 15import cuchaz.enigma.translation.mapping.EntryMap;
6import cuchaz.enigma.translation.mapping.EntryMapping; 16import cuchaz.enigma.translation.mapping.EntryMapping;
7import cuchaz.enigma.translation.mapping.MappingDelta; 17import cuchaz.enigma.translation.mapping.MappingDelta;
8import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
9import cuchaz.enigma.translation.mapping.serde.LfPrintWriter; 18import cuchaz.enigma.translation.mapping.serde.LfPrintWriter;
10import cuchaz.enigma.translation.mapping.serde.MappingHelper; 19import cuchaz.enigma.translation.mapping.serde.MappingHelper;
20import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
11import cuchaz.enigma.translation.mapping.serde.MappingsWriter; 21import cuchaz.enigma.translation.mapping.serde.MappingsWriter;
12import cuchaz.enigma.translation.mapping.tree.EntryTree; 22import cuchaz.enigma.translation.mapping.tree.EntryTree;
13import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; 23import cuchaz.enigma.translation.mapping.tree.EntryTreeNode;
@@ -17,17 +27,7 @@ import cuchaz.enigma.translation.representation.entry.FieldEntry;
17import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; 27import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
18import cuchaz.enigma.translation.representation.entry.MethodEntry; 28import cuchaz.enigma.translation.representation.entry.MethodEntry;
19 29
20import java.io.IOException;
21import java.io.PrintWriter;
22import java.nio.file.Files;
23import java.nio.file.Path;
24import java.util.Deque;
25import java.util.LinkedList;
26import java.util.List;
27import java.util.stream.StreamSupport;
28
29public final class TinyV2Writer implements MappingsWriter { 30public final class TinyV2Writer implements MappingsWriter {
30
31 private static final String MINOR_VERSION = "0"; 31 private static final String MINOR_VERSION = "0";
32 private final String obfHeader; 32 private final String obfHeader;
33 private final String deobfHeader; 33 private final String deobfHeader;
@@ -60,13 +60,16 @@ public final class TinyV2Writer implements MappingsWriter {
60 String fullName = classEntry.getFullName(); 60 String fullName = classEntry.getFullName();
61 writer.print(fullName); 61 writer.print(fullName);
62 Deque<String> parts = new LinkedList<>(); 62 Deque<String> parts = new LinkedList<>();
63
63 do { 64 do {
64 EntryMapping mapping = tree.get(classEntry); 65 EntryMapping mapping = tree.get(classEntry);
66
65 if (mapping != null && mapping.targetName() != null) { 67 if (mapping != null && mapping.targetName() != null) {
66 parts.addFirst(mapping.targetName()); 68 parts.addFirst(mapping.targetName());
67 } else { 69 } else {
68 parts.addFirst(classEntry.getName()); 70 parts.addFirst(classEntry.getName());
69 } 71 }
72
70 classEntry = classEntry.getOuterClass(); 73 classEntry = classEntry.getOuterClass();
71 } while (classEntry != null); 74 } while (classEntry != null);
72 75
@@ -82,6 +85,7 @@ public final class TinyV2Writer implements MappingsWriter {
82 85
83 for (EntryTreeNode<EntryMapping> child : node.getChildNodes()) { 86 for (EntryTreeNode<EntryMapping> child : node.getChildNodes()) {
84 Entry<?> entry = child.getEntry(); 87 Entry<?> entry = child.getEntry();
88
85 if (entry instanceof FieldEntry) { 89 if (entry instanceof FieldEntry) {
86 writeField(writer, child); 90 writeField(writer, child);
87 } else if (entry instanceof MethodEntry) { 91 } else if (entry instanceof MethodEntry) {
@@ -113,16 +117,19 @@ public final class TinyV2Writer implements MappingsWriter {
113 117
114 for (EntryTreeNode<EntryMapping> child : node.getChildNodes()) { 118 for (EntryTreeNode<EntryMapping> child : node.getChildNodes()) {
115 Entry<?> entry = child.getEntry(); 119 Entry<?> entry = child.getEntry();
120
116 if (entry instanceof LocalVariableEntry) { 121 if (entry instanceof LocalVariableEntry) {
117 writeParameter(writer, child); 122 writeParameter(writer, child);
118 } 123 }
124
119 // TODO write actual local variables 125 // TODO write actual local variables
120 } 126 }
121 } 127 }
122 128
123 private void writeField(PrintWriter writer, EntryTreeNode<EntryMapping> node) { 129 private void writeField(PrintWriter writer, EntryTreeNode<EntryMapping> node) {
124 if (node.getValue() == null || node.getValue().equals(EntryMapping.DEFAULT)) 130 if (node.getValue() == null || node.getValue().equals(EntryMapping.DEFAULT)) {
125 return; // Shortcut 131 return; // Shortcut
132 }
126 133
127 writer.print(indent(1)); 134 writer.print(indent(1));
128 writer.print("f\t"); 135 writer.print("f\t");
@@ -146,8 +153,9 @@ public final class TinyV2Writer implements MappingsWriter {
146 } 153 }
147 154
148 private void writeParameter(PrintWriter writer, EntryTreeNode<EntryMapping> node) { 155 private void writeParameter(PrintWriter writer, EntryTreeNode<EntryMapping> node) {
149 if (node.getValue() == null || node.getValue().equals(EntryMapping.DEFAULT)) 156 if (node.getValue() == null || node.getValue().equals(EntryMapping.DEFAULT)) {
150 return; // Shortcut 157 return; // Shortcut
158 }
151 159
152 writer.print(indent(2)); 160 writer.print(indent(2));
153 writer.print("p\t"); 161 writer.print("p\t");
@@ -156,6 +164,7 @@ public final class TinyV2Writer implements MappingsWriter {
156 writer.print(node.getEntry().getName()); 164 writer.print(node.getEntry().getName());
157 writer.print("\t"); 165 writer.print("\t");
158 EntryMapping mapping = node.getValue(); 166 EntryMapping mapping = node.getValue();
167
159 if (mapping == null || mapping.targetName() == null) { 168 if (mapping == null || mapping.targetName() == null) {
160 writer.println(); // todo ??? 169 writer.println(); // todo ???
161 } else { 170 } else {
@@ -177,5 +186,4 @@ public final class TinyV2Writer implements MappingsWriter {
177 private String indent(int level) { 186 private String indent(int level) {
178 return Strings.repeat("\t", level); 187 return Strings.repeat("\t", level);
179 } 188 }
180
181} 189}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java
index 255fa5f..b943cc8 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java
@@ -1,5 +1,11 @@
1package cuchaz.enigma.translation.mapping.tree; 1package cuchaz.enigma.translation.mapping.tree;
2 2
3import java.util.Collection;
4import java.util.Iterator;
5import java.util.stream.Stream;
6
7import javax.annotation.Nullable;
8
3import cuchaz.enigma.translation.Translator; 9import cuchaz.enigma.translation.Translator;
4import cuchaz.enigma.translation.mapping.EntryMap; 10import cuchaz.enigma.translation.mapping.EntryMap;
5import cuchaz.enigma.translation.mapping.EntryMapping; 11import cuchaz.enigma.translation.mapping.EntryMapping;
@@ -7,11 +13,6 @@ import cuchaz.enigma.translation.mapping.EntryResolver;
7import cuchaz.enigma.translation.mapping.MappingDelta; 13import cuchaz.enigma.translation.mapping.MappingDelta;
8import cuchaz.enigma.translation.representation.entry.Entry; 14import cuchaz.enigma.translation.representation.entry.Entry;
9 15
10import javax.annotation.Nullable;
11import java.util.Collection;
12import java.util.Iterator;
13import java.util.stream.Stream;
14
15public class DeltaTrackingTree<T> implements EntryTree<T> { 16public class DeltaTrackingTree<T> implements EntryTree<T> {
16 private final EntryTree<T> delegate; 17 private final EntryTree<T> delegate;
17 18
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTreeNode.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTreeNode.java
index eb26ea9..254b331 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTreeNode.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTreeNode.java
@@ -1,12 +1,13 @@
1package cuchaz.enigma.translation.mapping.tree; 1package cuchaz.enigma.translation.mapping.tree;
2 2
3import cuchaz.enigma.translation.representation.entry.Entry;
4
5import javax.annotation.Nullable;
6import java.util.ArrayList; 3import java.util.ArrayList;
7import java.util.Collection; 4import java.util.Collection;
8import java.util.List; 5import java.util.List;
9 6
7import javax.annotation.Nullable;
8
9import cuchaz.enigma.translation.representation.entry.Entry;
10
10public interface EntryTreeNode<T> { 11public interface EntryTreeNode<T> {
11 @Nullable 12 @Nullable
12 T getValue(); 13 T getValue();
@@ -22,16 +23,16 @@ public interface EntryTreeNode<T> {
22 default Collection<? extends EntryTreeNode<T>> getNodesRecursively() { 23 default Collection<? extends EntryTreeNode<T>> getNodesRecursively() {
23 Collection<EntryTreeNode<T>> nodes = new ArrayList<>(); 24 Collection<EntryTreeNode<T>> nodes = new ArrayList<>();
24 nodes.add(this); 25 nodes.add(this);
26
25 for (EntryTreeNode<T> node : getChildNodes()) { 27 for (EntryTreeNode<T> node : getChildNodes()) {
26 nodes.addAll(node.getNodesRecursively()); 28 nodes.addAll(node.getNodesRecursively());
27 } 29 }
30
28 return nodes; 31 return nodes;
29 } 32 }
30 33
31 default List<? extends Entry<?>> getChildrenRecursively() { 34 default List<? extends Entry<?>> getChildrenRecursively() {
32 return getNodesRecursively().stream() 35 return getNodesRecursively().stream().map(EntryTreeNode::getEntry).toList();
33 .map(EntryTreeNode::getEntry)
34 .toList();
35 } 36 }
36 37
37 default boolean hasValue() { 38 default boolean hasValue() {
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java
index 0992d34..2902373 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java
@@ -1,18 +1,27 @@
1package cuchaz.enigma.translation.mapping.tree; 1package cuchaz.enigma.translation.mapping.tree;
2 2
3import java.util.ArrayList;
4import java.util.Collection;
5import java.util.Collections;
6import java.util.HashMap;
7import java.util.HashSet;
8import java.util.Iterator;
9import java.util.List;
10import java.util.Map;
11import java.util.Set;
12import java.util.function.Function;
13import java.util.stream.Stream;
14import java.util.stream.StreamSupport;
15
16import javax.annotation.Nonnull;
17import javax.annotation.Nullable;
18
3import cuchaz.enigma.translation.Translator; 19import cuchaz.enigma.translation.Translator;
4import cuchaz.enigma.translation.mapping.EntryMap; 20import cuchaz.enigma.translation.mapping.EntryMap;
5import cuchaz.enigma.translation.mapping.EntryMapping; 21import cuchaz.enigma.translation.mapping.EntryMapping;
6import cuchaz.enigma.translation.mapping.EntryResolver; 22import cuchaz.enigma.translation.mapping.EntryResolver;
7import cuchaz.enigma.translation.representation.entry.Entry; 23import cuchaz.enigma.translation.representation.entry.Entry;
8 24
9import javax.annotation.Nonnull;
10import javax.annotation.Nullable;
11import java.util.*;
12import java.util.function.Function;
13import java.util.stream.Stream;
14import java.util.stream.StreamSupport;
15
16public class HashEntryTree<T> implements EntryTree<T> { 25public class HashEntryTree<T> implements EntryTree<T> {
17 private final Map<Entry<?>, HashTreeNode<T>> root = new HashMap<>(); 26 private final Map<Entry<?>, HashTreeNode<T>> root = new HashMap<>();
18 27
@@ -29,6 +38,7 @@ public class HashEntryTree<T> implements EntryTree<T> {
29 public void insert(Entry<?> entry, T value) { 38 public void insert(Entry<?> entry, T value) {
30 List<HashTreeNode<T>> path = computePath(entry, true); 39 List<HashTreeNode<T>> path = computePath(entry, true);
31 path.get(path.size() - 1).putValue(value); 40 path.get(path.size() - 1).putValue(value);
41
32 if (value == null) { 42 if (value == null) {
33 removeDeadAlong(path); 43 removeDeadAlong(path);
34 } 44 }
@@ -38,6 +48,7 @@ public class HashEntryTree<T> implements EntryTree<T> {
38 @Nullable 48 @Nullable
39 public T remove(Entry<?> entry) { 49 public T remove(Entry<?> entry) {
40 List<HashTreeNode<T>> path = computePath(entry, false); 50 List<HashTreeNode<T>> path = computePath(entry, false);
51
41 if (path.isEmpty()) { 52 if (path.isEmpty()) {
42 return null; 53 return null;
43 } 54 }
@@ -53,9 +64,11 @@ public class HashEntryTree<T> implements EntryTree<T> {
53 @Nullable 64 @Nullable
54 public T get(Entry<?> entry) { 65 public T get(Entry<?> entry) {
55 HashTreeNode<T> node = findNode(entry); 66 HashTreeNode<T> node = findNode(entry);
67
56 if (node == null) { 68 if (node == null) {
57 return null; 69 return null;
58 } 70 }
71
59 return node.getValue(); 72 return node.getValue();
60 } 73 }
61 74
@@ -67,18 +80,22 @@ public class HashEntryTree<T> implements EntryTree<T> {
67 @Override 80 @Override
68 public Collection<Entry<?>> getChildren(Entry<?> entry) { 81 public Collection<Entry<?>> getChildren(Entry<?> entry) {
69 HashTreeNode<T> leaf = findNode(entry); 82 HashTreeNode<T> leaf = findNode(entry);
83
70 if (leaf == null) { 84 if (leaf == null) {
71 return Collections.emptyList(); 85 return Collections.emptyList();
72 } 86 }
87
73 return leaf.getChildren(); 88 return leaf.getChildren();
74 } 89 }
75 90
76 @Override 91 @Override
77 public Collection<Entry<?>> getSiblings(Entry<?> entry) { 92 public Collection<Entry<?>> getSiblings(Entry<?> entry) {
78 Entry<?> parent = entry.getParent(); 93 Entry<?> parent = entry.getParent();
94
79 if (parent == null) { 95 if (parent == null) {
80 return getSiblings(entry, root.keySet()); 96 return getSiblings(entry, root.keySet());
81 } 97 }
98
82 return getSiblings(entry, getChildren(parent)); 99 return getSiblings(entry, getChildren(parent));
83 } 100 }
84 101
@@ -92,15 +109,18 @@ public class HashEntryTree<T> implements EntryTree<T> {
92 @Nullable 109 @Nullable
93 public HashTreeNode<T> findNode(Entry<?> target) { 110 public HashTreeNode<T> findNode(Entry<?> target) {
94 List<Entry<?>> parentChain = target.getAncestry(); 111 List<Entry<?>> parentChain = target.getAncestry();
112
95 if (parentChain.isEmpty()) { 113 if (parentChain.isEmpty()) {
96 return null; 114 return null;
97 } 115 }
98 116
99 HashTreeNode<T> node = root.get(parentChain.get(0)); 117 HashTreeNode<T> node = root.get(parentChain.get(0));
118
100 for (int i = 1; i < parentChain.size(); i++) { 119 for (int i = 1; i < parentChain.size(); i++) {
101 if (node == null) { 120 if (node == null) {
102 return null; 121 return null;
103 } 122 }
123
104 node = node.getChild(parentChain.get(i)); 124 node = node.getChild(parentChain.get(i));
105 } 125 }
106 126
@@ -109,6 +129,7 @@ public class HashEntryTree<T> implements EntryTree<T> {
109 129
110 private List<HashTreeNode<T>> computePath(Entry<?> target, boolean make) { 130 private List<HashTreeNode<T>> computePath(Entry<?> target, boolean make) {
111 List<Entry<?>> ancestry = target.getAncestry(); 131 List<Entry<?>> ancestry = target.getAncestry();
132
112 if (ancestry.isEmpty()) { 133 if (ancestry.isEmpty()) {
113 return Collections.emptyList(); 134 return Collections.emptyList();
114 } 135 }
@@ -117,6 +138,7 @@ public class HashEntryTree<T> implements EntryTree<T> {
117 138
118 Entry<?> rootEntry = ancestry.get(0); 139 Entry<?> rootEntry = ancestry.get(0);
119 HashTreeNode<T> node = make ? root.computeIfAbsent(rootEntry, HashTreeNode::new) : root.get(rootEntry); 140 HashTreeNode<T> node = make ? root.computeIfAbsent(rootEntry, HashTreeNode::new) : root.get(rootEntry);
141
120 if (node == null) { 142 if (node == null) {
121 return Collections.emptyList(); 143 return Collections.emptyList();
122 } 144 }
@@ -126,6 +148,7 @@ public class HashEntryTree<T> implements EntryTree<T> {
126 for (int i = 1; i < ancestry.size(); i++) { 148 for (int i = 1; i < ancestry.size(); i++) {
127 Entry<?> ancestor = ancestry.get(i); 149 Entry<?> ancestor = ancestry.get(i);
128 node = make ? node.computeChild(ancestor) : node.getChild(ancestor); 150 node = make ? node.computeChild(ancestor) : node.getChild(ancestor);
151
129 if (node == null) { 152 if (node == null) {
130 return Collections.emptyList(); 153 return Collections.emptyList();
131 } 154 }
@@ -139,6 +162,7 @@ public class HashEntryTree<T> implements EntryTree<T> {
139 private void removeDeadAlong(List<HashTreeNode<T>> path) { 162 private void removeDeadAlong(List<HashTreeNode<T>> path) {
140 for (int i = path.size() - 1; i >= 0; i--) { 163 for (int i = path.size() - 1; i >= 0; i--) {
141 HashTreeNode<T> node = path.get(i); 164 HashTreeNode<T> node = path.get(i);
165
142 if (node.isEmpty()) { 166 if (node.isEmpty()) {
143 if (i > 0) { 167 if (i > 0) {
144 HashTreeNode<T> parentNode = path.get(i - 1); 168 HashTreeNode<T> parentNode = path.get(i - 1);
@@ -156,17 +180,17 @@ public class HashEntryTree<T> implements EntryTree<T> {
156 @Nonnull 180 @Nonnull
157 public Iterator<EntryTreeNode<T>> iterator() { 181 public Iterator<EntryTreeNode<T>> iterator() {
158 Collection<EntryTreeNode<T>> nodes = new ArrayList<>(); 182 Collection<EntryTreeNode<T>> nodes = new ArrayList<>();
183
159 for (EntryTreeNode<T> node : root.values()) { 184 for (EntryTreeNode<T> node : root.values()) {
160 nodes.addAll(node.getNodesRecursively()); 185 nodes.addAll(node.getNodesRecursively());
161 } 186 }
187
162 return nodes.iterator(); 188 return nodes.iterator();
163 } 189 }
164 190
165 @Override 191 @Override
166 public Stream<Entry<?>> getAllEntries() { 192 public Stream<Entry<?>> getAllEntries() {
167 return StreamSupport.stream(spliterator(), false) 193 return StreamSupport.stream(spliterator(), false).filter(EntryTreeNode::hasValue).map(EntryTreeNode::getEntry);
168 .filter(EntryTreeNode::hasValue)
169 .map(EntryTreeNode::getEntry);
170 } 194 }
171 195
172 @Override 196 @Override
@@ -182,9 +206,11 @@ public class HashEntryTree<T> implements EntryTree<T> {
182 @Override 206 @Override
183 public HashEntryTree<T> translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) { 207 public HashEntryTree<T> translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
184 HashEntryTree<T> translatedTree = new HashEntryTree<>(); 208 HashEntryTree<T> translatedTree = new HashEntryTree<>();
209
185 for (EntryTreeNode<T> node : this) { 210 for (EntryTreeNode<T> node : this) {
186 translatedTree.insert(translator.translate(node.getEntry()), node.getValue()); 211 translatedTree.insert(translator.translate(node.getEntry()), node.getValue());
187 } 212 }
213
188 return translatedTree; 214 return translatedTree;
189 } 215 }
190} 216}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashTreeNode.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashTreeNode.java
index 0a990bd..3ddaf81 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashTreeNode.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashTreeNode.java
@@ -1,14 +1,15 @@
1package cuchaz.enigma.translation.mapping.tree; 1package cuchaz.enigma.translation.mapping.tree;
2 2
3import cuchaz.enigma.translation.representation.entry.Entry;
4
5import javax.annotation.Nonnull;
6import javax.annotation.Nullable;
7import java.util.Collection; 3import java.util.Collection;
8import java.util.HashMap; 4import java.util.HashMap;
9import java.util.Iterator; 5import java.util.Iterator;
10import java.util.Map; 6import java.util.Map;
11 7
8import javax.annotation.Nonnull;
9import javax.annotation.Nullable;
10
11import cuchaz.enigma.translation.representation.entry.Entry;
12
12public class HashTreeNode<T> implements EntryTreeNode<T>, Iterable<HashTreeNode<T>> { 13public class HashTreeNode<T> implements EntryTreeNode<T>, Iterable<HashTreeNode<T>> {
13 private final Entry<?> entry; 14 private final Entry<?> entry;
14 private final Map<Entry<?>, HashTreeNode<T>> children = new HashMap<>(); 15 private final Map<Entry<?>, HashTreeNode<T>> children = new HashMap<>();
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java
index e8480a2..24204f8 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java
@@ -1,9 +1,10 @@
1package cuchaz.enigma.translation.representation; 1package cuchaz.enigma.translation.representation;
2 2
3import cuchaz.enigma.analysis.Access; 3import java.lang.reflect.Modifier;
4
4import org.objectweb.asm.Opcodes; 5import org.objectweb.asm.Opcodes;
5 6
6import java.lang.reflect.Modifier; 7import cuchaz.enigma.analysis.Access;
7 8
8public class AccessFlags { 9public class AccessFlags {
9 public static final AccessFlags PRIVATE = new AccessFlags(Opcodes.ACC_PRIVATE); 10 public static final AccessFlags PRIVATE = new AccessFlags(Opcodes.ACC_PRIVATE);
@@ -110,15 +111,19 @@ public class AccessFlags {
110 @Override 111 @Override
111 public String toString() { 112 public String toString() {
112 StringBuilder builder = new StringBuilder(Access.get(this).toString().toLowerCase()); 113 StringBuilder builder = new StringBuilder(Access.get(this).toString().toLowerCase());
114
113 if (isStatic()) { 115 if (isStatic()) {
114 builder.append(" static"); 116 builder.append(" static");
115 } 117 }
118
116 if (isSynthetic()) { 119 if (isSynthetic()) {
117 builder.append(" synthetic"); 120 builder.append(" synthetic");
118 } 121 }
122
119 if (isBridge()) { 123 if (isBridge()) {
120 builder.append(" bridge"); 124 builder.append(" bridge");
121 } 125 }
126
122 return builder.toString(); 127 return builder.toString();
123 } 128 }
124} 129}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/Lambda.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/Lambda.java
index 13c7cd4..0854699 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/Lambda.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/Lambda.java
@@ -34,25 +34,20 @@ public class Lambda implements Translatable {
34 MethodEntry samMethod = new MethodEntry(getInterface(), invokedName, samMethodType); 34 MethodEntry samMethod = new MethodEntry(getInterface(), invokedName, samMethodType);
35 EntryMapping samMethodMapping = resolveMapping(resolver, mappings, samMethod); 35 EntryMapping samMethodMapping = resolveMapping(resolver, mappings, samMethod);
36 36
37 return TranslateResult.of( 37 return TranslateResult.of(samMethodMapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED,
38 samMethodMapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, 38 new Lambda(samMethodMapping.targetName() != null ? samMethodMapping.targetName() : invokedName, invokedType.extendedTranslate(translator, resolver, mappings).getValue(), samMethodType.extendedTranslate(translator, resolver, mappings).getValue(),
39 new Lambda( 39 implMethod.extendedTranslate(translator, resolver, mappings).getValue(), instantiatedMethodType.extendedTranslate(translator, resolver, mappings).getValue()));
40 samMethodMapping.targetName() != null ? samMethodMapping.targetName() : invokedName,
41 invokedType.extendedTranslate(translator, resolver, mappings).getValue(),
42 samMethodType.extendedTranslate(translator, resolver, mappings).getValue(),
43 implMethod.extendedTranslate(translator, resolver, mappings).getValue(),
44 instantiatedMethodType.extendedTranslate(translator, resolver, mappings).getValue()
45 )
46 );
47 } 40 }
48 41
49 private EntryMapping resolveMapping(EntryResolver resolver, EntryMap<EntryMapping> mappings, MethodEntry methodEntry) { 42 private EntryMapping resolveMapping(EntryResolver resolver, EntryMap<EntryMapping> mappings, MethodEntry methodEntry) {
50 for (MethodEntry entry : resolver.resolveEntry(methodEntry, ResolutionStrategy.RESOLVE_ROOT)) { 43 for (MethodEntry entry : resolver.resolveEntry(methodEntry, ResolutionStrategy.RESOLVE_ROOT)) {
51 EntryMapping mapping = mappings.get(entry); 44 EntryMapping mapping = mappings.get(entry);
45
52 if (mapping != null) { 46 if (mapping != null) {
53 return mapping; 47 return mapping;
54 } 48 }
55 } 49 }
50
56 return EntryMapping.DEFAULT; 51 return EntryMapping.DEFAULT;
57 } 52 }
58 53
@@ -82,14 +77,16 @@ public class Lambda implements Translatable {
82 77
83 @Override 78 @Override
84 public boolean equals(Object o) { 79 public boolean equals(Object o) {
85 if (this == o) return true; 80 if (this == o) {
86 if (o == null || getClass() != o.getClass()) return false; 81 return true;
82 }
83
84 if (o == null || getClass() != o.getClass()) {
85 return false;
86 }
87
87 Lambda lambda = (Lambda) o; 88 Lambda lambda = (Lambda) o;
88 return Objects.equals(invokedName, lambda.invokedName) && 89 return Objects.equals(invokedName, lambda.invokedName) && Objects.equals(invokedType, lambda.invokedType) && Objects.equals(samMethodType, lambda.samMethodType) && Objects.equals(implMethod, lambda.implMethod) && Objects.equals(instantiatedMethodType, lambda.instantiatedMethodType);
89 Objects.equals(invokedType, lambda.invokedType) &&
90 Objects.equals(samMethodType, lambda.samMethodType) &&
91 Objects.equals(implMethod, lambda.implMethod) &&
92 Objects.equals(instantiatedMethodType, lambda.instantiatedMethodType);
93 } 90 }
94 91
95 @Override 92 @Override
@@ -99,12 +96,6 @@ public class Lambda implements Translatable {
99 96
100 @Override 97 @Override
101 public String toString() { 98 public String toString() {
102 return "Lambda{" + 99 return "Lambda{" + "invokedName='" + invokedName + '\'' + ", invokedType=" + invokedType + ", samMethodType=" + samMethodType + ", implMethod=" + implMethod + ", instantiatedMethodType=" + instantiatedMethodType + '}';
103 "invokedName='" + invokedName + '\'' +
104 ", invokedType=" + invokedType +
105 ", samMethodType=" + samMethodType +
106 ", implMethod=" + implMethod +
107 ", instantiatedMethodType=" + instantiatedMethodType +
108 '}';
109 } 100 }
110} 101}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/MethodDescriptor.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/MethodDescriptor.java
index 998c944..571488c 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/MethodDescriptor.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/MethodDescriptor.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.translation.representation; 12package cuchaz.enigma.translation.representation;
13 13
@@ -27,7 +27,6 @@ import cuchaz.enigma.translation.mapping.EntryResolver;
27import cuchaz.enigma.translation.representation.entry.ClassEntry; 27import cuchaz.enigma.translation.representation.entry.ClassEntry;
28 28
29public class MethodDescriptor implements Translatable { 29public class MethodDescriptor implements Translatable {
30
31 private List<TypeDescriptor> argumentDescs; 30 private List<TypeDescriptor> argumentDescs;
32 private TypeDescriptor returnDesc; 31 private TypeDescriptor returnDesc;
33 32
@@ -35,8 +34,10 @@ public class MethodDescriptor implements Translatable {
35 try { 34 try {
36 this.argumentDescs = Lists.newArrayList(); 35 this.argumentDescs = Lists.newArrayList();
37 int i = 0; 36 int i = 0;
37
38 while (i < desc.length()) { 38 while (i < desc.length()) {
39 char c = desc.charAt(i); 39 char c = desc.charAt(i);
40
40 if (c == '(') { 41 if (c == '(') {
41 assert (this.argumentDescs.isEmpty()); 42 assert (this.argumentDescs.isEmpty());
42 assert (this.returnDesc == null); 43 assert (this.returnDesc == null);
@@ -50,6 +51,7 @@ public class MethodDescriptor implements Translatable {
50 i += type.length(); 51 i += type.length();
51 } 52 }
52 } 53 }
54
53 this.returnDesc = new TypeDescriptor(TypeDescriptor.parseFirst(desc.substring(i))); 55 this.returnDesc = new TypeDescriptor(TypeDescriptor.parseFirst(desc.substring(i)));
54 } catch (Exception ex) { 56 } catch (Exception ex) {
55 throw new IllegalArgumentException("Unable to parse method descriptor: " + desc, ex); 57 throw new IllegalArgumentException("Unable to parse method descriptor: " + desc, ex);
@@ -73,9 +75,11 @@ public class MethodDescriptor implements Translatable {
73 public String toString() { 75 public String toString() {
74 StringBuilder buf = new StringBuilder(); 76 StringBuilder buf = new StringBuilder();
75 buf.append("("); 77 buf.append("(");
78
76 for (TypeDescriptor desc : this.argumentDescs) { 79 for (TypeDescriptor desc : this.argumentDescs) {
77 buf.append(desc); 80 buf.append(desc);
78 } 81 }
82
79 buf.append(")"); 83 buf.append(")");
80 buf.append(this.returnDesc); 84 buf.append(this.returnDesc);
81 return buf.toString(); 85 return buf.toString();
@@ -108,23 +112,28 @@ public class MethodDescriptor implements Translatable {
108 return true; 112 return true;
109 } 113 }
110 } 114 }
115
111 return false; 116 return false;
112 } 117 }
113 118
114 public MethodDescriptor remap(Function<String, String> remapper) { 119 public MethodDescriptor remap(Function<String, String> remapper) {
115 List<TypeDescriptor> argumentDescs = new ArrayList<>(this.argumentDescs.size()); 120 List<TypeDescriptor> argumentDescs = new ArrayList<>(this.argumentDescs.size());
121
116 for (TypeDescriptor desc : this.argumentDescs) { 122 for (TypeDescriptor desc : this.argumentDescs) {
117 argumentDescs.add(desc.remap(remapper)); 123 argumentDescs.add(desc.remap(remapper));
118 } 124 }
125
119 return new MethodDescriptor(argumentDescs, returnDesc.remap(remapper)); 126 return new MethodDescriptor(argumentDescs, returnDesc.remap(remapper));
120 } 127 }
121 128
122 @Override 129 @Override
123 public TranslateResult<MethodDescriptor> extendedTranslate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) { 130 public TranslateResult<MethodDescriptor> extendedTranslate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
124 List<TypeDescriptor> translatedArguments = new ArrayList<>(argumentDescs.size()); 131 List<TypeDescriptor> translatedArguments = new ArrayList<>(argumentDescs.size());
132
125 for (TypeDescriptor argument : argumentDescs) { 133 for (TypeDescriptor argument : argumentDescs) {
126 translatedArguments.add(translator.translate(argument)); 134 translatedArguments.add(translator.translate(argument));
127 } 135 }
136
128 return TranslateResult.ungrouped(new MethodDescriptor(translatedArguments, translator.translate(returnDesc))); 137 return TranslateResult.ungrouped(new MethodDescriptor(translatedArguments, translator.translate(returnDesc)));
129 } 138 }
130 139
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/Signature.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/Signature.java
index 33b9797..a8278fc 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/Signature.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/Signature.java
@@ -35,6 +35,7 @@ public class Signature implements Translatable {
35 if (signature != null && !signature.isEmpty()) { 35 if (signature != null && !signature.isEmpty()) {
36 return new Signature(signature, true); 36 return new Signature(signature, true);
37 } 37 }
38
38 return new Signature(null, true); 39 return new Signature(null, true);
39 } 40 }
40 41
@@ -42,6 +43,7 @@ public class Signature implements Translatable {
42 if (signature != null && !signature.isEmpty()) { 43 if (signature != null && !signature.isEmpty()) {
43 return new Signature(signature, false); 44 return new Signature(signature, false);
44 } 45 }
46
45 return new Signature(null, false); 47 return new Signature(null, false);
46 } 48 }
47 49
@@ -57,13 +59,16 @@ public class Signature implements Translatable {
57 if (signature == null) { 59 if (signature == null) {
58 return this; 60 return this;
59 } 61 }
62
60 SignatureWriter writer = new SignatureWriter(); 63 SignatureWriter writer = new SignatureWriter();
61 SignatureVisitor visitor = new TranslationSignatureVisitor(remapper, writer); 64 SignatureVisitor visitor = new TranslationSignatureVisitor(remapper, writer);
65
62 if (isType) { 66 if (isType) {
63 new SignatureReader(signature).acceptType(visitor); 67 new SignatureReader(signature).acceptType(visitor);
64 } else { 68 } else {
65 new SignatureReader(signature).accept(visitor); 69 new SignatureReader(signature).accept(visitor);
66 } 70 }
71
67 return new Signature(writer.toString(), isType); 72 return new Signature(writer.toString(), isType);
68 } 73 }
69 74
@@ -71,16 +76,16 @@ public class Signature implements Translatable {
71 public boolean equals(Object obj) { 76 public boolean equals(Object obj) {
72 if (obj instanceof Signature) { 77 if (obj instanceof Signature) {
73 Signature other = (Signature) obj; 78 Signature other = (Signature) obj;
74 return (other.signature == null && signature == null || other.signature != null 79 return (other.signature == null && signature == null || other.signature != null && signature != null && other.signature.equals(signature)) && other.isType == this.isType;
75 && signature != null && other.signature.equals(signature))
76 && other.isType == this.isType;
77 } 80 }
81
78 return false; 82 return false;
79 } 83 }
80 84
81 @Override 85 @Override
82 public int hashCode() { 86 public int hashCode() {
83 int hash = (isType ? 1 : 0) << 16; 87 int hash = (isType ? 1 : 0) << 16;
88
84 if (signature != null) { 89 if (signature != null) {
85 hash |= signature.hashCode(); 90 hash |= signature.hashCode();
86 } 91 }
@@ -97,5 +102,4 @@ public class Signature implements Translatable {
97 public TranslateResult<Signature> extendedTranslate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) { 102 public TranslateResult<Signature> extendedTranslate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
98 return TranslateResult.ungrouped(this.remap(name -> translator.translate(new ClassEntry(name)).getFullName())); 103 return TranslateResult.ungrouped(this.remap(name -> translator.translate(new ClassEntry(name)).getFullName()));
99 } 104 }
100
101} 105}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java
index 6a1b82f..fd53522 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.translation.representation; 12package cuchaz.enigma.translation.representation;
13 13
@@ -26,7 +26,6 @@ import cuchaz.enigma.translation.mapping.EntryResolver;
26import cuchaz.enigma.translation.representation.entry.ClassEntry; 26import cuchaz.enigma.translation.representation.entry.ClassEntry;
27 27
28public class TypeDescriptor implements Translatable { 28public class TypeDescriptor implements Translatable {
29
30 protected final String desc; 29 protected final String desc;
31 30
32 public TypeDescriptor(String desc) { 31 public TypeDescriptor(String desc) {
@@ -42,7 +41,6 @@ public class TypeDescriptor implements Translatable {
42 } 41 }
43 42
44 public static String parseFirst(String in) { 43 public static String parseFirst(String in) {
45
46 if (in == null || in.length() <= 0) { 44 if (in == null || in.length() <= 0) {
47 throw new IllegalArgumentException("No desc to parse, input is empty!"); 45 throw new IllegalArgumentException("No desc to parse, input is empty!");
48 } 46 }
@@ -58,6 +56,7 @@ public class TypeDescriptor implements Translatable {
58 56
59 // then check for primitives 57 // then check for primitives
60 Primitive primitive = Primitive.get(c); 58 Primitive primitive = Primitive.get(c);
59
61 if (primitive != null) { 60 if (primitive != null) {
62 return in.substring(0, 1); 61 return in.substring(0, 1);
63 } 62 }
@@ -74,6 +73,7 @@ public class TypeDescriptor implements Translatable {
74 73
75 // then check for arrays 74 // then check for arrays
76 int dim = countArrayDimension(in); 75 int dim = countArrayDimension(in);
76
77 if (dim > 0) { 77 if (dim > 0) {
78 String arrayType = TypeDescriptor.parseFirst(in.substring(dim)); 78 String arrayType = TypeDescriptor.parseFirst(in.substring(dim));
79 return in.substring(0, dim + arrayType.length()); 79 return in.substring(0, dim + arrayType.length());
@@ -84,8 +84,11 @@ public class TypeDescriptor implements Translatable {
84 84
85 private static int countArrayDimension(String in) { 85 private static int countArrayDimension(String in) {
86 int i = 0; 86 int i = 0;
87 while (i < in.length() && in.charAt(i) == '[') 87
88 while (i < in.length() && in.charAt(i) == '[') {
88 i++; 89 i++;
90 }
91
89 return i; 92 return i;
90 } 93 }
91 94
@@ -94,6 +97,7 @@ public class TypeDescriptor implements Translatable {
94 // include the parameters too 97 // include the parameters too
95 StringBuilder buf = new StringBuilder(); 98 StringBuilder buf = new StringBuilder();
96 int depth = 0; 99 int depth = 0;
100
97 for (int i = 0; i < in.length(); i++) { 101 for (int i = 0; i < in.length(); i++) {
98 char c = in.charAt(i); 102 char c = in.charAt(i);
99 buf.append(c); 103 buf.append(c);
@@ -106,6 +110,7 @@ public class TypeDescriptor implements Translatable {
106 return buf.toString(); 110 return buf.toString();
107 } 111 }
108 } 112 }
113
109 return null; 114 return null;
110 } 115 }
111 116
@@ -130,6 +135,7 @@ public class TypeDescriptor implements Translatable {
130 if (!isPrimitive()) { 135 if (!isPrimitive()) {
131 throw new IllegalStateException("not a primitive"); 136 throw new IllegalStateException("not a primitive");
132 } 137 }
138
133 return Primitive.get(this.desc.charAt(0)); 139 return Primitive.get(this.desc.charAt(0));
134 } 140 }
135 141
@@ -142,13 +148,13 @@ public class TypeDescriptor implements Translatable {
142 String name = this.desc.substring(1, this.desc.length() - 1); 148 String name = this.desc.substring(1, this.desc.length() - 1);
143 149
144 int pos = name.indexOf('<'); 150 int pos = name.indexOf('<');
151
145 if (pos >= 0) { 152 if (pos >= 0) {
146 // remove the parameters from the class name 153 // remove the parameters from the class name
147 name = name.substring(0, pos); 154 name = name.substring(0, pos);
148 } 155 }
149 156
150 return new ClassEntry(name); 157 return new ClassEntry(name);
151
152 } else if (isArray() && getArrayType().isType()) { 158 } else if (isArray() && getArrayType().isType()) {
153 return getArrayType().getTypeEntry(); 159 return getArrayType().getTypeEntry();
154 } else { 160 } else {
@@ -164,6 +170,7 @@ public class TypeDescriptor implements Translatable {
164 if (!isArray()) { 170 if (!isArray()) {
165 throw new IllegalStateException("not an array"); 171 throw new IllegalStateException("not an array");
166 } 172 }
173
167 return countArrayDimension(this.desc); 174 return countArrayDimension(this.desc);
168 } 175 }
169 176
@@ -171,6 +178,7 @@ public class TypeDescriptor implements Translatable {
171 if (!isArray()) { 178 if (!isArray()) {
172 throw new IllegalStateException("not an array"); 179 throw new IllegalStateException("not an array");
173 } 180 }
181
174 return new TypeDescriptor(this.desc.substring(getArrayDimension())); 182 return new TypeDescriptor(this.desc.substring(getArrayDimension()));
175 } 183 }
176 184
@@ -194,8 +202,10 @@ public class TypeDescriptor implements Translatable {
194 202
195 public TypeDescriptor remap(Function<String, String> remapper) { 203 public TypeDescriptor remap(Function<String, String> remapper) {
196 String desc = this.desc; 204 String desc = this.desc;
205
197 if (isType() || (isArray() && containsType())) { 206 if (isType() || (isArray() && containsType())) {
198 String replacedName = remapper.apply(this.getTypeEntry().getFullName()); 207 String replacedName = remapper.apply(this.getTypeEntry().getFullName());
208
199 if (replacedName != null) { 209 if (replacedName != null) {
200 if (this.isType()) { 210 if (this.isType()) {
201 desc = "L" + replacedName + ";"; 211 desc = "L" + replacedName + ";";
@@ -204,28 +214,31 @@ public class TypeDescriptor implements Translatable {
204 } 214 }
205 } 215 }
206 } 216 }
217
207 return new TypeDescriptor(desc); 218 return new TypeDescriptor(desc);
208 } 219 }
209 220
210 private static String getArrayPrefix(int dimension) { 221 private static String getArrayPrefix(int dimension) {
211 StringBuilder buf = new StringBuilder(); 222 StringBuilder buf = new StringBuilder();
223
212 for (int i = 0; i < dimension; i++) { 224 for (int i = 0; i < dimension; i++) {
213 buf.append("["); 225 buf.append("[");
214 } 226 }
227
215 return buf.toString(); 228 return buf.toString();
216 } 229 }
217 230
218 public int getSize() { 231 public int getSize() {
219 switch (desc.charAt(0)) { 232 switch (desc.charAt(0)) {
220 case 'J': 233 case 'J':
221 case 'D': 234 case 'D':
222 if (desc.length() == 1) { 235 if (desc.length() == 1) {
223 return 2; 236 return 2;
224 } else { 237 } else {
225 return 1;
226 }
227 default:
228 return 1; 238 return 1;
239 }
240 default:
241 return 1;
229 } 242 }
230 } 243 }
231 244
@@ -248,6 +261,7 @@ public class TypeDescriptor implements Translatable {
248 261
249 static { 262 static {
250 lookup = Maps.newTreeMap(); 263 lookup = Maps.newTreeMap();
264
251 for (Primitive val : values()) { 265 for (Primitive val : values()) {
252 lookup.put(val.getCode(), val); 266 lookup.put(val.getCode(), val);
253 } 267 }
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java
index ab5a422..cb2faf0 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.translation.representation.entry; 12package cuchaz.enigma.translation.representation.entry;
13 13
@@ -39,8 +39,7 @@ public class ClassDefEntry extends ClassEntry implements DefEntry<ClassEntry> {
39 this(parent, className, signature, access, superClass, interfaces, null); 39 this(parent, className, signature, access, superClass, interfaces, null);
40 } 40 }
41 41
42 public ClassDefEntry(ClassEntry parent, String className, Signature signature, AccessFlags access, @Nullable ClassEntry superClass, 42 public ClassDefEntry(ClassEntry parent, String className, Signature signature, AccessFlags access, @Nullable ClassEntry superClass, ClassEntry[] interfaces, String javadocs) {
43 ClassEntry[] interfaces, String javadocs) {
44 super(parent, className, javadocs); 43 super(parent, className, javadocs);
45 Preconditions.checkNotNull(signature, "Class signature cannot be null"); 44 Preconditions.checkNotNull(signature, "Class signature cannot be null");
46 Preconditions.checkNotNull(access, "Class access cannot be null"); 45 Preconditions.checkNotNull(access, "Class access cannot be null");
@@ -87,10 +86,7 @@ public class ClassDefEntry extends ClassEntry implements DefEntry<ClassEntry> {
87 ClassEntry translatedSuper = translator.translate(superClass); 86 ClassEntry translatedSuper = translator.translate(superClass);
88 ClassEntry[] translatedInterfaces = Arrays.stream(interfaces).map(translator::translate).toArray(ClassEntry[]::new); 87 ClassEntry[] translatedInterfaces = Arrays.stream(interfaces).map(translator::translate).toArray(ClassEntry[]::new);
89 String docs = mapping.javadoc(); 88 String docs = mapping.javadoc();
90 return TranslateResult.of( 89 return TranslateResult.of(mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new ClassDefEntry(parent, translatedName, translatedSignature, translatedAccess, translatedSuper, translatedInterfaces, docs));
91 mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED,
92 new ClassDefEntry(parent, translatedName, translatedSignature, translatedAccess, translatedSuper, translatedInterfaces, docs)
93 );
94 } 90 }
95 91
96 @Override 92 @Override
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java
index b0adb2c..be5f0b3 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java
@@ -1,16 +1,22 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.translation.representation.entry; 12package cuchaz.enigma.translation.representation.entry;
13 13
14import java.util.List;
15import java.util.Objects;
16
17import javax.annotation.Nonnull;
18import javax.annotation.Nullable;
19
14import cuchaz.enigma.source.RenamableTokenType; 20import cuchaz.enigma.source.RenamableTokenType;
15import cuchaz.enigma.translation.TranslateResult; 21import cuchaz.enigma.translation.TranslateResult;
16import cuchaz.enigma.translation.Translator; 22import cuchaz.enigma.translation.Translator;
@@ -19,11 +25,6 @@ import cuchaz.enigma.translation.mapping.IdentifierValidation;
19import cuchaz.enigma.translation.representation.TypeDescriptor; 25import cuchaz.enigma.translation.representation.TypeDescriptor;
20import cuchaz.enigma.utils.validation.ValidationContext; 26import cuchaz.enigma.utils.validation.ValidationContext;
21 27
22import javax.annotation.Nonnull;
23import javax.annotation.Nullable;
24import java.util.List;
25import java.util.Objects;
26
27public class ClassEntry extends ParentedEntry<ClassEntry> implements Comparable<ClassEntry> { 28public class ClassEntry extends ParentedEntry<ClassEntry> implements Comparable<ClassEntry> {
28 private final String fullName; 29 private final String fullName;
29 30
@@ -37,6 +38,7 @@ public class ClassEntry extends ParentedEntry<ClassEntry> implements Comparable<
37 38
38 public ClassEntry(@Nullable ClassEntry parent, String className, @Nullable String javadocs) { 39 public ClassEntry(@Nullable ClassEntry parent, String className, @Nullable String javadocs) {
39 super(parent, className, javadocs); 40 super(parent, className, javadocs);
41
40 if (parent != null) { 42 if (parent != null) {
41 fullName = parent.getFullName() + "$" + name; 43 fullName = parent.getFullName() + "$" + name;
42 } else { 44 } else {
@@ -61,9 +63,11 @@ public class ClassEntry extends ParentedEntry<ClassEntry> implements Comparable<
61 @Override 63 @Override
62 public String getSimpleName() { 64 public String getSimpleName() {
63 int packagePos = name.lastIndexOf('/'); 65 int packagePos = name.lastIndexOf('/');
66
64 if (packagePos > 0) { 67 if (packagePos > 0) {
65 return name.substring(packagePos + 1); 68 return name.substring(packagePos + 1);
66 } 69 }
70
67 return name; 71 return name;
68 } 72 }
69 73
@@ -77,6 +81,7 @@ public class ClassEntry extends ParentedEntry<ClassEntry> implements Comparable<
77 if (this.isInnerClass()) { 81 if (this.isInnerClass()) {
78 return this.parent.getSimpleName() + "$" + this.name; 82 return this.parent.getSimpleName() + "$" + this.name;
79 } 83 }
84
80 return this.getSimpleName(); 85 return this.getSimpleName();
81 } 86 }
82 87
@@ -89,10 +94,7 @@ public class ClassEntry extends ParentedEntry<ClassEntry> implements Comparable<
89 94
90 String translatedName = mapping.targetName() != null ? mapping.targetName() : name; 95 String translatedName = mapping.targetName() != null ? mapping.targetName() : name;
91 String docs = mapping.javadoc(); 96 String docs = mapping.javadoc();
92 return TranslateResult.of( 97 return TranslateResult.of(mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new ClassEntry(parent, translatedName, docs));
93 mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED,
94 new ClassEntry(parent, translatedName, docs)
95 );
96 } 98 }
97 99
98 @Override 100 @Override
@@ -160,12 +162,14 @@ public class ClassEntry extends ParentedEntry<ClassEntry> implements Comparable<
160 if (parent == null) { 162 if (parent == null) {
161 return this; 163 return this;
162 } 164 }
165
163 return parent.getOutermostClass(); 166 return parent.getOutermostClass();
164 } 167 }
165 168
166 public ClassEntry buildClassEntry(List<ClassEntry> classChain) { 169 public ClassEntry buildClassEntry(List<ClassEntry> classChain) {
167 assert (classChain.contains(this)); 170 assert (classChain.contains(this));
168 StringBuilder buf = new StringBuilder(); 171 StringBuilder buf = new StringBuilder();
172
169 for (ClassEntry chainEntry : classChain) { 173 for (ClassEntry chainEntry : classChain) {
170 if (buf.length() == 0) { 174 if (buf.length() == 0) {
171 buf.append(chainEntry.getFullName()); 175 buf.append(chainEntry.getFullName());
@@ -178,6 +182,7 @@ public class ClassEntry extends ParentedEntry<ClassEntry> implements Comparable<
178 break; 182 break;
179 } 183 }
180 } 184 }
185
181 return new ClassEntry(buf.toString()); 186 return new ClassEntry(buf.toString());
182 } 187 }
183 188
@@ -188,9 +193,11 @@ public class ClassEntry extends ParentedEntry<ClassEntry> implements Comparable<
188 193
189 public static String getParentPackage(String name) { 194 public static String getParentPackage(String name) {
190 int pos = name.lastIndexOf('/'); 195 int pos = name.lastIndexOf('/');
196
191 if (pos > 0) { 197 if (pos > 0) {
192 return name.substring(0, pos); 198 return name.substring(0, pos);
193 } 199 }
200
194 return null; 201 return null;
195 } 202 }
196 203
@@ -215,9 +222,11 @@ public class ClassEntry extends ParentedEntry<ClassEntry> implements Comparable<
215 } 222 }
216 223
217 int index = name.lastIndexOf('$'); 224 int index = name.lastIndexOf('$');
225
218 if (index >= 0) { 226 if (index >= 0) {
219 return new ClassEntry(name.substring(0, index)); 227 return new ClassEntry(name.substring(0, index));
220 } 228 }
229
221 return null; 230 return null;
222 } 231 }
223 232
@@ -227,18 +236,22 @@ public class ClassEntry extends ParentedEntry<ClassEntry> implements Comparable<
227 } 236 }
228 237
229 int innerClassPos = name.lastIndexOf('$'); 238 int innerClassPos = name.lastIndexOf('$');
239
230 if (innerClassPos > 0) { 240 if (innerClassPos > 0) {
231 return name.substring(innerClassPos + 1); 241 return name.substring(innerClassPos + 1);
232 } 242 }
243
233 return name; 244 return name;
234 } 245 }
235 246
236 @Override 247 @Override
237 public String getSourceRemapName() { 248 public String getSourceRemapName() {
238 ClassEntry outerClass = getOuterClass(); 249 ClassEntry outerClass = getOuterClass();
250
239 if (outerClass != null) { 251 if (outerClass != null) {
240 return outerClass.getSourceRemapName() + "." + name; 252 return outerClass.getSourceRemapName() + "." + name;
241 } 253 }
254
242 return getSimpleName(); 255 return getSimpleName();
243 } 256 }
244 257
@@ -246,9 +259,11 @@ public class ClassEntry extends ParentedEntry<ClassEntry> implements Comparable<
246 public int compareTo(ClassEntry entry) { 259 public int compareTo(ClassEntry entry) {
247 String fullName = getFullName(); 260 String fullName = getFullName();
248 String otherFullName = entry.getFullName(); 261 String otherFullName = entry.getFullName();
262
249 if (fullName.length() != otherFullName.length()) { 263 if (fullName.length() != otherFullName.length()) {
250 return fullName.length() - otherFullName.length(); 264 return fullName.length() - otherFullName.length();
251 } 265 }
266
252 return fullName.compareTo(otherFullName); 267 return fullName.compareTo(otherFullName);
253 } 268 }
254} 269}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java
index 956f32c..9615ca8 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.translation.representation.entry; 12package cuchaz.enigma.translation.representation.entry;
13 13
@@ -111,34 +111,42 @@ public interface Entry<P extends Entry<?>> extends Translatable {
111 default ClassEntry getContainingClass() { 111 default ClassEntry getContainingClass() {
112 ClassEntry last = null; 112 ClassEntry last = null;
113 Entry<?> current = this; 113 Entry<?> current = this;
114
114 while (current != null) { 115 while (current != null) {
115 if (current instanceof ClassEntry) { 116 if (current instanceof ClassEntry) {
116 last = (ClassEntry) current; 117 last = (ClassEntry) current;
117 break; 118 break;
118 } 119 }
120
119 current = current.getParent(); 121 current = current.getParent();
120 } 122 }
123
121 return Objects.requireNonNull(last, () -> String.format("%s has no containing class?", this)); 124 return Objects.requireNonNull(last, () -> String.format("%s has no containing class?", this));
122 } 125 }
123 126
124 default ClassEntry getTopLevelClass() { 127 default ClassEntry getTopLevelClass() {
125 ClassEntry last = null; 128 ClassEntry last = null;
126 Entry<?> current = this; 129 Entry<?> current = this;
130
127 while (current != null) { 131 while (current != null) {
128 if (current instanceof ClassEntry) { 132 if (current instanceof ClassEntry) {
129 last = (ClassEntry) current; 133 last = (ClassEntry) current;
130 } 134 }
135
131 current = current.getParent(); 136 current = current.getParent();
132 } 137 }
138
133 return Objects.requireNonNull(last, () -> String.format("%s has no top level class?", this)); 139 return Objects.requireNonNull(last, () -> String.format("%s has no top level class?", this));
134 } 140 }
135 141
136 default List<Entry<?>> getAncestry() { 142 default List<Entry<?>> getAncestry() {
137 P parent = getParent(); 143 P parent = getParent();
138 List<Entry<?>> entries = new ArrayList<>(); 144 List<Entry<?>> entries = new ArrayList<>();
145
139 if (parent != null) { 146 if (parent != null) {
140 entries.addAll(parent.getAncestry()); 147 entries.addAll(parent.getAncestry());
141 } 148 }
149
142 entries.add(this); 150 entries.add(this);
143 return entries; 151 return entries;
144 } 152 }
@@ -147,12 +155,15 @@ public interface Entry<P extends Entry<?>> extends Translatable {
147 @SuppressWarnings("unchecked") 155 @SuppressWarnings("unchecked")
148 default <E extends Entry<?>> E findAncestor(Class<E> type) { 156 default <E extends Entry<?>> E findAncestor(Class<E> type) {
149 List<Entry<?>> ancestry = getAncestry(); 157 List<Entry<?>> ancestry = getAncestry();
158
150 for (int i = ancestry.size() - 1; i >= 0; i--) { 159 for (int i = ancestry.size() - 1; i >= 0; i--) {
151 Entry<?> ancestor = ancestry.get(i); 160 Entry<?> ancestor = ancestry.get(i);
161
152 if (type.isAssignableFrom(ancestor.getClass())) { 162 if (type.isAssignableFrom(ancestor.getClass())) {
153 return (E) ancestor; 163 return (E) ancestor;
154 } 164 }
155 } 165 }
166
156 return null; 167 return null;
157 } 168 }
158 169
@@ -167,6 +178,7 @@ public interface Entry<P extends Entry<?>> extends Translatable {
167 } 178 }
168 179
169 P parent = getParent(); 180 P parent = getParent();
181
170 if (parent == null) { 182 if (parent == null) {
171 return this; 183 return this;
172 } 184 }
@@ -184,6 +196,7 @@ public interface Entry<P extends Entry<?>> extends Translatable {
184 if (parentType.equals(getParentType())) { 196 if (parentType.equals(getParentType())) {
185 return (Entry<C>) this; 197 return (Entry<C>) this;
186 } 198 }
199
187 return null; 200 return null;
188 } 201 }
189} 202}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java
index 0efb6a9..492d72e 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java
@@ -1,16 +1,18 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.translation.representation.entry; 12package cuchaz.enigma.translation.representation.entry;
13 13
14import javax.annotation.Nonnull;
15
14import com.google.common.base.Preconditions; 16import com.google.common.base.Preconditions;
15 17
16import cuchaz.enigma.source.RenamableTokenType; 18import cuchaz.enigma.source.RenamableTokenType;
@@ -21,8 +23,6 @@ import cuchaz.enigma.translation.representation.AccessFlags;
21import cuchaz.enigma.translation.representation.Signature; 23import cuchaz.enigma.translation.representation.Signature;
22import cuchaz.enigma.translation.representation.TypeDescriptor; 24import cuchaz.enigma.translation.representation.TypeDescriptor;
23 25
24import javax.annotation.Nonnull;
25
26public class FieldDefEntry extends FieldEntry implements DefEntry<ClassEntry> { 26public class FieldDefEntry extends FieldEntry implements DefEntry<ClassEntry> {
27 private final AccessFlags access; 27 private final AccessFlags access;
28 private final Signature signature; 28 private final Signature signature;
@@ -59,13 +59,9 @@ public class FieldDefEntry extends FieldEntry implements DefEntry<ClassEntry> {
59 String translatedName = mapping.targetName() != null ? mapping.targetName() : name; 59 String translatedName = mapping.targetName() != null ? mapping.targetName() : name;
60 AccessFlags translatedAccess = mapping.accessModifier().transform(access); 60 AccessFlags translatedAccess = mapping.accessModifier().transform(access);
61 String docs = mapping.javadoc(); 61 String docs = mapping.javadoc();
62 return TranslateResult.of( 62 return TranslateResult.of(mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new FieldDefEntry(parent, translatedName, translatedDesc, translatedSignature, translatedAccess, docs));
63 mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED,
64 new FieldDefEntry(parent, translatedName, translatedDesc, translatedSignature, translatedAccess, docs)
65 );
66 } 63 }
67 64
68
69 @Override 65 @Override
70 public FieldDefEntry withName(String name) { 66 public FieldDefEntry withName(String name) {
71 return new FieldDefEntry(parent, name, desc, signature, access, javadocs); 67 return new FieldDefEntry(parent, name, desc, signature, access, javadocs);
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java
index db94011..c1592a4 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.translation.representation.entry; 12package cuchaz.enigma.translation.representation.entry;
13 13
@@ -66,10 +66,7 @@ public class FieldEntry extends ParentedEntry<ClassEntry> implements Comparable<
66 protected TranslateResult<FieldEntry> extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) { 66 protected TranslateResult<FieldEntry> extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) {
67 String translatedName = mapping.targetName() != null ? mapping.targetName() : name; 67 String translatedName = mapping.targetName() != null ? mapping.targetName() : name;
68 String docs = mapping.javadoc(); 68 String docs = mapping.javadoc();
69 return TranslateResult.of( 69 return TranslateResult.of(mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new FieldEntry(parent, translatedName, translator.translate(desc), docs));
70 mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED,
71 new FieldEntry(parent, translatedName, translator.translate(desc), docs)
72 );
73 } 70 }
74 71
75 @Override 72 @Override
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java
index c151de4..ac36a48 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java
@@ -34,10 +34,7 @@ public class LocalVariableDefEntry extends LocalVariableEntry {
34 TypeDescriptor translatedDesc = translator.translate(desc); 34 TypeDescriptor translatedDesc = translator.translate(desc);
35 String translatedName = mapping.targetName() != null ? mapping.targetName() : name; 35 String translatedName = mapping.targetName() != null ? mapping.targetName() : name;
36 String javadoc = mapping.javadoc(); 36 String javadoc = mapping.javadoc();
37 return TranslateResult.of( 37 return TranslateResult.of(mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new LocalVariableDefEntry(parent, index, translatedName, parameter, translatedDesc, javadoc));
38 mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED,
39 new LocalVariableDefEntry(parent, index, translatedName, parameter, translatedDesc, javadoc)
40 );
41 } 38 }
42 39
43 @Override 40 @Override
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java
index 1cf1a83..d22188b 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java
@@ -17,7 +17,6 @@ import cuchaz.enigma.translation.mapping.EntryMapping;
17 * 19/10/2016 17 * 19/10/2016
18 */ 18 */
19public class LocalVariableEntry extends ParentedEntry<MethodEntry> implements Comparable<LocalVariableEntry> { 19public class LocalVariableEntry extends ParentedEntry<MethodEntry> implements Comparable<LocalVariableEntry> {
20
21 protected final int index; 20 protected final int index;
22 protected final boolean parameter; 21 protected final boolean parameter;
23 22
@@ -53,10 +52,7 @@ public class LocalVariableEntry extends ParentedEntry<MethodEntry> implements Co
53 protected TranslateResult<LocalVariableEntry> extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) { 52 protected TranslateResult<LocalVariableEntry> extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) {
54 String translatedName = mapping.targetName() != null ? mapping.targetName() : name; 53 String translatedName = mapping.targetName() != null ? mapping.targetName() : name;
55 String javadoc = mapping.javadoc(); 54 String javadoc = mapping.javadoc();
56 return TranslateResult.of( 55 return TranslateResult.of(mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new LocalVariableEntry(parent, index, translatedName, parameter, javadoc));
57 mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED,
58 new LocalVariableEntry(parent, index, translatedName, parameter, javadoc)
59 );
60 } 56 }
61 57
62 @Override 58 @Override
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java
index 30ef706..c6a4ab7 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.translation.representation.entry; 12package cuchaz.enigma.translation.representation.entry;
13 13
@@ -59,10 +59,7 @@ public class MethodDefEntry extends MethodEntry implements DefEntry<ClassEntry>
59 String translatedName = mapping.targetName() != null ? mapping.targetName() : name; 59 String translatedName = mapping.targetName() != null ? mapping.targetName() : name;
60 AccessFlags translatedAccess = mapping.accessModifier().transform(access); 60 AccessFlags translatedAccess = mapping.accessModifier().transform(access);
61 String docs = mapping.javadoc(); 61 String docs = mapping.javadoc();
62 return TranslateResult.of( 62 return TranslateResult.of(mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new MethodDefEntry(parent, translatedName, translatedDesc, translatedSignature, translatedAccess, docs));
63 mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED,
64 new MethodDefEntry(parent, translatedName, translatedDesc, translatedSignature, translatedAccess, docs)
65 );
66 } 63 }
67 64
68 @Override 65 @Override
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java
index ab9c2d1..6fc3f0a 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.translation.representation.entry; 12package cuchaz.enigma.translation.representation.entry;
13 13
@@ -24,7 +24,6 @@ import cuchaz.enigma.translation.mapping.EntryMapping;
24import cuchaz.enigma.translation.representation.MethodDescriptor; 24import cuchaz.enigma.translation.representation.MethodDescriptor;
25 25
26public class MethodEntry extends ParentedEntry<ClassEntry> implements Comparable<MethodEntry> { 26public class MethodEntry extends ParentedEntry<ClassEntry> implements Comparable<MethodEntry> {
27
28 protected final MethodDescriptor descriptor; 27 protected final MethodDescriptor descriptor;
29 28
30 public MethodEntry(ClassEntry parent, String name, MethodDescriptor descriptor) { 29 public MethodEntry(ClassEntry parent, String name, MethodDescriptor descriptor) {
@@ -61,10 +60,7 @@ public class MethodEntry extends ParentedEntry<ClassEntry> implements Comparable
61 protected TranslateResult<? extends MethodEntry> extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) { 60 protected TranslateResult<? extends MethodEntry> extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) {
62 String translatedName = mapping.targetName() != null ? mapping.targetName() : name; 61 String translatedName = mapping.targetName() != null ? mapping.targetName() : name;
63 String docs = mapping.javadoc(); 62 String docs = mapping.javadoc();
64 return TranslateResult.of( 63 return TranslateResult.of(mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new MethodEntry(parent, translatedName, translator.translate(descriptor), docs));
65 mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED,
66 new MethodEntry(parent, translatedName, translator.translate(descriptor), docs)
67 );
68 } 64 }
69 65
70 @Override 66 @Override
@@ -97,6 +93,7 @@ public class MethodEntry extends ParentedEntry<ClassEntry> implements Comparable
97 MethodEntry methodEntry = (MethodEntry) entry; 93 MethodEntry methodEntry = (MethodEntry) entry;
98 return methodEntry.parent.equals(parent) && methodEntry.descriptor.canConflictWith(descriptor); 94 return methodEntry.parent.equals(parent) && methodEntry.descriptor.canConflictWith(descriptor);
99 } 95 }
96
100 return false; 97 return false;
101 } 98 }
102 99
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java
index 267bc11..ff5ffa3 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.translation.representation.entry; 12package cuchaz.enigma.translation.representation.entry;
13 13
@@ -80,9 +80,11 @@ public abstract class ParentedEntry<P extends Entry<?>> implements Entry<P> {
80 public TranslateResult<? extends ParentedEntry<P>> extendedTranslate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) { 80 public TranslateResult<? extends ParentedEntry<P>> extendedTranslate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
81 P parent = getParent(); 81 P parent = getParent();
82 EntryMapping mapping = resolveMapping(resolver, mappings); 82 EntryMapping mapping = resolveMapping(resolver, mappings);
83
83 if (parent == null) { 84 if (parent == null) {
84 return this.extendedTranslate(translator, mapping); 85 return this.extendedTranslate(translator, mapping);
85 } 86 }
87
86 P translatedParent = translator.translate(parent); 88 P translatedParent = translator.translate(parent);
87 return this.withParent(translatedParent).extendedTranslate(translator, mapping); 89 return this.withParent(translatedParent).extendedTranslate(translator, mapping);
88 } 90 }
@@ -90,10 +92,12 @@ public abstract class ParentedEntry<P extends Entry<?>> implements Entry<P> {
90 private EntryMapping resolveMapping(EntryResolver resolver, EntryMap<EntryMapping> mappings) { 92 private EntryMapping resolveMapping(EntryResolver resolver, EntryMap<EntryMapping> mappings) {
91 for (ParentedEntry<P> entry : resolver.resolveEntry(this, ResolutionStrategy.RESOLVE_ROOT)) { 93 for (ParentedEntry<P> entry : resolver.resolveEntry(this, ResolutionStrategy.RESOLVE_ROOT)) {
92 EntryMapping mapping = mappings.get(entry); 94 EntryMapping mapping = mappings.get(entry);
95
93 if (mapping != null) { 96 if (mapping != null) {
94 return mapping; 97 return mapping;
95 } 98 }
96 } 99 }
100
97 return EntryMapping.DEFAULT; 101 return EntryMapping.DEFAULT;
98 } 102 }
99} 103}
diff --git a/enigma/src/main/java/cuchaz/enigma/utils/AsmUtil.java b/enigma/src/main/java/cuchaz/enigma/utils/AsmUtil.java
index 7d34b02..6732a59 100644
--- a/enigma/src/main/java/cuchaz/enigma/utils/AsmUtil.java
+++ b/enigma/src/main/java/cuchaz/enigma/utils/AsmUtil.java
@@ -5,16 +5,16 @@ import org.objectweb.asm.ClassWriter;
5import org.objectweb.asm.tree.ClassNode; 5import org.objectweb.asm.tree.ClassNode;
6 6
7public class AsmUtil { 7public class AsmUtil {
8 public static byte[] nodeToBytes(ClassNode node) { 8 public static byte[] nodeToBytes(ClassNode node) {
9 ClassWriter w = new ClassWriter(0); 9 ClassWriter w = new ClassWriter(0);
10 node.accept(w); 10 node.accept(w);
11 return w.toByteArray(); 11 return w.toByteArray();
12 } 12 }
13 13
14 public static ClassNode bytesToNode(byte[] bytes) { 14 public static ClassNode bytesToNode(byte[] bytes) {
15 ClassReader r = new ClassReader(bytes); 15 ClassReader r = new ClassReader(bytes);
16 ClassNode node = new ClassNode(); 16 ClassNode node = new ClassNode();
17 r.accept(node, 0); 17 r.accept(node, 0);
18 return node; 18 return node;
19 } 19 }
20} 20}
diff --git a/enigma/src/main/java/cuchaz/enigma/utils/I18n.java b/enigma/src/main/java/cuchaz/enigma/utils/I18n.java
index 26e5b27..9551202 100644
--- a/enigma/src/main/java/cuchaz/enigma/utils/I18n.java
+++ b/enigma/src/main/java/cuchaz/enigma/utils/I18n.java
@@ -5,7 +5,11 @@ import java.io.IOException;
5import java.io.InputStream; 5import java.io.InputStream;
6import java.io.InputStreamReader; 6import java.io.InputStreamReader;
7import java.nio.charset.StandardCharsets; 7import java.nio.charset.StandardCharsets;
8import java.util.*; 8import java.util.ArrayList;
9import java.util.Arrays;
10import java.util.Collections;
11import java.util.Map;
12import java.util.Objects;
9import java.util.stream.Collectors; 13import java.util.stream.Collectors;
10import java.util.stream.Stream; 14import java.util.stream.Stream;
11 15
@@ -33,12 +37,16 @@ public class I18n {
33 } catch (IOException e) { 37 } catch (IOException e) {
34 e.printStackTrace(); 38 e.printStackTrace();
35 } 39 }
40
36 return Collections.emptyMap(); 41 return Collections.emptyMap();
37 } 42 }
38 43
39 public static String translateOrNull(String key) { 44 public static String translateOrNull(String key) {
40 String value = translations.get(key); 45 String value = translations.get(key);
41 if (value != null) return value; 46
47 if (value != null) {
48 return value;
49 }
42 50
43 return defaultTranslations.get(key); 51 return defaultTranslations.get(key);
44 } 52 }
@@ -50,6 +58,7 @@ public class I18n {
50 58
51 public static String translateOrEmpty(String key, Object... args) { 59 public static String translateOrEmpty(String key, Object... args) {
52 String text = translateOrNull(key); 60 String text = translateOrNull(key);
61
53 if (text != null) { 62 if (text != null) {
54 return String.format(text, args); 63 return String.format(text, args);
55 } else { 64 } else {
@@ -59,6 +68,7 @@ public class I18n {
59 68
60 public static String translateFormatted(String key, Object... args) { 69 public static String translateFormatted(String key, Object... args) {
61 String text = translateOrNull(key); 70 String text = translateOrNull(key);
71
62 if (text != null) { 72 if (text != null) {
63 return String.format(text, args); 73 return String.format(text, args);
64 } else if (args.length == 0) { 74 } else if (args.length == 0) {
@@ -84,6 +94,7 @@ public class I18n {
84 Stream<ResourceInfo> dirStream = resources.stream(); 94 Stream<ResourceInfo> dirStream = resources.stream();
85 dirStream.forEach(context -> { 95 dirStream.forEach(context -> {
86 String file = context.getResourceName(); 96 String file = context.getResourceName();
97
87 if (file.startsWith("lang/") && file.endsWith(".json")) { 98 if (file.startsWith("lang/") && file.endsWith(".json")) {
88 String fileName = file.substring(5, file.length() - 5); 99 String fileName = file.substring(5, file.length() - 5);
89 list.add(fileName); 100 list.add(fileName);
@@ -93,6 +104,7 @@ public class I18n {
93 } catch (IOException e) { 104 } catch (IOException e) {
94 e.printStackTrace(); 105 e.printStackTrace();
95 } 106 }
107
96 return list; 108 return list;
97 } 109 }
98 110
diff --git a/enigma/src/main/java/cuchaz/enigma/utils/Os.java b/enigma/src/main/java/cuchaz/enigma/utils/Os.java
index b493c04..eaa9360 100644
--- a/enigma/src/main/java/cuchaz/enigma/utils/Os.java
+++ b/enigma/src/main/java/cuchaz/enigma/utils/Os.java
@@ -14,12 +14,12 @@ public enum Os {
14 public static Os getOs() { 14 public static Os getOs() {
15 if (os == null) { 15 if (os == null) {
16 String osName = System.getProperty("os.name").toLowerCase(Locale.ROOT); 16 String osName = System.getProperty("os.name").toLowerCase(Locale.ROOT);
17
17 if (osName.contains("mac") || osName.contains("darwin")) { 18 if (osName.contains("mac") || osName.contains("darwin")) {
18 os = MAC; 19 os = MAC;
19 } else if (osName.contains("win")) { 20 } else if (osName.contains("win")) {
20 os = WINDOWS; 21 os = WINDOWS;
21 } else if (osName.contains("nix") || osName.contains("nux") 22 } else if (osName.contains("nix") || osName.contains("nux") || osName.contains("aix")) {
22 || osName.contains("aix")) {
23 os = LINUX; 23 os = LINUX;
24 } else if (osName.contains("sunos")) { 24 } else if (osName.contains("sunos")) {
25 os = SOLARIS; 25 os = SOLARIS;
@@ -27,7 +27,7 @@ public enum Os {
27 os = OTHER; 27 os = OTHER;
28 } 28 }
29 } 29 }
30
30 return os; 31 return os;
31 } 32 }
32 33}
33} \ No newline at end of file
diff --git a/enigma/src/main/java/cuchaz/enigma/utils/Pair.java b/enigma/src/main/java/cuchaz/enigma/utils/Pair.java
index bf02cef..10752ac 100644
--- a/enigma/src/main/java/cuchaz/enigma/utils/Pair.java
+++ b/enigma/src/main/java/cuchaz/enigma/utils/Pair.java
@@ -3,24 +3,21 @@ package cuchaz.enigma.utils;
3import java.util.Objects; 3import java.util.Objects;
4 4
5public class Pair<A, B> { 5public class Pair<A, B> {
6 public final A a; 6 public final A a;
7 public final B b; 7 public final B b;
8 8
9 public Pair(A a, B b) { 9 public Pair(A a, B b) {
10 this.a = a; 10 this.a = a;
11 this.b = b; 11 this.b = b;
12 } 12 }
13 13
14 @Override 14 @Override
15 public int hashCode() { 15 public int hashCode() {
16 return Objects.hashCode(a) * 31 + 16 return Objects.hashCode(a) * 31 + Objects.hashCode(b);
17 Objects.hashCode(b); 17 }
18 }
19 18
20 @Override 19 @Override
21 public boolean equals(Object o) { 20 public boolean equals(Object o) {
22 return o instanceof Pair && 21 return o instanceof Pair && Objects.equals(a, ((Pair<?, ?>) o).a) && Objects.equals(b, ((Pair<?, ?>) o).b);
23 Objects.equals(a, ((Pair<?, ?>) o).a) && 22 }
24 Objects.equals(b, ((Pair<?, ?>) o).b);
25 }
26} 23}
diff --git a/enigma/src/main/java/cuchaz/enigma/utils/Result.java b/enigma/src/main/java/cuchaz/enigma/utils/Result.java
index dcaabd5..354418a 100644
--- a/enigma/src/main/java/cuchaz/enigma/utils/Result.java
+++ b/enigma/src/main/java/cuchaz/enigma/utils/Result.java
@@ -5,7 +5,6 @@ import java.util.Optional;
5import java.util.function.Function; 5import java.util.function.Function;
6 6
7public final class Result<T, E> { 7public final class Result<T, E> {
8
9 private final T ok; 8 private final T ok;
10 private final E err; 9 private final E err;
11 10
@@ -39,56 +38,85 @@ public final class Result<T, E> {
39 } 38 }
40 39
41 public T unwrap() { 40 public T unwrap() {
42 if (this.isOk()) return this.ok; 41 if (this.isOk()) {
42 return this.ok;
43 }
44
43 throw new IllegalStateException(String.format("Called Result.unwrap on an Err value: %s", this.err)); 45 throw new IllegalStateException(String.format("Called Result.unwrap on an Err value: %s", this.err));
44 } 46 }
45 47
46 public E unwrapErr() { 48 public E unwrapErr() {
47 if (this.isErr()) return this.err; 49 if (this.isErr()) {
50 return this.err;
51 }
52
48 throw new IllegalStateException(String.format("Called Result.unwrapErr on an Ok value: %s", this.ok)); 53 throw new IllegalStateException(String.format("Called Result.unwrapErr on an Ok value: %s", this.ok));
49 } 54 }
50 55
51 public T unwrapOr(T fallback) { 56 public T unwrapOr(T fallback) {
52 if (this.isOk()) return this.ok; 57 if (this.isOk()) {
58 return this.ok;
59 }
60
53 return fallback; 61 return fallback;
54 } 62 }
55 63
56 public T unwrapOrElse(Function<E, T> fn) { 64 public T unwrapOrElse(Function<E, T> fn) {
57 if (this.isOk()) return this.ok; 65 if (this.isOk()) {
66 return this.ok;
67 }
68
58 return fn.apply(this.err); 69 return fn.apply(this.err);
59 } 70 }
60 71
61 @SuppressWarnings("unchecked") 72 @SuppressWarnings("unchecked")
62 public <U> Result<U, E> map(Function<T, U> op) { 73 public <U> Result<U, E> map(Function<T, U> op) {
63 if (!this.isOk()) return (Result<U, E>) this; 74 if (!this.isOk()) {
75 return (Result<U, E>) this;
76 }
77
64 return Result.ok(op.apply(this.ok)); 78 return Result.ok(op.apply(this.ok));
65 } 79 }
66 80
67 @SuppressWarnings("unchecked") 81 @SuppressWarnings("unchecked")
68 public <F> Result<T, F> mapErr(Function<E, F> op) { 82 public <F> Result<T, F> mapErr(Function<E, F> op) {
69 if (!this.isErr()) return (Result<T, F>) this; 83 if (!this.isErr()) {
84 return (Result<T, F>) this;
85 }
86
70 return Result.err(op.apply(this.err)); 87 return Result.err(op.apply(this.err));
71 } 88 }
72 89
73 @SuppressWarnings("unchecked") 90 @SuppressWarnings("unchecked")
74 public <U> Result<U, E> and(Result<U, E> next) { 91 public <U> Result<U, E> and(Result<U, E> next) {
75 if (this.isErr()) return (Result<U, E>) this; 92 if (this.isErr()) {
93 return (Result<U, E>) this;
94 }
95
76 return next; 96 return next;
77 } 97 }
78 98
79 @SuppressWarnings("unchecked") 99 @SuppressWarnings("unchecked")
80 public <U> Result<U, E> andThen(Function<T, Result<U, E>> op) { 100 public <U> Result<U, E> andThen(Function<T, Result<U, E>> op) {
81 if (this.isErr()) return (Result<U, E>) this; 101 if (this.isErr()) {
102 return (Result<U, E>) this;
103 }
104
82 return op.apply(this.ok); 105 return op.apply(this.ok);
83 } 106 }
84 107
85 @Override 108 @Override
86 public boolean equals(Object o) { 109 public boolean equals(Object o) {
87 if (this == o) return true; 110 if (this == o) {
88 if (o == null || getClass() != o.getClass()) return false; 111 return true;
112 }
113
114 if (o == null || getClass() != o.getClass()) {
115 return false;
116 }
117
89 Result<?, ?> result = (Result<?, ?>) o; 118 Result<?, ?> result = (Result<?, ?>) o;
90 return Objects.equals(ok, result.ok) && 119 return Objects.equals(ok, result.ok) && Objects.equals(err, result.err);
91 Objects.equals(err, result.err);
92 } 120 }
93 121
94 @Override 122 @Override
@@ -104,5 +132,4 @@ public final class Result<T, E> {
104 return String.format("Result.Err(%s)", this.err); 132 return String.format("Result.Err(%s)", this.err);
105 } 133 }
106 } 134 }
107
108} 135}
diff --git a/enigma/src/main/java/cuchaz/enigma/utils/TristateChange.java b/enigma/src/main/java/cuchaz/enigma/utils/TristateChange.java
index 864154c..2b4ed37 100644
--- a/enigma/src/main/java/cuchaz/enigma/utils/TristateChange.java
+++ b/enigma/src/main/java/cuchaz/enigma/utils/TristateChange.java
@@ -3,7 +3,6 @@ package cuchaz.enigma.utils;
3import java.util.Objects; 3import java.util.Objects;
4 4
5public final class TristateChange<T> { 5public final class TristateChange<T> {
6
7 private static final TristateChange<?> UNCHANGED = new TristateChange<>(Type.UNCHANGED, null); 6 private static final TristateChange<?> UNCHANGED = new TristateChange<>(Type.UNCHANGED, null);
8 private static final TristateChange<?> RESET = new TristateChange<>(Type.RESET, null); 7 private static final TristateChange<?> RESET = new TristateChange<>(Type.RESET, null);
9 8
@@ -46,17 +45,25 @@ public final class TristateChange<T> {
46 } 45 }
47 46
48 public T getNewValue() { 47 public T getNewValue() {
49 if (this.type != Type.SET) throw new IllegalStateException(String.format("No concrete value in %s", this)); 48 if (this.type != Type.SET) {
49 throw new IllegalStateException(String.format("No concrete value in %s", this));
50 }
51
50 return this.val; 52 return this.val;
51 } 53 }
52 54
53 @Override 55 @Override
54 public boolean equals(Object o) { 56 public boolean equals(Object o) {
55 if (this == o) return true; 57 if (this == o) {
56 if (o == null || getClass() != o.getClass()) return false; 58 return true;
59 }
60
61 if (o == null || getClass() != o.getClass()) {
62 return false;
63 }
64
57 TristateChange<?> that = (TristateChange<?>) o; 65 TristateChange<?> that = (TristateChange<?>) o;
58 return type == that.type && 66 return type == that.type && Objects.equals(val, that.val);
59 Objects.equals(val, that.val);
60 } 67 }
61 68
62 @Override 69 @Override
@@ -74,5 +81,4 @@ public final class TristateChange<T> {
74 RESET, 81 RESET,
75 SET, 82 SET,
76 } 83 }
77
78} 84}
diff --git a/enigma/src/main/java/cuchaz/enigma/utils/Utils.java b/enigma/src/main/java/cuchaz/enigma/utils/Utils.java
index ad4e936..081c941 100644
--- a/enigma/src/main/java/cuchaz/enigma/utils/Utils.java
+++ b/enigma/src/main/java/cuchaz/enigma/utils/Utils.java
@@ -1,18 +1,16 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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 * <p> 7*
8 * Contributors: 8* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.utils; 12package cuchaz.enigma.utils;
13 13
14import com.google.common.io.CharStreams;
15
16import java.io.IOException; 14import java.io.IOException;
17import java.io.InputStream; 15import java.io.InputStream;
18import java.io.InputStreamReader; 16import java.io.InputStreamReader;
@@ -30,70 +28,80 @@ import java.util.function.Supplier;
30import java.util.zip.ZipEntry; 28import java.util.zip.ZipEntry;
31import java.util.zip.ZipFile; 29import java.util.zip.ZipFile;
32 30
31import com.google.common.io.CharStreams;
32
33public class Utils { 33public class Utils {
34 public static String readStreamToString(InputStream in) throws IOException { 34 public static String readStreamToString(InputStream in) throws IOException {
35 return CharStreams.toString(new InputStreamReader(in, StandardCharsets.UTF_8)); 35 return CharStreams.toString(new InputStreamReader(in, StandardCharsets.UTF_8));
36 } 36 }
37 37
38 public static String readResourceToString(String path) throws IOException { 38 public static String readResourceToString(String path) throws IOException {
39 InputStream in = Utils.class.getResourceAsStream(path); 39 InputStream in = Utils.class.getResourceAsStream(path);
40 if (in == null) { 40
41 throw new IllegalArgumentException("Resource not found! " + path); 41 if (in == null) {
42 } 42 throw new IllegalArgumentException("Resource not found! " + path);
43 return readStreamToString(in); 43 }
44 } 44
45 45 return readStreamToString(in);
46 public static void delete(Path path) throws IOException { 46 }
47 if (Files.exists(path)) { 47
48 for (Path p : Files.walk(path).sorted(Comparator.reverseOrder()).toList()) { 48 public static void delete(Path path) throws IOException {
49 Files.delete(p); 49 if (Files.exists(path)) {
50 } 50 for (Path p : Files.walk(path).sorted(Comparator.reverseOrder()).toList()) {
51 } 51 Files.delete(p);
52 } 52 }
53 53 }
54 public static byte[] zipSha1(Path path) throws IOException { 54 }
55 MessageDigest digest; 55
56 try { 56 public static byte[] zipSha1(Path path) throws IOException {
57 digest = MessageDigest.getInstance("SHA-1"); 57 MessageDigest digest;
58 } catch (NoSuchAlgorithmException e) { 58
59 // Algorithm guaranteed to be supported 59 try {
60 throw new RuntimeException(e); 60 digest = MessageDigest.getInstance("SHA-1");
61 } 61 } catch (NoSuchAlgorithmException e) {
62 try (ZipFile zip = new ZipFile(path.toFile())) { 62 // Algorithm guaranteed to be supported
63 List<? extends ZipEntry> entries = Collections.list(zip.entries()); 63 throw new RuntimeException(e);
64 // only compare classes (some implementations may not generate directory entries) 64 }
65 entries.removeIf(entry -> !entry.getName().toLowerCase(Locale.ROOT).endsWith(".class")); 65
66 // different implementations may add zip entries in a different order 66 try (ZipFile zip = new ZipFile(path.toFile())) {
67 entries.sort(Comparator.comparing(ZipEntry::getName)); 67 List<? extends ZipEntry> entries = Collections.list(zip.entries());
68 byte[] buffer = new byte[8192]; 68 // only compare classes (some implementations may not generate directory entries)
69 for (ZipEntry entry : entries) { 69 entries.removeIf(entry -> !entry.getName().toLowerCase(Locale.ROOT).endsWith(".class"));
70 digest.update(entry.getName().getBytes(StandardCharsets.UTF_8)); 70 // different implementations may add zip entries in a different order
71 try (InputStream in = zip.getInputStream(entry)) { 71 entries.sort(Comparator.comparing(ZipEntry::getName));
72 int n; 72 byte[] buffer = new byte[8192];
73 while ((n = in.read(buffer)) != -1) { 73
74 digest.update(buffer, 0, n); 74 for (ZipEntry entry : entries) {
75 } 75 digest.update(entry.getName().getBytes(StandardCharsets.UTF_8));
76 } 76
77 } 77 try (InputStream in = zip.getInputStream(entry)) {
78 } 78 int n;
79 return digest.digest(); 79
80 } 80 while ((n = in.read(buffer)) != -1) {
81 81 digest.update(buffer, 0, n);
82 public static void withLock(Lock l, Runnable op) { 82 }
83 try { 83 }
84 l.lock(); 84 }
85 op.run(); 85 }
86 } finally { 86
87 l.unlock(); 87 return digest.digest();
88 } 88 }
89 } 89
90 90 public static void withLock(Lock l, Runnable op) {
91 public static <R> R withLock(Lock l, Supplier<R> op) { 91 try {
92 try { 92 l.lock();
93 l.lock(); 93 op.run();
94 return op.get(); 94 } finally {
95 } finally { 95 l.unlock();
96 l.unlock(); 96 }
97 } 97 }
98 } 98
99 public static <R> R withLock(Lock l, Supplier<R> op) {
100 try {
101 l.lock();
102 return op.get();
103 } finally {
104 l.unlock();
105 }
106 }
99} 107}
diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java
index 6bcdbde..b7e67f2 100644
--- a/enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java
+++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java
@@ -3,7 +3,6 @@ package cuchaz.enigma.utils.validation;
3import cuchaz.enigma.utils.I18n; 3import cuchaz.enigma.utils.I18n;
4 4
5public class Message { 5public class Message {
6
7 public static final Message EMPTY_FIELD = create(Type.ERROR, "empty_field"); 6 public static final Message EMPTY_FIELD = create(Type.ERROR, "empty_field");
8 public static final Message INVALID_IP = create(Type.ERROR, "invalid_ip"); 7 public static final Message INVALID_IP = create(Type.ERROR, "invalid_ip");
9 public static final Message NOT_INT = create(Type.ERROR, "not_int"); 8 public static final Message NOT_INT = create(Type.ERROR, "not_int");
@@ -46,5 +45,4 @@ public class Message {
46 WARNING, 45 WARNING,
47 ERROR, 46 ERROR,
48 } 47 }
49
50} 48}
diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/ParameterizedMessage.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/ParameterizedMessage.java
index 9ad5867..afcbf62 100644
--- a/enigma/src/main/java/cuchaz/enigma/utils/validation/ParameterizedMessage.java
+++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/ParameterizedMessage.java
@@ -4,7 +4,6 @@ import java.util.Arrays;
4import java.util.Objects; 4import java.util.Objects;
5 5
6public final class ParameterizedMessage { 6public final class ParameterizedMessage {
7
8 public final Message message; 7 public final Message message;
9 private final Object[] params; 8 private final Object[] params;
10 private final Validatable target; 9 private final Validatable target;
@@ -25,11 +24,15 @@ public final class ParameterizedMessage {
25 24
26 @Override 25 @Override
27 public boolean equals(Object o) { 26 public boolean equals(Object o) {
28 if (this == o) return true; 27 if (this == o) {
29 if (!(o instanceof ParameterizedMessage that)) return false; 28 return true;
30 return Objects.equals(message, that.message) && 29 }
31 Arrays.equals(params, that.params) && 30
32 Objects.equals(target, that.target); 31 if (!(o instanceof ParameterizedMessage that)) {
32 return false;
33 }
34
35 return Objects.equals(message, that.message) && Arrays.equals(params, that.params) && Objects.equals(target, that.target);
33 } 36 }
34 37
35 @Override 38 @Override
@@ -43,5 +46,4 @@ public final class ParameterizedMessage {
43 public String toString() { 46 public String toString() {
44 return String.format("ParameterizedMessage { message: %s, params: %s, target: %s }", message, Arrays.toString(params), target); 47 return String.format("ParameterizedMessage { message: %s, params: %s, target: %s }", message, Arrays.toString(params), target);
45 } 48 }
46
47} 49}
diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java
index 5067d7e..8a7a9ed 100644
--- a/enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java
+++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java
@@ -4,7 +4,6 @@ import java.io.PrintStream;
4import java.util.Arrays; 4import java.util.Arrays;
5 5
6public class PrintValidatable implements Validatable { 6public class PrintValidatable implements Validatable {
7
8 public static final PrintValidatable INSTANCE = new PrintValidatable(); 7 public static final PrintValidatable INSTANCE = new PrintValidatable();
9 8
10 @Override 9 @Override
@@ -16,9 +15,9 @@ public class PrintValidatable implements Validatable {
16 String text = message.getText(); 15 String text = message.getText();
17 String longText = message.getLongText(); 16 String longText = message.getLongText();
18 String type = switch (message.message.type) { 17 String type = switch (message.message.type) {
19 case INFO -> "info"; 18 case INFO -> "info";
20 case WARNING -> "warning"; 19 case WARNING -> "warning";
21 case ERROR -> "error"; 20 case ERROR -> "error";
22 }; 21 };
23 w.printf("%s: %s\n", type, text); 22 w.printf("%s: %s\n", type, text);
24 23
@@ -30,5 +29,4 @@ public class PrintValidatable implements Validatable {
30 @Override 29 @Override
31 public void clearMessages() { 30 public void clearMessages() {
32 } 31 }
33
34} 32}
diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/StandardValidation.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/StandardValidation.java
index 871b59d..123c9b6 100644
--- a/enigma/src/main/java/cuchaz/enigma/utils/validation/StandardValidation.java
+++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/StandardValidation.java
@@ -1,17 +1,20 @@
1package cuchaz.enigma.utils.validation; 1package cuchaz.enigma.utils.validation;
2 2
3public class StandardValidation { 3public class StandardValidation {
4
5 public static boolean notBlank(ValidationContext vc, String value) { 4 public static boolean notBlank(ValidationContext vc, String value) {
6 if (value.trim().isEmpty()) { 5 if (value.trim().isEmpty()) {
7 vc.raise(Message.EMPTY_FIELD); 6 vc.raise(Message.EMPTY_FIELD);
8 return false; 7 return false;
9 } 8 }
9
10 return true; 10 return true;
11 } 11 }
12 12
13 public static boolean isInt(ValidationContext vc, String value) { 13 public static boolean isInt(ValidationContext vc, String value) {
14 if (!notBlank(vc, value)) return false; 14 if (!notBlank(vc, value)) {
15 return false;
16 }
17
15 try { 18 try {
16 Integer.parseInt(value); 19 Integer.parseInt(value);
17 return true; 20 return true;
@@ -22,13 +25,17 @@ public class StandardValidation {
22 } 25 }
23 26
24 public static boolean isIntInRange(ValidationContext vc, String value, int min, int max) { 27 public static boolean isIntInRange(ValidationContext vc, String value, int min, int max) {
25 if (!isInt(vc, value)) return false; 28 if (!isInt(vc, value)) {
29 return false;
30 }
31
26 int intVal = Integer.parseInt(value); 32 int intVal = Integer.parseInt(value);
33
27 if (intVal < min || intVal > max) { 34 if (intVal < min || intVal > max) {
28 vc.raise(Message.FIELD_OUT_OF_RANGE_INT, min, max); 35 vc.raise(Message.FIELD_OUT_OF_RANGE_INT, min, max);
29 return false; 36 return false;
30 } 37 }
38
31 return true; 39 return true;
32 } 40 }
33
34} 41}
diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/Validatable.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/Validatable.java
index 765ee08..39e8c41 100644
--- a/enigma/src/main/java/cuchaz/enigma/utils/validation/Validatable.java
+++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/Validatable.java
@@ -1,9 +1,7 @@
1package cuchaz.enigma.utils.validation; 1package cuchaz.enigma.utils.validation;
2 2
3public interface Validatable { 3public interface Validatable {
4
5 void addMessage(ParameterizedMessage message); 4 void addMessage(ParameterizedMessage message);
6 5
7 void clearMessages(); 6 void clearMessages();
8
9} 7}
diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java
index 0ecb9fb..416e8a0 100644
--- a/enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java
+++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java
@@ -1,6 +1,10 @@
1package cuchaz.enigma.utils.validation; 1package cuchaz.enigma.utils.validation;
2 2
3import java.util.*; 3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.HashSet;
6import java.util.List;
7import java.util.Set;
4 8
5import javax.annotation.Nullable; 9import javax.annotation.Nullable;
6 10
@@ -15,7 +19,6 @@ import cuchaz.enigma.utils.validation.Message.Type;
15 * multiple errors and displaying them to the user at the same time. 19 * multiple errors and displaying them to the user at the same time.
16 */ 20 */
17public class ValidationContext { 21public class ValidationContext {
18
19 private Validatable activeElement = null; 22 private Validatable activeElement = null;
20 private final Set<Validatable> elements = new HashSet<>(); 23 private final Set<Validatable> elements = new HashSet<>();
21 private final List<ParameterizedMessage> messages = new ArrayList<>(); 24 private final List<ParameterizedMessage> messages = new ArrayList<>();
@@ -30,6 +33,7 @@ public class ValidationContext {
30 if (v != null) { 33 if (v != null) {
31 elements.add(v); 34 elements.add(v);
32 } 35 }
36
33 activeElement = v; 37 activeElement = v;
34 } 38 }
35 39
@@ -38,14 +42,16 @@ public class ValidationContext {
38 * that element about the message. 42 * that element about the message.
39 * 43 *
40 * @param message the message to raise 44 * @param message the message to raise
41 * @param args the arguments used when formatting the message text 45 * @param args the arguments used when formatting the message text
42 */ 46 */
43 public void raise(Message message, Object... args) { 47 public void raise(Message message, Object... args) {
44 ParameterizedMessage pm = new ParameterizedMessage(message, args, this.activeElement); 48 ParameterizedMessage pm = new ParameterizedMessage(message, args, this.activeElement);
49
45 if (!this.messages.contains(pm)) { 50 if (!this.messages.contains(pm)) {
46 if (activeElement != null) { 51 if (activeElement != null) {
47 activeElement.addMessage(pm); 52 activeElement.addMessage(pm);
48 } 53 }
54
49 messages.add(pm); 55 messages.add(pm);
50 } 56 }
51 } 57 }
@@ -72,6 +78,7 @@ public class ValidationContext {
72 for (ParameterizedMessage message : this.messages) { 78 for (ParameterizedMessage message : this.messages) {
73 PrintValidatable.formatMessage(System.err, message); 79 PrintValidatable.formatMessage(System.err, message);
74 } 80 }
81
75 throw new IllegalStateException("Errors encountered; cannot continue! Check error log for details."); 82 throw new IllegalStateException("Errors encountered; cannot continue! Check error log for details.");
76 } 83 }
77 } 84 }
@@ -90,5 +97,4 @@ public class ValidationContext {
90 elements.clear(); 97 elements.clear();
91 messages.clear(); 98 messages.clear();
92 } 99 }
93
94} 100}
diff --git a/enigma/src/test/java/cuchaz/enigma/ConfigTest.java b/enigma/src/test/java/cuchaz/enigma/ConfigTest.java
index a44f037..95689f8 100644
--- a/enigma/src/test/java/cuchaz/enigma/ConfigTest.java
+++ b/enigma/src/test/java/cuchaz/enigma/ConfigTest.java
@@ -1,13 +1,12 @@
1package cuchaz.enigma; 1package cuchaz.enigma;
2 2
3import static org.junit.Assert.assertEquals;
4
3import org.junit.Test; 5import org.junit.Test;
4 6
5import cuchaz.enigma.config.ConfigContainer; 7import cuchaz.enigma.config.ConfigContainer;
6 8
7import static org.junit.Assert.assertEquals;
8
9public class ConfigTest { 9public class ConfigTest {
10
11 @Test 10 @Test
12 public void serialize() { 11 public void serialize() {
13 ConfigContainer cc = new ConfigContainer(); 12 ConfigContainer cc = new ConfigContainer();
@@ -17,15 +16,7 @@ public class ConfigTest {
17 cc.data().section("a").section("b").section("c").setInt("c", 5); 16 cc.data().section("a").section("b").section("c").setInt("c", 5);
18 cc.data().section("a").section("b").section("c").setDouble("d", 3.5); 17 cc.data().section("a").section("b").section("c").setDouble("d", 3.5);
19 cc.data().section("a").section("b").section("c").setRgbColor("e", 0x123456); 18 cc.data().section("a").section("b").section("c").setRgbColor("e", 0x123456);
20 assertEquals("a=a\n" + 19 assertEquals("a=a\n" + "\n" + "[a][b][c]\n" + "a=abcd\n" + "b=true\n" + "c=5\n" + "d=3.5\n" + "e=#123456\n", cc.serialize());
21 "\n" +
22 "[a][b][c]\n" +
23 "a=abcd\n" +
24 "b=true\n" +
25 "c=5\n" +
26 "d=3.5\n" +
27 "e=#123456\n",
28 cc.serialize());
29 } 20 }
30 21
31 @Test 22 @Test
@@ -37,14 +28,7 @@ public class ConfigTest {
37 cc.data().section("a").section("b").section("c").setInt("c", 5); 28 cc.data().section("a").section("b").section("c").setInt("c", 5);
38 cc.data().section("a").section("b").section("c").setDouble("d", 3.5); 29 cc.data().section("a").section("b").section("c").setDouble("d", 3.5);
39 cc.data().section("a").section("b").section("c").setRgbColor("e", 0x123456); 30 cc.data().section("a").section("b").section("c").setRgbColor("e", 0x123456);
40 assertEquals(ConfigContainer.parse("a=a\n" + 31 assertEquals(ConfigContainer.parse("a=a\n" + "\n" + "[a][b][c]\n" + "a=abcd\n" + "b=true\n" + "c=5\n" + "d=3.5\n" + "e=#123456\n").data(), cc.data());
41 "\n" +
42 "[a][b][c]\n" +
43 "a=abcd\n" +
44 "b=true\n" +
45 "c=5\n" +
46 "d=3.5\n" +
47 "e=#123456\n").data(), cc.data());
48 } 32 }
49 33
50 @Test 34 @Test
@@ -52,12 +36,10 @@ public class ConfigTest {
52 ConfigContainer cc = new ConfigContainer(); 36 ConfigContainer cc = new ConfigContainer();
53 String thing = "\\[],\\,./'\"`~!@#$%^&*()_+-=|}{\n\\\\\r\b\u0000\uffff\u1234"; 37 String thing = "\\[],\\,./'\"`~!@#$%^&*()_+-=|}{\n\\\\\r\b\u0000\uffff\u1234";
54 cc.data().section(thing).setString(thing, thing); 38 cc.data().section(thing).setString(thing, thing);
55 cc.data().section(thing).setArray("arr", new String[] { thing, thing, thing, thing }); 39 cc.data().section(thing).setArray("arr", new String[]{thing, thing, thing, thing});
56 40
57 assertEquals( 41 assertEquals(
58 "[\\\\[\\],\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234]\n" + 42 "[\\\\[\\],\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234]\n" + "\\\\\\[],\\\\,./'\"`~!@#$%^&*()_+-\\=|}{\\n\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234=\\\\[],\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234\n" + "arr=\\\\\\\\[]\\\\,\\\\\\\\\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234,\\\\\\\\[]\\\\,\\\\\\\\\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234,\\\\\\\\[]\\\\,\\\\\\\\\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234,\\\\\\\\[]\\\\,\\\\\\\\\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234\n",
59 "\\\\\\[],\\\\,./'\"`~!@#$%^&*()_+-\\=|}{\\n\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234=\\\\[],\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234\n" +
60 "arr=\\\\\\\\[]\\\\,\\\\\\\\\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234,\\\\\\\\[]\\\\,\\\\\\\\\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234,\\\\\\\\[]\\\\,\\\\\\\\\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234,\\\\\\\\[]\\\\,\\\\\\\\\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234\n",
61 cc.serialize()); 43 cc.serialize());
62 44
63 ConfigContainer cc1 = ConfigContainer.parse(cc.serialize()); 45 ConfigContainer cc1 = ConfigContainer.parse(cc.serialize());
@@ -77,10 +59,8 @@ public class ConfigTest {
77 assertEquals("", ConfigContainer.parse("[").serialize()); 59 assertEquals("", ConfigContainer.parse("[").serialize());
78 assertEquals("[a]\na=b\nc=d\n", ConfigContainer.parse("[a]\na=b\n[\nc=d").serialize()); 60 assertEquals("[a]\na=b\nc=d\n", ConfigContainer.parse("[a]\na=b\n[\nc=d").serialize());
79 61
80
81 // not technically syntax errors but never something that gets generated 62 // not technically syntax errors but never something that gets generated
82 assertEquals("", ConfigContainer.parse("[a]").serialize()); 63 assertEquals("", ConfigContainer.parse("[a]").serialize());
83 assertEquals("", ConfigContainer.parse("[a]\n[b]").serialize()); 64 assertEquals("", ConfigContainer.parse("[a]\n[b]").serialize());
84 } 65 }
85
86} 66}
diff --git a/enigma/src/test/java/cuchaz/enigma/PackageVisibilityIndexTest.java b/enigma/src/test/java/cuchaz/enigma/PackageVisibilityIndexTest.java
index 1251535..1678f59 100644
--- a/enigma/src/test/java/cuchaz/enigma/PackageVisibilityIndexTest.java
+++ b/enigma/src/test/java/cuchaz/enigma/PackageVisibilityIndexTest.java
@@ -1,30 +1,31 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.analysis.index.JarIndex;
15import cuchaz.enigma.analysis.index.PackageVisibilityIndex;
16import cuchaz.enigma.classprovider.JarClassProvider;
17import cuchaz.enigma.translation.representation.entry.ClassEntry;
18import org.junit.Test;
19
20import java.nio.file.Path;
21import java.nio.file.Paths;
22
23import static cuchaz.enigma.TestEntryFactory.newClass; 14import static cuchaz.enigma.TestEntryFactory.newClass;
24import static org.hamcrest.MatcherAssert.assertThat; 15import static org.hamcrest.MatcherAssert.assertThat;
25import static org.hamcrest.Matchers.contains; 16import static org.hamcrest.Matchers.contains;
26import static org.hamcrest.Matchers.containsInAnyOrder; 17import static org.hamcrest.Matchers.containsInAnyOrder;
27 18
19import java.nio.file.Path;
20import java.nio.file.Paths;
21
22import org.junit.Test;
23
24import cuchaz.enigma.analysis.index.JarIndex;
25import cuchaz.enigma.analysis.index.PackageVisibilityIndex;
26import cuchaz.enigma.classprovider.JarClassProvider;
27import cuchaz.enigma.translation.representation.entry.ClassEntry;
28
28public class PackageVisibilityIndexTest { 29public class PackageVisibilityIndexTest {
29 public static final Path JAR = Paths.get("build/test-obf/packageAccess.jar"); 30 public static final Path JAR = Paths.get("build/test-obf/packageAccess.jar");
30 private static final ClassEntry KEEP = newClass("cuchaz/enigma/inputs/Keep"); 31 private static final ClassEntry KEEP = newClass("cuchaz/enigma/inputs/Keep");
@@ -46,10 +47,6 @@ public class PackageVisibilityIndexTest {
46 PackageVisibilityIndex visibilityIndex = jarIndex.getPackageVisibilityIndex(); 47 PackageVisibilityIndex visibilityIndex = jarIndex.getPackageVisibilityIndex();
47 assertThat(visibilityIndex.getPartition(BASE), containsInAnyOrder(BASE, SAME_PACKAGE_CHILD, SAME_PACKAGE_CHILD_INNER)); 48 assertThat(visibilityIndex.getPartition(BASE), containsInAnyOrder(BASE, SAME_PACKAGE_CHILD, SAME_PACKAGE_CHILD_INNER));
48 System.out.println(visibilityIndex.getPartitions()); 49 System.out.println(visibilityIndex.getPartitions());
49 assertThat(visibilityIndex.getPartitions(), containsInAnyOrder( 50 assertThat(visibilityIndex.getPartitions(), containsInAnyOrder(containsInAnyOrder(BASE, SAME_PACKAGE_CHILD, SAME_PACKAGE_CHILD_INNER), containsInAnyOrder(OTHER_PACKAGE_CHILD, OTHER_PACKAGE_CHILD_INNER), contains(KEEP)));
50 containsInAnyOrder(BASE, SAME_PACKAGE_CHILD, SAME_PACKAGE_CHILD_INNER),
51 containsInAnyOrder(OTHER_PACKAGE_CHILD, OTHER_PACKAGE_CHILD_INNER),
52 contains(KEEP)
53 ));
54 } 51 }
55} 52}
diff --git a/enigma/src/test/java/cuchaz/enigma/TestDeobfed.java b/enigma/src/test/java/cuchaz/enigma/TestDeobfed.java
index 2584d58..b19aa77 100644
--- a/enigma/src/test/java/cuchaz/enigma/TestDeobfed.java
+++ b/enigma/src/test/java/cuchaz/enigma/TestDeobfed.java
@@ -1,16 +1,20 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import static cuchaz.enigma.TestEntryFactory.newClass;
15import static org.hamcrest.MatcherAssert.assertThat;
16import static org.hamcrest.Matchers.containsInAnyOrder;
17
14import java.nio.file.Files; 18import java.nio.file.Files;
15import java.nio.file.Path; 19import java.nio.file.Path;
16import java.nio.file.Paths; 20import java.nio.file.Paths;
@@ -23,10 +27,6 @@ import cuchaz.enigma.source.Decompiler;
23import cuchaz.enigma.source.Decompilers; 27import cuchaz.enigma.source.Decompilers;
24import cuchaz.enigma.source.SourceSettings; 28import cuchaz.enigma.source.SourceSettings;
25 29
26import static cuchaz.enigma.TestEntryFactory.newClass;
27import static org.hamcrest.MatcherAssert.assertThat;
28import static org.hamcrest.Matchers.containsInAnyOrder;
29
30public class TestDeobfed { 30public class TestDeobfed {
31 public static final Path OBF = Paths.get("build/test-obf/translation.jar"); 31 public static final Path OBF = Paths.get("build/test-obf/translation.jar");
32 public static final Path DEOBF = Paths.get("build/test-deobf/translation.jar"); 32 public static final Path DEOBF = Paths.get("build/test-deobf/translation.jar");
@@ -45,31 +45,9 @@ public class TestDeobfed {
45 45
46 @Test 46 @Test
47 public void obfEntries() { 47 public void obfEntries() {
48 assertThat(deobfProject.getJarIndex().getEntryIndex().getClasses(), containsInAnyOrder( 48 assertThat(deobfProject.getJarIndex().getEntryIndex().getClasses(),
49 newClass("cuchaz/enigma/inputs/Keep"), 49 containsInAnyOrder(newClass("cuchaz/enigma/inputs/Keep"), newClass("a"), newClass("b"), newClass("c"), newClass("d"), newClass("d$1"), newClass("e"), newClass("f"), newClass("g"), newClass("g$a"), newClass("g$a$a"), newClass("g$b"), newClass("g$b$a"), newClass("h"),
50 newClass("a"), 50 newClass("h$a"), newClass("h$a$a"), newClass("h$b"), newClass("h$b$a"), newClass("h$b$a$a"), newClass("h$b$a$b"), newClass("i"), newClass("i$a"), newClass("i$b")));
51 newClass("b"),
52 newClass("c"),
53 newClass("d"),
54 newClass("d$1"),
55 newClass("e"),
56 newClass("f"),
57 newClass("g"),
58 newClass("g$a"),
59 newClass("g$a$a"),
60 newClass("g$b"),
61 newClass("g$b$a"),
62 newClass("h"),
63 newClass("h$a"),
64 newClass("h$a$a"),
65 newClass("h$b"),
66 newClass("h$b$a"),
67 newClass("h$b$a$a"),
68 newClass("h$b$a$b"),
69 newClass("i"),
70 newClass("i$a"),
71 newClass("i$b")
72 ));
73 } 51 }
74 52
75 @Test 53 @Test
diff --git a/enigma/src/test/java/cuchaz/enigma/TestDeobfuscator.java b/enigma/src/test/java/cuchaz/enigma/TestDeobfuscator.java
index 38940ca..587494e 100644
--- a/enigma/src/test/java/cuchaz/enigma/TestDeobfuscator.java
+++ b/enigma/src/test/java/cuchaz/enigma/TestDeobfuscator.java
@@ -1,24 +1,25 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import java.io.IOException;
15import java.nio.file.Paths;
16
17import org.junit.Test;
18
14import cuchaz.enigma.classprovider.ClasspathClassProvider; 19import cuchaz.enigma.classprovider.ClasspathClassProvider;
15import cuchaz.enigma.source.Decompiler; 20import cuchaz.enigma.source.Decompiler;
16import cuchaz.enigma.source.Decompilers; 21import cuchaz.enigma.source.Decompilers;
17import cuchaz.enigma.source.SourceSettings; 22import cuchaz.enigma.source.SourceSettings;
18import org.junit.Test;
19
20import java.io.IOException;
21import java.nio.file.Paths;
22 23
23public class TestDeobfuscator { 24public class TestDeobfuscator {
24 private EnigmaProject openProject() throws IOException { 25 private EnigmaProject openProject() throws IOException {
@@ -27,8 +28,7 @@ public class TestDeobfuscator {
27 } 28 }
28 29
29 @Test 30 @Test
30 public void loadJar() 31 public void loadJar() throws Exception {
31 throws Exception {
32 openProject(); 32 openProject();
33 } 33 }
34 34
diff --git a/enigma/src/test/java/cuchaz/enigma/TestEntryFactory.java b/enigma/src/test/java/cuchaz/enigma/TestEntryFactory.java
index 9e1425a..833e217 100644
--- a/enigma/src/test/java/cuchaz/enigma/TestEntryFactory.java
+++ b/enigma/src/test/java/cuchaz/enigma/TestEntryFactory.java
@@ -1,24 +1,24 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.analysis.EntryReference; 14import cuchaz.enigma.analysis.EntryReference;
15import cuchaz.enigma.translation.representation.*; 15import cuchaz.enigma.translation.representation.MethodDescriptor;
16import cuchaz.enigma.translation.representation.TypeDescriptor;
16import cuchaz.enigma.translation.representation.entry.ClassEntry; 17import cuchaz.enigma.translation.representation.entry.ClassEntry;
17import cuchaz.enigma.translation.representation.entry.FieldEntry; 18import cuchaz.enigma.translation.representation.entry.FieldEntry;
18import cuchaz.enigma.translation.representation.entry.MethodEntry; 19import cuchaz.enigma.translation.representation.entry.MethodEntry;
19 20
20public class TestEntryFactory { 21public class TestEntryFactory {
21
22 public static ClassEntry newClass(String name) { 22 public static ClassEntry newClass(String name) {
23 return new ClassEntry(name); 23 return new ClassEntry(name);
24 } 24 }
diff --git a/enigma/src/test/java/cuchaz/enigma/TestInnerClasses.java b/enigma/src/test/java/cuchaz/enigma/TestInnerClasses.java
index 6b60994..46086e5 100644
--- a/enigma/src/test/java/cuchaz/enigma/TestInnerClasses.java
+++ b/enigma/src/test/java/cuchaz/enigma/TestInnerClasses.java
@@ -1,16 +1,25 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import static cuchaz.enigma.TestEntryFactory.newClass;
15import static org.hamcrest.MatcherAssert.assertThat;
16import static org.hamcrest.Matchers.is;
17
18import java.nio.file.Path;
19import java.nio.file.Paths;
20
21import org.junit.Test;
22
14import cuchaz.enigma.analysis.index.JarIndex; 23import cuchaz.enigma.analysis.index.JarIndex;
15import cuchaz.enigma.classprovider.CachingClassProvider; 24import cuchaz.enigma.classprovider.CachingClassProvider;
16import cuchaz.enigma.classprovider.JarClassProvider; 25import cuchaz.enigma.classprovider.JarClassProvider;
@@ -18,17 +27,8 @@ import cuchaz.enigma.source.Decompiler;
18import cuchaz.enigma.source.Decompilers; 27import cuchaz.enigma.source.Decompilers;
19import cuchaz.enigma.source.SourceSettings; 28import cuchaz.enigma.source.SourceSettings;
20import cuchaz.enigma.translation.representation.entry.ClassEntry; 29import cuchaz.enigma.translation.representation.entry.ClassEntry;
21import org.junit.Test;
22
23import java.nio.file.Path;
24import java.nio.file.Paths;
25
26import static cuchaz.enigma.TestEntryFactory.newClass;
27import static org.hamcrest.MatcherAssert.assertThat;
28import static org.hamcrest.Matchers.is;
29 30
30public class TestInnerClasses { 31public class TestInnerClasses {
31
32 private static final ClassEntry SimpleOuter = newClass("d"); 32 private static final ClassEntry SimpleOuter = newClass("d");
33 private static final ClassEntry SimpleInner = newClass("d$a"); 33 private static final ClassEntry SimpleInner = newClass("d$a");
34 private static final ClassEntry ConstructorArgsOuter = newClass("c"); 34 private static final ClassEntry ConstructorArgsOuter = newClass("c");
@@ -61,26 +61,19 @@ public class TestInnerClasses {
61 61
62 @Test 62 @Test
63 public void classTree() { 63 public void classTree() {
64
65 // root level 64 // root level
66 assertThat(index.getEntryIndex().hasClass(ClassTreeRoot), is(true)); 65 assertThat(index.getEntryIndex().hasClass(ClassTreeRoot), is(true));
67 66
68 // level 1 67 // level 1
69 ClassEntry fullClassEntry = new ClassEntry(ClassTreeRoot.getName() 68 ClassEntry fullClassEntry = new ClassEntry(ClassTreeRoot.getName() + "$" + ClassTreeLevel1.getSimpleName());
70 + "$" + ClassTreeLevel1.getSimpleName());
71 assertThat(index.getEntryIndex().hasClass(fullClassEntry), is(true)); 69 assertThat(index.getEntryIndex().hasClass(fullClassEntry), is(true));
72 70
73 // level 2 71 // level 2
74 fullClassEntry = new ClassEntry(ClassTreeRoot.getName() 72 fullClassEntry = new ClassEntry(ClassTreeRoot.getName() + "$" + ClassTreeLevel1.getSimpleName() + "$" + ClassTreeLevel2.getSimpleName());
75 + "$" + ClassTreeLevel1.getSimpleName()
76 + "$" + ClassTreeLevel2.getSimpleName());
77 assertThat(index.getEntryIndex().hasClass(fullClassEntry), is(true)); 73 assertThat(index.getEntryIndex().hasClass(fullClassEntry), is(true));
78 74
79 // level 3 75 // level 3
80 fullClassEntry = new ClassEntry(ClassTreeRoot.getName() 76 fullClassEntry = new ClassEntry(ClassTreeRoot.getName() + "$" + ClassTreeLevel1.getSimpleName() + "$" + ClassTreeLevel2.getSimpleName() + "$" + ClassTreeLevel3.getSimpleName());
81 + "$" + ClassTreeLevel1.getSimpleName()
82 + "$" + ClassTreeLevel2.getSimpleName()
83 + "$" + ClassTreeLevel3.getSimpleName());
84 assertThat(index.getEntryIndex().hasClass(fullClassEntry), is(true)); 77 assertThat(index.getEntryIndex().hasClass(fullClassEntry), is(true));
85 } 78 }
86 79
diff --git a/enigma/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java b/enigma/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java
index 0790193..05565b6 100644
--- a/enigma/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java
+++ b/enigma/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java
@@ -1,16 +1,30 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByMethod;
15import static cuchaz.enigma.TestEntryFactory.newClass;
16import static cuchaz.enigma.TestEntryFactory.newMethod;
17import static org.hamcrest.MatcherAssert.assertThat;
18import static org.hamcrest.Matchers.containsInAnyOrder;
19import static org.hamcrest.Matchers.empty;
20import static org.hamcrest.Matchers.is;
21
22import java.nio.file.Path;
23import java.nio.file.Paths;
24import java.util.Collection;
25
26import org.junit.Test;
27
14import cuchaz.enigma.analysis.EntryReference; 28import cuchaz.enigma.analysis.EntryReference;
15import cuchaz.enigma.analysis.index.JarIndex; 29import cuchaz.enigma.analysis.index.JarIndex;
16import cuchaz.enigma.classprovider.CachingClassProvider; 30import cuchaz.enigma.classprovider.CachingClassProvider;
@@ -18,18 +32,8 @@ import cuchaz.enigma.classprovider.JarClassProvider;
18import cuchaz.enigma.translation.representation.entry.ClassEntry; 32import cuchaz.enigma.translation.representation.entry.ClassEntry;
19import cuchaz.enigma.translation.representation.entry.MethodDefEntry; 33import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
20import cuchaz.enigma.translation.representation.entry.MethodEntry; 34import cuchaz.enigma.translation.representation.entry.MethodEntry;
21import org.junit.Test;
22
23import java.nio.file.Path;
24import java.nio.file.Paths;
25import java.util.Collection;
26
27import static cuchaz.enigma.TestEntryFactory.*;
28import static org.hamcrest.MatcherAssert.assertThat;
29import static org.hamcrest.Matchers.*;
30 35
31public class TestJarIndexConstructorReferences { 36public class TestJarIndexConstructorReferences {
32
33 public static final Path JAR = Paths.get("build/test-obf/constructors.jar"); 37 public static final Path JAR = Paths.get("build/test-obf/constructors.jar");
34 private JarIndex index; 38 private JarIndex index;
35 39
@@ -47,8 +51,7 @@ public class TestJarIndexConstructorReferences {
47 51
48 @Test 52 @Test
49 public void obfEntries() { 53 public void obfEntries() {
50 assertThat(index.getEntryIndex().getClasses(), containsInAnyOrder(newClass("cuchaz/enigma/inputs/Keep"), baseClass, 54 assertThat(index.getEntryIndex().getClasses(), containsInAnyOrder(newClass("cuchaz/enigma/inputs/Keep"), baseClass, subClass, subsubClass, defaultClass, callerClass));
51 subClass, subsubClass, defaultClass, callerClass));
52 } 55 }
53 56
54 @Test 57 @Test
@@ -56,50 +59,36 @@ public class TestJarIndexConstructorReferences {
56 public void baseDefault() { 59 public void baseDefault() {
57 MethodEntry source = newMethod(baseClass, "<init>", "()V"); 60 MethodEntry source = newMethod(baseClass, "<init>", "()V");
58 Collection<EntryReference<MethodEntry, MethodDefEntry>> references = index.getReferenceIndex().getReferencesToMethod(source); 61 Collection<EntryReference<MethodEntry, MethodDefEntry>> references = index.getReferenceIndex().getReferencesToMethod(source);
59 assertThat(references, containsInAnyOrder( 62 assertThat(references, containsInAnyOrder(newBehaviorReferenceByMethod(source, callerClass.getName(), "a", "()V"), newBehaviorReferenceByMethod(source, subClass.getName(), "<init>", "()V"), newBehaviorReferenceByMethod(source, subClass.getName(), "<init>", "(III)V")));
60 newBehaviorReferenceByMethod(source, callerClass.getName(), "a", "()V"),
61 newBehaviorReferenceByMethod(source, subClass.getName(), "<init>", "()V"),
62 newBehaviorReferenceByMethod(source, subClass.getName(), "<init>", "(III)V")
63 ));
64 } 63 }
65 64
66 @Test 65 @Test
67 @SuppressWarnings("unchecked") 66 @SuppressWarnings("unchecked")
68 public void baseInt() { 67 public void baseInt() {
69 MethodEntry source = newMethod(baseClass, "<init>", "(I)V"); 68 MethodEntry source = newMethod(baseClass, "<init>", "(I)V");
70 assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder( 69 assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder(newBehaviorReferenceByMethod(source, callerClass.getName(), "b", "()V")));
71 newBehaviorReferenceByMethod(source, callerClass.getName(), "b", "()V")
72 ));
73 } 70 }
74 71
75 @Test 72 @Test
76 @SuppressWarnings("unchecked") 73 @SuppressWarnings("unchecked")
77 public void subDefault() { 74 public void subDefault() {
78 MethodEntry source = newMethod(subClass, "<init>", "()V"); 75 MethodEntry source = newMethod(subClass, "<init>", "()V");
79 assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder( 76 assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder(newBehaviorReferenceByMethod(source, callerClass.getName(), "c", "()V"), newBehaviorReferenceByMethod(source, subClass.getName(), "<init>", "(I)V")));
80 newBehaviorReferenceByMethod(source, callerClass.getName(), "c", "()V"),
81 newBehaviorReferenceByMethod(source, subClass.getName(), "<init>", "(I)V")
82 ));
83 } 77 }
84 78
85 @Test 79 @Test
86 @SuppressWarnings("unchecked") 80 @SuppressWarnings("unchecked")
87 public void subInt() { 81 public void subInt() {
88 MethodEntry source = newMethod(subClass, "<init>", "(I)V"); 82 MethodEntry source = newMethod(subClass, "<init>", "(I)V");
89 assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder( 83 assertThat(index.getReferenceIndex().getReferencesToMethod(source),
90 newBehaviorReferenceByMethod(source, callerClass.getName(), "d", "()V"), 84 containsInAnyOrder(newBehaviorReferenceByMethod(source, callerClass.getName(), "d", "()V"), newBehaviorReferenceByMethod(source, subClass.getName(), "<init>", "(II)V"), newBehaviorReferenceByMethod(source, subsubClass.getName(), "<init>", "(I)V")));
91 newBehaviorReferenceByMethod(source, subClass.getName(), "<init>", "(II)V"),
92 newBehaviorReferenceByMethod(source, subsubClass.getName(), "<init>", "(I)V")
93 ));
94 } 85 }
95 86
96 @Test 87 @Test
97 @SuppressWarnings("unchecked") 88 @SuppressWarnings("unchecked")
98 public void subIntInt() { 89 public void subIntInt() {
99 MethodEntry source = newMethod(subClass, "<init>", "(II)V"); 90 MethodEntry source = newMethod(subClass, "<init>", "(II)V");
100 assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder( 91 assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder(newBehaviorReferenceByMethod(source, callerClass.getName(), "e", "()V")));
101 newBehaviorReferenceByMethod(source, callerClass.getName(), "e", "()V")
102 ));
103 } 92 }
104 93
105 @Test 94 @Test
@@ -112,17 +101,13 @@ public class TestJarIndexConstructorReferences {
112 @SuppressWarnings("unchecked") 101 @SuppressWarnings("unchecked")
113 public void subsubInt() { 102 public void subsubInt() {
114 MethodEntry source = newMethod(subsubClass, "<init>", "(I)V"); 103 MethodEntry source = newMethod(subsubClass, "<init>", "(I)V");
115 assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder( 104 assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder(newBehaviorReferenceByMethod(source, callerClass.getName(), "f", "()V")));
116 newBehaviorReferenceByMethod(source, callerClass.getName(), "f", "()V")
117 ));
118 } 105 }
119 106
120 @Test 107 @Test
121 @SuppressWarnings("unchecked") 108 @SuppressWarnings("unchecked")
122 public void defaultConstructable() { 109 public void defaultConstructable() {
123 MethodEntry source = newMethod(defaultClass, "<init>", "()V"); 110 MethodEntry source = newMethod(defaultClass, "<init>", "()V");
124 assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder( 111 assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder(newBehaviorReferenceByMethod(source, callerClass.getName(), "g", "()V")));
125 newBehaviorReferenceByMethod(source, callerClass.getName(), "g", "()V")
126 ));
127 } 112 }
128} 113}
diff --git a/enigma/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java b/enigma/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java
index a9045f9..3f6f151 100644
--- a/enigma/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java
+++ b/enigma/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java
@@ -1,16 +1,34 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import static cuchaz.enigma.TestEntryFactory.newClass;
15import static cuchaz.enigma.TestEntryFactory.newField;
16import static cuchaz.enigma.TestEntryFactory.newMethod;
17import static cuchaz.enigma.TestEntryFactory.newFieldReferenceByMethod;
18import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByMethod;
19import static org.hamcrest.MatcherAssert.assertThat;
20import static org.hamcrest.Matchers.contains;
21import static org.hamcrest.Matchers.containsInAnyOrder;
22import static org.hamcrest.Matchers.empty;
23import static org.hamcrest.Matchers.is;
24
25import java.nio.file.Path;
26import java.nio.file.Paths;
27import java.util.Collection;
28
29import org.junit.Test;
30import org.objectweb.asm.Opcodes;
31
14import cuchaz.enigma.analysis.EntryReference; 32import cuchaz.enigma.analysis.EntryReference;
15import cuchaz.enigma.analysis.index.EntryIndex; 33import cuchaz.enigma.analysis.index.EntryIndex;
16import cuchaz.enigma.analysis.index.InheritanceIndex; 34import cuchaz.enigma.analysis.index.InheritanceIndex;
@@ -24,19 +42,8 @@ import cuchaz.enigma.translation.representation.entry.ClassEntry;
24import cuchaz.enigma.translation.representation.entry.FieldEntry; 42import cuchaz.enigma.translation.representation.entry.FieldEntry;
25import cuchaz.enigma.translation.representation.entry.MethodDefEntry; 43import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
26import cuchaz.enigma.translation.representation.entry.MethodEntry; 44import cuchaz.enigma.translation.representation.entry.MethodEntry;
27import org.junit.Test;
28import org.objectweb.asm.Opcodes;
29
30import java.nio.file.Path;
31import java.nio.file.Paths;
32import java.util.Collection;
33
34import static cuchaz.enigma.TestEntryFactory.*;
35import static org.hamcrest.MatcherAssert.assertThat;
36import static org.hamcrest.Matchers.*;
37 45
38public class TestJarIndexInheritanceTree { 46public class TestJarIndexInheritanceTree {
39
40 public static final Path JAR = Paths.get("build/test-obf/inheritanceTree.jar"); 47 public static final Path JAR = Paths.get("build/test-obf/inheritanceTree.jar");
41 private JarIndex index; 48 private JarIndex index;
42 49
@@ -55,21 +62,17 @@ public class TestJarIndexInheritanceTree {
55 62
56 @Test 63 @Test
57 public void obfEntries() { 64 public void obfEntries() {
58 assertThat(index.getEntryIndex().getClasses(), containsInAnyOrder( 65 assertThat(index.getEntryIndex().getClasses(), containsInAnyOrder(newClass("cuchaz/enigma/inputs/Keep"), baseClass, subClassA, subClassAA, subClassB));
59 newClass("cuchaz/enigma/inputs/Keep"), baseClass, subClassA, subClassAA, subClassB
60 ));
61 } 66 }
62 67
63 @Test 68 @Test
64 public void translationIndex() { 69 public void translationIndex() {
65
66 InheritanceIndex index = this.index.getInheritanceIndex(); 70 InheritanceIndex index = this.index.getInheritanceIndex();
67 71
68 // base class 72 // base class
69 assertThat(index.getParents(baseClass), is(empty())); 73 assertThat(index.getParents(baseClass), is(empty()));
70 assertThat(index.getAncestors(baseClass), is(empty())); 74 assertThat(index.getAncestors(baseClass), is(empty()));
71 assertThat(index.getChildren(baseClass), containsInAnyOrder(subClassA, subClassB 75 assertThat(index.getChildren(baseClass), containsInAnyOrder(subClassA, subClassB));
72 ));
73 76
74 // subclass a 77 // subclass a
75 assertThat(index.getParents(subClassA), contains(baseClass)); 78 assertThat(index.getParents(subClassA), contains(baseClass));
@@ -95,41 +98,22 @@ public class TestJarIndexInheritanceTree {
95 98
96 @Test 99 @Test
97 public void relatedMethodImplementations() { 100 public void relatedMethodImplementations() {
98
99 Collection<MethodEntry> entries; 101 Collection<MethodEntry> entries;
100 102
101 EntryResolver resolver = new IndexEntryResolver(index); 103 EntryResolver resolver = new IndexEntryResolver(index);
102 // getName() 104 // getName()
103 entries = resolver.resolveEquivalentMethods(newMethod(baseClass, "a", "()Ljava/lang/String;")); 105 entries = resolver.resolveEquivalentMethods(newMethod(baseClass, "a", "()Ljava/lang/String;"));
104 assertThat(entries, containsInAnyOrder( 106 assertThat(entries, containsInAnyOrder(newMethod(baseClass, "a", "()Ljava/lang/String;"), newMethod(subClassAA, "a", "()Ljava/lang/String;")));
105 newMethod(baseClass, "a", "()Ljava/lang/String;"),
106 newMethod(subClassAA, "a", "()Ljava/lang/String;")
107 ));
108 entries = resolver.resolveEquivalentMethods(newMethod(subClassAA, "a", "()Ljava/lang/String;")); 107 entries = resolver.resolveEquivalentMethods(newMethod(subClassAA, "a", "()Ljava/lang/String;"));
109 assertThat(entries, containsInAnyOrder( 108 assertThat(entries, containsInAnyOrder(newMethod(baseClass, "a", "()Ljava/lang/String;"), newMethod(subClassAA, "a", "()Ljava/lang/String;")));
110 newMethod(baseClass, "a", "()Ljava/lang/String;"),
111 newMethod(subClassAA, "a", "()Ljava/lang/String;")
112 ));
113 109
114 // doBaseThings() 110 // doBaseThings()
115 entries = resolver.resolveEquivalentMethods(newMethod(baseClass, "a", "()V")); 111 entries = resolver.resolveEquivalentMethods(newMethod(baseClass, "a", "()V"));
116 assertThat(entries, containsInAnyOrder( 112 assertThat(entries, containsInAnyOrder(newMethod(baseClass, "a", "()V"), newMethod(subClassAA, "a", "()V"), newMethod(subClassB, "a", "()V")));
117 newMethod(baseClass, "a", "()V"),
118 newMethod(subClassAA, "a", "()V"),
119 newMethod(subClassB, "a", "()V")
120 ));
121 entries = resolver.resolveEquivalentMethods(newMethod(subClassAA, "a", "()V")); 113 entries = resolver.resolveEquivalentMethods(newMethod(subClassAA, "a", "()V"));
122 assertThat(entries, containsInAnyOrder( 114 assertThat(entries, containsInAnyOrder(newMethod(baseClass, "a", "()V"), newMethod(subClassAA, "a", "()V"), newMethod(subClassB, "a", "()V")));
123 newMethod(baseClass, "a", "()V"),
124 newMethod(subClassAA, "a", "()V"),
125 newMethod(subClassB, "a", "()V")
126 ));
127 entries = resolver.resolveEquivalentMethods(newMethod(subClassB, "a", "()V")); 115 entries = resolver.resolveEquivalentMethods(newMethod(subClassB, "a", "()V"));
128 assertThat(entries, containsInAnyOrder( 116 assertThat(entries, containsInAnyOrder(newMethod(baseClass, "a", "()V"), newMethod(subClassAA, "a", "()V"), newMethod(subClassB, "a", "()V")));
129 newMethod(baseClass, "a", "()V"),
130 newMethod(subClassAA, "a", "()V"),
131 newMethod(subClassB, "a", "()V")
132 ));
133 117
134 // doBThings 118 // doBThings
135 entries = resolver.resolveEquivalentMethods(newMethod(subClassB, "b", "()V")); 119 entries = resolver.resolveEquivalentMethods(newMethod(subClassB, "b", "()V"));
@@ -143,55 +127,38 @@ public class TestJarIndexInheritanceTree {
143 127
144 // name 128 // name
145 references = index.getReferenceIndex().getReferencesToField(nameField); 129 references = index.getReferenceIndex().getReferencesToField(nameField);
146 assertThat(references, containsInAnyOrder( 130 assertThat(references, containsInAnyOrder(newFieldReferenceByMethod(nameField, baseClass.getName(), "<init>", "(Ljava/lang/String;)V"), newFieldReferenceByMethod(nameField, baseClass.getName(), "a", "()Ljava/lang/String;")));
147 newFieldReferenceByMethod(nameField, baseClass.getName(), "<init>", "(Ljava/lang/String;)V"),
148 newFieldReferenceByMethod(nameField, baseClass.getName(), "a", "()Ljava/lang/String;")
149 ));
150 131
151 // numThings 132 // numThings
152 references = index.getReferenceIndex().getReferencesToField(numThingsField); 133 references = index.getReferenceIndex().getReferencesToField(numThingsField);
153 assertThat(references, containsInAnyOrder( 134 assertThat(references, containsInAnyOrder(newFieldReferenceByMethod(numThingsField, subClassB.getName(), "<init>", "()V"), newFieldReferenceByMethod(numThingsField, subClassB.getName(), "b", "()V")));
154 newFieldReferenceByMethod(numThingsField, subClassB.getName(), "<init>", "()V"),
155 newFieldReferenceByMethod(numThingsField, subClassB.getName(), "b", "()V")
156 ));
157 } 135 }
158 136
159 @Test 137 @Test
160 @SuppressWarnings("unchecked") 138 @SuppressWarnings("unchecked")
161 public void behaviorReferences() { 139 public void behaviorReferences() {
162
163 MethodEntry source; 140 MethodEntry source;
164 Collection<EntryReference<MethodEntry, MethodDefEntry>> references; 141 Collection<EntryReference<MethodEntry, MethodDefEntry>> references;
165 142
166 // baseClass constructor 143 // baseClass constructor
167 source = newMethod(baseClass, "<init>", "(Ljava/lang/String;)V"); 144 source = newMethod(baseClass, "<init>", "(Ljava/lang/String;)V");
168 references = index.getReferenceIndex().getReferencesToMethod(source); 145 references = index.getReferenceIndex().getReferencesToMethod(source);
169 assertThat(references, containsInAnyOrder( 146 assertThat(references, containsInAnyOrder(newBehaviorReferenceByMethod(source, subClassA.getName(), "<init>", "(Ljava/lang/String;)V"), newBehaviorReferenceByMethod(source, subClassB.getName(), "<init>", "()V")));
170 newBehaviorReferenceByMethod(source, subClassA.getName(), "<init>", "(Ljava/lang/String;)V"),
171 newBehaviorReferenceByMethod(source, subClassB.getName(), "<init>", "()V")
172 ));
173 147
174 // subClassA constructor 148 // subClassA constructor
175 source = newMethod(subClassA, "<init>", "(Ljava/lang/String;)V"); 149 source = newMethod(subClassA, "<init>", "(Ljava/lang/String;)V");
176 references = index.getReferenceIndex().getReferencesToMethod(source); 150 references = index.getReferenceIndex().getReferencesToMethod(source);
177 assertThat(references, containsInAnyOrder( 151 assertThat(references, containsInAnyOrder(newBehaviorReferenceByMethod(source, subClassAA.getName(), "<init>", "()V")));
178 newBehaviorReferenceByMethod(source, subClassAA.getName(), "<init>", "()V")
179 ));
180 152
181 // baseClass.getName() 153 // baseClass.getName()
182 source = newMethod(baseClass, "a", "()Ljava/lang/String;"); 154 source = newMethod(baseClass, "a", "()Ljava/lang/String;");
183 references = index.getReferenceIndex().getReferencesToMethod(source); 155 references = index.getReferenceIndex().getReferencesToMethod(source);
184 assertThat(references, containsInAnyOrder( 156 assertThat(references, containsInAnyOrder(newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()Ljava/lang/String;"), newBehaviorReferenceByMethod(source, subClassB.getName(), "a", "()V")));
185 newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()Ljava/lang/String;"),
186 newBehaviorReferenceByMethod(source, subClassB.getName(), "a", "()V")
187 ));
188 157
189 // subclassAA.getName() 158 // subclassAA.getName()
190 source = newMethod(subClassAA, "a", "()Ljava/lang/String;"); 159 source = newMethod(subClassAA, "a", "()Ljava/lang/String;");
191 references = index.getReferenceIndex().getReferencesToMethod(source); 160 references = index.getReferenceIndex().getReferencesToMethod(source);
192 assertThat(references, containsInAnyOrder( 161 assertThat(references, containsInAnyOrder(newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()V")));
193 newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()V")
194 ));
195 } 162 }
196 163
197 @Test 164 @Test
@@ -225,6 +192,5 @@ public class TestJarIndexInheritanceTree {
225 assertThat(entryIndex.hasMethod(newMethod(subClassA, "b", "()V")), is(false)); 192 assertThat(entryIndex.hasMethod(newMethod(subClassA, "b", "()V")), is(false));
226 assertThat(entryIndex.hasMethod(newMethod(subClassAA, "b", "()V")), is(false)); 193 assertThat(entryIndex.hasMethod(newMethod(subClassAA, "b", "()V")), is(false));
227 assertThat(entryIndex.hasMethod(newMethod(subClassB, "b", "()V")), is(true)); 194 assertThat(entryIndex.hasMethod(newMethod(subClassB, "b", "()V")), is(true));
228
229 } 195 }
230} 196}
diff --git a/enigma/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java b/enigma/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java
index 6e3755c..dcbe95f 100644
--- a/enigma/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java
+++ b/enigma/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java
@@ -1,17 +1,41 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.analysis.*; 14import static cuchaz.enigma.TestEntryFactory.newClass;
15import static cuchaz.enigma.TestEntryFactory.newField;
16import static cuchaz.enigma.TestEntryFactory.newFieldReferenceByMethod;
17import static cuchaz.enigma.TestEntryFactory.newMethod;
18import static org.hamcrest.MatcherAssert.assertThat;
19import static org.hamcrest.Matchers.containsInAnyOrder;
20import static org.hamcrest.Matchers.empty;
21import static org.hamcrest.Matchers.hasSize;
22import static org.hamcrest.Matchers.is;
23import static org.hamcrest.Matchers.not;
24import static org.hamcrest.Matchers.nullValue;
25
26import java.nio.file.Path;
27import java.nio.file.Paths;
28import java.util.Collection;
29import java.util.List;
30
31import org.junit.Test;
32
33import cuchaz.enigma.analysis.ClassImplementationsTreeNode;
34import cuchaz.enigma.analysis.ClassInheritanceTreeNode;
35import cuchaz.enigma.analysis.EntryReference;
36import cuchaz.enigma.analysis.IndexTreeBuilder;
37import cuchaz.enigma.analysis.MethodImplementationsTreeNode;
38import cuchaz.enigma.analysis.MethodInheritanceTreeNode;
15import cuchaz.enigma.analysis.index.EntryIndex; 39import cuchaz.enigma.analysis.index.EntryIndex;
16import cuchaz.enigma.analysis.index.InheritanceIndex; 40import cuchaz.enigma.analysis.index.InheritanceIndex;
17import cuchaz.enigma.analysis.index.JarIndex; 41import cuchaz.enigma.analysis.index.JarIndex;
@@ -23,19 +47,8 @@ import cuchaz.enigma.translation.representation.entry.ClassEntry;
23import cuchaz.enigma.translation.representation.entry.FieldEntry; 47import cuchaz.enigma.translation.representation.entry.FieldEntry;
24import cuchaz.enigma.translation.representation.entry.MethodDefEntry; 48import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
25import cuchaz.enigma.translation.representation.entry.MethodEntry; 49import cuchaz.enigma.translation.representation.entry.MethodEntry;
26import org.junit.Test;
27
28import java.nio.file.Path;
29import java.nio.file.Paths;
30import java.util.Collection;
31import java.util.List;
32
33import static cuchaz.enigma.TestEntryFactory.*;
34import static org.hamcrest.MatcherAssert.assertThat;
35import static org.hamcrest.Matchers.*;
36 50
37public class TestJarIndexLoneClass { 51public class TestJarIndexLoneClass {
38
39 public static final Path JAR = Paths.get("build/test-obf/loneClass.jar"); 52 public static final Path JAR = Paths.get("build/test-obf/loneClass.jar");
40 private JarIndex index; 53 private JarIndex index;
41 54
@@ -47,10 +60,7 @@ public class TestJarIndexLoneClass {
47 60
48 @Test 61 @Test
49 public void obfEntries() { 62 public void obfEntries() {
50 assertThat(index.getEntryIndex().getClasses(), containsInAnyOrder( 63 assertThat(index.getEntryIndex().getClasses(), containsInAnyOrder(newClass("cuchaz/enigma/inputs/Keep"), newClass("a")));
51 newClass("cuchaz/enigma/inputs/Keep"),
52 newClass("a")
53 ));
54 } 64 }
55 65
56 @Test 66 @Test
@@ -112,9 +122,7 @@ public class TestJarIndexLoneClass {
112 @Test 122 @Test
113 public void relatedMethodImplementations() { 123 public void relatedMethodImplementations() {
114 Collection<MethodEntry> entries = index.getEntryResolver().resolveEquivalentMethods(newMethod("a", "a", "()Ljava/lang/String;")); 124 Collection<MethodEntry> entries = index.getEntryResolver().resolveEquivalentMethods(newMethod("a", "a", "()Ljava/lang/String;"));
115 assertThat(entries, containsInAnyOrder( 125 assertThat(entries, containsInAnyOrder(newMethod("a", "a", "()Ljava/lang/String;")));
116 newMethod("a", "a", "()Ljava/lang/String;")
117 ));
118 } 126 }
119 127
120 @Test 128 @Test
@@ -122,10 +130,7 @@ public class TestJarIndexLoneClass {
122 public void fieldReferences() { 130 public void fieldReferences() {
123 FieldEntry source = newField("a", "a", "Ljava/lang/String;"); 131 FieldEntry source = newField("a", "a", "Ljava/lang/String;");
124 Collection<EntryReference<FieldEntry, MethodDefEntry>> references = index.getReferenceIndex().getReferencesToField(source); 132 Collection<EntryReference<FieldEntry, MethodDefEntry>> references = index.getReferenceIndex().getReferencesToField(source);
125 assertThat(references, containsInAnyOrder( 133 assertThat(references, containsInAnyOrder(newFieldReferenceByMethod(source, "a", "<init>", "(Ljava/lang/String;)V"), newFieldReferenceByMethod(source, "a", "a", "()Ljava/lang/String;")));
126 newFieldReferenceByMethod(source, "a", "<init>", "(Ljava/lang/String;)V"),
127 newFieldReferenceByMethod(source, "a", "a", "()Ljava/lang/String;")
128 ));
129 } 134 }
130 135
131 @Test 136 @Test
diff --git a/enigma/src/test/java/cuchaz/enigma/TestMethodDescriptor.java b/enigma/src/test/java/cuchaz/enigma/TestMethodDescriptor.java
index a73880d..918466b 100644
--- a/enigma/src/test/java/cuchaz/enigma/TestMethodDescriptor.java
+++ b/enigma/src/test/java/cuchaz/enigma/TestMethodDescriptor.java
@@ -1,25 +1,28 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.translation.representation.MethodDescriptor; 14import static org.hamcrest.MatcherAssert.assertThat;
15import cuchaz.enigma.translation.representation.TypeDescriptor; 15import static org.hamcrest.Matchers.contains;
16import static org.hamcrest.Matchers.empty;
17import static org.hamcrest.Matchers.is;
18import static org.hamcrest.Matchers.not;
19
16import org.junit.Test; 20import org.junit.Test;
17 21
18import static org.hamcrest.MatcherAssert.assertThat; 22import cuchaz.enigma.translation.representation.MethodDescriptor;
19import static org.hamcrest.Matchers.*; 23import cuchaz.enigma.translation.representation.TypeDescriptor;
20 24
21public class TestMethodDescriptor { 25public class TestMethodDescriptor {
22
23 @Test 26 @Test
24 public void easiest() { 27 public void easiest() {
25 final MethodDescriptor sig = new MethodDescriptor("()V"); 28 final MethodDescriptor sig = new MethodDescriptor("()V");
@@ -31,26 +34,19 @@ public class TestMethodDescriptor {
31 public void primitives() { 34 public void primitives() {
32 { 35 {
33 final MethodDescriptor sig = new MethodDescriptor("(I)V"); 36 final MethodDescriptor sig = new MethodDescriptor("(I)V");
34 assertThat(sig.getArgumentDescs(), contains( 37 assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("I")));
35 new TypeDescriptor("I")
36 ));
37 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("V"))); 38 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("V")));
38 } 39 }
40
39 { 41 {
40 final MethodDescriptor sig = new MethodDescriptor("(I)I"); 42 final MethodDescriptor sig = new MethodDescriptor("(I)I");
41 assertThat(sig.getArgumentDescs(), contains( 43 assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("I")));
42 new TypeDescriptor("I")
43 ));
44 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("I"))); 44 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("I")));
45 } 45 }
46
46 { 47 {
47 final MethodDescriptor sig = new MethodDescriptor("(IBCJ)Z"); 48 final MethodDescriptor sig = new MethodDescriptor("(IBCJ)Z");
48 assertThat(sig.getArgumentDescs(), contains( 49 assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("I"), new TypeDescriptor("B"), new TypeDescriptor("C"), new TypeDescriptor("J")));
49 new TypeDescriptor("I"),
50 new TypeDescriptor("B"),
51 new TypeDescriptor("C"),
52 new TypeDescriptor("J")
53 ));
54 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("Z"))); 50 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("Z")));
55 } 51 }
56 } 52 }
@@ -63,20 +59,16 @@ public class TestMethodDescriptor {
63 assertThat(sig.getArgumentDescs().get(0), is(new TypeDescriptor("[LFoo;"))); 59 assertThat(sig.getArgumentDescs().get(0), is(new TypeDescriptor("[LFoo;")));
64 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("V"))); 60 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("V")));
65 } 61 }
62
66 { 63 {
67 final MethodDescriptor sig = new MethodDescriptor("(LFoo;)LBar;"); 64 final MethodDescriptor sig = new MethodDescriptor("(LFoo;)LBar;");
68 assertThat(sig.getArgumentDescs(), contains( 65 assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("LFoo;")));
69 new TypeDescriptor("LFoo;")
70 ));
71 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("LBar;"))); 66 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("LBar;")));
72 } 67 }
68
73 { 69 {
74 final MethodDescriptor sig = new MethodDescriptor("(LFoo;LMoo;LZoo;)LBar;"); 70 final MethodDescriptor sig = new MethodDescriptor("(LFoo;LMoo;LZoo;)LBar;");
75 assertThat(sig.getArgumentDescs(), contains( 71 assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("LFoo;"), new TypeDescriptor("LMoo;"), new TypeDescriptor("LZoo;")));
76 new TypeDescriptor("LFoo;"),
77 new TypeDescriptor("LMoo;"),
78 new TypeDescriptor("LZoo;")
79 ));
80 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("LBar;"))); 72 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("LBar;")));
81 } 73 }
82 } 74 }
@@ -85,25 +77,19 @@ public class TestMethodDescriptor {
85 public void arrays() { 77 public void arrays() {
86 { 78 {
87 final MethodDescriptor sig = new MethodDescriptor("([I)V"); 79 final MethodDescriptor sig = new MethodDescriptor("([I)V");
88 assertThat(sig.getArgumentDescs(), contains( 80 assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("[I")));
89 new TypeDescriptor("[I")
90 ));
91 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("V"))); 81 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("V")));
92 } 82 }
83
93 { 84 {
94 final MethodDescriptor sig = new MethodDescriptor("([I)[J"); 85 final MethodDescriptor sig = new MethodDescriptor("([I)[J");
95 assertThat(sig.getArgumentDescs(), contains( 86 assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("[I")));
96 new TypeDescriptor("[I")
97 ));
98 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("[J"))); 87 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("[J")));
99 } 88 }
89
100 { 90 {
101 final MethodDescriptor sig = new MethodDescriptor("([I[Z[F)[D"); 91 final MethodDescriptor sig = new MethodDescriptor("([I[Z[F)[D");
102 assertThat(sig.getArgumentDescs(), contains( 92 assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("[I"), new TypeDescriptor("[Z"), new TypeDescriptor("[F")));
103 new TypeDescriptor("[I"),
104 new TypeDescriptor("[Z"),
105 new TypeDescriptor("[F")
106 ));
107 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("[D"))); 93 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("[D")));
108 } 94 }
109 } 95 }
@@ -112,20 +98,13 @@ public class TestMethodDescriptor {
112 public void mixed() { 98 public void mixed() {
113 { 99 {
114 final MethodDescriptor sig = new MethodDescriptor("(I[JLFoo;)Z"); 100 final MethodDescriptor sig = new MethodDescriptor("(I[JLFoo;)Z");
115 assertThat(sig.getArgumentDescs(), contains( 101 assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("I"), new TypeDescriptor("[J"), new TypeDescriptor("LFoo;")));
116 new TypeDescriptor("I"),
117 new TypeDescriptor("[J"),
118 new TypeDescriptor("LFoo;")
119 ));
120 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("Z"))); 102 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("Z")));
121 } 103 }
104
122 { 105 {
123 final MethodDescriptor sig = new MethodDescriptor("(III)[LFoo;"); 106 final MethodDescriptor sig = new MethodDescriptor("(III)[LFoo;");
124 assertThat(sig.getArgumentDescs(), contains( 107 assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("I"), new TypeDescriptor("I"), new TypeDescriptor("I")));
125 new TypeDescriptor("I"),
126 new TypeDescriptor("I"),
127 new TypeDescriptor("I")
128 ));
129 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("[LFoo;"))); 108 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("[LFoo;")));
130 } 109 }
131 } 110 }
@@ -138,42 +117,37 @@ public class TestMethodDescriptor {
138 assertThat(sig.getArgumentDescs(), is(empty())); 117 assertThat(sig.getArgumentDescs(), is(empty()));
139 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("V"))); 118 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("V")));
140 } 119 }
120
141 { 121 {
142 final MethodDescriptor oldSig = new MethodDescriptor("(IJLFoo;)V"); 122 final MethodDescriptor oldSig = new MethodDescriptor("(IJLFoo;)V");
143 final MethodDescriptor sig = oldSig.remap(s -> null); 123 final MethodDescriptor sig = oldSig.remap(s -> null);
144 assertThat(sig.getArgumentDescs(), contains( 124 assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("I"), new TypeDescriptor("J"), new TypeDescriptor("LFoo;")));
145 new TypeDescriptor("I"),
146 new TypeDescriptor("J"),
147 new TypeDescriptor("LFoo;")
148 ));
149 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("V"))); 125 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("V")));
150 } 126 }
127
151 { 128 {
152 final MethodDescriptor oldSig = new MethodDescriptor("(LFoo;LBar;)LMoo;"); 129 final MethodDescriptor oldSig = new MethodDescriptor("(LFoo;LBar;)LMoo;");
153 final MethodDescriptor sig = oldSig.remap(s -> { 130 final MethodDescriptor sig = oldSig.remap(s -> {
154 if (s.equals("Foo")) { 131 if (s.equals("Foo")) {
155 return "Bar"; 132 return "Bar";
156 } 133 }
134
157 return null; 135 return null;
158 }); 136 });
159 assertThat(sig.getArgumentDescs(), contains( 137 assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("LBar;"), new TypeDescriptor("LBar;")));
160 new TypeDescriptor("LBar;"),
161 new TypeDescriptor("LBar;")
162 ));
163 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("LMoo;"))); 138 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("LMoo;")));
164 } 139 }
140
165 { 141 {
166 final MethodDescriptor oldSig = new MethodDescriptor("(LFoo;LBar;)LMoo;"); 142 final MethodDescriptor oldSig = new MethodDescriptor("(LFoo;LBar;)LMoo;");
167 final MethodDescriptor sig = oldSig.remap(s -> { 143 final MethodDescriptor sig = oldSig.remap(s -> {
168 if (s.equals("Moo")) { 144 if (s.equals("Moo")) {
169 return "Cow"; 145 return "Cow";
170 } 146 }
147
171 return null; 148 return null;
172 }); 149 });
173 assertThat(sig.getArgumentDescs(), contains( 150 assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("LFoo;"), new TypeDescriptor("LBar;")));
174 new TypeDescriptor("LFoo;"),
175 new TypeDescriptor("LBar;")
176 ));
177 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("LCow;"))); 151 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("LCow;")));
178 } 152 }
179 } 153 }
@@ -188,18 +162,16 @@ public class TestMethodDescriptor {
188 } else if (s.equals("Bar")) { 162 } else if (s.equals("Bar")) {
189 return "Beer"; 163 return "Beer";
190 } 164 }
165
191 return null; 166 return null;
192 }); 167 });
193 assertThat(sig.getArgumentDescs(), contains( 168 assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("[LFood;")));
194 new TypeDescriptor("[LFood;")
195 ));
196 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("[[[LBeer;"))); 169 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("[[[LBeer;")));
197 } 170 }
198 } 171 }
199 172
200 @Test 173 @Test
201 public void equals() { 174 public void equals() {
202
203 // base 175 // base
204 assertThat(new MethodDescriptor("()V"), is(new MethodDescriptor("()V"))); 176 assertThat(new MethodDescriptor("()V"), is(new MethodDescriptor("()V")));
205 177
diff --git a/enigma/src/test/java/cuchaz/enigma/TestTokensConstructors.java b/enigma/src/test/java/cuchaz/enigma/TestTokensConstructors.java
index a5e8367..8017ab6 100644
--- a/enigma/src/test/java/cuchaz/enigma/TestTokensConstructors.java
+++ b/enigma/src/test/java/cuchaz/enigma/TestTokensConstructors.java
@@ -1,31 +1,33 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.translation.representation.entry.MethodEntry;
15import org.junit.Ignore;
16import org.junit.Test;
17
18import java.nio.file.Paths;
19
20import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByMethod; 14import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByMethod;
21import static cuchaz.enigma.TestEntryFactory.newMethod; 15import static cuchaz.enigma.TestEntryFactory.newMethod;
22import static org.hamcrest.MatcherAssert.assertThat; 16import static org.hamcrest.MatcherAssert.assertThat;
23import static org.hamcrest.Matchers.*; 17import static org.hamcrest.Matchers.containsInAnyOrder;
18import static org.hamcrest.Matchers.empty;
19import static org.hamcrest.Matchers.is;
20import static org.hamcrest.Matchers.nullValue;
24 21
25public class TestTokensConstructors extends TokenChecker { 22import java.nio.file.Paths;
23
24import org.junit.Ignore;
25import org.junit.Test;
26
27import cuchaz.enigma.translation.representation.entry.MethodEntry;
26 28
27 public TestTokensConstructors() 29public class TestTokensConstructors extends TokenChecker {
28 throws Exception { 30 public TestTokensConstructors() throws Exception {
29 super(Paths.get("build/test-obf/constructors.jar")); 31 super(Paths.get("build/test-obf/constructors.jar"));
30 } 32 }
31 33
@@ -57,17 +59,10 @@ public class TestTokensConstructors extends TokenChecker {
57 @Ignore // TODO needs fixing, broke when compiling against J16 59 @Ignore // TODO needs fixing, broke when compiling against J16
58 public void baseDefaultReferences() { 60 public void baseDefaultReferences() {
59 MethodEntry source = newMethod("a", "<init>", "()V"); 61 MethodEntry source = newMethod("a", "<init>", "()V");
60 assertThat( 62 assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "a", "()V")), containsInAnyOrder("a"));
61 getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "a", "()V")), 63 assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "d", "<init>", "()V")), is(empty()) // implicit call, not decompiled to token
62 containsInAnyOrder("a")
63 );
64 assertThat(
65 getReferenceTokens(newBehaviorReferenceByMethod(source, "d", "<init>", "()V")),
66 is(empty()) // implicit call, not decompiled to token
67 ); 64 );
68 assertThat( 65 assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "d", "<init>", "(III)V")), is(empty()) // implicit call, not decompiled to token
69 getReferenceTokens(newBehaviorReferenceByMethod(source, "d", "<init>", "(III)V")),
70 is(empty()) // implicit call, not decompiled to token
71 ); 66 );
72 } 67 }
73 68
@@ -75,71 +70,44 @@ public class TestTokensConstructors extends TokenChecker {
75 @Ignore // TODO needs fixing, broke when compiling against J16 70 @Ignore // TODO needs fixing, broke when compiling against J16
76 public void baseIntReferences() { 71 public void baseIntReferences() {
77 MethodEntry source = newMethod("a", "<init>", "(I)V"); 72 MethodEntry source = newMethod("a", "<init>", "(I)V");
78 assertThat( 73 assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "b", "()V")), containsInAnyOrder("a"));
79 getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "b", "()V")),
80 containsInAnyOrder("a")
81 );
82 } 74 }
83 75
84 @Test 76 @Test
85 @Ignore // TODO needs fixing, broke when compiling against J16 77 @Ignore // TODO needs fixing, broke when compiling against J16
86 public void subDefaultReferences() { 78 public void subDefaultReferences() {
87 MethodEntry source = newMethod("d", "<init>", "()V"); 79 MethodEntry source = newMethod("d", "<init>", "()V");
88 assertThat( 80 assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "c", "()V")), containsInAnyOrder("d"));
89 getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "c", "()V")), 81 assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "d", "<init>", "(I)V")), containsInAnyOrder("this"));
90 containsInAnyOrder("d")
91 );
92 assertThat(
93 getReferenceTokens(newBehaviorReferenceByMethod(source, "d", "<init>", "(I)V")),
94 containsInAnyOrder("this")
95 );
96 } 82 }
97 83
98 @Test 84 @Test
99 @Ignore // TODO needs fixing, broke when compiling against J16 85 @Ignore // TODO needs fixing, broke when compiling against J16
100 public void subIntReferences() { 86 public void subIntReferences() {
101 MethodEntry source = newMethod("d", "<init>", "(I)V"); 87 MethodEntry source = newMethod("d", "<init>", "(I)V");
102 assertThat(getReferenceTokens( 88 assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "d", "()V")), containsInAnyOrder("d"));
103 newBehaviorReferenceByMethod(source, "b", "d", "()V")), 89 assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "d", "<init>", "(II)V")), containsInAnyOrder("this"));
104 containsInAnyOrder("d") 90 assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "e", "<init>", "(I)V")), containsInAnyOrder("super"));
105 );
106 assertThat(getReferenceTokens(
107 newBehaviorReferenceByMethod(source, "d", "<init>", "(II)V")),
108 containsInAnyOrder("this")
109 );
110 assertThat(getReferenceTokens(
111 newBehaviorReferenceByMethod(source, "e", "<init>", "(I)V")),
112 containsInAnyOrder("super")
113 );
114 } 91 }
115 92
116 @Test 93 @Test
117 @Ignore // TODO needs fixing, broke when compiling against J16 94 @Ignore // TODO needs fixing, broke when compiling against J16
118 public void subIntIntReferences() { 95 public void subIntIntReferences() {
119 MethodEntry source = newMethod("d", "<init>", "(II)V"); 96 MethodEntry source = newMethod("d", "<init>", "(II)V");
120 assertThat( 97 assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "e", "()V")), containsInAnyOrder("d"));
121 getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "e", "()V")),
122 containsInAnyOrder("d")
123 );
124 } 98 }
125 99
126 @Test 100 @Test
127 @Ignore // TODO needs fixing, broke when compiling against J16 101 @Ignore // TODO needs fixing, broke when compiling against J16
128 public void subsubIntReferences() { 102 public void subsubIntReferences() {
129 MethodEntry source = newMethod("e", "<init>", "(I)V"); 103 MethodEntry source = newMethod("e", "<init>", "(I)V");
130 assertThat( 104 assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "f", "()V")), containsInAnyOrder("e"));
131 getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "f", "()V")),
132 containsInAnyOrder("e")
133 );
134 } 105 }
135 106
136 @Test 107 @Test
137 @Ignore // TODO needs fixing, broke when compiling against J16 108 @Ignore // TODO needs fixing, broke when compiling against J16
138 public void defaultConstructableReferences() { 109 public void defaultConstructableReferences() {
139 MethodEntry source = newMethod("c", "<init>", "()V"); 110 MethodEntry source = newMethod("c", "<init>", "()V");
140 assertThat( 111 assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "g", "()V")), containsInAnyOrder("c"));
141 getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "g", "()V")),
142 containsInAnyOrder("c")
143 );
144 } 112 }
145} 113}
diff --git a/enigma/src/test/java/cuchaz/enigma/TestTranslator.java b/enigma/src/test/java/cuchaz/enigma/TestTranslator.java
index a420afe..93b7017 100644
--- a/enigma/src/test/java/cuchaz/enigma/TestTranslator.java
+++ b/enigma/src/test/java/cuchaz/enigma/TestTranslator.java
@@ -1,27 +1,28 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.translation.representation.entry.Entry; 14import static cuchaz.enigma.TestEntryFactory.newClass;
15import static cuchaz.enigma.TestEntryFactory.newField;
16import static cuchaz.enigma.TestEntryFactory.newMethod;
17
15import org.junit.BeforeClass; 18import org.junit.BeforeClass;
16import org.junit.Test; 19import org.junit.Test;
17 20
18import static cuchaz.enigma.TestEntryFactory.*; 21import cuchaz.enigma.translation.representation.entry.Entry;
19 22
20public class TestTranslator { 23public class TestTranslator {
21
22 @BeforeClass 24 @BeforeClass
23 public static void beforeClass() 25 public static void beforeClass() throws Exception {
24 throws Exception {
25 //TODO FIx 26 //TODO FIx
26 //deobfuscator = new Enigma(new JarFile("build/test-obf/translation.jar")); 27 //deobfuscator = new Enigma(new JarFile("build/test-obf/translation.jar"));
27 //try (InputStream in = TestTranslator.class.getResourceAsStream("/cuchaz/enigma/resources/translation.mappings")) { 28 //try (InputStream in = TestTranslator.class.getResourceAsStream("/cuchaz/enigma/resources/translation.mappings")) {
@@ -94,7 +95,6 @@ public class TestTranslator {
94 95
95 @Test 96 @Test
96 public void innerClasses() { 97 public void innerClasses() {
97
98 // classes 98 // classes
99 assertMapping(newClass("g"), newClass("deobf/G_OuterClass")); 99 assertMapping(newClass("g"), newClass("deobf/G_OuterClass"));
100 assertMapping(newClass("g$a"), newClass("deobf/G_OuterClass$A_InnerClass")); 100 assertMapping(newClass("g$a"), newClass("deobf/G_OuterClass$A_InnerClass"));
@@ -120,7 +120,6 @@ public class TestTranslator {
120 120
121 @Test 121 @Test
122 public void testGenerics() { 122 public void testGenerics() {
123
124 // classes 123 // classes
125 assertMapping(newClass("i"), newClass("deobf/I_Generics")); 124 assertMapping(newClass("i"), newClass("deobf/I_Generics"));
126 assertMapping(newClass("i$a"), newClass("deobf/I_Generics$A_Type")); 125 assertMapping(newClass("i$a"), newClass("deobf/I_Generics$A_Type"));
diff --git a/enigma/src/test/java/cuchaz/enigma/TestTypeDescriptor.java b/enigma/src/test/java/cuchaz/enigma/TestTypeDescriptor.java
index b9ebe55..280dadc 100644
--- a/enigma/src/test/java/cuchaz/enigma/TestTypeDescriptor.java
+++ b/enigma/src/test/java/cuchaz/enigma/TestTypeDescriptor.java
@@ -1,26 +1,26 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.translation.representation.TypeDescriptor;
15import org.junit.Test;
16
17import static cuchaz.enigma.TestEntryFactory.newClass; 14import static cuchaz.enigma.TestEntryFactory.newClass;
18import static org.hamcrest.MatcherAssert.assertThat; 15import static org.hamcrest.MatcherAssert.assertThat;
19import static org.hamcrest.Matchers.is; 16import static org.hamcrest.Matchers.is;
20import static org.hamcrest.Matchers.not; 17import static org.hamcrest.Matchers.not;
21 18
22public class TestTypeDescriptor { 19import org.junit.Test;
23 20
21import cuchaz.enigma.translation.representation.TypeDescriptor;
22
23public class TestTypeDescriptor {
24 @Test 24 @Test
25 public void isVoid() { 25 public void isVoid() {
26 assertThat(new TypeDescriptor("V").isVoid(), is(true)); 26 assertThat(new TypeDescriptor("V").isVoid(), is(true));
@@ -161,6 +161,7 @@ public class TestTypeDescriptor {
161 assertThat(TypeDescriptor.parseFirst("LFoo;LFoo;"), is(answer)); 161 assertThat(TypeDescriptor.parseFirst("LFoo;LFoo;"), is(answer));
162 assertThat(TypeDescriptor.parseFirst("LFoo;[LFoo;"), is(answer)); 162 assertThat(TypeDescriptor.parseFirst("LFoo;[LFoo;"), is(answer));
163 } 163 }
164
164 { 165 {
165 final String answer = "Ljava/lang/String;"; 166 final String answer = "Ljava/lang/String;";
166 assertThat(TypeDescriptor.parseFirst("Ljava/lang/String;"), is(answer)); 167 assertThat(TypeDescriptor.parseFirst("Ljava/lang/String;"), is(answer));
@@ -182,6 +183,7 @@ public class TestTypeDescriptor {
182 assertThat(TypeDescriptor.parseFirst("[I[I"), is(answer)); 183 assertThat(TypeDescriptor.parseFirst("[I[I"), is(answer));
183 assertThat(TypeDescriptor.parseFirst("[ILFoo;"), is(answer)); 184 assertThat(TypeDescriptor.parseFirst("[ILFoo;"), is(answer));
184 } 185 }
186
185 { 187 {
186 final String answer = "[[I"; 188 final String answer = "[[I";
187 assertThat(TypeDescriptor.parseFirst("[[I"), is(answer)); 189 assertThat(TypeDescriptor.parseFirst("[[I"), is(answer));
@@ -190,6 +192,7 @@ public class TestTypeDescriptor {
190 assertThat(TypeDescriptor.parseFirst("[[I[I"), is(answer)); 192 assertThat(TypeDescriptor.parseFirst("[[I[I"), is(answer));
191 assertThat(TypeDescriptor.parseFirst("[[ILFoo;"), is(answer)); 193 assertThat(TypeDescriptor.parseFirst("[[ILFoo;"), is(answer));
192 } 194 }
195
193 { 196 {
194 final String answer = "[LFoo;"; 197 final String answer = "[LFoo;";
195 assertThat(TypeDescriptor.parseFirst("[LFoo;"), is(answer)); 198 assertThat(TypeDescriptor.parseFirst("[LFoo;"), is(answer));
diff --git a/enigma/src/test/java/cuchaz/enigma/TokenChecker.java b/enigma/src/test/java/cuchaz/enigma/TokenChecker.java
index fb3a8da..5f510a4 100644
--- a/enigma/src/test/java/cuchaz/enigma/TokenChecker.java
+++ b/enigma/src/test/java/cuchaz/enigma/TokenChecker.java
@@ -1,28 +1,34 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import java.io.IOException;
15import java.nio.file.Path;
16import java.util.Collection;
17import java.util.List;
18
14import com.google.common.collect.Lists; 19import com.google.common.collect.Lists;
20
15import cuchaz.enigma.analysis.EntryReference; 21import cuchaz.enigma.analysis.EntryReference;
16import cuchaz.enigma.classprovider.CachingClassProvider; 22import cuchaz.enigma.classprovider.CachingClassProvider;
17import cuchaz.enigma.classprovider.JarClassProvider; 23import cuchaz.enigma.classprovider.JarClassProvider;
18import cuchaz.enigma.source.*; 24import cuchaz.enigma.source.Decompiler;
25import cuchaz.enigma.source.Decompilers;
26import cuchaz.enigma.source.Source;
27import cuchaz.enigma.source.SourceIndex;
28import cuchaz.enigma.source.SourceSettings;
29import cuchaz.enigma.source.Token;
19import cuchaz.enigma.translation.representation.entry.Entry; 30import cuchaz.enigma.translation.representation.entry.Entry;
20 31
21import java.io.IOException;
22import java.nio.file.Path;
23import java.util.Collection;
24import java.util.List;
25
26public class TokenChecker { 32public class TokenChecker {
27 private final Decompiler decompiler; 33 private final Decompiler decompiler;
28 34
@@ -41,9 +47,11 @@ public class TokenChecker {
41 47
42 // get the token value 48 // get the token value
43 Token token = index.getDeclarationToken(entry); 49 Token token = index.getDeclarationToken(entry);
50
44 if (token == null) { 51 if (token == null) {
45 return null; 52 return null;
46 } 53 }
54
47 return string.substring(token.start, token.end); 55 return string.substring(token.start, token.end);
48 } 56 }
49 57
@@ -56,9 +64,11 @@ public class TokenChecker {
56 64
57 // get the token values 65 // get the token values
58 List<String> values = Lists.newArrayList(); 66 List<String> values = Lists.newArrayList();
67
59 for (Token token : index.getReferenceTokens((EntryReference<Entry<?>, Entry<?>>) reference)) { 68 for (Token token : index.getReferenceTokens((EntryReference<Entry<?>, Entry<?>>) reference)) {
60 values.add(string.substring(token.start, token.end)); 69 values.add(string.substring(token.start, token.end));
61 } 70 }
71
62 return values; 72 return values;
63 } 73 }
64} 74}
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/Keep.java b/enigma/src/test/java/cuchaz/enigma/inputs/Keep.java
index 4dbe8e2..46d5380 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/Keep.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/Keep.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs; 12package cuchaz.enigma.inputs;
13 13
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/constructors/BaseClass.java b/enigma/src/test/java/cuchaz/enigma/inputs/constructors/BaseClass.java
index f07e1f8..a82db77 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/constructors/BaseClass.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/constructors/BaseClass.java
@@ -1,19 +1,18 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.constructors; 12package cuchaz.enigma.inputs.constructors;
13 13
14// a 14// a
15public class BaseClass { 15public class BaseClass {
16
17 // <init>()V 16 // <init>()V
18 public BaseClass() { 17 public BaseClass() {
19 System.out.println("Default constructor"); 18 System.out.println("Default constructor");
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/constructors/Caller.java b/enigma/src/test/java/cuchaz/enigma/inputs/constructors/Caller.java
index 71439fd..e81df1b 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/constructors/Caller.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/constructors/Caller.java
@@ -1,19 +1,18 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.constructors; 12package cuchaz.enigma.inputs.constructors;
13 13
14// b 14// b
15public class Caller { 15public class Caller {
16
17 // a()V 16 // a()V
18 public void callBaseDefault() { 17 public void callBaseDefault() {
19 // a.<init>()V 18 // a.<init>()V
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/constructors/DefaultConstructable.java b/enigma/src/test/java/cuchaz/enigma/inputs/constructors/DefaultConstructable.java
index c3d4170..55aa767 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/constructors/DefaultConstructable.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/constructors/DefaultConstructable.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.constructors; 12package cuchaz.enigma.inputs.constructors;
13 13
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/constructors/SubClass.java b/enigma/src/test/java/cuchaz/enigma/inputs/constructors/SubClass.java
index bc56b3b..1a90eba 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/constructors/SubClass.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/constructors/SubClass.java
@@ -1,19 +1,18 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.constructors; 12package cuchaz.enigma.inputs.constructors;
13 13
14// d extends a 14// d extends a
15public class SubClass extends BaseClass { 15public class SubClass extends BaseClass {
16
17 // <init>()V 16 // <init>()V
18 public SubClass() { 17 public SubClass() {
19 // a.<init>()V 18 // a.<init>()V
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/constructors/SubSubClass.java b/enigma/src/test/java/cuchaz/enigma/inputs/constructors/SubSubClass.java
index 87b69d3..ec5ac44 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/constructors/SubSubClass.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/constructors/SubSubClass.java
@@ -1,19 +1,18 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.constructors; 12package cuchaz.enigma.inputs.constructors;
13 13
14// e extends d 14// e extends d
15public class SubSubClass extends SubClass { 15public class SubSubClass extends SubClass {
16
17 // <init>(I)V 16 // <init>(I)V
18 public SubSubClass(int i) { 17 public SubSubClass(int i) {
19 // c.<init>(I)V 18 // c.<init>(I)V
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java b/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java
index b9c4929..87e849f 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java
@@ -1,19 +1,18 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.inheritanceTree; 12package cuchaz.enigma.inputs.inheritanceTree;
13 13
14// a 14// a
15public abstract class BaseClass { 15public abstract class BaseClass {
16
17 // a 16 // a
18 private String name; 17 private String name;
19 18
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java b/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java
index 50e963c..ba8c1b7 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java
@@ -1,19 +1,18 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.inheritanceTree; 12package cuchaz.enigma.inputs.inheritanceTree;
13 13
14// b extends a 14// b extends a
15public abstract class SubclassA extends BaseClass { 15public abstract class SubclassA extends BaseClass {
16
17 // <init>(Ljava/lang/String;)V 16 // <init>(Ljava/lang/String;)V
18 protected SubclassA(String name) { 17 protected SubclassA(String name) {
19 // call to a.<init>(Ljava/lang/String)V 18 // call to a.<init>(Ljava/lang/String)V
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java b/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java
index d0dd664..cfc696a 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java
@@ -1,19 +1,18 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.inheritanceTree; 12package cuchaz.enigma.inputs.inheritanceTree;
13 13
14// c extends a 14// c extends a
15public class SubclassB extends BaseClass { 15public class SubclassB extends BaseClass {
16
17 // a 16 // a
18 private int numThings; 17 private int numThings;
19 18
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java b/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java
index c584570..d3bb62e 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java
@@ -1,19 +1,18 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.inheritanceTree; 12package cuchaz.enigma.inputs.inheritanceTree;
13 13
14// d extends b 14// d extends b
15public class SubsubclassAA extends SubclassA { 15public class SubsubclassAA extends SubclassA {
16
17 protected SubsubclassAA() { 16 protected SubsubclassAA() {
18 // call to b.<init>(Ljava/lang/String;)V 17 // call to b.<init>(Ljava/lang/String;)V
19 super("AA"); 18 super("AA");
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java
index f652d87..515205a 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java
@@ -1,18 +1,17 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.innerClasses; 12package cuchaz.enigma.inputs.innerClasses;
13 13
14public class A_Anonymous { 14public class A_Anonymous {
15
16 public void foo() { 15 public void foo() {
17 Runnable runnable = new Runnable() { 16 Runnable runnable = new Runnable() {
18 @Override 17 @Override
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java
index d1b7601..6ec27ac 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java
@@ -1,18 +1,17 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.innerClasses; 12package cuchaz.enigma.inputs.innerClasses;
13 13
14public class B_AnonymousWithScopeArgs { 14public class B_AnonymousWithScopeArgs {
15
16 public static void foo(final D_Simple arg) { 15 public static void foo(final D_Simple arg) {
17 System.out.println(new Object() { 16 System.out.println(new Object() {
18 @Override 17 @Override
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java
index 94061fa..223c424 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java
@@ -1,19 +1,18 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.innerClasses; 12package cuchaz.enigma.inputs.innerClasses;
13 13
14@SuppressWarnings("unused") 14@SuppressWarnings("unused")
15public class C_ConstructorArgs { 15public class C_ConstructorArgs {
16
17 Inner i; 16 Inner i;
18 17
19 public void foo() { 18 public void foo() {
@@ -21,10 +20,9 @@ public class C_ConstructorArgs {
21 } 20 }
22 21
23 class Inner { 22 class Inner {
24
25 private int a; 23 private int a;
26 24
27 public Inner(int a) { 25 Inner(int a) {
28 this.a = a; 26 this.a = a;
29 } 27 }
30 } 28 }
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/D_Simple.java b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/D_Simple.java
index 71b3a6d..f401d5f 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/D_Simple.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/D_Simple.java
@@ -1,18 +1,17 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.innerClasses; 12package cuchaz.enigma.inputs.innerClasses;
13 13
14public class D_Simple { 14public class D_Simple {
15
16 class Inner { 15 class Inner {
17 // nothing to do 16 // nothing to do
18 } 17 }
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java
index 976ec42..0056bc6 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java
@@ -1,18 +1,17 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.innerClasses; 12package cuchaz.enigma.inputs.innerClasses;
13 13
14public class E_AnonymousWithOuterAccess { 14public class E_AnonymousWithOuterAccess {
15
16 // reproduction of error case documented at: 15 // reproduction of error case documented at:
17 // https://bitbucket.org/cuchaz/enigma/issue/61/stackoverflowerror-when-deobfuscating 16 // https://bitbucket.org/cuchaz/enigma/issue/61/stackoverflowerror-when-deobfuscating
18 17
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java
index b1de3c9..b2e9e2d 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java
@@ -1,28 +1,24 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.innerClasses; 12package cuchaz.enigma.inputs.innerClasses;
13 13
14public class F_ClassTree { 14public class F_ClassTree {
15
16 public class Level1 { 15 public class Level1 {
17
18 public int f1; 16 public int f1;
19 17
20 public class Level2 { 18 public class Level2 {
21
22 public int f2; 19 public int f2;
23 20
24 public class Level3 { 21 public class Level3 {
25
26 public int f3; 22 public int f3;
27 } 23 }
28 } 24 }
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/loneClass/LoneClass.java b/enigma/src/test/java/cuchaz/enigma/inputs/loneClass/LoneClass.java
index ddc4e31..e50d37f 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/loneClass/LoneClass.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/loneClass/LoneClass.java
@@ -1,18 +1,17 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.loneClass; 12package cuchaz.enigma.inputs.loneClass;
13 13
14public class LoneClass { 14public class LoneClass {
15
16 private String name; 15 private String name;
17 16
18 public LoneClass(String name) { 17 public LoneClass(String name) {
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/packageAccess/SamePackageChild.java b/enigma/src/test/java/cuchaz/enigma/inputs/packageAccess/SamePackageChild.java
index cf0f657..aa8de19 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/packageAccess/SamePackageChild.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/packageAccess/SamePackageChild.java
@@ -1,7 +1,6 @@
1package cuchaz.enigma.inputs.packageAccess; 1package cuchaz.enigma.inputs.packageAccess;
2 2
3public class SamePackageChild extends Base { 3public class SamePackageChild extends Base {
4
5 class Inner { 4 class Inner {
6 final int value; 5 final int value;
7 6
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/packageAccess/sub/OtherPackageChild.java b/enigma/src/test/java/cuchaz/enigma/inputs/packageAccess/sub/OtherPackageChild.java
index 19fb19c..5bcb763 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/packageAccess/sub/OtherPackageChild.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/packageAccess/sub/OtherPackageChild.java
@@ -3,7 +3,6 @@ package cuchaz.enigma.inputs.packageAccess.sub;
3import cuchaz.enigma.inputs.packageAccess.Base; 3import cuchaz.enigma.inputs.packageAccess.Base;
4 4
5public class OtherPackageChild extends Base { 5public class OtherPackageChild extends Base {
6
7 class Inner { 6 class Inner {
8 final int value; 7 final int value;
9 8
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/translation/A_Basic.java b/enigma/src/test/java/cuchaz/enigma/inputs/translation/A_Basic.java
index 26f3718..b411e0a 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/translation/A_Basic.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/translation/A_Basic.java
@@ -1,18 +1,17 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.translation; 12package cuchaz.enigma.inputs.translation;
13 13
14public class A_Basic { 14public class A_Basic {
15
16 public int one; 15 public int one;
17 public float two; 16 public float two;
18 public String three; 17 public String three;
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/translation/B_BaseClass.java b/enigma/src/test/java/cuchaz/enigma/inputs/translation/B_BaseClass.java
index fd7f6e7..d5e9a25 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/translation/B_BaseClass.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/translation/B_BaseClass.java
@@ -1,18 +1,17 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.translation; 12package cuchaz.enigma.inputs.translation;
13 13
14public class B_BaseClass { 14public class B_BaseClass {
15
16 public int f1; 15 public int f1;
17 public char f2; 16 public char f2;
18 17
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/translation/C_SubClass.java b/enigma/src/test/java/cuchaz/enigma/inputs/translation/C_SubClass.java
index 9d74e44..fd9e217 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/translation/C_SubClass.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/translation/C_SubClass.java
@@ -1,18 +1,17 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.translation; 12package cuchaz.enigma.inputs.translation;
13 13
14public class C_SubClass extends B_BaseClass { 14public class C_SubClass extends B_BaseClass {
15
16 public char f2; // shadows B_BaseClass.f2 15 public char f2; // shadows B_BaseClass.f2
17 public int f3; 16 public int f3;
18 public int f4; 17 public int f4;
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java b/enigma/src/test/java/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java
index 99c83bb..56cccc7 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.translation; 12package cuchaz.enigma.inputs.translation;
13 13
@@ -15,7 +15,6 @@ import java.util.ArrayList;
15import java.util.List; 15import java.util.List;
16 16
17public class D_AnonymousTesting { 17public class D_AnonymousTesting {
18
19 public List<Object> getObjs() { 18 public List<Object> getObjs() {
20 List<Object> objs = new ArrayList<Object>(); 19 List<Object> objs = new ArrayList<Object>();
21 objs.add(new Object() { 20 objs.add(new Object() {
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/translation/E_Bridges.java b/enigma/src/test/java/cuchaz/enigma/inputs/translation/E_Bridges.java
index 0b8cf2a..f44bdbf 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/translation/E_Bridges.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/translation/E_Bridges.java
@@ -1,20 +1,19 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.translation; 12package cuchaz.enigma.inputs.translation;
13 13
14import java.util.Iterator; 14import java.util.Iterator;
15 15
16public class E_Bridges implements Iterator<Object> { 16public class E_Bridges implements Iterator<Object> {
17
18 @Override 17 @Override
19 public boolean hasNext() { 18 public boolean hasNext() {
20 return false; 19 return false;
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/translation/F_ObjectMethods.java b/enigma/src/test/java/cuchaz/enigma/inputs/translation/F_ObjectMethods.java
index 8a92792..ac1d7b5 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/translation/F_ObjectMethods.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/translation/F_ObjectMethods.java
@@ -1,21 +1,19 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.translation; 12package cuchaz.enigma.inputs.translation;
13 13
14@SuppressWarnings("FinalizeCalledExplicitly") 14@SuppressWarnings("FinalizeCalledExplicitly")
15public class F_ObjectMethods { 15public class F_ObjectMethods {
16 16 public void callEmAll() throws Throwable {
17 public void callEmAll()
18 throws Throwable {
19 clone(); 17 clone();
20 equals(this); 18 equals(this);
21 finalize(); 19 finalize();
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/translation/G_OuterClass.java b/enigma/src/test/java/cuchaz/enigma/inputs/translation/G_OuterClass.java
index a1e6a85..4d99235 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/translation/G_OuterClass.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/translation/G_OuterClass.java
@@ -1,30 +1,29 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.translation; 12package cuchaz.enigma.inputs.translation;
13 13
14public class G_OuterClass { 14public class G_OuterClass {
15
16 public class A_InnerClass { 15 public class A_InnerClass {
17
18 public int f1; 16 public int f1;
19 public String f2; 17 public String f2;
20 18
21 public void m1() {} 19 public void m1() {
20 }
22 21
23 public class A_InnerInnerClass { 22 public class A_InnerInnerClass {
24
25 public int f3; 23 public int f3;
26 24
27 public void m2() {} 25 public void m2() {
26 }
28 } 27 }
29 } 28 }
30 29
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/translation/H_NamelessClass.java b/enigma/src/test/java/cuchaz/enigma/inputs/translation/H_NamelessClass.java
index 013c55a..d996dc8 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/translation/H_NamelessClass.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/translation/H_NamelessClass.java
@@ -1,30 +1,29 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.translation; 12package cuchaz.enigma.inputs.translation;
13 13
14public class H_NamelessClass { 14public class H_NamelessClass {
15
16 public class A_InnerClass { 15 public class A_InnerClass {
17
18 public int f1; 16 public int f1;
19 public String f2; 17 public String f2;
20 18
21 public void m1() {} 19 public void m1() {
20 }
22 21
23 public class A_InnerInnerClass { 22 public class A_InnerInnerClass {
24
25 public int f3; 23 public int f3;
26 24
27 public void m2() {} 25 public void m2() {
26 }
28 } 27 }
29 } 28 }
30 29
@@ -32,9 +31,11 @@ public class H_NamelessClass {
32 public class A_NamedInnerClass { 31 public class A_NamedInnerClass {
33 public int f4; 32 public int f4;
34 33
35 public class A_AnotherInnerClass {} 34 public class A_AnotherInnerClass {
35 }
36 36
37 public class B_YetAnotherInnerClass {} 37 public class B_YetAnotherInnerClass {
38 }
38 } 39 }
39 } 40 }
40} 41}
diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/translation/I_Generics.java b/enigma/src/test/java/cuchaz/enigma/inputs/translation/I_Generics.java
index fd2ebdd..9a9048c 100644
--- a/enigma/src/test/java/cuchaz/enigma/inputs/translation/I_Generics.java
+++ b/enigma/src/test/java/cuchaz/enigma/inputs/translation/I_Generics.java
@@ -1,13 +1,13 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.inputs.translation; 12package cuchaz.enigma.inputs.translation;
13 13
@@ -15,7 +15,6 @@ import java.util.List;
15import java.util.Map; 15import java.util.Map;
16 16
17public class I_Generics { 17public class I_Generics {
18
19 public List<Integer> f1; 18 public List<Integer> f1;
20 public List<A_Type> f2; 19 public List<A_Type> f2;
21 public Map<A_Type, A_Type> f3; 20 public Map<A_Type, A_Type> f3;
diff --git a/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestComments.java b/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestComments.java
index e831943..15ec44e 100644
--- a/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestComments.java
+++ b/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestComments.java
@@ -5,6 +5,8 @@ import java.net.URISyntaxException;
5import java.nio.file.Path; 5import java.nio.file.Path;
6import java.nio.file.Paths; 6import java.nio.file.Paths;
7 7
8import org.junit.Test;
9
8import cuchaz.enigma.ProgressListener; 10import cuchaz.enigma.ProgressListener;
9import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; 11import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat;
10import cuchaz.enigma.translation.mapping.serde.MappingParseException; 12import cuchaz.enigma.translation.mapping.serde.MappingParseException;
@@ -12,28 +14,24 @@ import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
12import cuchaz.enigma.translation.mapping.serde.enigma.EnigmaMappingsReader; 14import cuchaz.enigma.translation.mapping.serde.enigma.EnigmaMappingsReader;
13import cuchaz.enigma.translation.mapping.serde.tinyv2.TinyV2Writer; 15import cuchaz.enigma.translation.mapping.serde.tinyv2.TinyV2Writer;
14import cuchaz.enigma.translation.mapping.tree.EntryTree; 16import cuchaz.enigma.translation.mapping.tree.EntryTree;
15import org.junit.Test;
16 17
17public class TestComments { 18public class TestComments {
18 private static Path DIRECTORY; 19 private static Path DIRECTORY;
19
20 static {
21 try {
22 DIRECTORY = Paths.get(TestTinyV2InnerClasses.class.getResource("/comments/").toURI());
23 } catch (URISyntaxException e) {
24 throw new RuntimeException(e);
25 }
26 }
27 20
28 @Test 21 static {
29 public void testParseAndWrite() throws IOException, MappingParseException { 22 try {
30 ProgressListener progressListener = ProgressListener.none(); 23 DIRECTORY = Paths.get(TestTinyV2InnerClasses.class.getResource("/comments/").toURI());
31 MappingSaveParameters params = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); 24 } catch (URISyntaxException e) {
32 EntryTree<EntryMapping> mappings = EnigmaMappingsReader.DIRECTORY.read( 25 throw new RuntimeException(e);
33 DIRECTORY, progressListener, params); 26 }
27 }
34 28
35 new TinyV2Writer("intermediary", "named") 29 @Test
36 .write(mappings, DIRECTORY.resolve("convertedtiny.tiny"), progressListener, params); 30 public void testParseAndWrite() throws IOException, MappingParseException {
37 } 31 ProgressListener progressListener = ProgressListener.none();
32 MappingSaveParameters params = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF);
33 EntryTree<EntryMapping> mappings = EnigmaMappingsReader.DIRECTORY.read(DIRECTORY, progressListener, params);
38 34
39} \ No newline at end of file 35 new TinyV2Writer("intermediary", "named").write(mappings, DIRECTORY.resolve("convertedtiny.tiny"), progressListener, params);
36 }
37}
diff --git a/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestReadWriteCycle.java b/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestReadWriteCycle.java
index 510dd3c..681fd3f 100644
--- a/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestReadWriteCycle.java
+++ b/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestReadWriteCycle.java
@@ -1,5 +1,11 @@
1package cuchaz.enigma.translation.mapping; 1package cuchaz.enigma.translation.mapping;
2 2
3import java.io.File;
4import java.io.IOException;
5
6import org.junit.Assert;
7import org.junit.Test;
8
3import cuchaz.enigma.ProgressListener; 9import cuchaz.enigma.ProgressListener;
4import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; 10import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat;
5import cuchaz.enigma.translation.mapping.serde.MappingFormat; 11import cuchaz.enigma.translation.mapping.serde.MappingFormat;
@@ -12,46 +18,25 @@ import cuchaz.enigma.translation.representation.entry.Entry;
12import cuchaz.enigma.translation.representation.entry.FieldEntry; 18import cuchaz.enigma.translation.representation.entry.FieldEntry;
13import cuchaz.enigma.translation.representation.entry.MethodEntry; 19import cuchaz.enigma.translation.representation.entry.MethodEntry;
14import cuchaz.enigma.utils.Pair; 20import cuchaz.enigma.utils.Pair;
15import org.junit.Assert;
16import org.junit.Test;
17
18import java.io.File;
19import java.io.IOException;
20 21
21/** 22/**
22 * Tests that a MappingFormat can write out a fixed set of mappings and read them back without losing any information. 23 * Tests that a MappingFormat can write out a fixed set of mappings and read them back without losing any information.
23 * Javadoc skipped for Tiny (v1) as it doesn't support them. 24 * Javadoc skipped for Tiny (v1) as it doesn't support them.
24 */ 25 */
25public class TestReadWriteCycle { 26public class TestReadWriteCycle {
26
27 private final MappingSaveParameters parameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); 27 private final MappingSaveParameters parameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF);
28 28
29 private final Pair<ClassEntry, EntryMapping> testClazz = new Pair<>( 29 private final Pair<ClassEntry, EntryMapping> testClazz = new Pair<>(new ClassEntry("a/b/c"), new EntryMapping("alpha/beta/charlie", "this is a test class"));
30 new ClassEntry("a/b/c"),
31 new EntryMapping("alpha/beta/charlie", "this is a test class")
32 );
33 30
34 private final Pair<FieldEntry, EntryMapping> testField1 = new Pair<>( 31 private final Pair<FieldEntry, EntryMapping> testField1 = new Pair<>(FieldEntry.parse("a/b/c", "field1", "I"), new EntryMapping("mapped1", "this is field 1"));
35 FieldEntry.parse("a/b/c", "field1", "I"),
36 new EntryMapping("mapped1", "this is field 1")
37 );
38 32
39 private final Pair<FieldEntry, EntryMapping> testField2 = new Pair<>( 33 private final Pair<FieldEntry, EntryMapping> testField2 = new Pair<>(FieldEntry.parse("a/b/c", "field2", "I"), new EntryMapping("mapped2", "this is field 2"));
40 FieldEntry.parse("a/b/c", "field2", "I"),
41 new EntryMapping("mapped2", "this is field 2")
42 );
43 34
44 private final Pair<MethodEntry, EntryMapping> testMethod1 = new Pair<>( 35 private final Pair<MethodEntry, EntryMapping> testMethod1 = new Pair<>(MethodEntry.parse("a/b/c", "method1", "()V"), new EntryMapping("mapped3", "this is method1"));
45 MethodEntry.parse("a/b/c", "method1", "()V"),
46 new EntryMapping("mapped3", "this is method1")
47 );
48 36
49 private final Pair<MethodEntry, EntryMapping> testMethod2 = new Pair<>( 37 private final Pair<MethodEntry, EntryMapping> testMethod2 = new Pair<>(MethodEntry.parse("a/b/c", "method2", "()V"), new EntryMapping("mapped4", "this is method 2"));
50 MethodEntry.parse("a/b/c", "method2", "()V"),
51 new EntryMapping("mapped4", "this is method 2")
52 );
53 38
54 private void insertMapping(EntryTree<EntryMapping> mappings, Pair<? extends Entry<?>, EntryMapping> mappingPair){ 39 private void insertMapping(EntryTree<EntryMapping> mappings, Pair<? extends Entry<?>, EntryMapping> mappingPair) {
55 mappings.insert(mappingPair.a, mappingPair.b); 40 mappings.insert(mappingPair.a, mappingPair.b);
56 } 41 }
57 42
@@ -71,8 +56,8 @@ public class TestReadWriteCycle {
71 Assert.assertTrue("Test mapping insertion failed: testMethod2", testMappings.contains(testMethod2.a)); 56 Assert.assertTrue("Test mapping insertion failed: testMethod2", testMappings.contains(testMethod2.a));
72 57
73 File tempFile = File.createTempFile("readWriteCycle", tmpNameSuffix); 58 File tempFile = File.createTempFile("readWriteCycle", tmpNameSuffix);
74 tempFile.delete();//remove the auto created file 59 //remove the auto created file
75 60 tempFile.delete();
76 61
77 mappingFormat.write(testMappings, tempFile.toPath(), ProgressListener.none(), parameters); 62 mappingFormat.write(testMappings, tempFile.toPath(), ProgressListener.none(), parameters);
78 Assert.assertTrue("Written file not created", tempFile.exists()); 63 Assert.assertTrue("Written file not created", tempFile.exists());
diff --git a/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestTinyV2InnerClasses.java b/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestTinyV2InnerClasses.java
index 60c70b7..659ac53 100644
--- a/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestTinyV2InnerClasses.java
+++ b/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestTinyV2InnerClasses.java
@@ -1,25 +1,25 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin. 2* Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials 3* All rights reserved. This program and the accompanying materials
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* <p>Contributors:
9 * Jeff Martin - initial API and implementation 9* Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10******************************************************************************/
11 11
12package cuchaz.enigma.translation.mapping; 12package cuchaz.enigma.translation.mapping;
13 13
14import java.nio.file.Path;
15import java.nio.file.Paths;
16
14import cuchaz.enigma.Enigma; 17import cuchaz.enigma.Enigma;
15import cuchaz.enigma.EnigmaProject; 18import cuchaz.enigma.EnigmaProject;
16import cuchaz.enigma.ProgressListener; 19import cuchaz.enigma.ProgressListener;
17import cuchaz.enigma.classprovider.ClasspathClassProvider; 20import cuchaz.enigma.classprovider.ClasspathClassProvider;
18import cuchaz.enigma.translation.mapping.serde.enigma.EnigmaMappingsReader; 21import cuchaz.enigma.translation.mapping.serde.enigma.EnigmaMappingsReader;
19 22
20import java.nio.file.Path;
21import java.nio.file.Paths;
22
23public final class TestTinyV2InnerClasses { 23public final class TestTinyV2InnerClasses {
24 private Path jar; 24 private Path jar;
25 private Path mappings; 25 private Path mappings;
@@ -29,10 +29,9 @@ public final class TestTinyV2InnerClasses {
29 mappings = Paths.get(TestTinyV2InnerClasses.class.getResource("/tinyV2InnerClasses/").toURI()); 29 mappings = Paths.get(TestTinyV2InnerClasses.class.getResource("/tinyV2InnerClasses/").toURI());
30 } 30 }
31 31
32// @Test 32 // @Test
33 public void testMappings() throws Exception { 33 public void testMappings() throws Exception {
34 EnigmaProject project = Enigma.create().openJar(jar, new ClasspathClassProvider(), ProgressListener.none()); 34 EnigmaProject project = Enigma.create().openJar(jar, new ClasspathClassProvider(), ProgressListener.none());
35 project.setMappings(EnigmaMappingsReader.DIRECTORY.read(mappings, ProgressListener.none(), project.getEnigma().getProfile().getMappingSaveParameters())); 35 project.setMappings(EnigmaMappingsReader.DIRECTORY.read(mappings, ProgressListener.none(), project.getEnigma().getProfile().getMappingSaveParameters()));
36
37 } 36 }
38} 37}
diff --git a/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestV2Main.java b/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestV2Main.java
index 6e4d7b9..cc08b85 100644
--- a/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestV2Main.java
+++ b/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestV2Main.java
@@ -1,5 +1,8 @@
1package cuchaz.enigma.translation.mapping; 1package cuchaz.enigma.translation.mapping;
2 2
3import java.nio.file.Path;
4import java.nio.file.Paths;
5
3import cuchaz.enigma.ProgressListener; 6import cuchaz.enigma.ProgressListener;
4import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; 7import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat;
5import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; 8import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
@@ -7,9 +10,6 @@ import cuchaz.enigma.translation.mapping.serde.enigma.EnigmaMappingsReader;
7import cuchaz.enigma.translation.mapping.serde.tinyv2.TinyV2Writer; 10import cuchaz.enigma.translation.mapping.serde.tinyv2.TinyV2Writer;
8import cuchaz.enigma.translation.mapping.tree.EntryTree; 11import cuchaz.enigma.translation.mapping.tree.EntryTree;
9 12
10import java.nio.file.Path;
11import java.nio.file.Paths;
12
13public final class TestV2Main { 13public final class TestV2Main {
14 public static void main(String... args) throws Exception { 14 public static void main(String... args) throws Exception {
15 Path path = Paths.get(TestV2Main.class.getResource("/tinyV2InnerClasses/").toURI()); 15 Path path = Paths.get(TestV2Main.class.getResource("/tinyV2InnerClasses/").toURI());
diff --git a/enigma/src/test/java/cuchaz/enigma/translation/mapping/serde/recaf/TestRecaf.java b/enigma/src/test/java/cuchaz/enigma/translation/mapping/serde/recaf/TestRecaf.java
index 1026f57..bd1ec20 100644
--- a/enigma/src/test/java/cuchaz/enigma/translation/mapping/serde/recaf/TestRecaf.java
+++ b/enigma/src/test/java/cuchaz/enigma/translation/mapping/serde/recaf/TestRecaf.java
@@ -1,11 +1,6 @@
1package cuchaz.enigma.translation.mapping.serde.recaf; 1package cuchaz.enigma.translation.mapping.serde.recaf;
2 2
3import com.google.common.collect.Sets; 3import static org.junit.Assert.assertEquals;
4import com.google.common.jimfs.Jimfs;
5import cuchaz.enigma.ProgressListener;
6import cuchaz.enigma.translation.mapping.EntryMapping;
7import cuchaz.enigma.translation.mapping.tree.EntryTree;
8import org.junit.Test;
9 4
10import java.io.InputStream; 5import java.io.InputStream;
11import java.nio.charset.StandardCharsets; 6import java.nio.charset.StandardCharsets;
@@ -15,32 +10,37 @@ import java.nio.file.Path;
15import java.util.HashSet; 10import java.util.HashSet;
16import java.util.Set; 11import java.util.Set;
17 12
18import static org.junit.Assert.assertEquals; 13import com.google.common.collect.Sets;
14import com.google.common.jimfs.Jimfs;
15import org.junit.Test;
19 16
20public class TestRecaf { 17import cuchaz.enigma.ProgressListener;
18import cuchaz.enigma.translation.mapping.EntryMapping;
19import cuchaz.enigma.translation.mapping.tree.EntryTree;
21 20
22 @Test 21public class TestRecaf {
23 public void testIntegrity() throws Exception { 22 @Test
24 Set<String> contents; 23 public void testIntegrity() throws Exception {
25 try (InputStream in = getClass().getResourceAsStream("/recaf.mappings")) { 24 Set<String> contents;
26 contents = Sets.newHashSet(new String(in.readAllBytes(), StandardCharsets.UTF_8).split("\\R"));
27 }
28 25
29 try (FileSystem fs = Jimfs.newFileSystem()) { 26 try (InputStream in = getClass().getResourceAsStream("/recaf.mappings")) {
27 contents = Sets.newHashSet(new String(in.readAllBytes(), StandardCharsets.UTF_8).split("\\R"));
28 }
30 29
31 Path path = fs.getPath("recaf.mappings"); 30 try (FileSystem fs = Jimfs.newFileSystem()) {
32 Files.writeString(path, String.join("\n", contents)); 31 Path path = fs.getPath("recaf.mappings");
32 Files.writeString(path, String.join("\n", contents));
33 33
34 RecafMappingsWriter writer = RecafMappingsWriter.INSTANCE; 34 RecafMappingsWriter writer = RecafMappingsWriter.INSTANCE;
35 RecafMappingsReader reader = RecafMappingsReader.INSTANCE; 35 RecafMappingsReader reader = RecafMappingsReader.INSTANCE;
36 36
37 EntryTree<EntryMapping> mappings = reader.read(path, ProgressListener.none(), null); 37 EntryTree<EntryMapping> mappings = reader.read(path, ProgressListener.none(), null);
38 writer.write(mappings, path, ProgressListener.none(), null); 38 writer.write(mappings, path, ProgressListener.none(), null);
39 39
40 reader.read(path, ProgressListener.none(), null); 40 reader.read(path, ProgressListener.none(), null);
41 Set<String> newContents = new HashSet<>(Files.readAllLines(path)); 41 Set<String> newContents = new HashSet<>(Files.readAllLines(path));
42 42
43 assertEquals(contents, newContents); 43 assertEquals(contents, newContents);
44 } 44 }
45 } 45 }
46} 46}