This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 03a8eb855513 CAMEL-22894: Extract jq/jsonpath/xpath/simpleJsonpath
Simple functions into QueryLanguageFunctionFactory
03a8eb855513 is described below
commit 03a8eb8555132d83f49c88de2cae672124e52600
Author: Adriano Machado <[email protected]>
AuthorDate: Wed May 27 12:01:00 2026 -0400
CAMEL-22894: Extract jq/jsonpath/xpath/simpleJsonpath Simple functions into
QueryLanguageFunctionFactory
Extract the four query language functions (jq, jsonpath, xpath,
simpleJsonpath) from
SimpleFunctionExpression.createSimpleCustomLanguage() into a new
QueryLanguageFunctionFactory,
registered in SimpleFunctionDispatcher.BUILT_INS. Also removes dead code in
PropertiesFunctionFactory.createCode() where a parts.length > 2 guard could
never be true.
Closes #23566
---
.../language/simple/SimpleFunctionDispatcher.java | 2 +
.../simple/ast/SimpleFunctionExpression.java | 99 ------------------
.../functions/PropertiesFunctionFactory.java | 4 -
.../functions/QueryLanguageFunctionFactory.java | 111 ++++++++++++++++++++
.../QueryLanguageFunctionFactoryTest.java | 116 +++++++++++++++++++++
5 files changed, 229 insertions(+), 103 deletions(-)
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 3f5c2256a6a4..f4c9af9cbbd4 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
@@ -32,6 +32,7 @@ import
org.apache.camel.language.simple.functions.MessageFunctionFactory;
import org.apache.camel.language.simple.functions.MiscFunctionFactory;
import org.apache.camel.language.simple.functions.OutputFunctionFactory;
import org.apache.camel.language.simple.functions.PropertiesFunctionFactory;
+import org.apache.camel.language.simple.functions.QueryLanguageFunctionFactory;
import org.apache.camel.language.simple.functions.RandomFunctionFactory;
import org.apache.camel.language.simple.functions.SkipFunctionFactory;
import org.apache.camel.language.simple.functions.StringFunctionFactory;
@@ -71,6 +72,7 @@ public final class SimpleFunctionDispatcher {
new MathFunctionFactory(),
new StringFunctionFactory(),
new CollectionFunctionFactory(),
+ new QueryLanguageFunctionFactory(),
new MiscFunctionFactory(),
new SystemFunctionFactory(),
new PropertiesFunctionFactory(),
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 408897ceff38..ec5b0ea65697 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
@@ -118,12 +118,6 @@ public class SimpleFunctionExpression extends
LiteralExpression {
return answer;
}
- // custom languages
- answer = createSimpleCustomLanguage(function, strict);
- if (answer != null) {
- return answer;
- }
-
// camelContext OGNL
String remainder = ifStartsWithReturnRemainder("camelContext",
function);
if (remainder != null) {
@@ -317,99 +311,6 @@ public class SimpleFunctionExpression extends
LiteralExpression {
return null;
}
- private Expression createSimpleCustomLanguage(String function, boolean
strict) {
- // jq
- String remainder = ifStartsWithReturnRemainder("jq(", function);
- if (remainder != null) {
- String exp = StringHelper.beforeLast(remainder, ")");
- if (exp == null) {
- throw new SimpleParserException("Valid syntax: ${jq(exp)} was:
" + function, token.getIndex());
- }
- exp = StringHelper.removeLeadingAndEndingQuotes(exp);
- if (exp.startsWith("header:") || exp.startsWith("property:") ||
exp.startsWith("exchangeProperty:")
- || exp.startsWith("variable:")) {
- String input = StringHelper.before(exp, ",");
- exp = StringHelper.after(exp, ",");
- if (input != null) {
- input = input.trim();
- }
- if (exp != null) {
- exp = exp.trim();
- }
- return ExpressionBuilder.singleInputLanguageExpression("jq",
exp, input);
- }
- return ExpressionBuilder.languageExpression("jq", exp);
- }
- // simple-jsonpath
- remainder = ifStartsWithReturnRemainder("simpleJsonpath(", function);
- if (remainder != null) {
- String exp = StringHelper.beforeLast(remainder, ")");
- if (exp == null) {
- throw new SimpleParserException(
- "Valid syntax: ${simpleJsonpath(exp)} was: " +
function, token.getIndex());
- }
- String input = null;
- exp = StringHelper.removeLeadingAndEndingQuotes(exp);
- if (exp.startsWith("header:") || exp.startsWith("property:") ||
exp.startsWith("exchangeProperty:")
- || exp.startsWith("variable:")) {
- input = StringHelper.before(exp, ",");
- exp = StringHelper.after(exp, ",");
- if (input != null) {
- input = input.trim();
- }
- if (exp != null) {
- exp = exp.trim();
- }
- }
- return SimpleExpressionBuilder.simpleJsonPathExpression(input,
exp);
- }
- // jsonpath
- remainder = ifStartsWithReturnRemainder("jsonpath(", function);
- if (remainder != null) {
- String exp = StringHelper.beforeLast(remainder, ")");
- if (exp == null) {
- throw new SimpleParserException("Valid syntax:
${jsonpath(exp)} was: " + function, token.getIndex());
- }
- exp = StringHelper.removeLeadingAndEndingQuotes(exp);
- if (exp.startsWith("header:") || exp.startsWith("property:") ||
exp.startsWith("exchangeProperty:")
- || exp.startsWith("variable:")) {
- String input = StringHelper.before(exp, ",");
- exp = StringHelper.after(exp, ",");
- if (input != null) {
- input = input.trim();
- }
- if (exp != null) {
- exp = exp.trim();
- }
- return
ExpressionBuilder.singleInputLanguageExpression("jsonpath", exp, input);
- }
- return ExpressionBuilder.languageExpression("jsonpath", exp);
- }
- remainder = ifStartsWithReturnRemainder("xpath(", function);
- if (remainder != null) {
- String exp = StringHelper.beforeLast(remainder, ")");
- if (exp == null) {
- throw new SimpleParserException("Valid syntax: ${xpath(exp)}
was: " + function, token.getIndex());
- }
- exp = StringHelper.removeLeadingAndEndingQuotes(exp);
- if (exp.startsWith("header:") || exp.startsWith("property:") ||
exp.startsWith("exchangeProperty:")
- || exp.startsWith("variable:")) {
- String input = StringHelper.before(exp, ",");
- exp = StringHelper.after(exp, ",");
- if (input != null) {
- input = input.trim();
- }
- if (exp != null) {
- exp = exp.trim();
- }
- return
ExpressionBuilder.singleInputLanguageExpression("xpath", exp, input);
- }
- return ExpressionBuilder.languageExpression("xpath", exp);
- }
-
- return null;
- }
-
private Expression createSimpleFileExpression(String remainder, boolean
strict) {
if (ObjectHelper.equal(remainder, "name")) {
return SimpleExpressionBuilder.fileNameExpression();
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/PropertiesFunctionFactory.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/PropertiesFunctionFactory.java
index ccbff2b776ce..6f1595b58b9a 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/PropertiesFunctionFactory.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/PropertiesFunctionFactory.java
@@ -18,7 +18,6 @@ package org.apache.camel.language.simple.functions;
import org.apache.camel.CamelContext;
import org.apache.camel.Expression;
-import org.apache.camel.language.simple.types.SimpleParserException;
import org.apache.camel.spi.SimpleLanguageFunctionFactory;
import org.apache.camel.support.builder.ExpressionBuilder;
@@ -64,9 +63,6 @@ public final class PropertiesFunctionFactory implements
SimpleLanguageFunctionFa
String remainder = ifStartsWithReturnRemainder("properties:",
function);
if (remainder != null) {
String[] parts = remainder.split(":", 2);
- if (parts.length > 2) {
- throw new SimpleParserException("Valid syntax:
${properties:key[:default]} was: " + function, index);
- }
String key = parts[0].trim();
if (parts.length >= 2) {
return "properties(exchange, \"" + key + "\", \"" +
parts[1].trim() + "\")";
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/QueryLanguageFunctionFactory.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/QueryLanguageFunctionFactory.java
new file mode 100644
index 000000000000..fe681c3a0c8c
--- /dev/null
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/QueryLanguageFunctionFactory.java
@@ -0,0 +1,111 @@
+/*
+ * 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.support.builder.ExpressionBuilder;
+import org.apache.camel.util.StringHelper;
+
+import static
org.apache.camel.language.simple.SimpleFunctionHelper.ifStartsWithReturnRemainder;
+
+/**
+ * Built-in Simple functions that delegate evaluation to another Camel
language: {@code ${jq(exp)}},
+ * {@code ${jsonpath(exp)}}, {@code ${xpath(exp)}}, {@code
${simpleJsonpath(exp)}}.
+ * <p>
+ * All four functions support an optional input-source qualifier so the
language operates on a header, exchange
+ * property, or variable rather than the body: e.g. {@code
${jq(header:myHeader,.name)}}.
+ * <p>
+ * CSimple code generation is not supported for these functions (returns
{@code null}).
+ */
+public final class QueryLanguageFunctionFactory implements
SimpleLanguageFunctionFactory {
+
+ @Override
+ public Expression createFunction(CamelContext camelContext, String
function, int index) {
+ String remainder;
+
+ remainder = ifStartsWithReturnRemainder("jq(", function);
+ if (remainder != null) {
+ return delegateLanguageExpression("jq", remainder, function,
index);
+ }
+
+ remainder = ifStartsWithReturnRemainder("simpleJsonpath(", function);
+ if (remainder != null) {
+ return createSimpleJsonpathExpression(remainder, function, index);
+ }
+
+ remainder = ifStartsWithReturnRemainder("jsonpath(", function);
+ if (remainder != null) {
+ return delegateLanguageExpression("jsonpath", remainder, function,
index);
+ }
+
+ remainder = ifStartsWithReturnRemainder("xpath(", function);
+ if (remainder != null) {
+ return delegateLanguageExpression("xpath", remainder, function,
index);
+ }
+
+ return null;
+ }
+
+ private static Expression delegateLanguageExpression(String name, String
remainder, String function, int index) {
+ String exp = StringHelper.beforeLast(remainder, ")");
+ if (exp == null) {
+ throw new SimpleParserException("Valid syntax: ${" + name +
"(exp)} was: " + function, index);
+ }
+ exp = StringHelper.removeLeadingAndEndingQuotes(exp);
+ if (hasInputSource(exp)) {
+ String input = StringHelper.before(exp, ",");
+ exp = StringHelper.after(exp, ",");
+ if (input != null) {
+ input = input.trim();
+ }
+ if (exp != null) {
+ exp = exp.trim();
+ }
+ return ExpressionBuilder.singleInputLanguageExpression(name, exp,
input);
+ }
+ return ExpressionBuilder.languageExpression(name, exp);
+ }
+
+ private static Expression createSimpleJsonpathExpression(String remainder,
String function, int index) {
+ String exp = StringHelper.beforeLast(remainder, ")");
+ if (exp == null) {
+ throw new SimpleParserException("Valid syntax:
${simpleJsonpath(exp)} was: " + function, index);
+ }
+ String input = null;
+ exp = StringHelper.removeLeadingAndEndingQuotes(exp);
+ if (hasInputSource(exp)) {
+ input = StringHelper.before(exp, ",");
+ exp = StringHelper.after(exp, ",");
+ if (input != null) {
+ input = input.trim();
+ }
+ if (exp != null) {
+ exp = exp.trim();
+ }
+ }
+ return SimpleExpressionBuilder.simpleJsonPathExpression(input, exp);
+ }
+
+ private static boolean hasInputSource(String exp) {
+ return exp.startsWith("header:") || exp.startsWith("property:")
+ || exp.startsWith("exchangeProperty:") ||
exp.startsWith("variable:");
+ }
+}
diff --git
a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/QueryLanguageFunctionFactoryTest.java
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/QueryLanguageFunctionFactoryTest.java
new file mode 100644
index 000000000000..6f49c5e11f85
--- /dev/null
+++
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/QueryLanguageFunctionFactoryTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.Expression;
+import org.apache.camel.language.simple.types.SimpleParserException;
+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.assertThrows;
+
+public class QueryLanguageFunctionFactoryTest extends
AbstractSimpleFunctionFactoryTestSupport {
+
+ @Override
+ protected SimpleLanguageFunctionFactory createFactory() {
+ return new QueryLanguageFunctionFactory();
+ }
+
+ // --- xpath ---
+
+ @Test
+ public void testXpathBody() {
+ exchange.getIn().setBody("<root><name>World</name></root>");
+ assertEquals("World", evaluate("xpath('/root/name/text()')",
String.class));
+ }
+
+ @Test
+ public void testXpathWithHeaderInput() {
+ exchange.getIn().setHeader("myHdr", "<root><val>42</val></root>");
+ assertEquals("42", evaluate("xpath('header:myHdr,/root/val/text()')",
String.class));
+ }
+
+ @Test
+ public void testXpathMissingClosingParen() {
+ assertThrows(SimpleParserException.class,
+ () -> createFactory().createFunction(context,
"xpath('unclosed", 0));
+ }
+
+ // --- jq / jsonpath / simpleJsonpath: factory creates expressions
(languages not on classpath in core) ---
+
+ @Test
+ public void testJqCreatesFunctionExpression() {
+ Expression exp = createFactory().createFunction(context,
"jq('.name')", 0);
+ assertNotNull(exp);
+ }
+
+ @Test
+ public void testJqWithHeaderInputCreatesExpression() {
+ Expression exp = createFactory().createFunction(context,
"jq('header:myHdr,.name')", 0);
+ assertNotNull(exp);
+ }
+
+ @Test
+ public void testJqMissingClosingParen() {
+ assertThrows(SimpleParserException.class,
+ () -> createFactory().createFunction(context, "jq('unclosed",
0));
+ }
+
+ @Test
+ public void testJsonpathCreatesExpression() {
+ Expression exp = createFactory().createFunction(context,
"jsonpath('$.name')", 0);
+ assertNotNull(exp);
+ }
+
+ @Test
+ public void testJsonpathMissingClosingParen() {
+ assertThrows(SimpleParserException.class,
+ () -> createFactory().createFunction(context,
"jsonpath('unclosed", 0));
+ }
+
+ @Test
+ public void testSimpleJsonpathCreatesExpression() {
+ Expression exp = createFactory().createFunction(context,
"simpleJsonpath('$.name')", 0);
+ assertNotNull(exp);
+ }
+
+ @Test
+ public void testSimpleJsonpathMissingClosingParen() {
+ assertThrows(SimpleParserException.class,
+ () -> createFactory().createFunction(context,
"simpleJsonpath('unclosed", 0));
+ }
+
+ // --- createCode returns null (no CSimple support) ---
+
+ @Test
+ public void testCreateCodeReturnsNull() {
+ assertNull(createFactory().createCode(context, "jq('.name')", 0));
+ assertNull(createFactory().createCode(context, "jsonpath('$.name')",
0));
+ assertNull(createFactory().createCode(context, "xpath('/root')", 0));
+ assertNull(createFactory().createCode(context,
"simpleJsonpath('$.name')", 0));
+ }
+
+ // --- unrecognized ---
+
+ @Test
+ public void testUnrecognizedFunction() {
+ assertNull(createFactory().createFunction(context, "unknown", 0));
+ }
+}