Refactor Leaf(List)EffectiveStatementImpl
[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 com.google.common.collect.ImmutableList;
13 import java.util.Collection;
14 import java.util.List;
15 import java.util.Optional;
16 import org.eclipse.jdt.annotation.NonNullByDefault;
17 import org.opendaylight.yangtools.concepts.Mutable;
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.model.api.AddedByUsesAware;
20 import org.opendaylight.yangtools.yang.model.api.CopyableNode;
21 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
22 import org.opendaylight.yangtools.yang.model.api.DocumentedNode;
23 import org.opendaylight.yangtools.yang.model.api.MandatoryAware;
24 import org.opendaylight.yangtools.yang.model.api.MustConstraintAware;
25 import org.opendaylight.yangtools.yang.model.api.MustDefinition;
26 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
27 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.Status;
29 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.WhenConditionAware;
31 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
32 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
33 import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
34 import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceEffectiveStatement;
35 import org.opendaylight.yangtools.yang.model.api.stmt.WhenEffectiveStatement;
36 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory;
37 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
38
39 /**
40  * Mix-in interfaces providing services required by SchemaNode et al. These interfaces provide implementations, or
41  * implementation helpers based on default methods, so the correct behavior can be logically centralized.
42  */
43 @Beta
44 public final class EffectiveStatementMixins {
45     // Marker interface requiring all mixins to be derived from EffectiveStatement.
46     private interface Mixin<A, D extends DeclaredStatement<A>> extends EffectiveStatement<A, D> {
47         @SuppressWarnings("unchecked")
48         default <T> Collection<T> filterEffectiveStatements(final Class<T> type) {
49             // Yeah, this is not nice, but saves one transformation
50             return (Collection<T>) Collections2.filter(effectiveSubstatements(), type::isInstance);
51         }
52
53         default <T> List<T> filterEffectiveStatementsList(final Class<T> type) {
54             return effectiveSubstatements().stream().filter(type::isInstance).map(type::cast)
55                     .collect(ImmutableList.toImmutableList());
56         }
57     }
58
59     /**
60      * Bridge between {@link EffectiveStatementWithFlags} and {@link AddedByUsesAware}.
61      *
62      * @param <A> Argument type ({@link Void} if statement does not have argument.)
63      * @param <D> Class representing declared version of this statement.
64      */
65     public interface AddedByUsesMixin<A, D extends DeclaredStatement<A>>
66             extends EffectiveStatementWithFlags<A, D>, AddedByUsesAware {
67         @Override
68         default boolean isAddedByUses() {
69             return (flags() & FlagsBuilder.ADDED_BY_USES) != 0;
70         }
71     }
72
73     /**
74      * Bridge between {@link EffectiveStatementWithFlags} and {@link MustConstraintAware}.
75      *
76      * @param <A> Argument type ({@link Void} if statement does not have argument.)
77      * @param <D> Class representing declared version of this statement.
78      */
79     public interface MustConstraintMixin<A, D extends DeclaredStatement<A>> extends Mixin<A, D>, MustConstraintAware {
80         @Override
81         default Collection<MustDefinition> getMustConstraints() {
82             return filterEffectiveStatements(MustDefinition.class);
83         }
84     }
85
86     /**
87      * Bridge between {@link EffectiveStatementWithFlags} and {@link CopyableNode}.
88      *
89      * @param <A> Argument type ({@link Void} if statement does not have argument.)
90      * @param <D> Class representing declared version of this statement.
91      */
92     public interface CopyableMixin<A, D extends DeclaredStatement<A>> extends AddedByUsesMixin<A, D>, CopyableNode {
93         @Override
94         default boolean isAugmenting() {
95             return (flags() & FlagsBuilder.AUGMENTING) != 0;
96         }
97     }
98
99     /**
100      * Bridge between {@link EffectiveStatementWithFlags} and {@link DataSchemaNode}.
101      *
102      * @param <A> Argument type ({@link Void} if statement does not have argument.)
103      * @param <D> Class representing declared version of this statement.
104      */
105     public interface DataSchemaNodeMixin<A, D extends DeclaredStatement<A>>
106             extends DataSchemaNode, CopyableMixin<A, D>, SchemaNodeMixin<A, D>, WhenConditionMixin<A, D> {
107         @Override
108         default boolean isConfiguration() {
109             return (flags() & FlagsBuilder.CONFIGURATION) != 0;
110         }
111     }
112
113     /**
114      * Bridge between {@link EffectiveStatementWithFlags} and {@link DocumentedNode}.
115      *
116      * @param <A> Argument type ({@link Void} if statement does not have argument.)
117      * @param <D> Class representing declared version of this statement.
118      */
119     public interface DocumentedNodeMixin<A, D extends DeclaredStatement<A>> extends Mixin<A, D>, DocumentedNode {
120         /**
121          * Bridge between {@link EffectiveStatementWithFlags} and
122          * {@link org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus}.
123          *
124          * @param <A> Argument type ({@link Void} if statement does not have argument.)
125          * @param <D> Class representing declared version of this statement.
126          */
127         interface WithStatus<A, D extends DeclaredStatement<A>>
128                 extends EffectiveStatementWithFlags<A, D>, DocumentedNodeMixin<A, D>, DocumentedNode.WithStatus {
129             @Override
130             default Status getStatus() {
131                 final int status = flags() & FlagsBuilder.MASK_STATUS;
132                 switch (status) {
133                     case FlagsBuilder.STATUS_CURRENT:
134                         return Status.CURRENT;
135                     case FlagsBuilder.STATUS_DEPRECATED:
136                         return Status.DEPRECATED;
137                     case FlagsBuilder.STATUS_OBSOLETE:
138                         return Status.OBSOLETE;
139                     default:
140                         throw new IllegalStateException("Illegal status " + status);
141                 }
142             }
143         }
144
145         @Override
146         default Optional<String> getDescription() {
147             return findFirstEffectiveSubstatementArgument(DescriptionEffectiveStatement.class);
148         }
149
150         @Override
151         default Optional<String> getReference() {
152             return findFirstEffectiveSubstatementArgument(ReferenceEffectiveStatement.class);
153         }
154
155         @Override
156         default List<UnknownSchemaNode> getUnknownSchemaNodes() {
157             return filterEffectiveStatementsList(UnknownSchemaNode.class);
158         }
159     }
160
161     /**
162      * Bridge between {@link EffectiveStatementWithFlags} and {@link MandatoryAware}.
163      *
164      * @param <A> Argument type ({@link Void} if statement does not have argument.)
165      * @param <D> Class representing declared version of this statement.
166      */
167     public interface MandatoryMixin<A, D extends DeclaredStatement<A>>
168             extends EffectiveStatementWithFlags<A, D>, MandatoryAware {
169         @Override
170         default boolean isMandatory() {
171             return (flags() & FlagsBuilder.MANDATORY) != 0;
172         }
173     }
174
175     /**
176      * Bridge between {@link EffectiveStatementWithFlags} and {@link SchemaNode}.
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 SchemaNodeMixin<A, D extends DeclaredStatement<A>>
182             extends DocumentedNodeMixin.WithStatus<A, D>, SchemaNode {
183         @Override
184         default QName getQName() {
185             return getPath().getLastComponent();
186         }
187     }
188
189     /**
190      * Bridge between {@link EffectiveStatementWithFlags} and {@code ordered-by} statement.
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 UserOrderedMixin<A, D extends DeclaredStatement<A>> extends EffectiveStatementWithFlags<A, D> {
196         default boolean userOrdered() {
197             return (flags() & FlagsBuilder.USER_ORDERED) != 0;
198         }
199     }
200
201     /**
202      * Helper used to locate the effective {@code when} statement and exposing its argument as per
203      * {@link WhenConditionAware}.
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     public interface WhenConditionMixin<A, D extends DeclaredStatement<A>> extends Mixin<A, D>, WhenConditionAware {
209         @Override
210         default Optional<RevisionAwareXPath> getWhenCondition() {
211             return findFirstEffectiveSubstatementArgument(WhenEffectiveStatement.class);
212         }
213     }
214
215     /**
216      * Support interface for various mixins. Implementations are required to store 32bits worth of flags, which are
217      * globally assigned to sub-interfaces -- thus providing storage for many low-cardinality properties.
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     public interface EffectiveStatementWithFlags<A, D extends DeclaredStatement<A>> extends Mixin<A, D> {
223         /**
224          * Return flags assicated with this statements. Flags can be built using {@link FlagsBuilder}.
225          *
226          * @return Flag field value (32 bits).
227          */
228         int flags();
229
230         @NonNullByDefault
231         final class FlagsBuilder implements Mutable {
232             // We still have 25 flags remaining
233             static final int STATUS_CURRENT       = 0x0001;
234             static final int STATUS_DEPRECATED    = 0x0002;
235             static final int STATUS_OBSOLETE      = 0x0003;
236             static final int MASK_STATUS          = 0x0003;
237
238             static final int CONFIGURATION        = 0x0004;
239             static final int MANDATORY            = 0x0008;
240
241             static final int AUGMENTING           = 0x0010;
242             static final int ADDED_BY_USES        = 0x0020;
243             private static final int MASK_HISTORY = 0x0030;
244
245             static final int USER_ORDERED         = 0x0040;
246
247             private int flags;
248
249             public FlagsBuilder setConfiguration(final boolean config) {
250                 if (config) {
251                     flags |= CONFIGURATION;
252                 } else {
253                     flags &= ~CONFIGURATION;
254                 }
255                 return this;
256             }
257
258             public FlagsBuilder setHistory(final CopyHistory history) {
259                 int bits;
260                 if (history.contains(CopyType.ADDED_BY_USES_AUGMENTATION)) {
261                     bits = AUGMENTING | ADDED_BY_USES;
262                 } else {
263                     bits = 0;
264                     if (history.contains(CopyType.ADDED_BY_AUGMENTATION)) {
265                         bits |= AUGMENTING;
266                     }
267                     if (history.contains(CopyType.ADDED_BY_USES)) {
268                         bits |= ADDED_BY_USES;
269                     }
270                 }
271
272                 flags = flags & ~MASK_HISTORY | bits;
273                 return this;
274             }
275
276             public FlagsBuilder setMandatory(final boolean mandatory) {
277                 if (mandatory) {
278                     flags |= MANDATORY;
279                 } else {
280                     flags &= ~MANDATORY;
281                 }
282                 return this;
283             }
284
285             public FlagsBuilder setStatus(final Status status) {
286                 final int bits;
287                 switch (status) {
288                     case CURRENT:
289                         bits = STATUS_CURRENT;
290                         break;
291                     case DEPRECATED:
292                         bits = STATUS_DEPRECATED;
293                         break;
294                     case OBSOLETE:
295                         bits = STATUS_DEPRECATED;
296                         break;
297                     default:
298                         throw new IllegalStateException("Unhandled status " + status);
299                 }
300
301                 flags = flags & ~MASK_STATUS | bits;
302                 return this;
303             }
304
305             public FlagsBuilder setUserOrdered(final boolean userOrdered) {
306                 if (userOrdered) {
307                     flags |= USER_ORDERED;
308                 } else {
309                     flags &= ~USER_ORDERED;
310                 }
311                 return this;
312             }
313
314             public int toFlags() {
315                 return flags;
316             }
317         }
318     }
319 }