summaryrefslogtreecommitdiff
path: root/enigma-swing/src/main/java/cuchaz
diff options
context:
space:
mode:
authorGravatar modmuss502020-07-06 12:19:24 +0100
committerGravatar GitHub2020-07-06 13:19:24 +0200
commit467df0ee32517499a750a01aacfa724d7d5f2e36 (patch)
treed301f5607cf9ff9e4c7f101b922796c29f32a2cc /enigma-swing/src/main/java/cuchaz
parentHandle source export failures (#289) (diff)
downloadenigma-fork-467df0ee32517499a750a01aacfa724d7d5f2e36.tar.gz
enigma-fork-467df0ee32517499a750a01aacfa724d7d5f2e36.tar.xz
enigma-fork-467df0ee32517499a750a01aacfa724d7d5f2e36.zip
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 <git@dblsaiko.net>
Diffstat (limited to 'enigma-swing/src/main/java/cuchaz')
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java2
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/StatsDialog.java136
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java1
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java70
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java105
5 files changed, 212 insertions, 102 deletions
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 {
492 492
493 public void openStats(Set<StatsMember> includedMembers, String topLevelPackage) { 493 public void openStats(Set<StatsMember> includedMembers, String topLevelPackage) {
494 ProgressDialog.runOffThread(gui.getFrame(), progress -> { 494 ProgressDialog.runOffThread(gui.getFrame(), progress -> {
495 String data = new StatsGenerator(project).generate(progress, includedMembers, topLevelPackage); 495 String data = new StatsGenerator(project).generate(progress, includedMembers, topLevelPackage).getTreeJson();
496 496
497 try { 497 try {
498 File statsFile = File.createTempFile("stats", ".html"); 498 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 @@
1package cuchaz.enigma.gui.dialog; 1package cuchaz.enigma.gui.dialog;
2 2
3import java.awt.*; 3import java.awt.Container;
4import java.util.Arrays; 4import java.awt.Dimension;
5import java.awt.GridBagConstraints;
6import java.awt.GridBagLayout;
7import java.util.Collections;
8import java.util.HashMap;
5import java.util.Locale; 9import java.util.Locale;
6import java.util.Map; 10import java.util.Map;
7import java.util.Set; 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.JFrame;
18import javax.swing.JLabel;
19import javax.swing.JPanel;
20import javax.swing.JTextField;
21import javax.swing.SwingUtilities;
11 22
12import cuchaz.enigma.gui.Gui; 23import cuchaz.enigma.gui.Gui;
24import cuchaz.enigma.gui.stats.StatsGenerator;
13import cuchaz.enigma.gui.stats.StatsMember; 25import cuchaz.enigma.gui.stats.StatsMember;
26import cuchaz.enigma.gui.stats.StatsResult;
14import cuchaz.enigma.gui.util.ScaleUtil; 27import cuchaz.enigma.gui.util.ScaleUtil;
15import cuchaz.enigma.utils.I18n; 28import cuchaz.enigma.utils.I18n;
16 29
17public class StatsDialog { 30public class StatsDialog {
18 31
19 public static void show(Gui gui) { 32 public static void show(Gui gui) {
33 ProgressDialog.runOffThread(gui.getFrame(), listener -> {
34 final StatsGenerator statsGenerator = new StatsGenerator(gui.getController().project);
35 final Map<StatsMember, StatsResult> results = new HashMap<>();
36 for (StatsMember member : StatsMember.values()) {
37 results.put(member, statsGenerator.generate(listener, Collections.singleton(member), ""));
38 }
39 SwingUtilities.invokeLater(() -> show(gui, results));
40 });
41 }
42
43 public static void show(Gui gui, Map<StatsMember, StatsResult> results) {
20 // init frame 44 // init frame
21 JFrame frame = new JFrame(I18n.translate("menu.file.stats.title")); 45 JDialog dialog = new JDialog(gui.getFrame(), I18n.translate("menu.file.stats.title"), true);
22 JPanel checkboxesPanel = new JPanel(); 46 Container contentPane = dialog.getContentPane();
23 JPanel topLevelPackagePanel = new JPanel(); 47 contentPane.setLayout(new GridBagLayout());
24 JPanel buttonPanel = new JPanel(); 48
25 frame.setLayout(new GridLayout(3, 0)); 49 GridBagConstraints c = new GridBagConstraints();
26 frame.add(checkboxesPanel); 50 c.insets = ScaleUtil.getInsets(4, 4, 4, 4);
27 frame.add(topLevelPackagePanel); 51 c.gridy = 0;
28 frame.add(buttonPanel); 52
29 53 Map<StatsMember, JCheckBox> checkboxes = new HashMap<>();
30 // show checkboxes 54
31 Map<StatsMember, JCheckBox> checkboxes = Arrays 55 results.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(e -> {
32 .stream(StatsMember.values()) 56 StatsMember m = e.getKey();
33 .collect(Collectors.toMap(m -> m, m -> { 57 StatsResult result = e.getValue();
34 JCheckBox checkbox = new JCheckBox(I18n.translate("type." + m.name().toLowerCase(Locale.ROOT))); 58
35 checkboxesPanel.add(checkbox); 59 c.gridx = 0;
36 return checkbox; 60 c.weightx = 1.0;
37 })); 61 c.anchor = GridBagConstraints.WEST;
62 JCheckBox checkBox = new JCheckBox(I18n.translate("type." + m.name().toLowerCase(Locale.ROOT)));
63 checkboxes.put(m, checkBox);
64 contentPane.add(checkBox, c);
65
66 c.gridx = 1;
67 c.weightx = 0.0;
68 c.anchor = GridBagConstraints.EAST;
69 contentPane.add(new JLabel(Integer.toString(result.getMapped())), c);
70
71 c.gridx = 2;
72 contentPane.add(new JLabel("/"), c);
73
74 c.gridx = 3;
75 contentPane.add(new JLabel(Integer.toString(result.getTotal())), c);
76
77 c.gridx = 4;
78 contentPane.add(new JLabel(String.format("%.2f%%", result.getPercentage())), c);
79
80 c.gridy += 1;
81 });
82
83 c.gridx = 0;
84 c.gridwidth = 5;
85 c.weightx = 1.0;
86 c.anchor = GridBagConstraints.WEST;
38 87
39 // show top-level package option 88 // show top-level package option
40 JLabel topLevelPackageOption = new JLabel(I18n.translate("menu.file.stats.top_level_package")); 89 JLabel topLevelPackageOption = new JLabel(I18n.translate("menu.file.stats.top_level_package"));
90 contentPane.add(topLevelPackageOption, c);
91
92 c.gridy += 1;
93 c.weightx = 1.0;
94 c.fill = GridBagConstraints.HORIZONTAL;
41 JTextField topLevelPackage = new JTextField(); 95 JTextField topLevelPackage = new JTextField();
42 topLevelPackage.setPreferredSize(ScaleUtil.getDimension(200, 25)); 96 contentPane.add(topLevelPackage, c);
43 topLevelPackagePanel.add(topLevelPackageOption); 97
44 topLevelPackagePanel.add(topLevelPackage); 98 c.gridy += 1;
99 c.weighty = 1.0;
100 c.fill = GridBagConstraints.NONE;
101 c.anchor = GridBagConstraints.SOUTHEAST;
45 102
46 // show generate button 103 // show generate button
47 JButton button = new JButton(I18n.translate("menu.file.stats.generate")); 104 JButton button = new JButton(I18n.translate("menu.file.stats.generate"));
48 buttonPanel.add(button);
49 button.setEnabled(false); 105 button.setEnabled(false);
50 button.addActionListener(action -> { 106 button.addActionListener(action -> {
51 frame.dispose(); 107 dialog.dispose();
52 generateStats(gui, checkboxes, topLevelPackage.getText()); 108 generateStats(gui, checkboxes, topLevelPackage.getText());
53 }); 109 });
54 110
111 contentPane.add(button, c);
112
55 // add action listener to each checkbox 113 // add action listener to each checkbox
56 checkboxes.entrySet().forEach(checkbox -> { 114 checkboxes.forEach((key, value) -> value.addActionListener(action -> {
57 checkbox.getValue().addActionListener(action -> { 115 if (!button.isEnabled()) {
58 if (!button.isEnabled()) { 116 button.setEnabled(true);
59 button.setEnabled(true); 117 } else if (checkboxes.entrySet().stream().noneMatch(entry -> entry.getValue().isSelected())) {
60 } else if (checkboxes.entrySet().stream().allMatch(entry -> !entry.getValue().isSelected())) { 118 button.setEnabled(false);
61 button.setEnabled(false); 119 }
62 } 120 }));
63 });
64 });
65 121
66 // show the frame 122 // show the frame
67 frame.pack(); 123 dialog.pack();
68 frame.setVisible(true); 124 Dimension size = dialog.getSize();
69 frame.setSize(ScaleUtil.getDimension(500, 150)); 125 dialog.setMinimumSize(size);
70 frame.setResizable(false); 126 size.width = ScaleUtil.scale(350);
71 frame.setLocationRelativeTo(gui.getFrame()); 127 dialog.setSize(size);
128 dialog.setLocationRelativeTo(gui.getFrame());
129 dialog.setVisible(true);
72 } 130 }
73 131
74 private static void generateStats(Gui gui, Map<StatsMember, JCheckBox> checkboxes, String topLevelPackage) { 132 private static void generateStats(Gui gui, Map<StatsMember, JCheckBox> 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 {
145 this.closeMappingsItem.setEnabled(jarOpen); 145 this.closeMappingsItem.setEnabled(jarOpen);
146 this.exportSourceItem.setEnabled(jarOpen); 146 this.exportSourceItem.setEnabled(jarOpen);
147 this.exportJarItem.setEnabled(jarOpen); 147 this.exportJarItem.setEnabled(jarOpen);
148 this.statsItem.setEnabled(jarOpen);
148 } 149 }
149 150
150 public JMenuBar getUi() { 151 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 @@
1package cuchaz.enigma.gui.stats; 1package cuchaz.enigma.gui.stats;
2 2
3import com.google.gson.GsonBuilder;
4import cuchaz.enigma.EnigmaProject; 3import cuchaz.enigma.EnigmaProject;
5import cuchaz.enigma.ProgressListener; 4import cuchaz.enigma.ProgressListener;
6import cuchaz.enigma.analysis.index.EntryIndex; 5import cuchaz.enigma.analysis.index.EntryIndex;
@@ -30,9 +29,10 @@ public class StatsGenerator {
30 nameProposalServices = project.getEnigma().getServices().get(NameProposalService.TYPE); 29 nameProposalServices = project.getEnigma().getServices().get(NameProposalService.TYPE);
31 } 30 }
32 31
33 public String generate(ProgressListener progress, Set<StatsMember> includedMembers, String topLevelPackage) { 32 public StatsResult generate(ProgressListener progress, Set<StatsMember> includedMembers, String topLevelPackage) {
34 includedMembers = EnumSet.copyOf(includedMembers); 33 includedMembers = EnumSet.copyOf(includedMembers);
35 int totalWork = 0; 34 int totalWork = 0;
35 int totalMappable = 0;
36 36
37 if (includedMembers.contains(StatsMember.METHODS) || includedMembers.contains(StatsMember.PARAMETERS)) { 37 if (includedMembers.contains(StatsMember.METHODS) || includedMembers.contains(StatsMember.PARAMETERS)) {
38 totalWork += entryIndex.getMethods().size(); 38 totalWork += entryIndex.getMethods().size();
@@ -63,6 +63,7 @@ public class StatsGenerator {
63 if (root == method && !((MethodDefEntry) method).getAccess().isSynthetic()) { 63 if (root == method && !((MethodDefEntry) method).getAccess().isSynthetic()) {
64 if (includedMembers.contains(StatsMember.METHODS)) { 64 if (includedMembers.contains(StatsMember.METHODS)) {
65 update(counts, method); 65 update(counts, method);
66 totalMappable ++;
66 } 67 }
67 68
68 if (includedMembers.contains(StatsMember.PARAMETERS)) { 69 if (includedMembers.contains(StatsMember.PARAMETERS)) {
@@ -70,6 +71,7 @@ public class StatsGenerator {
70 for (TypeDescriptor argument : method.getDesc().getArgumentDescs()) { 71 for (TypeDescriptor argument : method.getDesc().getArgumentDescs()) {
71 update(counts, new LocalVariableEntry(method, index, "", true,null)); 72 update(counts, new LocalVariableEntry(method, index, "", true,null));
72 index += argument.getSize(); 73 index += argument.getSize();
74 totalMappable ++;
73 } 75 }
74 } 76 }
75 } 77 }
@@ -81,6 +83,7 @@ public class StatsGenerator {
81 progress.step(numDone++, I18n.translate("type.fields")); 83 progress.step(numDone++, I18n.translate("type.fields"));
82 if (!((FieldDefEntry)field).getAccess().isSynthetic()) { 84 if (!((FieldDefEntry)field).getAccess().isSynthetic()) {
83 update(counts, field); 85 update(counts, field);
86 totalMappable ++;
84 } 87 }
85 } 88 }
86 } 89 }
@@ -89,12 +92,13 @@ public class StatsGenerator {
89 for (ClassEntry clazz : entryIndex.getClasses()) { 92 for (ClassEntry clazz : entryIndex.getClasses()) {
90 progress.step(numDone++, I18n.translate("type.classes")); 93 progress.step(numDone++, I18n.translate("type.classes"));
91 update(counts, clazz); 94 update(counts, clazz);
95 totalMappable ++;
92 } 96 }
93 } 97 }
94 98
95 progress.step(-1, I18n.translate("progress.stats.data")); 99 progress.step(-1, I18n.translate("progress.stats.data"));
96 100
97 Tree<Integer> tree = new Tree<>(); 101 StatsResult.Tree<Integer> tree = new StatsResult.Tree<>();
98 102
99 for (Map.Entry<String, Integer> entry : counts.entrySet()) { 103 for (Map.Entry<String, Integer> entry : counts.entrySet()) {
100 if (entry.getKey().startsWith(topLevelPackage)) { 104 if (entry.getKey().startsWith(topLevelPackage)) {
@@ -103,7 +107,7 @@ public class StatsGenerator {
103 } 107 }
104 108
105 tree.collapse(tree.root); 109 tree.collapse(tree.root);
106 return new GsonBuilder().setPrettyPrinting().create().toJson(tree.root); 110 return new StatsResult(totalMappable, counts.values().stream().mapToInt(i -> i).sum(), tree);
107 } 111 }
108 112
109 private void update(Map<String, Integer> counts, Entry<?> entry) { 113 private void update(Map<String, Integer> counts, Entry<?> entry) {
@@ -139,62 +143,4 @@ public class StatsGenerator {
139 143
140 return true; 144 return true;
141 } 145 }
142
143 private static class Tree<T> {
144 public final Node<T> root;
145 private final Map<String, Node<T>> nodes = new HashMap<>();
146
147 public static class Node<T> {
148 public String name;
149 public T value;
150 public List<Node<T>> children = new ArrayList<>();
151 private final transient Map<String, Node<T>> namedChildren = new HashMap<>();
152
153 public Node(String name, T value) {
154 this.name = name;
155 this.value = value;
156 }
157 }
158
159 public Tree() {
160 root = new Node<>("", null);
161 }
162
163 public Node<T> getNode(String name) {
164 Node<T> node = nodes.get(name);
165
166 if (node == null) {
167 node = root;
168
169 for (String part : name.split("\\.")) {
170 Node<T> child = node.namedChildren.get(part);
171
172 if (child == null) {
173 child = new Node<>(part, null);
174 node.namedChildren.put(part, child);
175 node.children.add(child);
176 }
177
178 node = child;
179 }
180
181 nodes.put(name, node);
182 }
183
184 return node;
185 }
186
187 public void collapse(Node<T> node) {
188 while (node.children.size() == 1) {
189 Node<T> child = node.children.get(0);
190 node.name = node.name.isEmpty() ? child.name : node.name + "." + child.name;
191 node.children = child.children;
192 node.value = child.value;
193 }
194
195 for (Node<T> child : node.children) {
196 collapse(child);
197 }
198 }
199 }
200} 146}
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 @@
1package cuchaz.enigma.gui.stats;
2
3import com.google.gson.GsonBuilder;
4
5import java.util.ArrayList;
6import java.util.HashMap;
7import java.util.List;
8import java.util.Map;
9
10public final class StatsResult {
11
12 private final int total;
13 private final int unmapped;
14 private final Tree<Integer> tree;
15
16 public StatsResult(int total, int unmapped, Tree<Integer> tree) {
17 this.total = total;
18 this.unmapped = unmapped;
19 this.tree = tree;
20 }
21
22 public int getTotal() {
23 return total;
24 }
25
26 public int getUnmapped() {
27 return unmapped;
28 }
29
30 public int getMapped() {
31 return total - unmapped;
32 }
33
34 public double getPercentage() {
35 return (getMapped() * 100.0f) / total;
36 }
37
38 public String getTreeJson() {
39 return new GsonBuilder().setPrettyPrinting().create().toJson(tree.root);
40 }
41
42 @Override
43 public String toString() {
44 return String.format("%s/%s %.1f%%", getMapped(), total, getPercentage());
45 }
46
47 public static class Tree<T> {
48 public final Node<T> root;
49 private final Map<String, Node<T>> nodes = new HashMap<>();
50
51 public static class Node<T> {
52 public String name;
53 public T value;
54 public List<Node<T>> children = new ArrayList<>();
55 private final transient Map<String, Node<T>> namedChildren = new HashMap<>();
56
57 public Node(String name, T value) {
58 this.name = name;
59 this.value = value;
60 }
61 }
62
63 public Tree() {
64 root = new Node<>("", null);
65 }
66
67 public Node<T> getNode(String name) {
68 Node<T> node = nodes.get(name);
69
70 if (node == null) {
71 node = root;
72
73 for (String part : name.split("\\.")) {
74 Node<T> child = node.namedChildren.get(part);
75
76 if (child == null) {
77 child = new Node<>(part, null);
78 node.namedChildren.put(part, child);
79 node.children.add(child);
80 }
81
82 node = child;
83 }
84
85 nodes.put(name, node);
86 }
87
88 return node;
89 }
90
91 public void collapse(Node<T> node) {
92 while (node.children.size() == 1) {
93 Node<T> child = node.children.get(0);
94 node.name = node.name.isEmpty() ? child.name : node.name + "." + child.name;
95 node.children = child.children;
96 node.value = child.value;
97 }
98
99 for (Node<T> child : node.children) {
100 collapse(child);
101 }
102 }
103 }
104
105}