Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ The language supports the following data types:
4. **Decimal**: Numbers with decimal points (stored as BigDecimal). Examples: `3.14`, `-2.5`
5. **Boolean**: `true` or `false` (case-insensitive)
6. **Version**: Semantic version numbers. Examples: `1.0.6`, `2.3.1.5`
7. **Null**: Represented as `null`
7. **Date**: Date values in YYYY-MM-DD format. Examples: `2023-12-25`, `2024-01-15`
8. **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`
9. **Null**: Represented as `null`

### Field References

Expand Down Expand Up @@ -174,6 +176,7 @@ permissions CONTAINS_ALL ('read', 'write')
| `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:

Expand Down Expand Up @@ -233,6 +236,24 @@ permissions CONTAINS_ALL ('read', 'write', 'delete')
LEN(items) > 0
```

#### Working with Dates

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
```

#### String Handling

- Strings must be enclosed in single or double quotes
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ repositories {

dependencies {
implementation 'org.apache.maven:maven-artifact:3.5.2'
implementation 'org.antlr:antlr4-runtime:4.13.1'
implementation 'org.antlr:antlr4-runtime:4.13.2'
implementation 'io.vavr:vavr:0.10.4'
implementation 'com.github.ben-manes.caffeine:caffeine:2.9.3'
implementation 'org.projectlombok:lombok:1.18.26'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
*/
@AllArgsConstructor
public enum DataType {
STRING(6, false),
STRING(8, false),
INTEGER(3, true),
LONG(4, true),
DECIMAL(5, true),
VERSION(2, true),
BOOLEAN(1, false);
BOOLEAN(1, false),
DATE(6, false),
DATETIME(7, false);

public final int priority;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ public enum FunctionType {
MODE,
MEDIAN,
INT,
LEN;
LEN,
DAYS_ELAPSED;

public static Optional<FunctionType> getArrayFunctionFromSymbol(final String symbol) {
final String symbolUpperCase = symbol.toUpperCase();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public static void initialize() {
abstractDataTypeMap.put(DataType.LONG, new LongDataType());
abstractDataTypeMap.put(DataType.VERSION, new VersionDataType());
abstractDataTypeMap.put(DataType.BOOLEAN, new BooleanDataType());
abstractDataTypeMap.put(DataType.DATE, new DateDataType());
abstractDataTypeMap.put(DataType.DATETIME, new DateTimeDataType());
}

public static AbstractDataType getDataType(final DataType dataType) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.github.sidhant92.boolparser.datatype;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Optional;
import com.github.sidhant92.boolparser.constant.DataType;

/**
* @author sidhant.aggarwal
* @since 05/03/2023
*/
public class DateDataType extends AbstractDataType<LocalDate> {
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

public DateDataType() {
super(LocalDate.class);
}

@Override
public DataType getDataType() {
return DataType.DATE;
}

@Override
public boolean isValid(final Object value) {
boolean isValid = super.defaultIsValid(value);
if (!isValid) {
return parseDate(value.toString()).isPresent();
}
return true;
}

@Override
public boolean isValid(final Object value, final boolean useStrictValidation) {
if (!useStrictValidation) {
return isValid(value);
}
return super.defaultIsValid(value);
}

@Override
public Optional<LocalDate> getValue(Object value) {
final Optional<LocalDate> result = defaultGetValue(value);
if (result.isPresent()) {
return result;
}
return parseDate(value.toString());
}

private Optional<LocalDate> parseDate(String dateString) {
try {
return Optional.of(LocalDate.parse(dateString, DATE_FORMATTER));
} catch (DateTimeParseException ignored) {
return Optional.empty();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.github.sidhant92.boolparser.datatype;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Optional;
import com.github.sidhant92.boolparser.constant.DataType;

/**
* @author sidhant.aggarwal
* @since 05/03/2023
*/
public class DateTimeDataType extends AbstractDataType<LocalDateTime> {
private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

public DateTimeDataType() {
super(LocalDateTime.class);
}

@Override
public DataType getDataType() {
return DataType.DATETIME;
}

@Override
public boolean isValid(final Object value) {
boolean isValid = super.defaultIsValid(value);
if (!isValid) {
return parseDateTime(value.toString()).isPresent();
}
return true;
}

@Override
public boolean isValid(final Object value, final boolean useStrictValidation) {
if (!useStrictValidation) {
return isValid(value);
}
return super.defaultIsValid(value);
}

@Override
public Optional<LocalDateTime> getValue(Object value) {
final Optional<LocalDateTime> result = defaultGetValue(value);
if (result.isPresent()) {
return result;
}
return parseDateTime(value.toString());
}

private Optional<LocalDateTime> parseDateTime(String dateTimeString) {
try {
return Optional.of(LocalDateTime.parse(dateTimeString, DATETIME_FORMATTER));
} catch (DateTimeParseException ignored) {
return Optional.empty();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.github.sidhant92.boolparser.exception;

public class InvalidFunctionArgument extends RuntimeException {
public InvalidFunctionArgument(final String message) {
super(message);
}

public InvalidFunctionArgument() {
super();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.github.sidhant92.boolparser.constant.FunctionType;
import com.github.sidhant92.boolparser.function.arithmetic.AbstractFunction;
import com.github.sidhant92.boolparser.function.arithmetic.AvgFunction;
import com.github.sidhant92.boolparser.function.arithmetic.DaysElapsedFunction;
import com.github.sidhant92.boolparser.function.arithmetic.IntFunction;
import com.github.sidhant92.boolparser.function.arithmetic.LenFunction;
import com.github.sidhant92.boolparser.function.arithmetic.MaxFunction;
Expand Down Expand Up @@ -35,6 +36,7 @@ public static void initialize() {
arithmeticFunctionrMap.put(FunctionType.MODE, new ModeFunction());
arithmeticFunctionrMap.put(FunctionType.INT, new IntFunction());
arithmeticFunctionrMap.put(FunctionType.LEN, new LenFunction());
arithmeticFunctionrMap.put(FunctionType.DAYS_ELAPSED, new DaysElapsedFunction());
}

public static AbstractFunction getArithmeticFunction(final FunctionType functionType) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.github.sidhant92.boolparser.function.arithmetic;

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.List;
import com.github.sidhant92.boolparser.constant.ContainerDataType;
import com.github.sidhant92.boolparser.constant.DataType;
import com.github.sidhant92.boolparser.constant.FunctionType;
import com.github.sidhant92.boolparser.domain.EvaluatedNode;
import com.github.sidhant92.boolparser.exception.InvalidFunctionArgument;

/**
* Function to calculate the number of days elapsed since a given date.
* Returns a positive number for dates in the past, negative for dates in the future.
*
* @author sidhant.aggarwal
* @since 05/03/2023
*/
public class DaysElapsedFunction extends AbstractFunction {

@Override
public Object evaluate(final List<EvaluatedNode> items) {
if (items.size() != 1) {
throw new InvalidFunctionArgument("DAYS_ELAPSED function requires exactly one argument");
}

final EvaluatedNode item = items.get(0);
final Object value = item.getValue();

LocalDate inputDate;
if (value instanceof LocalDate) {
inputDate = (LocalDate) value;
} else if (value instanceof String) {
// Try to parse string as date if the DataType detection missed it
try {
inputDate = LocalDate.parse((String) value);
} catch (Exception e) {
throw new InvalidFunctionArgument("DAYS_ELAPSED function requires a valid date argument, got: " + value);
}
} else {
throw new InvalidFunctionArgument("DAYS_ELAPSED function requires a date argument, got: " + value.getClass().getSimpleName());
}

final LocalDate today = LocalDate.now();
return ChronoUnit.DAYS.between(inputDate, today);
}

@Override
public FunctionType getFunctionType() {
return FunctionType.DAYS_ELAPSED;
}

@Override
public List<ContainerDataType> getAllowedContainerTypes() {
return Arrays.asList(ContainerDataType.PRIMITIVE);
}

@Override
public List<DataType> getAllowedDataTypes() {
return Arrays.asList(DataType.DATE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ public List<ContainerDataType> getAllowedContainerTypes() {

@Override
public List<DataType> getAllowedDataTypes() {
return Arrays.asList(DataType.INTEGER, DataType.LONG, DataType.DECIMAL, DataType.STRING, DataType.VERSION);
return Arrays.asList(DataType.INTEGER, DataType.LONG, DataType.DECIMAL, DataType.STRING, DataType.VERSION, DataType.DATE, DataType.DATETIME);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ public List<ContainerDataType> getAllowedContainerTypes() {

@Override
public List<DataType> getAllowedDataTypes() {
return Arrays.asList(DataType.INTEGER, DataType.LONG, DataType.DECIMAL, DataType.STRING, DataType.VERSION);
return Arrays.asList(DataType.INTEGER, DataType.LONG, DataType.DECIMAL, DataType.STRING, DataType.VERSION, DataType.DATE, DataType.DATETIME);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ public List<ContainerDataType> getAllowedContainerTypes() {

@Override
public List<DataType> getAllowedDataTypes() {
return Arrays.asList(DataType.INTEGER, DataType.LONG, DataType.DECIMAL, DataType.STRING, DataType.VERSION);
return Arrays.asList(DataType.INTEGER, DataType.LONG, DataType.DECIMAL, DataType.STRING, DataType.VERSION, DataType.DATE, DataType.DATETIME);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ public List<ContainerDataType> getAllowedContainerTypes() {

@Override
public List<DataType> getAllowedDataTypes() {
return Arrays.asList(DataType.INTEGER, DataType.LONG, DataType.DECIMAL, DataType.STRING, DataType.VERSION);
return Arrays.asList(DataType.INTEGER, DataType.LONG, DataType.DECIMAL, DataType.STRING, DataType.VERSION, DataType.DATE, DataType.DATETIME);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package com.github.sidhant92.boolparser.parser.antlr;// Generated from /Users/sid/Desktop/filter2/BooleanExpression.g4 by ANTLR 4.13.1
// Generated from /Users/sidhantaggarwal/bool-parser-java/src/main/java/resources/BooleanExpression.g4 by ANTLR 4.13.2
package com.github.sidhant92.boolparser.parser.antlr;

import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ErrorNode;
Expand Down
Loading