Merge "BUG-2218: Keep existing link augmentations during discovery process"
[controller.git] / opendaylight / netconf / config-netconf-connector / src / main / java / org / opendaylight / controller / netconf / confignetconfconnector / operations / runtimerpc / RuntimeRpcElementResolved.java
index 838e5d87317fd503ba545019e93c38a8c56000a0..551da722c06732b60219d80044b3d6ca5319f3fa 100644 (file)
@@ -8,19 +8,24 @@
 
 package org.opendaylight.controller.netconf.confignetconfconnector.operations.runtimerpc;
 
-import com.google.common.base.Preconditions;
-import com.google.common.base.Strings;
-import com.google.common.collect.Maps;
-import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
-import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.ModuleRpcs;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
-
-import javax.management.ObjectName;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.ModuleRpcs;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.rev130405.Modules;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.rev130405.modules.Module;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.Maps;
+
 /**
  * Represents parsed xpath to runtime bean instance
  */
@@ -33,17 +38,22 @@ public final class RuntimeRpcElementResolved {
 
     private RuntimeRpcElementResolved(String namespace, String moduleName, String instanceName, String runtimeBeanName,
             Map<String, String> additionalAttributes) {
-        this.moduleName = moduleName;
-        this.instanceName = instanceName;
+        this.moduleName = Preconditions.checkNotNull(moduleName, "Module name");
+        this.instanceName =  Preconditions.checkNotNull(instanceName, "Instance name");
         this.additionalAttributes = additionalAttributes;
-        this.namespace = namespace;
-        this.runtimeBeanName = runtimeBeanName;
+        this.namespace = Preconditions.checkNotNull(namespace, "Namespace");
+        this.runtimeBeanName = Preconditions.checkNotNull(runtimeBeanName, "Runtime bean name");
     }
 
     public String getModuleName() {
         return moduleName;
     }
 
+    @VisibleForTesting
+    Map<String, String> getAdditionalAttributes() {
+        return additionalAttributes;
+    }
+
     public String getInstanceName() {
         return instanceName;
     }
@@ -68,12 +78,45 @@ public final class RuntimeRpcElementResolved {
         return ObjectNameUtil.createRuntimeBeanName(moduleName, instanceName, additionalAttributesJavaNames);
     }
 
-    private static final String xpathPatternBlueprint = "/" + XmlNetconfConstants.DATA_KEY + "/"
-            + XmlNetconfConstants.MODULES_KEY + "/" + XmlNetconfConstants.MODULE_KEY + "\\["
-            + XmlNetconfConstants.NAME_KEY + "='(.+)'\\]/" + XmlNetconfConstants.INSTANCE_KEY + "\\["
-            + XmlNetconfConstants.NAME_KEY + "='([^']+)'\\](.*)";
+    /**
+     * Pattern for an absolute instance identifier xpath pointing to a runtime bean instance e.g:
+     * <pre>
+     * /modules/module[name=instanceName][type=moduleType]
+     * </pre>
+     * or
+     * <pre>
+     * /a:modules/a:module[a:name=instanceName][a:type=moduleType]
+     * </pre>
+     */
+    private static final String xpathPatternBlueprint =
+            "/" + getRegExForPrefixedName(Modules.QNAME.getLocalName())+ "/" + getRegExForPrefixedName(Module.QNAME.getLocalName())
+
+                    + "\\["
+                    + "(?<key1>" + getRegExForPrefixedName(XmlNetconfConstants.TYPE_KEY) + "|" + getRegExForPrefixedName(XmlNetconfConstants.NAME_KEY) + ")"
+                    + "=('|\")?(?<value1>[^'\"\\]]+)('|\")?"
+                    + "( and |\\]\\[)"
+                    + "(?<key2>" + getRegExForPrefixedName(XmlNetconfConstants.TYPE_KEY) + "|" + getRegExForPrefixedName(XmlNetconfConstants.NAME_KEY) + ")"
+                    + "=('|\")?(?<value2>[^'\"\\]]+)('|\")?"
+                    + "\\]"
+
+                    + "(?<additional>.*)";
+
+    /**
+     * Return reg ex that matches either the name with or without a prefix
+     */
+    private static String getRegExForPrefixedName(final String name) {
+        return "([^:]+:)?" + name;
+    }
+
     private static final Pattern xpathPattern = Pattern.compile(xpathPatternBlueprint);
-    private static final String additionalPatternBlueprint = "(.+)\\[(.+)='(.+)'\\]";
+
+    /**
+     * Pattern for additional path elements inside xpath for instance identifier pointing to an inner runtime bean e.g:
+     * <pre>
+     * /modules/module[name=instanceName and type=moduleType]/inner[key=b]
+     * </pre>
+     */
+    private static final String additionalPatternBlueprint = getRegExForPrefixedName("(?<additionalKey>.+)") + "\\[(?<prefixedKey>" + getRegExForPrefixedName("(.+)") + ")=('|\")?(?<additionalValue>[^'\"\\]]+)('|\")?\\]";
     private static final Pattern additionalPattern = Pattern.compile(additionalPatternBlueprint);
 
     public static RuntimeRpcElementResolved fromXpath(String xpath, String elementName, String namespace) {
@@ -82,26 +125,64 @@ public final class RuntimeRpcElementResolved {
                 "Node %s with value '%s' not in required form on rpc element %s, required format is %s",
                 RuntimeRpc.CONTEXT_INSTANCE, xpath, elementName, xpathPatternBlueprint);
 
-        String moduleName = matcher.group(1);
-        String instanceName = matcher.group(2);
-        String additionalString = matcher.group(3);
-        HashMap<String, String> additionalAttributes = Maps.<String, String> newHashMap();
-        String runtimeBeanYangName = moduleName;
-        for (String additionalKeyValue : additionalString.split("/")) {
-            if (Strings.isNullOrEmpty(additionalKeyValue))
-                continue;
-            matcher = additionalPattern.matcher(additionalKeyValue);
-            Preconditions
-                    .checkState(
-                            matcher.matches(),
-                            "Attribute %s not in required form on rpc element %s, required format for additional attributes is  %s",
-                            additionalKeyValue, elementName, additionalPatternBlueprint);
-            String name = matcher.group(1);
-            runtimeBeanYangName = name;
-            additionalAttributes.put(name, matcher.group(3));
-        }
+        PatternGroupResolver groups = new PatternGroupResolver(matcher.group("key1"), matcher.group("value1"),
+                matcher.group("value2"), matcher.group("additional"));
+
+        String moduleName = groups.getModuleName();
+        String instanceName = groups.getInstanceName();
+
+        Map<String, String> additionalAttributes = groups.getAdditionalKeys(elementName, moduleName);
 
-        return new RuntimeRpcElementResolved(namespace, moduleName, instanceName, runtimeBeanYangName,
+        return new RuntimeRpcElementResolved(namespace, moduleName, instanceName, groups.getRuntimeBeanYangName(),
                 additionalAttributes);
     }
+
+    private static final class PatternGroupResolver {
+
+        private final String key1, value1, value2;
+        private final String additional;
+        private String runtimeBeanYangName;
+
+        PatternGroupResolver(String key1, String value1,  String value2, String additional) {
+            this.key1 = Preconditions.checkNotNull(key1);
+            this.value1 = Preconditions.checkNotNull(value1);
+            this.value2 = Preconditions.checkNotNull(value2);
+            this.additional = Preconditions.checkNotNull(additional);
+        }
+
+        String getModuleName() {
+            return key1.contains(XmlNetconfConstants.TYPE_KEY) ? value1 : value2;
+        }
+
+        String getInstanceName() {
+            return key1.contains(XmlNetconfConstants.NAME_KEY) ? value1 : value2;
+        }
+
+
+        Map<String, String> getAdditionalKeys(String elementName, String moduleName) {
+            HashMap<String, String> additionalAttributes = Maps.newHashMap();
+
+            runtimeBeanYangName = moduleName;
+            for (String additionalKeyValue : additional.split("/")) {
+                if (Strings.isNullOrEmpty(additionalKeyValue)){
+                    continue;
+                }
+                Matcher matcher = additionalPattern.matcher(additionalKeyValue);
+                Preconditions
+                        .checkState(
+                                matcher.matches(),
+                                "Attribute %s not in required form on rpc element %s, required format for additional attributes is: %s",
+                                additionalKeyValue, elementName, additionalPatternBlueprint);
+                String name = matcher.group("additionalKey");
+                runtimeBeanYangName = name;
+                additionalAttributes.put(name, matcher.group("additionalValue"));
+            }
+            return additionalAttributes;
+        }
+
+        private String getRuntimeBeanYangName() {
+            Preconditions.checkState(runtimeBeanYangName!=null);
+            return runtimeBeanYangName;
+        }
+    }
 }