+ /**
+ * Find child data schema node identified by its QName within a provided schema node. This method performs lookup
+ * in the namespace of all leafs, leaf-lists, lists, containers, choices, rpcs, actions, notifications, anydatas
+ * and anyxmls according to RFC6050/RFC7950 section 6.2.1.
+ *
+ * @param node
+ * schema node
+ * @param qname
+ * QName
+ * @return data child schema node
+ * @throws IllegalArgumentException
+ * if the schema node does not allow children
+ */
+ public static @Nullable SchemaNode findDataChildSchemaByQName(final SchemaNode node, final QName qname) {
+ if (node instanceof DataNodeContainer) {
+ SchemaNode child = ((DataNodeContainer) node).dataChildByName(qname);
+ if (child == null && node instanceof SchemaContext) {
+ child = tryFind(((SchemaContext) node).getOperations(), qname).orElse(null);
+ }
+ if (child == null && node instanceof NotificationNodeContainer) {
+ child = tryFind(((NotificationNodeContainer) node).getNotifications(), qname).orElse(null);
+ }
+ if (child == null && node instanceof ActionNodeContainer) {
+ child = tryFind(((ActionNodeContainer) node).getActions(), qname).orElse(null);
+ }
+
+ return child;
+ }
+ if (node instanceof ChoiceSchemaNode) {
+ return ((ChoiceSchemaNode) node).findCase(qname).orElse(null);
+ }
+ if (node instanceof OperationDefinition) {
+ switch (qname.getLocalName()) {
+ case "input":
+ return ((OperationDefinition) node).getInput();
+ case "output":
+ return ((OperationDefinition) node).getOutput();
+ default:
+ return null;
+ }
+ }
+
+ throw new IllegalArgumentException(String.format("Schema node %s does not allow children.", node));
+ }
+
+ /**
+ * Finds schema node for given path in schema context. This method performs lookup in both the namespace
+ * of groupings and the namespace of all leafs, leaf-lists, lists, containers, choices, rpcs, actions,
+ * notifications, anydatas and anyxmls according to Rfc6050/Rfc7950 section 6.2.1.
+ *
+ * <p>
+ * This method returns collection of SchemaNodes, because name conflicts can occur between the namespace
+ * of groupings and namespace of data nodes. This method finds and collects all schema nodes that matches supplied
+ * SchemaPath and returns them all as collection of schema nodes.
+ *
+ * @param schemaContext
+ * schema context
+ * @param path
+ * path
+ * @return collection of schema nodes on path
+ */
+ public static Collection<SchemaNode> findParentSchemaNodesOnPath(final SchemaContext schemaContext,
+ final SchemaPath path) {
+ return findParentSchemaNodesOnPath(schemaContext, path.getPathFromRoot());
+ }
+
+ /**
+ * Finds schema node for given path in schema context. This method performs lookup in both the namespace
+ * of groupings and the namespace of all leafs, leaf-lists, lists, containers, choices, rpcs, actions,
+ * notifications, anydatas and anyxmls according to Rfc6050/Rfc7950 section 6.2.1.
+ *
+ * <p>
+ * This method returns collection of SchemaNodes, because name conflicts can occur between the namespace
+ * of groupings and namespace of data nodes. This method finds and collects all schema nodes that matches supplied
+ * SchemaPath and returns them all as collection of schema nodes.
+ *
+ * @param schemaContext schema context
+ * @param path path
+ * @return collection of schema nodes on path
+ */
+ public static Collection<SchemaNode> findParentSchemaNodesOnPath(final SchemaContext schemaContext,
+ final Iterable<QName> path) {
+ final Collection<SchemaNode> currentNodes = new ArrayList<>();
+ final Collection<SchemaNode> childNodes = new ArrayList<>();
+ currentNodes.add(requireNonNull(schemaContext));
+ for (final QName qname : path) {
+ for (final SchemaNode current : currentNodes) {
+ childNodes.addAll(findChildSchemaNodesByQName(current, qname));
+ }
+ currentNodes.clear();
+ currentNodes.addAll(childNodes);
+ childNodes.clear();
+ }
+
+ return currentNodes;
+ }
+
+ /**
+ * Find child schema node identified by its QName within a provided schema node. This method performs lookup in both
+ * the namespace of groupings and the namespace of all leafs, leaf-lists, lists, containers, choices, rpcs,
+ * actions, notifications, anydatas and anyxmls according to RFC6050/RFC7950 section 6.2.1.
+ *
+ * <p>
+ * This method returns collection of SchemaNodes, because name conflicts can occur between the namespace
+ * of groupings and namespace of data nodes. This method finds and collects all schema nodes with supplied QName
+ * and returns them all as collection of schema nodes.
+ *
+ * @param node
+ * schema node
+ * @param qname
+ * QName
+ * @return collection of child schema nodes
+ * @throws IllegalArgumentException
+ * if the schema node does not allow children
+ */
+ public static Collection<SchemaNode> findChildSchemaNodesByQName(final SchemaNode node, final QName qname) {
+ final List<SchemaNode> childNodes = new ArrayList<>();
+ final SchemaNode dataNode = findDataChildSchemaByQName(node, qname);
+ if (dataNode != null) {
+ childNodes.add(dataNode);
+ }
+ if (node instanceof DataNodeContainer) {
+ tryFind(((DataNodeContainer) node).getGroupings(), qname).ifPresent(childNodes::add);
+ }
+ return childNodes.isEmpty() ? ImmutableList.of() : ImmutableList.copyOf(childNodes);
+ }
+
+ private static <T extends SchemaNode> Optional<T> tryFind(final Collection<T> nodes, final QName qname) {
+ return nodes.stream().filter(node -> qname.equals(node.getQName())).findFirst();
+ }