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

com.linkedin.restli.tools.data.PredicateExpressionParser Maven / Gradle / Ivy

Go to download

Pegasus is a framework for building robust, scalable service architectures using dynamic discovery and simple asychronous type-checked REST + JSON APIs.

The newest version!
/*
   Copyright (c) 2012 LinkedIn Corp.

   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.restli.tools.data;


import com.linkedin.data.it.Predicate;
import com.linkedin.data.it.Predicates;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import java.util.StringTokenizer;

/**
 * 

Parse boolean expression of {@link Predicate} names to a {@link Predicate}.

* *

Three boolean operators are supported: & (AND), | (OR) and ! (NOT). They are mapped to {@link com.linkedin.data.it.AndPredicate}, {@link com.linkedin.data.it.OrPredicate} and {@link com.linkedin.data.it.NotPredicate}, respectively. The precedence from high to low is ! > & > |.

* *

The expression is in infix style, therefore operands are specified around operators. For example, A & !B is parsed as AndPredicate(A, NotPredicate(B)).

* *

The expression is parsed from left to right. Parenthesis is allowed to change combination order. For example, A | B & C is parsed as OrPredicate(A, AndPredicate(B, C)), while (A | B) & C is AndPredicate(OrPredicate(A, B), C)).

* *

Whitespace characters are removed before parsing, therefore "A & B" is equivalent to "A&B".

* *

All class name must implement {@link Predicate} and takes no parameter in constructor. Otherwise exception is thrown.

* * @author Keren Jin */ public class PredicateExpressionParser { public static Predicate parse(String expression) { final Stack predicateStack = new Stack(); final Stack operatorStack = new Stack(); final String trimmedExpression = expression.replaceAll("\\s", ""); final StringTokenizer tokenizer = new StringTokenizer(trimmedExpression, OPERATORS, true); boolean isTokenMode = true; while (true) { final Character operator; final String token; if (isTokenMode) { if (tokenizer.hasMoreTokens()) { token = tokenizer.nextToken(); } else { break; } if (OPERATORS.contains(token)) { operator = token.charAt(0); } else { operator = null; } } else { operator = operatorStack.pop(); token = null; } isTokenMode = true; if (operator == null) { try { predicateStack.push(Class.forName(token).asSubclass(Predicate.class).newInstance()); } catch (ClassCastException e) { throw new RuntimeException(token + " must implement " + Predicate.class.getName(), e); } catch (Exception e) { throw new RuntimeException(e); } } else { if (operatorStack.empty() || operator == '(') { operatorStack.push(operator); } else if (operator == ')') { while (operatorStack.peek() != '(') { evaluate(predicateStack, operatorStack); } operatorStack.pop(); } else { if (OPERATOR_PRECEDENCE.get(operator) < OPERATOR_PRECEDENCE.get(operatorStack.peek())) { evaluate(predicateStack, operatorStack); isTokenMode = false; } operatorStack.push(operator); } } } while (!operatorStack.empty()) { evaluate(predicateStack, operatorStack); } if (predicateStack.size() > 1) { throw new RuntimeException("Invalid logical expression"); } return predicateStack.pop(); } private static void evaluate(Stack predicateStack, Stack operatorStack) { final char operator = operatorStack.pop(); final Predicate evaluatedPredicate; switch (operator) { case '&': case '|': evaluatedPredicate = evaluateMultiaryOperator(predicateStack, operatorStack, operator); break; case '!': evaluatedPredicate = Predicates.not(predicateStack.pop()); break; default: throw new RuntimeException("Unknown operator: " + operator); } predicateStack.push(evaluatedPredicate); } private static Predicate evaluateMultiaryOperator(Stack predicateStack, Stack operatorStack, char operator) { final Deque predicateOperands = new ArrayDeque(); predicateOperands.addFirst(predicateStack.pop()); predicateOperands.addFirst(predicateStack.pop()); while (!operatorStack.empty() && operator == operatorStack.peek()) { predicateOperands.addFirst(predicateStack.pop()); operatorStack.pop(); } switch (operator) { case '&': return Predicates.and(predicateOperands); case '|': return Predicates.or(predicateOperands); default: throw new RuntimeException("Logic error"); } } private static final String OPERATORS = "()!&|"; private static final Map OPERATOR_PRECEDENCE = new HashMap(); static { OPERATOR_PRECEDENCE.put('(', 0); OPERATOR_PRECEDENCE.put('|', 1); OPERATOR_PRECEDENCE.put('&', 2); OPERATOR_PRECEDENCE.put('!', 3); OPERATOR_PRECEDENCE.put(')', 4); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy