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