Revert "Revert "BUG-994: reorganize SchemaPath into a tree""
[yangtools.git] / yang / yang-model-util / src / main / java / org / opendaylight / yangtools / yang / model / util / SchemaContextUtil.java
index f05c20614d542618be17cef838aad0919f6b4e63..fc98c1daf0aef88d6e202743d0db8e3fcb56576d 100644 (file)
@@ -7,16 +7,21 @@
  */
 package org.opendaylight.yangtools.yang.model.util;
 
+import com.google.common.base.Function;
 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;
+
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
@@ -48,6 +53,8 @@ import org.slf4j.LoggerFactory;
  */
 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() {
     }
@@ -183,7 +190,7 @@ public final class SchemaContextUtil {
 
         SchemaPath actualNodePath = actualSchemaNode.getPath();
         if (actualNodePath != null) {
-            List<QName> qnamePath = resolveRelativeXPath(context, module, relativeXPath, actualSchemaNode);
+            Iterable<QName> qnamePath = resolveRelativeXPath(context, module, relativeXPath, actualSchemaNode);
 
             if (qnamePath != null) {
                 return findNodeInSchemaContext(context, qnamePath);
@@ -215,18 +222,15 @@ 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());
     }
 
-    public static SchemaNode findNodeInSchemaContext(final SchemaContext context, final List<QName> path) {
-        final QName current = path.get(0);
+    public static SchemaNode findNodeInSchemaContext(final SchemaContext context, final Iterable<QName> path) {
+        final QName current = path.iterator().next();
 
         LOG.trace("Looking up module {} in context {}", current, path);
         final Module module = context.findModuleByNamespaceAndRevision(current.getNamespace(), current.getRevision());
@@ -264,8 +268,8 @@ public final class SchemaContextUtil {
         return (GroupingDefinition) currentParent;
     }
 
-    private static SchemaNode findNodeInModule(final Module module, final List<QName> path) {
-        final QName current = path.get(0);
+    private static SchemaNode findNodeInModule(final Module module, final Iterable<QName> path) {
+        final QName current = path.iterator().next();
 
         LOG.trace("Looking for data container {} in module {}", current, module);
         SchemaNode parent = module.getDataChildByName(current);
@@ -307,14 +311,14 @@ public final class SchemaContextUtil {
         return null;
     }
 
-    private static SchemaNode findNodeInGrouping(final GroupingDefinition grouping, final List<QName> path) {
-        if (path.isEmpty()) {
+    private static SchemaNode findNodeInGrouping(final GroupingDefinition grouping, final Iterable<QName> path) {
+        final QName current = Iterables.getFirst(path, null);
+        if (current == null) {
             LOG.debug("Found grouping {}", grouping);
             return grouping;
         }
 
         LOG.trace("Looking for path {} in grouping {}", path, grouping);
-        final QName current = path.get(0);
         final DataSchemaNode node = grouping.getDataChildByName(current);
         if (node == null) {
             LOG.debug("No node matching {} found in grouping {}", current, grouping);
@@ -324,14 +328,14 @@ public final class SchemaContextUtil {
         return findNode(node, nextLevel(path));
     }
 
-    private static SchemaNode findNodeInRpc(final RpcDefinition rpc, final List<QName> path) {
-        if (path.isEmpty()) {
+    private static SchemaNode findNodeInRpc(final RpcDefinition rpc, final Iterable<QName> path) {
+        final QName current = Iterables.getFirst(path, null);
+        if (current == null) {
             LOG.debug("Found RPC {}", rpc);
             return rpc;
         }
 
         LOG.trace("Looking for path {} in rpc {}", path, rpc);
-        final QName current = path.get(0);
         switch (current.getLocalName()) {
         case "input":
             return findNode(rpc.getInput(), nextLevel(path));
@@ -343,14 +347,14 @@ public final class SchemaContextUtil {
         }
     }
 
-    private static SchemaNode findNodeInNotification(final NotificationDefinition ntf, final List<QName> path) {
-        if (path.isEmpty()) {
+    private static SchemaNode findNodeInNotification(final NotificationDefinition ntf, final Iterable<QName> path) {
+        final QName current = Iterables.getFirst(path, null);
+        if (current == null) {
             LOG.debug("Found notification {}", ntf);
             return ntf;
         }
 
         LOG.trace("Looking for path {} in notification {}", path, ntf);
-        final QName current = path.get(0);
         DataSchemaNode node = ntf.getDataChildByName(current);
         if (node == null) {
             LOG.debug("No node matching {} found in notification {}", current, ntf);
@@ -360,11 +364,11 @@ public final class SchemaContextUtil {
         return findNode(node, nextLevel(path));
     }
 
-    private static SchemaNode findNode(final ChoiceNode parent, final List<QName> path) {
-        if (path.isEmpty()) {
+    private static SchemaNode findNode(final ChoiceNode parent, final Iterable<QName> path) {
+        final QName current = Iterables.getFirst(path, null);
+        if (current == null) {
             return parent;
         }
-        QName current = path.get(0);
         ChoiceCaseNode node = parent.getCaseNodeByName(current);
         if (node != null) {
             return findNodeInCase(node, nextLevel(path));
@@ -372,12 +376,12 @@ public final class SchemaContextUtil {
         return null;
     }
 
-    private static SchemaNode findNode(final ContainerSchemaNode parent, final List<QName> path) {
-        if (path.isEmpty()) {
+    private static SchemaNode findNode(final ContainerSchemaNode parent, final Iterable<QName> path) {
+        final QName current = Iterables.getFirst(path, null);
+        if (current == null) {
             return parent;
         }
 
-        final QName current = path.get(0);
         final DataSchemaNode node = parent.getDataChildByName(current);
         if (node == null) {
             LOG.debug("Failed to find {} in parent {}", path, parent);
@@ -387,12 +391,12 @@ public final class SchemaContextUtil {
         return findNode(node, nextLevel(path));
     }
 
-    private static SchemaNode findNode(final ListSchemaNode parent, final List<QName> path) {
-        if (path.isEmpty()) {
+    private static SchemaNode findNode(final ListSchemaNode parent, final Iterable<QName> path) {
+        final QName current = Iterables.getFirst(path, null);
+        if (current == null) {
             return parent;
         }
 
-        QName current = path.get(0);
         DataSchemaNode node = parent.getDataChildByName(current);
         if (node == null) {
             LOG.debug("Failed to find {} in parent {}", path, parent);
@@ -401,9 +405,9 @@ public final class SchemaContextUtil {
         return findNode(node, nextLevel(path));
     }
 
-    private static SchemaNode findNode(final DataSchemaNode parent, final List<QName> path) {
+    private static SchemaNode findNode(final DataSchemaNode parent, final Iterable<QName> path) {
         final SchemaNode node;
-        if (!path.isEmpty()) {
+        if (!Iterables.isEmpty(path)) {
             if (parent instanceof ContainerSchemaNode) {
                 node = findNode((ContainerSchemaNode) parent, path);
             } else if (parent instanceof ListSchemaNode) {
@@ -425,12 +429,12 @@ public final class SchemaContextUtil {
         return node;
     }
 
-    public static SchemaNode findNodeInCase(final ChoiceCaseNode parent, final List<QName> path) {
-        if (path.isEmpty()) {
+    public static SchemaNode findNodeInCase(final ChoiceCaseNode parent, final Iterable<QName> path) {
+        final QName current = Iterables.getFirst(path, null);
+        if (current == null) {
             return parent;
         }
 
-        QName current = path.get(0);
         DataSchemaNode node = parent.getDataChildByName(current);
         if (node == null) {
             LOG.debug("Failed to find {} in parent {}", path, parent);
@@ -448,8 +452,8 @@ public final class SchemaContextUtil {
         return null;
     }
 
-    private static List<QName> nextLevel(final List<QName> path) {
-        return path.subList(1, path.size());
+    private static Iterable<QName> nextLevel(final Iterable<QName> path) {
+        return Iterables.skip(path, 1);
     }
 
     public static NotificationDefinition getNotificationByName(final Module module, final QName name) {
@@ -495,7 +499,7 @@ public final class SchemaContextUtil {
         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());
+            final SchemaNode targetGrouping = findNodeInSchemaContext(ctx, u.getGroupingPath().getPathFromRoot());
             Preconditions.checkArgument(targetGrouping instanceof GroupingDefinition,
                     "Failed to generate code for augment in %s", u);
 
@@ -521,13 +525,8 @@ public final class SchemaContextUtil {
             Object parent = null;
 
             // create schema path of parent node
-            SchemaPath sp = node.getPath();
-            List<QName> newNames = new ArrayList<>(sp.getPath());
-            // parentPath = nodePath - lastQName
-            newNames.remove(newNames.size() - 1);
-            SchemaPath newSp = SchemaPath.create(newNames, sp.isAbsolute());
-            // find parent node by its schema path
-            parent = findDataSchemaNode(ctx, newSp);
+            SchemaPath sp = node.getPath().getParent();
+            parent = findDataSchemaNode(ctx, sp);
 
             do {
                 tmpPath.add(currentName);
@@ -549,21 +548,17 @@ public final class SchemaContextUtil {
                 // groupings => parent is added by grouping too, so repeat same
                 // process for parent
                 if (result == null) {
+                    final SchemaNode sn = (SchemaNode) parent;
+
                     // set current name to name of parent node
-                    currentName = ((SchemaNode) parent).getQName();
+                    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
-                    SchemaPath nodeSp = ((SchemaNode) parent).getPath();
-                    List<QName> nodeNewNames = new ArrayList<>(nodeSp.getPath());
-                    nodeNewNames.remove(nodeNewNames.size() - 1);
-                    // set new parent based on path
-                    if (nodeNewNames.isEmpty()) {
-                        parent = getParentModule((SchemaNode) parent, ctx);
-                    } else {
-                        SchemaPath nodeNewSp = new SchemaPath(nodeNewNames, nodeSp.isAbsolute());
-                        parent = findDataSchemaNode(ctx, nodeNewSp);
-                    }
+                    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
@@ -591,11 +586,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);
@@ -635,7 +626,7 @@ public final class SchemaContextUtil {
     }
 
     private static DataSchemaNode getResultFromUses(final UsesNode u, final String currentName, final SchemaContext ctx) {
-        SchemaNode targetGrouping = findNodeInSchemaContext(ctx, u.getGroupingPath().getPath());
+        SchemaNode targetGrouping = findNodeInSchemaContext(ctx, u.getGroupingPath().getPathFromRoot());
 
         Preconditions.checkArgument(targetGrouping instanceof GroupingDefinition,
                 "Failed to generate code for augment in %s", u);
@@ -644,7 +635,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);
@@ -753,8 +744,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));
             }
@@ -788,14 +778,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);
         }
     }
 
@@ -854,7 +848,7 @@ public final class SchemaContextUtil {
      *            Schema Path for Leafref
      * @return list of QName
      */
-    private static List<QName> resolveRelativeXPath(final SchemaContext context, final Module module,
+    private static Iterable<QName> resolveRelativeXPath(final SchemaContext context, final Module module,
             final RevisionAwareXPath relativeXPath, final SchemaNode leafrefParentNode) {
         Preconditions.checkArgument(context != null, "Schema Context reference cannot be NULL");
         Preconditions.checkArgument(module != null, "Module reference cannot be NULL");
@@ -865,27 +859,22 @@ 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;
-        }
-        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));
-            }
-            absolutePath.addAll(sublist);
+        for (Iterator<String> it = xpaths.iterator(); it.hasNext() && it.next().contains(".."); ) {
+            ++colCount;
         }
 
-        return absolutePath;
+        final Iterable<QName> parent = leafrefParentNode.getPath().getPathFromRoot();
+        return Iterables.concat(Iterables.limit(parent, Iterables.size(parent) - colCount),
+                Iterables.transform(Iterables.skip(xpaths, colCount), new Function<String, QName>() {
+                    @Override
+                    public QName apply(final String input) {
+                        return stringPathPartToQName(context, module, input);
+                    }
+                }));
     }
 }