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