[ONOS-5884]YANG Serializer: Implement XML serializer.

Change-Id: Idffda88938d9c6c7b7c7814127a340bd2dc35039
diff --git a/serializers/utils/pom.xml b/serializers/utils/pom.xml
new file mode 100644
index 0000000..b1866f9
--- /dev/null
+++ b/serializers/utils/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2017-present Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos-yang-serializers</artifactId>
+        <version>1.12-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-yang-serializers-utils</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-yang-runtime</artifactId>
+            <version>1.12-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.dom4j</artifactId>
+            <version>1.6.1_5</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>3.0.2</version>
+                <configuration>
+                    <skipIfEmpty>true</skipIfEmpty>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>default</id>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/serializers/utils/src/main/java/org/onosproject/yang/serializers/utils/SerializerUtilException.java b/serializers/utils/src/main/java/org/onosproject/yang/serializers/utils/SerializerUtilException.java
new file mode 100644
index 0000000..eef46dc
--- /dev/null
+++ b/serializers/utils/src/main/java/org/onosproject/yang/serializers/utils/SerializerUtilException.java
@@ -0,0 +1,43 @@
+/*
+ *  Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.yang.serializers.utils;
+
+/**
+ * Represents class of errors related to serializer utils.
+ */
+public class SerializerUtilException extends RuntimeException {
+
+    /**
+     * Constructs an exception with the specified message.
+     *
+     * @param message the message describing the specific nature of the error
+     */
+    public SerializerUtilException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs an exception with the specified message and the underlying
+     * cause.
+     *
+     * @param message the message describing the specific nature of the error
+     * @param cause   the underlying cause of this error
+     */
+    public SerializerUtilException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/serializers/utils/src/main/java/org/onosproject/yang/serializers/utils/SerializersUtil.java b/serializers/utils/src/main/java/org/onosproject/yang/serializers/utils/SerializersUtil.java
new file mode 100644
index 0000000..dbb0222
--- /dev/null
+++ b/serializers/utils/src/main/java/org/onosproject/yang/serializers/utils/SerializersUtil.java
@@ -0,0 +1,378 @@
+/*
+ *  Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.yang.serializers.utils;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+import org.dom4j.Attribute;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.onosproject.yang.model.KeyLeaf;
+import org.onosproject.yang.model.LeafListKey;
+import org.onosproject.yang.model.ListKey;
+import org.onosproject.yang.model.NodeKey;
+import org.onosproject.yang.model.ResourceId;
+import org.onosproject.yang.runtime.AnnotatedNodeInfo;
+import org.onosproject.yang.runtime.Annotation;
+import org.onosproject.yang.runtime.DefaultAnnotatedNodeInfo;
+import org.onosproject.yang.runtime.DefaultAnnotation;
+import org.onosproject.yang.runtime.YangSerializerContext;
+import org.onosproject.yang.runtime.helperutils.SerializerHelper;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Utilities for serializers.
+ */
+public final class SerializersUtil {
+    private static final Splitter SLASH_SPLITTER = Splitter.on('/');
+    private static final Splitter COMMA_SPLITTER = Splitter.on(',');
+    private static final String QUOTES = "\"";
+    private static final String ROOT_ELEMENT_START = "<root ";
+    private static final String ROOT_ELEMENT_END = "</root>";
+    private static final String URI_ENCODING_CHAR_SET = "ISO-8859-1";
+    private static final String ERROR_LIST_MSG = "List/Leaf-list node should be " +
+            "in format \"nodeName=key\"or \"nodeName=instance-value\"";
+    private static final String EQUAL = "=";
+    private static final String COMMA = ",";
+    private static final String COLON = ":";
+    private static final String SLASH = "/";
+
+    // no instantiation
+    private SerializersUtil() {
+    }
+
+    /**
+     * Converts XML atrtibutes into annotated node info.
+     *
+     * @param element XML element
+     * @param id      resource id of an element
+     * @return annotated node info
+     */
+    public static AnnotatedNodeInfo convertXmlAttributesToAnnotations(Element element,
+                                                                      ResourceId id) {
+        Iterator iter = element.attributeIterator();
+        if (!iter.hasNext()) {
+            // element does not have any attributes
+            return null;
+        }
+        AnnotatedNodeInfo.Builder builder = DefaultAnnotatedNodeInfo.builder();
+        builder = builder.resourceId(id);
+        while (iter.hasNext()) {
+            Attribute attr = (Attribute) iter.next();
+            DefaultAnnotation annotation = new DefaultAnnotation(
+                    attr.getQualifiedName(), attr.getValue());
+            builder = builder.addAnnotation(annotation);
+        }
+        return builder.build();
+    }
+
+
+    /**
+     * Appends the XML data with root element.
+     *
+     * @param inputStream        XML data
+     * @param protocolAnnotation list of annoations for root element
+     * @return XML with root element
+     * @throws DocumentException if root element cannot be created
+     * @throws IOException       if input data cannot be read
+     */
+    public static String addRootElementWithAnnotation(InputStream inputStream,
+                                                      List<Annotation>
+                                                              protocolAnnotation)
+            throws DocumentException, IOException {
+        BufferedReader br;
+        StringBuilder sb = new StringBuilder();
+        String xmlData;
+        // Parse composite stream resourceData
+        br = new BufferedReader(new InputStreamReader(inputStream));
+        while ((xmlData = br.readLine()) != null) {
+            sb.append(xmlData);
+        }
+
+        StringBuilder rootElement = new StringBuilder(ROOT_ELEMENT_START);
+        if (protocolAnnotation != null) {
+            for (Annotation annotation : protocolAnnotation) {
+                rootElement.append(annotation.name()).append(EQUAL)
+                        .append(QUOTES).append(annotation.value()).append(QUOTES);
+            }
+        }
+        rootElement.append(">").append(sb.toString()).append(ROOT_ELEMENT_END);
+        return rootElement.toString();
+    }
+
+    /**
+     * Converts a URI string to resource identifier.
+     *
+     * @param uriString given URI
+     * @param context   YANG schema context information
+     * @return resource ID
+     */
+    public static ResourceId.Builder convertUriToRid(String uriString,
+                                                     YangSerializerContext context) {
+        if (uriString == null || uriString.isEmpty()) {
+            return null;
+        }
+
+        List<String> paths = urlPathArgsDecode(SLASH_SPLITTER.split(uriString));
+
+        if (!paths.isEmpty()) {
+            ResourceId.Builder ridBuilder =
+                    SerializerHelper.initializeResourceId(context);
+            processPathSegments(paths, ridBuilder);
+            return ridBuilder;
+        }
+
+        return null;
+    }
+
+    /**
+     * Converts a list of path from the original format to ISO-8859-1 code.
+     *
+     * @param paths the original paths
+     * @return list of decoded paths
+     */
+    public static List<String> urlPathArgsDecode(Iterable<String> paths) {
+        try {
+            List<String> decodedPathArgs = new ArrayList<>();
+            for (String pathArg : paths) {
+                String decode = URLDecoder.decode(pathArg,
+                                                  URI_ENCODING_CHAR_SET);
+                decodedPathArgs.add(decode);
+            }
+            return decodedPathArgs;
+        } catch (UnsupportedEncodingException e) {
+            throw new SerializerUtilException("Invalid URL path arg '" +
+                                                      paths + "': ", e);
+        }
+    }
+
+    private static ResourceId.Builder processPathSegments(List<String> paths,
+                                                          ResourceId.Builder builder) {
+        if (paths.isEmpty()) {
+            return builder;
+        }
+
+        boolean isLastSegment = paths.size() == 1;
+
+        String segment = paths.iterator().next();
+        processSinglePathSegment(segment, builder);
+
+        if (isLastSegment) {
+            // We have hit the base case of recursion.
+            return builder;
+        }
+
+        /*
+         * Chop off the first segment, and recursively process the rest
+         * of the path segments.
+         */
+        List<String> remainPaths = paths.subList(1, paths.size());
+        processPathSegments(remainPaths, builder);
+
+        return builder;
+    }
+
+    private static void processSinglePathSegment(String pathSegment,
+                                                 ResourceId.Builder builder) {
+        if (pathSegment.contains(COLON)) {
+            processPathSegmentWithNamespace(pathSegment, builder);
+        } else {
+            processPathSegmentWithoutNamespace(pathSegment, builder);
+        }
+    }
+
+    private static void processPathSegmentWithNamespace(String pathSegment,
+                                                        ResourceId.Builder builder) {
+
+        String nodeName = getLatterSegment(pathSegment, COLON);
+        String namespace = getPreSegment(pathSegment, COLON);
+        addNodeNameToRid(nodeName, namespace, builder);
+    }
+
+    private static void processPathSegmentWithoutNamespace(String nodeName,
+                                                           ResourceId.Builder builder) {
+        addNodeNameToRid(nodeName, null, builder);
+    }
+
+    private static void addNodeNameToRid(String nodeName,
+                                         String namespace,
+                                         ResourceId.Builder builder) {
+        if (nodeName.contains(EQUAL)) {
+            addListOrLeafList(nodeName, namespace, builder);
+        } else {
+            addLeaf(nodeName, namespace, builder);
+        }
+    }
+
+    private static void addListOrLeafList(String path,
+                                          String namespace,
+                                          ResourceId.Builder builder) {
+        String nodeName = getPreSegment(path, EQUAL);
+        String keyStr = getLatterSegment(path, EQUAL);
+        if (keyStr == null) {
+            throw new SerializerUtilException(ERROR_LIST_MSG);
+        }
+
+        if (keyStr.contains(COMMA)) {
+            List<String> keys = Lists.
+                    newArrayList(COMMA_SPLITTER.split(keyStr));
+            SerializerHelper.addToResourceId(builder, nodeName, namespace, keys);
+        } else {
+            SerializerHelper.addToResourceId(builder, nodeName, namespace,
+                                             Lists.newArrayList(keyStr));
+        }
+    }
+
+    private static void addLeaf(String nodeName,
+                                String namespace,
+                                ResourceId.Builder builder) {
+        checkNotNull(nodeName);
+        String value = null;
+        SerializerHelper.addToResourceId(builder, nodeName, namespace, value);
+    }
+
+    /**
+     * Returns the previous segment of a path which is separated by a split char.
+     * For example:
+     * <pre>
+     * "foo:bar", ":"   -->  "foo"
+     * </pre>
+     *
+     * @param path      the original path string
+     * @param splitChar char used to split the path
+     * @return the previous segment of the path
+     */
+    private static String getPreSegment(String path, String splitChar) {
+        int idx = path.lastIndexOf(splitChar);
+        if (idx == -1) {
+            return null;
+        }
+        return path.substring(0, idx);
+    }
+
+    /**
+     * Returns the latter segment of a path which is separated by a split char.
+     * For example:
+     * <pre>
+     * "foo:bar", ":"   -->  "bar"
+     * </pre>
+     *
+     * @param path      the original path string
+     * @param splitChar char used to split the path
+     * @return the latter segment of the path
+     */
+    private static String getLatterSegment(String path, String splitChar) {
+        int idx = path.lastIndexOf(splitChar);
+        if (idx == -1) {
+            return path;
+        }
+
+        return path.substring(idx + 1);
+    }
+
+    /**
+     * Converts a resource identifier to URI string.
+     *
+     * @param rid resource identifier
+     * @return URI
+     */
+    public static String convertRidToUri(ResourceId rid) {
+        if (rid == null) {
+            return null;
+        }
+
+        StringBuilder uriBuilder = new StringBuilder();
+        List<NodeKey> nodeKeyList = rid.nodeKeys();
+        String curNameSpace = null;
+        for (NodeKey key : nodeKeyList) {
+            curNameSpace = addNodeKeyToUri(key, curNameSpace, uriBuilder);
+        }
+        return uriBuilder.toString();
+    }
+
+    private static String addNodeKeyToUri(NodeKey key,
+                                          String curNameSpace,
+                                          StringBuilder uriBuilder) {
+        String newNameSpace;
+        if (key instanceof LeafListKey) {
+            newNameSpace = addLeafListNodeToUri((LeafListKey) key, curNameSpace, uriBuilder);
+        } else if (key instanceof ListKey) {
+            newNameSpace = addListNodeToUri((ListKey) key, curNameSpace, uriBuilder);
+        } else {
+            uriBuilder.append(SLASH);
+            newNameSpace = addNodeNameToUri(key, curNameSpace, uriBuilder);
+        }
+        return newNameSpace;
+    }
+
+    private static String addLeafListNodeToUri(LeafListKey key,
+                                               String curNameSpace,
+                                               StringBuilder uriBuilder) {
+
+        String newNameSpace = addNodeNameToUri(key, curNameSpace, uriBuilder);
+        uriBuilder.append(EQUAL);
+        uriBuilder.append(key.asString());
+        return newNameSpace;
+    }
+
+    private static String addListNodeToUri(ListKey key,
+                                           String curNameSpace,
+                                           StringBuilder uriBuilder) {
+        String newNameSpace = addNodeNameToUri(key, curNameSpace, uriBuilder);
+        uriBuilder.append(EQUAL);
+        String prefix = "";
+        for (KeyLeaf keyLeaf : key.keyLeafs()) {
+            uriBuilder.append(prefix);
+            prefix = COMMA;
+            uriBuilder.append(keyLeaf.leafValue().toString());
+        }
+
+        return newNameSpace;
+    }
+
+    private static String addNodeNameToUri(NodeKey key,
+                                           String curNameSpace,
+                                           StringBuilder uriBuilder) {
+        String nodeName = key.schemaId().name();
+        String newNameSpace = key.schemaId().namespace();
+
+        uriBuilder.append(nodeName);
+
+        if (newNameSpace == null) {
+            return curNameSpace;
+        }
+
+        if (!newNameSpace.equals(curNameSpace)) {
+            uriBuilder.append(COLON);
+            uriBuilder.append(newNameSpace);
+        }
+
+        return newNameSpace;
+    }
+
+}
diff --git a/serializers/utils/src/main/java/org/onosproject/yang/serializers/utils/package-info.java b/serializers/utils/src/main/java/org/onosproject/yang/serializers/utils/package-info.java
new file mode 100644
index 0000000..65d177b
--- /dev/null
+++ b/serializers/utils/src/main/java/org/onosproject/yang/serializers/utils/package-info.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2017. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+ * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan.
+ * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna.
+ * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus.
+ * Vestibulum commodo. Ut rhoncus gravida arcu.
+ */
+
+/**
+ * The XML codec implementation of the YangSerializer interface.
+ */
+package org.onosproject.yang.serializers.utils;