Add DataSchemaNode.effectiveConfig()
[yangtools.git] / yang / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / stmt / EffectiveStatementMixins.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 com.google.common.annotations.Beta;
11 import com.google.common.base.MoreObjects;
12 import com.google.common.base.Strings;
13 import com.google.common.collect.Collections2;
14 import com.google.common.collect.ImmutableSet;
15 import java.util.Collection;
16 import java.util.Optional;
17 import org.eclipse.jdt.annotation.NonNull;
18 import org.eclipse.jdt.annotation.NonNullByDefault;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.opendaylight.yangtools.concepts.Mutable;
21 import org.opendaylight.yangtools.yang.common.QName;
22 import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
23 import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
24 import org.opendaylight.yangtools.yang.model.api.AddedByUsesAware;
25 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
27 import org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition;
28 import org.opendaylight.yangtools.yang.model.api.ContainerLike;
29 import org.opendaylight.yangtools.yang.model.api.CopyableNode;
30 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
31 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.DocumentedNode;
34 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
35 import org.opendaylight.yangtools.yang.model.api.InputSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.MandatoryAware;
37 import org.opendaylight.yangtools.yang.model.api.MustConstraintAware;
38 import org.opendaylight.yangtools.yang.model.api.MustDefinition;
39 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
40 import org.opendaylight.yangtools.yang.model.api.NotificationNodeContainer;
41 import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
42 import org.opendaylight.yangtools.yang.model.api.OutputSchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.Status;
45 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
46 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
47 import org.opendaylight.yangtools.yang.model.api.UsesNode;
48 import org.opendaylight.yangtools.yang.model.api.WhenConditionAware;
49 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
50 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
51 import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
52 import org.opendaylight.yangtools.yang.model.api.stmt.ErrorAppTagEffectiveStatement;
53 import org.opendaylight.yangtools.yang.model.api.stmt.ErrorMessageEffectiveStatement;
54 import org.opendaylight.yangtools.yang.model.api.stmt.InputEffectiveStatement;
55 import org.opendaylight.yangtools.yang.model.api.stmt.OutputEffectiveStatement;
56 import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceEffectiveStatement;
57 import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
58 import org.opendaylight.yangtools.yang.model.api.stmt.WhenEffectiveStatement;
59 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory;
60 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
61 import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression.QualifiedBound;
62
63 /**
64  * Mix-in interfaces providing services required by SchemaNode et al. These interfaces provide implementations, or
65  * implementation helpers based on default methods, so the correct behavior can be logically centralized.
66  */
67 @Beta
68 public final class EffectiveStatementMixins {
69     // Marker interface requiring all mixins to be derived from EffectiveStatement.
70     private interface Mixin<A, D extends DeclaredStatement<A>> extends EffectiveStatement<A, D> {
71         @SuppressWarnings("unchecked")
72         default <T> @NonNull Collection<? extends @NonNull T> filterEffectiveStatements(final Class<T> type) {
73             // Yeah, this is not nice, but saves one transformation
74             return (Collection<? extends T>) Collections2.filter(effectiveSubstatements(), type::isInstance);
75         }
76     }
77
78     /**
79      * Bridge between {@link EffectiveStatement} and {@link AugmentationTarget}.
80      *
81      * @param <A> Argument type ({@link Void} if statement does not have argument.)
82      * @param <D> Class representing declared version of this statement.
83      */
84     public interface AugmentationTargetMixin<A, D extends DeclaredStatement<A>>
85             extends Mixin<A, D>, AugmentationTarget {
86         @Override
87         default Collection<? extends AugmentationSchemaNode> getAvailableAugmentations() {
88             return filterEffectiveStatements(AugmentationSchemaNode.class);
89         }
90     }
91
92     /**
93      * Bridge between {@link EffectiveStatementWithFlags} and {@link AddedByUsesAware}.
94      *
95      * @param <A> Argument type ({@link Void} if statement does not have argument.)
96      * @param <D> Class representing declared version of this statement.
97      */
98     public interface AddedByUsesMixin<A, D extends DeclaredStatement<A>>
99             extends EffectiveStatementWithFlags<A, D>, AddedByUsesAware {
100         @Override
101         @Deprecated
102         default boolean isAddedByUses() {
103             return (flags() & FlagsBuilder.ADDED_BY_USES) != 0;
104         }
105     }
106
107     /**
108      * Bridge between {@link EffectiveStatementWithFlags} and {@link ActionNodeContainer}.
109      *
110      * @param <A> Argument type ({@link Void} if statement does not have argument.)
111      * @param <D> Class representing declared version of this statement.
112      */
113     public interface ActionNodeContainerMixin<A, D extends DeclaredStatement<A>>
114             extends Mixin<A, D>, ActionNodeContainer {
115         @Override
116         default Collection<? extends ActionDefinition> getActions() {
117             return filterEffectiveStatements(ActionDefinition.class);
118         }
119     }
120
121     /**
122      * Bridge between {@link EffectiveStatementWithFlags} and {@link NotificationNodeContainer}.
123      *
124      * @param <A> Argument type ({@link Void} if statement does not have argument.)
125      * @param <D> Class representing declared version of this statement.
126      */
127     public interface NotificationNodeContainerMixin<A, D extends DeclaredStatement<A>>
128             extends Mixin<A, D>, NotificationNodeContainer {
129         @Override
130         default Collection<? extends NotificationDefinition> getNotifications() {
131             return filterEffectiveStatements(NotificationDefinition.class);
132         }
133     }
134
135     /**
136      * Bridge between {@link EffectiveStatementWithFlags} and {@link MustConstraintAware}.
137      *
138      * @param <A> Argument type ({@link Void} if statement does not have argument.)
139      * @param <D> Class representing declared version of this statement.
140      */
141     public interface MustConstraintMixin<A, D extends DeclaredStatement<A>> extends Mixin<A, D>, MustConstraintAware {
142         @Override
143         default Collection<? extends @NonNull MustDefinition> getMustConstraints() {
144             return filterEffectiveStatements(MustDefinition.class);
145         }
146     }
147
148     /**
149      * Bridge between {@link EffectiveStatementWithFlags} and {@link CopyableNode}.
150      *
151      * @param <A> Argument type ({@link Void} if statement does not have argument.)
152      * @param <D> Class representing declared version of this statement.
153      */
154     public interface CopyableMixin<A, D extends DeclaredStatement<A>> extends AddedByUsesMixin<A, D>, CopyableNode {
155         @Override
156         @Deprecated
157         default boolean isAugmenting() {
158             return (flags() & FlagsBuilder.AUGMENTING) != 0;
159         }
160     }
161
162     /**
163      * Bridge between {@link EffectiveStatementWithFlags} and {@link DataNodeContainer}.
164      *
165      * @param <A> Argument type ({@link Void} if statement does not have argument.)
166      * @param <D> Class representing declared version of this statement.
167      */
168     public interface DataNodeContainerMixin<A, D extends DeclaredStatement<A>> extends DataNodeContainer, Mixin<A, D> {
169         @Override
170         default Collection<? extends TypeDefinition<?>> getTypeDefinitions() {
171             return filterTypeDefinitions(this);
172         }
173
174         @Override
175         default Collection<? extends DataSchemaNode> getChildNodes() {
176             return filterEffectiveStatements(DataSchemaNode.class);
177         }
178
179         @Override
180         default Collection<? extends GroupingDefinition> getGroupings() {
181             return filterEffectiveStatements(GroupingDefinition.class);
182         }
183
184         @Override
185         default Collection<? extends UsesNode> getUses() {
186             return filterEffectiveStatements(UsesNode.class);
187         }
188     }
189
190     /**
191      * Bridge between {@link EffectiveStatementWithFlags} and {@link DataSchemaNode}.
192      *
193      * @param <A> Argument type ({@link Void} if statement does not have argument.)
194      * @param <D> Class representing declared version of this statement.
195      */
196     public interface DataSchemaNodeMixin<A, D extends DeclaredStatement<A>>
197             extends DataSchemaNode, CopyableMixin<A, D>, SchemaNodeMixin<A, D>, WhenConditionMixin<A, D> {
198         @Override
199         default Optional<Boolean> effectiveConfig() {
200             final int fl = flags() & FlagsBuilder.MASK_CONFIG;
201             switch (fl) {
202                 case FlagsBuilder.CONFIG_FALSE:
203                     return Optional.of(Boolean.FALSE);
204                 case FlagsBuilder.CONFIG_TRUE:
205                     return Optional.of(Boolean.TRUE);
206                 case FlagsBuilder.CONFIG_UNDEF:
207                     return Optional.empty();
208                 default:
209                     throw new IllegalStateException("Unhandled effective config flags " + fl);
210             }
211         }
212     }
213
214     /**
215      * Bridge between {@link EffectiveStatementWithFlags} and {@link DocumentedNode}.
216      *
217      * @param <A> Argument type ({@link Void} if statement does not have argument.)
218      * @param <D> Class representing declared version of this statement.
219      */
220     public interface DocumentedNodeMixin<A, D extends DeclaredStatement<A>> extends Mixin<A, D>, DocumentedNode {
221         /**
222          * Bridge between {@link EffectiveStatementWithFlags} and
223          * {@link org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus}.
224          *
225          * @param <A> Argument type ({@link Void} if statement does not have argument.)
226          * @param <D> Class representing declared version of this statement.
227          */
228         interface WithStatus<A, D extends DeclaredStatement<A>>
229                 extends EffectiveStatementWithFlags<A, D>, DocumentedNodeMixin<A, D>, DocumentedNode.WithStatus {
230             @Override
231             default Status getStatus() {
232                 final int status = flags() & FlagsBuilder.MASK_STATUS;
233                 switch (status) {
234                     case FlagsBuilder.STATUS_CURRENT:
235                         return Status.CURRENT;
236                     case FlagsBuilder.STATUS_DEPRECATED:
237                         return Status.DEPRECATED;
238                     case FlagsBuilder.STATUS_OBSOLETE:
239                         return Status.OBSOLETE;
240                     default:
241                         throw new IllegalStateException("Illegal status " + status);
242                 }
243             }
244         }
245
246         @Override
247         default Optional<String> getDescription() {
248             return findFirstEffectiveSubstatementArgument(DescriptionEffectiveStatement.class);
249         }
250
251         @Override
252         default Optional<String> getReference() {
253             return findFirstEffectiveSubstatementArgument(ReferenceEffectiveStatement.class);
254         }
255
256         @Override
257         default Collection<? extends UnknownSchemaNode> getUnknownSchemaNodes() {
258             return filterEffectiveStatements(UnknownSchemaNode.class);
259         }
260     }
261
262     /**
263      * Bridge between {@link EffectiveStatementWithFlags} and {@link ConstraintMetaDefinition}.
264      *
265      * @param <A> Argument type ({@link Void} if statement does not have argument.)
266      * @param <D> Class representing declared version of this statement.
267      */
268     public interface ConstraintMetaDefinitionMixin<A, D extends DeclaredStatement<A>> extends DocumentedNodeMixin<A, D>,
269             ConstraintMetaDefinition {
270         @Override
271         default Optional<String> getErrorAppTag() {
272             return findFirstEffectiveSubstatementArgument(ErrorAppTagEffectiveStatement.class);
273         }
274
275         @Override
276         default Optional<String> getErrorMessage() {
277             return findFirstEffectiveSubstatementArgument(ErrorMessageEffectiveStatement.class);
278         }
279     }
280
281     /**
282      * Bridge between {@link EffectiveStatementWithFlags} and {@link MandatoryAware}.
283      *
284      * @param <A> Argument type ({@link Void} if statement does not have argument.)
285      * @param <D> Class representing declared version of this statement.
286      */
287     public interface MandatoryMixin<A, D extends DeclaredStatement<A>>
288             extends EffectiveStatementWithFlags<A, D>, MandatoryAware {
289         @Override
290         default boolean isMandatory() {
291             return (flags() & FlagsBuilder.MANDATORY) != 0;
292         }
293     }
294
295     /**
296      * Bridge between {@link EffectiveStatementWithFlags} and {@code presence} statement.
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      */
301     public interface PresenceMixin<A, D extends DeclaredStatement<A>> extends EffectiveStatementWithFlags<A, D> {
302         default boolean presence() {
303             return (flags() & FlagsBuilder.PRESENCE) != 0;
304         }
305     }
306
307     /**
308      * Bridge between {@link EffectiveStatementWithFlags} and {@link SchemaNode}.
309      *
310      * @param <A> Argument type ({@link Void} if statement does not have argument.)
311      * @param <D> Class representing declared version of this statement.
312      */
313     public interface SchemaNodeMixin<A, D extends DeclaredStatement<A>>
314             extends DocumentedNodeMixin.WithStatus<A, D>, SchemaNode {
315         @Override
316         default QName getQName() {
317             return getPath().getLastComponent();
318         }
319     }
320
321     /**
322      * Bridge between {@link EffectiveStatementWithFlags} and {@link UnknownSchemaNode}.
323      *
324      * @param <A> Argument type ({@link Void} if statement does not have argument.)
325      * @param <D> Class representing declared version of this statement.
326      */
327     public interface UnknownSchemaNodeMixin<A, D extends DeclaredStatement<A>>
328             extends SchemaNodeMixin<A, D>, CopyableMixin<A, D>, UnknownSchemaNode {
329
330         @Override
331         default String getNodeParameter() {
332             return Strings.nullToEmpty(getDeclared().rawArgument());
333         }
334     }
335
336     /**
337      * Bridge between {@link EffectiveStatementWithFlags} and {@code ordered-by} statement.
338      *
339      * @param <A> Argument type ({@link Void} if statement does not have argument.)
340      * @param <D> Class representing declared version of this statement.
341      */
342     public interface UserOrderedMixin<A, D extends DeclaredStatement<A>> extends EffectiveStatementWithFlags<A, D> {
343         default boolean userOrdered() {
344             return (flags() & FlagsBuilder.USER_ORDERED) != 0;
345         }
346     }
347
348     /**
349      * Helper used to locate the effective {@code when} statement and exposing its argument as per
350      * {@link WhenConditionAware}.
351      *
352      * @param <A> Argument type ({@link Void} if statement does not have argument.)
353      * @param <D> Class representing declared version of this statement.
354      */
355     public interface WhenConditionMixin<A, D extends DeclaredStatement<A>> extends Mixin<A, D>, WhenConditionAware {
356         @Override
357         default Optional<QualifiedBound> getWhenCondition() {
358             return findFirstEffectiveSubstatementArgument(WhenEffectiveStatement.class);
359         }
360     }
361
362     /**
363      * Helper bridge for operation containers ({@code input} and {@code output}).
364      *
365      * @param <D> Class representing declared version of this statement.
366      */
367     public interface OperationContainerMixin<D extends DeclaredStatement<QName>>
368             extends ContainerLike, DocumentedNodeMixin.WithStatus<QName, D>, DataNodeContainerMixin<QName, D>,
369                     MustConstraintMixin<QName, D>, WhenConditionMixin<QName, D>, AugmentationTargetMixin<QName, D>,
370                     SchemaNodeMixin<QName, D>, CopyableMixin<QName, D> {
371         @Override
372         default @NonNull QName argument() {
373             return getQName();
374         }
375
376         @Override
377         default Optional<ActionDefinition> findAction(final QName qname) {
378             return Optional.empty();
379         }
380
381         @Override
382         default Optional<NotificationDefinition> findNotification(final QName qname) {
383             return Optional.empty();
384         }
385
386         @Override
387         default Collection<? extends ActionDefinition> getActions() {
388             return ImmutableSet.of();
389         }
390
391         @Override
392         default Collection<? extends NotificationDefinition> getNotifications() {
393             return ImmutableSet.of();
394         }
395
396         @Override
397         default Optional<Boolean> effectiveConfig() {
398             return Optional.empty();
399         }
400
401         default String defaultToString() {
402             return MoreObjects.toStringHelper(this).add("path", getPath()).toString();
403         }
404     }
405
406     /**
407      * Helper bridge for {@code anydata} and {@code anyxml} opaque data.
408      *
409      * @param <D> Class representing declared version of this statement.
410      */
411     public interface OpaqueDataSchemaNodeMixin<D extends DeclaredStatement<QName>>
412             extends DerivableSchemaNode, DataSchemaNodeMixin<QName, D>, DocumentedNodeMixin.WithStatus<QName, D>,
413                     MandatoryMixin<QName, D>, MustConstraintMixin<QName, D>, WhenConditionMixin<QName, D> {
414         @Override
415         default @NonNull QName argument() {
416             return getQName();
417         }
418     }
419
420     /**
421      * Helper bridge for {@code rpc} and {@code action} operations.
422      *
423      * @param <D> Class representing declared version of this statement.
424      */
425     public interface OperationDefinitionMixin<D extends DeclaredStatement<QName>>
426             extends SchemaNodeMixin<QName, D>, OperationDefinition {
427         @Override
428         default @NonNull QName argument() {
429             return getQName();
430         }
431
432         @Override
433         default Collection<? extends @NonNull TypeDefinition<?>> getTypeDefinitions() {
434             return filterTypeDefinitions(this);
435         }
436
437         @Override
438         default Collection<? extends @NonNull GroupingDefinition> getGroupings() {
439             return filterEffectiveStatements(GroupingDefinition.class);
440         }
441
442         @Override
443         default InputSchemaNode getInput() {
444             return findAsContainer(this, InputEffectiveStatement.class, InputSchemaNode.class);
445         }
446
447         @Override
448         default OutputSchemaNode getOutput() {
449             return findAsContainer(this, OutputEffectiveStatement.class, OutputSchemaNode.class);
450         }
451     }
452
453     /**
454      * Support interface for various mixins. Implementations are required to store 32bits worth of flags, which are
455      * globally assigned to sub-interfaces -- thus providing storage for many low-cardinality properties.
456      *
457      * @param <A> Argument type ({@link Void} if statement does not have argument.)
458      * @param <D> Class representing declared version of this statement.
459      */
460     public interface EffectiveStatementWithFlags<A, D extends DeclaredStatement<A>> extends Mixin<A, D> {
461         /**
462          * Return flags assicated with this statements. Flags can be built using {@link FlagsBuilder}.
463          *
464          * @return Flag field value (32 bits).
465          */
466         int flags();
467
468         @NonNullByDefault
469         final class FlagsBuilder implements Mutable {
470             // We still have 23 flags remaining
471             static final int STATUS_CURRENT       = 0x0001;
472             static final int STATUS_DEPRECATED    = 0x0002;
473             static final int STATUS_OBSOLETE      = 0x0003;
474             static final int MASK_STATUS          = 0x0003;
475
476             static final int MANDATORY            = 0x0004;
477
478             static final int AUGMENTING           = 0x0010;
479             static final int ADDED_BY_USES        = 0x0020;
480             private static final int MASK_HISTORY = 0x0030;
481
482             static final int USER_ORDERED         = 0x0040;
483             static final int PRESENCE             = 0x0080;
484
485             static final int CONFIG_UNDEF         = 0x0100;
486             static final int CONFIG_FALSE         = 0x0200;
487             static final int CONFIG_TRUE          = 0x0300;
488             static final int MASK_CONFIG          = CONFIG_TRUE;
489
490             private int flags;
491
492             public FlagsBuilder setConfiguration(final @Nullable Boolean config) {
493                 final int fl;
494                 if (config != null) {
495                     fl = config ? CONFIG_TRUE : CONFIG_FALSE;
496                 } else {
497                     fl = CONFIG_UNDEF;
498                 }
499                 flags = flags & ~MASK_CONFIG | fl;
500                 return this;
501             }
502
503             public FlagsBuilder setHistory(final CopyHistory history) {
504                 int bits;
505                 if (history.contains(CopyType.ADDED_BY_USES_AUGMENTATION)) {
506                     bits = AUGMENTING | ADDED_BY_USES;
507                 } else {
508                     bits = 0;
509                     if (history.contains(CopyType.ADDED_BY_AUGMENTATION)) {
510                         bits |= AUGMENTING;
511                     }
512                     if (history.contains(CopyType.ADDED_BY_USES)) {
513                         bits |= ADDED_BY_USES;
514                     }
515                 }
516
517                 flags = flags & ~MASK_HISTORY | bits;
518                 return this;
519             }
520
521             public FlagsBuilder setMandatory(final boolean mandatory) {
522                 if (mandatory) {
523                     flags |= MANDATORY;
524                 } else {
525                     flags &= ~MANDATORY;
526                 }
527                 return this;
528             }
529
530             public FlagsBuilder setPresence(final boolean presence) {
531                 if (presence) {
532                     flags |= PRESENCE;
533                 } else {
534                     flags &= ~PRESENCE;
535                 }
536                 return this;
537             }
538
539             public FlagsBuilder setStatus(final Status status) {
540                 final int bits;
541                 switch (status) {
542                     case CURRENT:
543                         bits = STATUS_CURRENT;
544                         break;
545                     case DEPRECATED:
546                         bits = STATUS_DEPRECATED;
547                         break;
548                     case OBSOLETE:
549                         bits = STATUS_OBSOLETE;
550                         break;
551                     default:
552                         throw new IllegalStateException("Unhandled status " + status);
553                 }
554
555                 flags = flags & ~MASK_STATUS | bits;
556                 return this;
557             }
558
559             public FlagsBuilder setUserOrdered(final boolean userOrdered) {
560                 if (userOrdered) {
561                     flags |= USER_ORDERED;
562                 } else {
563                     flags &= ~USER_ORDERED;
564                 }
565                 return this;
566             }
567
568             public int toFlags() {
569                 return flags;
570             }
571         }
572     }
573
574     private EffectiveStatementMixins() {
575     }
576
577     static <T extends ContainerLike> T findAsContainer(final EffectiveStatement<?, ?> stmt,
578             final Class<? extends EffectiveStatement<QName, ?>> type, final Class<T> target) {
579         return target.cast(stmt.findFirstEffectiveSubstatement(type).get());
580     }
581
582     static Collection<? extends @NonNull TypeDefinition<?>> filterTypeDefinitions(final Mixin<?, ?> stmt) {
583         return Collections2.transform(stmt.filterEffectiveStatements(TypedefEffectiveStatement.class),
584             TypedefEffectiveStatement::getTypeDefinition);
585     }
586 }