Add DataNodeContainer.dataChildByName()
[yangtools.git] / yang / yang-model-api / src / main / java / org / opendaylight / yangtools / yang / model / api / DataNodeContainer.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.model.api;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.annotations.Beta;
14 import java.util.Arrays;
15 import java.util.Collection;
16 import java.util.Iterator;
17 import java.util.NoSuchElementException;
18 import java.util.Optional;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.opendaylight.yangtools.yang.common.QName;
21
22 /**
23  * Node which can contains other nodes.
24  */
25 public interface DataNodeContainer {
26     /**
27      * Returns set of all newly defined types within this DataNodeContainer.
28      *
29      * @return typedef statements in lexicographical order
30      */
31     Collection<? extends TypeDefinition<?>> getTypeDefinitions();
32
33     /**
34      * Returns set of all child nodes defined within this DataNodeContainer. Although the return type is a collection,
35      * each node is guaranteed to be present at most once.
36      *
37      * <p>
38      * Note that the nodes returned are <strong>NOT</strong> {@code data nodes}, but rather {@link DataSchemaNode}s,
39      * hence {@link ChoiceSchemaNode} and {@link CaseSchemaNode} are present instead of their children. This
40      * is consistent with {@code schema tree}.
41      *
42      * @return child nodes in lexicographical order
43      */
44     Collection<? extends DataSchemaNode> getChildNodes();
45
46     /**
47      * Returns set of all groupings defined within this DataNodeContainer.
48      *
49      * @return grouping statements in lexicographical order
50      */
51     Collection<? extends GroupingDefinition> getGroupings();
52
53     /**
54      * Returns the child node corresponding to the specified name.
55      *
56      * <p>
57      * Note that the nodes searched are <strong>NOT</strong> {@code data nodes}, but rather {@link DataSchemaNode}s,
58      * hence {@link ChoiceSchemaNode} and {@link CaseSchemaNode} are returned instead of their matching children. This
59      * is consistent with {@code schema tree}.
60      *
61      * @param name QName of child
62      * @return child node of this DataNodeContainer if child with given name is present, null otherwise
63      * @throws NullPointerException if {@code name} is null
64      */
65     default @Nullable DataSchemaNode dataChildByName(final QName name) {
66         return findDataChildByName(name).orElse(null);
67     }
68
69     /**
70      * Returns the child node corresponding to the specified name.
71      *
72      * <p>
73      * Note that the nodes searched are <strong>NOT</strong> {@code data nodes}, but rather {@link DataSchemaNode}s,
74      * hence {@link ChoiceSchemaNode} and {@link CaseSchemaNode} are returned instead of their matching children. This
75      * is consistent with {@code schema tree}.
76      *
77      * @param name QName of child
78      * @return child node of this DataNodeContainer if child with given name is present, null otherwise
79      * @deprecated Use {@link #dataChildByName(QName)} or {@link #findDataChildByName(QName)} instead. This method will
80      *             be repurposed to assert existence in the next major release.
81      * @throws NullPointerException if {@code name} is null
82      */
83     @Deprecated(forRemoval = true)
84     default @Nullable DataSchemaNode getDataChildByName(final QName name) {
85         return dataChildByName(name);
86     }
87
88     /**
89      * Returns the child node corresponding to the specified name.
90      *
91      * <p>
92      * Note that the nodes searched are <strong>NOT</strong> {@code data nodes}, but rather {@link DataSchemaNode}s,
93      * hence {@link ChoiceSchemaNode} and {@link CaseSchemaNode} are returned instead of their matching children.
94      *
95      * @param name QName of child
96      * @return child node of this DataNodeContainer if child with given name is present, empty otherwise
97      * @throws NullPointerException if {@code name} is null
98      */
99     Optional<DataSchemaNode> findDataChildByName(QName name);
100
101     /**
102      * Returns the child node corresponding to the specified name.
103      *
104      * <p>
105      * Note that the nodes searched are <strong>NOT</strong> {@code data nodes}, but rather {@link DataSchemaNode}s,
106      * hence {@link ChoiceSchemaNode} and {@link CaseSchemaNode} are returned instead of their matching children.
107      *
108      * @param first QName of first child
109      * @param others QNames of subsequent children
110      * @return child node of this DataNodeContainer if child with given name is present, empty otherwise
111      * @throws NullPointerException if any argument is null
112      */
113     default Optional<DataSchemaNode> findDataChildByName(final QName first, final QName... others) {
114         Optional<DataSchemaNode> optCurrent = findDataChildByName(first);
115         for (QName qname : others) {
116             if (optCurrent.isPresent()) {
117                 final DataSchemaNode current = optCurrent.get();
118                 if (current instanceof DataNodeContainer) {
119                     optCurrent = ((DataNodeContainer) current).findDataChildByName(qname);
120                     continue;
121                 }
122             }
123
124             return Optional.empty();
125         }
126         return optCurrent;
127     }
128
129     /**
130      * Returns grouping nodes used ny this container.
131      *
132      * @return Set of all uses nodes defined within this DataNodeContainer
133      */
134     Collection<? extends UsesNode> getUses();
135
136     /**
137      * Returns a {@code data node} identified by a QName. This method is distinct from
138      * {@link #findDataChildByName(QName)} in that it skips over {@link ChoiceSchemaNode}s and {@link CaseSchemaNode}s,
139      * hence mirroring layout of the {@code data tree}, not {@code schema tree}.
140      *
141      * @param name QName identifier of the data node
142      * @return Direct or indirect child of this DataNodeContainer which is a {@code data node}, empty otherwise
143      * @throws NullPointerException if {@code name} is null
144      */
145     @Beta
146     default Optional<DataSchemaNode> findDataTreeChild(final QName name) {
147         // First we try to find a direct child and check if it is a data node (as per RFC7950)
148         final Optional<DataSchemaNode> optDataChild = findDataChildByName(name);
149         if (HelperMethods.isDataNode(optDataChild)) {
150             return optDataChild;
151         }
152
153         // There either is no such node present, or there are Choice/CaseSchemaNodes with the same name involved,
154         // hence we have to resort to a full search.
155         for (DataSchemaNode child : getChildNodes()) {
156             if (child instanceof ChoiceSchemaNode) {
157                 for (CaseSchemaNode choiceCase : ((ChoiceSchemaNode) child).getCases()) {
158                     final Optional<DataSchemaNode> caseChild = choiceCase.findDataTreeChild(name);
159                     if (caseChild.isPresent()) {
160                         return caseChild;
161                     }
162                 }
163             }
164         }
165
166         return Optional.empty();
167     }
168
169     /**
170      * Returns a {@code data node} identified by a series of QNames. This is equivalent to incrementally calling
171      * {@link #findDataTreeChild(QName)}.
172      *
173      * @param path Series of QNames towards identifying the requested data node
174      * @return Direct or indirect child of this DataNodeContainer which is a {@code data node}, empty otherwise
175      * @throws IllegalArgumentException if {@code path} is determined to go beyond a not-container-nor-list node.
176      * @throws NoSuchElementException if {@code path} is empty
177      * @throws NullPointerException if {@code path} is null or contains a null
178      */
179     @Beta
180     default Optional<DataSchemaNode> findDataTreeChild(final QName... path) {
181         return findDataTreeChild(Arrays.asList(path));
182     }
183
184     /**
185      * Returns a {@code data node} identified by a series of QNames. This is equivalent to incrementally calling
186      * {@link #findDataTreeChild(QName)}.
187      *
188      * @param path Series of QNames towards identifying the requested data node
189      * @return Direct or indirect child of this DataNodeContainer which is a {@code data node}, empty otherwise
190      * @throws IllegalArgumentException if {@code path} is determined to go beyond a not-container-nor-list node.
191      * @throws NoSuchElementException if {@code path} is empty
192      * @throws NullPointerException if {@code path} is null or contains a null
193      */
194     @Beta
195     default Optional<DataSchemaNode> findDataTreeChild(final Iterable<QName> path) {
196         final Iterator<QName> it = path.iterator();
197         DataNodeContainer parent = this;
198         do {
199             final Optional<DataSchemaNode> optChild = parent.findDataTreeChild(requireNonNull(it.next()));
200             if (optChild.isEmpty() || !it.hasNext()) {
201                 return optChild;
202             }
203
204             final DataSchemaNode child = optChild.get();
205             checkArgument(child instanceof DataNodeContainer, "Path %s extends beyond terminal child %s", path, child);
206             parent = (DataNodeContainer) child;
207         } while (true);
208     }
209 }