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");
+ }
+ };
+ }
+}