Make CopyHistory implement CopyableNode
[yangtools.git] / yang / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / stmt / EffectiveStatementMixins.java
index 46f09b90f86149663056bd383de6ec7dae632bcc..3eb78f0488bd4a1d2a9dba1b1488f9ae7b061c3c 100644 (file)
@@ -8,33 +8,60 @@
 package org.opendaylight.yangtools.yang.parser.rfc7950.stmt;
 
 import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Strings;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import java.util.Collection;
-import java.util.List;
 import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.concepts.Mutable;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
+import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.AddedByUsesAware;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
+import org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition;
+import org.opendaylight.yangtools.yang.model.api.ContainerLike;
 import org.opendaylight.yangtools.yang.model.api.CopyableNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DocumentedNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.InputSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.MandatoryAware;
 import org.opendaylight.yangtools.yang.model.api.MustConstraintAware;
 import org.opendaylight.yangtools.yang.model.api.MustDefinition;
-import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.NotificationNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
+import org.opendaylight.yangtools.yang.model.api.OutputSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
 import org.opendaylight.yangtools.yang.model.api.WhenConditionAware;
 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ErrorAppTagEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ErrorMessageEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.InputEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OutputEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.StatusEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.WhenEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.EffectiveStatementMixins.EffectiveStatementWithFlags.FlagsBuilder;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory;
-import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression.QualifiedBound;
 
 /**
  * Mix-in interfaces providing services required by SchemaNode et al. These interfaces provide implementations, or
@@ -45,14 +72,23 @@ public final class EffectiveStatementMixins {
     // Marker interface requiring all mixins to be derived from EffectiveStatement.
     private interface Mixin<A, D extends DeclaredStatement<A>> extends EffectiveStatement<A, D> {
         @SuppressWarnings("unchecked")
-        default <T> Collection<T> filterEffectiveStatements(final Class<T> type) {
+        default <T> @NonNull Collection<? extends @NonNull T> filterEffectiveStatements(final Class<T> type) {
             // Yeah, this is not nice, but saves one transformation
-            return (Collection<T>) Collections2.filter(effectiveSubstatements(), type::isInstance);
+            return (Collection<? extends T>) Collections2.filter(effectiveSubstatements(), type::isInstance);
         }
+    }
 
-        default <T> List<T> filterEffectiveStatementsList(final Class<T> type) {
-            return effectiveSubstatements().stream().filter(type::isInstance).map(type::cast)
-                    .collect(ImmutableList.toImmutableList());
+    /**
+     * Bridge between {@link EffectiveStatement} and {@link AugmentationTarget}.
+     *
+     * @param <A> Argument type ({@link Void} if statement does not have argument.)
+     * @param <D> Class representing declared version of this statement.
+     */
+    public interface AugmentationTargetMixin<A, D extends DeclaredStatement<A>>
+            extends Mixin<A, D>, AugmentationTarget {
+        @Override
+        default Collection<? extends AugmentationSchemaNode> getAvailableAugmentations() {
+            return filterEffectiveStatements(AugmentationSchemaNode.class);
         }
     }
 
@@ -65,11 +101,40 @@ public final class EffectiveStatementMixins {
     public interface AddedByUsesMixin<A, D extends DeclaredStatement<A>>
             extends EffectiveStatementWithFlags<A, D>, AddedByUsesAware {
         @Override
+        @Deprecated
         default boolean isAddedByUses() {
             return (flags() & FlagsBuilder.ADDED_BY_USES) != 0;
         }
     }
 
+    /**
+     * Bridge between {@link EffectiveStatementWithFlags} and {@link ActionNodeContainer}.
+     *
+     * @param <A> Argument type ({@link Void} if statement does not have argument.)
+     * @param <D> Class representing declared version of this statement.
+     */
+    public interface ActionNodeContainerMixin<A, D extends DeclaredStatement<A>>
+            extends Mixin<A, D>, ActionNodeContainer {
+        @Override
+        default Collection<? extends ActionDefinition> getActions() {
+            return filterEffectiveStatements(ActionDefinition.class);
+        }
+    }
+
+    /**
+     * Bridge between {@link EffectiveStatementWithFlags} and {@link NotificationNodeContainer}.
+     *
+     * @param <A> Argument type ({@link Void} if statement does not have argument.)
+     * @param <D> Class representing declared version of this statement.
+     */
+    public interface NotificationNodeContainerMixin<A, D extends DeclaredStatement<A>>
+            extends Mixin<A, D>, NotificationNodeContainer {
+        @Override
+        default Collection<? extends NotificationDefinition> getNotifications() {
+            return filterEffectiveStatements(NotificationDefinition.class);
+        }
+    }
+
     /**
      * Bridge between {@link EffectiveStatementWithFlags} and {@link MustConstraintAware}.
      *
@@ -78,7 +143,7 @@ public final class EffectiveStatementMixins {
      */
     public interface MustConstraintMixin<A, D extends DeclaredStatement<A>> extends Mixin<A, D>, MustConstraintAware {
         @Override
-        default Collection<MustDefinition> getMustConstraints() {
+        default Collection<? extends @NonNull MustDefinition> getMustConstraints() {
             return filterEffectiveStatements(MustDefinition.class);
         }
     }
@@ -91,11 +156,40 @@ public final class EffectiveStatementMixins {
      */
     public interface CopyableMixin<A, D extends DeclaredStatement<A>> extends AddedByUsesMixin<A, D>, CopyableNode {
         @Override
+        @Deprecated
         default boolean isAugmenting() {
             return (flags() & FlagsBuilder.AUGMENTING) != 0;
         }
     }
 
+    /**
+     * Bridge between {@link EffectiveStatementWithFlags} and {@link DataNodeContainer}.
+     *
+     * @param <A> Argument type ({@link Void} if statement does not have argument.)
+     * @param <D> Class representing declared version of this statement.
+     */
+    public interface DataNodeContainerMixin<A, D extends DeclaredStatement<A>> extends DataNodeContainer, Mixin<A, D> {
+        @Override
+        default Collection<? extends TypeDefinition<?>> getTypeDefinitions() {
+            return filterTypeDefinitions(this);
+        }
+
+        @Override
+        default Collection<? extends DataSchemaNode> getChildNodes() {
+            return filterEffectiveStatements(DataSchemaNode.class);
+        }
+
+        @Override
+        default Collection<? extends GroupingDefinition> getGroupings() {
+            return filterEffectiveStatements(GroupingDefinition.class);
+        }
+
+        @Override
+        default Collection<? extends UsesNode> getUses() {
+            return filterEffectiveStatements(UsesNode.class);
+        }
+    }
+
     /**
      * Bridge between {@link EffectiveStatementWithFlags} and {@link DataSchemaNode}.
      *
@@ -105,8 +199,18 @@ public final class EffectiveStatementMixins {
     public interface DataSchemaNodeMixin<A, D extends DeclaredStatement<A>>
             extends DataSchemaNode, CopyableMixin<A, D>, SchemaNodeMixin<A, D>, WhenConditionMixin<A, D> {
         @Override
-        default boolean isConfiguration() {
-            return (flags() & FlagsBuilder.CONFIGURATION) != 0;
+        default Optional<Boolean> effectiveConfig() {
+            final int fl = flags() & FlagsBuilder.MASK_CONFIG;
+            switch (fl) {
+                case FlagsBuilder.CONFIG_FALSE:
+                    return Optional.of(Boolean.FALSE);
+                case FlagsBuilder.CONFIG_TRUE:
+                    return Optional.of(Boolean.TRUE);
+                case FlagsBuilder.CONFIG_UNDEF:
+                    return Optional.empty();
+                default:
+                    throw new IllegalStateException("Unhandled effective config flags " + fl);
+            }
         }
     }
 
@@ -153,8 +257,27 @@ public final class EffectiveStatementMixins {
         }
 
         @Override
-        default List<UnknownSchemaNode> getUnknownSchemaNodes() {
-            return filterEffectiveStatementsList(UnknownSchemaNode.class);
+        default Collection<? extends UnknownSchemaNode> getUnknownSchemaNodes() {
+            return filterEffectiveStatements(UnknownSchemaNode.class);
+        }
+    }
+
+    /**
+     * Bridge between {@link EffectiveStatementWithFlags} and {@link ConstraintMetaDefinition}.
+     *
+     * @param <A> Argument type ({@link Void} if statement does not have argument.)
+     * @param <D> Class representing declared version of this statement.
+     */
+    public interface ConstraintMetaDefinitionMixin<A, D extends DeclaredStatement<A>> extends DocumentedNodeMixin<A, D>,
+            ConstraintMetaDefinition {
+        @Override
+        default Optional<String> getErrorAppTag() {
+            return findFirstEffectiveSubstatementArgument(ErrorAppTagEffectiveStatement.class);
+        }
+
+        @Override
+        default Optional<String> getErrorMessage() {
+            return findFirstEffectiveSubstatementArgument(ErrorMessageEffectiveStatement.class);
         }
     }
 
@@ -172,6 +295,18 @@ public final class EffectiveStatementMixins {
         }
     }
 
+    /**
+     * Bridge between {@link EffectiveStatementWithFlags} and {@code presence} statement.
+     *
+     * @param <A> Argument type ({@link Void} if statement does not have argument.)
+     * @param <D> Class representing declared version of this statement.
+     */
+    public interface PresenceMixin<A, D extends DeclaredStatement<A>> extends EffectiveStatementWithFlags<A, D> {
+        default boolean presence() {
+            return (flags() & FlagsBuilder.PRESENCE) != 0;
+        }
+    }
+
     /**
      * Bridge between {@link EffectiveStatementWithFlags} and {@link SchemaNode}.
      *
@@ -186,6 +321,21 @@ public final class EffectiveStatementMixins {
         }
     }
 
+    /**
+     * Bridge between {@link EffectiveStatementWithFlags} and {@link UnknownSchemaNode}.
+     *
+     * @param <A> Argument type ({@link Void} if statement does not have argument.)
+     * @param <D> Class representing declared version of this statement.
+     */
+    public interface UnknownSchemaNodeMixin<A, D extends DeclaredStatement<A>>
+            extends SchemaNodeMixin<A, D>, CopyableMixin<A, D>, UnknownSchemaNode {
+
+        @Override
+        default String getNodeParameter() {
+            return Strings.nullToEmpty(getDeclared().rawArgument());
+        }
+    }
+
     /**
      * Bridge between {@link EffectiveStatementWithFlags} and {@code ordered-by} statement.
      *
@@ -207,11 +357,102 @@ public final class EffectiveStatementMixins {
      */
     public interface WhenConditionMixin<A, D extends DeclaredStatement<A>> extends Mixin<A, D>, WhenConditionAware {
         @Override
-        default Optional<RevisionAwareXPath> getWhenCondition() {
+        default Optional<QualifiedBound> getWhenCondition() {
             return findFirstEffectiveSubstatementArgument(WhenEffectiveStatement.class);
         }
     }
 
+    /**
+     * Helper bridge for operation containers ({@code input} and {@code output}).
+     *
+     * @param <D> Class representing declared version of this statement.
+     */
+    public interface OperationContainerMixin<D extends DeclaredStatement<QName>>
+            extends ContainerLike, DocumentedNodeMixin.WithStatus<QName, D>, DataNodeContainerMixin<QName, D>,
+                    MustConstraintMixin<QName, D>, WhenConditionMixin<QName, D>, AugmentationTargetMixin<QName, D>,
+                    SchemaNodeMixin<QName, D>, CopyableMixin<QName, D> {
+        @Override
+        default @NonNull QName argument() {
+            return getQName();
+        }
+
+        @Override
+        default Optional<ActionDefinition> findAction(final QName qname) {
+            return Optional.empty();
+        }
+
+        @Override
+        default Optional<NotificationDefinition> findNotification(final QName qname) {
+            return Optional.empty();
+        }
+
+        @Override
+        default Collection<? extends ActionDefinition> getActions() {
+            return ImmutableSet.of();
+        }
+
+        @Override
+        default Collection<? extends NotificationDefinition> getNotifications() {
+            return ImmutableSet.of();
+        }
+
+        @Override
+        default Optional<Boolean> effectiveConfig() {
+            return Optional.empty();
+        }
+
+        default String defaultToString() {
+            return MoreObjects.toStringHelper(this).add("path", getPath()).toString();
+        }
+    }
+
+    /**
+     * Helper bridge for {@code anydata} and {@code anyxml} opaque data.
+     *
+     * @param <D> Class representing declared version of this statement.
+     */
+    public interface OpaqueDataSchemaNodeMixin<D extends DeclaredStatement<QName>>
+            extends DerivableSchemaNode, DataSchemaNodeMixin<QName, D>, DocumentedNodeMixin.WithStatus<QName, D>,
+                    MandatoryMixin<QName, D>, MustConstraintMixin<QName, D>, WhenConditionMixin<QName, D> {
+        @Override
+        default @NonNull QName argument() {
+            return getQName();
+        }
+    }
+
+    /**
+     * Helper bridge for {@code rpc} and {@code action} operations.
+     *
+     * @param <D> Class representing declared version of this statement.
+     */
+    public interface OperationDefinitionMixin<D extends DeclaredStatement<QName>>
+            extends SchemaNodeMixin<QName, D>, OperationDefinition {
+        @Override
+        default @NonNull QName argument() {
+            return getQName();
+        }
+
+        @Override
+        default Collection<? extends @NonNull TypeDefinition<?>> getTypeDefinitions() {
+            return filterTypeDefinitions(this);
+        }
+
+        @Override
+        default Collection<? extends @NonNull GroupingDefinition> getGroupings() {
+            return filterEffectiveStatements(GroupingDefinition.class);
+        }
+
+        @Override
+        default InputSchemaNode getInput() {
+            return findAsContainer(this, InputEffectiveStatement.class, InputSchemaNode.class);
+        }
+
+        @Override
+        default OutputSchemaNode getOutput() {
+            return findAsContainer(this, OutputEffectiveStatement.class, OutputSchemaNode.class);
+        }
+    }
+
     /**
      * Support interface for various mixins. Implementations are required to store 32bits worth of flags, which are
      * globally assigned to sub-interfaces -- thus providing storage for many low-cardinality properties.
@@ -229,47 +470,42 @@ public final class EffectiveStatementMixins {
 
         @NonNullByDefault
         final class FlagsBuilder implements Mutable {
-            // We still have 25 flags remaining
+            // We still have 23 flags remaining
             static final int STATUS_CURRENT       = 0x0001;
             static final int STATUS_DEPRECATED    = 0x0002;
             static final int STATUS_OBSOLETE      = 0x0003;
             static final int MASK_STATUS          = 0x0003;
 
-            static final int CONFIGURATION        = 0x0004;
-            static final int MANDATORY            = 0x0008;
+            static final int MANDATORY            = 0x0004;
 
             static final int AUGMENTING           = 0x0010;
             static final int ADDED_BY_USES        = 0x0020;
             private static final int MASK_HISTORY = 0x0030;
 
             static final int USER_ORDERED         = 0x0040;
+            static final int PRESENCE             = 0x0080;
+
+            static final int CONFIG_UNDEF         = 0x0100;
+            static final int CONFIG_FALSE         = 0x0200;
+            static final int CONFIG_TRUE          = 0x0300;
+            static final int MASK_CONFIG          = CONFIG_TRUE;
 
             private int flags;
 
-            public FlagsBuilder setConfiguration(final boolean config) {
-                if (config) {
-                    flags |= CONFIGURATION;
+            public FlagsBuilder setConfiguration(final @Nullable Boolean config) {
+                final int fl;
+                if (config != null) {
+                    fl = config ? CONFIG_TRUE : CONFIG_FALSE;
                 } else {
-                    flags &= ~CONFIGURATION;
+                    fl = CONFIG_UNDEF;
                 }
+                flags = flags & ~MASK_CONFIG | fl;
                 return this;
             }
 
             public FlagsBuilder setHistory(final CopyHistory history) {
-                int bits;
-                if (history.contains(CopyType.ADDED_BY_USES_AUGMENTATION)) {
-                    bits = AUGMENTING | ADDED_BY_USES;
-                } else {
-                    bits = 0;
-                    if (history.contains(CopyType.ADDED_BY_AUGMENTATION)) {
-                        bits |= AUGMENTING;
-                    }
-                    if (history.contains(CopyType.ADDED_BY_USES)) {
-                        bits |= ADDED_BY_USES;
-                    }
-                }
-
-                flags = flags & ~MASK_HISTORY | bits;
+                flags = flags & ~MASK_HISTORY
+                    | (history.isAugmenting() ? AUGMENTING : 0) | (history.isAddedByUses() ? ADDED_BY_USES : 0);
                 return this;
             }
 
@@ -282,6 +518,15 @@ public final class EffectiveStatementMixins {
                 return this;
             }
 
+            public FlagsBuilder setPresence(final boolean presence) {
+                if (presence) {
+                    flags |= PRESENCE;
+                } else {
+                    flags &= ~PRESENCE;
+                }
+                return this;
+            }
+
             public FlagsBuilder setStatus(final Status status) {
                 final int bits;
                 switch (status) {
@@ -292,7 +537,7 @@ public final class EffectiveStatementMixins {
                         bits = STATUS_DEPRECATED;
                         break;
                     case OBSOLETE:
-                        bits = STATUS_DEPRECATED;
+                        bits = STATUS_OBSOLETE;
                         break;
                     default:
                         throw new IllegalStateException("Unhandled status " + status);
@@ -316,4 +561,26 @@ public final class EffectiveStatementMixins {
             }
         }
     }
+
+    private EffectiveStatementMixins() {
+    }
+
+    static <T extends ContainerLike> T findAsContainer(final EffectiveStatement<?, ?> stmt,
+            final Class<? extends EffectiveStatement<QName, ?>> type, final Class<T> target) {
+        return target.cast(stmt.findFirstEffectiveSubstatement(type).get());
+    }
+
+    static Collection<? extends @NonNull TypeDefinition<?>> filterTypeDefinitions(final Mixin<?, ?> stmt) {
+        return Collections2.transform(stmt.filterEffectiveStatements(TypedefEffectiveStatement.class),
+            TypedefEffectiveStatement::getTypeDefinition);
+    }
+
+    public static int historyAndStatusFlags(final CopyHistory history,
+            final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
+        return new FlagsBuilder()
+                .setHistory(history)
+                .setStatus(AbstractStatementSupport.findFirstArgument(substatements,
+                    StatusEffectiveStatement.class, Status.CURRENT))
+                .toFlags();
+    }
 }