You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2017/01/13 11:30:51 UTC

[2/3] camel git commit: CAMEL-10702: camel-jsonpath - Have an easy way of defining predicates with a single operator instead of having to grok the half complex style of jsonpath.

CAMEL-10702: camel-jsonpath - Have an easy way of defining predicates with a single operator instead of having to grok the half complex style of jsonpath.


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/cfbe791d
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/cfbe791d
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/cfbe791d

Branch: refs/heads/master
Commit: cfbe791d6f08f5a4063f7b93555563e041adf98c
Parents: 69a19a3
Author: Claus Ibsen <da...@apache.org>
Authored: Fri Jan 13 11:33:15 2017 +0100
Committer: Claus Ibsen <da...@apache.org>
Committed: Fri Jan 13 12:30:42 2017 +0100

----------------------------------------------------------------------
 .../camel/jsonpath/JsonPathExpression.java      | 22 ++++-
 .../apache/camel/jsonpath/JsonPathLanguage.java |  2 +
 .../easypredicate/EasyPredicateOperators.java   | 96 ++++++++++++++++++++
 .../easypredicate/EasyPredicateParser.java      | 70 ++++++++++++++
 .../jsonpath/JsonPathWithEvenSimpleCBRTest.java | 80 ++++++++++++++++
 5 files changed, 268 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/cfbe791d/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathExpression.java
----------------------------------------------------------------------
diff --git a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathExpression.java b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathExpression.java
index 37eeab1..3e79f9a 100644
--- a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathExpression.java
+++ b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathExpression.java
@@ -22,6 +22,7 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.Exchange;
 import org.apache.camel.ExpressionEvaluationException;
 import org.apache.camel.ExpressionIllegalSyntaxException;
+import org.apache.camel.jsonpath.easypredicate.EasyPredicateParser;
 import org.apache.camel.support.ExpressionAdapter;
 
 public class JsonPathExpression extends ExpressionAdapter implements AfterPropertiesConfigured {
@@ -29,6 +30,7 @@ public class JsonPathExpression extends ExpressionAdapter implements AfterProper
     private final String expression;
     private JsonPathEngine engine;
 
+    private boolean predicate;
     private Class<?> resultType;
     private boolean suppressExceptions;
     private boolean allowSimple = true;
@@ -38,6 +40,17 @@ public class JsonPathExpression extends ExpressionAdapter implements AfterProper
         this.expression = expression;
     }
 
+    public boolean isPredicate() {
+        return predicate;
+    }
+
+    /**
+     * Whether to be evaluated as a predicate
+     */
+    public void setPredicate(boolean predicate) {
+        this.predicate = predicate;
+    }
+
     public Class<?> getResultType() {
         return resultType;
     }
@@ -102,10 +115,15 @@ public class JsonPathExpression extends ExpressionAdapter implements AfterProper
     }
 
     public void init() {
+        String exp = expression;
+        if (predicate) {
+            EasyPredicateParser parser = new EasyPredicateParser();
+            exp = parser.parse(expression);
+        }
         try {
-            engine = new JsonPathEngine(expression, suppressExceptions, allowSimple, options);
+            engine = new JsonPathEngine(exp, suppressExceptions, allowSimple, options);
         } catch (Exception e) {
-            throw new ExpressionIllegalSyntaxException(expression, e);
+            throw new ExpressionIllegalSyntaxException(exp, e);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/camel/blob/cfbe791d/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathLanguage.java
----------------------------------------------------------------------
diff --git a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathLanguage.java b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathLanguage.java
index 297823b..d2e4d0b 100644
--- a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathLanguage.java
+++ b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathLanguage.java
@@ -58,6 +58,7 @@ public class JsonPathLanguage extends LanguageSupport {
     @Override
     public Predicate createPredicate(final String predicate) {
         JsonPathExpression answer = new JsonPathExpression(predicate);
+        answer.setPredicate(true);
         answer.setResultType(resultType);
         answer.setSuppressExceptions(suppressExceptions);
         answer.setOptions(options);
@@ -68,6 +69,7 @@ public class JsonPathLanguage extends LanguageSupport {
     @Override
     public Expression createExpression(final String expression) {
         JsonPathExpression answer = new JsonPathExpression(expression);
+        answer.setPredicate(false);
         answer.setResultType(resultType);
         answer.setSuppressExceptions(suppressExceptions);
         answer.setOptions(options);

http://git-wip-us.apache.org/repos/asf/camel/blob/cfbe791d/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/easypredicate/EasyPredicateOperators.java
----------------------------------------------------------------------
diff --git a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/easypredicate/EasyPredicateOperators.java b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/easypredicate/EasyPredicateOperators.java
new file mode 100644
index 0000000..4f49504
--- /dev/null
+++ b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/easypredicate/EasyPredicateOperators.java
@@ -0,0 +1,96 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.jsonpath.easypredicate;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+public class EasyPredicateOperators {
+
+    private static final String EQ = "==";
+    private static final String NE = "!=";
+    private static final String LT = "<";
+    private static final String LE = "<=";
+    private static final String GT = ">";
+    private static final String GE = ">=";
+    private static final String REG = "=~";
+    private static final String IN = "in";
+    private static final String NIN = "nin";
+    private static final String SIZE = "size";
+    private static final String EMPTY = "empty";
+
+    private static final String[] OPS = new String[]{EQ, NE, LT, LE, GT, GE, REG, IN, NIN, SIZE, EMPTY};
+
+    private static final Pattern PATTERN = Pattern.compile("\\s(" + Arrays.stream(OPS).collect(Collectors.joining("|")) + ")\\s");
+
+    /**
+     * Does the expression have any operator?
+     */
+    public static boolean hasOperator(String exp) {
+        return Arrays.stream(OPS).anyMatch(o -> exp.contains(" " + o + ""));
+    }
+
+    /**
+     * Is this an operator
+     */
+    static boolean isOperator(String exp) {
+        return Arrays.stream(OPS).anyMatch(s -> Objects.equals(s, exp));
+    }
+
+    /**
+     * Gets the operator
+     */
+    static String getOperatorAtStart(String exp) {
+        return Arrays.stream(OPS).filter(o -> exp.startsWith(" " + o + "")).findFirst().orElse(null);
+    }
+
+    public static String[] tokens(String exp) {
+        List<String> list = new ArrayList<>();
+
+        StringBuilder part = new StringBuilder();
+        for (int i = 0; i < exp.length(); i++) {
+
+            // is there a new operator
+            String s = exp.substring(i);
+            String op = getOperatorAtStart(s);
+            if (op != null) {
+                if (part.length() > 0) {
+                    list.add(part.toString());
+                    part.setLength(0);
+                }
+                list.add(op.trim());
+                // move i ahead
+                i = i + op.length() + 1;
+            } else {
+                char ch = exp.charAt(i);
+                part.append(ch);
+            }
+        }
+
+        // ant leftovers
+        if (part.length() > 0) {
+            list.add(part.toString());
+        }
+
+        return list.toArray(new String[list.size()]);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/cfbe791d/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/easypredicate/EasyPredicateParser.java
----------------------------------------------------------------------
diff --git a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/easypredicate/EasyPredicateParser.java b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/easypredicate/EasyPredicateParser.java
new file mode 100644
index 0000000..f523f46
--- /dev/null
+++ b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/easypredicate/EasyPredicateParser.java
@@ -0,0 +1,70 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.jsonpath.easypredicate;
+
+import static org.apache.camel.jsonpath.easypredicate.EasyPredicateOperators.hasOperator;
+import static org.apache.camel.jsonpath.easypredicate.EasyPredicateOperators.isOperator;
+import static org.apache.camel.jsonpath.easypredicate.EasyPredicateOperators.tokens;
+
+public class EasyPredicateParser {
+
+    public String parse(String exp) {
+        if (exp.startsWith("$")) {
+            // regular json path so skip
+            return exp;
+        }
+
+        // must have an operator
+        if (!hasOperator(exp)) {
+            return exp;
+        }
+
+        StringBuilder sb = new StringBuilder();
+
+        // grab before operator
+        String[] parts = tokens(exp);
+
+        // only support one operator currently
+        if (parts.length == 3) {
+            String prev = parts[0];
+            String op = parts[1];
+            String next = parts[2];
+            if (isOperator(op)) {
+                int pos = prev.lastIndexOf(".");
+                String before = prev.substring(0, pos);
+                String after = prev.substring(pos + 1);
+                sb.append("$");
+                if (!before.startsWith(".")) {
+                    sb.append(".");
+                }
+                sb.append(before);
+                sb.append("[?(@.");
+                sb.append(after);
+                sb.append(" ");
+                sb.append(op);
+                sb.append(" ");
+                sb.append(next);
+                sb.append(")]");
+            }
+            return sb.toString();
+        }
+
+        // not able to parse so return as-is
+        return exp;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/cfbe791d/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathWithEvenSimpleCBRTest.java
----------------------------------------------------------------------
diff --git a/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathWithEvenSimpleCBRTest.java b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathWithEvenSimpleCBRTest.java
new file mode 100644
index 0000000..8db54d5
--- /dev/null
+++ b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathWithEvenSimpleCBRTest.java
@@ -0,0 +1,80 @@
+/**
+ * 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.jsonpath;
+
+import java.io.File;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.junit.Test;
+
+public class JsonPathWithEvenSimpleCBRTest extends CamelTestSupport {
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .choice()
+                        .when().jsonpath("store.book.price < ${header.cheap}")
+                            .to("mock:cheap")
+                        .when().jsonpath("store.book.price < ${header.average}")
+                            .to("mock:average")
+                        .otherwise()
+                            .to("mock:expensive");
+            }
+        };
+    }
+    
+    @Test
+    public void testCheap() throws Exception {
+        getMockEndpoint("mock:cheap").expectedMessageCount(1);
+        getMockEndpoint("mock:average").expectedMessageCount(0);
+        getMockEndpoint("mock:expensive").expectedMessageCount(0);
+
+        fluentTemplate.withHeader("cheap", 10).withHeader("average", 30).withBody(new File("src/test/resources/cheap.json"))
+                .to("direct:start").send();
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testAverage() throws Exception {
+        getMockEndpoint("mock:cheap").expectedMessageCount(0);
+        getMockEndpoint("mock:average").expectedMessageCount(1);
+        getMockEndpoint("mock:expensive").expectedMessageCount(0);
+
+        fluentTemplate.withHeader("cheap", 10).withHeader("average", 30).withBody(new File("src/test/resources/average.json"))
+                .to("direct:start").send();
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testExpensive() throws Exception {
+        getMockEndpoint("mock:cheap").expectedMessageCount(0);
+        getMockEndpoint("mock:average").expectedMessageCount(0);
+        getMockEndpoint("mock:expensive").expectedMessageCount(1);
+
+        fluentTemplate.withHeader("cheap", 10).withHeader("average", 30).withBody(new File("src/test/resources/expensive.json"))
+                .to("direct:start").send();
+
+        assertMockEndpointsSatisfied();
+    }
+
+}