Skip to content

Commit

Permalink
[NU-1701] Add conversions to primitive types to helper functions (#6807)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukasz-bigorajski authored Sep 9, 2024
1 parent a3d3f73 commit 2df535c
Show file tree
Hide file tree
Showing 6 changed files with 470 additions and 2 deletions.
17 changes: 16 additions & 1 deletion docs/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,26 @@
when entering into node form. Please be aware that changes in `tableDefinition.sql` are not refreshed.
To do this, use `/app/processingtype/reload` API
* [#6716](https://github.com/TouK/nussknacker/pull/6716) Fix type hints for #COLLECTION.merge function.
* [#6695](https://github.com/TouK/nussknacker/pull/6680) From now on, arrays on UI are visible as lists but on a
* [#6695](https://github.com/TouK/nussknacker/pull/6695) From now on, arrays on UI are visible as lists but on a
background they are stored as it is and SpeL converts them to lists in a runtime.
* [#6750](https://github.com/TouK/nussknacker/pull/6750) Add varargs to `#COLLECTION.concat` and `#COLLECTION.merge`.
* [#6778](https://github.com/TouK/nussknacker/pull/6778) SpeL: check for methods if a property for a given name does not exist.
* [#6769](https://github.com/TouK/nussknacker/pull/6769) Added possibility to choose presets and define lists for Long typed parameter inputs in fragments.
* [#6807](https://github.com/TouK/nussknacker/pull/6807) Add conversion functions to primitives to: `#CONV`:
* toNumberOrNull
* toString
* toBoolean
* toBooleanOrNull
* toInteger
* toIntegerOrNull
* toLong
* toLongOrNull
* toDouble
* toDoubleOrNull
* toBigInteger
* toBigIntegerOrNull
* toBigDecimal
* toBigDecimalOrNull

## 1.17

Expand Down
168 changes: 168 additions & 0 deletions engine/flink/tests/src/test/resources/extractedTypes/defaultModel.json
Original file line number Diff line number Diff line change
Expand Up @@ -13654,6 +13654,150 @@
}
}
],
"toBigDecimal": [
{
"description": "Convert any value to BigDecimal or throw exception in case of failure",
"name": "toBigDecimal",
"signature": {
"noVarArgs": [
{"name": "value", "refClazz": {"type": "Unknown"}}
],
"result": {"refClazzName": "java.math.BigDecimal"}
}
}
],
"toBigDecimalOrNull": [
{
"description": "Convert any value to BigDecimal or null in case of failure",
"name": "toBigDecimalOrNull",
"signature": {
"noVarArgs": [
{"name": "value", "refClazz": {"type": "Unknown"}}
],
"result": {"refClazzName": "java.math.BigDecimal"}
}
}
],
"toBigInteger": [
{
"description": "Convert any value to BigInteger or throw exception in case of failure",
"name": "toBigInteger",
"signature": {
"noVarArgs": [
{"name": "value", "refClazz": {"type": "Unknown"}}
],
"result": {"refClazzName": "java.math.BigInteger"}
}
}
],
"toBigIntegerOrNull": [
{
"description": "Convert any value to BigInteger or null in case of failure",
"name": "toBigIntegerOrNull",
"signature": {
"noVarArgs": [
{"name": "value", "refClazz": {"type": "Unknown"}}
],
"result": {"refClazzName": "java.math.BigInteger"}
}
}
],
"toBoolean": [
{
"description": "Convert any value to Boolean or throw exception in case of failure",
"name": "toBoolean",
"signature": {
"noVarArgs": [
{"name": "value", "refClazz": {"type": "Unknown"}}
],
"result": {"refClazzName": "java.lang.Boolean"}
}
}
],
"toBooleanOrNull": [
{
"description": "Convert any value to Boolean or throw exception in case of failure",
"name": "toBooleanOrNull",
"signature": {
"noVarArgs": [
{"name": "value", "refClazz": {"type": "Unknown"}}
],
"result": {"refClazzName": "java.lang.Boolean"}
}
}
],
"toDouble": [
{
"description": "Convert any value to Double or throw exception in case of failure",
"name": "toDouble",
"signature": {
"noVarArgs": [
{"name": "value", "refClazz": {"type": "Unknown"}}
],
"result": {"refClazzName": "java.lang.Double"}
}
}
],
"toDoubleOrNull": [
{
"description": "Convert any value to Double or null in case of failure",
"name": "toDoubleOrNull",
"signature": {
"noVarArgs": [
{"name": "value", "refClazz": {"type": "Unknown"}}
],
"result": {"refClazzName": "java.lang.Double"}
}
}
],
"toInteger": [
{
"description": "Convert any value to Integer or throw exception in case of failure",
"name": "toInteger",
"signature": {
"noVarArgs": [
{"name": "value", "refClazz": {"type": "Unknown"}}
],
"result": {"refClazzName": "java.lang.Integer"}
}
}
],
"toIntegerOrNull": [
{
"description": "Convert any value to Integer or null in case of failure",
"name": "toIntegerOrNull",
"signature": {
"noVarArgs": [
{"name": "value", "refClazz": {"type": "Unknown"}}
],
"result": {"refClazzName": "java.lang.Integer"}
}
}
],
"toLong": [
{
"description": "Convert any value to Long or throw exception in case of failure",
"name": "toLong",
"signature": {
"noVarArgs": [
{"name": "value", "refClazz": {"type": "Unknown"}}
],
"result": {"refClazzName": "java.lang.Long"}
}
}
],
"toLongOrNull": [
{
"description": "Convert any value to Long or null in case of failure",
"name": "toLongOrNull",
"signature": {
"noVarArgs": [
{"name": "value", "refClazz": {"type": "Unknown"}}
],
"result": {"refClazzName": "java.lang.Long"}
}
}
],
"toNumber": [
{
"description": "Parse string to number",
Expand Down Expand Up @@ -13686,6 +13830,30 @@
}
]
}
],
"toNumberOrNull": [
{
"description": "Parse any value to Number or null in case failure",
"name": "toNumberOrNull",
"signature": {
"noVarArgs": [
{"name": "value", "refClazz": {"type": "Unknown"}}
],
"result": {"refClazzName": "java.lang.Number"}
}
}
],
"toString": [
{
"description": "Convert any value to String",
"name": "toString",
"signature": {
"noVarArgs": [
{"name": "value", "refClazz": {"type": "Unknown"}}
],
"result": {"refClazzName": "java.lang.String"}
}
}
]
},
"staticMethods": {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ class SpelExpressionSpec extends AnyFunSuite with Matchers with ValidatedValuesD
parsed.leftMap(_.head).leftMap(_.message) shouldEqual expectedValidation
}

test("return invalid argument if arguments is not passed to method") {
test("return invalid argument if arguments are not passed to method") {
val parsed = parse[Any]("#processHelper.add", ctxWithGlobal)
val expectedValidation = Invalid("Mismatch parameter types. Found: add(). Required: add(Integer, Integer)")
parsed.isInvalid shouldBe true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package pl.touk.nussknacker.engine.util.functions

import pl.touk.nussknacker.engine.api.generics.GenericType
import pl.touk.nussknacker.engine.api.{Documentation, HideToString, ParamName}
import pl.touk.nussknacker.engine.util.functions.ConversionUtils.{stringToBigInteger, stringToBoolean}
import pl.touk.nussknacker.engine.util.functions.NumericUtils.ToNumberTypingFunction

import scala.util.Try

object conversion extends ConversionUtils

trait ConversionUtils extends HideToString {
Expand All @@ -20,4 +23,138 @@ trait ConversionUtils extends HideToString {
def toNumber(@ParamName("stringOrNumber") stringOrNumber: Any): java.lang.Number =
numeric.toNumber(stringOrNumber)

@Documentation(description = "Parse any value to Number or null in case failure")
def toNumberOrNull(@ParamName("value") value: Any): java.lang.Number = value match {
case v: String => Try(numeric.toNumber(v)).getOrElse(null)
case v: Number => v
case _ => null
}

@Documentation(description = "Convert any value to String")
def toString(@ParamName("value") value: Any): java.lang.String = value match {
case v: String => v
case null => null
case v => v.toString
}

@Documentation(description = "Convert any value to Boolean or throw exception in case of failure")
def toBoolean(@ParamName("value") value: Any): java.lang.Boolean = value match {
case v: String =>
stringToBoolean(v).getOrElse {
throw new IllegalArgumentException(s"Cannot convert: $value to Boolean")
}
case v: java.lang.Boolean => v
case null => null
case _ => throw new IllegalArgumentException(s"Cannot convert: $value to Boolean")
}

@Documentation(description = "Convert any value to Boolean or throw exception in case of failure")
def toBooleanOrNull(@ParamName("value") value: Any): java.lang.Boolean = value match {
case v: String => stringToBoolean(v).orNull
case v: java.lang.Boolean => v
case _ => null
}

@Documentation(description = "Convert any value to Integer or throw exception in case of failure")
def toInteger(@ParamName("value") value: Any): java.lang.Integer = value match {
case v: String => Integer.valueOf(numeric.toNumber(v).intValue())
case v: Number => v.intValue()
case null => null
case _ => throw new IllegalArgumentException(s"Cannot convert: $value to Integer")
}

@Documentation(description = "Convert any value to Integer or null in case of failure")
def toIntegerOrNull(@ParamName("value") value: Any): java.lang.Integer = value match {
case v: String => Try(Integer.valueOf(numeric.toNumber(v).intValue())).getOrElse(null)
case v: Number => v.intValue()
case _ => null
}

@Documentation(description = "Convert any value to Long or throw exception in case of failure")
def toLong(@ParamName("value") value: Any): java.lang.Long = value match {
case v: String => java.lang.Long.valueOf(numeric.toNumber(v).longValue())
case v: Number => v.longValue()
case null => null
case _ => throw new IllegalArgumentException(s"Cannot convert: $value to Long")
}

@Documentation(description = "Convert any value to Long or null in case of failure")
def toLongOrNull(@ParamName("value") value: Any): java.lang.Long = value match {
case v: String => Try(java.lang.Long.valueOf(numeric.toNumber(v).longValue())).getOrElse(null)
case v: Number => v.longValue()
case _ => null
}

@Documentation(description = "Convert any value to Double or throw exception in case of failure")
def toDouble(@ParamName("value") value: Any): java.lang.Double = value match {
case v: String => java.lang.Double.valueOf(numeric.toNumber(v).doubleValue())
case v: Number => v.doubleValue()
case null => null
case _ => throw new IllegalArgumentException(s"Cannot convert: $value to Double")
}

@Documentation(description = "Convert any value to Double or null in case of failure")
def toDoubleOrNull(@ParamName("value") value: Any): java.lang.Double = value match {
case v: String => Try(java.lang.Double.valueOf(numeric.toNumber(v).doubleValue())).getOrElse(null)
case v: Number => v.doubleValue()
case _ => null
}

@Documentation(description = "Convert any value to BigInteger or throw exception in case of failure")
def toBigInteger(@ParamName("value") value: Any): java.math.BigInteger = value match {
case v: String => stringToBigInteger(v)
case v: java.math.BigInteger => v
case v: java.math.BigDecimal => v.toBigInteger
case v: Number => java.math.BigInteger.valueOf(v.longValue())
case null => null
case _ => throw new IllegalArgumentException(s"Cannot convert: $value to BigInteger")
}

@Documentation(description = "Convert any value to BigInteger or null in case of failure")
def toBigIntegerOrNull(@ParamName("value") value: Any): java.math.BigInteger = value match {
case v: String => Try(stringToBigInteger(v)).getOrElse(null)
case v: java.math.BigInteger => v
case v: java.math.BigDecimal => v.toBigInteger
case v: Number => java.math.BigInteger.valueOf(v.longValue())
case _ => null
}

@Documentation(description = "Convert any value to BigDecimal or throw exception in case of failure")
def toBigDecimal(@ParamName("value") value: Any): java.math.BigDecimal = value match {
case v: String => new java.math.BigDecimal(v)
case v: java.math.BigInteger => new java.math.BigDecimal(v)
case v: java.math.BigDecimal => v
case v: Number => new java.math.BigDecimal(v.toString)
case null => null
case _ => throw new IllegalArgumentException(s"Cannot convert: $value to BigDecimal")
}

@Documentation(description = "Convert any value to BigDecimal or null in case of failure")
def toBigDecimalOrNull(@ParamName("value") value: Any): java.math.BigDecimal = value match {
case v: String => Try(new java.math.BigDecimal(v)).getOrElse(null)
case v: java.math.BigInteger => new java.math.BigDecimal(v)
case v: java.math.BigDecimal => v
case v: Number => Try(new java.math.BigDecimal(v.toString)).getOrElse(null)
case _ => null
}

}

object ConversionUtils {

private def stringToBigInteger(value: String): java.math.BigInteger =
numeric.toNumber(value) match {
case n: java.math.BigInteger => n
case n => java.math.BigInteger.valueOf(n.longValue())
}

private def stringToBoolean(value: String): Option[java.lang.Boolean] =
if ("true".equalsIgnoreCase(value)) {
Some(true)
} else if ("false".equalsIgnoreCase(value)) {
Some(false)
} else {
None
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ trait BaseSpelSpec {
"DATE_FORMAT" -> new DateFormatUtils(Locale.US),
"UTIL" -> util,
"NUMERIC" -> numeric,
"CONV" -> conversion
)

private val parser = SpelExpressionParser.default(
Expand All @@ -44,6 +45,7 @@ trait BaseSpelSpec {
classOf[DateFormatUtils],
util.getClass,
numeric.getClass,
conversion.getClass,
)

protected def evaluate[T: TypeTag](expr: String, localVariables: Map[String, Any] = Map.empty): T = {
Expand Down
Loading

0 comments on commit 2df535c

Please sign in to comment.