From 467df0ee32517499a750a01aacfa724d7d5f2e36 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Mon, 6 Jul 2020 12:19:24 +0100 Subject: Improve stats gen to show percentages (#272) * Improve stats gen to show percentages * Improve stats dialog * Update stats dialog title and button text Co-authored-by: 2xsaiko --- .../main/java/cuchaz/enigma/gui/GuiController.java | 2 +- .../java/cuchaz/enigma/gui/dialog/StatsDialog.java | 136 +++++++++++++++------ .../java/cuchaz/enigma/gui/elements/MenuBar.java | 1 + .../cuchaz/enigma/gui/stats/StatsGenerator.java | 70 ++--------- .../java/cuchaz/enigma/gui/stats/StatsResult.java | 105 ++++++++++++++++ 5 files changed, 212 insertions(+), 102 deletions(-) create mode 100644 enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java (limited to 'enigma-swing/src/main/java/cuchaz') 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 b62e9cf..124ad07 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java @@ -492,7 +492,7 @@ public class GuiController implements ClientPacketHandler { public void openStats(Set includedMembers, String topLevelPackage) { ProgressDialog.runOffThread(gui.getFrame(), progress -> { - String data = new StatsGenerator(project).generate(progress, includedMembers, topLevelPackage); + String data = new StatsGenerator(project).generate(progress, includedMembers, topLevelPackage).getTreeJson(); try { File statsFile = File.createTempFile("stats", ".html"); 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 d8d3acd..c691d75 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 @@ -1,74 +1,132 @@ package cuchaz.enigma.gui.dialog; -import java.awt.*; -import java.util.Arrays; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.util.Collections; +import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import javax.swing.*; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; import cuchaz.enigma.gui.Gui; +import cuchaz.enigma.gui.stats.StatsGenerator; import cuchaz.enigma.gui.stats.StatsMember; +import cuchaz.enigma.gui.stats.StatsResult; import cuchaz.enigma.gui.util.ScaleUtil; import cuchaz.enigma.utils.I18n; public class StatsDialog { public static void show(Gui gui) { + ProgressDialog.runOffThread(gui.getFrame(), listener -> { + final StatsGenerator statsGenerator = new StatsGenerator(gui.getController().project); + final Map results = new HashMap<>(); + for (StatsMember member : StatsMember.values()) { + results.put(member, statsGenerator.generate(listener, Collections.singleton(member), "")); + } + SwingUtilities.invokeLater(() -> show(gui, results)); + }); + } + + public static void show(Gui gui, Map results) { // init frame - JFrame frame = new JFrame(I18n.translate("menu.file.stats.title")); - JPanel checkboxesPanel = new JPanel(); - JPanel topLevelPackagePanel = new JPanel(); - JPanel buttonPanel = new JPanel(); - frame.setLayout(new GridLayout(3, 0)); - frame.add(checkboxesPanel); - frame.add(topLevelPackagePanel); - frame.add(buttonPanel); - - // show checkboxes - Map checkboxes = Arrays - .stream(StatsMember.values()) - .collect(Collectors.toMap(m -> m, m -> { - JCheckBox checkbox = new JCheckBox(I18n.translate("type." + m.name().toLowerCase(Locale.ROOT))); - checkboxesPanel.add(checkbox); - return checkbox; - })); + JDialog dialog = new JDialog(gui.getFrame(), I18n.translate("menu.file.stats.title"), true); + Container contentPane = dialog.getContentPane(); + contentPane.setLayout(new GridBagLayout()); + + GridBagConstraints c = new GridBagConstraints(); + c.insets = ScaleUtil.getInsets(4, 4, 4, 4); + c.gridy = 0; + + Map checkboxes = new HashMap<>(); + + results.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(e -> { + StatsMember m = e.getKey(); + StatsResult result = e.getValue(); + + c.gridx = 0; + c.weightx = 1.0; + c.anchor = GridBagConstraints.WEST; + JCheckBox checkBox = new JCheckBox(I18n.translate("type." + m.name().toLowerCase(Locale.ROOT))); + checkboxes.put(m, checkBox); + contentPane.add(checkBox, c); + + c.gridx = 1; + c.weightx = 0.0; + c.anchor = GridBagConstraints.EAST; + contentPane.add(new JLabel(Integer.toString(result.getMapped())), c); + + c.gridx = 2; + contentPane.add(new JLabel("/"), c); + + c.gridx = 3; + contentPane.add(new JLabel(Integer.toString(result.getTotal())), c); + + c.gridx = 4; + contentPane.add(new JLabel(String.format("%.2f%%", result.getPercentage())), c); + + c.gridy += 1; + }); + + c.gridx = 0; + c.gridwidth = 5; + c.weightx = 1.0; + c.anchor = GridBagConstraints.WEST; // show top-level package option JLabel topLevelPackageOption = new JLabel(I18n.translate("menu.file.stats.top_level_package")); + contentPane.add(topLevelPackageOption, c); + + c.gridy += 1; + c.weightx = 1.0; + c.fill = GridBagConstraints.HORIZONTAL; JTextField topLevelPackage = new JTextField(); - topLevelPackage.setPreferredSize(ScaleUtil.getDimension(200, 25)); - topLevelPackagePanel.add(topLevelPackageOption); - topLevelPackagePanel.add(topLevelPackage); + contentPane.add(topLevelPackage, c); + + c.gridy += 1; + c.weighty = 1.0; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.SOUTHEAST; // show generate button JButton button = new JButton(I18n.translate("menu.file.stats.generate")); - buttonPanel.add(button); button.setEnabled(false); button.addActionListener(action -> { - frame.dispose(); + dialog.dispose(); generateStats(gui, checkboxes, topLevelPackage.getText()); }); + contentPane.add(button, c); + // add action listener to each checkbox - checkboxes.entrySet().forEach(checkbox -> { - checkbox.getValue().addActionListener(action -> { - if (!button.isEnabled()) { - button.setEnabled(true); - } else if (checkboxes.entrySet().stream().allMatch(entry -> !entry.getValue().isSelected())) { - button.setEnabled(false); - } - }); - }); + checkboxes.forEach((key, value) -> value.addActionListener(action -> { + if (!button.isEnabled()) { + button.setEnabled(true); + } else if (checkboxes.entrySet().stream().noneMatch(entry -> entry.getValue().isSelected())) { + button.setEnabled(false); + } + })); // show the frame - frame.pack(); - frame.setVisible(true); - frame.setSize(ScaleUtil.getDimension(500, 150)); - frame.setResizable(false); - frame.setLocationRelativeTo(gui.getFrame()); + dialog.pack(); + Dimension size = dialog.getSize(); + dialog.setMinimumSize(size); + size.width = ScaleUtil.scale(350); + dialog.setSize(size); + dialog.setLocationRelativeTo(gui.getFrame()); + dialog.setVisible(true); } private static void generateStats(Gui gui, Map checkboxes, String topLevelPackage) { 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 1481a9d..768bc41 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 @@ -145,6 +145,7 @@ public class MenuBar { this.closeMappingsItem.setEnabled(jarOpen); this.exportSourceItem.setEnabled(jarOpen); this.exportJarItem.setEnabled(jarOpen); + this.statsItem.setEnabled(jarOpen); } public JMenuBar getUi() { 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 3d031a7..a5eea56 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,6 +1,5 @@ package cuchaz.enigma.gui.stats; -import com.google.gson.GsonBuilder; import cuchaz.enigma.EnigmaProject; import cuchaz.enigma.ProgressListener; import cuchaz.enigma.analysis.index.EntryIndex; @@ -30,9 +29,10 @@ public class StatsGenerator { nameProposalServices = project.getEnigma().getServices().get(NameProposalService.TYPE); } - public String generate(ProgressListener progress, Set includedMembers, String topLevelPackage) { + public StatsResult generate(ProgressListener progress, Set includedMembers, String topLevelPackage) { includedMembers = EnumSet.copyOf(includedMembers); int totalWork = 0; + int totalMappable = 0; if (includedMembers.contains(StatsMember.METHODS) || includedMembers.contains(StatsMember.PARAMETERS)) { totalWork += entryIndex.getMethods().size(); @@ -63,6 +63,7 @@ public class StatsGenerator { if (root == method && !((MethodDefEntry) method).getAccess().isSynthetic()) { if (includedMembers.contains(StatsMember.METHODS)) { update(counts, method); + totalMappable ++; } if (includedMembers.contains(StatsMember.PARAMETERS)) { @@ -70,6 +71,7 @@ public class StatsGenerator { for (TypeDescriptor argument : method.getDesc().getArgumentDescs()) { update(counts, new LocalVariableEntry(method, index, "", true,null)); index += argument.getSize(); + totalMappable ++; } } } @@ -81,6 +83,7 @@ public class StatsGenerator { progress.step(numDone++, I18n.translate("type.fields")); if (!((FieldDefEntry)field).getAccess().isSynthetic()) { update(counts, field); + totalMappable ++; } } } @@ -89,12 +92,13 @@ public class StatsGenerator { for (ClassEntry clazz : entryIndex.getClasses()) { progress.step(numDone++, I18n.translate("type.classes")); update(counts, clazz); + totalMappable ++; } } progress.step(-1, I18n.translate("progress.stats.data")); - Tree tree = new Tree<>(); + StatsResult.Tree tree = new StatsResult.Tree<>(); for (Map.Entry entry : counts.entrySet()) { if (entry.getKey().startsWith(topLevelPackage)) { @@ -103,7 +107,7 @@ public class StatsGenerator { } tree.collapse(tree.root); - return new GsonBuilder().setPrettyPrinting().create().toJson(tree.root); + return new StatsResult(totalMappable, counts.values().stream().mapToInt(i -> i).sum(), tree); } private void update(Map counts, Entry entry) { @@ -139,62 +143,4 @@ public class StatsGenerator { return true; } - - private static class Tree { - public final Node root; - private final Map> nodes = new HashMap<>(); - - public static class Node { - public String name; - public T value; - public List> children = new ArrayList<>(); - private final transient Map> namedChildren = new HashMap<>(); - - public Node(String name, T value) { - this.name = name; - this.value = value; - } - } - - public Tree() { - root = new Node<>("", null); - } - - public Node getNode(String name) { - Node node = nodes.get(name); - - if (node == null) { - node = root; - - for (String part : name.split("\\.")) { - Node child = node.namedChildren.get(part); - - if (child == null) { - child = new Node<>(part, null); - node.namedChildren.put(part, child); - node.children.add(child); - } - - node = child; - } - - nodes.put(name, node); - } - - return node; - } - - public void collapse(Node node) { - while (node.children.size() == 1) { - Node child = node.children.get(0); - node.name = node.name.isEmpty() ? child.name : node.name + "." + child.name; - node.children = child.children; - node.value = child.value; - } - - for (Node child : node.children) { - collapse(child); - } - } - } } 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 new file mode 100644 index 0000000..0a71a64 --- /dev/null +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java @@ -0,0 +1,105 @@ +package cuchaz.enigma.gui.stats; + +import com.google.gson.GsonBuilder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public final class StatsResult { + + private final int total; + private final int unmapped; + private final Tree tree; + + public StatsResult(int total, int unmapped, Tree tree) { + this.total = total; + this.unmapped = unmapped; + this.tree = tree; + } + + public int getTotal() { + return total; + } + + public int getUnmapped() { + return unmapped; + } + + public int getMapped() { + return total - unmapped; + } + + public double getPercentage() { + return (getMapped() * 100.0f) / total; + } + + public String getTreeJson() { + return new GsonBuilder().setPrettyPrinting().create().toJson(tree.root); + } + + @Override + public String toString() { + return String.format("%s/%s %.1f%%", getMapped(), total, getPercentage()); + } + + public static class Tree { + public final Node root; + private final Map> nodes = new HashMap<>(); + + public static class Node { + public String name; + public T value; + public List> children = new ArrayList<>(); + private final transient Map> namedChildren = new HashMap<>(); + + public Node(String name, T value) { + this.name = name; + this.value = value; + } + } + + public Tree() { + root = new Node<>("", null); + } + + public Node getNode(String name) { + Node node = nodes.get(name); + + if (node == null) { + node = root; + + for (String part : name.split("\\.")) { + Node child = node.namedChildren.get(part); + + if (child == null) { + child = new Node<>(part, null); + node.namedChildren.put(part, child); + node.children.add(child); + } + + node = child; + } + + nodes.put(name, node); + } + + return node; + } + + public void collapse(Node node) { + while (node.children.size() == 1) { + Node child = node.children.get(0); + node.name = node.name.isEmpty() ? child.name : node.name + "." + child.name; + node.children = child.children; + node.value = child.value; + } + + for (Node child : node.children) { + collapse(child); + } + } + } + +} -- cgit v1.2.3