X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=yang%2Fyang-model-util%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fyang%2Fmodel%2Futil%2FSchemaContextUtil.java;h=7ad5070f195a118faf8590d75e3671208029c578;hb=e704e6a6d1cc4db7ac1e1f53b54ec3bf51aaecc3;hp=2af7159ffed81dd8d70cabaf205648acbefc2b0d;hpb=17549edd2e88754a942ece54cf6cc10c6b56f39a;p=yangtools.git diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtil.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtil.java index 2af7159ffe..7ad5070f19 100644 --- a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtil.java +++ b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtil.java @@ -7,32 +7,30 @@ */ package org.opendaylight.yangtools.yang.model.util; +import com.google.common.annotations.Beta; import com.google.common.base.Function; +import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; - -import java.net.URI; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; +import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; - +import java.util.regex.Pattern; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.model.api.AugmentationSchema; -import org.opendaylight.yangtools.yang.model.api.AugmentationTarget; -import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; -import org.opendaylight.yangtools.yang.model.api.ChoiceNode; +import org.opendaylight.yangtools.yang.common.QNameModule; +import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode; import org.opendaylight.yangtools.yang.model.api.GroupingDefinition; -import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.ModuleImport; import org.opendaylight.yangtools.yang.model.api.NotificationDefinition; @@ -41,7 +39,8 @@ import org.opendaylight.yangtools.yang.model.api.RpcDefinition; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaNode; import org.opendaylight.yangtools.yang.model.api.SchemaPath; -import org.opendaylight.yangtools.yang.model.api.UsesNode; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -82,7 +81,7 @@ public final class SchemaContextUtil { Preconditions.checkArgument(context != null, "Schema Context reference cannot be NULL"); Preconditions.checkArgument(schemaPath != null, "Schema Path reference cannot be NULL"); - final List prefixedPath = (schemaPath.getPath()); + final Iterable prefixedPath = schemaPath.getPathFromRoot(); if (prefixedPath == null) { LOG.debug("Schema path {} has null path", schemaPath); return null; @@ -127,11 +126,11 @@ public final class SchemaContextUtil { Preconditions.checkArgument(module != null, "Module reference cannot be NULL"); Preconditions.checkArgument(nonCondXPath != null, "Non Conditional Revision Aware XPath cannot be NULL"); - String strXPath = nonCondXPath.toString(); + final String strXPath = nonCondXPath.toString(); if (strXPath != null) { Preconditions.checkArgument(strXPath.indexOf('[') == -1, "Revision Aware XPath may not contain a condition"); if (nonCondXPath.isAbsolute()) { - List qnamedPath = xpathToQNamePath(context, module, strXPath); + final List qnamedPath = xpathToQNamePath(context, module, strXPath); if (qnamedPath != null) { return findNodeInSchemaContext(context, qnamedPath); } @@ -189,9 +188,9 @@ public final class SchemaContextUtil { "Revision Aware XPath MUST be relative i.e. MUST contains ../, " + "for non relative Revision Aware XPath use findDataSchemaNode method"); - SchemaPath actualNodePath = actualSchemaNode.getPath(); + final SchemaPath actualNodePath = actualSchemaNode.getPath(); if (actualNodePath != null) { - Iterable qnamePath = resolveRelativeXPath(context, module, relativeXPath, actualSchemaNode); + final Iterable qnamePath = resolveRelativeXPath(context, module, relativeXPath, actualSchemaNode); if (qnamePath != null) { return findNodeInSchemaContext(context, qnamePath); @@ -223,10 +222,10 @@ public final class SchemaContextUtil { Preconditions.checkState(schemaNode.getPath() != null, "Schema Path for Schema Node is not " + "set properly (Schema Path is NULL)"); - final QName qname = Iterables.getFirst(schemaNode.getPath().getPathTowardsRoot(), null); + final QName qname = schemaNode.getPath().getLastComponent(); 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."); + "The Schema Path MUST contain at least ONE QName which defines namespace and Local name of path."); return context.findModuleByNamespaceAndRevision(qname.getNamespace(), qname.getRevision()); } @@ -243,483 +242,209 @@ public final class SchemaContextUtil { return findNodeInModule(module, path); } - public static GroupingDefinition findGrouping(final SchemaContext context, final Module module, final List path) { - QName first = path.get(0); - Module m = context.findModuleByNamespace(first.getNamespace()).iterator().next(); - DataNodeContainer currentParent = m; - for (QName qname : path) { - boolean found = false; - DataNodeContainer node = (DataNodeContainer) currentParent.getDataChildByName(qname.getLocalName()); - if (node == null) { - Set groupings = currentParent.getGroupings(); - for (GroupingDefinition gr : groupings) { - if (gr.getQName().getLocalName().equals(qname.getLocalName())) { - currentParent = gr; - found = true; - } - } - } else { - found = true; - currentParent = node; + /** + * Returns NotificationDefinition from Schema Context + * + * @param schema SchemaContext in which lookup should be performed. + * @param path Schema Path of notification + * @return Notification schema or null, if notification is not present in schema context. + */ + @Beta + @Nullable public static NotificationDefinition getNotificationSchema(@Nonnull final SchemaContext schema, @Nonnull final SchemaPath path) { + Preconditions.checkNotNull(schema, "Schema context must not be null."); + Preconditions.checkNotNull(path, "Schema path must not be null."); + for (final NotificationDefinition potential : schema.getNotifications()) { + if (path.equals(potential.getPath())) { + return potential; } - - Preconditions.checkArgument(found, "Failed to find referenced grouping: %s(%s)", path, qname.getLocalName()); } - - return (GroupingDefinition) currentParent; + return null; } - private static SchemaNode findNodeInModule(final Module module, final Iterable path) { - final QName current = path.iterator().next(); - - LOG.trace("Looking for data container {} in module {}", current, module); - SchemaNode parent = module.getDataChildByName(current); - if (parent != null) { - final SchemaNode ret = findNode((DataSchemaNode) parent, nextLevel(path)); - if (ret != null) { - return ret; - } - } - - LOG.trace("Looking for RPC {} in module {}", current, module); - parent = getRpcByName(module, current); - if (parent != null) { - final SchemaNode ret = findNodeInRpc((RpcDefinition) parent, nextLevel(path)); - if (ret != null) { - return ret; - } - } - - LOG.trace("Looking for notification {} in module {}", current, module); - parent = getNotificationByName(module, current); - if (parent != null) { - final SchemaNode ret = findNodeInNotification((NotificationDefinition) parent, nextLevel(path)); - if (ret != null) { - return ret; - } - } - - LOG.trace("Looking for grouping {} in module {}", current, module); - parent = getGroupingByName(module, current); - if (parent != null) { - final SchemaNode ret = findNodeInGrouping((GroupingDefinition) parent, nextLevel(path)); - if (ret != null) { - return ret; + /** + * Returns RPC Input or Output Data container from RPC definition. + * + * @param schema SchemaContext in which lookup should be performed. + * @param path Schema path of RPC input/output data container + * @return Notification schema or null, if notification is not present in schema context. + */ + @Beta + @Nullable public static ContainerSchemaNode getRpcDataSchema(@Nonnull final SchemaContext schema, @Nonnull final SchemaPath path) { + Preconditions.checkNotNull(schema, "Schema context must not be null."); + Preconditions.checkNotNull(path, "Schema path must not be null."); + final Iterator it = path.getPathFromRoot().iterator(); + Preconditions.checkArgument(it.hasNext(), "Rpc must have QName."); + final QName rpcName = it.next(); + Preconditions.checkArgument(it.hasNext(), "input or output must be part of path."); + final QName inOrOut = it.next(); + for (final RpcDefinition potential : schema.getOperations()) { + if (rpcName.equals(potential.getQName())) { + return SchemaNodeUtils.getRpcDataSchema(potential, inOrOut); } } - - LOG.debug("No node matching {} found in module {}", path, module); return null; } - private static SchemaNode findNodeInGrouping(final GroupingDefinition grouping, final Iterable 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 DataSchemaNode node = grouping.getDataChildByName(current); - if (node == null) { - LOG.debug("No node matching {} found in grouping {}", current, grouping); - return null; - } + private static SchemaNode findNodeInModule(final Module module, final Iterable path) { - return findNode(node, nextLevel(path)); - } + Preconditions.checkArgument(module != null, "Parent reference cannot be NULL"); + Preconditions.checkArgument(path != null, "Path reference cannot be NULL"); - private static SchemaNode findNodeInRpc(final RpcDefinition rpc, final Iterable 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); - switch (current.getLocalName()) { - case "input": - return findNode(rpc.getInput(), nextLevel(path)); - case "output": - return findNode(rpc.getOutput(), nextLevel(path)); - default: - LOG.debug("Invalid component {} of path {} in RPC {}", current, path, rpc); + if (!path.iterator().hasNext()) { + LOG.debug("No node matching {} found in node {}", path, module); return null; } - } - private static SchemaNode findNodeInNotification(final NotificationDefinition ntf, final Iterable path) { - final QName current = Iterables.getFirst(path, null); - if (current == null) { - LOG.debug("Found notification {}", ntf); - return ntf; - } + final QName current = path.iterator().next(); + LOG.trace("Looking for node {} in module {}", current, module); - LOG.trace("Looking for path {} in notification {}", path, ntf); - DataSchemaNode node = ntf.getDataChildByName(current); - if (node == null) { - LOG.debug("No node matching {} found in notification {}", current, ntf); - return null; - } + SchemaNode foundNode = null; + final Iterable nextPath = nextLevel(path); - return findNode(node, nextLevel(path)); - } - - private static SchemaNode findNode(final ChoiceNode parent, final Iterable path) { - final QName current = Iterables.getFirst(path, null); - if (current == null) { - return parent; - } - ChoiceCaseNode node = parent.getCaseNodeByName(current); - if (node != null) { - return findNodeInCase(node, nextLevel(path)); + foundNode = module.getDataChildByName(current); + if (foundNode != null && nextPath.iterator().hasNext()) { + foundNode = findNodeIn(foundNode, nextPath); } - return null; - } - private static SchemaNode findNode(final ContainerSchemaNode parent, final Iterable path) { - final QName current = Iterables.getFirst(path, null); - if (current == null) { - return parent; + if (foundNode == null) { + foundNode = getGroupingByName(module, current); + if (foundNode != null && nextPath.iterator().hasNext()) { + foundNode = findNodeIn(foundNode, nextPath); + } } - final DataSchemaNode node = parent.getDataChildByName(current); - if (node == null) { - LOG.debug("Failed to find {} in parent {}", path, parent); - return null; + if (foundNode == null) { + foundNode = getRpcByName(module, current); + if (foundNode != null && nextPath.iterator().hasNext()) { + foundNode = findNodeIn(foundNode, nextPath); + } } - return findNode(node, nextLevel(path)); - } - - private static SchemaNode findNode(final ListSchemaNode parent, final Iterable path) { - final QName current = Iterables.getFirst(path, null); - if (current == null) { - return parent; + if (foundNode == null) { + foundNode = getNotificationByName(module, current); + if (foundNode != null && nextPath.iterator().hasNext()) { + foundNode = findNodeIn(foundNode, nextPath); + } } - DataSchemaNode node = parent.getDataChildByName(current); - if (node == null) { - LOG.debug("Failed to find {} in parent {}", path, parent); - return null; + if (foundNode == null) { + LOG.debug("No node matching {} found in node {}", path, module); } - return findNode(node, nextLevel(path)); - } - private static SchemaNode findNode(final DataSchemaNode parent, final Iterable path) { - final SchemaNode node; - if (!Iterables.isEmpty(path)) { - if (parent instanceof ContainerSchemaNode) { - node = findNode((ContainerSchemaNode) parent, path); - } else if (parent instanceof ListSchemaNode) { - node = findNode((ListSchemaNode) parent, path); - } else if (parent instanceof ChoiceNode) { - node = findNode((ChoiceNode) parent, path); - } else { - throw new IllegalArgumentException( - String.format("Path nesting violation in parent %s path %s", parent, path)); - } - } else { - node = parent; - } + return foundNode; - if (node == null) { - LOG.debug("Failed to find {} in parent {}", path, parent); - return null; - } - return node; } - public static SchemaNode findNodeInCase(final ChoiceCaseNode parent, final Iterable path) { - final QName current = Iterables.getFirst(path, null); - if (current == null) { - return parent; - } + private static SchemaNode findNodeIn(final SchemaNode parent, final Iterable path) { - DataSchemaNode node = parent.getDataChildByName(current); - if (node == null) { - LOG.debug("Failed to find {} in parent {}", path, parent); + Preconditions.checkArgument(parent != null, "Parent reference cannot be NULL"); + Preconditions.checkArgument(path != null, "Path reference cannot be NULL"); + + if (!path.iterator().hasNext()) { + LOG.debug("No node matching {} found in node {}", path, parent); return null; } - return findNode(node, nextLevel(path)); - } - public static RpcDefinition getRpcByName(final Module module, final QName name) { - for (RpcDefinition rpc : module.getRpcs()) { - if (rpc.getQName().equals(name)) { - return rpc; - } - } - return null; - } + final QName current = path.iterator().next(); + LOG.trace("Looking for node {} in node {}", current, parent); - private static Iterable nextLevel(final Iterable path) { - return Iterables.skip(path, 1); - } + SchemaNode foundNode = null; + final Iterable nextPath = nextLevel(path); - public static NotificationDefinition getNotificationByName(final Module module, final QName name) { - for (NotificationDefinition notification : module.getNotifications()) { - if (notification.getQName().equals(name)) { - return notification; - } - } - return null; - } + if (parent instanceof DataNodeContainer) { + final DataNodeContainer parentDataNodeContainer = (DataNodeContainer) parent; - public static GroupingDefinition getGroupingByName(final Module module, final QName name) { - for (GroupingDefinition grouping : module.getGroupings()) { - if (grouping.getQName().equals(name)) { - return grouping; + foundNode = parentDataNodeContainer.getDataChildByName(current); + if (foundNode != null && nextPath.iterator().hasNext()) { + foundNode = findNodeIn(foundNode, nextPath); } - } - return null; - } - /** - * Utility method which search for original node defined in grouping. - * - * @param node - * @return - */ - public static DataSchemaNode findOriginal(final DataSchemaNode node, final SchemaContext ctx) { - DataSchemaNode result = findCorrectTargetFromGrouping(node, ctx); - if (result == null) { - result = findCorrectTargetFromAugment(node, ctx); - if (result != null) { - if (result.isAddedByUses()) { - result = findOriginal(result, ctx); + if (foundNode == null) { + foundNode = getGroupingByName(parentDataNodeContainer, current); + if (foundNode != null && nextPath.iterator().hasNext()) { + foundNode = findNodeIn(foundNode, nextPath); } } } - return result; - } - - 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().getPathFromRoot()); - 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; - } - - 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 tmpPath = new ArrayList<>(); - Object parent = null; - - // 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()) { - result = getResultFromUses(u, currentName.getLocalName(), ctx); - if (result != null) { - break; - } - } - } + if (foundNode == null && parent instanceof RpcDefinition) { + final RpcDefinition parentRpcDefinition = (RpcDefinition) parent; - // 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) { - 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); + if (current.getLocalName().equals("input")) { + foundNode = parentRpcDefinition.getInput(); + if (foundNode != null && nextPath.iterator().hasNext()) { + foundNode = findNodeIn(foundNode, nextPath); } - } while (!(parent instanceof Module)); - - return null; - } else { - return findCorrectImmediateTargetFromGrouping(node, ctx); - } - } - - private static DataSchemaNode findCorrectTargetFromAugment(final DataSchemaNode node, final SchemaContext ctx) { - if (!node.isAugmenting()) { - return null; - } + } - QName currentName = node.getQName(); - Object currentNode = node; - Object parent = node; - List tmpPath = new ArrayList(); - List tmpTree = new ArrayList(); - - AugmentationSchema augment = null; - do { - SchemaPath sp = ((SchemaNode) parent).getPath(); - parent = findDataSchemaNode(ctx, sp.getParent()); - if (parent instanceof AugmentationTarget) { - tmpPath.add(currentName); - tmpTree.add((SchemaNode) currentNode); - augment = findNodeInAugment(((AugmentationTarget) parent).getAvailableAugmentations(), currentName); - if (augment == null) { - currentName = ((DataSchemaNode) parent).getQName(); - currentNode = parent; + if (current.getLocalName().equals("output")) { + foundNode = parentRpcDefinition.getOutput(); + if (foundNode != null && nextPath.iterator().hasNext()) { + foundNode = findNodeIn(foundNode, nextPath); } } - } while (((DataSchemaNode) parent).isAugmenting() && augment == null); - if (augment == null) { - return null; - } else { - Collections.reverse(tmpPath); - Collections.reverse(tmpTree); - Object actualParent = augment; - DataSchemaNode result = null; - for (QName name : tmpPath) { - if (actualParent instanceof DataNodeContainer) { - result = ((DataNodeContainer) actualParent).getDataChildByName(name.getLocalName()); - actualParent = ((DataNodeContainer) actualParent).getDataChildByName(name.getLocalName()); - } else { - if (actualParent instanceof ChoiceNode) { - result = ((ChoiceNode) actualParent).getCaseNodeByName(name.getLocalName()); - actualParent = ((ChoiceNode) actualParent).getCaseNodeByName(name.getLocalName()); - } + if (foundNode == null) { + foundNode = getGroupingByName(parentRpcDefinition, current); + if (foundNode != null && nextPath.iterator().hasNext()) { + foundNode = findNodeIn(foundNode, nextPath); } } + } - if (result.isAddedByUses()) { - result = findCorrectTargetFromAugmentGrouping(result, augment, tmpTree, ctx); + if (foundNode == null && parent instanceof ChoiceSchemaNode) { + foundNode = ((ChoiceSchemaNode) parent).getCaseNodeByName(current); + if (foundNode != null && nextPath.iterator().hasNext()) { + foundNode = findNodeIn(foundNode, nextPath); } + } - return result; + if (foundNode == null) { + LOG.debug("No node matching {} found in node {}", path, parent); } - } - private static DataSchemaNode getResultFromUses(final UsesNode u, final String currentName, final SchemaContext ctx) { - SchemaNode targetGrouping = findNodeInSchemaContext(ctx, u.getGroupingPath().getPathFromRoot()); + return foundNode; - Preconditions.checkArgument(targetGrouping instanceof GroupingDefinition, - "Failed to generate code for augment in %s", u); - GroupingDefinition gr = (GroupingDefinition) targetGrouping; - return gr.getDataChildByName(currentName); } - private static Module getParentModule(final SchemaNode node, final SchemaContext ctx) { - QName qname = node.getPath().getPathFromRoot().iterator().next(); - URI namespace = qname.getNamespace(); - Date revision = qname.getRevision(); - return ctx.findModuleByNamespaceAndRevision(namespace, revision); + private static Iterable nextLevel(final Iterable path) { + return Iterables.skip(path, 1); } - private static DataSchemaNode getTargetNode(final List tmpPath, final DataSchemaNode node, final SchemaContext ctx) { - DataSchemaNode result = node; - if (tmpPath.size() == 1) { - if (result != null && result.isAddedByUses()) { - result = findOriginal(result, ctx); - } - return result; - } else { - DataSchemaNode newParent = result; - Collections.reverse(tmpPath); - - tmpPath.remove(0); - for (QName name : tmpPath) { - // searching by local name is must, because node has different - // namespace in its original location - if (newParent == null) { - break; - } - if (newParent instanceof DataNodeContainer) { - newParent = ((DataNodeContainer) newParent).getDataChildByName(name.getLocalName()); - } else { - newParent = ((ChoiceNode) newParent).getCaseNodeByName(name.getLocalName()); - } - } - if (newParent != null && newParent.isAddedByUses()) { - newParent = findOriginal(newParent, ctx); + private static RpcDefinition getRpcByName(final Module module, final QName name) { + for (final RpcDefinition rpc : module.getRpcs()) { + if (rpc.getQName().equals(name)) { + return rpc; } - return newParent; } + return null; } - private static AugmentationSchema findNodeInAugment(final Collection augments, final QName name) { - for (AugmentationSchema augment : augments) { - DataSchemaNode node = augment.getDataChildByName(name); - if (node != null) { - return augment; + private static NotificationDefinition getNotificationByName(final Module module, final QName name) { + for (final NotificationDefinition notification : module.getNotifications()) { + if (notification.getQName().equals(name)) { + return notification; } } return null; } - private static DataSchemaNode findCorrectTargetFromAugmentGrouping(final DataSchemaNode node, - final AugmentationSchema parentNode, final List dataTree, final SchemaContext ctx) { - - DataSchemaNode result = null; - QName currentName = node.getQName(); - List tmpPath = new ArrayList<>(); - tmpPath.add(currentName); - int i = 1; - Object parent = null; - - do { - if (dataTree.size() < 2 || dataTree.size() == i) { - parent = parentNode; - } else { - parent = dataTree.get(dataTree.size() - (i + 1)); - tmpPath.add(((SchemaNode) parent).getQName()); - } - - if (parent instanceof DataNodeContainer) { - DataNodeContainer dataNodeParent = (DataNodeContainer) parent; - for (UsesNode u : dataNodeParent.getUses()) { - if (result == null) { - result = getResultFromUses(u, currentName.getLocalName(), ctx); - } - } + private static GroupingDefinition getGroupingByName(final DataNodeContainer dataNodeContainer, final QName name) { + for (final GroupingDefinition grouping : dataNodeContainer.getGroupings()) { + if (grouping.getQName().equals(name)) { + return grouping; } + } + return null; + } - if (result == null) { - i = i + 1; - currentName = ((SchemaNode) parent).getQName(); + private static GroupingDefinition getGroupingByName(final RpcDefinition rpc, final QName name) { + for (final GroupingDefinition grouping : rpc.getGroupings()) { + if (grouping.getQName().equals(name)) { + return grouping; } - } while (result == null); - - if (result != null) { - result = getTargetNode(tmpPath, result, ctx); } - return result; + return null; } /** @@ -744,8 +469,8 @@ public final class SchemaContextUtil { Preconditions.checkArgument(parentModule != null, "Parent Module reference cannot be NULL"); Preconditions.checkArgument(xpath != null, "XPath string reference cannot be NULL"); - List path = new LinkedList(); - for (String pathComponent : SLASH_SPLITTER.split(xpath)) { + final List path = new LinkedList(); + for (final String pathComponent : SLASH_SPLITTER.split(xpath)) { if (!pathComponent.isEmpty()) { path.add(stringPathPartToQName(context, parentModule, pathComponent)); } @@ -783,12 +508,11 @@ public final class SchemaContextUtil { final Iterator prefixedName = COLON_SPLITTER.split(prefixedPathPart).iterator(); final String modulePrefix = prefixedName.next(); - Module module = resolveModuleForPrefix(context, parentModule, modulePrefix); + final Module module = resolveModuleForPrefix(context, parentModule, modulePrefix); Preconditions.checkArgument(module != null, "Failed to resolve xpath: no module found for prefix %s in module %s", modulePrefix, parentModule.getName()); - // FIXME: Module should have a QNameModule handle - return QName.create(module.getNamespace(), module.getRevision(), prefixedName.next()); + return QName.create(module.getQNameModule(), prefixedName.next()); } else { return QName.create(parentModule.getNamespace(), parentModule.getRevision(), prefixedPathPart); } @@ -827,8 +551,8 @@ public final class SchemaContextUtil { return module; } - Set imports = module.getImports(); - for (ModuleImport mi : imports) { + final Set imports = module.getImports(); + for (final ModuleImport mi : imports) { if (prefix.equals(mi.getPrefix())) { return context.findModuleByName(mi.getModuleName(), mi.getRevision()); } @@ -845,38 +569,261 @@ public final class SchemaContextUtil { * Yang Module * @param relativeXPath * Non conditional Revision Aware Relative XPath - * @param leafrefSchemaPath - * Schema Path for Leafref + * @param actualSchemaNode + * actual schema node * @return list of QName */ private static Iterable resolveRelativeXPath(final SchemaContext context, final Module module, - final RevisionAwareXPath relativeXPath, final SchemaNode leafrefParentNode) { + final RevisionAwareXPath relativeXPath, final SchemaNode actualSchemaNode) { Preconditions.checkArgument(context != null, "Schema Context reference cannot be NULL"); Preconditions.checkArgument(module != null, "Module reference cannot be NULL"); Preconditions.checkArgument(relativeXPath != null, "Non Conditional Revision Aware XPath cannot be NULL"); Preconditions.checkState(!relativeXPath.isAbsolute(), "Revision Aware XPath MUST be relative i.e. MUST contains ../, " + "for non relative Revision Aware XPath use findDataSchemaNode method"); - Preconditions.checkState(leafrefParentNode.getPath() != null, + Preconditions.checkState(actualSchemaNode.getPath() != null, "Schema Path reference for Leafref cannot be NULL"); final Iterable xpaths = SLASH_SPLITTER.split(relativeXPath.toString()); // Find out how many "parent" components there are // FIXME: is .contains() the right check here? + // FIXME: case ../../node1/node2/../node3/../node4 int colCount = 0; - for (Iterator it = xpaths.iterator(); it.hasNext() && it.next().contains(".."); ) { + for (final Iterator it = xpaths.iterator(); it.hasNext() && it.next().contains(".."); ) { ++colCount; } - final ImmutableList relative = ImmutableList.copyOf( + final Iterable schemaNodePath = actualSchemaNode.getPath().getPathFromRoot(); + + if (Iterables.size(schemaNodePath) - colCount >= 0) { + return Iterables.concat(Iterables.limit(schemaNodePath, Iterables.size(schemaNodePath) - colCount), + Iterables.transform(Iterables.skip(xpaths, colCount), new Function() { + @Override + public QName apply(final String input) { + return stringPathPartToQName(context, module, input); + } + })); + } + return Iterables.concat(schemaNodePath, Iterables.transform(Iterables.skip(xpaths, colCount), new Function() { @Override public QName apply(final String input) { return stringPathPartToQName(context, module, input); } })); - final Iterable parent = leafrefParentNode.getPath().getPathFromRoot(); - return Iterables.concat(Iterables.limit(parent, Iterables.size(parent) - colCount), relative); + } + + /** + * Extracts the base type of node on which schema node points to. If target node is again of type LeafrefTypeDefinition, methods will be call recursively until it reach concrete + * type definition. + * + * @param typeDefinition + * type of node which will be extracted + * @param schemaContext + * Schema Context + * @param schema + * Schema Node + * @return recursively found type definition this leafref is pointing to or null if the xpath is incorrect (null is there to preserve backwards compatibility) + */ + public static TypeDefinition getBaseTypeForLeafRef(final LeafrefTypeDefinition typeDefinition, final SchemaContext schemaContext, final SchemaNode schema) { + RevisionAwareXPath pathStatement = typeDefinition.getPathStatement(); + pathStatement = new RevisionAwareXPathImpl(stripConditionsFromXPathString(pathStatement), pathStatement.isAbsolute()); + + SchemaNode baseSchema = schema; + while (baseSchema instanceof DerivableSchemaNode) { + final Optional basePotential = ((DerivableSchemaNode) baseSchema).getOriginal(); + if (basePotential.isPresent()) { + baseSchema = basePotential.get(); + } else { + break; + } + } + + Module parentModule = findParentModuleOfReferencingType(schemaContext, baseSchema); + + final DataSchemaNode dataSchemaNode; + if(pathStatement.isAbsolute()) { + dataSchemaNode = (DataSchemaNode) SchemaContextUtil.findDataSchemaNode(schemaContext, parentModule, pathStatement); + } else { + dataSchemaNode = (DataSchemaNode) SchemaContextUtil.findDataSchemaNodeForRelativeXPath(schemaContext, parentModule, baseSchema, pathStatement); + } + + // FIXME this is just to preserve backwards compatibility since yangtools do not mind wrong leafref xpaths + // and current expected behaviour for such cases is to just use pure string + // This should throw an exception about incorrect XPath in leafref + if(dataSchemaNode == null) { + return null; + } + + final TypeDefinition targetTypeDefinition = typeDefinition(dataSchemaNode); + + if(targetTypeDefinition instanceof LeafrefTypeDefinition) { + return getBaseTypeForLeafRef(((LeafrefTypeDefinition) targetTypeDefinition), schemaContext, dataSchemaNode); + } else { + return targetTypeDefinition; + } + } + + private static Module findParentModuleOfReferencingType(final SchemaContext schemaContext, + final SchemaNode schemaNode) { + Preconditions.checkArgument(schemaContext != null, "Schema Context reference cannot be NULL!"); + Preconditions.checkArgument(schemaNode != null, "Schema Node cannot be NULL!"); + TypeDefinition nodeType = null; + + if (schemaNode instanceof LeafSchemaNode) { + nodeType = ((LeafSchemaNode) schemaNode).getType(); + } else if (schemaNode instanceof LeafListSchemaNode) { + nodeType = ((LeafListSchemaNode) schemaNode).getType(); + } + + if (nodeType.getBaseType() != null) { + while (nodeType.getBaseType() != null) { + nodeType = nodeType.getBaseType(); + } + + final QNameModule typeDefModuleQname = nodeType.getQName().getModule(); + return schemaContext.findModuleByNamespaceAndRevision(typeDefModuleQname.getNamespace(), + typeDefModuleQname.getRevision()); + } + + return SchemaContextUtil.findParentModule(schemaContext, schemaNode); + } + + /** + * @deprecated due to expensive lookup + * Returns parent Yang Module for specified Schema Context in which Schema + * Node is declared. If Schema Node is of type 'ExtendedType' it tries to find parent module + * in which the type was originally declared (needed for correct leafref path resolution).
+ * If the Schema Node is not present in Schema Context the + * operation will return null.
+ * If Schema Context or Schema Node contains null references + * the method will throw IllegalArgumentException + * + * @throws IllegalArgumentException + * + * @param schemaContext + * Schema Context + * @param schemaNode + * Schema Node + * @return Yang Module for specified Schema Context and Schema Node, if + * Schema Node is NOT present, the method will returns + * null + */ + @Deprecated + public static Module findParentModuleByType(final SchemaContext schemaContext, final SchemaNode schemaNode) { + Preconditions.checkArgument(schemaContext != null, "Schema Context reference cannot be NULL!"); + Preconditions.checkArgument(schemaNode != null, "Schema Node cannot be NULL!"); + TypeDefinition nodeType = null; + + if (schemaNode instanceof LeafSchemaNode) { + nodeType = ((LeafSchemaNode) schemaNode).getType(); + } else if (schemaNode instanceof LeafListSchemaNode) { + nodeType = ((LeafListSchemaNode) schemaNode).getType(); + } + + if (!BaseTypes.isYangBuildInType(nodeType) && nodeType.getBaseType() != null) { + while (nodeType.getBaseType() != null && !BaseTypes.isYangBuildInType(nodeType.getBaseType())) { + nodeType = nodeType.getBaseType(); + } + + QNameModule typeDefModuleQname = nodeType.getQName().getModule(); + + return schemaContext.findModuleByNamespaceAndRevision(typeDefModuleQname.getNamespace(), + typeDefModuleQname.getRevision()); + } + + return SchemaContextUtil.findParentModule(schemaContext, schemaNode); + } + + /** + * Returns base type for {@code typeDefinition} which belongs to module specified via {@code qName}. This handle case + * when leafref type isn't specified as type substatement of leaf or leaf-list but is defined in other module as typedef + * which is then imported to referenced module. + * + * Because {@code typeDefinition} is definied via typedef statement, only absolute path is meaningful. + * + * @param typeDefinition + * @param schemaContext + * @param qName + * @return + */ + public static TypeDefinition getBaseTypeForLeafRef(final LeafrefTypeDefinition typeDefinition, + final SchemaContext schemaContext, final QName qName) { + final RevisionAwareXPath pathStatement = typeDefinition.getPathStatement(); + final RevisionAwareXPath strippedPathStatement = new RevisionAwareXPathImpl(stripConditionsFromXPathString(pathStatement), pathStatement.isAbsolute()); + if (!strippedPathStatement.isAbsolute()) { + return null; + } + + final Module parentModule = schemaContext.findModuleByNamespaceAndRevision(qName.getNamespace(),qName.getRevision()); + final DataSchemaNode dataSchemaNode = (DataSchemaNode) SchemaContextUtil.findDataSchemaNode(schemaContext, parentModule, strippedPathStatement); + final TypeDefinition targetTypeDefinition = typeDefinition(dataSchemaNode); + if (targetTypeDefinition instanceof LeafrefTypeDefinition) { + return getBaseTypeForLeafRef(((LeafrefTypeDefinition) targetTypeDefinition), schemaContext, dataSchemaNode); + } else { + return targetTypeDefinition; + } + } + + private static final Pattern STRIP_PATTERN = Pattern.compile("\\[.*\\]"); + + /** + * Removes conditions from xPath pointed to target node. + * + * @param pathStatement + * xPath to target node + * @return string representation of xPath without conditions + * + */ + private static String stripConditionsFromXPathString(final RevisionAwareXPath pathStatement) { + return STRIP_PATTERN.matcher(pathStatement.toString()).replaceAll(""); + } + + /** + * Extracts the base type of leaf schema node until it reach concrete type of TypeDefinition. + * + * @param node + * a node representing LeafSchemaNode + * @return concrete type definition of node value + */ + private static TypeDefinition typeDefinition(final LeafSchemaNode node) { + TypeDefinition baseType = node.getType(); + while (baseType.getBaseType() != null) { + baseType = baseType.getBaseType(); + } + return baseType; + } + + /** + * Extracts the base type of leaf schema node until it reach concrete type of TypeDefinition. + * + * @param node + * a node representing LeafListSchemaNode + * @return concrete type definition of node value + */ + private static TypeDefinition typeDefinition(final LeafListSchemaNode node) { + TypeDefinition baseType = node.getType(); + while (baseType.getBaseType() != null) { + baseType = baseType.getBaseType(); + } + return baseType; + } + + /** + * Gets the base type of DataSchemaNode value. + * + * @param node + * a node representing DataSchemaNode + * @return concrete type definition of node value + */ + private static TypeDefinition typeDefinition(final DataSchemaNode node) { + if (node instanceof LeafListSchemaNode) { + return typeDefinition((LeafListSchemaNode) node); + } else if (node instanceof LeafSchemaNode) { + return typeDefinition((LeafSchemaNode) node); + } else { + throw new IllegalArgumentException("Unhandled parameter types: " + Arrays. asList(node).toString()); + } } }