下面列出了android.view.accessibility.AccessibilityNodeInfo#getChild ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
/**
* This should be used when it's already determined that the node is NAF and
* a further check of its children is in order. A node maybe a container
* such as LinerLayout and may be set to be clickable but have no text or
* content description but it is counting on one of its children to fulfill
* the requirement for being accessibility friendly by having one or more of
* its children fill the text or content-description. Such a combination is
* considered by this dumper as acceptable for accessibility.
*
* @param node
* @return false if node fails the check.
*/
private static boolean childNafCheck(AccessibilityNodeInfo node) {
int childCount = node.getChildCount();
for (int x = 0; x < childCount; x++) {
AccessibilityNodeInfo childNode = node.getChild(x);
if (!safeCharSeqToString(childNode.getContentDescription())
.isEmpty()
|| !safeCharSeqToString(childNode.getText()).isEmpty())
return true;
if (childNafCheck(childNode))
return true;
}
return false;
}
private List<AccessibilityNodeInfo> flattenTree(AccessibilityNodeInfo rootNode) {
List<AccessibilityNodeInfo> allNodes = new ArrayList<AccessibilityNodeInfo>();
Queue<AccessibilityNodeInfo> Q = new LinkedList<AccessibilityNodeInfo>();
Q.add(rootNode);
// BFS, level-order traversal
while (!Q.isEmpty()) {
AccessibilityNodeInfo node = Q.poll();
allNodes.add(node);
for (int i = 0; i < node.getChildCount(); i++) {
AccessibilityNodeInfo child = node.getChild(i);
Q.add(child);
}
}
return allNodes;
}
/**
* Create a new {@link ViewHierarchyElementAndroid} from an {@link AccessibilityNodeInfo} and
* appends it and its children to {@code elementList}. The new elements' {@link
* ViewHierarchyElementAndroid#getId()} will match their index in {@code elementList}. This also
* adds the newly created elements as children to the provided {@code parent} element.
*
* @param forInfo The non-null {@link AccessibilityNodeInfo} from which to create the elements
* @param elementList The list to hold the elements
* @param parent The {@link ViewHierarchyElementAndroid} corresponding to {@code forInfo}'s
* parent, or {@code null} if {@code forInfo} is a root view.
* @param elementToNodeInfoMap A {@link Map} to populate with the {@link
* ViewHierarchyElementAndroid}s created during construction of the hierarchy mapped to their
* originating {@link AccessibilityNodeInfo}s
* @return The newly created element
*/
private static ViewHierarchyElementAndroid buildViewHierarchy(
AccessibilityNodeInfo forInfo,
List<ViewHierarchyElementAndroid> elementList,
@Nullable ViewHierarchyElementAndroid parent,
Map<ViewHierarchyElementAndroid, AccessibilityNodeInfo> elementToNodeInfoMap) {
checkNotNull(forInfo, "Attempted to build hierarchy from null root node");
ViewHierarchyElementAndroid element =
ViewHierarchyElementAndroid.newBuilder(elementList.size(), parent, forInfo).build();
elementList.add(element);
elementToNodeInfoMap.put(element, AccessibilityNodeInfo.obtain(forInfo));
for (int i = 0; i < forInfo.getChildCount(); ++i) {
AccessibilityNodeInfo child = forInfo.getChild(i);
if (child != null) {
element.addChild(buildViewHierarchy(child, elementList, element, elementToNodeInfoMap));
child.recycle();
}
}
return element;
}
/**
* @param nodeInfo
* @param accessibilityEvent
* @return 消息列表的最后一个Item是否为视频通话结束或取消
*/
private boolean isVideoChatEnded(AccessibilityNodeInfo nodeInfo, AccessibilityEvent accessibilityEvent) {
String pkg = accessibilityEvent.getPackageName().toString();
List<AccessibilityNodeInfo> listNode = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mobileqq:id/listView1");
AccessibilityNodeInfo tempNode = null;
try {
if (Constant.QQ_PKG.equals(pkg)) {
if (!AppUtils.isListEmpty(listNode)) {
tempNode = listNode.get(0);
tempNode = tempNode.getChild(tempNode.getChildCount() - 1);
if (tempNode != null && tempNode.getClassName().equals(RelativeLayout.class.getName())) {
return !AppUtils.isListEmpty(tempNode.findAccessibilityNodeInfosByText("拒绝"))
|| !AppUtils.isListEmpty(tempNode.findAccessibilityNodeInfosByText("通话时长"))
|| !AppUtils.isListEmpty(tempNode.findAccessibilityNodeInfosByText("取消"));
}
}
}
} finally {
if (tempNode != null) {
tempNode.recycle();
}
}
return false;
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Nullable
private static AccessibilityNodeInfo getNetworkConsentAgreeButton(@NonNull AccessibilityNodeInfo node) {
if (node.isClickable()) {
Log.d(TAG, "Consent dialog button found. Text: " + node.getText() + ", ID: " + node.getViewIdResourceName());
// Text is locale dependent, but viewId seems to be consistent...
if (NETWORK_CONSENT_ACCEPT_BUTTON_VIEW_ID.equals(node.getViewIdResourceName())) {
return node;
}
}
int count = node.getChildCount();
for (int i = 0; i < count; i++) {
AccessibilityNodeInfo child = node.getChild(i);
if (child != null) {
AccessibilityNodeInfo button = getNetworkConsentAgreeButton(child);
if (button != null) {
return button;
}
}
}
return null;
}
private static AccessibilityNodeInfo refreshFromChild(
AccessibilityNodeInfo node) {
if (node.getChildCount() > 0) {
AccessibilityNodeInfo firstChild = node.getChild(0);
if (firstChild != null) {
AccessibilityNodeInfo parent = firstChild.getParent();
firstChild.recycle();
if (node.equals(parent)) {
return parent;
} else {
recycleNodes(parent);
}
}
}
return null;
}
/**
* 爬遍所有节点查找可点的按钮,用于解决Android5.1等组件层次分离的情况
* @param root 界面根节点
*/
void crawlButton(AccessibilityNodeInfo root)
{
AccessibilityNodeInfo child;
Integer size = root.getChildCount();
for (Integer i=0;i < size;i++)
{
child = root.getChild(i);
if (child != null)
{
if (child.getClassName().toString().equals("android.widget.Button"))
{
// Log.d("crawl-button", child.toString());
//Log.d("click", "OPEN");
child.performAction(AccessibilityNodeInfo.ACTION_CLICK); // 拆红包
cnt_open += 1;
flags_detail = 1; // 红包有效
state = State.DETAIL;
}
crawlButton(child);
}
}
}
private static void logNodeHierachy(AccessibilityNodeInfo nodeInfo, int depth) {
Rect bounds = new Rect();
nodeInfo.getBoundsInScreen(bounds);
StringBuilder sb = new StringBuilder();
if (depth > 0) {
for (int i=0; i<depth; i++) {
sb.append(" ");
}
sb.append("\u2514 ");
}
sb.append(nodeInfo.getClassName());
sb.append(" (" + nodeInfo.getChildCount() + ")");
sb.append(" " + bounds.toString());
if (nodeInfo.getText() != null) {
sb.append(" - \"" + nodeInfo.getText() + "\"");
}
Log.v(TAG, sb.toString());
for (int i=0; i<nodeInfo.getChildCount(); i++) {
AccessibilityNodeInfo childNode = nodeInfo.getChild(i);
if (childNode != null) {
logNodeHierachy(childNode, depth + 1);
}
}
}
/**
* 点击,打开视频聊天面板
*
* @param nodeInfo
* @return 是否找到
*/
private boolean preStartVideoChat(AccessibilityNodeInfo nodeInfo) {
//并不能直接通过ID或者Text找到+号按钮,应该是动态添加的,先找到输入库,通过输入框找到相同的parent,+号的Index在倒数第二的位置上
List<AccessibilityNodeInfo> inputNodes = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mobileqq:id/inputBar");
if (!AppUtils.isListEmpty(inputNodes)) {
AccessibilityNodeInfo node = inputNodes.get(0).getParent();
node = node.getChild(Math.max(0, node.getChildCount() - 2));
if (node.getClassName().toString().contains(ImageView.class.getName())) {
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
return true;
}
}
return false;
}
private AccessibilityNodeInfo getPreviousSibling(
AccessibilityNodeInfo from) {
AccessibilityNodeInfo ret = null;
AccessibilityNodeInfo parent = from.getParent();
if (parent == null) {
return null;
}
AccessibilityNodeInfo prev = null;
AccessibilityNodeInfo cur = null;
try {
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; ++i) {
cur = parent.getChild(i);
if (cur == null) {
return null;
}
if (cur.equals(from)) {
ret = prev;
prev = null;
return ret;
}
if (prev != null) {
prev.recycle();
}
prev = cur;
cur = null;
}
} finally {
parent.recycle();
if (prev != null) {
prev.recycle();
}
if (cur != null) {
cur.recycle();
}
}
return ret;
}
private void findChildView2(AccessibilityNodeInfo info, String parentText) {
parentText = parentText + " |-- " + info.getText();
L.d("得到控件 " + parentText);
for (int i = 0; i < info.getChildCount(); i++) {
AccessibilityNodeInfo child = info.getChild(i);
if (child != null) {
// L.d("得到子控件 " + child.getText());
findChildView2(child, parentText + "");
} else {
// L.d("得到所有控件" + info.getText());
}
}
}
private void compute_feature_vector(Hashtable<String, FeatureValuePair> map, AccessibilityNodeInfo node, int level) {
int child_cnt = node.getChildCount();
if (child_cnt > 0) {
for (int i = 0; i < child_cnt; i++) {
AccessibilityNodeInfo child = node.getChild(i);
compute_feature_vector(map, child, level + 1);
}
} else {
String key = node.getClassName().toString() + "@" + level;
FeatureValuePair value;
int size = 0;
CharSequence text = node.getText();
if (text != null) {
size = text.toString().length();
}
if (node.getClassName().toString().equals(ImageView.class.getCanonicalName())) {
Rect bounds = new Rect();
node.getBoundsInScreen(bounds);
String timestamp = new SimpleDateFormat("MM-dd'T'HH-mm-ss-SSS").format(new Date());
size = get_imageview_size("/data/local/tmp/local/tmp/haos.png", bounds, "/data/local/tmp/local/tmp/" + key + "_" + timestamp + ".png");
} else {
// Util.log(node.getClassName() + " NOT " + ImageView.class.getCanonicalName());
}
value = map.containsKey(key) ? map.get(key) : new FeatureValuePair(0, 0);
value.count++;
value.size += size;
map.put(key, value);
// Util.log("Leaf: " + key + ", " + size);
}
}
/**
* 遍历以当前 nodeInfo 为根节点的节点树,将EditText节点存储在editTextNodes中
*
* @param nodeInfo current root node
* @param editTextNodes store EditText nodes.
*/
private void traverse(AccessibilityNodeInfo nodeInfo,
List<AccessibilityNodeInfo> editTextNodes) {
if (nodeInfo.getChildCount() == 0) { // 叶子节点
handleLeafNodeInfo(nodeInfo, editTextNodes);
} else {
for (int i = 0; i < nodeInfo.getChildCount(); i++) {
AccessibilityNodeInfo childNodeInfo = nodeInfo.getChild(i);
if (childNodeInfo != null) {
traverse(childNodeInfo, editTextNodes);
}
}
}
}
private AccessibilityNodeInfo getLastChild(
AccessibilityNodeInfo from) {
if (from.getChildCount() < 1) {
return null;
}
return from.getChild(from.getChildCount() - 1);
}
/**
* Traverses the {@link AccessibilityNodeInfo} hierarchy starting at {@code node}, and returns
* a list of nodes which match the {@code selector} criteria. <br />
* <strong>Note:</strong> The caller must release each {@link AccessibilityNodeInfo} instance
* by calling {@link AccessibilityNodeInfo#recycle()} to avoid leaking resources.
*
* @param node The root of the {@link AccessibilityNodeInfo} subtree we are currently searching.
* @param index The index of this node underneath its parent.
* @param depth The distance between {@code node} and the root node.
* @param partialMatches The current list of {@link PartialMatch}es that need to be updated.
* @return A {@link List} of {@link AccessibilityNodeInfo}s that meet the search criteria.
*/
private List<AccessibilityNodeInfo> findMatches(AccessibilityNodeInfo node,
int index, int depth, SinglyLinkedList<PartialMatch> partialMatches) {
List<AccessibilityNodeInfo> ret = new ArrayList<AccessibilityNodeInfo>();
// Don't bother searching the subtree if it is not visible
// if (!node.isVisibleToUser()) {
// return ret;
// }
// Update partial matches
for (PartialMatch partialMatch : partialMatches) {
partialMatches = partialMatch.update(node, index, depth, partialMatches);
}
// Create a new match, if necessary
PartialMatch currentMatch = PartialMatch.accept(node, mSelector, index, depth);
if (currentMatch != null) {
partialMatches = SinglyLinkedList.prepend(currentMatch, partialMatches);
}
// For each child
int numChildren = node.getChildCount();
boolean hasNullChild = false;
for (int i = 0; i < numChildren; i++) {
AccessibilityNodeInfo child = node.getChild(i);
if (child == null) {
if (!hasNullChild) {
Log.w(TAG, String.format("Node returned null child: %s", node.toString()));
}
hasNullChild = true;
Log.w(TAG, String.format("Skipping null child (%s of %s)", i, numChildren));
continue;
}
// Add any matches found under the child subtree
ret.addAll(findMatches(child, i, depth + 1, partialMatches));
// We're done with the child
child.recycle();
// Return early if we sound a match and shortCircuit is true
if (!ret.isEmpty() && mShortCircuit) {
return ret;
}
}
// Finalize match, if necessary
if (currentMatch != null && currentMatch.finalizeMatch() && node.isVisibleToUser()) {
ret.add(AccessibilityNodeInfo.obtain(node));
}
return ret;
}
private static void dumpNodeRec(
AccessibilityNodeInfo node,
XmlSerializer serializer,
int index,
int width,
int height,
boolean withClassName)
throws IOException {
serializer.startTag("", withClassName ? safeTagString(node.getClassName()) : "node");
if (!nafExcludedClass(node) && !nafCheck(node)) {
serializer.attribute("", "NAF", Boolean.toString(true));
}
serializer.attribute("", "index", Integer.toString(index));
final String text;
if (node.getRangeInfo() == null) {
text = safeCharSeqToString(node.getText());
} else {
text = Float.toString(node.getRangeInfo().getCurrent());
}
serializer.attribute("", "text", text);
serializer.attribute("", "class", safeCharSeqToString(node.getClassName()));
serializer.attribute("", "package", safeCharSeqToString(node.getPackageName()));
serializer.attribute("", "content-desc", safeCharSeqToString(node.getContentDescription()));
serializer.attribute("", "checkable", Boolean.toString(node.isCheckable()));
serializer.attribute("", "checked", Boolean.toString(node.isChecked()));
serializer.attribute("", "clickable", Boolean.toString(node.isClickable()));
serializer.attribute("", "enabled", Boolean.toString(node.isEnabled()));
serializer.attribute("", "focusable", Boolean.toString(node.isFocusable()));
serializer.attribute("", "focused", Boolean.toString(node.isFocused()));
serializer.attribute("", "scrollable", Boolean.toString(node.isScrollable()));
serializer.attribute("", "long-clickable", Boolean.toString(node.isLongClickable()));
serializer.attribute("", "password", Boolean.toString(node.isPassword()));
serializer.attribute("", "selected", Boolean.toString(node.isSelected()));
/** True if the device is >= API 18 */
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
serializer.attribute(
"", "bounds", getVisibleBoundsInScreen(node, width, height).toShortString());
serializer.attribute("", "resource-id", safeCharSeqToString(node.getViewIdResourceName()));
}
int count = node.getChildCount();
for (int i = 0; i < count; i++) {
AccessibilityNodeInfo child = node.getChild(i);
if (child != null) {
if (child.isVisibleToUser()) {
dumpNodeRec(child, serializer, i, width, height, withClassName);
child.recycle();
} else {
Log.i(TAG, String.format("Skipping invisible child: %s", child.toString()));
}
} else {
Log.i(TAG, String.format("Null child %d/%d, parent: %s", i, count, node.toString()));
}
}
serializer.endTag("", withClassName ? safeTagString(node.getClassName()) : "node");
}
private static void dumpNodeRec(AccessibilityNodeInfo node,
XmlSerializer serializer, int index, int width, int height)
throws IOException {
serializer.startTag("", "node");
if (!nafExcludedClass(node) && !nafCheck(node))
serializer.attribute("", "NAF", Boolean.toString(true));
serializer.attribute("", "index", Integer.toString(index));
serializer.attribute("", "text", safeCharSeqToString(node.getText()));
serializer.attribute("", "resource-id",
safeCharSeqToString(node.getViewIdResourceName()));
serializer.attribute("", "class",
safeCharSeqToString(node.getClassName()));
serializer.attribute("", "package",
safeCharSeqToString(node.getPackageName()));
serializer.attribute("", "content-desc",
safeCharSeqToString(node.getContentDescription()));
serializer.attribute("", "checkable",
Boolean.toString(node.isCheckable()));
serializer.attribute("", "checked", Boolean.toString(node.isChecked()));
serializer.attribute("", "clickable",
Boolean.toString(node.isClickable()));
serializer.attribute("", "enabled", Boolean.toString(node.isEnabled()));
serializer.attribute("", "focusable",
Boolean.toString(node.isFocusable()));
serializer.attribute("", "focused", Boolean.toString(node.isFocused()));
serializer.attribute("", "scrollable",
Boolean.toString(node.isScrollable()));
serializer.attribute("", "long-clickable",
Boolean.toString(node.isLongClickable()));
serializer.attribute("", "password",
Boolean.toString(node.isPassword()));
serializer.attribute("", "selected",
Boolean.toString(node.isSelected()));
serializer.attribute("", "bounds", AccessibilityNodeInfoHelper
.getVisibleBoundsInScreen(node, width, height).toShortString());
int count = node.getChildCount();
for (int i = 0; i < count; i++) {
AccessibilityNodeInfo child = node.getChild(i);
if (child != null) {
if (child.isVisibleToUser()) {
dumpNodeRec(child, serializer, i, width, height);
child.recycle();
} else {
Log.i(LOGTAG,
String.format("Skipping invisible child: %s",
child.toString()));
}
} else {
Log.i(LOGTAG, String.format("Null child %d/%d, parent: %s", i,
count, node.toString()));
}
}
serializer.endTag("", "node");
}
/**
* Traverses the {@link AccessibilityNodeInfo} hierarchy starting at {@code node}, and returns
* a list of nodes which match the {@code selector} criteria. <br />
* <strong>Note:</strong> The caller must release each {@link AccessibilityNodeInfo} instance
* by calling {@link AccessibilityNodeInfo#recycle()} to avoid leaking resources.
*
* @param node The root of the {@link AccessibilityNodeInfo} subtree we are currently searching.
* @param index The index of this node underneath its parent.
* @param depth The distance between {@code node} and the root node.
* @param partialMatches The current list of {@link PartialMatch}es that need to be updated.
* @return A {@link List} of {@link AccessibilityNodeInfo}s that meet the search criteria.
*/
private List<AccessibilityNodeInfo> findMatches(AccessibilityNodeInfo node,
int index, int depth, SinglyLinkedList<PartialMatch> partialMatches) {
List<AccessibilityNodeInfo> ret = new ArrayList<AccessibilityNodeInfo>();
// Don't bother searching the subtree if it is not visible
if (!node.isVisibleToUser()) {
return ret;
}
// Update partial matches
for (PartialMatch partialMatch : partialMatches) {
partialMatches = partialMatch.update(node, index, depth, partialMatches);
}
// Create a new match, if necessary
PartialMatch currentMatch = PartialMatch.accept(node, mSelector, index, depth);
if (currentMatch != null) {
partialMatches = SinglyLinkedList.prepend(currentMatch, partialMatches);
}
// For each child
int numChildren = node.getChildCount();
boolean hasNullChild = false;
for (int i = 0; i < numChildren; i++) {
AccessibilityNodeInfo child = node.getChild(i);
if (child == null) {
if (!hasNullChild) {
Log.w(TAG, String.format("Node returned null child: %s", node.toString()));
}
hasNullChild = true;
Log.w(TAG, String.format("Skipping null child (%s of %s)", i, numChildren));
continue;
}
// Add any matches found under the child subtree
ret.addAll(findMatches(child, i, depth + 1, partialMatches));
// We're done with the child
child.recycle();
// Return early if we sound a match and shortCircuit is true
if (!ret.isEmpty() && mShortCircuit) {
return ret;
}
}
// Finalize match, if necessary
if (currentMatch != null && currentMatch.finalizeMatch()) {
ret.add(AccessibilityNodeInfo.obtain(node));
}
return ret;
}
/**
* 好友:
* 姓名 last-3 android.widget.TextView
* 消息 5-[]-last android.widget.TextView focusable
* 文本框 7-0 android.widget.EditText
* 发送按钮 7-1 android.widget.Button
* <p>
* 群:
* 群名 1
* 消息 4/5-
* <p>
* 群里的临时会话:
* 姓名 1-0
* 消息 6-[]-last
* 文本框 8-0
* 发送按钮 8-1
*/
protected boolean init(AccessibilityNodeInfo root) {
if (root.getChildCount() < 10) {
// 正常是13
// 有其他消息是14
// 非好友是10
Log.v(TAG, "init: root.childCount: " + root.getChildCount());
return false;
}
// List<AccessibilityNodeInfo> inputList;
// List<AccessibilityNodeInfo> sendList;
// inputList = root.findAccessibilityNodeInfosByViewId(IdInput);
// sendList = root.findAccessibilityNodeInfosByViewId(IdSend);
// if (inputList.size() == 0) {
// Log.d(TAG, "init: input is null, return");
// return false;
// }
// if (sendList.size() == 0) {
// Log.d(TAG, "init: send button is null, return");
// return false;
// }
// inputNode = inputList.get(0);
// sendBtnNode = sendList.get(0);
List<AccessibilityNodeInfo> titleList;
titleList = root.findAccessibilityNodeInfosByViewId(IdTitle);
if (titleList.size() == 0) {
Log.d(TAG, "init: title is null, return");
return false;
}
titleNode = titleList.get(0);
if (titleNode.getText() == null) {
Log.d(TAG, "init: name is null,return");
return false;
}
title = titleNode.getText() + "";
chatGroupViewNode = root.getChild(0);
if (chatGroupViewNode == null) {
Log.d(TAG, "init: chatView node is null, return");
return false;
}
if (!IdChatGroupView.equals(chatGroupViewNode.getViewIdResourceName())) {
Log.d(TAG, "init: not chat view, return");
return false;
}
isOtherMsg = IdOtherMsg.equals(root.getChild(1).getViewIdResourceName());
return true;
}
/**
* Recursively examine the {@link AccessibilityNodeInfo} object
*
* @param parent the {@link AccessibilityNodeInfo} parent object
* @return the extracted text or null if no text was contained in the child objects
*/
private String examineChild(@Nullable final AccessibilityNodeInfo parent) {
if (DEBUG) {
MyLog.i(CLS_NAME, "examineChild");
}
if (parent != null) {
for (int i = 0; i < parent.getChildCount(); i++) {
final AccessibilityNodeInfo nodeInfo = parent.getChild(i);
if (nodeInfo != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "examineChild: nodeInfo: getClassName: " + nodeInfo.getClassName());
MyLog.i(CLS_NAME, "examineChild: nodeInfo: contentDesc: " + nodeInfo.getContentDescription());
}
if (nodeInfo.getText() != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "examineChild: have text: returning: " + nodeInfo.getText().toString());
}
return nodeInfo.getText().toString();
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "examineChild: text: null: recurse");
}
final int childCount = nodeInfo.getChildCount();
if (DEBUG) {
MyLog.i(CLS_NAME, "examineChild: childCount: " + childCount);
}
if (childCount > 0) {
final String text = examineChild(nodeInfo);
if (text != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "examineChild: have recursive text: returning: " + text);
}
return text;
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "examineChild: recursive text: null");
}
}
}
}
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "examineChild: nodeInfo null");
}
}
}
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "examineChild: parent null");
}
}
return null;
}