org.mvel2.util.ASTBinaryTree Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tbel Show documentation
Show all versions of tbel Show documentation
TBEL is a powerful expression language for ThingsBoard platform user-defined functions.
Original implementation is based on MVEL.
package org.mvel2.util;
import org.mvel2.ast.ASTNode;
import org.mvel2.ast.EndOfStatement;
import org.mvel2.ast.OperatorNode;
import static org.mvel2.Operator.*;
public class ASTBinaryTree {
private final ASTNode root;
private ASTBinaryTree left;
private ASTBinaryTree right;
public ASTBinaryTree(ASTNode node) {
this.root = node;
}
public ASTBinaryTree append(ASTNode node) {
if (comparePrecedence(root, node) >= 0) {
ASTBinaryTree tree = new ASTBinaryTree(node);
tree.left = this;
return tree;
} else {
if (left == null) throw new RuntimeException("Missing left node");
if (right == null) {
right = new ASTBinaryTree(node);
} else {
right = right.append(node);
}
return this;
}
}
public Class> getReturnType(boolean strongTyping) {
if (!(root instanceof OperatorNode)) return root.getEgressType();
if (left == null || right == null) throw new RuntimeException("Malformed expression");
Class> leftType = left.getReturnType(strongTyping);
Class> rightType = right.getReturnType(strongTyping);
switch (root.getOperator()) {
case CONTAINS:
case SOUNDEX:
case INSTANCEOF:
case SIMILARITY:
case REGEX:
return Boolean.class;
case ADD:
if (leftType.equals(String.class) || rightType.equals(String.class)) return String.class;
case SUB:
case MULT:
case DIV:
if (strongTyping && !CompatibilityStrategy.areEqualityCompatible(leftType, rightType))
throw new RuntimeException("Associative operation requires compatible types. Found " + leftType + " and " + rightType);
return leftType == rightType && (leftType.isPrimitive() || Number.class.isAssignableFrom(leftType)) ? leftType : Double.class;
case MOD:
if (strongTyping && !CompatibilityStrategy.areEqualityCompatible(leftType, rightType))
throw new RuntimeException("Associative operation requires compatible types. Found " + leftType + " and " + rightType);
return Integer.class;
case TERNARY_ELSE:
if (strongTyping && !CompatibilityStrategy.areEqualityCompatible(leftType, rightType))
throw new RuntimeException("Associative operation requires compatible types. Found " + leftType + " and " + rightType);
return leftType;
case EQUAL:
case NEQUAL:
if (strongTyping && !CompatibilityStrategy.areEqualityCompatible(leftType, rightType))
throw new RuntimeException("Comparison operation requires compatible types. Found " + leftType + " and " + rightType);
return Boolean.class;
case LTHAN:
case LETHAN:
case GTHAN:
case GETHAN:
if (strongTyping && !CompatibilityStrategy.areComparisonCompatible(leftType, rightType))
throw new RuntimeException("Comparison operation requires compatible types. Found " + leftType + " and " + rightType);
return Boolean.class;
case AND:
case OR:
if (strongTyping) {
if (leftType != Boolean.class && leftType != Boolean.TYPE)
throw new RuntimeException("Left side of logical operation is not of type boolean. Found " + leftType);
if (rightType != Boolean.class && rightType != Boolean.TYPE)
throw new RuntimeException("Right side of logical operation is not of type boolean. Found " + rightType);
}
return Boolean.class;
case TERNARY:
if (strongTyping && leftType != Boolean.class && leftType != Boolean.TYPE)
throw new RuntimeException("Condition of ternary operator is not of type boolean. Found " + leftType);
return rightType;
}
// TODO: should throw new RuntimeException("Unknown operator");
// it doesn't because I am afraid I am not covering all the OperatorNode types
return root.getEgressType();
}
private int comparePrecedence(ASTNode node1, ASTNode node2) {
if (!(node1 instanceof OperatorNode) && !(node2 instanceof OperatorNode)) return 0;
if (node1 instanceof OperatorNode && node2 instanceof OperatorNode) {
return PTABLE[node1.getOperator()] - PTABLE[node2.getOperator()];
}
return node1 instanceof OperatorNode ? -1 : 1;
}
public static ASTBinaryTree buildTree(ASTIterator input) {
ASTIterator iter = new ASTLinkedList(input.firstNode());
ASTBinaryTree tree = new ASTBinaryTree(iter.nextNode());
while (iter.hasMoreNodes()) {
ASTNode node = iter.nextNode();
if (node instanceof EndOfStatement) {
if (iter.hasMoreNodes()) tree = new ASTBinaryTree(iter.nextNode());
} else {
tree = tree.append(node);
}
}
return tree;
}
}