/*
*
* Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*
*/
package org.opendaylight.controller.cluster.datastore.util;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import org.opendaylight.controller.protobuff.messages.common.SimpleNormalizedNodeMessage;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.serializer.DomFromNormalizedNodeSerializerFactory;
import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.StringWriter;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/*
*
* EncoderDecoderUtil
helps in wrapping the NormalizedNode into a SimpleNormalizedNode
* protobuf message containing the XML representation of the NormalizeNode
*
* @author: syedbahm
*/
public class EncoderDecoderUtil {
static DocumentBuilderFactory factory;
private static DomFromNormalizedNodeSerializerFactory serializerFactory =
DomFromNormalizedNodeSerializerFactory
.getInstance(XmlDocumentUtils.getDocument(),
DomUtils.defaultValueCodecProvider());
private static DomToNormalizedNodeParserFactory parserFactory =
DomToNormalizedNodeParserFactory
.getInstance(DomUtils.defaultValueCodecProvider());
static {
factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
factory.setCoalescing(true);
factory.setIgnoringElementContentWhitespace(true);
factory.setIgnoringComments(true);
}
private static DataSchemaNode findChildNode(Collection children,
String name) {
List containers = Lists.newArrayList();
for (DataSchemaNode dataSchemaNode : children) {
if (dataSchemaNode.getQName().getLocalName().equals(name))
return dataSchemaNode;
if (dataSchemaNode instanceof DataNodeContainer) {
containers.add((DataNodeContainer) dataSchemaNode);
} else if (dataSchemaNode instanceof ChoiceNode) {
containers.addAll(((ChoiceNode) dataSchemaNode).getCases());
}
}
for (DataNodeContainer container : containers) {
DataSchemaNode retVal =
findChildNode(container.getChildNodes(), name);
if (retVal != null) {
return retVal;
}
}
return null;
}
private static DataSchemaNode getSchemaNode(SchemaContext context,
QName qname) {
for (Module module : context
.findModuleByNamespace(qname.getNamespace())) {
// we will take the first child as the start of the
if (module.getChildNodes() != null || !module.getChildNodes()
.isEmpty()) {
DataSchemaNode found =
findChildNode(module.getChildNodes(), qname.getLocalName());
return found;
}
}
return null;
}
private static String toString(Element xml) {
try {
Transformer transformer =
TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StreamResult result = new StreamResult(new StringWriter());
DOMSource source = new DOMSource(xml);
transformer.transform(source, result);
return result.getWriter().toString();
} catch (IllegalArgumentException | TransformerFactoryConfigurationError
| TransformerException e) {
throw new RuntimeException("Unable to serialize xml element " + xml,
e);
}
}
private static String toString(Iterable xmlIterable) {
try {
Transformer transformer =
TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StreamResult result = new StreamResult(new StringWriter());
Iterator iterator = xmlIterable.iterator();
DOMSource source;
if(iterator.hasNext()) {
source = new DOMSource((org.w3c.dom.Node) iterator.next());
transformer.transform(source, result);
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
}
while(iterator.hasNext()) {
source = new DOMSource((org.w3c.dom.Node) iterator.next());
transformer.transform(source, result);
}
System.out.println(result.getWriter().toString());
return result.getWriter().toString();
} catch (IllegalArgumentException | TransformerFactoryConfigurationError
| TransformerException e) {
throw new RuntimeException("Unable to serialize xml element(s) " + xmlIterable.toString(),
e);
}
}
private static Iterable serialize(DataSchemaNode schemaNode, NormalizedNode normalizedNode){
if(schemaNode instanceof ContainerSchemaNode){ //1
return serializerFactory
.getContainerNodeSerializer()
.serialize((ContainerSchemaNode) schemaNode,
(ContainerNode) normalizedNode);
} else if(schemaNode instanceof ChoiceNode){ //2
return serializerFactory
.getChoiceNodeSerializer()
.serialize((ChoiceNode) schemaNode,
(org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode) normalizedNode);
} else if(schemaNode instanceof LeafSchemaNode){ //3
return serializerFactory
.getLeafNodeSerializer()
.serialize((LeafSchemaNode) schemaNode, (LeafNode) normalizedNode);
} else if(schemaNode instanceof ListSchemaNode){ //4
return serializerFactory
.getMapNodeSerializer()
.serialize((ListSchemaNode) schemaNode, (MapNode) normalizedNode);
} else if(schemaNode instanceof LeafListSchemaNode){ //5
return serializerFactory
.getLeafSetNodeSerializer()
.serialize((LeafListSchemaNode) schemaNode, (LeafSetNode) normalizedNode);
} else if(schemaNode instanceof AugmentationSchema){//6
return serializerFactory
.getAugmentationNodeSerializer()
.serialize((AugmentationSchema) schemaNode, (AugmentationNode) normalizedNode);
} else if(schemaNode instanceof ListSchemaNode && normalizedNode instanceof LeafSetEntryNode){ //7
return serializerFactory
.getLeafSetEntryNodeSerializer()
.serialize((LeafListSchemaNode) schemaNode, (LeafSetEntryNode) normalizedNode);
} else if(schemaNode instanceof ListSchemaNode){ //8
return serializerFactory
.getMapEntryNodeSerializer()
.serialize((ListSchemaNode) schemaNode, (MapEntryNode) normalizedNode);
}
throw new UnsupportedOperationException(schemaNode.getClass().toString());
}
private static NormalizedNode parse(Document doc, DataSchemaNode schemaNode){
if(schemaNode instanceof ContainerSchemaNode){
return parserFactory
.getContainerNodeParser()
.parse(Collections.singletonList(doc.getDocumentElement()),
(ContainerSchemaNode) schemaNode);
} else if(schemaNode instanceof ChoiceNode){
return parserFactory
.getChoiceNodeParser()
.parse(Collections.singletonList(doc.getDocumentElement()),
(ChoiceNode) schemaNode);
} else if(schemaNode instanceof LeafNode){
return parserFactory
.getLeafNodeParser()
.parse(Collections.singletonList(doc.getDocumentElement()),
(LeafSchemaNode) schemaNode);
} else if(schemaNode instanceof ListSchemaNode){
return parserFactory
.getMapNodeParser()
.parse(Collections.singletonList(doc.getDocumentElement()),
(ListSchemaNode) schemaNode);
} else if(schemaNode instanceof LeafListSchemaNode){
return parserFactory
.getLeafSetNodeParser()
.parse(Collections.singletonList(doc.getDocumentElement()),
(LeafListSchemaNode) schemaNode);
} else if(schemaNode instanceof AugmentationSchema){
return parserFactory
.getAugmentationNodeParser()
.parse(Collections.singletonList(doc.getDocumentElement()),
(AugmentationSchema) schemaNode);
} else if(schemaNode instanceof ListSchemaNode){
return parserFactory
.getMapEntryNodeParser()
.parse(Collections.singletonList(doc.getDocumentElement()),
(ListSchemaNode) schemaNode);
}
throw new UnsupportedOperationException(schemaNode.getClass().toString());
}
/**
* Helps in generation of NormalizedNodeXml message for the supplied NormalizedNode
*
* @param sc --SchemaContext
* @param normalizedNode -- Normalized Node to be encoded
* @return SimpleNormalizedNodeMessage.NormalizedNodeXml
*/
public static SimpleNormalizedNodeMessage.NormalizedNodeXml encode(
SchemaContext sc, NormalizedNode, ?> normalizedNode) {
Preconditions.checkArgument(sc != null, "Schema context found null");
Preconditions.checkArgument(normalizedNode != null,
"normalized node found null");
DataSchemaNode schemaNode = getSchemaNode(sc,
normalizedNode.getIdentifier()
.getNodeType()
);
Preconditions.checkState(schemaNode != null,
"Couldn't find schema node for " + normalizedNode.getIdentifier());
Iterable els = serialize(schemaNode, normalizedNode);
String xmlString = toString(els.iterator().next());
SimpleNormalizedNodeMessage.NormalizedNodeXml.Builder builder =
SimpleNormalizedNodeMessage.NormalizedNodeXml.newBuilder();
builder.setXmlString(xmlString);
builder
.setNodeIdentifier(normalizedNode.getIdentifier()
.getNodeType().toString());
return builder.build();
}
/**
* Utilizes the SimpleNormalizedNodeMessage.NormalizedNodeXml to convert into NormalizedNode
*
* @param sc -- schema context
* @param normalizedNodeXml -- containing the normalized Node XML
* @return NormalizedNode return
* @throws Exception
*/
public static NormalizedNode decode(SchemaContext sc,
SimpleNormalizedNodeMessage.NormalizedNodeXml normalizedNodeXml)
throws Exception {
Preconditions
.checkArgument(sc != null, "schema context seems to be null");
Preconditions.checkArgument(normalizedNodeXml != null,
"SimpleNormalizedNodeMessage.NormalizedNodeXml found to be null");
QName qname = QName.create(normalizedNodeXml.getNodeIdentifier());
// here we will try to get back the NormalizedNode
DataSchemaNode schemaNode = getSchemaNode(sc, qname);
// now we need to read the XML
Document doc =
factory.newDocumentBuilder().parse(
new ByteArrayInputStream(
normalizedNodeXml.getXmlString().getBytes(
"utf-8"))
);
doc.getDocumentElement().normalize();
return parse(doc, schemaNode);
}
}