Skip to content

Commit

Permalink
Merge pull request #2 from sidhant92/develop
Browse files Browse the repository at this point in the history
Support Quotes and update README
  • Loading branch information
sidhant92 authored Jul 20, 2020
2 parents 83a3604 + 107681b commit 28e39aa
Show file tree
Hide file tree
Showing 8 changed files with 538 additions and 452 deletions.
113 changes: 113 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,115 @@
# boolparser
A Boolean Expression Parser

The library can help parse complex and nested boolean expressions.
The filter expression is SQL-like syntax, where you can use boolean operators and parentheses to combine individual filters.

#### Textual Equality

Format: `${attributeName}:${value}`

Example: `name:john`

#### Numeric Comparisons

Format: `${attributeName} ${operator} ${value}`

Example: `price > 12.99`

The ${value} must be numeric. Supported operators are `<`, `<=`, `=`, `!=`, `>=` and `>`, with the same semantics as in virtually all programming languages.

#### Numeric Range

Format: `${attributeName}:${lowerBound} TO ${upperBound}`

Example: `price:5.99 TO 100`

`${lowerBound}` and `${upperBound}` must be numeric. Both are inclusive.

#### Boolean operators

Example:

`price < 10 AND (category:Book OR NOT category:Ebook)`

Individual filters can be combined via boolean operators. The following operators are supported:

* `OR`: must match any of the combined conditions (disjunction)
* `AND`: must match all of the combined conditions (conjunction)
* `NOT`: negate a filter

Parentheses, `(` and `)`, can be used for grouping.

You cannot negate a group of filters, only an individual filter. For example, NOT(filter1 OR filter2) is not allowed.

#### Usage Notes
* Phrases that includes quotes, like `content:"It's a wonderful day"`
* Phrases that includes quotes, like `attribute:'She said "Hello World"'`

## Usage
POM
```xml
<dependencies>
<dependency>
<groupId>com.github.sidhant92</groupId>
<artifactId>boolparser</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
```
Gradle
```
dependencies {
implementation "com.github.sidhant92:boolparser:1.0.0"
}
```

```
final PEGBoolExpressionParser boolExpressionParser = new PEGBoolExpressionParser();
final Optional<Node> nodeOptional = boolExpressionParser.parseExpression("name:test");
```

### Node Types
####
StringToken
```
private final String field;
private final String value;
```

####
NumericToken
```
private final String field;
private final Object value;
private final Operator operator;
private final DataType dataType;
```

####
NumericRangeToken
```
private final String field;
private final Object fromValue;
private final Object toValue;
private final DataType fromDataType;
private final DataType toDataType;
```

####
BoolExpression
```
private final List<Node> orOperations = new ArrayList<>();
private final List<Node> andOperations = new ArrayList<>();
private final List<Node> notOperations = new ArrayList<>();
```
67 changes: 66 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
plugins {
id 'java'
id 'maven'
id 'signing'
}

group 'com.github.sidhant92'
version '1.0-SNAPSHOT'
version '1.0.1'

repositories {
mavenCentral()
Expand Down Expand Up @@ -36,3 +38,66 @@ test {
html.enabled = false
}
}

task javadocJar(type: Jar) {
classifier = 'javadoc'
from javadoc
}

task sourcesJar(type: Jar) {
classifier = 'sources'
from sourceSets.main.allSource
}

artifacts {
archives javadocJar, sourcesJar
}

signing {
sign configurations.archives
}

uploadArchives {
repositories {
mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }

repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
authentication(userName: ossrhUsername, password: ossrhPassword)
}

snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") {
authentication(userName: ossrhUsername, password: ossrhPassword)
}

pom.project {
name 'Example Application'
packaging 'jar'
// optionally artifactId can be defined here
description 'A application used as an example on how to set up pushing its components to the Central Repository.'
url 'http://www.example.com/example-application'

scm {
connection 'scm:svn:http://foo.googlecode.com/svn/trunk/'
developerConnection 'scm:svn:https://foo.googlecode.com/svn/trunk/'
url 'http://foo.googlecode.com/svn/trunk/'
}

licenses {
license {
name 'The Apache License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}

developers {
developer {
id 'sidhant92'
name 'Sidhant Aggarwal'
email 'sidhant92@me.com'
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
import java.util.List;

public interface Actions {
public TreeNode make_logical_and(String input, int start, int end, List<TreeNode> elements);
public TreeNode make_logical_or(String input, int start, int end, List<TreeNode> elements);
public TreeNode make_numeric_range_token(String input, int start, int end, List<TreeNode> elements);
public TreeNode make_numeric_token(String input, int start, int end, List<TreeNode> elements);
public TreeNode make_primary(String input, int start, int end, List<TreeNode> elements);
public TreeNode make_string_token(String input, int start, int end, List<TreeNode> elements);
TreeNode make_logical_and(String input, int start, int end, List<TreeNode> elements);
TreeNode make_logical_or(String input, int start, int end, List<TreeNode> elements);
TreeNode make_numeric_range_token(String input, int start, int end, List<TreeNode> elements);
TreeNode make_numeric_token(String input, int start, int end, List<TreeNode> elements);
TreeNode make_primary(String input, int start, int end, List<TreeNode> elements);
TreeNode make_string_token(String input, int start, int end, List<TreeNode> elements);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,13 @@
public class ActionsImpl implements Actions {
@Override
public TreeNode make_string_token(String input, int start, int end, List<TreeNode> elements) {
final StringNode stringNode = new StringNode(elements.get(2).text, elements.get(6).text);
if (elements.get(0).text.equals("NOT")) {
final BooleanNode booleanNode = new BooleanNode();
booleanNode.addClause(stringNode, LogicalOperationType.NOT);
return booleanNode;
} else {
return stringNode;
}
final StringNode stringNode = new StringNode(getCapturedString(elements.get(2).text), getCapturedString(elements.get(6).text));
return checkNotExpression(elements, stringNode);
}

@Override
public TreeNode make_numeric_token(String input, int start, int end, List<TreeNode> elements) {
final String field = elements.get(2).text;
final String field = getCapturedString(elements.get(2).text);
final String operator = elements.get(4).text;
final String stringValue = elements.get(6).text;
Object value;
Expand All @@ -40,18 +34,12 @@ public TreeNode make_numeric_token(String input, int start, int end, List<TreeNo
valueDataType = DataType.DOUBLE;
}
final NumericNode numericNode = new NumericNode(field, value, operator, valueDataType);
if (elements.get(0).text.equals("NOT")) {
final BooleanNode booleanNode = new BooleanNode();
booleanNode.addClause(numericNode, LogicalOperationType.NOT);
return booleanNode;
} else {
return numericNode;
}
return checkNotExpression(elements, numericNode);
}

@Override
public TreeNode make_numeric_range_token(String input, int start, int end, List<TreeNode> elements) {
final String field = elements.get(2).text;
final String field = getCapturedString(elements.get(2).text);
final String fromStringValue = elements.get(6).text;
final String toStringValue = elements.get(10).text;
Object fromValue, toValue;
Expand All @@ -71,17 +59,33 @@ public TreeNode make_numeric_range_token(String input, int start, int end, List<
toValueDataType = DataType.DOUBLE;
}
final NumericRangeNode numericRangeNode = new NumericRangeNode(field, fromValue, toValue, fromValueDataType, toValueDataType);
return checkNotExpression(elements, numericRangeNode);
}

public TreeNode make_primary(String input, int start, int end, List<TreeNode> elements) {
return elements.get(1);
}

private TreeNode checkNotExpression(final List<TreeNode> elements, final TreeNode node) {
if (elements.get(0).text.equals("NOT")) {
final BooleanNode booleanNode = new BooleanNode();
booleanNode.addClause(numericRangeNode, LogicalOperationType.NOT);
booleanNode.addClause(node, LogicalOperationType.NOT);
return booleanNode;
} else {
return numericRangeNode;
return node;
}
}

public TreeNode make_primary(String input, int start, int end, List<TreeNode> elements) {
return elements.get(1);
private String getCapturedString(final String input) {
final int firstPosition = 0;
final int lastPosition = input.length() - 1;
if (firstPosition != lastPosition && input.charAt(firstPosition) == '"' && input.charAt(lastPosition) == '"') {
return input.substring(1, input.length() - 1);
} else if (firstPosition != lastPosition && input.charAt(firstPosition) == '\'' && input.charAt(lastPosition) == '\'') {
return input.substring(1, input.length() - 1);
} else {
return input;
}
}

public TreeNode make_logical_and(String input, int start, int end, List<TreeNode> elements) {
Expand Down
Loading

0 comments on commit 28e39aa

Please sign in to comment.