bcv-vf/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourceviewer/DecompilerSelectionPane.java

215 lines
8.3 KiB
Java

package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import javax.swing.AbstractButton;
import javax.swing.ButtonGroup;
import javax.swing.JMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JSeparator;
import the.bytecode.club.bytecodeviewer.Configuration;
import the.bytecode.club.bytecodeviewer.SettingsSerializer;
import the.bytecode.club.bytecodeviewer.bootloader.BootState;
import the.bytecode.club.bytecodeviewer.decompilers.Decompiler;
import the.bytecode.club.bytecodeviewer.gui.components.DecompilerViewComponent;
import the.bytecode.club.bytecodeviewer.translation.TranslatedComponents;
import the.bytecode.club.bytecodeviewer.translation.components.TranslatedJMenu;
import the.bytecode.club.bytecodeviewer.translation.components.TranslatedJRadioButtonMenuItem;
import static the.bytecode.club.bytecodeviewer.gui.components.DecompilerViewComponent.DecompilerComponentType.BYTECODE;
import static the.bytecode.club.bytecodeviewer.gui.components.DecompilerViewComponent.DecompilerComponentType.BYTECODE_NON_EDITABLE;
import static the.bytecode.club.bytecodeviewer.gui.components.DecompilerViewComponent.DecompilerComponentType.JAVA;
import static the.bytecode.club.bytecodeviewer.gui.components.DecompilerViewComponent.DecompilerComponentType.JAVA_AND_BYTECODE;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
* Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
/**
* @author Konloch
* @since 6/21/2021
*/
public class DecompilerSelectionPane
{
private final int paneID;
private final JMenu menu;
private final ButtonGroup group = new ButtonGroup();
private final JRadioButtonMenuItem none = new TranslatedJRadioButtonMenuItem("None", TranslatedComponents.NONE);
private final JRadioButtonMenuItem hexcode = new TranslatedJRadioButtonMenuItem("Hexcode", TranslatedComponents.HEXCODE);
private final DecompilerViewComponent procyon = new DecompilerViewComponent("Procyon", JAVA, Decompiler.PROCYON_DECOMPILER);
private final DecompilerViewComponent CFR = new DecompilerViewComponent("CFR", JAVA, Decompiler.CFR_DECOMPILER);
private final DecompilerViewComponent JADX = new DecompilerViewComponent("JADX", JAVA, Decompiler.JADX_DECOMPILER);
private final DecompilerViewComponent JD = new DecompilerViewComponent("JD-GUI", JAVA, Decompiler.JD_DECOMPILER);
private final DecompilerViewComponent fern = new DecompilerViewComponent("FernFlower", JAVA, Decompiler.FERNFLOWER_DECOMPILER);
private final DecompilerViewComponent krakatau = new DecompilerViewComponent( "Krakatau", JAVA_AND_BYTECODE, Decompiler.KRAKATAU_DECOMPILER, Decompiler.KRAKATAU_DISASSEMBLER);
private final DecompilerViewComponent smali = new DecompilerViewComponent("Smali", BYTECODE, Decompiler.SMALI_DISASSEMBLER);
private final DecompilerViewComponent bytecode = new DecompilerViewComponent("Bytecode", BYTECODE_NON_EDITABLE, Decompiler.BYTECODE_DISASSEMBLER);
private final DecompilerViewComponent asmTextify = new DecompilerViewComponent("ASM Textify", BYTECODE_NON_EDITABLE, Decompiler.ASM_TEXTIFY_DISASSEMBLER);
private final DecompilerViewComponent javap = new DecompilerViewComponent("Javap", BYTECODE_NON_EDITABLE, Decompiler.JAVAP_DISASSEMBLER);
//TODO when adding new decompilers insert the DecompilerViewComponent object into here
// also in the group, then finally the build menu
public List<DecompilerViewComponent> components = new ArrayList<>(Arrays.asList(
procyon, CFR, JADX, JD, fern, krakatau, smali, bytecode, asmTextify, javap));
public DecompilerSelectionPane(int paneID)
{
this.paneID = paneID;
if(paneID == 1)
this.menu = new TranslatedJMenu("Pane " + 1, TranslatedComponents.PANE_1);
else if(paneID == 2)
this.menu = new TranslatedJMenu("Pane " + 2, TranslatedComponents.PANE_2);
else
this.menu = new TranslatedJMenu("Pane " + paneID, TranslatedComponents.PANE_3);
buildMenu();
}
/**
* Sets the default decompilers for each pane
*/
public void setDefault()
{
switch(paneID)
{
case 1:
group.setSelected(fern.getJava().getModel(), true);
break;
case 2:
group.setSelected(bytecode.getBytecode().getModel(), true);
break;
case 3:
group.setSelected(none.getModel(), true);
break;
}
}
/**
* Builds the Decompiler View menu
*/
public void buildMenu()
{
//build the radiobutton group
group.add(none);
group.add(hexcode);
components.forEach(decompilerViewComponent -> decompilerViewComponent.addToGroup(group));
//build the action commands
none.setActionCommand(Decompiler.NONE.name());
hexcode.setActionCommand(Decompiler.HEXCODE_VIEWER.name());
for(DecompilerViewComponent component : components)
{
for(Decompiler decompiler : component.getDecompilers())
{
String cmd = decompiler.name();
//TODO this is pretty janky and will break if a decompiler doesn't end with _DECOMPILER suffix
if(cmd.endsWith("DECOMPILER"))
component.getJava().setActionCommand(cmd);
else// if(cmd.endsWith("DISASSEMBLER"))
component.getBytecode().setActionCommand(cmd);
}
}
//auto-save on decompiler change
Enumeration<AbstractButton> it = group.getElements();
while(it.hasMoreElements())
{
AbstractButton button = it.nextElement();
button.addActionListener((event)->
{
if(Configuration.bootState != BootState.GUI_SHOWING)
return;
SettingsSerializer.saveSettingsAsync();
});
}
//build the menu
menu.add(none);
menu.add(new JSeparator());
menu.add(procyon.getMenu());
menu.add(CFR.getMenu());
if(!Configuration.jadxGroupedWithSmali)
menu.add(JADX.getMenu());
menu.add(JD.getMenu());
menu.add(fern.getMenu());
menu.add(krakatau.getMenu());
menu.add(new JSeparator());
if(Configuration.jadxGroupedWithSmali)
menu.add(JADX.getMenu());
menu.add(smali.getMenu());
menu.add(new JSeparator());
menu.add(bytecode.getMenu());
menu.add(javap.getMenu());
menu.add(asmTextify.getMenu());
menu.add(new JSeparator());
menu.add(hexcode);
}
public Decompiler getSelectedDecompiler()
{
return Decompiler.valueOf(group.getSelection().getActionCommand());
}
public void setSelectedDecompiler(Decompiler decompiler)
{
Enumeration<AbstractButton> it = group.getElements();
while(it.hasMoreElements())
{
AbstractButton button = it.nextElement();
if(button.getActionCommand().equals(decompiler.name()))
{
group.setSelected(button.getModel(), true);
break;
}
}
}
public boolean isPaneEditable()
{
String cmd = group.getSelection().getActionCommand();
for(DecompilerViewComponent component : components)
for (Decompiler decompiler : component.getDecompilers())
if(decompiler.name().equalsIgnoreCase(cmd))
return component.getEditable().isSelected();
return false;
}
public void setPaneEditable(boolean value)
{
String cmd = group.getSelection().getActionCommand();
for(DecompilerViewComponent component : components)
for (Decompiler decompiler : component.getDecompilers())
if(decompiler.name().equalsIgnoreCase(cmd))
{
component.getEditable().setSelected(value);
return;
}
}
public JMenu getMenu()
{
return menu;
}
}