package io.undertow.server.handlers.builder;
import io.undertow.UndertowLogger;
import io.undertow.UndertowMessages;
import io.undertow.attribute.ExchangeAttribute;
import io.undertow.attribute.ExchangeAttributeParser;
import io.undertow.attribute.ExchangeAttributes;
import io.undertow.predicate.Predicate;
import io.undertow.predicate.PredicateBuilder;
import io.undertow.predicate.Predicates;
import io.undertow.predicate.PredicatesHandler;
import io.undertow.server.HandlerWrapper;
import io.undertow.util.FileUtils;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
public class PredicatedHandlersParser {
public static final String ELSE = "else";
public static final String ARROW = "->";
public static final String NOT = "not";
public static final String OR = "or";
public static final String AND = "and";
public static final String TRUE = "true";
public static final String FALSE = "false";
public static List<PredicatedHandler> parse(final File file, final ClassLoader classLoader) {
return parse(file.toPath(), classLoader);
}
public static List<PredicatedHandler> parse(final Path file, final ClassLoader classLoader) {
try {
return parse(new String(Files.readAllBytes(file), StandardCharsets.UTF_8), classLoader);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static List<PredicatedHandler> parse(final InputStream inputStream, final ClassLoader classLoader) {
return parse(FileUtils.readFile(inputStream), classLoader);
}
public static List<PredicatedHandler> parse(final String contents, final ClassLoader classLoader) {
Deque<Token> tokens = tokenize(contents);
Node node = parse(contents, tokens);
Map<String, PredicateBuilder> predicateBuilders = loadPredicateBuilders(classLoader);
Map<String, HandlerBuilder> handlerBuilders = loadHandlerBuilders(classLoader);
final ExchangeAttributeParser attributeParser = ExchangeAttributes.parser(classLoader);
return handleNode(contents, node, predicateBuilders, handlerBuilders, attributeParser);
}
public static Predicate parsePredicate(String string, ClassLoader classLoader) {
Deque<Token> tokens = tokenize(string);
Node node = parse(string, tokens);
Map<String, PredicateBuilder> predicateBuilders = loadPredicateBuilders(classLoader);
final ExchangeAttributeParser attributeParser = ExchangeAttributes.parser(classLoader);
return handlePredicateNode(string, node, predicateBuilders, attributeParser);
}
public static HandlerWrapper parseHandler(String string, ClassLoader classLoader) {
Deque<Token> tokens = tokenize(string);
Node node = parse(string, tokens);
Map<String, HandlerBuilder> handlerBuilders = loadHandlerBuilders(classLoader);
final ExchangeAttributeParser attributeParser = ExchangeAttributes.parser(classLoader);
return handleHandlerNode(string, (ExpressionNode)node, handlerBuilders, attributeParser);
}
private static List<PredicatedHandler> handleNode(String contents, Node node, Map<String, PredicateBuilder> predicateBuilders, Map<String, HandlerBuilder> handlerBuilders, ExchangeAttributeParser attributeParser) {
if(node instanceof BlockNode) {
return handleBlockNode(contents, (BlockNode) node, predicateBuilders, handlerBuilders, attributeParser);
} else if(node instanceof ExpressionNode) {
HandlerWrapper handler = handleHandlerNode(contents, (ExpressionNode) node, handlerBuilders, attributeParser);
return Collections.singletonList(new PredicatedHandler(Predicates.truePredicate(), handler));
} else if(node instanceof PredicateOperatorNode) {
return Collections.singletonList(handlePredicateOperatorNode(contents, (PredicateOperatorNode)node, predicateBuilders, handlerBuilders, attributeParser));
} else {
throw error(contents, node.getToken().getPosition(), "unexpected token " + node.getToken());
}
}
private static PredicatedHandler handlePredicateOperatorNode(String contents, PredicateOperatorNode node, Map<String, PredicateBuilder> predicateBuilders, Map<String, HandlerBuilder> handlerBuilders, ExchangeAttributeParser parser) {
Predicate predicate = handlePredicateNode(contents, node.getLeft(), predicateBuilders, parser);
HandlerWrapper ret = handlePredicatedAction(contents, node.getRight(), predicateBuilders, handlerBuilders, parser);
HandlerWrapper elseBranch = null;
if(node.getElseBranch() != null) {
elseBranch = handlePredicatedAction(contents, node.getElseBranch(), predicateBuilders, handlerBuilders, parser);
}
return new PredicatedHandler(predicate, ret, elseBranch);
}
private static HandlerWrapper handlePredicatedAction(String contents, Node node, Map<String, PredicateBuilder> predicateBuilders, Map<String, HandlerBuilder> handlerBuilders, ExchangeAttributeParser parser) {
if(node instanceof ExpressionNode) {
return handleHandlerNode(contents, (ExpressionNode) node, handlerBuilders, parser);
} else if(node instanceof BlockNode) {
List<PredicatedHandler> handlers = handleBlockNode(contents, (BlockNode) node, predicateBuilders, handlerBuilders, parser);
return new PredicatesHandler.Wrapper(handlers, false);
} else {
throw error(contents, node.getToken().getPosition(), "unexpected token " + node.getToken());
}
}
private static List<PredicatedHandler> handleBlockNode(String contents, BlockNode node, Map<String, PredicateBuilder> predicateBuilders, Map<String, HandlerBuilder> handlerBuilders, ExchangeAttributeParser parser) {
List<PredicatedHandler> ret = new ArrayList<>();
for(Node line : node.getBlock()) {
ret.addAll(handleNode(contents, line, predicateBuilders, handlerBuilders, parser));
}
return ret;
}
private static HandlerWrapper handleHandlerNode(String contents, ExpressionNode node, Map<String, HandlerBuilder> handlerBuilders, ExchangeAttributeParser parser) {
Token token = node.getToken();
HandlerBuilder builder = handlerBuilders.get(token.getToken());
if (builder == null) {
throw error(contents, token.getPosition(), "no handler named " + token.getToken() + " known handlers are " + handlerBuilders.keySet());
}
Map<String, Object> parameters = new HashMap<>();
for(Map.Entry<String, Node> val : node.getValues().entrySet()) {
String name = val.getKey();
if(name == null) {
if(builder.defaultParameter() == null) {
throw error(contents, token.getPosition(), "default parameter not supported");
}
name = builder.defaultParameter();
}
Class<?> type = builder.parameters().get(name);
if(type == null) {
throw error(contents, val.getValue().getToken().getPosition(), "unknown parameter " + name);
}
if(val.getValue() instanceof ValueNode) {
parameters.put(name, coerceToType(contents, val.getValue().getToken(), type, parser));
} else if(val.getValue() instanceof ArrayNode) {
parameters.put(name, readArrayType(contents, name, (ArrayNode)val.getValue(), parser, type));
} else {
throw error(contents, val.getValue().getToken().getPosition(), "unexpected node " + val.getValue());
}
}
return builder.build(parameters);
}
private static Predicate handlePredicateNode(String contents, Node node, Map<String, PredicateBuilder> handlerBuilders, ExchangeAttributeParser parser) {
if(node instanceof AndNode) {
AndNode andNode = (AndNode)node;
return Predicates.and(handlePredicateNode(contents, andNode.getLeft(), handlerBuilders, parser), handlePredicateNode(contents, andNode.getRight(), handlerBuilders, parser));
} else if(node instanceof OrNode) {
OrNode orNode = (OrNode)node;
return Predicates.or(handlePredicateNode(contents, orNode.getLeft(), handlerBuilders, parser), handlePredicateNode(contents, orNode.getRight(), handlerBuilders, parser));
} else if(node instanceof NotNode) {
NotNode orNode = (NotNode)node;
return Predicates.not(handlePredicateNode(contents, orNode.getNode(), handlerBuilders, parser));
} else if(node instanceof ExpressionNode) {
return handlePredicateExpressionNode(contents, (ExpressionNode) node, handlerBuilders, parser);
}else if(node instanceof OperatorNode) {
switch (node.getToken().getToken()) {
case TRUE: {
return Predicates.truePredicate();
}
case FALSE: {
return Predicates.falsePredicate();
}
}
}
throw error(contents, node.getToken().getPosition(), "unexpected node " + node);
}
private static Predicate handlePredicateExpressionNode(String contents, ExpressionNode node, Map<String, PredicateBuilder> handlerBuilders, ExchangeAttributeParser parser) {
Token token = node.getToken();
PredicateBuilder builder = handlerBuilders.get(token.getToken());
if (builder == null) {
throw error(contents, token.getPosition(), "no predicate named " + token.getToken() + " known predicates are " + handlerBuilders.keySet());
}
Map<String, Object> parameters = new HashMap<>();
for(Map.Entry<String, Node> val : node.getValues().entrySet()) {
String name = val.getKey();
if(name == null) {
if(builder.defaultParameter() == null) {
throw error(contents, token.getPosition(), "default parameter not supported");
}
name = builder.defaultParameter();
}
Class<?> type = builder.parameters().get(name);
if(type == null) {
throw error(contents, val.getValue().getToken().getPosition(), "unknown parameter " + name);
}
if(val.getValue() instanceof ValueNode) {
parameters.put(name, coerceToType(contents, val.getValue().getToken(), type, parser));
} else if(val.getValue() instanceof ArrayNode) {
parameters.put(name, readArrayType(contents, name, (ArrayNode)val.getValue(), parser, type));
} else {
throw error(contents, val.getValue().getToken().getPosition(), "unexpected node " + val.getValue());
}
}
return builder.build(parameters);
}
private static Object readArrayType(final String string, String paramName, ArrayNode value, ExchangeAttributeParser parser, Class type) {
if (!type.isArray()) {
throw error(string, value.getToken().getPosition(), "parameter is not an array type " + paramName);
}
Class<?> componentType = type.getComponentType();
final List<Object> values = new ArrayList<>();
for(Token token : value.getValues()) {
values.add(coerceToType(string, token, componentType, parser));
}
Object array = Array.newInstance(componentType, values.size());
for (int i = 0; i < values.size(); ++i) {
Array.set(array, i, values.get(i));
}
return array;
}
private static Object coerceToType(final String string, final Token token, final Class<?> type, final ExchangeAttributeParser attributeParser) {
if (type.isArray()) {
Object array = Array.newInstance(type.getComponentType(), 1);
Array.set(array, 0, coerceToType(string, token, type.getComponentType(), attributeParser));
return array;
}
if (type == String.class) {
return token.getToken();
} else if (type.equals(Boolean.class) || type.equals(boolean.class)) {
return Boolean.valueOf(token.getToken());
} else if (type.equals(Byte.class) || type.equals(byte.class)) {
return Byte.valueOf(token.getToken());
} else if (type.equals(Character.class) || type.equals(char.class)) {
if (token.getToken().length() != 1) {
throw error(string, token.getPosition(), "Cannot coerce " + token.getToken() + " to a Character");
}
return Character.valueOf(token.getToken().charAt(0));
} else if (type.equals(Short.class) || type.equals(short.class)) {
return Short.valueOf(token.getToken());
} else if (type.equals(Integer.class) || type.equals(int.class)) {
return Integer.valueOf(token.getToken());
} else if (type.equals(Long.class) || type.equals(long.class)) {
return Long.valueOf(token.getToken());
} else if (type.equals(Float.class) || type.equals(float.class)) {
return Float.valueOf(token.getToken());
} else if (type.equals(Double.class) || type.equals(double.class)) {
return Double.valueOf(token.getToken());
} else if (type.equals(ExchangeAttribute.class)) {
return attributeParser.parse(token.getToken());
}
return token.getToken();
}
private static Map<String, PredicateBuilder> loadPredicateBuilders(final ClassLoader classLoader) {
ServiceLoader<PredicateBuilder> loader = ServiceLoader.load(PredicateBuilder.class, classLoader);
final Map<String, PredicateBuilder> ret = new HashMap<>();
for (PredicateBuilder builder : loader) {
if (ret.containsKey(builder.name())) {
if (ret.get(builder.name()).getClass() != builder.getClass()) {
throw UndertowMessages.MESSAGES.moreThanOnePredicateWithName(builder.name(), builder.getClass(), ret.get(builder.name()).getClass());
}
} else {
ret.put(builder.name(), builder);
}
}
return ret;
}
private static Map<String, HandlerBuilder> loadHandlerBuilders(final ClassLoader classLoader) {
ServiceLoader<HandlerBuilder> loader = ServiceLoader.load(HandlerBuilder.class, classLoader);
final Map<String, HandlerBuilder> ret = new HashMap<>();
for (HandlerBuilder builder : loader) {
if (ret.containsKey(builder.name())) {
if (ret.get(builder.name()).getClass() != builder.getClass()) {
throw UndertowMessages.MESSAGES.moreThanOneHandlerWithName(builder.name(), builder.getClass(), ret.get(builder.name()).getClass());
}
} else {
ret.put(builder.name(), builder);
}
}
return ret;
}
static Node parse(final String string, Deque<Token> tokens) {
return parse(string, tokens, true);
}
static Node parse(final String string, Deque<Token> tokens, boolean topLevel) {
Deque<Token> operatorStack = new ArrayDeque<>();
Deque<Node> output = new ArrayDeque<>();
List<Node> blocks = new ArrayList<>();
while (!tokens.isEmpty()) {
Token token = tokens.poll();
if(token.getToken().equals("{")) {
output.push(parse(string, tokens, false));
} else if(token.getToken().equals("}")) {
if(topLevel) {
throw error(string, token.getPosition(), "Unexpected token");
}
break;
} else if(token.getToken().equals("\n") || token.getToken().equals(";")) {
handleLineEnd(string, operatorStack, output, blocks);
} else if (isSpecialChar(token.getToken())) {
if (token.getToken().equals("(")) {
operatorStack.push(token);
} else if (token.getToken().equals(")")) {
for (; ; ) {
Token op = operatorStack.pop();
if (op == null) {
throw error(string, token.getPosition(), "Unexpected end of input");
} else if (op.getToken().equals("(")) {
break;
} else {
output.push(new OperatorNode(op));
}
}
} else {
output.push(new OperatorNode(token));
}
} else {
if (isOperator(token.getToken()) && !token.getToken().equals(ELSE)) {
int prec = precedence(token.getToken());
Token top = operatorStack.peek();
while (top != null) {
if (top.getToken().equals("(")) {
break;
}
int exitingPrec = precedence(top.getToken());
if (prec <= exitingPrec) {
output.push(new OperatorNode(operatorStack.pop()));
} else {
break;
}
top = operatorStack.peek();
}
operatorStack.push(token);
} else {
output.push(parseExpression(string, token, tokens));
}
}
}
handleLineEnd(string, operatorStack, output, blocks);
if(blocks.size() == 1) {
return blocks.get(0);
} else {
return new BlockNode(new Token("", 0), blocks);
}
}
private static void handleLineEnd(String string, Deque<Token> operatorStack, Deque<Node> output, List<Node> blocks) {
while (!operatorStack.isEmpty()) {
Token op = operatorStack.pop();
if (op.getToken().equals(")")) {
throw error(string, string.length(), "Mismatched parenthesis");
}
output.push(new OperatorNode(op));
}
if(output.isEmpty()) {
return;
}
Node predicate = collapseOutput(output.pop(), output);
if (!output.isEmpty()) {
throw error(string, output.getFirst().getToken().getPosition(), "Invalid expression");
}
blocks.add(predicate);
}
private static Node collapseOutput(final Node token, final Deque<Node> tokens) {
if (token instanceof OperatorNode) {
OperatorNode node = (OperatorNode) token;
if (node.token.getToken().equals(AND)) {
Node n1 = collapseOutput(tokens.pop(), tokens);
Node n2 = collapseOutput(tokens.pop(), tokens);
return new AndNode(token.getToken(), n2, n1);
} else if (node.token.getToken().equals(OR)) {
Node n1 = collapseOutput(tokens.pop(), tokens);
Node n2 = collapseOutput(tokens.pop(), tokens);
return new OrNode(token.getToken(), n2, n1);
} else if (node.token.getToken().equals(NOT)) {
Node n1 = collapseOutput(tokens.pop(), tokens);
return new NotNode(token.getToken(), n1);
} else if (node.token.getToken().equals(ARROW)) {
Node n1 = collapseOutput(tokens.pop(), tokens);
Node n2 = null;
Node elseBranch = null;
final Node popped = tokens.pop();
if(popped.getToken().getToken().equals(ELSE)) {
elseBranch = n1;
n1 = collapseOutput(tokens.pop(), tokens);
n2 = collapseOutput(tokens.pop(), tokens);
} else {
n2 = collapseOutput(popped, tokens);
}
return new PredicateOperatorNode(token.getToken(), n2, n1, elseBranch);
} else {
return token;
}
} else {
return token;
}
}
private static Node parseExpression(final String string, final Token token, final Deque<Token> tokens) {
if (token.getToken().equals(TRUE)) {
return new OperatorNode(token);
} else if (token.getToken().equals(FALSE)) {
return new OperatorNode(token);
} else {
Token next = tokens.peek();
String endChar = ")";
if (next != null && (next.getToken().equals("[") || next.getToken().equals("("))) {
if (next.getToken().equals("[")) {
endChar = "]";
UndertowLogger.ROOT_LOGGER.oldStylePredicateSyntax(string);
}
final Map<String, Node> values = new HashMap<>();
tokens.poll();
next = tokens.poll();
if (next == null) {
throw error(string, string.length(), "Unexpected end of input");
}
if (next.getToken().equals("{")) {
return handleSingleArrayValue(string, token, tokens, endChar);
}
while (!next.getToken().equals(endChar)) {
Token equals = tokens.poll();
if (equals == null) {
throw error(string, string.length(), "Unexpected end of input");
}
if (!equals.getToken().equals("=")) {
if (equals.getToken().equals(endChar) && values.isEmpty()) {
return handleSingleValue(token, next);
} else if (equals.getToken().equals(",")) {
tokens.push(equals);
tokens.push(next);
return handleSingleVarArgsValue(string, token, tokens, endChar);
}
throw error(string, equals.getPosition(), "Unexpected token");
}
Token value = tokens.poll();
if (value == null) {
throw error(string, string.length(), "Unexpected end of input");
}
if (value.getToken().equals("{")) {
values.put(next.getToken(), new ArrayNode(value, readArrayType(string, tokens,"}")));
} else {
if (isOperator(value.getToken()) || isSpecialChar(value.getToken())) {
throw error(string, value.getPosition(), "Unexpected token");
}
values.put(next.getToken(), new ValueNode(value));
}
next = tokens.poll();
if (next == null) {
throw error(string, string.length(), "Unexpected end of input");
}
if (!next.getToken().equals(endChar)) {
if (!next.getToken().equals(",")) {
throw error(string, string.length(), "Expecting , or " + endChar);
}
next = tokens.poll();
if (next == null) {
throw error(string, string.length(), "Unexpected end of input");
}
}
}
return new ExpressionNode(token, values);
} else {
if (next != null && isSpecialChar(next.getToken())) {
throw error(string, next.getPosition(), "Unexpected character");
}
return new ExpressionNode(token, Collections.<String, Node>emptyMap());
}
}
}
private static Node handleSingleArrayValue(final String string, final Token builder, final Deque<Token> tokens, String endChar) {
List<Token> array = readArrayType(string, tokens, "}");
Token close = tokens.poll();
if (!close.getToken().equals(endChar)) {
throw error(string, close.getPosition(), "expected " + endChar);
}
return new ExpressionNode(builder, Collections.<String, Node>singletonMap(null, new ArrayNode(builder, array)));
}
private static Node handleSingleVarArgsValue(final String string, final Token expressionName, final Deque<Token> tokens, String endChar) {
List<Token> array = readArrayType(string, tokens, endChar);
return new ExpressionNode(expressionName, Collections.<String, Node>singletonMap(null, new ArrayNode(expressionName, array)));
}
private static List<Token> readArrayType(final String string, final Deque<Token> tokens, String expectedEndToken) {
final List<Token> values = new ArrayList<>();
Token token = tokens.poll();
if(token.getToken().equals(expectedEndToken)) {
return Collections.emptyList();
}
while (token != null) {
Token commaOrEnd = tokens.poll();
values.add(token);
if (commaOrEnd.getToken().equals(expectedEndToken)) {
return values;
} else if (!commaOrEnd.getToken().equals(",")) {
throw error(string, commaOrEnd.getPosition(), "expected either , or " + expectedEndToken);
}
token = tokens.poll();
}
throw error(string, string.length(), "unexpected end of input in array");
}
private static Node handleSingleValue(final Token token, final Token next) {
return new ExpressionNode(token, Collections.<String, Node>singletonMap(null, new ValueNode(next)));
}
private static int precedence(String operator) {
if (operator.equals(NOT)) {
return 3;
} else if (operator.equals(AND)) {
return 2;
} else if (operator.equals(OR)) {
return 1;
} else if (operator.equals(ARROW)) {
return -1000;
}
throw new IllegalStateException();
}
private static boolean isOperator(final String op) {
return op.equals(AND) || op.equals(OR) || op.equals(NOT) || op.equals(ARROW);
}
private static boolean isSpecialChar(String token) {
if (token.length() == 1) {
char c = token.charAt(0);
switch (c) {
case '(':
case ')':
case ',':
case '=':
case '[':
case ']':
return true;
default:
return false;
}
}
return false;
}
public static Deque<Token> tokenize(final String string) {
char currentStringDelim = 0;
boolean inVariable = false;
int pos = 0;
StringBuilder current = new StringBuilder();
Deque<Token> ret = new ArrayDeque<>();
while (pos < string.length()) {
char c = string.charAt(pos);
if (currentStringDelim != 0) {
if (c == currentStringDelim && current.charAt(current.length() - 1) != '\\') {
ret.add(new Token(current.toString(), pos));
current.setLength(0);
currentStringDelim = 0;
} else if (c == '\n' || c == '\r') {
ret.add(new Token(current.toString(), pos));
current.setLength(0);
currentStringDelim = 0;
ret.add(new Token("\n", pos));
} else {
current.append(c);
}
} else {
switch (c) {
case ' ':
case '\t': {
if (current.length() != 0) {
ret.add(new Token(current.toString(), pos));
current.setLength(0);
}
break;
}
case '\r':
case '\n': {
if (current.length() != 0) {
ret.add(new Token(current.toString(), pos));
current.setLength(0);
}
ret.add(new Token("\n", pos));
break;
}
case ';':
case '(':
case ')':
case ',':
case '=':
case '[':
case ']':
case '{':
case '}': {
if (inVariable) {
current.append(c);
if (c == '}') {
inVariable = false;
}
} else {
if (current.length() != 0) {
ret.add(new Token(current.toString(), pos));
current.setLength(0);
}
ret.add(new Token("" + c, pos));
}
break;
}
case '"':
case '\'': {
if (current.length() != 0) {
throw error(string, pos, "Unexpected token");
}
currentStringDelim = c;
break;
}
case '%':
case '$': {
current.append(c);
if (string.charAt(pos + 1) == '{') {
inVariable = true;
}
break;
}
case '-':
if (inVariable) {
current.append(c);
} else {
if (pos != string.length() && string.charAt(pos + 1) == '>') {
pos++;
if (current.length() != 0) {
ret.add(new Token(current.toString(), pos));
current.setLength(0);
}
ret.add(new Token(ARROW, pos));
} else {
current.append(c);
}
}
break;
default:
current.append(c);
}
}
++pos;
}
if (current.length() > 0) {
ret.add(new Token(current.toString(), string.length()));
}
return ret;
}
private static IllegalStateException error(final String string, int pos, String reason) {
StringBuilder b = new StringBuilder();
int linePos = 0;
for (int i = 0; i < string.length(); ++i) {
if (string.charAt(i) == '\n') {
if (i >= pos) {
break;
} else {
linePos = 0;
}
} else if (i < pos) {
linePos++;
}
b.append(string.charAt(i));
}
b.append('\n');
for (int i = 0; i < linePos; ++i) {
b.append(' ');
}
b.append('^');
throw UndertowMessages.MESSAGES.errorParsingPredicateString(reason, b.toString());
}
public interface Node {
Token getToken();
}
static class ExpressionNode implements Node {
private final Token token;
private final Map<String, Node> values;
private ExpressionNode(Token token, Map<String, Node> values) {
this.token = token;
this.values = values;
}
public Token getToken() {
return token;
}
public Map<String, Node> getValues() {
return values;
}
}
static class ArrayNode implements Node {
private final Token start;
private final List<Token> values;
private ArrayNode(Token start, List<Token> tokens) {
this.start = start;
this.values = tokens;
}
public List<Token> getValues() {
return values;
}
@Override
public Token getToken() {
return start;
}
}
static class ValueNode implements Node {
private final Token value;
private ValueNode(Token value) {
this.value = value;
}
public Token getValue() {
return value;
}
@Override
public String toString() {
return value.getToken();
}
@Override
public Token getToken() {
return value;
}
}
static class OperatorNode implements Node {
private final Token token;
private OperatorNode(Token token) {
this.token = token;
}
public Token getToken() {
return token;
}
}
static class AndNode implements Node {
private final Token token;
private final Node left;
private final Node right;
AndNode(Token token, Node left, Node right) {
this.token = token;
this.left = left;
this.right = right;
}
public Node getLeft() {
return left;
}
public Node getRight() {
return right;
}
public Token getToken() {
return token;
}
}
static class OrNode implements Node {
private final Token token;
private final Node left;
private final Node right;
OrNode(Token token, Node left, Node right) {
this.token = token;
this.left = left;
this.right = right;
}
public Node getLeft() {
return left;
}
public Node getRight() {
return right;
}
public Token getToken() {
return token;
}
}
static class PredicateOperatorNode implements Node {
private final Token token;
private final Node left;
private final Node right;
private final Node elseBranch;
PredicateOperatorNode(Token token, Node left, Node right, Node elseBranch) {
this.token = token;
this.left = left;
this.right = right;
this.elseBranch = elseBranch;
}
public Node getLeft() {
return left;
}
public Node getRight() {
return right;
}
public Node getElseBranch() {
return elseBranch;
}
@Override
public Token getToken() {
return token;
}
}
static class NotNode implements Node {
private final Token token;
private final Node node;
NotNode(Token token, Node node) {
this.token = token;
this.node = node;
}
public Node getNode() {
return node;
}
public Token getToken() {
return token;
}
}
static class BlockNode implements Node {
private final Token token;
private final List<Node> block;
BlockNode(Token token, List<Node> block) {
this.token = token;
this.block = block;
}
public List<Node> getBlock() {
return block;
}
@Override
public Token getToken() {
return token;
}
}
static final class Token {
private final String token;
private final int position;
Token(final String token, final int position) {
this.token = token;
this.position = position;
}
public String getToken() {
return token;
}
public int getPosition() {
return position;
}
@Override
public String toString() {
return token + " <" + position + ">";
}
}
}