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=132edfc347bdb7dd2405aefd755cfc832a118bd9;hb=e250f536389b2104171dd6c10a0a27783d602e0b;hp=1d7813f2baf0951a086f84c75bcede6d9faf29f8;hpb=a8e6502ba9309a17cc292d0aafe1c61ad471ab35;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 1d7813f2ba..132edfc347 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,36 @@ */ package org.opendaylight.yangtools.yang.model.util; -import java.util.ArrayList; +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.util.Arrays; +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.ChoiceCaseNode; import org.opendaylight.yangtools.yang.model.api.ChoiceNode; 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.GroupingDefinition; +import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.ModuleImport; +import org.opendaylight.yangtools.yang.model.api.NotificationDefinition; import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath; +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.RpcDefinition; -import org.opendaylight.yangtools.yang.model.api.NotificationDefinition; -import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; - -import org.opendaylight.yangtools.yang.model.api.GroupingDefinition; - -import com.google.common.base.Preconditions; - -import java.util.Set; +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; /** * The Schema Context Util contains support methods for searching through Schema @@ -41,7 +45,10 @@ import java.util.Set; * instantiable. * */ -public class SchemaContextUtil { +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() { } @@ -64,14 +71,18 @@ public class SchemaContextUtil { * @return SchemaNode from the end of the Schema Path or null * if the Node is not present. */ - public static SchemaNode findDataSchemaNode(SchemaContext context, SchemaPath schemaPath) { + public static SchemaNode findDataSchemaNode(final SchemaContext context, final SchemaPath schemaPath) { Preconditions.checkArgument(context != null, "Schema Context reference cannot be NULL"); Preconditions.checkArgument(schemaPath != null, "Schema Path reference cannot be NULL"); - List prefixedPath = (schemaPath.getPath()); - if (prefixedPath != null) { - return findNodeInSchemaContext(context, prefixedPath); + + final Iterable prefixedPath = schemaPath.getPathFromRoot(); + if (prefixedPath == null) { + LOG.debug("Schema path {} has null path", schemaPath); + return null; } - return null; + + LOG.trace("Looking for path {} in context {}", schemaPath, context); + return findNodeInSchemaContext(context, prefixedPath); } /** @@ -104,16 +115,14 @@ public class SchemaContextUtil { * Non-conditional Revision Aware XPath, or null if the * DataSchemaNode is not present in Schema Context. */ - public static SchemaNode findDataSchemaNode(SchemaContext context, Module module, RevisionAwareXPath nonCondXPath) { + public static SchemaNode findDataSchemaNode(final SchemaContext context, final Module module, final RevisionAwareXPath nonCondXPath) { Preconditions.checkArgument(context != null, "Schema Context reference cannot be NULL"); 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(); if (strXPath != null) { - if (strXPath.contains("[")) { - throw new IllegalArgumentException("Revision Aware XPath cannot contains condition"); - } + Preconditions.checkArgument(strXPath.indexOf('[') == -1, "Revision Aware XPath may not contain a condition"); if (nonCondXPath.isAbsolute()) { List qnamedPath = xpathToQNamePath(context, module, strXPath); if (qnamedPath != null) { @@ -163,8 +172,8 @@ public class SchemaContextUtil { * given relative Revision Aware XPath, otherwise will return * null. */ - public static SchemaNode findDataSchemaNodeForRelativeXPath(SchemaContext context, Module module, - SchemaNode actualSchemaNode, RevisionAwareXPath relativeXPath) { + public static SchemaNode findDataSchemaNodeForRelativeXPath(final SchemaContext context, final Module module, + final SchemaNode actualSchemaNode, final RevisionAwareXPath relativeXPath) { Preconditions.checkArgument(context != null, "Schema Context reference cannot be NULL"); Preconditions.checkArgument(module != null, "Module reference cannot be NULL"); Preconditions.checkArgument(actualSchemaNode != null, "Actual Schema Node reference cannot be NULL"); @@ -175,7 +184,7 @@ public class SchemaContextUtil { SchemaPath actualNodePath = actualSchemaNode.getPath(); if (actualNodePath != null) { - List qnamePath = resolveRelativeXPath(context, module, relativeXPath, actualSchemaNode); + Iterable qnamePath = resolveRelativeXPath(context, module, relativeXPath, actualSchemaNode); if (qnamePath != null) { return findNodeInSchemaContext(context, qnamePath); @@ -201,167 +210,214 @@ public class SchemaContextUtil { * Schema Node is NOT present, the method will returns * null */ - public static Module findParentModule(SchemaContext context, SchemaNode schemaNode) { + public static Module findParentModule(final SchemaContext context, final SchemaNode schemaNode) { Preconditions.checkArgument(context != null, "Schema Context reference cannot be NULL!"); Preconditions.checkArgument(schemaNode != null, "Schema Node cannot be NULL!"); Preconditions.checkState(schemaNode.getPath() != null, "Schema Path for Schema Node is not " + "set properly (Schema Path is NULL)"); - List 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(SchemaContext context, List path) { - QName current = path.get(0); - Module module = context.findModuleByNamespaceAndRevision(current.getNamespace(), current.getRevision()); - if (module == null) + public static SchemaNode findNodeInSchemaContext(final SchemaContext context, final Iterable 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()); + if (module == null) { + LOG.debug("Module {} not found", current); return null; + } + return findNodeInModule(module, path); } - public static GroupingDefinition findGrouping(SchemaContext context, Module module, 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; + 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; } - if (!found) { - throw new IllegalArgumentException("Failed to find referenced grouping: " + path + "(" - + qname.getLocalName() + ")"); + } + + 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; } } - return (GroupingDefinition) currentParent; - } + 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; + } + } - private static SchemaNode findNodeInModule(Module module, List path) { - QName current = path.get(0); - SchemaNode node = module.getDataChildByName(current); - if (node != null) - return findNode((DataSchemaNode) node, nextLevel(path)); - node = getRpcByName(module, current); - if (node != null) - return findNodeInRpc((RpcDefinition) node, nextLevel(path)); - node = getNotificationByName(module, current); - if (node != null) - return findNodeInNotification((NotificationDefinition) node, nextLevel(path)); - node = getGroupingByName(module, current); - if (node != null) - return findNodeInGrouping((GroupingDefinition) node, nextLevel(path)); - return node; + 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; + } + } + + LOG.debug("No node matching {} found in module {}", path, module); + return null; } - private static SchemaNode findNodeInGrouping(GroupingDefinition grouping, List path) { - if (path.isEmpty()) + 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; - QName current = path.get(0); - DataSchemaNode node = grouping.getDataChildByName(current); + } + + LOG.trace("Looking for path {} in grouping {}", path, grouping); + final DataSchemaNode node = grouping.getDataChildByName(current); + if (node != null) return findNode(node, nextLevel(path)); + + for (GroupingDefinition groupingDefinition : grouping.getGroupings()) { + if (groupingDefinition.getQName().equals(current)) + return findNodeInGrouping(groupingDefinition, nextLevel(path)); + } + + LOG.debug("No node matching {} found in grouping {}", current, grouping); return null; } - private static SchemaNode findNodeInRpc(RpcDefinition rpc, List path) { - if (path.isEmpty()) + 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; - QName current = path.get(0); + } + + 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); + return null; } - return null; } - private static SchemaNode findNodeInNotification(NotificationDefinition rpc, List path) { - if (path.isEmpty()) - return rpc; - QName current = path.get(0); - DataSchemaNode node = rpc.getDataChildByName(current); - if (node != null) - return findNode(node, nextLevel(path)); - 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; + } + + 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; + } + + return findNode(node, nextLevel(path)); } - private static SchemaNode findNode(ChoiceNode parent, List path) { - if (path.isEmpty()) + private static SchemaNode findNode(final ChoiceNode parent, final Iterable 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) + if (node != null) { return findNodeInCase(node, nextLevel(path)); + } return null; } - private static SchemaNode findNode(ContainerSchemaNode parent, List path) { - if (path.isEmpty()) + private static SchemaNode findNode(final ContainerSchemaNode parent, final Iterable 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) - return findNode(node, nextLevel(path)); - return null; + } + + final DataSchemaNode node = parent.getDataChildByName(current); + if (node == null) { + LOG.debug("Failed to find {} in parent {}", path, parent); + return null; + } + + return findNode(node, nextLevel(path)); } - private static SchemaNode findNode(ListSchemaNode parent, List path) { - if (path.isEmpty()) + private static SchemaNode findNode(final ListSchemaNode parent, final Iterable 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) - return findNode(node, nextLevel(path)); - return null; + if (node == null) { + LOG.debug("Failed to find {} in parent {}", path, parent); + return null; + } + return findNode(node, nextLevel(path)); } - private static SchemaNode findNode(DataSchemaNode parent, List path) { - SchemaNode result = null; - if (path.isEmpty()) { - result = parent; - } else { + private static SchemaNode findNode(final DataSchemaNode parent, final Iterable path) { + final SchemaNode node; + if (!Iterables.isEmpty(path)) { if (parent instanceof ContainerSchemaNode) { - result = findNode((ContainerSchemaNode) parent, path); + node = findNode((ContainerSchemaNode) parent, path); } else if (parent instanceof ListSchemaNode) { - result = findNode((ListSchemaNode) parent, path); + node = findNode((ListSchemaNode) parent, path); } else if (parent instanceof ChoiceNode) { - result = findNode((ChoiceNode) parent, path); + node = findNode((ChoiceNode) parent, path); } else { - throw new IllegalArgumentException("Path nesting violation"); + throw new IllegalArgumentException( + String.format("Path nesting violation in parent %s path %s", parent, path)); } + } else { + node = parent; } - return result; + + if (node == null) { + LOG.debug("Failed to find {} in parent {}", path, parent); + return null; + } + return node; } - public static SchemaNode findNodeInCase(ChoiceCaseNode parent, List path) { - if (path.isEmpty()) + private static SchemaNode findNodeInCase(final ChoiceCaseNode parent, final Iterable 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) - return findNode(node, nextLevel(path)); - return null; + if (node == null) { + LOG.debug("Failed to find {} in parent {}", path, parent); + return null; + } + return findNode(node, nextLevel(path)); } - public static RpcDefinition getRpcByName(Module module, QName name) { + private static RpcDefinition getRpcByName(final Module module, final QName name) { for (RpcDefinition rpc : module.getRpcs()) { if (rpc.getQName().equals(name)) { return rpc; @@ -370,11 +426,11 @@ public class SchemaContextUtil { return null; } - private static List nextLevel(List path) { - return path.subList(1, path.size()); + private static Iterable nextLevel(final Iterable path) { + return Iterables.skip(path, 1); } - public static NotificationDefinition getNotificationByName(Module module, QName name) { + private static NotificationDefinition getNotificationByName(final Module module, final QName name) { for (NotificationDefinition notification : module.getNotifications()) { if (notification.getQName().equals(name)) { return notification; @@ -383,7 +439,7 @@ public class SchemaContextUtil { return null; } - public static GroupingDefinition getGroupingByName(Module module, QName name) { + private static GroupingDefinition getGroupingByName(final Module module, final QName name) { for (GroupingDefinition grouping : module.getGroupings()) { if (grouping.getQName().equals(name)) { return grouping; @@ -409,14 +465,13 @@ public class SchemaContextUtil { * XPath String * @return return a list of QName */ - private static List xpathToQNamePath(SchemaContext context, Module parentModule, String xpath) { + private static List xpathToQNamePath(final SchemaContext context, final Module parentModule, final String xpath) { Preconditions.checkArgument(context != null, "Schema Context reference cannot be NULL"); Preconditions.checkArgument(parentModule != null, "Parent Module reference cannot be NULL"); Preconditions.checkArgument(xpath != null, "XPath string reference cannot be NULL"); List path = new LinkedList(); - String[] prefixedPath = xpath.split("/"); - for (String pathComponent : prefixedPath) { + for (String pathComponent : SLASH_SPLITTER.split(xpath)) { if (!pathComponent.isEmpty()) { path.add(stringPathPartToQName(context, parentModule, pathComponent)); } @@ -445,22 +500,22 @@ public class SchemaContextUtil { * Prefixed Path Part string * @return QName from prefixed Path Part String. */ - private static QName stringPathPartToQName(SchemaContext context, Module parentModule, String prefixedPathPart) { + private static QName stringPathPartToQName(final SchemaContext context, final Module parentModule, final String prefixedPathPart) { Preconditions.checkArgument(context != null, "Schema Context reference cannot be NULL"); 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 (module == null) { - throw new IllegalArgumentException("Failed to resolve xpath: no module found for prefix " - + prefixedName[0] + " in module " + parentModule.getName()); - } else { - return new QName(module.getNamespace(), module.getRevision(), prefixedName[1]); - } + if (prefixedPathPart.indexOf(':') != -1) { + final Iterator 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", + modulePrefix, parentModule.getName()); + + return QName.create(module.getQNameModule(), prefixedName.next()); } else { - return new QName(parentModule.getNamespace(), parentModule.getRevision(), prefixedPathPart); + return QName.create(parentModule.getNamespace(), parentModule.getRevision(), prefixedPathPart); } } @@ -488,7 +543,7 @@ public class SchemaContextUtil { * @return Module for given prefix in specified Schema Context if is * present, otherwise returns null */ - private static Module resolveModuleForPrefix(SchemaContext context, Module module, String prefix) { + private static Module resolveModuleForPrefix(final SchemaContext context, final Module module, final String prefix) { Preconditions.checkArgument(context != null, "Schema Context reference cannot be NULL"); Preconditions.checkArgument(module != null, "Module reference cannot be NULL"); Preconditions.checkArgument(prefix != null, "Prefix string cannot be NULL"); @@ -515,42 +570,148 @@ public 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 List resolveRelativeXPath(SchemaContext context, Module module, - RevisionAwareXPath relativeXPath, SchemaNode leafrefParentNode) { + private static Iterable resolveRelativeXPath(final SchemaContext context, final Module module, + 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"); - List absolutePath = new LinkedList(); - String strXPath = relativeXPath.toString(); - String[] xpaths = strXPath.split("/"); + 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; - while (xpaths[colCount].contains("..")) { - colCount = colCount + 1; - } - List path = leafrefParentNode.getPath().getPath(); - if (path != null) { - int lenght = path.size() - colCount; - absolutePath.addAll(path.subList(0, lenght)); - List xpathsList = Arrays.asList(xpaths); - List sublistedXPath = xpathsList.subList(colCount, xpaths.length); - List sublist = new ArrayList<>(); - for (String pathPart : sublistedXPath) { - sublist.add(stringPathPartToQName(context, module, pathPart)); - } - absolutePath.addAll(sublist); + for (Iterator it = xpaths.iterator(); it.hasNext() && it.next().contains(".."); ) { + ++colCount; + } + + 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); + } + })); + } - return absolutePath; + /** + * 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()); + + final Module parentModule = SchemaContextUtil.findParentModule(schemaContext, schema); + + final DataSchemaNode dataSchemaNode; + if(pathStatement.isAbsolute()) { + dataSchemaNode = (DataSchemaNode) SchemaContextUtil.findDataSchemaNode(schemaContext, parentModule, pathStatement); + } else { + dataSchemaNode = (DataSchemaNode) SchemaContextUtil.findDataSchemaNodeForRelativeXPath(schemaContext, parentModule, schema, 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; + } + } + + /** + * 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 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()); + } } }