Merge "Bug 1328: Improved argument checks in generated RPC Router"
[controller.git] / opendaylight / md-sal / sal-protocolbuffer-encoding / src / main / java / org / opendaylight / controller / cluster / datastore / util / EncoderDecoderUtil.java
1 package org.opendaylight.controller.cluster.datastore.util;
2
3 import com.google.common.base.Preconditions;
4 import com.google.common.collect.Lists;
5 import org.opendaylight.controller.protobuff.messages.common.SimpleNormalizedNodeMessage;
6 import org.opendaylight.yangtools.yang.common.QName;
7 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
8 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
9 import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
10 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
11 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
12 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.serializer.DomFromNormalizedNodeSerializerFactory;
13 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
14 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
15 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
16 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
17 import org.opendaylight.yangtools.yang.model.api.Module;
18 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
19 import org.w3c.dom.Document;
20 import org.w3c.dom.Element;
21
22 import javax.xml.parsers.DocumentBuilderFactory;
23 import javax.xml.transform.OutputKeys;
24 import javax.xml.transform.Transformer;
25 import javax.xml.transform.TransformerException;
26 import javax.xml.transform.TransformerFactory;
27 import javax.xml.transform.TransformerFactoryConfigurationError;
28 import javax.xml.transform.dom.DOMSource;
29 import javax.xml.transform.stream.StreamResult;
30 import java.io.ByteArrayInputStream;
31 import java.io.StringWriter;
32 import java.util.Collections;
33 import java.util.List;
34 import java.util.Set;
35
36 /*
37  * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
38  *
39  * This program and the accompanying materials are made available under the terms of the Eclipse
40  * Public License v1.0 which accompanies this distribution, and is available at
41  * http://www.eclipse.org/legal/epl-v10.html
42  */
43 /*
44  *
45  * <code>EncoderDecoderUtil</code> helps in wrapping the NormalizedNode into a SimpleNormalizedNode
46  * protobuf message containing the XML representation of the NormalizeNode
47  *
48  * @author: syedbahm
49  */
50 public class EncoderDecoderUtil {
51   static DocumentBuilderFactory factory;
52   static {
53     factory = DocumentBuilderFactory.newInstance();
54     factory.setNamespaceAware(true);
55     factory.setCoalescing(true);
56     factory.setIgnoringElementContentWhitespace(true);
57     factory.setIgnoringComments(true);
58   }
59
60   private static DataSchemaNode findChildNode(Set<DataSchemaNode> children,
61       String name) {
62     List<DataNodeContainer> containers = Lists.newArrayList();
63
64     for (DataSchemaNode dataSchemaNode : children) {
65       if (dataSchemaNode.getQName().getLocalName().equals(name))
66         return dataSchemaNode;
67       if (dataSchemaNode instanceof DataNodeContainer) {
68         containers.add((DataNodeContainer) dataSchemaNode);
69       } else if (dataSchemaNode instanceof ChoiceNode) {
70         containers.addAll(((ChoiceNode) dataSchemaNode).getCases());
71       }
72     }
73
74     for (DataNodeContainer container : containers) {
75       DataSchemaNode retVal = findChildNode(container.getChildNodes(), name);
76       if (retVal != null) {
77         return retVal;
78       }
79     }
80
81     return null;
82   }
83
84   private static DataSchemaNode getSchemaNode(SchemaContext context, QName qname) {
85
86     for (Module module : context.findModuleByNamespace(qname.getNamespace())) {
87       // we will take the first child as the start of the
88       if (module.getChildNodes() != null || !module.getChildNodes().isEmpty()) {
89
90         DataSchemaNode found =
91             findChildNode(module.getChildNodes(), qname.getLocalName());
92         return found;
93       }
94     }
95     return null;
96   }
97
98   private static String toString(Element xml) {
99     try {
100       Transformer transformer =
101           TransformerFactory.newInstance().newTransformer();
102       transformer.setOutputProperty(OutputKeys.INDENT, "yes");
103
104       StreamResult result = new StreamResult(new StringWriter());
105       DOMSource source = new DOMSource(xml);
106       transformer.transform(source, result);
107
108       return result.getWriter().toString();
109     } catch (IllegalArgumentException | TransformerFactoryConfigurationError
110         | TransformerException e) {
111       throw new RuntimeException("Unable to serialize xml element " + xml, e);
112     }
113   }
114
115   /**
116    * Helps in generation of NormalizedNodeXml message for the supplied NormalizedNode
117    *
118    * @param sc --SchemaContext
119    * @param normalizedNode -- Normalized Node to be encoded
120    * @return SimpleNormalizedNodeMessage.NormalizedNodeXml
121    */
122   public static SimpleNormalizedNodeMessage.NormalizedNodeXml encode(
123       SchemaContext sc, NormalizedNode<?, ?> normalizedNode) {
124     Preconditions.checkArgument(sc != null, "Schema context found null");
125     Preconditions.checkArgument(normalizedNode != null,
126         "normalized node found null");
127     ContainerSchemaNode containerNode =
128         (ContainerSchemaNode) getSchemaNode(sc, normalizedNode.getIdentifier()
129             .getNodeType());
130     Preconditions.checkState(containerNode != null,
131         "Couldn't find schema node for " + normalizedNode.getIdentifier());
132     Iterable<Element> els =
133         DomFromNormalizedNodeSerializerFactory
134             .getInstance(XmlDocumentUtils.getDocument(),
135                 DomUtils.defaultValueCodecProvider())
136             .getContainerNodeSerializer()
137             .serialize(containerNode, (ContainerNode) normalizedNode);
138     String xmlString = toString(els.iterator().next());
139     SimpleNormalizedNodeMessage.NormalizedNodeXml.Builder builder =
140         SimpleNormalizedNodeMessage.NormalizedNodeXml.newBuilder();
141     builder.setXmlString(xmlString);
142     builder.setNodeIdentifier(((ContainerNode) normalizedNode).getIdentifier()
143         .getNodeType().toString());
144     return builder.build();
145
146   }
147
148   /**
149    * Utilizes the SimpleNormalizedNodeMessage.NormalizedNodeXml to convert into NormalizedNode
150    *
151    * @param sc -- schema context
152    * @param normalizedNodeXml -- containing the normalized Node XML
153    * @return NormalizedNode return
154    * @throws Exception
155    */
156
157   public static NormalizedNode decode(SchemaContext sc,
158       SimpleNormalizedNodeMessage.NormalizedNodeXml normalizedNodeXml)
159       throws Exception {
160     Preconditions.checkArgument(sc != null, "schema context seems to be null");
161     Preconditions.checkArgument(normalizedNodeXml != null,
162         "SimpleNormalizedNodeMessage.NormalizedNodeXml found to be null");
163     QName qname = QName.create(normalizedNodeXml.getNodeIdentifier());
164
165     // here we will try to get back the NormalizedNode
166     ContainerSchemaNode containerSchemaNode =
167         (ContainerSchemaNode) getSchemaNode(sc, qname);
168
169     // now we need to read the XML
170
171     Document doc =
172         factory.newDocumentBuilder().parse(
173             new ByteArrayInputStream(normalizedNodeXml.getXmlString().getBytes(
174                 "utf-8")));
175     doc.getDocumentElement().normalize();
176
177     ContainerNode result =
178         DomToNormalizedNodeParserFactory
179             .getInstance(DomUtils.defaultValueCodecProvider())
180             .getContainerNodeParser()
181             .parse(Collections.singletonList(doc.getDocumentElement()),
182                 containerSchemaNode);
183
184     return (NormalizedNode) result;
185
186   }
187
188
189
190 }