e16d066b49207e6d002b90ccf3fd1d9a79e0d285
[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.opendaylight.yangtools.concepts.Mutable;
20 import org.opendaylight.yangtools.yang.common.QName;
21 import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
22 import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
23 import org.opendaylight.yangtools.yang.model.api.AddedByUsesAware;
24 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
26 import org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition;
27 import org.opendaylight.yangtools.yang.model.api.ContainerLike;
28 import org.opendaylight.yangtools.yang.model.api.CopyableNode;
29 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
30 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.DocumentedNode;
33 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
34 import org.opendaylight.yangtools.yang.model.api.InputSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.MandatoryAware;
36 import org.opendaylight.yangtools.yang.model.api.MustConstraintAware;
37 import org.opendaylight.yangtools.yang.model.api.MustDefinition;
38 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
39 import org.opendaylight.yangtools.yang.model.api.NotificationNodeContainer;
40 import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
41 import org.opendaylight.yangtools.yang.model.api.OutputSchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.Status;
44 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
45 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
46 import org.opendaylight.yangtools.yang.model.api.UsesNode;
47 import org.opendaylight.yangtools.yang.model.api.WhenConditionAware;
48 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
49 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
50 import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
51 import org.opendaylight.yangtools.yang.model.api.stmt.ErrorAppTagEffectiveStatement;
52 import org.opendaylight.yangtools.yang.model.api.stmt.ErrorMessageEffectiveStatement;
53 import org.opendaylight.yangtools.yang.model.api.stmt.InputEffectiveStatement;
54 import org.opendaylight.yangtools.yang.model.api.stmt.OutputEffectiveStatement;
55 import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceEffectiveStatement;
56 import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
57 import org.opendaylight.yangtools.yang.model.api.stmt.WhenEffectiveStatement;
58 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory;
59 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
60 import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression.QualifiedBound;
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
323     /**
324      * Bridge between {@link EffectiveStatementWithFlags} and {@code ordered-by} statement.
325      *
326      * @param <A> Argument type ({@link Void} if statement does not have argument.)
327      * @param <D> Class representing declared version of this statement.
328      */
329     public interface UserOrderedMixin<A, D extends DeclaredStatement<A>> extends EffectiveStatementWithFlags<A, D> {
330         default boolean userOrdered() {
331             return (flags() & FlagsBuilder.USER_ORDERED) != 0;
332         }
333     }
334
335     /**
336      * Helper used to locate the effective {@code when} statement and exposing its argument as per
337      * {@link WhenConditionAware}.
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 WhenConditionMixin<A, D extends DeclaredStatement<A>> extends Mixin<A, D>, WhenConditionAware {
343         @Override
344         default Optional<QualifiedBound> getWhenCondition() {
345             return findFirstEffectiveSubstatementArgument(WhenEffectiveStatement.class);
346         }
347     }
348
349     /**
350      * Helper bridge for operation containers ({@code input} and {@code output}).
351      *
352      * @param <D> Class representing declared version of this statement.
353      */
354     public interface OperationContainerMixin<D extends DeclaredStatement<QName>>
355             extends ContainerLike, DocumentedNodeMixin.WithStatus<QName, D>, DataNodeContainerMixin<QName, D>,
356                     MustConstraintMixin<QName, D>, WhenConditionMixin<QName, D>, AugmentationTargetMixin<QName, D>,
357                     SchemaNodeMixin<QName, D>, CopyableMixin<QName, D> {
358         @Override
359         default @NonNull QName argument() {
360             return getQName();
361         }
362
363         @Override
364         default Optional<ActionDefinition> findAction(final QName qname) {
365             return Optional.empty();
366         }
367
368         @Override
369         default Optional<NotificationDefinition> findNotification(final QName qname) {
370             return Optional.empty();
371         }
372
373         @Override
374         default Collection<? extends ActionDefinition> getActions() {
375             return ImmutableSet.of();
376         }
377
378         @Override
379         default Collection<? extends NotificationDefinition> getNotifications() {
380             return ImmutableSet.of();
381         }
382
383         @Override
384         default boolean isConfiguration() {
385             return false;
386         }
387
388         default String defaultToString() {
389             return MoreObjects.toStringHelper(this).add("path", getPath()).toString();
390         }
391     }
392
393     /**
394      * Helper bridge for {@code anydata} and {@code anyxml} opaque data.
395      *
396      * @param <D> Class representing declared version of this statement.
397      */
398     public interface OpaqueDataSchemaNodeMixin<D extends DeclaredStatement<QName>>
399             extends DerivableSchemaNode, DataSchemaNodeMixin<QName, D>, DocumentedNodeMixin.WithStatus<QName, D>,
400                     MandatoryMixin<QName, D>, MustConstraintMixin<QName, D>, WhenConditionMixin<QName, D> {
401         @Override
402         default @NonNull QName argument() {
403             return getQName();
404         }
405     }
406
407     /**
408      * Helper bridge for {@code rpc} and {@code action} operations.
409      *
410      * @param <D> Class representing declared version of this statement.
411      */
412     public interface OperationDefinitionMixin<D extends DeclaredStatement<QName>>
413             extends SchemaNodeMixin<QName, D>, OperationDefinition {
414         @Override
415         default @NonNull QName argument() {
416             return getQName();
417         }
418
419         @Override
420         default Collection<? extends TypeDefinition<?>> getTypeDefinitions() {
421             return filterTypeDefinitions(this);
422         }
423
424         @Override
425         default Collection<? extends GroupingDefinition> getGroupings() {
426             return filterEffectiveStatements(GroupingDefinition.class);
427         }
428
429         @Override
430         default InputSchemaNode getInput() {
431             return findAsContainer(this, InputEffectiveStatement.class, InputSchemaNode.class);
432         }
433
434         @Override
435         default OutputSchemaNode getOutput() {
436             return findAsContainer(this, OutputEffectiveStatement.class, OutputSchemaNode.class);
437         }
438     }
439
440     /**
441      * Support interface for various mixins. Implementations are required to store 32bits worth of flags, which are
442      * globally assigned to sub-interfaces -- thus providing storage for many low-cardinality properties.
443      *
444      * @param <A> Argument type ({@link Void} if statement does not have argument.)
445      * @param <D> Class representing declared version of this statement.
446      */
447     public interface EffectiveStatementWithFlags<A, D extends DeclaredStatement<A>> extends Mixin<A, D> {
448         /**
449          * Return flags assicated with this statements. Flags can be built using {@link FlagsBuilder}.
450          *
451          * @return Flag field value (32 bits).
452          */
453         int flags();
454
455         @NonNullByDefault
456         final class FlagsBuilder implements Mutable {
457             // We still have 24 flags remaining
458             static final int STATUS_CURRENT       = 0x0001;
459             static final int STATUS_DEPRECATED    = 0x0002;
460             static final int STATUS_OBSOLETE      = 0x0003;
461             static final int MASK_STATUS          = 0x0003;
462
463             static final int CONFIGURATION        = 0x0004;
464             static final int MANDATORY            = 0x0008;
465
466             static final int AUGMENTING           = 0x0010;
467             static final int ADDED_BY_USES        = 0x0020;
468             private static final int MASK_HISTORY = 0x0030;
469
470             static final int USER_ORDERED         = 0x0040;
471             static final int PRESENCE             = 0x0080;
472
473             private int flags;
474
475             public FlagsBuilder setConfiguration(final boolean config) {
476                 if (config) {
477                     flags |= CONFIGURATION;
478                 } else {
479                     flags &= ~CONFIGURATION;
480                 }
481                 return this;
482             }
483
484             public FlagsBuilder setHistory(final CopyHistory history) {
485                 int bits;
486                 if (history.contains(CopyType.ADDED_BY_USES_AUGMENTATION)) {
487                     bits = AUGMENTING | ADDED_BY_USES;
488                 } else {
489                     bits = 0;
490                     if (history.contains(CopyType.ADDED_BY_AUGMENTATION)) {
491                         bits |= AUGMENTING;
492                     }
493                     if (history.contains(CopyType.ADDED_BY_USES)) {
494                         bits |= ADDED_BY_USES;
495                     }
496                 }
497
498                 flags = flags & ~MASK_HISTORY | bits;
499                 return this;
500             }
501
502             public FlagsBuilder setMandatory(final boolean mandatory) {
503                 if (mandatory) {
504                     flags |= MANDATORY;
505                 } else {
506                     flags &= ~MANDATORY;
507                 }
508                 return this;
509             }
510
511             public FlagsBuilder setPresence(final boolean presence) {
512                 if (presence) {
513                     flags |= PRESENCE;
514                 } else {
515                     flags &= ~PRESENCE;
516                 }
517                 return this;
518             }
519
520             public FlagsBuilder setStatus(final Status status) {
521                 final int bits;
522                 switch (status) {
523                     case CURRENT:
524                         bits = STATUS_CURRENT;
525                         break;
526                     case DEPRECATED:
527                         bits = STATUS_DEPRECATED;
528                         break;
529                     case OBSOLETE:
530                         bits = STATUS_OBSOLETE;
531                         break;
532                     default:
533                         throw new IllegalStateException("Unhandled status " + status);
534                 }
535
536                 flags = flags & ~MASK_STATUS | bits;
537                 return this;
538             }
539
540             public FlagsBuilder setUserOrdered(final boolean userOrdered) {
541                 if (userOrdered) {
542                     flags |= USER_ORDERED;
543                 } else {
544                     flags &= ~USER_ORDERED;
545                 }
546                 return this;
547             }
548
549             public int toFlags() {
550                 return flags;
551             }
552         }
553     }
554
555     private EffectiveStatementMixins() {
556     }
557
558     static <T extends ContainerLike> T findAsContainer(final EffectiveStatement<?, ?> stmt,
559             final Class<? extends EffectiveStatement<QName, ?>> type, Class<T> target) {
560         return target.cast(stmt.findFirstEffectiveSubstatement(type).get());
561     }
562
563     static Collection<? extends TypeDefinition<?>> filterTypeDefinitions(final Mixin<?, ?> stmt) {
564         return Collections2.transform(stmt.filterEffectiveStatements(TypedefEffectiveStatement.class),
565             TypedefEffectiveStatement::getTypeDefinition);
566     }
567 }