blob: a75563d4c15ff8988b6ad86c8dd8f9846039e091 [file] [log] [blame]
// 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.doris.common;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
/**
* Generic tree structure. Only concrete subclasses of this can be instantiated.
*/
public class TreeNode<NodeType extends TreeNode<NodeType>> {
protected ArrayList<NodeType> children = Lists.newArrayList();
public NodeType getChild(int i) {
return hasChild(i) ? children.get(i) : null;
}
public void addChild(NodeType n) {
children.add(n);
}
public void addChildren(List <? extends NodeType > n) {
children.addAll(n);
}
public boolean hasChild(int i) { return children.size() > i; }
public void setChild(int index, NodeType n) { children.set(index, n); }
public ArrayList<NodeType> getChildren() { return children; }
public void clearChildren() { children.clear(); }
/**
* Count the total number of nodes in this tree. Leaf node will return 1.
* Non-leaf node will include all its children.
*/
public int numNodes() {
int numNodes = 1;
for (NodeType child: children) numNodes += child.numNodes();
return numNodes;
}
/**
* Add all nodes in the tree that satisfy 'predicate' to the list 'matches'
* This node is checked first, followed by its children in order. If the node
* itself matches, the children are skipped.
*/
public <C extends TreeNode<NodeType>, D extends C> void collect(
Predicate<? super C> predicate, Collection<D> matches) {
// TODO: the semantics of this function are very strange. contains()
// checks using .equals() on the nodes. In the case of literals, slotrefs
// and maybe others, two different tree node objects can be equal and
// this function would only return one of them. This is not intuitive.
// We rely on these semantics to not have duplicate nodes. Investigate this.
if (predicate.apply((C) this) && !matches.contains(this)) {
matches.add((D) this);
return;
}
for (NodeType child: children) child.collect(predicate, matches);
}
/**
* Add all nodes in the tree that are of class 'cl' to the list 'matches'.
* This node is checked first, followed by its children in order. If the node
* itself is of class 'cl', the children are skipped.
*/
public <C extends TreeNode<NodeType>, D extends C> void collect(
Class cl, Collection<D> matches) {
if (cl.equals(getClass())) {
matches.add((D) this);
return;
}
for (NodeType child: children) child.collect(cl, matches);
}
/**
* Add all nodes in the tree that satisfy 'predicate' to the list 'matches'
* This node is checked first, followed by its children in order. All nodes
* that match in the subtree are added.
*/
public <C extends TreeNode<NodeType>, D extends C> void collectAll(
Predicate<? super C> predicate, List<D> matches) {
if (predicate.apply((C) this)) matches.add((D) this);
for (NodeType child: children) child.collectAll(predicate, matches);
}
/**
* For each expression in 'nodeList', collect all subexpressions satisfying 'predicate'
* into 'matches'
*/
public static <C extends TreeNode<C>, D extends C> void collect(
Collection<C> nodeList, Predicate<? super C> predicate, Collection<D> matches) {
for (C node: nodeList) node.collect(predicate, matches);
}
/**
* For each expression in 'nodeList', collect all subexpressions of class 'cl'
* into 'matches'
*/
public static <C extends TreeNode<C>, D extends C> void collect(
Collection<C> nodeList, Class cl, Collection<D> matches) {
for (C node: nodeList) node.collect(cl, matches);
}
public boolean contains(Class cl) {
if (cl.isAssignableFrom(this.getClass()) && this.getClass().isAssignableFrom(cl)) {
return true;
}
for (NodeType child : children) {
if (child.contains(cl)) {
return true;
}
}
return false;
}
/**
* Return true if this node or any of its children satisfy 'predicate'.
*/
public <C extends TreeNode<NodeType>> boolean contains(
Predicate<? super C> predicate) {
if (predicate.apply((C) this)) return true;
for (NodeType child: children) if (child.contains(predicate)) return true;
return false;
}
/**
* For each node in nodeList, return true if any subexpression satisfies
* contains('predicate').
*/
public static <C extends TreeNode<C>, D extends C> boolean contains(
Collection<C> nodeList, Predicate<? super C> predicate) {
for (C node: nodeList) if (node.contains(predicate)) return true;
return false;
}
/**
* Return true if any node in nodeList contains children of class cl.
*/
public static <C extends TreeNode<C>> boolean contains(
List<C> nodeList, Class cl) {
for (C node: nodeList) if (node.contains(cl)) return true;
return false;
}
public boolean containsSubclass(Class cl) {
if (cl.isAssignableFrom(this.getClass())) {
return true;
}
for (NodeType child : children) {
if (child.containsSubclass(cl)) {
return true;
}
}
return false;
}
/**
* Return 'this' or first child that is exactly of class 'cl'.
* Looks for matching children via depth-first, left-to-right traversal.
*/
public <C extends NodeType> C findFirstOf(Class<C> cl) {
if (this.getClass().equals(cl)) {
return (C) this;
}
for (NodeType child : children) {
NodeType result = child.findFirstOf(cl);
if (result != null) {
return (C) result;
}
}
return null;
}
}