Add a proper grouping statement policy
[yangtools.git] / yang / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / stmt / AbstractDeclaredEffectiveStatement.java
1 /*
2  * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.parser.rfc7950.stmt;
9
10 import static com.google.common.base.Verify.verify;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.annotations.Beta;
14 import com.google.common.collect.ImmutableList;
15 import com.google.common.collect.ImmutableMap;
16 import java.util.HashMap;
17 import java.util.Map;
18 import java.util.Optional;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.opendaylight.yangtools.yang.common.QName;
22 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
23 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
25 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
26 import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
27 import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
28 import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeAwareEffectiveStatement;
29 import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeEffectiveStatement;
30 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeAwareEffectiveStatement;
31 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
32 import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.EffectiveStatementMixins.DataNodeContainerMixin;
33
34 /**
35  * Base stateless superclass for statements which (logically) always have an associated {@link DeclaredStatement}. This
36  * is notably not true for all {@code case} statements, some of which may actually be implied.
37  *
38  * <p>
39  * Note implementations are not strictly required to make the declared statement available, they are free to throw
40  * {@link UnsupportedOperationException} from {@link #getDeclared()}, rendering any services relying on declared
41  * statement to be not available.
42  *
43  * @param <A> Argument type ({@link Void} if statement does not have argument.)
44  * @param <D> Class representing declared version of this statement.
45  */
46 @Beta
47 public abstract class AbstractDeclaredEffectiveStatement<A, D extends DeclaredStatement<A>>
48         extends AbstractEffectiveStatement<A, D> {
49     @Override
50     public final StatementSource getStatementSource() {
51         return StatementSource.DECLARATION;
52     }
53
54     @Override
55     public abstract @NonNull D getDeclared();
56
57     /**
58      * Base stateless superclass form {@link SchemaTreeAwareEffectiveStatement}s. It maintains the contents of schema
59      * tree namespace based of effective substatements.
60      *
61      * @param <A> Argument type ({@link Void} if statement does not have argument.)
62      * @param <D> Class representing declared version of this statement.
63      * @param <E> Class representing effective version of this statement.
64      */
65     public abstract static class WithSchemaTree<A, D extends DeclaredStatement<A>,
66             E extends SchemaTreeAwareEffectiveStatement<A, D>> extends AbstractDeclaredEffectiveStatement<A, D> {
67         @Override
68         @SuppressWarnings("unchecked")
69         protected <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends Map<K, V>> getNamespaceContents(
70                 final Class<N> namespace) {
71             if (SchemaTreeAwareEffectiveStatement.Namespace.class.equals(namespace)) {
72                 return Optional.of((Map<K, V>) schemaTreeNamespace());
73             }
74             return super.getNamespaceContents(namespace);
75         }
76
77         /**
78          * Indexing support for {@link DataNodeContainer#dataChildByName(QName)}.
79          */
80         protected final @Nullable DataSchemaNode dataSchemaNode(final QName name) {
81             // Only DataNodeContainer subclasses should be calling this method
82             verify(this instanceof DataNodeContainer);
83             final SchemaTreeEffectiveStatement<?> child = schemaTreeNamespace().get(requireNonNull(name));
84             return child instanceof DataSchemaNode ? (DataSchemaNode) child : null;
85         }
86
87         protected abstract Map<QName, SchemaTreeEffectiveStatement<?>> schemaTreeNamespace();
88     }
89
90     /**
91      * Base stateless superclass form {@link DataTreeAwareEffectiveStatement}s. It maintains the contents of data tree
92      * namespace based of effective substatements.
93      *
94      * @param <A> Argument type ({@link Void} if statement does not have argument.)
95      * @param <D> Class representing declared version of this statement.
96      * @param <E> Class representing effective version of this statement.
97      */
98     public abstract static class WithDataTree<A, D extends DeclaredStatement<A>,
99             E extends DataTreeAwareEffectiveStatement<A, D>> extends WithSchemaTree<A, D, E> {
100         @Override
101         @SuppressWarnings("unchecked")
102         protected <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends Map<K, V>> getNamespaceContents(
103                 final Class<N> namespace) {
104             if (DataTreeAwareEffectiveStatement.Namespace.class.equals(namespace)) {
105                 return Optional.of((Map<K, V>) dataTreeNamespace());
106             }
107             return super.getNamespaceContents(namespace);
108         }
109
110         protected abstract Map<QName, DataTreeEffectiveStatement<?>> dataTreeNamespace();
111     }
112
113     /**
114      * A stateful version of {@link AbstractDeclaredEffectiveStatement}, which holds (and requires) a declared
115      * statement.
116      *
117      * @param <A> Argument type ({@link Void} if statement does not have argument.)
118      * @param <D> Class representing declared version of this statement.
119      */
120     public abstract static class Default<A, D extends DeclaredStatement<A>>
121             extends AbstractDeclaredEffectiveStatement<A, D> {
122         private final @NonNull D declared;
123
124         protected Default(final D declared) {
125             this.declared = requireNonNull(declared);
126         }
127
128         @Override
129         public final D getDeclared() {
130             return declared;
131         }
132     }
133
134     /**
135      * Utility class for implementing DataNodeContainer-type statements.
136      */
137     public abstract static class DefaultDataNodeContainer<A, D extends DeclaredStatement<A>> extends Default<A, D>
138             implements DataNodeContainerMixin<A, D> {
139         private final @NonNull ImmutableMap<QName, DataSchemaNode> dataChildren;
140         private final @NonNull Object substatements;
141
142         protected DefaultDataNodeContainer(final D declared,
143                 final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
144             super(declared);
145             this.substatements = maskList(substatements);
146
147             // Note: we do not leak this map, so iteration order does not matter
148             final Map<QName, DataSchemaNode> tmp = new HashMap<>();
149
150             for (EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
151                 if (stmt instanceof DataSchemaNode) {
152                     final DataSchemaNode node = (DataSchemaNode) stmt;
153                     final QName id = node.getQName();
154                     final DataSchemaNode prev = tmp.put(id, node);
155                     if (prev != null) {
156                         throw new SubstatementIndexingException(
157                             "Cannot add child with name " + id + ", a conflicting child already exists");
158                     }
159                 }
160             }
161
162             dataChildren = ImmutableMap.copyOf(tmp);
163         }
164
165         @Override
166         public final ImmutableList<? extends EffectiveStatement<?, ?>> effectiveSubstatements() {
167             return unmaskList(substatements);
168         }
169
170         @Override
171         public final DataSchemaNode dataChildByName(final QName name) {
172             return dataChildren.get(requireNonNull(name));
173         }
174     }
175
176     /**
177      * An extra building block on top of {@link Default}, which is wiring {@link #argument()} to the declared statement.
178      * This is mostly useful for arguments that are not subject to inference transformation -- for example Strings in
179      * {@code description}, etc. This explicitly is not true of statements which underwent namespace binding via
180      * {@code uses} or similar.
181      *
182      * @param <A> Argument type ({@link Void} if statement does not have argument.)
183      * @param <D> Class representing declared version of this statement.
184      */
185     public abstract static class DefaultArgument<A, D extends DeclaredStatement<A>> extends Default<A, D> {
186         public abstract static class WithSubstatements<A, D extends DeclaredStatement<A>>
187                 extends DefaultArgument<A, D> {
188             private final @NonNull Object substatements;
189
190             protected WithSubstatements(final D declared,
191                     final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
192                 super(declared);
193                 this.substatements = maskList(substatements);
194             }
195
196             @Override
197             public final ImmutableList<? extends EffectiveStatement<?, ?>> effectiveSubstatements() {
198                 return unmaskList(substatements);
199             }
200         }
201
202         protected DefaultArgument(final D declared) {
203             super(declared);
204         }
205
206         @Override
207         public final A argument() {
208             return getDeclared().argument();
209         }
210     }
211
212     /**
213      * A building block on top of {@link Default}, which adds an explicit argument value, which is not related to the
214      * context. This is mostly useful when the effective argument value reflects additional statements and similar.
215      *
216      * @param <A> Argument type ({@link Void} if statement does not have argument.)
217      * @param <D> Class representing declared version of this statement.
218      */
219     public abstract static class DefaultWithArgument<A, D extends DeclaredStatement<A>> extends Default<A, D> {
220         public abstract static class WithSubstatements<A, D extends DeclaredStatement<A>>
221                 extends DefaultWithArgument<A, D> {
222             private final @NonNull Object substatements;
223
224             protected WithSubstatements(final D declared, final A argument,
225                     final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
226                 super(declared, argument);
227                 this.substatements = maskList(substatements);
228             }
229
230             @Override
231             public final ImmutableList<? extends EffectiveStatement<?, ?>> effectiveSubstatements() {
232                 return unmaskList(substatements);
233             }
234         }
235
236         private final A argument;
237
238         protected DefaultWithArgument(final D declared, final A argument) {
239             super(declared);
240             this.argument = argument;
241         }
242
243         @Override
244         public final A argument() {
245             return argument;
246         }
247     }
248
249     /**
250      * Stateful version of {@link WithSchemaTree}. Schema tree namespace is eagerly instantiated (and checked).
251      *
252      * @param <A> Argument type ({@link Void} if statement does not have argument.)
253      * @param <D> Class representing declared version of this statement.
254      * @param <E> Class representing effective version of this statement.
255      */
256     public abstract static class DefaultWithSchemaTree<A, D extends DeclaredStatement<A>,
257             E extends SchemaTreeAwareEffectiveStatement<A, D>> extends WithSchemaTree<A, D, E> {
258         public abstract static class WithSubstatements<A, D extends DeclaredStatement<A>,
259                 E extends SchemaTreeAwareEffectiveStatement<A, D>> extends DefaultWithSchemaTree<A, D, E> {
260             private final @NonNull Object substatements;
261
262             protected WithSubstatements(final D declared,
263                     final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
264                 super(declared, substatements);
265                 this.substatements = maskList(substatements);
266             }
267
268             @Override
269             public final ImmutableList<? extends EffectiveStatement<?, ?>> effectiveSubstatements() {
270                 return unmaskList(substatements);
271             }
272         }
273
274         private final @NonNull ImmutableMap<QName, SchemaTreeEffectiveStatement<?>> schemaTree;
275         private final @NonNull D declared;
276
277         protected DefaultWithSchemaTree(final D declared,
278                 final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
279             this.declared = requireNonNull(declared);
280             this.schemaTree = ImmutableMap.copyOf(createSchemaTreeNamespace(substatements));
281         }
282
283         @Override
284         public final D getDeclared() {
285             return declared;
286         }
287
288         @Override
289         protected final Map<QName, SchemaTreeEffectiveStatement<?>> schemaTreeNamespace() {
290             return schemaTree;
291         }
292     }
293
294     /**
295      * Stateful version of {@link WithDataTree}. Schema tree and data tree namespaces are eagerly instantiated
296      * (and checked).
297      *
298      * @param <A> Argument type ({@link Void} if statement does not have argument.)
299      * @param <D> Class representing declared version of this statement.
300      * @param <E> Class representing effective version of this statement.
301      */
302     public abstract static class DefaultWithDataTree<A, D extends DeclaredStatement<A>,
303             E extends DataTreeAwareEffectiveStatement<A, D>> extends WithDataTree<A, D, E> {
304         public abstract static class WithSubstatements<A, D extends DeclaredStatement<A>,
305                 E extends DataTreeAwareEffectiveStatement<A, D>> extends DefaultWithDataTree<A, D, E> {
306             private final @NonNull Object substatements;
307
308             protected WithSubstatements(final D declared,
309                     final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
310                 super(declared, substatements);
311                 this.substatements = maskList(substatements);
312             }
313
314             @Override
315             public final ImmutableList<? extends EffectiveStatement<?, ?>> effectiveSubstatements() {
316                 return unmaskList(substatements);
317             }
318         }
319
320         private final @NonNull ImmutableMap<QName, SchemaTreeEffectiveStatement<?>> schemaTree;
321         private final @NonNull ImmutableMap<QName, DataTreeEffectiveStatement<?>> dataTree;
322         private final @NonNull D declared;
323
324         protected DefaultWithDataTree(final D declared,
325                 final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
326             this.declared = requireNonNull(declared);
327             final Map<QName, SchemaTreeEffectiveStatement<?>> schema = createSchemaTreeNamespace(substatements);
328             this.schemaTree = ImmutableMap.copyOf(schema);
329             this.dataTree = createDataTreeNamespace(schema.values(), schemaTree);
330         }
331
332         @Override
333         public final D getDeclared() {
334             return declared;
335         }
336
337         @Override
338         protected final Map<QName, SchemaTreeEffectiveStatement<?>> schemaTreeNamespace() {
339             return schemaTree;
340         }
341
342         @Override
343         protected final Map<QName, DataTreeEffectiveStatement<?>> dataTreeNamespace() {
344             return dataTree;
345         }
346     }
347 }