8f1a7446d7299fe5822f896e734089a78b809717
[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.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 import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.EffectiveStatementMixins.DataNodeContainerMixin;
32 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
33 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
34 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
35
36 /**
37  * Base stateless superclass for statements which (logically) always have an associated {@link DeclaredStatement}. This
38  * is notably not true for all {@code case} statements, some of which may actually be implied.
39  *
40  * <p>
41  * Note implementations are not strictly required to make the declared statement available, they are free to throw
42  * {@link UnsupportedOperationException} from {@link #getDeclared()}, rendering any services relying on declared
43  * statement to be not available.
44  *
45  * @param <A> Argument type ({@link Void} if statement does not have argument.)
46  * @param <D> Class representing declared version of this statement.
47  */
48 @Beta
49 public abstract class AbstractDeclaredEffectiveStatement<A, D extends DeclaredStatement<A>>
50         extends AbstractEffectiveStatement<A, D> {
51     @Override
52     public final StatementSource getStatementSource() {
53         return StatementSource.DECLARATION;
54     }
55
56     @Override
57     public abstract @NonNull D getDeclared();
58
59     /**
60      * Base stateless superclass form {@link SchemaTreeAwareEffectiveStatement}s. It maintains the contents of schema
61      * tree namespace based of effective substatements.
62      *
63      * @param <A> Argument type ({@link Void} if statement does not have argument.)
64      * @param <D> Class representing declared version of this statement.
65      * @param <E> Class representing effective version of this statement.
66      */
67     public abstract static class WithSchemaTree<A, D extends DeclaredStatement<A>,
68             E extends SchemaTreeAwareEffectiveStatement<A, D>> extends AbstractDeclaredEffectiveStatement<A, D> {
69         @Override
70         @SuppressWarnings("unchecked")
71         protected <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends Map<K, V>> getNamespaceContents(
72                 final Class<N> namespace) {
73             if (SchemaTreeAwareEffectiveStatement.Namespace.class.equals(namespace)) {
74                 return Optional.of((Map<K, V>) schemaTreeNamespace());
75             }
76             return super.getNamespaceContents(namespace);
77         }
78
79         /**
80          * Indexing support for {@link DataNodeContainer#findDataChildByName(QName)}.
81          */
82         protected final Optional<DataSchemaNode> findDataSchemaNode(final QName name) {
83             // Only DataNodeContainer subclasses should be calling this method
84             verify(this instanceof DataNodeContainer);
85             final SchemaTreeEffectiveStatement<?> child = schemaTreeNamespace().get(requireNonNull(name));
86             return child instanceof DataSchemaNode ? Optional.of((DataSchemaNode) child) : Optional.empty();
87         }
88
89         protected abstract Map<QName, SchemaTreeEffectiveStatement<?>> schemaTreeNamespace();
90     }
91
92     /**
93      * Base stateless superclass form {@link DataTreeAwareEffectiveStatement}s. It maintains the contents of data tree
94      * namespace based of effective substatements.
95      *
96      * @param <A> Argument type ({@link Void} if statement does not have argument.)
97      * @param <D> Class representing declared version of this statement.
98      * @param <E> Class representing effective version of this statement.
99      */
100     public abstract static class WithDataTree<A, D extends DeclaredStatement<A>,
101             E extends DataTreeAwareEffectiveStatement<A, D>> extends WithSchemaTree<A, D, E> {
102         @Override
103         @SuppressWarnings("unchecked")
104         protected <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends Map<K, V>> getNamespaceContents(
105                 final Class<N> namespace) {
106             if (DataTreeAwareEffectiveStatement.Namespace.class.equals(namespace)) {
107                 return Optional.of((Map<K, V>) dataTreeNamespace());
108             }
109             return super.getNamespaceContents(namespace);
110         }
111
112         protected abstract Map<QName, DataTreeEffectiveStatement<?>> dataTreeNamespace();
113     }
114
115     /**
116      * A stateful version of {@link AbstractDeclaredEffectiveStatement}, which holds (and requires) a declared
117      * statement.
118      *
119      * @param <A> Argument type ({@link Void} if statement does not have argument.)
120      * @param <D> Class representing declared version of this statement.
121      */
122     public abstract static class Default<A, D extends DeclaredStatement<A>>
123             extends AbstractDeclaredEffectiveStatement<A, D> {
124         private final @NonNull D declared;
125
126         protected Default(final D declared) {
127             this.declared = requireNonNull(declared);
128         }
129
130         @Override
131         public final D getDeclared() {
132             return declared;
133         }
134     }
135
136     /**
137      * Utility class for implementing DataNodeContainer-type statements.
138      */
139     public abstract static class DefaultDataNodeContainer<A, D extends DeclaredStatement<A>> extends Default<A, D>
140             implements DataNodeContainerMixin<A, D> {
141         private final @NonNull ImmutableMap<QName, DataSchemaNode> dataChildren;
142         private final @NonNull Object substatements;
143
144         protected DefaultDataNodeContainer(final D declared, final StatementSourceReference ref,
145                 final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
146             super(declared);
147             this.substatements = maskList(substatements);
148
149             // Note: we do not leak this map, so iteration order does not matter
150             final Map<QName, DataSchemaNode> tmp = new HashMap<>();
151
152             for (EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
153                 if (stmt instanceof DataSchemaNode) {
154                     final DataSchemaNode node = (DataSchemaNode) stmt;
155                     final QName id = node.getQName();
156                     final DataSchemaNode prev = tmp.put(id, node);
157                     SourceException.throwIf(prev != null, ref,
158                             "Cannot add child with name %s, a conflicting child already exists", id);
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 Optional<DataSchemaNode> findDataChildByName(final QName name) {
172             return Optional.ofNullable(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      * Stateful version of {@link WithSchemaTree}. Schema tree namespace is eagerly instantiated (and checked).
214      *
215      * @param <A> Argument type ({@link Void} if statement does not have argument.)
216      * @param <D> Class representing declared version of this statement.
217      * @param <E> Class representing effective version of this statement.
218      */
219     public abstract static class DefaultWithSchemaTree<A, D extends DeclaredStatement<A>,
220             E extends SchemaTreeAwareEffectiveStatement<A, D>> extends WithSchemaTree<A, D, E> {
221         public abstract static class WithSubstatements<A, D extends DeclaredStatement<A>,
222                 E extends SchemaTreeAwareEffectiveStatement<A, D>> extends DefaultWithSchemaTree<A, D, E> {
223             private final @NonNull Object substatements;
224
225             protected WithSubstatements(final D declared, final StmtContext<?, ?, ?> ctx,
226                     final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
227                 super(declared, ctx, substatements);
228                 this.substatements = maskList(substatements);
229             }
230
231             @Override
232             public final ImmutableList<? extends EffectiveStatement<?, ?>> effectiveSubstatements() {
233                 return unmaskList(substatements);
234             }
235         }
236
237         private final @NonNull ImmutableMap<QName, SchemaTreeEffectiveStatement<?>> schemaTree;
238         private final @NonNull D declared;
239
240         protected DefaultWithSchemaTree(final D declared, final StmtContext<?, ?, ?> ctx,
241                 final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
242             this.declared = requireNonNull(declared);
243             this.schemaTree = ImmutableMap.copyOf(createSchemaTreeNamespace(
244                 ctx.getStatementSourceReference(), substatements));
245         }
246
247         @Override
248         public final D getDeclared() {
249             return declared;
250         }
251
252         @Override
253         protected final Map<QName, SchemaTreeEffectiveStatement<?>> schemaTreeNamespace() {
254             return schemaTree;
255         }
256     }
257
258     /**
259      * Stateful version of {@link WithDataTree}. Schema tree and data tree namespaces are eagerly instantiated
260      * (and checked).
261      *
262      * @param <A> Argument type ({@link Void} if statement does not have argument.)
263      * @param <D> Class representing declared version of this statement.
264      * @param <E> Class representing effective version of this statement.
265      */
266     public abstract static class DefaultWithDataTree<A, D extends DeclaredStatement<A>,
267             E extends DataTreeAwareEffectiveStatement<A, D>> extends WithDataTree<A, D, E> {
268         public abstract static class WithSubstatements<A, D extends DeclaredStatement<A>,
269                 E extends DataTreeAwareEffectiveStatement<A, D>> extends DefaultWithDataTree<A, D, E> {
270             private final @NonNull Object substatements;
271
272             protected WithSubstatements(final D declared, final StmtContext<?, ?, ?> ctx,
273                     final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
274                 super(declared, ctx, substatements);
275                 this.substatements = maskList(substatements);
276             }
277
278             @Override
279             public final ImmutableList<? extends EffectiveStatement<?, ?>> effectiveSubstatements() {
280                 return unmaskList(substatements);
281             }
282         }
283
284         private final @NonNull ImmutableMap<QName, SchemaTreeEffectiveStatement<?>> schemaTree;
285         private final @NonNull ImmutableMap<QName, DataTreeEffectiveStatement<?>> dataTree;
286         private final @NonNull D declared;
287
288         protected DefaultWithDataTree(final D declared, final StmtContext<?, ?, ?> ctx,
289                 final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
290             this.declared = requireNonNull(declared);
291             final StatementSourceReference ref = ctx.getStatementSourceReference();
292             final Map<QName, SchemaTreeEffectiveStatement<?>> schema = createSchemaTreeNamespace(ref, substatements);
293             this.schemaTree = ImmutableMap.copyOf(schema);
294             this.dataTree = createDataTreeNamespace(ref, schema.values(), schemaTree);
295         }
296
297         @Override
298         public final D getDeclared() {
299             return declared;
300         }
301
302         @Override
303         protected final Map<QName, SchemaTreeEffectiveStatement<?>> schemaTreeNamespace() {
304             return schemaTree;
305         }
306
307         @Override
308         protected final Map<QName, DataTreeEffectiveStatement<?>> dataTreeNamespace() {
309             return dataTree;
310         }
311     }
312 }