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 2016/12/16 09:56:03 UTC

camel git commit: CAMEL-10609: Add skip method to simple language

Repository: camel
Updated Branches:
  refs/heads/master 5ca5b1756 -> ff42c8d1e


CAMEL-10609: Add skip method to simple language


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

Branch: refs/heads/master
Commit: ff42c8d1eee8a8051f135a67ec9f6aa2db031802
Parents: 5ca5b17
Author: Claus Ibsen <da...@apache.org>
Authored: Fri Dec 16 10:53:39 2016 +0100
Committer: Claus Ibsen <da...@apache.org>
Committed: Fri Dec 16 10:55:52 2016 +0100

----------------------------------------------------------------------
 camel-core/src/main/docs/simple-language.adoc   |   4 +
 .../apache/camel/builder/ExpressionBuilder.java |  35 ++++++
 .../simple/ast/SimpleFunctionExpression.java    |  12 ++
 .../org/apache/camel/util/SkipIterator.java     | 122 +++++++++++++++++++
 .../camel/processor/SplitterSkipTest.java       |  73 +++++++++++
 5 files changed, 246 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/ff42c8d1/camel-core/src/main/docs/simple-language.adoc
----------------------------------------------------------------------
diff --git a/camel-core/src/main/docs/simple-language.adoc b/camel-core/src/main/docs/simple-language.adoc
index 7bf9e98..53fc510 100644
--- a/camel-core/src/main/docs/simple-language.adoc
+++ b/camel-core/src/main/docs/simple-language.adoc
@@ -278,6 +278,10 @@ link:splitter.html[Splitter] EIP to split a message body and group/batch
 the splitted sub message into a group of N sub lists. This method works
 similar to the collate method in Groovy.
 
+|skip(number) |Iterator |*Camel 2.19:* The skip function iterates the message body and skips
+the first number of items. This can be used with the
+link:splitter.html[Splitter] EIP to split a message body and skip the first N number of items.
+
 |messageHistory |String |*Camel 2.17:* The message history of the current exchange how it has
 been routed. This is similar to the route stack-trace message history
 the error handler logs in case of an unhandled exception.

http://git-wip-us.apache.org/repos/asf/camel/blob/ff42c8d1/camel-core/src/main/java/org/apache/camel/builder/ExpressionBuilder.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/builder/ExpressionBuilder.java b/camel-core/src/main/java/org/apache/camel/builder/ExpressionBuilder.java
index 549e47c..6051555 100644
--- a/camel-core/src/main/java/org/apache/camel/builder/ExpressionBuilder.java
+++ b/camel-core/src/main/java/org/apache/camel/builder/ExpressionBuilder.java
@@ -71,6 +71,7 @@ import org.apache.camel.util.IOHelper;
 import org.apache.camel.util.MessageHelper;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.OgnlHelper;
+import org.apache.camel.util.SkipIterator;
 import org.apache.camel.util.StringHelper;
 
 
@@ -1667,6 +1668,22 @@ public final class ExpressionBuilder {
         };
     }
 
+    public static Expression skipIteratorExpression(final Expression expression, final int skip) {
+        return new ExpressionAdapter() {
+            public Object evaluate(Exchange exchange) {
+                // evaluate expression as iterator
+                Iterator<?> it = expression.evaluate(exchange, Iterator.class);
+                ObjectHelper.notNull(it, "expression: " + expression + " evaluated on " + exchange + " must return an java.util.Iterator");
+                return new SkipIterator(exchange, it, skip);
+            }
+
+            @Override
+            public String toString() {
+                return "skip " + expression + " " + skip + " times";
+            }
+        };
+    }
+
     /**
      * Returns a sort expression which will sort the expression with the given comparator.
      * <p/>
@@ -2332,6 +2349,24 @@ public final class ExpressionBuilder {
     }
 
     /**
+     * Returns an iterator to skip (iterate) the given expression
+     */
+    public static Expression skipExpression(final String expression, final int number) {
+        return new ExpressionAdapter() {
+            public Object evaluate(Exchange exchange) {
+                // use simple language
+                Expression exp = exchange.getContext().resolveLanguage("simple").createExpression(expression);
+                return ExpressionBuilder.skipIteratorExpression(exp, number).evaluate(exchange, Object.class);
+            }
+
+            @Override
+            public String toString() {
+                return "skip(" + expression + "," + number + ")";
+            }
+        };
+    }
+
+    /**
      * Returns an iterator to collate (iterate) the given expression
      */
     public static Expression collateExpression(final String expression, final int group) {

http://git-wip-us.apache.org/repos/asf/camel/blob/ff42c8d1/camel-core/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java b/camel-core/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
index 8bd5e57..caddbab 100644
--- a/camel-core/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
+++ b/camel-core/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
@@ -448,6 +448,18 @@ public class SimpleFunctionExpression extends LiteralExpression {
             }
         }
 
+        // skip function
+        remainder = ifStartsWithReturnRemainder("skip", function);
+        if (remainder != null) {
+            String values = ObjectHelper.between(remainder, "(", ")");
+            if (values == null || ObjectHelper.isEmpty(values)) {
+                throw new SimpleParserException("Valid syntax: ${skip(number)} was: " + function, token.getIndex());
+            }
+            String exp = "${body}";
+            int num = Integer.parseInt(values.trim());
+            return ExpressionBuilder.skipExpression(exp, num);
+        }
+
         // collate function
         remainder = ifStartsWithReturnRemainder("collate", function);
         if (remainder != null) {

http://git-wip-us.apache.org/repos/asf/camel/blob/ff42c8d1/camel-core/src/main/java/org/apache/camel/util/SkipIterator.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/util/SkipIterator.java b/camel-core/src/main/java/org/apache/camel/util/SkipIterator.java
new file mode 100644
index 0000000..ee43064
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/util/SkipIterator.java
@@ -0,0 +1,122 @@
+/**
+ * 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.util;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Scanner;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+
+/**
+ * Skip based {@link Iterator} which skips the given {@link Iterator} a number of times.
+ */
+public final class SkipIterator implements Iterator<Object>, Closeable {
+
+    private final CamelContext camelContext;
+    private final Exchange exchange;
+    private final Iterator<?> it;
+    private final int skip;
+    private boolean closed;
+    private final AtomicBoolean hasSkip = new AtomicBoolean();
+
+    /**
+     * Creates a new skip iterator
+     *
+     * @param exchange  the exchange used to create this group iterator
+     * @param it        the iterator
+     * @param skip      number of times to skip
+     * @throws IllegalArgumentException is thrown if skip is not a positive number
+     */
+    public SkipIterator(Exchange exchange, Iterator<?> it, int skip) {
+        this.exchange = exchange;
+        this.camelContext = exchange.getContext();
+        this.it = it;
+        this.skip = skip;
+        if (skip < 0) {
+            throw new IllegalArgumentException("Skip must not be a negative number, was: " + skip);
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+        try {
+            if (it instanceof Scanner) {
+                // special for Scanner which implement the Closeable since JDK7 
+                Scanner scanner = (Scanner) it;
+                scanner.close();
+                IOException ioException = scanner.ioException();
+                if (ioException != null) {
+                    throw ioException;
+                }
+            } else if (it instanceof Closeable) {
+                IOHelper.closeWithException((Closeable) it);
+            }
+        } finally {
+            // we are now closed
+            closed = true;
+        }
+    }
+
+    @Override
+    public boolean hasNext() {
+        if (closed) {
+            return false;
+        }
+
+        if (hasSkip.compareAndSet(false, true)) {
+            doSkip();
+        }
+
+        boolean answer = it.hasNext();
+        if (!answer) {
+            // auto close
+            try {
+                close();
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+        return answer;
+    }
+
+    @Override
+    public Object next() {
+        if (hasSkip.compareAndSet(false, true)) {
+            doSkip();
+        }
+
+        return it.next();
+    }
+
+    private void doSkip() {
+        for (int i = 0; i < skip; i++) {
+            if (it.hasNext()) {
+                // skip
+                it.next();
+            }
+        }
+    }
+
+    @Override
+    public void remove() {
+        it.remove();
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/ff42c8d1/camel-core/src/test/java/org/apache/camel/processor/SplitterSkipTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/processor/SplitterSkipTest.java b/camel-core/src/test/java/org/apache/camel/processor/SplitterSkipTest.java
new file mode 100644
index 0000000..4efe162
--- /dev/null
+++ b/camel-core/src/test/java/org/apache/camel/processor/SplitterSkipTest.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.processor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+
+public class SplitterSkipTest extends ContextTestSupport {
+
+    public void testSplitterSkip() throws Exception {
+        getMockEndpoint("mock:line").expectedBodiesReceived("C", "D", "E");
+
+        List<Object> data = new ArrayList<Object>();
+        data.add("A");
+        data.add("B");
+        data.add("C");
+        data.add("D");
+        data.add("E");
+        template.sendBody("direct:start", data);
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public void testSplitterEmpty() throws Exception {
+        getMockEndpoint("mock:line").expectedMessageCount(0);
+
+        List<Object> data = new ArrayList<Object>();
+        data.add("A");
+        data.add("B");
+        template.sendBody("direct:start", data);
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public void testSplitterEmptyAgain() throws Exception {
+        getMockEndpoint("mock:line").expectedMessageCount(0);
+
+        List<Object> data = new ArrayList<Object>();
+        data.add("A");
+        template.sendBody("direct:start", data);
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .split(simple("${skip(2)}"))
+                        .to("mock:line");
+            }
+        };
+    }
+}