Added codec utilities for w3c.Document format. 35/4035/1
authorTony Tkacik <ttkacik@cisco.com>
Mon, 6 Jan 2014 19:42:25 +0000 (20:42 +0100)
committerTony Tkacik <ttkacik@cisco.com>
Mon, 6 Jan 2014 19:42:25 +0000 (20:42 +0100)
Signed-off-by: Tony Tkacik <ttkacik@cisco.com>
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/DataNodeCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlCodecProvider.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlDocumentUtils.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/test/codecs/TypeDefinitionAwareCodecTests.java [new file with mode: 0644]

diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/DataNodeCodec.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/DataNodeCodec.java
new file mode 100644 (file)
index 0000000..70c81ef
--- /dev/null
@@ -0,0 +1,13 @@
+package org.opendaylight.yangtools.yang.data.api.codec;
+
+import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.yangtools.yang.data.api.Node;
+
+public interface DataNodeCodec<I> extends Codec<I, Node<?>> {
+
+    @Override
+    public Node<?> deserialize(I input);
+
+    @Override
+    public I serialize(Node<?> input);
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlCodecProvider.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlCodecProvider.java
new file mode 100644 (file)
index 0000000..6b7b9f7
--- /dev/null
@@ -0,0 +1,10 @@
+package org.opendaylight.yangtools.yang.data.impl.codec.xml;
+
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+public interface XmlCodecProvider {
+
+    TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> codecFor(TypeDefinition<?> baseType);
+
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlDocumentUtils.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlDocumentUtils.java
new file mode 100644 (file)
index 0000000..14b4731
--- /dev/null
@@ -0,0 +1,168 @@
+package org.opendaylight.yangtools.yang.data.impl.codec.xml;
+
+import java.util.Set;
+
+import javax.activation.UnsupportedDataTypeException;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+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.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.YangNode;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.google.common.base.Preconditions;
+
+public class XmlDocumentUtils {
+
+    private static final XmlCodecProvider DEFAULT_XML_VALUE_CODEC_PROVIDER = new XmlCodecProvider() {
+
+        @Override
+        public TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> codecFor(TypeDefinition<?> baseType) {
+            return TypeDefinitionAwareCodec.from(baseType);
+        }
+    };
+
+    private static final Logger logger = LoggerFactory.getLogger(XmlDocumentUtils.class);
+
+    public static Document toDocument(CompositeNode data, DataNodeContainer schema, XmlCodecProvider codecProvider)
+            throws UnsupportedDataTypeException {
+        Preconditions.checkNotNull(data);
+        Preconditions.checkNotNull(schema);
+
+        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+        Document doc = null;
+        try {
+            DocumentBuilder bob = dbf.newDocumentBuilder();
+            doc = bob.newDocument();
+        } catch (ParserConfigurationException e) {
+            return null;
+        }
+
+        if (schema instanceof ContainerSchemaNode || schema instanceof ListSchemaNode) {
+            doc.appendChild(createXmlRootElement(doc, data, schema, codecProvider));
+            return doc;
+        } else {
+            throw new UnsupportedDataTypeException(
+                    "Schema can be ContainerSchemaNode or ListSchemaNode. Other types are not supported yet.");
+        }
+    }
+
+    private static Element createXmlRootElement(Document doc, Node<?> data, YangNode schema,
+            XmlCodecProvider codecProvider) throws UnsupportedDataTypeException {
+        QName dataType = data.getNodeType();
+        Element itemEl = doc.createElementNS(dataType.getNamespace().toString(), dataType.getLocalName());
+        if (data instanceof SimpleNode<?>) {
+            if (schema instanceof LeafListSchemaNode) {
+                writeValueByType(itemEl, (SimpleNode<?>) data, ((LeafListSchemaNode) schema).getType(),
+                        (DataSchemaNode) schema, codecProvider);
+            } else if (schema instanceof LeafSchemaNode) {
+                writeValueByType(itemEl, (SimpleNode<?>) data, ((LeafSchemaNode) schema).getType(),
+                        (DataSchemaNode) schema, codecProvider);
+            } else {
+                Object value = data.getValue();
+                if (value != null) {
+                    itemEl.setTextContent(String.valueOf(value));
+                }
+            }
+        } else { // CompositeNode
+            for (Node<?> child : ((CompositeNode) data).getChildren()) {
+                DataSchemaNode childSchema = null;
+                if (schema != null) {
+                    childSchema = findFirstSchemaForNode(child, ((DataNodeContainer) schema).getChildNodes());
+                    if (logger.isDebugEnabled()) {
+                        if (childSchema == null) {
+                            logger.debug("Probably the data node \""
+                                    + ((child == null) ? "" : child.getNodeType().getLocalName())
+                                    + "\" is not conform to schema");
+                        }
+                    }
+                }
+                itemEl.appendChild(createXmlRootElement(doc, child, childSchema, codecProvider));
+            }
+        }
+        return itemEl;
+    }
+
+    public static void writeValueByType(Element element, SimpleNode<?> node, TypeDefinition<?> type,
+            DataSchemaNode schema, XmlCodecProvider codecProvider) {
+
+        TypeDefinition<?> baseType = resolveBaseTypeFrom(type);
+
+        if (baseType instanceof IdentityrefTypeDefinition && node.getValue() instanceof QName) {
+            if (node.getValue() instanceof QName) {
+                QName value = (QName) node.getValue();
+                String prefix = "x";
+                if (value.getPrefix() != null && !value.getPrefix().isEmpty()) {
+                    prefix = value.getPrefix();
+                }
+                element.setAttribute("xmlns:" + prefix, value.getNamespace().toString());
+                element.setTextContent(prefix + ":" + value.getLocalName());
+            } else {
+                logger.debug("Value of {}:{} is not instance of QName but is {}",
+                        baseType.getQName().getNamespace(), //
+                        baseType.getQName().getLocalName(), //
+                        node.getValue().getClass());
+                element.setTextContent(String.valueOf(node.getValue()));
+            }
+        } else {
+            if (node.getValue() != null) {
+                try {
+                    String value = codecProvider.codecFor(baseType).serialize(node.getValue());
+                    element.setTextContent(value);
+                } catch (ClassCastException e) {
+                    element.setTextContent(String.valueOf(node.getValue()));
+                    logger.error("Provided node did not have type required by mapping. Using stream instead. {}",e);
+                }
+            }
+        }
+    }
+
+    public final static TypeDefinition<?> resolveBaseTypeFrom(TypeDefinition<?> type) {
+        TypeDefinition<?> superType = type;
+        while (superType.getBaseType() != null) {
+            superType = superType.getBaseType();
+        }
+        return superType;
+    }
+
+    private static final DataSchemaNode findFirstSchemaForNode(Node<?> node, Set<DataSchemaNode> dataSchemaNode) {
+        if (dataSchemaNode != null && node != null) {
+            for (DataSchemaNode dsn : dataSchemaNode) {
+                if (node.getNodeType().getLocalName().equals(dsn.getQName().getLocalName())) {
+                    return dsn;
+                } else if (dsn instanceof ChoiceNode) {
+                    for (ChoiceCaseNode choiceCase : ((ChoiceNode) dsn).getCases()) {
+                        DataSchemaNode foundDsn = findFirstSchemaForNode(node, choiceCase.getChildNodes());
+                        if (foundDsn != null) {
+                            return foundDsn;
+                        }
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    public static final XmlCodecProvider defaultValueCodecProvider() {
+        return DEFAULT_XML_VALUE_CODEC_PROVIDER;
+    }
+
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/test/codecs/TypeDefinitionAwareCodecTests.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/test/codecs/TypeDefinitionAwareCodecTests.java
new file mode 100644 (file)
index 0000000..f276dfa
--- /dev/null
@@ -0,0 +1,43 @@
+package org.opendaylight.yangtools.yang.data.impl.test.codecs;
+
+import static org.junit.Assert.*;
+
+import java.util.Set;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+
+import com.google.common.collect.ImmutableSet;
+
+public class TypeDefinitionAwareCodecTests {
+
+    @Test
+    public void bitsEmptySerialization() throws Exception {
+        String serialized = TypeDefinitionAwareCodec.BITS_DEFAULT_CODEC.serialize(ImmutableSet.<String> of());
+        assertNotNull(serialized);
+        assertEquals("", serialized);
+
+        Set<String> deserialized = TypeDefinitionAwareCodec.BITS_DEFAULT_CODEC.deserialize("");
+        assertNotNull(deserialized);
+        assertTrue(deserialized.isEmpty());
+
+        Set<String> deserializedFromNull = TypeDefinitionAwareCodec.BITS_DEFAULT_CODEC.deserialize(null);
+        assertNotNull(deserializedFromNull);
+        assertTrue(deserializedFromNull.isEmpty());
+    }
+
+    @Test
+    public void bitsMultipleSerialization() throws Exception {
+        ImmutableSet<String> toSerialize = ImmutableSet.of("foo", "bar");
+
+        String serialized = TypeDefinitionAwareCodec.BITS_DEFAULT_CODEC.serialize(toSerialize);
+        assertNotNull(serialized);
+        assertTrue(serialized.contains("foo"));
+        assertTrue(serialized.contains("bar"));
+
+        Set<String> deserialized = TypeDefinitionAwareCodec.BITS_DEFAULT_CODEC.deserialize("  foo bar     ");
+        assertNotNull(deserialized);
+        assertEquals(toSerialize, deserialized);
+    }
+
+}