diff --git a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc index 5e9feaee135b7..d9c36382d0510 100644 --- a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc +++ b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc @@ -392,7 +392,7 @@ The `isEmpty` is primary for checking if a value is either `null` or empty strin The `isAlpha` / `isAlphaNumeric` and `isNumeric` is for checking if a value only contains `A..Z` or `A.Z0..9`, or `0..9` characters. Camel will use from the JDK `Character.isLetter` / `Character.isLetterOrDigit`, or `Character.isDigit` methods internally. -So `${isAlpha('Hello World'}` will actually return `false` because there is a whitespace. However `${isAlpha('HelloWorld'}` returns `true`. +So `${isAlpha('Hello World')}` will actually return `false` because there is a whitespace. However `${isAlpha('HelloWorld')}` returns `true`. TIP: You can use the `regex` operator to use regular expressions for more advanced tests. @@ -408,8 +408,8 @@ The date functions is used for parsing and formatting with date and times. |Function |Response Type |Description |`date:millis` | `long` | Returns the current timestamp as millis in unix epoch. |`date:command` | `Date` | Evaluates to a `java.util.Date` object. Supported commands are: `now` for current timestamp, `exchangeCreated` for the timestamp when the current exchange was created, `header.xxx` to use the `Long/Date` object in the header with the key xxx. `variable.xxx` to use the `Long/Date` in the variable with the key xxx. `exchangeProperty.xxx` to use the `Long/Date` object in the exchange property with the key xxx. `file` for the last modified timestamp of the file (only available with a File consumer). Command accepts offsets such as: `now-24h` or `header.xxx+1h` or `now+1h30m-100`. -|`date:command:pattern` | `String` | Date formatting using `java.text.SimpleDateFormat` patterns. See `data:command` function for additional documentation on the commands. -|`date-with-timezone:command:timezone:pattern` | `String` | Date formatting using `java.text.SimpleDateFormat` timezones and patterns. See `data:command` function for additional documentation on the commands. +|`date:command:pattern` | `String` | Date formatting using `java.text.SimpleDateFormat` patterns. See `date:command` function for additional documentation on the commands. +|`date-with-timezone:command:timezone:pattern` | `String` | Date formatting using `java.text.SimpleDateFormat` timezones and patterns. See `date:command` function for additional documentation on the commands. |==== For example to get the current time use `${date:now}` which is returned as a `java.util.Date` object. @@ -449,13 +449,13 @@ The `abs` function returns the absolute value of a numeric value. It converts th A few examples `${abs(-5)}` returns `5`, and `${abs(5)}` also returns `5`. The `average` function calculates the average (mean) of integral numbers (not floating point). -Assume message body is `[10, 20, 30]` (as `List` or similar) then `${avg()}` results in `20` +Assume message body is `[10, 20, 30]` (as `List` or similar) then `${average()}` results in `20` The `ceil` functions returns the value of number rounded up to the nearest integer that is greater than or equal to number. For example `${ceil(5.7)}` returns `6`, and `${ceil(5.1)}` also returns `6`. The `floor` functions returns the value of number rounded down to the nearest integer that is smaller or equal to number. -For example `${ceil(5.7)}` returns `5`, and `${ceil(5.1)}` also returns `5`. +For example `${floor(5.7)}` returns `5`, and `${floor(5.1)}` also returns `5`. The `max` and `min` functions in are used to find the maximum or minimum value among a set of numeric integral values. For example message body contains a list `[10, 5, 30, 15]` then `${max()}` returns `30` and `${min()}` returns `5`. @@ -469,7 +469,7 @@ For example `${range(10)}` returns the numbers `0..9`. And `${range(1,10)}` retu The `sum` function adds up numeric values (not floating point) and returns the result as a long. Assume message body is `[10, 20, 30]` (as `List` or similar) then `${sum()}` results in `60` You can also use the `sum` function to add or subtract numbers. For example if you want to add 2 to a number, you can do -`$sum($\{body},2)}`, and likewise to subtract you use negative number, such as `$sum($\{body},-2)}`. +`${sum($\{body},2)}`, and likewise to subtract you use negative number, such as `${sum($\{body},-2)}`. === Other Functions @@ -484,7 +484,7 @@ Other kind of functions. |`load(file)` | `String` | Loads the content of the resource from classpath (cannot load from file-system to avoid dangerous situations). |`null` | `null` | Returns a `null` value. |`sys.key` | `String` | *Deprecated* To lookup the JVM system property with the given key. -|`sysenv.key` | `String` | To lookup the JVM system property with the given key. +|`sysenv.key` | `String` | To lookup the OS environment variable with the given key. |`threadId` | `String` | Returns the id of the current thread. |`threadName` | `String` | Returns the name of the current thread. |`uuid(kind)` | `String` | Returns a UUID using the Camel `UuidGenerator`. You can choose kind between `default`, `classic`, `short`, `simple` and `random` as the kind. If no kind is given, then `default` is used. It is also possible to use a custom `UuidGenerator` and bind the bean to the xref:manual::registry.adoc[Registry] with an id. For example `${uuid(myGenerator)}` where the id is `myGenerator`. @@ -500,7 +500,7 @@ The `hostname` function returns the OS hostname and does not require using paren The `null` function returns a `null` value such as `$\{null}`. -To use JVM system properties you use the `${sysenv.key}` function, such as `${sysenv.java.version}` or `${sysenv.java.io.tmpdir}`. +To use OS environment variables you can also use the `${sysenv.key}` function (an alias for `${env.key}`), such as `${sysenv.PATH}` or `${sysenv.HOME}`. You can get the current thread id by the `$\{threadId}` function, and the name via `$\{threadName}`. @@ -570,7 +570,7 @@ To use semicolon as separator you would use `${join(;)}`. The `prefix` argument `${join(&,id=)}` would return `id=A&id=B&id=C`. The `lowercase` and `uppercase` functions are as the name implies used for lower and uppercasing. -So `${lowercase('Hello World`)}` returns `hello world` and `${uppercase('Hello World`)}` returns `HELLO WORLD`. +So `${lowercase('Hello World')}` returns `hello world` and `${uppercase('Hello World')}` returns `HELLO WORLD`. You can use the `normalizeWhitespace` function to _clean up_ a value by removing extra whitespaces. This is done by ensuring that there are exactly only 1 whitespace between words. And as well trimming the value for empty whitespace @@ -579,7 +579,7 @@ in the beginning and end. Suppose the message body is a String value with `" H The `pad` function returns a copy of the string with extra padding, if necessary, so that its total number of characters is at least the absolute value of the width parameter. If width is a positive number, then the string is padded to the right; if negative, it is padded to the left. The optional separator specifies the padding character(s) to use. If not specified, it defaults to the space character. -If the message body contains `foo` then `${pad(5)}` return `"foo "` and `${pad(-5)}` returns `" foo"`, and `${pad(-5,'@')}` returns `@@foo`. +If the message body contains `foo` then `${pad($\{body},5)}` returns `"foo "` and `${pad($\{body},-5)}` returns `" foo"`, and `${pad($\{body},-5,'@')}` returns `@@foo`. The `replace` function is used for finding a given text in a string and replacing it with another. For example the message body contains `Hello a how are you`, then `${replace(a,b)}` returns `Hello b how bre you`. @@ -610,9 +610,9 @@ And using `${quote('Hi from me')}` then `"Hi from me"` is returned. The `safeQuote` function is quoting the value depending on the value type (uses the same logic as `kindOfType` function). In essence values that are null, boolean or numbers are not quoted, -while everything else is. The `safeQuote` function is useful when working with JSon data. +while everything else is. The `safeQuote` function is useful when working with JSON data. -For example when doing JSon to JSon mapping you can extract values form the source document, +For example when doing JSON to JSON mapping you can extract values form the source document, to be included in the output, but the values may or may not need to be quoted. Then you can use the `~>` chain operator to call the `safeQuote` function as shown below: @@ -625,34 +625,34 @@ to call the `safeQuote` function as shown below: ---- -=== XML & JSon Functions +=== XML & JSON Functions -Functions to work with XML and JSon payloads. +Functions to work with XML and JSON payloads. [width="100%",cols="10%,10%,80%",options="header",] |==== |Function |Response Type |Description -|`jq(exp)` | `Object` | When working with JSon data, then this allows using the JQ language, for example, to extract data from the message body (in JSon format). This requires having camel-jq JAR on the classpath. -|`jq(input,exp)` | `Object` | Same as `jq(exp)` but to use the _input_ expression as the source of the JSon document. -|`jsonpath(exp)` | `Object` | When working with JSon data, then this allows using the JsonPath language, for example, to extract data from the message body (in JSon format). This requires having camel-jsonpath JAR on the classpath. -|`jsonpath(input,exp)` | `Object` | Same as `jsonpath(exp)` but to use the _input_ expression as the source of the JSon document. -|`pretty(exp)` | `String` | Converts the expression to a `String`, and attempts to pretty print (if JSon or XML) otherwise return the value as-is. -|`prettyBody` | `String` | Converts the message body to a `String`, and attempts to pretty print (if JSon or XML) otherwise return the value as-is. -|`simpleJsonpath(exp)` | `Object` | When working with JSon data, then this allows using built-in Simple JsonPath, for example, to extract data from the message body (in JSon format). -|`simpleJsonpath(input,exp)` | `Object` | Same as `simpleJsonpath(exp)` but to use the _input_ expression as the source of the JSon document. -|`toJson(exp)` | `String` | Converts the expression to a JSon `String` representation. String values are returned as-is, null values return null, all other types are serialized to JSon. -|`toJsonBody` | `String` | Converts the message body to a JSon `String` representation. String values are returned as-is, null values return null, all other types are serialized to JSon. -|`toPrettyJson(exp)` | `String` | Converts the expression to a JSon `String` representation. String values are returned as-is, null values return null, all other types are serialized to JSon in pretty mode. -|`toPrettyJsonBody` | `String` | Converts the message body to a JSon `String` representation. String values are returned as-is, null values return null, all other types are serialized to JSon in pretty mode. +|`jq(exp)` | `Object` | When working with JSON data, then this allows using the JQ language, for example, to extract data from the message body (in JSON format). This requires having camel-jq JAR on the classpath. +|`jq(input,exp)` | `Object` | Same as `jq(exp)` but to use the _input_ expression as the source of the JSON document. +|`jsonpath(exp)` | `Object` | When working with JSON data, then this allows using the JsonPath language, for example, to extract data from the message body (in JSON format). This requires having camel-jsonpath JAR on the classpath. +|`jsonpath(input,exp)` | `Object` | Same as `jsonpath(exp)` but to use the _input_ expression as the source of the JSON document. +|`pretty(exp)` | `String` | Converts the expression to a `String`, and attempts to pretty print (if JSON or XML) otherwise return the value as-is. +|`prettyBody` | `String` | Converts the message body to a `String`, and attempts to pretty print (if JSON or XML) otherwise return the value as-is. +|`simpleJsonpath(exp)` | `Object` | When working with JSON data, then this allows using built-in Simple JsonPath, for example, to extract data from the message body (in JSON format). +|`simpleJsonpath(input,exp)` | `Object` | Same as `simpleJsonpath(exp)` but to use the _input_ expression as the source of the JSON document. +|`toJson(exp)` | `String` | Converts the expression to a JSON `String` representation. String values are returned as-is, null values return null, all other types are serialized to JSON. +|`toJsonBody` | `String` | Converts the message body to a JSON `String` representation. String values are returned as-is, null values return null, all other types are serialized to JSON. +|`toPrettyJson(exp)` | `String` | Converts the expression to a JSON `String` representation. String values are returned as-is, null values return null, all other types are serialized to JSON in pretty mode. +|`toPrettyJsonBody` | `String` | Converts the message body to a JSON `String` representation. String values are returned as-is, null values return null, all other types are serialized to JSON in pretty mode. |`xpath(exp)` | `Object` | When working with XML data, then this allows using the XPath language, for example, to extract data from the message body (in XML format). This requires having camel-xpath JAR on the classpath. -|`xpath(input,exp)` | `Object` | When working with XML data, then this allows using the XPath language, for example, to extract data from the message body (in XML format). This requires having camel-xpath JAR on the classpath. For input you can choose `header:key`, `exchangeProperty:key` or `variable:key` to use as input for the JSon payload instead of the message body. +|`xpath(input,exp)` | `Object` | When working with XML data, then this allows using the XPath language, for example, to extract data from the message body (in XML format). This requires having camel-xpath JAR on the classpath. For input you can choose `header:key`, `exchangeProperty:key` or `variable:key` to use as input for the JSON payload instead of the message body. |==== -The simple language has support for working with XML and JSon data by offering functions that leverage the -xref:languages:jsonpath-language.adoc[JSonPath], xref:languages:jq-language.adoc[JQ], xref:languages:xpath-language.adoc[XPath] languages. +The simple language has support for working with XML and JSON data by offering functions that leverage the +xref:languages:jsonpath-language.adoc[JSONPath], xref:languages:jq-language.adoc[JQ], xref:languages:xpath-language.adoc[XPath] languages. This also mean that you must include the required JARs on the classpath. -The `jq` function (JSon Query) is used for using the JQ language to obtain data from an existing JSon payload. +The `jq` function (JSON Query) is used for using the JQ language to obtain data from an existing JSON payload. For example given this payload: [source,json] @@ -676,7 +676,7 @@ from("direct:map") .to("log:data"); ---- -The `jsonpath` function works similar to `jq` but uses JSonPath. +The `jsonpath` function works similar to `jq` but uses JSONPath. When using `${jsonpath($.id)}` will also return `123`. And the data mapping can be done as follows with JSonPath instead of JQ: @@ -693,10 +693,10 @@ from("direct:map") .to("log:data"); ---- -The `simpleJsonpath` function is using Camel's built in JSon library from `camel-util-json` that is a basic JSon parser. +The `simpleJsonpath` function is using Camel's built in JSON library from `camel-util-json` that is a basic JSON parser. The syntax for the path is similar to Camel's bean OGNL syntax to use a dot notation to walk down a nested structure. -Because the input JSon is not nested then the mapping example is just: +Because the input JSON is not nested then the mapping example is just: [source,java] ---- @@ -710,7 +710,7 @@ from("direct:map") .to("log:data"); ---- -However, if there are nested JSon such as: +However, if there are nested JSON such as: [source,json] ---- @@ -738,7 +738,7 @@ However, if there are nested JSon such as: } ---- -Then you can retrieve data from the JSon payload as follows: +Then you can retrieve data from the JSON payload as follows: [source,text] ---- @@ -763,9 +763,9 @@ For example, we attempt to refer to a non-existing attribute library.book[0]?.cheese ---- -The optional marker can also be used sooner in the path, such as `foo?.bar.age` which will not fail if there are no nested _bar_ node in the JSon payload. +The optional marker can also be used sooner in the path, such as `foo?.bar.age` which will not fail if there are no nested _bar_ node in the JSON payload. -The `toJson` function is to convert the payload to JSon as a `String` value. However, if the payload is already a `String` then no conversion happens. +The `toJson` function is to convert the payload to JSON as a `String` value. However, if the payload is already a `String` then no conversion happens. So suppose the payload is a `Map` object as follows: [source,java] @@ -775,9 +775,23 @@ map.put("name", "Jack"); map.put("id", 123); ---- -Then `toJson` will output: `{"name":"Jack","id":123}`. +Then `toJson` will output: -There are also _pretty_ alternatives so when calling `toPrettyJson` will output: TODO: +[source,json] +---- +{"name":"Jack","id":123} +---- + + +There are also _pretty_ alternatives so when calling `toPrettyJson` will output: + +[source,json] +---- +{ + "name": "Jack", + "id": 123 +} +---- The `xpath` function is for working with XML and using XPath expressions to extract data from XML payloads. @@ -805,15 +819,15 @@ And the data mapping can be done with XPath as follows: from("direct:map") .transform().simple(""" { - "roll": ${jsonpath($.id)}, - "years": ${jsonpath($.age)}, - "fullname": "${jsonpath($.name)}" + "id": ${xpath(/order/@id)}, + "item": "${xpath(/order/item)}", + "fullname": "${xpath(/order/first)} ${xpath(/order/last)}" }""") .to("log:data"); ---- -The `pretty` function is used for pretty printing JSon or XML data as a String value. -For example given the following JSon payload (in a single line): `{"id": 123, "age": 42, "name": "scott"}` +The `pretty` function is used for pretty printing JSON or XML data as a String value. +For example given the following JSON payload (in a single line): `{"id": 123, "age": 42, "name": "scott"}` then `$\{pretty}` will output this nicely formatted: [source,json] @@ -948,7 +962,7 @@ The following special symbols can be used when escaped with `\` as below: |`\\n` | To use newline character. |`\\t` | To use tab character. |`\\r` | To use carriage return character. -|`\\}` | To use the `}` character as text. This may be needed when building a JSon structure with the simple language. +|`\\}` | To use the `}` character as text. This may be needed when building a JSON structure with the simple language. |==== For example to use a new-line character in the split function `${split(\\n)}`. @@ -1100,7 +1114,7 @@ And there can be as many chains: [source,text] ---- -${leftValue} ~> ${midValue} ~> ${midValue} -> ${rightValue} +${leftValue} ~> ${midValue} ~> ${midValue} ~> ${rightValue} ---- For example if the message body contains `Hello World` then the follow would return `WORLD`: @@ -1309,14 +1323,14 @@ For instance: [source,java] ----- -simple("${header.title} contains 'Camel' && ${header.type'} == 'gold'") +simple("${header.title} contains 'Camel' && ${header.type} == 'gold'") ----- And of course the `||` is also supported. The sample would be: [source,java] ----- -simple("${header.title} contains 'Camel' || ${header.type'} == 'gold'") +simple("${header.title} contains 'Camel' || ${header.type} == 'gold'") ----- == Init Blocks @@ -1358,7 +1372,7 @@ Here are a couple of examples: $init{ $cheese := 'Hello ${body}'; $minAge := 18; - $foo := ${uppercase('Hello ${body}'}; + $foo := ${uppercase('Hello ${body}')}; $bar := ${header.code > 999 ? 'Gold' : 'Silver'}; }init$ ---- @@ -1382,7 +1396,7 @@ $init{ The local variables can then easily be used in the Simple language either using the `${variable.foo}` syntax or the shorthand syntax `$foo`. -For example as below where we do a basic JSon mapping: +For example as below where we do a basic JSON mapping: [source,text] ---- @@ -1868,6 +1882,7 @@ Instead of the message body then a simple expression can be nested as input, for ---- +[#_replacing_double_and_single_quotes] == Replacing double and single quotes You can use the `replace` function to more easily replace all single or double quotes in the message body, @@ -1964,7 +1979,7 @@ e.g., to refer to a file on the classpath you can do: .setHeader("myHeader").simple("resource:classpath:mysimple.txt") ---- -== Pretty XML or JSon +== Pretty XML or JSON From *Camel 4.18* onwards then the Simple language can _pretty format_ the output. @@ -2113,7 +2128,7 @@ reg.addFunction(new FooSimpleFunction()); ---- TIP: The custom function can then be made discoverable by Camel by dependency injection. -If you use standalone Camel you can add `@BindToRegistry("foo-function)` to the class. +If you use standalone Camel you can add `@BindToRegistry("foo-function")` to the class. For Spring Boot use `@Component` or `@Service` and Quarkus you can for example use `@ApplicationScoped`. diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionDispatcher.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionDispatcher.java index 2c76cc113d4ea..2080ac72c648b 100644 --- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionDispatcher.java +++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionDispatcher.java @@ -27,6 +27,7 @@ import org.apache.camel.language.simple.functions.HeaderFunctionFactory; import org.apache.camel.language.simple.functions.JoinFunctionFactory; import org.apache.camel.language.simple.functions.MathFunctionFactory; +import org.apache.camel.language.simple.functions.MiscFunctionFactory; import org.apache.camel.language.simple.functions.RandomFunctionFactory; import org.apache.camel.language.simple.functions.SkipFunctionFactory; import org.apache.camel.language.simple.functions.StringFunctionFactory; @@ -63,7 +64,8 @@ public final class SimpleFunctionDispatcher { new JoinFunctionFactory(), new MathFunctionFactory(), new StringFunctionFactory(), - new CollectionFunctionFactory()); + new CollectionFunctionFactory(), + new MiscFunctionFactory()); private static final List EXPRESSION_ENTRIES = List.of( new Entry("camel-attachments", SimpleFunctionDispatcher::isAttachmentFunction), diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java index 0719dacccf9d8..5708f18505005 100644 --- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java +++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java @@ -27,6 +27,7 @@ import org.apache.camel.language.simple.SimpleFunctionDispatcher; import org.apache.camel.language.simple.SimpleFunctionHelper; import org.apache.camel.language.simple.SimplePredicateParser; +import org.apache.camel.language.simple.functions.DirectFunctionFactory; import org.apache.camel.language.simple.types.SimpleParserException; import org.apache.camel.language.simple.types.SimpleToken; import org.apache.camel.spi.Language; @@ -102,9 +103,11 @@ private Expression createSimpleExpression(CamelContext camelContext, String func return exp; } + private static final DirectFunctionFactory DIRECT_FACTORY = new DirectFunctionFactory(); + private Expression doCreateSimpleExpression(CamelContext camelContext, String function, boolean strict) { // return the function directly if we can create function without analyzing the prefix - Expression answer = createSimpleExpressionDirectly(camelContext, function); + Expression answer = DIRECT_FACTORY.createFunction(camelContext, function, token.getIndex()); if (answer != null) { return answer; } @@ -375,15 +378,11 @@ private Expression doCreateSimpleExpression(CamelContext camelContext, String fu return SimpleExpressionBuilder.cacheExpression(exp); } - // miscellaneous functions + // miscellaneous and other built-in functions Expression builtIn = SimpleFunctionDispatcher.tryCreateBuiltIn(camelContext, function, token.getIndex()); if (builtIn != null) { return builtIn; } - Expression misc = createSimpleExpressionMisc(function); - if (misc != null) { - return misc; - } // functions from external components (attachments, base64, html, ...) Expression external = SimpleFunctionDispatcher.tryCreateExternal(camelContext, function, token.getIndex()); @@ -563,46 +562,6 @@ private Expression createSimpleCustomLanguage(String function, boolean strict) { return null; } - private Expression createSimpleExpressionDirectly(CamelContext camelContext, String expression) { - if (ObjectHelper.equal(expression, "id")) { - return ExpressionBuilder.messageIdExpression(); - } else if (ObjectHelper.equal(expression, "messageTimestamp")) { - return ExpressionBuilder.messageTimestampExpression(); - } else if (ObjectHelper.equal(expression, "exchangeId")) { - return ExpressionBuilder.exchangeIdExpression(); - } else if (ObjectHelper.equal(expression, "exchange")) { - return ExpressionBuilder.exchangeExpression(); - } else if (ObjectHelper.equal(expression, "logExchange")) { - return ExpressionBuilder.logExchange(); - } else if (ObjectHelper.equal(expression, "exception")) { - return ExpressionBuilder.exchangeExceptionExpression(); - } else if (ObjectHelper.equal(expression, "exception.message")) { - return ExpressionBuilder.exchangeExceptionMessageExpression(); - } else if (ObjectHelper.equal(expression, "exception.stacktrace")) { - return ExpressionBuilder.exchangeExceptionStackTraceExpression(); - } else if (ObjectHelper.equal(expression, "threadId")) { - return ExpressionBuilder.threadIdExpression(); - } else if (ObjectHelper.equal(expression, "threadName")) { - return ExpressionBuilder.threadNameExpression(); - } else if (ObjectHelper.equal(expression, "hostname")) { - return ExpressionBuilder.hostnameExpression(); - } else if (ObjectHelper.equal(expression, "camelId")) { - return ExpressionBuilder.camelContextNameExpression(); - } else if (ObjectHelper.equal(expression, "routeId")) { - return ExpressionBuilder.routeIdExpression(); - } else if (ObjectHelper.equal(expression, "fromRouteId")) { - return ExpressionBuilder.fromRouteIdExpression(); - } else if (ObjectHelper.equal(expression, "routeGroup")) { - return ExpressionBuilder.routeGroupExpression(); - } else if (ObjectHelper.equal(expression, "stepId")) { - return ExpressionBuilder.stepIdExpression(); - } else if (ObjectHelper.equal(expression, "null")) { - return SimpleExpressionBuilder.nullExpression(); - } - - return null; - } - private Expression createSimpleFileExpression(String remainder, boolean strict) { if (ObjectHelper.equal(remainder, "name")) { return SimpleExpressionBuilder.fileNameExpression(); @@ -639,251 +598,6 @@ private Expression createSimpleFileExpression(String remainder, boolean strict) return null; } - private Expression createSimpleExpressionMisc(String function) { - String remainder; - - // isEmpty function - remainder = ifStartsWithReturnRemainder("isEmpty(", function); - if (remainder != null) { - String exp = null; - String value = StringHelper.beforeLast(remainder, ")"); - if (ObjectHelper.isNotEmpty(value)) { - exp = StringHelper.removeQuotes(value); - } - return SimpleExpressionBuilder.isEmptyExpression(exp); - } - - // isAlpha function - remainder = ifStartsWithReturnRemainder("isAlpha(", function); - if (remainder != null) { - String exp = null; - String value = StringHelper.beforeLast(remainder, ")"); - if (ObjectHelper.isNotEmpty(value)) { - exp = StringHelper.removeQuotes(value); - } - return SimpleExpressionBuilder.isAlphaExpression(exp); - } - remainder = ifStartsWithReturnRemainder("isAlphaNumeric(", function); - if (remainder != null) { - String exp = null; - String value = StringHelper.beforeLast(remainder, ")"); - if (ObjectHelper.isNotEmpty(value)) { - exp = StringHelper.removeQuotes(value); - } - return SimpleExpressionBuilder.isAlphaNumericExpression(exp); - } - // isNumeric function - remainder = ifStartsWithReturnRemainder("isNumeric(", function); - if (remainder != null) { - String exp = null; - String value = StringHelper.beforeLast(remainder, ")"); - if (ObjectHelper.isNotEmpty(value)) { - exp = StringHelper.removeQuotes(value); - } - return SimpleExpressionBuilder.isNumericExpression(exp); - } - // not function - remainder = ifStartsWithReturnRemainder("not(", function); - if (remainder != null) { - String exp = "${body}"; - String value = StringHelper.beforeLast(remainder, ")"); - if (ObjectHelper.isNotEmpty(value)) { - exp = value; - } - return SimpleExpressionBuilder.isNotPredicate(exp); - } - - // whichKind function - remainder = ifStartsWithReturnRemainder("kindOfType(", function); - if (remainder != null) { - String exp = null; - String value = StringHelper.beforeLast(remainder, ")"); - if (ObjectHelper.isNotEmpty(value)) { - exp = StringHelper.removeQuotes(value); - } - return SimpleExpressionBuilder.kindOfTypeExpression(exp); - } - - // throwException function - remainder = ifStartsWithReturnRemainder("throwException(", function); - if (remainder != null) { - String msg; - String type = null; - String values = StringHelper.beforeLast(remainder, ")"); - if (values == null || ObjectHelper.isEmpty(values)) { - throw new SimpleParserException( - "Valid syntax: ${throwException(msg)} or ${throwException(type,msg)} was: " + function, - token.getIndex()); - } - if (values.contains(",")) { - String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', true, true); - if (tokens.length > 2) { - throw new SimpleParserException( - "Valid syntax: ${throwException(msg)} or ${throwException(type,msg)} was: " + function, - token.getIndex()); - } - msg = StringHelper.removeQuotes(tokens[0]); - type = StringHelper.removeQuotes(tokens[1]); - } else { - msg = StringHelper.removeQuotes(values.trim()); - } - return SimpleExpressionBuilder.throwExceptionExpression(msg, type); - } - // assert function - remainder = ifStartsWithReturnRemainder("assert(", function); - if (remainder != null) { - String exp; - String msg; - String values = StringHelper.beforeLast(remainder, ")"); - if (values == null || ObjectHelper.isEmpty(values)) { - throw new SimpleParserException( - "Valid syntax: ${assert(exp,msg)} was: " + function, - token.getIndex()); - } - String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', true, true); - if (tokens.length != 2) { - throw new SimpleParserException( - "Valid syntax: ${assert(exp,msg)} was: " + function, - token.getIndex()); - } - exp = tokens[0]; - msg = StringHelper.removeQuotes(tokens[1]); - return SimpleExpressionBuilder.assertExpression(exp, msg); - } - - // convertTo function - remainder = ifStartsWithReturnRemainder("convertTo(", function); - if (remainder != null) { - String exp = "${body}"; - String type; - // do not use beforeLast as this supports OGNL - String values = StringHelper.before(remainder, ")"); - if (values == null || ObjectHelper.isEmpty(values)) { - throw new SimpleParserException( - "Valid syntax: ${convertTo(type)} or ${convertTo(exp,type)} was: " + function, - token.getIndex()); - } - if (values.contains(",")) { - String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', true, true); - if (tokens.length > 2) { - throw new SimpleParserException( - "Valid syntax: ${convertTo(type)} or ${convertTo(exp,type)} was: " + function, - token.getIndex()); - } - exp = StringHelper.removeQuotes(tokens[0]); - type = StringHelper.removeQuotes(tokens[1]); - } else { - type = StringHelper.removeQuotes(values.trim()); - } - remainder = StringHelper.after(remainder, ")"); - if (ObjectHelper.isNotEmpty(remainder)) { - boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(remainder); - if (invalid) { - throw new SimpleParserException( - "Valid syntax: ${convertTo(type).OGNL} or ${convertTo(exp,type).OGNL} was: " + function, - token.getIndex()); - } - return SimpleExpressionBuilder.convertToOgnlExpression(exp, type, remainder); - } else { - return SimpleExpressionBuilder.convertToExpression(exp, type); - } - } - - // messageHistory function - remainder = ifStartsWithReturnRemainder("messageHistory", function); - if (remainder != null) { - boolean detailed; - String values = StringHelper.between(remainder, "(", ")"); - if (values == null || ObjectHelper.isEmpty(values)) { - detailed = true; - } else { - detailed = Boolean.parseBoolean(values); - } - return SimpleExpressionBuilder.messageHistoryExpression(detailed); - } else if (ObjectHelper.equal(function, "messageHistory")) { - return SimpleExpressionBuilder.messageHistoryExpression(true); - } - - // uuid function - remainder = ifStartsWithReturnRemainder("uuid", function); - if (remainder != null) { - String values = StringHelper.between(remainder, "(", ")"); - return SimpleExpressionBuilder.uuidExpression(values); - } else if (ObjectHelper.equal(function, "uuid")) { - return SimpleExpressionBuilder.uuidExpression(null); - } - - // hash function - remainder = ifStartsWithReturnRemainder("hash(", function); - if (remainder != null) { - String values = StringHelper.beforeLast(remainder, ")"); - if (values == null || ObjectHelper.isEmpty(values)) { - throw new SimpleParserException( - "Valid syntax: ${hash(value,algorithm)} or ${hash(value)} was: " + function, token.getIndex()); - } - if (values.contains(",")) { - String[] tokens = values.split(",", 2); - if (tokens.length > 2) { - throw new SimpleParserException( - "Valid syntax: ${hash(value,algorithm)} or ${hash(value)} was: " + function, token.getIndex()); - } - return SimpleExpressionBuilder.hashExpression(tokens[0].trim(), tokens[1].trim()); - } else { - return SimpleExpressionBuilder.hashExpression(values.trim(), "SHA-256"); - } - } - - // empty function - remainder = ifStartsWithReturnRemainder("empty(", function); - if (remainder != null) { - String value = StringHelper.before(remainder, ")"); - if (ObjectHelper.isEmpty(value)) { - throw new SimpleParserException( - "Valid syntax: ${empty()} but was: " + function, token.getIndex()); - } - return SimpleExpressionBuilder.newEmptyExpression(value); - } - // newEmpty function - remainder = ifStartsWithReturnRemainder("newEmpty(", function); - if (remainder != null) { - String value = StringHelper.before(remainder, ")"); - if (ObjectHelper.isEmpty(value)) { - throw new SimpleParserException( - "Valid syntax: ${newEmpty()} but was: " + function, token.getIndex()); - } - return SimpleExpressionBuilder.newEmptyExpression(value); - } - // iif function - remainder = ifStartsWithReturnRemainder("iif(", function); - if (remainder != null) { - String values = StringHelper.beforeLast(remainder, ")"); - if (values == null || ObjectHelper.isEmpty(values)) { - throw new SimpleParserException( - "Valid syntax: ${iif(predicate,trueExpression,falseExpression)} was: " + function, token.getIndex()); - } - String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', true, true); - if (tokens.length > 3) { - throw new SimpleParserException( - "Valid syntax: ${iif(predicate,trueExpression,falseExpression)} was: " + function, token.getIndex()); - } - return SimpleExpressionBuilder.iifExpression(tokens[0].trim(), tokens[1].trim(), tokens[2].trim()); - } - - // load function - remainder = ifStartsWithReturnRemainder("load(", function); - if (remainder != null) { - String value = StringHelper.beforeLast(remainder, ")"); - if (ObjectHelper.isEmpty(value)) { - throw new SimpleParserException( - "Valid syntax: ${load(name)} but was: " + function, token.getIndex()); - } - value = StringHelper.removeQuotes(value); - return SimpleExpressionBuilder.loadExpression(value); - } - - return null; - } - @Deprecated(since = "4.21") public static String ifStartsWithReturnRemainder(String prefix, String text) { return SimpleFunctionHelper.ifStartsWithReturnRemainder(prefix, text); @@ -898,7 +612,7 @@ private String doCreateCode(CamelContext camelContext, String expression) throws String function = getText(); // return the function directly if we can create function without analyzing the prefix - String answer = createCodeDirectly(function); + String answer = DIRECT_FACTORY.createCode(camelContext, function, token.getIndex()); if (answer != null) { return answer; } @@ -1090,14 +804,27 @@ private String doCreateCode(CamelContext camelContext, String expression) throws } } - // miscellaneous functions + // miscellaneous and other built-in functions String builtIn = SimpleFunctionDispatcher.tryCreateCodeBuiltIn(camelContext, function, token.getIndex()); if (builtIn != null) { return builtIn; } - String misc = createCodeExpressionMisc(camelContext, function); - if (misc != null) { - return misc; + + // not() code-gen requires skipFileFunctions from the parser context and cannot be in MiscFunctionFactory + String notRemainder = ifStartsWithReturnRemainder("not(", function); + if (notRemainder != null) { + String exp = "body"; + String values = StringHelper.beforeLast(notRemainder, ")"); + if (ObjectHelper.isNotEmpty(values)) { + String[] tokens = codeSplitSafe(values, ',', true, true); + if (tokens.length != 1) { + throw new SimpleParserException("Valid syntax: ${not(exp)} was: " + function, token.getIndex()); + } + SimplePredicateParser predicateParser + = new SimplePredicateParser(camelContext, tokens[0], true, skipFileFunctions, null); + exp = predicateParser.parseCode(); + } + return "Object o = " + exp + ";\n return isNot(exchange, o);"; } // code from external components (attachments, base64, ...) @@ -1109,44 +836,6 @@ private String doCreateCode(CamelContext camelContext, String expression) throws throw new SimpleParserException("Unknown function: " + function, token.getIndex()); } - public String createCodeDirectly(String expression) throws SimpleParserException { - if (ObjectHelper.equal(expression, "id")) { - return "message.getMessageId()"; - } else if (ObjectHelper.equal(expression, "messageTimestamp")) { - return "message.getMessageTimestamp()"; - } else if (ObjectHelper.equal(expression, "exchangeId")) { - return "exchange.getExchangeId()"; - } else if (ObjectHelper.equal(expression, "exchange")) { - return "exchange"; - } else if (ObjectHelper.equal(expression, "logExchange")) { - return "logExchange(exchange)"; - } else if (ObjectHelper.equal(expression, "exception")) { - return "exception(exchange)"; - } else if (ObjectHelper.equal(expression, "exception.message")) { - return "exceptionMessage(exchange)"; - } else if (ObjectHelper.equal(expression, "exception.stacktrace")) { - return "exceptionStacktrace(exchange)"; - } else if (ObjectHelper.equal(expression, "threadId")) { - return "threadId()"; - } else if (ObjectHelper.equal(expression, "threadName")) { - return "threadName()"; - } else if (ObjectHelper.equal(expression, "hostname")) { - return "hostName()"; - } else if (ObjectHelper.equal(expression, "camelId")) { - return "context.getName()"; - } else if (ObjectHelper.equal(expression, "fromRouteId")) { - return "fromRouteId(exchange)"; - } else if (ObjectHelper.equal(expression, "routeId")) { - return "routeId(exchange)"; - } else if (ObjectHelper.equal(expression, "stepId")) { - return "stepId(exchange)"; - } else if (ObjectHelper.equal(expression, "null")) { - return "null"; - } - - return null; - } - private String createCodeExchangeProperty(final String function) { // exchangePropertyAsIndex String remainder = ifStartsWithReturnRemainder("exchangePropertyAsIndex(", function); @@ -1301,388 +990,6 @@ private String createCodeFileExpression(String remainder) { throw new SimpleParserException("Unknown file language syntax: " + remainder, token.getIndex()); } - private String createCodeExpressionMisc(CamelContext camelContext, String function) { - String remainder; - - // kindOfType function - remainder = ifStartsWithReturnRemainder("kindOfType(", function); - if (remainder != null) { - String exp = null; - String values = StringHelper.beforeLast(remainder, ")"); - if (ObjectHelper.isNotEmpty(values)) { - String[] tokens = codeSplitSafe(values, ',', true, true); - if (tokens.length != 1) { - throw new SimpleParserException( - "Valid syntax: ${kindOfType(exp)} was: " + function, token.getIndex()); - } - // single quotes should be double quotes - String s = tokens[0]; - if (StringHelper.isSingleQuoted(s)) { - s = StringHelper.removeLeadingAndEndingQuotes(s); - s = StringQuoteHelper.doubleQuote(s); - } - exp = s; - } - if (ObjectHelper.isEmpty(exp)) { - exp = "body"; - } - return "Object o = " + exp + ";\n return kindOfType(exchange, o);"; - } - - // isEmpty function - remainder = ifStartsWithReturnRemainder("isEmpty(", function); - if (remainder != null) { - String exp = null; - String values = StringHelper.beforeLast(remainder, ")"); - if (ObjectHelper.isNotEmpty(values)) { - String[] tokens = codeSplitSafe(values, ',', true, true); - if (tokens.length != 1) { - throw new SimpleParserException( - "Valid syntax: ${isEmpty(exp)} was: " + function, token.getIndex()); - } - // single quotes should be double quotes - String s = tokens[0]; - if (StringHelper.isSingleQuoted(s)) { - s = StringHelper.removeLeadingAndEndingQuotes(s); - s = StringQuoteHelper.doubleQuote(s); - } - exp = s; - } - if (ObjectHelper.isEmpty(exp)) { - exp = "body"; - } - return "Object o = " + exp + ";\n return isEmpty(exchange, o);"; - } - - // isNumeric function - remainder = ifStartsWithReturnRemainder("isAlpha(", function); - if (remainder != null) { - String exp = null; - String values = StringHelper.beforeLast(remainder, ")"); - if (ObjectHelper.isNotEmpty(values)) { - String[] tokens = codeSplitSafe(values, ',', true, true); - if (tokens.length != 1) { - throw new SimpleParserException( - "Valid syntax: ${isAlpha(exp)} was: " + function, token.getIndex()); - } - // single quotes should be double quotes - String s = tokens[0]; - if (StringHelper.isSingleQuoted(s)) { - s = StringHelper.removeLeadingAndEndingQuotes(s); - s = StringQuoteHelper.doubleQuote(s); - } - exp = s; - } - if (ObjectHelper.isEmpty(exp)) { - exp = "body"; - } - return "Object o = " + exp + ";\n return isAlpha(exchange, o);"; - } - // isAlphaNumeric function - remainder = ifStartsWithReturnRemainder("isAlphaNumeric(", function); - if (remainder != null) { - String exp = null; - String values = StringHelper.beforeLast(remainder, ")"); - if (ObjectHelper.isNotEmpty(values)) { - String[] tokens = codeSplitSafe(values, ',', true, true); - if (tokens.length != 1) { - throw new SimpleParserException( - "Valid syntax: ${isAlphaNumeric(exp)} was: " + function, token.getIndex()); - } - // single quotes should be double quotes - String s = tokens[0]; - if (StringHelper.isSingleQuoted(s)) { - s = StringHelper.removeLeadingAndEndingQuotes(s); - s = StringQuoteHelper.doubleQuote(s); - } - exp = s; - } - if (ObjectHelper.isEmpty(exp)) { - exp = "body"; - } - return "Object o = " + exp + ";\n return isAlphaNumeric(exchange, o);"; - } - // isNumeric function - remainder = ifStartsWithReturnRemainder("isNumeric(", function); - if (remainder != null) { - String exp = null; - String values = StringHelper.beforeLast(remainder, ")"); - if (ObjectHelper.isNotEmpty(values)) { - String[] tokens = codeSplitSafe(values, ',', true, true); - if (tokens.length != 1) { - throw new SimpleParserException( - "Valid syntax: ${isNumeric(exp)} was: " + function, token.getIndex()); - } - // single quotes should be double quotes - String s = tokens[0]; - if (StringHelper.isSingleQuoted(s)) { - s = StringHelper.removeLeadingAndEndingQuotes(s); - s = StringQuoteHelper.doubleQuote(s); - } - exp = s; - } - if (ObjectHelper.isEmpty(exp)) { - exp = "body"; - } - return "Object o = " + exp + ";\n return isNumeric(exchange, o);"; - } - // not function - remainder = ifStartsWithReturnRemainder("not(", function); - if (remainder != null) { - String exp = "body"; - String values = StringHelper.beforeLast(remainder, ")"); - if (ObjectHelper.isNotEmpty(values)) { - String[] tokens = codeSplitSafe(values, ',', true, true); - if (tokens.length != 1) { - throw new SimpleParserException( - "Valid syntax: ${not(exp)} was: " + function, token.getIndex()); - } - - // Parse the condition as a predicate and generate code - SimplePredicateParser predicateParser - = new SimplePredicateParser(camelContext, tokens[0], true, skipFileFunctions, null); - exp = predicateParser.parseCode(); - } - return "Object o = " + exp + ";\n return isNot(exchange, o);"; - } - - // convertTo function - remainder = ifStartsWithReturnRemainder("convertTo(", function); - if (remainder != null) { - String ognl = null; - String exp = "body"; - String type; - String values = StringHelper.before(remainder, ")"); - if (values == null || ObjectHelper.isEmpty(values)) { - throw new SimpleParserException( - "Valid syntax: ${convertTo(type)} or ${convertTo(exp,type)} was: " + function, - token.getIndex()); - } - if (values.contains(",")) { - String[] tokens = codeSplitSafe(values, ',', true, true); - if (tokens.length > 2) { - throw new SimpleParserException( - "Valid syntax: ${convertTo(type)} or ${convertTo(exp,type)} was: " + function, - token.getIndex()); - } - String s = tokens[0].trim(); - s = StringHelper.removeLeadingAndEndingQuotes(s); - s = StringQuoteHelper.doubleQuote(s); - exp = s; - type = tokens[1]; - } else { - type = values.trim(); - } - type = appendClass(type); - type = type.replace('$', '.'); - if (ObjectHelper.isEmpty(exp)) { - exp = "null"; - } - - remainder = StringHelper.after(remainder, ")"); - if (ObjectHelper.isNotEmpty(remainder)) { - boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(remainder); - if (invalid) { - throw new SimpleParserException( - "Valid syntax: ${convertTo(type).OGNL} or ${convertTo(exp,type).OGNL} was: " + function, - token.getIndex()); - } - ognl = ognlCodeMethods(remainder, type); - } - - String code = "Object value = " + exp + ";\n return convertTo(exchange, " + type + ", value)"; - if (ognl != null) { - code += ognl; - } - code += ";"; - return code; - } - // throwException function - remainder = ifStartsWithReturnRemainder("throwException(", function); - if (remainder != null) { - String msg; - String type = "IllegalArgumentException"; - String values = StringHelper.before(remainder, ")"); - if (values == null || ObjectHelper.isEmpty(values)) { - throw new SimpleParserException( - "Valid syntax: ${throwException(msg)} or ${throwException(msg,type)} was: " + function, - token.getIndex()); - } - if (values.contains(",")) { - String[] tokens = codeSplitSafe(values, ',', true, true); - if (tokens.length > 2) { - throw new SimpleParserException( - "Valid syntax: ${throwException(msg)} or ${throwException(msg,type)} was: " + function, - token.getIndex()); - } - msg = tokens[0]; - type = tokens[1]; - } else { - msg = values.trim(); - } - msg = StringHelper.removeLeadingAndEndingQuotes(msg); - msg = StringQuoteHelper.doubleQuote(msg); - type = appendClass(type); - type = type.replace('$', '.'); - return "return throwException(exchange, " + msg + ", " + type + ");"; - } - // assertExpression function - remainder = ifStartsWithReturnRemainder("assertExpression(", function); - if (remainder != null) { - throw new UnsupportedOperationException("assertExpression is not supported in csimple language"); - } - - // messageHistory function - remainder = ifStartsWithReturnRemainder("messageHistory", function); - if (remainder != null) { - boolean detailed; - String values = StringHelper.between(remainder, "(", ")"); - if (values == null || ObjectHelper.isEmpty(values)) { - detailed = true; - } else { - detailed = Boolean.parseBoolean(values); - } - return "messageHistory(exchange, " + (detailed ? "true" : "false") + ")"; - } else if (ObjectHelper.equal(function, "messageHistory")) { - return "messageHistory(exchange, true)"; - } - - // empty function - remainder = ifStartsWithReturnRemainder("empty(", function); - if (remainder != null) { - String value = StringHelper.beforeLast(remainder, ")"); - if (ObjectHelper.isEmpty(value)) { - throw new SimpleParserException( - "Valid syntax: ${empty()} but was: " + function, token.getIndex()); - } - value = StringQuoteHelper.doubleQuote(value); - return "newEmpty(exchange, " + value + ")"; - } - // newEmpty - remainder = ifStartsWithReturnRemainder("newEmpty(", function); - if (remainder != null) { - String value = StringHelper.beforeLast(remainder, ")"); - if (ObjectHelper.isEmpty(value)) { - throw new SimpleParserException( - "Valid syntax: ${newEmpty()} but was: " + function, token.getIndex()); - } - value = StringQuoteHelper.doubleQuote(value); - return "newEmpty(exchange, " + value + ")"; - } - - // hash function - remainder = ifStartsWithReturnRemainder("hash(", function); - if (remainder != null) { - String values = StringHelper.beforeLast(remainder, ")"); - if (values == null || ObjectHelper.isEmpty(values)) { - throw new SimpleParserException( - "Valid syntax: ${hash(value,algorithm)} or ${hash(value)} was: " + function, token.getIndex()); - } - String[] tokens = codeSplitSafe(values, ',', true, true); - if (tokens.length > 2) { - throw new SimpleParserException( - "Valid syntax: ${hash(value,algorithm)} or ${hash(value)} was: " + function, token.getIndex()); - } - // single quotes should be double quotes - for (int i = 0; i < tokens.length; i++) { - String s = tokens[i]; - if (StringHelper.isSingleQuoted(s)) { - s = StringHelper.removeLeadingAndEndingQuotes(s); - s = StringQuoteHelper.doubleQuote(s); - tokens[i] = s; - } - } - String algo = "\"SHA-256\""; - if (tokens.length == 2) { - algo = tokens[1]; - if (!StringHelper.isQuoted(algo)) { - algo = StringQuoteHelper.doubleQuote(algo); - } - } - return "var val = " + tokens[0] + ";\n return hash(exchange, val, " + algo + ");"; - } - - // uuid function - remainder = ifStartsWithReturnRemainder("uuid", function); - if (remainder == null && "uuid".equals(function)) { - remainder = "(default)"; - } - if (remainder != null) { - String generator = StringHelper.between(remainder, "(", ")"); - if (generator == null) { - generator = "default"; - } - StringBuilder sb = new StringBuilder(128); - if ("classic".equals(generator)) { - sb.append(" UuidGenerator uuid = new org.apache.camel.support.ClassicUuidGenerator();\n"); - sb.append("return uuid.generateUuid();"); - } else if ("short".equals(generator)) { - sb.append(" UuidGenerator uuid = new org.apache.camel.support.ShortUuidGenerator();\n"); - sb.append("return uuid.generateUuid();"); - } else if ("simple".equals(generator)) { - sb.append(" UuidGenerator uuid = new org.apache.camel.support.SimpleUuidGenerator();\n"); - sb.append("return uuid.generateUuid();"); - } else if ("default".equals(generator)) { - sb.append(" UuidGenerator uuid = new org.apache.camel.support.DefaultUuidGenerator();\n"); - sb.append("return uuid.generateUuid();"); - } else if ("random".equals(generator)) { - sb.append(" UuidGenerator uuid = new org.apache.camel.support.RandomUuidGenerator();\n"); - sb.append("return uuid.generateUuid();"); - } else { - generator = StringQuoteHelper.doubleQuote(generator); - sb.append("if (uuid == null) uuid = customUuidGenerator(exchange, ").append(generator) - .append("); return uuid.generateUuid();"); - } - return sb.toString(); - } - - // iif function - remainder = ifStartsWithReturnRemainder("iif(", function); - if (remainder != null) { - String values = StringHelper.beforeLast(remainder, ")"); - if (values == null || ObjectHelper.isEmpty(values)) { - throw new SimpleParserException( - "Valid syntax: ${iif(predicate,trueExpression,falseExpression)} was: " + function, token.getIndex()); - } - String[] tokens = codeSplitSafe(values, ',', true, true); - if (tokens.length != 3) { - throw new SimpleParserException( - "Valid syntax: ${iif(predicate,trueExpression,falseExpression)} was: " + function, token.getIndex()); - } - // single quotes should be double quotes - for (int i = 0; i < 3; i++) { - String s = tokens[i]; - if (StringHelper.isSingleQuoted(s)) { - s = StringHelper.removeLeadingAndEndingQuotes(s); - s = StringQuoteHelper.doubleQuote(s); - tokens[i] = s; - } - } - - return "Object o = " + tokens[0] - + ";\n boolean b = convertTo(exchange, boolean.class, o);\n return b ? " - + tokens[1] + " : " + tokens[2]; - } - - // load function - remainder = ifStartsWithReturnRemainder("load(", function); - if (remainder != null) { - String value = StringHelper.beforeLast(remainder, ")"); - if (ObjectHelper.isEmpty(value)) { - throw new SimpleParserException( - "Valid syntax: ${load(name)} but was: " + function, token.getIndex()); - } - // single quotes should be double quotes - if (StringHelper.isSingleQuoted(value)) { - value = StringHelper.removeLeadingAndEndingQuotes(value); - value = StringQuoteHelper.doubleQuote(value); - } - return "Object o = " + value + ";\n return load(exchange, o);"; - } - - return null; - } - @Deprecated(since = "4.21") public static String ognlCodeMethods(String remainder, String type) { return SimpleFunctionHelper.ognlCodeMethods(remainder, type); diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/DirectFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/DirectFunctionFactory.java new file mode 100644 index 0000000000000..54dd9290de12f --- /dev/null +++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/DirectFunctionFactory.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.language.simple.functions; + +import org.apache.camel.CamelContext; +import org.apache.camel.Expression; +import org.apache.camel.language.simple.SimpleExpressionBuilder; +import org.apache.camel.spi.SimpleLanguageFunctionFactory; +import org.apache.camel.support.builder.ExpressionBuilder; +import org.apache.camel.util.ObjectHelper; + +/** + * Built-in Simple keyword functions that are matched by exact name (no prefix): {@code ${id}}, + * {@code ${messageTimestamp}}, {@code ${exchangeId}}, {@code ${exchange}}, {@code ${logExchange}}, + * {@code ${exception}}, {@code ${exception.message}}, {@code ${exception.stacktrace}}, {@code ${threadId}}, + * {@code ${threadName}}, {@code ${hostname}}, {@code ${camelId}}, {@code ${routeId}}, {@code ${fromRouteId}}, + * {@code ${routeGroup}}, {@code ${stepId}}, {@code ${null}}. + * + *

+ * These functions must be dispatched before the inline prefix checks in {@code SimpleFunctionExpression} + * (e.g., the {@code exception} OGNL check and the {@code exchange} OGNL check), because names like {@code exchangeId} + * and {@code exception.message} share a prefix with those checks. + */ +public final class DirectFunctionFactory implements SimpleLanguageFunctionFactory { + + @Override + public Expression createFunction(CamelContext camelContext, String function, int index) { + if (ObjectHelper.equal(function, "id")) { + return ExpressionBuilder.messageIdExpression(); + } else if (ObjectHelper.equal(function, "messageTimestamp")) { + return ExpressionBuilder.messageTimestampExpression(); + } else if (ObjectHelper.equal(function, "exchangeId")) { + return ExpressionBuilder.exchangeIdExpression(); + } else if (ObjectHelper.equal(function, "exchange")) { + return ExpressionBuilder.exchangeExpression(); + } else if (ObjectHelper.equal(function, "logExchange")) { + return ExpressionBuilder.logExchange(); + } else if (ObjectHelper.equal(function, "exception")) { + return ExpressionBuilder.exchangeExceptionExpression(); + } else if (ObjectHelper.equal(function, "exception.message")) { + return ExpressionBuilder.exchangeExceptionMessageExpression(); + } else if (ObjectHelper.equal(function, "exception.stacktrace")) { + return ExpressionBuilder.exchangeExceptionStackTraceExpression(); + } else if (ObjectHelper.equal(function, "threadId")) { + return ExpressionBuilder.threadIdExpression(); + } else if (ObjectHelper.equal(function, "threadName")) { + return ExpressionBuilder.threadNameExpression(); + } else if (ObjectHelper.equal(function, "hostname")) { + return ExpressionBuilder.hostnameExpression(); + } else if (ObjectHelper.equal(function, "camelId")) { + return ExpressionBuilder.camelContextNameExpression(); + } else if (ObjectHelper.equal(function, "routeId")) { + return ExpressionBuilder.routeIdExpression(); + } else if (ObjectHelper.equal(function, "fromRouteId")) { + return ExpressionBuilder.fromRouteIdExpression(); + } else if (ObjectHelper.equal(function, "routeGroup")) { + return ExpressionBuilder.routeGroupExpression(); + } else if (ObjectHelper.equal(function, "stepId")) { + return ExpressionBuilder.stepIdExpression(); + } else if (ObjectHelper.equal(function, "null")) { + return SimpleExpressionBuilder.nullExpression(); + } + + return null; + } + + @Override + public String createCode(CamelContext camelContext, String function, int index) { + if (ObjectHelper.equal(function, "id")) { + return "message.getMessageId()"; + } else if (ObjectHelper.equal(function, "messageTimestamp")) { + return "message.getMessageTimestamp()"; + } else if (ObjectHelper.equal(function, "exchangeId")) { + return "exchange.getExchangeId()"; + } else if (ObjectHelper.equal(function, "exchange")) { + return "exchange"; + } else if (ObjectHelper.equal(function, "logExchange")) { + return "logExchange(exchange)"; + } else if (ObjectHelper.equal(function, "exception")) { + return "exception(exchange)"; + } else if (ObjectHelper.equal(function, "exception.message")) { + return "exceptionMessage(exchange)"; + } else if (ObjectHelper.equal(function, "exception.stacktrace")) { + return "exceptionStacktrace(exchange)"; + } else if (ObjectHelper.equal(function, "threadId")) { + return "threadId()"; + } else if (ObjectHelper.equal(function, "threadName")) { + return "threadName()"; + } else if (ObjectHelper.equal(function, "hostname")) { + return "hostName()"; + } else if (ObjectHelper.equal(function, "camelId")) { + return "context.getName()"; + } else if (ObjectHelper.equal(function, "fromRouteId")) { + return "fromRouteId(exchange)"; + } else if (ObjectHelper.equal(function, "routeId")) { + return "routeId(exchange)"; + } else if (ObjectHelper.equal(function, "stepId")) { + return "stepId(exchange)"; + } else if (ObjectHelper.equal(function, "null")) { + return "null"; + } + + return null; + } +} diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MiscFunctionFactory.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MiscFunctionFactory.java new file mode 100644 index 0000000000000..a173f147621e4 --- /dev/null +++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/MiscFunctionFactory.java @@ -0,0 +1,550 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.language.simple.functions; + +import org.apache.camel.CamelContext; +import org.apache.camel.Expression; +import org.apache.camel.language.simple.SimpleExpressionBuilder; +import org.apache.camel.language.simple.types.SimpleParserException; +import org.apache.camel.spi.SimpleLanguageFunctionFactory; +import org.apache.camel.util.ObjectHelper; +import org.apache.camel.util.OgnlHelper; +import org.apache.camel.util.StringHelper; +import org.apache.camel.util.StringQuoteHelper; + +import static org.apache.camel.language.simple.SimpleFunctionHelper.appendClass; +import static org.apache.camel.language.simple.SimpleFunctionHelper.codeSplitSafe; +import static org.apache.camel.language.simple.SimpleFunctionHelper.ifStartsWithReturnRemainder; +import static org.apache.camel.language.simple.SimpleFunctionHelper.ognlCodeMethods; + +/** + * Built-in Simple miscellaneous functions: {@code ${isEmpty}}, {@code ${isAlpha}}, {@code ${isAlphaNumeric}}, + * {@code ${isNumeric}}, {@code ${not}}, {@code ${kindOfType}}, {@code ${throwException}}, {@code ${assert}}, + * {@code ${convertTo}}, {@code ${messageHistory}}, {@code ${uuid}}, {@code ${hash}}, {@code ${empty}}, + * {@code ${newEmpty}}, {@code ${iif}}, {@code ${load}}. + * + *

+ * Note: the {@code ${not}} CSimple code-generation path requires access to the {@code skipFileFunctions} flag from the + * enclosing parser context and is therefore kept in {@code SimpleFunctionExpression} rather than here. + */ +public final class MiscFunctionFactory implements SimpleLanguageFunctionFactory { + + @Override + public Expression createFunction(CamelContext camelContext, String function, int index) { + String remainder; + + remainder = ifStartsWithReturnRemainder("isEmpty(", function); + if (remainder != null) { + String exp = null; + String value = StringHelper.beforeLast(remainder, ")"); + if (ObjectHelper.isNotEmpty(value)) { + exp = StringHelper.removeQuotes(value); + } + return SimpleExpressionBuilder.isEmptyExpression(exp); + } + + remainder = ifStartsWithReturnRemainder("isAlpha(", function); + if (remainder != null) { + String exp = null; + String value = StringHelper.beforeLast(remainder, ")"); + if (ObjectHelper.isNotEmpty(value)) { + exp = StringHelper.removeQuotes(value); + } + return SimpleExpressionBuilder.isAlphaExpression(exp); + } + + remainder = ifStartsWithReturnRemainder("isAlphaNumeric(", function); + if (remainder != null) { + String exp = null; + String value = StringHelper.beforeLast(remainder, ")"); + if (ObjectHelper.isNotEmpty(value)) { + exp = StringHelper.removeQuotes(value); + } + return SimpleExpressionBuilder.isAlphaNumericExpression(exp); + } + + remainder = ifStartsWithReturnRemainder("isNumeric(", function); + if (remainder != null) { + String exp = null; + String value = StringHelper.beforeLast(remainder, ")"); + if (ObjectHelper.isNotEmpty(value)) { + exp = StringHelper.removeQuotes(value); + } + return SimpleExpressionBuilder.isNumericExpression(exp); + } + + remainder = ifStartsWithReturnRemainder("not(", function); + if (remainder != null) { + String exp = "${body}"; + String value = StringHelper.beforeLast(remainder, ")"); + if (ObjectHelper.isNotEmpty(value)) { + exp = value; + } + return SimpleExpressionBuilder.isNotPredicate(exp); + } + + remainder = ifStartsWithReturnRemainder("kindOfType(", function); + if (remainder != null) { + String exp = null; + String value = StringHelper.beforeLast(remainder, ")"); + if (ObjectHelper.isNotEmpty(value)) { + exp = StringHelper.removeQuotes(value); + } + return SimpleExpressionBuilder.kindOfTypeExpression(exp); + } + + remainder = ifStartsWithReturnRemainder("throwException(", function); + if (remainder != null) { + String msg; + String type = null; + String values = StringHelper.beforeLast(remainder, ")"); + if (values == null || ObjectHelper.isEmpty(values)) { + throw new SimpleParserException( + "Valid syntax: ${throwException(msg)} or ${throwException(type,msg)} was: " + function, index); + } + if (values.contains(",")) { + String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', true, true); + if (tokens.length > 2) { + throw new SimpleParserException( + "Valid syntax: ${throwException(msg)} or ${throwException(type,msg)} was: " + function, index); + } + msg = StringHelper.removeQuotes(tokens[0]); + type = StringHelper.removeQuotes(tokens[1]); + } else { + msg = StringHelper.removeQuotes(values.trim()); + } + return SimpleExpressionBuilder.throwExceptionExpression(msg, type); + } + + remainder = ifStartsWithReturnRemainder("assert(", function); + if (remainder != null) { + String values = StringHelper.beforeLast(remainder, ")"); + if (values == null || ObjectHelper.isEmpty(values)) { + throw new SimpleParserException("Valid syntax: ${assert(exp,msg)} was: " + function, index); + } + String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', true, true); + if (tokens.length != 2) { + throw new SimpleParserException("Valid syntax: ${assert(exp,msg)} was: " + function, index); + } + return SimpleExpressionBuilder.assertExpression(tokens[0], StringHelper.removeQuotes(tokens[1])); + } + + remainder = ifStartsWithReturnRemainder("convertTo(", function); + if (remainder != null) { + String exp = "${body}"; + String type; + String values = StringHelper.before(remainder, ")"); + if (values == null || ObjectHelper.isEmpty(values)) { + throw new SimpleParserException( + "Valid syntax: ${convertTo(type)} or ${convertTo(exp,type)} was: " + function, index); + } + if (values.contains(",")) { + String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', true, true); + if (tokens.length > 2) { + throw new SimpleParserException( + "Valid syntax: ${convertTo(type)} or ${convertTo(exp,type)} was: " + function, index); + } + exp = StringHelper.removeQuotes(tokens[0]); + type = StringHelper.removeQuotes(tokens[1]); + } else { + type = StringHelper.removeQuotes(values.trim()); + } + remainder = StringHelper.after(remainder, ")"); + if (ObjectHelper.isNotEmpty(remainder)) { + boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(remainder); + if (invalid) { + throw new SimpleParserException( + "Valid syntax: ${convertTo(type).OGNL} or ${convertTo(exp,type).OGNL} was: " + function, index); + } + return SimpleExpressionBuilder.convertToOgnlExpression(exp, type, remainder); + } else { + return SimpleExpressionBuilder.convertToExpression(exp, type); + } + } + + remainder = ifStartsWithReturnRemainder("messageHistory", function); + if (remainder != null) { + boolean detailed; + String values = StringHelper.between(remainder, "(", ")"); + if (values == null || ObjectHelper.isEmpty(values)) { + detailed = true; + } else { + detailed = Boolean.parseBoolean(values); + } + return SimpleExpressionBuilder.messageHistoryExpression(detailed); + } else if (ObjectHelper.equal(function, "messageHistory")) { + return SimpleExpressionBuilder.messageHistoryExpression(true); + } + + remainder = ifStartsWithReturnRemainder("uuid", function); + if (remainder != null) { + String values = StringHelper.between(remainder, "(", ")"); + return SimpleExpressionBuilder.uuidExpression(values); + } else if (ObjectHelper.equal(function, "uuid")) { + return SimpleExpressionBuilder.uuidExpression(null); + } + + remainder = ifStartsWithReturnRemainder("hash(", function); + if (remainder != null) { + String values = StringHelper.beforeLast(remainder, ")"); + if (values == null || ObjectHelper.isEmpty(values)) { + throw new SimpleParserException( + "Valid syntax: ${hash(value,algorithm)} or ${hash(value)} was: " + function, index); + } + if (values.contains(",")) { + String[] tokens = values.split(",", 2); + if (tokens.length > 2) { + throw new SimpleParserException( + "Valid syntax: ${hash(value,algorithm)} or ${hash(value)} was: " + function, index); + } + return SimpleExpressionBuilder.hashExpression(tokens[0].trim(), tokens[1].trim()); + } else { + return SimpleExpressionBuilder.hashExpression(values.trim(), "SHA-256"); + } + } + + remainder = ifStartsWithReturnRemainder("empty(", function); + if (remainder != null) { + String value = StringHelper.before(remainder, ")"); + if (ObjectHelper.isEmpty(value)) { + throw new SimpleParserException("Valid syntax: ${empty()} but was: " + function, index); + } + return SimpleExpressionBuilder.newEmptyExpression(value); + } + + remainder = ifStartsWithReturnRemainder("newEmpty(", function); + if (remainder != null) { + String value = StringHelper.before(remainder, ")"); + if (ObjectHelper.isEmpty(value)) { + throw new SimpleParserException("Valid syntax: ${newEmpty()} but was: " + function, index); + } + return SimpleExpressionBuilder.newEmptyExpression(value); + } + + remainder = ifStartsWithReturnRemainder("iif(", function); + if (remainder != null) { + String values = StringHelper.beforeLast(remainder, ")"); + if (values == null || ObjectHelper.isEmpty(values)) { + throw new SimpleParserException( + "Valid syntax: ${iif(predicate,trueExpression,falseExpression)} was: " + function, index); + } + String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', true, true); + if (tokens.length > 3) { + throw new SimpleParserException( + "Valid syntax: ${iif(predicate,trueExpression,falseExpression)} was: " + function, index); + } + return SimpleExpressionBuilder.iifExpression(tokens[0].trim(), tokens[1].trim(), tokens[2].trim()); + } + + remainder = ifStartsWithReturnRemainder("load(", function); + if (remainder != null) { + String value = StringHelper.beforeLast(remainder, ")"); + if (ObjectHelper.isEmpty(value)) { + throw new SimpleParserException("Valid syntax: ${load(name)} but was: " + function, index); + } + return SimpleExpressionBuilder.loadExpression(StringHelper.removeQuotes(value)); + } + + return null; + } + + @Override + public String createCode(CamelContext camelContext, String function, int index) { + String remainder; + + remainder = ifStartsWithReturnRemainder("kindOfType(", function); + if (remainder != null) { + return codeUnaryObject("kindOfType", remainder, function, index); + } + + remainder = ifStartsWithReturnRemainder("isEmpty(", function); + if (remainder != null) { + return codeUnaryObject("isEmpty", remainder, function, index); + } + + remainder = ifStartsWithReturnRemainder("isAlpha(", function); + if (remainder != null) { + return codeUnaryObject("isAlpha", remainder, function, index); + } + + remainder = ifStartsWithReturnRemainder("isAlphaNumeric(", function); + if (remainder != null) { + return codeUnaryObject("isAlphaNumeric", remainder, function, index); + } + + remainder = ifStartsWithReturnRemainder("isNumeric(", function); + if (remainder != null) { + return codeUnaryObject("isNumeric", remainder, function, index); + } + + remainder = ifStartsWithReturnRemainder("convertTo(", function); + if (remainder != null) { + return codeConvertTo(remainder, function, index); + } + + remainder = ifStartsWithReturnRemainder("throwException(", function); + if (remainder != null) { + return codeThrowException(remainder, function, index); + } + + remainder = ifStartsWithReturnRemainder("assertExpression(", function); + if (remainder != null) { + throw new UnsupportedOperationException("assertExpression is not supported in csimple language"); + } + + remainder = ifStartsWithReturnRemainder("messageHistory", function); + if (remainder != null) { + boolean detailed; + String values = StringHelper.between(remainder, "(", ")"); + if (values == null || ObjectHelper.isEmpty(values)) { + detailed = true; + } else { + detailed = Boolean.parseBoolean(values); + } + return "messageHistory(exchange, " + (detailed ? "true" : "false") + ")"; + } else if (ObjectHelper.equal(function, "messageHistory")) { + return "messageHistory(exchange, true)"; + } + + remainder = ifStartsWithReturnRemainder("empty(", function); + if (remainder != null) { + String value = StringHelper.beforeLast(remainder, ")"); + if (ObjectHelper.isEmpty(value)) { + throw new SimpleParserException("Valid syntax: ${empty()} but was: " + function, index); + } + return "newEmpty(exchange, " + StringQuoteHelper.doubleQuote(value) + ")"; + } + + remainder = ifStartsWithReturnRemainder("newEmpty(", function); + if (remainder != null) { + String value = StringHelper.beforeLast(remainder, ")"); + if (ObjectHelper.isEmpty(value)) { + throw new SimpleParserException("Valid syntax: ${newEmpty()} but was: " + function, index); + } + return "newEmpty(exchange, " + StringQuoteHelper.doubleQuote(value) + ")"; + } + + remainder = ifStartsWithReturnRemainder("hash(", function); + if (remainder != null) { + return codeHash(remainder, function, index); + } + + remainder = ifStartsWithReturnRemainder("uuid", function); + if (remainder == null && "uuid".equals(function)) { + remainder = "(default)"; + } + if (remainder != null) { + return codeUuid(remainder, function); + } + + remainder = ifStartsWithReturnRemainder("iif(", function); + if (remainder != null) { + return codeIif(remainder, function, index); + } + + remainder = ifStartsWithReturnRemainder("load(", function); + if (remainder != null) { + String value = StringHelper.beforeLast(remainder, ")"); + if (ObjectHelper.isEmpty(value)) { + throw new SimpleParserException("Valid syntax: ${load(name)} but was: " + function, index); + } + if (StringHelper.isSingleQuoted(value)) { + value = StringHelper.removeLeadingAndEndingQuotes(value); + value = StringQuoteHelper.doubleQuote(value); + } + return "Object o = " + value + ";\n return load(exchange, o);"; + } + + return null; + } + + private static String codeUnaryObject(String name, String remainder, String function, int index) { + String exp = null; + String values = StringHelper.beforeLast(remainder, ")"); + if (ObjectHelper.isNotEmpty(values)) { + String[] tokens = codeSplitSafe(values, ',', true, true); + if (tokens.length != 1) { + throw new SimpleParserException("Valid syntax: ${" + name + "(exp)} was: " + function, index); + } + String s = tokens[0]; + if (StringHelper.isSingleQuoted(s)) { + s = StringHelper.removeLeadingAndEndingQuotes(s); + s = StringQuoteHelper.doubleQuote(s); + } + exp = s; + } + if (ObjectHelper.isEmpty(exp)) { + exp = "body"; + } + return "Object o = " + exp + ";\n return " + name + "(exchange, o);"; + } + + private static String codeConvertTo(String remainder, String function, int index) { + String ognl = null; + String exp = "body"; + String type; + String values = StringHelper.before(remainder, ")"); + if (values == null || ObjectHelper.isEmpty(values)) { + throw new SimpleParserException( + "Valid syntax: ${convertTo(type)} or ${convertTo(exp,type)} was: " + function, index); + } + if (values.contains(",")) { + String[] tokens = codeSplitSafe(values, ',', true, true); + if (tokens.length > 2) { + throw new SimpleParserException( + "Valid syntax: ${convertTo(type)} or ${convertTo(exp,type)} was: " + function, index); + } + String s = tokens[0].trim(); + s = StringHelper.removeLeadingAndEndingQuotes(s); + s = StringQuoteHelper.doubleQuote(s); + exp = s; + type = tokens[1]; + } else { + type = values.trim(); + } + type = appendClass(type); + type = type.replace('$', '.'); + if (ObjectHelper.isEmpty(exp)) { + exp = "null"; + } + + remainder = StringHelper.after(remainder, ")"); + if (ObjectHelper.isNotEmpty(remainder)) { + boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(remainder); + if (invalid) { + throw new SimpleParserException( + "Valid syntax: ${convertTo(type).OGNL} or ${convertTo(exp,type).OGNL} was: " + function, index); + } + ognl = ognlCodeMethods(remainder, type); + } + + String code = "Object value = " + exp + ";\n return convertTo(exchange, " + type + ", value)"; + if (ognl != null) { + code += ognl; + } + code += ";"; + return code; + } + + private static String codeThrowException(String remainder, String function, int index) { + String msg; + String type = "IllegalArgumentException"; + String values = StringHelper.before(remainder, ")"); + if (values == null || ObjectHelper.isEmpty(values)) { + throw new SimpleParserException( + "Valid syntax: ${throwException(msg)} or ${throwException(msg,type)} was: " + function, index); + } + if (values.contains(",")) { + String[] tokens = codeSplitSafe(values, ',', true, true); + if (tokens.length > 2) { + throw new SimpleParserException( + "Valid syntax: ${throwException(msg)} or ${throwException(msg,type)} was: " + function, index); + } + msg = tokens[0]; + type = tokens[1]; + } else { + msg = values.trim(); + } + msg = StringHelper.removeLeadingAndEndingQuotes(msg); + msg = StringQuoteHelper.doubleQuote(msg); + type = appendClass(type); + type = type.replace('$', '.'); + return "return throwException(exchange, " + msg + ", " + type + ");"; + } + + private static String codeHash(String remainder, String function, int index) { + String values = StringHelper.beforeLast(remainder, ")"); + if (values == null || ObjectHelper.isEmpty(values)) { + throw new SimpleParserException( + "Valid syntax: ${hash(value,algorithm)} or ${hash(value)} was: " + function, index); + } + String[] tokens = codeSplitSafe(values, ',', true, true); + if (tokens.length > 2) { + throw new SimpleParserException( + "Valid syntax: ${hash(value,algorithm)} or ${hash(value)} was: " + function, index); + } + for (int i = 0; i < tokens.length; i++) { + String s = tokens[i]; + if (StringHelper.isSingleQuoted(s)) { + s = StringHelper.removeLeadingAndEndingQuotes(s); + s = StringQuoteHelper.doubleQuote(s); + tokens[i] = s; + } + } + String algo = "\"SHA-256\""; + if (tokens.length == 2) { + algo = tokens[1]; + if (!StringHelper.isQuoted(algo)) { + algo = StringQuoteHelper.doubleQuote(algo); + } + } + return "var val = " + tokens[0] + ";\n return hash(exchange, val, " + algo + ");"; + } + + private static String codeUuid(String remainder, String function) { + String generator = StringHelper.between(remainder, "(", ")"); + if (generator == null) { + generator = "default"; + } + StringBuilder sb = new StringBuilder(128); + if ("classic".equals(generator)) { + sb.append(" UuidGenerator uuid = new org.apache.camel.support.ClassicUuidGenerator();\n"); + sb.append("return uuid.generateUuid();"); + } else if ("short".equals(generator)) { + sb.append(" UuidGenerator uuid = new org.apache.camel.support.ShortUuidGenerator();\n"); + sb.append("return uuid.generateUuid();"); + } else if ("simple".equals(generator)) { + sb.append(" UuidGenerator uuid = new org.apache.camel.support.SimpleUuidGenerator();\n"); + sb.append("return uuid.generateUuid();"); + } else if ("default".equals(generator)) { + sb.append(" UuidGenerator uuid = new org.apache.camel.support.DefaultUuidGenerator();\n"); + sb.append("return uuid.generateUuid();"); + } else if ("random".equals(generator)) { + sb.append(" UuidGenerator uuid = new org.apache.camel.support.RandomUuidGenerator();\n"); + sb.append("return uuid.generateUuid();"); + } else { + generator = StringQuoteHelper.doubleQuote(generator); + sb.append("if (uuid == null) uuid = customUuidGenerator(exchange, ").append(generator) + .append("); return uuid.generateUuid();"); + } + return sb.toString(); + } + + private static String codeIif(String remainder, String function, int index) { + String values = StringHelper.beforeLast(remainder, ")"); + if (values == null || ObjectHelper.isEmpty(values)) { + throw new SimpleParserException( + "Valid syntax: ${iif(predicate,trueExpression,falseExpression)} was: " + function, index); + } + String[] tokens = codeSplitSafe(values, ',', true, true); + if (tokens.length != 3) { + throw new SimpleParserException( + "Valid syntax: ${iif(predicate,trueExpression,falseExpression)} was: " + function, index); + } + for (int i = 0; i < 3; i++) { + String s = tokens[i]; + if (StringHelper.isSingleQuoted(s)) { + s = StringHelper.removeLeadingAndEndingQuotes(s); + s = StringQuoteHelper.doubleQuote(s); + tokens[i] = s; + } + } + return "Object o = " + tokens[0] + + ";\n boolean b = convertTo(exchange, boolean.class, o);\n return b ? " + + tokens[1] + " : " + tokens[2]; + } +} diff --git a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/DirectFunctionFactoryTest.java b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/DirectFunctionFactoryTest.java new file mode 100644 index 0000000000000..e508686261a10 --- /dev/null +++ b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/DirectFunctionFactoryTest.java @@ -0,0 +1,181 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.language.simple.functions; + +import org.apache.camel.Exchange; +import org.apache.camel.spi.SimpleLanguageFunctionFactory; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +public class DirectFunctionFactoryTest extends AbstractSimpleFunctionFactoryTestSupport { + + @Override + protected SimpleLanguageFunctionFactory createFactory() { + return new DirectFunctionFactory(); + } + + // --- expression evaluation --- + + @Test + public void testId() { + assertNotNull(evaluate("id", String.class)); + } + + @Test + public void testExchangeId() { + assertEquals(exchange.getExchangeId(), evaluate("exchangeId", String.class)); + } + + @Test + public void testExchange() { + assertSame(exchange, evaluate("exchange", Exchange.class)); + } + + @Test + public void testExceptionNull() { + assertNull(evaluate("exception")); + } + + @Test + public void testExceptionMessage() { + exchange.setException(new IllegalStateException("boom")); + assertEquals("boom", evaluate("exception.message", String.class)); + } + + @Test + public void testExceptionStacktrace() { + exchange.setException(new IllegalStateException("boom")); + String trace = evaluate("exception.stacktrace", String.class); + assertNotNull(trace); + } + + @Test + public void testThreadId() { + assertNotNull(evaluate("threadId")); + } + + @Test + public void testThreadName() { + assertNotNull(evaluate("threadName", String.class)); + } + + @Test + public void testHostname() { + assertNotNull(evaluate("hostname", String.class)); + } + + @Test + public void testCamelId() { + assertEquals(context.getName(), evaluate("camelId", String.class)); + } + + @Test + public void testNull() { + assertNull(evaluate("null")); + } + + // --- code generation --- + + @Test + public void testCreateCodeId() { + assertEquals("message.getMessageId()", createCode("id")); + } + + @Test + public void testCreateCodeMessageTimestamp() { + assertEquals("message.getMessageTimestamp()", createCode("messageTimestamp")); + } + + @Test + public void testCreateCodeExchangeId() { + assertEquals("exchange.getExchangeId()", createCode("exchangeId")); + } + + @Test + public void testCreateCodeExchange() { + assertEquals("exchange", createCode("exchange")); + } + + @Test + public void testCreateCodeLogExchange() { + assertEquals("logExchange(exchange)", createCode("logExchange")); + } + + @Test + public void testCreateCodeException() { + assertEquals("exception(exchange)", createCode("exception")); + } + + @Test + public void testCreateCodeExceptionMessage() { + assertEquals("exceptionMessage(exchange)", createCode("exception.message")); + } + + @Test + public void testCreateCodeExceptionStacktrace() { + assertEquals("exceptionStacktrace(exchange)", createCode("exception.stacktrace")); + } + + @Test + public void testCreateCodeThreadId() { + assertEquals("threadId()", createCode("threadId")); + } + + @Test + public void testCreateCodeThreadName() { + assertEquals("threadName()", createCode("threadName")); + } + + @Test + public void testCreateCodeHostname() { + assertEquals("hostName()", createCode("hostname")); + } + + @Test + public void testCreateCodeCamelId() { + assertEquals("context.getName()", createCode("camelId")); + } + + @Test + public void testCreateCodeRouteId() { + assertEquals("routeId(exchange)", createCode("routeId")); + } + + @Test + public void testCreateCodeFromRouteId() { + assertEquals("fromRouteId(exchange)", createCode("fromRouteId")); + } + + @Test + public void testCreateCodeStepId() { + assertEquals("stepId(exchange)", createCode("stepId")); + } + + @Test + public void testCreateCodeNull() { + assertEquals("null", createCode("null")); + } + + @Test + public void testCreateCodeUnknown() { + assertNull(createFactory().createCode(context, "unknown", 0)); + } +} diff --git a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/MiscFunctionFactoryTest.java b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/MiscFunctionFactoryTest.java new file mode 100644 index 0000000000000..a5be49e28d1b6 --- /dev/null +++ b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/MiscFunctionFactoryTest.java @@ -0,0 +1,266 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.language.simple.functions; + +import java.util.List; +import java.util.Map; + +import org.apache.camel.spi.SimpleLanguageFunctionFactory; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class MiscFunctionFactoryTest extends AbstractSimpleFunctionFactoryTestSupport { + + @Override + protected SimpleLanguageFunctionFactory createFactory() { + return new MiscFunctionFactory(); + } + + // --- isEmpty --- + + @Test + public void testIsEmptyTrue() { + exchange.getIn().setBody(""); + assertTrue((Boolean) evaluate("isEmpty(${body})")); + } + + @Test + public void testIsEmptyFalse() { + exchange.getIn().setBody("hello"); + assertFalse((Boolean) evaluate("isEmpty(${body})")); + } + + // --- isAlpha --- + + @Test + public void testIsAlphaTrue() { + exchange.getIn().setBody("hello"); + assertTrue((Boolean) evaluate("isAlpha(${body})")); + } + + @Test + public void testIsAlphaFalse() { + exchange.getIn().setBody("hello123"); + assertFalse((Boolean) evaluate("isAlpha(${body})")); + } + + // --- isAlphaNumeric --- + + @Test + public void testIsAlphaNumericTrue() { + exchange.getIn().setBody("hello123"); + assertTrue((Boolean) evaluate("isAlphaNumeric(${body})")); + } + + @Test + public void testIsAlphaNumericFalse() { + exchange.getIn().setBody("hello 123"); + assertFalse((Boolean) evaluate("isAlphaNumeric(${body})")); + } + + // --- isNumeric --- + + @Test + public void testIsNumericTrue() { + exchange.getIn().setBody("123"); + assertTrue((Boolean) evaluate("isNumeric(${body})")); + } + + @Test + public void testIsNumericFalse() { + exchange.getIn().setBody("abc"); + assertFalse((Boolean) evaluate("isNumeric(${body})")); + } + + // --- not --- + + @Test + public void testNotEmpty() { + exchange.getIn().setBody(""); + assertTrue((Boolean) evaluate("not(${body})")); + } + + @Test + public void testNotNonEmpty() { + exchange.getIn().setBody("hello"); + assertFalse((Boolean) evaluate("not(${body})")); + } + + // --- convertTo --- + + @Test + public void testConvertToString() { + exchange.getIn().setBody(42); + assertEquals("42", evaluate("convertTo(java.lang.String)", String.class)); + } + + @Test + public void testConvertToInteger() { + exchange.getIn().setBody("42"); + assertEquals(42, evaluate("convertTo(java.lang.Integer)", Integer.class)); + } + + // --- uuid --- + + @Test + public void testUuid() { + assertNotNull(evaluate("uuid", String.class)); + } + + // --- hash --- + + @Test + public void testHash() { + exchange.getIn().setBody("hello"); + String result = evaluate("hash(${body})", String.class); + assertNotNull(result); + assertFalse(result.isEmpty()); + } + + // --- empty / newEmpty --- + + @Test + public void testEmpty() { + Object result = evaluate("empty(List)"); + assertInstanceOf(List.class, result); + assertTrue(((List) result).isEmpty()); + } + + @Test + public void testNewEmpty() { + Object result = evaluate("newEmpty(Map)"); + assertInstanceOf(Map.class, result); + assertTrue(((Map) result).isEmpty()); + } + + // --- iif --- + + @Test + public void testIifTrue() { + exchange.getIn().setBody(true); + assertEquals("'yes'", evaluate("iif(${body},'yes','no')", String.class)); + } + + @Test + public void testIifFalse() { + exchange.getIn().setBody(false); + assertEquals("'no'", evaluate("iif(${body},'yes','no')", String.class)); + } + + // --- code generation --- + + @Test + public void testCreateCodeIsEmpty() { + assertEquals("Object o = ${body};\n return isEmpty(exchange, o);", createCode("isEmpty(${body})")); + } + + @Test + public void testCreateCodeIsAlpha() { + assertEquals("Object o = ${body};\n return isAlpha(exchange, o);", createCode("isAlpha(${body})")); + } + + @Test + public void testCreateCodeIsAlphaNumeric() { + assertEquals("Object o = ${body};\n return isAlphaNumeric(exchange, o);", + createCode("isAlphaNumeric(${body})")); + } + + @Test + public void testCreateCodeIsNumeric() { + assertEquals("Object o = ${body};\n return isNumeric(exchange, o);", createCode("isNumeric(${body})")); + } + + @Test + public void testCreateCodeKindOfType() { + assertEquals("Object o = body;\n return kindOfType(exchange, o);", createCode("kindOfType()")); + } + + @Test + public void testCreateCodeConvertTo() { + assertEquals("Object value = body;\n return convertTo(exchange, java.lang.String.class, value);", + createCode("convertTo(java.lang.String)")); + } + + @Test + public void testCreateCodeConvertToShortName() { + assertEquals("Object value = body;\n return convertTo(exchange, String.class, value);", + createCode("convertTo(String)")); + } + + @Test + public void testCreateCodeThrowException() { + assertEquals("return throwException(exchange, \"bad input\", IllegalArgumentException.class);", + createCode("throwException('bad input')")); + } + + @Test + public void testCreateCodeMessageHistory() { + assertEquals("messageHistory(exchange, true)", createCode("messageHistory")); + } + + @Test + public void testCreateCodeMessageHistoryFalse() { + assertEquals("messageHistory(exchange, false)", createCode("messageHistory(false)")); + } + + @Test + public void testCreateCodeEmpty() { + assertEquals("newEmpty(exchange, \"List\")", createCode("empty(List)")); + } + + @Test + public void testCreateCodeNewEmpty() { + assertEquals("newEmpty(exchange, \"Map\")", createCode("newEmpty(Map)")); + } + + @Test + public void testCreateCodeHash() { + assertEquals("var val = ${body};\n return hash(exchange, val, \"SHA-256\");", + createCode("hash(${body})")); + } + + @Test + public void testCreateCodeHashWithAlgorithm() { + assertEquals("var val = ${body};\n return hash(exchange, val, \"MD5\");", + createCode("hash(${body},MD5)")); + } + + @Test + public void testCreateCodeUuid() { + String code = createCode("uuid"); + assertNotNull(code); + assertTrue(code.contains("generateUuid")); + } + + @Test + public void testCreateCodeIif() { + assertEquals( + "Object o = ${body};\n boolean b = convertTo(exchange, boolean.class, o);\n return b ? \"yes\" : \"no\"", + createCode("iif(${body},'yes','no')")); + } + + @Test + public void testCreateCodeUnknown() { + assertNull(createFactory().createCode(context, "unknown", 0)); + } +} diff --git a/docs/local-build.sh b/docs/local-build.sh index cd375d8b38f76..8f07d6069f6a9 100755 --- a/docs/local-build.sh +++ b/docs/local-build.sh @@ -17,8 +17,10 @@ # limitations under the License. # -CW=./../../camel-website -LOCAL=./../camel +CW="./../../camel-website" -cd $CW || (echo 'camel-website not in expected location $CW' && exit) -./antora-local-build.sh $LOCAL $* +parent_path="${PWD%/*}" +LOCAL="./../${parent_path##*/}" + +cd $CW || (echo "camel-website not in expected location $CW" && exit) +./antora-local-build.sh "$LOCAL" "$@"