This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch jq
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 399fdba662b5d26a0269f396ccb6761c1762edfa
Author: Claus Ibsen <claus.ib...@gmail.com>
AuthorDate: Mon Feb 5 07:26:44 2024 +0100

    CAMEL-20386: camel-jq - Add @Jq annotation for bean parameter binding as 
the other languages. Also fix so null result is returned as null, and not as 
'null' string.
---
 .../main/java/org/apache/camel/language/jq/Jq.java | 45 +++++++++----
 .../language/jq/JqAnnotationExpressionFactory.java | 73 ++++++++++++++++++++++
 .../org/apache/camel/language/jq/JqExpression.java | 10 ++-
 .../org/apache/camel/language/jq/JqBeanTest.java   | 73 ++++++++++++++++++++++
 .../apache/camel/language/jq/JqExpressionTest.java | 14 +++--
 5 files changed, 197 insertions(+), 18 deletions(-)

diff --git 
a/components/camel-jq/src/main/java/org/apache/camel/language/jq/Jq.java 
b/components/camel-jq/src/main/java/org/apache/camel/language/jq/Jq.java
index 1eda7478817..307cbcdfccc 100644
--- a/components/camel-jq/src/main/java/org/apache/camel/language/jq/Jq.java
+++ b/components/camel-jq/src/main/java/org/apache/camel/language/jq/Jq.java
@@ -16,21 +16,42 @@
  */
 package org.apache.camel.language.jq;
 
-import org.apache.camel.CamelContext;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
-public final class Jq {
+import org.apache.camel.support.language.LanguageAnnotation;
 
-    private Jq() {
-    }
+/**
+ * An annotation used to inject a JQ expression into a method parameter when 
using Bean Integration.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
+@LanguageAnnotation(language = "jq", factory = 
JqAnnotationExpressionFactory.class)
+public @interface Jq {
+
+    String value();
+
+    /**
+     * The desired return type.
+     */
+    Class<?> resultType() default Object.class;
 
-    public static JqExpression expression(String expression) {
-        return new JqExpression(expression);
-    }
+    /**
+     * The name of the variable we want to apply the expression to.
+     */
+    String variableName() default "";
 
-    public static JqExpression expression(CamelContext context, String 
expression) {
-        JqExpression answer = new JqExpression(expression);
-        answer.init(context);
-        return answer;
-    }
+    /**
+     * The name of the header we want to apply the expression to.
+     */
+    String headerName() default "";
 
+    /**
+     * The name of the exchange property we want to apply the expression to.
+     */
+    String propertyName() default "";
 }
diff --git 
a/components/camel-jq/src/main/java/org/apache/camel/language/jq/JqAnnotationExpressionFactory.java
 
b/components/camel-jq/src/main/java/org/apache/camel/language/jq/JqAnnotationExpressionFactory.java
new file mode 100644
index 00000000000..20ecba2ddb3
--- /dev/null
+++ 
b/components/camel-jq/src/main/java/org/apache/camel/language/jq/JqAnnotationExpressionFactory.java
@@ -0,0 +1,73 @@
+/*
+ * 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.jq;
+
+import java.lang.annotation.Annotation;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Expression;
+import org.apache.camel.support.builder.ExpressionBuilder;
+import org.apache.camel.support.language.DefaultAnnotationExpressionFactory;
+import org.apache.camel.support.language.LanguageAnnotation;
+import org.apache.camel.util.ObjectHelper;
+
+public class JqAnnotationExpressionFactory extends 
DefaultAnnotationExpressionFactory {
+
+    @Override
+    public Expression createExpression(
+            CamelContext camelContext, Annotation annotation,
+            LanguageAnnotation languageAnnotation, Class<?> 
expressionReturnType) {
+
+        String expression = getExpressionFromAnnotation(annotation);
+        JqExpression answer = new JqExpression(expression);
+
+        Class<?> resultType = getResultType(annotation);
+        if (resultType.equals(Object.class)) {
+            resultType = expressionReturnType;
+        }
+        if (resultType != null) {
+            answer.setResultType(resultType);
+        }
+
+        if (annotation instanceof Jq) {
+            Jq jqAnnotation = (Jq) annotation;
+
+            String variableName = null;
+            String headerName = null;
+            String propertyName = null;
+            if (ObjectHelper.isNotEmpty(jqAnnotation.variableName())) {
+                variableName = jqAnnotation.variableName();
+            }
+            if (ObjectHelper.isNotEmpty(jqAnnotation.headerName())) {
+                headerName = jqAnnotation.headerName();
+            }
+            if (ObjectHelper.isNotEmpty(jqAnnotation.propertyName())) {
+                propertyName = jqAnnotation.propertyName();
+            }
+            if (variableName != null || headerName != null || propertyName != 
null) {
+                
answer.setSource(ExpressionBuilder.singleInputExpression(variableName, 
headerName, propertyName));
+            }
+        }
+
+        answer.init(camelContext);
+        return answer;
+    }
+
+    private Class<?> getResultType(Annotation annotation) {
+        return (Class<?>) getAnnotationObjectValue(annotation, "resultType");
+    }
+}
diff --git 
a/components/camel-jq/src/main/java/org/apache/camel/language/jq/JqExpression.java
 
b/components/camel-jq/src/main/java/org/apache/camel/language/jq/JqExpression.java
index 1c9e3f6b068..6f0029811d2 100644
--- 
a/components/camel-jq/src/main/java/org/apache/camel/language/jq/JqExpression.java
+++ 
b/components/camel-jq/src/main/java/org/apache/camel/language/jq/JqExpression.java
@@ -156,11 +156,16 @@ public class JqExpression extends ExpressionAdapter 
implements ExpressionResultT
             this.query.apply(scope, payload, outputs::add);
 
             if (outputs.size() == 1) {
+                JsonNode out = outputs.get(0);
+                // special if null
+                if (out.isNull()) {
+                    return null;
+                }
+
                 // no need to convert output
                 if (resultType == JsonNode.class) {
-                    return outputs.get(0);
+                    return out;
                 }
-
                 return this.typeConverter.convertTo(resultType, exchange, 
outputs.get(0));
             } else if (outputs.size() > 1) {
                 // no need to convert outputs
@@ -169,6 +174,7 @@ public class JqExpression extends ExpressionAdapter 
implements ExpressionResultT
                 }
 
                 return outputs.stream()
+                        .filter(o -> !o.isNull()) // skip null
                         .map(item -> this.typeConverter.convertTo(resultType, 
exchange, item))
                         .collect(Collectors.toList());
             }
diff --git 
a/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqBeanTest.java
 
b/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqBeanTest.java
new file mode 100644
index 00000000000..8fadf0f8bdb
--- /dev/null
+++ 
b/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqBeanTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.jq;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class JqBeanTest extends CamelTestSupport {
+
+    @Test
+    public void testFullName() throws Exception {
+        String json = "{\"person\" : {\"firstname\" : \"foo\", \"middlename\" 
: \"foo2\", \"lastname\" : \"bar\"}}";
+        getMockEndpoint("mock:result").expectedBodiesReceived("foo foo2 bar");
+        template.sendBody("direct:start", json);
+        MockEndpoint.assertIsSatisfied(context);
+    }
+
+    @Test
+    public void testFullNameTwo() throws Exception {
+        String json = "{\"person\" : {\"firstname\" : \"foo\", \"middlename\" 
: \"foo2\", \"lastname\" : \"bar\"}}";
+        String json2 = "{\"person\" : {\"firstname\" : \"bar\", \"middlename\" 
: \"bar2\", \"lastname\" : \"foo\"}}";
+        getMockEndpoint("mock:result").expectedBodiesReceived("foo foo2 bar", 
"bar bar2 foo");
+        template.sendBody("direct:start", json);
+        template.sendBody("direct:start", json2);
+        MockEndpoint.assertIsSatisfied(context);
+    }
+
+    @Test
+    public void testFirstAndLastName() throws Exception {
+        String json = "{\"person\" : {\"firstname\" : \"foo\", \"lastname\" : 
\"bar\"}}";
+        getMockEndpoint("mock:result").expectedBodiesReceived("foo bar");
+        template.sendBody("direct:start", json);
+        MockEndpoint.assertIsSatisfied(context);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                
from("direct:start").bean(FullNameBean.class).to("mock:result");
+            }
+        };
+    }
+
+    protected static class FullNameBean {
+        public static String getName(
+                @Jq(".person.firstname") String first,
+                @Jq(value = ".person.middlename") String middle,
+                @Jq(".person.lastname") String last) {
+            if (middle != null) {
+                return first + " " + middle + " " + last;
+            }
+            return first + " " + last;
+        }
+    }
+}
diff --git 
a/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionTest.java
 
b/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionTest.java
index 9e7fe8928cd..f35f446273f 100644
--- 
a/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionTest.java
+++ 
b/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionTest.java
@@ -121,10 +121,10 @@ public class JqExpressionTest {
             exchange.getMessage().setBody(node);
             exchange.getMessage().setHeader("CommitterName", "Andrea");
 
-            assertThat(Jq.expression(context, 
"has(\"baz\")").matches(exchange)).isTrue();
-            assertThat(Jq.expression(context, 
"has(\"bar\")").matches(exchange)).isFalse();
-            assertThat(Jq.expression(context, "header(\"CommitterName\") == 
\"Andrea\"").matches(exchange)).isTrue();
-            assertThat(Jq.expression(context, "header(\"CommitterName\") != 
\"Andrea\"").matches(exchange)).isFalse();
+            assertThat(jq(context, "has(\"baz\")").matches(exchange)).isTrue();
+            assertThat(jq(context, 
"has(\"bar\")").matches(exchange)).isFalse();
+            assertThat(jq(context, "header(\"CommitterName\") == 
\"Andrea\"").matches(exchange)).isTrue();
+            assertThat(jq(context, "header(\"CommitterName\") != 
\"Andrea\"").matches(exchange)).isFalse();
         }
     }
 
@@ -262,4 +262,10 @@ public class JqExpressionTest {
         }
     }
 
+    private static JqExpression jq(CamelContext context, String expression) {
+        JqExpression answer = new JqExpression(expression);
+        answer.init(context);
+        return answer;
+    }
+
 }

Reply via email to