658d40829328d9cd2c892e1e47ff2c7e61ebbffd
[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      * @deprecated Use {@link #findDataChildByName(QName)} instead.
64      * @throws NullPointerException if {@code name} is null
65      */
66     @Deprecated
67     default @Nullable DataSchemaNode getDataChildByName(final QName name) {
68         return findDataChildByName(name).orElse(null);
69     }
70
71     /**
72      * Returns the child node corresponding to the specified name.
73      *
74      * <p>
75      * Note that the nodes searched are <strong>NOT</strong> {@code data nodes}, but rather {@link DataSchemaNode}s,
76      * hence {@link ChoiceSchemaNode} and {@link CaseSchemaNode} are returned instead of their matching children.
77      *
78      * @param name QName of child
79      * @return child node of this DataNodeContainer if child with given name is present, empty otherwise
80      * @throws NullPointerException if {@code name} is null
81      */
82     Optional<DataSchemaNode> findDataChildByName(QName name);
83
84     /**
85      * Returns the child node corresponding to the specified name.
86      *
87      * <p>
88      * Note that the nodes searched are <strong>NOT</strong> {@code data nodes}, but rather {@link DataSchemaNode}s,
89      * hence {@link ChoiceSchemaNode} and {@link CaseSchemaNode} are returned instead of their matching children.
90      *
91      * @param first QName of first child
92      * @param others QNames of subsequent children
93      * @return child node of this DataNodeContainer if child with given name is present, empty otherwise
94      * @throws NullPointerException if any argument is null
95      */
96     default Optional<DataSchemaNode> findDataChildByName(final QName first, final QName... others) {
97         Optional<DataSchemaNode> optCurrent = findDataChildByName(first);
98         for (QName qname : others) {
99             if (optCurrent.isPresent()) {
100                 final DataSchemaNode current = optCurrent.get();
101                 if (current instanceof DataNodeContainer) {
102                     optCurrent = ((DataNodeContainer) current).findDataChildByName(qname);
103                     continue;
104                 }
105             }
106
107             return Optional.empty();
108         }
109         return optCurrent;
110     }
111
112     /**
113      * Returns grouping nodes used ny this container.
114      *
115      * @return Set of all uses nodes defined within this DataNodeContainer
116      */
117     Collection<? extends UsesNode> getUses();
118
119     /**
120      * Returns a {@code data node} identified by a QName. This method is distinct from
121      * {@link #findDataChildByName(QName)} in that it skips over {@link ChoiceSchemaNode}s and {@link CaseSchemaNode}s,
122      * hence mirroring layout of the {@code data tree}, not {@code schema tree}.
123      *
124      * @param name QName identifier of the data node
125      * @return Direct or indirect child of this DataNodeContainer which is a {@code data node}, empty otherwise
126      * @throws NullPointerException if {@code name} is null
127      */
128     @Beta
129     default Optional<DataSchemaNode> findDataTreeChild(final QName name) {
130         // First we try to find a direct child and check if it is a data node (as per RFC7950)
131         final Optional<DataSchemaNode> optDataChild = findDataChildByName(name);
132         if (HelperMethods.isDataNode(optDataChild)) {
133             return optDataChild;
134         }
135
136         // There either is no such node present, or there are Choice/CaseSchemaNodes with the same name involved,
137         // hence we have to resort to a full search.
138         for (DataSchemaNode child : getChildNodes()) {
139             if (child instanceof ChoiceSchemaNode) {
140                 for (CaseSchemaNode choiceCase : ((ChoiceSchemaNode) child).getCases()) {
141                     final Optional<DataSchemaNode> caseChild = choiceCase.findDataTreeChild(name);
142                     if (caseChild.isPresent()) {
143                         return caseChild;
144                     }
145                 }
146             }
147         }
148
149         return Optional.empty();
150     }
151
152     /**
153      * Returns a {@code data node} identified by a series of QNames. This is equivalent to incrementally calling
154      * {@link #findDataTreeChild(QName)}.
155      *
156      * @param path Series of QNames towards identifying the requested data node
157      * @return Direct or indirect child of this DataNodeContainer which is a {@code data node}, empty otherwise
158      * @throws IllegalArgumentException if {@code path} is determined to go beyond a not-container-nor-list node.
159      * @throws NoSuchElementException if {@code path} is empty
160      * @throws NullPointerException if {@code path} is null or contains a null
161      */
162     @Beta
163     default Optional<DataSchemaNode> findDataTreeChild(final QName... path) {
164         return findDataTreeChild(Arrays.asList(path));
165     }
166
167     /**
168      * Returns a {@code data node} identified by a series of QNames. This is equivalent to incrementally calling
169      * {@link #findDataTreeChild(QName)}.
170      *
171      * @param path Series of QNames towards identifying the requested data node
172      * @return Direct or indirect child of this DataNodeContainer which is a {@code data node}, empty otherwise
173      * @throws IllegalArgumentException if {@code path} is determined to go beyond a not-container-nor-list node.
174      * @throws NoSuchElementException if {@code path} is empty
175      * @throws NullPointerException if {@code path} is null or contains a null
176      */
177     @Beta
178     default Optional<DataSchemaNode> findDataTreeChild(final Iterable<QName> path) {
179         final Iterator<QName> it = path.iterator();
180         DataNodeContainer parent = this;
181         do {
182             final Optional<DataSchemaNode> optChild = parent.findDataTreeChild(requireNonNull(it.next()));
183             if (optChild.isEmpty() || !it.hasNext()) {
184                 return optChild;
185             }
186
187             final DataSchemaNode child = optChild.get();
188             checkArgument(child instanceof DataNodeContainer, "Path %s extends beyond terminal child %s", path, child);
189             parent = (DataNodeContainer) child;
190         } while (true);
191     }
192 }