From bd02525d63e64ba7bb083739ca2a4601c3816750 Mon Sep 17 00:00:00 2001 From: Philipp Ossler Date: Tue, 19 Sep 2023 10:38:11 +0200 Subject: [PATCH 1/2] build: Update mvn-scalafmt plugin Update the plugin. Add a minimal configuration file. (cherry picked from commit 284b05992a796c8b1ae928b2c02e01ac51da611d) --- .scalafmt.conf | 6 ++++++ pom.xml | 10 +++++----- 2 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 .scalafmt.conf diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 000000000..2de268ffb --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,6 @@ +version = 3.2.1 +runner.dialect = scala213 + +maxColumn = 100 + +align.preset = most \ No newline at end of file diff --git a/pom.xml b/pom.xml index 1c25be517..ad36aa866 100644 --- a/pom.xml +++ b/pom.xml @@ -237,10 +237,10 @@ org.antipathy - mvn-scalafmt_2.12 - 0.8_1.5.1 + mvn-scalafmt_2.13 + 1.1.1684076452.9f83818 - --diff + ${project.basedir}/.scalafmt.conf @@ -432,7 +432,7 @@ org.antipathy - mvn-scalafmt_2.12 + mvn-scalafmt_2.13 scala-format @@ -468,7 +468,7 @@ org.antipathy - mvn-scalafmt_2.12 + mvn-scalafmt_2.13 scala-format From 3ec34f9f060437a427c753289423d354ae720b61 Mon Sep 17 00:00:00 2001 From: Philipp Ossler Date: Wed, 4 Oct 2023 15:35:40 +0200 Subject: [PATCH 2/2] style: auto-format --- .../scala/org/camunda/feel/FeelEngine.scala | 177 ++-- .../org/camunda/feel/FeelEngineClock.scala | 10 +- .../camunda/feel/api/EvaluationFailure.scala | 15 +- .../feel/api/EvaluationFailureType.scala | 5 +- .../camunda/feel/api/EvaluationResult.scala | 41 +- .../org/camunda/feel/api/FeelEngineApi.scala | 288 +++--- .../camunda/feel/api/FeelEngineBuilder.scala | 40 +- .../org/camunda/feel/api/ParseResult.scala | 24 +- .../org/camunda/feel/context/Context.scala | 4 +- .../camunda/feel/context/CustomContext.scala | 7 +- .../feel/context/CustomFunctionProvider.scala | 22 +- .../feel/context/FunctionProvider.scala | 9 +- .../feel/context/JavaFunctionProvider.scala | 34 +- .../feel/context/VariableProvider.scala | 11 +- .../feel/impl/DefaultValueMapper.scala | 52 +- .../camunda/feel/impl/JavaValueMapper.scala | 17 +- .../camunda/feel/impl/SpiServiceLoader.scala | 3 +- .../builtin/BooleanBuiltinFunctions.scala | 27 +- .../feel/impl/builtin/BuiltinFunction.scala | 10 +- .../builtin/ContextBuiltinFunctions.scala | 116 +-- .../builtin/ConversionBuiltinFunctions.scala | 226 +++-- .../impl/builtin/ListBuiltinFunctions.scala | 352 ++++---- .../builtin/NumericBuiltinFunctions.scala | 184 ++-- .../impl/builtin/RangeBuiltinFunction.scala | 126 ++- .../impl/builtin/StringBuiltinFunctions.scala | 190 ++-- .../builtin/TemporalBuiltinFunctions.scala | 32 +- .../impl/interpreter/BuiltinFunctions.scala | 11 +- .../feel/impl/interpreter/EvalContext.scala | 124 +-- .../impl/interpreter/FeelInterpreter.scala | 831 ++++++++++-------- .../impl/interpreter/JavaClassMapper.scala | 29 +- .../feel/impl/interpreter/ObjectContext.scala | 34 +- .../impl/parser/ExpressionValidator.scala | 31 +- .../camunda/feel/impl/parser/FeelParser.scala | 99 +-- .../feel/impl/script/CompiledFeelScript.scala | 3 +- .../script/FeelExpressionScriptEngine.scala | 3 +- .../feel/impl/script/FeelScriptEngine.scala | 19 +- .../impl/script/FeelScriptEngineFactory.scala | 9 +- .../script/FeelUnaryTestsScriptEngine.scala | 3 +- .../FeelUnaryTestsScriptEngineFactory.scala | 4 +- .../org/camunda/feel/syntaxtree/Exp.scala | 18 +- .../feel/syntaxtree/FunctionParameters.scala | 6 +- .../org/camunda/feel/syntaxtree/Val.scala | 150 ++-- .../camunda/feel/syntaxtree/ZonedTime.scala | 19 +- .../feel/valuemapper/CustomValueMapper.scala | 40 +- .../valuemapper/JavaCustomValueMapper.scala | 49 +- .../feel/valuemapper/ValueMapper.scala | 6 +- src/main/scala/org/camunda/package.scala | 31 +- .../ExternalFunctionsConfigurationTest.scala | 25 +- .../camunda/feel/api/FeelEngineApiTest.scala | 21 +- .../org/camunda/feel/api/FeelEngineTest.scala | 53 +- .../api/StringRepresentationTypeTest.scala | 25 +- .../feel/api/context/CustomContextTest.scala | 44 +- .../feel/api/context/CustomFunctionTest.scala | 14 +- .../api/context/JavaCustomFunctionTest.scala | 3 +- .../BuiltinValueMapperInputTest.scala | 93 +- .../BuiltinValueMapperOutputTest.scala | 3 +- .../valuemapper/CustomValueMapperTest.scala | 19 +- .../feel/examples/SpecExampleTest.scala | 47 +- .../feel/impl/EvaluationResultMatchers.scala | 73 +- .../camunda/feel/impl/FeelEngineTest.scala | 52 +- .../feel/impl/FeelIntegrationTest.scala | 35 +- .../feel/impl/SuppressedFailuresTest.scala | 18 +- .../builtin/BuiltinContextFunctionsTest.scala | 69 +- .../BuiltinConversionFunctionsTest.scala | 143 ++- .../impl/builtin/BuiltinFunctionsTest.scala | 35 +- .../builtin/BuiltinListFunctionsTest.scala | 89 +- .../builtin/BuiltinNumberFunctionTest.scala | 14 +- .../builtin/BuiltinRangeFunctionTest.scala | 55 +- .../builtin/BuiltinStringFunctionsTest.scala | 36 +- .../BuiltinTemporalFunctionsTest.scala | 38 +- .../impl/interpreter/ComparisonTypeTest.scala | 16 +- .../DateTimeDurationPropertiesTest.scala | 83 +- .../interpreter/EvaluationErrorMatcher.scala | 24 +- .../InterpreterBeanExpressionTest.scala | 4 +- .../InterpreterBooleanExpressionTest.scala | 25 +- .../InterpreterContextExpressionTest.scala | 42 +- .../InterpreterDateTimeExpressionTest.scala | 466 +++++----- .../InterpreterExpressionTest.scala | 39 +- .../interpreter/InterpreterFunctionTest.scala | 66 +- .../InterpreterListExpressionTest.scala | 113 ++- .../InterpreterLiteralExpressionTest.scala | 81 +- ...terNonExistingVariableExpressionTest.scala | 14 +- .../InterpreterNumberExpressionTest.scala | 9 +- .../InterpreterStringExpressionTest.scala | 30 +- .../interpreter/InterpreterUnaryTest.scala | 339 ++++--- .../impl/script/ScriptEngineFactoryTest.scala | 4 +- .../impl/script/ScriptEngineManagerTest.scala | 10 +- .../feel/impl/script/ScriptEngineTest.scala | 18 +- .../feel/impl/script/TestValueMapper.scala | 3 +- .../script/UnaryTestsScriptEngineTest.scala | 13 +- .../valuemapper/DefaultValueMapperTest.scala | 63 +- .../valuemapper/JavaValueMapperTest.scala | 10 +- 92 files changed, 3130 insertions(+), 2887 deletions(-) diff --git a/src/main/scala/org/camunda/feel/FeelEngine.scala b/src/main/scala/org/camunda/feel/FeelEngine.scala index afd36f475..e794a126a 100644 --- a/src/main/scala/org/camunda/feel/FeelEngine.scala +++ b/src/main/scala/org/camunda/feel/FeelEngine.scala @@ -17,8 +17,19 @@ package org.camunda.feel import fastparse.Parsed -import org.camunda.feel.FeelEngine.{Configuration, EvalExpressionResult, EvalUnaryTestsResult, Failure} -import org.camunda.feel.api.{EvaluationFailure, EvaluationFailureType, EvaluationResult, FailedEvaluationResult, SuccessfulEvaluationResult} +import org.camunda.feel.FeelEngine.{ + Configuration, + EvalExpressionResult, + EvalUnaryTestsResult, + Failure +} +import org.camunda.feel.api.{ + EvaluationFailure, + EvaluationFailureType, + EvaluationResult, + FailedEvaluationResult, + SuccessfulEvaluationResult +} import org.camunda.feel.context.{Context, FunctionProvider} import org.camunda.feel.impl.interpreter.{BuiltinFunctions, EvalContext, FeelInterpreter} import org.camunda.feel.impl.parser.{ExpressionValidator, FeelParser} @@ -48,16 +59,16 @@ object FeelEngine { case class Failure(message: String) - /** - * @deprecated Use [[org.camunda.feel.api.FeelEngineBuilder]] instead. + /** @deprecated + * Use [[org.camunda.feel.api.FeelEngineBuilder]] instead. */ @deprecated class Builder { - private var functionProvider_ : FunctionProvider = defaultFunctionProvider - private var valueMapper_ : ValueMapper = defaultValueMapper + private var functionProvider_ : FunctionProvider = defaultFunctionProvider + private var valueMapper_ : ValueMapper = defaultValueMapper private var customValueMappers_ : List[CustomValueMapper] = List.empty - private var clock_ : FeelEngineClock = defaultClock - private var configuration_ : Configuration = defaultConfiguration + private var clock_ : FeelEngineClock = defaultClock + private var configuration_ : Configuration = defaultConfiguration def functionProvider(functionProvider: FunctionProvider): Builder = { functionProvider_ = functionProvider @@ -95,7 +106,7 @@ object FeelEngine { } object UnaryTests { - val inputVariable: String = "inputVariableName" + val inputVariable: String = "inputVariableName" val defaultInputVariable: String = "cellInput" } @@ -111,7 +122,8 @@ class FeelEngine( private val interpreter = new FeelInterpreter() private val validator = new ExpressionValidator( - externalFunctionsEnabled = configuration.externalFunctionsEnabled) + externalFunctionsEnabled = configuration.externalFunctionsEnabled + ) logger.info( s"Engine created. [" + @@ -123,52 +135,53 @@ class FeelEngine( private def rootContext(): EvalContext = EvalContext.create( valueMapper = valueMapper, - functionProvider = FunctionProvider.CompositeFunctionProvider(List( - new BuiltinFunctions(clock, valueMapper), - functionProvider - )) + functionProvider = FunctionProvider.CompositeFunctionProvider( + List( + new BuiltinFunctions(clock, valueMapper), + functionProvider + ) + ) ) - private def parse(parser: String => Parsed[Exp], - expression: String): Either[Failure, ParsedExpression] = + private def parse( + parser: String => Parsed[Exp], + expression: String + ): Either[Failure, ParsedExpression] = Try { parser(expression) match { - case Parsed.Success(exp, _) => Right(ParsedExpression(exp, expression)) + case Parsed.Success(exp, _) => Right(ParsedExpression(exp, expression)) case Parsed.Failure(_, _, extra) => - Left(Failure( - s"failed to parse expression '$expression': ${extra.trace().aggregateMsg}")) + Left(Failure(s"failed to parse expression '$expression': ${extra.trace().aggregateMsg}")) } - }.recover(failure => - Left(Failure(s"failed to parse expression '$expression': $failure"))) - .get + }.recover(failure => Left(Failure(s"failed to parse expression '$expression': $failure"))).get - private def validate( - exp: ParsedExpression): Either[Failure, ParsedExpression] = { + private def validate(exp: ParsedExpression): Either[Failure, ParsedExpression] = { validator .validateExpression(exp.expression) .map(failure => - Failure( - s"""validation of expression '${exp.text}' failed: ${failure.message}""")) + Failure(s"""validation of expression '${exp.text}' failed: ${failure.message}""") + ) .toLeft(exp) } - private def eval(exp: ParsedExpression, - context: EvalContext): EvaluationResult = { + private def eval(exp: ParsedExpression, context: EvalContext): EvaluationResult = { interpreter.eval(exp.expression)(context) match { case _ if containsAssertionError(context) => { val failureMessage = getAssertErrorMessage(context) FailedEvaluationResult( - failure = Failure(s"Assertion failure on evaluate the expression '${exp.text}': ${failureMessage}"), + failure = Failure( + s"Assertion failure on evaluate the expression '${exp.text}': ${failureMessage}" + ), suppressedFailures = context.failureCollector.failures ) } - case ValError(cause) => + case ValError(cause) => FailedEvaluationResult( failure = Failure(s"failed to evaluate expression '${exp.text}': $cause"), suppressedFailures = context.failureCollector.failures ) - case value => + case value => SuccessfulEvaluationResult( result = valueMapper.unpackVal(value), suppressedFailures = context.failureCollector.failures @@ -176,135 +189,147 @@ class FeelEngine( } } - /** - * Check if an {@link EvaluationFailureType.ASSERT_FAILURE} error is raised during the evaluation of an expression - * @param context the context of the evaluation - * @return true if an an {@link EvaluationFailureType.ASSERT_FAILURE} is raised, false otherwise - */ + /** Check if an {@link EvaluationFailureType.ASSERT_FAILURE} error is raised during the evaluation + * of an expression + * @param context + * the context of the evaluation + * @return + * true if an an {@link EvaluationFailureType.ASSERT_FAILURE} is raised, false otherwise + */ private def containsAssertionError(context: EvalContext): Boolean = { context.failureCollector.failures.exists(_.failureType == EvaluationFailureType.ASSERT_FAILURE) } private def getAssertErrorMessage(context: EvalContext): String = { - context.failureCollector.failures.find(_.failureType == EvaluationFailureType.ASSERT_FAILURE).get.failureMessage + context.failureCollector.failures + .find(_.failureType == EvaluationFailureType.ASSERT_FAILURE) + .get + .failureMessage } // ============ public API ============ - /** - * @deprecated Use [[org.camunda.feel.api.FeelEngineApi]] instead. + /** @deprecated + * Use [[org.camunda.feel.api.FeelEngineApi]] instead. */ @deprecated def evalExpression( expression: String, - variables: java.util.Map[String, Object]): EvalExpressionResult = + variables: java.util.Map[String, Object] + ): EvalExpressionResult = evalExpression( expression = expression, context = Context.StaticContext(variables.asScala.toMap) ) - /** - * @deprecated Use [[org.camunda.feel.api.FeelEngineApi]] instead. + /** @deprecated + * Use [[org.camunda.feel.api.FeelEngineApi]] instead. */ @deprecated def evalExpression( expression: String, - variables: Map[String, Any] = Map()): EvalExpressionResult = + variables: Map[String, Any] = Map() + ): EvalExpressionResult = evalExpression( expression = expression, context = Context.StaticContext(variables) ) - /** - * @deprecated Use [[org.camunda.feel.api.FeelEngineApi]] instead. + /** @deprecated + * Use [[org.camunda.feel.api.FeelEngineApi]] instead. */ - @deprecated def evalExpression(expression: String, - context: Context): EvalExpressionResult = + @deprecated def evalExpression(expression: String, context: Context): EvalExpressionResult = parseExpression(expression) .flatMap(parsedExpression => eval(parsedExpression, context)) - /** - * @deprecated Use [[org.camunda.feel.api.FeelEngineApi]] instead. + /** @deprecated + * Use [[org.camunda.feel.api.FeelEngineApi]] instead. */ @deprecated def evalUnaryTests( expression: String, - variables: java.util.Map[String, Object]): EvalUnaryTestsResult = + variables: java.util.Map[String, Object] + ): EvalUnaryTestsResult = evalUnaryTests( expression = expression, context = Context.StaticContext(variables.asScala.toMap) ) - /** - * @deprecated Use [[org.camunda.feel.api.FeelEngineApi]] instead. + /** @deprecated + * Use [[org.camunda.feel.api.FeelEngineApi]] instead. */ @deprecated def evalUnaryTests( expression: String, - variables: Map[String, Any] = Map()): EvalUnaryTestsResult = + variables: Map[String, Any] = Map() + ): EvalUnaryTestsResult = evalUnaryTests( expression = expression, context = Context.StaticContext(variables) ) - /** - * @deprecated Use [[org.camunda.feel.api.FeelEngineApi]] instead. + /** @deprecated + * Use [[org.camunda.feel.api.FeelEngineApi]] instead. */ - @deprecated def evalUnaryTests(expression: String, - context: Context): EvalUnaryTestsResult = { + @deprecated def evalUnaryTests(expression: String, context: Context): EvalUnaryTestsResult = { parseUnaryTests(expression) .flatMap(parsedExpression => eval(parsedExpression, context)) .map(value => value.asInstanceOf[Boolean]) } - /** - * @deprecated Use [[org.camunda.feel.api.FeelEngineApi]] instead. + /** @deprecated + * Use [[org.camunda.feel.api.FeelEngineApi]] instead. */ @deprecated def eval(exp: ParsedExpression, context: Context): EvalExpressionResult = evaluate(expression = exp, context = context).toEither - /** - * @deprecated Use [[org.camunda.feel.api.FeelEngineApi]] instead. + /** @deprecated + * Use [[org.camunda.feel.api.FeelEngineApi]] instead. */ - @deprecated def eval(exp: ParsedExpression, - variables: java.util.Map[String, Object]): EvalExpressionResult = + @deprecated def eval( + exp: ParsedExpression, + variables: java.util.Map[String, Object] + ): EvalExpressionResult = eval( exp = exp, context = Context.StaticContext(variables.asScala.toMap) ) - /** - * @deprecated Use [[org.camunda.feel.api.FeelEngineApi]] instead. + /** @deprecated + * Use [[org.camunda.feel.api.FeelEngineApi]] instead. */ - @deprecated def eval(parsedExpression: ParsedExpression, - variables: Map[String, Any] = Map()): EvalExpressionResult = + @deprecated def eval( + parsedExpression: ParsedExpression, + variables: Map[String, Any] = Map() + ): EvalExpressionResult = eval( exp = parsedExpression, context = Context.StaticContext(variables) ) - /** - * @deprecated Use [[org.camunda.feel.api.FeelEngineApi]] instead. + /** @deprecated + * Use [[org.camunda.feel.api.FeelEngineApi]] instead. */ @deprecated def parseExpression(expression: String): Either[Failure, ParsedExpression] = parse(FeelParser.parseExpression, expression) .flatMap(validate) - /** - * @deprecated Use [[org.camunda.feel.api.FeelEngineApi]] instead. + /** @deprecated + * Use [[org.camunda.feel.api.FeelEngineApi]] instead. */ @deprecated def parseUnaryTests(expression: String): Either[Failure, ParsedExpression] = parse(FeelParser.parseUnaryTests, expression) .flatMap(validate) - /** - * @deprecated Use [[org.camunda.feel.api.FeelEngineApi]] instead. + /** @deprecated + * Use [[org.camunda.feel.api.FeelEngineApi]] instead. */ @deprecated def evaluate(expression: ParsedExpression, context: Context): EvaluationResult = Try { validate(expression) match { - case Right(_) => eval(expression, rootContext().merge(context)) + case Right(_) => eval(expression, rootContext().merge(context)) case Left(failure) => FailedEvaluationResult(failure = failure) } }.recover(failure => FailedEvaluationResult( - failure = Failure(s"failed to evaluate expression '${expression.text}' : $failure"))) - .get + failure = Failure(s"failed to evaluate expression '${expression.text}' : $failure") + ) + ).get } diff --git a/src/main/scala/org/camunda/feel/FeelEngineClock.scala b/src/main/scala/org/camunda/feel/FeelEngineClock.scala index 1b1fcb805..01d7ae081 100644 --- a/src/main/scala/org/camunda/feel/FeelEngineClock.scala +++ b/src/main/scala/org/camunda/feel/FeelEngineClock.scala @@ -18,13 +18,12 @@ package org.camunda.feel import java.time.ZonedDateTime -/** - * The clock that is used by the engine to access the current time. +/** The clock that is used by the engine to access the current time. */ trait FeelEngineClock { - /** - * @return the current time of the clock + /** @return + * the current time of the clock */ def getCurrentTime: ZonedDateTime @@ -32,8 +31,7 @@ trait FeelEngineClock { object FeelEngineClock { - /** - * Access the current time from the system clock. + /** Access the current time from the system clock. */ object SystemClock extends FeelEngineClock { override def getCurrentTime: ZonedDateTime = ZonedDateTime.now() diff --git a/src/main/scala/org/camunda/feel/api/EvaluationFailure.scala b/src/main/scala/org/camunda/feel/api/EvaluationFailure.scala index 1a4723fa5..998befdea 100644 --- a/src/main/scala/org/camunda/feel/api/EvaluationFailure.scala +++ b/src/main/scala/org/camunda/feel/api/EvaluationFailure.scala @@ -16,16 +16,17 @@ */ package org.camunda.feel.api -/** - * A failure that occurred during the evaluation of an FEEL expression. +/** A failure that occurred during the evaluation of an FEEL expression. * - * @param failureType The type of failure. - * @param failureMessage The message that describes the failure. + * @param failureType + * The type of failure. + * @param failureMessage + * The message that describes the failure. */ case class EvaluationFailure( - failureType: EvaluationFailureType, - failureMessage: String - ) { + failureType: EvaluationFailureType, + failureMessage: String +) { override def toString: String = s"[$failureType] $failureMessage" } diff --git a/src/main/scala/org/camunda/feel/api/EvaluationFailureType.scala b/src/main/scala/org/camunda/feel/api/EvaluationFailureType.scala index d4be146bf..c42a3f73c 100644 --- a/src/main/scala/org/camunda/feel/api/EvaluationFailureType.scala +++ b/src/main/scala/org/camunda/feel/api/EvaluationFailureType.scala @@ -18,8 +18,7 @@ package org.camunda.feel.api sealed trait EvaluationFailureType -/** - * Defines the type of an evaluation failure. +/** Defines the type of an evaluation failure. */ object EvaluationFailureType { @@ -42,5 +41,3 @@ object EvaluationFailureType { case object ASSERT_FAILURE extends EvaluationFailureType } - - diff --git a/src/main/scala/org/camunda/feel/api/EvaluationResult.scala b/src/main/scala/org/camunda/feel/api/EvaluationResult.scala index cb5c8a131..704adf8c5 100644 --- a/src/main/scala/org/camunda/feel/api/EvaluationResult.scala +++ b/src/main/scala/org/camunda/feel/api/EvaluationResult.scala @@ -18,44 +18,36 @@ package org.camunda.feel.api import org.camunda.feel.FeelEngine.Failure -/** - * The result of an expression evaluation. +/** The result of an expression evaluation. */ sealed trait EvaluationResult { - - /** - * The result value of the evaluation. + + /** The result value of the evaluation. */ val result: Any - /** - * The cause if the evaluation failed. + /** The cause if the evaluation failed. */ val failure: Failure - /** - * Is true if the evaluation was successful. + /** Is true if the evaluation was successful. */ val isSuccess: Boolean - /** - * The suppressed failures that occurred during the evaluation. These failures doesn't result in + /** The suppressed failures that occurred during the evaluation. These failures doesn't result in * an evaluation failure but may indicate an unintended behavior. Use them for debugging purpose. */ val suppressedFailures: List[EvaluationFailure] - /** - * Is true if the evaluation failed. + /** Is true if the evaluation failed. */ def isFailure: Boolean = !isSuccess - /** - * Is true if the evaluation has suppressed failures. + /** Is true if the evaluation has suppressed failures. */ def hasSuppressedFailures: Boolean = suppressedFailures.nonEmpty - /** - * Returns the evaluation result as an Either type. If the evaluation was successful, it returns + /** Returns the evaluation result as an Either type. If the evaluation was successful, it returns * the result as Right. Otherwise, it returns the failure as Left. */ def toEither: Either[Failure, Any] = @@ -65,16 +57,17 @@ sealed trait EvaluationResult { } case class SuccessfulEvaluationResult( - result: Any, - suppressedFailures: List[EvaluationFailure] = List.empty) extends EvaluationResult { + result: Any, + suppressedFailures: List[EvaluationFailure] = List.empty +) extends EvaluationResult { override val isSuccess: Boolean = true - override val failure: Failure = Failure("") + override val failure: Failure = Failure("") } case class FailedEvaluationResult( - failure: Failure, - suppressedFailures: List[EvaluationFailure] = List.empty - ) extends EvaluationResult { + failure: Failure, + suppressedFailures: List[EvaluationFailure] = List.empty +) extends EvaluationResult { override val isSuccess: Boolean = false - override val result: Any = failure + override val result: Any = failure } diff --git a/src/main/scala/org/camunda/feel/api/FeelEngineApi.scala b/src/main/scala/org/camunda/feel/api/FeelEngineApi.scala index bcc2fc82d..301e0d5af 100644 --- a/src/main/scala/org/camunda/feel/api/FeelEngineApi.scala +++ b/src/main/scala/org/camunda/feel/api/FeelEngineApi.scala @@ -23,53 +23,59 @@ import org.camunda.feel.syntaxtree.ParsedExpression import scala.jdk.CollectionConverters.MapHasAsScala -/** - * The API to interact with the FEEL engine. +/** The API to interact with the FEEL engine. * - * @param engine the FEEL engine to interact with + * @param engine + * the FEEL engine to interact with */ class FeelEngineApi(engine: FeelEngine) { // =========== parse expression =========== - /** - * Parses an FEEL expression. + /** Parses an FEEL expression. * - * @param expression the expression to parse - * @return the result of the parsing as [[ParseResult]] + * @param expression + * the expression to parse + * @return + * the result of the parsing as [[ParseResult]] */ def parseExpression(expression: String): ParseResult = engine.parseExpression(expression = expression) match { case Right(parsedExpression) => SuccessfulParseResult(parsedExpression = parsedExpression) - case Left(failure) => FailedParseResult( - expression = expression, - failure = failure - ) + case Left(failure) => + FailedParseResult( + expression = expression, + failure = failure + ) } - /** - * Parses an FEEL unary-tests expression. + /** Parses an FEEL unary-tests expression. * - * @param expression the expression to parse - * @return the result of the parsing as [[ParseResult]] + * @param expression + * the expression to parse + * @return + * the result of the parsing as [[ParseResult]] */ def parseUnaryTests(expression: String): ParseResult = engine.parseUnaryTests(expression = expression) match { case Right(parsedExpression) => SuccessfulParseResult(parsedExpression = parsedExpression) - case Left(failure) => FailedParseResult( - expression = expression, - failure = failure - ) + case Left(failure) => + FailedParseResult( + expression = expression, + failure = failure + ) } // =========== evaluate parsed expression =========== - /** - * Evaluates a parsed expression with the given context. + /** Evaluates a parsed expression with the given context. * - * @param expression the parsed expression to evaluate - * @param context the context for the evaluation - * @return the result of the evaluation as [[EvaluationResult]] + * @param expression + * the parsed expression to evaluate + * @param context + * the context for the evaluation + * @return + * the result of the evaluation as [[EvaluationResult]] */ def evaluate(expression: ParsedExpression, context: Context): EvaluationResult = engine.evaluate( @@ -77,70 +83,101 @@ class FeelEngineApi(engine: FeelEngine) { context = context ) - /** - * Evaluates a parsed expression with the given context. + /** Evaluates a parsed expression with the given context. * - * @param expression the parsed expression to evaluate - * @param variables the variable context for the evaluation - * @return the result of the evaluation as [[EvaluationResult]] + * @param expression + * the parsed expression to evaluate + * @param variables + * the variable context for the evaluation + * @return + * the result of the evaluation as [[EvaluationResult]] */ - def evaluate(expression: ParsedExpression, variables: Map[String, Any] = Map()): EvaluationResult = + def evaluate( + expression: ParsedExpression, + variables: Map[String, Any] = Map() + ): EvaluationResult = evaluate( expression = expression, context = Context.StaticContext(variables = variables) ) - /** - * Evaluates a parsed expression with the given context. + /** Evaluates a parsed expression with the given context. * - * @param expression the parsed expression to evaluate - * @param variables the variable context for the evaluation. - * @return the result of the evaluation as [[EvaluationResult]] + * @param expression + * the parsed expression to evaluate + * @param variables + * the variable context for the evaluation. + * @return + * the result of the evaluation as [[EvaluationResult]] */ - def evaluate(expression: ParsedExpression, variables: java.util.Map[String, Object]): EvaluationResult = + def evaluate( + expression: ParsedExpression, + variables: java.util.Map[String, Object] + ): EvaluationResult = evaluate( expression = expression, context = Context.StaticContext(variables = variables.asScala.toMap) ) - /** - * Evaluates a parsed unary-tests expression with the given input value and context. + /** Evaluates a parsed unary-tests expression with the given input value and context. * - * @param expression the parsed expression to evaluate - * @param inputValue the input value for the evaluation of the unary-tests - * @param context the context for the evaluation - * @return the result of the evaluation as [[EvaluationResult]] + * @param expression + * the parsed expression to evaluate + * @param inputValue + * the input value for the evaluation of the unary-tests + * @param context + * the context for the evaluation + * @return + * the result of the evaluation as [[EvaluationResult]] */ - def evaluateWithInput(expression: ParsedExpression, inputValue: Any, context: Context): EvaluationResult = + def evaluateWithInput( + expression: ParsedExpression, + inputValue: Any, + context: Context + ): EvaluationResult = engine.evaluate( expression = expression, context = createContextWithInput(context, inputValue) ) - /** - * Evaluates a parsed unary-tests expression with the given input value and context. + /** Evaluates a parsed unary-tests expression with the given input value and context. * - * @param expression the parsed expression to evaluate - * @param inputValue the input value for the evaluation of the unary-tests - * @param variables the variable context for the evaluation - * @return the result of the evaluation as [[EvaluationResult]] + * @param expression + * the parsed expression to evaluate + * @param inputValue + * the input value for the evaluation of the unary-tests + * @param variables + * the variable context for the evaluation + * @return + * the result of the evaluation as [[EvaluationResult]] */ - def evaluateWithInput(expression: ParsedExpression, inputValue: Any, variables: Map[String, Any] = Map()): EvaluationResult = + def evaluateWithInput( + expression: ParsedExpression, + inputValue: Any, + variables: Map[String, Any] = Map() + ): EvaluationResult = evaluateWithInput( expression = expression, inputValue = inputValue, context = Context.StaticContext(variables = variables) ) - /** - * Evaluates a parsed unary-tests expression with the given input value and context. + /** Evaluates a parsed unary-tests expression with the given input value and context. * - * @param expression the parsed expression to evaluate - * @param inputValue the input value for the evaluation of the unary-tests - * @param variables the variable context for the evaluation - * @return the result of the evaluation as [[EvaluationResult]] + * @param expression + * the parsed expression to evaluate + * @param inputValue + * the input value for the evaluation of the unary-tests + * @param variables + * the variable context for the evaluation + * @return + * the result of the evaluation as [[EvaluationResult]] */ - def evaluateWithInput(expression: ParsedExpression, inputValue: Any, variables: java.util.Map[String, Object]): EvaluationResult = + def evaluateWithInput( + expression: ParsedExpression, + inputValue: Any, + variables: java.util.Map[String, Object] + ): EvaluationResult = evaluateWithInput( expression = expression, inputValue = inputValue, @@ -149,48 +186,62 @@ class FeelEngineApi(engine: FeelEngine) { // =========== parse and evaluate expression =========== - /** - * Evaluates an FEEL expression with the given context. This is a shortcut and skips the explicit + /** Evaluates an FEEL expression with the given context. This is a shortcut and skips the explicit * parsing step before the evaluation. * - * @param expression the expression to evaluate - * @param context the context for the evaluation - * @return the result of the evaluation as [[EvaluationResult]] + * @param expression + * the expression to evaluate + * @param context + * the context for the evaluation + * @return + * the result of the evaluation as [[EvaluationResult]] */ def evaluateExpression(expression: String, context: Context): EvaluationResult = engine.parseExpression(expression = expression) match { - case Right(parsedExpression) => evaluate( - expression = parsedExpression, - context = context - ) - case Left(parseFailure) => FailedEvaluationResult( - failure = parseFailure - ) + case Right(parsedExpression) => + evaluate( + expression = parsedExpression, + context = context + ) + case Left(parseFailure) => + FailedEvaluationResult( + failure = parseFailure + ) } - /** - * Evaluates an FEEL expression with the given context. This is a shortcut and skips the explicit + /** Evaluates an FEEL expression with the given context. This is a shortcut and skips the explicit * parsing step before the evaluation. * - * @param expression the expression to evaluate - * @param variables the variable context for the evaluation - * @return the result of the evaluation as [[EvaluationResult]] + * @param expression + * the expression to evaluate + * @param variables + * the variable context for the evaluation + * @return + * the result of the evaluation as [[EvaluationResult]] */ - def evaluateExpression(expression: String, variables: Map[String, Any] = Map()): EvaluationResult = + def evaluateExpression( + expression: String, + variables: Map[String, Any] = Map() + ): EvaluationResult = evaluateExpression( expression = expression, context = Context.StaticContext(variables = variables) ) - /** - * Evaluates an FEEL expression with the given context. This is a shortcut and skips the explicit + /** Evaluates an FEEL expression with the given context. This is a shortcut and skips the explicit * parsing step before the evaluation. * - * @param expression the expression to evaluate - * @param variables the variable context for the evaluation - * @return the result of the evaluation as [[EvaluationResult]] + * @param expression + * the expression to evaluate + * @param variables + * the variable context for the evaluation + * @return + * the result of the evaluation as [[EvaluationResult]] */ - def evaluateExpression(expression: String, variables: java.util.Map[String, Object]): EvaluationResult = + def evaluateExpression( + expression: String, + variables: java.util.Map[String, Object] + ): EvaluationResult = evaluateExpression( expression = expression, context = Context.StaticContext(variables = variables.asScala.toMap) @@ -198,52 +249,71 @@ class FeelEngineApi(engine: FeelEngine) { // =========== parse and evaluate unary-tests =========== - /** - * Evaluates an FEEL unary-tests expression with the given input value and context. This is a + /** Evaluates an FEEL unary-tests expression with the given input value and context. This is a * shortcut and skips the explicit parsing step before the evaluation. * - * @param expression the expression to evaluate - * @param inputValue the input value for the evaluation of the unary-tests - * @param context the context for the evaluation - * @return the result of the evaluation as [[EvaluationResult]] + * @param expression + * the expression to evaluate + * @param inputValue + * the input value for the evaluation of the unary-tests + * @param context + * the context for the evaluation + * @return + * the result of the evaluation as [[EvaluationResult]] */ def evaluateUnaryTests(expression: String, inputValue: Any, context: Context): EvaluationResult = engine.parseUnaryTests(expression = expression) match { - case Right(parsedExpression) => evaluate( - expression = parsedExpression, - context = createContextWithInput(context, inputValue) - ) - case Left(parseFailure) => FailedEvaluationResult( - failure = parseFailure - ) + case Right(parsedExpression) => + evaluate( + expression = parsedExpression, + context = createContextWithInput(context, inputValue) + ) + case Left(parseFailure) => + FailedEvaluationResult( + failure = parseFailure + ) } - /** - * Evaluates an FEEL unary-tests expression with the given input value and context. This is a + /** Evaluates an FEEL unary-tests expression with the given input value and context. This is a * shortcut and skips the explicit parsing step before the evaluation. * - * @param expression the expression to evaluate - * @param inputValue the input value for the evaluation of the unary-tests - * @param variables the variable context for the evaluation - * @return the result of the evaluation as [[EvaluationResult]] + * @param expression + * the expression to evaluate + * @param inputValue + * the input value for the evaluation of the unary-tests + * @param variables + * the variable context for the evaluation + * @return + * the result of the evaluation as [[EvaluationResult]] */ - def evaluateUnaryTests(expression: String, inputValue: Any, variables: Map[String, Any] = Map()): EvaluationResult = + def evaluateUnaryTests( + expression: String, + inputValue: Any, + variables: Map[String, Any] = Map() + ): EvaluationResult = evaluateUnaryTests( expression = expression, inputValue = inputValue, context = Context.StaticContext(variables = variables) ) - /** - * Evaluates an FEEL unary-tests expression with the given input value and context. This is a + /** Evaluates an FEEL unary-tests expression with the given input value and context. This is a * shortcut and skips the explicit parsing step before the evaluation. * - * @param expression the expression to evaluate - * @param inputValue the input value for the evaluation of the unary-tests - * @param variables the variable context for the evaluation - * @return the result of the evaluation as [[EvaluationResult]] + * @param expression + * the expression to evaluate + * @param inputValue + * the input value for the evaluation of the unary-tests + * @param variables + * the variable context for the evaluation + * @return + * the result of the evaluation as [[EvaluationResult]] */ - def evaluateUnaryTests(expression: String, inputValue: Object, variables: java.util.Map[String, Object]): EvaluationResult = + def evaluateUnaryTests( + expression: String, + inputValue: Object, + variables: java.util.Map[String, Object] + ): EvaluationResult = evaluateUnaryTests( expression = expression, inputValue = inputValue, @@ -253,7 +323,7 @@ class FeelEngineApi(engine: FeelEngine) { // =========== internal helpers =========== private def createContextWithInput(context: Context, inputValue: Any): EvalContext = { - val inputVariable = FeelEngine.UnaryTests.defaultInputVariable + val inputVariable = FeelEngine.UnaryTests.defaultInputVariable val wrappedInputValue = engine.valueMapper.toVal(inputValue) EvalContext.wrap(context, engine.valueMapper).add(inputVariable -> wrappedInputValue) diff --git a/src/main/scala/org/camunda/feel/api/FeelEngineBuilder.scala b/src/main/scala/org/camunda/feel/api/FeelEngineBuilder.scala index f7971d835..d864f3c4b 100644 --- a/src/main/scala/org/camunda/feel/api/FeelEngineBuilder.scala +++ b/src/main/scala/org/camunda/feel/api/FeelEngineBuilder.scala @@ -22,53 +22,47 @@ import org.camunda.feel.context.FunctionProvider import org.camunda.feel.valuemapper.{CustomValueMapper, ValueMapper} import org.camunda.feel.valuemapper.ValueMapper.CompositeValueMapper -/** - * Builds a new instance of the FEEL engine. Use the setters to customize the engine. +/** Builds a new instance of the FEEL engine. Use the setters to customize the engine. */ -case class FeelEngineBuilder private( - functionProvider: FunctionProvider = defaultFunctionProvider, - valueMapper: ValueMapper = FeelEngine.defaultValueMapper, - customValueMappers: List[CustomValueMapper] = List.empty, - clock: FeelEngineClock = FeelEngine.defaultClock, - configuration: Configuration = FeelEngine.defaultConfiguration - ) { +case class FeelEngineBuilder private ( + functionProvider: FunctionProvider = defaultFunctionProvider, + valueMapper: ValueMapper = FeelEngine.defaultValueMapper, + customValueMappers: List[CustomValueMapper] = List.empty, + clock: FeelEngineClock = FeelEngine.defaultClock, + configuration: Configuration = FeelEngine.defaultConfiguration +) { - /** - * Sets the given [[FunctionProvider]] for the engine. + /** Sets the given [[FunctionProvider]] for the engine. */ def withFunctionProvider(functionProvider: FunctionProvider): FeelEngineBuilder = copy(functionProvider = functionProvider) - /** - * Sets the given [[CustomValueMapper]] for the engine. Can be called multiple times to chain the + /** Sets the given [[CustomValueMapper]] for the engine. Can be called multiple times to chain the * value mappers. */ def withCustomValueMapper(customValueMapper: CustomValueMapper): FeelEngineBuilder = copy(valueMapper = CompositeValueMapper(customValueMapper :: customValueMappers)) - /** - * Sets the given [[ValueMapper]] for the engine. Overwrites the [[CustomValueMapper]]s that were set before. + /** Sets the given [[ValueMapper]] for the engine. Overwrites the [[CustomValueMapper]]s that were + * set before. */ def withValueMapper(valueMapper: ValueMapper): FeelEngineBuilder = copy(valueMapper = valueMapper) - /** - * Sets the given [[FeelEngineClock]] for the engine. + /** Sets the given [[FeelEngineClock]] for the engine. */ def withClock(clock: FeelEngineClock): FeelEngineBuilder = copy(clock = clock) - /** - * Enables/disables external FEEL functions for the engine. + /** Enables/disables external FEEL functions for the engine. */ def withEnabledExternalFunctions(enabled: Boolean): FeelEngineBuilder = copy(configuration = configuration.copy(externalFunctionsEnabled = enabled)) - - /** - * Creates a new engine with the given configuration. + /** Creates a new engine with the given configuration. * - * @return the API to access the engine. + * @return + * the API to access the engine. */ def build(): FeelEngineApi = new FeelEngineApi( engine = new FeelEngine( diff --git a/src/main/scala/org/camunda/feel/api/ParseResult.scala b/src/main/scala/org/camunda/feel/api/ParseResult.scala index ac63d6c1a..0d9cd2a0b 100644 --- a/src/main/scala/org/camunda/feel/api/ParseResult.scala +++ b/src/main/scala/org/camunda/feel/api/ParseResult.scala @@ -19,34 +19,28 @@ package org.camunda.feel.api import org.camunda.feel.FeelEngine.Failure import org.camunda.feel.syntaxtree.{ConstNull, ParsedExpression} -/** - * The result of an expression parsing. +/** The result of an expression parsing. */ sealed trait ParseResult { - /** - * The parsed expression if the parsing was successful. + /** The parsed expression if the parsing was successful. */ val parsedExpression: ParsedExpression - /** - * The cause if the parsing failed. + /** The cause if the parsing failed. */ val failure: Failure - /** - * Is true if the parsing was successful. + /** Is true if the parsing was successful. */ val isSuccess: Boolean - /** - * Is true if the parsing failed. + /** Is true if the parsing failed. */ def isFailure: Boolean = !isSuccess - /** - * Returns the parsing result as an Either type. If the parsing was successful, it returns - * the result as Right. Otherwise, it returns the failure as Left. + /** Returns the parsing result as an Either type. If the parsing was successful, it returns the + * result as Right. Otherwise, it returns the failure as Left. */ def toEither: Either[Failure, ParsedExpression] = if (isSuccess) Right(parsedExpression) @@ -56,10 +50,10 @@ sealed trait ParseResult { case class SuccessfulParseResult(parsedExpression: ParsedExpression) extends ParseResult { override val isSuccess: Boolean = true - override val failure: Failure = Failure("") + override val failure: Failure = Failure("") } case class FailedParseResult(expression: String, failure: Failure) extends ParseResult { - override val isSuccess: Boolean = false + override val isSuccess: Boolean = false override val parsedExpression: ParsedExpression = ParsedExpression(ConstNull, expression) } diff --git a/src/main/scala/org/camunda/feel/context/Context.scala b/src/main/scala/org/camunda/feel/context/Context.scala index 31a216a01..37c7e52c6 100644 --- a/src/main/scala/org/camunda/feel/context/Context.scala +++ b/src/main/scala/org/camunda/feel/context/Context.scala @@ -18,8 +18,8 @@ package org.camunda.feel.context import org.camunda.feel.syntaxtree.ValFunction -/** - * A Context provides access to the variables/fields and functions/methods in the scope represented by this Context. +/** A Context provides access to the variables/fields and functions/methods in the scope represented + * by this Context. */ trait Context { diff --git a/src/main/scala/org/camunda/feel/context/CustomContext.scala b/src/main/scala/org/camunda/feel/context/CustomContext.scala index 94e622d9f..318f5a691 100644 --- a/src/main/scala/org/camunda/feel/context/CustomContext.scala +++ b/src/main/scala/org/camunda/feel/context/CustomContext.scala @@ -16,10 +16,9 @@ */ package org.camunda.feel.context -/** - * Override this class if you want to implement a custom Context. - * Call the corresponding super method to handle the default/error case. - * Typically you will just override one of the dynamic providers (variableProvider, functionProvider) +/** Override this class if you want to implement a custom Context. Call the corresponding super + * method to handle the default/error case. Typically you will just override one of the dynamic + * providers (variableProvider, functionProvider) */ abstract class CustomContext extends Context { diff --git a/src/main/scala/org/camunda/feel/context/CustomFunctionProvider.scala b/src/main/scala/org/camunda/feel/context/CustomFunctionProvider.scala index 27733698d..8f7b84d9a 100644 --- a/src/main/scala/org/camunda/feel/context/CustomFunctionProvider.scala +++ b/src/main/scala/org/camunda/feel/context/CustomFunctionProvider.scala @@ -18,27 +18,29 @@ package org.camunda.feel.context import org.camunda.feel.syntaxtree.ValFunction -/** - * Provides one or more functions which can be used in an expression. +/** Provides one or more functions which can be used in an expression. */ trait CustomFunctionProvider extends FunctionProvider { - /** - * Returns a list of functions for the given name. There can be multiple functions with different parameters. + /** Returns a list of functions for the given name. There can be multiple functions with different + * parameters. * - * @param name the name of the function - * @return a list of functions or an empty list, if no function is provided for this name + * @param name + * the name of the function + * @return + * a list of functions or an empty list, if no function is provided for this name */ override def getFunctions(name: String): List[ValFunction] = getFunction(name) .map(List(_)) .getOrElse(List.empty) - /** - * Returns the function for the given name. + /** Returns the function for the given name. * - * @param name the name of the function - * @return the function or [[None]], if no function is provided for this name + * @param name + * the name of the function + * @return + * the function or [[None]], if no function is provided for this name */ def getFunction(name: String): Option[ValFunction] diff --git a/src/main/scala/org/camunda/feel/context/FunctionProvider.scala b/src/main/scala/org/camunda/feel/context/FunctionProvider.scala index bc871d5f1..6c04dca10 100644 --- a/src/main/scala/org/camunda/feel/context/FunctionProvider.scala +++ b/src/main/scala/org/camunda/feel/context/FunctionProvider.scala @@ -51,8 +51,7 @@ object FunctionProvider { override def functionNames: Iterable[String] = functions.keys } - case class CacheFunctionProvider(provider: FunctionProvider) - extends FunctionProvider { + case class CacheFunctionProvider(provider: FunctionProvider) extends FunctionProvider { private val cache: mutable.Map[String, List[ValFunction]] = mutable.Map.empty @@ -64,12 +63,12 @@ object FunctionProvider { cache.keys ++ provider.functionNames } - case class CompositeFunctionProvider(providers: List[FunctionProvider]) - extends FunctionProvider { + case class CompositeFunctionProvider(providers: List[FunctionProvider]) extends FunctionProvider { override def getFunctions(name: String): List[ValFunction] = providers.foldLeft(List[ValFunction]())((functions, provider) => - functions ++ provider.getFunctions(name)) + functions ++ provider.getFunctions(name) + ) override def functionNames: Iterable[String] = providers.flatMap(_.functionNames) diff --git a/src/main/scala/org/camunda/feel/context/JavaFunctionProvider.scala b/src/main/scala/org/camunda/feel/context/JavaFunctionProvider.scala index a1de819b3..c706f3f54 100644 --- a/src/main/scala/org/camunda/feel/context/JavaFunctionProvider.scala +++ b/src/main/scala/org/camunda/feel/context/JavaFunctionProvider.scala @@ -18,37 +18,35 @@ package org.camunda.feel.context import java.util.{Collections, Optional} import org.camunda.feel.syntaxtree.{Val, ValFunction} -import scala.jdk.CollectionConverters.{ - CollectionHasAsScala, - ListHasAsScala, - SeqHasAsJava -} +import scala.jdk.CollectionConverters.{CollectionHasAsScala, ListHasAsScala, SeqHasAsJava} -/** - * Provides one or more functions which can be used in an expression. +/** Provides one or more functions which can be used in an expression. */ abstract class JavaFunctionProvider extends CustomFunctionProvider { - /** - * Returns the function for the given name. + /** Returns the function for the given name. * - * @param functionName the name of the function - * @return the function or [[Optional.empty()]], if no function is provided for this name + * @param functionName + * the name of the function + * @return + * the function or [[Optional.empty()]], if no function is provided for this name */ def resolveFunction(functionName: String): Optional[JavaFunction] - /** - * Returns the names of all functions. + /** Returns the names of all functions. * - * @return the names of all functions + * @return + * the names of all functions */ def getFunctionNames(): java.util.Collection[String] - /** - * Returns a list of functions for the given name. There can be multiple functions with different parameters. + /** Returns a list of functions for the given name. There can be multiple functions with different + * parameters. * - * @param functionName the name of the function - * @return a list of functions or an empty list, if no function is provided for this name + * @param functionName + * the name of the function + * @return + * a list of functions or an empty list, if no function is provided for this name */ def resolveFunctions(functionName: String): java.util.List[JavaFunction] = { val function = resolveFunction(functionName) diff --git a/src/main/scala/org/camunda/feel/context/VariableProvider.scala b/src/main/scala/org/camunda/feel/context/VariableProvider.scala index 1e45312e0..49a7cb041 100644 --- a/src/main/scala/org/camunda/feel/context/VariableProvider.scala +++ b/src/main/scala/org/camunda/feel/context/VariableProvider.scala @@ -42,8 +42,7 @@ object VariableProvider { override def keys: Iterable[String] = List.empty } - case class StaticVariableProvider(variables: Map[String, Any]) - extends VariableProvider { + case class StaticVariableProvider(variables: Map[String, Any]) extends VariableProvider { override def getVariable(name: String): Option[Any] = variables.get(name) @@ -51,15 +50,14 @@ object VariableProvider { } - case class CacheVariableProvider(provider: VariableProvider) - extends VariableProvider { + case class CacheVariableProvider(provider: VariableProvider) extends VariableProvider { private val cache: mutable.Map[String, Any] = mutable.Map.empty override def getVariable(name: String): Option[Any] = cache.get(name) match { case Some(value) => Some(value) - case None => + case None => provider.getVariable(name) match { case Some(value) => cache.put(name, value); Some(value) case None => None @@ -70,8 +68,7 @@ object VariableProvider { } - case class CompositeVariableProvider(providers: List[VariableProvider]) - extends VariableProvider { + case class CompositeVariableProvider(providers: List[VariableProvider]) extends VariableProvider { override def getVariable(name: String): Option[Any] = { for (provider <- providers) { diff --git a/src/main/scala/org/camunda/feel/impl/DefaultValueMapper.scala b/src/main/scala/org/camunda/feel/impl/DefaultValueMapper.scala index 87c471b0d..2598adc19 100644 --- a/src/main/scala/org/camunda/feel/impl/DefaultValueMapper.scala +++ b/src/main/scala/org/camunda/feel/impl/DefaultValueMapper.scala @@ -79,30 +79,31 @@ class DefaultValueMapper extends CustomValueMapper { case x: YearMonthDuration => Some(ValYearMonthDuration(x)) case x: DayTimeDuration => Some(ValDayTimeDuration(x)) case x: List[_] => Some(ValList(x map innerValueMapper)) - case x: Map[_, _] => + case x: Map[_, _] => Some { val (functions, variables) = x - .map { - case (key, value) => key.toString -> innerValueMapper(value) + .map { case (key, value) => + key.toString -> innerValueMapper(value) } .partition { case (key, value) => value.isInstanceOf[ValFunction] } ValContext( Context.StaticContext( variables = variables, - functions = functions.map { - case (key, f) => key -> List(f.asInstanceOf[ValFunction]) + functions = functions.map { case (key, f) => + key -> List(f.asInstanceOf[ValFunction]) } - )) + ) + ) } - case Some(x) => Some(innerValueMapper(x)) - case None => Some(ValNull) - case x if (isEnumeration(x)) => Some(ValString(x.toString)) + case Some(x) => Some(innerValueMapper(x)) + case None => Some(ValNull) + case x if (isEnumeration(x)) => Some(ValString(x.toString)) // extended java types - case x: java.math.BigDecimal => Some(ValNumber(x)) - case x: java.math.BigInteger => Some(ValNumber(BigDecimal(x))) - case x: java.util.Date => + case x: java.math.BigDecimal => Some(ValNumber(x)) + case x: java.math.BigInteger => Some(ValNumber(BigDecimal(x))) + case x: java.util.Date => Some( ValLocalDateTime( LocalDateTime.ofInstant(x.toInstant, ZoneId.systemDefault()) @@ -112,25 +113,25 @@ class DefaultValueMapper extends CustomValueMapper { Some( ValDateTime(x.toZonedDateTime()) ) - case x: java.time.OffsetTime => + case x: java.time.OffsetTime => Some( ValTime(ZonedTime.of(x)) ) - case x: java.util.List[_] => + case x: java.util.List[_] => Some( ValList(x.asScala.toList map innerValueMapper) ) - case x: java.util.Map[_, _] => + case x: java.util.Map[_, _] => Some( - ValContext(Context.StaticContext(x.asScala.map { - case (key, value) => key.toString -> innerValueMapper(value) + ValContext(Context.StaticContext(x.asScala.map { case (key, value) => + key.toString -> innerValueMapper(value) }.toMap)) ) - case x: java.lang.Enum[_] => Some(ValString(x.name)) + case x: java.lang.Enum[_] => Some(ValString(x.name)) // other objects case x: Throwable => Some(ValError(x.getMessage)) - case x => + case x => try { Some( ValContext(Context.CacheContext(ObjectContext(x))) @@ -159,14 +160,13 @@ class DefaultValueMapper extends CustomValueMapper { case ValYearMonthDuration(duration) => Some(duration) case ValDayTimeDuration(duration) => Some(duration) case ValList(list) => Some(list map innerValueMapper) - case ValContext(c: Context) => + case ValContext(c: Context) => Some( - c.variableProvider.getVariables.map { - case (key, value) => - value match { - case packed: Val => key -> innerValueMapper(packed) - case unpacked => key -> unpacked - } + c.variableProvider.getVariables.map { case (key, value) => + value match { + case packed: Val => key -> innerValueMapper(packed) + case unpacked => key -> unpacked + } }.toMap ) diff --git a/src/main/scala/org/camunda/feel/impl/JavaValueMapper.scala b/src/main/scala/org/camunda/feel/impl/JavaValueMapper.scala index 8a4b35752..a05796538 100644 --- a/src/main/scala/org/camunda/feel/impl/JavaValueMapper.scala +++ b/src/main/scala/org/camunda/feel/impl/JavaValueMapper.scala @@ -22,13 +22,11 @@ import org.camunda.feel.valuemapper.CustomValueMapper import scala.jdk.CollectionConverters.{MapHasAsJava, SeqHasAsJava} -/** - * Transform FEEL types into common Java objects. This includes numbers, lists and contexts. +/** Transform FEEL types into common Java objects. This includes numbers, lists and contexts. */ class JavaValueMapper extends CustomValueMapper { - override def unpackVal(value: Val, - innerValueMapper: Val => Any): Option[Any] = + override def unpackVal(value: Val, innerValueMapper: Val => Any): Option[Any] = value match { case ValNumber(number) => @@ -48,12 +46,11 @@ class JavaValueMapper extends CustomValueMapper { case ValContext(context: Context) => Some( context.variableProvider.getVariables - .map { - case (key, value) => - value match { - case packed: Val => key -> innerValueMapper(packed) - case unpacked => key -> unpacked - } + .map { case (key, value) => + value match { + case packed: Val => key -> innerValueMapper(packed) + case unpacked => key -> unpacked + } } .toMap .asJava: java.util.Map[String, Any] diff --git a/src/main/scala/org/camunda/feel/impl/SpiServiceLoader.scala b/src/main/scala/org/camunda/feel/impl/SpiServiceLoader.scala index 9e4a063c6..ecadcae49 100644 --- a/src/main/scala/org/camunda/feel/impl/SpiServiceLoader.scala +++ b/src/main/scala/org/camunda/feel/impl/SpiServiceLoader.scala @@ -51,7 +51,8 @@ object SpiServiceLoader { } catch { case t: Throwable => System.err.println( - s"Failed to load service provider: ${classTag[T].runtimeClass.getSimpleName}") + s"Failed to load service provider: ${classTag[T].runtimeClass.getSimpleName}" + ) t.printStackTrace() throw t } diff --git a/src/main/scala/org/camunda/feel/impl/builtin/BooleanBuiltinFunctions.scala b/src/main/scala/org/camunda/feel/impl/builtin/BooleanBuiltinFunctions.scala index 0d3af6ef5..e6d49a8c5 100644 --- a/src/main/scala/org/camunda/feel/impl/builtin/BooleanBuiltinFunctions.scala +++ b/src/main/scala/org/camunda/feel/impl/builtin/BooleanBuiltinFunctions.scala @@ -22,24 +22,27 @@ import org.camunda.feel.syntaxtree.{Val, ValBoolean, ValError, ValNull, ValStrin object BooleanBuiltinFunctions { def functions = Map( - "not" -> List(notFunction), - "is defined" -> List(isDefinedFunction), + "not" -> List(notFunction), + "is defined" -> List(isDefinedFunction), "get or else" -> List(getOrElse), - "assert" -> List(assertFunction, assertFunction2) + "assert" -> List(assertFunction, assertFunction2) ) private def notFunction = - builtinFunction(params = List("negand"), invoke = { - case List(ValBoolean(negand)) => ValBoolean(!negand) - case List(_: Val) => ValNull - case _ => ValNull - }) + builtinFunction( + params = List("negand"), + invoke = { + case List(ValBoolean(negand)) => ValBoolean(!negand) + case List(_: Val) => ValNull + case _ => ValNull + } + ) private def isDefinedFunction = builtinFunction( params = List("value"), invoke = { - case List(ValNull) => ValBoolean(false) - case _ => ValBoolean(true) + case List(ValNull) => ValBoolean(false) + case _ => ValBoolean(true) } ) @@ -55,7 +58,7 @@ object BooleanBuiltinFunctions { params = List("value", "condition"), invoke = { case List(value, ValBoolean(true)) => value - case _ => ValError("The condition is not fulfilled") + case _ => ValError("The condition is not fulfilled") } ) @@ -63,7 +66,7 @@ object BooleanBuiltinFunctions { params = List("value", "condition", "cause"), invoke = { case List(value, ValBoolean(true), _) => value - case List(_, _, ValString(cause)) => ValError(cause) + case List(_, _, ValString(cause)) => ValError(cause) } ) diff --git a/src/main/scala/org/camunda/feel/impl/builtin/BuiltinFunction.scala b/src/main/scala/org/camunda/feel/impl/builtin/BuiltinFunction.scala index 6dc9c9f80..ad79bc964 100644 --- a/src/main/scala/org/camunda/feel/impl/builtin/BuiltinFunction.scala +++ b/src/main/scala/org/camunda/feel/impl/builtin/BuiltinFunction.scala @@ -21,9 +21,11 @@ import org.camunda.feel.syntaxtree.{Val, ValError, ValFunction, ValNull} object BuiltinFunction { - def builtinFunction(params: List[String], - invoke: PartialFunction[List[Val], Any], - hasVarArgs: Boolean = false): ValFunction = { + def builtinFunction( + params: List[String], + invoke: PartialFunction[List[Val], Any], + hasVarArgs: Boolean = false + ): ValFunction = { ValFunction( params = params, invoke = invoke.orElse(error), @@ -34,7 +36,7 @@ object BuiltinFunction { private def error: PartialFunction[List[Val], Any] = { case args if (args.exists(_.isInstanceOf[ValError])) => args.filter(_.isInstanceOf[ValError]).head.asInstanceOf[ValError] - case args => + case args => val argumentList = args.map("'" + _ + "'").mkString(", ") ValError(s"Illegal arguments: $argumentList") } diff --git a/src/main/scala/org/camunda/feel/impl/builtin/ContextBuiltinFunctions.scala b/src/main/scala/org/camunda/feel/impl/builtin/ContextBuiltinFunctions.scala index 94480767b..8fb68233e 100644 --- a/src/main/scala/org/camunda/feel/impl/builtin/ContextBuiltinFunctions.scala +++ b/src/main/scala/org/camunda/feel/impl/builtin/ContextBuiltinFunctions.scala @@ -27,25 +27,25 @@ import scala.annotation.tailrec class ContextBuiltinFunctions(valueMapper: ValueMapper) { def functions = Map( - "get entries" -> List(getEntriesFunction("context"), - getEntriesFunction("m")), - "get value" -> List(getValueFunction(List("m", "key")), - getValueFunction(List("context", "key")), getValueFunction2), - "context put" -> List(contextPutFunction, contextPutFunction2), - "put" -> List(contextPutFunction), // deprecated function name + "get entries" -> List(getEntriesFunction("context"), getEntriesFunction("m")), + "get value" -> List( + getValueFunction(List("m", "key")), + getValueFunction(List("context", "key")), + getValueFunction2 + ), + "context put" -> List(contextPutFunction, contextPutFunction2), + "put" -> List(contextPutFunction), // deprecated function name "context merge" -> List(contextMergeFunction), - "put all" -> List(contextMergeFunction), // deprecated function name - "context" -> List(contextFunction) + "put all" -> List(contextMergeFunction), // deprecated function name + "context" -> List(contextFunction) ) private def getEntriesFunction(paramName: String) = builtinFunction( params = List(paramName), - invoke = { - case List(ValContext(c: Context)) => - c.variableProvider.getVariables.map { - case (key, value) => - Map("key" -> ValString(key), "value" -> value) - }.toList + invoke = { case List(ValContext(c: Context)) => + c.variableProvider.getVariables.map { case (key, value) => + Map("key" -> ValString(key), "value" -> value) + }.toList } ) @@ -53,7 +53,7 @@ class ContextBuiltinFunctions(valueMapper: ValueMapper) { params = parameters, invoke = { case List(context: ValContext, keys: ValList) => getValueFunction2.invoke(List(context, keys)) - case List(ValContext(c), ValString(key)) => + case List(ValContext(c), ValString(key)) => c.variableProvider .getVariable(key) .getOrElse(ValNull) @@ -66,21 +66,21 @@ class ContextBuiltinFunctions(valueMapper: ValueMapper) { case List(ValContext(context), ValList(keys)) if isListOfStrings(keys) => val listOfKeys = keys.asInstanceOf[List[ValString]].map(_.value) getValueRecursive(context, listOfKeys) - case List(ValContext(_), ValList(_)) => ValNull + case List(ValContext(_), ValList(_)) => ValNull } ) @tailrec private def getValueRecursive(context: Context, keys: List[String]): Val = { keys match { - case Nil => ValNull + case Nil => ValNull case head :: tail => val result = context.variableProvider.getVariable(head).map(valueMapper.toVal) result match { - case None => ValNull + case None => ValNull case Some(value: Val) if tail.isEmpty => value - case Some(ValContext(nestedContext)) => getValueRecursive(nestedContext, tail) - case Some(_) => ValNull + case Some(ValContext(nestedContext)) => getValueRecursive(nestedContext, tail) + case Some(_) => ValNull } } } @@ -88,13 +88,16 @@ class ContextBuiltinFunctions(valueMapper: ValueMapper) { private def contextPutFunction = builtinFunction( params = List("context", "key", "value"), invoke = { - case List(ValContext(_), ValString(_), ValError(_)) => ValNull - case List(context: ValContext, ValString(key), value) => contextPut( - contextValue = context, key = key, value = value - ) + case List(ValContext(_), ValString(_), ValError(_)) => ValNull + case List(context: ValContext, ValString(key), value) => + contextPut( + contextValue = context, + key = key, + value = value + ) // delegate to other context put function with keys - case List(ValContext(_), ValList(_), ValError(_)) => ValNull - case List(ValContext(_), ValList(keys), _) if keys.isEmpty => ValNull + case List(ValContext(_), ValList(_), ValError(_)) => ValNull + case List(ValContext(_), ValList(keys), _) if keys.isEmpty => ValNull case List(context: ValContext, keys: ValList, value) if isListOfStrings(keys.items) => contextPutFunction2.invoke(List(context, keys, value)) } @@ -102,15 +105,15 @@ class ContextBuiltinFunctions(valueMapper: ValueMapper) { private def contextPut(contextValue: ValContext, key: String, value: Val): ValContext = { ValContext( - StaticContext( - variables = contextValue.context.variableProvider.getVariables + (key -> value))) + StaticContext(variables = contextValue.context.variableProvider.getVariables + (key -> value)) + ) } private def contextPutFunction2 = builtinFunction( params = List("context", "keys", "value"), invoke = { - case List(ValContext(_), ValList(_), ValError(_)) => ValNull - case List(ValContext(_), ValList(keys), _) if keys.isEmpty => ValNull + case List(ValContext(_), ValList(_), ValError(_)) => ValNull + case List(ValContext(_), ValList(keys), _) if keys.isEmpty => ValNull case List(context: ValContext, ValList(keys), value) if isListOfStrings(keys) => val listOfKeys = keys.asInstanceOf[List[ValString]].map(_.value) @@ -120,19 +123,24 @@ class ContextBuiltinFunctions(valueMapper: ValueMapper) { @tailrec private def contextPutWithKeys( - contextValue: ValContext, - keys: List[String], value: Val, - parentContextUpdater: ValContext => ValContext = identity): ValContext = { + contextValue: ValContext, + keys: List[String], + value: Val, + parentContextUpdater: ValContext => ValContext = identity + ): ValContext = { keys match { - case Nil => contextValue - case key :: Nil => + case Nil => contextValue + case key :: Nil => val modifiedContext = contextPut(contextValue = contextValue, key = key, value = value) parentContextUpdater(modifiedContext) case key :: tail => - val contextOfKey = contextValue.context.variableProvider.getVariable(key).map { - case contextOfKey: ValContext => contextOfKey - case _ => ValContext(EmptyContext) - }.getOrElse(ValContext(EmptyContext)) + val contextOfKey = contextValue.context.variableProvider + .getVariable(key) + .map { + case contextOfKey: ValContext => contextOfKey + case _ => ValContext(EmptyContext) + } + .getOrElse(ValContext(EmptyContext)) // recursive call for the next key with its nested context contextPutWithKeys( @@ -141,11 +149,12 @@ class ContextBuiltinFunctions(valueMapper: ValueMapper) { value = value, parentContextUpdater = // pass a lambda to update this context with the modified nested context - nestedContext => contextPut( - contextValue = contextValue, - key = key, - value = nestedContext - ) + nestedContext => + contextPut( + contextValue = contextValue, + key = key, + value = nestedContext + ) ) } } @@ -162,12 +171,12 @@ class ContextBuiltinFunctions(valueMapper: ValueMapper) { variables = contexts .flatMap(_ match { case ValContext(c) => c.variableProvider.getVariables - case _ => Map.empty + case _ => Map.empty }) .toMap ) ) - case _ => ValNull + case _ => ValNull }, hasVarArgs = true ) @@ -179,14 +188,13 @@ class ContextBuiltinFunctions(valueMapper: ValueMapper) { params = List("entries"), invoke = { case List(ValList(entries)) if isListOfKeyValuePairs(entries) => - ValContext(StaticContext(variables = entries.flatMap { - case ValContext(context) => - val getValue = context.variableProvider.getVariable(_) - getValue("key") - .map { case ValString(key) => key } - .flatMap(key => getValue("value").map(value => key -> value)) + ValContext(StaticContext(variables = entries.flatMap { case ValContext(context) => + val getValue = context.variableProvider.getVariable(_) + getValue("key") + .map { case ValString(key) => key } + .flatMap(key => getValue("value").map(value => key -> value)) }.toMap)) - case _ => ValNull + case _ => ValNull } ) @@ -197,7 +205,7 @@ class ContextBuiltinFunctions(valueMapper: ValueMapper) { keys.contains("value") && context.variableProvider .getVariable("key") .exists(_.isInstanceOf[ValString]) - case _ => false + case _ => false } } diff --git a/src/main/scala/org/camunda/feel/impl/builtin/ConversionBuiltinFunctions.scala b/src/main/scala/org/camunda/feel/impl/builtin/ConversionBuiltinFunctions.scala index 294b9c98a..087746530 100644 --- a/src/main/scala/org/camunda/feel/impl/builtin/ConversionBuiltinFunctions.scala +++ b/src/main/scala/org/camunda/feel/impl/builtin/ConversionBuiltinFunctions.scala @@ -17,8 +17,43 @@ package org.camunda.feel.impl.builtin import org.camunda.feel.impl.builtin.BuiltinFunction.builtinFunction -import org.camunda.feel.syntaxtree.{Val, ValBoolean, ValDate, ValDateTime, ValDayTimeDuration, ValError, ValLocalDateTime, ValLocalTime, ValNull, ValNumber, ValString, ValTime, ValYearMonthDuration, ZonedTime} -import org.camunda.feel.{Date, YearMonthDuration, dateFormatter, dateTimeFormatter, isDayTimeDuration, isLocalDateTime, isOffsetDateTime, isOffsetTime, isValidDate, isYearMonthDuration, localDateTimeFormatter, localTimeFormatter, stringToDate, stringToDateTime, stringToDayTimeDuration, stringToLocalDateTime, stringToLocalTime, stringToNumber, stringToYearMonthDuration} +import org.camunda.feel.syntaxtree.{ + Val, + ValBoolean, + ValDate, + ValDateTime, + ValDayTimeDuration, + ValError, + ValLocalDateTime, + ValLocalTime, + ValNull, + ValNumber, + ValString, + ValTime, + ValYearMonthDuration, + ZonedTime +} +import org.camunda.feel.{ + Date, + YearMonthDuration, + dateFormatter, + dateTimeFormatter, + isDayTimeDuration, + isLocalDateTime, + isOffsetDateTime, + isOffsetTime, + isValidDate, + isYearMonthDuration, + localDateTimeFormatter, + localTimeFormatter, + stringToDate, + stringToDateTime, + stringToDayTimeDuration, + stringToLocalDateTime, + stringToLocalTime, + stringToNumber, + stringToYearMonthDuration +} import java.math.BigDecimal import java.time.{LocalDate, LocalTime, Period, ZoneId, ZoneOffset} @@ -28,12 +63,12 @@ import scala.util.Try object ConversionBuiltinFunctions { def functions = Map( - "date" -> List(dateFunction, dateFunction3), - "date and time" -> List(dateTime, dateTime2), - "time" -> List(timeFunction, timeFunction3, timeFunction4), - "number" -> List(numberFunction, numberFunction2, numberFunction3), - "string" -> List(stringFunction), - "duration" -> List(durationFunction), + "date" -> List(dateFunction, dateFunction3), + "date and time" -> List(dateTime, dateTime2), + "time" -> List(timeFunction, timeFunction3, timeFunction4), + "number" -> List(numberFunction, numberFunction2, numberFunction3), + "string" -> List(stringFunction), + "duration" -> List(durationFunction), "years and months duration" -> List(durationFunction2) ) @@ -49,40 +84,41 @@ object ConversionBuiltinFunctions { private def dateFunction3 = builtinFunction( params = List("year", "month", "day"), - invoke = { - case List(ValNumber(year), ValNumber(month), ValNumber(day)) => - Try { - ValDate(LocalDate.of(year.intValue, month.intValue, day.intValue)) - }.getOrElse { - ValError( - s"Failed to parse date from: year=$year, month=$month, day=$day") - } + invoke = { case List(ValNumber(year), ValNumber(month), ValNumber(day)) => + Try { + ValDate(LocalDate.of(year.intValue, month.intValue, day.intValue)) + }.getOrElse { + ValError(s"Failed to parse date from: year=$year, month=$month, day=$day") + } } ) private def dateTime = - builtinFunction(params = List("from"), invoke = { - case List(ValString(from)) => parseDateTime(from) - }) + builtinFunction( + params = List("from"), + invoke = { case List(ValString(from)) => + parseDateTime(from) + } + ) private def dateTime2 = builtinFunction( params = List("date", "time"), invoke = { - case List(ValDate(date), ValLocalTime(time)) => + case List(ValDate(date), ValLocalTime(time)) => ValLocalDateTime(date.atTime(time)) - case List(ValDate(date), ValTime(time)) => + case List(ValDate(date), ValTime(time)) => ValDateTime(time.withDate(date)) case List(ValLocalDateTime(dateTime), ValLocalTime(time)) => ValLocalDateTime(dateTime.toLocalDate().atTime(time)) - case List(ValLocalDateTime(dateTime), ValTime(time)) => + case List(ValLocalDateTime(dateTime), ValTime(time)) => ValDateTime(time.withDate(dateTime.toLocalDate())) - case List(ValDateTime(dateTime), ValLocalTime(time)) => + case List(ValDateTime(dateTime), ValLocalTime(time)) => ValLocalDateTime(dateTime.toLocalDate().atTime(time)) - case List(ValDateTime(dateTime), ValTime(time)) => + case List(ValDateTime(dateTime), ValTime(time)) => ValDateTime(time.withDate(dateTime.toLocalDate())) - case List(ValLocalDateTime(date), ValString(timezone)) => + case List(ValLocalDateTime(date), ValString(timezone)) => ValDateTime(date.atZone(ZoneId.of(timezone))) - case List(ValDateTime(date), ValString(timezone)) => + case List(ValDateTime(date), ValString(timezone)) => ValDateTime(date.withZoneSameInstant(ZoneId.of(timezone))) } ) @@ -93,79 +129,77 @@ object ConversionBuiltinFunctions { case List(ValString(from)) => parseTime(from) case List(ValLocalDateTime(from)) => ValLocalTime(from.toLocalTime()) case List(ValDateTime(from)) => ValTime(ZonedTime.of(from)) - case List(ValDate(from)) => + case List(ValDate(from)) => ValTime(ZonedTime.of(LocalTime.MIDNIGHT, ZoneOffset.UTC)) } ) private def timeFunction3 = builtinFunction( params = List("hour", "minute", "second"), - invoke = { - case List(ValNumber(hour), ValNumber(minute), ValNumber(second)) => - Try { - val nanos = second.bigDecimal - .remainder(BigDecimal.ONE) - .movePointRight(9) - .intValue - ValLocalTime( - LocalTime - .of(hour.intValue, minute.intValue, second.intValue, nanos)) - }.getOrElse { - ValError( - s"Failed to parse local-time from: hour=$hour, minute=$minute, second=$second") - } + invoke = { case List(ValNumber(hour), ValNumber(minute), ValNumber(second)) => + Try { + val nanos = second.bigDecimal + .remainder(BigDecimal.ONE) + .movePointRight(9) + .intValue + ValLocalTime( + LocalTime + .of(hour.intValue, minute.intValue, second.intValue, nanos) + ) + }.getOrElse { + ValError(s"Failed to parse local-time from: hour=$hour, minute=$minute, second=$second") + } } ) private def timeFunction4 = builtinFunction( params = List("hour", "minute", "second", "offset"), invoke = { - case List(ValNumber(hour), - ValNumber(minute), - ValNumber(second), - ValDayTimeDuration(offset)) => + case List( + ValNumber(hour), + ValNumber(minute), + ValNumber(second), + ValDayTimeDuration(offset) + ) => Try { - val nanos = second.bigDecimal + val nanos = second.bigDecimal .remainder(BigDecimal.ONE) .movePointRight(9) .intValue - val localTime = + val localTime = LocalTime.of(hour.intValue, minute.intValue, second.intValue, nanos) val zonedOffset = ZoneOffset.ofTotalSeconds(offset.getSeconds.toInt) ValTime(ZonedTime.of(localTime, zonedOffset)) }.getOrElse { ValError( - s"Failed to parse time from: hour=$hour, minute=$minute, second=$second, offset=$offset") + s"Failed to parse time from: hour=$hour, minute=$minute, second=$second, offset=$offset" + ) } - case List(ValNumber(hour), - ValNumber(minute), - ValNumber(second), - ValNull) => + case List(ValNumber(hour), ValNumber(minute), ValNumber(second), ValNull) => Try { - ValLocalTime( - LocalTime.of(hour.intValue, minute.intValue, second.intValue)) + ValLocalTime(LocalTime.of(hour.intValue, minute.intValue, second.intValue)) }.getOrElse { - ValError( - s"Failed to parse local-time from: hour=$hour, minute=$minute, second=$second") + ValError(s"Failed to parse local-time from: hour=$hour, minute=$minute, second=$second") } } ) private def numberFunction = - builtinFunction(params = List("from"), invoke = { - case List(ValString(from)) => ValNumber(from) - }) + builtinFunction( + params = List("from"), + invoke = { case List(ValString(from)) => + ValNumber(from) + } + ) private def numberFunction2 = builtinFunction( params = List("from", "grouping separator"), invoke = { - case List(ValString(from), ValString(grouping)) - if (isValidGroupingSeparator(grouping)) => + case List(ValString(from), ValString(grouping)) if (isValidGroupingSeparator(grouping)) => ValNumber(from.replace(grouping, "")) - case List(ValString(from), ValString(grouping)) => - ValError( - s"illegal argument for grouping. Must be one of ' ', ',' or '.'") + case List(ValString(from), ValString(grouping)) => + ValError(s"illegal argument for grouping. Must be one of ' ', ',' or '.'") } ) @@ -174,17 +208,18 @@ object ConversionBuiltinFunctions { invoke = { case List(ValString(from), ValString(grouping), ValString(decimal)) if (isValidGroupingSeparator(grouping) && isValidDecimalSeparator( - decimal) && grouping != decimal) => + decimal + ) && grouping != decimal) => ValNumber(from.replace(grouping, "").replace(decimal, ".")) - case List(ValString(from), ValNull, ValString(decimal)) - if isValidDecimalSeparator(decimal) => + case List(ValString(from), ValNull, ValString(decimal)) if isValidDecimalSeparator(decimal) => ValNumber(from.replace(decimal, ".")) case List(ValString(from), ValString(grouping), ValNull) if isValidGroupingSeparator(grouping) => ValNumber(from.replace(grouping, "")) - case List(ValString(from), ValString(grouping), ValString(decimal)) => + case List(ValString(from), ValString(grouping), ValString(decimal)) => ValError( - s"illegal arguments for grouping or decimal. Must be one of ' ' (grouping only), ',' or '.'") + s"illegal arguments for grouping or decimal. Must be one of ' ' (grouping only), ',' or '.'" + ) } ) @@ -196,57 +231,60 @@ object ConversionBuiltinFunctions { private def stringFunction = builtinFunction( params = List("from"), - invoke = { - case List(from) => ValString(from.toString) + invoke = { case List(from) => + ValString(from.toString) } ) private def durationFunction = - builtinFunction(params = List("from"), invoke = { - case List(ValString(from)) => parseDuration(from) - }) + builtinFunction( + params = List("from"), + invoke = { case List(ValString(from)) => + parseDuration(from) + } + ) private def durationFunction2 = builtinFunction( params = List("from", "to"), invoke = { - case List(ValDate(from), ValDate(to)) => + case List(ValDate(from), ValDate(to)) => ValYearMonthDuration(Period.between(from, to).withDays(0).normalized) case List(ValLocalDateTime(from), ValLocalDateTime(to)) => ValYearMonthDuration( Period .between(from.toLocalDate, to.toLocalDate) .withDays(0) - .normalized) - case List(ValDateTime(from), ValDateTime(to)) => + .normalized + ) + case List(ValDateTime(from), ValDateTime(to)) => ValYearMonthDuration( Period .between(from.toLocalDate, to.toLocalDate) .withDays(0) - .normalized) - case List(ValDateTime(from), ValLocalDateTime(to)) => + .normalized + ) + case List(ValDateTime(from), ValLocalDateTime(to)) => ValYearMonthDuration( Period .between(from.toLocalDate, to.toLocalDate) .withDays(0) - .normalized) - case List(ValLocalDateTime(from), ValDateTime(to)) => + .normalized + ) + case List(ValLocalDateTime(from), ValDateTime(to)) => ValYearMonthDuration( Period .between(from.toLocalDate, to.toLocalDate) .withDays(0) - .normalized) - case List(ValDate(from), ValDateTime(to)) => - ValYearMonthDuration( - Period.between(from, to.toLocalDate()).withDays(0).normalized) - case List(ValDate(from), ValLocalDateTime(to)) => - ValYearMonthDuration( - Period.between(from, to.toLocalDate()).withDays(0).normalized) - case List(ValDateTime(from), ValDate(to)) => - ValYearMonthDuration( - Period.between(from.toLocalDate(), to).withDays(0).normalized) - case List(ValLocalDateTime(from), ValDate(to)) => - ValYearMonthDuration( - Period.between(from.toLocalDate(), to).withDays(0).normalized) + .normalized + ) + case List(ValDate(from), ValDateTime(to)) => + ValYearMonthDuration(Period.between(from, to.toLocalDate()).withDays(0).normalized) + case List(ValDate(from), ValLocalDateTime(to)) => + ValYearMonthDuration(Period.between(from, to.toLocalDate()).withDays(0).normalized) + case List(ValDateTime(from), ValDate(to)) => + ValYearMonthDuration(Period.between(from.toLocalDate(), to).withDays(0).normalized) + case List(ValLocalDateTime(from), ValDate(to)) => + ValYearMonthDuration(Period.between(from.toLocalDate(), to).withDays(0).normalized) } ) diff --git a/src/main/scala/org/camunda/feel/impl/builtin/ListBuiltinFunctions.scala b/src/main/scala/org/camunda/feel/impl/builtin/ListBuiltinFunctions.scala index 4880c05aa..1289bf916 100644 --- a/src/main/scala/org/camunda/feel/impl/builtin/ListBuiltinFunctions.scala +++ b/src/main/scala/org/camunda/feel/impl/builtin/ListBuiltinFunctions.scala @@ -34,69 +34,75 @@ import scala.annotation.tailrec object ListBuiltinFunctions { def functions = Map( - "list contains" -> List(listContainsFunction), - "count" -> List(countFunction), - "min" -> List(minFunction), - "max" -> List(maxFunction), - "sum" -> List(sumFunction), - "product" -> List(productFunction), - "mean" -> List(meanFunction), - "median" -> List(medianFunction), - "stddev" -> List(stddevFunction), - "mode" -> List(modeFunction), - "and" -> List(andFunction), - "all" -> List(andFunction), - "or" -> List(orFunction), - "any" -> List(orFunction), - "sublist" -> List(sublistFunction, sublistFunction3), - "append" -> List(appendFunction), - "concatenate" -> List(concatenateFunction), - "insert before" -> List(insertBeforeFunction), - "remove" -> List(removeFunction), - "reverse" -> List(reverseFunction), - "index of" -> List(indexOfFunction), - "union" -> List(unionFunction), - "distinct values" -> List(distinctValuesFunction), + "list contains" -> List(listContainsFunction), + "count" -> List(countFunction), + "min" -> List(minFunction), + "max" -> List(maxFunction), + "sum" -> List(sumFunction), + "product" -> List(productFunction), + "mean" -> List(meanFunction), + "median" -> List(medianFunction), + "stddev" -> List(stddevFunction), + "mode" -> List(modeFunction), + "and" -> List(andFunction), + "all" -> List(andFunction), + "or" -> List(orFunction), + "any" -> List(orFunction), + "sublist" -> List(sublistFunction, sublistFunction3), + "append" -> List(appendFunction), + "concatenate" -> List(concatenateFunction), + "insert before" -> List(insertBeforeFunction), + "remove" -> List(removeFunction), + "reverse" -> List(reverseFunction), + "index of" -> List(indexOfFunction), + "union" -> List(unionFunction), + "distinct values" -> List(distinctValuesFunction), "duplicate values" -> List(duplicateValuesFunction), - "flatten" -> List(flattenFunction), - "sort" -> List(sortFunction), - "string join" -> List(joinFunction, - joinWithDelimiterFunction, - joinWithDelimiterAndPrefixAndSuffixFunction) + "flatten" -> List(flattenFunction), + "sort" -> List(sortFunction), + "string join" -> List( + joinFunction, + joinWithDelimiterFunction, + joinWithDelimiterAndPrefixAndSuffixFunction + ) ) private def listContainsFunction = - builtinFunction(params = List("list", "element"), invoke = { - case List(ValList(list), element) => ValBoolean(list.contains(element)) - }) + builtinFunction( + params = List("list", "element"), + invoke = { case List(ValList(list), element) => + ValBoolean(list.contains(element)) + } + ) private def countFunction = - builtinFunction(params = List("list"), invoke = { - case List(ValList(list)) => ValNumber(list.size) - }) + builtinFunction( + params = List("list"), + invoke = { case List(ValList(list)) => + ValNumber(list.size) + } + ) private def minFunction = builtinFunction( params = List("list"), - invoke = { - case List(l @ ValList(list)) => - list match { - case Nil => ValNull - case _ if (l.isComparable) => list.min - case _ => ValError(s"$l is not comparable") - } + invoke = { case List(l @ ValList(list)) => + list match { + case Nil => ValNull + case _ if (l.isComparable) => list.min + case _ => ValError(s"$l is not comparable") + } }, hasVarArgs = true ) private def maxFunction = builtinFunction( params = List("list"), - invoke = { - case List(l @ ValList(list)) => - list match { - case Nil => ValNull - case _ if (l.isComparable) => list.max - case _ => ValError(s"$l is not comparable") - } + invoke = { case List(l @ ValList(list)) => + list match { + case Nil => ValNull + case _ if (l.isComparable) => list.max + case _ => ValError(s"$l is not comparable") + } }, hasVarArgs = true ) @@ -105,14 +111,13 @@ object ListBuiltinFunctions { params = List("list"), invoke = { case List(ValList(list)) if list.isEmpty => ValNull - case List(ValList(list)) => + case List(ValList(list)) => withListOfNumbers(list, numbers => ValNumber(numbers.sum)) }, hasVarArgs = true ) - private def withListOfNumbers(list: List[Val], - f: List[Number] => Val): Val = { + private def withListOfNumbers(list: List[Val], f: List[Number] => Val): Val = { list .map(_ match { case n: ValNumber => n @@ -128,7 +133,7 @@ object ListBuiltinFunctions { params = List("list"), invoke = { case List(ValList(list)) if list.isEmpty => ValNull - case List(ValList(list)) => + case List(ValList(list)) => withListOfNumbers(list, numbers => ValNumber(numbers.product)) }, hasVarArgs = true @@ -136,14 +141,12 @@ object ListBuiltinFunctions { private def meanFunction = builtinFunction( params = List("list"), - invoke = { - case List(ValList(list)) => - list match { - case Nil => ValNull - case l => - withListOfNumbers(list, - numbers => ValNumber(numbers.sum / numbers.size)) - } + invoke = { case List(ValList(list)) => + list match { + case Nil => ValNull + case l => + withListOfNumbers(list, numbers => ValNumber(numbers.sum / numbers.size)) + } }, hasVarArgs = true ) @@ -152,7 +155,7 @@ object ListBuiltinFunctions { params = List("list"), invoke = { case List(ValList(list)) if list.isEmpty => ValNull - case List(ValList(list)) => + case List(ValList(list)) => withListOfNumbers( list, numbers => { @@ -176,16 +179,16 @@ object ListBuiltinFunctions { params = List("list"), invoke = { case List(ValList(list)) if list.isEmpty => ValNull - case List(ValList(list)) => + case List(ValList(list)) => withListOfNumbers( list, numbers => { - val sum = numbers.sum + val sum = numbers.sum val mean = sum / numbers.size - val d = ((0: Number) /: numbers) { - case (dev, n) => dev + (n - mean).pow(2) + val d = ((0: Number) /: numbers) { case (dev, n) => + dev + (n - mean).pow(2) } val stddev = Math.sqrt((d / (numbers.size - 1)).toDouble) @@ -201,7 +204,7 @@ object ListBuiltinFunctions { params = List("list"), invoke = { case List(ValList(list)) if list.isEmpty => ValList(List.empty) - case List(ValList(list)) => + case List(ValList(list)) => withListOfNumbers( list, numbers => { @@ -228,9 +231,13 @@ object ListBuiltinFunctions { ) private def andFunction = - builtinFunction(params = List("list"), invoke = { - case List(ValList(list)) => all(list) - }, hasVarArgs = true) + builtinFunction( + params = List("list"), + invoke = { case List(ValList(list)) => + all(list) + }, + hasVarArgs = true + ) private def all(items: List[Val]): Val = { items.foldLeft[Val](ValBoolean(true)) { @@ -242,9 +249,13 @@ object ListBuiltinFunctions { } private def orFunction = - builtinFunction(params = List("list"), invoke = { - case List(ValList(list)) => atLeastOne(list) - }, hasVarArgs = true) + builtinFunction( + params = List("list"), + invoke = { case List(ValList(list)) => + atLeastOne(list) + }, + hasVarArgs = true + ) private def atLeastOne(items: List[Val]): Val = { items.foldLeft[Val](ValBoolean(false)) { @@ -256,18 +267,22 @@ object ListBuiltinFunctions { } private def sublistFunction = - builtinFunction(params = List("list", "start"), invoke = { - case List(ValList(list), ValNumber(start)) => + builtinFunction( + params = List("list", "start"), + invoke = { case List(ValList(list), ValNumber(start)) => ValList(list.slice(listIndex(list, start.intValue), list.length)) - }) + } + ) private def sublistFunction3 = builtinFunction( params = List("list", "start", "length"), - invoke = { - case List(ValList(list), ValNumber(start), ValNumber(length)) => - ValList( - list.slice(listIndex(list, start.intValue), - listIndex(list, start.intValue) + length.intValue)) + invoke = { case List(ValList(list), ValNumber(start), ValNumber(length)) => + ValList( + list.slice( + listIndex(list, start.intValue), + listIndex(list, start.intValue) + length.intValue + ) + ) } ) @@ -279,61 +294,74 @@ object ListBuiltinFunctions { } private def appendFunction = - builtinFunction(params = List("list", "items"), invoke = { - case List(ValList(list), ValList(items)) => ValList(list ++ items) - }, hasVarArgs = true) + builtinFunction( + params = List("list", "items"), + invoke = { case List(ValList(list), ValList(items)) => + ValList(list ++ items) + }, + hasVarArgs = true + ) private def concatenateFunction = builtinFunction( params = List("lists"), - invoke = { - case List(ValList(lists)) => - ValList( - lists - .flatMap(_ match { - case ValList(list) => list - case v => List(v) - }) - .toList) + invoke = { case List(ValList(lists)) => + ValList( + lists + .flatMap(_ match { + case ValList(list) => list + case v => List(v) + }) + .toList + ) }, hasVarArgs = true ) private def insertBeforeFunction = builtinFunction( params = List("list", "position", "newItem"), - invoke = { - case List(ValList(list), ValNumber(position), newItem: Val) => - ValList(list + invoke = { case List(ValList(list), ValNumber(position), newItem: Val) => + ValList( + list .take(listIndex(list, position.intValue)) ++ (newItem :: Nil) ++ list - .drop(listIndex(list, position.intValue))) + .drop(listIndex(list, position.intValue)) + ) } ) private def removeFunction = builtinFunction( params = List("list", "position"), - invoke = { - case List(ValList(list), ValNumber(position)) => - ValList( - list.take(listIndex(list, position.intValue)) ++ list.drop( - listIndex(list, position.intValue + 1))) + invoke = { case List(ValList(list), ValNumber(position)) => + ValList( + list.take(listIndex(list, position.intValue)) ++ list.drop( + listIndex(list, position.intValue + 1) + ) + ) } ) private def reverseFunction = - builtinFunction(params = List("list"), invoke = { - case List(ValList(list)) => ValList(list.reverse) - }) + builtinFunction( + params = List("list"), + invoke = { case List(ValList(list)) => + ValList(list.reverse) + } + ) private def indexOfFunction = - builtinFunction(params = List("list", "match"), invoke = { - case List(ValList(list), m: Val) => + builtinFunction( + params = List("list", "match"), + invoke = { case List(ValList(list), m: Val) => ValList(indexOfList(list, m) map (ValNumber(_))) - }) + } + ) @tailrec - private def indexOfList(list: List[Val], - item: Val, - from: Int = 0, - indexList: List[Int] = List()): List[Int] = { + private def indexOfList( + list: List[Val], + item: Val, + from: Int = 0, + indexList: List[Int] = List() + ): List[Int] = { val index = list.indexOf(item, from) if (index >= 0) { @@ -345,34 +373,43 @@ object ListBuiltinFunctions { private def unionFunction = builtinFunction( params = List("lists"), - invoke = { - case List(ValList(lists)) => - ValList( - lists - .flatMap(_ match { - case ValList(list) => list - case v => List(v) - }) - .toList - .distinct) + invoke = { case List(ValList(lists)) => + ValList( + lists + .flatMap(_ match { + case ValList(list) => list + case v => List(v) + }) + .toList + .distinct + ) }, hasVarArgs = true ) private def distinctValuesFunction = - builtinFunction(params = List("list"), invoke = { - case List(ValList(list)) => ValList(list.distinct) - }) + builtinFunction( + params = List("list"), + invoke = { case List(ValList(list)) => + ValList(list.distinct) + } + ) private def duplicateValuesFunction = - builtinFunction(params = List("list"), invoke = { - case List(ValList(list)) => ValList(list.distinct.filter(x => list.count(_ == x) > 1)) - }) + builtinFunction( + params = List("list"), + invoke = { case List(ValList(list)) => + ValList(list.distinct.filter(x => list.count(_ == x) > 1)) + } + ) private def flattenFunction = - builtinFunction(params = List("list"), invoke = { - case List(ValList(list)) => ValList(flatten(list)) - }) + builtinFunction( + params = List("list"), + invoke = { case List(ValList(list)) => + ValList(flatten(list)) + } + ) private def flatten(list: List[Val]): List[Val] = { list.flatten { @@ -384,33 +421,29 @@ object ListBuiltinFunctions { private def sortFunction = builtinFunction( params = List("list", "precedes"), invoke = { - case List(ValList(list), ValFunction(params, f, _)) - if (params.size == 2) => { + case List(ValList(list), ValFunction(params, f, _)) if (params.size == 2) => { try { - ValList(list.sortWith { - case (x, y) => - f(List(x, y)) match { - case ValBoolean(isMet) => isMet - case e => - throw new RuntimeException(s"expected boolean but found '$e'") - } + ValList(list.sortWith { case (x, y) => + f(List(x, y)) match { + case ValBoolean(isMet) => isMet + case e => + throw new RuntimeException(s"expected boolean but found '$e'") + } }) } catch { case e: Throwable => - ValError( - s"fail to sort list by given precedes function: ${e.getMessage}") + ValError(s"fail to sort list by given precedes function: ${e.getMessage}") } } - case List(ValList(list), ValFunction(params, _, _)) => - ValError( - s"expect boolean function with 2 arguments, but found '${params.size}'") + case List(ValList(list), ValFunction(params, _, _)) => + ValError(s"expect boolean function with 2 arguments, but found '${params.size}'") } ) private def joinFunction = builtinFunction( params = List("list"), - invoke = { - case List(ValList(list)) => joinStringList(list = list) + invoke = { case List(ValList(list)) => + joinStringList(list = list) } ) @@ -419,36 +452,32 @@ object ListBuiltinFunctions { invoke = { case List(ValList(list), ValString(delimiter)) => joinStringList(list = list, delimiter = delimiter) - case List(ValList(list), ValNull) => joinStringList(list = list) + case List(ValList(list), ValNull) => joinStringList(list = list) } ) private def joinWithDelimiterAndPrefixAndSuffixFunction = builtinFunction( params = List("list", "delimiter", "prefix", "suffix"), invoke = { - case List(ValList(list), - ValString(delimiter), - ValString(prefix), - ValString(suffix)) => - joinStringList(list = list, - delimiter = delimiter, - prefix = prefix, - suffix = suffix) + case List(ValList(list), ValString(delimiter), ValString(prefix), ValString(suffix)) => + joinStringList(list = list, delimiter = delimiter, prefix = prefix, suffix = suffix) case List(ValList(list), ValNull, ValString(prefix), ValString(suffix)) => joinStringList(list = list, prefix = prefix, suffix = suffix) case List(ValList(list), ValString(delimiter), ValNull, ValNull) => joinStringList(list = list, delimiter = delimiter) - case List(ValList(list), ValNull, ValNull, ValNull) => + case List(ValList(list), ValNull, ValNull, ValNull) => joinStringList(list = list) } ) - private def joinStringList(list: List[Val], - delimiter: String = "", - prefix: String = "", - suffix: String = ""): Val = { + private def joinStringList( + list: List[Val], + delimiter: String = "", + prefix: String = "", + suffix: String = "" + ): Val = { val isStringList = list.forall { case _: ValString => true @@ -464,8 +493,7 @@ object ListBuiltinFunctions { .filterNot(_ == ValNull) .map { case ValString(x) => x } - ValString( - stringList.mkString(start = prefix, sep = delimiter, end = suffix)) + ValString(stringList.mkString(start = prefix, sep = delimiter, end = suffix)) } } diff --git a/src/main/scala/org/camunda/feel/impl/builtin/NumericBuiltinFunctions.scala b/src/main/scala/org/camunda/feel/impl/builtin/NumericBuiltinFunctions.scala index e6f74e52d..b35b90120 100644 --- a/src/main/scala/org/camunda/feel/impl/builtin/NumericBuiltinFunctions.scala +++ b/src/main/scala/org/camunda/feel/impl/builtin/NumericBuiltinFunctions.scala @@ -35,83 +35,86 @@ import scala.util.Random object NumericBuiltinFunctions { def functions = Map( - "decimal" -> List(decimalFunction, decimalFunction3), - "floor" -> List(floorFunction, floorFunction2), - "ceiling" -> List(ceilingFunction, ceilingFunction2), - "abs" -> List(absFunction(paramName = "number"), - absFunction(paramName = "n")), - "modulo" -> List(moduloFunction), - "sqrt" -> List(sqrtFunction), - "log" -> List(logFunction), - "exp" -> List(expFunction), - "odd" -> List(oddFunction), - "even" -> List(evenFunction), - "round up" -> List(roundUpFunction), - "round down" -> List(roundDownFunction), - "round half up" -> List(roundHalfUpFunction), + "decimal" -> List(decimalFunction, decimalFunction3), + "floor" -> List(floorFunction, floorFunction2), + "ceiling" -> List(ceilingFunction, ceilingFunction2), + "abs" -> List(absFunction(paramName = "number"), absFunction(paramName = "n")), + "modulo" -> List(moduloFunction), + "sqrt" -> List(sqrtFunction), + "log" -> List(logFunction), + "exp" -> List(expFunction), + "odd" -> List(oddFunction), + "even" -> List(evenFunction), + "round up" -> List(roundUpFunction), + "round down" -> List(roundDownFunction), + "round half up" -> List(roundHalfUpFunction), "round half down" -> List(roundHalfDownFunction), - "random number" -> List(randomNumberFunction) + "random number" -> List(randomNumberFunction) ) private def decimalFunction = builtinFunction( params = List("n", "scale"), - invoke = { - case List(ValNumber(n), ValNumber(scale)) => - round(n, scale, RoundingMode.HALF_EVEN) + invoke = { case List(ValNumber(n), ValNumber(scale)) => + round(n, scale, RoundingMode.HALF_EVEN) } ) def decimalFunction3 = builtinFunction( params = List("n", "scale", "mode"), invoke = { - case List(ValNumber(n), ValNumber(scale), ValString(mode)) - if (isRoundingMode(mode)) => { + case List(ValNumber(n), ValNumber(scale), ValString(mode)) if (isRoundingMode(mode)) => { val roundingMode = RoundingMode.withName(mode.toUpperCase) round(n, scale, roundingMode) } - case List(ValNumber(_), ValNumber(_), ValString(mode)) => { + case List(ValNumber(_), ValNumber(_), ValString(mode)) => { val roundingModes = RoundingMode.values.mkString(", ") - ValError( - s"Illegal argument '$mode' for rounding mode. Must be one of: $roundingModes") + ValError(s"Illegal argument '$mode' for rounding mode. Must be one of: $roundingModes") } } ) private def floorFunction2 = - builtinFunction(params = List("n", "scala"), invoke = { - case List(ValNumber(n), ValNumber(scale)) => + builtinFunction( + params = List("n", "scala"), + invoke = { case List(ValNumber(n), ValNumber(scale)) => round(n, scale, RoundingMode.FLOOR) - }) + } + ) private def ceilingFunction2 = - builtinFunction(params = List("n", "scale"), invoke = { - case List(ValNumber(n), ValNumber(scale)) => + builtinFunction( + params = List("n", "scale"), + invoke = { case List(ValNumber(n), ValNumber(scale)) => round(n, scale, RoundingMode.CEILING) - }) + } + ) private def floorFunction = - builtinFunction(params = List("n"), invoke = { - case List(ValNumber(n)) => round(n, 0, RoundingMode.FLOOR) - }) + builtinFunction( + params = List("n"), + invoke = { case List(ValNumber(n)) => + round(n, 0, RoundingMode.FLOOR) + } + ) private def ceilingFunction = - builtinFunction(params = List("n"), invoke = { - case List(ValNumber(n)) => round(n, 0, RoundingMode.CEILING) - }) + builtinFunction( + params = List("n"), + invoke = { case List(ValNumber(n)) => + round(n, 0, RoundingMode.CEILING) + } + ) private def isRoundingMode(mode: String) = RoundingMode.values.map(_.toString).contains(mode.toUpperCase) - private def round(n: Number, - scale: Number, - roundingMode: RoundingMode.Value): Val = { + private def round(n: Number, scale: Number, roundingMode: RoundingMode.Value): Val = { try { val x = n.setScale(scale.intValue, roundingMode) ValNumber(x) } catch { case e: ArithmeticException => - ValError( - s"Failed to apply rounding mode '$roundingMode': ${e.getMessage}") + ValError(s"Failed to apply rounding mode '$roundingMode': ${e.getMessage}") } } @@ -119,73 +122,100 @@ object NumericBuiltinFunctions { builtinFunction( params = List(paramName), invoke = { - case List(ValNumber(n)) => ValNumber(n.abs) + case List(ValNumber(n)) => ValNumber(n.abs) case List(ValYearMonthDuration(n)) if n.isNegative => ValYearMonthDuration(n.negated()) - case List(ValYearMonthDuration(n)) => ValYearMonthDuration(n) - case List(ValDayTimeDuration(n)) => ValDayTimeDuration(n.abs()) + case List(ValYearMonthDuration(n)) => ValYearMonthDuration(n) + case List(ValDayTimeDuration(n)) => ValDayTimeDuration(n.abs()) } ) private def moduloFunction = - builtinFunction(params = List("dividend", "divisor"), invoke = { - case List(ValNumber(dividend), ValNumber(divisor)) => + builtinFunction( + params = List("dividend", "divisor"), + invoke = { case List(ValNumber(dividend), ValNumber(divisor)) => ValNumber(((dividend % divisor) + divisor) % divisor) - }) + } + ) private def sqrtFunction = - builtinFunction(params = List("number"), invoke = { - case List(ValNumber(n)) if n < 0 => ValNull - case List(ValNumber(n)) => ValNumber(Math.sqrt(n.toDouble)) - }) + builtinFunction( + params = List("number"), + invoke = { + case List(ValNumber(n)) if n < 0 => ValNull + case List(ValNumber(n)) => ValNumber(Math.sqrt(n.toDouble)) + } + ) private def logFunction = - builtinFunction(params = List("number"), invoke = { - case List(ValNumber(n)) => ValNumber(Math.log(n.toDouble)) - }) + builtinFunction( + params = List("number"), + invoke = { case List(ValNumber(n)) => + ValNumber(Math.log(n.toDouble)) + } + ) private def expFunction = - builtinFunction(params = List("number"), invoke = { - case List(ValNumber(n)) => ValNumber(Math.exp(n.toDouble)) - }) + builtinFunction( + params = List("number"), + invoke = { case List(ValNumber(n)) => + ValNumber(Math.exp(n.toDouble)) + } + ) private def oddFunction = - builtinFunction(params = List("number"), invoke = { - case List(ValNumber(n)) => ValBoolean(n.abs % 2 == 1) - }) + builtinFunction( + params = List("number"), + invoke = { case List(ValNumber(n)) => + ValBoolean(n.abs % 2 == 1) + } + ) private def evenFunction = - builtinFunction(params = List("number"), invoke = { - case List(ValNumber(n)) => ValBoolean(n % 2 == 0) - }) + builtinFunction( + params = List("number"), + invoke = { case List(ValNumber(n)) => + ValBoolean(n % 2 == 0) + } + ) private def roundUpFunction = - builtinFunction(params = List("n", "scale"), invoke = { - case List(ValNumber(n), ValNumber(scale)) => + builtinFunction( + params = List("n", "scale"), + invoke = { case List(ValNumber(n), ValNumber(scale)) => round(n, scale, RoundingMode.UP) - }) + } + ) private def roundDownFunction = - builtinFunction(params = List("n", "scale"), invoke = { - case List(ValNumber(n), ValNumber(scale)) => + builtinFunction( + params = List("n", "scale"), + invoke = { case List(ValNumber(n), ValNumber(scale)) => round(n, scale, RoundingMode.DOWN) - }) + } + ) private def roundHalfUpFunction = - builtinFunction(params = List("n", "scale"), invoke = { - case List(ValNumber(n), ValNumber(scale)) => + builtinFunction( + params = List("n", "scale"), + invoke = { case List(ValNumber(n), ValNumber(scale)) => round(n, scale, RoundingMode.HALF_UP) - }) + } + ) private def roundHalfDownFunction = - builtinFunction(params = List("n", "scala"), invoke = { - case List(ValNumber(n), ValNumber(scale)) => + builtinFunction( + params = List("n", "scala"), + invoke = { case List(ValNumber(n), ValNumber(scale)) => round(n, scale, RoundingMode.HALF_DOWN) - }) + } + ) private def randomNumberFunction = - builtinFunction(params = List(), invoke = { - case List() => + builtinFunction( + params = List(), + invoke = { case List() => ValNumber(Random.nextDouble()) - }) + } + ) } diff --git a/src/main/scala/org/camunda/feel/impl/builtin/RangeBuiltinFunction.scala b/src/main/scala/org/camunda/feel/impl/builtin/RangeBuiltinFunction.scala index 43ad71207..4606e8141 100644 --- a/src/main/scala/org/camunda/feel/impl/builtin/RangeBuiltinFunction.scala +++ b/src/main/scala/org/camunda/feel/impl/builtin/RangeBuiltinFunction.scala @@ -35,68 +35,73 @@ import scala.annotation.tailrec object RangeBuiltinFunction { def functions = Map( - "before" -> List( + "before" -> List( beforeFunction("point1", "point2"), beforeFunction("point", "range"), beforeFunction("range", "point"), beforeFunction("range1", "range2") ), - "after" -> List( + "after" -> List( afterFunction("point1", "point2"), afterFunction("point", "range"), afterFunction("range", "point"), afterFunction("range1", "range2") ), - "meets" -> List(meetsFunction), - "met by" -> List(metByFunction), - "overlaps" -> List(overlapsFunction), + "meets" -> List(meetsFunction), + "met by" -> List(metByFunction), + "overlaps" -> List(overlapsFunction), "overlaps before" -> List(overlapsBeforeFunction), - "overlaps after" -> List(overlapsAfterFunction), - "finishes" -> List( + "overlaps after" -> List(overlapsAfterFunction), + "finishes" -> List( finishesFunction("point", "range"), finishesFunction("range1", "range2") ), - "finished by" -> List( + "finished by" -> List( finishedByFunction("point", "range"), finishedByFunction("range1", "range2") ), - "includes" -> List( + "includes" -> List( includesFunction("range", "point"), includesFunction("range1", "range2") ), - "during" -> List( + "during" -> List( duringFunction("point", "range"), duringFunction("range1", "range2") ), - "starts" -> List( + "starts" -> List( starts("point", "range"), starts("range1", "range2") ), - "started by" -> List( + "started by" -> List( startedBy("range", "point"), startedBy("range1", "range2") ), - "coincides" -> List( + "coincides" -> List( coincides("point1", "point2"), coincides("range1", "range2") ) ) - private def rangeBuiltinFunction(params: (String, String), - invoke: PartialFunction[(Val, Val), Any]) = - builtinFunction(params = List(params._1, params._2), invoke = { - case List(x, y) if isComparable(x, y) => invoke(x, y) - }) + private def rangeBuiltinFunction( + params: (String, String), + invoke: PartialFunction[(Val, Val), Any] + ) = + builtinFunction( + params = List(params._1, params._2), + invoke = { + case List(x, y) if isComparable(x, y) => invoke(x, y) + } + ) @tailrec private def isComparable(x: Val, y: Val): Boolean = (x, y) match { case (ValRange(start1, _), ValRange(start2, _)) => isComparable(start1.value, start2.value) - case (ValRange(start, _), point: Val) => isComparable(point, start.value) - case (point: Val, ValRange(start, _)) => isComparable(point, start.value) - case (point1: Val, point2: Val) => + case (ValRange(start, _), point: Val) => isComparable(point, start.value) + case (point: Val, ValRange(start, _)) => isComparable(point, start.value) + case (point1: Val, point2: Val) => isPointValue(point1) && isPointValue(point2) && point1.getClass == point2.getClass - case _ => false + case _ => false } private def isPointValue(value: Val): Boolean = value match { @@ -117,13 +122,13 @@ object RangeBuiltinFunction { invoke = { case (ValRange(_, end1), ValRange(start2, _)) => ValBoolean( - end1.value < start2.value || (!end1.isClosed | !start2.isClosed) & end1.value == start2.value) - case (point: Val, ValRange(start, _)) => - ValBoolean( - point < start.value || (point == start.value & !start.isClosed)) - case (ValRange(_, end), point: Val) => + end1.value < start2.value || (!end1.isClosed | !start2.isClosed) & end1.value == start2.value + ) + case (point: Val, ValRange(start, _)) => + ValBoolean(point < start.value || (point == start.value & !start.isClosed)) + case (ValRange(_, end), point: Val) => ValBoolean(end.value < point || (end.value == point & !end.isClosed)) - case (point1, point2) => ValBoolean(point1 < point2) + case (point1, point2) => ValBoolean(point1 < point2) } ) @@ -133,66 +138,59 @@ object RangeBuiltinFunction { invoke = { case (ValRange(start1, _), ValRange(_, end2)) => ValBoolean( - start1.value > end2.value || ((!start1.isClosed | !end2.isClosed) & start1.value == end2.value)) - case (point: Val, ValRange(_, end)) => + start1.value > end2.value || ((!start1.isClosed | !end2.isClosed) & start1.value == end2.value) + ) + case (point: Val, ValRange(_, end)) => ValBoolean(point > end.value || (point == end.value & !end.isClosed)) - case (ValRange(start, _), point: Val) => - ValBoolean( - start.value > point || (start.value == point & !start.isClosed)) - case (point1, point2) => ValBoolean(point1 > point2) + case (ValRange(start, _), point: Val) => + ValBoolean(start.value > point || (start.value == point & !start.isClosed)) + case (point1, point2) => ValBoolean(point1 > point2) } ) private def meetsFunction = rangeBuiltinFunction( params = ("range1", "range2"), - invoke = { - case (ValRange(_, end1), ValRange(start2, _)) => - ValBoolean( - end1.isClosed && start2.isClosed && end1.value == start2.value) + invoke = { case (ValRange(_, end1), ValRange(start2, _)) => + ValBoolean(end1.isClosed && start2.isClosed && end1.value == start2.value) } ) private def metByFunction = rangeBuiltinFunction( params = ("range1", "range2"), - invoke = { - case (ValRange(start1, _), ValRange(_, end2)) => - ValBoolean( - start1.isClosed && end2.isClosed && start1.value == end2.value) + invoke = { case (ValRange(start1, _), ValRange(_, end2)) => + ValBoolean(start1.isClosed && end2.isClosed && start1.value == end2.value) } ) private def overlapsFunction = rangeBuiltinFunction( params = ("range1", "range2"), - invoke = { - case (ValRange(start1, end1), ValRange(start2, end2)) => - ValBoolean( - (end1.value > start2.value || (end1.value == start2.value && end1.isClosed && start2.isClosed)) && (start1.value < end2.value || (start1.value == end2.value && start1.isClosed && end2.isClosed)) - ) + invoke = { case (ValRange(start1, end1), ValRange(start2, end2)) => + ValBoolean( + (end1.value > start2.value || (end1.value == start2.value && end1.isClosed && start2.isClosed)) && (start1.value < end2.value || (start1.value == end2.value && start1.isClosed && end2.isClosed)) + ) } ) private def overlapsBeforeFunction = rangeBuiltinFunction( params = ("range1", "range2"), - invoke = { - case (ValRange(start1, end1), ValRange(start2, end2)) => - ValBoolean( - (start1.value < start2.value || (start1.value == start2.value && start1.isClosed && !start2.isClosed)) && (end1.value > start2.value || (end1.value == start2.value && end1.isClosed && start2.isClosed)) && (end1.value < end2.value || (end1.value == end2.value && (!end1.isClosed || end2.isClosed))) - ) + invoke = { case (ValRange(start1, end1), ValRange(start2, end2)) => + ValBoolean( + (start1.value < start2.value || (start1.value == start2.value && start1.isClosed && !start2.isClosed)) && (end1.value > start2.value || (end1.value == start2.value && end1.isClosed && start2.isClosed)) && (end1.value < end2.value || (end1.value == end2.value && (!end1.isClosed || end2.isClosed))) + ) } ) private def overlapsAfterFunction = rangeBuiltinFunction( params = ("range1", "range2"), - invoke = { - case (ValRange(start1, end1), ValRange(start2, end2)) => - ValBoolean( - (start2.value < start1.value || (start2.value == start1.value && start2.isClosed && !start1.isClosed)) && (end2.value > start1.value || (end2.value == start1.value && end2.isClosed && start1.isClosed)) && (end2.value < end1.value || (end2.value == end1.value && (!end2.isClosed || end1.isClosed))) - ) + invoke = { case (ValRange(start1, end1), ValRange(start2, end2)) => + ValBoolean( + (start2.value < start1.value || (start2.value == start1.value && start2.isClosed && !start1.isClosed)) && (end2.value > start1.value || (end2.value == start1.value && end2.isClosed && start1.isClosed)) && (end2.value < end1.value || (end2.value == end1.value && (!end2.isClosed || end1.isClosed))) + ) } ) @@ -204,7 +202,7 @@ object RangeBuiltinFunction { ValBoolean( end1.isClosed == end2.isClosed && end1.value == end2.value && (start1.value > start2.value || (start1.value == start2.value && (!start1.isClosed || start2.isClosed))) ) - case (point: Val, ValRange(_, end)) => + case (point: Val, ValRange(_, end)) => ValBoolean(end.isClosed && end.value == point) } ) @@ -217,7 +215,7 @@ object RangeBuiltinFunction { ValBoolean( end1.isClosed == end2.isClosed && end1.value == end2.value && (start1.value < start2.value || (start1.value == start2.value && (start1.isClosed || !start2.isClosed))) ) - case (ValRange(_, end), point: Val) => + case (ValRange(_, end), point: Val) => ValBoolean( end.isClosed && end.value == point ) @@ -232,7 +230,7 @@ object RangeBuiltinFunction { ValBoolean( (start1.value < start2.value || (start1.value == start2.value && (start1.isClosed || !start2.isClosed))) && (end1.value > end2.value || (end1.value == end2.value && (end1.isClosed || !end2.isClosed))) ) - case (ValRange(start, end), point: Val) => + case (ValRange(start, end), point: Val) => ValBoolean( (start.value < point && end.value > point) || (start.value == point && start.isClosed) || (end.value == point && end.isClosed) ) @@ -247,7 +245,7 @@ object RangeBuiltinFunction { ValBoolean( (start2.value < start1.value || (start2.value == start1.value && (start2.isClosed || !start1.isClosed))) && (end2.value > end1.value || (end2.value == end1.value && (end2.isClosed || !end1.isClosed))) ) - case (point: Val, ValRange(start, end)) => + case (point: Val, ValRange(start, end)) => ValBoolean( (start.value < point && end.value > point) || (start.value == point && start.isClosed) || (end.value == point && end.isClosed) ) @@ -262,7 +260,7 @@ object RangeBuiltinFunction { ValBoolean( start1.value == start2.value && start1.isClosed == start2.isClosed && (end1.value < end2.value || (end1.value == end2.value && (!end1.isClosed || end2.isClosed))) ) - case (point: Val, ValRange(start, _)) => + case (point: Val, ValRange(start, _)) => ValBoolean(start.value == point && start.isClosed) } ) @@ -275,7 +273,7 @@ object RangeBuiltinFunction { ValBoolean( start1.value == start2.value && start1.isClosed == start2.isClosed && (end2.value < end1.value || (end2.value == end1.value && (!end2.isClosed || end1.isClosed))) ) - case (ValRange(start, _), point: Val) => + case (ValRange(start, _), point: Val) => ValBoolean(start.value == point && start.isClosed) } ) @@ -288,7 +286,7 @@ object RangeBuiltinFunction { ValBoolean( start1.value == start2.value && start1.isClosed == start2.isClosed && end1.value == end2.value && end1.isClosed == end2.isClosed ) - case (point1, point2) => + case (point1, point2) => ValBoolean(point1 == point2) } ) diff --git a/src/main/scala/org/camunda/feel/impl/builtin/StringBuiltinFunctions.scala b/src/main/scala/org/camunda/feel/impl/builtin/StringBuiltinFunctions.scala index e2d0ace89..5308b9685 100644 --- a/src/main/scala/org/camunda/feel/impl/builtin/StringBuiltinFunctions.scala +++ b/src/main/scala/org/camunda/feel/impl/builtin/StringBuiltinFunctions.scala @@ -17,13 +17,7 @@ package org.camunda.feel.impl.builtin import org.camunda.feel.impl.builtin.BuiltinFunction.builtinFunction -import org.camunda.feel.syntaxtree.{ - ValBoolean, - ValError, - ValList, - ValNumber, - ValString -} +import org.camunda.feel.syntaxtree.{ValBoolean, ValError, ValList, ValNumber, ValString} import java.util.regex.Pattern import scala.util.Try @@ -31,37 +25,37 @@ import scala.util.Try object StringBuiltinFunctions { def functions = Map( - "substring" -> List(substringFunction, substringFunction3), - "string length" -> List(stringLengthFunction), - "upper case" -> List(upperCaseFunction), - "lower case" -> List(lowerCaseFunction), + "substring" -> List(substringFunction, substringFunction3), + "string length" -> List(stringLengthFunction), + "upper case" -> List(upperCaseFunction), + "lower case" -> List(lowerCaseFunction), "substring before" -> List(substringBeforeFunction), - "substring after" -> List(substringAfterFunction), - "replace" -> List(replaceFunction, replaceFunction4), - "contains" -> List(containsFunction), - "starts with" -> List(startsWithFunction), - "ends with" -> List(endsWithFunction), - "matches" -> List(matchesFunction, matchesFunction3), - "split" -> List(splitFunction), - "extract" -> List(extractFunction) + "substring after" -> List(substringAfterFunction), + "replace" -> List(replaceFunction, replaceFunction4), + "contains" -> List(containsFunction), + "starts with" -> List(startsWithFunction), + "ends with" -> List(endsWithFunction), + "matches" -> List(matchesFunction, matchesFunction3), + "split" -> List(splitFunction), + "extract" -> List(extractFunction) ) private def substringFunction = builtinFunction( params = List("string", "start position"), - invoke = { - case List(ValString(string), ValNumber(start)) => - ValString(string.substring(stringIndex(string, start.intValue))) + invoke = { case List(ValString(string), ValNumber(start)) => + ValString(string.substring(stringIndex(string, start.intValue))) } ) private def substringFunction3 = builtinFunction( params = List("string", "start position", "length"), - invoke = { - case List(ValString(string), ValNumber(start), ValNumber(length)) => - ValString( - string.substring( - stringIndex(string, start.intValue), - stringIndex(string, start.intValue) + length.intValue)) + invoke = { case List(ValString(string), ValNumber(start), ValNumber(length)) => + ValString( + string.substring( + stringIndex(string, start.intValue), + stringIndex(string, start.intValue) + length.intValue + ) + ) } ) @@ -73,19 +67,28 @@ object StringBuiltinFunctions { } private def stringLengthFunction = - builtinFunction(params = List("string"), invoke = { - case List(ValString(string)) => ValNumber(string.length) - }) + builtinFunction( + params = List("string"), + invoke = { case List(ValString(string)) => + ValNumber(string.length) + } + ) private def upperCaseFunction = - builtinFunction(params = List("string"), invoke = { - case List(ValString(string)) => ValString(string.toUpperCase) - }) + builtinFunction( + params = List("string"), + invoke = { case List(ValString(string)) => + ValString(string.toUpperCase) + } + ) private def lowerCaseFunction = - builtinFunction(params = List("string"), invoke = { - case List(ValString(string)) => ValString(string.toLowerCase) - }) + builtinFunction( + params = List("string"), + invoke = { case List(ValString(string)) => + ValString(string.toLowerCase) + } + ) private def substringBeforeFunction = builtinFunction( params = List("string", "match"), @@ -117,27 +120,23 @@ object StringBuiltinFunctions { private def replaceFunction = builtinFunction( params = List("input", "pattern", "replacement"), - invoke = { - case List(ValString(input), ValString(pattern), ValString(replacement)) => - Try(Pattern.compile(pattern)) - .map { pattern => - val m = pattern.matcher(input) - ValString(m.replaceAll(replacement)) - } - .recover { _ => - ValError(s"Invalid pattern '$pattern'") - } - .get + invoke = { case List(ValString(input), ValString(pattern), ValString(replacement)) => + Try(Pattern.compile(pattern)) + .map { pattern => + val m = pattern.matcher(input) + ValString(m.replaceAll(replacement)) + } + .recover { _ => + ValError(s"Invalid pattern '$pattern'") + } + .get } ) private def replaceFunction4 = builtinFunction( params = List("input", "pattern", "replacement", "flags"), invoke = { - case List(ValString(input), - ValString(pattern), - ValString(replacement), - ValString(flags)) => + case List(ValString(input), ValString(pattern), ValString(replacement), ValString(flags)) => Try(Pattern.compile(pattern, patternFlags(flags))) .map { pattern => val m = pattern.matcher(input) @@ -170,22 +169,28 @@ object StringBuiltinFunctions { } private def containsFunction = - builtinFunction(params = List("string", "match"), invoke = { - case List(ValString(string), ValString(m)) => + builtinFunction( + params = List("string", "match"), + invoke = { case List(ValString(string), ValString(m)) => ValBoolean(string.contains(m)) - }) + } + ) private def startsWithFunction = - builtinFunction(params = List("string", "match"), invoke = { - case List(ValString(string), ValString(m)) => + builtinFunction( + params = List("string", "match"), + invoke = { case List(ValString(string), ValString(m)) => ValBoolean(string.startsWith(m)) - }) + } + ) private def endsWithFunction = - builtinFunction(params = List("string", "match"), invoke = { - case List(ValString(string), ValString(m)) => + builtinFunction( + params = List("string", "match"), + invoke = { case List(ValString(string), ValString(m)) => ValBoolean(string.endsWith(m)) - }) + } + ) private def matchesFunction = builtinFunction( params = List("input", "pattern"), @@ -206,49 +211,46 @@ object StringBuiltinFunctions { private def matchesFunction3 = builtinFunction( params = List("input", "pattern", "flags"), - invoke = { - case List(ValString(input), ValString(pattern), ValString(flags)) => - Try(Pattern.compile(pattern, patternFlags(flags))) - .map { pattern => - val m = pattern.matcher(input) - ValBoolean(m.find) - } - .recover { _ => - ValError(s"Invalid pattern '$pattern'") - } - .get + invoke = { case List(ValString(input), ValString(pattern), ValString(flags)) => + Try(Pattern.compile(pattern, patternFlags(flags))) + .map { pattern => + val m = pattern.matcher(input) + ValBoolean(m.find) + } + .recover { _ => + ValError(s"Invalid pattern '$pattern'") + } + .get } ) private def splitFunction = builtinFunction( params = List("string", "delimiter"), - invoke = { - case List(ValString(string), ValString(delimiter)) => - Try(Pattern.compile(delimiter)) - .map { pattern => - val r = pattern.split(string, -1) - ValList(r.map(ValString).toList) - } - .recover { _ => - ValError(s"Invalid pattern for delimiter '$delimiter'") - } - .get + invoke = { case List(ValString(string), ValString(delimiter)) => + Try(Pattern.compile(delimiter)) + .map { pattern => + val r = pattern.split(string, -1) + ValList(r.map(ValString).toList) + } + .recover { _ => + ValError(s"Invalid pattern for delimiter '$delimiter'") + } + .get } ) private def extractFunction = builtinFunction( params = List("input", "pattern"), - invoke = { - case List(ValString(input), ValString(pattern)) => - Try(pattern.r) - .map { regex => - val matches = regex.findAllIn(input).map(ValString) - ValList(matches.toList) - } - .recover { _ => - ValError(s"Invalid pattern '$pattern'") - } - .get + invoke = { case List(ValString(input), ValString(pattern)) => + Try(pattern.r) + .map { regex => + val matches = regex.findAllIn(input).map(ValString) + ValList(matches.toList) + } + .recover { _ => + ValError(s"Invalid pattern '$pattern'") + } + .get } ) diff --git a/src/main/scala/org/camunda/feel/impl/builtin/TemporalBuiltinFunctions.scala b/src/main/scala/org/camunda/feel/impl/builtin/TemporalBuiltinFunctions.scala index 25743a0b0..ab0c2d122 100644 --- a/src/main/scala/org/camunda/feel/impl/builtin/TemporalBuiltinFunctions.scala +++ b/src/main/scala/org/camunda/feel/impl/builtin/TemporalBuiltinFunctions.scala @@ -36,30 +36,28 @@ import java.util.Locale class TemporalBuiltinFunctions(clock: FeelEngineClock) { def functions = Map( - "now" -> List(nowFunction), - "today" -> List(todayFunction), - "day of year" -> List(dateTimeFunction(getDayOfYear)), - "day of week" -> List(dateTimeFunction(getDayOfWeek)), - "month of year" -> List(dateTimeFunction(getMonthOfYear)), - "week of year" -> List(dateTimeFunction(getWeekOfYear)), + "now" -> List(nowFunction), + "today" -> List(todayFunction), + "day of year" -> List(dateTimeFunction(getDayOfYear)), + "day of week" -> List(dateTimeFunction(getDayOfWeek)), + "month of year" -> List(dateTimeFunction(getMonthOfYear)), + "week of year" -> List(dateTimeFunction(getWeekOfYear)), "last day of month" -> List(dateTimeFunction(getLastDayOfMonth)) ) private def nowFunction = builtinFunction( params = List.empty, - invoke = { - case _ => - val now = clock.getCurrentTime - ValDateTime(now) + invoke = { case _ => + val now = clock.getCurrentTime + ValDateTime(now) } ) private def todayFunction = builtinFunction( params = List.empty, - invoke = { - case _ => - val today = clock.getCurrentTime.toLocalDate - ValDate(today) + invoke = { case _ => + val today = clock.getCurrentTime.toLocalDate + ValDate(today) } ) @@ -79,20 +77,20 @@ class TemporalBuiltinFunctions(clock: FeelEngineClock) { } private def getDayOfWeek(date: Date): ValString = { - val dayOfWeek = date.getDayOfWeek + val dayOfWeek = date.getDayOfWeek val displayName = dayOfWeek.getDisplayName(TextStyle.FULL, Locale.ENGLISH) ValString(displayName) } private def getMonthOfYear(date: Date): ValString = { - val month = date.getMonth + val month = date.getMonth val displayName = month.getDisplayName(TextStyle.FULL, Locale.ENGLISH) ValString(displayName) } private def getWeekOfYear(date: Date): ValNumber = { val temporalField = WeekFields.ISO.weekOfWeekBasedYear() - val weekOfYear = date.get(temporalField) + val weekOfYear = date.get(temporalField) ValNumber(weekOfYear) } diff --git a/src/main/scala/org/camunda/feel/impl/interpreter/BuiltinFunctions.scala b/src/main/scala/org/camunda/feel/impl/interpreter/BuiltinFunctions.scala index 26cfebf54..001901252 100644 --- a/src/main/scala/org/camunda/feel/impl/interpreter/BuiltinFunctions.scala +++ b/src/main/scala/org/camunda/feel/impl/interpreter/BuiltinFunctions.scala @@ -18,7 +18,16 @@ package org.camunda.feel.impl.interpreter import org.camunda.feel.FeelEngineClock import org.camunda.feel.context.FunctionProvider -import org.camunda.feel.impl.builtin.{BooleanBuiltinFunctions, ContextBuiltinFunctions, ConversionBuiltinFunctions, ListBuiltinFunctions, NumericBuiltinFunctions, RangeBuiltinFunction, StringBuiltinFunctions, TemporalBuiltinFunctions} +import org.camunda.feel.impl.builtin.{ + BooleanBuiltinFunctions, + ContextBuiltinFunctions, + ConversionBuiltinFunctions, + ListBuiltinFunctions, + NumericBuiltinFunctions, + RangeBuiltinFunction, + StringBuiltinFunctions, + TemporalBuiltinFunctions +} import org.camunda.feel.syntaxtree.ValFunction import org.camunda.feel.valuemapper.ValueMapper diff --git a/src/main/scala/org/camunda/feel/impl/interpreter/EvalContext.scala b/src/main/scala/org/camunda/feel/impl/interpreter/EvalContext.scala index 8926bcf91..d0e52abc4 100644 --- a/src/main/scala/org/camunda/feel/impl/interpreter/EvalContext.scala +++ b/src/main/scala/org/camunda/feel/impl/interpreter/EvalContext.scala @@ -18,19 +18,34 @@ package org.camunda.feel.impl.interpreter import org.camunda.feel.api.EvaluationFailureType import org.camunda.feel.context.Context.{EmptyContext, StaticContext} -import org.camunda.feel.context.FunctionProvider.{CompositeFunctionProvider, EmptyFunctionProvider, StaticFunctionProvider} -import org.camunda.feel.context.VariableProvider.{CompositeVariableProvider, EmptyVariableProvider, StaticVariableProvider} +import org.camunda.feel.context.FunctionProvider.{ + CompositeFunctionProvider, + EmptyFunctionProvider, + StaticFunctionProvider +} +import org.camunda.feel.context.VariableProvider.{ + CompositeVariableProvider, + EmptyVariableProvider, + StaticVariableProvider +} import org.camunda.feel.context.{Context, FunctionProvider, VariableProvider} -import org.camunda.feel.impl.interpreter.EvalContext.{mergeFunctionProviders, mergeVariableProvider, toSortedVariableProvider, wrap} +import org.camunda.feel.impl.interpreter.EvalContext.{ + mergeFunctionProviders, + mergeVariableProvider, + toSortedVariableProvider, + wrap +} import org.camunda.feel.syntaxtree.{Val, ValError, ValFunction} import org.camunda.feel.valuemapper.ValueMapper import scala.collection.immutable.SeqMap -class EvalContext(val valueMapper: ValueMapper, - val variableProvider: VariableProvider, - val functionProvider: FunctionProvider, - val failureCollector: EvaluationFailureCollector) extends Context { +class EvalContext( + val valueMapper: ValueMapper, + val variableProvider: VariableProvider, + val functionProvider: FunctionProvider, + val failureCollector: EvaluationFailureCollector +) extends Context { def variable(name: String): Val = { variableProvider @@ -40,14 +55,14 @@ class EvalContext(val valueMapper: ValueMapper, } def function(name: String, paramCount: Int): Val = { - val filter = (f: ValFunction) => f.params.size == paramCount || - (f.params.size < paramCount && f.hasVarArgs) + val filter = (f: ValFunction) => + f.params.size == paramCount || + (f.params.size < paramCount && f.hasVarArgs) functionProvider .getFunctions(name) .find(filter) - .getOrElse(ValError( - s"No function found with name '$name' and $paramCount parameters")) + .getOrElse(ValError(s"No function found with name '$name' and $paramCount parameters")) } def function(name: String, parameters: Set[String]): Val = { @@ -56,11 +71,8 @@ class EvalContext(val valueMapper: ValueMapper, functionProvider .getFunctions(name) .find(filter) - .getOrElse(ValError( - s"No function found with name '$name' and parameters: ${ - parameters - .mkString(",") - }")) + .getOrElse(ValError(s"No function found with name '$name' and parameters: ${parameters + .mkString(",")}")) } def merge(otherContext: EvalContext): EvalContext = new EvalContext( @@ -77,7 +89,7 @@ class EvalContext(val valueMapper: ValueMapper, def add(entry: (String, Val)): EvalContext = entry match { case (k: String, f: ValFunction) => addFunction(k, f) - case (k: String, v) => addVariable(k, v) + case (k: String, v) => addVariable(k, v) } private def addVariable(key: String, variable: Val): EvalContext = new EvalContext( @@ -124,54 +136,66 @@ object EvalContext { functionProvider = EmptyFunctionProvider ) - def create(valueMapper: ValueMapper, functionProvider: FunctionProvider): EvalContext = new EvalContext( - valueMapper = valueMapper, - variableProvider = EmptyVariableProvider, - functionProvider = functionProvider, - failureCollector = new EvaluationFailureCollector - ) + def create(valueMapper: ValueMapper, functionProvider: FunctionProvider): EvalContext = + new EvalContext( + valueMapper = valueMapper, + variableProvider = EmptyVariableProvider, + functionProvider = functionProvider, + failureCollector = new EvaluationFailureCollector + ) def wrap(context: Context, valueMapper: ValueMapper): EvalContext = context match { - case evalContext: EvalContext => evalContext - case EmptyContext => empty(valueMapper) - case StaticContext(variables, functions) => new EvalContext( - valueMapper = valueMapper, - variableProvider = toSortedVariableProvider(variables), - functionProvider = StaticFunctionProvider(functions), - failureCollector = new EvaluationFailureCollector - ) - case _ => new EvalContext( - valueMapper = valueMapper, - variableProvider = context.variableProvider, - functionProvider = context.functionProvider, - failureCollector = new EvaluationFailureCollector - ) + case evalContext: EvalContext => evalContext + case EmptyContext => empty(valueMapper) + case StaticContext(variables, functions) => + new EvalContext( + valueMapper = valueMapper, + variableProvider = toSortedVariableProvider(variables), + functionProvider = StaticFunctionProvider(functions), + failureCollector = new EvaluationFailureCollector + ) + case _ => + new EvalContext( + valueMapper = valueMapper, + variableProvider = context.variableProvider, + functionProvider = context.functionProvider, + failureCollector = new EvaluationFailureCollector + ) } - private def mergeVariableProvider(provider: VariableProvider, otherProvider: VariableProvider): VariableProvider = { + private def mergeVariableProvider( + provider: VariableProvider, + otherProvider: VariableProvider + ): VariableProvider = { (provider, otherProvider) match { - case (EmptyVariableProvider, EmptyVariableProvider) => EmptyVariableProvider - case (EmptyVariableProvider, otherProvider) => otherProvider - case (thisProvider, EmptyVariableProvider) => thisProvider + case (EmptyVariableProvider, EmptyVariableProvider) => EmptyVariableProvider + case (EmptyVariableProvider, otherProvider) => otherProvider + case (thisProvider, EmptyVariableProvider) => thisProvider case (StaticVariableProvider(thisVariables), StaticVariableProvider(otherVariables)) => StaticVariableProvider(thisVariables ++ otherVariables) - case (thisProvider, otherProvider) => CompositeVariableProvider(List(thisProvider, otherProvider)) + case (thisProvider, otherProvider) => + CompositeVariableProvider(List(thisProvider, otherProvider)) } } - private def mergeFunctionProviders(provider: FunctionProvider, otherProvider: FunctionProvider): FunctionProvider = { + private def mergeFunctionProviders( + provider: FunctionProvider, + otherProvider: FunctionProvider + ): FunctionProvider = { (provider, otherProvider) match { - case (EmptyFunctionProvider, EmptyFunctionProvider) => EmptyFunctionProvider - case (EmptyFunctionProvider, otherProvider) => otherProvider - case (thisProvider, EmptyFunctionProvider) => thisProvider + case (EmptyFunctionProvider, EmptyFunctionProvider) => EmptyFunctionProvider + case (EmptyFunctionProvider, otherProvider) => otherProvider + case (thisProvider, EmptyFunctionProvider) => thisProvider case (StaticFunctionProvider(thisFunctions), StaticFunctionProvider(otherFunctions)) => { - val allKeys = thisFunctions.keys ++ otherFunctions.keys - val functionsByKey = (key: String) => thisFunctions.getOrElse(key, List.empty) ++ otherFunctions.getOrElse(key, List.empty) - val allFunctions = allKeys.map(key => key -> functionsByKey(key)).toMap + val allKeys = thisFunctions.keys ++ otherFunctions.keys + val functionsByKey = (key: String) => + thisFunctions.getOrElse(key, List.empty) ++ otherFunctions.getOrElse(key, List.empty) + val allFunctions = allKeys.map(key => key -> functionsByKey(key)).toMap StaticFunctionProvider(allFunctions) } - case (thisProvider, otherProvider) => CompositeFunctionProvider(List(thisProvider, otherProvider)) + case (thisProvider, otherProvider) => + CompositeFunctionProvider(List(thisProvider, otherProvider)) } } diff --git a/src/main/scala/org/camunda/feel/impl/interpreter/FeelInterpreter.scala b/src/main/scala/org/camunda/feel/impl/interpreter/FeelInterpreter.scala index bfd4f489b..ad7064f76 100644 --- a/src/main/scala/org/camunda/feel/impl/interpreter/FeelInterpreter.scala +++ b/src/main/scala/org/camunda/feel/impl/interpreter/FeelInterpreter.scala @@ -21,12 +21,21 @@ import org.camunda.feel.api.EvaluationFailureType import org.camunda.feel.context.Context import org.camunda.feel.syntaxtree._ import org.camunda.feel.valuemapper.ValueMapper -import org.camunda.feel.{Date, DateTime, DayTimeDuration, LocalDateTime, LocalTime, Number, Time, YearMonthDuration} +import org.camunda.feel.{ + Date, + DateTime, + DayTimeDuration, + LocalDateTime, + LocalTime, + Number, + Time, + YearMonthDuration +} import java.time.{Duration, Period} -/** - * @author Philipp Ossler +/** @author + * Philipp Ossler */ class FeelInterpreter { @@ -53,9 +62,9 @@ class FeelInterpreter { case ConstContext(entries) => foldEither[(String, Exp), EvalContext]( EvalContext.empty(context.valueMapper), - entries, { - case (ctx, (key, value)) => - eval(value)(context.merge(ctx)).toEither.map(v => ctx.add(key -> v)) + entries, + { case (ctx, (key, value)) => + eval(value)(context.merge(ctx)).toEither.map(v => ctx.add(key -> v)) }, ValContext ) @@ -63,40 +72,40 @@ class FeelInterpreter { case range: ConstRange => toRange(range) // simple unary tests - case InputEqualTo(x) => + case InputEqualTo(x) => withVal(input, i => checkEquality(i, eval(x), _ == _, ValBoolean)) - case InputLessThan(x) => + case InputLessThan(x) => withVal(input, i => dualOp(i, eval(x), _ < _, ValBoolean)) - case InputLessOrEqual(x) => + case InputLessOrEqual(x) => withVal(input, i => dualOp(i, eval(x), _ <= _, ValBoolean)) - case InputGreaterThan(x) => + case InputGreaterThan(x) => withVal(input, i => dualOp(i, eval(x), _ > _, ValBoolean)) - case InputGreaterOrEqual(x) => + case InputGreaterOrEqual(x) => withVal(input, i => dualOp(i, eval(x), _ >= _, ValBoolean)) case InputInRange(range @ ConstRange(start, end)) => - unaryOpDual(eval(start.value), - eval(end.value), - isInRange(range), - ValBoolean) + unaryOpDual(eval(start.value), eval(end.value), isInRange(range), ValBoolean) case UnaryTestExpression(x) => withVal(eval(x), unaryTestExpression) // arithmetic operations - case Addition(x, y) => withValOrNull(addOp(eval(x), eval(y))) - case Subtraction(x, y) => withValOrNull(subOp(eval(x), eval(y))) - case Multiplication(x, y) => withValOrNull(mulOp(eval(x), eval(y))) - case Division(x, y) => withValOrNull(divOp(eval(x), eval(y))) - case Exponentiation(x, y) => + case Addition(x, y) => withValOrNull(addOp(eval(x), eval(y))) + case Subtraction(x, y) => withValOrNull(subOp(eval(x), eval(y))) + case Multiplication(x, y) => withValOrNull(mulOp(eval(x), eval(y))) + case Division(x, y) => withValOrNull(divOp(eval(x), eval(y))) + case Exponentiation(x, y) => withValOrNull( - dualNumericOp(eval(x), - eval(y), - (x, y) => - if (y.isWhole) { - x.pow(y.toInt) - } else { - math.pow(x.toDouble, y.toDouble) - }, - ValNumber)) + dualNumericOp( + eval(x), + eval(y), + (x, y) => + if (y.isWhole) { + x.pow(y.toInt) + } else { + math.pow(x.toDouble, y.toDouble) + }, + ValNumber + ) + ) case ArithmeticNegation(x) => withValOrNull(withNumber(eval(x), x => ValNumber(-x))) @@ -115,63 +124,69 @@ class FeelInterpreter { // control structures case If(condition, statement, elseStatement) => - withBooleanOrFalse(eval(condition), - isMet => - if (isMet) { - eval(statement) - } else { - eval(elseStatement) - }) - case In(x, test) => + withBooleanOrFalse( + eval(condition), + isMet => + if (isMet) { + eval(statement) + } else { + eval(elseStatement) + } + ) + case In(x, test) => withVal(eval(x), x => eval(test)(context.add(inputKey -> x))) - case InstanceOf(x, typeName) => - withVal(eval(x), x => { - typeName match { - case "Any" if x != ValNull => ValBoolean(true) - case "years and months duration" => withType(x, t => ValBoolean(t == "year-month-duration")) - case "days and time duration" => withType(x, t => ValBoolean(t == "day-time-duration")) - case "date and time" => withType(x, t => ValBoolean(t == "date time")) - case _ => withType(x, t => ValBoolean(t == typeName)) + case InstanceOf(x, typeName) => + withVal( + eval(x), + x => { + typeName match { + case "Any" if x != ValNull => ValBoolean(true) + case "years and months duration" => + withType(x, t => ValBoolean(t == "year-month-duration")) + case "days and time duration" => + withType(x, t => ValBoolean(t == "day-time-duration")) + case "date and time" => withType(x, t => ValBoolean(t == "date time")) + case _ => withType(x, t => ValBoolean(t == typeName)) + } } - }) + ) // context - case Ref(names) => + case Ref(names) => val name = names.head context.variable(name) match { case _: ValError => error(EvaluationFailureType.NO_VARIABLE_FOUND, s"No variable found with name '$name'") ValNull - case value => ref(value, names.tail) + case value => ref(value, names.tail) } case PathExpression(exp, key) => withVal(eval(exp), v => path(v, key)) // list - case SomeItem(iterators, condition) => + case SomeItem(iterators, condition) => withCartesianProduct( iterators, p => - atLeastOneValue(p.map(vars => () => eval(condition)(context.addAll(vars))), - ValBoolean)) + atLeastOneValue(p.map(vars => () => eval(condition)(context.addAll(vars))), ValBoolean) + ) case EveryItem(iterators, condition) => withCartesianProduct( iterators, - p => - allValues(p.map(vars => () => eval(condition)(context.addAll(vars))), - ValBoolean)) - case For(iterators, exp) => + p => allValues(p.map(vars => () => eval(condition)(context.addAll(vars))), ValBoolean) + ) + case For(iterators, exp) => withCartesianProduct( iterators, p => ValList((List[Val]() /: p) { case (partial, vars) => { val iterationContext = context.addAll(vars).add("partial" -> ValList(partial)) - val value = eval(exp)(iterationContext) + val value = eval(exp)(iterationContext) partial ++ (value :: Nil) } }) ) - case Filter(list, filter) => + case Filter(list, filter) => withList( eval(list), l => { @@ -179,13 +194,12 @@ class FeelInterpreter { (item: Val) => eval(filter)(filterContext(item)) filter match { - case ConstNumber(index) => filterList(l.items, index) - case ArithmeticNegation(ConstNumber(index)) => + case ConstNumber(index) => filterList(l.items, index) + case ArithmeticNegation(ConstNumber(index)) => filterList(l.items, -index) - case _: Comparison | _: FunctionInvocation | - _: QualifiedFunctionInvocation => + case _: Comparison | _: FunctionInvocation | _: QualifiedFunctionInvocation => filterList(l.items, evalFilterWithItem) - case _ => + case _ => eval(filter) match { case ValNumber(index) => filterList(l.items, index) case _ => filterList(l.items, evalFilterWithItem) @@ -193,50 +207,62 @@ class FeelInterpreter { } } ) - case IterationContext(start, end) => - withNumbers(eval(start), eval(end), (x, y) => { - val range = if (x < y) { - (x to y).by(1) - } else { - (x to y).by(-1) + case IterationContext(start, end) => + withNumbers( + eval(start), + eval(end), + (x, y) => { + val range = if (x < y) { + (x to y).by(1) + } else { + (x to y).by(-1) + } + ValList(range.map(ValNumber).toList) } - ValList(range.map(ValNumber).toList) - }) + ) // functions - case FunctionInvocation(name, params) => - withFunction(findFunction(context, name, params), - f => invokeFunction(f, params) match { - case ValError(failure) if name == "assert" => - error(EvaluationFailureType.ASSERT_FAILURE, failure) - ValError(failure) - case ValError(failure) => - error(EvaluationFailureType.FUNCTION_INVOCATION_FAILURE, s"Failed to invoke function '$name': $failure") - ValNull - case result => result - }) + case FunctionInvocation(name, params) => + withFunction( + findFunction(context, name, params), + f => + invokeFunction(f, params) match { + case ValError(failure) if name == "assert" => + error(EvaluationFailureType.ASSERT_FAILURE, failure) + ValError(failure) + case ValError(failure) => + error( + EvaluationFailureType.FUNCTION_INVOCATION_FAILURE, + s"Failed to invoke function '$name': $failure" + ) + ValNull + case result => result + } + ) case QualifiedFunctionInvocation(path, name, params) => withContext( eval(path), c => withFunction( - findFunction(EvalContext.wrap(c.context, context.valueMapper), - name, - params), - f => invokeFunction(f, params))) - case FunctionDefinition(params, body) => + findFunction(EvalContext.wrap(c.context, context.valueMapper), name, params), + f => invokeFunction(f, params) + ) + ) + case FunctionDefinition(params, body) => ValFunction( params, paramValues => body match { case JavaFunctionInvocation(className, methodName, arguments) => - invokeJavaFunction(className, - methodName, - arguments, - paramValues, - context.valueMapper) - case _ => eval(body)(context.addAll((params zip paramValues).toMap)) - } + invokeJavaFunction( + className, + methodName, + arguments, + paramValues, + context.valueMapper + ) + case _ => eval(body)(context.addAll((params zip paramValues).toMap)) + } ) // unsupported expression @@ -244,19 +270,28 @@ class FeelInterpreter { } - private def mapEither[T, R](it: Iterable[T], - f: T => Either[ValError, R], - resultMapping: List[R] => Val): Val = { - - foldEither[T, List[R]](List(), it, { - case (xs, x) => f(x).map(xs :+ _) - }, resultMapping) + private def mapEither[T, R]( + it: Iterable[T], + f: T => Either[ValError, R], + resultMapping: List[R] => Val + ): Val = { + + foldEither[T, List[R]]( + List(), + it, + { case (xs, x) => + f(x).map(xs :+ _) + }, + resultMapping + ) } - private def foldEither[T, R](start: R, - it: Iterable[T], - op: (R, T) => Either[ValError, R], - resultMapping: R => Val): Val = { + private def foldEither[T, R]( + start: R, + it: Iterable[T], + op: (R, T) => Either[ValError, R], + resultMapping: R => Val + ): Val = { val result = it.foldLeft[Either[ValError, R]](Right(start)) { (result, x) => result.flatMap(xs => op(xs, x)) @@ -268,36 +303,45 @@ class FeelInterpreter { } } - private def error(failureType: EvaluationFailureType, failureMessage: String)(implicit context: EvalContext): ValError = { - context.addFailure(failureType, failureMessage) - ValError(failureMessage) + private def error(failureType: EvaluationFailureType, failureMessage: String)(implicit + context: EvalContext + ): ValError = { + context.addFailure(failureType, failureMessage) + ValError(failureMessage) } private def withValOrNull(x: Val): Val = x match { case _: ValError => ValNull - case _ => x + case _ => x } - private def unaryOpDual( - x: Val, - y: Val, - c: (Val, Val, Val) => Boolean, - f: Boolean => Val)(implicit context: EvalContext): Val = + private def unaryOpDual(x: Val, y: Val, c: (Val, Val, Val) => Boolean, f: Boolean => Val)(implicit + context: EvalContext + ): Val = withVal( - input, { + input, + { case i if !isComparable(i, x, y) || !hasSameType(i, x, y) => error(EvaluationFailureType.NOT_COMPARABLE, s"Can't compare '$input' with '$x' and '$y'") ValNull - case i => f(c(i, x, y)) + case i => f(c(i, x, y)) } ) - private def withNumbers(x: Val, y: Val, f: (Number, Number) => Val)(implicit context: EvalContext): Val = - withNumber(x, x => { - withNumber(y, y => { - f(x, y) - }) - }) + private def withNumbers(x: Val, y: Val, f: (Number, Number) => Val)(implicit + context: EvalContext + ): Val = + withNumber( + x, + x => { + withNumber( + y, + y => { + f(x, y) + } + ) + } + ) private def withNumber(x: Val, f: Number => Val)(implicit context: EvalContext): Val = x match { case ValNumber(x) => f(x) @@ -306,22 +350,24 @@ class FeelInterpreter { private def withBoolean(x: Val, f: Boolean => Val)(implicit context: EvalContext): Val = x match { case ValBoolean(x) => f(x) - case _ => error(EvaluationFailureType.INVALID_TYPE,s"Expected boolean but found '$x'") + case _ => error(EvaluationFailureType.INVALID_TYPE, s"Expected boolean but found '$x'") } - private def withBooleanOrNull(x: Val, f: Boolean => Val)(implicit context: EvalContext): Val = x match { - case ValBoolean(x) => f(x) - case _ => - error(EvaluationFailureType.INVALID_TYPE, s"Expected boolean but found '$x'") - ValNull - } + private def withBooleanOrNull(x: Val, f: Boolean => Val)(implicit context: EvalContext): Val = + x match { + case ValBoolean(x) => f(x) + case _ => + error(EvaluationFailureType.INVALID_TYPE, s"Expected boolean but found '$x'") + ValNull + } - private def withBooleanOrFalse(x: Val, f: Boolean => Val)(implicit context: EvalContext): Val = x match { - case ValBoolean(x) => f(x) - case _ => - error(EvaluationFailureType.INVALID_TYPE, s"Expected boolean but found '$x'") - f(false) - } + private def withBooleanOrFalse(x: Val, f: Boolean => Val)(implicit context: EvalContext): Val = + x match { + case ValBoolean(x) => f(x) + case _ => + error(EvaluationFailureType.INVALID_TYPE, s"Expected boolean but found '$x'") + f(false) + } private def withString(x: Val, f: String => Val)(implicit context: EvalContext): Val = x match { case ValString(x) => f(x) @@ -333,37 +379,48 @@ class FeelInterpreter { case _ => error(EvaluationFailureType.INVALID_TYPE, s"Expected date but found '$x'") } - private def withLocalTime(x: Val, f: LocalTime => Val)(implicit context: EvalContext): Val = x match { - case ValLocalTime(x) => f(x) - case _ => error(EvaluationFailureType.INVALID_TYPE, s"Expected local time but found '$x'") - } + private def withLocalTime(x: Val, f: LocalTime => Val)(implicit context: EvalContext): Val = + x match { + case ValLocalTime(x) => f(x) + case _ => error(EvaluationFailureType.INVALID_TYPE, s"Expected local time but found '$x'") + } private def withTime(x: Val, f: Time => Val)(implicit context: EvalContext): Val = x match { case ValTime(x) => f(x) case _ => error(EvaluationFailureType.INVALID_TYPE, s"Expected time but found '$x'") } - private def withDateTime(x: Val, f: DateTime => Val)(implicit context: EvalContext): Val = x match { - case ValDateTime(x) => f(x) - case _ => error(EvaluationFailureType.INVALID_TYPE, s"Expected date-and-time but found '$x'") - } + private def withDateTime(x: Val, f: DateTime => Val)(implicit context: EvalContext): Val = + x match { + case ValDateTime(x) => f(x) + case _ => error(EvaluationFailureType.INVALID_TYPE, s"Expected date-and-time but found '$x'") + } - private def withLocalDateTime(x: Val, f: LocalDateTime => Val)(implicit context: EvalContext): Val = + private def withLocalDateTime(x: Val, f: LocalDateTime => Val)(implicit + context: EvalContext + ): Val = x match { case ValLocalDateTime(x) => f(x) - case _ => error(EvaluationFailureType.INVALID_TYPE, s"Expected local date-and-time but found '$x'") + case _ => + error(EvaluationFailureType.INVALID_TYPE, s"Expected local date-and-time but found '$x'") } - private def withYearMonthDuration(x: Val, f: YearMonthDuration => Val)(implicit context: EvalContext): Val = + private def withYearMonthDuration(x: Val, f: YearMonthDuration => Val)(implicit + context: EvalContext + ): Val = x match { case ValYearMonthDuration(x) => f(x) - case _ => error(EvaluationFailureType.INVALID_TYPE, s"Expected years-months-duration but found '$x'") + case _ => + error(EvaluationFailureType.INVALID_TYPE, s"Expected years-months-duration but found '$x'") } - private def withDayTimeDuration(x: Val, f: DayTimeDuration => Val)(implicit context: EvalContext): Val = + private def withDayTimeDuration(x: Val, f: DayTimeDuration => Val)(implicit + context: EvalContext + ): Val = x match { case ValDayTimeDuration(x) => f(x) - case _ => error(EvaluationFailureType.INVALID_TYPE, s"Expected days-time-duration but found '$x'") + case _ => + error(EvaluationFailureType.INVALID_TYPE, s"Expected days-time-duration but found '$x'") } private def withVal(x: Val, f: Val => Val): Val = x match { @@ -381,42 +438,44 @@ class FeelInterpreter { case OpenConstRangeBoundary(_) => i > x case ClosedConstRangeBoundary(_) => i >= x } - val inEnd = range.end match { + val inEnd = range.end match { case OpenConstRangeBoundary(_) => i < y case ClosedConstRangeBoundary(_) => i <= y } inStart && inEnd } - private def atLeastOne(xs: List[Exp], f: Boolean => Val)( - implicit context: EvalContext): Val = + private def atLeastOne(xs: List[Exp], f: Boolean => Val)(implicit context: EvalContext): Val = atLeastOneValue(xs map (x => () => eval(x)), f) - private def atLeastOneValue(items: List[() => Val], f: Boolean => Val)(implicit context: EvalContext): Val = { + private def atLeastOneValue(items: List[() => Val], f: Boolean => Val)(implicit + context: EvalContext + ): Val = { items.foldLeft(f(false)) { case (ValBoolean(true), _) => f(true) - case (ValNull, item) => + case (ValNull, item) => item() match { case ValBoolean(true) => f(true) case _ => ValNull } - case (_, item) => withBooleanOrNull(item(), f) + case (_, item) => withBooleanOrNull(item(), f) } } - private def all(xs: List[Exp], f: Boolean => Val)( - implicit context: EvalContext): Val = + private def all(xs: List[Exp], f: Boolean => Val)(implicit context: EvalContext): Val = allValues(xs map (x => () => eval(x)), f) - private def allValues(items: List[() => Val], f: Boolean => Val)(implicit context: EvalContext): Val = { + private def allValues(items: List[() => Val], f: Boolean => Val)(implicit + context: EvalContext + ): Val = { items.foldLeft(f(true)) { case (ValBoolean(false), _) => f(false) - case (ValNull, item) => + case (ValNull, item) => item() match { case ValBoolean(false) => f(false) case _ => ValNull } - case (_, item) => withBooleanOrNull(item(), f) + case (_, item) => withBooleanOrNull(item(), f) } } @@ -431,43 +490,39 @@ class FeelInterpreter { case _: ValError => error(EvaluationFailureType.NO_VARIABLE_FOUND, s"No input value found.") ValNull - case inputValue => inputValue + case inputValue => inputValue } - private def dualNumericOp( - x: Val, - y: Val, - op: (Number, Number) => Number, - f: Number => Val)(implicit context: EvalContext): Val = + private def dualNumericOp(x: Val, y: Val, op: (Number, Number) => Number, f: Number => Val)( + implicit context: EvalContext + ): Val = x match { case ValNumber(x) => withNumber(y, y => f(op(x, y))) case _ => error(EvaluationFailureType.INVALID_TYPE, s"Expected number but found '$x'") } - private def checkEquality( - x: Val, - y: Val, - c: (Any, Any) => Boolean, - f: Boolean => Val)(implicit context: EvalContext): Val = + private def checkEquality(x: Val, y: Val, c: (Any, Any) => Boolean, f: Boolean => Val)(implicit + context: EvalContext + ): Val = x match { - case ValNull => f(c(ValNull, y.toOption.getOrElse(ValNull))) - case x if (y == ValNull) => f(c(x.toOption.getOrElse(ValNull), ValNull)) - case _ : ValError => f(c(ValNull, y.toOption.getOrElse(ValNull))) + case ValNull => f(c(ValNull, y.toOption.getOrElse(ValNull))) + case x if (y == ValNull) => f(c(x.toOption.getOrElse(ValNull), ValNull)) + case _: ValError => f(c(ValNull, y.toOption.getOrElse(ValNull))) case _ if (y.isInstanceOf[ValError]) => f(c(ValNull, x.toOption.getOrElse(ValNull))) - case _ if !hasSameType(x, y) => + case _ if !hasSameType(x, y) => error(EvaluationFailureType.NOT_COMPARABLE, s"Can't compare '$x' with '$y'") ValNull - case ValNumber(x) => withNumber(y, y => f(c(x, y))) - case ValBoolean(x) => withBoolean(y, y => f(c(x, y))) - case ValString(x) => withString(y, y => f(c(x, y))) - case ValDate(x) => withDate(y, y => f(c(x, y))) - case ValLocalTime(x) => withLocalTime(y, y => f(c(x, y))) - case ValTime(x) => withTime(y, y => f(c(x, y))) - case ValLocalDateTime(x) => withLocalDateTime(y, y => f(c(x, y))) - case ValDateTime(x) => withDateTime(y, y => f(c(x, y))) - case ValYearMonthDuration(x) => withYearMonthDuration(y, y => f(c(x, y))) - case ValDayTimeDuration(x) => withDayTimeDuration(y, y => f(c(x, y))) - case ValList(x) => + case ValNumber(x) => withNumber(y, y => f(c(x, y))) + case ValBoolean(x) => withBoolean(y, y => f(c(x, y))) + case ValString(x) => withString(y, y => f(c(x, y))) + case ValDate(x) => withDate(y, y => f(c(x, y))) + case ValLocalTime(x) => withLocalTime(y, y => f(c(x, y))) + case ValTime(x) => withTime(y, y => f(c(x, y))) + case ValLocalDateTime(x) => withLocalDateTime(y, y => f(c(x, y))) + case ValDateTime(x) => withDateTime(y, y => f(c(x, y))) + case ValYearMonthDuration(x) => withYearMonthDuration(y, y => f(c(x, y))) + case ValDayTimeDuration(x) => withDayTimeDuration(y, y => f(c(x, y))) + case ValList(x) => withList( y, y => { @@ -475,20 +530,19 @@ class FeelInterpreter { f(false) } else { - val isEqual = x.zip(y.items).foldRight(true) { - case ((x, y), listIsEqual) => - listIsEqual && { - checkEquality(x, y, c, f) match { - case ValBoolean(itemIsEqual) => itemIsEqual - case _ => false - } + val isEqual = x.zip(y.items).foldRight(true) { case ((x, y), listIsEqual) => + listIsEqual && { + checkEquality(x, y, c, f) match { + case ValBoolean(itemIsEqual) => itemIsEqual + case _ => false } + } } f(isEqual) } } ) - case ValContext(x) => + case ValContext(x) => withContext( y, y => { @@ -499,65 +553,63 @@ class FeelInterpreter { f(false) } else { - val isEqual = xVars.keys.foldRight(true) { - case (key, contextIsEqual) => - contextIsEqual && { - val xVal = context.valueMapper.toVal(xVars(key)) - val yVal = context.valueMapper.toVal(yVars(key)) - - checkEquality(xVal, yVal, c, f) match { - case ValBoolean(entryIsEqual) => entryIsEqual - case _ => false - } + val isEqual = xVars.keys.foldRight(true) { case (key, contextIsEqual) => + contextIsEqual && { + val xVal = context.valueMapper.toVal(xVars(key)) + val yVal = context.valueMapper.toVal(yVars(key)) + + checkEquality(xVal, yVal, c, f) match { + case ValBoolean(entryIsEqual) => entryIsEqual + case _ => false } + } } f(isEqual) } } ) - case _ => + case _ => error(EvaluationFailureType.NOT_COMPARABLE, s"Can't compare '$x' with '$y'") ValNull } - private def dualOp(x: Val, - y: Val, - c: (Val, Val) => Boolean, - f: Boolean => Val)(implicit context: EvalContext): Val = + private def dualOp(x: Val, y: Val, c: (Val, Val) => Boolean, f: Boolean => Val)(implicit + context: EvalContext + ): Val = x match { case _ if !isComparable(x, y) || !hasSameType(x, y) => error(EvaluationFailureType.NOT_COMPARABLE, s"Can't compare '$x' with '$y'") ValNull - case _ => f(c(x, y)) + case _ => f(c(x, y)) } private def addOp(x: Val, y: Val)(implicit context: EvalContext): Val = x match { - case ValNumber(x) => withNumber(y, y => ValNumber(x + y)) - case ValString(x) => withString(y, y => ValString(x + y)) - case ValLocalTime(x) => withDayTimeDuration(y, y => ValLocalTime(x.plus(y))) - case ValTime(x) => withDayTimeDuration(y, y => ValTime(x.plus(y))) - case ValLocalDateTime(x) => + case ValNumber(x) => withNumber(y, y => ValNumber(x + y)) + case ValString(x) => withString(y, y => ValString(x + y)) + case ValLocalTime(x) => withDayTimeDuration(y, y => ValLocalTime(x.plus(y))) + case ValTime(x) => withDayTimeDuration(y, y => ValTime(x.plus(y))) + case ValLocalDateTime(x) => y match { case ValYearMonthDuration(y) => ValLocalDateTime(x.plus(y)) case ValDayTimeDuration(y) => ValLocalDateTime(x.plus(y)) - case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't add '$y' to '$x'") + case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't add '$y' to '$x'") } - case ValDateTime(x) => + case ValDateTime(x) => y match { case ValYearMonthDuration(y) => ValDateTime(x.plus(y)) case ValDayTimeDuration(y) => ValDateTime(x.plus(y)) - case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't add '$y' to '$x'") + case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't add '$y' to '$x'") } case ValYearMonthDuration(x) => y match { case ValYearMonthDuration(y) => ValYearMonthDuration(x.plus(y).normalized) - case ValLocalDateTime(y) => ValLocalDateTime(y.plus(x)) - case ValDateTime(y) => ValDateTime(y.plus(x)) - case ValDate(y) => ValDate(y.plus(x)) - case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't add '$y' to '$x'") + case ValLocalDateTime(y) => ValLocalDateTime(y.plus(x)) + case ValDateTime(y) => ValDateTime(y.plus(x)) + case ValDate(y) => ValDate(y.plus(x)) + case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't add '$y' to '$x'") } - case ValDayTimeDuration(x) => + case ValDayTimeDuration(x) => y match { case ValDayTimeDuration(y) => ValDayTimeDuration(x.plus(y)) case ValLocalDateTime(y) => ValLocalDateTime(y.plus(x)) @@ -565,97 +617,93 @@ class FeelInterpreter { case ValLocalTime(y) => ValLocalTime(y.plus(x)) case ValTime(y) => ValTime(y.plus(x)) case ValDate(y) => ValDate(y.atStartOfDay().plus(x).toLocalDate()) - case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't add '$y' to '$x'") + case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't add '$y' to '$x'") } - case ValDate(x) => + case ValDate(x) => y match { - case ValDayTimeDuration(y) => + case ValDayTimeDuration(y) => ValDate(x.atStartOfDay().plus(y).toLocalDate()) case ValYearMonthDuration(y) => ValDate(x.plus(y)) - case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't add '$y' to '$x'") + case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't add '$y' to '$x'") } - case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't add '$y' to '$x'") + case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't add '$y' to '$x'") } private def subOp(x: Val, y: Val)(implicit context: EvalContext): Val = x match { - case ValNumber(x) => withNumber(y, y => ValNumber(x - y)) - case ValLocalTime(x) => + case ValNumber(x) => withNumber(y, y => ValNumber(x - y)) + case ValLocalTime(x) => y match { case ValLocalTime(y) => ValDayTimeDuration(Duration.between(y, x)) case ValDayTimeDuration(y) => ValLocalTime(x.minus(y)) case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't subtract '$y' from '$x'") } - case ValTime(x) => + case ValTime(x) => y match { case ValTime(y) => ValDayTimeDuration(ZonedTime.between(x, y)) case ValDayTimeDuration(y) => ValTime(x.minus(y)) case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't subtract '$y' from '$x'") } - case ValLocalDateTime(x) => + case ValLocalDateTime(x) => y match { case ValLocalDateTime(y) => ValDayTimeDuration(Duration.between(y, x)) case ValYearMonthDuration(y) => ValLocalDateTime(x.minus(y)) case ValDayTimeDuration(y) => ValLocalDateTime(x.minus(y)) - case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't subtract '$y' from '$x'") + case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't subtract '$y' from '$x'") } - case ValDateTime(x) => + case ValDateTime(x) => y match { case ValDateTime(y) => ValDayTimeDuration(Duration.between(y, x)) case ValYearMonthDuration(y) => ValDateTime(x.minus(y)) case ValDayTimeDuration(y) => ValDateTime(x.minus(y)) - case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't subtract '$y' from '$x'") + case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't subtract '$y' from '$x'") } - case ValDate(x) => + case ValDate(x) => y match { - case ValDate(y) => + case ValDate(y) => ValDayTimeDuration(Duration.between(y.atStartOfDay, x.atStartOfDay)) case ValYearMonthDuration(y) => ValDate(x.minus(y)) - case ValDayTimeDuration(y) => + case ValDayTimeDuration(y) => ValDate(x.atStartOfDay.minus(y).toLocalDate()) - case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't subtract '$y' from '$x'") + case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't subtract '$y' from '$x'") } case ValYearMonthDuration(x) => withYearMonthDuration(y, y => ValYearMonthDuration(x.minus(y).normalized)) - case ValDayTimeDuration(x) => + case ValDayTimeDuration(x) => withDayTimeDuration(y, y => ValDayTimeDuration(x.minus(y))) - case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't subtract '$y' from '$x'") + case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't subtract '$y' from '$x'") } private def mulOp(x: Val, y: Val)(implicit context: EvalContext): Val = x match { - case ValNumber(x) => + case ValNumber(x) => y match { - case ValNumber(y) => ValNumber(x * y) + case ValNumber(y) => ValNumber(x * y) case ValYearMonthDuration(y) => ValYearMonthDuration(y.multipliedBy(x.intValue).normalized) - case ValDayTimeDuration(y) => + case ValDayTimeDuration(y) => ValDayTimeDuration(y.multipliedBy(x.intValue)) - case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't multiply '$x' by '$y'") + case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't multiply '$x' by '$y'") } case ValYearMonthDuration(x) => - withNumber( - y, - y => ValYearMonthDuration(x.multipliedBy(y.intValue).normalized)) - case ValDayTimeDuration(x) => + withNumber(y, y => ValYearMonthDuration(x.multipliedBy(y.intValue).normalized)) + case ValDayTimeDuration(x) => withNumber(y, y => ValDayTimeDuration(x.multipliedBy(y.intValue))) - case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't multiply '$x' by '$y'") + case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't multiply '$x' by '$y'") } private def divOp(x: Val, y: Val)(implicit context: EvalContext): Val = y match { case ValNumber(y) if (y != 0) => x match { - case ValNumber(x) => ValNumber(x / y) + case ValNumber(x) => ValNumber(x / y) case ValYearMonthDuration(x) => - ValYearMonthDuration( - Period.ofMonths((x.toTotalMonths() / y).intValue).normalized) - case ValDayTimeDuration(x) => + ValYearMonthDuration(Period.ofMonths((x.toTotalMonths() / y).intValue).normalized) + case ValDayTimeDuration(x) => ValDayTimeDuration(Duration.ofMillis((x.toMillis() / y).intValue)) - case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't divide '$x' by '$y'") + case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't divide '$x' by '$y'") } case ValYearMonthDuration(y) if (!y.isZero) => - withYearMonthDuration(x, - x => ValNumber(x.toTotalMonths / y.toTotalMonths)) - case ValDayTimeDuration(y) if (!y.isZero) => + withYearMonthDuration(x, x => ValNumber(x.toTotalMonths / y.toTotalMonths)) + case ValDayTimeDuration(y) if (!y.isZero) => withDayTimeDuration(x, x => ValNumber(x.toMillis / y.toMillis)) case _ => error(EvaluationFailureType.INVALID_TYPE, s"Can't divide '$x' by '$y'") @@ -666,31 +714,35 @@ class FeelInterpreter { input, i => x match { - case ValBoolean(true) => ValBoolean(true) // the expression is true - case ValList(ys) if ys.contains(i) => ValBoolean(true) // the expression contains the input value + case ValBoolean(true) => ValBoolean(true) // the expression is true + case ValList(ys) if ys.contains(i) => + ValBoolean(true) // the expression contains the input value case _ => checkEquality(i, x, _ == _, ValBoolean) match { - case ValBoolean(true) => ValBoolean(true) // the expression is the input value - case _ if x == ValBoolean(false) => ValBoolean(false) // the expression is false - case _ if x.isInstanceOf[ValList] => ValBoolean(false) // the expression is a list but doesn't contain the input value - case ValNull => ValNull // the expression can't be compared to the input value - case _ => ValBoolean(false) // the expression is not the input value + case ValBoolean(true) => ValBoolean(true) // the expression is the input value + case _ if x == ValBoolean(false) => ValBoolean(false) // the expression is false + case _ if x.isInstanceOf[ValList] => + ValBoolean(false) // the expression is a list but doesn't contain the input value + case ValNull => ValNull // the expression can't be compared to the input value + case _ => ValBoolean(false) // the expression is not the input value } } ) - private def withFunction(x: Val, f: ValFunction => Val)(implicit context: EvalContext): Val = x match { - case x: ValFunction => f(x) - case ValError(failure) => - error(EvaluationFailureType.NO_FUNCTION_FOUND, failure) - ValNull - case _ => - error(EvaluationFailureType.INVALID_TYPE, s"Expected function but found '$x'") - ValNull - } + private def withFunction(x: Val, f: ValFunction => Val)(implicit context: EvalContext): Val = + x match { + case x: ValFunction => f(x) + case ValError(failure) => + error(EvaluationFailureType.NO_FUNCTION_FOUND, failure) + ValNull + case _ => + error(EvaluationFailureType.INVALID_TYPE, s"Expected function but found '$x'") + ValNull + } - private def invokeFunction(function: ValFunction, params: FunctionParameters)( - implicit context: EvalContext): Val = { + private def invokeFunction(function: ValFunction, params: FunctionParameters)(implicit + context: EvalContext + ): Val = { val paramList: List[Val] = params match { case PositionalFunctionParameters(params) => { @@ -720,43 +772,44 @@ class FeelInterpreter { function.invoke(paramList) match { case e: ValError => e - case result => context.valueMapper.toVal(result) + case result => context.valueMapper.toVal(result) } } - private def findFunction(ctx: EvalContext, - name: String, - params: FunctionParameters): Val = params match { - case PositionalFunctionParameters(params) => ctx.function(name, params.size) - case NamedFunctionParameters(params) => ctx.function(name, params.keySet) - } + private def findFunction(ctx: EvalContext, name: String, params: FunctionParameters): Val = + params match { + case PositionalFunctionParameters(params) => ctx.function(name, params.size) + case NamedFunctionParameters(params) => ctx.function(name, params.keySet) + } - private def withType(x: Val, f: String => ValBoolean)(implicit context: EvalContext): Val = x match { - case ValNumber(_) => f("number") - case ValBoolean(_) => f("boolean") - case ValString(_) => f("string") - case ValDate(_) => f("date") - case ValLocalTime(_) => f("time") - case ValTime(_) => f("time") - case ValLocalDateTime(_) => f("date time") - case ValDateTime(_) => f("date time") - case ValYearMonthDuration(_) => f("year-month-duration") - case ValDayTimeDuration(_) => f("day-time-duration") - case ValNull => f("null") - case ValList(_) => f("list") - case ValContext(_) => f("context") - case ValFunction(_, _, _) => f("function") - case _ => error(EvaluationFailureType.INVALID_TYPE ,s"Unknown type '${x.getClass.getName}' of '$x'") - } + private def withType(x: Val, f: String => ValBoolean)(implicit context: EvalContext): Val = + x match { + case ValNumber(_) => f("number") + case ValBoolean(_) => f("boolean") + case ValString(_) => f("string") + case ValDate(_) => f("date") + case ValLocalTime(_) => f("time") + case ValTime(_) => f("time") + case ValLocalDateTime(_) => f("date time") + case ValDateTime(_) => f("date time") + case ValYearMonthDuration(_) => f("year-month-duration") + case ValDayTimeDuration(_) => f("day-time-duration") + case ValNull => f("null") + case ValList(_) => f("list") + case ValContext(_) => f("context") + case ValFunction(_, _, _) => f("function") + case _ => + error(EvaluationFailureType.INVALID_TYPE, s"Unknown type '${x.getClass.getName}' of '$x'") + } private def withList(x: Val, f: ValList => Val)(implicit context: EvalContext): Val = x match { case x: ValList => f(x) case _ => error(EvaluationFailureType.INVALID_TYPE, s"Expected list but found '$x'") } - private def withLists( - lists: List[(String, Val)], - f: List[(String, ValList)] => Val)(implicit context: EvalContext): Val = { + private def withLists(lists: List[(String, Val)], f: List[(String, ValList)] => Val)(implicit + context: EvalContext + ): Val = { lists .map { case (name, it) => name -> withList(it, list => list) } .find(_._2.isInstanceOf[ValError]) match { @@ -767,57 +820,70 @@ class FeelInterpreter { private def withCartesianProduct( iterators: List[(String, Exp)], - f: List[Map[String, Val]] => Val)(implicit context: EvalContext): Val = - withLists(iterators.map { case (name, it) => name -> eval(it) }, - lists => f(flattenAndZipLists(lists))) - - private def flattenAndZipLists( - lists: List[(String, ValList)]): List[Map[String, Val]] = lists match { - case Nil => List() - case (name, list) :: Nil => list.items map (v => Map(name -> v)) // flatten - case (name, list) :: tail => - for { v <- list.items; values <- flattenAndZipLists(tail) } yield - values + (name -> v) // zip - } + f: List[Map[String, Val]] => Val + )(implicit context: EvalContext): Val = + withLists( + iterators.map { case (name, it) => name -> eval(it) }, + lists => f(flattenAndZipLists(lists)) + ) - private def filterList(list: List[Val], filter: Val => Val)( - implicit context: EvalContext): Val = { + private def flattenAndZipLists(lists: List[(String, ValList)]): List[Map[String, Val]] = + lists match { + case Nil => List() + case (name, list) :: Nil => list.items map (v => Map(name -> v)) // flatten + case (name, list) :: tail => + for { + v <- list.items; values <- flattenAndZipLists(tail) + } yield values + (name -> v) // zip + } + + private def filterList(list: List[Val], filter: Val => Val)(implicit + context: EvalContext + ): Val = { val conditionNotFulfilled = ValString("_") - val withBooleanFilter = (list: List[Val]) => mapEither[Val, Val]( - list, - item => - (filter(item) match { - case ValBoolean(true) => item - case _ => conditionNotFulfilled - }).toEither, - items => ValList(items.filterNot(_ == conditionNotFulfilled)) - ) + val withBooleanFilter = (list: List[Val]) => + mapEither[Val, Val]( + list, + item => + (filter(item) match { + case ValBoolean(true) => item + case _ => conditionNotFulfilled + }).toEither, + items => ValList(items.filterNot(_ == conditionNotFulfilled)) + ) // The filter function could return a boolean or a number. If it returns a number then we use // the number as the index for the list. Otherwise, the boolean function determine if the // condition is fulfilled for the given item. // Note that the code could look more elegant but we want to avoid unintended invocations of // the function because the invocations could be observed by the function provider (see #359). - list.headOption.map(head => - withVal(filter(head), { - case ValNumber(index) => filterList(list, index) - case ValBoolean(isFulFilled) => withBooleanFilter(list.tail) match { - case ValList(fulFilledItems) if isFulFilled => ValList(head :: fulFilledItems) - case fulFilledItems: ValList => fulFilledItems - case error => error - } - case _ => withBooleanFilter(list.tail) match { - case ValList(fulFilledItems) => ValList(fulFilledItems) - case error => error - } - }) - ).getOrElse( - // Return always an empty list if the given list is empty. Note that we would return `null` - // instead, if the filter is a number. But if it is a function, we would need to evaluate the - // function first to see that it returns a number. - ValList(List.empty) - ) + list.headOption + .map(head => + withVal( + filter(head), + { + case ValNumber(index) => filterList(list, index) + case ValBoolean(isFulFilled) => + withBooleanFilter(list.tail) match { + case ValList(fulFilledItems) if isFulFilled => ValList(head :: fulFilledItems) + case fulFilledItems: ValList => fulFilledItems + case error => error + } + case _ => + withBooleanFilter(list.tail) match { + case ValList(fulFilledItems) => ValList(fulFilledItems) + case error => error + } + } + ) + ) + .getOrElse( + // Return always an empty list if the given list is empty. Note that we would return `null` + // instead, if the filter is a number. But if it is a function, we would need to evaluate the + // function first to see that it returns a number. + ValList(List.empty) + ) } private def filterList(list: List[Val], index: Number): Val = { @@ -837,22 +903,21 @@ class FeelInterpreter { } } - private def withContext(x: Val, f: ValContext => Val)(implicit context: EvalContext): Val = x match { - case x: ValContext => f(x) - case _ => error(EvaluationFailureType.INVALID_TYPE, s"Expect context but found '$x'") - } + private def withContext(x: Val, f: ValContext => Val)(implicit context: EvalContext): Val = + x match { + case x: ValContext => f(x) + case _ => error(EvaluationFailureType.INVALID_TYPE, s"Expect context but found '$x'") + } - private def filterContext(x: Val)( - implicit context: EvalContext): EvalContext = + private def filterContext(x: Val)(implicit context: EvalContext): EvalContext = x match { case ValContext(ctx: Context) => context.add("item" -> x).merge(ctx) case v => context.add("item" -> v) } - private def ref(x: Val, names: List[String])( - implicit context: EvalContext): Val = + private def ref(x: Val, names: List[String])(implicit context: EvalContext): Val = names match { - case Nil => x + case Nil => x case n :: ns => withVal( path(x, n), @@ -866,7 +931,7 @@ class FeelInterpreter { EvalContext.wrap(ctx.context, context.valueMapper).variable(key) match { case _: ValError => val detailedMessage = ctx.context.variableProvider.keys match { - case Nil => "The context is empty" + case Nil => "The context is empty" case keys => s"Available keys: ${keys.map("'" + _ + "'").mkString(", ")}" } error( @@ -874,35 +939,37 @@ class FeelInterpreter { failureMessage = s"No context entry found with key '$key'. $detailedMessage" ) ValNull - case x: Val => x + case x: Val => x } - case ValList(list) => ValList(list map (item => path(item, key))) - case ValNull => + case ValList(list) => ValList(list map (item => path(item, key))) + case ValNull => error( failureType = EvaluationFailureType.NO_CONTEXT_ENTRY_FOUND, failureMessage = s"No context entry found with key '$key'. The context is null" ) ValNull - case value => + case value => value.property(key).getOrElse { val propertyNames: String = value.propertyNames().map("'" + _ + "'").mkString(", ") error( failureType = EvaluationFailureType.NO_PROPERTY_FOUND, - failureMessage = s"No property found with name '$key' of value '$value'. Available properties: $propertyNames" + failureMessage = + s"No property found with name '$key' of value '$value'. Available properties: $propertyNames" ) ValNull } } - private def evalContextEntry(key: String, exp: Exp)( - implicit context: EvalContext): Val = + private def evalContextEntry(key: String, exp: Exp)(implicit context: EvalContext): Val = withVal(eval(exp), value => value) - private def invokeJavaFunction(className: String, - methodName: String, - arguments: List[String], - paramValues: List[Val], - valueMapper: ValueMapper)(implicit context: EvalContext): Val = { + private def invokeJavaFunction( + className: String, + methodName: String, + arguments: List[String], + paramValues: List[Val], + valueMapper: ValueMapper + )(implicit context: EvalContext): Val = { try { val clazz = JavaClassMapper.loadClass(className) @@ -911,8 +978,8 @@ class FeelInterpreter { val method = clazz.getDeclaredMethod(methodName, argTypes: _*) - val argJavaObjects = paramValues zip argTypes map { - case (obj, clazz) => JavaClassMapper.asJavaObject(obj, clazz) + val argJavaObjects = paramValues zip argTypes map { case (obj, clazz) => + JavaClassMapper.asJavaObject(obj, clazz) } val result = method.invoke(null, argJavaObjects: _*) @@ -921,13 +988,20 @@ class FeelInterpreter { } catch { case e: ClassNotFoundException => - error(EvaluationFailureType.FUNCTION_INVOCATION_FAILURE, s"Failed to load class '$className'") - case e: NoSuchMethodException => - error(EvaluationFailureType.FUNCTION_INVOCATION_FAILURE, - s"Failed to get method with name '$methodName' and arguments '$arguments' from class '$className'") - case _: Throwable => - error(EvaluationFailureType.FUNCTION_INVOCATION_FAILURE, - s"Failed to invoke method with name '$methodName' and arguments '$arguments' from class '$className'") + error( + EvaluationFailureType.FUNCTION_INVOCATION_FAILURE, + s"Failed to load class '$className'" + ) + case e: NoSuchMethodException => + error( + EvaluationFailureType.FUNCTION_INVOCATION_FAILURE, + s"Failed to get method with name '$methodName' and arguments '$arguments' from class '$className'" + ) + case _: Throwable => + error( + EvaluationFailureType.FUNCTION_INVOCATION_FAILURE, + s"Failed to invoke method with name '$methodName' and arguments '$arguments' from class '$className'" + ) } } @@ -945,8 +1019,8 @@ class FeelInterpreter { ) } else { error(EvaluationFailureType.INVALID_TYPE, s"Invalid range definition '$range'") - } - ) + } + ) ) } @@ -963,8 +1037,7 @@ class FeelInterpreter { case _ => false } - private def toRangeBoundary(boundary: ConstRangeBoundary, - value: Val): RangeBoundary = { + private def toRangeBoundary(boundary: ConstRangeBoundary, value: Val): RangeBoundary = { boundary match { case OpenConstRangeBoundary(_) => OpenRangeBoundary(value) case ClosedConstRangeBoundary(_) => ClosedRangeBoundary(value) diff --git a/src/main/scala/org/camunda/feel/impl/interpreter/JavaClassMapper.scala b/src/main/scala/org/camunda/feel/impl/interpreter/JavaClassMapper.scala index dadb52184..1be8ec864 100644 --- a/src/main/scala/org/camunda/feel/impl/interpreter/JavaClassMapper.scala +++ b/src/main/scala/org/camunda/feel/impl/interpreter/JavaClassMapper.scala @@ -16,16 +16,10 @@ */ package org.camunda.feel.impl.interpreter -import org.camunda.feel.syntaxtree.{ - Val, - ValBoolean, - ValNull, - ValNumber, - ValString -} +import org.camunda.feel.syntaxtree.{Val, ValBoolean, ValNull, ValNumber, ValString} -/** - * @author Philipp +/** @author + * Philipp */ object JavaClassMapper { @@ -45,21 +39,20 @@ object JavaClassMapper { def asJavaObject(value: Val, clazz: Class[_]): java.lang.Object = (value, clazz) match { - case (ValNull, _) => null + case (ValNull, _) => null case (ValBoolean(b), java.lang.Boolean.TYPE) => java.lang.Boolean.valueOf(b) - case (ValString(s), stringClass) => java.lang.String.valueOf(s) - case (ValNumber(n), java.lang.Integer.TYPE) => + case (ValString(s), stringClass) => java.lang.String.valueOf(s) + case (ValNumber(n), java.lang.Integer.TYPE) => java.lang.Integer.valueOf(n.intValue) - case (ValNumber(n), java.lang.Long.TYPE) => + case (ValNumber(n), java.lang.Long.TYPE) => java.lang.Long.valueOf(n.longValue) - case (ValNumber(n), java.lang.Float.TYPE) => + case (ValNumber(n), java.lang.Float.TYPE) => java.lang.Float.valueOf(n.floatValue) - case (ValNumber(n), java.lang.Double.TYPE) => + case (ValNumber(n), java.lang.Double.TYPE) => java.lang.Double.valueOf(n.doubleValue) - case _ => - throw new IllegalArgumentException( - s"can not cast value '$value' to class '$clazz'") + case _ => + throw new IllegalArgumentException(s"can not cast value '$value' to class '$clazz'") } } diff --git a/src/main/scala/org/camunda/feel/impl/interpreter/ObjectContext.scala b/src/main/scala/org/camunda/feel/impl/interpreter/ObjectContext.scala index 03064c442..e9e9e4256 100644 --- a/src/main/scala/org/camunda/feel/impl/interpreter/ObjectContext.scala +++ b/src/main/scala/org/camunda/feel/impl/interpreter/ObjectContext.scala @@ -21,15 +21,15 @@ import org.camunda.feel.syntaxtree.ValFunction import java.lang.reflect.Method -/** - * A context that wraps the fields and methods of a given JVM object +/** A context that wraps the fields and methods of a given JVM object * - * @param obj the JVM object to be wrapped + * @param obj + * the JVM object to be wrapped */ case class ObjectContext(obj: Any) extends Context { private lazy val publicFields = obj.getClass.getFields - private lazy val allFields = obj.getClass.getDeclaredFields + private lazy val allFields = obj.getClass.getDeclaredFields private lazy val publicMethodsWithoutArguments = obj.getClass.getMethods .filter(method => method.getParameterCount == 0) @@ -41,20 +41,19 @@ case class ObjectContext(obj: Any) extends Context { fieldForName.map(_.get(obj)) orElse { val methods = publicMethodsWithoutArguments find (method => - isGetterOf(method, name) || isBooleanGetterOf(method, name)) + isGetterOf(method, name) || isBooleanGetterOf(method, name) + ) methods.map(_.invoke(obj)) } } override def keys: Iterable[String] = { - val fieldsWithPublicGetter = allFields.filter( - field => - publicMethodsWithoutArguments.exists( - method => - isGetterOf(method, field.getName) || isBooleanGetterOf( - method, - field.getName))) + val fieldsWithPublicGetter = allFields.filter(field => + publicMethodsWithoutArguments.exists(method => + isGetterOf(method, field.getName) || isBooleanGetterOf(method, field.getName) + ) + ) publicFields.map(_.getName) ++ fieldsWithPublicGetter.map(_.getName) } @@ -73,10 +72,10 @@ case class ObjectContext(obj: Any) extends Context { params, params => { - val paramJavaObjects = params zip method.getParameterTypes map { - case (obj, clazz) => JavaClassMapper.asJavaObject(obj, clazz) + val paramJavaObjects = params zip method.getParameterTypes map { case (obj, clazz) => + JavaClassMapper.asJavaObject(obj, clazz) } - val result = method.invoke(obj, paramJavaObjects: _*) + val result = method.invoke(obj, paramJavaObjects: _*) result } ) @@ -103,8 +102,9 @@ case class ObjectContext(obj: Any) extends Context { private def isBooleanGetterOf(method: Method, fieldName: String): Boolean = { val returnType = method.getReturnType - method.getName == getBooleanGetterName(fieldName) && (returnType == java.lang.Boolean.TYPE || returnType == classOf[ - java.lang.Boolean]) + method.getName == getBooleanGetterName( + fieldName + ) && (returnType == java.lang.Boolean.TYPE || returnType == classOf[java.lang.Boolean]) } } diff --git a/src/main/scala/org/camunda/feel/impl/parser/ExpressionValidator.scala b/src/main/scala/org/camunda/feel/impl/parser/ExpressionValidator.scala index 3399ea8bb..f18bc764b 100644 --- a/src/main/scala/org/camunda/feel/impl/parser/ExpressionValidator.scala +++ b/src/main/scala/org/camunda/feel/impl/parser/ExpressionValidator.scala @@ -62,12 +62,15 @@ class ExpressionValidator(externalFunctionsEnabled: Boolean) { private def validate(exp: Exp): List[Failure] = exp match { // validate expression case JavaFunctionInvocation(_, _, _) if !externalFunctionsEnabled => - List(Failure( - "External functions are disabled. Use the FunctionProvider SPI (recommended) or enable external function in the configuration.")) + List( + Failure( + "External functions are disabled. Use the FunctionProvider SPI (recommended) or enable external function in the configuration." + ) + ) // delegate to inner expression - case ConstList(items) => items.flatMap(validate) - case ConstContext(entries) => + case ConstList(items) => items.flatMap(validate) + case ConstContext(entries) => entries.flatMap { case (_, value) => validate(value) } case ConstRange(start, end) => validate(start.value) ++ validate(end.value) @@ -93,20 +96,18 @@ class ExpressionValidator(externalFunctionsEnabled: Boolean) { case If(condition, statement, elseStatement) => validate(condition) ++ validate(statement) ++ validate(elseStatement) - case Disjunction(x, y) => validate(x) ++ validate(y) - case Conjunction(x, y) => validate(x) ++ validate(y) - case In(x, test) => validate(x) ++ validate(test) + case Disjunction(x, y) => validate(x) ++ validate(y) + case Conjunction(x, y) => validate(x) ++ validate(y) + case In(x, test) => validate(x) ++ validate(test) - case AtLeastOne(xs) => xs.flatMap(validate) - case SomeItem(iterators, condition) => - iterators.flatMap { case (_, value) => validate(value) } ++ validate( - condition) + case AtLeastOne(xs) => xs.flatMap(validate) + case SomeItem(iterators, condition) => + iterators.flatMap { case (_, value) => validate(value) } ++ validate(condition) case EveryItem(iterators, condition) => - iterators.flatMap { case (_, value) => validate(value) } ++ validate( - condition) - case For(iterators, body) => + iterators.flatMap { case (_, value) => validate(value) } ++ validate(condition) + case For(iterators, body) => iterators.flatMap { case (_, value) => validate(value) } ++ validate(body) - case Filter(list, filter) => validate(list) ++ validate(filter) + case Filter(list, filter) => validate(list) ++ validate(filter) case PathExpression(path, _) => validate(path) case Not(x) => validate(x) diff --git a/src/main/scala/org/camunda/feel/impl/parser/FeelParser.scala b/src/main/scala/org/camunda/feel/impl/parser/FeelParser.scala index d697ef328..0017e584a 100644 --- a/src/main/scala/org/camunda/feel/impl/parser/FeelParser.scala +++ b/src/main/scala/org/camunda/feel/impl/parser/FeelParser.scala @@ -113,8 +113,7 @@ import org.camunda.feel.{ import scala.util.Try -/** - * The parser is written following the FEEL grammar definition in the DMN specification. +/** The parser is written following the FEEL grammar definition in the DMN specification. * * In order to understand how the parser works, it is recommended to read the documentation first: * [[https://www.lihaoyi.com/fastparse]]. Additional resources: @@ -163,9 +162,7 @@ object FeelParser { private def javaLikeIdentifier[_: P]: P[String] = P( - CharPred(Character.isJavaIdentifierStart) ~~ CharsWhile( - Character.isJavaIdentifierPart, - 0) + CharPred(Character.isJavaIdentifierStart) ~~ CharsWhile(Character.isJavaIdentifierPart, 0) ).! // an identifier wrapped in backticks. it can contain any char (e.g. `a b`, `a+b`). @@ -197,11 +194,10 @@ object FeelParser { // --------------- utility parsers --------------- // shortcut function to define an optional parser - private def optional[_: P](optionalParser: Exp => P[Exp]): Exp => P[Exp] = { - base => - optionalParser(base).?.map( - _.fold(base)(result => result) - ) + private def optional[_: P](optionalParser: Exp => P[Exp]): Exp => P[Exp] = { base => + optionalParser(base).?.map( + _.fold(base)(result => result) + ) } // --------------- expressions --------------- @@ -238,15 +234,15 @@ object FeelParser { private def ifOp[_: P]: P[Exp] = P( "if" ~ expression ~ "then" ~ expression ~ "else" ~ expression - ).map { - case (condition, thenExp, elseExp) => If(condition, thenExp, elseExp) + ).map { case (condition, thenExp, elseExp) => + If(condition, thenExp, elseExp) } private def forOp[_: P]: P[Exp] = P( "for" ~ listIterator.rep(1, sep = ",") ~ "return" ~ expression - ).map { - case (iterators, exp) => For(iterators.toList, exp) + ).map { case (iterators, exp) => + For(iterators.toList, exp) } private def listIterator[_: P]: P[(String, Exp)] = P( @@ -256,8 +252,8 @@ object FeelParser { private def iterationContext[_: P]: P[Exp] = P( expLvl4 ~ ".." ~ expLvl4 - ).map { - case (start, end) => IterationContext(start, end) + ).map { case (start, end) => + IterationContext(start, end) } private def quantifiedOp[_: P]: P[Exp] = @@ -265,7 +261,7 @@ object FeelParser { ("some" | "every").! ~ listIterator .rep(1, sep = ",") ~ "satisfies" ~ expression ).map { - case ("some", iterators, condition) => + case ("some", iterators, condition) => SomeItem(iterators.toList, condition) case ("every", iterators, condition) => EveryItem(iterators.toList, condition) @@ -274,15 +270,15 @@ object FeelParser { private def disjunction[_: P]: P[Exp] = P( expLvl2 ~ ("or" ~ expLvl2).rep - ).map { - case (base, ops) => ops.foldLeft(base)(Disjunction) + ).map { case (base, ops) => + ops.foldLeft(base)(Disjunction) } private def conjunction[_: P]: P[Exp] = P( expLvl3 ~ ("and" ~ expLvl3).rep - ).map { - case (base, ops) => ops.foldLeft(base)(Conjunction) + ).map { case (base, ops) => + ops.foldLeft(base)(Conjunction) } private def comparison[_: P](value: Exp): P[Exp] = @@ -303,8 +299,8 @@ object FeelParser { private def between[_: P](x: Exp): P[Exp] = P( "between" ~ expLvl4 ~ "and" ~ expLvl4 - ).map { - case (a, b) => Conjunction(GreaterOrEqual(x, a), LessOrEqual(x, b)) + ).map { case (a, b) => + Conjunction(GreaterOrEqual(x, a), LessOrEqual(x, b)) } private def instanceOf[_: P](value: Exp): P[Exp] = @@ -336,30 +332,28 @@ object FeelParser { private def addSub[_: P]: P[Exp] = P( mathOpLvl2 ~ (CharIn("+\\-").! ~ mathOpLvl2).rep - ).map { - case (value, ops) => - ops.foldLeft(value) { - case (x, ("+", y)) => Addition(x, y) - case (x, ("-", y)) => Subtraction(x, y) - } + ).map { case (value, ops) => + ops.foldLeft(value) { + case (x, ("+", y)) => Addition(x, y) + case (x, ("-", y)) => Subtraction(x, y) + } } private def mulDiv[_: P]: P[Exp] = P( mathOpLvl3 ~ (CharIn("*/").! ~ mathOpLvl3).rep - ).map { - case (value, ops) => - ops.foldLeft(value) { - case (x, ("*", y)) => Multiplication(x, y) - case (x, ("/", y)) => Division(x, y) - } + ).map { case (value, ops) => + ops.foldLeft(value) { + case (x, ("*", y)) => Multiplication(x, y) + case (x, ("/", y)) => Division(x, y) + } } private def exponent[_: P]: P[Exp] = P( mathOpLvl4 ~ ("**" ~ mathOpLvl4).rep - ).map { - case (value, ops) => ops.foldLeft(value)(Exponentiation) + ).map { case (value, ops) => + ops.foldLeft(value)(Exponentiation) } private def mathNegation[_: P]: P[Exp] = @@ -469,8 +463,8 @@ object FeelParser { P( "function" ~ "(" ~ parameter .rep(0, sep = ",") ~ ")" ~ (externalFunction | expression) - ).map { - case (parameters, body) => FunctionDefinition(parameters.toList, body) + ).map { case (parameters, body) => + FunctionDefinition(parameters.toList, body) } private def parameter[_: P]: P[String] = parameterName @@ -489,9 +483,8 @@ object FeelParser { "class" ~ ":" ~ stringWithQuotes ~ "," ~ "method signature" ~ ":" ~ javaMethodSignature ~ "}" ~ "}" - ).map { - case (className, (methodName, parameters)) => - JavaFunctionInvocation(className, methodName, parameters.toList) + ).map { case (className, (methodName, parameters)) => + JavaFunctionInvocation(className, methodName, parameters.toList) } private def javaMethodSignature[_: P]: P[(String, Seq[String])] = P( @@ -508,18 +501,18 @@ object FeelParser { ((identifierWithWhitespaces | functionNameWithReservedWord) .map(List(_)) | qualifiedName) ~ "(" ~ functionParameters.? ~ ")" ).map { - case (name :: Nil, None) => + case (name :: Nil, None) => FunctionInvocation(name, PositionalFunctionParameters(List.empty)) case (name :: Nil, Some(parameters)) => FunctionInvocation(name, parameters) - case (names, None) => - QualifiedFunctionInvocation(Ref(names.dropRight(1)), - names.last, - PositionalFunctionParameters(List.empty)) - case (names, Some(parameters)) => - QualifiedFunctionInvocation(Ref(names.dropRight(1)), - names.last, - parameters) + case (names, None) => + QualifiedFunctionInvocation( + Ref(names.dropRight(1)), + names.last, + PositionalFunctionParameters(List.empty) + ) + case (names, Some(parameters)) => + QualifiedFunctionInvocation(Ref(names.dropRight(1)), names.last, parameters) } // List all built-in function names that contains a reserved word. These names are not allowed as @@ -616,8 +609,8 @@ object FeelParser { private def range[_: P]: P[ConstRange] = P( rangeStart ~ ".." ~ rangeEnd - ).map { - case (start, end) => ConstRange(start, end) + ).map { case (start, end) => + ConstRange(start, end) } private def rangeStart[_: P]: P[ConstRangeBoundary] = diff --git a/src/main/scala/org/camunda/feel/impl/script/CompiledFeelScript.scala b/src/main/scala/org/camunda/feel/impl/script/CompiledFeelScript.scala index 1322cd95f..2703a14cd 100644 --- a/src/main/scala/org/camunda/feel/impl/script/CompiledFeelScript.scala +++ b/src/main/scala/org/camunda/feel/impl/script/CompiledFeelScript.scala @@ -19,8 +19,7 @@ package org.camunda.feel.impl.script import javax.script.{CompiledScript, ScriptContext, ScriptEngine} import org.camunda.feel.syntaxtree.ParsedExpression -case class CompiledFeelScript(engine: FeelScriptEngine, - val expression: ParsedExpression) +case class CompiledFeelScript(engine: FeelScriptEngine, val expression: ParsedExpression) extends CompiledScript { def getEngine: ScriptEngine = engine diff --git a/src/main/scala/org/camunda/feel/impl/script/FeelExpressionScriptEngine.scala b/src/main/scala/org/camunda/feel/impl/script/FeelExpressionScriptEngine.scala index 40398c9b9..130e146d2 100644 --- a/src/main/scala/org/camunda/feel/impl/script/FeelExpressionScriptEngine.scala +++ b/src/main/scala/org/camunda/feel/impl/script/FeelExpressionScriptEngine.scala @@ -20,8 +20,7 @@ import org.camunda.feel.impl.parser.FeelParser import javax.script.ScriptEngineFactory -class FeelExpressionScriptEngine(val factory: ScriptEngineFactory) - extends FeelScriptEngine { +class FeelExpressionScriptEngine(val factory: ScriptEngineFactory) extends FeelScriptEngine { val eval = (expression: String, context: Map[String, Any]) => engine.evalExpression(expression, context) diff --git a/src/main/scala/org/camunda/feel/impl/script/FeelScriptEngine.scala b/src/main/scala/org/camunda/feel/impl/script/FeelScriptEngine.scala index 4a83ebe57..119e323b8 100644 --- a/src/main/scala/org/camunda/feel/impl/script/FeelScriptEngine.scala +++ b/src/main/scala/org/camunda/feel/impl/script/FeelScriptEngine.scala @@ -38,10 +38,7 @@ import javax.script.{ import scala.annotation.tailrec import scala.jdk.CollectionConverters.MapHasAsScala -trait FeelScriptEngine - extends AbstractScriptEngine - with ScriptEngine - with Compilable { +trait FeelScriptEngine extends AbstractScriptEngine with ScriptEngine with Compilable { val eval: (String, Map[String, Any]) => EvalExpressionResult @@ -68,14 +65,14 @@ trait FeelScriptEngine def eval(script: String, context: ScriptContext): Object = { val engineContext = getEngineContext(context) - val result = eval(script, engineContext) + val result = eval(script, engineContext) handleEvaluationResult(result) } def eval(script: CompiledFeelScript, context: ScriptContext): Object = { val engineContext = getEngineContext(context) - val result = engine.eval(script.expression, engineContext) + val result = engine.eval(script.expression, engineContext) handleEvaluationResult(result) } @@ -87,11 +84,12 @@ trait FeelScriptEngine } def compile(script: String): CompiledScript = parse(script) match { - case Parsed.Success(exp, _) => + case Parsed.Success(exp, _) => CompiledFeelScript(this, ParsedExpression(exp, script)) case Parsed.Failure(_, _, extra) => throw new ScriptException( - s"failed to parse expression '$script':\n${extra.trace().aggregateMsg}") + s"failed to parse expression '$script':\n${extra.trace().aggregateMsg}" + ) } private def handleEvaluationResult(result: EvalExpressionResult): Object = @@ -118,13 +116,12 @@ trait FeelScriptEngine } @tailrec - private def read(reader: Reader, - buffer: StringBuffer = new StringBuffer): String = { + private def read(reader: Reader, buffer: StringBuffer = new StringBuffer): String = { val chars = new Array[Char](16 * 1024) reader.read(chars, 0, chars.length) match { case -1 => buffer.toString - case i => + case i => buffer.append(chars, 0, i) read(reader, buffer) } diff --git a/src/main/scala/org/camunda/feel/impl/script/FeelScriptEngineFactory.scala b/src/main/scala/org/camunda/feel/impl/script/FeelScriptEngineFactory.scala index efd8126d6..05b46e3c4 100644 --- a/src/main/scala/org/camunda/feel/impl/script/FeelScriptEngineFactory.scala +++ b/src/main/scala/org/camunda/feel/impl/script/FeelScriptEngineFactory.scala @@ -19,8 +19,8 @@ package org.camunda.feel.impl.script import javax.script.{ScriptEngine, ScriptEngineFactory} import scala.jdk.CollectionConverters.SeqHasAsJava -/** - * @author Philipp Ossler +/** @author + * Philipp Ossler */ class FeelScriptEngineFactory extends ScriptEngineFactory { @@ -42,10 +42,7 @@ class FeelScriptEngineFactory extends ScriptEngineFactory { def getMimeTypes(): java.util.List[String] = List.empty.asJava def getNames(): java.util.List[String] = - List(ENGINE_NAME, - LANGUAGE_NAME, - LANGUAGE_SHORT_NAME, - LANGUAGE_QUALIFIED_NAME).asJava + List(ENGINE_NAME, LANGUAGE_NAME, LANGUAGE_SHORT_NAME, LANGUAGE_QUALIFIED_NAME).asJava def getOutputStatement(x$1: String): String = throw new UnsupportedOperationException() diff --git a/src/main/scala/org/camunda/feel/impl/script/FeelUnaryTestsScriptEngine.scala b/src/main/scala/org/camunda/feel/impl/script/FeelUnaryTestsScriptEngine.scala index a726f6420..9f9720a58 100644 --- a/src/main/scala/org/camunda/feel/impl/script/FeelUnaryTestsScriptEngine.scala +++ b/src/main/scala/org/camunda/feel/impl/script/FeelUnaryTestsScriptEngine.scala @@ -20,8 +20,7 @@ import org.camunda.feel.impl.parser.FeelParser import javax.script.ScriptEngineFactory -class FeelUnaryTestsScriptEngine(val factory: ScriptEngineFactory) - extends FeelScriptEngine { +class FeelUnaryTestsScriptEngine(val factory: ScriptEngineFactory) extends FeelScriptEngine { val eval = (expression: String, context: Map[String, Any]) => engine.evalUnaryTests(expression, context) diff --git a/src/main/scala/org/camunda/feel/impl/script/FeelUnaryTestsScriptEngineFactory.scala b/src/main/scala/org/camunda/feel/impl/script/FeelUnaryTestsScriptEngineFactory.scala index d588d474d..c796309ec 100644 --- a/src/main/scala/org/camunda/feel/impl/script/FeelUnaryTestsScriptEngineFactory.scala +++ b/src/main/scala/org/camunda/feel/impl/script/FeelUnaryTestsScriptEngineFactory.scala @@ -19,8 +19,8 @@ package org.camunda.feel.impl.script import javax.script.{ScriptEngine, ScriptEngineFactory} import scala.jdk.CollectionConverters.SeqHasAsJava -/** - * @author Philipp Ossler +/** @author + * Philipp Ossler */ class FeelUnaryTestsScriptEngineFactory extends ScriptEngineFactory { diff --git a/src/main/scala/org/camunda/feel/syntaxtree/Exp.scala b/src/main/scala/org/camunda/feel/syntaxtree/Exp.scala index 0bd8aff6f..7cc7cff88 100644 --- a/src/main/scala/org/camunda/feel/syntaxtree/Exp.scala +++ b/src/main/scala/org/camunda/feel/syntaxtree/Exp.scala @@ -27,8 +27,8 @@ import org.camunda.feel.{ YearMonthDuration } -/** - * @author Philipp Ossler +/** @author + * Philipp Ossler */ sealed trait Exp @@ -65,8 +65,7 @@ case class ConstList(items: List[Exp]) extends Exp case class ConstContext(entries: List[(String, Exp)]) extends Exp -case class ConstRange(start: ConstRangeBoundary, end: ConstRangeBoundary) - extends Exp +case class ConstRange(start: ConstRangeBoundary, end: ConstRangeBoundary) extends Exp case class InputLessThan(x: Exp) extends Exp @@ -114,17 +113,12 @@ case class GreaterThan(x: Exp, y: Exp) extends Comparison case class GreaterOrEqual(x: Exp, y: Exp) extends Comparison -case class FunctionInvocation(function: String, params: FunctionParameters) - extends Exp +case class FunctionInvocation(function: String, params: FunctionParameters) extends Exp -case class JavaFunctionInvocation(className: String, - methodName: String, - arguments: List[String]) +case class JavaFunctionInvocation(className: String, methodName: String, arguments: List[String]) extends Exp -case class QualifiedFunctionInvocation(path: Exp, - function: String, - params: FunctionParameters) +case class QualifiedFunctionInvocation(path: Exp, function: String, params: FunctionParameters) extends Exp case class FunctionDefinition(parameters: List[String], body: Exp) extends Exp diff --git a/src/main/scala/org/camunda/feel/syntaxtree/FunctionParameters.scala b/src/main/scala/org/camunda/feel/syntaxtree/FunctionParameters.scala index 61c060201..d77e55f5f 100644 --- a/src/main/scala/org/camunda/feel/syntaxtree/FunctionParameters.scala +++ b/src/main/scala/org/camunda/feel/syntaxtree/FunctionParameters.scala @@ -18,8 +18,6 @@ package org.camunda.feel.syntaxtree sealed trait FunctionParameters -case class PositionalFunctionParameters(params: List[Exp]) - extends FunctionParameters +case class PositionalFunctionParameters(params: List[Exp]) extends FunctionParameters -case class NamedFunctionParameters(params: Map[String, Exp]) - extends FunctionParameters +case class NamedFunctionParameters(params: Map[String, Exp]) extends FunctionParameters diff --git a/src/main/scala/org/camunda/feel/syntaxtree/Val.scala b/src/main/scala/org/camunda/feel/syntaxtree/Val.scala index 0eb723949..fda5327a3 100644 --- a/src/main/scala/org/camunda/feel/syntaxtree/Val.scala +++ b/src/main/scala/org/camunda/feel/syntaxtree/Val.scala @@ -17,56 +17,62 @@ package org.camunda.feel.syntaxtree import org.camunda.feel.context.Context -import org.camunda.feel.{Date, DateTime, DayTimeDuration, LocalDateTime, LocalTime, Number, Time, YearMonthDuration, dateTimeFormatter, localDateTimeFormatter, localTimeFormatter} +import org.camunda.feel.{ + Date, + DateTime, + DayTimeDuration, + LocalDateTime, + LocalTime, + Number, + Time, + YearMonthDuration, + dateTimeFormatter, + localDateTimeFormatter, + localTimeFormatter +} import java.time.Duration import java.util.regex.Pattern -/** - * FEEL supports the following datatypes: - * number - * string - * boolean - * days and time duration - * years and months duration - * time - * date and time -Duration and date/time datatypes have no literal syntax. They must be constructed from a string representation using a -built-in function (10.3.4.1). +/** FEEL supports the following datatypes: number string boolean days and time duration years and + * months duration time date and time Duration and date/time datatypes have no literal syntax. They + * must be constructed from a string representation using a built-in function (10.3.4.1). * - * @author Philipp Ossler + * @author + * Philipp Ossler */ sealed trait Val extends Ordered[Val] { protected val properties: Map[String, Val] = Map.empty - /** - * Returns the value of a given property. The available properties depends on the value type. + /** Returns the value of a given property. The available properties depends on the value type. * - * @param name the name of the property - * @return the value of the property, or None if it doesn't exist + * @param name + * the name of the property + * @return + * the value of the property, or None if it doesn't exist */ def property(name: String): Option[Val] = properties.get(name) - /** - * Returns the names of the available properties. + /** Returns the names of the available properties. * - * @return the available property names + * @return + * the available property names */ def propertyNames(): Iterable[String] = properties.keys override def compare(that: Val): Int = (this, that) match { - case (ValNumber(x), ValNumber(y)) => x compare y - case (ValString(x), ValString(y)) => x compare y - case (ValDate(x), ValDate(y)) => x.compareTo(y) - case (ValLocalTime(x), ValLocalTime(y)) => x.compareTo(y) - case (ValTime(x), ValTime(y)) => x.compareTo(y) - case (ValLocalDateTime(x), ValLocalDateTime(y)) => x.compareTo(y) - case (ValDateTime(x), ValDateTime(y)) => x.compareTo(y) + case (ValNumber(x), ValNumber(y)) => x compare y + case (ValString(x), ValString(y)) => x compare y + case (ValDate(x), ValDate(y)) => x.compareTo(y) + case (ValLocalTime(x), ValLocalTime(y)) => x.compareTo(y) + case (ValTime(x), ValTime(y)) => x.compareTo(y) + case (ValLocalDateTime(x), ValLocalDateTime(y)) => x.compareTo(y) + case (ValDateTime(x), ValDateTime(y)) => x.compareTo(y) case (ValYearMonthDuration(x), ValYearMonthDuration(y)) => x.toTotalMonths compare y.toTotalMonths - case (ValDayTimeDuration(x), ValDayTimeDuration(y)) => x.compareTo(y) - case _ => + case (ValDayTimeDuration(x), ValDayTimeDuration(y)) => x.compareTo(y) + case _ => throw new IllegalArgumentException(s"$this can not be compared to $that") } @@ -80,12 +86,11 @@ sealed trait Val extends Ordered[Val] { case _: ValDateTime => true case _: ValYearMonthDuration => true case _: ValDayTimeDuration => true - case ValList(list) => + case ValList(list) => list.headOption - .map(head => - head.isComparable && list.forall(_.getClass == head.getClass)) + .map(head => head.isComparable && list.forall(_.getClass == head.getClass)) .getOrElse(false) - case _ => false + case _ => false } def toEither: Either[ValError, Val] = this match { @@ -114,9 +119,9 @@ case class ValString(value: String) extends Val { case class ValDate(value: Date) extends Val { override protected val properties: Map[String, Val] = Map( - "year" -> ValNumber(value.getYear), - "month" -> ValNumber(value.getMonthValue), - "day" -> ValNumber(value.getDayOfMonth), + "year" -> ValNumber(value.getYear), + "month" -> ValNumber(value.getMonthValue), + "day" -> ValNumber(value.getDayOfMonth), "weekday" -> ValNumber(value.getDayOfWeek.getValue) ) @@ -125,11 +130,11 @@ case class ValDate(value: Date) extends Val { case class ValLocalTime(value: LocalTime) extends Val { override protected val properties: Map[String, Val] = Map( - "hour" -> ValNumber(value.getHour), - "minute" -> ValNumber(value.getMinute), - "second" -> ValNumber(value.getSecond), + "hour" -> ValNumber(value.getHour), + "minute" -> ValNumber(value.getMinute), + "second" -> ValNumber(value.getSecond), "time offset" -> ValNull, - "timezone" -> ValNull + "timezone" -> ValNull ) override def toString: String = value.format(localTimeFormatter) @@ -137,12 +142,12 @@ case class ValLocalTime(value: LocalTime) extends Val { case class ValTime(value: Time) extends Val { override protected val properties: Map[String, Val] = Map( - "hour" -> ValNumber(value.getHour), - "minute" -> ValNumber(value.getMinute), - "second" -> ValNumber(value.getSecond), + "hour" -> ValNumber(value.getHour), + "minute" -> ValNumber(value.getMinute), + "second" -> ValNumber(value.getSecond), "time offset" -> ValDayTimeDuration(Duration.ofSeconds(value.getOffsetInTotalSeconds)), - "timezone" -> value.getZoneId.map(ValString).getOrElse(ValNull) + "timezone" -> value.getZoneId.map(ValString).getOrElse(ValNull) ) override def toString: String = value.format @@ -150,15 +155,15 @@ case class ValTime(value: Time) extends Val { case class ValLocalDateTime(value: LocalDateTime) extends Val { override val properties: Map[String, Val] = Map( - "year" -> ValNumber(value.getYear), - "month" -> ValNumber(value.getMonthValue), - "day" -> ValNumber(value.getDayOfMonth), - "weekday" -> ValNumber(value.getDayOfWeek.getValue), - "hour" -> ValNumber(value.getHour), - "minute" -> ValNumber(value.getMinute), - "second" -> ValNumber(value.getSecond), + "year" -> ValNumber(value.getYear), + "month" -> ValNumber(value.getMonthValue), + "day" -> ValNumber(value.getDayOfMonth), + "weekday" -> ValNumber(value.getDayOfWeek.getValue), + "hour" -> ValNumber(value.getHour), + "minute" -> ValNumber(value.getMinute), + "second" -> ValNumber(value.getSecond), "time offset" -> ValNull, - "timezone" -> ValNull + "timezone" -> ValNull ) override def toString: String = value.format(localDateTimeFormatter) @@ -166,16 +171,15 @@ case class ValLocalDateTime(value: LocalDateTime) extends Val { case class ValDateTime(value: DateTime) extends Val { override val properties: Map[String, Val] = Map( - "year" -> ValNumber(value.getYear), - "month" -> ValNumber(value.getMonthValue), - "day" -> ValNumber(value.getDayOfMonth), - "weekday" -> ValNumber(value.getDayOfWeek.getValue), - "hour" -> ValNumber(value.getHour), - "minute" -> ValNumber(value.getMinute), - "second" -> ValNumber(value.getSecond), - "time offset" -> ValDayTimeDuration( - Duration.ofSeconds(value.getOffset.getTotalSeconds)), - "timezone" -> { + "year" -> ValNumber(value.getYear), + "month" -> ValNumber(value.getMonthValue), + "day" -> ValNumber(value.getDayOfMonth), + "weekday" -> ValNumber(value.getDayOfWeek.getValue), + "hour" -> ValNumber(value.getHour), + "minute" -> ValNumber(value.getMinute), + "second" -> ValNumber(value.getSecond), + "time offset" -> ValDayTimeDuration(Duration.ofSeconds(value.getOffset.getTotalSeconds)), + "timezone" -> { if (hasTimeZone) ValString(value.getZone.getId) else ValNull } @@ -205,7 +209,7 @@ case class ValYearMonthDuration(value: YearMonthDuration) extends Val { override def toString: String = ValYearMonthDuration.format(value) override val properties: Map[String, Val] = Map( - "years" -> ValNumber(value.getYears), + "years" -> ValNumber(value.getYears), "months" -> ValNumber(value.getMonths) ) } @@ -213,7 +217,7 @@ case class ValYearMonthDuration(value: YearMonthDuration) extends Val { object ValYearMonthDuration { def format(value: YearMonthDuration): String = { - val year = value.getYears + val year = value.getYears val month = value.getMonths % 12 if (year == 0 && month == 0) @@ -236,11 +240,11 @@ object ValYearMonthDuration { } case class ValDayTimeDuration(value: DayTimeDuration) extends Val { - override def toString: String = ValDayTimeDuration.format(value) + override def toString: String = ValDayTimeDuration.format(value) override val properties: Map[String, Val] = Map( - "days" -> ValNumber(value.toDays), - "hours" -> ValNumber(value.toHours % 24), + "days" -> ValNumber(value.toDays), + "hours" -> ValNumber(value.toHours % 24), "minutes" -> ValNumber(value.toMinutes % 60), "seconds" -> ValNumber(value.getSeconds % 60) ) @@ -249,9 +253,9 @@ case class ValDayTimeDuration(value: DayTimeDuration) extends Val { object ValDayTimeDuration { def format(value: DayTimeDuration): String = { - val day = value.toDays - val hour = value.toHours % 24 - val minute = value.toMinutes % 60 + val day = value.toDays + val hour = value.toHours % 24 + val minute = value.toMinutes % 60 val second = value.getSeconds % 60 if (day == 0 && hour == 0 && minute == 0 && second == 0) @@ -287,9 +291,7 @@ case object ValNull extends Val { override def toString: String = "null" } -case class ValFunction(params: List[String], - invoke: List[Val] => Any, - hasVarArgs: Boolean = false) +case class ValFunction(params: List[String], invoke: List[Val] => Any, hasVarArgs: Boolean = false) extends Val { val paramSet: Set[String] = params.toSet @@ -310,7 +312,7 @@ case class ValList(items: List[Val]) extends Val { case class ValRange(start: RangeBoundary, end: RangeBoundary) extends Val { override def toString: String = { val startSymbol = if (start.isClosed) "[" else "(" - val endSymbol = if (end.isClosed) "]" else ")" + val endSymbol = if (end.isClosed) "]" else ")" s"$startSymbol${start.value}..${end.value}$endSymbol" } diff --git a/src/main/scala/org/camunda/feel/syntaxtree/ZonedTime.scala b/src/main/scala/org/camunda/feel/syntaxtree/ZonedTime.scala index 52804ac16..12f017f79 100644 --- a/src/main/scala/org/camunda/feel/syntaxtree/ZonedTime.scala +++ b/src/main/scala/org/camunda/feel/syntaxtree/ZonedTime.scala @@ -18,10 +18,7 @@ package org.camunda.feel.syntaxtree import java.time.format.DateTimeFormatterBuilder import java.time.temporal.TemporalAmount -import org.camunda.feel.{ - localTimeFormatter, - timeFormatterWithOffsetAndOptionalPrefix -} +import org.camunda.feel.{localTimeFormatter, timeFormatterWithOffsetAndOptionalPrefix} import java.time.{ Duration, @@ -35,16 +32,14 @@ import java.time.{ } import scala.util.Try -case class ZonedTime(time: LocalTime, - offset: ZoneOffset, - zone: Option[ZoneId]) { +case class ZonedTime(time: LocalTime, offset: ZoneOffset, zone: Option[ZoneId]) { import ZonedTime._ val hasTimeZone = zone.isDefined val nanos = { - val nod = time.toNanoOfDay + val nod = time.toNanoOfDay val offsetNanos = offset.getTotalSeconds() * NANOS_PER_SECOND nod - offsetNanos } @@ -124,7 +119,7 @@ object ZonedTime { val localTime = LocalTime.from(temporal) - val zoneId = ZoneId.from(temporal) + val zoneId = ZoneId.from(temporal) val offset: ZoneOffset = Try(ZoneOffset.from(temporal)) .getOrElse(zoneId.getRules.getStandardOffset(Instant.now)) @@ -148,15 +143,15 @@ object ZonedTime { def of(offsetTime: OffsetTime): ZonedTime = { val localTime = offsetTime.toLocalTime() - val offset = offsetTime.getOffset + val offset = offsetTime.getOffset ZonedTime(localTime, offset, None) } def of(dateTime: ZonedDateTime): ZonedTime = { val localTime = dateTime.toLocalTime() - val offset = dateTime.getOffset - val zone = { + val offset = dateTime.getOffset + val zone = { if (dateTime.getZone.equals(offset)) { None } else { diff --git a/src/main/scala/org/camunda/feel/valuemapper/CustomValueMapper.scala b/src/main/scala/org/camunda/feel/valuemapper/CustomValueMapper.scala index 57897b5e2..e933d2648 100644 --- a/src/main/scala/org/camunda/feel/valuemapper/CustomValueMapper.scala +++ b/src/main/scala/org/camunda/feel/valuemapper/CustomValueMapper.scala @@ -18,36 +18,40 @@ package org.camunda.feel.valuemapper import org.camunda.feel.syntaxtree.Val -/** - * Transform objects into FEEL types and the other way around. +/** Transform objects into FEEL types and the other way around. * - * Multiple mappers are chained and invoked in order of their [[CustomValueMapper.priority]]. If one - * mapper can't transform the object then the next handler of the chain is invoked. + * Multiple mappers are chained and invoked in order of their [[CustomValueMapper.priority]]. If + * one mapper can't transform the object then the next handler of the chain is invoked. */ trait CustomValueMapper { - /** - * Transform the given object into a FEEL type - one of [[Val]] (e.g. [[Double]] to [[ValNumber]]). - * If it can't be transformed then it returns [[None]] instead and the object is passed to the next mapper in the chain. + /** Transform the given object into a FEEL type - one of [[Val]] (e.g. [[Double]] to + * [[ValNumber]]). If it can't be transformed then it returns [[None]] instead and the object is + * passed to the next mapper in the chain. * - * @param x the object to transform - * @param innerValueMapper the mapper function to transform inner values of a collection type - * @return the FEEL representation of the object + * @param x + * the object to transform + * @param innerValueMapper + * the mapper function to transform inner values of a collection type + * @return + * the FEEL representation of the object */ def toVal(x: Any, innerValueMapper: Any => Val): Option[Val] - /** - * Transform the given FEEL type into a base Scala/Java object (e.g. [[ValNumber]] to [[Double]]). - * If it can't be transformed then it returns [[None]] instead and the object is passed to the next mapper in the chain. + /** Transform the given FEEL type into a base Scala/Java object (e.g. [[ValNumber]] to + * [[Double]]). If it can't be transformed then it returns [[None]] instead and the object is + * passed to the next mapper in the chain. * - * @param value the FEEL type to transform - * @param innerValueMapper the mapper function to transform inner values of a collection type - * @return the base object of the FEEL type + * @param value + * the FEEL type to transform + * @param innerValueMapper + * the mapper function to transform inner values of a collection type + * @return + * the base object of the FEEL type */ def unpackVal(value: Val, innerValueMapper: Val => Any): Option[Any] - /** - * The priority of this mapper in the chain. The mappers are invoked in order of their priority, + /** The priority of this mapper in the chain. The mappers are invoked in order of their priority, * starting with the highest priority. */ val priority: Int = 1 diff --git a/src/main/scala/org/camunda/feel/valuemapper/JavaCustomValueMapper.scala b/src/main/scala/org/camunda/feel/valuemapper/JavaCustomValueMapper.scala index f55482f17..5acc72c31 100644 --- a/src/main/scala/org/camunda/feel/valuemapper/JavaCustomValueMapper.scala +++ b/src/main/scala/org/camunda/feel/valuemapper/JavaCustomValueMapper.scala @@ -20,16 +20,21 @@ import org.camunda.feel.syntaxtree.Val abstract class JavaCustomValueMapper extends CustomValueMapper { - /** - * Transform the given object into a FEEL type - one of [[Val]] (e.g. [[Double]] to [[ValNumber]]). - * If it can't be transformed then it returns [[None]] instead and the object is passed to the next mapper in the chain. + /** Transform the given object into a FEEL type - one of [[Val]] (e.g. [[Double]] to + * [[ValNumber]]). If it can't be transformed then it returns [[None]] instead and the object is + * passed to the next mapper in the chain. * - * @param x the object to transform - * @param innerValueMapper the mapper function to transform inner values of a collection type - * @return the FEEL representation of the object + * @param x + * the object to transform + * @param innerValueMapper + * the mapper function to transform inner values of a collection type + * @return + * the FEEL representation of the object */ - def toValue(x: Any, innerValueMapper: java.util.function.Function[Any, Val]) - : java.util.Optional[Val] + def toValue( + x: Any, + innerValueMapper: java.util.function.Function[Any, Val] + ): java.util.Optional[Val] override def toVal(x: Any, innerValueMapper: Any => Val): Option[Val] = { toValue(x, innerValue => innerValueMapper.apply(innerValue)) match { @@ -38,28 +43,30 @@ abstract class JavaCustomValueMapper extends CustomValueMapper { } } - /** - * Transform the given FEEL type into a base Scala/Java object (e.g. [[ValNumber]] to [[Double]]). - * If it can't be transformed then it returns [[None]] instead and the object is passed to the next mapper in the chain. + /** Transform the given FEEL type into a base Scala/Java object (e.g. [[ValNumber]] to + * [[Double]]). If it can't be transformed then it returns [[None]] instead and the object is + * passed to the next mapper in the chain. * - * @param value the FEEL type to transform - * @param innerValueMapper the mapper function to transform inner values of a collection type - * @return the base object of the FEEL type + * @param value + * the FEEL type to transform + * @param innerValueMapper + * the mapper function to transform inner values of a collection type + * @return + * the base object of the FEEL type */ - def unpackValue(value: Val, - innerValueMapper: java.util.function.Function[Val, Any]) - : java.util.Optional[Any] + def unpackValue( + value: Val, + innerValueMapper: java.util.function.Function[Val, Any] + ): java.util.Optional[Any] - override def unpackVal(value: Val, - innerValueMapper: Val => Any): Option[Any] = { + override def unpackVal(value: Val, innerValueMapper: Val => Any): Option[Any] = { unpackValue(value, innerValue => innerValueMapper.apply(innerValue)) match { case x if (x.isPresent) => Some(x.get) case _ => None } } - /** - * The priority of this mapper in the chain. The mappers are invoked in order of their priority, + /** The priority of this mapper in the chain. The mappers are invoked in order of their priority, * starting with the highest priority. */ override val priority: Int = 1 diff --git a/src/main/scala/org/camunda/feel/valuemapper/ValueMapper.scala b/src/main/scala/org/camunda/feel/valuemapper/ValueMapper.scala index 5e89b1bd8..54c237910 100644 --- a/src/main/scala/org/camunda/feel/valuemapper/ValueMapper.scala +++ b/src/main/scala/org/camunda/feel/valuemapper/ValueMapper.scala @@ -29,8 +29,7 @@ trait ValueMapper { object ValueMapper { - case class CompositeValueMapper(customMappers: List[CustomValueMapper]) - extends ValueMapper { + case class CompositeValueMapper(customMappers: List[CustomValueMapper]) extends ValueMapper { val customMappersByPriority = (DefaultValueMapper.instance :: customMappers).distinct @@ -43,8 +42,7 @@ object ValueMapper { case _ => } } - throw new IllegalArgumentException( - s"no value mapper found for '$x' ('${x.getClass}')") + throw new IllegalArgumentException(s"no value mapper found for '$x' ('${x.getClass}')") } override def unpackVal(value: Val): Any = { diff --git a/src/main/scala/org/camunda/package.scala b/src/main/scala/org/camunda/package.scala index f13c604e9..96a6486df 100644 --- a/src/main/scala/org/camunda/package.scala +++ b/src/main/scala/org/camunda/package.scala @@ -32,25 +32,18 @@ import java.util.regex.Pattern import org.camunda.feel.syntaxtree.ZonedTime import java.time.format.{DateTimeFormatterBuilder, SignStyle} -import java.time.{ - Duration, - LocalDate, - LocalDateTime, - LocalTime, - Period, - ZonedDateTime -} +import java.time.{Duration, LocalDate, LocalDateTime, LocalTime, Period, ZonedDateTime} -/** - * @author Philipp Ossler +/** @author + * Philipp Ossler */ package object feel { - //// common + // // common val logger = LoggerFactory.getLogger("org.camunda.feel.FeelEngine") - //// type definitions + // // type definitions type Number = BigDecimal @@ -68,7 +61,7 @@ package object feel { type DayTimeDuration = java.time.Duration - //// string to type conversions + // // string to type conversions import scala.language.implicitConversions @@ -94,27 +87,29 @@ package object feel { implicit def stringToDayTimeDuration(duration: String): DayTimeDuration = Duration.parse(duration) - //// date/time/duration type parsing and formatting + // // date/time/duration type parsing and formatting private lazy val datePatter = Pattern.compile("""-?([1-9]\d{0,4})?\d{4}-[01][0-9]-[0-3]\d""") def isValidDate(date: String): Boolean = datePatter.matcher(date).matches - private lazy val offsetTimePattern = Pattern.compile( - """T?\d{2}:\d{2}:\d{2}(\.\d{1,9})?([+-]\d{2}:\d{2}|Z|@.*)""") + private lazy val offsetTimePattern = + Pattern.compile("""T?\d{2}:\d{2}:\d{2}(\.\d{1,9})?([+-]\d{2}:\d{2}|Z|@.*)""") def isOffsetTime(time: String): Boolean = offsetTimePattern.matcher(time).matches private lazy val offsetDateTimePattern = Pattern.compile( - """-?([1-9]\d{0,4})?\d{4}-[01][0-9]-[0-3]\dT[0-2][0-9]:[0-5][0-9](:[0-5][0-9])?(\.\d{1,9})?([+-][01][0-9]:[0-5][0-9]|Z|@.*)""") + """-?([1-9]\d{0,4})?\d{4}-[01][0-9]-[0-3]\dT[0-2][0-9]:[0-5][0-9](:[0-5][0-9])?(\.\d{1,9})?([+-][01][0-9]:[0-5][0-9]|Z|@.*)""" + ) def isOffsetDateTime(dateTime: String): Boolean = offsetDateTimePattern.matcher(dateTime).matches private lazy val localDateTimePatter = Pattern.compile( - """-?([1-9]\d{0,4})?\d{4}-[01][0-9]-[0-3]\dT[0-2][0-9]:[0-5][0-9](:[0-5][0-9])?(\.\d{1,9})?""") + """-?([1-9]\d{0,4})?\d{4}-[01][0-9]-[0-3]\dT[0-2][0-9]:[0-5][0-9](:[0-5][0-9])?(\.\d{1,9})?""" + ) def isLocalDateTime(dateTime: String): Boolean = localDateTimePatter.matcher(dateTime).matches diff --git a/src/test/scala/org/camunda/feel/api/ExternalFunctionsConfigurationTest.scala b/src/test/scala/org/camunda/feel/api/ExternalFunctionsConfigurationTest.scala index a3edb9706..96fc10e8d 100644 --- a/src/test/scala/org/camunda/feel/api/ExternalFunctionsConfigurationTest.scala +++ b/src/test/scala/org/camunda/feel/api/ExternalFunctionsConfigurationTest.scala @@ -27,7 +27,8 @@ class ExternalFunctionsConfigurationTest extends AnyFlatSpec with Matchers { val defaultEngine = new FeelEngine() val engineWithEnabledFunctions = new FeelEngine( - configuration = Configuration(externalFunctionsEnabled = true)) + configuration = Configuration(externalFunctionsEnabled = true) + ) val externalFunctionInvocation = """{ @@ -41,44 +42,50 @@ class ExternalFunctionsConfigurationTest extends AnyFlatSpec with Matchers { val disabledExternalFunctionFailure = Failure( s"validation of expression '$externalFunctionInvocation' failed: " + - "External functions are disabled. Use the FunctionProvider SPI (recommended) or enable external function in the configuration.") + "External functions are disabled. Use the FunctionProvider SPI (recommended) or enable external function in the configuration." + ) val invocationResult = 1 "A (default) FeelEngine" should "fail to parse an external function" in { defaultEngine.parseExpression(externalFunctionInvocation) should be( - Left(disabledExternalFunctionFailure)) + Left(disabledExternalFunctionFailure) + ) } it should "fail to evaluate an external function" in { defaultEngine.evalExpression(externalFunctionInvocation) should be( - Left(disabledExternalFunctionFailure)) + Left(disabledExternalFunctionFailure) + ) } it should "fail to evaluate a parsed external function" in { defaultEngine.eval(parsedExternalFunctionInvocation) should be( - Left(disabledExternalFunctionFailure)) + Left(disabledExternalFunctionFailure) + ) } "A FEEL engine with enabled external functions" should "parse an external function" in { - engineWithEnabledFunctions.parseExpression(externalFunctionInvocation) shouldBe a[ - Right[_, ParsedExpression]] + engineWithEnabledFunctions + .parseExpression(externalFunctionInvocation) shouldBe a[Right[_, ParsedExpression]] } it should "evaluate an external function" in { engineWithEnabledFunctions.evalExpression(externalFunctionInvocation) should be( - Right(invocationResult)) + Right(invocationResult) + ) } it should "evaluate a parsed external function" in { engineWithEnabledFunctions.eval(parsedExternalFunctionInvocation) should be( - Right(invocationResult)) + Right(invocationResult) + ) } } diff --git a/src/test/scala/org/camunda/feel/api/FeelEngineApiTest.scala b/src/test/scala/org/camunda/feel/api/FeelEngineApiTest.scala index 96b34993d..1dbad5574 100644 --- a/src/test/scala/org/camunda/feel/api/FeelEngineApiTest.scala +++ b/src/test/scala/org/camunda/feel/api/FeelEngineApiTest.scala @@ -143,7 +143,6 @@ class FeelEngineApiTest extends AnyFlatSpec with Matchers with EitherValues { evaluationResult.toEither should be(Right(evaluationResult.result)) } - it should "evaluate a unary-tests expression" in { val evaluationResult = engine.evaluateUnaryTests( expression = "< 3", @@ -175,10 +174,12 @@ class FeelEngineApiTest extends AnyFlatSpec with Matchers with EitherValues { evaluationResult.failure should be(Failure("")) evaluationResult.hasSuppressedFailures should be(true) - evaluationResult.suppressedFailures should contain(EvaluationFailure( - failureType = EvaluationFailureType.NO_VARIABLE_FOUND, - failureMessage = "No variable found with name 'x'" - )) + evaluationResult.suppressedFailures should contain( + EvaluationFailure( + failureType = EvaluationFailureType.NO_VARIABLE_FOUND, + failureMessage = "No variable found with name 'x'" + ) + ) evaluationResult.toEither.isRight should be(true) evaluationResult.toEither should be(Right(evaluationResult.result)) @@ -197,10 +198,12 @@ class FeelEngineApiTest extends AnyFlatSpec with Matchers with EitherValues { evaluationResult.failure should be(Failure("")) evaluationResult.hasSuppressedFailures should be(true) - evaluationResult.suppressedFailures should contain(EvaluationFailure( - failureType = EvaluationFailureType.NO_VARIABLE_FOUND, - failureMessage = "No variable found with name 'x'" - )) + evaluationResult.suppressedFailures should contain( + EvaluationFailure( + failureType = EvaluationFailureType.NO_VARIABLE_FOUND, + failureMessage = "No variable found with name 'x'" + ) + ) evaluationResult.toEither.isRight should be(true) evaluationResult.toEither should be(Right(evaluationResult.result)) diff --git a/src/test/scala/org/camunda/feel/api/FeelEngineTest.scala b/src/test/scala/org/camunda/feel/api/FeelEngineTest.scala index daf96dace..d33e8456f 100644 --- a/src/test/scala/org/camunda/feel/api/FeelEngineTest.scala +++ b/src/test/scala/org/camunda/feel/api/FeelEngineTest.scala @@ -24,8 +24,8 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.EitherValues -/** - * @author Philipp Ossler +/** @author + * Philipp Ossler */ class FeelEngineTest extends AnyFlatSpec with Matchers with EitherValues { @@ -33,14 +33,12 @@ class FeelEngineTest extends AnyFlatSpec with Matchers with EitherValues { "A FeelEngine" should "evaluate a unaryTests '< 3'" in { - engine.evalUnaryTests( - "< 3", - variables = Map(UnaryTests.defaultInputVariable -> 2)) should be( - Right(true)) - engine.evalUnaryTests( - "< 3", - variables = Map(UnaryTests.defaultInputVariable -> 3)) should be( - Right(false)) + engine.evalUnaryTests("< 3", variables = Map(UnaryTests.defaultInputVariable -> 2)) should be( + Right(true) + ) + engine.evalUnaryTests("< 3", variables = Map(UnaryTests.defaultInputVariable -> 3)) should be( + Right(false) + ) } it should "evaluate a expression '2+4'" in { @@ -50,16 +48,16 @@ class FeelEngineTest extends AnyFlatSpec with Matchers with EitherValues { it should "evaluate an unaryTest with custom input variable name" in { - engine.evalUnaryTests("< 3", - variables = Map( - "myInput" -> 2, - UnaryTests.inputVariable -> "myInput")) should be( + engine.evalUnaryTests( + "< 3", + variables = Map("myInput" -> 2, UnaryTests.inputVariable -> "myInput") + ) should be( Right(true) ) - engine.evalUnaryTests("< 3", - variables = Map( - "myInput" -> 3, - UnaryTests.inputVariable -> "myInput")) should be( + engine.evalUnaryTests( + "< 3", + variables = Map("myInput" -> 3, UnaryTests.inputVariable -> "myInput") + ) should be( Right(false) ) } @@ -83,20 +81,21 @@ class FeelEngineTest extends AnyFlatSpec with Matchers with EitherValues { it should "fail to parse an expression 'x+'" in { engine.parseExpression("x+").left.value.message should startWith( - "failed to parse expression 'x+'") + "failed to parse expression 'x+'" + ) } it should "parse an unaryTests '< 3'" in { val expr = engine.parseUnaryTests("< 3") expr shouldBe a[Right[_, ParsedExpression]] - engine.eval(expr.value, Map(UnaryTests.defaultInputVariable -> 2)) should be( - Right(true)) + engine.eval(expr.value, Map(UnaryTests.defaultInputVariable -> 2)) should be(Right(true)) } it should "fail to parse an unaryTests '<'" in { engine.parseUnaryTests("<").left.value.message should startWith( - "failed to parse expression '<'") + "failed to parse expression '<'" + ) } it should "return suppressed failures" in { @@ -106,10 +105,12 @@ class FeelEngineTest extends AnyFlatSpec with Matchers with EitherValues { evaluationResult.isSuccess should be(true) evaluationResult.hasSuppressedFailures should be(true) - evaluationResult.suppressedFailures should contain(EvaluationFailure( - failureType = EvaluationFailureType.NO_VARIABLE_FOUND, - failureMessage = "No variable found with name 'x'" - )) + evaluationResult.suppressedFailures should contain( + EvaluationFailure( + failureType = EvaluationFailureType.NO_VARIABLE_FOUND, + failureMessage = "No variable found with name 'x'" + ) + ) case Left(_) => fail("Expected the expression to be parsed successfully") } diff --git a/src/test/scala/org/camunda/feel/api/StringRepresentationTypeTest.scala b/src/test/scala/org/camunda/feel/api/StringRepresentationTypeTest.scala index 6943088b0..817d50b4f 100644 --- a/src/test/scala/org/camunda/feel/api/StringRepresentationTypeTest.scala +++ b/src/test/scala/org/camunda/feel/api/StringRepresentationTypeTest.scala @@ -17,15 +17,32 @@ package org.camunda.feel.api import org.camunda.feel.context.Context.{EmptyContext, StaticContext} -import org.camunda.feel.syntaxtree.{ClosedRangeBoundary, OpenRangeBoundary, ValContext, ValDate, ValDateTime, ValDayTimeDuration, ValError, ValFunction, ValList, ValLocalDateTime, ValLocalTime, ValNull, ValNumber, ValRange, ValString, ValTime, ValYearMonthDuration, ZonedTime} +import org.camunda.feel.syntaxtree.{ + ClosedRangeBoundary, + OpenRangeBoundary, + ValContext, + ValDate, + ValDateTime, + ValDayTimeDuration, + ValError, + ValFunction, + ValList, + ValLocalDateTime, + ValLocalTime, + ValNull, + ValNumber, + ValRange, + ValString, + ValTime, + ValYearMonthDuration, + ZonedTime +} import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers import java.time.{Duration, LocalDate, LocalDateTime, LocalTime, Period, ZonedDateTime} -class StringRepresentationTypeTest - extends AnyFlatSpec - with Matchers { +class StringRepresentationTypeTest extends AnyFlatSpec with Matchers { "Null" should "return 'null'" in { ValNull.toString should be("null") diff --git a/src/test/scala/org/camunda/feel/api/context/CustomContextTest.scala b/src/test/scala/org/camunda/feel/api/context/CustomContextTest.scala index 2de333ff1..811363c28 100644 --- a/src/test/scala/org/camunda/feel/api/context/CustomContextTest.scala +++ b/src/test/scala/org/camunda/feel/api/context/CustomContextTest.scala @@ -25,7 +25,11 @@ import org.camunda.feel.syntaxtree._ import org.scalatest.matchers.should.Matchers import org.scalatest.flatspec.AnyFlatSpec -class CustomContextTest extends AnyFlatSpec with Matchers with FeelEngineTest with EvaluationResultMatchers { +class CustomContextTest + extends AnyFlatSpec + with Matchers + with FeelEngineTest + with EvaluationResultMatchers { "A default context" should "provide its members" in { evaluateExpression( @@ -51,9 +55,9 @@ class CustomContextTest extends AnyFlatSpec with Matchers with FeelEngineTest wi override def variableProvider: VariableProvider = new VariableProvider { override def getVariable(name: String): Option[Any] = name match { - case "a" => Some(2) + case "a" => Some(2) case UnaryTests.defaultInputVariable => Some(2) - case _ => None + case _ => None } override def keys: Iterable[String] = @@ -67,13 +71,9 @@ class CustomContextTest extends AnyFlatSpec with Matchers with FeelEngineTest wi context = myCustomContext ) should returnResult(2) - evaluateExpression( - expression = "floor(3.8)", - context = myCustomContext) should returnResult(3) + evaluateExpression(expression = "floor(3.8)", context = myCustomContext) should returnResult(3) - evaluateUnaryTests( - expression = "2", - context = myCustomContext) should returnResult(true) + evaluateUnaryTests(expression = "2", context = myCustomContext) should returnResult(true) } it should "provide its functions" in { @@ -91,9 +91,12 @@ class CustomContextTest extends AnyFlatSpec with Matchers with FeelEngineTest wi } val myFunctionProvider = new FunctionProvider { - val f = ValFunction(List("x"), { - case List(ValNumber(x)) => ValNumber(x + 2) - }) + val f = ValFunction( + List("x"), + { case List(ValNumber(x)) => + ValNumber(x + 2) + } + ) override def getFunctions(name: String): List[ValFunction] = { functionCallCount += 1; @@ -146,7 +149,8 @@ class CustomContextTest extends AnyFlatSpec with Matchers with FeelEngineTest wi val inputVariableContext = StaticVariableProvider( Map( UnaryTests.inputVariable -> "myInputVariable" - )) + ) + ) it should "evaluate unary-test" in { val variables: Map[String, _] = Map("myInputVariable" -> 8, "foo" -> 7) @@ -154,12 +158,11 @@ class CustomContextTest extends AnyFlatSpec with Matchers with FeelEngineTest wi val context: CustomContext = new CustomContext { override val variableProvider = VariableProvider.CompositeVariableProvider( - List(inputVariableContext, SimpleTestContext(variables))) + List(inputVariableContext, SimpleTestContext(variables)) + ) } - evaluateUnaryTests( - expression = "foo", - context = context) should returnResult(false) + evaluateUnaryTests(expression = "foo", context = context) should returnResult(false) } it should "return null if input value doesn't exist" in { @@ -168,12 +171,11 @@ class CustomContextTest extends AnyFlatSpec with Matchers with FeelEngineTest wi val context: CustomContext = new CustomContext { override val variableProvider = VariableProvider.CompositeVariableProvider( - List(inputVariableContext, SimpleTestContext(variables))) + List(inputVariableContext, SimpleTestContext(variables)) + ) } - evaluateUnaryTests( - expression = "foo", - context = context) should returnResult(false) + evaluateUnaryTests(expression = "foo", context = context) should returnResult(false) } } diff --git a/src/test/scala/org/camunda/feel/api/context/CustomFunctionTest.scala b/src/test/scala/org/camunda/feel/api/context/CustomFunctionTest.scala index d692f9cd4..ae0421fdb 100644 --- a/src/test/scala/org/camunda/feel/api/context/CustomFunctionTest.scala +++ b/src/test/scala/org/camunda/feel/api/context/CustomFunctionTest.scala @@ -29,8 +29,8 @@ class CustomFunctionTest extends AnyFlatSpec with Matchers { "foo" -> ValFunction( params = List("x"), - invoke = { - case List(ValNumber(x)) => ValNumber(x + 1) + invoke = { case List(ValNumber(x)) => + ValNumber(x + 1) } ) ) @@ -41,8 +41,8 @@ class CustomFunctionTest extends AnyFlatSpec with Matchers { "bar" -> ValFunction( params = List("x"), - invoke = { - case List(ValNumber(x)) => ValNumber(x + 2) + invoke = { case List(ValNumber(x)) => + ValNumber(x + 2) } ) ) @@ -60,14 +60,14 @@ class CustomFunctionTest extends AnyFlatSpec with Matchers { val engine = new FeelEngine( FunctionProvider.CompositeFunctionProvider( List(functionProviderFoo, functionProviderBar) - )) + ) + ) engine.evalExpression("foo(2)") should be(Right(3)) engine.evalExpression("bar(2)") should be(Right(4)) } - class TestFunctionProvider(functions: Map[String, ValFunction]) - extends CustomFunctionProvider { + class TestFunctionProvider(functions: Map[String, ValFunction]) extends CustomFunctionProvider { override def getFunction(name: String): Option[ValFunction] = functions.get(name) diff --git a/src/test/scala/org/camunda/feel/api/context/JavaCustomFunctionTest.scala b/src/test/scala/org/camunda/feel/api/context/JavaCustomFunctionTest.scala index c417a7057..18d2fffd4 100644 --- a/src/test/scala/org/camunda/feel/api/context/JavaCustomFunctionTest.scala +++ b/src/test/scala/org/camunda/feel/api/context/JavaCustomFunctionTest.scala @@ -28,8 +28,7 @@ class JavaCustomFunctionTest extends AnyFlatSpec with Matchers { it should "call java custom function" in { - engine.evalExpression("myCustomFunction()", context = EmptyContext) should be( - Right("foo")) + engine.evalExpression("myCustomFunction()", context = EmptyContext) should be(Right("foo")) } } diff --git a/src/test/scala/org/camunda/feel/api/valuemapper/BuiltinValueMapperInputTest.scala b/src/test/scala/org/camunda/feel/api/valuemapper/BuiltinValueMapperInputTest.scala index adeed63f7..829e0165e 100644 --- a/src/test/scala/org/camunda/feel/api/valuemapper/BuiltinValueMapperInputTest.scala +++ b/src/test/scala/org/camunda/feel/api/valuemapper/BuiltinValueMapperInputTest.scala @@ -36,8 +36,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("foo" -> java.lang.String.valueOf("3.4")) engine - .evalExpression("foo + \"hello\"", - context = Context.StaticContext(variables, null)) + .evalExpression("foo + \"hello\"", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe a[java.lang.String] } @@ -45,8 +44,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("foo" -> java.lang.Float.valueOf("3.4")) engine - .evalExpression("foo + 1", - context = Context.StaticContext(variables, null)) + .evalExpression("foo + 1", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe a[java.lang.Double] } @@ -54,8 +52,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("foo" -> java.lang.Double.valueOf("3.4")) engine - .evalExpression("foo + 1", - context = Context.StaticContext(variables, null)) + .evalExpression("foo + 1", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe a[java.lang.Double] } @@ -63,8 +60,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("foo" -> java.lang.Integer.valueOf("3")) engine - .evalExpression("foo + 1", - context = Context.StaticContext(variables, null)) + .evalExpression("foo + 1", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe a[java.lang.Long] } @@ -72,8 +68,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("foo" -> java.lang.Long.valueOf("3")) engine - .evalExpression("foo + 1", - context = Context.StaticContext(variables, null)) + .evalExpression("foo + 1", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe a[java.lang.Long] } @@ -81,8 +76,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("foo" -> java.lang.Short.valueOf("3")) engine - .evalExpression("foo + 1", - context = Context.StaticContext(variables, null)) + .evalExpression("foo + 1", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe a[java.lang.Long] } @@ -90,8 +84,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("foo" -> java.lang.Boolean.valueOf("true")) engine - .evalExpression("foo or false", - context = Context.StaticContext(variables, null)) + .evalExpression("foo or false", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe a[java.lang.Boolean] } @@ -109,8 +102,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("foo" -> nullValue) engine - .evalExpression("foo = null", - context = Context.StaticContext(variables, null)) + .evalExpression("foo = null", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe true } @@ -119,8 +111,10 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("foo" -> new SimpleTestPojo("foo")) engine - .evalExpression("foo.getMyString() = \"foo\"", - context = Context.StaticContext(variables, null)) + .evalExpression( + "foo.getMyString() = \"foo\"", + context = Context.StaticContext(variables, null) + ) .getOrElse() shouldBe true } @@ -129,8 +123,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("foo" -> new SimpleTestPojo("foo")) engine - .evalExpression("foo.myString = \"foo\"", - context = Context.StaticContext(variables, null)) + .evalExpression("foo.myString = \"foo\"", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe true } @@ -141,8 +134,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("foo" -> pojo) engine - .evalExpression("foo.isEnabled() = true", - context = Context.StaticContext(variables, null)) + .evalExpression("foo.isEnabled() = true", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe true } @@ -153,8 +145,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("foo" -> pojo) engine - .evalExpression("foo.enabled = true", - context = Context.StaticContext(variables, null)) + .evalExpression("foo.enabled = true", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe true } @@ -165,8 +156,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("foo" -> pojo) engine - .evalExpression("foo.enabled = false", - context = Context.StaticContext(variables, null)) + .evalExpression("foo.enabled = false", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe true } @@ -177,8 +167,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("foo" -> pojo) engine - .evalExpression("foo.getDisabled() = true", - context = Context.StaticContext(variables, null)) + .evalExpression("foo.getDisabled() = true", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe true } @@ -189,8 +178,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("foo" -> pojo) engine - .evalExpression("foo.disabled = true", - context = Context.StaticContext(variables, null)) + .evalExpression("foo.disabled = true", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe true } @@ -201,8 +189,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("bar" -> pojo) engine - .evalExpression("bar.isFoo() = \"baz\"", - context = Context.StaticContext(variables, null)) + .evalExpression("bar.isFoo() = \"baz\"", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe true } @@ -213,8 +200,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("bar" -> pojo) engine - .evalExpression("bar.foo = \"baz\"", - context = Context.StaticContext(variables, null)) + .evalExpression("bar.foo = \"baz\"", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe false } @@ -224,8 +210,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("bar" -> pojo) engine - .evalExpression("bar.foo = \"baz\"", - context = Context.StaticContext(variables, null)) + .evalExpression("bar.foo = \"baz\"", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe true } @@ -235,8 +220,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("bar" -> pojo) engine - .evalExpression("bar.getEnabled() = true", - context = Context.StaticContext(variables, null)) + .evalExpression("bar.getEnabled() = true", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe true } @@ -246,8 +230,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("bar" -> pojo) engine - .evalExpression("bar.enabled = true", - context = Context.StaticContext(variables, null)) + .evalExpression("bar.enabled = true", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe true } @@ -257,8 +240,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("bar" -> pojo) engine - .evalExpression("bar.isDisabled = false", - context = Context.StaticContext(variables, null)) + .evalExpression("bar.isDisabled = false", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe true } @@ -268,8 +250,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("bar" -> pojo) engine - .evalExpression("bar.disabled = false", - context = Context.StaticContext(variables, null)) + .evalExpression("bar.disabled = false", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe true } @@ -279,8 +260,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("bar" -> pojo) engine - .evalExpression("bar.isBaz = \"foo\"", - context = Context.StaticContext(variables, null)) + .evalExpression("bar.isBaz = \"foo\"", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe true } @@ -290,8 +270,7 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { val variables = Map("bar" -> pojo) engine - .evalExpression("bar.baz = \"foo\"", - context = Context.StaticContext(variables, null)) + .evalExpression("bar.baz = \"foo\"", context = Context.StaticContext(variables, null)) .getOrElse() shouldBe false } @@ -307,7 +286,8 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { engine .evalExpression( "map.foo = \"myString\" and map.bar = 2 and map.baz.foo = \"bar\"", - context = Context.StaticContext(variables, null)) + context = Context.StaticContext(variables, null) + ) .getOrElse() shouldBe true } @@ -323,19 +303,20 @@ class BuiltinValueMapperInputTest extends AnyFlatSpec with Matchers { engine .evalExpression( "list[1] = \"myString\" and list[2] = 2 and list[3].foo = \"bar\"", - context = Context.StaticContext(variables, null)) + context = Context.StaticContext(variables, null) + ) .getOrElse() shouldBe true } class MyScalaType { - private val foo: String = "baz" - private val baz: String = "foo" + private val foo: String = "baz" + private val baz: String = "foo" private val enabled: Boolean = true - private val disabled = false + private val disabled = false - def getFoo: String = foo - def isBaz: String = baz + def getFoo: String = foo + def isBaz: String = baz def getEnabled: Boolean = enabled - def isDisabled = disabled + def isDisabled = disabled } } diff --git a/src/test/scala/org/camunda/feel/api/valuemapper/BuiltinValueMapperOutputTest.scala b/src/test/scala/org/camunda/feel/api/valuemapper/BuiltinValueMapperOutputTest.scala index eab8fd9ca..4d86c18d0 100644 --- a/src/test/scala/org/camunda/feel/api/valuemapper/BuiltinValueMapperOutputTest.scala +++ b/src/test/scala/org/camunda/feel/api/valuemapper/BuiltinValueMapperOutputTest.scala @@ -98,8 +98,7 @@ class BuiltinValueMapperOutputTest extends AnyFlatSpec with Matchers { it should "return date-time with zone id as java.time.ZonedDateTime" in { engine - .evalExpression( - """ date and time("2019-08-12T22:22:22@Europe/Berlin") """) + .evalExpression(""" date and time("2019-08-12T22:22:22@Europe/Berlin") """) .map { _ shouldBe ZonedDateTime.of( LocalDateTime.parse("2019-08-12T22:22:22"), diff --git a/src/test/scala/org/camunda/feel/api/valuemapper/CustomValueMapperTest.scala b/src/test/scala/org/camunda/feel/api/valuemapper/CustomValueMapperTest.scala index cf65d68a2..21d715002 100644 --- a/src/test/scala/org/camunda/feel/api/valuemapper/CustomValueMapperTest.scala +++ b/src/test/scala/org/camunda/feel/api/valuemapper/CustomValueMapperTest.scala @@ -52,8 +52,8 @@ class CustomValueMapperTest extends AnyFlatSpec with Matchers { } class Person extends DomainObject { - val name = Property[String]("name", None) - val language = Property[Language]("language", None) + val name = Property[String]("name", None) + val language = Property[Language]("language", None) val properties = Map((name.name -> name), (language.name -> language)) } @@ -75,8 +75,7 @@ class CustomValueMapperTest extends AnyFlatSpec with Matchers { case e: Enum => Some( ValContext( - Context.StaticContext( - e.items.map(e => e.id -> innerValueMapper(e)).toMap) + Context.StaticContext(e.items.map(e => e.id -> innerValueMapper(e)).toMap) ) ) @@ -89,14 +88,12 @@ class CustomValueMapperTest extends AnyFlatSpec with Matchers { } } - override def unpackVal(value: Val, - innerValueMapper: Val => Any): Option[Any] = + override def unpackVal(value: Val, innerValueMapper: Val => Any): Option[Any] = None } val valueMapper: ValueMapper = - ValueMapper.CompositeValueMapper( - List(DefaultValueMapper.instance, new MyCustomValueMapper)) + ValueMapper.CompositeValueMapper(List(DefaultValueMapper.instance, new MyCustomValueMapper)) val engine = new FeelEngine(valueMapper = valueMapper) @@ -153,10 +150,8 @@ class CustomValueMapperTest extends AnyFlatSpec with Matchers { engine.evalExpression("p.name", context) should be(Right("Tom")) engine.evalExpression("p.language", context) should be(Right("EN")) - engine.evalExpression("p.language = Language.EN", context) should be( - Right(true)) - engine.evalExpression("p.language = Language.DE", context) should be( - Right(false)) + engine.evalExpression("p.language = Language.EN", context) should be(Right(true)) + engine.evalExpression("p.language = Language.DE", context) should be(Right(false)) } } diff --git a/src/test/scala/org/camunda/feel/examples/SpecExampleTest.scala b/src/test/scala/org/camunda/feel/examples/SpecExampleTest.scala index 64ac27e0a..ec1eda44b 100644 --- a/src/test/scala/org/camunda/feel/examples/SpecExampleTest.scala +++ b/src/test/scala/org/camunda/feel/examples/SpecExampleTest.scala @@ -21,13 +21,9 @@ import org.camunda.feel.syntaxtree._ import org.scalatest.matchers.should.Matchers import org.scalatest.flatspec.AnyFlatSpec -class SpecExampleTest - extends AnyFlatSpec - with Matchers - with FeelIntegrationTest { +class SpecExampleTest extends AnyFlatSpec with Matchers with FeelIntegrationTest { - val context: Val = eval( - """ + val context: Val = eval(""" { applicant: { @@ -74,8 +70,8 @@ class SpecExampleTest it should "evaluate an if,in" in { evalWithContext( - """ if applicant.maritalStatus in ("M","S") then "valid" else "not valid" """) should be( - ValString("valid")) + """ if applicant.maritalStatus in ("M","S") then "valid" else "not valid" """ + ) should be(ValString("valid")) } @@ -87,8 +83,8 @@ class SpecExampleTest it should "invoke an user-defined function" in { - val rate: BigDecimal = 0.25 - val term: BigDecimal = 36 + val rate: BigDecimal = 0.25 + val term: BigDecimal = 36 val amount: BigDecimal = 100000 evalWithContext(""" PMT( @@ -98,23 +94,26 @@ class SpecExampleTest ) """) should be( - ValNumber((amount * rate / 12) / (1 - (1 + rate / 12) - .pow(-36)))) // ~ 3975.982590125562 + ValNumber( + (amount * rate / 12) / (1 - (1 + rate / 12) + .pow(-36)) + ) + ) // ~ 3975.982590125562 } it should "sum a filtered list of context" in { evalWithContext( - """ sum( credit_history[record_date > date("2011-01-01")].weight) """) should be( - ValNumber(150)) + """ sum( credit_history[record_date > date("2011-01-01")].weight) """ + ) should be(ValNumber(150)) } it should "determine if list satisfies" in { - evalWithContext( - """ some ch in credit_history satisfies ch.event = "bankruptcy" """) should be( - ValBoolean(false)) + evalWithContext(""" some ch in credit_history satisfies ch.event = "bankruptcy" """) should be( + ValBoolean(false) + ) } it should "execute nested path and filter expressions" in { @@ -127,17 +126,17 @@ class SpecExampleTest Map("id" -> 7902, "deptNum" -> 20, "name" -> "Ford"), Map("id" -> 7900, "deptNum" -> 30, "name" -> "James") ), - "DeptTable" -> List( - Map("number" -> 10, "name" -> "Sales", "manager" -> "Smith"), - Map("number" -> 20, "name" -> "Finance", "manager" -> "Jones"), + "DeptTable" -> List( + Map("number" -> 10, "name" -> "Sales", "manager" -> "Smith"), + Map("number" -> 20, "name" -> "Finance", "manager" -> "Jones"), Map("number" -> 30, "name" -> "Engineering", "manager" -> "King") ), - "LastName" -> "Clark" + "LastName" -> "Clark" ) - eval( - "DeptTable[number = EmployeeTable[name=LastName].deptNum[1]].manager[1]", - ctx) should be(ValString("Smith")) + eval("DeptTable[number = EmployeeTable[name=LastName].deptNum[1]].manager[1]", ctx) should be( + ValString("Smith") + ) } private def evalWithContext(exp: String) = { diff --git a/src/test/scala/org/camunda/feel/impl/EvaluationResultMatchers.scala b/src/test/scala/org/camunda/feel/impl/EvaluationResultMatchers.scala index 652142ee4..fbb5e383f 100644 --- a/src/test/scala/org/camunda/feel/impl/EvaluationResultMatchers.scala +++ b/src/test/scala/org/camunda/feel/impl/EvaluationResultMatchers.scala @@ -16,7 +16,13 @@ */ package org.camunda.feel.impl -import org.camunda.feel.api.{EvaluationFailure, EvaluationFailureType, EvaluationResult, FailedEvaluationResult, SuccessfulEvaluationResult} +import org.camunda.feel.api.{ + EvaluationFailure, + EvaluationFailureType, + EvaluationResult, + FailedEvaluationResult, + SuccessfulEvaluationResult +} import org.scalatest.matchers.{MatchResult, Matcher} trait EvaluationResultMatchers { @@ -38,51 +44,58 @@ trait EvaluationResultMatchers { class EvaluationResultValueMatcher(expectedResult: Any) extends Matcher[EvaluationResult] { override def apply(evaluationResult: EvaluationResult): MatchResult = { evaluationResult match { - case SuccessfulEvaluationResult(result, _) => MatchResult( - result == expectedResult, - s"the evaluation didn't returned '$expectedResult' but '${evaluationResult.result}'", - s"The evaluation returned '${evaluationResult.result}' as expected", - ) - case FailedEvaluationResult(failure, _) => MatchResult( - false, - s"the evaluation didn't returned '$expectedResult' but failed with '${failure.message}'", - s"the evaluation didn't returned '$expectedResult' but failed with '${failure.message}'", - ) + case SuccessfulEvaluationResult(result, _) => + MatchResult( + result == expectedResult, + s"the evaluation didn't returned '$expectedResult' but '${evaluationResult.result}'", + s"The evaluation returned '${evaluationResult.result}' as expected" + ) + case FailedEvaluationResult(failure, _) => + MatchResult( + false, + s"the evaluation didn't returned '$expectedResult' but failed with '${failure.message}'", + s"the evaluation didn't returned '$expectedResult' but failed with '${failure.message}'" + ) } } } - class SuppressedFailureMatcher(expectedFailure: EvaluationFailure) extends Matcher[EvaluationResult] { + class SuppressedFailureMatcher(expectedFailure: EvaluationFailure) + extends Matcher[EvaluationResult] { override def apply(evaluationResult: EvaluationResult): MatchResult = { - val matchResult = (suppressedFailures: List[EvaluationFailure]) => MatchResult( - suppressedFailures.contains(expectedFailure), - s"the evaluation didn't report '$expectedFailure' but '$suppressedFailures'", - s"the evaluation reported '$expectedFailure' as expected", - ) + val matchResult = (suppressedFailures: List[EvaluationFailure]) => + MatchResult( + suppressedFailures.contains(expectedFailure), + s"the evaluation didn't report '$expectedFailure' but '$suppressedFailures'", + s"the evaluation reported '$expectedFailure' as expected" + ) evaluationResult match { case SuccessfulEvaluationResult(_, suppressedFailures) => matchResult(suppressedFailures) - case FailedEvaluationResult(_, suppressedFailures) => matchResult(suppressedFailures) + case FailedEvaluationResult(_, suppressedFailures) => matchResult(suppressedFailures) } } } - class EvaluationResultFailureMatcher(expectedFailureMessage: String) extends Matcher[EvaluationResult] { + class EvaluationResultFailureMatcher(expectedFailureMessage: String) + extends Matcher[EvaluationResult] { override def apply(evaluationResult: EvaluationResult): MatchResult = { evaluationResult match { - case SuccessfulEvaluationResult(result, _) => MatchResult( - false, - s"the evaluation didn't fail with '$expectedFailureMessage' but returned '${result}'", - s"the evaluation didn't fail with '$expectedFailureMessage' but returned '${result}'" - ) - case FailedEvaluationResult(failure, _) => MatchResult( - failure.message.contains(expectedFailureMessage), - s"the evaluation failure message didn't contain '$expectedFailureMessage' but was '${failure.message}'", - s"the evaluation failure message contained '${failure.message}' as expected", - ) + case SuccessfulEvaluationResult(result, _) => + MatchResult( + false, + s"the evaluation didn't fail with '$expectedFailureMessage' but returned '${result}'", + s"the evaluation didn't fail with '$expectedFailureMessage' but returned '${result}'" + ) + case FailedEvaluationResult(failure, _) => + MatchResult( + failure.message.contains(expectedFailureMessage), + s"the evaluation failure message didn't contain '$expectedFailureMessage' but was '${failure.message}'", + s"the evaluation failure message contained '${failure.message}' as expected" + ) } } } } -object EvaluationResultMatchers extends EvaluationResultMatchers \ No newline at end of file +object EvaluationResultMatchers extends EvaluationResultMatchers diff --git a/src/test/scala/org/camunda/feel/impl/FeelEngineTest.scala b/src/test/scala/org/camunda/feel/impl/FeelEngineTest.scala index 5bbb3909f..54a13381d 100644 --- a/src/test/scala/org/camunda/feel/impl/FeelEngineTest.scala +++ b/src/test/scala/org/camunda/feel/impl/FeelEngineTest.scala @@ -16,7 +16,13 @@ */ package org.camunda.feel.impl -import org.camunda.feel.api.{EvaluationResult, FeelEngineApi, FeelEngineBuilder, SuccessfulEvaluationResult, FailedEvaluationResult} +import org.camunda.feel.api.{ + EvaluationResult, + FeelEngineApi, + FeelEngineBuilder, + SuccessfulEvaluationResult, + FailedEvaluationResult +} import org.camunda.feel.context.Context import org.camunda.feel.syntaxtree.ValFunction @@ -27,35 +33,37 @@ trait FeelEngineTest { .build() def evaluateExpression( - expression: String, - variables: Map[String, Any] = Map(), - functions: Map[String, ValFunction] = Map() - ): EvaluationResult = { + expression: String, + variables: Map[String, Any] = Map(), + functions: Map[String, ValFunction] = Map() + ): EvaluationResult = { val context = Context.StaticContext( variables = variables, - functions = functions.map { case (n, f) => n -> List(f) }) + functions = functions.map { case (n, f) => n -> List(f) } + ) engine.evaluateExpression(expression, context) } def evaluateExpression( - expression: String, - context: Context - ): EvaluationResult = { + expression: String, + context: Context + ): EvaluationResult = { engine.evaluateExpression(expression, context) } def evaluateUnaryTests( - expression: String, - inputValue: Any, - variables: Map[String, Any] = Map(), - functions: Map[String, ValFunction] = Map() - ): EvaluationResult = { + expression: String, + inputValue: Any, + variables: Map[String, Any] = Map(), + functions: Map[String, ValFunction] = Map() + ): EvaluationResult = { val context = Context.StaticContext( variables = variables, - functions = functions.map { case (n, f) => n -> List(f) }) + functions = functions.map { case (n, f) => n -> List(f) } + ) engine.evaluateUnaryTests( expression = expression, @@ -65,9 +73,9 @@ trait FeelEngineTest { } def evaluateUnaryTests( - expression: String, - context: Context - ): EvaluationResult = { + expression: String, + context: Context + ): EvaluationResult = { // use parse + evaluate to avoid setting an input value val parsedExpression = engine.parseUnaryTests(expression).parsedExpression @@ -80,10 +88,12 @@ trait FeelEngineTest { def evaluateFunction(function: String): ValFunction = { engine.evaluateExpression(function) match { case SuccessfulEvaluationResult(result: ValFunction, _) => result - case SuccessfulEvaluationResult(result, _) => + case SuccessfulEvaluationResult(result, _) => throw new AssertionError(s"Expected to return a function but was '$result'") - case FailedEvaluationResult(failure, _) => - throw new AssertionError(s"Expected to return a function but failed with '${failure.message}'") + case FailedEvaluationResult(failure, _) => + throw new AssertionError( + s"Expected to return a function but failed with '${failure.message}'" + ) } } diff --git a/src/test/scala/org/camunda/feel/impl/FeelIntegrationTest.scala b/src/test/scala/org/camunda/feel/impl/FeelIntegrationTest.scala index eb1d8fbd3..709116f5b 100644 --- a/src/test/scala/org/camunda/feel/impl/FeelIntegrationTest.scala +++ b/src/test/scala/org/camunda/feel/impl/FeelIntegrationTest.scala @@ -19,11 +19,7 @@ package org.camunda.feel.impl import fastparse.Parsed import org.camunda.feel.FeelEngine.UnaryTests import org.camunda.feel.context.Context -import org.camunda.feel.impl.interpreter.{ - BuiltinFunctions, - EvalContext, - FeelInterpreter -} +import org.camunda.feel.impl.interpreter.{BuiltinFunctions, EvalContext, FeelInterpreter} import org.camunda.feel.impl.parser.FeelParser import org.camunda.feel.syntaxtree.{Val, ValError, ValFunction} import org.camunda.feel.valuemapper.ValueMapper @@ -52,9 +48,12 @@ trait FeelIntegrationTest { ): Val = { val context = - Context.StaticContext(variables = variables, functions = functions.map { - case (n, f) => n -> List(f) - }.toMap) + Context.StaticContext( + variables = variables, + functions = functions.map { case (n, f) => + n -> List(f) + }.toMap + ) eval(expression, context) } @@ -62,28 +61,25 @@ trait FeelIntegrationTest { def eval(expression: String, context: Context): Val = { FeelParser.parseExpression(expression) match { - case Parsed.Success(exp, _) => + case Parsed.Success(exp, _) => interpreter.eval(exp)(rootContext.merge(context)) case Parsed.Failure(_, _, extra) => { - ValError( - s"failed to parse expression '$expression':\n${extra.trace().aggregateMsg}") + ValError(s"failed to parse expression '$expression':\n${extra.trace().aggregateMsg}") } } } - def evalUnaryTests(input: Any, - expression: String, - variables: Map[String, Any] = Map()): Val = { + def evalUnaryTests(input: Any, expression: String, variables: Map[String, Any] = Map()): Val = { - val inputEntry = UnaryTests.defaultInputVariable -> input - val variableValues = (variables + inputEntry).map{ case (key,value) => + val inputEntry = UnaryTests.defaultInputVariable -> input + val variableValues = (variables + inputEntry).map { case (key, value) => key -> rootContext.valueMapper.toVal(value) } - val ctx = rootContext.addAll(variableValues) + val ctx = rootContext.addAll(variableValues) FeelParser.parseUnaryTests(expression) match { case Parsed.Success(exp, _) => interpreter.eval(exp)(ctx) - case e: Parsed.Failure => { + case e: Parsed.Failure => { ValError(s"failed to parse expression '$expression':\n$e") } } @@ -92,7 +88,8 @@ trait FeelIntegrationTest { val rootContext: EvalContext = EvalContext.wrap( Context.StaticContext( variables = Map.empty, - functions = new BuiltinFunctions(clock, ValueMapper.defaultValueMapper).functions), + functions = new BuiltinFunctions(clock, ValueMapper.defaultValueMapper).functions + ), ValueMapper.defaultValueMapper ) diff --git a/src/test/scala/org/camunda/feel/impl/SuppressedFailuresTest.scala b/src/test/scala/org/camunda/feel/impl/SuppressedFailuresTest.scala index 796a82cda..ab66f09b7 100644 --- a/src/test/scala/org/camunda/feel/impl/SuppressedFailuresTest.scala +++ b/src/test/scala/org/camunda/feel/impl/SuppressedFailuresTest.scala @@ -20,10 +20,11 @@ import org.camunda.feel.api.{EvaluationFailureType, EvaluationFailure} import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -class SuppressedFailuresTest extends AnyFlatSpec - with FeelEngineTest - with Matchers - with EvaluationResultMatchers { +class SuppressedFailuresTest + extends AnyFlatSpec + with FeelEngineTest + with Matchers + with EvaluationResultMatchers { "The engine" should "report a suppressed failure for a non-existing variable" in { evaluateExpression("x + 1") should reportFailure( @@ -42,7 +43,8 @@ class SuppressedFailuresTest extends AnyFlatSpec it should "report a suppressed failure for a non-existing property" in { evaluateExpression(""" @"P1Y".days """) should reportFailure( failureType = EvaluationFailureType.NO_PROPERTY_FOUND, - failureMessage = "No property found with name 'days' of value 'P1Y'. Available properties: 'years', 'months'" + failureMessage = + "No property found with name 'days' of value 'P1Y'. Available properties: 'years', 'months'" ) } @@ -97,10 +99,10 @@ class SuppressedFailuresTest extends AnyFlatSpec it should "report a suppressed failure only once" in { val evaluationResult = evaluateExpression("1 + x") - evaluationResult.hasSuppressedFailures should be (true) - evaluationResult.suppressedFailures should have size(2) + evaluationResult.hasSuppressedFailures should be(true) + evaluationResult.suppressedFailures should have size (2) - evaluationResult.suppressedFailures should contain inOrder( + evaluationResult.suppressedFailures should contain inOrder ( EvaluationFailure( failureType = EvaluationFailureType.NO_VARIABLE_FOUND, failureMessage = "No variable found with name 'x'" diff --git a/src/test/scala/org/camunda/feel/impl/builtin/BuiltinContextFunctionsTest.scala b/src/test/scala/org/camunda/feel/impl/builtin/BuiltinContextFunctionsTest.scala index bb023e7e6..3485d395b 100644 --- a/src/test/scala/org/camunda/feel/impl/builtin/BuiltinContextFunctionsTest.scala +++ b/src/test/scala/org/camunda/feel/impl/builtin/BuiltinContextFunctionsTest.scala @@ -22,11 +22,11 @@ import org.camunda.feel.syntaxtree._ import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -/** - * @author Philipp +/** @author + * Philipp */ class BuiltinContextFunctionsTest - extends AnyFlatSpec + extends AnyFlatSpec with Matchers with FeelEngineTest with EvaluationResultMatchers { @@ -84,7 +84,9 @@ class BuiltinContextFunctionsTest } it should "return a context when a path is provided" in { - evaluateExpression("""get value({x: {y: {z:1}}}, ["x", "y"]) = {z:1}""") should returnResult(true) + evaluateExpression("""get value({x: {y: {z:1}}}, ["x", "y"]) = {z:1}""") should returnResult( + true + ) } it should "return null if non-existing path is provided" in { @@ -226,7 +228,9 @@ class BuiltinContextFunctionsTest } it should "be invoked with named parameters (keys)" in { - evaluateExpression(""" context put(context: {x:{y:1}}, keys: ["x","y"], value: 2) """) should returnResult( + evaluateExpression( + """ context put(context: {x:{y:1}}, keys: ["x","y"], value: 2) """ + ) should returnResult( Map("x" -> Map("y" -> 2)) ) } @@ -246,9 +250,13 @@ class BuiltinContextFunctionsTest "A put function (deprecated)" should "behave as the context put function" in { evaluateExpression(""" put({}, "x", 1) = context put({}, "x", 1) """) should returnResult(true) - evaluateExpression(""" put({x:1}, "y", 2) = context put({x:1}, "y", 2) """) should returnResult(true) + evaluateExpression(""" put({x:1}, "y", 2) = context put({x:1}, "y", 2) """) should returnResult( + true + ) - evaluateExpression(""" put({x:1}, "x", 2) = context put({x:1}, "x", 2) """) should returnResult(true) + evaluateExpression(""" put({x:1}, "x", 2) = context put({x:1}, "x", 2) """) should returnResult( + true + ) } "A context merge function" should "return a single empty context" in { @@ -280,11 +288,15 @@ class BuiltinContextFunctionsTest } it should "add all entries at the end of the context" in { - evaluateExpression(" get entries(context merge({a: 1, b: 2}, {c: 3, d: 4})).key ") should returnResult( + evaluateExpression( + " get entries(context merge({a: 1, b: 2}, {c: 3, d: 4})).key " + ) should returnResult( List("a", "b", "c", "d") ) - evaluateExpression(" get entries(context merge({d: 1, c: 2}, {b: 3, a: 4})).key ") should returnResult( + evaluateExpression( + " get entries(context merge({d: 1, c: 2}, {b: 3, a: 4})).key " + ) should returnResult( List("d", "c", "b", "a") ) } @@ -296,17 +308,23 @@ class BuiltinContextFunctionsTest } it should "override entries in order" in { - evaluateExpression(""" context merge({x:1,y:3,z:1}, {x:2,y:2,z:3}, {x:3,y:1,z:2}) """) should returnResult( + evaluateExpression( + """ context merge({x:1,y:3,z:1}, {x:2,y:2,z:3}, {x:3,y:1,z:2}) """ + ) should returnResult( Map("x" -> 3, "y" -> 1, "z" -> 2) ) } it should "override entries and keep the original order" in { - evaluateExpression(" get entries(context merge({a: 1, b: 2, c: 3}, {b: 20, d: 4})).key ") should returnResult( + evaluateExpression( + " get entries(context merge({a: 1, b: 2, c: 3}, {b: 20, d: 4})).key " + ) should returnResult( List("a", "b", "c", "d") ) - evaluateExpression(" get entries(context merge({c: 1, b: 2, a: 3}, {b: 20, d: 4})).key ") should returnResult( + evaluateExpression( + " get entries(context merge({c: 1, b: 2, a: 3}, {b: 20, d: 4})).key " + ) should returnResult( List("c", "b", "a", "d") ) } @@ -373,7 +391,8 @@ class BuiltinContextFunctionsTest it should "return a context with multiple entries" in { evaluateExpression( - """ context([{"key":"a", "value":1}, {"key":"b", "value":true}, {"key":"c", "value":"ok"}]) """) should returnResult( + """ context([{"key":"a", "value":1}, {"key":"b", "value":true}, {"key":"c", "value":"ok"}]) """ + ) should returnResult( Map("a" -> 1, "b" -> true, "c" -> "ok") ) } @@ -391,25 +410,23 @@ class BuiltinContextFunctionsTest } it should "return a context with the same order as the given entries" in { - evaluateExpression( - """get entries(context([ + evaluateExpression("""get entries(context([ {"key":"a","value":1}, {"key":"b","value":2}, {"key":"c","value":3} ])).key""") should returnResult(List("a", "b", "c")) - evaluateExpression( - """get entries(context([ + evaluateExpression("""get entries(context([ {"key":"c","value":1}, {"key":"b","value":2}, {"key":"a","value":3} - ])).key""") should returnResult(List("c", "b", "a") - ) + ])).key""") should returnResult(List("c", "b", "a")) } it should "override entries in order" in { evaluateExpression( - """ context([{"key":"a", "value":1}, {"key":"a", "value":3}, {"key":"a", "value":2}]) """) should returnResult( + """ context([{"key":"a", "value":1}, {"key":"a", "value":3}, {"key":"a", "value":2}]) """ + ) should returnResult( Map("a" -> 2) ) } @@ -417,8 +434,12 @@ class BuiltinContextFunctionsTest it should "be the reverse operation to `get entries()`" in { evaluateExpression(""" context(get entries({})) = {} """) should returnResult(true) evaluateExpression(""" context(get entries({a:1})) = {a:1} """) should returnResult(true) - evaluateExpression(""" context(get entries({a:1,b:2})) = {a:1, b:2} """) should returnResult(true) - evaluateExpression(""" context(get entries({a:1,b:2})[key="a"]) = {a:1} """) should returnResult(true) + evaluateExpression(""" context(get entries({a:1,b:2})) = {a:1, b:2} """) should returnResult( + true + ) + evaluateExpression( + """ context(get entries({a:1,b:2})[key="a"]) = {a:1} """ + ) should returnResult(true) } it should "return null if one entry is not a context" in { @@ -434,7 +455,9 @@ class BuiltinContextFunctionsTest } it should "return null if the key of one entry is not a string" in { - evaluateExpression(""" context([{"key":"a", "value":1}, {"key":2, "value":2}]) """) should returnNull() + evaluateExpression( + """ context([{"key":"a", "value":1}, {"key":2, "value":2}]) """ + ) should returnNull() } } diff --git a/src/test/scala/org/camunda/feel/impl/builtin/BuiltinConversionFunctionsTest.scala b/src/test/scala/org/camunda/feel/impl/builtin/BuiltinConversionFunctionsTest.scala index 0600cccc2..e317f2dd6 100644 --- a/src/test/scala/org/camunda/feel/impl/builtin/BuiltinConversionFunctionsTest.scala +++ b/src/test/scala/org/camunda/feel/impl/builtin/BuiltinConversionFunctionsTest.scala @@ -26,27 +26,23 @@ import java.time.ZonedDateTime import scala.math.BigDecimal.double2bigDecimal import scala.math.BigDecimal.int2bigDecimal -/** - * @author Philipp +/** @author + * Philipp */ -class BuiltinConversionFunctionsTest - extends AnyFlatSpec - with Matchers - with FeelIntegrationTest { +class BuiltinConversionFunctionsTest extends AnyFlatSpec with Matchers with FeelIntegrationTest { "A date() function" should "convert String" in { - eval(""" date(x) """, Map("x" -> "2012-12-25")) should be( - ValDate("2012-12-25")) + eval(""" date(x) """, Map("x" -> "2012-12-25")) should be(ValDate("2012-12-25")) } it should "convert Date-Time" in { - eval(""" date( date and time("2012-12-25T11:00:00") ) """) should be( - ValDate("2012-12-25")) + eval(""" date( date and time("2012-12-25T11:00:00") ) """) should be(ValDate("2012-12-25")) eval(""" date( date and time("2012-12-25T11:00:00+01:00") ) """) should be( - ValDate("2012-12-25")) + ValDate("2012-12-25") + ) } it should "convert (year,month,day)" in { @@ -57,71 +53,73 @@ class BuiltinConversionFunctionsTest "A date and time() function" should "convert String" in { eval(""" date and time(x) """, Map("x" -> "2012-12-24T23:59:00")) should be( - ValLocalDateTime("2012-12-24T23:59:00")) + ValLocalDateTime("2012-12-24T23:59:00") + ) eval(""" date and time(x) """, Map("x" -> "2012-12-24T23:59:00+01:00")) should be( - ValDateTime("2012-12-24T23:59:00+01:00")) + ValDateTime("2012-12-24T23:59:00+01:00") + ) } it should "convert (DateTime, Timezone)" in { eval("""date and time(@"2020-07-31T14:27:30@Europe/Berlin", "Z")""") should be( - ValDateTime(ZonedDateTime.parse("2020-07-31T12:27:30Z"))) + ValDateTime(ZonedDateTime.parse("2020-07-31T12:27:30Z")) + ) eval( - """date and time(@"2020-07-31T14:27:30@Europe/Berlin", "America/Los_Angeles")""") should be( - ValDateTime( - ZonedDateTime.parse("2020-07-31T05:27:30-07:00[America/Los_Angeles]"))) + """date and time(@"2020-07-31T14:27:30@Europe/Berlin", "America/Los_Angeles")""" + ) should be(ValDateTime(ZonedDateTime.parse("2020-07-31T05:27:30-07:00[America/Los_Angeles]"))) - eval( - """date and time(@"2020-07-31T14:27:30", "Z")""") should be( - ValDateTime( - ZonedDateTime.parse("2020-07-31T14:27:30Z"))) + eval("""date and time(@"2020-07-31T14:27:30", "Z")""") should be( + ValDateTime(ZonedDateTime.parse("2020-07-31T14:27:30Z")) + ) } it should "convert (Date,Time)" in { eval(""" date and time(date("2012-12-24"),time("T23:59:00")) """) should be( - ValLocalDateTime("2012-12-24T23:59:00")) + ValLocalDateTime("2012-12-24T23:59:00") + ) eval(""" date and time(date("2012-12-24"),time("T23:59:00+01:00")) """) should be( - ValDateTime("2012-12-24T23:59:00+01:00")) + ValDateTime("2012-12-24T23:59:00+01:00") + ) } it should "convert (DateTime,Time)" in { + eval(""" date and time(date and time("2012-12-24T10:24:00"),time("T23:59:00")) """) should be( + ValLocalDateTime("2012-12-24T23:59:00") + ) eval( - """ date and time(date and time("2012-12-24T10:24:00"),time("T23:59:00")) """) should be( - ValLocalDateTime("2012-12-24T23:59:00")) - eval( - """ date and time(date and time("2012-12-24T10:24:00"),time("T23:59:00+01:00")) """) should be( - ValDateTime("2012-12-24T23:59:00+01:00")) + """ date and time(date and time("2012-12-24T10:24:00"),time("T23:59:00+01:00")) """ + ) should be(ValDateTime("2012-12-24T23:59:00+01:00")) eval( - """ date and time(date and time("2012-12-24T10:24:00+01:00"),time("T23:59:00")) """) should be( - ValLocalDateTime("2012-12-24T23:59:00")) + """ date and time(date and time("2012-12-24T10:24:00+01:00"),time("T23:59:00")) """ + ) should be(ValLocalDateTime("2012-12-24T23:59:00")) eval( - """ date and time(date and time("2012-12-24T10:24:00+01:00"),time("T23:59:00+01:00")) """) should be( - ValDateTime("2012-12-24T23:59:00+01:00")) + """ date and time(date and time("2012-12-24T10:24:00+01:00"),time("T23:59:00+01:00")) """ + ) should be(ValDateTime("2012-12-24T23:59:00+01:00")) } "A time() function" should "convert String" in { - eval(""" time(x) """, Map("x" -> "23:59:00")) should be( - ValLocalTime("23:59:00")) + eval(""" time(x) """, Map("x" -> "23:59:00")) should be(ValLocalTime("23:59:00")) - eval(""" time(x) """, Map("x" -> "23:59:00+01:00")) should be( - ValTime("23:59:00+01:00")) + eval(""" time(x) """, Map("x" -> "23:59:00+01:00")) should be(ValTime("23:59:00+01:00")) eval(""" time(x) """, Map("x" -> "23:59:00@Europe/Paris")) should be( - ValTime("23:59:00@Europe/Paris")) + ValTime("23:59:00@Europe/Paris") + ) } it should "convert Date-Time" in { - eval(""" time( date and time("2012-12-25T11:00:00") ) """) should be( - ValLocalTime("11:00:00")) + eval(""" time( date and time("2012-12-25T11:00:00") ) """) should be(ValLocalTime("11:00:00")) eval(""" time( date and time("2012-12-25T11:00:00+01:00") ) """) should be( - ValTime("11:00:00+01:00")) + ValTime("11:00:00+01:00") + ) } it should "convert (hour,minute,second)" in { @@ -131,8 +129,7 @@ class BuiltinConversionFunctionsTest it should "convert (hour,minute,second, offset)" in { - eval(""" time(14, 30, 0, duration("PT1H")) """) should be( - ValTime("14:30:00+01:00")) + eval(""" time(14, 30, 0, duration("PT1H")) """) should be(ValTime("14:30:00+01:00")) } "A number() function" should "convert String" in { @@ -177,9 +174,9 @@ class BuiltinConversionFunctionsTest it should "be invoked with named parameter" in { - eval( - """ number(from: "1.500", grouping separator: ".", decimal separator: null) """) should be( - ValNumber(1500)) + eval(""" number(from: "1.500", grouping separator: ".", decimal separator: null) """) should be( + ValNumber(1500) + ) } "A string() function" should "convert Number" in { @@ -200,16 +197,17 @@ class BuiltinConversionFunctionsTest it should "convert Time" in { eval(""" string(time("23:59:00")) """) should be(ValString("23:59:00")) - eval(""" string(time("23:59:00+01:00")) """) should be( - ValString("23:59:00+01:00")) + eval(""" string(time("23:59:00+01:00")) """) should be(ValString("23:59:00+01:00")) } it should "convert Date-Time" in { eval(""" string(date and time("2012-12-25T11:00:00")) """) should be( - ValString("2012-12-25T11:00:00")) + ValString("2012-12-25T11:00:00") + ) eval(""" string(date and time("2012-12-25T11:00:00+02:00")) """) should be( - ValString("2012-12-25T11:00:00+02:00")) + ValString("2012-12-25T11:00:00+02:00") + ) } it should "convert zero-length days-time-duration" in { eval(""" string(@"-PT0S") """) should be(ValString("P0D")) @@ -254,58 +252,47 @@ class BuiltinConversionFunctionsTest "A duration() function" should "convert day-time-String" in { - eval(""" duration(x) """, Map("x" -> "P2DT20H14M")) should be( - ValDayTimeDuration("P2DT20H14M")) + eval(""" duration(x) """, Map("x" -> "P2DT20H14M")) should be(ValDayTimeDuration("P2DT20H14M")) } it should "convert day-time-String with negative duration" in { - eval(""" duration(x) """, Map("x" -> "-PT5M")) should be( - ValDayTimeDuration("-PT5M")) + eval(""" duration(x) """, Map("x" -> "-PT5M")) should be(ValDayTimeDuration("-PT5M")) - eval(""" duration(x) """, Map("x" -> "PT-5M")) should be( - ValDayTimeDuration("PT-5M")) + eval(""" duration(x) """, Map("x" -> "PT-5M")) should be(ValDayTimeDuration("PT-5M")) - eval(""" duration(x) """, Map("x" -> "P-1D")) should be( - ValDayTimeDuration("P-1D")) + eval(""" duration(x) """, Map("x" -> "P-1D")) should be(ValDayTimeDuration("P-1D")) - eval(""" duration(x) """, Map("x" -> "PT-2H")) should be( - ValDayTimeDuration("PT-2H")) + eval(""" duration(x) """, Map("x" -> "PT-2H")) should be(ValDayTimeDuration("PT-2H")) - eval(""" duration(x) """, Map("x" -> "PT-3M-4S")) should be( - ValDayTimeDuration("PT-3M-4S")) + eval(""" duration(x) """, Map("x" -> "PT-3M-4S")) should be(ValDayTimeDuration("PT-3M-4S")) } it should "convert year-month-String" in { - eval(""" duration(x) """, Map("x" -> "P2Y4M")) should be( - ValYearMonthDuration("P2Y4M")) + eval(""" duration(x) """, Map("x" -> "P2Y4M")) should be(ValYearMonthDuration("P2Y4M")) } it should "convert year-month-String with negative duration" in { - eval(""" duration(x) """, Map("x" -> "-P1Y2M")) should be( - ValYearMonthDuration("-P1Y2M")) + eval(""" duration(x) """, Map("x" -> "-P1Y2M")) should be(ValYearMonthDuration("-P1Y2M")) - eval(""" duration(x) """, Map("x" -> "P-1Y")) should be( - ValYearMonthDuration("P-1Y")) + eval(""" duration(x) """, Map("x" -> "P-1Y")) should be(ValYearMonthDuration("P-1Y")) - eval(""" duration(x) """, Map("x" -> "P-2M")) should be( - ValYearMonthDuration("P-2M")) + eval(""" duration(x) """, Map("x" -> "P-2M")) should be(ValYearMonthDuration("P-2M")) - eval(""" duration(x) """, Map("x" -> "P-1Y-2M")) should be( - ValYearMonthDuration("P-1Y-2M")) + eval(""" duration(x) """, Map("x" -> "P-1Y-2M")) should be(ValYearMonthDuration("P-1Y-2M")) } "A years and months duration(from,to) function" should "convert (Date,Date)" in { + eval(""" years and months duration( date("2011-12-22"), date("2013-08-24") ) """) should be( + ValYearMonthDuration("P1Y8M") + ) eval( - """ years and months duration( date("2011-12-22"), date("2013-08-24") ) """) should be( - ValYearMonthDuration("P1Y8M")) - eval( - """ years and months duration( date and time("2011-12-22T10:00:00"), date and time("2013-08-24T10:00:00") ) """) should be( - ValYearMonthDuration("P1Y8M")) + """ years and months duration( date and time("2011-12-22T10:00:00"), date and time("2013-08-24T10:00:00") ) """ + ) should be(ValYearMonthDuration("P1Y8M")) eval( - """ years and months duration( date and time("2011-12-22T10:00:00+01:00"), date and time("2013-08-24T10:00:00+01:00") ) """) should be( - ValYearMonthDuration("P1Y8M")) + """ years and months duration( date and time("2011-12-22T10:00:00+01:00"), date and time("2013-08-24T10:00:00+01:00") ) """ + ) should be(ValYearMonthDuration("P1Y8M")) } } diff --git a/src/test/scala/org/camunda/feel/impl/builtin/BuiltinFunctionsTest.scala b/src/test/scala/org/camunda/feel/impl/builtin/BuiltinFunctionsTest.scala index b70d77fb3..e7ec96111 100644 --- a/src/test/scala/org/camunda/feel/impl/builtin/BuiltinFunctionsTest.scala +++ b/src/test/scala/org/camunda/feel/impl/builtin/BuiltinFunctionsTest.scala @@ -21,8 +21,8 @@ import org.camunda.feel.impl.FeelEngineTest import org.scalatest.matchers.should.Matchers import org.scalatest.flatspec.AnyFlatSpec -/** - * @author Philipp +/** @author + * Philipp */ class BuiltinFunctionsTest extends AnyFlatSpec @@ -103,41 +103,41 @@ class BuiltinFunctionsTest "A get or else(value: Any, default: Any) function" should "return the value if not null" in { evaluateExpression( - expression = "get or else(3, 1)", + expression = "get or else(3, 1)" ) should returnResult(3) evaluateExpression( - expression = """get or else("value", "default")""", + expression = """get or else("value", "default")""" ) should returnResult("value") evaluateExpression( - expression = "get or else(value:3, default:1)", + expression = "get or else(value:3, default:1)" ) should returnResult(3) } it should "return the default param if value is null" in { evaluateExpression( - expression = "get or else(null, 1)", + expression = "get or else(null, 1)" ) should returnResult(1) evaluateExpression( - expression = """get or else(null, "default")""", + expression = """get or else(null, "default")""" ) should returnResult("default") evaluateExpression( - expression = "get or else(value:null, default:1)", + expression = "get or else(value:null, default:1)" ) should returnResult(1) } it should "return null if both value and default params are null" in { evaluateExpression( - expression = "get or else(null, null)", + expression = "get or else(null, null)" ) should returnNull() evaluateExpression( - expression = "get or else(value:null, default:null)", + expression = "get or else(value:null, default:null)" ) should returnNull() } @@ -172,7 +172,7 @@ class BuiltinFunctionsTest ) should failWith("The condition is not fulfilled") evaluateExpression( - expression = """assert(x, x != null)""", + expression = """assert(x, x != null)""" ) should failWith("The condition is not fulfilled") evaluateExpression( @@ -187,7 +187,9 @@ class BuiltinFunctionsTest evaluateExpression( expression = """list contains(assert(my_list, my_list != null), 2)""" - ) should failWith("Assertion failure on evaluate the expression 'list contains(assert(my_list, my_list != null), 2)': The condition is not fulfilled") + ) should failWith( + "Assertion failure on evaluate the expression 'list contains(assert(my_list, my_list != null), 2)': The condition is not fulfilled" + ) } "A assert(value: Any, condition: Any, cause: String) function" should "return the value if the condition evaluated to true" in { @@ -221,7 +223,7 @@ class BuiltinFunctionsTest ) should failWith("The condition is not true") evaluateExpression( - expression = """assert(x, x != null, "The condition is not true")""", + expression = """assert(x, x != null, "The condition is not true")""" ) should failWith("The condition is not true") evaluateExpression( @@ -235,7 +237,10 @@ class BuiltinFunctionsTest ) should failWith("The condition is not true") evaluateExpression( - expression = """list contains(assert(my_list, my_list != null, "The condition is not true"), 2)""" - ) should failWith("Assertion failure on evaluate the expression 'list contains(assert(my_list, my_list != null, \"The condition is not true\"), 2)': The condition is not true") + expression = + """list contains(assert(my_list, my_list != null, "The condition is not true"), 2)""" + ) should failWith( + "Assertion failure on evaluate the expression 'list contains(assert(my_list, my_list != null, \"The condition is not true\"), 2)': The condition is not true" + ) } } diff --git a/src/test/scala/org/camunda/feel/impl/builtin/BuiltinListFunctionsTest.scala b/src/test/scala/org/camunda/feel/impl/builtin/BuiltinListFunctionsTest.scala index dccc23c6d..aca5a1254 100644 --- a/src/test/scala/org/camunda/feel/impl/builtin/BuiltinListFunctionsTest.scala +++ b/src/test/scala/org/camunda/feel/impl/builtin/BuiltinListFunctionsTest.scala @@ -24,13 +24,10 @@ import org.camunda.feel.syntaxtree._ import scala.math.BigDecimal.int2bigDecimal -/** - * @author Philipp +/** @author + * Philipp */ -class BuiltinListFunctionsTest - extends AnyFlatSpec - with Matchers - with FeelIntegrationTest { +class BuiltinListFunctionsTest extends AnyFlatSpec with Matchers with FeelIntegrationTest { "A list contains() function" should "return if the list contains Number" in { @@ -64,9 +61,9 @@ class BuiltinListFunctionsTest it should "return the minimum item of date" in { - eval( - """ min([date("2017-01-01"), date("2018-01-01"), date("2019-01-01")]) """) should be( - ValDate("2017-01-01")) + eval(""" min([date("2017-01-01"), date("2018-01-01"), date("2019-01-01")]) """) should be( + ValDate("2017-01-01") + ) } it should "return null if value is not comparable" in { @@ -87,9 +84,9 @@ class BuiltinListFunctionsTest it should "return the maximum item of date" in { - eval( - """ max([date("2017-01-01"), date("2018-01-01"), date("2019-01-01")]) """) should be( - ValDate("2019-01-01")) + eval(""" max([date("2017-01-01"), date("2018-01-01"), date("2019-01-01")]) """) should be( + ValDate("2019-01-01") + ) } it should "return null if value is not comparable" in { @@ -149,8 +146,7 @@ class BuiltinListFunctionsTest it should "return the mode of the list" in { eval(" mode(6, 3, 9, 6, 6) ") should be(ValList(List(ValNumber(6)))) - eval(" mode([6, 1, 9, 6, 1]) ") should be( - ValList(List(ValNumber(1), ValNumber(6)))) + eval(" mode([6, 1, 9, 6, 1]) ") should be(ValList(List(ValNumber(1), ValNumber(6)))) } "A and() / all() function" should "return true if empty list" in { @@ -245,48 +241,45 @@ class BuiltinListFunctionsTest "A sublist() function" should "return list starting with _" in { - eval(" sublist([1,2,3], 2) ") should be( - ValList(List(ValNumber(2), ValNumber(3)))) + eval(" sublist([1,2,3], 2) ") should be(ValList(List(ValNumber(2), ValNumber(3)))) } it should "return list starting with _ and length _" in { - eval(" sublist([1,2,3], 1, 2) ") should be( - ValList(List(ValNumber(1), ValNumber(2)))) + eval(" sublist([1,2,3], 1, 2) ") should be(ValList(List(ValNumber(1), ValNumber(2)))) } "A append() function" should "return list with item appended" in { - eval(" append([1,2], 3) ") should be( - ValList(List(ValNumber(1), ValNumber(2), ValNumber(3)))) - eval(" append([1], 2, 3) ") should be( - ValList(List(ValNumber(1), ValNumber(2), ValNumber(3)))) + eval(" append([1,2], 3) ") should be(ValList(List(ValNumber(1), ValNumber(2), ValNumber(3)))) + eval(" append([1], 2, 3) ") should be(ValList(List(ValNumber(1), ValNumber(2), ValNumber(3)))) } "A concatenate() function" should "return list with item appended" in { eval(" concatenate([1,2],[3]) ") should be( - ValList(List(ValNumber(1), ValNumber(2), ValNumber(3)))) + ValList(List(ValNumber(1), ValNumber(2), ValNumber(3))) + ) eval(" concatenate([1],[2],[3]) ") should be( - ValList(List(ValNumber(1), ValNumber(2), ValNumber(3)))) + ValList(List(ValNumber(1), ValNumber(2), ValNumber(3))) + ) } "A insert before() function" should "return list with new item at _" in { eval(" insert before([1,3],2,2) ") should be( - ValList(List(ValNumber(1), ValNumber(2), ValNumber(3)))) + ValList(List(ValNumber(1), ValNumber(2), ValNumber(3))) + ) } "A remove() function" should "return list with item at _ removed" in { - eval(" remove([1,1,3],2) ") should be( - ValList(List(ValNumber(1), ValNumber(3)))) + eval(" remove([1,1,3],2) ") should be(ValList(List(ValNumber(1), ValNumber(3)))) } "A reverse() function" should "reverse the list" in { - eval(" reverse([1,2,3]) ") should be( - ValList(List(ValNumber(3), ValNumber(2), ValNumber(1)))) + eval(" reverse([1,2,3]) ") should be(ValList(List(ValNumber(3), ValNumber(2), ValNumber(1)))) } "A index of() function" should "return empty list if no match" in { @@ -297,47 +290,47 @@ class BuiltinListFunctionsTest it should "return list of positions containing the match" in { eval(" index of([1,2,3,2], 1) ") should be(ValList(List(ValNumber(1)))) - eval(" index of([1,2,3,2], 2) ") should be( - ValList(List(ValNumber(2), ValNumber(4)))) + eval(" index of([1,2,3,2], 2) ") should be(ValList(List(ValNumber(2), ValNumber(4)))) eval(" index of([1,2,3,2], 3) ") should be(ValList(List(ValNumber(3)))) } "A union() function" should "concatenate with duplicate removal" in { - eval(" union([1,2],[2,3]) ") should be( - ValList(List(ValNumber(1), ValNumber(2), ValNumber(3)))) + eval(" union([1,2],[2,3]) ") should be(ValList(List(ValNumber(1), ValNumber(2), ValNumber(3)))) eval(" union([1,2],[2,3], [4]) ") should be( - ValList(List(ValNumber(1), ValNumber(2), ValNumber(3), ValNumber(4)))) + ValList(List(ValNumber(1), ValNumber(2), ValNumber(3), ValNumber(4))) + ) } "A distinct values() function" should "remove duplicates" in { eval(" distinct values([1,2,3,2,1]) ") should be( - ValList(List(ValNumber(1), ValNumber(2), ValNumber(3)))) + ValList(List(ValNumber(1), ValNumber(2), ValNumber(3))) + ) } "A duplicate values() function" should "return duplicate values" in { - eval(" duplicate values([1,2,3,2,1]) ") should be( - ValList(List(ValNumber(1), ValNumber(2)))) + eval(" duplicate values([1,2,3,2,1]) ") should be(ValList(List(ValNumber(1), ValNumber(2)))) } it should "return null duplicate values" in { - eval(" duplicate values([1,2,1,null,null]) ") should be( - ValList(List(ValNumber(1), ValNull))) + eval(" duplicate values([1,2,1,null,null]) ") should be(ValList(List(ValNumber(1), ValNull))) } it should "return duplicate values for named parameters" in { eval(" duplicate values(list: [1,2,3,2,1]) ") should be( - ValList(List(ValNumber(1), ValNumber(2)))) + ValList(List(ValNumber(1), ValNumber(2))) + ) } "A flatten() function" should "flatten nested lists" in { eval(" flatten([[1,2],[[3]], 4]) ") should be( - ValList(List(ValNumber(1), ValNumber(2), ValNumber(3), ValNumber(4)))) + ValList(List(ValNumber(1), ValNumber(2), ValNumber(3), ValNumber(4))) + ) } it should "flatten a huge list of lists" in { @@ -353,12 +346,8 @@ class BuiltinListFunctionsTest "A sort() function" should "sort list of numbers" in { eval(" sort(list: [3,1,4,5,2], precedes: function(x,y) x < y) ") should be( - ValList( - List(ValNumber(1), - ValNumber(2), - ValNumber(3), - ValNumber(4), - ValNumber(5)))) + ValList(List(ValNumber(1), ValNumber(2), ValNumber(3), ValNumber(4), ValNumber(5))) + ) } "A product() function" should "return null if empty list" in { @@ -401,13 +390,13 @@ class BuiltinListFunctionsTest } it should "return joined strings with custom separator" in { - eval(""" string join(["foo","bar","baz"], "::") """) should be( - ValString("foo::bar::baz")) + eval(""" string join(["foo","bar","baz"], "::") """) should be(ValString("foo::bar::baz")) } it should "return joined strings with custom separator, a prefix and a suffix" in { eval(""" string join(["foo","bar","baz"], "::", "hello-", "-goodbye") """) should be( - ValString("hello-foo::bar::baz-goodbye")) + ValString("hello-foo::bar::baz-goodbye") + ) } it should "return null if the list contains other values than strings" in { diff --git a/src/test/scala/org/camunda/feel/impl/builtin/BuiltinNumberFunctionTest.scala b/src/test/scala/org/camunda/feel/impl/builtin/BuiltinNumberFunctionTest.scala index 58e30883a..c30accc1c 100644 --- a/src/test/scala/org/camunda/feel/impl/builtin/BuiltinNumberFunctionTest.scala +++ b/src/test/scala/org/camunda/feel/impl/builtin/BuiltinNumberFunctionTest.scala @@ -24,13 +24,10 @@ import org.scalatest.flatspec.AnyFlatSpec import scala.math.BigDecimal.{double2bigDecimal, int2bigDecimal} -/** - * @author Philipp +/** @author + * Philipp */ -class BuiltinNumberFunctionsTest - extends AnyFlatSpec - with Matchers - with FeelIntegrationTest { +class BuiltinNumberFunctionsTest extends AnyFlatSpec with Matchers with FeelIntegrationTest { "A decimal() function" should "return number with a given scale" in { @@ -260,17 +257,16 @@ class BuiltinNumberFunctionsTest eval(" round half down(-1.126, 2) ") should be(ValNumber(-1.13)) } - "A random number() function" should "return a number" in { - eval(" random number() ") shouldBe a [ValNumber] + eval(" random number() ") shouldBe a[ValNumber] } it should "return a number between 0.0 and 1.0 " in { eval(" random number() ") match { case ValNumber(x) => x should (be >= BigDecimal(0) and be <= BigDecimal(1)) - case other => fail() + case other => fail() } } } diff --git a/src/test/scala/org/camunda/feel/impl/builtin/BuiltinRangeFunctionTest.scala b/src/test/scala/org/camunda/feel/impl/builtin/BuiltinRangeFunctionTest.scala index de1d6a42e..c3fa1f1a1 100644 --- a/src/test/scala/org/camunda/feel/impl/builtin/BuiltinRangeFunctionTest.scala +++ b/src/test/scala/org/camunda/feel/impl/builtin/BuiltinRangeFunctionTest.scala @@ -21,10 +21,7 @@ import org.camunda.feel.syntaxtree.{ValBoolean, ValNull} import org.scalatest.matchers.should.Matchers import org.scalatest.flatspec.AnyFlatSpec -class BuiltinRangeFunctionTest - extends AnyFlatSpec - with Matchers - with FeelIntegrationTest { +class BuiltinRangeFunctionTest extends AnyFlatSpec with Matchers with FeelIntegrationTest { "A before() function" should "return true when a low number is entered before a high number" in { @@ -714,64 +711,62 @@ class BuiltinRangeFunctionTest it should "support date values" in { - eval(""" before(date("2021-12-21"), date("2021-12-24")) """) should be( - ValBoolean(true)) - eval( - """ before(date("2021-12-21"), [date("2021-12-01")..date("2021-12-24")])""") should be( - ValBoolean(false)) + eval(""" before(date("2021-12-21"), date("2021-12-24")) """) should be(ValBoolean(true)) + eval(""" before(date("2021-12-21"), [date("2021-12-01")..date("2021-12-24")])""") should be( + ValBoolean(false) + ) } it should "support time values" in { - eval(""" before(time("12:00:00+01:00"), time("13:00:00+01:00")) """) should be( - ValBoolean(true)) + eval(""" before(time("12:00:00+01:00"), time("13:00:00+01:00")) """) should be(ValBoolean(true)) eval( - """ before(time("12:00:00+01:00"), [time("10:00:00+01:00")..time("13:00:00+01:00")])""") should be( - ValBoolean(false)) + """ before(time("12:00:00+01:00"), [time("10:00:00+01:00")..time("13:00:00+01:00")])""" + ) should be(ValBoolean(false)) } it should "support time values (local)" in { - eval(""" before(time("12:00:00"), time("13:00:00")) """) should be( - ValBoolean(true)) + eval(""" before(time("12:00:00"), time("13:00:00")) """) should be(ValBoolean(true)) eval(""" before(time("12:00:00"), [time("10:00:00")..time("13:00:00")])""") should be( - ValBoolean(false)) + ValBoolean(false) + ) } it should "support date-time values" in { eval( - """ before(date and time("2021-12-21T12:00:00+01:00"), date and time("2021-12-24T12:00:00+01:00")) """) should be( - ValBoolean(true)) + """ before(date and time("2021-12-21T12:00:00+01:00"), date and time("2021-12-24T12:00:00+01:00")) """ + ) should be(ValBoolean(true)) eval( - """ before(date and time("2021-12-21T12:00:00+01:00"), [date and time("2021-12-01T12:00:00+01:00")..date and time("2021-12-24T12:00:00+01:00")])""") should be( - ValBoolean(false)) + """ before(date and time("2021-12-21T12:00:00+01:00"), [date and time("2021-12-01T12:00:00+01:00")..date and time("2021-12-24T12:00:00+01:00")])""" + ) should be(ValBoolean(false)) } it should "support date-time values (local)" in { eval( - """ before(date and time("2021-12-21T12:00:00"), date and time("2021-12-24T12:00:00")) """) should be( - ValBoolean(true)) + """ before(date and time("2021-12-21T12:00:00"), date and time("2021-12-24T12:00:00")) """ + ) should be(ValBoolean(true)) eval( - """ before(date and time("2021-12-21T12:00:00"), [date and time("2021-12-01T12:00:00")..date and time("2021-12-24T12:00:00")])""") should be( - ValBoolean(false)) + """ before(date and time("2021-12-21T12:00:00"), [date and time("2021-12-01T12:00:00")..date and time("2021-12-24T12:00:00")])""" + ) should be(ValBoolean(false)) } it should "support year-month-duration values" in { - eval(""" before(duration("P3M"), duration("P6M")) """) should be( - ValBoolean(true)) + eval(""" before(duration("P3M"), duration("P6M")) """) should be(ValBoolean(true)) eval(""" before(duration("P3M"), [duration("P1M")..duration("P6M")])""") should be( - ValBoolean(false)) + ValBoolean(false) + ) } it should "support days-times-duration values" in { - eval(""" before(duration("PT3H"), duration("PT6H")) """) should be( - ValBoolean(true)) + eval(""" before(duration("PT3H"), duration("PT6H")) """) should be(ValBoolean(true)) eval(""" before(duration("PT3H"), [duration("PT1H")..duration("PT6H")])""") should be( - ValBoolean(false)) + ValBoolean(false) + ) } it should "return null for string values" in { diff --git a/src/test/scala/org/camunda/feel/impl/builtin/BuiltinStringFunctionsTest.scala b/src/test/scala/org/camunda/feel/impl/builtin/BuiltinStringFunctionsTest.scala index f86d043ef..c93605846 100644 --- a/src/test/scala/org/camunda/feel/impl/builtin/BuiltinStringFunctionsTest.scala +++ b/src/test/scala/org/camunda/feel/impl/builtin/BuiltinStringFunctionsTest.scala @@ -23,13 +23,10 @@ import org.camunda.feel.syntaxtree._ import scala.math.BigDecimal.int2bigDecimal -/** - * @author Philipp +/** @author + * Philipp */ -class BuiltinStringFunctionsTest - extends AnyFlatSpec - with Matchers - with FeelIntegrationTest { +class BuiltinStringFunctionsTest extends AnyFlatSpec with Matchers with FeelIntegrationTest { "A substring() function" should "return string with _ characters" in { @@ -47,8 +44,7 @@ class BuiltinStringFunctionsTest } it should "be invoked with named parameters" in { - eval(""" substring(string: "foobar", start position:3) """) should be( - ValString("obar")) + eval(""" substring(string: "foobar", start position:3) """) should be(ValString("obar")) } "A string length() function" should "return the length of a String" in { @@ -84,13 +80,12 @@ class BuiltinStringFunctionsTest "A replace() function" should "replace a String" in { - eval(""" replace("abcd", "(ab)|(a)", "[1=$1][2=$2]") """) should be( - ValString("[1=ab][2=]cd")) + eval(""" replace("abcd", "(ab)|(a)", "[1=$1][2=$2]") """) should be(ValString("[1=ab][2=]cd")) } it should "replace a String with regex pattern" in (eval( - """ replace("0123456789", "(\d{3})(\d{3})(\d{4})", "($1) $2-$3") """) should be( - ValString("(012) 345-6789"))) + """ replace("0123456789", "(\d{3})(\d{3})(\d{4})", "($1) $2-$3") """ + ) should be(ValString("(012) 345-6789"))) it should "return null if the pattern is invalid" in { eval(""" replace("abc", "([a-z)", "$1") """) should be(ValNull) @@ -131,26 +126,25 @@ class BuiltinStringFunctionsTest "A split() function" should "return a list of substrings" in { eval(""" split("John Doe", "\s") """) should be( - ValList(List(ValString("John"), ValString("Doe")))) + ValList(List(ValString("John"), ValString("Doe"))) + ) eval(""" split("a;b;c;;", ";") """) should be( - ValList( - List(ValString("a"), - ValString("b"), - ValString("c"), - ValString(""), - ValString("")))) + ValList(List(ValString("a"), ValString("b"), ValString("c"), ValString(""), ValString(""))) + ) } "An extract() function" should "return a list of strings matching a pattern" in { eval(""" extract("this is foobar and folbar", "fo[a-z]*") """) should be( - ValList(List(ValString("foobar"), ValString("folbar")))) + ValList(List(ValString("foobar"), ValString("folbar"))) + ) eval(""" extract("nothing", "fo[a-z]*") """) should be(ValList(List())) eval(""" extract("This is fobbar!", "fo[a-z]*") """) should be( - ValList(List(ValString("fobbar")))) + ValList(List(ValString("fobbar"))) + ) } it should "return null if the pattern is invalid" in { diff --git a/src/test/scala/org/camunda/feel/impl/builtin/BuiltinTemporalFunctionsTest.scala b/src/test/scala/org/camunda/feel/impl/builtin/BuiltinTemporalFunctionsTest.scala index a6c0bdb1f..b2ac39d36 100644 --- a/src/test/scala/org/camunda/feel/impl/builtin/BuiltinTemporalFunctionsTest.scala +++ b/src/test/scala/org/camunda/feel/impl/builtin/BuiltinTemporalFunctionsTest.scala @@ -44,21 +44,19 @@ class BuiltinTemporalFunctionsTest ZoneId.of("Europe/Berlin") ) - private val date = "date(2019,9,17)" + private val date = "date(2019,9,17)" private val localDateTime = """date and time("2019-09-17T14:30:00")""" - private val dateTime = + private val dateTime = """date and time("2019-09-17T14:30:00@Europe/Berlin")""" - "The now() function" should "return the current date-time" in withClock { - clock => - clock.currentTime(now) - eval(""" now() """) should be(ValDateTime(now)) + "The now() function" should "return the current date-time" in withClock { clock => + clock.currentTime(now) + eval(""" now() """) should be(ValDateTime(now)) } - "The today() function" should "return the current date" in withClock { - clock => - clock.currentTime(now) - eval(""" today() """) should be(ValDate(now.toLocalDate)) + "The today() function" should "return the current date" in withClock { clock => + clock.currentTime(now) + eval(""" today() """) should be(ValDate(now.toLocalDate)) } "The day of year() function" should "return the day within the year" in { @@ -110,22 +108,22 @@ class BuiltinTemporalFunctionsTest "A last day of month() function" should "return the the last day of the month" in { eval(""" last day of month(date(2022,10,17)) """) should be( - ValDate(LocalDate.parse("2022-10-31"))) - - eval(s"last day of month($date)") should be( - ValDate(LocalDate.parse("2019-09-30"))) - eval(s"last day of month($localDateTime)") should be( - ValDate(LocalDate.parse("2019-09-30"))) - eval(s"last day of month($dateTime)") should be( - ValDate(LocalDate.parse("2019-09-30"))) + ValDate(LocalDate.parse("2022-10-31")) + ) + + eval(s"last day of month($date)") should be(ValDate(LocalDate.parse("2019-09-30"))) + eval(s"last day of month($localDateTime)") should be(ValDate(LocalDate.parse("2019-09-30"))) + eval(s"last day of month($dateTime)") should be(ValDate(LocalDate.parse("2019-09-30"))) } it should "take the leap years into account" in { eval(""" last day of month(date("2022-02-01")) """) should be( - ValDate(LocalDate.parse("2022-02-28"))) + ValDate(LocalDate.parse("2022-02-28")) + ) eval(""" last day of month(date("2024-02-01")) """) should be( - ValDate(LocalDate.parse("2024-02-29"))) + ValDate(LocalDate.parse("2024-02-29")) + ) } } diff --git a/src/test/scala/org/camunda/feel/impl/interpreter/ComparisonTypeTest.scala b/src/test/scala/org/camunda/feel/impl/interpreter/ComparisonTypeTest.scala index fd46b51dc..8899dc2eb 100644 --- a/src/test/scala/org/camunda/feel/impl/interpreter/ComparisonTypeTest.scala +++ b/src/test/scala/org/camunda/feel/impl/interpreter/ComparisonTypeTest.scala @@ -21,10 +21,11 @@ import org.camunda.feel.impl.{EvaluationResultMatchers, FeelEngineTest} import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -class ComparisonTypeTest extends AnyFlatSpec - with Matchers - with FeelEngineTest - with EvaluationResultMatchers { +class ComparisonTypeTest + extends AnyFlatSpec + with Matchers + with FeelEngineTest + with EvaluationResultMatchers { "An equal operator" should "compare two values of the same type" in { evaluateExpression("1 = 1") should returnResult(true) @@ -107,7 +108,7 @@ class ComparisonTypeTest extends AnyFlatSpec } it should "compare two null values" in { - evaluateUnaryTests(expression = "null", inputValue = null)should returnResult(true) + evaluateUnaryTests(expression = "null", inputValue = null) should returnResult(true) } it should "return null if the values have a different type" in { @@ -132,7 +133,10 @@ class ComparisonTypeTest extends AnyFlatSpec } it should "return null if the values have a different type" in { - evaluateUnaryTests(expression = "< 1", inputValue = true) should (returnNull() and reportFailure( + evaluateUnaryTests( + expression = "< 1", + inputValue = true + ) should (returnNull() and reportFailure( failureType = EvaluationFailureType.NOT_COMPARABLE, failureMessage = "Can't compare 'true' with '1'" )) diff --git a/src/test/scala/org/camunda/feel/impl/interpreter/DateTimeDurationPropertiesTest.scala b/src/test/scala/org/camunda/feel/impl/interpreter/DateTimeDurationPropertiesTest.scala index c6fcf7136..d5dab65c4 100644 --- a/src/test/scala/org/camunda/feel/impl/interpreter/DateTimeDurationPropertiesTest.scala +++ b/src/test/scala/org/camunda/feel/impl/interpreter/DateTimeDurationPropertiesTest.scala @@ -23,8 +23,8 @@ import org.scalatest.matchers.should.Matchers import java.time.{Duration, ZonedDateTime} -/** - * @author Philipp Ossler +/** @author + * Philipp Ossler */ class DateTimeDurationPropertiesTest extends AnyFlatSpec @@ -56,7 +56,8 @@ class DateTimeDurationPropertiesTest evaluateExpression(""" date("2020-09-30").seconds """) should ( returnNull() and reportFailure( failureType = EvaluationFailureType.NO_PROPERTY_FOUND, - failureMessage = "No property found with name 'seconds' of value '2020-09-30'. Available properties: 'year', 'month', 'day', 'weekday'" + failureMessage = + "No property found with name 'seconds' of value '2020-09-30'. Available properties: 'year', 'month', 'day', 'weekday'" ) ) } @@ -67,7 +68,7 @@ class DateTimeDurationPropertiesTest evaluateExpression(""" @"2017-03-10".day """) should returnResult(10) } - ///// ----- + // /// ----- "A time" should "has a hour property" in { @@ -96,7 +97,7 @@ class DateTimeDurationPropertiesTest evaluateExpression(""" time("11:45:30@Europe/Paris").timezone """) should returnResult( "Europe/Paris" ) - + evaluateExpression(""" time("11:45:30+02:00").timezone """) should returnNull() } @@ -104,7 +105,8 @@ class DateTimeDurationPropertiesTest evaluateExpression(""" time("11:45:30+02:00").day """) should ( returnNull() and reportFailure( failureType = EvaluationFailureType.NO_PROPERTY_FOUND, - failureMessage = "No property found with name 'day' of value '11:45:30+02:00'. Available properties: 'timezone', 'second', 'time offset', 'minute', 'hour'" + failureMessage = + "No property found with name 'day' of value '11:45:30+02:00'. Available properties: 'timezone', 'second', 'time offset', 'minute', 'hour'" ) ) } @@ -115,7 +117,7 @@ class DateTimeDurationPropertiesTest evaluateExpression(""" @"11:45:30+02:00".second """) should returnResult(30) } - ///// ----- + // /// ----- "A local time" should "has a hour property" in { @@ -146,51 +148,68 @@ class DateTimeDurationPropertiesTest evaluateExpression(""" time("11:45:30").day """) should ( returnNull() and reportFailure( failureType = EvaluationFailureType.NO_PROPERTY_FOUND, - failureMessage = "No property found with name 'day' of value '11:45:30'. Available properties: 'timezone', 'second', 'time offset', 'minute', 'hour'" + failureMessage = + "No property found with name 'day' of value '11:45:30'. Available properties: 'timezone', 'second', 'time offset', 'minute', 'hour'" ) ) } - ///// ----- + // /// ----- "A date-time" should "has a year property" in { - evaluateExpression(""" date and time("2017-03-10T11:45:30+02:00").year """) should returnResult(2017) + evaluateExpression(""" date and time("2017-03-10T11:45:30+02:00").year """) should returnResult( + 2017 + ) } it should "has a month property" in { - evaluateExpression(""" date and time("2017-03-10T11:45:30+02:00").month """) should returnResult(3) + evaluateExpression( + """ date and time("2017-03-10T11:45:30+02:00").month """ + ) should returnResult(3) } it should "has a day property" in { - evaluateExpression(""" date and time("2017-03-10T11:45:30+02:00").day """) should returnResult(10) + evaluateExpression(""" date and time("2017-03-10T11:45:30+02:00").day """) should returnResult( + 10 + ) } it should "has a weekday property" in { - evaluateExpression(""" date and time("2020-09-30T22:50:30+02:00").weekday """) should returnResult(3) + evaluateExpression( + """ date and time("2020-09-30T22:50:30+02:00").weekday """ + ) should returnResult(3) } it should "has a hour property" in { - evaluateExpression(""" date and time("2017-03-10T11:45:30+02:00").hour """) should returnResult(11) + evaluateExpression(""" date and time("2017-03-10T11:45:30+02:00").hour """) should returnResult( + 11 + ) } it should "has a minute property" in { - evaluateExpression(""" date and time("2017-03-10T11:45:30+02:00").minute """) should returnResult(45) + evaluateExpression( + """ date and time("2017-03-10T11:45:30+02:00").minute """ + ) should returnResult(45) } it should "has a second property" in { - evaluateExpression(""" date and time("2017-03-10T11:45:30+02:00").second """) should returnResult(30) + evaluateExpression( + """ date and time("2017-03-10T11:45:30+02:00").second """ + ) should returnResult(30) } it should "has a time offset property" in { - evaluateExpression(""" date and time("2017-03-10T11:45:30+02:00").time offset """) should returnResult( + evaluateExpression( + """ date and time("2017-03-10T11:45:30+02:00").time offset """ + ) should returnResult( Duration.parse("PT2H") ) } @@ -200,21 +219,26 @@ class DateTimeDurationPropertiesTest evaluateExpression( expression = """ dateTime.time offset """, variables = Map("dateTime" -> ZonedDateTime.parse("2017-03-10T11:45:30+02:00")) - ) should returnResult(Duration.parse("PT2H")) + ) should returnResult(Duration.parse("PT2H")) } it should "has a timezone property" in { - evaluateExpression(""" date and time("2017-03-10T11:45:30@Europe/Paris").timezone """) should returnResult("Europe/Paris") + evaluateExpression( + """ date and time("2017-03-10T11:45:30@Europe/Paris").timezone """ + ) should returnResult("Europe/Paris") - evaluateExpression(""" date and time("2017-03-10T11:45:30+02:00").timezone """) should returnNull() + evaluateExpression( + """ date and time("2017-03-10T11:45:30+02:00").timezone """ + ) should returnNull() } it should "return null if the property is not available" in { evaluateExpression(""" date and time("2020-09-30T22:50:30+02:00").days """) should ( returnNull() and reportFailure( failureType = EvaluationFailureType.NO_PROPERTY_FOUND, - failureMessage = "No property found with name 'days' of value '2020-09-30T22:50:30+02:00'. Available properties: 'timezone', 'year', 'second', 'month', 'day', 'time offset', 'weekday', 'minute', 'hour'" + failureMessage = + "No property found with name 'days' of value '2020-09-30T22:50:30+02:00'. Available properties: 'timezone', 'year', 'second', 'month', 'day', 'time offset', 'weekday', 'minute', 'hour'" ) ) } @@ -225,7 +249,7 @@ class DateTimeDurationPropertiesTest evaluateExpression(""" @"2017-03-10T11:45:30+02:00".day """) should returnResult(10) } - ///// ----- + // /// ----- "A local date-time" should "has a year property" in { @@ -275,12 +299,13 @@ class DateTimeDurationPropertiesTest evaluateExpression(""" date and time("2020-09-30T22:50:30").days """) should ( returnNull() and reportFailure( failureType = EvaluationFailureType.NO_PROPERTY_FOUND, - failureMessage = "No property found with name 'days' of value '2020-09-30T22:50:30'. Available properties: 'timezone', 'year', 'second', 'month', 'day', 'time offset', 'weekday', 'minute', 'hour'" + failureMessage = + "No property found with name 'days' of value '2020-09-30T22:50:30'. Available properties: 'timezone', 'year', 'second', 'month', 'day', 'time offset', 'weekday', 'minute', 'hour'" ) ) } - ///// ----- + // /// ----- "A year-month-duration" should "has a years property" in { @@ -296,7 +321,8 @@ class DateTimeDurationPropertiesTest evaluateExpression(""" duration("P2Y3M").day """) should ( returnNull() and reportFailure( failureType = EvaluationFailureType.NO_PROPERTY_FOUND, - failureMessage = "No property found with name 'day' of value 'P2Y3M'. Available properties: 'years', 'months'" + failureMessage = + "No property found with name 'day' of value 'P2Y3M'. Available properties: 'years', 'months'" ) ) } @@ -306,7 +332,7 @@ class DateTimeDurationPropertiesTest evaluateExpression(""" @"P2Y3M".months """) should returnResult(3) } - ///// ----- + // /// ----- "A day-time-duration" should "has a days property" in { @@ -332,9 +358,10 @@ class DateTimeDurationPropertiesTest evaluateExpression(""" duration("P1DT2H10M30S").day """) should ( returnNull() and reportFailure( failureType = EvaluationFailureType.NO_PROPERTY_FOUND, - failureMessage = "No property found with name 'day' of value 'P1DT2H10M30S'. Available properties: 'days', 'hours', 'minutes', 'seconds'" - ) + failureMessage = + "No property found with name 'day' of value 'P1DT2H10M30S'. Available properties: 'days', 'hours', 'minutes', 'seconds'" ) + ) } it should "has properties with @-notation" in { diff --git a/src/test/scala/org/camunda/feel/impl/interpreter/EvaluationErrorMatcher.scala b/src/test/scala/org/camunda/feel/impl/interpreter/EvaluationErrorMatcher.scala index 632d0bea3..8adfe6cc7 100644 --- a/src/test/scala/org/camunda/feel/impl/interpreter/EvaluationErrorMatcher.scala +++ b/src/test/scala/org/camunda/feel/impl/interpreter/EvaluationErrorMatcher.scala @@ -23,16 +23,18 @@ trait EvaluationErrorMatcher { class EvaluationErrorMatcher(expectedMessage: String) extends BeMatcher[Val] { override def apply(result: Val): MatchResult = result match { - case ValError(failure) => MatchResult( - failure.startsWith(expectedMessage), - s"$result doesn't start with '$expectedMessage'", - s"$result starts with '$expectedMessage'", - ) - case _ => MatchResult( - false, - s"$result is not an error", - s"$result is an error" - ) + case ValError(failure) => + MatchResult( + failure.startsWith(expectedMessage), + s"$result doesn't start with '$expectedMessage'", + s"$result starts with '$expectedMessage'" + ) + case _ => + MatchResult( + false, + s"$result is not an error", + s"$result is an error" + ) } } @@ -41,4 +43,4 @@ trait EvaluationErrorMatcher { def aParseError: EvaluationErrorMatcher = anError(expectedMessage = "failed to parse expression") } -object EvaluationErrorMatcher extends EvaluationErrorMatcher \ No newline at end of file +object EvaluationErrorMatcher extends EvaluationErrorMatcher diff --git a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterBeanExpressionTest.scala b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterBeanExpressionTest.scala index 6e7e65d11..e1a29fc82 100644 --- a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterBeanExpressionTest.scala +++ b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterBeanExpressionTest.scala @@ -21,8 +21,8 @@ import org.camunda.feel.impl.{EvaluationResultMatchers, FeelEngineTest} import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -/** - * @author Philipp Ossler +/** @author + * Philipp Ossler */ class InterpreterBeanExpressionTest extends AnyFlatSpec diff --git a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterBooleanExpressionTest.scala b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterBooleanExpressionTest.scala index 2df3e74d8..d0adf6133 100644 --- a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterBooleanExpressionTest.scala +++ b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterBooleanExpressionTest.scala @@ -21,13 +21,10 @@ import org.camunda.feel.syntaxtree._ import org.scalatest.matchers.should.Matchers import org.scalatest.flatspec.AnyFlatSpec -/** - * @author Philipp Ossler +/** @author + * Philipp Ossler */ -class InterpreterBooleanExpressionTest - extends AnyFlatSpec - with Matchers - with FeelIntegrationTest { +class InterpreterBooleanExpressionTest extends AnyFlatSpec with Matchers with FeelIntegrationTest { "A boolean" should "compare with '='" in { @@ -64,23 +61,19 @@ class InterpreterBooleanExpressionTest } it should "be in conjunction with some/every-expression" in { - eval(" (10 > 5) and (some y in [1,2,3] satisfies y > 2) ") should be( - ValBoolean(true)) + eval(" (10 > 5) and (some y in [1,2,3] satisfies y > 2) ") should be(ValBoolean(true)) - eval(" (some y in [1,2,3] satisfies y > 2) and (10 > 5) ") should be( - ValBoolean(true)) + eval(" (some y in [1,2,3] satisfies y > 2) and (10 > 5) ") should be(ValBoolean(true)) eval( - " (some y in [1,2,3] satisfies y > 2) and (every x in [1,2,3] satisfies x < 5) ") should be( - ValBoolean(true)) + " (some y in [1,2,3] satisfies y > 2) and (every x in [1,2,3] satisfies x < 5) " + ) should be(ValBoolean(true)) } it should "be in conjunction (with parentheses)" in { - eval("x and (y)", Map("x" -> true, "y" -> false)) should be( - ValBoolean(false)) + eval("x and (y)", Map("x" -> true, "y" -> false)) should be(ValBoolean(false)) - eval("(x) and y", Map("x" -> true, "y" -> false)) should be( - ValBoolean(false)) + eval("(x) and y", Map("x" -> true, "y" -> false)) should be(ValBoolean(false)) } it should "be in disjunction" in { diff --git a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterContextExpressionTest.scala b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterContextExpressionTest.scala index 5a6de5a99..6ae5bf645 100644 --- a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterContextExpressionTest.scala +++ b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterContextExpressionTest.scala @@ -21,8 +21,8 @@ import org.camunda.feel.impl.{EvaluationResultMatchers, FeelEngineTest} import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -/** - * @author Philipp Ossler +/** @author + * Philipp Ossler */ class InterpreterContextExpressionTest extends AnyFlatSpec @@ -129,7 +129,7 @@ class InterpreterContextExpressionTest failureType = EvaluationFailureType.NO_CONTEXT_ENTRY_FOUND, failureMessage = "No context entry found with key 'x'. The context is empty" ) - ) + ) } it should "return null if no entry exists with the key" in { @@ -139,7 +139,7 @@ class InterpreterContextExpressionTest failureType = EvaluationFailureType.NO_CONTEXT_ENTRY_FOUND, failureMessage = "No context entry found with key 'z'. Available keys: 'x', 'y'" ) - ) + ) } it should "return null if the context is null" in { @@ -150,8 +150,9 @@ class InterpreterContextExpressionTest returnNull() and reportFailure( failureType = EvaluationFailureType.NO_CONTEXT_ENTRY_FOUND, - failureMessage = "No context entry found with key 'b'. The context is null") - ) + failureMessage = "No context entry found with key 'b'. The context is null" + ) + ) } it should "return null if the chained context is null" in { @@ -159,10 +160,12 @@ class InterpreterContextExpressionTest returnNull() and reportFailure( failureType = EvaluationFailureType.NO_CONTEXT_ENTRY_FOUND, - failureMessage = "No context entry found with key 'b'. Available keys: 'a'") and + failureMessage = "No context entry found with key 'b'. Available keys: 'a'" + ) and reportFailure( failureType = EvaluationFailureType.NO_CONTEXT_ENTRY_FOUND, - failureMessage = "No context entry found with key 'c'. The context is null") + failureMessage = "No context entry found with key 'c'. The context is null" + ) ) } @@ -173,7 +176,7 @@ class InterpreterContextExpressionTest failureType = EvaluationFailureType.NO_CONTEXT_ENTRY_FOUND, failureMessage = "No context entry found with key 'z'. The context is empty" ) - ) + ) } it should "return the value of a key with whitespaces" in { @@ -198,7 +201,8 @@ class InterpreterContextExpressionTest ) should returnResult("\uD83D\uDE00") evaluateExpression( - expression = "{ friend+of+mine:2, hello_there:{ how_are_you?:2, are_you_happy?:`friend+of+mine`+3 } }.hello_there.`are_you_happy?`" + expression = + "{ friend+of+mine:2, hello_there:{ how_are_you?:2, are_you_happy?:`friend+of+mine`+3 } }.hello_there.`are_you_happy?`" ) should returnResult(5) } @@ -220,25 +224,29 @@ class InterpreterContextExpressionTest returnResult(List(1, null)) and reportFailure( failureType = EvaluationFailureType.NO_CONTEXT_ENTRY_FOUND, - failureMessage = "No context entry found with key 'a'. Available keys: 'b'") - ) + failureMessage = "No context entry found with key 'a'. Available keys: 'b'" + ) + ) evaluateExpression("[ {a:1}, {b:2} ].b") should ( returnResult(List(null, 2)) and reportFailure( failureType = EvaluationFailureType.NO_CONTEXT_ENTRY_FOUND, - failureMessage = "No context entry found with key 'b'. Available keys: 'a'") - ) + failureMessage = "No context entry found with key 'b'. Available keys: 'a'" + ) + ) evaluateExpression("[ {a:1}, {b:2} ].c") should ( returnResult(List(null, null)) and reportFailure( failureType = EvaluationFailureType.NO_CONTEXT_ENTRY_FOUND, - failureMessage = "No context entry found with key 'c'. Available keys: 'a'") and + failureMessage = "No context entry found with key 'c'. Available keys: 'a'" + ) and reportFailure( failureType = EvaluationFailureType.NO_CONTEXT_ENTRY_FOUND, - failureMessage = "No context entry found with key 'c'. Available keys: 'b'") - ) + failureMessage = "No context entry found with key 'c'. Available keys: 'b'" + ) + ) } "A context filter" should "access a context entry by key" in { diff --git a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterDateTimeExpressionTest.scala b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterDateTimeExpressionTest.scala index 38e26059b..0bbb705f8 100644 --- a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterDateTimeExpressionTest.scala +++ b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterDateTimeExpressionTest.scala @@ -22,390 +22,344 @@ import org.camunda.feel.syntaxtree._ import org.scalatest.matchers.should.Matchers import org.scalatest.flatspec.AnyFlatSpec -/** - * @author Philipp Ossler +/** @author + * Philipp Ossler */ -class InterpreterDateTimeExpressionTest - extends AnyFlatSpec - with Matchers - with FeelIntegrationTest { +class InterpreterDateTimeExpressionTest extends AnyFlatSpec with Matchers with FeelIntegrationTest { "A time" should "subtract from another time" in { - eval(""" time("10:30:00") - time("09:00:00") """) should be( - ValDayTimeDuration("PT1H30M")) + eval(""" time("10:30:00") - time("09:00:00") """) should be(ValDayTimeDuration("PT1H30M")) - eval(""" time("09:00:00") - time("10:00:00") """) should be( - ValDayTimeDuration("PT-1H")) + eval(""" time("09:00:00") - time("10:00:00") """) should be(ValDayTimeDuration("PT-1H")) eval(""" time("12:00:00+01:00") - time("10:00:00+01:00") """) should be( - ValDayTimeDuration("PT2H")) + ValDayTimeDuration("PT2H") + ) } it should "compare with '='" in { - eval(""" time("10:00:00") = time("10:00:00") """) should be( - ValBoolean(true)) - eval(""" time("10:00:00") = time("10:30:00") """) should be( - ValBoolean(false)) + eval(""" time("10:00:00") = time("10:00:00") """) should be(ValBoolean(true)) + eval(""" time("10:00:00") = time("10:30:00") """) should be(ValBoolean(false)) - eval(""" time("10:00:00+01:00") = time("10:30:00+01:00") """) should be( - ValBoolean(false)) - eval(""" time("10:00:00+01:00") = time("10:00:00+02:00") """) should be( - ValBoolean(false)) - eval(""" time("10:00:00+01:00") = time("10:00:00+01:00") """) should be( - ValBoolean(true)) + eval(""" time("10:00:00+01:00") = time("10:30:00+01:00") """) should be(ValBoolean(false)) + eval(""" time("10:00:00+01:00") = time("10:00:00+02:00") """) should be(ValBoolean(false)) + eval(""" time("10:00:00+01:00") = time("10:00:00+01:00") """) should be(ValBoolean(true)) } it should "compare with '!='" in { - eval(""" time("10:00:00") != time("10:00:00") """) should be( - ValBoolean(false)) - eval(""" time("10:00:00") != time("22:00:00") """) should be( - ValBoolean(true)) + eval(""" time("10:00:00") != time("10:00:00") """) should be(ValBoolean(false)) + eval(""" time("10:00:00") != time("22:00:00") """) should be(ValBoolean(true)) - eval(""" time("10:00:00+01:00") != time("10:00:00+01:00") """) should be( - ValBoolean(false)) - eval(""" time("10:00:00+01:00") != time("22:00:00+01:00") """) should be( - ValBoolean(true)) + eval(""" time("10:00:00+01:00") != time("10:00:00+01:00") """) should be(ValBoolean(false)) + eval(""" time("10:00:00+01:00") != time("22:00:00+01:00") """) should be(ValBoolean(true)) } it should "compare with '<'" in { - eval(""" time("10:00:00") < time("11:00:00") """) should be( - ValBoolean(true)) - eval(""" time("10:00:00") < time("10:00:00") """) should be( - ValBoolean(false)) + eval(""" time("10:00:00") < time("11:00:00") """) should be(ValBoolean(true)) + eval(""" time("10:00:00") < time("10:00:00") """) should be(ValBoolean(false)) - eval(""" time("10:00:00+01:00") < time("11:00:00+01:00") """) should be( - ValBoolean(true)) - eval(""" time("10:00:00+01:00") < time("10:00:00+01:00") """) should be( - ValBoolean(false)) + eval(""" time("10:00:00+01:00") < time("11:00:00+01:00") """) should be(ValBoolean(true)) + eval(""" time("10:00:00+01:00") < time("10:00:00+01:00") """) should be(ValBoolean(false)) } it should "compare with '<='" in { - eval(""" time("10:00:00") <= time("10:00:00") """) should be( - ValBoolean(true)) - eval(""" time("10:00:01") <= time("10:00:00") """) should be( - ValBoolean(false)) + eval(""" time("10:00:00") <= time("10:00:00") """) should be(ValBoolean(true)) + eval(""" time("10:00:01") <= time("10:00:00") """) should be(ValBoolean(false)) - eval(""" time("10:00:00+01:00") <= time("10:00:00+01:00") """) should be( - ValBoolean(true)) - eval(""" time("11:00:00+01:00") <= time("10:00:00+01:00") """) should be( - ValBoolean(false)) + eval(""" time("10:00:00+01:00") <= time("10:00:00+01:00") """) should be(ValBoolean(true)) + eval(""" time("11:00:00+01:00") <= time("10:00:00+01:00") """) should be(ValBoolean(false)) } it should "compare with '>'" in { - eval(""" time("11:00:00") > time("11:00:00") """) should be( - ValBoolean(false)) - eval(""" time("10:15:00") > time("10:00:00") """) should be( - ValBoolean(true)) + eval(""" time("11:00:00") > time("11:00:00") """) should be(ValBoolean(false)) + eval(""" time("10:15:00") > time("10:00:00") """) should be(ValBoolean(true)) - eval(""" time("11:00:00+01:00") > time("11:00:00+01:00") """) should be( - ValBoolean(false)) - eval(""" time("10:15:00+01:00") > time("10:00:00+01:00") """) should be( - ValBoolean(true)) + eval(""" time("11:00:00+01:00") > time("11:00:00+01:00") """) should be(ValBoolean(false)) + eval(""" time("10:15:00+01:00") > time("10:00:00+01:00") """) should be(ValBoolean(true)) } it should "compare with '>='" in { - eval(""" time("11:00:00") >= time("11:00:00") """) should be( - ValBoolean(true)) - eval(""" time("09:00:00") >= time("11:15:00") """) should be( - ValBoolean(false)) + eval(""" time("11:00:00") >= time("11:00:00") """) should be(ValBoolean(true)) + eval(""" time("09:00:00") >= time("11:15:00") """) should be(ValBoolean(false)) - eval(""" time("11:00:00+01:00") >= time("11:00:00+01:00") """) should be( - ValBoolean(true)) - eval(""" time("09:00:00+01:00") >= time("11:15:00+01:00") """) should be( - ValBoolean(false)) + eval(""" time("11:00:00+01:00") >= time("11:00:00+01:00") """) should be(ValBoolean(true)) + eval(""" time("09:00:00+01:00") >= time("11:15:00+01:00") """) should be(ValBoolean(false)) } it should "compare with 'between _ and _'" in { eval(""" time("08:30:00") between time("08:00:00") and time("10:00:00") """) should be( - ValBoolean(true)) + ValBoolean(true) + ) eval(""" time("08:30:00") between time("09:00:00") and time("10:00:00") """) should be( - ValBoolean(false)) + ValBoolean(false) + ) eval( - """ time("08:30:00+01:00") between time("08:00:00+01:00") and time("10:00:00+01:00") """) should be( - ValBoolean(true)) + """ time("08:30:00+01:00") between time("08:00:00+01:00") and time("10:00:00+01:00") """ + ) should be(ValBoolean(true)) eval( - """ time("08:30:00+01:00") between time("09:00:00+01:00") and time("10:00:00+01:00") """) should be( - ValBoolean(false)) + """ time("08:30:00+01:00") between time("09:00:00+01:00") and time("10:00:00+01:00") """ + ) should be(ValBoolean(false)) } "A date" should "subtract from another date" in { - eval(""" date("2012-12-25") - date("2012-12-24") """) should be( - ValDayTimeDuration("P1D")) + eval(""" date("2012-12-25") - date("2012-12-24") """) should be(ValDayTimeDuration("P1D")) - eval(""" date("2012-12-24") - date("2012-12-25") """) should be( - ValDayTimeDuration("P-1D")) + eval(""" date("2012-12-24") - date("2012-12-25") """) should be(ValDayTimeDuration("P-1D")) - eval(""" date("2013-02-25") - date("2012-12-24") """) should be( - ValDayTimeDuration("P63D")) + eval(""" date("2013-02-25") - date("2012-12-24") """) should be(ValDayTimeDuration("P63D")) } it should "subtract date from date as string" in { - eval(""" string(date("2020-04-07") - date("2020-04-01")) """) should be( - ValString("P6D")) + eval(""" string(date("2020-04-07") - date("2020-04-01")) """) should be(ValString("P6D")) } it should "compare with '='" in { - eval(""" date("2017-01-10") = date("2017-01-10") """) should be( - ValBoolean(true)) - eval(""" date("2017-01-10") = date("2017-01-11") """) should be( - ValBoolean(false)) + eval(""" date("2017-01-10") = date("2017-01-10") """) should be(ValBoolean(true)) + eval(""" date("2017-01-10") = date("2017-01-11") """) should be(ValBoolean(false)) } it should "compare with '!='" in { - eval(""" date("2017-01-10") != date("2017-01-10") """) should be( - ValBoolean(false)) - eval(""" date("2017-01-10") != date("2017-02-10") """) should be( - ValBoolean(true)) + eval(""" date("2017-01-10") != date("2017-01-10") """) should be(ValBoolean(false)) + eval(""" date("2017-01-10") != date("2017-02-10") """) should be(ValBoolean(true)) } it should "compare with '<'" in { - eval(""" date("2016-01-10") < date("2017-01-10") """) should be( - ValBoolean(true)) - eval(""" date("2017-01-10") < date("2017-01-10") """) should be( - ValBoolean(false)) + eval(""" date("2016-01-10") < date("2017-01-10") """) should be(ValBoolean(true)) + eval(""" date("2017-01-10") < date("2017-01-10") """) should be(ValBoolean(false)) } it should "compare with '<='" in { - eval(""" date("2017-01-10") <= date("2017-01-10") """) should be( - ValBoolean(true)) - eval(""" date("2017-01-20") <= date("2017-01-10") """) should be( - ValBoolean(false)) + eval(""" date("2017-01-10") <= date("2017-01-10") """) should be(ValBoolean(true)) + eval(""" date("2017-01-20") <= date("2017-01-10") """) should be(ValBoolean(false)) } it should "compare with '>'" in { - eval(""" date("2017-01-10") > date("2017-01-10") """) should be( - ValBoolean(false)) - eval(""" date("2017-02-17") > date("2017-01-10") """) should be( - ValBoolean(true)) + eval(""" date("2017-01-10") > date("2017-01-10") """) should be(ValBoolean(false)) + eval(""" date("2017-02-17") > date("2017-01-10") """) should be(ValBoolean(true)) } it should "compare with '>='" in { - eval(""" date("2017-01-10") >= date("2017-01-10") """) should be( - ValBoolean(true)) - eval(""" date("2017-01-10") >= date("2018-01-10") """) should be( - ValBoolean(false)) + eval(""" date("2017-01-10") >= date("2017-01-10") """) should be(ValBoolean(true)) + eval(""" date("2017-01-10") >= date("2018-01-10") """) should be(ValBoolean(false)) } it should "compare with 'between _ and _'" in { - eval( - """ date("2017-01-10") between date("2017-01-01") and date("2018-01-10") """) should be( - ValBoolean(true)) - eval( - """ date("2017-01-10") between date("2017-02-01") and date("2017-03-01") """) should be( - ValBoolean(false)) + eval(""" date("2017-01-10") between date("2017-01-01") and date("2018-01-10") """) should be( + ValBoolean(true) + ) + eval(""" date("2017-01-10") between date("2017-02-01") and date("2017-03-01") """) should be( + ValBoolean(false) + ) } "A date-time" should "subtract from another date-time" in { eval( - """ date and time("2017-01-10T10:30:00") - date and time("2017-01-01T10:00:00") """) should be( - ValDayTimeDuration("P9DT30M")) + """ date and time("2017-01-10T10:30:00") - date and time("2017-01-01T10:00:00") """ + ) should be(ValDayTimeDuration("P9DT30M")) eval( - """ date and time("2017-01-10T10:00:00") - date and time("2017-01-10T10:30:00") """) should be( - ValDayTimeDuration("PT-30M")) + """ date and time("2017-01-10T10:00:00") - date and time("2017-01-10T10:30:00") """ + ) should be(ValDayTimeDuration("PT-30M")) eval( - """ date and time("2017-01-10T10:30:00+01:00") - date and time("2017-01-01T10:00:00+01:00") """) should be( - ValDayTimeDuration("P9DT30M")) + """ date and time("2017-01-10T10:30:00+01:00") - date and time("2017-01-01T10:00:00+01:00") """ + ) should be(ValDayTimeDuration("P9DT30M")) eval( - """ date and time("2017-01-10T10:00:00+01:00") - date and time("2017-01-10T10:30:00+01:00") """) should be( - ValDayTimeDuration("PT-30M")) + """ date and time("2017-01-10T10:00:00+01:00") - date and time("2017-01-10T10:30:00+01:00") """ + ) should be(ValDayTimeDuration("PT-30M")) } it should "compare with '='" in { eval( - """ date and time("2017-01-10T10:30:00") = date and time("2017-01-10T10:30:00") """) should be( - ValBoolean(true)) + """ date and time("2017-01-10T10:30:00") = date and time("2017-01-10T10:30:00") """ + ) should be(ValBoolean(true)) eval( - """ date and time("2017-01-10T10:30:00") = date and time("2017-01-10T14:00:00") """) should be( - ValBoolean(false)) + """ date and time("2017-01-10T10:30:00") = date and time("2017-01-10T14:00:00") """ + ) should be(ValBoolean(false)) eval( - """ date and time("2017-01-10T10:30:00+01:00") = date and time("2017-01-10T10:30:00+01:00") """) should be( - ValBoolean(true)) + """ date and time("2017-01-10T10:30:00+01:00") = date and time("2017-01-10T10:30:00+01:00") """ + ) should be(ValBoolean(true)) eval( - """ date and time("2017-01-10T10:30:00+01:00") = date and time("2017-01-10T14:00:00+01:00") """) should be( - ValBoolean(false)) + """ date and time("2017-01-10T10:30:00+01:00") = date and time("2017-01-10T14:00:00+01:00") """ + ) should be(ValBoolean(false)) } it should "compare with '!='" in { eval( - """ date and time("2017-01-10T10:30:00") != date and time("2017-01-10T10:30:00") """) should be( - ValBoolean(false)) + """ date and time("2017-01-10T10:30:00") != date and time("2017-01-10T10:30:00") """ + ) should be(ValBoolean(false)) eval( - """ date and time("2017-01-10T10:30:00") != date and time("2017-01-11T10:30:00") """) should be( - ValBoolean(true)) + """ date and time("2017-01-10T10:30:00") != date and time("2017-01-11T10:30:00") """ + ) should be(ValBoolean(true)) eval( - """ date and time("2017-01-10T10:30:00+01:00") != date and time("2017-01-10T10:30:00+01:00") """) should be( - ValBoolean(false)) + """ date and time("2017-01-10T10:30:00+01:00") != date and time("2017-01-10T10:30:00+01:00") """ + ) should be(ValBoolean(false)) eval( - """ date and time("2017-01-10T10:30:00+01:00") != date and time("2017-01-11T10:30:00+01:00") """) should be( - ValBoolean(true)) + """ date and time("2017-01-10T10:30:00+01:00") != date and time("2017-01-11T10:30:00+01:00") """ + ) should be(ValBoolean(true)) } it should "compare with '<'" in { eval( - """ date and time("2017-01-10T10:30:00") < date and time("2017-02-10T10:00:00") """) should be( - ValBoolean(true)) + """ date and time("2017-01-10T10:30:00") < date and time("2017-02-10T10:00:00") """ + ) should be(ValBoolean(true)) eval( - """ date and time("2017-01-10T10:30:00") < date and time("2017-01-10T10:30:00") """) should be( - ValBoolean(false)) + """ date and time("2017-01-10T10:30:00") < date and time("2017-01-10T10:30:00") """ + ) should be(ValBoolean(false)) eval( - """ date and time("2017-01-10T10:30:00+01:00") < date and time("2017-02-10T10:00:00+01:00") """) should be( - ValBoolean(true)) + """ date and time("2017-01-10T10:30:00+01:00") < date and time("2017-02-10T10:00:00+01:00") """ + ) should be(ValBoolean(true)) eval( - """ date and time("2017-01-10T10:30:00+01:00") < date and time("2017-01-10T10:30:00+01:00") """) should be( - ValBoolean(false)) + """ date and time("2017-01-10T10:30:00+01:00") < date and time("2017-01-10T10:30:00+01:00") """ + ) should be(ValBoolean(false)) } it should "compare with '<='" in { eval( - """ date and time("2017-01-10T10:30:00") <= date and time("2017-01-10T10:30:00") """) should be( - ValBoolean(true)) + """ date and time("2017-01-10T10:30:00") <= date and time("2017-01-10T10:30:00") """ + ) should be(ValBoolean(true)) eval( - """ date and time("2017-02-10T10:00:00") <= date and time("2017-01-10T10:30:00") """) should be( - ValBoolean(false)) + """ date and time("2017-02-10T10:00:00") <= date and time("2017-01-10T10:30:00") """ + ) should be(ValBoolean(false)) eval( - """ date and time("2017-01-10T10:30:00+01:00") <= date and time("2017-01-10T10:30:00+01:00") """) should be( - ValBoolean(true)) + """ date and time("2017-01-10T10:30:00+01:00") <= date and time("2017-01-10T10:30:00+01:00") """ + ) should be(ValBoolean(true)) eval( - """ date and time("2017-02-10T10:00:00+01:00") <= date and time("2017-01-10T10:30:00+01:00") """) should be( - ValBoolean(false)) + """ date and time("2017-02-10T10:00:00+01:00") <= date and time("2017-01-10T10:30:00+01:00") """ + ) should be(ValBoolean(false)) } it should "compare with '>'" in { eval( - """ date and time("2017-01-10T10:30:00") > date and time("2017-01-10T10:30:00") """) should be( - ValBoolean(false)) + """ date and time("2017-01-10T10:30:00") > date and time("2017-01-10T10:30:00") """ + ) should be(ValBoolean(false)) eval( - """ date and time("2018-01-10T10:30:00") > date and time("2017-01-10T10:30:00") """) should be( - ValBoolean(true)) + """ date and time("2018-01-10T10:30:00") > date and time("2017-01-10T10:30:00") """ + ) should be(ValBoolean(true)) eval( - """ date and time("2017-01-10T10:30:00+01:00") > date and time("2017-01-10T10:30:00+01:00") """) should be( - ValBoolean(false)) + """ date and time("2017-01-10T10:30:00+01:00") > date and time("2017-01-10T10:30:00+01:00") """ + ) should be(ValBoolean(false)) eval( - """ date and time("2018-01-10T10:30:00+01:00") > date and time("2017-01-10T10:30:00+01:00") """) should be( - ValBoolean(true)) + """ date and time("2018-01-10T10:30:00+01:00") > date and time("2017-01-10T10:30:00+01:00") """ + ) should be(ValBoolean(true)) } it should "compare with '>='" in { eval( - """ date and time("2017-01-10T10:30:00") >= date and time("2017-01-10T10:30:00") """) should be( - ValBoolean(true)) + """ date and time("2017-01-10T10:30:00") >= date and time("2017-01-10T10:30:00") """ + ) should be(ValBoolean(true)) eval( - """ date and time("2017-01-10T10:30:00") >= date and time("2017-01-10T10:30:01") """) should be( - ValBoolean(false)) + """ date and time("2017-01-10T10:30:00") >= date and time("2017-01-10T10:30:01") """ + ) should be(ValBoolean(false)) eval( - """ date and time("2017-01-10T10:30:00+01:00") >= date and time("2017-01-10T10:30:00+01:00") """) should be( - ValBoolean(true)) + """ date and time("2017-01-10T10:30:00+01:00") >= date and time("2017-01-10T10:30:00+01:00") """ + ) should be(ValBoolean(true)) eval( - """ date and time("2017-01-10T10:30:00+01:00") >= date and time("2017-01-10T10:30:01+01:00") """) should be( - ValBoolean(false)) + """ date and time("2017-01-10T10:30:00+01:00") >= date and time("2017-01-10T10:30:01+01:00") """ + ) should be(ValBoolean(false)) } it should "compare with 'between _ and _'" in { eval( - """ date and time("2017-01-10T10:30:00") between date and time("2017-01-10T09:00:00") and date and time("2017-01-10T14:00:00") """) should be( - ValBoolean(true)) + """ date and time("2017-01-10T10:30:00") between date and time("2017-01-10T09:00:00") and date and time("2017-01-10T14:00:00") """ + ) should be(ValBoolean(true)) eval( - """ date and time("2017-01-10T10:30:00") between date and time("2017-01-10T11:00:00") and date and time("2017-01-11T08:00:00") """) should be( - ValBoolean(false)) + """ date and time("2017-01-10T10:30:00") between date and time("2017-01-10T11:00:00") and date and time("2017-01-11T08:00:00") """ + ) should be(ValBoolean(false)) eval( - """ date and time("2017-01-10T10:30:00+01:00") between date and time("2017-01-10T09:00:00+01:00") and date and time("2017-01-10T14:00:00+01:00") """) should be( - ValBoolean(true)) + """ date and time("2017-01-10T10:30:00+01:00") between date and time("2017-01-10T09:00:00+01:00") and date and time("2017-01-10T14:00:00+01:00") """ + ) should be(ValBoolean(true)) eval( - """ date and time("2017-01-10T10:30:00+01:00") between date and time("2017-01-10T11:00:00+01:00") and date and time("2017-01-11T08:00:00+01:00") """) should be( - ValBoolean(false)) + """ date and time("2017-01-10T10:30:00+01:00") between date and time("2017-01-10T11:00:00+01:00") and date and time("2017-01-11T08:00:00+01:00") """ + ) should be(ValBoolean(false)) } "A year-month-duration" should "add to year-month-duration" in { - eval(""" duration("P2M") + duration("P3M") """) should be( - ValYearMonthDuration("P5M")) - eval(""" duration("P1Y") + duration("P6M") """) should be( - ValYearMonthDuration("P1Y6M")) + eval(""" duration("P2M") + duration("P3M") """) should be(ValYearMonthDuration("P5M")) + eval(""" duration("P1Y") + duration("P6M") """) should be(ValYearMonthDuration("P1Y6M")) } it should "add to date-time" in { eval(""" duration("P1M") + date and time("2017-01-10T10:30:00") """) should be( - ValLocalDateTime("2017-02-10T10:30:00")) + ValLocalDateTime("2017-02-10T10:30:00") + ) eval(""" date and time("2017-01-10T10:30:00") + duration("P1Y") """) should be( - ValLocalDateTime("2018-01-10T10:30:00")) + ValLocalDateTime("2018-01-10T10:30:00") + ) eval(""" duration("P1M") + date and time("2017-01-10T10:30:00+01:00") """) should be( - ValDateTime("2017-02-10T10:30:00+01:00")) + ValDateTime("2017-02-10T10:30:00+01:00") + ) eval(""" date and time("2017-01-10T10:30:00+01:00") + duration("P1Y") """) should be( - ValDateTime("2018-01-10T10:30:00+01:00")) + ValDateTime("2018-01-10T10:30:00+01:00") + ) } it should "add to date" in { - eval(""" duration("P1M") + date("2017-01-10") """) should be( - ValDate("2017-02-10")) - eval(""" date("2017-01-10") + duration("P1Y") """) should be( - ValDate("2018-01-10")) + eval(""" duration("P1M") + date("2017-01-10") """) should be(ValDate("2017-02-10")) + eval(""" date("2017-01-10") + duration("P1Y") """) should be(ValDate("2018-01-10")) } it should "subtract from year-month-duration" in { - eval(""" duration("P1Y") - duration("P3M") """) should be( - ValYearMonthDuration("P9M")) - eval(""" duration("P5M") - duration("P6M") """) should be( - ValYearMonthDuration("P-1M")) + eval(""" duration("P1Y") - duration("P3M") """) should be(ValYearMonthDuration("P9M")) + eval(""" duration("P5M") - duration("P6M") """) should be(ValYearMonthDuration("P-1M")) } it should "subtract from date-time" in { eval(""" date and time("2017-01-10T10:30:00") - duration("P1M") """) should be( - ValLocalDateTime("2016-12-10T10:30:00")) + ValLocalDateTime("2016-12-10T10:30:00") + ) eval(""" date and time("2017-01-10T10:30:00+01:00") - duration("P1M") """) should be( - ValDateTime("2016-12-10T10:30:00+01:00")) + ValDateTime("2016-12-10T10:30:00+01:00") + ) } it should "subtract from date" in { - eval(""" date("2017-01-10") - duration("P1M") """) should be( - ValDate("2016-12-10")) + eval(""" date("2017-01-10") - duration("P1M") """) should be(ValDate("2016-12-10")) - eval(""" date("2017-01-10") - duration("P1Y") """) should be( - ValDate("2016-01-10")) + eval(""" date("2017-01-10") - duration("P1Y") """) should be(ValDate("2016-01-10")) - eval(""" date("2017-01-10") - duration("P1Y1M") """) should be( - ValDate("2015-12-10")) + eval(""" date("2017-01-10") - duration("P1Y1M") """) should be(ValDate("2015-12-10")) } it should "multiply by '3'" in { @@ -437,8 +391,7 @@ class InterpreterDateTimeExpressionTest it should "compare with '!='" in { - eval(""" duration("P2M") != duration("P2M") """) should be( - ValBoolean(false)) + eval(""" duration("P2M") != duration("P2M") """) should be(ValBoolean(false)) eval(""" duration("P2M") != duration("P1Y") """) should be(ValBoolean(true)) } @@ -451,8 +404,7 @@ class InterpreterDateTimeExpressionTest it should "compare with '<='" in { eval(""" duration("P2M") <= duration("P2M") """) should be(ValBoolean(true)) - eval(""" duration("P1Y2M") <= duration("P2M") """) should be( - ValBoolean(false)) + eval(""" duration("P1Y2M") <= duration("P2M") """) should be(ValBoolean(false)) } it should "compare with '>'" in { @@ -464,97 +416,88 @@ class InterpreterDateTimeExpressionTest it should "compare with '>='" in { eval(""" duration("P2M") >= duration("P2M") """) should be(ValBoolean(true)) - eval(""" duration("P2M") >= duration("P5M") """) should be( - ValBoolean(false)) + eval(""" duration("P2M") >= duration("P5M") """) should be(ValBoolean(false)) } it should "compare with 'between _ and _'" in { eval(""" duration("P3M") between duration("P2M") and duration("P6M") """) should be( - ValBoolean(true)) + ValBoolean(true) + ) eval(""" duration("P1Y") between duration("P2M") and duration("P6M") """) should be( - ValBoolean(false)) + ValBoolean(false) + ) } "A day-time-duration" should "add to day-time-duration" in { - eval(""" duration("PT4H") + duration("PT2H") """) should be( - ValDayTimeDuration("PT6H")) - eval(""" duration("P1D") + duration("PT6H") """) should be( - ValDayTimeDuration("P1DT6H")) + eval(""" duration("PT4H") + duration("PT2H") """) should be(ValDayTimeDuration("PT6H")) + eval(""" duration("P1D") + duration("PT6H") """) should be(ValDayTimeDuration("P1DT6H")) } it should "add to date-time" in { eval(""" duration("PT1H") + date and time("2017-01-10T10:30:00") """) should be( - ValLocalDateTime("2017-01-10T11:30:00")) + ValLocalDateTime("2017-01-10T11:30:00") + ) eval(""" date and time("2017-01-10T10:30:00") + duration("P1D") """) should be( - ValLocalDateTime("2017-01-11T10:30:00")) + ValLocalDateTime("2017-01-11T10:30:00") + ) eval(""" duration("PT1H") + date and time("2017-01-10T10:30:00+01:00") """) should be( - ValDateTime("2017-01-10T11:30:00+01:00")) + ValDateTime("2017-01-10T11:30:00+01:00") + ) eval(""" date and time("2017-01-10T10:30:00+01:00") + duration("P1D") """) should be( - ValDateTime("2017-01-11T10:30:00+01:00")) + ValDateTime("2017-01-11T10:30:00+01:00") + ) } it should "add to date" in { - eval(""" duration("PT1H") + date("2017-01-10") """) should be( - ValDate("2017-01-10")) - eval(""" duration("P1D") + date("2017-01-10") """) should be( - ValDate("2017-01-11")) - eval(""" date("2017-01-10") + duration("PT1M") """) should be( - ValDate("2017-01-10")) - eval(""" date("2017-01-10") + duration("P1D") """) should be( - ValDate("2017-01-11")) + eval(""" duration("PT1H") + date("2017-01-10") """) should be(ValDate("2017-01-10")) + eval(""" duration("P1D") + date("2017-01-10") """) should be(ValDate("2017-01-11")) + eval(""" date("2017-01-10") + duration("PT1M") """) should be(ValDate("2017-01-10")) + eval(""" date("2017-01-10") + duration("P1D") """) should be(ValDate("2017-01-11")) } it should "add to time" in { - eval(""" duration("PT1H") + time("10:30:00") """) should be( - ValLocalTime("11:30:00")) - eval(""" time("10:30:00") + duration("P1D") """) should be( - ValLocalTime("10:30:00")) + eval(""" duration("PT1H") + time("10:30:00") """) should be(ValLocalTime("11:30:00")) + eval(""" time("10:30:00") + duration("P1D") """) should be(ValLocalTime("10:30:00")) - eval(""" duration("PT1H") + time("10:30:00+01:00") """) should be( - ValTime("11:30:00+01:00")) - eval(""" time("10:30:00+01:00") + duration("P1D") """) should be( - ValTime("10:30:00+01:00")) + eval(""" duration("PT1H") + time("10:30:00+01:00") """) should be(ValTime("11:30:00+01:00")) + eval(""" time("10:30:00+01:00") + duration("P1D") """) should be(ValTime("10:30:00+01:00")) } it should "subtract from day-time-duration" in { - eval(""" duration("PT6H") - duration("PT2H") """) should be( - ValDayTimeDuration("PT4H")) - eval(""" duration("PT22H") - duration("P1D") """) should be( - ValDayTimeDuration("PT-2H")) + eval(""" duration("PT6H") - duration("PT2H") """) should be(ValDayTimeDuration("PT4H")) + eval(""" duration("PT22H") - duration("P1D") """) should be(ValDayTimeDuration("PT-2H")) } it should "subtract from date-time" in { eval(""" date and time("2017-01-10T10:30:00") - duration("PT1H") """) should be( - ValLocalDateTime("2017-01-10T09:30:00")) + ValLocalDateTime("2017-01-10T09:30:00") + ) eval(""" date and time("2017-01-10T10:30:00+01:00") - duration("PT1H") """) should be( - ValDateTime("2017-01-10T09:30:00+01:00")) + ValDateTime("2017-01-10T09:30:00+01:00") + ) } it should "subtract from date" in { - eval(""" date("2017-01-10") - duration("PT1H") """) should be( - ValDate("2017-01-09")) + eval(""" date("2017-01-10") - duration("PT1H") """) should be(ValDate("2017-01-09")) - eval(""" date("2017-01-10") - duration("P1DT1H") """) should be( - ValDate("2017-01-08")) + eval(""" date("2017-01-10") - duration("P1DT1H") """) should be(ValDate("2017-01-08")) } it should "subtract from time" in { - eval(""" time("10:30:00") - duration("PT1H") """) should be( - ValLocalTime("09:30:00")) + eval(""" time("10:30:00") - duration("PT1H") """) should be(ValLocalTime("09:30:00")) - eval(""" time("10:30:00+01:00") - duration("PT1H") """) should be( - ValTime("09:30:00+01:00")) + eval(""" time("10:30:00+01:00") - duration("PT1H") """) should be(ValTime("09:30:00+01:00")) } it should "multiply by '3'" in { @@ -580,59 +523,48 @@ class InterpreterDateTimeExpressionTest it should "compare with '='" in { - eval(""" duration("PT6H") = duration("PT6H") """) should be( - ValBoolean(true)) - eval(""" duration("PT6H") = duration("PT2H") """) should be( - ValBoolean(false)) + eval(""" duration("PT6H") = duration("PT6H") """) should be(ValBoolean(true)) + eval(""" duration("PT6H") = duration("PT2H") """) should be(ValBoolean(false)) } it should "compare with '!='" in { - eval(""" duration("PT6H") != duration("PT6H") """) should be( - ValBoolean(false)) - eval(""" duration("PT6H") != duration("P1D") """) should be( - ValBoolean(true)) + eval(""" duration("PT6H") != duration("PT6H") """) should be(ValBoolean(false)) + eval(""" duration("PT6H") != duration("P1D") """) should be(ValBoolean(true)) } it should "compare with '<'" in { - eval(""" duration("PT6H") < duration("PT12H") """) should be( - ValBoolean(true)) - eval(""" duration("PT6H") < duration("PT6H") """) should be( - ValBoolean(false)) + eval(""" duration("PT6H") < duration("PT12H") """) should be(ValBoolean(true)) + eval(""" duration("PT6H") < duration("PT6H") """) should be(ValBoolean(false)) } it should "compare with '<='" in { - eval(""" duration("PT6H") <= duration("PT6H") """) should be( - ValBoolean(true)) - eval(""" duration("PT6H") <= duration("PT1H") """) should be( - ValBoolean(false)) + eval(""" duration("PT6H") <= duration("PT6H") """) should be(ValBoolean(true)) + eval(""" duration("PT6H") <= duration("PT1H") """) should be(ValBoolean(false)) } it should "compare with '>'" in { - eval(""" duration("PT6H") > duration("PT6H") """) should be( - ValBoolean(false)) + eval(""" duration("PT6H") > duration("PT6H") """) should be(ValBoolean(false)) eval(""" duration("P1D") > duration("PT6H") """) should be(ValBoolean(true)) } it should "compare with '>='" in { - eval(""" duration("PT6H") >= duration("PT6H") """) should be( - ValBoolean(true)) - eval(""" duration("PT6H") >= duration("PT6H1M") """) should be( - ValBoolean(false)) + eval(""" duration("PT6H") >= duration("PT6H") """) should be(ValBoolean(true)) + eval(""" duration("PT6H") >= duration("PT6H1M") """) should be(ValBoolean(false)) } it should "compare with 'between _ and _'" in { - eval( - """ duration("PT8H") between duration("PT6H") and duration("PT12H") """) should be( - ValBoolean(true)) - eval( - """ duration("PT2H") between duration("PT6H") and duration("PT12H") """) should be( - ValBoolean(false)) + eval(""" duration("PT8H") between duration("PT6H") and duration("PT12H") """) should be( + ValBoolean(true) + ) + eval(""" duration("PT2H") between duration("PT6H") and duration("PT12H") """) should be( + ValBoolean(false) + ) } } diff --git a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterExpressionTest.scala b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterExpressionTest.scala index c16f8f281..f20c29dc6 100644 --- a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterExpressionTest.scala +++ b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterExpressionTest.scala @@ -23,13 +23,10 @@ import org.camunda.feel.syntaxtree._ import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -/** - * @author Philipp Ossler - */ -class InterpreterExpressionTest - extends AnyFlatSpec - with Matchers - with FeelIntegrationTest { +/** @author + * Philipp Ossler + */ +class InterpreterExpressionTest extends AnyFlatSpec with Matchers with FeelIntegrationTest { "An expression" should "be an if-then-else (with parentheses)" in { val exp = """ if (x < 5) then "low" else "high" """ @@ -73,26 +70,22 @@ class InterpreterExpressionTest } it should "be an if-then-else (with variable and function call -> then)" in { - eval("if 7 > var then flatten(xs) else []", - Map("xs" -> List(1, 2), "var" -> 3)) should be( + eval("if 7 > var then flatten(xs) else []", Map("xs" -> List(1, 2), "var" -> 3)) should be( ValList(List(ValNumber(1), ValNumber(2))) ) } it should "be an if-then-else (with variable and function call -> else)" in { - eval("if false then var else flatten(xs)", - Map("xs" -> List(1, 2), "var" -> 3)) should be( + eval("if false then var else flatten(xs)", Map("xs" -> List(1, 2), "var" -> 3)) should be( ValList(List(ValNumber(1), ValNumber(2))) ) } it should "be a simple positive unary test" in { - eval("< 3", Map(UnaryTests.defaultInputVariable -> 2)) should be( - ValBoolean(true)) + eval("< 3", Map(UnaryTests.defaultInputVariable -> 2)) should be(ValBoolean(true)) - eval("(2 .. 4)", Map(UnaryTests.defaultInputVariable -> 5)) should be( - ValBoolean(false)) + eval("(2 .. 4)", Map(UnaryTests.defaultInputVariable -> 5)) should be(ValBoolean(false)) } it should "be an instance of (literal)" in { @@ -127,7 +120,9 @@ class InterpreterExpressionTest } it should "be an instance of (date and time)" in { - eval("""date and time("2023-03-07T11:27:00") instance of date and time""") should be(ValBoolean(true)) + eval("""date and time("2023-03-07T11:27:00") instance of date and time""") should be( + ValBoolean(true) + ) eval(""" @"2023-03-07T11:27:00" instance of date and time""") should be(ValBoolean(true)) eval("1 instance of date and time") should be(ValBoolean(false)) } @@ -186,8 +181,7 @@ class InterpreterExpressionTest eval("{x:(xs[1])}.x", context) should be(ValNumber(1)) eval("{x:(xs)[1]}.x", context) should be(ValNumber(1)) - eval("{x:(xs)}.x", context) should be( - ValList(List(ValNumber(1), ValNumber(2), ValNumber(3)))) + eval("{x:(xs)}.x", context) should be(ValList(List(ValNumber(1), ValNumber(2), ValNumber(3)))) } it should "contains nested filter expressions" in { @@ -313,8 +307,7 @@ class InterpreterExpressionTest ).foreach { variableName => it should s"contain a key-word ($variableName)" in { - eval(s"$variableName = true", Map(variableName -> true)) should be( - ValBoolean(true)) + eval(s"$variableName = true", Map(variableName -> true)) should be(ValBoolean(true)) } } @@ -327,16 +320,14 @@ class InterpreterExpressionTest } it should "be written as single line comments /* .. */" in { - eval( - """ + eval(""" /* the first item */ [1,2,3][1] """) should be(ValNumber(1)) } it should "be written as block comments /* .. */" in { - eval( - """ + eval(""" /* * the first item */ diff --git a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterFunctionTest.scala b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterFunctionTest.scala index 34320024a..aaf8302c8 100644 --- a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterFunctionTest.scala +++ b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterFunctionTest.scala @@ -21,8 +21,8 @@ import org.camunda.feel.impl.{EvaluationResultMatchers, FeelEngineTest} import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -/** - * @author Philipp Ossler +/** @author + * Philipp Ossler */ class InterpreterFunctionTest extends AnyFlatSpec @@ -38,7 +38,7 @@ class InterpreterFunctionTest "A function invocation" should "invoke a function without parameter" in { evaluateExpression( - expression = "f()", + expression = "f()", functions = Map("f" -> evaluateFunction("""function() "invoked" """)) ) should returnResult("invoked") } @@ -125,37 +125,55 @@ class InterpreterFunctionTest evaluateExpression(expression = "f()", functions = functions) should ( returnNull() and - reportFailure(EvaluationFailureType.NO_FUNCTION_FOUND, "No function found with name 'f' and 0 parameters") - ) + reportFailure( + EvaluationFailureType.NO_FUNCTION_FOUND, + "No function found with name 'f' and 0 parameters" + ) + ) evaluateExpression(expression = "f(1)", functions = functions) should ( returnNull() and - reportFailure(EvaluationFailureType.NO_FUNCTION_FOUND, "No function found with name 'f' and 1 parameters") - ) + reportFailure( + EvaluationFailureType.NO_FUNCTION_FOUND, + "No function found with name 'f' and 1 parameters" + ) + ) evaluateExpression(expression = "f(x:1,z:3)", functions = functions) should ( returnNull() and - reportFailure(EvaluationFailureType.NO_FUNCTION_FOUND, "No function found with name 'f' and parameters: x,z") - ) + reportFailure( + EvaluationFailureType.NO_FUNCTION_FOUND, + "No function found with name 'f' and parameters: x,z" + ) + ) evaluateExpression(expression = "f(x:1,y:2,z:3)", functions = functions) should ( returnNull() and - reportFailure(EvaluationFailureType.NO_FUNCTION_FOUND, "No function found with name 'f' and parameters: x,y,z") - ) + reportFailure( + EvaluationFailureType.NO_FUNCTION_FOUND, + "No function found with name 'f' and parameters: x,y,z" + ) + ) } it should "return null if no function exists with the name" in { evaluateExpression("f()") should ( returnNull() and - reportFailure(EvaluationFailureType.NO_FUNCTION_FOUND, "No function found with name 'f' and 0 parameters") - ) + reportFailure( + EvaluationFailureType.NO_FUNCTION_FOUND, + "No function found with name 'f' and 0 parameters" + ) + ) } it should "return null if the name doesn't resolve to a function" in { evaluateExpression(expression = "f()", variables = Map("x" -> "a variable")) should ( returnNull() and - reportFailure(EvaluationFailureType.NO_FUNCTION_FOUND, "No function found with name 'f' and 0 parameters") - ) + reportFailure( + EvaluationFailureType.NO_FUNCTION_FOUND, + "No function found with name 'f' and 0 parameters" + ) + ) } it should "return null for a built-in function if invoked with wrong arguments" in { @@ -163,8 +181,9 @@ class InterpreterFunctionTest returnNull() and reportFailure( failureType = EvaluationFailureType.FUNCTION_INVOCATION_FAILURE, - failureMessage = "Failed to invoke function 'number': Illegal arguments: 'null'") - ) + failureMessage = "Failed to invoke function 'number': Illegal arguments: 'null'" + ) + ) } it should "replace not set parameters with null" in { @@ -240,7 +259,9 @@ class InterpreterFunctionTest "An external Java function invocation" should "invoke a function with a double parameter" in { val functions = Map( "cos" -> evaluateFunction( - """ function(angle) external { java: { class: "java.lang.Math", method signature: "cos(double)" } } """)) + """ function(angle) external { java: { class: "java.lang.Math", method signature: "cos(double)" } } """ + ) + ) evaluateExpression( expression = "cos(0)", @@ -256,8 +277,7 @@ class InterpreterFunctionTest it should "invoke a function with two int parameters" in { evaluateExpression( expression = "max(1,2)", - functions = Map("max" -> evaluateFunction( - """ function(x,y) external { java: { + functions = Map("max" -> evaluateFunction(""" function(x,y) external { java: { class: "java.lang.Math", method signature: "max(int, int)" } } """)) ) should returnResult(2) } @@ -265,8 +285,7 @@ class InterpreterFunctionTest it should "invoke a function with a long parameters" in { evaluateExpression( expression = "abs(-1)", - functions = Map("abs" -> evaluateFunction( - """ function(a) external { java: { + functions = Map("abs" -> evaluateFunction(""" function(a) external { java: { class: "java.lang.Math", method signature: "abs(long)" } } """)) ) should returnResult(1) } @@ -274,8 +293,7 @@ class InterpreterFunctionTest it should "invoke a function with a float parameters" in { evaluateExpression( expression = "round(3.2)", - functions = Map("round" -> evaluateFunction( - """ function(a) external { java: { + functions = Map("round" -> evaluateFunction(""" function(a) external { java: { class: "java.lang.Math", method signature: "round(float)" } } """)) ) should returnResult(3) } diff --git a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterListExpressionTest.scala b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterListExpressionTest.scala index 8d94f11be..a6ee4257a 100644 --- a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterListExpressionTest.scala +++ b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterListExpressionTest.scala @@ -24,11 +24,11 @@ import org.scalatest.matchers.should.Matchers import scala.collection.mutable.ListBuffer -/** - * @author Philipp Ossler +/** @author + * Philipp Ossler */ class InterpreterListExpressionTest - extends AnyFlatSpec + extends AnyFlatSpec with Matchers with FeelEngineTest with EvaluationResultMatchers { @@ -178,23 +178,20 @@ class InterpreterListExpressionTest val result = evaluateExpression( expression = "[1,2,3,4][f(item)]", variables = Map(), - functions = Map("f" -> ValFunction( - params = List("x"), - invoke = { - case List(x) => + functions = Map( + "f" -> ValFunction( + params = List("x"), + invoke = { case List(x) => functionInvocations += x ValBoolean(x == ValNumber(3)) - } - ))) + } + ) + ) + ) result should returnResult(List(3)) - functionInvocations should be(List( - ValNumber(1), - ValNumber(2), - ValNumber(3), - ValNumber(4)) - ) + functionInvocations should be(List(ValNumber(1), ValNumber(2), ValNumber(3), ValNumber(4))) } it should "be filtered via custom numeric function" in { @@ -203,20 +200,20 @@ class InterpreterListExpressionTest val result = evaluateExpression( expression = "[1,2,3,4][f(item)]", variables = Map(), - functions = Map("f" -> ValFunction( - params = List("x"), - invoke = { - case List(x) => + functions = Map( + "f" -> ValFunction( + params = List("x"), + invoke = { case List(x) => functionInvocations += x ValNumber(3) - } - ))) + } + ) + ) + ) result should returnResult(3) - functionInvocations should be(List( - ValNumber(1)) - ) + functionInvocations should be(List(ValNumber(1))) } it should "be filtered multiple times (from literal)" in { @@ -230,7 +227,9 @@ class InterpreterListExpressionTest evaluateExpression("xs[1][1]", Map("xs" -> listOfLists)) should returnResult(1) evaluateExpression("xs[1][1][1]", Map("xs" -> List(listOfLists))) should returnResult(1) - evaluateExpression("xs[1][1][1][1]", Map("xs" -> List(List(listOfLists)))) should returnResult(1) + evaluateExpression("xs[1][1][1][1]", Map("xs" -> List(List(listOfLists)))) should returnResult( + 1 + ) } it should "be filtered multiple times (from function invocation)" in { @@ -243,8 +242,14 @@ class InterpreterListExpressionTest val listOfLists = List(List(1)) evaluateExpression("x.y[1][1]", Map("x" -> Map("y" -> listOfLists))) should returnResult(1) - evaluateExpression("x.y[1][1][1]", Map("x" -> Map("y" -> List(listOfLists)))) should returnResult(1) - evaluateExpression("x.y[1][1][1][1]", Map("x" -> Map("y" -> List(List(listOfLists))))) should returnResult(1) + evaluateExpression( + "x.y[1][1][1]", + Map("x" -> Map("y" -> List(listOfLists))) + ) should returnResult(1) + evaluateExpression( + "x.y[1][1][1][1]", + Map("x" -> Map("y" -> List(List(listOfLists)))) + ) should returnResult(1) } it should "be filtered multiple times (from context projection)" in { @@ -256,10 +261,17 @@ class InterpreterListExpressionTest it should "be filtered multiple times (in a context)" in { val listOfLists = List(List(1)) - evaluateExpression("{z: x.y[1][1]}.z", Map("x" -> Map("y" -> listOfLists))) should returnResult(1) - evaluateExpression("{z: x.y[1][1][1]}.z", Map("x" -> Map("y" -> List(listOfLists)))) should returnResult(1) - evaluateExpression("{z: x.y[1][1][1][1]}.z", - Map("x" -> Map("y" -> List(List(listOfLists))))) should returnResult(1) + evaluateExpression("{z: x.y[1][1]}.z", Map("x" -> Map("y" -> listOfLists))) should returnResult( + 1 + ) + evaluateExpression( + "{z: x.y[1][1][1]}.z", + Map("x" -> Map("y" -> List(listOfLists))) + ) should returnResult(1) + evaluateExpression( + "{z: x.y[1][1][1][1]}.z", + Map("x" -> Map("y" -> List(List(listOfLists)))) + ) should returnResult(1) } it should "be filtered if the filter doesn't always return a boolean or a number" in { @@ -306,33 +318,38 @@ class InterpreterListExpressionTest } it should "return null if compare to not a list" in { - evaluateExpression("[] = 1") should ( - returnNull() and reportFailure( - failureType = EvaluationFailureType.NOT_COMPARABLE, - failureMessage = "Can't compare '[]' with '1'" - )) + evaluateExpression("[] = 1") should (returnNull() and reportFailure( + failureType = EvaluationFailureType.NOT_COMPARABLE, + failureMessage = "Can't compare '[]' with '1'" + )) } "A for-expression" should "iterate over a range" in { evaluateExpression("for x in 1..3 return x * 2") should returnResult(List(2, 4, 6)) - evaluateExpression("for x in 1..n return x * 2", Map("n" -> 3)) should returnResult(List(2, 4, 6)) + evaluateExpression("for x in 1..n return x * 2", Map("n" -> 3)) should returnResult( + List(2, 4, 6) + ) } it should "iterate over a range in descending order" in { evaluateExpression("for x in 3..1 return x * 2") should returnResult(List(6, 4, 2)) - evaluateExpression("for x in n..1 return x * 2", Map("n" -> 3)) should returnResult(List(6, 4, 2)) + evaluateExpression("for x in n..1 return x * 2", Map("n" -> 3)) should returnResult( + List(6, 4, 2) + ) } it should "access the partial result" in { - evaluateExpression("for x in 1..5 return if (x = 1) then 1 else x + sum(partial)") should returnResult( + evaluateExpression( + "for x in 1..5 return if (x = 1) then 1 else x + sum(partial)" + ) should returnResult( List(1, 3, 7, 15, 31) ) evaluateExpression( - "for i in 1..8 return if (i <= 2) then 1 else partial[-1] + partial[-2]") should returnResult( - List(1, 1, 2, 3, 5, 8, 13, 21)) + "for i in 1..8 return if (i <= 2) then 1 else partial[-1] + partial[-2]" + ) should returnResult(List(1, 1, 2, 3, 5, 8, 13, 21)) } private val hugeList: List[Int] = (1 to 10000).toList @@ -342,9 +359,15 @@ class InterpreterListExpressionTest } it should "be checked with 'some'" in { - evaluateExpression("some x in xs satisfies x >= 10000", Map("xs" -> hugeList)) should returnResult(true) + evaluateExpression( + "some x in xs satisfies x >= 10000", + Map("xs" -> hugeList) + ) should returnResult(true) - evaluateExpression("some x in xs satisfies x > 10000", Map("xs" -> hugeList)) should returnResult(false) + evaluateExpression( + "some x in xs satisfies x > 10000", + Map("xs" -> hugeList) + ) should returnResult(false) } it should "be checked with 'some' (invalid condition)" in { @@ -352,7 +375,9 @@ class InterpreterListExpressionTest } it should "be checked with 'every'" in { - evaluateExpression("every x in xs satisfies x > 0", Map("xs" -> hugeList)) should returnResult(true) + evaluateExpression("every x in xs satisfies x > 0", Map("xs" -> hugeList)) should returnResult( + true + ) } it should "be checked with 'every' (invalid condition)" in { diff --git a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterLiteralExpressionTest.scala b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterLiteralExpressionTest.scala index 4740dc0b7..74be0a055 100644 --- a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterLiteralExpressionTest.scala +++ b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterLiteralExpressionTest.scala @@ -32,13 +32,10 @@ import java.time.{ ZonedDateTime } -/** - * @author Philipp Ossler +/** @author + * Philipp Ossler */ -class InterpreterLiteralExpressionTest - extends AnyFlatSpec - with Matchers - with FeelIntegrationTest { +class InterpreterLiteralExpressionTest extends AnyFlatSpec with Matchers with FeelIntegrationTest { "A literal" should "be a number" in { @@ -103,8 +100,7 @@ class InterpreterLiteralExpressionTest result shouldBe a[ValContext] result match { case ValContext(context) => - context.variableProvider.getVariables should be( - Map("a" -> ValNumber(1))) + context.variableProvider.getVariables should be(Map("a" -> ValNumber(1))) } } @@ -116,36 +112,40 @@ class InterpreterLiteralExpressionTest // nested eval("[ [1], [2] ]") should be( - ValList(List(ValList(List(ValNumber(1))), ValList(List(ValNumber(2)))))) + ValList(List(ValList(List(ValNumber(1))), ValList(List(ValNumber(2))))) + ) } "A date literal" should "be defined" in { eval(""" date("2021-09-08") """) should be( ValDate( LocalDate.parse("2021-09-08") - )) + ) + ) } it should "be defined with '@'" in { eval(""" @"2021-09-08" """) should be( ValDate( LocalDate.parse("2021-09-08") - )) + ) + ) } "A time literal" should "be defined without offset" in { eval(""" time("10:30:00") """) should be( ValLocalTime( LocalTime.parse("10:30:00") - )) + ) + ) } it should "be defined with offset" in { eval(""" time("10:30:00+02:00") """) should be( ValTime( - ZonedTime.of(time = LocalTime.parse("10:30:00"), - offset = ZoneOffset.ofHours(2)) - )) + ZonedTime.of(time = LocalTime.parse("10:30:00"), offset = ZoneOffset.ofHours(2)) + ) + ) } it should "be defined with timezone" in { @@ -155,22 +155,24 @@ class InterpreterLiteralExpressionTest time = LocalTime.parse("10:30:00"), zoneId = ZoneId.of("Europe/Berlin") ) - )) + ) + ) } it should "be defined with '@' and no offset" in { eval(""" @"10:30:00" """) should be( ValLocalTime( LocalTime.parse("10:30:00") - )) + ) + ) } it should "be defined with '@' and offset" in { eval(""" @"10:30:00+02:00" """) should be( ValTime( - ZonedTime.of(time = LocalTime.parse("10:30:00"), - offset = ZoneOffset.ofHours(2)) - )) + ZonedTime.of(time = LocalTime.parse("10:30:00"), offset = ZoneOffset.ofHours(2)) + ) + ) } it should "be defined with '@' and timezone" in { @@ -180,22 +182,24 @@ class InterpreterLiteralExpressionTest time = LocalTime.parse("10:30:00"), zoneId = ZoneId.of("Europe/Berlin") ) - )) + ) + ) } "A date-time literal" should "be defined without offset" in { eval(""" date and time("2021-09-08T10:30:00") """) should be( ValLocalDateTime( LocalDateTime.parse("2021-09-08T10:30:00") - )) + ) + ) } it should "be defined with offset" in { eval(""" date and time("2021-09-08T10:30:00+02:00") """) should be( ValDateTime( - ZonedDateTime.of(LocalDateTime.parse("2021-09-08T10:30:00"), - ZoneOffset.ofHours(2)) - )) + ZonedDateTime.of(LocalDateTime.parse("2021-09-08T10:30:00"), ZoneOffset.ofHours(2)) + ) + ) } it should "be defined with timezone" in { @@ -205,22 +209,24 @@ class InterpreterLiteralExpressionTest LocalDateTime.parse("2021-09-08T10:30:00"), ZoneId.of("Europe/Berlin") ) - )) + ) + ) } it should "be defined with '@' and no offset" in { eval(""" @"2021-09-08T10:30:00" """) should be( ValLocalDateTime( LocalDateTime.parse("2021-09-08T10:30:00") - )) + ) + ) } it should "be defined with '@' and offset" in { eval(""" @"2021-09-08T10:30:00+02:00" """) should be( ValDateTime( - ZonedDateTime.of(LocalDateTime.parse("2021-09-08T10:30:00"), - ZoneOffset.ofHours(2)) - )) + ZonedDateTime.of(LocalDateTime.parse("2021-09-08T10:30:00"), ZoneOffset.ofHours(2)) + ) + ) } it should "be defined with '@' and timezone" in { @@ -230,35 +236,40 @@ class InterpreterLiteralExpressionTest LocalDateTime.parse("2021-09-08T10:30:00"), ZoneId.of("Europe/Berlin") ) - )) + ) + ) } "A years-months duration" should "be defined" in { eval(""" duration("P1Y6M") """) should be( ValYearMonthDuration( Period.ofYears(1).withMonths(6) - )) + ) + ) } it should "be defined with '@'" in { eval(""" @"P1Y6M" """) should be( ValYearMonthDuration( Period.ofYears(1).withMonths(6) - )) + ) + ) } "A days-time duration" should "be defined" in { eval(""" duration("P1DT12H30M") """) should be( ValDayTimeDuration( Duration.parse("P1DT12H30M") - )) + ) + ) } it should "be defined with '@'" in { eval(""" @"P1DT12H30M" """) should be( ValDayTimeDuration( Duration.parse("P1DT12H30M") - )) + ) + ) } } diff --git a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterNonExistingVariableExpressionTest.scala b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterNonExistingVariableExpressionTest.scala index fdc972711..515286dc0 100644 --- a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterNonExistingVariableExpressionTest.scala +++ b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterNonExistingVariableExpressionTest.scala @@ -23,7 +23,7 @@ import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers class InterpreterNonExistingVariableExpressionTest - extends AnyFlatSpec + extends AnyFlatSpec with Matchers with FeelEngineTest with EvaluationResultMatchers { @@ -102,32 +102,36 @@ class InterpreterNonExistingVariableExpressionTest evaluateExpression("non_existing") should ( returnNull() and reportFailure( failureType = EvaluationFailureType.NO_VARIABLE_FOUND, - failureMessage = "No variable found with name 'non_existing'") + failureMessage = "No variable found with name 'non_existing'" ) + ) } "A non-existing input value" should "be equal to null" in { evaluateUnaryTests(expression = "null", context = Context.EmptyContext) should ( returnResult(true) and reportFailure( failureType = EvaluationFailureType.NO_VARIABLE_FOUND, - failureMessage = "No input value found.") + failureMessage = "No input value found." ) + ) } it should "not be equal to a non-null value" in { evaluateUnaryTests("2", context = Context.EmptyContext) should ( returnResult(false) and reportFailure( failureType = EvaluationFailureType.NO_VARIABLE_FOUND, - failureMessage = "No input value found.") + failureMessage = "No input value found." ) + ) } it should "not compare to a non-null value" in { evaluateUnaryTests("< 2", context = Context.EmptyContext) should ( returnNull() and reportFailure( failureType = EvaluationFailureType.NO_VARIABLE_FOUND, - failureMessage = "No input value found.") + failureMessage = "No input value found." ) + ) } } diff --git a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterNumberExpressionTest.scala b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterNumberExpressionTest.scala index 17ce5709f..5efcd7385 100644 --- a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterNumberExpressionTest.scala +++ b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterNumberExpressionTest.scala @@ -21,13 +21,10 @@ import org.camunda.feel.syntaxtree._ import org.scalatest.matchers.should.Matchers import org.scalatest.flatspec.AnyFlatSpec -/** - * @author Philipp Ossler +/** @author + * Philipp Ossler */ -class InterpreterNumberExpressionTest - extends AnyFlatSpec - with Matchers - with FeelIntegrationTest { +class InterpreterNumberExpressionTest extends AnyFlatSpec with Matchers with FeelIntegrationTest { "A number" should "add to '4'" in { diff --git a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterStringExpressionTest.scala b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterStringExpressionTest.scala index 03b6ca0bd..08259214a 100644 --- a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterStringExpressionTest.scala +++ b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterStringExpressionTest.scala @@ -21,13 +21,10 @@ import org.camunda.feel.syntaxtree._ import org.scalatest.matchers.should.Matchers import org.scalatest.flatspec.AnyFlatSpec -/** - * @author Philipp Ossler +/** @author + * Philipp Ossler */ -class InterpreterStringExpressionTest - extends AnyFlatSpec - with Matchers - with FeelIntegrationTest { +class InterpreterStringExpressionTest extends AnyFlatSpec with Matchers with FeelIntegrationTest { "A string" should "concatenates to another String" in { @@ -77,19 +74,20 @@ class InterpreterStringExpressionTest eval(""" "a" != null """) should be(ValBoolean(true)) } - List(""" \' """, - """ \" """, - """ \\ """, - """ \n """, - """ \r """, - """ \t """, - """ \u269D """, - """ \U101EF """) + List( + """ \' """, + """ \" """, + """ \\ """, + """ \n """, + """ \r """, + """ \t """, + """ \u269D """, + """ \U101EF """ + ) .foreach { escapeChar => it should s"contains an escape sequence ($escapeChar)" in { - eval(s""" "a $escapeChar b" """) should be( - ValString(s"""a $escapeChar b""")) + eval(s""" "a $escapeChar b" """) should be(ValString(s"""a $escapeChar b""")) } } diff --git a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterUnaryTest.scala b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterUnaryTest.scala index a355c8983..35816ea1b 100644 --- a/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterUnaryTest.scala +++ b/src/test/scala/org/camunda/feel/impl/interpreter/InterpreterUnaryTest.scala @@ -21,13 +21,10 @@ import org.camunda.feel.syntaxtree._ import org.scalatest.matchers.should.Matchers import org.scalatest.flatspec.AnyFlatSpec -/** - * @author Philipp Ossler +/** @author + * Philipp Ossler */ -class InterpreterUnaryTest - extends AnyFlatSpec - with Matchers - with FeelIntegrationTest { +class InterpreterUnaryTest extends AnyFlatSpec with Matchers with FeelIntegrationTest { "A number" should "compare with '<'" in { @@ -125,8 +122,7 @@ class InterpreterUnaryTest evalUnaryTests(3, "a.b", Map("a" -> new A(4))) should be(ValBoolean(false)) evalUnaryTests(3, "< a.b", Map("a" -> new A(4))) should be(ValBoolean(true)) - evalUnaryTests(3, "< a.b", Map("a" -> new A(2))) should be( - ValBoolean(false)) + evalUnaryTests(3, "< a.b", Map("a" -> new A(2))) should be(ValBoolean(false)) } it should "compare to null" in { @@ -197,10 +193,8 @@ class InterpreterUnaryTest evalUnaryTests(true, "true and null") shouldBe ValBoolean(false) evalUnaryTests(true, "false and null") shouldBe ValBoolean(false) - evalUnaryTests(true, """true and "otherwise" """) shouldBe ValBoolean( - false) - evalUnaryTests(true, """false and "otherwise" """) shouldBe ValBoolean( - false) + evalUnaryTests(true, """true and "otherwise" """) shouldBe ValBoolean(false) + evalUnaryTests(true, """false and "otherwise" """) shouldBe ValBoolean(false) } it should "compare to a disjunction (or)" in { @@ -213,263 +207,250 @@ class InterpreterUnaryTest evalUnaryTests(true, "false or null") shouldBe ValBoolean(false) evalUnaryTests(true, """true or "otherwise" """) shouldBe ValBoolean(true) - evalUnaryTests(true, """false or "otherwise" """) shouldBe ValBoolean( - false) + evalUnaryTests(true, """false or "otherwise" """) shouldBe ValBoolean(false) } "A date" should "compare with '<'" in { - evalUnaryTests(date("2015-09-17"), """< date("2015-09-18")""") should be( - ValBoolean(true)) - evalUnaryTests(date("2015-09-18"), """< date("2015-09-18")""") should be( - ValBoolean(false)) - evalUnaryTests(date("2015-09-19"), """< date("2015-09-18")""") should be( - ValBoolean(false)) + evalUnaryTests(date("2015-09-17"), """< date("2015-09-18")""") should be(ValBoolean(true)) + evalUnaryTests(date("2015-09-18"), """< date("2015-09-18")""") should be(ValBoolean(false)) + evalUnaryTests(date("2015-09-19"), """< date("2015-09-18")""") should be(ValBoolean(false)) } it should "compare with '<='" in { - evalUnaryTests(date("2015-09-17"), """<= date("2015-09-18")""") should be( - ValBoolean(true)) - evalUnaryTests(date("2015-09-18"), """<= date("2015-09-18")""") should be( - ValBoolean(true)) - evalUnaryTests(date("2015-09-19"), """<= date("2015-09-18")""") should be( - ValBoolean(false)) + evalUnaryTests(date("2015-09-17"), """<= date("2015-09-18")""") should be(ValBoolean(true)) + evalUnaryTests(date("2015-09-18"), """<= date("2015-09-18")""") should be(ValBoolean(true)) + evalUnaryTests(date("2015-09-19"), """<= date("2015-09-18")""") should be(ValBoolean(false)) } it should "compare with '>'" in { - evalUnaryTests(date("2015-09-17"), """> date("2015-09-18")""") should be( - ValBoolean(false)) - evalUnaryTests(date("2015-09-18"), """> date("2015-09-18")""") should be( - ValBoolean(false)) - evalUnaryTests(date("2015-09-19"), """> date("2015-09-18")""") should be( - ValBoolean(true)) + evalUnaryTests(date("2015-09-17"), """> date("2015-09-18")""") should be(ValBoolean(false)) + evalUnaryTests(date("2015-09-18"), """> date("2015-09-18")""") should be(ValBoolean(false)) + evalUnaryTests(date("2015-09-19"), """> date("2015-09-18")""") should be(ValBoolean(true)) } it should "compare with '>='" in { - evalUnaryTests(date("2015-09-17"), """>= date("2015-09-18")""") should be( - ValBoolean(false)) - evalUnaryTests(date("2015-09-18"), """>= date("2015-09-18")""") should be( - ValBoolean(true)) - evalUnaryTests(date("2015-09-19"), """>= date("2015-09-18")""") should be( - ValBoolean(true)) + evalUnaryTests(date("2015-09-17"), """>= date("2015-09-18")""") should be(ValBoolean(false)) + evalUnaryTests(date("2015-09-18"), """>= date("2015-09-18")""") should be(ValBoolean(true)) + evalUnaryTests(date("2015-09-19"), """>= date("2015-09-18")""") should be(ValBoolean(true)) } it should "be equal to another date" in { - evalUnaryTests(date("2015-09-17"), """date("2015-09-18")""") should be( - ValBoolean(false)) - evalUnaryTests(date("2015-09-18"), """date("2015-09-18")""") should be( - ValBoolean(true)) + evalUnaryTests(date("2015-09-17"), """date("2015-09-18")""") should be(ValBoolean(false)) + evalUnaryTests(date("2015-09-18"), """date("2015-09-18")""") should be(ValBoolean(true)) } it should """be in interval '(date("2015-09-17")..date("2015-09-19")]'""" in { - evalUnaryTests(date("2015-09-17"), - """(date("2015-09-17")..date("2015-09-19"))""") should be( - ValBoolean(false)) - evalUnaryTests(date("2015-09-18"), - """(date("2015-09-17")..date("2015-09-19"))""") should be( - ValBoolean(true)) - evalUnaryTests(date("2015-09-19"), - """(date("2015-09-17")..date("2015-09-19"))""") should be( - ValBoolean(false)) + evalUnaryTests(date("2015-09-17"), """(date("2015-09-17")..date("2015-09-19"))""") should be( + ValBoolean(false) + ) + evalUnaryTests(date("2015-09-18"), """(date("2015-09-17")..date("2015-09-19"))""") should be( + ValBoolean(true) + ) + evalUnaryTests(date("2015-09-19"), """(date("2015-09-17")..date("2015-09-19"))""") should be( + ValBoolean(false) + ) } it should """be in interval '[date("2015-09-17")..date("2015-09-19")]'""" in { - evalUnaryTests(date("2015-09-17"), - """[date("2015-09-17")..date("2015-09-19")]""") should be( - ValBoolean(true)) - evalUnaryTests(date("2015-09-18"), - """[date("2015-09-17")..date("2015-09-19")]""") should be( - ValBoolean(true)) - evalUnaryTests(date("2015-09-19"), - """[date("2015-09-17")..date("2015-09-19")]""") should be( - ValBoolean(true)) + evalUnaryTests(date("2015-09-17"), """[date("2015-09-17")..date("2015-09-19")]""") should be( + ValBoolean(true) + ) + evalUnaryTests(date("2015-09-18"), """[date("2015-09-17")..date("2015-09-19")]""") should be( + ValBoolean(true) + ) + evalUnaryTests(date("2015-09-19"), """[date("2015-09-17")..date("2015-09-19")]""") should be( + ValBoolean(true) + ) } "A time" should "compare with '<'" in { - evalUnaryTests(localTime("08:31:14"), """< time("10:00:00")""") should be( - ValBoolean(true)) - evalUnaryTests(localTime("10:10:00"), """< time("10:00:00")""") should be( - ValBoolean(false)) - evalUnaryTests(localTime("11:31:14"), """< time("10:00:00")""") should be( - ValBoolean(false)) + evalUnaryTests(localTime("08:31:14"), """< time("10:00:00")""") should be(ValBoolean(true)) + evalUnaryTests(localTime("10:10:00"), """< time("10:00:00")""") should be(ValBoolean(false)) + evalUnaryTests(localTime("11:31:14"), """< time("10:00:00")""") should be(ValBoolean(false)) evalUnaryTests(time("10:00:00+01:00"), """< time("11:00:00+01:00")""") should be( - ValBoolean(true)) + ValBoolean(true) + ) evalUnaryTests(time("10:00:00+01:00"), """< time("10:00:00+01:00")""") should be( - ValBoolean(false)) + ValBoolean(false) + ) } it should "be equal to another time" in { - evalUnaryTests(localTime("08:31:14"), """time("10:00:00")""") should be( - ValBoolean(false)) - evalUnaryTests(localTime("08:31:14"), """time("08:31:14")""") should be( - ValBoolean(true)) + evalUnaryTests(localTime("08:31:14"), """time("10:00:00")""") should be(ValBoolean(false)) + evalUnaryTests(localTime("08:31:14"), """time("08:31:14")""") should be(ValBoolean(true)) evalUnaryTests(time("10:00:00+01:00"), """time("10:00:00+02:00")""") should be( - ValBoolean(false)) + ValBoolean(false) + ) evalUnaryTests(time("10:00:00+01:00"), """time("11:00:00+02:00")""") should be( - ValBoolean(false)) - evalUnaryTests(time("10:00:00+01:00"), """time("10:00:00+01:00")""") should be( - ValBoolean(true)) + ValBoolean(false) + ) + evalUnaryTests(time("10:00:00+01:00"), """time("10:00:00+01:00")""") should be(ValBoolean(true)) } it should """be in interval '[time("08:00:00")..time("10:00:00")]'""" in { - evalUnaryTests(localTime("07:45:10"), - """[time("08:00:00")..time("10:00:00")]""") should be( - ValBoolean(false)) - evalUnaryTests(localTime("09:15:20"), - """[time("08:00:00")..time("10:00:00")]""") should be( - ValBoolean(true)) - evalUnaryTests(localTime("11:30:30"), - """[time("08:00:00")..time("10:00:00")]""") should be( - ValBoolean(false)) + evalUnaryTests(localTime("07:45:10"), """[time("08:00:00")..time("10:00:00")]""") should be( + ValBoolean(false) + ) + evalUnaryTests(localTime("09:15:20"), """[time("08:00:00")..time("10:00:00")]""") should be( + ValBoolean(true) + ) + evalUnaryTests(localTime("11:30:30"), """[time("08:00:00")..time("10:00:00")]""") should be( + ValBoolean(false) + ) evalUnaryTests( time("11:30:00+01:00"), - """[time("08:00:00+01:00")..time("10:00:00+01:00")]""") should be( - ValBoolean(false)) + """[time("08:00:00+01:00")..time("10:00:00+01:00")]""" + ) should be(ValBoolean(false)) evalUnaryTests( time("09:30:00+01:00"), - """[time("08:00:00+01:00")..time("10:00:00+01:00")]""") should be( - ValBoolean(true)) + """[time("08:00:00+01:00")..time("10:00:00+01:00")]""" + ) should be(ValBoolean(true)) } "A date-time" should "compare with '<'" in { - evalUnaryTests(localDateTime("2015-09-17T08:31:14"), - """< date and time("2015-09-17T10:00:00")""") should be( - ValBoolean(true)) - evalUnaryTests(localDateTime("2015-09-17T10:10:00"), - """< date and time("2015-09-17T10:00:00")""") should be( - ValBoolean(false)) - evalUnaryTests(localDateTime("2015-09-17T11:31:14"), - """< date and time("2015-09-17T10:00:00")""") should be( - ValBoolean(false)) + evalUnaryTests( + localDateTime("2015-09-17T08:31:14"), + """< date and time("2015-09-17T10:00:00")""" + ) should be(ValBoolean(true)) + evalUnaryTests( + localDateTime("2015-09-17T10:10:00"), + """< date and time("2015-09-17T10:00:00")""" + ) should be(ValBoolean(false)) + evalUnaryTests( + localDateTime("2015-09-17T11:31:14"), + """< date and time("2015-09-17T10:00:00")""" + ) should be(ValBoolean(false)) evalUnaryTests( dateTime("2015-09-17T10:00:00+01:00"), - """< date and time("2015-09-17T12:00:00+01:00")""") should be( - ValBoolean(true)) + """< date and time("2015-09-17T12:00:00+01:00")""" + ) should be(ValBoolean(true)) evalUnaryTests( dateTime("2015-09-17T10:00:00+01:00"), - """< date and time("2015-09-17T09:00:00+01:00")""") should be( - ValBoolean(false)) + """< date and time("2015-09-17T09:00:00+01:00")""" + ) should be(ValBoolean(false)) } it should "be equal to another date-time" in { - evalUnaryTests(localDateTime("2015-09-17T08:31:14"), - """date and time("2015-09-17T10:00:00")""") should be( - ValBoolean(false)) - evalUnaryTests(localDateTime("2015-09-17T08:31:14"), - """date and time("2015-09-17T08:31:14")""") should be( - ValBoolean(true)) + evalUnaryTests( + localDateTime("2015-09-17T08:31:14"), + """date and time("2015-09-17T10:00:00")""" + ) should be(ValBoolean(false)) + evalUnaryTests( + localDateTime("2015-09-17T08:31:14"), + """date and time("2015-09-17T08:31:14")""" + ) should be(ValBoolean(true)) - evalUnaryTests(dateTime("2015-09-17T08:30:00+01:00"), - """date and time("2015-09-17T09:30:00+01:00")""") should be( - ValBoolean(false)) - evalUnaryTests(dateTime("2015-09-17T08:30:00+01:00"), - """date and time("2015-09-17T08:30:00+02:00")""") should be( - ValBoolean(false)) - evalUnaryTests(dateTime("2015-09-17T08:30:00+01:00"), - """date and time("2015-09-17T08:30:00+01:00")""") should be( - ValBoolean(true)) + evalUnaryTests( + dateTime("2015-09-17T08:30:00+01:00"), + """date and time("2015-09-17T09:30:00+01:00")""" + ) should be(ValBoolean(false)) + evalUnaryTests( + dateTime("2015-09-17T08:30:00+01:00"), + """date and time("2015-09-17T08:30:00+02:00")""" + ) should be(ValBoolean(false)) + evalUnaryTests( + dateTime("2015-09-17T08:30:00+01:00"), + """date and time("2015-09-17T08:30:00+01:00")""" + ) should be(ValBoolean(true)) } it should """be in interval '[dante and time("2015-09-17T08:00:00")..date and time("2015-09-17T10:00:00")]'""" in { evalUnaryTests( localDateTime("2015-09-17T07:45:10"), - """[date and time("2015-09-17T08:00:00")..date and time("2015-09-17T10:00:00")]""") should be( - ValBoolean(false)) + """[date and time("2015-09-17T08:00:00")..date and time("2015-09-17T10:00:00")]""" + ) should be(ValBoolean(false)) evalUnaryTests( localDateTime("2015-09-17T09:15:20"), - """[date and time("2015-09-17T08:00:00")..date and time("2015-09-17T10:00:00")]""") should be( - ValBoolean(true)) + """[date and time("2015-09-17T08:00:00")..date and time("2015-09-17T10:00:00")]""" + ) should be(ValBoolean(true)) evalUnaryTests( localDateTime("2015-09-17T11:30:30"), - """[date and time("2015-09-17T08:00:00")..date and time("2015-09-17T10:00:00")]""") should be( - ValBoolean(false)) + """[date and time("2015-09-17T08:00:00")..date and time("2015-09-17T10:00:00")]""" + ) should be(ValBoolean(false)) evalUnaryTests( dateTime("2015-09-17T08:30:00+01:00"), - """[date and time("2015-09-17T09:00:00+01:00")..date and time("2015-09-17T10:00:00+01:00")]""") should be( - ValBoolean(false)) + """[date and time("2015-09-17T09:00:00+01:00")..date and time("2015-09-17T10:00:00+01:00")]""" + ) should be(ValBoolean(false)) evalUnaryTests( dateTime("2015-09-17T08:30:00+01:00"), - """[date and time("2015-09-17T08:00:00+01:00")..date and time("2015-09-17T10:00:00+01:00")]""") should be( - ValBoolean(true)) + """[date and time("2015-09-17T08:00:00+01:00")..date and time("2015-09-17T10:00:00+01:00")]""" + ) should be(ValBoolean(true)) } "A year-month-duration" should "compare with '<'" in { - evalUnaryTests(yearMonthDuration("P1Y"), """< duration("P2Y")""") should be( - ValBoolean(true)) - evalUnaryTests(yearMonthDuration("P1Y"), """< duration("P1Y")""") should be( - ValBoolean(false)) - evalUnaryTests(yearMonthDuration("P1Y2M"), """< duration("P1Y")""") should be( - ValBoolean(false)) + evalUnaryTests(yearMonthDuration("P1Y"), """< duration("P2Y")""") should be(ValBoolean(true)) + evalUnaryTests(yearMonthDuration("P1Y"), """< duration("P1Y")""") should be(ValBoolean(false)) + evalUnaryTests(yearMonthDuration("P1Y2M"), """< duration("P1Y")""") should be(ValBoolean(false)) } it should "be equal to another duration" in { - evalUnaryTests(yearMonthDuration("P1Y4M"), """duration("P1Y3M")""") should be( - ValBoolean(false)) - evalUnaryTests(yearMonthDuration("P1Y4M"), """duration("P1Y4M")""") should be( - ValBoolean(true)) + evalUnaryTests(yearMonthDuration("P1Y4M"), """duration("P1Y3M")""") should be(ValBoolean(false)) + evalUnaryTests(yearMonthDuration("P1Y4M"), """duration("P1Y4M")""") should be(ValBoolean(true)) } it should """be in interval '[duration("P1Y")..duration("P2Y")]'""" in { - evalUnaryTests(yearMonthDuration("P6M"), - """[duration("P1Y")..duration("P2Y")]""") should be( - ValBoolean(false)) - evalUnaryTests(yearMonthDuration("P1Y8M"), - """[duration("P1Y")..duration("P2Y")]""") should be( - ValBoolean(true)) - evalUnaryTests(yearMonthDuration("P2Y1M"), - """[duration("P1Y")..duration("P2Y")]""") should be( - ValBoolean(false)) + evalUnaryTests(yearMonthDuration("P6M"), """[duration("P1Y")..duration("P2Y")]""") should be( + ValBoolean(false) + ) + evalUnaryTests(yearMonthDuration("P1Y8M"), """[duration("P1Y")..duration("P2Y")]""") should be( + ValBoolean(true) + ) + evalUnaryTests(yearMonthDuration("P2Y1M"), """[duration("P1Y")..duration("P2Y")]""") should be( + ValBoolean(false) + ) } "A day-time-duration" should "compare with '<'" in { evalUnaryTests(dayTimeDuration("P1DT4H"), """< duration("P2DT4H")""") should be( - ValBoolean(true)) + ValBoolean(true) + ) evalUnaryTests(dayTimeDuration("P2DT4H"), """< duration("P2DT4H")""") should be( - ValBoolean(false)) + ValBoolean(false) + ) evalUnaryTests(dayTimeDuration("P2DT8H"), """< duration("P2DT4H")""") should be( - ValBoolean(false)) + ValBoolean(false) + ) } it should "be equal to another duration" in { - evalUnaryTests(dayTimeDuration("P1DT4H"), """duration("P2DT4H")""") should be( - ValBoolean(false)) - evalUnaryTests(dayTimeDuration("P2DT4H"), """duration("P2DT4H")""") should be( - ValBoolean(true)) + evalUnaryTests(dayTimeDuration("P1DT4H"), """duration("P2DT4H")""") should be(ValBoolean(false)) + evalUnaryTests(dayTimeDuration("P2DT4H"), """duration("P2DT4H")""") should be(ValBoolean(true)) } it should """be in interval '[duration("P1D")..duration("P2D")]'""" in { - evalUnaryTests(dayTimeDuration("PT4H"), - """[duration("P1D")..duration("P2D")]""") should be( - ValBoolean(false)) - evalUnaryTests(dayTimeDuration("P1DT4H"), - """[duration("P1D")..duration("P2D")]""") should be( - ValBoolean(true)) - evalUnaryTests(dayTimeDuration("P2DT4H"), - """[duration("P1D")..duration("P2D")]""") should be( - ValBoolean(false)) + evalUnaryTests(dayTimeDuration("PT4H"), """[duration("P1D")..duration("P2D")]""") should be( + ValBoolean(false) + ) + evalUnaryTests(dayTimeDuration("P1DT4H"), """[duration("P1D")..duration("P2D")]""") should be( + ValBoolean(true) + ) + evalUnaryTests(dayTimeDuration("P2DT4H"), """[duration("P1D")..duration("P2D")]""") should be( + ValBoolean(false) + ) } "A list" should "be equal to another list" in { @@ -484,17 +465,13 @@ class InterpreterUnaryTest } it should "be checked in an every expression" in { - evalUnaryTests(List(1, 2, 3), "every x in ? satisfies x > 3") should be( - ValBoolean(false)) - evalUnaryTests(List(4, 5, 6), "every x in ? satisfies x > 3") should be( - ValBoolean(true)) + evalUnaryTests(List(1, 2, 3), "every x in ? satisfies x > 3") should be(ValBoolean(false)) + evalUnaryTests(List(4, 5, 6), "every x in ? satisfies x > 3") should be(ValBoolean(true)) } it should "be checked in a some expression" in { - evalUnaryTests(List(1, 2, 3), "some x in ? satisfies x > 4") should be( - ValBoolean(false)) - evalUnaryTests(List(4, 5, 6), "some x in ? satisfies x > 4") should be( - ValBoolean(true)) + evalUnaryTests(List(1, 2, 3), "some x in ? satisfies x > 4") should be(ValBoolean(false)) + evalUnaryTests(List(4, 5, 6), "some x in ? satisfies x > 4") should be(ValBoolean(true)) } "A context" should "be equal to another context" in { @@ -524,10 +501,8 @@ class InterpreterUnaryTest "A function" should "be invoked with ? (input value)" in { - evalUnaryTests("foo", """ starts with(?, "f") """) should be( - ValBoolean(true)) - evalUnaryTests("foo", """ starts with(?, "b") """) should be( - ValBoolean(false)) + evalUnaryTests("foo", """ starts with(?, "f") """) should be(ValBoolean(true)) + evalUnaryTests("foo", """ starts with(?, "b") """) should be(ValBoolean(false)) } it should "be invoked as endpoint" in { @@ -549,13 +524,13 @@ class InterpreterUnaryTest it should "be compared to literal" in { - evalUnaryTests(date("2019-08-12"), - """ date(now) """, - Map("now" -> "2019-08-12")) should be(ValBoolean(true)) + evalUnaryTests(date("2019-08-12"), """ date(now) """, Map("now" -> "2019-08-12")) should be( + ValBoolean(true) + ) - evalUnaryTests(date("2019-08-12"), - """ date(now) """, - Map("now" -> "2019-08-13")) should be(ValBoolean(false)) + evalUnaryTests(date("2019-08-12"), """ date(now) """, Map("now" -> "2019-08-13")) should be( + ValBoolean(false) + ) } it should "be compared with a list value" in { @@ -565,8 +540,8 @@ class InterpreterUnaryTest } it should "be compared with a list variable" in { - evalUnaryTests(2, "x", Map("x" -> List(1,2,3))) should be (ValBoolean(true)) - evalUnaryTests(4, "x", Map("x" -> List(1,2,3))) should be (ValBoolean(false)) + evalUnaryTests(2, "x", Map("x" -> List(1, 2, 3))) should be(ValBoolean(true)) + evalUnaryTests(4, "x", Map("x" -> List(1, 2, 3))) should be(ValBoolean(false)) } } diff --git a/src/test/scala/org/camunda/feel/impl/script/ScriptEngineFactoryTest.scala b/src/test/scala/org/camunda/feel/impl/script/ScriptEngineFactoryTest.scala index 3701074fb..e33c45c87 100644 --- a/src/test/scala/org/camunda/feel/impl/script/ScriptEngineFactoryTest.scala +++ b/src/test/scala/org/camunda/feel/impl/script/ScriptEngineFactoryTest.scala @@ -19,8 +19,8 @@ package org.camunda.feel.impl.script import org.scalatest.matchers.should.Matchers import org.scalatest.flatspec.AnyFlatSpec -/** - * @author Philipp Ossler +/** @author + * Philipp Ossler */ class ScriptEngineFactoryTest extends AnyFlatSpec with Matchers { diff --git a/src/test/scala/org/camunda/feel/impl/script/ScriptEngineManagerTest.scala b/src/test/scala/org/camunda/feel/impl/script/ScriptEngineManagerTest.scala index d54484ce6..95930e89f 100644 --- a/src/test/scala/org/camunda/feel/impl/script/ScriptEngineManagerTest.scala +++ b/src/test/scala/org/camunda/feel/impl/script/ScriptEngineManagerTest.scala @@ -21,8 +21,8 @@ import org.scalatest.flatspec.AnyFlatSpec import javax.script.ScriptEngineManager import scala.collection.JavaConverters._ -/** - * @author Philipp Ossler +/** @author + * Philipp Ossler */ class ScriptEngineManagerTest extends AnyFlatSpec with Matchers { @@ -44,8 +44,7 @@ class ScriptEngineManagerTest extends AnyFlatSpec with Matchers { it should "get feel script engine by qualified name" in { - val engine = scriptEngineManager.getEngineByName( - "http://www.omg.org/spec/FEEL/20140401") + val engine = scriptEngineManager.getEngineByName("http://www.omg.org/spec/FEEL/20140401") engine.getClass should be(classOf[FeelExpressionScriptEngine]) } @@ -70,7 +69,8 @@ class ScriptEngineManagerTest extends AnyFlatSpec with Matchers { val factories = scriptEngineManager.getEngineFactories (factories.asScala.map(f => f.getClass)) should contain allOf (classOf[ - FeelScriptEngineFactory], classOf[FeelUnaryTestsScriptEngineFactory]) + FeelScriptEngineFactory + ], classOf[FeelUnaryTestsScriptEngineFactory]) } it should "get feel unary tests script engine by name" in { diff --git a/src/test/scala/org/camunda/feel/impl/script/ScriptEngineTest.scala b/src/test/scala/org/camunda/feel/impl/script/ScriptEngineTest.scala index 2134f3b78..f94058e02 100644 --- a/src/test/scala/org/camunda/feel/impl/script/ScriptEngineTest.scala +++ b/src/test/scala/org/camunda/feel/impl/script/ScriptEngineTest.scala @@ -24,8 +24,8 @@ import javax.script.ScriptContext import javax.script.SimpleScriptContext import javax.script.ScriptException -/** - * @author Philipp Ossler +/** @author + * Philipp Ossler */ class ScriptEngineTest extends AnyFlatSpec with Matchers { @@ -38,7 +38,7 @@ class ScriptEngineTest extends AnyFlatSpec with Matchers { it should "evaluate a simpleUnaryTest script '< 3' using engine scope" in { - val context = new SimpleScriptContext + val context = new SimpleScriptContext val bindings = scriptEngine.createBindings() bindings.put("cellInput", 2) context.setBindings(bindings, ScriptContext.ENGINE_SCOPE) @@ -49,7 +49,7 @@ class ScriptEngineTest extends AnyFlatSpec with Matchers { it should "evaluate a simpleUnaryTest script '< 3' using global scope" in { - val context = new SimpleScriptContext + val context = new SimpleScriptContext val bindings = scriptEngine.createBindings() bindings.put("cellInput", 2) context.setBindings(bindings, ScriptContext.GLOBAL_SCOPE) @@ -71,7 +71,7 @@ class ScriptEngineTest extends AnyFlatSpec with Matchers { compiledScript should not be (null) - val context = new SimpleScriptContext + val context = new SimpleScriptContext val bindings = scriptEngine.createBindings() bindings.put("cellInput", 2) context.setBindings(bindings, ScriptContext.ENGINE_SCOPE) @@ -105,9 +105,11 @@ class ScriptEngineTest extends AnyFlatSpec with Matchers { it should "be configured by a custom clock" in { - val now = ZonedDateTime.of(LocalDate.parse("2020-07-31"), - LocalTime.parse("14:27:30"), - ZoneId.of("Europe/Berlin")) + val now = ZonedDateTime.of( + LocalDate.parse("2020-07-31"), + LocalTime.parse("14:27:30"), + ZoneId.of("Europe/Berlin") + ) PinnedClock.currentTime = now diff --git a/src/test/scala/org/camunda/feel/impl/script/TestValueMapper.scala b/src/test/scala/org/camunda/feel/impl/script/TestValueMapper.scala index 28f511c0b..30a2f282d 100644 --- a/src/test/scala/org/camunda/feel/impl/script/TestValueMapper.scala +++ b/src/test/scala/org/camunda/feel/impl/script/TestValueMapper.scala @@ -22,8 +22,7 @@ import org.camunda.feel.valuemapper.CustomValueMapper // DO NOT DELETE, used in ScriptEngineTest through src/test/resources/META-INF/services class TestValueMapper extends CustomValueMapper { - override def unpackVal(value: Val, - innerValueMapper: Val => Any): Option[Any] = + override def unpackVal(value: Val, innerValueMapper: Val => Any): Option[Any] = value match { case ValNull => Some("foobar") case _ => None diff --git a/src/test/scala/org/camunda/feel/impl/script/UnaryTestsScriptEngineTest.scala b/src/test/scala/org/camunda/feel/impl/script/UnaryTestsScriptEngineTest.scala index 6e18a51ae..edfa898fc 100644 --- a/src/test/scala/org/camunda/feel/impl/script/UnaryTestsScriptEngineTest.scala +++ b/src/test/scala/org/camunda/feel/impl/script/UnaryTestsScriptEngineTest.scala @@ -22,13 +22,12 @@ import javax.script.ScriptContext import javax.script.SimpleScriptContext import javax.script.ScriptException -/** - * @author Philipp Ossler +/** @author + * Philipp Ossler */ class UnaryTestsScriptEngineTest extends AnyFlatSpec with Matchers { - val scriptEngine = new FeelUnaryTestsScriptEngine( - new FeelUnaryTestsScriptEngineFactory) + val scriptEngine = new FeelUnaryTestsScriptEngine(new FeelUnaryTestsScriptEngineFactory) "The feel unary tests script engine" should "get the script engine factory" in { @@ -37,7 +36,7 @@ class UnaryTestsScriptEngineTest extends AnyFlatSpec with Matchers { it should "evaluate a simpleUnaryTest script '< 3'" in { - val context = new SimpleScriptContext + val context = new SimpleScriptContext val bindings = scriptEngine.createBindings() bindings.put("cellInput", 2) context.setBindings(bindings, ScriptContext.ENGINE_SCOPE) @@ -48,7 +47,7 @@ class UnaryTestsScriptEngineTest extends AnyFlatSpec with Matchers { it should "evaluate a simpleUnaryTest script 'not(3,4)'" in { - val context = new SimpleScriptContext + val context = new SimpleScriptContext val bindings = scriptEngine.createBindings() bindings.put("cellInput", 2) context.setBindings(bindings, ScriptContext.ENGINE_SCOPE) @@ -63,7 +62,7 @@ class UnaryTestsScriptEngineTest extends AnyFlatSpec with Matchers { compiledScript should not be (null) - val context = new SimpleScriptContext + val context = new SimpleScriptContext val bindings = scriptEngine.createBindings() bindings.put("cellInput", 2) context.setBindings(bindings, ScriptContext.ENGINE_SCOPE) diff --git a/src/test/scala/org/camunda/feel/impl/valuemapper/DefaultValueMapperTest.scala b/src/test/scala/org/camunda/feel/impl/valuemapper/DefaultValueMapperTest.scala index d54975348..fc5d79f54 100644 --- a/src/test/scala/org/camunda/feel/impl/valuemapper/DefaultValueMapperTest.scala +++ b/src/test/scala/org/camunda/feel/impl/valuemapper/DefaultValueMapperTest.scala @@ -26,9 +26,10 @@ import org.camunda.feel.valuemapper.ValueMapper import org.scalatest.matchers.should.Matchers import org.scalatest.flatspec.AnyFlatSpec -/** - * @author Philipp Ossler - * @author Falko Menge +/** @author + * Philipp Ossler + * @author + * Falko Menge */ class DefaultValueMapperTest extends AnyFlatSpec with Matchers { @@ -107,19 +108,20 @@ class DefaultValueMapperTest extends AnyFlatSpec with Matchers { it should "convert from List" in { - valueMapper.toVal(List(1, 2)) should be( - ValList(List(ValNumber(1), ValNumber(2)))) + valueMapper.toVal(List(1, 2)) should be(ValList(List(ValNumber(1), ValNumber(2)))) } it should "convert from java.util.List" in { valueMapper.toVal(java.util.Arrays.asList(1, 2)) should be( - ValList(List(ValNumber(1), ValNumber(2)))) + ValList(List(ValNumber(1), ValNumber(2))) + ) } it should "convert from Map" in { valueMapper.toVal(Map("a" -> 2)) should be( - ValContext(Context.StaticContext(Map("a" -> ValNumber(2))))) + ValContext(Context.StaticContext(Map("a" -> ValNumber(2)))) + ) } it should "convert from java.util.Map" in { @@ -127,81 +129,78 @@ class DefaultValueMapperTest extends AnyFlatSpec with Matchers { val map = new java.util.HashMap[String, Object] map.put("a", new java.lang.Integer(2)) - valueMapper.toVal(map) should be( - ValContext(Context.StaticContext(Map("a" -> ValNumber(2))))) + valueMapper.toVal(map) should be(ValContext(Context.StaticContext(Map("a" -> ValNumber(2))))) } it should "convert from LocalDate" in { - valueMapper.toVal(java.time.LocalDate.parse("2017-04-02")) should be( - ValDate("2017-04-02")) + valueMapper.toVal(java.time.LocalDate.parse("2017-04-02")) should be(ValDate("2017-04-02")) } it should "convert from LocalTime" in { - valueMapper.toVal(java.time.LocalTime.parse("12:04:30")) should be( - ValLocalTime("12:04:30")) + valueMapper.toVal(java.time.LocalTime.parse("12:04:30")) should be(ValLocalTime("12:04:30")) } it should "convert from OffsetTime" in { valueMapper.toVal(java.time.OffsetTime.parse("12:04:30+01:00")) should be( - ValTime("12:04:30+01:00")) + ValTime("12:04:30+01:00") + ) } it should "convert from LocalDateTime" in { valueMapper.toVal(java.time.LocalDateTime.parse("2017-04-02T12:04:30")) should be( - ValLocalDateTime("2017-04-02T12:04:30")) + ValLocalDateTime("2017-04-02T12:04:30") + ) } it should "convert from OffsetDateTime" in { - valueMapper.toVal( - java.time.OffsetDateTime.parse("2017-04-02T12:04:30+01:00")) should be( - ValDateTime("2017-04-02T12:04:30+01:00")) + valueMapper.toVal(java.time.OffsetDateTime.parse("2017-04-02T12:04:30+01:00")) should be( + ValDateTime("2017-04-02T12:04:30+01:00") + ) } it should "convert from ZonedDateTime with zone offset" in { - valueMapper.toVal( - java.time.ZonedDateTime.parse("2017-04-02T12:04:30+01:00")) should be( - ValDateTime("2017-04-02T12:04:30+01:00")) + valueMapper.toVal(java.time.ZonedDateTime.parse("2017-04-02T12:04:30+01:00")) should be( + ValDateTime("2017-04-02T12:04:30+01:00") + ) } it should "convert from ZonedDateTime with zone offset 'Z'" in { valueMapper.toVal(java.time.ZonedDateTime.parse("2017-04-02T12:04:30Z")) should be( - ValDateTime("2017-04-02T12:04:30Z")) + ValDateTime("2017-04-02T12:04:30Z") + ) } it should "convert from ZonedDateTime with zone id" in { valueMapper.toVal( java.time.ZonedDateTime - .parse("2017-04-02T12:04:30+02:00[Europe/Paris]")) should be( - ValDateTime("2017-04-02T12:04:30@Europe/Paris")) + .parse("2017-04-02T12:04:30+02:00[Europe/Paris]") + ) should be(ValDateTime("2017-04-02T12:04:30@Europe/Paris")) } it should "convert from java.util.Date" in { - val format = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss") + val format = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss") val dateTime = LocalDateTime.parse("2017-04-02T12:04:30") - valueMapper.toVal(format.parse("2017-04-02T12:04:30")) should be( - ValLocalDateTime(dateTime)) + valueMapper.toVal(format.parse("2017-04-02T12:04:30")) should be(ValLocalDateTime(dateTime)) } it should "convert from Period" in { - valueMapper.toVal(java.time.Period.parse("P2Y4M")) should be( - ValYearMonthDuration("P2Y4M")) + valueMapper.toVal(java.time.Period.parse("P2Y4M")) should be(ValYearMonthDuration("P2Y4M")) } it should "convert from Duration" in { - valueMapper.toVal(java.time.Duration.parse("PT4H22M")) should be( - ValDayTimeDuration("PT4H22M")) + valueMapper.toVal(java.time.Duration.parse("PT4H22M")) should be(ValDayTimeDuration("PT4H22M")) } it should "convert from object" in { @@ -223,7 +222,7 @@ class DefaultValueMapperTest extends AnyFlatSpec with Matchers { it should "convert from object with public getters" in { case class Obj(private val a: Int, private val b: String) { - def getA(): Int = a + def getA(): Int = a def getB(): String = b } diff --git a/src/test/scala/org/camunda/feel/impl/valuemapper/JavaValueMapperTest.scala b/src/test/scala/org/camunda/feel/impl/valuemapper/JavaValueMapperTest.scala index 289e9a957..6a94d3205 100644 --- a/src/test/scala/org/camunda/feel/impl/valuemapper/JavaValueMapperTest.scala +++ b/src/test/scala/org/camunda/feel/impl/valuemapper/JavaValueMapperTest.scala @@ -26,8 +26,7 @@ import org.scalatest.flatspec.AnyFlatSpec class JavaValueMapperTest extends AnyFlatSpec with Matchers { val valueMapper = - ValueMapper.CompositeValueMapper( - List(DefaultValueMapper.instance, new JavaValueMapper())) + ValueMapper.CompositeValueMapper(List(DefaultValueMapper.instance, new JavaValueMapper())) "The JavaValueMapper" should "return whole number as java.lang.Long" in { @@ -66,8 +65,7 @@ class JavaValueMapperTest extends AnyFlatSpec with Matchers { list.add("a") list.add("b") - valueMapper.unpackVal(ValList(List(ValString("a"), ValString("b")))) should be( - list) + valueMapper.unpackVal(ValList(List(ValString("a"), ValString("b")))) should be(list) } it should "return context as java.util.Map" in { @@ -77,8 +75,8 @@ class JavaValueMapperTest extends AnyFlatSpec with Matchers { map.put("y", "2") valueMapper.unpackVal( - ValContext(Context.StaticContext( - variables = Map("x" -> "1", "y" -> "2")))) should be(map) + ValContext(Context.StaticContext(variables = Map("x" -> "1", "y" -> "2"))) + ) should be(map) } }