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=53c1604b7c42d5d9c4efcfcc36a4034281d7bff6;hb=c677b2dc5bb5dd33fd3f58343fc81f02aeef08bd;hp=ed8fbd15e1f9d7b23f91454458663b5029415554;hpb=5c1f875f69e35248aa4115c429bd962160beeef4;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 ed8fbd15e1..53c1604b7c 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,45 +7,65 @@ */ 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.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import java.util.Queue; 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; +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.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.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.UsesNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * The Schema Context Util contains support methods for searching through Schema Context modules for specified schema - * nodes via Schema Path or Revision Aware XPath. The Schema Context Util is designed as mixin, - * so it is not instantiable. + * The Schema Context Util contains support methods for searching through Schema + * Context modules for specified schema nodes via Schema Path or Revision Aware + * XPath. The Schema Context Util is designed as mixin, so it is not + * instantiable. * - * @author Lukas Sedlak */ 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() { } /** - * Method attempts to find DataSchemaNode in Schema Context via specified Schema Path. The returned - * DataSchemaNode from method will be the node at the end of the SchemaPath. If the DataSchemaNode is not present - * in the Schema Context the method will return null. - *
- * In case that Schema Context or Schema Path are not specified correctly (i.e. contains null - * values) the method will return IllegalArgumentException. + * Method attempts to find DataSchemaNode in Schema Context via specified + * Schema Path. The returned DataSchemaNode from method will be the node at + * the end of the SchemaPath. If the DataSchemaNode is not present in the + * Schema Context the method will return null.
+ * In case that Schema Context or Schema Path are not specified correctly + * (i.e. contains null values) the method will return + * IllegalArgumentException. * * @throws IllegalArgumentException * @@ -53,72 +73,65 @@ public final class SchemaContextUtil { * Schema Context * @param schemaPath * Schema Path to search for - * @return DataSchemaNode from the end of the Schema Path or - * null if the Node is not present. + * @return SchemaNode from the end of the Schema Path or null + * if the Node is not present. */ - public static DataSchemaNode findDataSchemaNode(final SchemaContext context, final SchemaPath schemaPath) { - if (context == null) { - throw new IllegalArgumentException("Schema Context reference cannot be NULL!"); - } - if (schemaPath == null) { - throw new IllegalArgumentException("Schema Path reference cannot be NULL"); - } - - final Module module = resolveModuleFromSchemaPath(context, schemaPath); - final Queue prefixedPath = new LinkedList<>(schemaPath.getPath()); + 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"); - if ((module != null) && (prefixedPath != null)) { - return findSchemaNodeForGivenPath(context, module, prefixedPath); + final List prefixedPath = (schemaPath.getPath()); + 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); } /** - * Method attempts to find DataSchemaNode inside of provided Schema Context and Yang Module accordingly to - * Non-conditional Revision Aware XPath. The specified Module MUST be present in Schema Context otherwise the - * operation would fail and return null. - *
- * The Revision Aware XPath MUST be specified WITHOUT the conditional statement (i.e. without [cond]) in path, - * because in this state the Schema Context is completely unaware of data state and will be not able to properly - * resolve XPath. If the XPath contains condition the method will return IllegalArgumentException. - *
- * In case that Schema Context or Module or Revision Aware XPath contains null references the method - * will throw IllegalArgumentException - *
- * If the Revision Aware XPath is correct and desired Data Schema Node is present in Yang module or in depending - * module in Schema Context the method will return specified Data Schema Node, otherwise the operation will fail - * and method will return null. + * Method attempts to find DataSchemaNode inside of provided Schema Context + * and Yang Module accordingly to Non-conditional Revision Aware XPath. The + * specified Module MUST be present in Schema Context otherwise the + * operation would fail and return null.
+ * The Revision Aware XPath MUST be specified WITHOUT the conditional + * statement (i.e. without [cond]) in path, because in this state the Schema + * Context is completely unaware of data state and will be not able to + * properly resolve XPath. If the XPath contains condition the method will + * return IllegalArgumentException.
+ * In case that Schema Context or Module or Revision Aware XPath contains + * null references the method will throw + * IllegalArgumentException
+ * If the Revision Aware XPath is correct and desired Data Schema Node is + * present in Yang module or in depending module in Schema Context the + * method will return specified Data Schema Node, otherwise the operation + * will fail and method will return null. * * @throws IllegalArgumentException * - * @param context Schema Context - * @param module Yang Module - * @param nonCondXPath Non Conditional Revision Aware XPath - * @return Returns Data Schema Node for specified Schema Context for given Non-conditional Revision Aware XPath, - * or null if the DataSchemaNode is not present in Schema Context. + * @param context + * Schema Context + * @param module + * Yang Module + * @param nonCondXPath + * Non Conditional Revision Aware XPath + * @return Returns Data Schema Node for specified Schema Context for given + * Non-conditional Revision Aware XPath, or null if the + * DataSchemaNode is not present in Schema Context. */ - public static DataSchemaNode findDataSchemaNode(final SchemaContext context, final Module module, - final RevisionAwareXPath nonCondXPath) { - if (context == null) { - throw new IllegalArgumentException("Schema Context reference cannot be NULL!"); - } - if (module == null) { - throw new IllegalArgumentException("Module reference cannot be NULL!"); - } - if (nonCondXPath == null) { - throw new IllegalArgumentException("Non Conditional Revision Aware XPath cannot be NULL!"); - } + 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"); - final String strXPath = nonCondXPath.toString(); + 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()) { - final Queue qnamedPath = xpathToQNamePath(context, module, strXPath); + List qnamedPath = xpathToQNamePath(context, module, strXPath); if (qnamedPath != null) { - final DataSchemaNode dataNode = findSchemaNodeForGivenPath(context, module, qnamedPath); - return dataNode; + return findNodeInSchemaContext(context, qnamedPath); } } } @@ -126,366 +139,694 @@ public final class SchemaContextUtil { } /** - * Method attempts to find DataSchemaNode inside of provided Schema Context and Yang Module accordingly to - * Non-conditional relative Revision Aware XPath. The specified Module MUST be present in Schema Context otherwise - * the operation would fail and return null. - *
- * The relative Revision Aware XPath MUST be specified WITHOUT the conditional statement (i.e. without [cond]) in - * path, because in this state the Schema Context is completely unaware of data state and will be not able to - * properly resolve XPath. If the XPath contains condition the method will return IllegalArgumentException. - *
- * The Actual Schema Node MUST be specified correctly because from this Schema Node will search starts. If the - * Actual Schema Node is not correct the operation will simply fail, because it will be unable to find desired - * DataSchemaNode. - *
- * In case that Schema Context or Module or Actual Schema Node or relative Revision Aware XPath contains - * null references the method will throw IllegalArgumentException - *
- * If the Revision Aware XPath doesn't have flag isAbsolute == false the method will - * throw IllegalArgumentException. - *
- * If the relative Revision Aware XPath is correct and desired Data Schema Node is present in Yang module or in - * depending module in Schema Context the method will return specified Data Schema Node, - * otherwise the operation will fail - * and method will return null. + * Method attempts to find DataSchemaNode inside of provided Schema Context + * and Yang Module accordingly to Non-conditional relative Revision Aware + * XPath. The specified Module MUST be present in Schema Context otherwise + * the operation would fail and return null.
+ * The relative Revision Aware XPath MUST be specified WITHOUT the + * conditional statement (i.e. without [cond]) in path, because in this + * state the Schema Context is completely unaware of data state and will be + * not able to properly resolve XPath. If the XPath contains condition the + * method will return IllegalArgumentException.
+ * The Actual Schema Node MUST be specified correctly because from this + * Schema Node will search starts. If the Actual Schema Node is not correct + * the operation will simply fail, because it will be unable to find desired + * DataSchemaNode.
+ * In case that Schema Context or Module or Actual Schema Node or relative + * Revision Aware XPath contains null references the method + * will throw IllegalArgumentException
+ * If the Revision Aware XPath doesn't have flag + * isAbsolute == false the method will throw + * IllegalArgumentException.
+ * If the relative Revision Aware XPath is correct and desired Data Schema + * Node is present in Yang module or in depending module in Schema Context + * the method will return specified Data Schema Node, otherwise the + * operation will fail and method will return null. * * @throws IllegalArgumentException * - * @param context Schema Context - * @param module Yang Module - * @param actualSchemaNode Actual Schema Node - * @param relativeXPath Relative Non Conditional Revision Aware XPath - * @return DataSchemaNode if is present in specified Schema Context for given relative Revision Aware XPath, - * otherwise will return null. + * @param context + * Schema Context + * @param module + * Yang Module + * @param actualSchemaNode + * Actual Schema Node + * @param relativeXPath + * Relative Non Conditional Revision Aware XPath + * @return DataSchemaNode if is present in specified Schema Context for + * given relative Revision Aware XPath, otherwise will return + * null. */ - public static DataSchemaNode findDataSchemaNodeForRelativeXPath(final SchemaContext context, final Module module, + public static SchemaNode findDataSchemaNodeForRelativeXPath(final SchemaContext context, final Module module, final SchemaNode actualSchemaNode, final RevisionAwareXPath relativeXPath) { - if (context == null) { - throw new IllegalArgumentException("Schema Context reference cannot be NULL!"); - } - if (module == null) { - throw new IllegalArgumentException("Module reference cannot be NULL!"); - } - if (actualSchemaNode == null) { - throw new IllegalArgumentException("Actual Schema Node reference cannot be NULL!"); - } - if (relativeXPath == null) { - throw new IllegalArgumentException("Non Conditional Revision Aware XPath cannot be NULL!"); - } - if (relativeXPath.isAbsolute()) { - throw new IllegalArgumentException("Revision Aware XPath MUST be relative i.e. MUST contains ../, " - + "for non relative Revision Aware XPath use findDataSchemaNode method!"); - } - - final SchemaPath actualNodePath = actualSchemaNode.getPath(); + 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"); + 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"); + + SchemaPath actualNodePath = actualSchemaNode.getPath(); if (actualNodePath != null) { - final Queue qnamePath = resolveRelativeXPath(context, module, relativeXPath, actualNodePath); + List qnamePath = resolveRelativeXPath(context, module, relativeXPath, actualSchemaNode); if (qnamePath != null) { - final DataSchemaNode dataNode = findSchemaNodeForGivenPath(context, module, qnamePath); - return dataNode; + return findNodeInSchemaContext(context, qnamePath); } } return null; } /** - * Retrieve information from Schema Path and returns the module reference to which Schema Node belongs. The - * search for correct Module is based on namespace within the last item in Schema Path. If schema context - * contains module with namespace specified in last item of Schema Path, then operation will returns Module - * reference, otherwise returns null - *
- * If Schema Context or Schema Node contains null references the method will throw IllegalArgumentException + * Returns parent Yang Module for specified Schema Context in which Schema + * Node is declared. 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 context Schema Context - * @param schemaPath Schema Path - * @return Module reference for given Schema Path if module is present in Schema Context, - * otherwise returns null + * @param context + * 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 */ - private static Module resolveModuleFromSchemaPath(final SchemaContext context, final SchemaPath schemaPath) { - if (context == null) { - throw new IllegalArgumentException("Schema Context reference cannot be NULL!"); + 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)"); + + 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 path) { + final QName current = path.get(0); + + 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; } - if (schemaPath == null) { - throw new IllegalArgumentException("Schema Path reference cannot be NULL"); + + 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; + } + + Preconditions.checkArgument(found, "Failed to find referenced grouping: %s(%s)", path, qname.getLocalName()); } - final List path = schemaPath.getPath(); - if (!path.isEmpty()) { - final QName qname = path.get(path.size() - 1); + return (GroupingDefinition) currentParent; + } + + private static SchemaNode findNodeInModule(final Module module, final List path) { + final QName current = path.get(0); + + 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 ((qname != null) && (qname.getNamespace() != null)) { - return context.findModuleByNamespace(qname.getNamespace()); + 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; + } + } + + LOG.debug("No node matching {} found in module {}", path, module); return null; } - /** - * Returns the Yang Module from specified Schema Context in which the TypeDefinition is declared. If the - * TypeDefinition si not present in Schema Context then the method will return null - * - * If Schema Context or TypeDefinition contains null references the method will throw IllegalArgumentException - * - * @throws IllegalArgumentException - * - * @param context Schema Context - * @param type Type Definition - * @return Yang Module in which the TypeDefinition is declared, if is not present, returns null. - */ - public static Module findParentModuleForTypeDefinition(final SchemaContext context, final TypeDefinition type) { - final SchemaPath schemaPath = type.getPath(); - if (schemaPath == null) { - throw new IllegalArgumentException("Schema Path reference cannot be NULL"); - } - final List qnamedPath = schemaPath.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."); - } - - if (type instanceof ExtendedType) { - final QName qname = qnamedPath.get(qnamedPath.size() - 1); - if ((qname != null) && (qname.getNamespace() != null)) { - return context.findModuleByNamespace(qname.getNamespace()); + private static SchemaNode findNodeInGrouping(final GroupingDefinition grouping, final List path) { + if (path.isEmpty()) { + 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); + return null; + } + + return findNode(node, nextLevel(path)); + } + + private static SchemaNode findNodeInRpc(final RpcDefinition rpc, final List path) { + if (path.isEmpty()) { + 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)); + case "output": + return findNode(rpc.getOutput(), nextLevel(path)); + default: + LOG.debug("Invalid component {} of path {} in RPC {}", current, path, rpc); + return null; + } + } + + private static SchemaNode findNodeInNotification(final NotificationDefinition ntf, final List path) { + if (path.isEmpty()) { + 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); + return null; + } + + return findNode(node, nextLevel(path)); + } + + private static SchemaNode findNode(final ChoiceNode parent, final List path) { + if (path.isEmpty()) { + return parent; + } + QName current = path.get(0); + ChoiceCaseNode node = parent.getCaseNodeByName(current); + if (node != null) { + return findNodeInCase(node, nextLevel(path)); + } + return null; + } + + private static SchemaNode findNode(final ContainerSchemaNode parent, final List path) { + if (path.isEmpty()) { + 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); + return null; + } + + return findNode(node, nextLevel(path)); + } + + private static SchemaNode findNode(final ListSchemaNode parent, final List path) { + if (path.isEmpty()) { + return parent; + } + + QName current = path.get(0); + 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(final DataSchemaNode parent, final List path) { + final SchemaNode node; + if (!path.isEmpty()) { + 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 { - final QName qname = qnamedPath.get(qnamedPath.size() - 2); - if ((qname != null) && (qname.getNamespace() != null)) { - return context.findModuleByNamespace(qname.getNamespace()); + node = parent; + } + + if (node == null) { + LOG.debug("Failed to find {} in parent {}", path, parent); + return null; + } + return node; + } + + public static SchemaNode findNodeInCase(final ChoiceCaseNode parent, final List path) { + if (path.isEmpty()) { + return parent; + } + + QName current = path.get(0); + DataSchemaNode node = parent.getDataChildByName(current); + if (node == null) { + LOG.debug("Failed to find {} in parent {}", 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; + } + + private static List nextLevel(final List path) { + return path.subList(1, path.size()); + } + + public static NotificationDefinition getNotificationByName(final Module module, final QName name) { + for (NotificationDefinition notification : module.getNotifications()) { + if (notification.getQName().equals(name)) { + return notification; + } + } + return null; + } + + public static GroupingDefinition getGroupingByName(final Module module, final QName name) { + for (GroupingDefinition grouping : module.getGroupings()) { + if (grouping.getQName().equals(name)) { + return grouping; } } return null; } /** - * Returns parent Yang Module for specified Schema Context in which Schema Node is declared. 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 + * Utility method which search for original node defined in grouping. * - * @param context 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 + * @param node + * @return */ - public static Module findParentModule(final SchemaContext context, final SchemaNode schemaNode) { - if (context == null) { - throw new IllegalArgumentException("Schema Context reference cannot be NULL!"); + 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 (schemaNode == null) { - throw new IllegalArgumentException("Schema Node cannot be NULL!"); + 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().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; + } + + LOG.debug("Skipped grouping {}, no matching node found", gr); } - final SchemaPath schemaPath = schemaNode.getPath(); - if (schemaPath == null) { - throw new IllegalStateException("Schema Path for Schema Node is not " - + "set properly (Schema Path is NULL)"); + 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 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); + } + } while (!(parent instanceof Module)); + + return null; + } else { + return findCorrectImmediateTargetFromGrouping(node, ctx); } - final List qnamedPath = schemaPath.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."); + } + + 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; + } + } + } 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 (result.isAddedByUses()) { + result = findCorrectTargetFromAugmentGrouping(result, augment, tmpTree, ctx); + } + + return result; } - final QName qname = qnamedPath.get(qnamedPath.size() - 1); - return context.findModuleByNamespace(qname.getNamespace()); } - /** - * Method will attempt to find DataSchemaNode from specified Module and Queue of QNames through the Schema - * Context. The QNamed path could be defined across multiple modules in Schema Context so the method is called - * recursively. If the QNamed path contains QNames that are not part of any Module or Schema Context Path the - * operation will fail and returns null - *
- * If Schema Context, Module or Queue of QNames refers to null values, - * the method will throws IllegalArgumentException - * - * @throws IllegalArgumentException - * - * @param context Schema Context - * @param module Yang Module - * @param qnamedPath Queue of QNames - * @return DataSchemaNode if is present in Module(s) for specified Schema Context and given QNamed Path, - * otherwise will return null. - */ - private static DataSchemaNode findSchemaNodeForGivenPath(final SchemaContext context, final Module module, - final Queue qnamedPath) { - if (context == null) { - throw new IllegalArgumentException("Schema Context reference cannot be NULL!"); + private static DataSchemaNode getResultFromUses(final UsesNode u, final String currentName, final SchemaContext ctx) { + SchemaNode targetGrouping = findNodeInSchemaContext(ctx, u.getGroupingPath().getPath()); + + 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 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); + } + return newParent; } - if (module == null) { - throw new IllegalArgumentException("Module reference cannot be NULL!"); - } - if (module.getNamespace() == null) { - throw new IllegalArgumentException("Namespace for Module cannot contains NULL reference!"); - } - 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."); - } - - DataNodeContainer nextNode = module; - final URI moduleNamespace = module.getNamespace(); - - QName childNodeQName; - DataSchemaNode schemaNode = null; - while ((nextNode != null) && !qnamedPath.isEmpty()) { - childNodeQName = qnamedPath.peek(); - if (childNodeQName != null) { - final URI childNodeNamespace = childNodeQName.getNamespace(); - - schemaNode = nextNode.getDataChildByName(childNodeQName); - if (schemaNode != null) { - if (schemaNode instanceof ContainerSchemaNode) { - nextNode = (ContainerSchemaNode) schemaNode; - } else if (schemaNode instanceof ListSchemaNode) { - nextNode = (ListSchemaNode) schemaNode; - } else if (schemaNode instanceof ChoiceNode) { - final ChoiceNode choice = (ChoiceNode) schemaNode; - qnamedPath.poll(); - if (!qnamedPath.isEmpty()) { - childNodeQName = qnamedPath.peek(); - nextNode = choice.getCaseNodeByName(childNodeQName); - schemaNode = (DataSchemaNode) nextNode; - } - } else { - nextNode = 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; + } + } + 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); } - } else if (!childNodeNamespace.equals(moduleNamespace)) { - final Module nextModule = context.findModuleByNamespace(childNodeNamespace); - schemaNode = findSchemaNodeForGivenPath(context, nextModule, qnamedPath); - return schemaNode; } - qnamedPath.poll(); } + + if (result == null) { + i = i + 1; + currentName = ((SchemaNode) parent).getQName(); + } + } while (result == null); + + if (result != null) { + result = getTargetNode(tmpPath, result, ctx); } - return schemaNode; + return result; } /** - * Transforms string representation of XPath to Queue of QNames. The XPath is split by "/" and for each part of - * XPath is assigned correct module in Schema Path. - *
- * If Schema Context, Parent Module or XPath string contains null values, - * the method will throws IllegalArgumentException + * Transforms string representation of XPath to Queue of QNames. The XPath + * is split by "/" and for each part of XPath is assigned correct module in + * Schema Path.
+ * If Schema Context, Parent Module or XPath string contains + * null values, the method will throws IllegalArgumentException * * @throws IllegalArgumentException * - * @param context Schema Context - * @param parentModule Parent Module - * @param xpath XPath String - * @return + * @param context + * Schema Context + * @param parentModule + * Parent Module + * @param xpath + * XPath String + * @return return a list of QName */ - private static Queue xpathToQNamePath(final SchemaContext context, final Module parentModule, - final String xpath) { - if (context == null) { - throw new IllegalArgumentException("Schema Context reference cannot be NULL!"); - } - if (parentModule == null) { - throw new IllegalArgumentException("Parent Module reference cannot be NULL!"); - } - if (xpath == null) { - throw new IllegalArgumentException("XPath string reference cannot be NULL!"); - } - - final Queue path = new LinkedList<>(); - final String[] prefixedPath = xpath.split("/"); - for (int i = 0; i < prefixedPath.length; ++i) { - if (!prefixedPath[i].isEmpty()) { - path.add(stringPathPartToQName(context, parentModule, prefixedPath[i])); + 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(); + for (String pathComponent : SLASH_SPLITTER.split(xpath)) { + if (!pathComponent.isEmpty()) { + path.add(stringPathPartToQName(context, parentModule, pathComponent)); } } return path; } /** - * Transforms part of Prefixed Path as java String to QName. - *
- * If the string contains module prefix separated by ":" (i.e. mod:container) this module is provided from from - * Parent Module list of imports. If the Prefixed module is present in Schema Context the QName can be - * constructed. - *
- * If the Prefixed Path Part does not contains prefix the Parent's Module namespace is taken for construction of - * QName. - *
- * If Schema Context, Parent Module or Prefixed Path Part refers to null the method will throw - * IllegalArgumentException + * Transforms part of Prefixed Path as java String to QName.
+ * If the string contains module prefix separated by ":" (i.e. + * mod:container) this module is provided from from Parent Module list of + * imports. If the Prefixed module is present in Schema Context the QName + * can be constructed.
+ * If the Prefixed Path Part does not contains prefix the Parent's Module + * namespace is taken for construction of QName.
+ * If Schema Context, Parent Module or Prefixed Path Part refers to + * null the method will throw IllegalArgumentException * * @throws IllegalArgumentException * - * @param context Schema Context - * @param parentModule Parent Module - * @param prefixedPathPart Prefixed Path Part string + * @param context + * Schema Context + * @param parentModule + * Parent Module + * @param prefixedPathPart + * Prefixed Path Part string * @return QName from prefixed Path Part String. */ - private static QName stringPathPartToQName(final SchemaContext context, final Module parentModule, - final String prefixedPathPart) { - if (context == null) { - throw new IllegalArgumentException("Schema Context reference cannot be NULL!"); - } - if (parentModule == null) { - throw new IllegalArgumentException("Parent Module reference cannot be NULL!"); - } - if (prefixedPathPart == null) { - throw new IllegalArgumentException("Prefixed Path Part cannot be NULL!"); - } + 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(":")) { - final String[] prefixedName = prefixedPathPart.split(":"); - final Module module = resolveModuleForPrefix(context, parentModule, prefixedName[0]); - if (module != null) { - 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()); + + // 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); } - return null; } /** - * Method will attempt to resolve and provide Module reference for specified module prefix. Each Yang module - * could contains multiple imports which MUST be associated with corresponding module prefix. The method simply - * looks into module imports and returns the module that is bounded with specified prefix. If the prefix is not - * present in module or the prefixed module is not present in specified Schema Context, - * the method will return null. - *
- * If String prefix is the same as prefix of the specified Module the reference to this module is returned. - *
- * If Schema Context, Module or Prefix are referring to null the method will return - * IllegalArgumentException + * Method will attempt to resolve and provide Module reference for specified + * module prefix. Each Yang module could contains multiple imports which + * MUST be associated with corresponding module prefix. The method simply + * looks into module imports and returns the module that is bounded with + * specified prefix. If the prefix is not present in module or the prefixed + * module is not present in specified Schema Context, the method will return + * null.
+ * If String prefix is the same as prefix of the specified Module the + * reference to this module is returned.
+ * If Schema Context, Module or Prefix are referring to null + * the method will return IllegalArgumentException * * @throws IllegalArgumentException * - * @param context Schema Context - * @param module Yang Module - * @param prefix Module Prefix - * @return Module for given prefix in specified Schema Context if is present, otherwise returns null + * @param context + * Schema Context + * @param module + * Yang Module + * @param prefix + * Module Prefix + * @return Module for given prefix in specified Schema Context if is + * present, otherwise returns null */ private static Module resolveModuleForPrefix(final SchemaContext context, final Module module, final String prefix) { - if (context == null) { - throw new IllegalArgumentException("Schema Context reference cannot be NULL!"); - } - if (module == null) { - throw new IllegalArgumentException("Module reference cannot be NULL!"); - } - if (prefix == null) { - throw new IllegalArgumentException("Prefix string cannot be NULL!"); - } + 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"); if (prefix.equals(module.getPrefix())) { return module; } - final Set imports = module.getImports(); - for (final ModuleImport mi : imports) { + Set imports = module.getImports(); + for (ModuleImport mi : imports) { if (prefix.equals(mi.getPrefix())) { return context.findModuleByName(mi.getModuleName(), mi.getRevision()); } @@ -496,52 +837,46 @@ public final class SchemaContextUtil { /** * @throws IllegalArgumentException * - * @param context Schema Context - * @param module Yang Module - * @param relativeXPath Non conditional Revision Aware Relative XPath - * @param leafrefSchemaPath Schema Path for Leafref - * @return + * @param context + * Schema Context + * @param module + * Yang Module + * @param relativeXPath + * Non conditional Revision Aware Relative XPath + * @param leafrefSchemaPath + * Schema Path for Leafref + * @return list of QName */ - private static Queue resolveRelativeXPath(final SchemaContext context, final Module module, - final RevisionAwareXPath relativeXPath, final SchemaPath leafrefSchemaPath) { - final Queue absolutePath = new LinkedList<>(); - if (context == null) { - throw new IllegalArgumentException("Schema Context reference cannot be NULL!"); - } - if (module == null) { - throw new IllegalArgumentException("Module reference cannot be NULL!"); - } - if (relativeXPath == null) { - throw new IllegalArgumentException("Non Conditional Revision Aware XPath cannot be NULL!"); - } - if (relativeXPath.isAbsolute()) { - throw new IllegalArgumentException("Revision Aware XPath MUST be relative i.e. MUST contains ../, " - + "for non relative Revision Aware XPath use findDataSchemaNode method!"); - } - if (leafrefSchemaPath == null) { - throw new IllegalArgumentException("Schema Path reference for Leafref cannot be NULL!"); - } - - final String strXPath = relativeXPath.toString(); - if (strXPath != null) { - final String[] xpaths = strXPath.split("/"); - if (xpaths != null) { - int colCount = 0; - while (xpaths[colCount].contains("..")) { - ++colCount; - } - final List path = leafrefSchemaPath.getPath(); - if (path != null) { - int lenght = path.size() - colCount - 1; - for (int i = 0; i < lenght; ++i) { - absolutePath.add(path.get(i)); - } - for (int i = colCount; i < xpaths.length; ++i) { - absolutePath.add(stringPathPartToQName(context, module, xpaths[i])); - } - } + private static List 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"); + 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, + "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? + int colCount = 0; + for (Iterator it = xpaths.iterator(); it.hasNext() && it.next().contains(".."); ) { + ++colCount; + } + + final List absolutePath = new LinkedList(); + List path = leafrefParentNode.getPath().getPath(); + if (path != null) { + int lenght = path.size() - colCount; + absolutePath.addAll(path.subList(0, lenght)); + for (String s : Iterables.skip(xpaths, colCount)) { + absolutePath.add(stringPathPartToQName(context, module, s)); } } + return absolutePath; } }