diff --git a/framework/tst/dslabs/framework/testing/visualization/DebuggerWindow.java b/framework/tst/dslabs/framework/testing/visualization/DebuggerWindow.java
index 6af65624..47818e47 100644
--- a/framework/tst/dslabs/framework/testing/visualization/DebuggerWindow.java
+++ b/framework/tst/dslabs/framework/testing/visualization/DebuggerWindow.java
@@ -22,8 +22,11 @@
package dslabs.framework.testing.visualization;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import dslabs.framework.Address;
+import dslabs.framework.Message;
+import dslabs.framework.Timer;
import dslabs.framework.testing.Event;
import dslabs.framework.testing.StatePredicate;
import dslabs.framework.testing.StatePredicate.PredicateResult;
@@ -51,13 +54,17 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
import java.util.prefs.BackingStoreException;
import java.util.stream.Collectors;
import javax.swing.AbstractAction;
import javax.swing.AbstractButton;
import javax.swing.ButtonGroup;
import javax.swing.InputMap;
+import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
@@ -142,6 +149,8 @@ private static boolean runningInWSL() {
static final int WINDOW_DEFAULT_WIDTH = 1440, WINDOW_DEFAULT_HEIGHT = 810;
static final String LINE_WRAPPING_FORMAT = "%1s";
+ private static final String HIDE_ALL = "Hide All", SHOW_ALL = "Show All";
+
private final Address[] addresses;
private final Map
statePanels = new HashMap<>();
@@ -152,6 +161,14 @@ private static boolean runningInWSL() {
private final List> prunes = new ArrayList<>();
private final List> goals = new ArrayList<>();
+ private final JXTaskPane messageVisibilityPane, timerVisibilityPane;
+ private final JButton messagesVisibilityToggle, timersVisibilityToggle;
+ private final SortedMap, JCheckBox>
+ messageCheckBoxes =
+ new TreeMap<>(Comparator.comparing(Class::getName));
+ private final SortedMap, JCheckBox> timerCheckBoxes =
+ new TreeMap<>(Comparator.comparing(Class::getName));
+
private final Pair exceptionPanel;
private final JXMultiSplitPane splitPane;
@@ -333,6 +350,40 @@ public void actionPerformed(ActionEvent e) {
sideBar.add(exceptionPane);
updateExceptionPane();
+ messageVisibilityPane = new JXTaskPane("Show/Hide Messages");
+ messagesVisibilityToggle = new JButton(HIDE_ALL);
+ messagesVisibilityToggle.addActionListener(__ -> {
+ if (messageCheckBoxes.values().stream()
+ .allMatch(AbstractButton::isSelected)) {
+ messageCheckBoxes.values()
+ .forEach(x -> x.setSelected(false));
+ } else {
+ messageCheckBoxes.values()
+ .forEach(x -> x.setSelected(true));
+ }
+ setState(currentState);
+ });
+ messageVisibilityPane.add(messagesVisibilityToggle);
+ messageVisibilityPane.setVisible(false);
+ sideBar.add(messageVisibilityPane);
+
+ timerVisibilityPane = new JXTaskPane("Show/Hide Timers");
+ timersVisibilityToggle = new JButton(HIDE_ALL);
+ timersVisibilityToggle.addActionListener(__ -> {
+ if (timerCheckBoxes.values().stream()
+ .allMatch(AbstractButton::isSelected)) {
+ timerCheckBoxes.values().forEach(x -> x.setSelected(false));
+ } else {
+ timerCheckBoxes.values().forEach(x -> x.setSelected(true));
+ }
+ setState(currentState);
+ });
+ timerVisibilityPane.add(timersVisibilityToggle);
+ timerVisibilityPane.setVisible(false);
+ sideBar.add(timerVisibilityPane);
+
+ updateDeliverablesVisibilityPane();
+
sideBar.setMinimumSize(new Dimension(20, 0));
// Don't let sidebar be too large on startup
sideBar.setPreferredSize(new Dimension(
@@ -395,7 +446,7 @@ public void actionPerformed(ActionEvent e) {
public void actionPerformed(ActionEvent e) {
List p =
currentState.pathToBestDescendent();
- if (p.size() > 0) {
+ if (!p.isEmpty()) {
setState(p.get(0));
}
}
@@ -582,6 +633,42 @@ private void updateExceptionPane() {
}
}
+ private void updateDeliverablesVisibilityPane() {
+ for (var m : currentState.network()) {
+ Class extends Message> messageType = m.message().getClass();
+ if (!messageCheckBoxes.containsKey(messageType)) {
+ int position = messageCheckBoxes.headMap(messageType).size();
+ JCheckBox checkBox = new JCheckBox(messageType.getSimpleName());
+ checkBox.setToolTipText(messageType.getName());
+ checkBox.setSelected(true);
+ checkBox.addActionListener(__ -> setState(currentState));
+ messageVisibilityPane.add(checkBox, position);
+ messageCheckBoxes.put(messageType, checkBox);
+ if (!messageVisibilityPane.isVisible()) {
+ messageVisibilityPane.setVisible(true);
+ }
+ }
+ }
+ for (Address a : currentState.addresses()) {
+ for (var t : currentState.timers(a)) {
+ Class extends Timer> timerType = t.timer().getClass();
+ if (!timerCheckBoxes.containsKey(timerType)) {
+ int position = timerCheckBoxes.headMap(timerType).size();
+ JCheckBox checkBox =
+ new JCheckBox(timerType.getSimpleName());
+ checkBox.setToolTipText(timerType.getName());
+ checkBox.setSelected(true);
+ checkBox.addActionListener(__ -> setState(currentState));
+ timerVisibilityPane.add(checkBox, position);
+ timerCheckBoxes.put(timerType, checkBox);
+ if (!timerVisibilityPane.isVisible()) {
+ timerVisibilityPane.setVisible(true);
+ }
+ }
+ }
+ }
+ }
+
private void layoutNodes() {
/*
* We must reset the JXMultiSplitPane every time. Unfortunately, hiding
@@ -655,6 +742,18 @@ private void layoutNodes() {
void reset() {
stateTreeCanvas.reset();
eventsPanel.reset();
+
+ messageVisibilityPane.setVisible(false);
+ messageVisibilityPane.removeAll();
+ messageVisibilityPane.add(messagesVisibilityToggle);
+
+ timerVisibilityPane.setVisible(false);
+ timerVisibilityPane.removeAll();
+ timerVisibilityPane.add(timersVisibilityToggle);
+
+ messageCheckBoxes.clear();
+ timerCheckBoxes.clear();
+
setState(EventTreeState.convert(DebuggerWindow.this.initialState));
}
@@ -667,14 +766,40 @@ void deliverEvent(Event e) {
void setState(@NonNull EventTreeState s) {
stateTreeCanvas.showEvent(s);
currentState = s;
+
+ ImmutableSet> hiddenMessageTypes =
+ messageCheckBoxes.entrySet().stream()
+ .filter(e -> !e.getValue().isSelected())
+ .map(Entry::getKey)
+ .collect(ImmutableSet.toImmutableSet());
+ ImmutableSet> hiddenTimerTypes =
+ timerCheckBoxes.entrySet().stream()
+ .filter(e -> !e.getValue().isSelected())
+ .map(Entry::getKey)
+ .collect(ImmutableSet.toImmutableSet());
+
+ if (hiddenMessageTypes.isEmpty()) {
+ messagesVisibilityToggle.setText(HIDE_ALL);
+ } else {
+ messagesVisibilityToggle.setText(SHOW_ALL);
+ }
+
+ if (hiddenTimerTypes.isEmpty()) {
+ timersVisibilityToggle.setText(HIDE_ALL);
+ } else {
+ timersVisibilityToggle.setText(SHOW_ALL);
+ }
+
for (Address a : currentState.addresses()) {
assert statePanels.containsKey(a);
statePanels.get(a).updateState(currentState,
ignoreSearchSettings ? null : searchSettings,
- viewDeliveredMessages);
+ viewDeliveredMessages, hiddenMessageTypes,
+ hiddenTimerTypes);
}
eventsPanel.update(currentState);
updatePredicatePanes();
updateExceptionPane();
+ updateDeliverablesVisibilityPane();
}
}
diff --git a/framework/tst/dslabs/framework/testing/visualization/SingleNodePanel.java b/framework/tst/dslabs/framework/testing/visualization/SingleNodePanel.java
index d6661742..798a151a 100644
--- a/framework/tst/dslabs/framework/testing/visualization/SingleNodePanel.java
+++ b/framework/tst/dslabs/framework/testing/visualization/SingleNodePanel.java
@@ -22,9 +22,13 @@
package dslabs.framework.testing.visualization;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Streams;
import dslabs.framework.Address;
+import dslabs.framework.Message;
+import dslabs.framework.Timer;
import dslabs.framework.testing.Event;
import dslabs.framework.testing.MessageEnvelope;
import dslabs.framework.testing.TimerEnvelope;
@@ -111,7 +115,8 @@ class SingleNodePanel extends JPanel {
// XXX: why does this need w 100%, h 100%? Shouldn't grow handle it?
add(mainSplitPane, "grow, h 100%");
- updateState(s, settings, viewDeliveredMessages);
+ updateState(s, settings, viewDeliveredMessages, ImmutableSet.of(),
+ ImmutableSet.of());
add(new JLabel(a.toString()), "center");
}
@@ -131,7 +136,9 @@ public void paint(Graphics g) {
}
void updateState(EventTreeState s, SearchSettings settings,
- boolean viewDeliveredMessages) {
+ boolean viewDeliveredMessages,
+ ImmutableSet> hiddenMessageTypes,
+ ImmutableSet> hiddenTimerTypes) {
boolean nodeIsDiffed = !s.isInitialState() &&
s.previousEvent().locationRootAddress().equals(address);
if (nodeIsDiffed) {
@@ -145,16 +152,28 @@ void updateState(EventTreeState s, SearchSettings settings,
final boolean pruned =
settings != null && settings.shouldPrune(s.state());
- updateMessages(s, settings, viewDeliveredMessages, pruned);
- updateTimers(s, settings, pruned);
+ updateMessages(s, settings, viewDeliveredMessages, pruned,
+ hiddenMessageTypes);
+ updateTimers(s, settings, pruned, hiddenTimerTypes);
}
private void updateMessages(final EventTreeState s,
final SearchSettings settings,
final boolean viewDeliveredMessages,
- final boolean pruned) {
- final Set ms = Sets.newHashSet(
- viewDeliveredMessages ? s.network() : s.undeliveredMessages());
+ final boolean pruned,
+ ImmutableSet> hiddenMessageTypes) {
+ // The set of messages to display
+ final ImmutableSet ms;
+ {
+ Iterable messagesWithoutFilter =
+ viewDeliveredMessages ? s.network() :
+ s.undeliveredMessages();
+ ms = Streams.stream(messagesWithoutFilter)
+ .filter(m -> !hiddenMessageTypes.contains(
+ m.message().getClass()))
+ .collect(ImmutableSet.toImmutableSet());
+ }
+
boolean repaintMessageBox = false;
for (MessageEnvelope message : ms) {
if (!message.to().rootAddress().equals(address)) {
@@ -183,7 +202,8 @@ private void updateMessages(final EventTreeState s,
messageBox.revalidate();
messageBox.repaint();
}
- for (Entry> messageEntry : messages.entrySet()) {
+ for (Entry> messageEntry
+ : messages.entrySet()) {
MessageEnvelope message = messageEntry.getKey();
ObjectJTree tree = messageEntry.getValue().getRight();
if (!s.isInitialState() &&
@@ -197,18 +217,35 @@ private void updateMessages(final EventTreeState s,
private void updateTimers(final EventTreeState s,
final SearchSettings settings,
- final boolean pruned) {
+ final boolean pruned,
+ ImmutableSet> hiddenTimerTypes) {
boolean repaintTimerBox = false;
// Track numbers of times timers are seen to infer which ones are new
- Map otf = null, ctf = new HashMap<>(), stf =
- s.timerFrequencies(address);
- if (!s.isInitialState()) {
- otf = s.parent().timerFrequencies(address);
- }
+
+ // The previous state's timers' frequencies (no need to remove hidden
+ // timer types)
+ final ImmutableMap otf =
+ s.isInitialState() ? ImmutableMap.of() : ImmutableMap.copyOf(
+ s.parent().timerFrequencies(address));
+
+ // The current pane's timers' frequencies
+ HashMap ctf = new HashMap<>();
+
+ // This state's timers' frequencies
+ ImmutableMap stf =
+ s.timerFrequencies(address).entrySet().stream()
+ .filter(e -> !hiddenTimerTypes.contains(
+ e.getKey().timer().getClass())).collect(
+ ImmutableMap.toImmutableMap(Entry::getKey,
+ Entry::getValue));
// The new timer queue to materialize to the user
- final var newTimers = Lists.newArrayList(s.timers(address).iterator());
+ final ImmutableList newTimers =
+ Streams.stream(s.timers(address))
+ .filter(t -> !hiddenTimerTypes.contains(
+ t.timer().getClass()))
+ .collect(ImmutableList.toImmutableList());
/*
First, remove all the timers that aren't present in the new state,
@@ -246,7 +283,7 @@ private void updateTimers(final EventTreeState s,
while (i < newTimers.size()) {
var t = newTimers.get(i);
final int tf = ctf.getOrDefault(t, 0) + 1;
- final boolean tIsNew = otf != null && tf > otf.getOrDefault(t, 0);
+ final boolean tIsNew = tf > otf.getOrDefault(t, 0);
// This is too clever by half, but it works
final Supplier tIsDeliverable =
() -> timerSet.add(t) && s.canStepTimer(t);