Skip to content

Commit

Permalink
Adds new generic DataType.BINARY (#482)
Browse files Browse the repository at this point in the history
  • Loading branch information
oswaldobapvicjr authored Jul 7, 2024
1 parent 5c9fe96 commit 4a43208
Show file tree
Hide file tree
Showing 11 changed files with 195 additions and 5 deletions.
15 changes: 15 additions & 0 deletions docs/concepts/datatypes.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ EvalEx supports the following data types:
| ARRAY | java.util.List |
| STRUCTURE | java.util.Map |
| EXPRESSION_NODE | com.ezylang.evalex.parser.ASTNode |
| BINARY[^1] | java.lang.Object |
| NULL | null |

[^1]: Since 3.3.0

Data is stored in an _EvaluationValue_, which holds the value and the data type.

### NUMBER
Expand Down Expand Up @@ -214,6 +217,18 @@ Note that the above expression is not evaluated as "2 * 4 + 3", which would resu
Instead, the sub-expression "4 + 3" is calculated first, when it comes to finding the value of the
variable _b_. Resulting in calculation of "2 * 7", which is 14.


### BINARY

A representation for an undefined (raw), non-null object that could not fit in any of the previous
data types.

This allows for special functions to handle any object type.

The binary data type is **disabled** by default and can be enabled by setting a dedicated property
in the [Configuration](../configuration/configuration.html).


### NULL

A representation for _null_ objects.
Expand Down
3 changes: 3 additions & 0 deletions docs/concepts/parsing_evaluation.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,11 @@ _EvaluationValue_ will be of one of the types:
- NUMBER - If the expression resulted in a number.
- STRING - If the expression resulted in a string.
- BOOLEAN - If the expression resulted in a boolean value.
- DATE_TIME - If the expression resulted in a date/time value.
- DURATION - If the expression resulted in a duration value.
- ARRAY - If the expression resulted in an array.
- STRUCTURE - If the expression resulted in a structure.
- BINARY - If the expression could not be converted to any of the previous types.

The _EvaluationValue_ has methods to check and retrieve/convert the evaluation value.

Expand Down
10 changes: 10 additions & 0 deletions docs/configuration/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ ExpressionConfiguration configuration=ExpressionConfiguration.builder()
.powerOfPrecedence(OperatorIfc.OPERATOR_PRECEDENCE_POWER)
.stripTrailingZeros(true)
.structuresAllowed(true)
.binaryAllowed(false)
.singleQuoteStringLiteralsAllowed(false)
.zoneId(ZoneId.systemDefault())
.build();
Expand All @@ -45,6 +46,15 @@ Specifies if the array index function is allowed (default is true). If set to fa
will throw a _ParseException_, if there is a '[' is encountered in the expression and also no
operator or function is defined for this character.

### Binary allowed

Specifies if the binary[^1] (raw) data type is allowed for expressions that can not be converted to any
known data type.

See chapter [Data Types](../concepts/datatypes.html) for details.

[^1]: Since 3.3.0

### Data Accessor

The Data Accessor is responsible for storing and retrieving variable values.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,13 @@ public class ExpressionConfiguration {
/** Support for structures in expressions are allowed or not. */
@Builder.Default private final boolean structuresAllowed = true;

/**
* Support for the binary (undefined) data type is allowed or not.
*
* @since 3.3.0
*/
@Builder.Default private final boolean binaryAllowed = false;

/** Support for implicit multiplication, like in (a+b)(b+c) are allowed or not. */
@Builder.Default private final boolean implicitMultiplicationAllowed = true;

Expand Down
25 changes: 24 additions & 1 deletion src/main/java/com/ezylang/evalex/data/EvaluationValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ public enum DataType {
*/
EXPRESSION_NODE,
/** A null value */
NULL
NULL,
/** Raw (undefined) type, stored as an {@link Object}. */
BINARY
}

Object value;
Expand Down Expand Up @@ -219,6 +221,17 @@ public static EvaluationValue structureValue(Map<?, ?> value) {
return new EvaluationValue(value, DataType.STRUCTURE);
}

/**
* Creates a new binary (raw) value.
*
* @param value The Object to use.
* @return the new binary value.
* @since 3.3.0
*/
public static EvaluationValue binaryValue(Object value) {
return new EvaluationValue(value, DataType.BINARY);
}

/**
* Checks if the value is of type {@link DataType#NUMBER}.
*
Expand Down Expand Up @@ -295,6 +308,16 @@ public boolean isNullValue() {
return getDataType() == DataType.NULL;
}

/**
* Checks if the value is of type {@link DataType#BINARY}.
*
* @return <code>true</code> or <code>false</code>.
* @since 3.3.0
*/
public boolean isBinaryValue() {
return getDataType() == DataType.BINARY;
}

/**
* Creates a {@link DataType#NUMBER} value from a {@link String}.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
Copyright 2012-2024 Udo Klimaschewski
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.ezylang.evalex.data.conversion;

import com.ezylang.evalex.config.ExpressionConfiguration;
import com.ezylang.evalex.data.EvaluationValue;

/**
* Converter to convert to the BINARY data type.
*
* @author oswaldobapvicjr
* @since 3.3.0
*/
public class BinaryConverter implements ConverterIfc {
@Override
public EvaluationValue convert(Object object, ExpressionConfiguration configuration) {
return EvaluationValue.binaryValue(object);
}

@Override
public boolean canConvert(Object object) {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ public EvaluationValue convertObject(Object object, ExpressionConfiguration conf
}
}

if (configuration.isBinaryAllowed()) {
return EvaluationValue.binaryValue(object);
}

throw new IllegalArgumentException(
"Unsupported data type '" + object.getClass().getName() + "'");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,14 @@ void testStructuresAllowed() {
assertThat(configuration.isStructuresAllowed()).isFalse();
}

@Test
void testBinaryAllowed() {
ExpressionConfiguration configuration =
ExpressionConfiguration.builder().binaryAllowed(true).build();

assertThat(configuration.isArraysAllowed()).isTrue();
}

@Test
void testSingleQuoteStringLiteralsAllowed() {
ExpressionConfiguration configuration =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,23 @@ void testNestedEvaluationValueNull() {
}

@Test
void testException() {
void testDefaultEvaluationWithBinaryAllowed() {
ExpressionConfiguration configuration =
ExpressionConfiguration.builder().binaryAllowed(true).build();
final Object rawValue = new Object();
EvaluationValue converted = converter.convertObject(rawValue, configuration);

assertThat(converted.getDataType()).isEqualTo(EvaluationValue.DataType.BINARY);
assertThat(converted.isBinaryValue()).isTrue();
assertThat(converted.getValue()).isSameAs(rawValue);
}

@Test
void testExceptionWithBinaryNotAllowed() {
ExpressionConfiguration configuration =
ExpressionConfiguration.builder().binaryAllowed(false).build();
final Error error = new Error();
assertThatThrownBy(() -> converter.convertObject(error, defaultConfiguration))
assertThatThrownBy(() -> converter.convertObject(error, configuration))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Unsupported data type 'java.lang.Error'");
}
Expand Down
28 changes: 26 additions & 2 deletions src/test/java/com/ezylang/evalex/data/EvaluationValueTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,31 @@
class EvaluationValueTest {

@Test
void testUnsupportedDataType() {
final ExpressionConfiguration configuration = defaultConfiguration();
void testUnsupportedDataTypeWithBinaryNotAllowed() {
final ExpressionConfiguration configuration =
ExpressionConfiguration.builder().binaryAllowed(false).build();
assertThatThrownBy(() -> EvaluationValue.of(Locale.FRANCE, configuration))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Unsupported data type 'java.util.Locale'");
}

@Test
void testUnsupportedDataTypeWithBinaryAllowed() {
final ExpressionConfiguration configuration =
ExpressionConfiguration.builder().binaryAllowed(true).build();
Object rawValue = new Object();
EvaluationValue value = EvaluationValue.of(rawValue, configuration);
assertThat(value.isStringValue()).isFalse();
assertThat(value.isNumberValue()).isFalse();
assertThat(value.isBooleanValue()).isFalse();
assertThat(value.isStructureValue()).isFalse();
assertThat(value.isArrayValue()).isFalse();
assertThat(value.isExpressionNode()).isFalse();
assertThat(value.isNullValue()).isFalse();
assertThat(value.isBinaryValue()).isTrue();
assertThat(value.getValue()).isSameAs(rawValue);
}

@Test
void testString() {
EvaluationValue value = EvaluationValue.of("Hello World", defaultConfiguration());
Expand All @@ -53,6 +71,7 @@ void testString() {
assertThat(value.isArrayValue()).isFalse();
assertThat(value.isExpressionNode()).isFalse();
assertThat(value.isNullValue()).isFalse();
assertThat(value.isBinaryValue()).isFalse();
assertDataIsCorrect(
value, "Hello World", BigDecimal.ZERO, false, Instant.EPOCH, Duration.ZERO, String.class);
}
Expand Down Expand Up @@ -93,6 +112,7 @@ void testBooleanTrue() {
assertThat(value.isArrayValue()).isFalse();
assertThat(value.isExpressionNode()).isFalse();
assertThat(value.isNullValue()).isFalse();
assertThat(value.isBinaryValue()).isFalse();
assertDataIsCorrect(
value, "true", BigDecimal.ONE, true, Instant.EPOCH, Duration.ZERO, Boolean.class);
}
Expand Down Expand Up @@ -407,6 +427,7 @@ void testArray() {
assertThat(value.isStringValue()).isFalse();
assertThat(value.isExpressionNode()).isFalse();
assertThat(value.isNullValue()).isFalse();
assertThat(value.isBinaryValue()).isFalse();

assertThat(value.getArrayValue()).hasSize(2);
assertThat(value.getArrayValue().get(0).getStringValue()).isEqualTo("1");
Expand Down Expand Up @@ -443,6 +464,7 @@ void testStructure() {
assertThat(value.isArrayValue()).isFalse();
assertThat(value.isExpressionNode()).isFalse();
assertThat(value.isNullValue()).isFalse();
assertThat(value.isBinaryValue()).isFalse();

assertThat(value.getStructureValue()).hasSize(2);
assertThat(value.getStructureValue().get("a").getStringValue()).isEqualTo("Hello");
Expand Down Expand Up @@ -476,6 +498,7 @@ void testExpressionNode() {
assertThat(value.isArrayValue()).isFalse();
assertThat(value.isStringValue()).isFalse();
assertThat(value.isNullValue()).isFalse();
assertThat(value.isBinaryValue()).isFalse();

assertDataIsCorrect(
value,
Expand Down Expand Up @@ -537,6 +560,7 @@ void testNull() {
assertThat(value.isArrayValue()).isFalse();
assertThat(value.isExpressionNode()).isFalse();
assertThat(value.isNullValue()).isTrue();
assertThat(value.isBinaryValue()).isFalse();
assertDataIsCorrect(value, null, null, null);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
Copyright 2012-2024 Udo Klimaschewski
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.ezylang.evalex.data.conversion;

import static org.assertj.core.api.Assertions.assertThat;

import com.ezylang.evalex.config.ExpressionConfiguration;
import com.ezylang.evalex.data.EvaluationValue;
import org.junit.jupiter.api.Test;

class BinaryConverterTest {

private final ExpressionConfiguration defaultConfiguration =
ExpressionConfiguration.defaultConfiguration();

private final BinaryConverter converter = new BinaryConverter();

@Test
void testObject() {
Object object = new Object();

EvaluationValue converted = converter.convert(object, defaultConfiguration);

assertThat(converted.getDataType()).isEqualTo(EvaluationValue.DataType.BINARY);
assertThat(converted.getValue()).isSameAs(object);
}

@Test
void testCanConvert() {
assertThat(converter.canConvert(new Object())).isTrue();
}
}

0 comments on commit 4a43208

Please sign in to comment.