/**
* Copyright (C) 2014-2016 LinkedIn Corp. (pinot-core@linkedin.com)
*
* Licensed 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 com.linkedin.pinot.common.request.transform;
import com.linkedin.pinot.pql.parsers.pql2.ast.AstNode;
import com.linkedin.pinot.pql.parsers.pql2.ast.FunctionCallAstNode;
import com.linkedin.pinot.pql.parsers.pql2.ast.IdentifierAstNode;
import com.linkedin.pinot.pql.parsers.pql2.ast.LiteralAstNode;
import java.util.ArrayList;
import java.util.List;
/**
* Class for representing expression trees for transforms.
* <p> - A TransformExpressionTree node has either transform function or a column name, or a literal.</p>
* <p> - Leaf nodes either have column name or literal, whereas non-leaf nodes have transform function.</p>
* <p> - Transform function in non-leaf nodes is applied to its children nodes. </p>
*/
public class TransformExpressionTree {
// Enum for expression represented by the tree.
public enum ExpressionType {
FUNCTION,
IDENTIFIER,
LITERAL
}
private final String _expression;
private final String _transformName;
private final ExpressionType _expressionType;
private List<TransformExpressionTree> _children;
/**
* Constructor for the class
* @param root Root AstNode for the tree
*/
public TransformExpressionTree(AstNode root) {
_children = null;
if (root instanceof FunctionCallAstNode) {
FunctionCallAstNode functionCallAstNode = (FunctionCallAstNode) root;
_expression = functionCallAstNode.getExpression();
_expressionType = ExpressionType.FUNCTION;
_transformName = functionCallAstNode.getName();
} else if (root instanceof IdentifierAstNode) {
_expression = ((IdentifierAstNode) root).getName();
_expressionType = ExpressionType.IDENTIFIER;
_transformName = null;
} else if (root instanceof LiteralAstNode) {
_expressionType = ExpressionType.LITERAL;
_expression = ((LiteralAstNode) root).getValueAsString();
_transformName = null;
} else {
throw new IllegalArgumentException(
"Illegal AstNode type for TransformExpressionTree: " + root.getClass().getName());
}
}
/**
* Given the root node for AST tree, builds the expression tree, and returns
* the root node of the expression tree.
*
* @param rootNode Root Node of AST tree
* @return Root node of the Expression tree
*/
public static TransformExpressionTree buildTree(AstNode rootNode) {
TransformExpressionTree expressionTree;
if (!rootNode.hasChildren()) {
return new TransformExpressionTree(rootNode);
} else {
expressionTree = new TransformExpressionTree(rootNode);
expressionTree._children = new ArrayList<>();
for (AstNode child : rootNode.getChildren()) {
expressionTree._children.add(buildTree(child));
}
}
return expressionTree;
}
/**
* Getter for the expression string.
*
* @return Expression string name
*/
@Override
public String toString() {
return _expression;
}
/**
* Getter for transform function name.
* @return transform function name
*/
public String getTransformName() {
return _transformName;
}
/**
* Getter for children of current node.
* @return children of current node
*/
public List<TransformExpressionTree> getChildren() {
return _children;
}
/**
* Returns the Expression type, which can be one of the following:
* <ul>
* <li> {@link ExpressionType#FUNCTION}</li>
* <li> {@link ExpressionType#IDENTIFIER}</li>
* <li> {@link ExpressionType#LITERAL}</li>
* </ul>
* @return Expression type.
*/
public ExpressionType getExpressionType() {
return _expressionType;
}
/**
* Returns if the tree represents a column name (ie no expression), false otherwise
*
* @return True if tress represents column, false otherwise.
*/
public boolean isColumn() {
return (_expressionType == ExpressionType.IDENTIFIER);
}
/**
* Returns a list of columns from the leaf nodes of the tree.
* Caller passes a list where columns are stored and returned.
*
* @param columns Output columns
*/
public void getColumns(List<String> columns) {
if (_children == null || _children.isEmpty()) {
if (_expressionType == ExpressionType.IDENTIFIER) {
columns.add(_expression);
}
return;
}
for (TransformExpressionTree child : _children) {
child.getColumns(columns);
}
}
}