Merge "Bug 1892 - Karaf setenv should use '=' instead of '=='"
[controller.git] / opendaylight / md-sal / sal-clustering-commons / src / main / java / org / opendaylight / controller / xml / codec / XmlUtils.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.xml.codec;
9
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;
34
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;
48 import java.util.Map;
49 import java.util.Set;
50
51 /**
52  * Common XML-related utility methods, which are not specific to a particular
53  * JAXP API.
54  */
55 public class XmlUtils {
56
57   public static final XmlCodecProvider DEFAULT_XML_CODEC_PROVIDER = new XmlCodecProvider() {
58     @Override
59     public TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> codecFor(final TypeDefinition<?> baseType) {
60       return TypeDefinitionAwareCodec.from(baseType);
61     }
62   };
63
64   private XmlUtils() {
65   }
66
67   private static final String BLANK = "";
68   private static final Logger LOG = LoggerFactory.getLogger(XmlUtils.class);
69
70   /**
71    * Converts the composite node to xml using rpc input schema node
72    * @param cNode
73    * @param schemaContext
74    * @return xml String
75    */
76   public static String inputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){
77     if(LOG.isDebugEnabled()) {
78         LOG.debug("Converting input composite node to xml {}", cNode);
79     }
80     if (cNode == null) {
81         return BLANK;
82     }
83
84     if(schemaContext == null) {
85         return BLANK;
86     }
87
88     Document domTree = null;
89     try {
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());
95           }
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);
100           }
101           break;
102         }
103       }
104
105     } catch (UnsupportedDataTypeException e) {
106       LOG.error("Error during translation of CompositeNode to Document", e);
107     }
108     return domTransformer(domTree);
109   }
110
111   /**
112    * Converts the composite node to xml String using rpc output schema node
113    * @param cNode
114    * @param schemaContext
115    * @return xml string
116    */
117   public static String outputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){
118     if(LOG.isDebugEnabled()) {
119         LOG.debug("Converting output composite node to xml {}", cNode);
120     }
121     if (cNode == null) {
122         return BLANK;
123     }
124
125     if(schemaContext == null) {
126         return BLANK;
127     }
128
129     Document domTree = null;
130     try {
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());
136           }
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);
141           }
142           break;
143         }
144       }
145
146     } catch (UnsupportedDataTypeException e) {
147       LOG.error("Error during translation of CompositeNode to Document", e);
148     }
149     return domTransformer(domTree);
150   }
151
152   private static String domTransformer(Document domTree) {
153     StringWriter writer = new StringWriter();
154     try {
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) {
160
161       LOG.error("Error during translation of Document to OutputStream", e);
162     }
163     if(LOG.isDebugEnabled()) {
164         LOG.debug("Document to string conversion complete, xml string is  {} ", writer.toString());
165     }
166     return writer.toString();
167   }
168
169   public static CompositeNode xmlToCompositeNode(String xml){
170     if (xml==null || xml.length()==0) {
171         return null;
172     }
173
174     Node<?> dataTree;
175     try {
176       dataTree = XmlTreeBuilder.buildDataTree(new ByteArrayInputStream(xml.getBytes()));
177     } catch (XMLStreamException e) {
178       LOG.error("Error during building data tree from XML", e);
179       return null;
180     }
181     if (dataTree == null) {
182       LOG.error("data tree is null");
183       return null;
184     }
185     if (dataTree instanceof SimpleNode) {
186       LOG.error("RPC XML was resolved as SimpleNode");
187       return null;
188     }
189     return (CompositeNode) dataTree;
190   }
191
192   /**
193    * Converts the xml to composite node using rpc input schema node
194    * @param rpc
195    * @param xml
196    * @param schemaContext
197    * @return CompositeNode object based on the input, if any of the input parameter is null, a null object is returned
198    */
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);
202     }
203     if (xml==null || xml.length()==0) {
204         return null;
205     }
206
207     if(rpc == null) {
208         return null;
209     }
210
211     if(schemaContext == null) {
212         return null;
213     }
214
215     CompositeNode compositeNode = null;
216     try {
217
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);
224           }
225           if(rpcDef.getInput() == null) {
226             LOG.warn("found rpc definition's input is null");
227             return null;
228           }
229
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);
234             return null;
235           }
236           Element xmlData = (Element)nodeList.item(0);
237
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);
242           }
243           final CompositeNodeBuilder<ImmutableCompositeNode> it = ImmutableCompositeNode.builder();
244           it.setQName(rpc);
245           it.add(ImmutableCompositeNode.create(input, dataNodes));
246           compositeNode = it.toInstance();
247           break;
248         }
249       }
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);
254     }
255     if(LOG.isDebugEnabled()) {
256         LOG.debug("Xml to composite node conversion complete {} ", compositeNode);
257     }
258     return compositeNode;
259   }
260
261   public static TypeDefinition<?> resolveBaseTypeFrom(final @Nonnull TypeDefinition<?> type) {
262     TypeDefinition<?> superType = type;
263     while (superType.getBaseType() != null) {
264       superType = superType.getBaseType();
265     }
266     return superType;
267   }
268
269   /**
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']
277    * </flow-ref>
278    *
279    */
280
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();
288
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("']");
300         }
301       } else if (pathArgument instanceof NodeWithValue) {
302         textContent.append("[.='");
303         textContent.append(((NodeWithValue) pathArgument).getValue());
304         textContent.append("']");
305       }
306     }
307
308     return textContent.toString();
309   }
310 }