package jetbrains.mps.debugger.api.ui.tree; /*Generated by MPS */ import jetbrains.mps.ide.ui.tree.MPSTree; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.actionSystem.DataKey; import jetbrains.mps.debug.api.programState.IValue; import org.jetbrains.annotations.NotNull; import jetbrains.mps.debug.api.AbstractUiState; import com.intellij.openapi.project.Project; import com.intellij.openapi.application.ApplicationManager; import javax.swing.KeyStroke; import java.awt.event.KeyEvent; import javax.swing.AbstractAction; import java.awt.event.ActionEvent; import jetbrains.mps.ide.project.ProjectHelper; import jetbrains.mps.openapi.navigation.EditorNavigator; import com.intellij.openapi.actionSystem.ActionGroup; import jetbrains.mps.ide.ui.tree.MPSTreeNode; import jetbrains.mps.workbench.action.BaseGroup; import com.intellij.openapi.actionSystem.ActionManager; import java.util.List; import jetbrains.mps.debug.api.programState.IWatchable; import jetbrains.mps.ide.ui.tree.TextTreeNode; import java.util.Map; import jetbrains.mps.debug.api.programState.WatchablesCategory; import jetbrains.mps.internal.collections.runtime.MapSequence; import java.util.HashMap; import org.jetbrains.mps.openapi.model.SNodeReference; import jetbrains.mps.debug.api.programState.Watchable2; import java.util.ArrayList; import java.util.LinkedHashMap; import jetbrains.mps.internal.collections.runtime.ListSequence; import java.util.SortedSet; import jetbrains.mps.internal.collections.runtime.SortedSetSequence; import java.util.TreeSet; import jetbrains.mps.internal.collections.runtime.SetSequence; import java.util.Collections; import jetbrains.mps.util.ToStringComparator; import java.util.Comparator; import jetbrains.mps.ide.messages.Icons; import org.jetbrains.annotations.Nullable; import javax.swing.tree.TreePath; import org.jetbrains.annotations.NonNls; import jetbrains.mps.ide.actions.MPSCommonDataKeys; import jetbrains.mps.baseLanguage.closures.runtime._FunctionTypes; public class VariablesTree extends MPSTree implements DataProvider { private static final String COMMAND_OPEN_NODE_IN_PROJECT = "COMMAND_OPEN_NODE_IN_PROJECT"; public static final DataKey<IValue> MPS_DEBUGGER_VALUE = DataKey.create("MPS_DEBUGGER_VALUE"); @NotNull private AbstractUiState myUiState; private final Project myProject; public VariablesTree(Project project, AbstractUiState state) { ApplicationManager.getApplication().assertIsDispatchThread(); myUiState = state; myProject = project; getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), COMMAND_OPEN_NODE_IN_PROJECT); getActionMap().put(COMMAND_OPEN_NODE_IN_PROJECT, new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { AbstractWatchableNode selectedNode = findSelectedNode(); if (selectedNode != null && selectedNode.getNode() != null) { final jetbrains.mps.project.Project mpsProject = ProjectHelper.fromIdeaProject(getProject()); new EditorNavigator(mpsProject).shallFocus(false).shallSelect(true).open(selectedNode.getNode()); } } }); setRootVisible(false); setShowsRootHandles(true); } @Override protected ActionGroup createPopupActionGroup(MPSTreeNode node) { if (node instanceof AbstractWatchableNode) { return ((BaseGroup) ActionManager.getInstance().getAction("jetbrains.mps.debugger.api.ui.actions.AbstractWatchableNodeActions_ActionGroup")); } return null; } public void setUiState(@NotNull AbstractUiState uiState) { ApplicationManager.getApplication().assertIsDispatchThread(); myUiState = uiState; } @Override protected MPSTreeNode rebuild() { List<IWatchable> watchables = myUiState.getWatchables(); if (watchables.isEmpty()) { return createEmptyTree(); } MPSTreeNode rootTreeNode = new TextTreeNode("Local Variables"); rootTreeNode.setTree(this); // collecting nodes Map<WatchablesCategory, List<IWatchable>> orphanesByCategory = MapSequence.fromMap(new HashMap<WatchablesCategory, List<IWatchable>>()); Map<WatchablesCategory, Map<SNodeReference, List<IWatchable>>> nodeToVarsMapByCategory = MapSequence.fromMap(new HashMap<WatchablesCategory, Map<SNodeReference, List<IWatchable>>>()); for (IWatchable watchable : watchables) { WatchablesCategory category = watchable.getCategory(); SNodeReference node = (watchable instanceof Watchable2 ? ((Watchable2) watchable).getSourceNode() : ((watchable.getNode() == null ? null : watchable.getNode().getReference()))); if (node == null) { List<IWatchable> orphanes = MapSequence.fromMap(orphanesByCategory).get(category); if (orphanes == null) { orphanes = new ArrayList<IWatchable>(); MapSequence.fromMap(orphanesByCategory).put(category, orphanes); } orphanes.add(watchable); } else { Map<SNodeReference, List<IWatchable>> nodeToVarsMap = MapSequence.fromMap(nodeToVarsMapByCategory).get(category); if (nodeToVarsMap == null) { nodeToVarsMap = MapSequence.fromMap(new LinkedHashMap<SNodeReference, List<IWatchable>>(16, (float) 0.75, false)); MapSequence.fromMap(nodeToVarsMapByCategory).put(category, nodeToVarsMap); } List<IWatchable> watchableList = MapSequence.fromMap(nodeToVarsMap).get(node); if (watchableList == null) { watchableList = ListSequence.fromList(new ArrayList<IWatchable>()); MapSequence.fromMap(nodeToVarsMap).put(node, watchableList); } ListSequence.fromList(watchableList).addElement(watchable); } } SortedSet<WatchablesCategory> keys = SortedSetSequence.fromSetWithValues(new TreeSet<WatchablesCategory>(), SetSequence.fromSet(MapSequence.fromMap(orphanesByCategory).keySet()).union(SetSequence.fromSet(MapSequence.fromMap(nodeToVarsMapByCategory).keySet()))); for (WatchablesCategory category : keys) { List<IWatchable> orphanes = MapSequence.fromMap(orphanesByCategory).get(category); Map<SNodeReference, List<IWatchable>> nodeToVarsMap = MapSequence.fromMap(nodeToVarsMapByCategory).get(category); if (orphanes == null) { orphanes = ListSequence.fromList(new ArrayList<IWatchable>()); } if (nodeToVarsMap == null) { nodeToVarsMap = MapSequence.fromMap(new HashMap<SNodeReference, List<IWatchable>>()); } // sorting List<SNodeReference> nodes = ListSequence.fromList(new ArrayList<SNodeReference>()); ListSequence.fromList(nodes).addSequence(SetSequence.fromSet(MapSequence.fromMap(nodeToVarsMap).keySet())); Collections.sort(nodes, new ToStringComparator()); Collections.sort(orphanes, new Comparator<IWatchable>() { @Override public int compare(IWatchable o1, IWatchable o2) { return o1.getName().compareTo(o2.getName()); } }); // adding nodes for (SNodeReference snode : MapSequence.fromMap(nodeToVarsMap).keySet()) { List<IWatchable> watchablesWithNodes = MapSequence.fromMap(nodeToVarsMap).get(snode); if (ListSequence.fromList(watchablesWithNodes).count() == 1) { rootTreeNode.add(new WatchableNode(ListSequence.fromList(watchablesWithNodes).first(), myUiState)); } else { NodeTreeNode nodeTreeNode = new NodeTreeNode(snode); for (IWatchable watchable : watchablesWithNodes) { nodeTreeNode.add(new WatchableNode(watchable, myUiState)); } rootTreeNode.add(nodeTreeNode); } } for (IWatchable watchable : orphanes) { rootTreeNode.add(new WatchableNode(watchable, myUiState)); } } return rootTreeNode; } private MPSTreeNode createEmptyTree() { TextTreeNode rootNode = new TextTreeNode(""); TextTreeNode messageNode = new TextTreeNode("No local variables available") { @Override public boolean isLeaf() { return true; } }; messageNode.setIcon(Icons.INFORMATION_ICON); rootNode.add(messageNode); return rootNode; } @Nullable private AbstractWatchableNode findSelectedNode() { TreePath selectionPath = getSelectionPath(); if (selectionPath == null) { return null; } Object selectedNode = selectionPath.getLastPathComponent(); if (selectedNode instanceof AbstractWatchableNode) { return ((AbstractWatchableNode) selectedNode); } return null; } @Override @Nullable public Object getData(@NonNls String dataId) { if (MPSCommonDataKeys.NODE.is(dataId)) { AbstractWatchableNode selectedNode = findSelectedNode(); if (selectedNode != null && selectedNode.getNode() != null) { return selectedNode.getNode().resolve(ProjectHelper.getProjectRepository(getProject())); } } else if (MPS_DEBUGGER_VALUE.is(dataId)) { AbstractWatchableNode selectedNode = findSelectedNode(); if (selectedNode != null) { if (selectedNode instanceof WatchableNode) { return ((WatchableNode) selectedNode).getValue(); } } } else if (MPSCommonDataKeys.TREE_NODE.is(dataId)) { return findSelectedNode(); } return null; } public Project getProject() { return myProject; } private void stringToPath(String pathString, final _FunctionTypes._void_P1_E0<? super TreePath> callback) { String[] components = pathString.split(MPSTree.TREE_PATH_SEPARATOR); final List<Object> path = new ArrayList<Object>(); MPSTreeNode current = getRootNode(); if (!(current.isInitialized())) { current.init(); } path.add(current); if (components.length == 0) { callback.invoke(new TreePath(path.toArray())); } else { stringToPath(current, components, 0, path, new _FunctionTypes._void_P0_E0() { public void invoke() { callback.invoke(new TreePath(path.toArray())); } }); } } private void stringToPath(MPSTreeNode current, final String[] path, final int index, final List<Object> result, final _FunctionTypes._void_P0_E0 callback) { if (index >= path.length) { callback.invoke(); return; } String component = path[index]; if ((component == null || component.length() == 0)) { stringToPath(current, path, index + 1, result, callback); } else { boolean found = false; for (int i = 0; i < current.getChildCount(); i++) { final MPSTreeNode node = (MPSTreeNode) current.getChildAt(i); if (node.getNodeIdentifier().replaceAll(TREE_PATH_SEPARATOR, "-").equals(component)) { found = true; ListSequence.fromList(result).addElement(node); if (!(node.isInitialized())) { if (node instanceof WatchableNode) { ((WatchableNode) node).init(new _FunctionTypes._void_P0_E0() { public void invoke() { stringToPath(node, path, index + 1, result, callback); } }); } else { node.init(); } } break; } } if (!(found)) { callback.invoke(); } } } protected void expandPaths(List<String> paths) { for (String path : paths) { stringToPath(path, new _FunctionTypes._void_P1_E0<TreePath>() { public void invoke(TreePath treePath) { if (treePath != null) { expandPath(treePath); } } }); } } protected void selectPaths(final List<String> paths) { final List<TreePath> treePaths = new ArrayList<TreePath>(); for (int i = 0; i < paths.size(); i++) { // yep, this line is required for the code to work %| final int j = i; stringToPath(paths.get(i), new _FunctionTypes._void_P1_E0<TreePath>() { public void invoke(TreePath treePath) { treePaths.add(treePath); if (j == paths.size() - 1) { setSelectionPaths(treePaths.toArray(new TreePath[treePaths.size()])); } } }); } } }