1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.uiautomator.tree;
18
19import java.util.ArrayList;
20import java.util.Collections;
21import java.util.List;
22
23public class BasicTreeNode {
24
25    private static final BasicTreeNode[] CHILDREN_TEMPLATE = new BasicTreeNode[] {};
26
27    protected BasicTreeNode mParent;
28
29    protected final List<BasicTreeNode> mChildren = new ArrayList<BasicTreeNode>();
30
31    public int x, y, width, height;
32
33    // whether the boundary fields are applicable for the node or not
34    // RootWindowNode has no bounds, but UiNodes should
35    protected boolean mHasBounds = false;
36
37    public void addChild(BasicTreeNode child) {
38        if (child == null) {
39            throw new NullPointerException("Cannot add null child");
40        }
41        if (mChildren.contains(child)) {
42            throw new IllegalArgumentException("node already a child");
43        }
44        mChildren.add(child);
45        child.mParent = this;
46    }
47
48    public List<BasicTreeNode> getChildrenList() {
49        return Collections.unmodifiableList(mChildren);
50    }
51
52    public BasicTreeNode[] getChildren() {
53        return mChildren.toArray(CHILDREN_TEMPLATE);
54    }
55
56    public BasicTreeNode getParent() {
57        return mParent;
58    }
59
60    public boolean hasChild() {
61        return mChildren.size() != 0;
62    }
63
64    public int getChildCount() {
65        return mChildren.size();
66    }
67
68    public void clearAllChildren() {
69        for (BasicTreeNode child : mChildren) {
70            child.clearAllChildren();
71        }
72        mChildren.clear();
73    }
74
75    /**
76     *
77     * Find nodes in the tree containing the coordinate
78     *
79     * The found node should have bounds covering the coordinate, and none of its children's
80     * bounds covers it. Depending on the layout, some app may have multiple nodes matching it,
81     * the caller must provide a {@link IFindNodeListener} to receive all found nodes
82     *
83     * @param px
84     * @param py
85     * @return
86     */
87    public boolean findLeafMostNodesAtPoint(int px, int py, IFindNodeListener listener) {
88        boolean foundInChild = false;
89        for (BasicTreeNode node : mChildren) {
90            foundInChild |= node.findLeafMostNodesAtPoint(px, py, listener);
91        }
92        // checked all children, if at least one child covers the point, return directly
93        if (foundInChild) return true;
94        // check self if the node has no children, or no child nodes covers the point
95        if (mHasBounds) {
96            if (x <= px && px <= x + width && y <= py && py <= y + height) {
97                listener.onFoundNode(this);
98                return true;
99            } else {
100                return false;
101            }
102        } else {
103            return false;
104        }
105    }
106
107    public Object[] getAttributesArray () {
108        return null;
109    };
110
111    public static interface IFindNodeListener {
112        void onFoundNode(BasicTreeNode node);
113    }
114}
115