Decouple config and netconf subsystems.
[controller.git] / opendaylight / netconf / netconf-impl / src / main / java / org / opendaylight / controller / netconf / impl / SubtreeFilter.java
index 6e8158413388d555e9d102354854f684f336d733..597380e5c118e99fe7916e5ceca36290b611d0df 100644 (file)
@@ -10,11 +10,12 @@ package org.opendaylight.controller.netconf.impl;
 
 import com.google.common.base.Optional;
 import java.io.IOException;
-import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import java.util.Map;
+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.controller.netconf.api.xml.XmlNetconfConstants;
 import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation.OperationNameAndNamespace;
-import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Attr;
@@ -27,17 +28,9 @@ import org.xml.sax.SAXException;
  * See <a href="http://tools.ietf.org/html/rfc6241#section-6">rfc6241</a> for details.
  */
 public class SubtreeFilter {
-    private static final Logger logger = LoggerFactory.getLogger(SubtreeFilter.class);
-
-    static Document applySubtreeFilter(Document requestDocument, Document rpcReply) throws NetconfDocumentedException {
-        // FIXME: rpcReply document must be reread otherwise some nodes do not inherit namespaces. (services/service)
-        try {
-            rpcReply = XmlUtil.readXmlToDocument(XmlUtil.toString(rpcReply, true));
-        } catch (SAXException | IOException e) {
-            logger.error("Cannot transform document", e);
-            throw new NetconfDocumentedException("Cannot transform document");
-        }
+    private static final Logger LOG = LoggerFactory.getLogger(SubtreeFilter.class);
 
+    static Document applySubtreeFilter(Document requestDocument, Document rpcReply) throws DocumentedException {
         OperationNameAndNamespace operationNameAndNamespace = new OperationNameAndNamespace(requestDocument);
         if (XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0.equals(operationNameAndNamespace.getNamespace()) &&
                 XmlNetconfConstants.GET.equals(operationNameAndNamespace.getOperationName()) ||
@@ -46,26 +39,36 @@ public class SubtreeFilter {
             // not implement filtering.
             Optional<XmlElement> maybeFilter = operationNameAndNamespace.getOperationElement().getOnlyChildElementOptionally(
                     XmlNetconfConstants.FILTER, XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
-            if (maybeFilter.isPresent() && (
-                    "subtree".equals(maybeFilter.get().getAttribute("type"))||
-                            "subtree".equals(maybeFilter.get().getAttribute("type", XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0))
-            )) {
+            if (!maybeFilter.isPresent()) {
+                return rpcReply;
+            }
 
+            // FIXME: rpcReply document must be reread otherwise some nodes do not inherit namespaces. (services/service)
+            try {
+                rpcReply = XmlUtil.readXmlToDocument(XmlUtil.toString(rpcReply, true));
+            } catch (SAXException | IOException e) {
+                LOG.error("Cannot transform document", e);
+                throw new DocumentedException("Cannot transform document" + e);
+            }
+            XmlElement filter = maybeFilter.get();
+            if ("subtree".equals(filter.getAttribute("type"))||
+                    "subtree".equals(filter.getAttribute("type", XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0))) {
 
                 // do
                 return filtered(maybeFilter.get(), rpcReply);
             }
         }
+
         return rpcReply; // return identical document
     }
 
-    private static Document filtered(XmlElement filter, Document originalReplyDocument) throws NetconfDocumentedException {
+    private static Document filtered(XmlElement filter, Document originalReplyDocument) throws DocumentedException {
         Document result = XmlUtil.newDocument();
         // even if filter is empty, copy /rpc/data
         Element rpcReply = originalReplyDocument.getDocumentElement();
         Node rpcReplyDst = result.importNode(rpcReply, false);
         result.appendChild(rpcReplyDst);
-        XmlElement dataSrc = XmlElement.fromDomElement(rpcReply).getOnlyChildElement("data", XmlNetconfConstants.RFC4741_TARGET_NAMESPACE);
+        XmlElement dataSrc = XmlElement.fromDomElement(rpcReply).getOnlyChildElement("data", XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
         Element dataDst = (Element) result.importNode(dataSrc.getDomElement(), false);
         rpcReplyDst.appendChild(dataDst);
         addSubtree(filter, dataSrc, XmlElement.fromDomElement(dataDst));
@@ -73,7 +76,7 @@ public class SubtreeFilter {
         return result;
     }
 
-    private static void addSubtree(XmlElement filter, XmlElement src, XmlElement dst) {
+    private static void addSubtree(XmlElement filter, XmlElement src, XmlElement dst) throws DocumentedException {
         for (XmlElement srcChild : src.getChildElements()) {
             for (XmlElement filterChild : filter.getChildElements()) {
                 addSubtree2(filterChild, srcChild, dst);
@@ -81,7 +84,7 @@ public class SubtreeFilter {
         }
     }
 
-    private static MatchingResult addSubtree2(XmlElement filter, XmlElement src, XmlElement dstParent) {
+    private static MatchingResult addSubtree2(XmlElement filter, XmlElement src, XmlElement dstParent) throws DocumentedException {
         Document document = dstParent.getDomElement().getOwnerDocument();
         MatchingResult matches = matches(src, filter);
         if (matches != MatchingResult.NO_MATCH && matches != MatchingResult.CONTENT_MISMATCH) {
@@ -123,7 +126,7 @@ public class SubtreeFilter {
      * Shallow compare src node to filter: tag name and namespace must match.
      * If filter node has no children and has text content, it also must match.
      */
-    private static MatchingResult matches(XmlElement src, XmlElement filter) {
+    private static MatchingResult matches(XmlElement src, XmlElement filter) throws DocumentedException {
         boolean tagMatch = src.getName().equals(filter.getName()) &&
                 src.getNamespaceOptionally().equals(filter.getNamespaceOptionally());
         MatchingResult result = null;
@@ -131,7 +134,7 @@ public class SubtreeFilter {
             // match text content
             Optional<String> maybeText = filter.getOnlyTextContentOptionally();
             if (maybeText.isPresent()) {
-                if (maybeText.equals(src.getOnlyTextContentOptionally())) {
+                if (maybeText.equals(src.getOnlyTextContentOptionally()) || prefixedContentMatches(filter, src)) {
                     result = MatchingResult.CONTENT_MATCH;
                 } else {
                     result = MatchingResult.CONTENT_MISMATCH;
@@ -159,10 +162,37 @@ public class SubtreeFilter {
         if (result == null) {
             result = MatchingResult.NO_MATCH;
         }
-        logger.debug("Matching {} to {} resulted in {}", src, filter, tagMatch);
+        LOG.debug("Matching {} to {} resulted in {}", src, filter, result);
         return result;
     }
 
+    private static boolean prefixedContentMatches(final XmlElement filter, final XmlElement src) throws DocumentedException {
+        final Map.Entry<String, String> prefixToNamespaceOfFilter;
+        final Map.Entry<String, String> prefixToNamespaceOfSrc;
+        try {
+            prefixToNamespaceOfFilter = filter.findNamespaceOfTextContent();
+            prefixToNamespaceOfSrc = src.findNamespaceOfTextContent();
+        } catch (IllegalArgumentException e) {
+            //if we can't find namespace of prefix - it's not a prefix, so it doesn't match
+            return false;
+        }
+
+        final String prefix = prefixToNamespaceOfFilter.getKey();
+        // If this is not a prefixed content, we do not need to continue since content do not match
+        if (prefix.equals(XmlElement.DEFAULT_NAMESPACE_PREFIX)) {
+            return false;
+        }
+        // Namespace mismatch
+        if (!prefixToNamespaceOfFilter.getValue().equals(prefixToNamespaceOfSrc.getValue())) {
+            return false;
+        }
+
+        final String unprefixedFilterContent = filter.getTextContent().substring(prefixToNamespaceOfFilter.getKey().length() + 1);
+        final String unprefixedSrcContnet = src.getTextContent().substring(prefixToNamespaceOfSrc.getKey().length() + 1);
+        // Finally compare unprefixed content
+        return unprefixedFilterContent.equals(unprefixedSrcContnet);
+    }
+
     enum MatchingResult {
         NO_MATCH, TAG_MATCH, CONTENT_MATCH, CONTENT_MISMATCH
     }