All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.undertow.server.handlers.builder.PredicatedHandlersParser Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * 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 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.HashSet;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;

/**
 * Parser for the undertow-handlers.conf file.
 * 

* This file has a line by line syntax, specifying predicate -> handler. If no predicate is specified then * the line is assumed to just contain a handler. * * @author Stuart Douglas */ 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 parse(final File file, final ClassLoader classLoader) { return parse(file.toPath(), classLoader); } public static List 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 parse(final InputStream inputStream, final ClassLoader classLoader) { return parse(FileUtils.readFile(inputStream), classLoader); } public static List parse(final String contents, final ClassLoader classLoader) { Deque tokens = tokenize(contents); Node node = parse(contents, tokens); Map predicateBuilders = loadPredicateBuilders(classLoader); Map 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 tokens = tokenize(string); Node node = parse(string, tokens); Map predicateBuilders = loadPredicateBuilders(classLoader); final ExchangeAttributeParser attributeParser = ExchangeAttributes.parser(classLoader); return handlePredicateNode(string, node, predicateBuilders, attributeParser); } public static HandlerWrapper parseHandler(String string, ClassLoader classLoader) { Deque tokens = tokenize(string); Node node = parse(string, tokens); Map handlerBuilders = loadHandlerBuilders(classLoader); final ExchangeAttributeParser attributeParser = ExchangeAttributes.parser(classLoader); return handleHandlerNode(string, (ExpressionNode)node, handlerBuilders, attributeParser); } private static List handleNode(String contents, Node node, Map predicateBuilders, Map 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 predicateBuilders, Map 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 predicateBuilders, Map handlerBuilders, ExchangeAttributeParser parser) { if(node instanceof ExpressionNode) { return handleHandlerNode(contents, (ExpressionNode) node, handlerBuilders, parser); } else if(node instanceof BlockNode) { List handlers = handleBlockNode(contents, (BlockNode) node, predicateBuilders, handlerBuilders, parser); return new PredicatesHandler.Wrapper(handlers, false); } else if(node instanceof PredicateOperatorNode) { List handlers = Collections.singletonList(handlePredicateOperatorNode(contents, (PredicateOperatorNode)node, predicateBuilders, handlerBuilders, parser)); return new PredicatesHandler.Wrapper(handlers, false); } else { throw error(contents, node.getToken().getPosition(), "unexpected token " + node.getToken()); } } private static List handleBlockNode(String contents, BlockNode node, Map predicateBuilders, Map handlerBuilders, ExchangeAttributeParser parser) { List 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 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 parameters = new HashMap<>(); Set requiredParameters = builder.requiredParameters(); requiredParameters = requiredParameters == null? Collections.emptySet() : new HashSet<>(requiredParameters); for(Map.Entry 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()); } requiredParameters.remove(name); } if (!requiredParameters.isEmpty()) { throw error(contents, token.getPosition(), "required parameters " + requiredParameters + " not provided for handler " + builder.name()); } return builder.build(parameters); } private static Predicate handlePredicateNode(String contents, Node node, Map 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 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 parameters = new HashMap<>(); Set requiredParameters = builder.requiredParameters(); requiredParameters = requiredParameters == null? Collections.emptySet() : new HashSet<>(requiredParameters); for(Map.Entry 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()); } requiredParameters.remove(name); } if (!requiredParameters.isEmpty()) { throw error(contents, token.getPosition(), "required parameters " + requiredParameters + " not provided for predicate " + builder.name()); } 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 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 loadPredicateBuilders(final ClassLoader classLoader) { ServiceLoader loader = ServiceLoader.load(PredicateBuilder.class, classLoader); final Map 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 loadHandlerBuilders(final ClassLoader classLoader) { ServiceLoader loader = ServiceLoader.load(HandlerBuilder.class, classLoader); final Map 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 tokens) { return parse(string, tokens, true); } static Node parse(final String string, Deque tokens, boolean topLevel) { //shunting yard algorithm //gets rid or parentheses and fixes up operator ordering Deque operatorStack = new ArrayDeque<>(); Deque output = new ArrayDeque<>(); List 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(";")) { if(token.getToken().equals(";") && tokens.peek()!=null && tokens.peek().getToken().equals(ELSE)) { // something() -> predicate; ELSE predicate; - dont end processing since its followed by ELSE and its singular block continue; } 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 operatorStack, Deque output, List 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; } //now we have our tokens for this line 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 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 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 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()) { //single value case 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.emptyMap()); } } } private static Node handleSingleArrayValue(final String string, final Token builder, final Deque tokens, String endChar) { List 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.singletonMap(null, new ArrayNode(builder, array))); } private static Node handleSingleVarArgsValue(final String string, final Token expressionName, final Deque tokens, String endChar) { List array = readArrayType(string, tokens, endChar); return new ExpressionNode(expressionName, Collections.singletonMap(null, new ArrayNode(expressionName, array))); } private static List readArrayType(final String string, final Deque tokens, String expectedEndToken) { final List 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.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 tokenize(final String string) { char currentStringDelim = 0; boolean inVariable = false; int pos = 0; StringBuilder current = new StringBuilder(); Deque ret = new ArrayDeque<>(); while (pos < string.length()) { char c = string.charAt(pos); if (currentStringDelim != 0) { if (c == currentStringDelim) { if (current.length() > 0 && current.charAt(current.length() - 1) == '\\') { current.setLength(current.length() - 1); current.append(c); } else { 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) { //truncate the string at the error line 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(); } /** * A parsed expression */ static class ExpressionNode implements Node { private final Token token; private final Map values; private ExpressionNode(Token token, Map values) { this.token = token; this.values = values; } public Token getToken() { return token; } public Map getValues() { return values; } } static class ArrayNode implements Node { private final Token start; private final List values; private ArrayNode(Token start, List tokens) { this.start = start; this.values = tokens; } public List 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 block; BlockNode(Token token, List block) { this.token = token; this.block = block; } public List 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 + ">"; } } }