Refactor anyxml/anydata
[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.collect.Collections2;
13 import com.google.common.collect.ImmutableList;
14 import com.google.common.collect.ImmutableSet;
15 import java.util.Collection;
16 import java.util.List;
17 import java.util.Optional;
18 import java.util.Set;
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.ContainerSchemaNode;
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.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.RevisionAwareXPath;
41 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.Status;
43 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
44 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
45 import org.opendaylight.yangtools.yang.model.api.UsesNode;
46 import org.opendaylight.yangtools.yang.model.api.WhenConditionAware;
47 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
48 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
49 import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
50 import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceEffectiveStatement;
51 import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
52 import org.opendaylight.yangtools.yang.model.api.stmt.WhenEffectiveStatement;
53 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory;
54 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
55
56 /**
57  * Mix-in interfaces providing services required by SchemaNode et al. These interfaces provide implementations, or
58  * implementation helpers based on default methods, so the correct behavior can be logically centralized.
59  */
60 @Beta
61 public final class EffectiveStatementMixins {
62     // Marker interface requiring all mixins to be derived from EffectiveStatement.
63     private interface Mixin<A, D extends DeclaredStatement<A>> extends EffectiveStatement<A, D> {
64         @SuppressWarnings("unchecked")
65         default <T> Collection<T> filterEffectiveStatements(final Class<T> type) {
66             // Yeah, this is not nice, but saves one transformation
67             return (Collection<T>) Collections2.filter(effectiveSubstatements(), type::isInstance);
68         }
69
70         // FIXME: YANGTOOLS-1068: eliminate this once we can return collections
71         default <T> List<T> filterEffectiveStatementsList(final Class<T> type) {
72             return ImmutableList.copyOf(filterEffectiveStatements(type));
73         }
74
75         // FIXME: YANGTOOLS-1068: eliminate this once we can return collections
76         default <T> Set<T> filterEffectiveStatementsSet(final Class<T> type) {
77             return ImmutableSet.copyOf(filterEffectiveStatements(type));
78         }
79     }
80
81     /**
82      * Bridge between {@link EffectiveStatement} and {@link AugmentationTarget}.
83      *
84      * @param <A> Argument type ({@link Void} if statement does not have argument.)
85      * @param <D> Class representing declared version of this statement.
86      */
87     public interface AugmentationTargetMixin<A, D extends DeclaredStatement<A>>
88             extends Mixin<A, D>, AugmentationTarget {
89         @Override
90         default Set<AugmentationSchemaNode> getAvailableAugmentations() {
91             return filterEffectiveStatementsSet(AugmentationSchemaNode.class);
92         }
93     }
94
95     /**
96      * Bridge between {@link EffectiveStatementWithFlags} and {@link AddedByUsesAware}.
97      *
98      * @param <A> Argument type ({@link Void} if statement does not have argument.)
99      * @param <D> Class representing declared version of this statement.
100      */
101     public interface AddedByUsesMixin<A, D extends DeclaredStatement<A>>
102             extends EffectiveStatementWithFlags<A, D>, AddedByUsesAware {
103         @Override
104         default boolean isAddedByUses() {
105             return (flags() & FlagsBuilder.ADDED_BY_USES) != 0;
106         }
107     }
108
109     /**
110      * Bridge between {@link EffectiveStatementWithFlags} and {@link ActionNodeContainer}.
111      *
112      * @param <A> Argument type ({@link Void} if statement does not have argument.)
113      * @param <D> Class representing declared version of this statement.
114      */
115     public interface ActionNodeContainerMixin<A, D extends DeclaredStatement<A>>
116             extends Mixin<A, D>, ActionNodeContainer {
117         @Override
118         default Set<ActionDefinition> getActions() {
119             return filterEffectiveStatementsSet(ActionDefinition.class);
120         }
121     }
122
123     /**
124      * Bridge between {@link EffectiveStatementWithFlags} and {@link NotificationNodeContainer}.
125      *
126      * @param <A> Argument type ({@link Void} if statement does not have argument.)
127      * @param <D> Class representing declared version of this statement.
128      */
129     public interface NotificationNodeContainerMixin<A, D extends DeclaredStatement<A>>
130             extends Mixin<A, D>, NotificationNodeContainer {
131         @Override
132         default Set<NotificationDefinition> getNotifications() {
133             return filterEffectiveStatementsSet(NotificationDefinition.class);
134         }
135     }
136
137     /**
138      * Bridge between {@link EffectiveStatementWithFlags} and {@link MustConstraintAware}.
139      *
140      * @param <A> Argument type ({@link Void} if statement does not have argument.)
141      * @param <D> Class representing declared version of this statement.
142      */
143     public interface MustConstraintMixin<A, D extends DeclaredStatement<A>> extends Mixin<A, D>, MustConstraintAware {
144         @Override
145         default Collection<MustDefinition> getMustConstraints() {
146             return filterEffectiveStatements(MustDefinition.class);
147         }
148     }
149
150     /**
151      * Bridge between {@link EffectiveStatementWithFlags} and {@link CopyableNode}.
152      *
153      * @param <A> Argument type ({@link Void} if statement does not have argument.)
154      * @param <D> Class representing declared version of this statement.
155      */
156     public interface CopyableMixin<A, D extends DeclaredStatement<A>> extends AddedByUsesMixin<A, D>, CopyableNode {
157         @Override
158         default boolean isAugmenting() {
159             return (flags() & FlagsBuilder.AUGMENTING) != 0;
160         }
161     }
162
163     /**
164      * Bridge between {@link EffectiveStatementWithFlags} and {@link DataNodeContainer}.
165      *
166      * @param <A> Argument type ({@link Void} if statement does not have argument.)
167      * @param <D> Class representing declared version of this statement.
168      */
169     public interface DataNodeContainerMixin<A, D extends DeclaredStatement<A>> extends DataNodeContainer, Mixin<A, D> {
170         @Override
171         default Set<TypeDefinition<?>> getTypeDefinitions() {
172             // TODO: the cast here is needed to work around Java 11 javac type inference issue
173             return (Set) effectiveSubstatements().stream().filter(TypedefEffectiveStatement.class::isInstance)
174                     .map(stmt -> ((TypedefEffectiveStatement) stmt).getTypeDefinition())
175                     .collect(ImmutableSet.toImmutableSet());
176         }
177
178         @Override
179         default Collection<DataSchemaNode> getChildNodes() {
180             return filterEffectiveStatements(DataSchemaNode.class);
181         }
182
183         @Override
184         default Set<GroupingDefinition> getGroupings() {
185             return filterEffectiveStatementsSet(GroupingDefinition.class);
186         }
187
188         @Override
189         default Set<UsesNode> getUses() {
190             return filterEffectiveStatementsSet(UsesNode.class);
191         }
192     }
193
194     /**
195      * Bridge between {@link EffectiveStatementWithFlags} and {@link DataSchemaNode}.
196      *
197      * @param <A> Argument type ({@link Void} if statement does not have argument.)
198      * @param <D> Class representing declared version of this statement.
199      */
200     public interface DataSchemaNodeMixin<A, D extends DeclaredStatement<A>>
201             extends DataSchemaNode, CopyableMixin<A, D>, SchemaNodeMixin<A, D>, WhenConditionMixin<A, D> {
202         @Override
203         default boolean isConfiguration() {
204             return (flags() & FlagsBuilder.CONFIGURATION) != 0;
205         }
206     }
207
208     /**
209      * Bridge between {@link EffectiveStatementWithFlags} and {@link DocumentedNode}.
210      *
211      * @param <A> Argument type ({@link Void} if statement does not have argument.)
212      * @param <D> Class representing declared version of this statement.
213      */
214     public interface DocumentedNodeMixin<A, D extends DeclaredStatement<A>> extends Mixin<A, D>, DocumentedNode {
215         /**
216          * Bridge between {@link EffectiveStatementWithFlags} and
217          * {@link org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus}.
218          *
219          * @param <A> Argument type ({@link Void} if statement does not have argument.)
220          * @param <D> Class representing declared version of this statement.
221          */
222         interface WithStatus<A, D extends DeclaredStatement<A>>
223                 extends EffectiveStatementWithFlags<A, D>, DocumentedNodeMixin<A, D>, DocumentedNode.WithStatus {
224             @Override
225             default Status getStatus() {
226                 final int status = flags() & FlagsBuilder.MASK_STATUS;
227                 switch (status) {
228                     case FlagsBuilder.STATUS_CURRENT:
229                         return Status.CURRENT;
230                     case FlagsBuilder.STATUS_DEPRECATED:
231                         return Status.DEPRECATED;
232                     case FlagsBuilder.STATUS_OBSOLETE:
233                         return Status.OBSOLETE;
234                     default:
235                         throw new IllegalStateException("Illegal status " + status);
236                 }
237             }
238         }
239
240         @Override
241         default Optional<String> getDescription() {
242             return findFirstEffectiveSubstatementArgument(DescriptionEffectiveStatement.class);
243         }
244
245         @Override
246         default Optional<String> getReference() {
247             return findFirstEffectiveSubstatementArgument(ReferenceEffectiveStatement.class);
248         }
249
250         @Override
251         default List<UnknownSchemaNode> getUnknownSchemaNodes() {
252             return filterEffectiveStatementsList(UnknownSchemaNode.class);
253         }
254     }
255
256     /**
257      * Bridge between {@link EffectiveStatementWithFlags} and {@link MandatoryAware}.
258      *
259      * @param <A> Argument type ({@link Void} if statement does not have argument.)
260      * @param <D> Class representing declared version of this statement.
261      */
262     public interface MandatoryMixin<A, D extends DeclaredStatement<A>>
263             extends EffectiveStatementWithFlags<A, D>, MandatoryAware {
264         @Override
265         default boolean isMandatory() {
266             return (flags() & FlagsBuilder.MANDATORY) != 0;
267         }
268     }
269
270     /**
271      * Bridge between {@link EffectiveStatementWithFlags} and {@code presence} statement.
272      *
273      * @param <A> Argument type ({@link Void} if statement does not have argument.)
274      * @param <D> Class representing declared version of this statement.
275      */
276     public interface PresenceMixin<A, D extends DeclaredStatement<A>> extends EffectiveStatementWithFlags<A, D> {
277         default boolean presence() {
278             return (flags() & FlagsBuilder.PRESENCE) != 0;
279         }
280     }
281
282     /**
283      * Bridge between {@link EffectiveStatementWithFlags} and {@link SchemaNode}.
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 SchemaNodeMixin<A, D extends DeclaredStatement<A>>
289             extends DocumentedNodeMixin.WithStatus<A, D>, SchemaNode {
290         @Override
291         default QName getQName() {
292             return getPath().getLastComponent();
293         }
294     }
295
296     /**
297      * Bridge between {@link EffectiveStatementWithFlags} and {@code ordered-by} statement.
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      */
302     public interface UserOrderedMixin<A, D extends DeclaredStatement<A>> extends EffectiveStatementWithFlags<A, D> {
303         default boolean userOrdered() {
304             return (flags() & FlagsBuilder.USER_ORDERED) != 0;
305         }
306     }
307
308     /**
309      * Helper used to locate the effective {@code when} statement and exposing its argument as per
310      * {@link WhenConditionAware}.
311      *
312      * @param <A> Argument type ({@link Void} if statement does not have argument.)
313      * @param <D> Class representing declared version of this statement.
314      */
315     public interface WhenConditionMixin<A, D extends DeclaredStatement<A>> extends Mixin<A, D>, WhenConditionAware {
316         @Override
317         default Optional<RevisionAwareXPath> getWhenCondition() {
318             return findFirstEffectiveSubstatementArgument(WhenEffectiveStatement.class);
319         }
320     }
321
322     /**
323      * Helper bridge for operation containers ({@code input} and {@code output}).
324      *
325      * @param <D> Class representing declared version of this statement.
326      */
327     public interface OperationContainerMixin<D extends DeclaredStatement<QName>>
328             extends ContainerSchemaNode, DocumentedNodeMixin.WithStatus<QName, D>, DataNodeContainerMixin<QName, D>,
329                     MustConstraintMixin<QName, D>, WhenConditionMixin<QName, D>, AugmentationTargetMixin<QName, D>,
330                     CopyableMixin<QName, D> {
331         @Override
332         default @NonNull QName argument() {
333             return getPath().getLastComponent();
334         }
335
336         @Override
337         default QName getQName() {
338             return argument();
339         }
340
341         @Override
342         default Optional<ActionDefinition> findAction(final QName qname) {
343             return Optional.empty();
344         }
345
346         @Override
347         default Optional<NotificationDefinition> findNotification(final QName qname) {
348             return Optional.empty();
349         }
350
351         @Override
352         default Set<ActionDefinition> getActions() {
353             return ImmutableSet.of();
354         }
355
356         @Override
357         default Set<NotificationDefinition> getNotifications() {
358             return ImmutableSet.of();
359         }
360
361         @Override
362         default boolean isConfiguration() {
363             return false;
364         }
365
366         @Override
367         default boolean isPresenceContainer() {
368             // FIXME: this should not really be here
369             return false;
370         }
371
372         default String defaultToString() {
373             return MoreObjects.toStringHelper(this).add("path", getPath()).toString();
374         }
375     }
376
377     /**
378      * Helper bridge for {@code anydata} and {@code anyxml} opaque data..
379      *
380      * @param <D> Class representing declared version of this statement.
381      */
382     public interface OpaqueDataSchemaNodeMixin<D extends DeclaredStatement<QName>>
383             extends DerivableSchemaNode, DataSchemaNodeMixin<QName, D>, DocumentedNodeMixin.WithStatus<QName, D>,
384                     MandatoryMixin<QName, D>, MustConstraintMixin<QName, D>, WhenConditionMixin<QName, D> {
385         @Override
386         default @NonNull QName argument() {
387             return getPath().getLastComponent();
388         }
389
390         @Override
391         default QName getQName() {
392             return argument();
393         }
394     }
395
396     /**
397      * Support interface for various mixins. Implementations are required to store 32bits worth of flags, which are
398      * globally assigned to sub-interfaces -- thus providing storage for many low-cardinality properties.
399      *
400      * @param <A> Argument type ({@link Void} if statement does not have argument.)
401      * @param <D> Class representing declared version of this statement.
402      */
403     public interface EffectiveStatementWithFlags<A, D extends DeclaredStatement<A>> extends Mixin<A, D> {
404         /**
405          * Return flags assicated with this statements. Flags can be built using {@link FlagsBuilder}.
406          *
407          * @return Flag field value (32 bits).
408          */
409         int flags();
410
411         @NonNullByDefault
412         final class FlagsBuilder implements Mutable {
413             // We still have 24 flags remaining
414             static final int STATUS_CURRENT       = 0x0001;
415             static final int STATUS_DEPRECATED    = 0x0002;
416             static final int STATUS_OBSOLETE      = 0x0003;
417             static final int MASK_STATUS          = 0x0003;
418
419             static final int CONFIGURATION        = 0x0004;
420             static final int MANDATORY            = 0x0008;
421
422             static final int AUGMENTING           = 0x0010;
423             static final int ADDED_BY_USES        = 0x0020;
424             private static final int MASK_HISTORY = 0x0030;
425
426             static final int USER_ORDERED         = 0x0040;
427             static final int PRESENCE             = 0x0080;
428
429             private int flags;
430
431             public FlagsBuilder setConfiguration(final boolean config) {
432                 if (config) {
433                     flags |= CONFIGURATION;
434                 } else {
435                     flags &= ~CONFIGURATION;
436                 }
437                 return this;
438             }
439
440             public FlagsBuilder setHistory(final CopyHistory history) {
441                 int bits;
442                 if (history.contains(CopyType.ADDED_BY_USES_AUGMENTATION)) {
443                     bits = AUGMENTING | ADDED_BY_USES;
444                 } else {
445                     bits = 0;
446                     if (history.contains(CopyType.ADDED_BY_AUGMENTATION)) {
447                         bits |= AUGMENTING;
448                     }
449                     if (history.contains(CopyType.ADDED_BY_USES)) {
450                         bits |= ADDED_BY_USES;
451                     }
452                 }
453
454                 flags = flags & ~MASK_HISTORY | bits;
455                 return this;
456             }
457
458             public FlagsBuilder setMandatory(final boolean mandatory) {
459                 if (mandatory) {
460                     flags |= MANDATORY;
461                 } else {
462                     flags &= ~MANDATORY;
463                 }
464                 return this;
465             }
466
467             public FlagsBuilder setPresence(final boolean presence) {
468                 if (presence) {
469                     flags |= PRESENCE;
470                 } else {
471                     flags &= ~PRESENCE;
472                 }
473                 return this;
474             }
475
476             public FlagsBuilder setStatus(final Status status) {
477                 final int bits;
478                 switch (status) {
479                     case CURRENT:
480                         bits = STATUS_CURRENT;
481                         break;
482                     case DEPRECATED:
483                         bits = STATUS_DEPRECATED;
484                         break;
485                     case OBSOLETE:
486                         bits = STATUS_OBSOLETE;
487                         break;
488                     default:
489                         throw new IllegalStateException("Unhandled status " + status);
490                 }
491
492                 flags = flags & ~MASK_STATUS | bits;
493                 return this;
494             }
495
496             public FlagsBuilder setUserOrdered(final boolean userOrdered) {
497                 if (userOrdered) {
498                     flags |= USER_ORDERED;
499                 } else {
500                     flags &= ~USER_ORDERED;
501                 }
502                 return this;
503             }
504
505             public int toFlags() {
506                 return flags;
507             }
508         }
509     }
510 }