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