+
+ /**
+ * Returns a {@code data node} identified by a QName. This method is distinct from
+ * {@link #findDataChildByName(QName)} in that it skips over {@link ChoiceSchemaNode}s and {@link CaseSchemaNode}s,
+ * hence mirroring layout of the {@code data tree}, not {@code schema tree}.
+ *
+ * @param name QName identifier of the data node
+ * @return Direct or indirect child of this DataNodeContainer which is a {@code data node}, empty otherwise
+ * @throws NullPointerException if {@code name} is null
+ */
+ @Beta
+ default Optional<DataSchemaNode> findDataTreeChild(final QName name) {
+ // First we try to find a direct child and check if it is a data node (as per RFC7950)
+ final Optional<DataSchemaNode> optDataChild = findDataChildByName(name);
+ if (HelperMethods.isDataNode(optDataChild)) {
+ return optDataChild;
+ }
+
+ // There either is no such node present, or there are Choice/CaseSchemaNodes with the same name involved,
+ // hence we have to resort to a full search.
+ for (DataSchemaNode child : getChildNodes()) {
+ if (child instanceof ChoiceSchemaNode) {
+ for (CaseSchemaNode choiceCase : ((ChoiceSchemaNode) child).getCases().values()) {
+ final Optional<DataSchemaNode> caseChild = choiceCase.findDataTreeChild(name);
+ if (caseChild.isPresent()) {
+ return caseChild;
+ }
+ }
+ }
+ }
+
+ return Optional.empty();
+ }
+
+ /**
+ * Returns a {@code data node} identified by a series of QNames. This is equivalent to incrementally calling
+ * {@link #findDataTreeChild(QName)}.
+ *
+ * @param path Series of QNames towards identifying the requested data node
+ * @return Direct or indirect child of this DataNodeContainer which is a {@code data node}, empty otherwise
+ * @throws IllegalArgumentException if {@code path} is determined to go beyond a not-container-nor-list node.
+ * @throws NoSuchElementException if {@code path} is empty
+ * @throws NullPointerException if {@code path} is null or contains a null
+ */
+ @Beta
+ default Optional<DataSchemaNode> findDataTreeChild(final QName... path) {
+ return findDataTreeChild(Arrays.asList(path));
+ }
+
+ /**
+ * Returns a {@code data node} identified by a series of QNames. This is equivalent to incrementally calling
+ * {@link #findDataTreeChild(QName)}.
+ *
+ * @param path Series of QNames towards identifying the requested data node
+ * @return Direct or indirect child of this DataNodeContainer which is a {@code data node}, empty otherwise
+ * @throws IllegalArgumentException if {@code path} is determined to go beyond a not-container-nor-list node.
+ * @throws NoSuchElementException if {@code path} is empty
+ * @throws NullPointerException if {@code path} is null or contains a null
+ */
+ @Beta
+ default Optional<DataSchemaNode> findDataTreeChild(final Iterable<QName> path) {
+ final Iterator<QName> it = path.iterator();
+ DataNodeContainer parent = this;
+ do {
+ final Optional<DataSchemaNode> optChild = parent.findDataTreeChild(requireNonNull(it.next()));
+ if (!optChild.isPresent() || !it.hasNext()) {
+ return optChild;
+ }
+
+ final DataSchemaNode child = optChild.get();
+ checkArgument(child instanceof DataNodeContainer, "Path %s extends beyond terminal child %s", path, child);
+ parent = (DataNodeContainer) child;
+ } while (true);
+ }