Merge "BUG-944: optimize SchemaContextUtil"
[yangtools.git] / yang / yang-model-util / src / main / java / org / opendaylight / yangtools / yang / model / util / SchemaContextUtil.java
index 64d7328004e9ea33497b372d351e56ecd4428ca3..53c1604b7c42d5d9c4efcfcc36a4034281d7bff6 100644 (file)
@@ -7,12 +7,16 @@
  */
 package org.opendaylight.yangtools.yang.model.util;
 
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+
 import java.net.URI;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
@@ -39,8 +43,6 @@ import org.opendaylight.yangtools.yang.model.api.UsesNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Preconditions;
-
 /**
  * The Schema Context Util contains support methods for searching through Schema
  * Context modules for specified schema nodes via Schema Path or Revision Aware
@@ -50,6 +52,8 @@ import com.google.common.base.Preconditions;
  */
 public final class SchemaContextUtil {
     private static final Logger LOG = LoggerFactory.getLogger(SchemaContextUtil.class);
+    private static final Splitter COLON_SPLITTER = Splitter.on(':');
+    private static final Splitter SLASH_SPLITTER = Splitter.on('/');
 
     private SchemaContextUtil() {
     }
@@ -217,13 +221,10 @@ public final class SchemaContextUtil {
         Preconditions.checkState(schemaNode.getPath() != null, "Schema Path for Schema Node is not "
                 + "set properly (Schema Path is NULL)");
 
-        List<QName> qnamedPath = schemaNode.getPath().getPath();
-        if (qnamedPath == null || qnamedPath.isEmpty()) {
-            throw new IllegalStateException("Schema Path contains invalid state of path parts."
-                    + "The Schema Path MUST contain at least ONE QName which defines namespace and Local name"
-                    + "of path.");
-        }
-        QName qname = qnamedPath.get(qnamedPath.size() - 1);
+        final QName qname = Iterables.getFirst(schemaNode.getPath().getPathTowardsRoot(), null);
+        Preconditions.checkState(qname != null,
+                "Schema Path contains invalid state of path parts. " +
+                "The Schema Path MUST contain at least ONE QName which defines namespace and Local name of path.");
         return context.findModuleByNamespaceAndRevision(qname.getNamespace(), qname.getRevision());
     }
 
@@ -491,69 +492,82 @@ public final class SchemaContextUtil {
         return result;
     }
 
-    private static DataSchemaNode findCorrectTargetFromGrouping(final DataSchemaNode node, final SchemaContext ctx) {
-        if (node.getPath().getPath().size() == 1) {
-            // uses is under module statement
-            Module m = findParentModule(ctx, node);
-            DataSchemaNode result = null;
-            for (UsesNode u : m.getUses()) {
-                SchemaNode targetGrouping = findNodeInSchemaContext(ctx, u.getGroupingPath().getPath());
-                if (!(targetGrouping instanceof GroupingDefinition)) {
-                    throw new IllegalArgumentException(String.format("Failed to generate code for augment in %s", u));
-                }
-                GroupingDefinition gr = (GroupingDefinition) targetGrouping;
-                result = gr.getDataChildByName(node.getQName().getLocalName());
-            }
-            if (result == null) {
-                throw new IllegalArgumentException("Failed to generate code for augment");
+    private static DataSchemaNode findCorrectImmediateTargetFromGrouping(final DataSchemaNode node, final SchemaContext ctx) {
+        // uses is under module statement
+        final Module m = findParentModule(ctx, node);
+        Preconditions.checkArgument(m != null, "Failed to find module for node {} in context {}", node, ctx);
+
+        for (final UsesNode u : m.getUses()) {
+            final SchemaNode targetGrouping = findNodeInSchemaContext(ctx, u.getGroupingPath().getPath());
+            Preconditions.checkArgument(targetGrouping instanceof GroupingDefinition,
+                    "Failed to generate code for augment in %s", u);
+
+            LOG.trace("Checking grouping {} for node {}", targetGrouping, node);
+            final GroupingDefinition gr = (GroupingDefinition) targetGrouping;
+            final DataSchemaNode result = gr.getDataChildByName(node.getQName().getLocalName());
+            if (result != null) {
+                return result;
             }
-            return result;
-        } else {
-            DataSchemaNode result = null;
+
+            LOG.debug("Skipped grouping {}, no matching node found", gr);
+        }
+
+        throw new IllegalArgumentException(
+                String.format("Failed to find uses node matching {} in context {}", node, ctx));
+    }
+
+    private static DataSchemaNode findCorrectTargetFromGrouping(final DataSchemaNode node, final SchemaContext ctx) {
+        if (node.getPath().getPath().size() != 1) {
             QName currentName = node.getQName();
+            // tmpPath is used to track level of nesting
             List<QName> tmpPath = new ArrayList<>();
             Object parent = null;
 
-            SchemaPath sp = node.getPath();
-            List<QName> names = sp.getPath();
-            List<QName> newNames = new ArrayList<>(names);
-            newNames.remove(newNames.size() - 1);
-            SchemaPath newSp = SchemaPath.create(newNames, sp.isAbsolute());
-            parent = findDataSchemaNode(ctx, newSp);
+            // create schema path of parent node
+            SchemaPath sp = node.getPath().getParent();
+            parent = findDataSchemaNode(ctx, sp);
 
             do {
                 tmpPath.add(currentName);
+
+                DataSchemaNode result = null;
+                // search parent node's used groupings for presence of wanted
+                // node
                 if (parent instanceof DataNodeContainer) {
                     DataNodeContainer dataNodeParent = (DataNodeContainer) parent;
                     for (UsesNode u : dataNodeParent.getUses()) {
-                        if (result == null) {
-                            result = getResultFromUses(u, currentName.getLocalName(), ctx);
+                        result = getResultFromUses(u, currentName.getLocalName(), ctx);
+                        if (result != null) {
+                            break;
                         }
                     }
                 }
+
+                // if node is not found in any of current parent's used
+                // groupings => parent is added by grouping too, so repeat same
+                // process for parent
                 if (result == null) {
-                    currentName = ((SchemaNode) parent).getQName();
-                    if (parent instanceof SchemaNode) {
-                        SchemaPath nodeSp = ((SchemaNode) parent).getPath();
-                        List<QName> nodeNames = nodeSp.getPath();
-                        List<QName> nodeNewNames = new ArrayList<>(nodeNames);
-                        nodeNewNames.remove(nodeNewNames.size() - 1);
-                        if (nodeNewNames.isEmpty()) {
-                            parent = getParentModule((SchemaNode) parent, ctx);
-                        } else {
-                            SchemaPath nodeNewSp = SchemaPath.create(nodeNewNames, nodeSp.isAbsolute());
-                            parent = findDataSchemaNode(ctx, nodeNewSp);
-                        }
-                    } else {
-                        throw new IllegalArgumentException("Failed to generate code for augment");
-                    }
+                    final SchemaNode sn = (SchemaNode) parent;
+
+                    // set current name to name of parent node
+                    currentName = sn.getQName();
+                    Preconditions.checkArgument(parent instanceof SchemaNode,
+                            "Failed to generate code for augmend node {} at parent {}", node, parent);
+
+                    // create schema path for parent of current parent
+                    final SchemaPath parentSp = sn.getPath().getParent();
+                    parent = parentSp.getPathFromRoot().iterator().hasNext() ? findDataSchemaNode(ctx, parentSp)
+                            : getParentModule(sn, ctx);
+                } else {
+                    // if wanted node was found in grouping, traverse this node
+                    // based on level of nesting
+                    return getTargetNode(tmpPath, result, ctx);
                 }
-            } while (result == null && !(parent instanceof Module));
+            } while (!(parent instanceof Module));
 
-            if (result != null) {
-                result = getTargetNode(tmpPath, result, ctx);
-            }
-            return result;
+            return null;
+        } else {
+            return findCorrectImmediateTargetFromGrouping(node, ctx);
         }
     }
 
@@ -571,11 +585,7 @@ public final class SchemaContextUtil {
         AugmentationSchema augment = null;
         do {
             SchemaPath sp = ((SchemaNode) parent).getPath();
-            List<QName> names = sp.getPath();
-            List<QName> newNames = new ArrayList<>(names);
-            newNames.remove(newNames.size() - 1);
-            SchemaPath newSp = SchemaPath.create(newNames, sp.isAbsolute());
-            parent = findDataSchemaNode(ctx, newSp);
+            parent = findDataSchemaNode(ctx, sp.getParent());
             if (parent instanceof AugmentationTarget) {
                 tmpPath.add(currentName);
                 tmpTree.add((SchemaNode) currentNode);
@@ -624,7 +634,7 @@ public final class SchemaContextUtil {
     }
 
     private static Module getParentModule(final SchemaNode node, final SchemaContext ctx) {
-        QName qname = node.getPath().getPath().get(0);
+        QName qname = node.getPath().getPathFromRoot().iterator().next();
         URI namespace = qname.getNamespace();
         Date revision = qname.getRevision();
         return ctx.findModuleByNamespaceAndRevision(namespace, revision);
@@ -733,8 +743,7 @@ public final class SchemaContextUtil {
         Preconditions.checkArgument(xpath != null, "XPath string reference cannot be NULL");
 
         List<QName> path = new LinkedList<QName>();
-        String[] prefixedPath = xpath.split("/");
-        for (String pathComponent : prefixedPath) {
+        for (String pathComponent : SLASH_SPLITTER.split(xpath)) {
             if (!pathComponent.isEmpty()) {
                 path.add(stringPathPartToQName(context, parentModule, pathComponent));
             }
@@ -768,14 +777,18 @@ public final class SchemaContextUtil {
         Preconditions.checkArgument(parentModule != null, "Parent Module reference cannot be NULL");
         Preconditions.checkArgument(prefixedPathPart != null, "Prefixed Path Part cannot be NULL!");
 
-        if (prefixedPathPart.contains(":")) {
-            String[] prefixedName = prefixedPathPart.split(":");
-            Module module = resolveModuleForPrefix(context, parentModule, prefixedName[0]);
+        if (prefixedPathPart.indexOf(':') != -1) {
+            final Iterator<String> prefixedName = COLON_SPLITTER.split(prefixedPathPart).iterator();
+            final String modulePrefix = prefixedName.next();
+
+            Module module = resolveModuleForPrefix(context, parentModule, modulePrefix);
             Preconditions.checkArgument(module != null, "Failed to resolve xpath: no module found for prefix %s in module %s",
-                    prefixedName[0], parentModule.getName());
-            return new QName(module.getNamespace(), module.getRevision(), prefixedName[1]);
+                    modulePrefix, parentModule.getName());
+
+            // FIXME: Module should have a QNameModule handle
+            return QName.create(module.getNamespace(), module.getRevision(), prefixedName.next());
         } else {
-            return new QName(parentModule.getNamespace(), parentModule.getRevision(), prefixedPathPart);
+            return QName.create(parentModule.getNamespace(), parentModule.getRevision(), prefixedPathPart);
         }
     }
 
@@ -845,25 +858,23 @@ public final class SchemaContextUtil {
         Preconditions.checkState(leafrefParentNode.getPath() != null,
                 "Schema Path reference for Leafref cannot be NULL");
 
-        List<QName> absolutePath = new LinkedList<QName>();
-        String strXPath = relativeXPath.toString();
-        String[] xpaths = strXPath.split("/");
+        final Iterable<String> xpaths = SLASH_SPLITTER.split(relativeXPath.toString());
 
+        // Find out how many "parent" components there are
+        // FIXME: is .contains() the right check here?
         int colCount = 0;
-        while (xpaths[colCount].contains("..")) {
-            colCount = colCount + 1;
+        for (Iterator<String> it = xpaths.iterator(); it.hasNext() && it.next().contains(".."); ) {
+            ++colCount;
         }
+
+        final List<QName> absolutePath = new LinkedList<QName>();
         List<QName> path = leafrefParentNode.getPath().getPath();
         if (path != null) {
             int lenght = path.size() - colCount;
             absolutePath.addAll(path.subList(0, lenght));
-            List<String> xpathsList = Arrays.asList(xpaths);
-            List<String> sublistedXPath = xpathsList.subList(colCount, xpaths.length);
-            List<QName> sublist = new ArrayList<>();
-            for (String pathPart : sublistedXPath) {
-                sublist.add(stringPathPartToQName(context, module, pathPart));
+            for (String s : Iterables.skip(xpaths, colCount)) {
+                absolutePath.add(stringPathPartToQName(context, module, s));
             }
-            absolutePath.addAll(sublist);
         }
 
         return absolutePath;