org.netbeans.jellytools.nodes.Node Maven / Gradle / Ivy
The newest version!
/*
* 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.netbeans.jellytools.nodes;
import java.awt.Component;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import javax.swing.JTree;
import javax.swing.tree.TreePath;
import org.netbeans.jellytools.Bundle;
import org.netbeans.jellytools.JellyVersion;
import org.netbeans.jellytools.actions.Action;
import org.netbeans.jellytools.actions.ActionNoBlock;
import org.netbeans.jemmy.*;
import org.netbeans.jemmy.operators.JPopupMenuOperator;
import org.netbeans.jemmy.operators.JTreeOperator;
import org.netbeans.jemmy.operators.Operator;
import org.openide.explorer.view.Visualizer;
/**
* Ancestor class for all nodes. Nodes should help to easier testing of
* JTree's. The most frequent usage in IDE is in file views but nodes can be
* used in any component which includes a JTree instance. Nodes are also used as
* parameters for action's performing.
Example:
*
* Node node = new Node(new SourcePackagesNode("My Project"), "org.netbeans.jellytools.nodes|Node.java");
* System.out.println(node.getText());
* new OpenAction().performAPI(node);
*
*/
public class Node {
static final String linkSuffix = Bundle.getString("org.openide.loaders.Bundle", "FMT_shadowName", new String[]{""});
/**
* JTreeOperator of tree where node lives
*/
protected JTreeOperator treeOperator;
/**
* TreePath of node
*/
protected TreePath treePath;
/**
* Path of node names representing this node.
*/
protected String stringPath;
/**
* Comparator used for this node instance.
*/
private Operator.StringComparator comparator;
static {
// Checks if you run on correct jemmy version. Writes message to jemmy log if not.
JellyVersion.checkJemmyVersion();
}
/**
* creates new Node instance
*
* @param treeOperator JTreeOperator of tree where node lives
* @param treePath String tree path of node
*/
public Node(JTreeOperator treeOperator, String treePath) {
this(treeOperator, new NodesJTreeOperator(treeOperator).findPath(treePath, "|"));
}
/**
* creates new Node instance
*
* @param treeOperator JTreeOperator of tree where node lives
* @param treePath String tree path of node
* @param indexes String list of indexes of nodes in each level
*/
public Node(JTreeOperator treeOperator, String treePath, String indexes) {
this(treeOperator, new NodesJTreeOperator(treeOperator).findPath(treePath, indexes, "|"));
}
/**
* creates new Node instance
*
* @param parent parent Node
* @param treeSubPath String tree sub-path from parent
*/
public Node(Node parent, String treeSubPath) {
this(parent.tree(), parent.findSubPath(treeSubPath, "|"));
}
/**
* creates new Node instance
*
* @param parent parent Node
* @param childIndex int index of child under parent node
*/
public Node(Node parent, int childIndex) {
this(parent.tree(), parent.tree().getChildPath(parent.getTreePath(), childIndex));
}
/**
* creates new Node instance
*
* @param treeOperator JTreeOperator of tree where node lives
* @param path TreePath of node
*/
public Node(JTreeOperator treeOperator, TreePath path) {
// Only for plain JTreeOperator creates NodesJTreeOperator. If treeOperator
// is something different (like TreeTableOperator$RenderedTreeOperator)
// we cannot replace it.
if (treeOperator.getClass().getName().endsWith("JTreeOperator")) {
this.treeOperator = new NodesJTreeOperator(treeOperator);
} else {
this.treeOperator = treeOperator;
}
this.treePath = path;
this.stringPath = convertPath(path);
}
/**
* Sets comparator for this node. Comparator is used for all methods after
* this method is called.
*
* @param comparator new comparator to be set (e.g. new
* Operator.DefaultStringComparator(true, true); to search string item
* exactly and case sensitive)
*/
public void setComparator(Operator.StringComparator comparator) {
this.comparator = comparator;
tree().setComparator(comparator);
}
/**
* Gets comparator for this node instance.
*
* @return comparator for this node instance.
*/
public Operator.StringComparator getComparator() {
if (comparator == null) {
comparator = tree().getComparator();
}
return comparator;
}
/**
* getter for JTreeOperator of tree where node lives
*
* @return JTreeOperator of tree where node lives
*/
public JTreeOperator tree() {
return treeOperator;
}
/**
* Getter for TreePath of node.
*
* @return TreePath of node
*/
public TreePath getTreePath() {
if (treePath.getLastPathComponent().toString().isEmpty() || tree().getRowForPath(treePath) < 0) {
// node was removed or re-created
treePath = tree().findPath(stringPath);
}
return treePath;
}
/**
* getter for node text
*
* @return String node text
*/
public String getText() {
return getTreePath().getLastPathComponent().toString();
}
private static String convertPath(TreePath path) {
if (path == null) {
return null;
}
int pathCount = path.getPathCount();
if (pathCount < 2) {
return "";
}
StringBuilder bufResult = new StringBuilder(path.getPathComponent(1).toString());
for (int i = 2; i < pathCount; i++) {
bufResult.append("|").append(path.getPathComponent(i).toString());
}
return bufResult.toString();
}
/**
* getter for node path
*
* @return String node path
*/
public String getPath() {
return convertPath(getTreePath());
}
/**
* getter for path of parent node
*
* @return String path of parent node
*/
public String getParentPath() {
return convertPath(getTreePath().getParentPath());
}
/**
* Returns Object instance which represents org.openide.nodes.Node for this
* jellytools node.
*
* @return Object instance which represents org.openide.nodes.Node
*/
public Object getOpenideNode() {
return Visualizer.findNode(this.getTreePath().getLastPathComponent());
}
/**
* calls popup menu on node
*
* @return JPopupMenuOperator
*/
public JPopupMenuOperator callPopup() {
return new JPopupMenuOperator(treeOperator.callPopupOnPath(getTreePath()));
}
/**
* performs action on node through main menu
*
* @param menuPath main menu path of action
*/
public void performMenuAction(String menuPath) {
new Action(menuPath, null).performMenu(this);
}
/**
* performs action on node through popup menu
*
* @param popupPath popup menu path of action
*/
public void performPopupAction(String popupPath) {
new Action(null, popupPath).performPopup(this);
}
/**
* performs action on node through API menu
*
* @param systemActionClass String class name of SystemAction (use null
* value if API mode is not supported)
*/
public void performAPIAction(String systemActionClass) {
new Action(null, null, systemActionClass).performAPI(this);
}
/**
* performs action on node through main menu
*
* @param menuPath main menu path of action
*/
public void performMenuActionNoBlock(String menuPath) {
new ActionNoBlock(menuPath, null).performMenu(this);
}
/**
* performs action on node through popup menu
*
* @param popupPath popup menu path of action
*/
public void performPopupActionNoBlock(String popupPath) {
new ActionNoBlock(null, popupPath).performPopup(this);
}
/**
* performs action on node through API menu
*
* @param systemActionClass String class name of SystemAction (use null
* value if API mode is not supported)
*/
public void performAPIActionNoBlock(String systemActionClass) {
new ActionNoBlock(null, null, systemActionClass).performAPI(this);
}
/**
* selects node
*/
public void select() {
tree().selectPath(getTreePath());
// We need to click on path on Mac because selectPath doesn't fire selection change event
if (System.getProperty("os.name").toLowerCase().indexOf("mac") > -1) { // NOI18N
try {
// sleep to workaround IDE's behavior. IDE consider as double click
// two single clicks on the same position with delay shorter than 300 ms.
// See org.openide.awt.MouseUtils.isDoubleClick().
Thread.sleep(300);
} catch (Exception e) {
throw new JemmyException("Sleeping interrupted", e);
}
tree().clickOnPath(getTreePath());
}
// sleep to workaround IDE's behavior. IDE consider as double click
// two single clicks on the same position with delay shorter than 300 ms.
// See org.openide.awt.MouseUtils.isDoubleClick().
try {
Thread.sleep(300);
} catch (Exception e) {
throw new JemmyException("Sleeping interrupted", e);
}
}
/**
* adds node into set of selected nodes
*/
public void addSelectionPath() {
tree().addSelectionPath(getTreePath());
}
/**
* tests if node is leaf
*
* @return boolean true when node does not have children
*/
public boolean isLeaf() {
return tree().getChildCount(getTreePath()) < 1;
}
/**
* returns list of names of children
*
* @return String[] list of names of children
*/
public String[] getChildren() {
tree().expandPath(getTreePath());
Object o[] = tree().getChildren(getTreePath().getLastPathComponent());
if (o == null) {
return new String[0];
}
String s[] = new String[o.length];
for (int i = 0; i < o.length; i++) {
s[i] = o[i].toString();
}
return s;
}
/**
* determines if current node is link
*
* @return boolean true if node is link
*/
public boolean isLink() {
return getText().endsWith(linkSuffix);
}
/**
* verifies if node is still present. It expands parent path of the node
* during verification.
*
* @return boolean true when node is still present
*/
public boolean isPresent() {
// do not use getTreePath() in this method
tree().expandPath(treePath.getParentPath());
int row = tree().getRowForPath(treePath);
if (row < 0) {
return false;
} else {
return treePath.equals(tree().getPathForRow(row));
}
}
/**
* verifies node's popup path for presence (without invocation)
*
* @param popupPath String popup path
*/
public void verifyPopup(String popupPath) {
verifyPopup(new String[]{popupPath});
}
/**
* verifies node's popup paths for presence (without invocation)
*
* @param popupPaths String[] popup paths
*/
public void verifyPopup(String[] popupPaths) {
//invocation of root popup
final JPopupMenuOperator popup = callPopup();
for (int i = 0; i < popupPaths.length; i++) {
try {
popup.showMenuItem(popupPaths[i], "|");
} catch (NullPointerException npe) {
throw new JemmyException("Popup path [" + popupPaths[i] + "] not found.");
}
}
//close popup and wait until is not visible
popup.waitState(new ComponentChooser() {
@Override
public boolean checkComponent(Component comp) {
popup.pushKey(KeyEvent.VK_ESCAPE);
return !popup.isVisible();
}
@Override
public String getDescription() {
return "Popup menu closed";
}
});
}
static class StringArraySubPathChooser implements JTreeOperator.TreePathChooser {
String[] arr;
int[] indices;
JTreeOperator.StringComparator comparator;
TreePath parentPath;
int parentPathCount;
StringArraySubPathChooser(TreePath parentPath, String[] arr, int[] indices, JTreeOperator.StringComparator comparator) {
this.arr = arr;
this.comparator = comparator;
this.indices = indices;
this.parentPath = parentPath;
this.parentPathCount = parentPath.getPathCount();
}
/**
* implementation of JTreeOperator.TreePathChooser
*
* @param path TreePath
* @param indexInParent int
* @return boolean
*/
@Override
public boolean checkPath(TreePath path, int indexInParent) {
return (path.getPathCount() == arr.length + parentPathCount
&& hasAsParent(path, indexInParent));
}
/**
* implementation of JTreeOperator.TreePathChooser
*
* @param path TreePath
* @param indexInParent int
* @return boolean
*/
@Override
public boolean hasAsParent(TreePath path, int indexInParent) {
if (path.getPathCount() <= parentPathCount) {
return path.isDescendant(parentPath);
}
if (arr.length + parentPathCount < path.getPathCount()) {
return (false);
}
if (indices.length >= path.getPathCount() - parentPathCount
&& indices[path.getPathCount() - parentPathCount - 1] != indexInParent) {
return (false);
}
Object[] comps = path.getPath();
for (int i = parentPathCount; i < comps.length; i++) {
if (!comparator.equals(comps[i].toString(), arr[i - parentPathCount])) {
return (false);
}
}
return (true);
}
/**
* implementation of JTreeOperator.TreePathChooser
*
* @return String description
*/
@Override
public String getDescription() {
StringBuilder bufDesc = new StringBuilder();
Object parr[] = parentPath.getPath();
for (int i = 0; i < parr.length; i++) {
bufDesc.append(parr[i].toString()).append(", ");
}
for (int i = 0; i < arr.length; i++) {
bufDesc.append(arr[i]).append(", ");
}
String desc = bufDesc.toString();
if (desc.length() > 0) {
desc = desc.substring(0, desc.length() - 2);
}
return ("[ " + desc + " ]");
}
}
TreePath findSubPath(String subPath, String delimiter) {
return findSubPath(subPath, "", delimiter);
}
TreePath findSubPath(String subPath, String indexes, String delimiter) {
String indexStr[] = tree().parseString(indexes, delimiter);
int indexInt[] = new int[indexStr.length];
for (int i = 0; i < indexStr.length; i++) {
indexInt[i] = Integer.parseInt(indexStr[i]);
}
TreePath foundTreePath;
try {
foundTreePath = tree().findPath(new Node.StringArraySubPathChooser(getTreePath(),
tree().parseString(subPath, delimiter),
indexInt,
getComparator()));
} catch (JTreeOperator.NoSuchPathException e) {
// try it once more. Probably IDE somehow changed nodes.
foundTreePath = tree().findPath(new Node.StringArraySubPathChooser(getTreePath(),
tree().parseString(subPath, delimiter),
indexInt,
getComparator()));
}
return foundTreePath;
}
/**
* Expands current node to see children
*/
public void expand() {
treeOperator.expandPath(getTreePath());
waitExpanded();
}
/**
* Collapse current node to hide children
*/
public void collapse() {
treeOperator.collapsePath(getTreePath());
waitCollapsed();
}
/**
* Waits for node to be expanded
*/
public void waitExpanded() {
treeOperator.waitExpanded(getTreePath());
}
/**
* Waits for node to be collapsed
*/
public void waitCollapsed() {
treeOperator.waitCollapsed(getTreePath());
}
/**
* Informs if current node is expanded
*
* @return boolean true when node is expanded
*/
public boolean isExpanded() {
return treeOperator.isExpanded(getTreePath());
}
/**
* Informs if current node is collapsed
*
* @return boolean true when node is collapsed
*/
public boolean isCollapsed() {
return treeOperator.isCollapsed(getTreePath());
}
/*
* protected Action[] getActions() { return null; }
*
* public boolean hasAction(Class actionClass) { Action actions[] =
* getActions(); for (int i=0; actions!=null && i popupPaths = new ArrayList();
String path;
for (int i = 0; i < actions.length; i++) {
path = actions[i].getPopupPath();
if (path != null) {
popupPaths.add(path);
}
}
verifyPopup(popupPaths.toArray(new String[0]));
}
/**
* Checks whether child with specified name is present under this node.
*
* @param childName name of child node
* @return true if child is present; false otherwise
*/
public boolean isChildPresent(String childName) {
String[] children = this.getChildren();
for (int i = 0; i < children.length; i++) {
if (getComparator().equals(children[i], childName)) {
return true;
}
}
return false;
}
/**
* Waits until child with specified name is not present under this node. It
* can throw TimeoutExpiredException, if child is still present.
*
* @param childName name of child node
*/
public void waitChildNotPresent(final String childName) {
try {
new Waiter(new Waitable() {
@Override
public Object actionProduced(Object anObject) {
return isChildPresent(childName) ? null : Boolean.TRUE;
}
@Override
public String getDescription() {
return ("Child \"" + childName + "\" not present under parent \"" + getPath() + "\"");
}
}).waitAction(null);
} catch (InterruptedException e) {
throw new JemmyException("Interrupted.", e);
}
}
/**
* Waits until this node is no longer present. It can throw
* TimeoutExpiredException, if the node is still present.
*/
public void waitNotPresent() {
try {
new Waiter(new Waitable() {
@Override
public Object actionProduced(Object anObject) {
return isPresent() ? null : Boolean.TRUE;
}
@Override
public String getDescription() {
// do not use getPath() or getTreePath() here
return ("Wait node " + convertPath(treePath) + " not present.");
}
}).waitAction(null);
} catch (InterruptedException e) {
throw new JemmyException("Interrupted.", e);
}
}
/**
* For IDE nodes we need to call openideNode.getChildren().getNodes(true);
* which should satisfy that all children are correctly initialized. In fact
* it skips 'Please, wait..." node and final tree should not be regenerated
* anymore.
*/
private static class NodesJTreeOperator extends JTreeOperator {
/**
* Creates new instance of NodesJTreeOperator from given JTreeOperator.
*
* @param origOperator original JTreeOperator
*/
public NodesJTreeOperator(JTreeOperator origOperator) {
super((JTree) origOperator.getSource());
copyEnvironment(origOperator);
}
/**
* Expands path and waits until all children are ready. This method is
* used in JTreeOperator.findPathPrimitive, so we need it override here.
*
* @param treePath tree path to be expanded
*/
@Override
public void expandPath(final TreePath treePath) {
super.expandPath(treePath);
// call getNodes(true) but restrict it by timeout to prevent occasional infinite loop
try {
new Waiter(new Waitable() {
@Override
public Object actionProduced(Object anObject) {
try {
Visualizer.findNode(treePath.getLastPathComponent()).getChildren().getNodes(true);
} catch (ClassCastException e) {
// ignore for trees in IDE which are not compound from VisualizerNode instances
}
return Boolean.TRUE;
}
@Override
public String getDescription() {
return "org.openide.nodes.Node.getChildren().getNodes(true)"; //NOI18N
}
}).waitAction(null);
} catch (InterruptedException e) {
throw new JemmyException("Interrupted.", e);
} catch (TimeoutExpiredException tee) {
// ignore and just prints nodes to jemmy log
for (org.openide.nodes.Node n : Visualizer.findNode(treePath.getLastPathComponent()).getChildren().getNodes()) {
getOutput().printLine(" " + n.getDisplayName() + " " + n);
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy