Support AnydataNode in ParameterAwareNormalizedNodeWriter
[netconf.git] / restconf / restconf-nb-rfc8040 / src / main / java / org / opendaylight / restconf / nb / rfc8040 / jersey / providers / ParameterAwareNormalizedNodeWriter.java
index 55c404a1f7c3135a8d6bb7d8a5a4e529b2008ebe..8872e56ae56dbdf35882d787f5394e592b60cc30 100644 (file)
@@ -5,24 +5,26 @@
  * 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.restconf.nb.rfc8040.jersey.providers;
 
+import static java.util.Objects.requireNonNull;
 import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
 
 import com.google.common.annotations.Beta;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
 import com.google.common.collect.Iterables;
 import java.io.IOException;
 import java.util.Collection;
 import java.util.List;
-import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
 import java.util.Set;
+import javax.xml.transform.dom.DOMSource;
+import org.opendaylight.restconf.nb.rfc8040.DepthParam;
 import org.opendaylight.restconf.nb.rfc8040.jersey.providers.api.RestconfNormalizedNodeWriter;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.AnydataNode;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyxmlNode;
 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
@@ -32,11 +34,10 @@ 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.api.schema.OrderedLeafSetNode;
-import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
-import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamAttributeWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.UserLeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UserMapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -56,10 +57,10 @@ public class ParameterAwareNormalizedNodeWriter implements RestconfNormalizedNod
     protected final List<Set<QName>> fields;
     protected int currentDepth = 0;
 
-    private ParameterAwareNormalizedNodeWriter(final NormalizedNodeStreamWriter writer, final Integer maxDepth,
+    private ParameterAwareNormalizedNodeWriter(final NormalizedNodeStreamWriter writer, final DepthParam depth,
                                                final List<Set<QName>> fields) {
-        this.writer = Preconditions.checkNotNull(writer);
-        this.maxDepth = maxDepth;
+        this.writer = requireNonNull(writer);
+        maxDepth = depth == null ? null : depth.value();
         this.fields = fields;
     }
 
@@ -76,33 +77,31 @@ public class ParameterAwareNormalizedNodeWriter implements RestconfNormalizedNod
      * @return A new instance.
      */
     public static ParameterAwareNormalizedNodeWriter forStreamWriter(
-            final NormalizedNodeStreamWriter writer, final Integer maxDepth, final List<Set<QName>> fields) {
+            final NormalizedNodeStreamWriter writer, final DepthParam maxDepth, final List<Set<QName>> fields) {
         return forStreamWriter(writer, true,  maxDepth, fields);
     }
 
     /**
      * Create a new writer backed by a {@link NormalizedNodeStreamWriter}. Unlike the simple
-     * {@link #forStreamWriter(NormalizedNodeStreamWriter, Integer, List)}
-     * method, this allows the caller to switch off RFC6020 XML compliance, providing better
-     * throughput. The reason is that the XML mapping rules in RFC6020 require the encoding
-     * to emit leaf nodes which participate in a list's key first and in the order in which
-     * they are defined in the key. For JSON, this requirement is completely relaxed and leaves
-     * can be ordered in any way we see fit. The former requires a bit of work: first a lookup
-     * for each key and then for each emitted node we need to check whether it was already
-     * emitted.
+     * {@link #forStreamWriter(NormalizedNodeStreamWriter, DepthParam, List)} method, this allows the caller to
+     * switch off RFC6020 XML compliance, providing better throughput. The reason is that the XML mapping rules in
+     * RFC6020 require the encoding to emit leaf nodes which participate in a list's key first and in the order in which
+     * they are defined in the key. For JSON, this requirement is completely relaxed and leaves can be ordered in any
+     * way we see fit. The former requires a bit of work: first a lookup for each key and then for each emitted node we
+     * need to check whether it was already emitted.
      *
      * @param writer Back-end writer
      * @param orderKeyLeaves whether the returned instance should be RFC6020 XML compliant.
-     * @param maxDepth Maximal depth to write
+     * @param depth Maximal depth to write
      * @param fields Selected child nodes to write
      * @return A new instance.
      */
     public static ParameterAwareNormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer,
                                                                      final boolean orderKeyLeaves,
-                                                                     final Integer maxDepth,
+                                                                     final DepthParam depth,
                                                                      final List<Set<QName>> fields) {
-        return orderKeyLeaves ? new OrderedParameterAwareNormalizedNodeWriter(writer, maxDepth, fields)
-                : new ParameterAwareNormalizedNodeWriter(writer, maxDepth, fields);
+        return orderKeyLeaves ? new OrderedParameterAwareNormalizedNodeWriter(writer, depth, fields)
+                : new ParameterAwareNormalizedNodeWriter(writer, depth, fields);
     }
 
     /**
@@ -114,7 +113,7 @@ public class ParameterAwareNormalizedNodeWriter implements RestconfNormalizedNod
      * @throws IOException when thrown from the backing writer.
      */
     @Override
-    public final ParameterAwareNormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException {
+    public final ParameterAwareNormalizedNodeWriter write(final NormalizedNode node) throws IOException {
         if (wasProcessedAsCompositeNode(node)) {
             return this;
         }
@@ -149,31 +148,41 @@ public class ParameterAwareNormalizedNodeWriter implements RestconfNormalizedNod
         return children instanceof Collection ? ((Collection<?>) children).size() : UNKNOWN_SIZE;
     }
 
-    private boolean wasProcessAsSimpleNode(final NormalizedNode<?, ?> node) throws IOException {
+    private boolean wasProcessAsSimpleNode(final NormalizedNode node) throws IOException {
         if (node instanceof LeafSetEntryNode) {
             if (selectedByParameters(node, false)) {
                 final LeafSetEntryNode<?> nodeAsLeafList = (LeafSetEntryNode<?>) node;
-                if (writer instanceof NormalizedNodeStreamAttributeWriter) {
-                    ((NormalizedNodeStreamAttributeWriter) writer).leafSetEntryNode(nodeAsLeafList.getNodeType(),
-                            nodeAsLeafList.getValue(), nodeAsLeafList.getAttributes());
-                } else {
-                    writer.leafSetEntryNode(nodeAsLeafList.getNodeType(), nodeAsLeafList.getValue());
-                }
+                writer.startLeafSetEntryNode(nodeAsLeafList.getIdentifier());
+                writer.scalarValue(nodeAsLeafList.body());
+                writer.endNode();
             }
             return true;
         } else if (node instanceof LeafNode) {
             final LeafNode<?> nodeAsLeaf = (LeafNode<?>)node;
-            if (writer instanceof NormalizedNodeStreamAttributeWriter) {
-                ((NormalizedNodeStreamAttributeWriter) writer).leafNode(
-                        nodeAsLeaf.getIdentifier(), nodeAsLeaf.getValue(), nodeAsLeaf.getAttributes());
-            } else {
-                writer.leafNode(nodeAsLeaf.getIdentifier(), nodeAsLeaf.getValue());
-            }
-            return true;
-        } else if (node instanceof AnyXmlNode) {
-            final AnyXmlNode anyXmlNode = (AnyXmlNode)node;
-            writer.anyxmlNode(anyXmlNode.getIdentifier(), anyXmlNode.getValue());
+            writer.startLeafNode(nodeAsLeaf.getIdentifier());
+            writer.scalarValue(nodeAsLeaf.body());
+            writer.endNode();
             return true;
+        } else if (node instanceof AnyxmlNode) {
+            final AnyxmlNode<?> anyxmlNode = (AnyxmlNode<?>)node;
+            final Class<?> objectModel = anyxmlNode.bodyObjectModel();
+            if (writer.startAnyxmlNode(anyxmlNode.getIdentifier(), objectModel)) {
+                if (DOMSource.class.isAssignableFrom(objectModel)) {
+                    writer.domSourceValue((DOMSource) anyxmlNode.body());
+                } else {
+                    writer.scalarValue(anyxmlNode.body());
+                }
+                writer.endNode();
+                return true;
+            }
+        } else if (node instanceof AnydataNode) {
+            final AnydataNode<?> anydataNode = (AnydataNode<?>)node;
+            final Class<?> objectModel = anydataNode.bodyObjectModel();
+            if (writer.startAnydataNode(anydataNode.getIdentifier(), objectModel)) {
+                writer.scalarValue(anydataNode.body());
+                writer.endNode();
+                return true;
+            }
         }
 
         return false;
@@ -186,7 +195,7 @@ public class ParameterAwareNormalizedNodeWriter implements RestconfNormalizedNod
      * @param mixinParent {@code true} if parent is mixin, {@code false} otherwise
      * @return {@code true} if node will be written, {@code false} otherwise
      */
-    protected boolean selectedByParameters(final NormalizedNode<?, ?> node, final boolean mixinParent) {
+    protected boolean selectedByParameters(final NormalizedNode node, final boolean mixinParent) {
         // nodes to be written are not limited by fields, only by depth
         if (fields == null) {
             return maxDepth == null || currentDepth < maxDepth;
@@ -204,7 +213,7 @@ public class ParameterAwareNormalizedNodeWriter implements RestconfNormalizedNod
 
         // write only selected nodes
         if (currentDepth > 0 && currentDepth <= fields.size()) {
-            return fields.get(currentDepth - 1).contains(node.getNodeType());
+            return fields.get(currentDepth - 1).contains(node.getIdentifier().getNodeType());
         }
 
         // after this depth only depth parameter is used to determine when to write node
@@ -219,9 +228,9 @@ public class ParameterAwareNormalizedNodeWriter implements RestconfNormalizedNod
      * @return True
      * @throws IOException when the writer reports it
      */
-    protected final boolean writeChildren(final Iterable<? extends NormalizedNode<?, ?>> children,
+    protected final boolean writeChildren(final Iterable<? extends NormalizedNode> children,
                                           final boolean mixinParent) throws IOException {
-        for (final NormalizedNode<?, ?> child : children) {
+        for (final NormalizedNode child : children) {
             if (selectedByParameters(child, mixinParent)) {
                 write(child);
             }
@@ -232,95 +241,87 @@ public class ParameterAwareNormalizedNodeWriter implements RestconfNormalizedNod
 
     protected boolean writeMapEntryChildren(final MapEntryNode mapEntryNode) throws IOException {
         if (selectedByParameters(mapEntryNode, false)) {
-            writeChildren(mapEntryNode.getValue(), false);
+            writeChildren(mapEntryNode.body(), false);
         } else if (fields == null && maxDepth != null && currentDepth == maxDepth) {
-            writeOnlyKeys(mapEntryNode.getIdentifier().getKeyValues());
+            writeOnlyKeys(mapEntryNode.getIdentifier().entrySet());
         }
         return true;
     }
 
-    private void writeOnlyKeys(final Map<QName, Object> keyValues) throws IllegalArgumentException, IOException {
-        for (final Map.Entry<QName, Object> entry : keyValues.entrySet()) {
-            writer.leafNode(new NodeIdentifier(entry.getKey()), entry.getValue());
+    private void writeOnlyKeys(final Set<Entry<QName, Object>> entries) throws IOException {
+        for (final Entry<QName, Object> entry : entries) {
+            writer.startLeafNode(new NodeIdentifier(entry.getKey()));
+            writer.scalarValue(entry.getValue());
+            writer.endNode();
         }
         writer.endNode();
     }
 
     protected boolean writeMapEntryNode(final MapEntryNode node) throws IOException {
-        if (writer instanceof NormalizedNodeStreamAttributeWriter) {
-            ((NormalizedNodeStreamAttributeWriter) writer)
-                    .startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue()), node.getAttributes());
-        } else {
-            writer.startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue()));
-        }
+        writer.startMapEntryNode(node.getIdentifier(), childSizeHint(node.body()));
         currentDepth++;
         writeMapEntryChildren(node);
         currentDepth--;
         return true;
     }
 
-    private boolean wasProcessedAsCompositeNode(final NormalizedNode<?, ?> node) throws IOException {
+    private boolean wasProcessedAsCompositeNode(final NormalizedNode node) throws IOException {
         boolean processedAsCompositeNode = false;
         if (node instanceof ContainerNode) {
             final ContainerNode n = (ContainerNode) node;
-            if (!n.getNodeType().equals(ROOT_DATA_QNAME)) {
-                if (writer instanceof NormalizedNodeStreamAttributeWriter) {
-                    ((NormalizedNodeStreamAttributeWriter) writer).startContainerNode(
-                            n.getIdentifier(), childSizeHint(n.getValue()), n.getAttributes());
-                } else {
-                    writer.startContainerNode(n.getIdentifier(), childSizeHint(n.getValue()));
-                }
+            if (!n.getIdentifier().getNodeType().withoutRevision().equals(ROOT_DATA_QNAME)) {
+                writer.startContainerNode(n.getIdentifier(), childSizeHint(n.body()));
                 currentDepth++;
-                processedAsCompositeNode = writeChildren(n.getValue(), false);
+                processedAsCompositeNode = writeChildren(n.body(), false);
                 currentDepth--;
             } else {
                 // write child nodes of data root container
-                for (final NormalizedNode<?, ?> child : n.getValue()) {
+                for (final NormalizedNode child : n.body()) {
                     currentDepth++;
                     if (selectedByParameters(child, false)) {
                         write(child);
                     }
                     currentDepth--;
-                    processedAsCompositeNode = true;
                 }
+                processedAsCompositeNode = true;
             }
         } else if (node instanceof MapEntryNode) {
             processedAsCompositeNode = writeMapEntryNode((MapEntryNode) node);
         } else if (node instanceof UnkeyedListEntryNode) {
             final UnkeyedListEntryNode n = (UnkeyedListEntryNode) node;
-            writer.startUnkeyedListItem(n.getIdentifier(), childSizeHint(n.getValue()));
+            writer.startUnkeyedListItem(n.getIdentifier(), childSizeHint(n.body()));
             currentDepth++;
-            processedAsCompositeNode = writeChildren(n.getValue(), false);
+            processedAsCompositeNode = writeChildren(n.body(), false);
             currentDepth--;
         } else if (node instanceof ChoiceNode) {
             final ChoiceNode n = (ChoiceNode) node;
-            writer.startChoiceNode(n.getIdentifier(), childSizeHint(n.getValue()));
-            processedAsCompositeNode = writeChildren(n.getValue(), true);
+            writer.startChoiceNode(n.getIdentifier(), childSizeHint(n.body()));
+            processedAsCompositeNode = writeChildren(n.body(), true);
         } else if (node instanceof AugmentationNode) {
             final AugmentationNode n = (AugmentationNode) node;
             writer.startAugmentationNode(n.getIdentifier());
-            processedAsCompositeNode = writeChildren(n.getValue(), true);
+            processedAsCompositeNode = writeChildren(n.body(), true);
         } else if (node instanceof UnkeyedListNode) {
             final UnkeyedListNode n = (UnkeyedListNode) node;
-            writer.startUnkeyedList(n.getIdentifier(), childSizeHint(n.getValue()));
-            processedAsCompositeNode = writeChildren(n.getValue(), false);
-        } else if (node instanceof OrderedMapNode) {
-            final OrderedMapNode n = (OrderedMapNode) node;
-            writer.startOrderedMapNode(n.getIdentifier(), childSizeHint(n.getValue()));
-            processedAsCompositeNode = writeChildren(n.getValue(), true);
+            writer.startUnkeyedList(n.getIdentifier(), childSizeHint(n.body()));
+            processedAsCompositeNode = writeChildren(n.body(), false);
+        } else if (node instanceof UserMapNode) {
+            final UserMapNode n = (UserMapNode) node;
+            writer.startOrderedMapNode(n.getIdentifier(), childSizeHint(n.body()));
+            processedAsCompositeNode = writeChildren(n.body(), true);
         } else if (node instanceof MapNode) {
             final MapNode n = (MapNode) node;
-            writer.startMapNode(n.getIdentifier(), childSizeHint(n.getValue()));
-            processedAsCompositeNode = writeChildren(n.getValue(), true);
+            writer.startMapNode(n.getIdentifier(), childSizeHint(n.body()));
+            processedAsCompositeNode = writeChildren(n.body(), true);
         } else if (node instanceof LeafSetNode) {
             final LeafSetNode<?> n = (LeafSetNode<?>) node;
-            if (node instanceof OrderedLeafSetNode) {
-                writer.startOrderedLeafSet(n.getIdentifier(), childSizeHint(n.getValue()));
+            if (node instanceof UserLeafSetNode) {
+                writer.startOrderedLeafSet(n.getIdentifier(), childSizeHint(n.body()));
             } else {
-                writer.startLeafSet(n.getIdentifier(), childSizeHint(n.getValue()));
+                writer.startLeafSet(n.getIdentifier(), childSizeHint(n.body()));
             }
             currentDepth++;
-            processedAsCompositeNode = writeChildren(n.getValue(), true);
+            processedAsCompositeNode = writeChildren(n.body(), true);
             currentDepth--;
         }
 
@@ -330,26 +331,21 @@ public class ParameterAwareNormalizedNodeWriter implements RestconfNormalizedNod
     private static final class OrderedParameterAwareNormalizedNodeWriter extends ParameterAwareNormalizedNodeWriter {
         private static final Logger LOG = LoggerFactory.getLogger(OrderedParameterAwareNormalizedNodeWriter.class);
 
-        OrderedParameterAwareNormalizedNodeWriter(final NormalizedNodeStreamWriter writer, final Integer maxDepth,
+        OrderedParameterAwareNormalizedNodeWriter(final NormalizedNodeStreamWriter writer, final DepthParam depth,
                                                   final List<Set<QName>> fields) {
-            super(writer, maxDepth, fields);
+            super(writer, depth, fields);
         }
 
         @Override
         protected boolean writeMapEntryNode(final MapEntryNode node) throws IOException {
             final NormalizedNodeStreamWriter writer = getWriter();
-            if (writer instanceof NormalizedNodeStreamAttributeWriter) {
-                ((NormalizedNodeStreamAttributeWriter) writer).startMapEntryNode(
-                        node.getIdentifier(), childSizeHint(node.getValue()), node.getAttributes());
-            } else {
-                writer.startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue()));
-            }
+            writer.startMapEntryNode(node.getIdentifier(), childSizeHint(node.body()));
 
-            final Set<QName> qnames = node.getIdentifier().getKeyValues().keySet();
+            final Set<QName> qnames = node.getIdentifier().keySet();
             // Write out all the key children
             currentDepth++;
             for (final QName qname : qnames) {
-                final Optional<? extends NormalizedNode<?, ?>> child = node.getChild(new NodeIdentifier(qname));
+                final Optional<? extends NormalizedNode> child = node.findChildByArg(new NodeIdentifier(qname));
                 if (child.isPresent()) {
                     if (selectedByParameters(child.get(), false)) {
                         write(child.get());
@@ -363,11 +359,11 @@ public class ParameterAwareNormalizedNodeWriter implements RestconfNormalizedNod
             currentDepth++;
             // Write all the rest
             final boolean result =
-                    writeChildren(Iterables.filter(node.getValue(), input -> {
+                    writeChildren(Iterables.filter(node.body(), input -> {
                         if (input instanceof AugmentationNode) {
                             return true;
                         }
-                        if (!qnames.contains(input.getNodeType())) {
+                        if (!qnames.contains(input.getIdentifier().getNodeType())) {
                             return true;
                         }