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