Correct root vs. nested writeout
[netconf.git] / netconf / mdsal-netconf-connector / src / main / java / org / opendaylight / netconf / mdsal / connector / ops / get / AbstractGet.java
index 6ce25640eacac864ad1762c3de5e9420fc3a7419..c323069e12b3dee5ca98e74264240a40cdb534af 100644 (file)
@@ -7,41 +7,39 @@
  */
 package org.opendaylight.netconf.mdsal.connector.ops.get;
 
+import static com.google.common.base.Preconditions.checkArgument;
+
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Optional;
-import com.google.common.collect.Iterables;
 import java.io.IOException;
+import java.util.Optional;
 import javax.xml.stream.XMLOutputFactory;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
 import javax.xml.transform.dom.DOMResult;
 import org.opendaylight.netconf.api.DocumentedException;
-import org.opendaylight.netconf.api.DocumentedException.ErrorSeverity;
-import org.opendaylight.netconf.api.DocumentedException.ErrorTag;
-import org.opendaylight.netconf.api.DocumentedException.ErrorType;
 import org.opendaylight.netconf.api.xml.XmlElement;
 import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
 import org.opendaylight.netconf.mdsal.connector.CurrentSchemaContext;
 import org.opendaylight.netconf.mdsal.connector.ops.Datastore;
 import org.opendaylight.netconf.util.mapping.AbstractSingletonNetconfOperation;
+import org.opendaylight.yangtools.yang.common.ErrorSeverity;
+import org.opendaylight.yangtools.yang.common.ErrorTag;
+import org.opendaylight.yangtools.yang.common.ErrorType;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.YangInstanceIdentifierWriter;
 import org.opendaylight.yangtools.yang.data.codec.xml.XMLStreamNormalizedNodeStreamWriter;
-import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 
+// FIXME: seal when we have JDK17+
 public abstract class AbstractGet extends AbstractSingletonNetconfOperation {
     private static final XMLOutputFactory XML_OUTPUT_FACTORY;
-    private static final YangInstanceIdentifier ROOT = YangInstanceIdentifier.EMPTY;
     private static final String FILTER = "filter";
 
     static {
@@ -49,68 +47,73 @@ public abstract class AbstractGet extends AbstractSingletonNetconfOperation {
         XML_OUTPUT_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);
     }
 
+    // FIXME: hide this field
     protected final CurrentSchemaContext schemaContext;
     private final FilterContentValidator validator;
 
-    public AbstractGet(final String netconfSessionIdForReporting, final CurrentSchemaContext schemaContext) {
+    // FIXME: package-private
+    protected AbstractGet(final String netconfSessionIdForReporting, final CurrentSchemaContext schemaContext) {
         super(netconfSessionIdForReporting);
         this.schemaContext = schemaContext;
-        this.validator = new FilterContentValidator(schemaContext);
+        validator = new FilterContentValidator(schemaContext);
     }
 
-    protected Node transformNormalizedNode(final Document document, final NormalizedNode<?, ?> data,
+    // FIXME: hide this method
+    // FIXME: throw a DocumentedException
+    protected Node transformNormalizedNode(final Document document, final NormalizedNode data,
                                            final YangInstanceIdentifier dataRoot) {
         final DOMResult result = new DOMResult(document.createElement(XmlNetconfConstants.DATA_KEY));
-
         final XMLStreamWriter xmlWriter = getXmlStreamWriter(result);
+        final EffectiveModelContext currentContext = schemaContext.getCurrentContext();
 
         final NormalizedNodeStreamWriter nnStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(xmlWriter,
-                schemaContext.getCurrentContext(), getSchemaPath(dataRoot));
+            currentContext);
 
-        final NormalizedNodeWriter nnWriter = NormalizedNodeWriter.forStreamWriter(nnStreamWriter, true);
+        try {
+            if (dataRoot.isEmpty()) {
+                writeRoot(nnStreamWriter, data);
+            } else {
+                write(nnStreamWriter, currentContext, dataRoot.coerceParent(), data);
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
 
-        writeRootElement(xmlWriter, nnWriter, (ContainerNode) data);
         return result.getNode();
     }
 
-    private static XMLStreamWriter getXmlStreamWriter(final DOMResult result) {
-        try {
-            return XML_OUTPUT_FACTORY.createXMLStreamWriter(result);
-        } catch (final XMLStreamException e) {
-            throw new RuntimeException(e);
+    private static void write(final NormalizedNodeStreamWriter nnStreamWriter,
+            final EffectiveModelContext currentContext, final YangInstanceIdentifier parent, final NormalizedNode data)
+                throws IOException {
+        try (var yiidWriter = YangInstanceIdentifierWriter.open(nnStreamWriter, currentContext, parent)) {
+            try (var nnWriter = NormalizedNodeWriter.forStreamWriter(nnStreamWriter, true)) {
+                nnWriter.write(data);
+            }
         }
     }
 
-    private static SchemaPath getSchemaPath(final YangInstanceIdentifier dataRoot) {
-        return SchemaPath.create(
-                Iterables.transform(dataRoot.getPathArguments(), PathArgument::getNodeType), dataRoot.equals(ROOT));
+    private static void writeRoot(final NormalizedNodeStreamWriter nnStreamWriter, final NormalizedNode data)
+            throws IOException {
+        checkArgument(data instanceof ContainerNode, "Unexpected root data %s", data);
+
+        try (var nnWriter = NormalizedNodeWriter.forStreamWriter(nnStreamWriter, true)) {
+            for (var child : ((ContainerNode) data).body()) {
+                nnWriter.write(child);
+            }
+        }
     }
 
-    private static void writeRootElement(final XMLStreamWriter xmlWriter, final NormalizedNodeWriter nnWriter,
-                                  final ContainerNode data) {
+    private static XMLStreamWriter getXmlStreamWriter(final DOMResult result) {
         try {
-            if (data.getNodeType().equals(SchemaContext.NAME)) {
-                for (final DataContainerChild<? extends PathArgument, ?> child : data.getValue()) {
-                    nnWriter.write(child);
-                }
-            } else {
-                nnWriter.write(data);
-            }
-            nnWriter.flush();
-            xmlWriter.flush();
-        } catch (XMLStreamException | IOException e) {
+            return XML_OUTPUT_FACTORY.createXMLStreamWriter(result);
+        } catch (final XMLStreamException e) {
             throw new RuntimeException(e);
         }
     }
 
     protected Element serializeNodeWithParentStructure(final Document document, final YangInstanceIdentifier dataRoot,
-                                                       final NormalizedNode<?, ?> node) {
-        if (!dataRoot.equals(ROOT)) {
-            return (Element) transformNormalizedNode(document,
-                    ImmutableNodes.fromInstanceId(schemaContext.getCurrentContext(), dataRoot, node),
-                    ROOT);
-        }
-        return (Element) transformNormalizedNode(document, node, ROOT);
+                                                       final NormalizedNode node) {
+        return (Element) transformNormalizedNode(document, node, dataRoot);
     }
 
     /**
@@ -127,12 +130,12 @@ public abstract class AbstractGet extends AbstractSingletonNetconfOperation {
         final Optional<XmlElement> filterElement = operationElement.getOnlyChildElementOptionally(FILTER);
         if (filterElement.isPresent()) {
             if (filterElement.get().getChildElements().size() == 0) {
-                return Optional.absent();
+                return Optional.empty();
             }
             return Optional.of(getInstanceIdentifierFromFilter(filterElement.get()));
         }
 
-        return Optional.of(ROOT);
+        return Optional.of(YangInstanceIdentifier.empty());
     }
 
     @VisibleForTesting
@@ -177,10 +180,9 @@ public abstract class AbstractGet extends AbstractSingletonNetconfOperation {
         private static Optional<Datastore> parseSource(final XmlElement xml) throws DocumentedException {
             final Optional<XmlElement> sourceElement = xml.getOnlyChildElementOptionally(XmlNetconfConstants.SOURCE_KEY,
                     XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
-
             return sourceElement.isPresent()
                     ? Optional.of(Datastore.valueOf(sourceElement.get().getOnlyChildElement().getName()))
-                    : Optional.absent();
+                    : Optional.empty();
         }
 
         private static void validateInputRpc(final XmlElement xml, final String operationName) throws
@@ -192,7 +194,5 @@ public abstract class AbstractGet extends AbstractSingletonNetconfOperation {
         public Optional<Datastore> getDatastore() {
             return datastore;
         }
-
     }
-
 }