A Boolean Expression Parser for Java that helps parse and evaluate complex boolean and arithmetic expressions.
This library helps parse and evaluate complex and nested boolean expressions. The expressions use a SQL-like syntax, where you can use boolean operators and parentheses to combine individual expressions.
An expression can be as simple as name = 'Sidhant' or as complex as (age >= 18 AND age <= 65) AND (status = 'active' OR status = 'pending').
<dependencies> <dependency> <groupId>com.github.sidhant92</groupId> <artifactId>bool-parser-java</artifactId> <version>2.0.0</version> </dependency> </dependencies>dependencies { implementation "com.github.sidhant92:bool-parser-java:2.0.0" } final BoolParser boolParser = new BoolParser(); final Try<Node> nodeOptional = boolParser.parseExpression("name = 'test'");There are two implementations for the parser:
BoolParser: Standard implementationCachedBoolParser: Takes input for the max cache size to improve performance
The language supports the following data types:
- String: Text values enclosed in single (
') or double (") quotes. Examples:'hello',"world" - Integer: Whole numbers without decimal points. Examples:
42,-10 - Long: Large whole numbers. Example:
1611473334114 - Decimal: Numbers with decimal points (stored as BigDecimal). Examples:
3.14,-2.5 - Boolean:
trueorfalse(case-insensitive) - Version: Semantic version numbers. Examples:
1.0.6,2.3.1.5 - Date: Date values in YYYY-MM-DD format. Examples:
2023-12-25,2024-01-15 - DateTime: Date and time values in YYYY-MM-DD HH:MM:SS format. Examples:
2023-12-25 14:30:00,2024-01-15 09:15:30 - Null: Represented as
null
Fields reference values in the data context:
- Simple fields: Direct reference to a field. Example:
age - Nested fields: Access nested objects using dot notation. Example:
person.details.age - Default field: When a default field is specified, operators can be used without explicitly naming the field. Example:
>= 18(with "age" as default field)
| Operator | Description | Example |
|---|---|---|
= | Equal to | name = 'John' |
!= | Not equal to | status != 'pending' |
> | Greater than | age > 18 |
>= | Greater than or equal to | price >= 9.99 |
< | Less than | temperature < 32 |
<= | Less than or equal to | quantity <= 100 |
| Operator | Description | Example |
|---|---|---|
AND or && | Logical AND | age > 18 AND status = 'active' |
OR or || | Logical OR | category = 'book' OR category = 'ebook' |
NOT or not | Logical NOT | NOT (price < 10) |
| Operator | Description | Example |
|---|---|---|
+ | Addition | price + tax |
- | Subtraction | total - discount |
* | Multiplication | quantity * price |
/ | Division | total / count |
% | Modulus | id % 10 |
^ | Exponent | 2 ^ 3 |
Compare field values against literals or other fields:
age > 21 name = 'Alice' price < maxPrice Combine expressions with logical operators:
(age > 21) AND (status = 'active') category = 'book' OR category = 'ebook' NOT (price > 100) Test if a value falls within a range (both bounds are inclusive):
age 18 TO 65 price 9.99 TO 49.99 Test if a value is in a set:
status IN ('pending', 'processing', 'shipped') category NOT IN ('restricted', 'discontinued') Test if an array contains specific values:
tags CONTAINS_ANY ('sale', 'new') permissions CONTAINS_ALL ('read', 'write') | Function | Description | Example |
|---|---|---|
MIN | Minimum value | MIN(1, 2, 3) or MIN(numbers) |
MAX | Maximum value | MAX(1, 2, 3) or MAX(numbers) |
SUM | Sum of values | SUM(1, 2, 3) or SUM(numbers) |
AVG | Average of values | AVG(1, 2, 3) or AVG(numbers) |
MEAN | Mean of values | MEAN(1, 2, 3) or MEAN(numbers) |
MEDIAN | Median of values | MEDIAN(1, 2, 3) or MEDIAN(numbers) |
MODE | Mode of values | MODE(1, 1, 2, 3) or MODE(numbers) |
LEN | Length of string or array | LEN('hello') or LEN(items) |
INT | Convert to integer | INT(2.7) or INT(value) |
DAYS_ELAPSED | Number of days elapsed since a date | DAYS_ELAPSED(2023-01-01) or DAYS_ELAPSED(startDate) |
Functions can be used in both boolean and arithmetic contexts:
age > MIN(requiredAge, recommendedAge) LEN(name) > 3 Complex conditions can be created by combining expressions:
(age >= 18 AND age <= 65) AND (status = 'active' OR status = 'pending') Arithmetic expressions can be used within boolean expressions:
price > (basePrice + tax) age > (minimumAge + 5) Functions can be nested:
MAX(1, 2, MIN(5, 7, 87)) Check if a field is null or not:
lastLogin = null email != null Arrays can be used with specific operators and functions:
// Check if the 'roles' array contains any of the specified values roles CONTAINS_ANY ('admin', 'editor') // Check if the 'permissions' array contains all of the specified values permissions CONTAINS_ALL ('read', 'write', 'delete') // Get the length of an array LEN(items) > 0 Date and DateTime values can be used in comparisons and with the DAYS_ELAPSED function:
// Date comparisons startDate > 2023-01-01 endDate <= 2024-12-31 // DateTime comparisons lastLogin >= 2023-06-15 09:30:00 createdAt < 2024-01-01 00:00:00 // Using DAYS_ELAPSED function DAYS_ELAPSED(startDate) > 30 DAYS_ELAPSED(2023-01-01) < 365 - Strings must be enclosed in single or double quotes
- Quotes within strings can be handled:
content = "It's a wonderful day"attribute = 'She said "Hello World"'
Operators and function names are case-insensitive:
// These are equivalent name = 'John' AND age > 21 NAME = 'John' and AGE > 21 However, field names and string values are case-sensitive.
The library can be used to evaluate boolean expressions against a data context.
Usage examples:
final BooleanExpressionEvaluator booleanExpressionEvaluator = new BooleanExpressionEvaluator(new BoolParser()); final Map<String, Object> data = new HashMap<>(); data.put("age", 26); final Try<Boolean> resultOptional = booleanExpressionEvaluator.evaluate("age >= 27", data); assertTrue(resultOptional.isSuccess()); assertFalse(resultOptional.get());final BooleanExpressionEvaluator booleanExpressionEvaluator = new BooleanExpressionEvaluator(new BoolParser()); final Map<String, Object> data = new HashMap<>(); data.put("age", 25); data.put("name", "sid"); final Try<Boolean> resultOptional = booleanExpressionEvaluator.evaluate("name = 'sid' AND age = 25", data); assertTrue(resultOptional.isSuccess()); assertTrue(resultOptional.get());final BooleanExpressionEvaluator booleanExpressionEvaluator = new BooleanExpressionEvaluator(new BoolParser()); final Map<String, Object> data = new HashMap<>(); data.put("age", 25); data.put("name", "sid"); data.put("num", 45); final Try<Boolean> resultOptional = booleanExpressionEvaluator.evaluate("name = 'sid' AND (age = 25 OR num = 44)", data); assertTrue(resultOptional.isSuccess()); assertTrue(resultOptional.get());final BooleanExpressionEvaluator booleanExpressionEvaluator = new BooleanExpressionEvaluator(new BoolParser()); final Map<String, Object> data = new HashMap<>(); data.put("app_version", "1.5.9"); final Try<Boolean> resultOptional = booleanExpressionEvaluator.evaluate("app_version < 1.5.10", data); assertTrue(resultOptional.isSuccess()); assertTrue(resultOptional.get());The return type is Try<Boolean>. Failure means that parsing has failed and any fallback can be used.
For a complete list of examples please check out the test file
The library can also evaluate arithmetic expressions, supporting both literals and variables from the data context.
final ArithmeticExpressionEvaluator evaluator = new ArithmeticExpressionEvaluator(new BoolParser()); final Map<String, Object> data = new HashMap<>(); data.put("a", 10); final Try<Object> resultOptional = evaluator.evaluate("a + 5", data); assertTrue(resultOptional.isSuccess()); assertEquals(resultOptional.get(), 15);final ArithmeticExpressionEvaluator evaluator = new ArithmeticExpressionEvaluator(new BoolParser()); final Map<String, Object> data = new HashMap<>(); data.put("a", 10); final Try<Object> resultOptional = evaluator.evaluate("((5 * 2) + a) * 2 + (1 + 3 * (a / 2))", data); assertTrue(resultOptional.isSuccess()); assertEquals(resultOptional.get(), 56);final ArithmeticExpressionEvaluator evaluator = new ArithmeticExpressionEvaluator(new BoolParser()); final Map<String, Object> data = new HashMap<>(); data.put("a", 10); final Try<Object> resultOptional = evaluator.evaluate("min(1, 2, 3)", data); assertTrue(resultOptional.isSuccess()); assertEquals(resultOptional.get(), 1);For a complete list of examples please check out the test file
The parser produces different node types based on the expression:
private final Node left; private final Node right; private final Operator operator; private final DataType dataType;private final String field; private final Object fromValue; private final Object toValue; private final DataType fromDataType; private final DataType toDataType;private Node left; private Node right; private LogicalOperationType operator;private final DataType dataType; private final Object value;private final String field;private final String field; private final List<Node> items;