2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.controller.xml.codec;
10 import com.google.common.base.Optional;
11 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
12 import org.opendaylight.yangtools.yang.common.QName;
13 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
14 import org.opendaylight.yangtools.yang.data.api.Node;
15 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
16 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
17 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
18 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
20 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
21 import org.opendaylight.yangtools.yang.data.impl.XmlTreeBuilder;
22 import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
23 import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider;
24 import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
25 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
26 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
27 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30 import org.w3c.dom.Document;
31 import org.w3c.dom.Element;
32 import org.w3c.dom.NodeList;
33 import org.xml.sax.SAXException;
35 import javax.activation.UnsupportedDataTypeException;
36 import javax.annotation.Nonnull;
37 import javax.xml.stream.XMLStreamException;
38 import javax.xml.transform.OutputKeys;
39 import javax.xml.transform.Transformer;
40 import javax.xml.transform.TransformerException;
41 import javax.xml.transform.TransformerFactory;
42 import javax.xml.transform.dom.DOMSource;
43 import javax.xml.transform.stream.StreamResult;
44 import java.io.ByteArrayInputStream;
45 import java.io.IOException;
46 import java.io.StringWriter;
47 import java.util.List;
52 * Common XML-related utility methods, which are not specific to a particular
55 public class XmlUtils {
57 public static final XmlCodecProvider DEFAULT_XML_CODEC_PROVIDER = new XmlCodecProvider() {
59 public TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> codecFor(final TypeDefinition<?> baseType) {
60 return TypeDefinitionAwareCodec.from(baseType);
67 private static final String BLANK = "";
68 private static final Logger LOG = LoggerFactory.getLogger(XmlUtils.class);
71 * Converts the composite node to xml using rpc input schema node
73 * @param schemaContext
76 public static String inputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){
77 if(LOG.isDebugEnabled()) {
78 LOG.debug("Converting input composite node to xml {}", cNode);
84 if(schemaContext == null) {
88 Document domTree = null;
90 Set<RpcDefinition> rpcs = schemaContext.getOperations();
91 for(RpcDefinition rpc : rpcs) {
92 if(rpc.getQName().equals(cNode.getNodeType())){
93 if(LOG.isDebugEnabled()) {
94 LOG.debug("Found the rpc definition from schema context matching with input composite node {}", rpc.getQName());
96 CompositeNode inputContainer = cNode.getFirstCompositeByName(QName.create(cNode.getNodeType(), "input"));
97 domTree = XmlDocumentUtils.toDocument(inputContainer, rpc.getInput(), XmlDocumentUtils.defaultValueCodecProvider());
98 if(LOG.isDebugEnabled()) {
99 LOG.debug("input composite node to document conversion complete, document is {}", domTree);
105 } catch (UnsupportedDataTypeException e) {
106 LOG.error("Error during translation of CompositeNode to Document", e);
108 return domTransformer(domTree);
112 * Converts the composite node to xml String using rpc output schema node
114 * @param schemaContext
117 public static String outputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){
118 if(LOG.isDebugEnabled()) {
119 LOG.debug("Converting output composite node to xml {}", cNode);
125 if(schemaContext == null) {
129 Document domTree = null;
131 Set<RpcDefinition> rpcs = schemaContext.getOperations();
132 for(RpcDefinition rpc : rpcs) {
133 if(rpc.getQName().equals(cNode.getNodeType())){
134 if(LOG.isDebugEnabled()) {
135 LOG.debug("Found the rpc definition from schema context matching with output composite node {}", rpc.getQName());
137 CompositeNode outputContainer = cNode.getFirstCompositeByName(QName.create(cNode.getNodeType(), "output"));
138 domTree = XmlDocumentUtils.toDocument(outputContainer, rpc.getOutput(), XmlDocumentUtils.defaultValueCodecProvider());
139 if(LOG.isDebugEnabled()) {
140 LOG.debug("output composite node to document conversion complete, document is {}", domTree);
146 } catch (UnsupportedDataTypeException e) {
147 LOG.error("Error during translation of CompositeNode to Document", e);
149 return domTransformer(domTree);
152 private static String domTransformer(Document domTree) {
153 StringWriter writer = new StringWriter();
155 TransformerFactory tf = TransformerFactory.newInstance();
156 Transformer transformer = tf.newTransformer();
157 transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
158 transformer.transform(new DOMSource(domTree), new StreamResult(writer));
159 } catch (TransformerException e) {
161 LOG.error("Error during translation of Document to OutputStream", e);
163 if(LOG.isDebugEnabled()) {
164 LOG.debug("Document to string conversion complete, xml string is {} ", writer.toString());
166 return writer.toString();
169 public static CompositeNode xmlToCompositeNode(String xml){
170 if (xml==null || xml.length()==0) {
176 dataTree = XmlTreeBuilder.buildDataTree(new ByteArrayInputStream(xml.getBytes()));
177 } catch (XMLStreamException e) {
178 LOG.error("Error during building data tree from XML", e);
181 if (dataTree == null) {
182 LOG.error("data tree is null");
185 if (dataTree instanceof SimpleNode) {
186 LOG.error("RPC XML was resolved as SimpleNode");
189 return (CompositeNode) dataTree;
193 * Converts the xml to composite node using rpc input schema node
196 * @param schemaContext
197 * @return CompositeNode object based on the input, if any of the input parameter is null, a null object is returned
199 public static CompositeNode inputXmlToCompositeNode(QName rpc, String xml, SchemaContext schemaContext){
200 if(LOG.isDebugEnabled()) {
201 LOG.debug("Converting input xml to composite node {}", xml);
203 if (xml==null || xml.length()==0) {
211 if(schemaContext == null) {
215 CompositeNode compositeNode = null;
218 Document doc = XmlUtil.readXmlToDocument(xml);
219 Set<RpcDefinition> rpcs = schemaContext.getOperations();
220 for(RpcDefinition rpcDef : rpcs) {
221 if(rpcDef.getQName().equals(rpc)){
222 if(LOG.isDebugEnabled()) {
223 LOG.debug("found the rpc definition from schema context matching rpc {}", rpc);
225 if(rpcDef.getInput() == null) {
226 LOG.warn("found rpc definition's input is null");
230 QName input = rpcDef.getInput().getQName();
231 NodeList nodeList = doc.getElementsByTagNameNS(input.getNamespace().toString(), "input");
232 if(nodeList == null || nodeList.getLength() < 1) {
233 LOG.warn("xml does not have input entry. {}", xml);
236 Element xmlData = (Element)nodeList.item(0);
238 List<Node<?>> dataNodes = XmlDocumentUtils.toDomNodes(xmlData,
239 Optional.of(rpcDef.getInput().getChildNodes()), schemaContext);
240 if(LOG.isDebugEnabled()) {
241 LOG.debug("Converted xml input to list of nodes {}", dataNodes);
243 final CompositeNodeBuilder<ImmutableCompositeNode> it = ImmutableCompositeNode.builder();
245 it.add(ImmutableCompositeNode.create(input, dataNodes));
246 compositeNode = it.toInstance();
250 } catch (SAXException e) {
251 LOG.error("Error during building data tree from XML", e);
252 } catch (IOException e) {
253 LOG.error("Error during building data tree from XML", e);
255 if(LOG.isDebugEnabled()) {
256 LOG.debug("Xml to composite node conversion complete {} ", compositeNode);
258 return compositeNode;
261 public static TypeDefinition<?> resolveBaseTypeFrom(final @Nonnull TypeDefinition<?> type) {
262 TypeDefinition<?> superType = type;
263 while (superType.getBaseType() != null) {
264 superType = superType.getBaseType();
270 * This code is picked from yangtools and modified to add type of instance identifier
271 * output of instance identifier something like below for a flow ref composite node of type instance identifier,
272 * which has path arguments with predicates, whose value is of type java.lang.short
273 * <flow-ref xmlns:bgkj="urn:opendaylight:flow:inventory" xmlns:jdlk="urn:opendaylight:inventory">
274 * /jdlk:nodes/jdlk:node[jdlk:id='openflow:205558455098190@java.lang.String']
275 * /bgkj:table[bgkj:id='3@java.lang.Short']
276 * /bgkj:flow[bgkj:id='156@java.lang.String']
281 public static String encodeIdentifier(final RandomPrefix prefixes, final YangInstanceIdentifier id) {
282 StringBuilder textContent = new StringBuilder();
283 for (PathArgument pathArgument : id.getPathArguments()) {
284 textContent.append('/');
285 textContent.append(prefixes.encodeQName(pathArgument.getNodeType()));
286 if (pathArgument instanceof NodeIdentifierWithPredicates) {
287 Map<QName, Object> predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues();
289 for (QName keyValue : predicates.keySet()) {
290 Object value = predicates.get(keyValue);
291 String type = value.getClass().getName();
292 String predicateValue = String.valueOf(value);
293 textContent.append('[');
294 textContent.append(prefixes.encodeQName(keyValue));
295 textContent.append("='");
296 textContent.append(predicateValue);
297 textContent.append("@");
298 textContent.append(type);
299 textContent.append("']");
301 } else if (pathArgument instanceof NodeWithValue) {
302 textContent.append("[.='");
303 textContent.append(((NodeWithValue) pathArgument).getValue());
304 textContent.append("']");
308 return textContent.toString();