Author: pmouawad
Date: Tue Feb 17 21:59:29 2015
New Revision: 1660514
URL: http://svn.apache.org/r1660514
Log:
Bug 57561 - Module controller UI : Replace combobox by tree
Bugzilla Id: 57561
Modified:
jmeter/trunk/src/components/org/apache/jmeter/control/gui/ModuleControllerGui.java
jmeter/trunk/xdocs/changes.xml
Modified:
jmeter/trunk/src/components/org/apache/jmeter/control/gui/ModuleControllerGui.java
URL:
http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/control/gui/ModuleControllerGui.java?rev=1660514&r1=1660513&r2=1660514&view=diff
==============================================================================
---
jmeter/trunk/src/components/org/apache/jmeter/control/gui/ModuleControllerGui.java
(original)
+++
jmeter/trunk/src/components/org/apache/jmeter/control/gui/ModuleControllerGui.java
Tue Feb 17 21:59:29 2015
@@ -18,7 +18,9 @@
package org.apache.jmeter.control.gui;
+import java.awt.Component;
import java.awt.Dimension;
+import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collection;
@@ -26,56 +28,85 @@ import java.util.Iterator;
import javax.swing.Box;
import javax.swing.BoxLayout;
-import javax.swing.DefaultComboBoxModel;
+import javax.swing.ImageIcon;
import javax.swing.JButton;
-import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
+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.jmeter.control.Controller;
import org.apache.jmeter.control.ModuleController;
+import org.apache.jmeter.control.TestFragmentController;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.action.ActionNames;
-import org.apache.jmeter.gui.action.ActionRouter;
import org.apache.jmeter.gui.tree.JMeterTreeNode;
import org.apache.jmeter.gui.util.MenuFactory;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.TestPlan;
-import org.apache.jmeter.testelement.WorkBench;
import org.apache.jmeter.threads.AbstractThreadGroup;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.gui.layout.VerticalLayout;
/**
- * ModuleController Gui.
+ * ModuleControllerGui provides UI for configuring ModuleController element.
+ * It contains filtered copy of test plan tree (called Module to run tree)
+ * in which target element can be specified by selecting node.
+ * Allowed types of elements in Module to run tree:
+ * - TestPlan - cannot be referenced
+ * - AbstractThreadGroup - cannot be referenced
+ * - Controller (except ModuleController)
+ * - TestFragmentController
*
*/
-public class ModuleControllerGui extends AbstractControllerGui implements
ActionListener
-// implements UnsharedComponent
-{
+public class ModuleControllerGui extends AbstractControllerGui implements
ActionListener {
+ /**
+ *
+ */
+ private static final long serialVersionUID = -4195441608252523573L;
- private static final long serialVersionUID = 240L;
+ private static final String SEPARATOR = " > ";
+ private static final TreeNode[] EMPTY_TREE_NODES = new TreeNode[0];
+
private JMeterTreeNode selected = null;
+
+ /**
+ * Model of a Module to run tree. It is a copy of a test plan tree.
+ * User object of each element is set to a correlated JMeterTreeNode
element of a test plan tree.
+ */
+ private final DefaultTreeModel moduleToRunTreeModel;
+
+ /**
+ * Module to run tree. Allows to select a module to be executed. Many
ModuleControllers can reference
+ * the same element.
+ */
+ private final JTree moduleToRunTreeNodes;
- private final JComboBox nodes;
-
- private final DefaultComboBoxModel nodesModel;
-
+ /**
+ * Used in case if referenced element does not exist in test plan tree
(after removing it for example)
+ */
private final JLabel warningLabel;
+ /**
+ * Helps navigating test plan
+ */
private JButton expandButton;
/**
* Initializes the gui panel for the ModuleController instance.
*/
public ModuleControllerGui() {
- nodesModel = new DefaultComboBoxModel();
- nodes = new JComboBox(nodesModel);
+ moduleToRunTreeModel = new DefaultTreeModel(new
DefaultMutableTreeNode());
+ moduleToRunTreeNodes = new JTree(moduleToRunTreeModel);
+ moduleToRunTreeNodes.setCellRenderer(new
ModuleControllerCellRenderer());
+
warningLabel = new JLabel(""); // $NON-NLS-1$
init();
}
@@ -112,7 +143,7 @@ public class ModuleControllerGui extends
}
buf.append(iter.next());
if (iter.hasNext()) {
- buf.append(" > "); // $NON-NLS-1$
+ buf.append(SEPARATOR); // $NON-NLS-1$
}
}
return buf.toString();
@@ -132,11 +163,18 @@ public class ModuleControllerGui extends
/** {@inheritDoc}} */
@Override
public void modifyTestElement(TestElement element) {
- configureTestElement(element);
- TreeNodeWrapper tnw = (TreeNodeWrapper) nodesModel.getSelectedItem();
- if (tnw != null && tnw.getTreeNode() != null) {
- selected = tnw.getTreeNode();
- if (selected != null) {
+ configureTestElement(element);
+ JMeterTreeNode tn = null;
+ DefaultMutableTreeNode lastSelected = (DefaultMutableTreeNode)
this.moduleToRunTreeNodes.getLastSelectedPathComponent();
+ if (lastSelected != null && lastSelected.getUserObject() instanceof
JMeterTreeNode) {
+ tn = (JMeterTreeNode) lastSelected.getUserObject();
+ }
+ if (tn != null) {
+ selected = tn;
+ //prevent from selecting thread group or test plan elements
+ if (selected != null
+ && !(selected.getTestElement() instanceof
AbstractThreadGroup)
+ && !(selected.getTestElement() instanceof TestPlan)) {
((ModuleController) element).setSelectedNode(selected);
}
}
@@ -146,8 +184,6 @@ public class ModuleControllerGui extends
@Override
public void clearGui() {
super.clearGui();
-
- nodes.setSelectedIndex(-1);
selected = null;
}
@@ -176,93 +212,177 @@ public class ModuleControllerGui extends
setBorder(makeBorder());
add(makeTitlePanel());
- // DROP-DOWN MENU
JPanel modulesPanel = new JPanel();
- modulesPanel.setLayout(new BoxLayout(modulesPanel, BoxLayout.X_AXIS));
- JLabel nodesLabel = new
JLabel(JMeterUtils.getResString("module_controller_module_to_run")); //
$NON-NLS-1$
- modulesPanel.add(nodesLabel);
- modulesPanel.add(Box.createRigidArea(new Dimension(5,0)));
- nodesLabel.setLabelFor(nodes);
- reinitialize();
- modulesPanel.add(nodes);
- modulesPanel.add(warningLabel);
-
- modulesPanel.add(Box.createRigidArea(new Dimension(5,0)));
+
expandButton = new JButton(JMeterUtils.getResString("expand"));
//$NON-NLS-1$
expandButton.addActionListener(this);
modulesPanel.add(expandButton);
+ modulesPanel.setLayout(new BoxLayout(modulesPanel, BoxLayout.Y_AXIS));
+ modulesPanel.add(Box.createRigidArea(new Dimension(0,5)));
+
+ JLabel nodesLabel = new
JLabel(JMeterUtils.getResString("module_controller_module_to_run")); //
$NON-NLS-1$
+ modulesPanel.add(nodesLabel);
+ modulesPanel.add(warningLabel);
add(modulesPanel);
+
+ JPanel treePanel = new JPanel();
+ treePanel.setLayout(new FlowLayout(FlowLayout.LEFT));
+ treePanel.add(moduleToRunTreeNodes);
+ add(treePanel);
}
- private void reinitialize() {
- nodesModel.removeAllElements();
- GuiPackage gp = GuiPackage.getInstance();
- JMeterTreeNode root;
- if (gp != null) {
- root = (JMeterTreeNode)
GuiPackage.getInstance().getTreeModel().getRoot();
- buildNodesModel(root, "", 0); // $NON-NLS-1$
- }
- if (selected != null) {
- TreeNodeWrapper current;
- for (int i = 0; i < nodesModel.getSize(); i++) {
- current = (TreeNodeWrapper) nodesModel.getElementAt(i);
- if (current.getTreeNode() != null &&
current.getTreeNode().equals(selected)) {
- nodesModel.setSelectedItem(current);
- break;
- }
- }
- }
- }
-
- private void buildNodesModel(JMeterTreeNode node, String parent_name, int
level) {
- if (level == 0 && (parent_name == null || parent_name.length() == 0)) {
- nodesModel.addElement(new TreeNodeWrapper(null, "")); //
$NON-NLS-1$
- }
- String separator = " > "; // $NON-NLS-1$
- if (node != null) {
- StringBuilder name = new StringBuilder();
- for (int i = 0; i < node.getChildCount(); i++) {
- name.setLength(0);
- JMeterTreeNode cur = (JMeterTreeNode) node.getChildAt(i);
- TestElement te = cur.getTestElement();
- if (te instanceof AbstractThreadGroup) {
- name.append(parent_name);
- name.append(cur.getName());
- name.append(separator);
- buildNodesModel(cur, name.toString(), level);
- } else if (te instanceof Controller && !(te instanceof
ModuleController)) {
- name.append(parent_name);
- name.append(cur.getName());
- TreeNodeWrapper tnw = new TreeNodeWrapper(cur,
name.toString());
- nodesModel.addElement(tnw);
- name.append(separator);
- buildNodesModel(cur, name.toString(), level + 1);
- } else if (te instanceof TestPlan || te instanceof WorkBench) {
- name.append(cur.getName());
- name.append(separator);
- buildNodesModel(cur, name.toString(), 0);
- }
- }
- }
- }
-
+ /**
+ * Recursively traverse module to run tree in order to find JMeterTreeNode
element given by testPlanPath
+ * in a DefaultMutableTreeNode tree
+ *
+ * @param level - current search level
+ * @param testPlanPath - path of a test plan tree element
+ * @param parent - module to run tree parent element
+ *
+ * @return path of a found element
+ */
+ private TreeNode[] findPathInTreeModel(int level, TreeNode[]
testPlanPath, DefaultMutableTreeNode parent)
+ {
+ if(level >= testPlanPath.length) {
+ return EMPTY_TREE_NODES;
+ }
+ int childCount = parent.getChildCount();
+ JMeterTreeNode searchedTreeNode =
+ (JMeterTreeNode) testPlanPath[level];
+
+ for (int i = 0; i < childCount; i++) {
+ DefaultMutableTreeNode child = (DefaultMutableTreeNode)
parent.getChildAt(i);
+ JMeterTreeNode childUserObj = (JMeterTreeNode)
child.getUserObject();
+
+ if(!childUserObj.equals(searchedTreeNode)){
+ continue;
+ } else {
+ if(level == (testPlanPath.length - 1)){
+ return child.getPath();
+ } else {
+ return findPathInTreeModel(level+1,
testPlanPath, child);
+ }
+ }
+ }
+ return EMPTY_TREE_NODES;
+ }
+
+ /**
+ * Expand module to run tree to selected JMeterTreeNode and set
selection path to it
+ * @param selected - referenced module to run
+ */
+ private void focusSelectedOnTree(JMeterTreeNode selected)
+ {
+ TreeNode[] path = selected.getPath();
+ TreeNode[] filteredPath = new TreeNode[path.length-1];
+
+ //ignore first element of path - WorkBench, (why WorkBench is
appearing in the path ???)
+ for(int i = 1; i < path.length; i++){
+ filteredPath[i-1] = path[i];
+ }
+
+ DefaultMutableTreeNode root = (DefaultMutableTreeNode)
moduleToRunTreeNodes.getModel().getRoot();
+ //treepath of test plan tree and module to run tree cannot be
compared directly - moduleToRunTreeModel.getPathToRoot()
+ //custom method for finding an JMeterTreeNode element in
DefaultMutableTreeNode have to be used
+ TreeNode[] dmtnPath = this.findPathInTreeModel(1, filteredPath,
root);
+ if (dmtnPath.length>0) {
+ TreePath treePath = new TreePath(dmtnPath);
+ moduleToRunTreeNodes.setSelectionPath(treePath);
+ moduleToRunTreeNodes.scrollPathToVisible(treePath);
+ }
+ }
+
+ /**
+ *
+ */
+ private void reinitialize() {
+ ((DefaultMutableTreeNode)
moduleToRunTreeModel.getRoot()).removeAllChildren();
+
+ GuiPackage gp = GuiPackage.getInstance();
+ JMeterTreeNode root;
+ if (gp != null) {
+ root = (JMeterTreeNode)
GuiPackage.getInstance().getTreeModel().getRoot();
+ buildTreeNodeModel(root, 0, null);
+ moduleToRunTreeModel.nodeStructureChanged((TreeNode)
moduleToRunTreeModel.getRoot());
+ }
+ if (selected != null) {
+ //expand Module to run tree to selected node and set
selection path to it
+ this.focusSelectedOnTree(selected);
+ }
+ }
+
+ /**
+ * Recursively build module to run tree. Only 4 types of elements are
allowed to be added:
+ * - All controllers except ModuleController
+ * - TestPlan
+ * - TestFragmentController
+ * - AbstractThreadGroup
+ *
+ * @param node - element of test plan tree
+ * @param level - level of element in a tree
+ * @param parent
+ */
+ private void buildTreeNodeModel(JMeterTreeNode node, int level,
+ DefaultMutableTreeNode parent) {
+
+ if (node != null) {
+ for (int i = 0; i < node.getChildCount(); i++) {
+ JMeterTreeNode cur = (JMeterTreeNode)
node.getChildAt(i);
+ TestElement te = cur.getTestElement();
+
+ if (te instanceof Controller
+ && !(te instanceof
ModuleController) && level > 0) {
+ DefaultMutableTreeNode newNode = new
DefaultMutableTreeNode(cur);
+ parent.add(newNode);
+ buildTreeNodeModel(cur, level + 1,
newNode);
+
+ } else if (te instanceof
TestFragmentController) {
+ DefaultMutableTreeNode newNode = new
DefaultMutableTreeNode(cur);
+ parent.add(newNode);
+ buildTreeNodeModel(cur, level + 1,
newNode);
+
+ } else if (te instanceof AbstractThreadGroup) {
+ DefaultMutableTreeNode newNode = new
DefaultMutableTreeNode(cur);
+ parent.add(newNode);
+ buildTreeNodeModel(cur, level + 1,
newNode);
+ }
+
+ else if (te instanceof TestPlan) {
+ ((DefaultMutableTreeNode)
moduleToRunTreeModel.getRoot())
+ .setUserObject(cur);
+ buildTreeNodeModel(cur, level,
+
(DefaultMutableTreeNode) moduleToRunTreeModel.getRoot());
+ }
+ }
+ }
+ }
+
+ /**
+ * Implementation of Expand button - moves focus to a test plan tree
element referenced by
+ * selected element in Module to run tree
+ */
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==expandButton) {
- // reset previous result
- ActionRouter.getInstance().doActionNow(new
ActionEvent(e.getSource(), e.getID(), ActionNames.SEARCH_RESET));
- JMeterTreeNode currentSelectedNode = null;
- TreeNodeWrapper tnw = (TreeNodeWrapper)
nodesModel.getSelectedItem();
- if (tnw != null && tnw.getTreeNode() != null) {
- currentSelectedNode = tnw.getTreeNode();
+ JMeterTreeNode tn = null;
+ DefaultMutableTreeNode selected = (DefaultMutableTreeNode)
+ this.moduleToRunTreeNodes.getLastSelectedPathComponent();
+ if(selected != null && selected.getUserObject() instanceof
JMeterTreeNode){
+ tn = (JMeterTreeNode) selected.getUserObject();
}
- if (currentSelectedNode != null) {
- expandToSelectNode(currentSelectedNode);
+ if(tn != null){
+ TreePath treePath = new TreePath(tn.getPath());
+ //changing selection in a test plan tree
+
GuiPackage.getInstance().getTreeListener().getJTree()
+ .setSelectionPath(treePath);
+ //expanding tree to make referenced element
visible in test plan tree
+
GuiPackage.getInstance().getTreeListener().getJTree()
+ .scrollPathToVisible(treePath);
}
- return;
}
}
+
/**
* @param selected JMeterTreeNode tree node to expand
*/
@@ -272,4 +392,36 @@ public class ModuleControllerGui extends
jTree.expandPath(new TreePath(selected.getPath()));
selected.setMarkedBySearch(true);
}
+
+ /**
+ * Renderer class for printing "module to run" tree
+ */
+ private static class ModuleControllerCellRenderer extends
DefaultTreeCellRenderer {
+
+ private static final long serialVersionUID =
1129098620102526299L;
+
+ /**
+ * @see
javax.swing.tree.DefaultTreeCellRenderer#getTreeCellRendererComponent(javax.swing.JTree,
java.lang.Object, boolean, boolean, boolean, int, boolean)
+ */
+ @Override
+ public Component getTreeCellRendererComponent(JTree tree, Object
value, boolean selected, boolean expanded,
+ boolean leaf, int row, boolean hasFocus) {
+ JMeterTreeNode node = (JMeterTreeNode)((DefaultMutableTreeNode)
value).getUserObject();
+ if(node != null){
+ super.getTreeCellRendererComponent(tree, node.getName(),
selected, expanded, leaf, row,
+ hasFocus);
+ //print same icon as in test plan tree
+ boolean enabled = node.isEnabled();
+ ImageIcon icon = node.getIcon(enabled);
+ if (icon != null) {
+ if (enabled) {
+ setIcon(icon);
+ } else {
+ setDisabledIcon(icon);
+ }
+ }
+ }
+ return this;
+ }
+ }
}
Modified: jmeter/trunk/xdocs/changes.xml
URL:
http://svn.apache.org/viewvc/jmeter/trunk/xdocs/changes.xml?rev=1660514&r1=1660513&r2=1660514&view=diff
==============================================================================
--- jmeter/trunk/xdocs/changes.xml (original)
+++ jmeter/trunk/xdocs/changes.xml Tue Feb 17 21:59:29 2015
@@ -207,6 +207,7 @@ See <bugzilla>56357</bugzilla> for deta
<h3>Controllers</h3>
<ul>
+<li><bug>57561</bug>Module controller UI : Replace combobox by tree.
Contributed by Maciej Franek (maciej.franek at gmail.com)</li>
</ul>
<h3>Listeners</h3>
@@ -258,6 +259,7 @@ See <bugzilla>56357</bugzilla> for deta
<li><a href="http://blazemeter.com">BlazeMeter Ltd.</a></li>
<li>Benoit Wiart (benoit.wiart at gmail.com)</li>
<li>Pascal Schumacher (pascal.schumacher at t-systems.com)</li>
+<li>Maciej Franek (maciej.franek at gmail.com)</li>
</ul>
<br/>