Repository: camel Updated Branches: refs/heads/master a5dbf6688 -> 2c01b8e84
CAMEL-7801 XMLTokenizer's wrapped mode to handle grouping without replicating the wrapper part Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/2c01b8e8 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/2c01b8e8 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/2c01b8e8 Branch: refs/heads/master Commit: 2c01b8e843e4a3d2eecce452470fba02a46ca790 Parents: a5dbf66 Author: Akitoshi Yoshida <a...@apache.org> Authored: Wed Sep 10 18:09:48 2014 +0200 Committer: Akitoshi Yoshida <a...@apache.org> Committed: Thu Sep 11 10:52:37 2014 +0200 ---------------------------------------------------------------------- .../apache/camel/builder/ExpressionBuilder.java | 6 + .../language/tokenizer/XMLTokenizeLanguage.java | 7 - .../model/language/XMLTokenizerExpression.java | 14 +- .../support/XMLTokenExpressionIterator.java | 95 ++++- .../XMLTokenizeLanguageGroupingTest.java | 141 +++++++ .../XMLTokenizeWrapLanguageGroupingTest.java | 148 +++++++ .../processor/SplitGroupMultiXmlTokenTest.java | 6 +- .../SplitGroupWrappedMultiXmlTokenTest.java | 81 ++++ .../XMLTokenExpressionIteratorGroupingTest.java | 415 +++++++++++++++++++ 9 files changed, 887 insertions(+), 26 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/2c01b8e8/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 dfb4b99..4473fe7 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 @@ -1259,6 +1259,12 @@ public final class ExpressionBuilder { return new XMLTokenExpressionIterator(path, mode); } + + public static Expression tokenizeXMLAwareExpression(String path, char mode, int group) { + ObjectHelper.notEmpty(path, "path"); + + return new XMLTokenExpressionIterator(path, mode, group); + } /** * Returns a tokenize expression which will tokenize the string with the http://git-wip-us.apache.org/repos/asf/camel/blob/2c01b8e8/camel-core/src/main/java/org/apache/camel/language/tokenizer/XMLTokenizeLanguage.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/language/tokenizer/XMLTokenizeLanguage.java b/camel-core/src/main/java/org/apache/camel/language/tokenizer/XMLTokenizeLanguage.java index 2b38128..b42357f 100644 --- a/camel-core/src/main/java/org/apache/camel/language/tokenizer/XMLTokenizeLanguage.java +++ b/camel-core/src/main/java/org/apache/camel/language/tokenizer/XMLTokenizeLanguage.java @@ -74,13 +74,6 @@ public class XMLTokenizeLanguage extends LanguageSupport { ObjectHelper.notNull(path, "token"); Expression answer = ExpressionBuilder.tokenizeXMLAwareExpression(path, mode); - - // if group then wrap answer in group expression - if (group > 0) { - //REVISIT wrap the xml tokens with a group element to turn the result into xml? - answer = ExpressionBuilder.groupIteratorExpression(answer, null, group); - } - return answer; } http://git-wip-us.apache.org/repos/asf/camel/blob/2c01b8e8/camel-core/src/main/java/org/apache/camel/model/language/XMLTokenizerExpression.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/model/language/XMLTokenizerExpression.java b/camel-core/src/main/java/org/apache/camel/model/language/XMLTokenizerExpression.java index 8e96044..323024a 100644 --- a/camel-core/src/main/java/org/apache/camel/model/language/XMLTokenizerExpression.java +++ b/camel-core/src/main/java/org/apache/camel/model/language/XMLTokenizerExpression.java @@ -87,6 +87,9 @@ public class XMLTokenizerExpression extends NamespaceAwareExpression { if (mode != null) { setProperty(expression, "mode", mode); } + if (group != null) { + setProperty(expression, "group", group); + } } @Override @@ -98,17 +101,14 @@ public class XMLTokenizerExpression extends NamespaceAwareExpression { if (mode != null) { setProperty(predicate, "mode", mode); } + if (group != null) { + setProperty(predicate, "group", group); + } } @Override public Expression createExpression(CamelContext camelContext) { - Expression answer = super.createExpression(camelContext); - if (group != null) { - if (group > 0) { - //REVISIT wrap the xml tokens with a group element to turn the result into xml? - answer = ExpressionBuilder.groupIteratorExpression(answer, null, group); - } - } + Expression answer = super.createExpression(camelContext); return answer; } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/camel/blob/2c01b8e8/camel-core/src/main/java/org/apache/camel/support/XMLTokenExpressionIterator.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/support/XMLTokenExpressionIterator.java b/camel-core/src/main/java/org/apache/camel/support/XMLTokenExpressionIterator.java index d48a2d4..f233281 100644 --- a/camel-core/src/main/java/org/apache/camel/support/XMLTokenExpressionIterator.java +++ b/camel-core/src/main/java/org/apache/camel/support/XMLTokenExpressionIterator.java @@ -53,12 +53,18 @@ import org.slf4j.LoggerFactory; public class XMLTokenExpressionIterator extends ExpressionAdapter implements NamespaceAware { protected final String path; protected char mode; + protected int group; protected Map<String, String> nsmap; public XMLTokenExpressionIterator(String path, char mode) { + this(path, mode, 1); + } + + public XMLTokenExpressionIterator(String path, char mode, int group) { ObjectHelper.notEmpty(path, "path"); this.path = path; this.mode = mode; + this.group = group > 1 ? group : 1; } @Override @@ -74,6 +80,14 @@ public class XMLTokenExpressionIterator extends ExpressionAdapter implements Nam this.mode = mode != null ? mode.charAt(0) : 0; } + public int getGroup() { + return group; + } + + public void setGroup(int group) { + this.group = group; + } + protected Iterator<?> createIterator(InputStream in, String charset) throws XMLStreamException, UnsupportedEncodingException { Reader reader; if (charset == null) { @@ -81,12 +95,12 @@ public class XMLTokenExpressionIterator extends ExpressionAdapter implements Nam } else { reader = new InputStreamReader(in, charset); } - XMLTokenIterator iterator = new XMLTokenIterator(path, nsmap, mode, reader); + XMLTokenIterator iterator = new XMLTokenIterator(path, nsmap, mode, group, reader); return iterator; } protected Iterator<?> createIterator(Reader in) throws XMLStreamException { - XMLTokenIterator iterator = new XMLTokenIterator(path, nsmap, mode, in); + XMLTokenIterator iterator = new XMLTokenIterator(path, nsmap, mode, group, in); return iterator; } @@ -147,12 +161,14 @@ public class XMLTokenExpressionIterator extends ExpressionAdapter implements Nam private AttributedQName[] splitpath; private int index; private char mode; + private int group; private RecordableReader in; private XMLStreamReader reader; private List<QName> path; private List<Map<String, String>> namespaces; private List<String> segments; private List<QName> segmentlog; + private List<String> tokens; private int code; private int consumed; private boolean backtrack; @@ -164,10 +180,20 @@ public class XMLTokenExpressionIterator extends ExpressionAdapter implements Nam public XMLTokenIterator(String path, Map<String, String> nsmap, char mode, InputStream in, String charset) throws XMLStreamException, UnsupportedEncodingException { // woodstox's getLocation().etCharOffset() does not return the offset correctly for InputStream, so use Reader instead. + this(path, nsmap, mode, 1, new InputStreamReader(in, charset)); + } + + public XMLTokenIterator(String path, Map<String, String> nsmap, char mode, int group, InputStream in, String charset) + throws XMLStreamException, UnsupportedEncodingException { + // woodstox's getLocation().etCharOffset() does not return the offset correctly for InputStream, so use Reader instead. this(path, nsmap, mode, new InputStreamReader(in, charset)); } public XMLTokenIterator(String path, Map<String, String> nsmap, char mode, Reader in) throws XMLStreamException { + this(path, nsmap, mode, 1, in); + } + + public XMLTokenIterator(String path, Map<String, String> nsmap, char mode, int group, Reader in) throws XMLStreamException { final String[] sl = path.substring(1).split("/"); this.splitpath = new AttributedQName[sl.length]; for (int i = 0; i < sl.length; i++) { @@ -182,6 +208,7 @@ public class XMLTokenExpressionIterator extends ExpressionAdapter implements Nam } this.mode = mode != 0 ? mode : 'i'; + this.group = group > 0 ? group : 1; this.in = new RecordableReader(in); this.reader = new StaxConverter().createXMLStreamReader(this.in); @@ -201,7 +228,10 @@ public class XMLTokenExpressionIterator extends ExpressionAdapter implements Nam } else if (this.mode == 'i') { this.namespaces = new ArrayList<Map<String, String>>(); } - + // when grouping the tokens, allocate the storage to temporarily store tokens. + if (this.group > 1) { + this.tokens = new ArrayList<String>(); + } this.nextToken = getNextToken(); } @@ -336,7 +366,7 @@ public class XMLTokenExpressionIterator extends ExpressionAdapter implements Nam private String createContextualToken(String token) { StringBuilder sb = new StringBuilder(); - if (mode == 'w') { + if (mode == 'w' && group == 1) { for (int i = 0; i < segments.size(); i++) { sb.append(segments.get(i)); } @@ -389,17 +419,45 @@ public class XMLTokenExpressionIterator extends ExpressionAdapter implements Nam } sb.append(token.substring(ep + 1, bp)); } + } else { + return token; } return sb.toString(); } + private String getGroupedToken() { + StringBuilder sb = new StringBuilder(); + if (mode == 'w') { + // for wrapped + for (int i = 0; i < segments.size(); i++) { + sb.append(segments.get(i)); + } + for (String s : tokens) { + sb.append(s); + } + for (int i = path.size() - 1; i >= 0; i--) { + QName q = path.get(i); + sb.append("</").append(makeName(q)).append(">"); + } + } else { + // for injected, unwrapped, text + sb.append("<group>"); + for (String s : tokens) { + sb.append(s); + } + sb.append("</group>"); + } + tokens.clear(); + return sb.toString(); + } + private String getNextToken() throws XMLStreamException { - int code = 0; - while (code != XMLStreamConstants.END_DOCUMENT) { - code = readNext(); + int xcode = 0; + while (xcode != XMLStreamConstants.END_DOCUMENT) { + xcode = readNext(); - switch (code) { + switch (xcode) { case XMLStreamConstants.START_ELEMENT: depth++; QName name = reader.getName(); @@ -424,7 +482,14 @@ public class XMLTokenExpressionIterator extends ExpressionAdapter implements Nam token = getCurrentToken(); backtrack = true; trackdepth = depth; - return token; + if (group > 1) { + tokens.add(token); + if (group == tokens.size()) { + return getGroupedToken(); + } + } else { + return token; + } } else { // intermediary match down(); @@ -437,6 +502,13 @@ public class XMLTokenExpressionIterator extends ExpressionAdapter implements Nam } break; case XMLStreamConstants.END_ELEMENT: + if ((backtrack || (trackdepth > 0 && depth == trackdepth)) + && (mode == 'w' && group > 1 && tokens.size() > 0)) { + // flush the left over using the current context + code = XMLStreamConstants.END_ELEMENT; + return getGroupedToken(); + } + depth--; QName endname = reader.getName(); LOG.trace("ee={}", endname); @@ -474,6 +546,11 @@ public class XMLTokenExpressionIterator extends ExpressionAdapter implements Nam break; case XMLStreamConstants.END_DOCUMENT: LOG.trace("depth={}", depth); + if (group > 1 && tokens.size() > 0) { + // flush the left over before really going EoD + code = XMLStreamConstants.END_DOCUMENT; + return getGroupedToken(); + } break; default: break; http://git-wip-us.apache.org/repos/asf/camel/blob/2c01b8e8/camel-core/src/test/java/org/apache/camel/language/tokenizer/XMLTokenizeLanguageGroupingTest.java ---------------------------------------------------------------------- diff --git a/camel-core/src/test/java/org/apache/camel/language/tokenizer/XMLTokenizeLanguageGroupingTest.java b/camel-core/src/test/java/org/apache/camel/language/tokenizer/XMLTokenizeLanguageGroupingTest.java new file mode 100644 index 0000000..79fd0df --- /dev/null +++ b/camel-core/src/test/java/org/apache/camel/language/tokenizer/XMLTokenizeLanguageGroupingTest.java @@ -0,0 +1,141 @@ +/** + * 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.tokenizer; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.builder.xml.Namespaces; +import org.apache.camel.component.mock.MockEndpoint; + +public class XMLTokenizeLanguageGroupingTest extends ContextTestSupport { + + public void testSendClosedTagMessageToTokenize() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("<group><c:child some_attr='a' anotherAttr='a' xmlns:c=\"urn:c\"></c:child>" + + "<c:child some_attr='b' anotherAttr='b' xmlns:c=\"urn:c\"></c:child></group>"); + + template.sendBody("direct:start", + "<?xml version='1.0' encoding='UTF-8'?><c:parent xmlns:c='urn:c'><c:child some_attr='a' anotherAttr='a'></c:child><c:child some_attr='b' anotherAttr='b'></c:child></c:parent>"); + + assertMockEndpointsSatisfied(); + } + + public void testSendClosedTagWithLineBreaksMessageToTokenize() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("<group><c:child some_attr='a' anotherAttr='a' xmlns:c=\"urn:c\">\n</c:child>" + + "<c:child some_attr='b' anotherAttr='b' xmlns:c=\"urn:c\">\n</c:child></group>"); + + template.sendBody("direct:start", + "<?xml version='1.0' encoding='UTF-8'?>\n" + + "<c:parent xmlns:c='urn:c'>\n" + + "<c:child some_attr='a' anotherAttr='a'>\n" + + "</c:child>\n" + + "<c:child some_attr='b' anotherAttr='b'>\n" + + "</c:child>\n" + + "</c:parent>"); + + assertMockEndpointsSatisfied(); + } + + public void testSendSelfClosingTagMessageToTokenize() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("<group><c:child some_attr='a' anotherAttr='a' xmlns:c=\"urn:c\"/>" + + "<c:child some_attr='b' anotherAttr='b' xmlns:c=\"urn:c\"/></group>"); + + template.sendBody("direct:start", + "<?xml version='1.0' encoding='UTF-8'?><c:parent xmlns:c='urn:c'><c:child some_attr='a' anotherAttr='a' /><c:child some_attr='b' anotherAttr='b' /></c:parent>"); + + assertMockEndpointsSatisfied(); + } + + public void testSendMixedClosingTagMessageToTokenize() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived( + "<group><c:child some_attr='a' anotherAttr='a' xmlns:c=\"urn:c\">ha</c:child>" + + "<c:child some_attr='b' anotherAttr='b' xmlns:c=\"urn:c\"/></group>", + "<group><c:child some_attr='c' xmlns:c=\"urn:c\"></c:child></group>"); + + template.sendBody("direct:start", + "<?xml version='1.0' encoding='UTF-8'?><c:parent xmlns:c='urn:c'><c:child some_attr='a' anotherAttr='a'>ha</c:child>" + + "<c:child some_attr='b' anotherAttr='b' /><c:child some_attr='c'></c:child></c:parent>"); + + assertMockEndpointsSatisfied(); + } + + public void testSendMixedClosingTagInsideMessageToTokenize() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived( + "<group><c:child name='child1' xmlns:c=\"urn:c\"><grandchild name='grandchild1'/> <grandchild name='grandchild2'/></c:child>" + + "<c:child name='child2' xmlns:c=\"urn:c\"><grandchild name='grandchild1'></grandchild><grandchild name='grandchild2'></grandchild></c:child></group>"); + + template.sendBody("direct:start", + "<c:parent xmlns:c='urn:c'><c:child name='child1'><grandchild name='grandchild1'/> <grandchild name='grandchild2'/></c:child>" + + "<c:child name='child2'><grandchild name='grandchild1'></grandchild><grandchild name='grandchild2'></grandchild></c:child></c:parent>"); + + assertMockEndpointsSatisfied(); + } + + public void testSendNamespacedChildMessageToTokenize() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived( + "<group><c:child xmlns:c='urn:c' some_attr='a' anotherAttr='a'></c:child><c:child xmlns:c='urn:c' some_attr='b' anotherAttr='b' /></group>"); + + template.sendBody("direct:start", + "<?xml version='1.0' encoding='UTF-8'?><c:parent xmlns:c='urn:c'><c:child xmlns:c='urn:c' some_attr='a' anotherAttr='a'></c:child>" + + "<c:child xmlns:c='urn:c' some_attr='b' anotherAttr='b' /></c:parent>"); + + assertMockEndpointsSatisfied(); + } + + public void testSendNamespacedParentMessageToTokenize() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived( + "<group><c:child some_attr='a' anotherAttr='a' xmlns:d=\"urn:d\" xmlns:c=\"urn:c\"></c:child>" + + "<c:child some_attr='b' anotherAttr='b' xmlns:d=\"urn:d\" xmlns:c=\"urn:c\"/></group>"); + + template.sendBody("direct:start", + "<?xml version='1.0' encoding='UTF-8'?><c:parent xmlns:c='urn:c' xmlns:d=\"urn:d\"><c:child some_attr='a' anotherAttr='a'></c:child>" + + "<c:child some_attr='b' anotherAttr='b'/></c:parent>"); + + assertMockEndpointsSatisfied(); + } + + public void testSendMoreParentsMessageToTokenize() throws Exception { + MockEndpoint result = getMockEndpoint("mock:result"); + if (isJavaVersion("1.8")) { + result.expectedBodiesReceived( + "<group><c:child some_attr='a' anotherAttr='a' xmlns:c=\"urn:c\" xmlns:d=\"urn:d\" xmlns:g=\"urn:g\"></c:child>" + + "<c:child some_attr='b' anotherAttr='b' xmlns:c=\"urn:c\" xmlns:d=\"urn:d\" xmlns:g=\"urn:g\"/></group>"); + } else { + result.expectedBodiesReceived( + "<group><c:child some_attr='a' anotherAttr='a' xmlns:g=\"urn:g\" xmlns:d=\"urn:d\" xmlns:c=\"urn:c\"></c:child>" + + "<c:child some_attr='b' anotherAttr='b' xmlns:g=\"urn:g\" xmlns:d=\"urn:d\" xmlns:c=\"urn:c\"/></group>"); + } + + template.sendBody("direct:start", + "<?xml version='1.0' encoding='UTF-8'?><g:greatgrandparent xmlns:g='urn:g'><grandparent><uncle/><aunt>emma</aunt><c:parent xmlns:c='urn:c' xmlns:d=\"urn:d\">" + + "<c:child some_attr='a' anotherAttr='a'></c:child><c:child some_attr='b' anotherAttr='b'/></c:parent></grandparent></g:greatgrandparent>"); + + assertMockEndpointsSatisfied(); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + Namespaces ns = new Namespaces("C", "urn:c"); + public void configure() { + from("direct:start") + .split().xtokenize("//C:child", 'i', ns, 2) + .to("mock:result") + .end(); + } + }; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/2c01b8e8/camel-core/src/test/java/org/apache/camel/language/tokenizer/XMLTokenizeWrapLanguageGroupingTest.java ---------------------------------------------------------------------- diff --git a/camel-core/src/test/java/org/apache/camel/language/tokenizer/XMLTokenizeWrapLanguageGroupingTest.java b/camel-core/src/test/java/org/apache/camel/language/tokenizer/XMLTokenizeWrapLanguageGroupingTest.java new file mode 100644 index 0000000..728a70f --- /dev/null +++ b/camel-core/src/test/java/org/apache/camel/language/tokenizer/XMLTokenizeWrapLanguageGroupingTest.java @@ -0,0 +1,148 @@ +/** + * 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.tokenizer; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.builder.xml.Namespaces; + +public class XMLTokenizeWrapLanguageGroupingTest extends ContextTestSupport { + + public void testSendClosedTagMessageToTokenize() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("<?xml version='1.0' encoding='UTF-8'?><c:parent xmlns:c='urn:c'><c:child some_attr='a' anotherAttr='a'></c:child>" + + "<c:child some_attr='b' anotherAttr='b'></c:child></c:parent>"); + + template.sendBody("direct:start", + "<?xml version='1.0' encoding='UTF-8'?><c:parent xmlns:c='urn:c'><c:child some_attr='a' anotherAttr='a'></c:child><c:child some_attr='b' anotherAttr='b'></c:child></c:parent>"); + + assertMockEndpointsSatisfied(); + } + + public void testSendClosedTagWithLineBreaksMessageToTokenize() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("<?xml version='1.0' encoding='UTF-8'?>\n<c:parent xmlns:c='urn:c'>\n<c:child some_attr='a' anotherAttr='a'>\n</c:child>" + + "<c:child some_attr='b' anotherAttr='b'>\n</c:child></c:parent>"); + + template.sendBody("direct:start", + "<?xml version='1.0' encoding='UTF-8'?>\n" + + "<c:parent xmlns:c='urn:c'>\n" + + "<c:child some_attr='a' anotherAttr='a'>\n" + + "</c:child>\n" + + "<c:child some_attr='b' anotherAttr='b'>\n" + + "</c:child>\n" + + "</c:parent>"); + + assertMockEndpointsSatisfied(); + } + + public void testSendSelfClosingTagMessageToTokenize() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("<?xml version='1.0' encoding='UTF-8'?><c:parent xmlns:c='urn:c'><c:child some_attr='a' anotherAttr='a' />" + + "<c:child some_attr='b' anotherAttr='b' /></c:parent>"); + + template.sendBody("direct:start", + "<?xml version='1.0' encoding='UTF-8'?><c:parent xmlns:c='urn:c'><c:child some_attr='a' anotherAttr='a' /><c:child some_attr='b' anotherAttr='b' /></c:parent>"); + + assertMockEndpointsSatisfied(); + } + + public void testSendMixedClosingTagMessageToTokenize() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived( + "<?xml version='1.0' encoding='UTF-8'?><c:parent xmlns:c='urn:c'><c:child some_attr='a' anotherAttr='a'>ha</c:child>" + + "<c:child some_attr='b' anotherAttr='b' /></c:parent>", + "<?xml version='1.0' encoding='UTF-8'?><c:parent xmlns:c='urn:c'><c:child some_attr='c'></c:child></c:parent>"); + + template.sendBody("direct:start", + "<?xml version='1.0' encoding='UTF-8'?><c:parent xmlns:c='urn:c'><c:child some_attr='a' anotherAttr='a'>ha</c:child>" + + "<c:child some_attr='b' anotherAttr='b' /><c:child some_attr='c'></c:child></c:parent>"); + + assertMockEndpointsSatisfied(); + } + + public void testSendMixedClosingTagInsideMessageToTokenize() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived( + "<c:parent xmlns:c='urn:c'><c:child name='child1'><grandchild name='grandchild1'/> <grandchild name='grandchild2'/></c:child>" + + "<c:child name='child2'><grandchild name='grandchild1'></grandchild><grandchild name='grandchild2'></grandchild></c:child></c:parent>"); + + template.sendBody("direct:start", + "<c:parent xmlns:c='urn:c'><c:child name='child1'><grandchild name='grandchild1'/> <grandchild name='grandchild2'/></c:child>" + + "<c:child name='child2'><grandchild name='grandchild1'></grandchild><grandchild name='grandchild2'></grandchild></c:child></c:parent>"); + + assertMockEndpointsSatisfied(); + } + + public void testSendNamespacedChildMessageToTokenize() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived( + "<?xml version='1.0' encoding='UTF-8'?><c:parent xmlns:c='urn:c'><c:child xmlns:c='urn:c' some_attr='a' anotherAttr='a'></c:child>" + + "<c:child xmlns:c='urn:c' some_attr='b' anotherAttr='b' /></c:parent>"); + + template.sendBody("direct:start", + "<?xml version='1.0' encoding='UTF-8'?><c:parent xmlns:c='urn:c'><c:child xmlns:c='urn:c' some_attr='a' anotherAttr='a'></c:child>" + + "<c:child xmlns:c='urn:c' some_attr='b' anotherAttr='b' /></c:parent>"); + + assertMockEndpointsSatisfied(); + } + + public void testSendNamespacedParentMessageToTokenize() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived( + "<?xml version='1.0' encoding='UTF-8'?><c:parent xmlns:c='urn:c' xmlns:d=\"urn:d\"><c:child some_attr='a' anotherAttr='a'></c:child>" + + "<c:child some_attr='b' anotherAttr='b'/></c:parent>"); + + template.sendBody("direct:start", + "<?xml version='1.0' encoding='UTF-8'?><c:parent xmlns:c='urn:c' xmlns:d=\"urn:d\"><c:child some_attr='a' anotherAttr='a'></c:child><c:child some_attr='b' anotherAttr='b'/></c:parent>"); + + assertMockEndpointsSatisfied(); + } + + public void testSendMoreParentsMessageToTokenize() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived( + "<?xml version='1.0' encoding='UTF-8'?><g:greatgrandparent xmlns:g='urn:g'><grandparent><uncle/><aunt>emma</aunt><c:parent xmlns:c='urn:c' xmlns:d=\"urn:d\">" + + "<c:child some_attr='a' anotherAttr='a'></c:child>" + + "<c:child some_attr='b' anotherAttr='b'/></c:parent></grandparent></g:greatgrandparent>"); + + template.sendBody("direct:start", + "<?xml version='1.0' encoding='UTF-8'?><g:greatgrandparent xmlns:g='urn:g'><grandparent><uncle/><aunt>emma</aunt><c:parent xmlns:c='urn:c' xmlns:d=\"urn:d\">" + + "<c:child some_attr='a' anotherAttr='a'></c:child><c:child some_attr='b' anotherAttr='b'/></c:parent></grandparent></g:greatgrandparent>"); + + assertMockEndpointsSatisfied(); + } + + public void testSendParentMessagesWithDifferentAttributesToTokenize() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived( + "<?xml version='1.0' encoding='UTF-8'?><g:grandparent xmlns:g='urn:g'><c:parent name='e' xmlns:c='urn:c' xmlns:d=\"urn:d\">" + + "<c:child some_attr='a' anotherAttr='a'></c:child></c:parent></g:grandparent>", + "<?xml version='1.0' encoding='UTF-8'?><g:grandparent xmlns:g='urn:g'><c:parent name='f' xmlns:c='urn:c' xmlns:d=\"urn:d\">" + + "<c:child some_attr='b' anotherAttr='b'/></c:parent></g:grandparent>"); + + template.sendBody("direct:start", + "<?xml version='1.0' encoding='UTF-8'?><g:grandparent xmlns:g='urn:g'><c:parent name='e' xmlns:c='urn:c' xmlns:d=\"urn:d\">" + + "<c:child some_attr='a' anotherAttr='a'></c:child></c:parent><c:parent name='f' xmlns:c='urn:c' xmlns:d=\"urn:d\"><c:child some_attr='b' anotherAttr='b'/>" + + "</c:parent></g:grandparent>"); + assertMockEndpointsSatisfied(); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + Namespaces ns = new Namespaces("C", "urn:c"); + public void configure() { + from("direct:start") + .split().xtokenize("//C:child", 'w', ns, 2) + .to("mock:result") + .end(); + } + }; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/2c01b8e8/camel-core/src/test/java/org/apache/camel/processor/SplitGroupMultiXmlTokenTest.java ---------------------------------------------------------------------- diff --git a/camel-core/src/test/java/org/apache/camel/processor/SplitGroupMultiXmlTokenTest.java b/camel-core/src/test/java/org/apache/camel/processor/SplitGroupMultiXmlTokenTest.java index 86f4201..67a1f90 100644 --- a/camel-core/src/test/java/org/apache/camel/processor/SplitGroupMultiXmlTokenTest.java +++ b/camel-core/src/test/java/org/apache/camel/processor/SplitGroupMultiXmlTokenTest.java @@ -36,9 +36,9 @@ public class SplitGroupMultiXmlTokenTest extends ContextTestSupport { public void testTokenXMLPairGroup() throws Exception { MockEndpoint mock = getMockEndpoint("mock:split"); mock.expectedMessageCount(3); - mock.message(0).body().isEqualTo("<order id=\"1\" xmlns=\"http:acme.com\">Camel in Action</order><order id=\"2\" xmlns=\"http:acme.com\">ActiveMQ in Action</order>"); - mock.message(1).body().isEqualTo("<order id=\"3\" xmlns=\"http:acme.com\">Spring in Action</order><order id=\"4\" xmlns=\"http:acme.com\">Scala in Action</order>"); - mock.message(2).body().isEqualTo("<order id=\"5\" xmlns=\"http:acme.com\">Groovy in Action</order>"); + mock.message(0).body().isEqualTo("<group><order id=\"1\" xmlns=\"http:acme.com\">Camel in Action</order><order id=\"2\" xmlns=\"http:acme.com\">ActiveMQ in Action</order></group>"); + mock.message(1).body().isEqualTo("<group><order id=\"3\" xmlns=\"http:acme.com\">Spring in Action</order><order id=\"4\" xmlns=\"http:acme.com\">Scala in Action</order></group>"); + mock.message(2).body().isEqualTo("<group><order id=\"5\" xmlns=\"http:acme.com\">Groovy in Action</order></group>"); String body = createBody(); template.sendBodyAndHeader("file:target/pair", body, Exchange.FILE_NAME, "orders.xml"); http://git-wip-us.apache.org/repos/asf/camel/blob/2c01b8e8/camel-core/src/test/java/org/apache/camel/processor/SplitGroupWrappedMultiXmlTokenTest.java ---------------------------------------------------------------------- diff --git a/camel-core/src/test/java/org/apache/camel/processor/SplitGroupWrappedMultiXmlTokenTest.java b/camel-core/src/test/java/org/apache/camel/processor/SplitGroupWrappedMultiXmlTokenTest.java new file mode 100644 index 0000000..4c1fa94 --- /dev/null +++ b/camel-core/src/test/java/org/apache/camel/processor/SplitGroupWrappedMultiXmlTokenTest.java @@ -0,0 +1,81 @@ +/** + * 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 org.apache.camel.ContextTestSupport; +import org.apache.camel.Exchange; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.builder.xml.Namespaces; +import org.apache.camel.component.mock.MockEndpoint; + +/** + * + */ +public class SplitGroupWrappedMultiXmlTokenTest extends ContextTestSupport { + + @Override + protected void setUp() throws Exception { + deleteDirectory("target/pair"); + super.setUp(); + } + + public void testTokenXMLPairGroup() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:split"); + mock.expectedMessageCount(3); + mock.message(0).body().isEqualTo( + "<?xml version=\"1.0\"?>\n<orders xmlns=\"http:acme.com\">\n <order id=\"1\">Camel in Action</order><order id=\"2\">ActiveMQ in Action</order></orders>"); + mock.message(1).body().isEqualTo( + "<?xml version=\"1.0\"?>\n<orders xmlns=\"http:acme.com\">\n <order id=\"3\">Spring in Action</order><order id=\"4\">Scala in Action</order></orders>"); + mock.message(2).body().isEqualTo( + "<?xml version=\"1.0\"?>\n<orders xmlns=\"http:acme.com\">\n <order id=\"5\">Groovy in Action</order></orders>"); + + String body = createBody(); + template.sendBodyAndHeader("file:target/pair", body, Exchange.FILE_NAME, "orders.xml"); + + assertMockEndpointsSatisfied(); + } + + protected String createBody() { + StringBuilder sb = new StringBuilder("<?xml version=\"1.0\"?>\n"); + sb.append("<orders xmlns=\"http:acme.com\">\n"); + sb.append(" <order id=\"1\">Camel in Action</order>\n"); + sb.append(" <order id=\"2\">ActiveMQ in Action</order>\n"); + sb.append(" <order id=\"3\">Spring in Action</order>\n"); + sb.append(" <order id=\"4\">Scala in Action</order>\n"); + sb.append(" <order id=\"5\">Groovy in Action</order>\n"); + sb.append("</orders>"); + return sb.toString(); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + Namespaces ns = new Namespaces("", "http:acme.com"); + @Override + public void configure() throws Exception { + // START SNIPPET: e1 + from("file:target/pair") + // split the order child tags, and inherit namespaces from the orders root tag + .split().xtokenize("//order", 'w', ns, 2) + .to("log:split") + .to("mock:split"); + // END SNIPPET: e1 + } + }; + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/2c01b8e8/camel-core/src/test/java/org/apache/camel/support/XMLTokenExpressionIteratorGroupingTest.java ---------------------------------------------------------------------- diff --git a/camel-core/src/test/java/org/apache/camel/support/XMLTokenExpressionIteratorGroupingTest.java b/camel-core/src/test/java/org/apache/camel/support/XMLTokenExpressionIteratorGroupingTest.java new file mode 100644 index 0000000..20f5d45 --- /dev/null +++ b/camel-core/src/test/java/org/apache/camel/support/XMLTokenExpressionIteratorGroupingTest.java @@ -0,0 +1,415 @@ +/** + * 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.support; + +import java.io.ByteArrayInputStream; +import java.io.Closeable; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import junit.framework.TestCase; + +/** + * + */ +public class XMLTokenExpressionIteratorGroupingTest extends TestCase { + + private static final byte[] TEST_BODY = ( + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='1' xmlns:c='urn:c'>" + + "<c:C attr='1'>peach</c:C>" + + "<c:C attr='2'/>" + + "<c:C attr='3'>orange</c:C>" + + "<c:C attr='4'/>" + + "</c:B>" + + "<c:B attr='2' xmlns:c='urn:c'>" + + "<c:C attr='5'>mango</c:C>" + + "<c:C attr='6'/>" + + "<c:C attr='7'>pear</c:C>" + + "<c:C attr='8'/>" + + "</c:B>" + + "</g:A>").getBytes(); + + private static final String[] RESULTS_WRAPPED_SIZE1 = { + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='1' xmlns:c='urn:c'>" + + "<c:C attr='1'>peach</c:C>" + + "</c:B>" + + "</g:A>", + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='1' xmlns:c='urn:c'>" + + "<c:C attr='2'/>" + + "</c:B>" + + "</g:A>", + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='1' xmlns:c='urn:c'>" + + "<c:C attr='3'>orange</c:C>" + + "</c:B>" + + "</g:A>", + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='1' xmlns:c='urn:c'>" + + "<c:C attr='4'/>" + + "</c:B>" + + "</g:A>", + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='2' xmlns:c='urn:c'>" + + "<c:C attr='5'>mango</c:C>" + + "</c:B>" + + "</g:A>", + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='2' xmlns:c='urn:c'>" + + "<c:C attr='6'/>" + + "</c:B>" + + "</g:A>", + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='2' xmlns:c='urn:c'>" + + "<c:C attr='7'>pear</c:C>" + + "</c:B>" + + "</g:A>", + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='2' xmlns:c='urn:c'>" + + "<c:C attr='8'/>" + + "</c:B>" + + "</g:A>"}; + + private static final String[] RESULTS_WRAPPED_SIZE2 = { + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='1' xmlns:c='urn:c'>" + + "<c:C attr='1'>peach</c:C>" + + "<c:C attr='2'/>" + + "</c:B>" + + "</g:A>", + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='1' xmlns:c='urn:c'>" + + "<c:C attr='3'>orange</c:C>" + + "<c:C attr='4'/>" + + "</c:B>" + + "</g:A>", + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='2' xmlns:c='urn:c'>" + + "<c:C attr='5'>mango</c:C>" + + "<c:C attr='6'/>" + + "</c:B>" + + "</g:A>", + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='2' xmlns:c='urn:c'>" + + "<c:C attr='7'>pear</c:C>" + + "<c:C attr='8'/>" + + "</c:B>" + + "</g:A>"}; + + private static final String[] RESULTS_WRAPPED_SIZE3L = { + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='1' xmlns:c='urn:c'>" + + "<c:C attr='1'>peach</c:C>" + + "<c:C attr='2'/>" + + "<c:C attr='3'>orange</c:C>" + + "</c:B>" + + "</g:A>", + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='1' xmlns:c='urn:c'>" + + "<c:C attr='4'/>" + + "</c:B>" + + "</g:A>", + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='2' xmlns:c='urn:c'>" + + "<c:C attr='5'>mango</c:C>" + + "<c:C attr='6'/>" + + "<c:C attr='7'>pear</c:C>" + + "</c:B>" + + "</g:A>", + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='2' xmlns:c='urn:c'>" + + "<c:C attr='8'/>" + + "</c:B>" + + "</g:A>"}; + + private static final String[] RESULTS_WRAPPED_SIZE3U = { + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='1' xmlns:c='urn:c'>" + + "<c:C attr='1'>peach</c:C>" + + "<c:C attr='2'/>" + + "<c:C attr='3'>orange</c:C>" + + "</c:B>" + + "</g:A>", + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='1' xmlns:c='urn:c'>" + + "<c:C attr='4'/>" + + "</c:B>" + + "<c:B attr='2' xmlns:c='urn:c'>" + + "<c:C attr='5'>mango</c:C>" + + "<c:C attr='6'/>" + + "</c:B>" + + "</g:A>", + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='2' xmlns:c='urn:c'>" + + "<c:C attr='7'>pear</c:C>" + + "<c:C attr='8'/>" + + "</c:B>" + + "</g:A>"}; + + private static final String[] RESULTS_WRAPPED_SIZE4 = { + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='1' xmlns:c='urn:c'>" + + "<c:C attr='1'>peach</c:C>" + + "<c:C attr='2'/>" + + "<c:C attr='3'>orange</c:C>" + + "<c:C attr='4'/>" + + "</c:B>" + + "</g:A>", + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='2' xmlns:c='urn:c'>" + + "<c:C attr='5'>mango</c:C>" + + "<c:C attr='6'/>" + + "<c:C attr='7'>pear</c:C>" + + "<c:C attr='8'/>" + + "</c:B>" + + "</g:A>"}; + + private static final String[] RESULTS_WRAPPED_SIZE5L = RESULTS_WRAPPED_SIZE4; + + private static final String[] RESULTS_WRAPPED_SIZE5U = { + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='1' xmlns:c='urn:c'>" + + "<c:C attr='1'>peach</c:C>" + + "<c:C attr='2'/>" + + "<c:C attr='3'>orange</c:C>" + + "<c:C attr='4'/>" + + "</c:B>" + + "<c:B attr='2' xmlns:c='urn:c'>" + + "<c:C attr='5'>mango</c:C>" + + "</c:B>" + + "</g:A>", + "<?xml version='1.0' encoding='UTF-8'?>" + + "<g:A xmlns:g='urn:g'>" + + "<c:B attr='2' xmlns:c='urn:c'>" + + "<c:C attr='6'/>" + + "<c:C attr='7'>pear</c:C>" + + "<c:C attr='8'/>" + + "</c:B>" + + "</g:A>"}; + + private static final String[] RESULTS_INJECTED_SIZE1 = { + "<c:C attr='1' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\">peach</c:C>", + "<c:C attr='2' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\"/>", + "<c:C attr='3' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\">orange</c:C>", + "<c:C attr='4' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\"/>", + "<c:C attr='5' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\">mango</c:C>", + "<c:C attr='6' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\"/>", + "<c:C attr='7' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\">pear</c:C>", + "<c:C attr='8' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\"/>"}; + + private static final String[] RESULTS_INJECTED_SIZE2 = { + "<group>" + + "<c:C attr='1' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\">peach</c:C>" + + "<c:C attr='2' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\"/>" + + "</group>", + "<group>" + + "<c:C attr='3' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\">orange</c:C>" + + "<c:C attr='4' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\"/>" + + "</group>", + "<group>" + + "<c:C attr='5' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\">mango</c:C>" + + "<c:C attr='6' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\"/>" + + "</group>", + "<group>" + + "<c:C attr='7' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\">pear</c:C>" + + "<c:C attr='8' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\"/>" + + "</group>"}; + + private static final String[] RESULTS_INJECTED_SIZE3 = { + "<group>" + + "<c:C attr='1' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\">peach</c:C>" + + "<c:C attr='2' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\"/>" + + "<c:C attr='3' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\">orange</c:C>" + + "</group>", + "<group>" + + "<c:C attr='4' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\"/>" + + "<c:C attr='5' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\">mango</c:C>" + + "<c:C attr='6' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\"/>" + + "</group>", + "<group>" + + "<c:C attr='7' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\">pear</c:C>" + + "<c:C attr='8' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\"/>" + + "</group>"}; + + private static final String[] RESULTS_INJECTED_SIZE4 = { + "<group>" + + "<c:C attr='1' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\">peach</c:C>" + + "<c:C attr='2' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\"/>" + + "<c:C attr='3' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\">orange</c:C>" + + "<c:C attr='4' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\"/>" + + "</group>", + "<group>" + + "<c:C attr='5' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\">mango</c:C>" + + "<c:C attr='6' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\"/>" + + "<c:C attr='7' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\">pear</c:C>" + + "<c:C attr='8' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\"/>" + + "</group>"}; + + private static final String[] RESULTS_INJECTED_SIZE5 = { + "<group>" + + "<c:C attr='1' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\">peach</c:C>" + + "<c:C attr='2' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\"/>" + + "<c:C attr='3' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\">orange</c:C>" + + "<c:C attr='4' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\"/>" + + "<c:C attr='5' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\">mango</c:C>" + + "</group>", + "<group>" + + "<c:C attr='6' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\"/>" + + "<c:C attr='7' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\">pear</c:C>" + + "<c:C attr='8' xmlns:g=\"urn:g\" xmlns:c=\"urn:c\"/>" + + "</group>"}; + + private Map<String, String> nsmap; + + + @Override + public void setUp() { + nsmap = new HashMap<String, String>(); + nsmap.put("g", "urn:g"); + nsmap.put("c", "urn:c"); + } + + // wrapped mode + public void testExtractWrappedSize1() throws Exception { + invokeAndVerify("//c:C", + 'w', 1, new ByteArrayInputStream(TEST_BODY), "utf-8", RESULTS_WRAPPED_SIZE1); + } + + public void testExtractWrappedSize2() throws Exception { + invokeAndVerify("//c:C", + 'w', 2, new ByteArrayInputStream(TEST_BODY), "utf-8", RESULTS_WRAPPED_SIZE2); + } + + public void disabledtestExtractWrappedSize3L() throws Exception { + invokeAndVerify("//c:C", + 'w', 3, new ByteArrayInputStream(TEST_BODY), "utf-8", RESULTS_WRAPPED_SIZE3L); + } + + // disabled: not working for now as the context extraction across two ancestor paths is not working + public void disabledtestExtractWrappedSize3U() throws Exception { + invokeAndVerify("//c:C", + 'W', 3, new ByteArrayInputStream(TEST_BODY), "utf-8", RESULTS_WRAPPED_SIZE3U); + } + + public void testExtractWrappedSize4() throws Exception { + invokeAndVerify("//c:C", + 'w', 4, new ByteArrayInputStream(TEST_BODY), "utf-8", RESULTS_WRAPPED_SIZE4); + } + + public void disabledtestExtractWrappedSize5L() throws Exception { + invokeAndVerify("//c:C", + 'w', 5, new ByteArrayInputStream(TEST_BODY), "utf-8", RESULTS_WRAPPED_SIZE5L); + } + + // disabled: not working for now as the context extraction across two ancestor paths is not working + public void disabledtestExtractWrappedSize5U() throws Exception { + invokeAndVerify("//c:C", + 'W', 5, new ByteArrayInputStream(TEST_BODY), "utf-8", RESULTS_WRAPPED_SIZE5U); + } + + // injected mode + public void testExtractInjectedSize1() throws Exception { + invokeAndVerify("//c:C", + 'i', 1, new ByteArrayInputStream(TEST_BODY), "utf-8", RESULTS_INJECTED_SIZE1); + } + + public void testExtractInjectedSize2() throws Exception { + invokeAndVerify("//c:C", + 'i', 2, new ByteArrayInputStream(TEST_BODY), "utf-8", RESULTS_INJECTED_SIZE2); + } + + public void testExtractInjectedSize3() throws Exception { + invokeAndVerify("//c:C", + 'i', 3, new ByteArrayInputStream(TEST_BODY), "utf-8", RESULTS_INJECTED_SIZE3); + } + + public void testExtractInjectedSize4() throws Exception { + invokeAndVerify("//c:C", + 'i', 4, new ByteArrayInputStream(TEST_BODY), "utf-8", RESULTS_INJECTED_SIZE4); + } + + public void testExtractInjectedSize5() throws Exception { + invokeAndVerify("//c:C", + 'i', 5, new ByteArrayInputStream(TEST_BODY), "utf-8", RESULTS_INJECTED_SIZE5); + } + + public void testExtractWrappedLeftOver() throws Exception { + final byte[] data = ("<?xml version='1.0' encoding='UTF-8'?><g:A xmlns:g='urn:g'><c:B attr='1' xmlns:c='urn:c'>" + + "<c:C attr='1'>peach</c:C>" + + "<c:C attr='2'/>" + + "<c:C attr='3'>orange</c:C>" + + "</c:B></g:A>").getBytes(); + final String[] results = {"<?xml version='1.0' encoding='UTF-8'?><g:A xmlns:g='urn:g'><c:B attr='1' xmlns:c='urn:c'>" + + "<c:C attr='1'>peach</c:C><c:C attr='2'/>" + + "</c:B></g:A>", + "<?xml version='1.0' encoding='UTF-8'?><g:A xmlns:g='urn:g'><c:B attr='1' xmlns:c='urn:c'>" + + "<c:C attr='3'>orange</c:C>" + + "</c:B></g:A>"}; + invokeAndVerify("//c:C", + 'w', 2, new ByteArrayInputStream(data), "utf-8", results); + } + + private void invokeAndVerify(String path, char mode, int group, + InputStream in, String charset, String[] expected) + throws Exception { + XMLTokenExpressionIterator xtei = new XMLTokenExpressionIterator(path, mode); + xtei.setNamespaces(nsmap); + xtei.setGroup(group); + + Iterator<?> it = xtei.createIterator(in, "utf-8"); + List<String> results = new ArrayList<String>(); + while (it.hasNext()) { + results.add((String)it.next()); + } + ((Closeable)it).close(); + + assertEquals("token count", expected.length, results.size()); + for (int i = 0; i < expected.length; i++) { + assertEquals("mismatch [" + i + "]", expected[i], results.get(i)); + } + } +}