/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.inspector.gui; import java.awt.BorderLayout; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JTree; import javax.swing.SwingWorker; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import org.apache.zookeeper.inspector.manager.NodeListener; import org.apache.zookeeper.inspector.manager.ZooInspectorManager; import com.nitido.utils.toaster.Toaster; /** * A {@link JPanel} for showing the tree view of all the nodes in the zookeeper * instance */ public class ZooInspectorTreeViewer extends JPanel implements NodeListener { private final ZooInspectorManager zooInspectorManager; private final JTree tree; private final Toaster toasterManager; /** * @param zooInspectorManager * - the {@link ZooInspectorManager} for the application * @param listener * - the {@link TreeSelectionListener} to listen for changes in * the selected node on the node tree */ public ZooInspectorTreeViewer( final ZooInspectorManager zooInspectorManager, TreeSelectionListener listener) { this.zooInspectorManager = zooInspectorManager; this.setLayout(new BorderLayout()); final JPopupMenu popupMenu = new JPopupMenu(); final JMenuItem addNotify = new JMenuItem("Add Change Notification"); this.toasterManager = new Toaster(); this.toasterManager.setBorderColor(Color.BLACK); this.toasterManager.setMessageColor(Color.BLACK); this.toasterManager.setToasterColor(Color.WHITE); addNotify.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { List<String> selectedNodes = getSelectedNodes(); zooInspectorManager.addWatchers(selectedNodes, ZooInspectorTreeViewer.this); } }); final JMenuItem removeNotify = new JMenuItem( "Remove Change Notification"); removeNotify.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { List<String> selectedNodes = getSelectedNodes(); zooInspectorManager.removeWatchers(selectedNodes); } }); tree = new JTree(new DefaultMutableTreeNode()); tree.setCellRenderer(new ZooInspectorTreeCellRenderer()); tree.setEditable(false); tree.getSelectionModel().addTreeSelectionListener(listener); tree.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.isPopupTrigger() || e.getButton() == MouseEvent.BUTTON3) { // TODO only show add if a selected node isn't being // watched, and only show remove if a selected node is being // watched popupMenu.removeAll(); popupMenu.add(addNotify); popupMenu.add(removeNotify); popupMenu.show(ZooInspectorTreeViewer.this, e.getX(), e .getY()); } } }); this.add(tree, BorderLayout.CENTER); } /** * Refresh the tree view */ public void refreshView() { final Set<TreePath> expandedNodes = new LinkedHashSet<TreePath>(); int rowCount = tree.getRowCount(); for (int i = 0; i < rowCount; i++) { TreePath path = tree.getPathForRow(i); if (tree.isExpanded(path)) { expandedNodes.add(path); } } final TreePath[] selectedNodes = tree.getSelectionPaths(); SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { @Override protected Boolean doInBackground() throws Exception { tree.setModel(new DefaultTreeModel(new ZooInspectorTreeNode( "/", null))); return true; } @Override protected void done() { for (TreePath path : expandedNodes) { tree.expandPath(path); } tree.getSelectionModel().setSelectionPaths(selectedNodes); } }; worker.execute(); } /** * clear the tree view of all nodes */ public void clearView() { tree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode())); } /** * @author Colin * */ private static class ZooInspectorTreeCellRenderer extends DefaultTreeCellRenderer { public ZooInspectorTreeCellRenderer() { setLeafIcon(ZooInspectorIconResources.getTreeLeafIcon()); setOpenIcon(ZooInspectorIconResources.getTreeOpenIcon()); setClosedIcon(ZooInspectorIconResources.getTreeClosedIcon()); } } /** * @author Colin * */ private class ZooInspectorTreeNode implements TreeNode { private final String nodePath; private final String nodeName; private final ZooInspectorTreeNode parent; public ZooInspectorTreeNode(String nodePath, ZooInspectorTreeNode parent) { this.parent = parent; this.nodePath = nodePath; int index = nodePath.lastIndexOf("/"); if (index == -1) { throw new IllegalArgumentException("Invalid node path" + nodePath); } this.nodeName = nodePath.substring(index + 1); } /* * (non-Javadoc) * * @see javax.swing.tree.TreeNode#children() */ public Enumeration<TreeNode> children() { List<String> children = zooInspectorManager .getChildren(this.nodePath); Collections.sort(children); List<TreeNode> returnChildren = new ArrayList<TreeNode>(); for (String child : children) { returnChildren.add(new ZooInspectorTreeNode((this.nodePath .equals("/") ? "" : this.nodePath) + "/" + child, this)); } return Collections.enumeration(returnChildren); } /* * (non-Javadoc) * * @see javax.swing.tree.TreeNode#getAllowsChildren() */ public boolean getAllowsChildren() { return zooInspectorManager.isAllowsChildren(this.nodePath); } /* * (non-Javadoc) * * @see javax.swing.tree.TreeNode#getChildAt(int) */ public TreeNode getChildAt(int childIndex) { String child = zooInspectorManager.getNodeChild(this.nodePath, childIndex); if (child != null) { return new ZooInspectorTreeNode((this.nodePath.equals("/") ? "" : this.nodePath) + "/" + child, this); } return null; } /* * (non-Javadoc) * * @see javax.swing.tree.TreeNode#getChildCount() */ public int getChildCount() { return zooInspectorManager.getNumChildren(this.nodePath); } /* * (non-Javadoc) * * @see javax.swing.tree.TreeNode#getIndex(javax.swing.tree.TreeNode) */ public int getIndex(TreeNode node) { return zooInspectorManager.getNodeIndex(this.nodePath); } /* * (non-Javadoc) * * @see javax.swing.tree.TreeNode#getParent() */ public TreeNode getParent() { return this.parent; } /* * (non-Javadoc) * * @see javax.swing.tree.TreeNode#isLeaf() */ public boolean isLeaf() { return !zooInspectorManager.hasChildren(this.nodePath); } @Override public String toString() { return this.nodeName; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + getOuterType().hashCode(); result = prime * result + ((nodePath == null) ? 0 : nodePath.hashCode()); result = prime * result + ((parent == null) ? 0 : parent.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; ZooInspectorTreeNode other = (ZooInspectorTreeNode) obj; if (!getOuterType().equals(other.getOuterType())) return false; if (nodePath == null) { if (other.nodePath != null) return false; } else if (!nodePath.equals(other.nodePath)) return false; if (parent == null) { if (other.parent != null) return false; } else if (!parent.equals(other.parent)) return false; return true; } private ZooInspectorTreeViewer getOuterType() { return ZooInspectorTreeViewer.this; } } /** * @return {@link List} of the currently selected nodes */ public List<String> getSelectedNodes() { TreePath[] paths = tree.getSelectionPaths(); List<String> selectedNodes = new ArrayList<String>(); if (paths != null) { for (TreePath path : paths) { StringBuilder sb = new StringBuilder(); Object[] pathArray = path.getPath(); for (Object o : pathArray) { String nodeName = o.toString(); if (nodeName.length() > 0) { sb.append("/"); sb.append(o.toString()); } } selectedNodes.add(sb.toString()); } } return selectedNodes; } /* * (non-Javadoc) * * @see * org.apache.zookeeper.inspector.manager.NodeListener#processEvent(java * .lang.String, java.lang.String, java.util.Map) */ public void processEvent(String nodePath, String eventType, Map<String, String> eventInfo) { StringBuilder sb = new StringBuilder(); sb.append("Node: "); sb.append(nodePath); sb.append("\nEvent: "); sb.append(eventType); if (eventInfo != null) { for (Map.Entry<String, String> entry : eventInfo.entrySet()) { sb.append("\n"); sb.append(entry.getKey()); sb.append(": "); sb.append(entry.getValue()); } } this.toasterManager.showToaster(ZooInspectorIconResources .getInformationIcon(), sb.toString()); } }