Clean up ModifyAction
[netconf.git] / netconf / sal-netconf-connector / src / main / java / org / opendaylight / netconf / sal / connect / netconf / util / SchemalessRpcStructureTransformer.java
index 98d3d30f91066802cc1ce715caa716ae7da412d3..7f4fa6b87794b9f3d72e92490ec48e61baa9e7ce 100644 (file)
@@ -7,27 +7,31 @@
  */
 package org.opendaylight.netconf.sal.connect.netconf.util;
 
+import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CONFIG_NODEID;
 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CONFIG_QNAME;
+import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DATA_NODEID;
+import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_FILTER_NODEID;
+import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_FILTER_QNAME;
+import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_OPERATION_QNAME;
+import static org.opendaylight.netconf.util.NetconfUtil.NETCONF_DATA_QNAME;
+import static org.opendaylight.netconf.util.NetconfUtil.appendListKeyNodes;
+import static org.opendaylight.netconf.util.NetconfUtil.writeSchemalessFilter;
 
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import java.util.Collections;
 import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Map.Entry;
+import java.util.Optional;
 import javax.xml.transform.dom.DOMSource;
-import org.opendaylight.controller.config.util.xml.DocumentedException;
-import org.opendaylight.controller.config.util.xml.XmlElement;
-import org.opendaylight.controller.config.util.xml.XmlUtil;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.ModifyAction;
+import org.opendaylight.netconf.api.DocumentedException;
+import org.opendaylight.netconf.api.ModifyAction;
+import org.opendaylight.netconf.api.xml.XmlElement;
+import org.opendaylight.netconf.api.xml.XmlUtil;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyxmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DOMSourceAnyxmlNode;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
-import org.w3c.dom.Attr;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
@@ -40,29 +44,28 @@ class SchemalessRpcStructureTransformer implements RpcStructureTransformer {
     /**
      * Selects elements in anyxml data node, which matches path arguments QNames. Since class in not context aware,
      * method searches for all elements as they are named in path.
-     * @param data data, must be of type {@link AnyXmlNode}
+     * @param data data, must be of type {@link DOMSourceAnyxmlNode}
      * @param path path to select
      * @return selected data
      */
     @Override
-    public Optional<NormalizedNode<?, ?>> selectFromDataStructure(
-            final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> data,
+    public Optional<NormalizedNode> selectFromDataStructure(final DataContainerChild data,
             final YangInstanceIdentifier path) {
-        Preconditions.checkArgument(data instanceof AnyXmlNode);
-        final List<XmlElement> xmlElements = selectMatchingNodes(getSourceElement(((AnyXmlNode)data).getValue()), path);
-        final Document result = XmlUtil.newDocument();
-        final QName dataQName = NetconfMessageTransformUtil.NETCONF_DATA_QNAME;
-        final Element dataElement =
-                result.createElementNS(dataQName.getNamespace().toString(), dataQName.getLocalName());
+        if (!(data instanceof DOMSourceAnyxmlNode anyxml)) {
+            throw new IllegalArgumentException("Unexpected data " + data.prettyTree());
+        }
+
+        final var result = XmlUtil.newDocument();
+        final var dataElement = result.createElementNS(NETCONF_DATA_QNAME.getNamespace().toString(),
+            NETCONF_DATA_QNAME.getLocalName());
         result.appendChild(dataElement);
-        for (XmlElement xmlElement : xmlElements) {
+        for (var xmlElement : selectMatchingNodes(getSourceElement(anyxml.body()), path)) {
             dataElement.appendChild(result.importNode(xmlElement.getDomElement(), true));
         }
-        final AnyXmlNode resultAnyxml = Builders.anyXmlBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(dataQName))
-                .withValue(new DOMSource(result))
-                .build();
-        return Optional.of(resultAnyxml);
+        return Optional.of(Builders.anyXmlBuilder()
+            .withNodeIdentifier(NETCONF_DATA_NODEID)
+            .withValue(new DOMSource(result))
+            .build());
     }
 
     /**
@@ -74,41 +77,38 @@ class SchemalessRpcStructureTransformer implements RpcStructureTransformer {
      * @return config structure
      */
     @Override
-    public AnyXmlNode createEditConfigStructure(final Optional<NormalizedNode<?, ?>> data,
-                                                final YangInstanceIdentifier dataPath,
-                                                final Optional<ModifyAction> operation) {
-        Preconditions.checkArgument(data.isPresent());
-        Preconditions.checkArgument(data.get() instanceof AnyXmlNode);
-
-        final AnyXmlNode anxmlData = (AnyXmlNode) data.get();
-        final Document document = XmlUtil.newDocument();
-        final Element dataNode = (Element) document.importNode(getSourceElement(anxmlData.getValue()), true);
+    public DOMSourceAnyxmlNode createEditConfigStructure(final Optional<NormalizedNode> data,
+            final YangInstanceIdentifier dataPath, final Optional<ModifyAction> operation) {
+        final var dataValue = data.orElseThrow();
+        if (!(dataValue instanceof DOMSourceAnyxmlNode anxmlData)) {
+            throw new IllegalArgumentException("Unexpected data " + dataValue.prettyTree());
+        }
+
+        final var document = XmlUtil.newDocument();
+        final var dataNode = (Element) document.importNode(getSourceElement(anxmlData.body()), true);
         checkDataValidForPath(dataPath, dataNode);
 
-        final Element configElement = document.createElementNS(NETCONF_CONFIG_QNAME.getNamespace().toString(),
+        final var configElement = document.createElementNS(NETCONF_CONFIG_QNAME.getNamespace().toString(),
                 NETCONF_CONFIG_QNAME.getLocalName());
         document.appendChild(configElement);
 
         final Element parentXmlStructure;
-        if (dataPath.equals(YangInstanceIdentifier.EMPTY)) {
+        if (dataPath.isEmpty()) {
             parentXmlStructure = dataNode;
             configElement.appendChild(parentXmlStructure);
         } else {
-            final List<YangInstanceIdentifier.PathArgument> pathArguments = dataPath.getPathArguments();
-            //last will be appended later
-            final List<YangInstanceIdentifier.PathArgument> pathWithoutLast =
-                    pathArguments.subList(0, pathArguments.size() - 1);
-            parentXmlStructure = instanceIdToXmlStructure(pathWithoutLast, configElement);
-        }
-        if (operation.isPresent()) {
-            setOperationAttribute(operation, document, dataNode);
+            final var pathArguments = dataPath.getPathArguments();
+            // last will be appended later
+            parentXmlStructure = instanceIdToXmlStructure(pathArguments.subList(0, pathArguments.size() - 1),
+                configElement);
         }
+        operation.ifPresent(modifyAction -> setOperationAttribute(modifyAction, document, dataNode));
         //append data
         parentXmlStructure.appendChild(document.importNode(dataNode, true));
         return Builders.anyXmlBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NETCONF_CONFIG_QNAME))
-                .withValue(new DOMSource(document.getDocumentElement()))
-                .build();
+            .withNodeIdentifier(NETCONF_CONFIG_NODEID)
+            .withValue(new DOMSource(document.getDocumentElement()))
+            .build();
     }
 
     /**
@@ -118,51 +118,63 @@ class SchemalessRpcStructureTransformer implements RpcStructureTransformer {
      * @return filter structure
      */
     @Override
-    public DataContainerChild<?, ?> toFilterStructure(final YangInstanceIdentifier path) {
-        final Document document = XmlUtil.newDocument();
-        final QName filterQname = NetconfMessageTransformUtil.NETCONF_FILTER_QNAME;
-        final Element filter =
-                document.createElementNS(filterQname.getNamespace().toString(), filterQname.getLocalName());
-        final Attr a = document.createAttributeNS(filterQname.getNamespace().toString(), "type");
-        a.setTextContent("subtree");
-        filter.setAttributeNode(a);
+    public AnyxmlNode<?> toFilterStructure(final YangInstanceIdentifier path) {
+        final var document = XmlUtil.newDocument();
+        instanceIdToXmlStructure(path.getPathArguments(), prepareFilterElement(document));
+        return buildFilterXmlNode(document);
+    }
+
+    @Override
+    public AnyxmlNode<?> toFilterStructure(final List<FieldsFilter> fieldsFilters) {
+        final var document = XmlUtil.newDocument();
+        final var filterElement = prepareFilterElement(document);
+        for (var filter : fieldsFilters) {
+            writeSchemalessFilter(filter.path(), filter.fields(), filterElement);
+        }
+        return buildFilterXmlNode(document);
+    }
+
+    private static Element prepareFilterElement(final Document document) {
+        // FIXME: use a constant
+        final var filterNs = NETCONF_FILTER_QNAME.getNamespace().toString();
+        final var filter = document.createElementNS(filterNs, NETCONF_FILTER_QNAME.getLocalName());
+        final var attr = document.createAttributeNS(filterNs, "type");
+        attr.setTextContent("subtree");
+        filter.setAttributeNode(attr);
         document.appendChild(filter);
-        instanceIdToXmlStructure(path.getPathArguments(), filter);
+        return filter;
+    }
+
+    private static AnyxmlNode<?> buildFilterXmlNode(final Document document) {
         return Builders.anyXmlBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(filterQname))
-                .withValue(new DOMSource(document.getDocumentElement()))
-                .build();
+            .withNodeIdentifier(NETCONF_FILTER_NODEID)
+            .withValue(new DOMSource(document.getDocumentElement()))
+            .build();
     }
 
     private static void checkDataValidForPath(final YangInstanceIdentifier dataPath, final Element dataNode) {
         //if datapath is empty, consider dataNode to be a root node
-        if (dataPath.equals(YangInstanceIdentifier.EMPTY)) {
+        if (dataPath.isEmpty()) {
             return;
         }
-        final XmlElement dataElement = XmlElement.fromDomElement(dataNode);
-        final YangInstanceIdentifier.PathArgument lastPathArgument = dataPath.getLastPathArgument();
-        final QName nodeType = lastPathArgument.getNodeType();
+        final var dataElement = XmlElement.fromDomElement(dataNode);
+        final var  lastPathArgument = dataPath.getLastPathArgument();
+        final var nodeType = lastPathArgument.getNodeType();
         if (!nodeType.getNamespace().toString().equals(dataNode.getNamespaceURI())
                 || !nodeType.getLocalName().equals(dataElement.getName())) {
             throw new IllegalStateException(
                     String.format("Can't write data '%s' to path %s", dataNode.getTagName(), dataPath));
         }
-        if (lastPathArgument instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates) {
+        if (lastPathArgument instanceof NodeIdentifierWithPredicates) {
             checkKeyValuesValidForPath(dataElement, lastPathArgument);
         }
-
     }
 
-    private static void checkKeyValuesValidForPath(final XmlElement dataElement,
-                                            final YangInstanceIdentifier.PathArgument lastPathArgument) {
-        final YangInstanceIdentifier.NodeIdentifierWithPredicates keyedId =
-                (YangInstanceIdentifier.NodeIdentifierWithPredicates) lastPathArgument;
-        final Map<QName, Object> keyValues = keyedId.getKeyValues();
-        for (Entry<QName, Object> entry : keyValues.entrySet()) {
-            QName qualifiedName = entry.getKey();
-            final List<XmlElement> key =
-                    dataElement.getChildElementsWithinNamespace(qualifiedName.getLocalName(),
-                            qualifiedName.getNamespace().toString());
+    private static void checkKeyValuesValidForPath(final XmlElement dataElement, final PathArgument lastPathArgument) {
+        for (var entry : ((NodeIdentifierWithPredicates) lastPathArgument).entrySet()) {
+            final var qname = entry.getKey();
+            final var key = dataElement.getChildElementsWithinNamespace(qname.getLocalName(),
+                qname.getNamespace().toString());
             if (key.isEmpty()) {
                 throw new IllegalStateException("No key present in xml");
             }
@@ -181,33 +193,24 @@ class SchemalessRpcStructureTransformer implements RpcStructureTransformer {
         }
     }
 
-    private static void setOperationAttribute(final Optional<ModifyAction> operation, final Document document,
-                                       final Element dataNode) {
-        final QName operationQname = NetconfMessageTransformUtil.NETCONF_OPERATION_QNAME;
-        final Attr operationAttribute =
-                document.createAttributeNS(operationQname.getNamespace().toString(), operationQname.getLocalName());
-        operationAttribute.setTextContent(toOperationString(operation.get()));
+    private static void setOperationAttribute(final ModifyAction operation, final Document document,
+            final Element dataNode) {
+        final var operationAttribute = document.createAttributeNS(NETCONF_OPERATION_QNAME.getNamespace().toString(),
+            NETCONF_OPERATION_QNAME.getLocalName());
+        operationAttribute.setTextContent(operation.xmlValue());
         dataNode.setAttributeNode(operationAttribute);
     }
 
-    private static Element instanceIdToXmlStructure(final List<YangInstanceIdentifier.PathArgument> pathArguments,
-                                                    final Element data) {
-        final Document doc = data.getOwnerDocument();
-        Element parent = data;
-        for (YangInstanceIdentifier.PathArgument pathArgument : pathArguments) {
-            final QName nodeType = pathArgument.getNodeType();
-            final Element element = doc.createElementNS(nodeType.getNamespace().toString(), nodeType.getLocalName());
+    private static Element instanceIdToXmlStructure(final List<PathArgument> pathArguments, final Element data) {
+        final var doc = data.getOwnerDocument();
+        var parent = data;
+        for (var pathArgument : pathArguments) {
+            final var nodeType = pathArgument.getNodeType();
+            final var element = doc.createElementNS(nodeType.getNamespace().toString(), nodeType.getLocalName());
             parent.appendChild(element);
             //if path argument is list id, add also keys to resulting xml
-            if (pathArgument instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates) {
-                YangInstanceIdentifier.NodeIdentifierWithPredicates listNode =
-                        (YangInstanceIdentifier.NodeIdentifierWithPredicates) pathArgument;
-                for (Map.Entry<QName, Object> key : listNode.getKeyValues().entrySet()) {
-                    final Element keyElement =
-                            doc.createElementNS(key.getKey().getNamespace().toString(), key.getKey().getLocalName());
-                    keyElement.setTextContent(key.getValue().toString());
-                    element.appendChild(keyElement);
-                }
+            if (pathArgument instanceof NodeIdentifierWithPredicates nip) {
+                appendListKeyNodes(element, nip);
             }
             parent = element;
         }
@@ -215,32 +218,24 @@ class SchemalessRpcStructureTransformer implements RpcStructureTransformer {
     }
 
     private static List<XmlElement> selectMatchingNodes(final Element domElement, final YangInstanceIdentifier path) {
-        XmlElement element = XmlElement.fromDomElement(domElement);
-        for (YangInstanceIdentifier.PathArgument pathArgument : path.getPathArguments()) {
-            List<XmlElement> childElements = element.getChildElements(pathArgument.getNodeType().getLocalName());
+        var element = XmlElement.fromDomElement(domElement);
+        for (var pathArgument : path.getPathArguments()) {
+            var childElements = element.getChildElements(pathArgument.getNodeType().getLocalName());
             if (childElements.size() == 1) {
                 element = childElements.get(0);
             } else {
                 return childElements;
             }
         }
-        return Collections.singletonList(element);
-    }
-
-    private static String toOperationString(final ModifyAction operation) {
-        return operation.name().toLowerCase(Locale.ROOT);
+        return List.of(element);
     }
 
     private static Element getSourceElement(final DOMSource source) {
-        final Node node = source.getNode();
-        switch (node.getNodeType()) {
-            case Node.DOCUMENT_NODE:
-                return ((Document)node).getDocumentElement();
-            case Node.ELEMENT_NODE:
-                return (Element) node;
-            default:
-                throw new IllegalStateException("DOMSource node must be document or element.");
-        }
+        final var node = source.getNode();
+        return switch (node.getNodeType()) {
+            case Node.DOCUMENT_NODE -> ((Document) node).getDocumentElement();
+            case Node.ELEMENT_NODE -> (Element) node;
+            default -> throw new IllegalStateException("DOMSource node must be document or element.");
+        };
     }
-
 }