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