Convert pattern statement
[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      * 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, final StmtContext<?, ?, ?> ctx,
263                     final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
264                 super(declared, ctx, 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, final StmtContext<?, ?, ?> ctx,
278                 final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
279             this.declared = requireNonNull(declared);
280             this.schemaTree = ImmutableMap.copyOf(createSchemaTreeNamespace(
281                 ctx.getStatementSourceReference(), substatements));
282         }
283
284         @Override
285         public final D getDeclared() {
286             return declared;
287         }
288
289         @Override
290         protected final Map<QName, SchemaTreeEffectiveStatement<?>> schemaTreeNamespace() {
291             return schemaTree;
292         }
293     }
294
295     /**
296      * Stateful version of {@link WithDataTree}. Schema tree and data tree namespaces are eagerly instantiated
297      * (and checked).
298      *
299      * @param <A> Argument type ({@link Void} if statement does not have argument.)
300      * @param <D> Class representing declared version of this statement.
301      * @param <E> Class representing effective version of this statement.
302      */
303     public abstract static class DefaultWithDataTree<A, D extends DeclaredStatement<A>,
304             E extends DataTreeAwareEffectiveStatement<A, D>> extends WithDataTree<A, D, E> {
305         public abstract static class WithSubstatements<A, D extends DeclaredStatement<A>,
306                 E extends DataTreeAwareEffectiveStatement<A, D>> extends DefaultWithDataTree<A, D, E> {
307             private final @NonNull Object substatements;
308
309             protected WithSubstatements(final D declared, final StmtContext<?, ?, ?> ctx,
310                     final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
311                 super(declared, ctx, substatements);
312                 this.substatements = maskList(substatements);
313             }
314
315             @Override
316             public final ImmutableList<? extends EffectiveStatement<?, ?>> effectiveSubstatements() {
317                 return unmaskList(substatements);
318             }
319         }
320
321         private final @NonNull ImmutableMap<QName, SchemaTreeEffectiveStatement<?>> schemaTree;
322         private final @NonNull ImmutableMap<QName, DataTreeEffectiveStatement<?>> dataTree;
323         private final @NonNull D declared;
324
325         protected DefaultWithDataTree(final D declared, final StmtContext<?, ?, ?> ctx,
326                 final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
327             this.declared = requireNonNull(declared);
328             final StatementSourceReference ref = ctx.getStatementSourceReference();
329             final Map<QName, SchemaTreeEffectiveStatement<?>> schema = createSchemaTreeNamespace(ref, substatements);
330             this.schemaTree = ImmutableMap.copyOf(schema);
331             this.dataTree = createDataTreeNamespace(ref, schema.values(), schemaTree);
332         }
333
334         @Override
335         public final D getDeclared() {
336             return declared;
337         }
338
339         @Override
340         protected final Map<QName, SchemaTreeEffectiveStatement<?>> schemaTreeNamespace() {
341             return schemaTree;
342         }
343
344         @Override
345         protected final Map<QName, DataTreeEffectiveStatement<?>> dataTreeNamespace() {
346             return dataTree;
347         }
348     }
349 }