Repository: camel Updated Branches: refs/heads/master 059aaf024 -> 94d527783
CAMEL-8250: camel-saxon add type converter for Saxon types. Thanks to Stephan Siano for the patch. Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/94d52778 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/94d52778 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/94d52778 Branch: refs/heads/master Commit: 94d527783719f495c05df4caea7dcece2113d065 Parents: 059aaf0 Author: Claus Ibsen <davscl...@apache.org> Authored: Sat Jan 17 11:50:03 2015 +0100 Committer: Claus Ibsen <davscl...@apache.org> Committed: Sat Jan 17 11:50:03 2015 +0100 ---------------------------------------------------------------------- components/camel-saxon/pom.xml | 3 +- .../camel/converter/saxon/SaxonConverter.java | 132 +++++++++++++++++ .../services/org/apache/camel/TypeConverter | 18 +++ .../converter/saxon/SaxonConverterTest.java | 143 +++++++++++++++++++ 4 files changed, 295 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/94d52778/components/camel-saxon/pom.xml ---------------------------------------------------------------------- diff --git a/components/camel-saxon/pom.xml b/components/camel-saxon/pom.xml index d2da420..bc0cc9f 100644 --- a/components/camel-saxon/pom.xml +++ b/components/camel-saxon/pom.xml @@ -32,7 +32,8 @@ <properties> <camel.osgi.export.pkg> org.apache.camel.component.xquery.*, - org.apache.camel.language.xquery.* + org.apache.camel.language.xquery.*, + org.apache.camel.converter.saxon.* </camel.osgi.export.pkg> <camel.osgi.export.service> org.apache.camel.spi.ComponentResolver;component=xquery, http://git-wip-us.apache.org/repos/asf/camel/blob/94d52778/components/camel-saxon/src/main/java/org/apache/camel/converter/saxon/SaxonConverter.java ---------------------------------------------------------------------- diff --git a/components/camel-saxon/src/main/java/org/apache/camel/converter/saxon/SaxonConverter.java b/components/camel-saxon/src/main/java/org/apache/camel/converter/saxon/SaxonConverter.java new file mode 100644 index 0000000..5e18e64 --- /dev/null +++ b/components/camel-saxon/src/main/java/org/apache/camel/converter/saxon/SaxonConverter.java @@ -0,0 +1,132 @@ +/** + * 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.converter.saxon; + +import java.util.LinkedList; +import java.util.List; +import javax.xml.transform.dom.DOMSource; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import net.sf.saxon.Configuration; +import net.sf.saxon.dom.DOMNodeList; +import net.sf.saxon.dom.NodeOverNodeInfo; +import net.sf.saxon.om.DocumentInfo; +import net.sf.saxon.om.NodeInfo; +import net.sf.saxon.trans.XPathException; +import net.sf.saxon.type.Type; + +import org.apache.camel.Converter; +import org.apache.camel.Exchange; +import org.apache.camel.FallbackConverter; +import org.apache.camel.TypeConverter; +import org.apache.camel.spi.TypeConverterRegistry; + +@Converter +public final class SaxonConverter { + + private SaxonConverter() { + } + + @Converter + public static Document toDOMDocument(NodeInfo node) throws XPathException { + switch (node.getNodeKind()) { + case Type.DOCUMENT: + // DOCUMENT type nodes can be wrapped directly + return (Document) NodeOverNodeInfo.wrap(node); + case Type.ELEMENT: + // ELEMENT nodes need to build a new DocumentInfo before wrapping + Configuration config = node.getConfiguration(); + DocumentInfo documentInfo = config.buildDocument(node); + return (Document) NodeOverNodeInfo.wrap(documentInfo); + default: + return null; + } + } + + @Converter + public static Node toDOMNode(NodeInfo node) { + return NodeOverNodeInfo.wrap(node); + } + + @Converter + public static DOMSource toDOMSourceFromNodeInfo(NodeInfo nodeInfo) { + return new DOMSource(toDOMNode(nodeInfo)); + } + + @Converter + public static NodeList toDOMNodeList(List<? extends NodeInfo> nodeList) { + List<Node> domNodeList = new LinkedList<Node>(); + if (nodeList != null) { + for (NodeInfo ni : nodeList) { + domNodeList.add(NodeOverNodeInfo.wrap(ni)); + } + } + return new DOMNodeList(domNodeList); + } + + @FallbackConverter + public static <T> T convertTo(Class<T> type, Exchange exchange, Object value, TypeConverterRegistry registry) { + if (NodeInfo.class.isAssignableFrom(value.getClass())) { + // use a fallback type converter so we can convert the embedded body if the value is NodeInfo + NodeInfo ni = (NodeInfo) value; + // first try to find a Converter for Node + TypeConverter tc = registry.lookup(type, Node.class); + if (tc != null) { + Node node = NodeOverNodeInfo.wrap(ni); + return tc.convertTo(type, exchange, node); + } + // if this does not exist we can also try NodeList (there are some type converters for that) as + // the default Xerces Node implementation also implements NodeList. + tc = registry.lookup(type, NodeList.class); + if (tc != null) { + List<NodeInfo> nil = new LinkedList<NodeInfo>(); + nil.add((NodeInfo) value); + return tc.convertTo(type, exchange, toDOMNodeList(nil)); + } + } else if (List.class.isAssignableFrom(value.getClass())) { + TypeConverter tc = registry.lookup(type, NodeList.class); + if (tc != null) { + List<NodeInfo> lion = new LinkedList<NodeInfo>(); + for (Object o : (List<?>) value) { + if (o instanceof NodeInfo) { + lion.add((NodeInfo) o); + } + } + if (lion.size() > 0) { + NodeList nl = toDOMNodeList(lion); + return tc.convertTo(type, exchange, nl); + } + } + } else if (NodeOverNodeInfo.class.isAssignableFrom(value.getClass())) { + // NodeOverNode info is a read-only Node implementation from Saxon. In contrast to the JDK + // com.sun.org.apache.xerces.internal.dom.NodeImpl class it does not implement NodeList, but + // many Camel type converters are based on that interface. Therefore we convert to NodeList and + // try type conversion in the fallback type converter. + TypeConverter tc = registry.lookup(type, NodeList.class); + if (tc != null) { + List<Node> domNodeList = new LinkedList<Node>(); + domNodeList.add((NodeOverNodeInfo) value); + return tc.convertTo(type, exchange, new DOMNodeList(domNodeList)); + } + } + + return null; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/94d52778/components/camel-saxon/src/main/resources/META-INF/services/org/apache/camel/TypeConverter ---------------------------------------------------------------------- diff --git a/components/camel-saxon/src/main/resources/META-INF/services/org/apache/camel/TypeConverter b/components/camel-saxon/src/main/resources/META-INF/services/org/apache/camel/TypeConverter new file mode 100644 index 0000000..b666928 --- /dev/null +++ b/components/camel-saxon/src/main/resources/META-INF/services/org/apache/camel/TypeConverter @@ -0,0 +1,18 @@ +# +# 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. +# + +org.apache.camel.converter.saxon.SaxonConverter \ No newline at end of file http://git-wip-us.apache.org/repos/asf/camel/blob/94d52778/components/camel-saxon/src/test/java/org/apache/camel/converter/saxon/SaxonConverterTest.java ---------------------------------------------------------------------- diff --git a/components/camel-saxon/src/test/java/org/apache/camel/converter/saxon/SaxonConverterTest.java b/components/camel-saxon/src/test/java/org/apache/camel/converter/saxon/SaxonConverterTest.java new file mode 100644 index 0000000..5509dc3 --- /dev/null +++ b/components/camel-saxon/src/test/java/org/apache/camel/converter/saxon/SaxonConverterTest.java @@ -0,0 +1,143 @@ +/** + * 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.converter.saxon; + +import java.io.InputStream; +import java.util.LinkedList; +import java.util.List; + +import javax.xml.namespace.NamespaceContext; +import javax.xml.transform.dom.DOMSource; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import net.sf.saxon.om.NodeInfo; +import net.sf.saxon.trans.XPathException; +import net.sf.saxon.xpath.XPathEvaluator; + +import org.apache.camel.Exchange; +import org.apache.camel.StringSource; +import org.apache.camel.builder.xml.DefaultNamespaceContext; +import org.apache.camel.impl.DefaultExchange; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Before; +import org.junit.Test; + +public class SaxonConverterTest extends CamelTestSupport { + private static final String CONTENT = "<a xmlns=\"http://www.apache.org/test\"><b foo=\"bar\">test</b><c><d>foobar</d></c></a>"; + private static final String CONTENT_B = "<b xmlns=\"http://www.apache.org/test\" foo=\"bar\">test</b>"; + private static final NamespaceContext NS_CONTEXT = (new DefaultNamespaceContext()).add("ns1", "http://www.apache.org/test"); + + private Exchange exchange; + private XPathEvaluator evaluator; + private NodeInfo doc; + + @Before + public void setUp() throws Exception { + super.setUp(); + exchange = new DefaultExchange(context); + evaluator = new XPathEvaluator(); + doc = evaluator.setSource(new StringSource(CONTENT)); + } + + @Test + public void convertToDOMSource() throws XPathException { + DOMSource source = context.getTypeConverter().convertTo(DOMSource.class, exchange, doc); + assertNotNull(source); + String string = context.getTypeConverter().convertTo(String.class, exchange, source); + assertEquals(CONTENT, string); + } + + @Test + public void convertToDocument() throws XPathException { + Document document = context.getTypeConverter().convertTo(Document.class, exchange, doc); + assertNotNull(document); + String string = context.getTypeConverter().convertTo(String.class, exchange, document); + assertEquals(CONTENT, string); + } + + @Test + public void convertSubNodeToDocument() throws XPathException, XPathExpressionException { + evaluator.setNamespaceContext(NS_CONTEXT); + Object nodeObj = evaluator.evaluate("/ns1:a/ns1:b", doc, XPathConstants.NODE); + assertNotNull(nodeObj); + Document document = context.getTypeConverter().convertTo(Document.class, exchange, nodeObj); + assertNotNull(document); + String string = context.getTypeConverter().convertTo(String.class, exchange, document); + assertEquals(CONTENT_B, string); + } + + @Test + public void convertSubNodeSetToDocument() throws XPathException, XPathExpressionException { + evaluator.setNamespaceContext(NS_CONTEXT); + Object nodeObj = evaluator.evaluate("/ns1:a/ns1:b", doc, XPathConstants.NODESET); + assertNotNull(nodeObj); + Document document = context.getTypeConverter().convertTo(Document.class, exchange, nodeObj); + assertNotNull(document); + String string = context.getTypeConverter().convertTo(String.class, exchange, document); + assertEquals(CONTENT_B, string); + } + + @Test + public void convertToNode() throws XPathException { + Node node = context.getTypeConverter().convertTo(Node.class, exchange, doc); + assertNotNull(node); + String string = context.getTypeConverter().convertTo(String.class, exchange, node); + assertEquals(CONTENT, string); + } + + @Test + public void convertToNodeList() throws XPathException { + List<NodeInfo> nil = new LinkedList<NodeInfo>(); + nil.add(doc); + NodeList nodeList = context.getTypeConverter().convertTo(NodeList.class, exchange, nil); + assertNotNull(nodeList); + assertEquals(1, nodeList.getLength()); + String string = context.getTypeConverter().convertTo(String.class, exchange, nodeList); + assertEquals(CONTENT, string); + } + + @Test + public void convertToInputStream() throws XPathException { + InputStream is = context.getTypeConverter().convertTo(InputStream.class, exchange, doc); + assertNotNull(is); + String string = context.getTypeConverter().convertTo(String.class, exchange, is); + assertEquals(CONTENT, string); + } + + @Test + public void convertToByteArray() throws XPathException { + byte[] ba = context.getTypeConverter().convertTo(byte[].class, exchange, doc); + assertNotNull(ba); + String string = context.getTypeConverter().convertTo(String.class, exchange, ba); + assertEquals(CONTENT, string); + } + + @Test + public void convertToNodeAndByteArray() throws XPathException { + Node node = context.getTypeConverter().convertTo(Node.class, exchange, doc); + assertNotNull(node); + byte[] ba = context.getTypeConverter().convertTo(byte[].class, exchange, node); + assertNotNull(ba); + String string = context.getTypeConverter().convertTo(String.class, exchange, ba); + assertEquals(CONTENT, string); + } +}