d4e6df5ce8820490b660db8766f17dd1d65ccee7
[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.collect.Collections2;
12 import java.util.Collection;
13 import java.util.Optional;
14 import org.eclipse.jdt.annotation.NonNullByDefault;
15 import org.opendaylight.yangtools.concepts.Mutable;
16 import org.opendaylight.yangtools.yang.common.QName;
17 import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
18 import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
19 import org.opendaylight.yangtools.yang.model.api.AddedByUsesAware;
20 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
21 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
22 import org.opendaylight.yangtools.yang.model.api.CopyableNode;
23 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
24 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.DocumentedNode;
26 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
27 import org.opendaylight.yangtools.yang.model.api.MandatoryAware;
28 import org.opendaylight.yangtools.yang.model.api.MustConstraintAware;
29 import org.opendaylight.yangtools.yang.model.api.MustDefinition;
30 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
31 import org.opendaylight.yangtools.yang.model.api.NotificationNodeContainer;
32 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
33 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.Status;
35 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
36 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.UsesNode;
38 import org.opendaylight.yangtools.yang.model.api.WhenConditionAware;
39 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
40 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
41 import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
42 import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceEffectiveStatement;
43 import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
44 import org.opendaylight.yangtools.yang.model.api.stmt.WhenEffectiveStatement;
45 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory;
46 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
47
48 /**
49  * Mix-in interfaces providing services required by SchemaNode et al. These interfaces provide implementations, or
50  * implementation helpers based on default methods, so the correct behavior can be logically centralized.
51  */
52 @Beta
53 public final class EffectiveStatementMixins {
54     // Marker interface requiring all mixins to be derived from EffectiveStatement.
55     private interface Mixin<A, D extends DeclaredStatement<A>> extends EffectiveStatement<A, D> {
56         @SuppressWarnings("unchecked")
57         default <T> Collection<? extends T> filterEffectiveStatements(final Class<T> type) {
58             // Yeah, this is not nice, but saves one transformation
59             return (Collection<? extends T>) Collections2.filter(effectiveSubstatements(), type::isInstance);
60         }
61     }
62
63     /**
64      * Bridge between {@link EffectiveStatement} and {@link AugmentationTarget}.
65      *
66      * @param <A> Argument type ({@link Void} if statement does not have argument.)
67      * @param <D> Class representing declared version of this statement.
68      */
69     public interface AugmentationTargetMixin<A, D extends DeclaredStatement<A>>
70             extends Mixin<A, D>, AugmentationTarget {
71         @Override
72         default Collection<? extends AugmentationSchemaNode> getAvailableAugmentations() {
73             return filterEffectiveStatements(AugmentationSchemaNode.class);
74         }
75     }
76
77     /**
78      * Bridge between {@link EffectiveStatementWithFlags} and {@link AddedByUsesAware}.
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 AddedByUsesMixin<A, D extends DeclaredStatement<A>>
84             extends EffectiveStatementWithFlags<A, D>, AddedByUsesAware {
85         @Override
86         default boolean isAddedByUses() {
87             return (flags() & FlagsBuilder.ADDED_BY_USES) != 0;
88         }
89     }
90
91     /**
92      * Bridge between {@link EffectiveStatementWithFlags} and {@link ActionNodeContainer}.
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 ActionNodeContainerMixin<A, D extends DeclaredStatement<A>>
98             extends Mixin<A, D>, ActionNodeContainer {
99         @Override
100         default Collection<? extends ActionDefinition> getActions() {
101             return filterEffectiveStatements(ActionDefinition.class);
102         }
103     }
104
105     /**
106      * Bridge between {@link EffectiveStatementWithFlags} and {@link NotificationNodeContainer}.
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 NotificationNodeContainerMixin<A, D extends DeclaredStatement<A>>
112             extends Mixin<A, D>, NotificationNodeContainer {
113         @Override
114         default Collection<? extends NotificationDefinition> getNotifications() {
115             return filterEffectiveStatements(NotificationDefinition.class);
116         }
117     }
118
119     /**
120      * Bridge between {@link EffectiveStatementWithFlags} and {@link MustConstraintAware}.
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 MustConstraintMixin<A, D extends DeclaredStatement<A>> extends Mixin<A, D>, MustConstraintAware {
126         @Override
127         default Collection<? extends MustDefinition> getMustConstraints() {
128             return filterEffectiveStatements(MustDefinition.class);
129         }
130     }
131
132     /**
133      * Bridge between {@link EffectiveStatementWithFlags} and {@link CopyableNode}.
134      *
135      * @param <A> Argument type ({@link Void} if statement does not have argument.)
136      * @param <D> Class representing declared version of this statement.
137      */
138     public interface CopyableMixin<A, D extends DeclaredStatement<A>> extends AddedByUsesMixin<A, D>, CopyableNode {
139         @Override
140         default boolean isAugmenting() {
141             return (flags() & FlagsBuilder.AUGMENTING) != 0;
142         }
143     }
144
145     /**
146      * Bridge between {@link EffectiveStatementWithFlags} and {@link DataNodeContainer}.
147      *
148      * @param <A> Argument type ({@link Void} if statement does not have argument.)
149      * @param <D> Class representing declared version of this statement.
150      */
151     public interface DataNodeContainerMixin<A, D extends DeclaredStatement<A>> extends DataNodeContainer, Mixin<A, D> {
152         @Override
153         default Collection<? extends TypeDefinition<?>> getTypeDefinitions() {
154             // TODO: the cast here is needed to work around Java 11 javac type inference issue
155             return Collections2.transform(filterEffectiveStatements(TypedefEffectiveStatement.class),
156                 TypedefEffectiveStatement::getTypeDefinition);
157         }
158
159         @Override
160         default Collection<? extends DataSchemaNode> getChildNodes() {
161             return filterEffectiveStatements(DataSchemaNode.class);
162         }
163
164         @Override
165         default Collection<? extends GroupingDefinition> getGroupings() {
166             return filterEffectiveStatements(GroupingDefinition.class);
167         }
168
169         @Override
170         default Collection<? extends UsesNode> getUses() {
171             return filterEffectiveStatements(UsesNode.class);
172         }
173     }
174
175     /**
176      * Bridge between {@link EffectiveStatementWithFlags} and {@link DataSchemaNode}.
177      *
178      * @param <A> Argument type ({@link Void} if statement does not have argument.)
179      * @param <D> Class representing declared version of this statement.
180      */
181     public interface DataSchemaNodeMixin<A, D extends DeclaredStatement<A>>
182             extends DataSchemaNode, CopyableMixin<A, D>, SchemaNodeMixin<A, D>, WhenConditionMixin<A, D> {
183         @Override
184         default boolean isConfiguration() {
185             return (flags() & FlagsBuilder.CONFIGURATION) != 0;
186         }
187     }
188
189     /**
190      * Bridge between {@link EffectiveStatementWithFlags} and {@link DocumentedNode}.
191      *
192      * @param <A> Argument type ({@link Void} if statement does not have argument.)
193      * @param <D> Class representing declared version of this statement.
194      */
195     public interface DocumentedNodeMixin<A, D extends DeclaredStatement<A>> extends Mixin<A, D>, DocumentedNode {
196         /**
197          * Bridge between {@link EffectiveStatementWithFlags} and
198          * {@link org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus}.
199          *
200          * @param <A> Argument type ({@link Void} if statement does not have argument.)
201          * @param <D> Class representing declared version of this statement.
202          */
203         interface WithStatus<A, D extends DeclaredStatement<A>>
204                 extends EffectiveStatementWithFlags<A, D>, DocumentedNodeMixin<A, D>, DocumentedNode.WithStatus {
205             @Override
206             default Status getStatus() {
207                 final int status = flags() & FlagsBuilder.MASK_STATUS;
208                 switch (status) {
209                     case FlagsBuilder.STATUS_CURRENT:
210                         return Status.CURRENT;
211                     case FlagsBuilder.STATUS_DEPRECATED:
212                         return Status.DEPRECATED;
213                     case FlagsBuilder.STATUS_OBSOLETE:
214                         return Status.OBSOLETE;
215                     default:
216                         throw new IllegalStateException("Illegal status " + status);
217                 }
218             }
219         }
220
221         @Override
222         default Optional<String> getDescription() {
223             return findFirstEffectiveSubstatementArgument(DescriptionEffectiveStatement.class);
224         }
225
226         @Override
227         default Optional<String> getReference() {
228             return findFirstEffectiveSubstatementArgument(ReferenceEffectiveStatement.class);
229         }
230
231         @Override
232         default Collection<? extends UnknownSchemaNode> getUnknownSchemaNodes() {
233             return filterEffectiveStatements(UnknownSchemaNode.class);
234         }
235     }
236
237     /**
238      * Bridge between {@link EffectiveStatementWithFlags} and {@link MandatoryAware}.
239      *
240      * @param <A> Argument type ({@link Void} if statement does not have argument.)
241      * @param <D> Class representing declared version of this statement.
242      */
243     public interface MandatoryMixin<A, D extends DeclaredStatement<A>>
244             extends EffectiveStatementWithFlags<A, D>, MandatoryAware {
245         @Override
246         default boolean isMandatory() {
247             return (flags() & FlagsBuilder.MANDATORY) != 0;
248         }
249     }
250
251     /**
252      * Bridge between {@link EffectiveStatementWithFlags} and {@code presence} statement.
253      *
254      * @param <A> Argument type ({@link Void} if statement does not have argument.)
255      * @param <D> Class representing declared version of this statement.
256      */
257     public interface PresenceMixin<A, D extends DeclaredStatement<A>> extends EffectiveStatementWithFlags<A, D> {
258         default boolean presence() {
259             return (flags() & FlagsBuilder.PRESENCE) != 0;
260         }
261     }
262
263     /**
264      * Bridge between {@link EffectiveStatementWithFlags} and {@link SchemaNode}.
265      *
266      * @param <A> Argument type ({@link Void} if statement does not have argument.)
267      * @param <D> Class representing declared version of this statement.
268      */
269     public interface SchemaNodeMixin<A, D extends DeclaredStatement<A>>
270             extends DocumentedNodeMixin.WithStatus<A, D>, SchemaNode {
271         @Override
272         default QName getQName() {
273             return getPath().getLastComponent();
274         }
275     }
276
277     /**
278      * Bridge between {@link EffectiveStatementWithFlags} and {@code ordered-by} statement.
279      *
280      * @param <A> Argument type ({@link Void} if statement does not have argument.)
281      * @param <D> Class representing declared version of this statement.
282      */
283     public interface UserOrderedMixin<A, D extends DeclaredStatement<A>> extends EffectiveStatementWithFlags<A, D> {
284         default boolean userOrdered() {
285             return (flags() & FlagsBuilder.USER_ORDERED) != 0;
286         }
287     }
288
289     /**
290      * Helper used to locate the effective {@code when} statement and exposing its argument as per
291      * {@link WhenConditionAware}.
292      *
293      * @param <A> Argument type ({@link Void} if statement does not have argument.)
294      * @param <D> Class representing declared version of this statement.
295      */
296     public interface WhenConditionMixin<A, D extends DeclaredStatement<A>> extends Mixin<A, D>, WhenConditionAware {
297         @Override
298         default Optional<RevisionAwareXPath> getWhenCondition() {
299             return findFirstEffectiveSubstatementArgument(WhenEffectiveStatement.class);
300         }
301     }
302
303     /**
304      * Support interface for various mixins. Implementations are required to store 32bits worth of flags, which are
305      * globally assigned to sub-interfaces -- thus providing storage for many low-cardinality properties.
306      *
307      * @param <A> Argument type ({@link Void} if statement does not have argument.)
308      * @param <D> Class representing declared version of this statement.
309      */
310     public interface EffectiveStatementWithFlags<A, D extends DeclaredStatement<A>> extends Mixin<A, D> {
311         /**
312          * Return flags assicated with this statements. Flags can be built using {@link FlagsBuilder}.
313          *
314          * @return Flag field value (32 bits).
315          */
316         int flags();
317
318         @NonNullByDefault
319         final class FlagsBuilder implements Mutable {
320             // We still have 24 flags remaining
321             static final int STATUS_CURRENT       = 0x0001;
322             static final int STATUS_DEPRECATED    = 0x0002;
323             static final int STATUS_OBSOLETE      = 0x0003;
324             static final int MASK_STATUS          = 0x0003;
325
326             static final int CONFIGURATION        = 0x0004;
327             static final int MANDATORY            = 0x0008;
328
329             static final int AUGMENTING           = 0x0010;
330             static final int ADDED_BY_USES        = 0x0020;
331             private static final int MASK_HISTORY = 0x0030;
332
333             static final int USER_ORDERED         = 0x0040;
334             static final int PRESENCE             = 0x0080;
335
336             private int flags;
337
338             public FlagsBuilder setConfiguration(final boolean config) {
339                 if (config) {
340                     flags |= CONFIGURATION;
341                 } else {
342                     flags &= ~CONFIGURATION;
343                 }
344                 return this;
345             }
346
347             public FlagsBuilder setHistory(final CopyHistory history) {
348                 int bits;
349                 if (history.contains(CopyType.ADDED_BY_USES_AUGMENTATION)) {
350                     bits = AUGMENTING | ADDED_BY_USES;
351                 } else {
352                     bits = 0;
353                     if (history.contains(CopyType.ADDED_BY_AUGMENTATION)) {
354                         bits |= AUGMENTING;
355                     }
356                     if (history.contains(CopyType.ADDED_BY_USES)) {
357                         bits |= ADDED_BY_USES;
358                     }
359                 }
360
361                 flags = flags & ~MASK_HISTORY | bits;
362                 return this;
363             }
364
365             public FlagsBuilder setMandatory(final boolean mandatory) {
366                 if (mandatory) {
367                     flags |= MANDATORY;
368                 } else {
369                     flags &= ~MANDATORY;
370                 }
371                 return this;
372             }
373
374             public FlagsBuilder setPresence(final boolean presence) {
375                 if (presence) {
376                     flags |= PRESENCE;
377                 } else {
378                     flags &= ~PRESENCE;
379                 }
380                 return this;
381             }
382
383             public FlagsBuilder setStatus(final Status status) {
384                 final int bits;
385                 switch (status) {
386                     case CURRENT:
387                         bits = STATUS_CURRENT;
388                         break;
389                     case DEPRECATED:
390                         bits = STATUS_DEPRECATED;
391                         break;
392                     case OBSOLETE:
393                         bits = STATUS_DEPRECATED;
394                         break;
395                     default:
396                         throw new IllegalStateException("Unhandled status " + status);
397                 }
398
399                 flags = flags & ~MASK_STATUS | bits;
400                 return this;
401             }
402
403             public FlagsBuilder setUserOrdered(final boolean userOrdered) {
404                 if (userOrdered) {
405                     flags |= USER_ORDERED;
406                 } else {
407                     flags &= ~USER_ORDERED;
408                 }
409                 return this;
410             }
411
412             public int toFlags() {
413                 return flags;
414             }
415         }
416     }
417 }