Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stats rework #152

Merged
merged 17 commits into from
Sep 30, 2023
82 changes: 2 additions & 80 deletions enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java
Original file line number Diff line number Diff line change
@@ -1,29 +1,19 @@
package cuchaz.enigma.gui;

import cuchaz.enigma.gui.config.keybind.KeyBinds;
import cuchaz.enigma.gui.elements.ClassTreeCellRenderer;
import cuchaz.enigma.gui.node.ClassSelectorClassNode;
import cuchaz.enigma.gui.node.SortedMutableTreeNode;
import cuchaz.enigma.stats.StatType;
import cuchaz.enigma.gui.util.StatsManager;
import cuchaz.enigma.stats.StatsResult;
import cuchaz.enigma.gui.util.GuiUtil;
import cuchaz.enigma.translation.representation.entry.ClassEntry;
import cuchaz.enigma.utils.I18n;

import javax.annotation.Nullable;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.awt.Component;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
Expand All @@ -34,15 +24,13 @@ public class ClassSelector extends JTree {

private final Comparator<ClassEntry> comparator;
private final GuiController controller;
private final StatsManager statsManager;

private NestedPackages packageManager;
private ClassSelectionListener selectionListener;

public ClassSelector(Gui gui, Comparator<ClassEntry> comparator) {
this.comparator = comparator;
this.controller = gui.getController();
this.statsManager = gui.getStatsManager();

// configure the tree control
this.setEditable(false);
Expand Down Expand Up @@ -83,64 +71,7 @@ public ClassSelector(Gui gui, Comparator<ClassEntry> comparator) {
}
}));

this.setCellRenderer(new DefaultTreeCellRenderer() {
{
this.setLeafIcon(null);
}

@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);

if (gui.getController().getProject() != null && leaf && value instanceof ClassSelectorClassNode node) {
class TooltipPanel extends JPanel {
@Override
public String getToolTipText(MouseEvent event) {
StringBuilder text = new StringBuilder(I18n.translateFormatted("class_selector.tooltip.stats_for", node.getDeobfEntry().getSimpleName()));
text.append(System.lineSeparator());
StatsResult stats = ClassSelector.this.statsManager.getStats(node);

if (stats == null) {
text.append(I18n.translate("class_selector.tooltip.stats_not_generated"));
} else {
if ((event.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) != 0) {
for (int i = 0; i < StatType.values().length; i++) {
StatType type = StatType.values()[i];
text.append(type.getName()).append(": ").append(stats.toString(type)).append(i == StatType.values().length - 1 ? "" : "\n");
}
} else {
text.append(stats);
}
}

return text.toString();
}
}

JPanel panel = new TooltipPanel();
panel.setOpaque(false);
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
JLabel nodeLabel = new JLabel(GuiUtil.getClassIcon(gui, node.getObfEntry()));
panel.add(nodeLabel);

StatsResult stats = ClassSelector.this.statsManager.getStats(node);
if (stats == null) {
// calculate stats on a separate thread for performance reasons
this.setIcon(GuiUtil.PENDING_STATUS_ICON);
node.reloadStats(gui, ClassSelector.this, false);
} else {
this.setIcon(GuiUtil.getDeobfuscationIcon(stats));
}

panel.add(this);

return panel;
}

return this;
}
});

this.setCellRenderer(new ClassTreeCellRenderer(gui, this));
ToolTipManager.sharedInstance().registerComponent(this);

// init defaults
Expand Down Expand Up @@ -182,7 +113,6 @@ public void setClasses(@Nullable Collection<ClassEntry> classEntries) {
// update the tree control
this.packageManager = new NestedPackages(classEntries, this.comparator, this.controller.getProject().getMapper());
this.setModel(new DefaultTreeModel(this.packageManager.getRoot()));
this.invalidateStats();

this.restoreExpansionState(state);
}
Expand Down Expand Up @@ -360,14 +290,6 @@ public void reload() {
this.reload(this.packageManager.getRoot(), true);
}

/**
* Invalidates the stats for all classes in the tree, forcing them to be reloaded.
* Stats will be calculated asynchronously for each entry the next time that entry is visible.
*/
public void invalidateStats() {
this.statsManager.invalidateStats();
}

/**
* Requests an asynchronous reload of the stats for the given class.
* On completion, the class's stats icon will be updated.
Expand Down
10 changes: 0 additions & 10 deletions enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,8 @@
import cuchaz.enigma.gui.util.GuiUtil;
import cuchaz.enigma.gui.util.LanguageUtil;
import cuchaz.enigma.gui.util.ScaleUtil;
import cuchaz.enigma.gui.util.StatsManager;
import cuchaz.enigma.network.ServerMessage;
import cuchaz.enigma.source.Token;
import cuchaz.enigma.stats.StatsGenerator;
import cuchaz.enigma.translation.mapping.EntryChange;
import cuchaz.enigma.translation.representation.entry.ClassEntry;
import cuchaz.enigma.translation.representation.entry.Entry;
Expand Down Expand Up @@ -93,7 +91,6 @@ public class Gui {

private final JLabel connectionStatusLabel;
private final NotificationManager notificationManager;
private final StatsManager statsManager;

public final JFileChooser jarFileChooser;
public final JFileChooser tinyMappingsFileChooser;
Expand Down Expand Up @@ -126,7 +123,6 @@ public Gui(EnigmaProfile profile, Set<EditableType> editableTypes, boolean visib
this.connectionStatusLabel = new JLabel();
this.notificationManager = new NotificationManager(this);
this.searchDialog = new SearchDialog(this);
this.statsManager = new StatsManager();

this.showsProgressBars = true;

Expand Down Expand Up @@ -272,10 +268,6 @@ public GuiController getController() {
return this.controller;
}

public StatsManager getStatsManager() {
return this.statsManager;
}

public List<Throwable> getCrashHistory() {
return this.crashHistory;
}
Expand All @@ -298,8 +290,6 @@ public void onFinishOpenJar(String jarName) {
this.mainWindow.setTitle(Enigma.NAME + " - " + jarName);
this.editorTabbedPane.closeAllEditorTabs();

this.statsManager.setStatsGenerator(new StatsGenerator(this.controller.getProject()));

// update menu
this.isJarOpen = true;

Expand Down
21 changes: 17 additions & 4 deletions enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package cuchaz.enigma.gui;

import com.google.gson.GsonBuilder;
import cuchaz.enigma.Enigma;
import cuchaz.enigma.EnigmaProfile;
import cuchaz.enigma.EnigmaProject;
Expand Down Expand Up @@ -38,6 +39,8 @@
import cuchaz.enigma.source.SourceIndex;
import cuchaz.enigma.source.Token;
import cuchaz.enigma.stats.StatsGenerator;
import cuchaz.enigma.stats.StatsResult;
import cuchaz.enigma.stats.StatsTree;
import cuchaz.enigma.translation.TranslateResult;
import cuchaz.enigma.translation.Translator;
import cuchaz.enigma.translation.mapping.EntryChange;
Expand Down Expand Up @@ -86,6 +89,7 @@ public class GuiController implements ClientPacketHandler {

private EnigmaProject project;
private IndexTreeBuilder indexTreeBuilder;
private StatsGenerator statsGenerator;

private Path loadedMappingPath;
private MappingFormat loadedMappingFormat;
Expand Down Expand Up @@ -117,6 +121,8 @@ public CompletableFuture<Void> openJar(final Path jarPath) {
this.project = this.enigma.openJar(jarPath, new ClasspathClassProvider(), progress);
this.indexTreeBuilder = new IndexTreeBuilder(this.project.getJarIndex());
this.chp = new ClassHandleProvider(this.project, UiConfig.getDecompiler().service);
this.statsGenerator = new StatsGenerator(this.project);

SwingUtilities.invokeLater(() -> {
this.gui.onFinishOpenJar(jarPath.getFileName().toString());
this.refreshClasses();
Expand All @@ -128,6 +134,7 @@ public void closeJar() {
this.chp.destroy();
this.chp = null;
this.project = null;
this.statsGenerator = null;
this.gui.onCloseJar();
}

Expand All @@ -154,7 +161,7 @@ public CompletableFuture<Void> openMappings(MappingFormat format, Path path) {

this.refreshClasses();
this.chp.invalidateJavadoc();
this.gui.getStatsManager().setStatsGenerator(new StatsGenerator(this.project));
this.statsGenerator = new StatsGenerator(this.project);
} catch (MappingParseException e) {
JOptionPane.showMessageDialog(this.gui.getFrame(), e.getMessage());
}
Expand Down Expand Up @@ -539,17 +546,19 @@ private void applyChange0(ValidationContext vc, EntryChange<?> change, boolean u
}
}

public void openStats(Set<StatType> includedTypes, String topLevelPackage, boolean includeSynthetic) {
public void openStatsTree(Set<StatType> includedTypes) {
ProgressDialog.runOffThread(this.gui, progress -> {
String data = this.gui.getStatsManager().getGenerator().generate(progress, includedTypes, topLevelPackage, includeSynthetic).getTreeJson();
StatsResult overall = this.getStatsGenerator().getResultNullable().getOverall();
StatsTree<Integer> tree = overall.buildTree(UiConfig.getLastTopLevelPackage(), includedTypes);
String treeJson = new GsonBuilder().setPrettyPrinting().create().toJson(tree.root);
ix0rai marked this conversation as resolved.
Show resolved Hide resolved

try {
File statsFile = File.createTempFile("stats", ".html");

try (FileWriter w = new FileWriter(statsFile)) {
w.write(
Utils.readResourceToString("/stats.html")
.replace("/*data*/", data)
.replace("/*data*/", treeJson)
);
}

Expand Down Expand Up @@ -586,6 +595,10 @@ public Enigma getEnigma() {
return this.enigma;
}

public StatsGenerator getStatsGenerator() {
return this.statsGenerator;
}

public void createClient(String username, String ip, int port, char[] password) throws IOException {
this.client = new EnigmaClient(this, ip, port);
this.client.connect();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import cuchaz.enigma.gui.Gui;
import cuchaz.enigma.gui.config.UiConfig;
import cuchaz.enigma.stats.ProjectStatsResult;
import cuchaz.enigma.stats.StatType;
import cuchaz.enigma.stats.StatsResult;
import cuchaz.enigma.gui.util.GridBagConstraintsBuilder;
import cuchaz.enigma.gui.util.ScaleUtil;
import cuchaz.enigma.utils.I18n;
Expand All @@ -27,13 +27,12 @@
public class StatsDialog {
public static void show(Gui gui) {
ProgressDialog.runOffThread(gui, listener -> {
StatsResult result = gui.getStatsManager().getGenerator().generate(listener, Set.of(StatType.values()), "", false);

ProjectStatsResult result = gui.getController().getStatsGenerator().getResult(false);
SwingUtilities.invokeLater(() -> show(gui, result, ""));
});
}

public static void show(Gui gui, StatsResult result, String packageName) {
public static void show(Gui gui, ProjectStatsResult result, String packageName) {
// init frame
JDialog dialog = new JDialog(gui.getFrame(), packageName.isEmpty() ? I18n.translate("menu.file.stats.title") : I18n.translateFormatted("menu.file.stats.title_filtered", packageName), true);
Container contentPane = dialog.getContentPane();
Expand All @@ -44,7 +43,7 @@ public static void show(Gui gui, StatsResult result, String packageName) {
Map<StatType, JCheckBox> checkboxes = new EnumMap<>(StatType.class);

final int[] i = {0};
result.getTypes().stream().sorted(Comparator.comparing(StatType::getName)).forEach(type -> {
result.getOverall().getTypes().stream().sorted(Comparator.comparing(StatType::getName)).forEach(type -> {
JCheckBox checkBox = new JCheckBox(type.getName());
checkboxes.put(type, checkBox);
contentPane.add(checkBox, cb.pos(0, i[0]).weightX(1.0).anchor(GridBagConstraints.WEST).build());
Expand All @@ -63,31 +62,30 @@ public static void show(Gui gui, StatsResult result, String packageName) {

// show top-level package option
JLabel topLevelPackageOption = new JLabel(I18n.translate("menu.file.stats.top_level_package"));
contentPane.add(topLevelPackageOption, cb1.pos(0, result.getTypes().size() + 1).build());
contentPane.add(topLevelPackageOption, cb1.pos(0, result.getOverall().getTypes().size() + 1).build());

JTextField topLevelPackage = new JTextField();
topLevelPackage.setText(UiConfig.getLastTopLevelPackage());
contentPane.add(topLevelPackage, cb1.pos(0, result.getTypes().size() + 2).fill(GridBagConstraints.HORIZONTAL).build());
contentPane.add(topLevelPackage, cb1.pos(0, result.getOverall().getTypes().size() + 2).fill(GridBagConstraints.HORIZONTAL).build());

// show synthetic members option
JCheckBox syntheticParametersOption = new JCheckBox(I18n.translate("menu.file.stats.synthetic_parameters"));
syntheticParametersOption.setSelected(UiConfig.shouldIncludeSyntheticParameters());
contentPane.add(syntheticParametersOption, cb1.pos(0, result.getOverall().getTypes().size() + 4).build());

// Show filter button
// show filter button
JButton filterButton = new JButton(I18n.translate("menu.file.stats.filter"));
filterButton.addActionListener(action -> {
dialog.dispose();
ProgressDialog.runOffThread(gui, listener -> {
UiConfig.setLastTopLevelPackage(topLevelPackage.getText());
UiConfig.save();

StatsResult statResult = gui.getStatsManager().getGenerator().generate(listener, Set.of(StatType.values()), UiConfig.getLastTopLevelPackage(), false);

SwingUtilities.invokeLater(() -> show(gui, statResult, UiConfig.getLastTopLevelPackage()));
ProjectStatsResult projectResult = gui.getController().getStatsGenerator().getResult(syntheticParametersOption.isSelected()).filter(UiConfig.getLastTopLevelPackage());
SwingUtilities.invokeLater(() -> show(gui, projectResult, UiConfig.getLastTopLevelPackage()));
});
});
contentPane.add(filterButton, cb1.pos(0, result.getTypes().size() + 3).anchor(GridBagConstraints.EAST).build());

// show synthetic members option
JCheckBox syntheticParametersOption = new JCheckBox(I18n.translate("menu.file.stats.synthetic_parameters"));
syntheticParametersOption.setSelected(UiConfig.shouldIncludeSyntheticParameters());
contentPane.add(syntheticParametersOption, cb1.pos(0, result.getTypes().size() + 4).build());
contentPane.add(filterButton, cb1.pos(0, result.getOverall().getTypes().size() + 3).anchor(GridBagConstraints.EAST).build());

// show generate button
JButton button = new JButton(I18n.translate("menu.file.stats.generate"));
Expand All @@ -99,10 +97,10 @@ public static void show(Gui gui, StatsResult result, String packageName) {
UiConfig.setIncludeSyntheticParameters(syntheticParametersOption.isSelected());
UiConfig.save();

generateStats(gui, checkboxes, topLevelPackage.getText(), syntheticParametersOption.isSelected());
generateStats(gui, checkboxes);
});

contentPane.add(button, cb1.pos(0, result.getTypes().size() + 5).weightY(1.0).anchor(GridBagConstraints.SOUTHWEST).build());
contentPane.add(button, cb1.pos(0, result.getOverall().getTypes().size() + 5).weightY(1.0).anchor(GridBagConstraints.SOUTHWEST).build());

// add action listener to each checkbox
checkboxes.forEach((key, value) -> value.addActionListener(action -> {
Expand All @@ -123,7 +121,7 @@ public static void show(Gui gui, StatsResult result, String packageName) {
dialog.setVisible(true);
}

private static void generateStats(Gui gui, Map<StatType, JCheckBox> checkboxes, String topLevelPackage, boolean includeSynthetic) {
private static void generateStats(Gui gui, Map<StatType, JCheckBox> checkboxes) {
// get members from selected checkboxes
Set<StatType> includedMembers = checkboxes
.entrySet()
Expand All @@ -134,7 +132,7 @@ private static void generateStats(Gui gui, Map<StatType, JCheckBox> checkboxes,

// checks if a project is open
if (gui.getController().getProject() != null) {
gui.getController().openStats(includedMembers, topLevelPackage, includeSynthetic);
gui.getController().openStatsTree(includedMembers);
}
}
}
Loading