2 * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.parser.rfc7950.stmt;
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;
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.DocumentedNode;
33 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
34 import org.opendaylight.yangtools.yang.model.api.MandatoryAware;
35 import org.opendaylight.yangtools.yang.model.api.MustConstraintAware;
36 import org.opendaylight.yangtools.yang.model.api.MustDefinition;
37 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
38 import org.opendaylight.yangtools.yang.model.api.NotificationNodeContainer;
39 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
40 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.Status;
42 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
43 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.UsesNode;
45 import org.opendaylight.yangtools.yang.model.api.WhenConditionAware;
46 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
47 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
48 import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
49 import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceEffectiveStatement;
50 import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
51 import org.opendaylight.yangtools.yang.model.api.stmt.WhenEffectiveStatement;
52 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory;
53 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
56 * Mix-in interfaces providing services required by SchemaNode et al. These interfaces provide implementations, or
57 * implementation helpers based on default methods, so the correct behavior can be logically centralized.
60 public final class EffectiveStatementMixins {
61 // Marker interface requiring all mixins to be derived from EffectiveStatement.
62 private interface Mixin<A, D extends DeclaredStatement<A>> extends EffectiveStatement<A, D> {
63 @SuppressWarnings("unchecked")
64 default <T> Collection<T> filterEffectiveStatements(final Class<T> type) {
65 // Yeah, this is not nice, but saves one transformation
66 return (Collection<T>) Collections2.filter(effectiveSubstatements(), type::isInstance);
69 // FIXME: YANGTOOLS-1068: eliminate this once we can return collections
70 default <T> List<T> filterEffectiveStatementsList(final Class<T> type) {
71 return ImmutableList.copyOf(filterEffectiveStatements(type));
74 // FIXME: YANGTOOLS-1068: eliminate this once we can return collections
75 default <T> Set<T> filterEffectiveStatementsSet(final Class<T> type) {
76 return ImmutableSet.copyOf(filterEffectiveStatements(type));
81 * Bridge between {@link EffectiveStatement} and {@link AugmentationTarget}.
83 * @param <A> Argument type ({@link Void} if statement does not have argument.)
84 * @param <D> Class representing declared version of this statement.
86 public interface AugmentationTargetMixin<A, D extends DeclaredStatement<A>>
87 extends Mixin<A, D>, AugmentationTarget {
89 default Set<AugmentationSchemaNode> getAvailableAugmentations() {
90 return filterEffectiveStatementsSet(AugmentationSchemaNode.class);
95 * Bridge between {@link EffectiveStatementWithFlags} and {@link AddedByUsesAware}.
97 * @param <A> Argument type ({@link Void} if statement does not have argument.)
98 * @param <D> Class representing declared version of this statement.
100 public interface AddedByUsesMixin<A, D extends DeclaredStatement<A>>
101 extends EffectiveStatementWithFlags<A, D>, AddedByUsesAware {
103 default boolean isAddedByUses() {
104 return (flags() & FlagsBuilder.ADDED_BY_USES) != 0;
109 * Bridge between {@link EffectiveStatementWithFlags} and {@link ActionNodeContainer}.
111 * @param <A> Argument type ({@link Void} if statement does not have argument.)
112 * @param <D> Class representing declared version of this statement.
114 public interface ActionNodeContainerMixin<A, D extends DeclaredStatement<A>>
115 extends Mixin<A, D>, ActionNodeContainer {
117 default Set<ActionDefinition> getActions() {
118 return filterEffectiveStatementsSet(ActionDefinition.class);
123 * Bridge between {@link EffectiveStatementWithFlags} and {@link NotificationNodeContainer}.
125 * @param <A> Argument type ({@link Void} if statement does not have argument.)
126 * @param <D> Class representing declared version of this statement.
128 public interface NotificationNodeContainerMixin<A, D extends DeclaredStatement<A>>
129 extends Mixin<A, D>, NotificationNodeContainer {
131 default Set<NotificationDefinition> getNotifications() {
132 return filterEffectiveStatementsSet(NotificationDefinition.class);
137 * Bridge between {@link EffectiveStatementWithFlags} and {@link MustConstraintAware}.
139 * @param <A> Argument type ({@link Void} if statement does not have argument.)
140 * @param <D> Class representing declared version of this statement.
142 public interface MustConstraintMixin<A, D extends DeclaredStatement<A>> extends Mixin<A, D>, MustConstraintAware {
144 default Collection<MustDefinition> getMustConstraints() {
145 return filterEffectiveStatements(MustDefinition.class);
150 * Bridge between {@link EffectiveStatementWithFlags} and {@link CopyableNode}.
152 * @param <A> Argument type ({@link Void} if statement does not have argument.)
153 * @param <D> Class representing declared version of this statement.
155 public interface CopyableMixin<A, D extends DeclaredStatement<A>> extends AddedByUsesMixin<A, D>, CopyableNode {
157 default boolean isAugmenting() {
158 return (flags() & FlagsBuilder.AUGMENTING) != 0;
163 * Bridge between {@link EffectiveStatementWithFlags} and {@link DataNodeContainer}.
165 * @param <A> Argument type ({@link Void} if statement does not have argument.)
166 * @param <D> Class representing declared version of this statement.
168 public interface DataNodeContainerMixin<A, D extends DeclaredStatement<A>> extends DataNodeContainer, Mixin<A, D> {
170 default Set<TypeDefinition<?>> getTypeDefinitions() {
171 // TODO: the cast here is needed to work around Java 11 javac type inference issue
172 return (Set) effectiveSubstatements().stream().filter(TypedefEffectiveStatement.class::isInstance)
173 .map(stmt -> ((TypedefEffectiveStatement) stmt).getTypeDefinition())
174 .collect(ImmutableSet.toImmutableSet());
178 default Collection<DataSchemaNode> getChildNodes() {
179 return filterEffectiveStatements(DataSchemaNode.class);
183 default Set<GroupingDefinition> getGroupings() {
184 return filterEffectiveStatementsSet(GroupingDefinition.class);
188 default Set<UsesNode> getUses() {
189 return filterEffectiveStatementsSet(UsesNode.class);
194 * Bridge between {@link EffectiveStatementWithFlags} and {@link DataSchemaNode}.
196 * @param <A> Argument type ({@link Void} if statement does not have argument.)
197 * @param <D> Class representing declared version of this statement.
199 public interface DataSchemaNodeMixin<A, D extends DeclaredStatement<A>>
200 extends DataSchemaNode, CopyableMixin<A, D>, SchemaNodeMixin<A, D>, WhenConditionMixin<A, D> {
202 default boolean isConfiguration() {
203 return (flags() & FlagsBuilder.CONFIGURATION) != 0;
208 * Bridge between {@link EffectiveStatementWithFlags} and {@link DocumentedNode}.
210 * @param <A> Argument type ({@link Void} if statement does not have argument.)
211 * @param <D> Class representing declared version of this statement.
213 public interface DocumentedNodeMixin<A, D extends DeclaredStatement<A>> extends Mixin<A, D>, DocumentedNode {
215 * Bridge between {@link EffectiveStatementWithFlags} and
216 * {@link org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus}.
218 * @param <A> Argument type ({@link Void} if statement does not have argument.)
219 * @param <D> Class representing declared version of this statement.
221 interface WithStatus<A, D extends DeclaredStatement<A>>
222 extends EffectiveStatementWithFlags<A, D>, DocumentedNodeMixin<A, D>, DocumentedNode.WithStatus {
224 default Status getStatus() {
225 final int status = flags() & FlagsBuilder.MASK_STATUS;
227 case FlagsBuilder.STATUS_CURRENT:
228 return Status.CURRENT;
229 case FlagsBuilder.STATUS_DEPRECATED:
230 return Status.DEPRECATED;
231 case FlagsBuilder.STATUS_OBSOLETE:
232 return Status.OBSOLETE;
234 throw new IllegalStateException("Illegal status " + status);
240 default Optional<String> getDescription() {
241 return findFirstEffectiveSubstatementArgument(DescriptionEffectiveStatement.class);
245 default Optional<String> getReference() {
246 return findFirstEffectiveSubstatementArgument(ReferenceEffectiveStatement.class);
250 default List<UnknownSchemaNode> getUnknownSchemaNodes() {
251 return filterEffectiveStatementsList(UnknownSchemaNode.class);
256 * Bridge between {@link EffectiveStatementWithFlags} and {@link MandatoryAware}.
258 * @param <A> Argument type ({@link Void} if statement does not have argument.)
259 * @param <D> Class representing declared version of this statement.
261 public interface MandatoryMixin<A, D extends DeclaredStatement<A>>
262 extends EffectiveStatementWithFlags<A, D>, MandatoryAware {
264 default boolean isMandatory() {
265 return (flags() & FlagsBuilder.MANDATORY) != 0;
270 * Bridge between {@link EffectiveStatementWithFlags} and {@code presence} statement.
272 * @param <A> Argument type ({@link Void} if statement does not have argument.)
273 * @param <D> Class representing declared version of this statement.
275 public interface PresenceMixin<A, D extends DeclaredStatement<A>> extends EffectiveStatementWithFlags<A, D> {
276 default boolean presence() {
277 return (flags() & FlagsBuilder.PRESENCE) != 0;
282 * Bridge between {@link EffectiveStatementWithFlags} and {@link SchemaNode}.
284 * @param <A> Argument type ({@link Void} if statement does not have argument.)
285 * @param <D> Class representing declared version of this statement.
287 public interface SchemaNodeMixin<A, D extends DeclaredStatement<A>>
288 extends DocumentedNodeMixin.WithStatus<A, D>, SchemaNode {
290 default QName getQName() {
291 return getPath().getLastComponent();
296 * Bridge between {@link EffectiveStatementWithFlags} and {@code ordered-by} statement.
298 * @param <A> Argument type ({@link Void} if statement does not have argument.)
299 * @param <D> Class representing declared version of this statement.
301 public interface UserOrderedMixin<A, D extends DeclaredStatement<A>> extends EffectiveStatementWithFlags<A, D> {
302 default boolean userOrdered() {
303 return (flags() & FlagsBuilder.USER_ORDERED) != 0;
308 * Helper used to locate the effective {@code when} statement and exposing its argument as per
309 * {@link WhenConditionAware}.
311 * @param <A> Argument type ({@link Void} if statement does not have argument.)
312 * @param <D> Class representing declared version of this statement.
314 public interface WhenConditionMixin<A, D extends DeclaredStatement<A>> extends Mixin<A, D>, WhenConditionAware {
316 default Optional<RevisionAwareXPath> getWhenCondition() {
317 return findFirstEffectiveSubstatementArgument(WhenEffectiveStatement.class);
322 * Helper bridge for operation containers ({@code input} and {@code output}).
324 * @param <D> Class representing declared version of this statement.
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> {
332 default @NonNull QName argument() {
333 return getPath().getLastComponent();
337 default QName getQName() {
342 default Optional<ActionDefinition> findAction(final QName qname) {
343 return Optional.empty();
347 default Optional<NotificationDefinition> findNotification(final QName qname) {
348 return Optional.empty();
352 default Set<ActionDefinition> getActions() {
353 return ImmutableSet.of();
357 default Set<NotificationDefinition> getNotifications() {
358 return ImmutableSet.of();
362 default boolean isConfiguration() {
367 default boolean isPresenceContainer() {
368 // FIXME: this should not really be here
372 default String defaultToString() {
373 return MoreObjects.toStringHelper(this).add("path", getPath()).toString();
378 * Support interface for various mixins. Implementations are required to store 32bits worth of flags, which are
379 * globally assigned to sub-interfaces -- thus providing storage for many low-cardinality properties.
381 * @param <A> Argument type ({@link Void} if statement does not have argument.)
382 * @param <D> Class representing declared version of this statement.
384 public interface EffectiveStatementWithFlags<A, D extends DeclaredStatement<A>> extends Mixin<A, D> {
386 * Return flags assicated with this statements. Flags can be built using {@link FlagsBuilder}.
388 * @return Flag field value (32 bits).
393 final class FlagsBuilder implements Mutable {
394 // We still have 24 flags remaining
395 static final int STATUS_CURRENT = 0x0001;
396 static final int STATUS_DEPRECATED = 0x0002;
397 static final int STATUS_OBSOLETE = 0x0003;
398 static final int MASK_STATUS = 0x0003;
400 static final int CONFIGURATION = 0x0004;
401 static final int MANDATORY = 0x0008;
403 static final int AUGMENTING = 0x0010;
404 static final int ADDED_BY_USES = 0x0020;
405 private static final int MASK_HISTORY = 0x0030;
407 static final int USER_ORDERED = 0x0040;
408 static final int PRESENCE = 0x0080;
412 public FlagsBuilder setConfiguration(final boolean config) {
414 flags |= CONFIGURATION;
416 flags &= ~CONFIGURATION;
421 public FlagsBuilder setHistory(final CopyHistory history) {
423 if (history.contains(CopyType.ADDED_BY_USES_AUGMENTATION)) {
424 bits = AUGMENTING | ADDED_BY_USES;
427 if (history.contains(CopyType.ADDED_BY_AUGMENTATION)) {
430 if (history.contains(CopyType.ADDED_BY_USES)) {
431 bits |= ADDED_BY_USES;
435 flags = flags & ~MASK_HISTORY | bits;
439 public FlagsBuilder setMandatory(final boolean mandatory) {
448 public FlagsBuilder setPresence(final boolean presence) {
457 public FlagsBuilder setStatus(final Status status) {
461 bits = STATUS_CURRENT;
464 bits = STATUS_DEPRECATED;
467 bits = STATUS_OBSOLETE;
470 throw new IllegalStateException("Unhandled status " + status);
473 flags = flags & ~MASK_STATUS | bits;
477 public FlagsBuilder setUserOrdered(final boolean userOrdered) {
479 flags |= USER_ORDERED;
481 flags &= ~USER_ORDERED;
486 public int toFlags() {