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