Refactor ListEffectiveStatementImpl 57/86857/18
authorRobert Varga <robert.varga@pantheon.tech>
Sun, 12 Jan 2020 18:12:09 +0000 (19:12 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Tue, 14 Jan 2020 13:22:18 +0000 (14:22 +0100)
ListEffectiveStatement implementation is immensely big -- it takes
anywhere between 112 and 224 bytes (120 typical), most of which are
really only indices on substatements, which we can construct on demand.

Applying mixin + layout classes here allows us to drop the size of
these statements to 40/48 bytes typical, with 88 bytes worst case.

The effect on retained memory is ~12% memory foot print reduction,
driven by both reduction of number of ImmutableSets and reduced object
size.

JIRA: YANGTOOLS-1065
Change-Id: Iebcf16b500a874dff94bb21b6f9f59bf056fea51
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractDeclaredEffectiveStatement.java
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractSchemaEffectiveDocumentedNode.java
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/EffectiveStatementMixins.java
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/EffectiveStmtUtils.java
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/AbstractListEffectiveStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/AbstractListStatementSupport.java
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/EmptyListEffectiveStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/ListEffectiveStatementImpl.java [deleted file]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/RegularListEffectiveStatement.java [new file with mode: 0644]

index 5b9edf3206bed452f54fd7d973e189692008ad0d..95341eeb27138e454d71bc1aac39609b6f66358a 100644 (file)
@@ -7,14 +7,30 @@
  */
 package org.opendaylight.yangtools.yang.parser.rfc7950.stmt;
 
+import static com.google.common.base.Verify.verify;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import java.util.Optional;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 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.meta.IdentifierNamespace;
 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
 import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeAwareEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeAwareEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
 
 /**
  * Base stateless superclass for statements which (logically) always have an associated {@link DeclaredStatement}. This
@@ -44,6 +60,62 @@ public abstract class AbstractDeclaredEffectiveStatement<A, D extends DeclaredSt
     @Override
     public abstract @NonNull D getDeclared();
 
+    /**
+     * Base stateless superclass form {@link SchemaTreeAwareEffectiveStatement}s. It maintains the contents of schema
+     * tree namespace based of effective substatements.
+     *
+     * @param <A> Argument type ({@link Void} if statement does not have argument.)
+     * @param <D> Class representing declared version of this statement.
+     * @param <E> Class representing effective version of this statement.
+     */
+    public abstract static class WithSchemaTree<A, D extends DeclaredStatement<A>,
+            E extends SchemaTreeAwareEffectiveStatement<A, D>> extends AbstractDeclaredEffectiveStatement<A, D> {
+        @Override
+        @SuppressWarnings("unchecked")
+        protected <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends Map<K, V>> getNamespaceContents(
+                final Class<N> namespace) {
+            if (SchemaTreeAwareEffectiveStatement.Namespace.class.equals(namespace)) {
+                return Optional.of((Map<K, V>) schemaTreeNamespace());
+            }
+            return super.getNamespaceContents(namespace);
+        }
+
+        /**
+         * Indexing support for {@link DataNodeContainer#findDataChildByName(QName)}.
+         */
+        protected final Optional<DataSchemaNode> findDataSchemaNode(final QName name) {
+            // Only DataNodeContainer subclasses should be calling this method
+            verify(this instanceof DataNodeContainer);
+            final SchemaTreeEffectiveStatement<?> child = schemaTreeNamespace().get(requireNonNull(name));
+            return child instanceof DataSchemaNode ? Optional.of((DataSchemaNode) child) : Optional.empty();
+        }
+
+        protected abstract ImmutableMap<QName, SchemaTreeEffectiveStatement<?>> schemaTreeNamespace();
+    }
+
+    /**
+     * Base stateless superclass form {@link DataTreeAwareEffectiveStatement}s. It maintains the contents of data tree
+     * namespace based of effective substatements.
+     *
+     * @param <A> Argument type ({@link Void} if statement does not have argument.)
+     * @param <D> Class representing declared version of this statement.
+     * @param <E> Class representing effective version of this statement.
+     */
+    public abstract static class WithDataTree<A, D extends DeclaredStatement<A>,
+            E extends DataTreeAwareEffectiveStatement<A, D>> extends WithSchemaTree<A, D, E> {
+        @Override
+        @SuppressWarnings("unchecked")
+        protected <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends Map<K, V>> getNamespaceContents(
+                final Class<N> namespace) {
+            if (DataTreeAwareEffectiveStatement.Namespace.class.equals(namespace)) {
+                return Optional.of((Map<K, V>) dataTreeNamespace());
+            }
+            return super.getNamespaceContents(namespace);
+        }
+
+        protected abstract ImmutableMap<QName, DataTreeEffectiveStatement<?>> dataTreeNamespace();
+    }
+
     /**
      * A stateful version of {@link AbstractDeclaredEffectiveStatement}, which holds (and requires) a declared
      * statement.
@@ -84,4 +156,72 @@ public abstract class AbstractDeclaredEffectiveStatement<A, D extends DeclaredSt
             return getDeclared().argument();
         }
     }
+
+    /**
+     * Stateful version of {@link WithSchemaTree}. Schema tree namespace is eagerly instantiated (and checked).
+     *
+     * @param <A> Argument type ({@link Void} if statement does not have argument.)
+     * @param <D> Class representing declared version of this statement.
+     * @param <E> Class representing effective version of this statement.
+     */
+    public abstract static class DefaultWithSchemaTree<A, D extends DeclaredStatement<A>,
+            E extends SchemaTreeAwareEffectiveStatement<A, D>> extends WithSchemaTree<A, D, E> {
+        private final @NonNull ImmutableMap<QName, SchemaTreeEffectiveStatement<?>> schemaTree;
+        private final @NonNull D declared;
+
+        protected DefaultWithSchemaTree(final D declared, final StmtContext<?, ?, ?> ctx,
+                final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
+            this.declared = requireNonNull(declared);
+            this.schemaTree = AbstractSchemaEffectiveDocumentedNode.createSchemaTreeNamespace(
+                ctx.getStatementSourceReference(), substatements);
+        }
+
+        @Override
+        public final D getDeclared() {
+            return declared;
+        }
+
+        @Override
+        protected final ImmutableMap<QName, SchemaTreeEffectiveStatement<?>> schemaTreeNamespace() {
+            return schemaTree;
+        }
+    }
+
+    /**
+     * Stateful version of {@link WithDataTree}. Schema tree and data tree namespaces are eagerly instantiated
+     * (and checked).
+     *
+     * @param <A> Argument type ({@link Void} if statement does not have argument.)
+     * @param <D> Class representing declared version of this statement.
+     * @param <E> Class representing effective version of this statement.
+     */
+    public abstract static class DefaultWithDataTree<A, D extends DeclaredStatement<A>,
+            E extends DataTreeAwareEffectiveStatement<A, D>> extends WithDataTree<A, D, E> {
+        private final @NonNull ImmutableMap<QName, SchemaTreeEffectiveStatement<?>> schemaTree;
+        private final @NonNull ImmutableMap<QName, DataTreeEffectiveStatement<?>> dataTree;
+        private final @NonNull D declared;
+
+        protected DefaultWithDataTree(final D declared, final StmtContext<?, ?, ?> ctx,
+                final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
+            this.declared = requireNonNull(declared);
+            final StatementSourceReference ref = ctx.getStatementSourceReference();
+            this.schemaTree = AbstractSchemaEffectiveDocumentedNode.createSchemaTreeNamespace(ref, substatements);
+            this.dataTree = AbstractSchemaEffectiveDocumentedNode.createDataTreeNamespace(ref, schemaTree);
+        }
+
+        @Override
+        public final D getDeclared() {
+            return declared;
+        }
+
+        @Override
+        protected final ImmutableMap<QName, SchemaTreeEffectiveStatement<?>> schemaTreeNamespace() {
+            return schemaTree;
+        }
+
+        @Override
+        protected final ImmutableMap<QName, DataTreeEffectiveStatement<?>> dataTreeNamespace() {
+            return dataTree;
+        }
+    }
 }
index b5a9757ea953b7e936a58c61dfdb4b8df70fdbdd..79ae09fee62edd01e50e645716b1b21f0002609a 100644 (file)
@@ -14,6 +14,7 @@ import com.google.common.annotations.Beta;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import java.lang.invoke.VarHandle;
+import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Optional;
@@ -22,6 +23,7 @@ import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 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.meta.IdentifierNamespace;
 import org.opendaylight.yangtools.yang.model.api.stmt.CaseEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement;
@@ -53,37 +55,16 @@ public abstract class AbstractSchemaEffectiveDocumentedNode<A, D extends Declare
         // EffectiveStatements and SchemaNode interfaces -- which do not overlap completely where child lookups are
         // concerned. This ensures that we have SchemaTree index available for use with child lookups.
         if (this instanceof SchemaTreeAwareEffectiveStatement || this instanceof DataNodeContainer) {
-            final StatementSourceReference ref = ctx.getStatementSourceReference();
-            final Map<QName, SchemaTreeEffectiveStatement<?>> schemaChildren = new LinkedHashMap<>();
-            streamEffectiveSubstatements(SchemaTreeEffectiveStatement.class).forEach(child -> {
-                putChild(schemaChildren, child, ref, "schema");
-            });
-            schemaTreeNamespace = ImmutableMap.copyOf(schemaChildren);
-
-            if (this instanceof DataTreeAwareEffectiveStatement && !schemaTreeNamespace.isEmpty()) {
-                final Map<QName, DataTreeEffectiveStatement<?>> dataChildren = new LinkedHashMap<>();
-                boolean sameAsSchema = true;
-
-                for (SchemaTreeEffectiveStatement<?> child : schemaTreeNamespace.values()) {
-                    if (child instanceof DataTreeEffectiveStatement) {
-                        putChild(dataChildren, (DataTreeEffectiveStatement<?>) child, ref, "data");
-                    } else {
-                        sameAsSchema = false;
-                        putChoiceDataChildren(dataChildren, ref, child);
-                    }
-                }
-
-                // This is a mighty hack to lower memory usage: if we consumed all schema tree children as data nodes,
-                // the two maps are equal and hence we can share the instance.
-                dataTreeNamespace = sameAsSchema ? (ImmutableMap) schemaTreeNamespace
-                        : ImmutableMap.copyOf(dataChildren);
-            } else {
-                dataTreeNamespace = ImmutableMap.of();
-            }
+            schemaTreeNamespace = createSchemaTreeNamespace(ctx.getStatementSourceReference(),
+                effectiveSubstatements());
         } else {
-            dataTreeNamespace = ImmutableMap.of();
             schemaTreeNamespace = ImmutableMap.of();
         }
+        if (this instanceof DataTreeAwareEffectiveStatement && !schemaTreeNamespace.isEmpty()) {
+            dataTreeNamespace = createDataTreeNamespace(ctx.getStatementSourceReference(), schemaTreeNamespace);
+        } else {
+            dataTreeNamespace = ImmutableMap.of();
+        }
     }
 
     @Override
@@ -116,6 +97,34 @@ public abstract class AbstractSchemaEffectiveDocumentedNode<A, D extends Declare
         return child instanceof DataSchemaNode ? Optional.of((DataSchemaNode) child) : Optional.empty();
     }
 
+    static @NonNull ImmutableMap<QName, SchemaTreeEffectiveStatement<?>> createSchemaTreeNamespace(
+            final StatementSourceReference ref, final Collection<? extends EffectiveStatement<?, ?>> substatements) {
+        final Map<QName, SchemaTreeEffectiveStatement<?>> schemaChildren = new LinkedHashMap<>();
+        substatements.stream().filter(SchemaTreeEffectiveStatement.class::isInstance)
+            .forEach(child -> putChild(schemaChildren, (SchemaTreeEffectiveStatement) child, ref, "schema"));
+        return ImmutableMap.copyOf(schemaChildren);
+    }
+
+    static @NonNull ImmutableMap<QName, DataTreeEffectiveStatement<?>> createDataTreeNamespace(
+            final StatementSourceReference ref,
+            final ImmutableMap<QName, SchemaTreeEffectiveStatement<?>> schemaTreeNamespace) {
+        final Map<QName, DataTreeEffectiveStatement<?>> dataChildren = new LinkedHashMap<>();
+        boolean sameAsSchema = true;
+
+        for (SchemaTreeEffectiveStatement<?> child : schemaTreeNamespace.values()) {
+            if (child instanceof DataTreeEffectiveStatement) {
+                putChild(dataChildren, (DataTreeEffectiveStatement<?>) child, ref, "data");
+            } else {
+                sameAsSchema = false;
+                putChoiceDataChildren(dataChildren, ref, child);
+            }
+        }
+
+        // This is a mighty hack to lower memory usage: if we consumed all schema tree children as data nodes,
+        // the two maps are equal and hence we can share the instance.
+        return sameAsSchema ? (ImmutableMap) schemaTreeNamespace : ImmutableMap.copyOf(dataChildren);
+    }
+
     @SuppressWarnings("unchecked")
     private <T> @NonNull ImmutableSet<T> loadSet(final VarHandle vh, final @NonNull Class<T> clazz) {
         final ImmutableSet<T> computed = ImmutableSet.copyOf(allSubstatementsOfType(clazz));
index 46f09b90f86149663056bd383de6ec7dae632bcc..1584c90d12d7ac45f6cf5506d06e05857c0ed71b 100644 (file)
@@ -10,28 +10,41 @@ package org.opendaylight.yangtools.yang.parser.rfc7950.stmt;
 import com.google.common.annotations.Beta;
 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 java.util.Set;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 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.CopyableNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DocumentedNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
 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.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.NotificationNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
 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.ReferenceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.WhenEffectiveStatement;
 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory;
 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
@@ -50,9 +63,28 @@ public final class EffectiveStatementMixins {
             return (Collection<T>) Collections2.filter(effectiveSubstatements(), type::isInstance);
         }
 
+        // FIXME: YANGTOOLS-1068: eliminate this once we can return collections
         default <T> List<T> filterEffectiveStatementsList(final Class<T> type) {
-            return effectiveSubstatements().stream().filter(type::isInstance).map(type::cast)
-                    .collect(ImmutableList.toImmutableList());
+            return ImmutableList.copyOf(filterEffectiveStatements(type));
+        }
+
+        // FIXME: YANGTOOLS-1068: eliminate this once we can return collections
+        default <T> Set<T> filterEffectiveStatementsSet(final Class<T> type) {
+            return ImmutableSet.copyOf(filterEffectiveStatements(type));
+        }
+    }
+
+    /**
+     * 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 Set<AugmentationSchemaNode> getAvailableAugmentations() {
+            return filterEffectiveStatementsSet(AugmentationSchemaNode.class);
         }
     }
 
@@ -70,6 +102,34 @@ public final class EffectiveStatementMixins {
         }
     }
 
+    /**
+     * 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 Set<ActionDefinition> getActions() {
+            return filterEffectiveStatementsSet(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 Set<NotificationDefinition> getNotifications() {
+            return filterEffectiveStatementsSet(NotificationDefinition.class);
+        }
+    }
+
     /**
      * Bridge between {@link EffectiveStatementWithFlags} and {@link MustConstraintAware}.
      *
@@ -96,6 +156,37 @@ public final class EffectiveStatementMixins {
         }
     }
 
+    /**
+     * 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 Set<TypeDefinition<?>> getTypeDefinitions() {
+            // TODO: the cast here is needed to work around Java 11 javac type inference issue
+            return (Set) effectiveSubstatements().stream().filter(TypedefEffectiveStatement.class::isInstance)
+                    .map(stmt -> ((TypedefEffectiveStatement) stmt).getTypeDefinition())
+                    .collect(ImmutableSet.toImmutableSet());
+        }
+
+        @Override
+        default Collection<DataSchemaNode> getChildNodes() {
+            return filterEffectiveStatements(DataSchemaNode.class);
+        }
+
+        @Override
+        default Set<GroupingDefinition> getGroupings() {
+            return filterEffectiveStatementsSet(GroupingDefinition.class);
+        }
+
+        @Override
+        default Set<UsesNode> getUses() {
+            return filterEffectiveStatementsSet(UsesNode.class);
+        }
+    }
+
     /**
      * Bridge between {@link EffectiveStatementWithFlags} and {@link DataSchemaNode}.
      *
index 0b88f7139353ccf4e1348d5542734cbfceac1abc..d3fec27761bab12cfe021862af5318cbedff65e7 100644 (file)
@@ -5,12 +5,12 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-
 package org.opendaylight.yangtools.yang.parser.rfc7950.stmt;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Optional;
@@ -19,12 +19,15 @@ import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.YangVersion;
 import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.MaxElementsEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.MinElementsEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
@@ -165,4 +168,35 @@ public final class EffectiveStmtUtils {
         }
         return false;
     }
+
+    public static void checkUniqueGroupings(final StmtContext<?, ?, ?> ctx,
+            final Collection<? extends EffectiveStatement<?, ?>> statements) {
+        checkUniqueNodes(ctx, statements, GroupingDefinition.class);
+    }
+
+    public static void checkUniqueTypedefs(final StmtContext<?, ?, ?> ctx,
+            final Collection<? extends EffectiveStatement<?, ?>> statements) {
+        final Set<Object> typedefs = new HashSet<>();
+        for (EffectiveStatement<?, ?> stmt : statements) {
+            if (stmt instanceof TypedefEffectiveStatement
+                    && !typedefs.add(((TypedefEffectiveStatement) stmt).getTypeDefinition())) {
+                throw EffectiveStmtUtils.createNameCollisionSourceException(ctx, stmt);
+            }
+        }
+    }
+
+    public static void checkUniqueUses(final StmtContext<?, ?, ?> ctx,
+            final Collection<? extends EffectiveStatement<?, ?>> statements) {
+        checkUniqueNodes(ctx, statements, UsesNode.class);
+    }
+
+    private static void checkUniqueNodes(final StmtContext<?, ?, ?> ctx,
+            final Collection<? extends EffectiveStatement<?, ?>> statements, final Class<?> type) {
+        final Set<Object> nodes = new HashSet<>();
+        for (EffectiveStatement<?, ?> stmt : statements) {
+            if (type.isInstance(stmt) && !nodes.add(stmt)) {
+                throw EffectiveStmtUtils.createNameCollisionSourceException(ctx, stmt);
+            }
+        }
+    }
 }
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/AbstractListEffectiveStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/AbstractListEffectiveStatement.java
new file mode 100644 (file)
index 0000000..4263f58
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.rfc7950.stmt.list;
+
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UniqueConstraint;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ListEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ListStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.compat.ActionNodeContainerCompat;
+import org.opendaylight.yangtools.yang.model.api.stmt.compat.NotificationNodeContainerCompat;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractDeclaredEffectiveStatement.DefaultWithDataTree;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.EffectiveStatementMixins.ActionNodeContainerMixin;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.EffectiveStatementMixins.AugmentationTargetMixin;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.EffectiveStatementMixins.DataNodeContainerMixin;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.EffectiveStatementMixins.DataSchemaNodeMixin;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.EffectiveStatementMixins.MustConstraintMixin;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.EffectiveStatementMixins.NotificationNodeContainerMixin;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.EffectiveStatementMixins.UserOrderedMixin;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.EffectiveStatementMixins.WhenConditionMixin;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.EffectiveStmtUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+abstract class AbstractListEffectiveStatement
+        extends DefaultWithDataTree<QName, ListStatement, ListEffectiveStatement>
+        implements ListEffectiveStatement, ListSchemaNode, DerivableSchemaNode,
+            ActionNodeContainerCompat<QName, ListStatement>, NotificationNodeContainerCompat<QName, ListStatement>,
+            DataSchemaNodeMixin<QName, ListStatement>, UserOrderedMixin<QName, ListStatement>,
+            DataNodeContainerMixin<QName, ListStatement>, WhenConditionMixin<QName, ListStatement>,
+            AugmentationTargetMixin<QName, ListStatement>, NotificationNodeContainerMixin<QName, ListStatement>,
+            ActionNodeContainerMixin<QName, ListStatement>, MustConstraintMixin<QName, ListStatement> {
+    private final int flags;
+    // Variable: either a single substatement or an ImmutableList
+    private final @NonNull Object substatements;
+    private final @NonNull SchemaPath path;
+    // Variable: either a single QName or an ImmutableList
+    private final @NonNull Object keyDefinition;
+
+    AbstractListEffectiveStatement(final ListStatement declared, final SchemaPath path, final int flags,
+            final StmtContext<?, ?, ?> ctx, final ImmutableList<? extends EffectiveStatement<?, ?>> substatements,
+            final ImmutableList<QName> keyDefinition) {
+        super(declared, ctx, substatements);
+
+        EffectiveStmtUtils.checkUniqueGroupings(ctx, substatements);
+        EffectiveStmtUtils.checkUniqueTypedefs(ctx, substatements);
+        EffectiveStmtUtils.checkUniqueUses(ctx, substatements);
+
+        this.substatements = substatements.size() == 1 ? substatements.get(0) : substatements;
+        this.path = requireNonNull(path);
+        this.keyDefinition = keyDefinition.size() == 1 ? keyDefinition.get(0) : keyDefinition;
+        this.flags = flags;
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Override
+    public final ImmutableList<? extends EffectiveStatement<?, ?>> effectiveSubstatements() {
+        return (ImmutableList) listFrom(substatements, EffectiveStatement.class);
+    }
+
+    @Override
+    public final int flags() {
+        return flags;
+    }
+
+    @Override
+    public final @NonNull QName argument() {
+        return getQName();
+    }
+
+    @Override
+    public final SchemaPath getPath() {
+        return path;
+    }
+
+    @Override
+    public final boolean isUserOrdered() {
+        return userOrdered();
+    }
+
+    @Override
+    public final List<QName> getKeyDefinition() {
+        return listFrom(keyDefinition, QName.class);
+    }
+
+    @Override
+    public final Optional<DataSchemaNode> findDataChildByName(final QName name) {
+        return findDataSchemaNode(name);
+    }
+
+    @Override
+    public final Collection<UniqueConstraint> getUniqueConstraints() {
+        return effectiveSubstatements().stream()
+                .filter(UniqueConstraint.class::isInstance)
+                .map(UniqueConstraint.class::cast)
+                .collect(ImmutableList.toImmutableList());
+    }
+
+    @Override
+    public final int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(getQName());
+        result = prime * result + Objects.hashCode(getPath());
+        return result;
+    }
+
+    @Override
+    public final boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof AbstractListEffectiveStatement)) {
+            return false;
+        }
+        final AbstractListEffectiveStatement other = (AbstractListEffectiveStatement) obj;
+        return Objects.equals(getQName(), other.getQName()) && Objects.equals(getPath(), other.getPath());
+    }
+
+    @Override
+    public final String toString() {
+        return "list " + getQName().getLocalName();
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T> @NonNull ImmutableList<T> listFrom(final Object obj, final Class<T> type) {
+        if (obj instanceof ImmutableList) {
+            return (ImmutableList<T>) obj;
+        }
+        verify(type.isInstance(obj), "Unexpected list value %s", obj);
+        return ImmutableList.of(type.cast(obj));
+    }
+}
index f6d460f6f73578c894750a239ac7f697279c2c9c..91d2e7750ba9ae2c8cdacd390a4ff40f4ae0482b 100644 (file)
@@ -7,18 +7,40 @@
  */
 package org.opendaylight.yangtools.yang.parser.rfc7950.stmt.list;
 
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.KeyEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ListEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.ListStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OrderedByEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.stmt.StatusEffectiveStatement;
 import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ChildSchemaNodeNamespace;
-import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.BaseQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.EffectiveStatementMixins.EffectiveStatementWithFlags.FlagsBuilder;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.EffectiveStmtUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-abstract class AbstractListStatementSupport
-        extends AbstractQNameStatementSupport<ListStatement, EffectiveStatement<QName, ListStatement>> {
+abstract class AbstractListStatementSupport extends BaseQNameStatementSupport<ListStatement, ListEffectiveStatement> {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractListStatementSupport.class);
 
     AbstractListStatementSupport() {
         super(YangStmtMapping.LIST);
@@ -30,8 +52,7 @@ abstract class AbstractListStatementSupport
     }
 
     @Override
-    public final void onStatementAdded(
-            final Mutable<QName, ListStatement, EffectiveStatement<QName, ListStatement>> stmt) {
+    public final void onStatementAdded(final Mutable<QName, ListStatement, ListEffectiveStatement> stmt) {
         stmt.coerceParentContext().addToNs(ChildSchemaNodeNamespace.class, stmt.coerceStatementArgument(), stmt);
     }
 
@@ -41,8 +62,64 @@ abstract class AbstractListStatementSupport
     }
 
     @Override
-    public final EffectiveStatement<QName, ListStatement> createEffective(
-            final StmtContext<QName, ListStatement, EffectiveStatement<QName, ListStatement>> ctx) {
-        return new ListEffectiveStatementImpl(ctx);
+    protected final ListEffectiveStatement createEffective(
+            final StmtContext<QName, ListStatement, ListEffectiveStatement> ctx,
+            final ListStatement declared, final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
+        final StatementSourceReference ref = ctx.getStatementSourceReference();
+        final SchemaPath path = ctx.getSchemaPath().get();
+        final ListSchemaNode original = (ListSchemaNode) ctx.getOriginalCtx().map(StmtContext::buildEffective)
+                .orElse(null);
+
+        final ImmutableList<QName> keyDefinition;
+        final KeyEffectiveStatement keyStmt = findFirstStatement(substatements, KeyEffectiveStatement.class);
+        if (keyStmt != null) {
+            final List<QName> keyDefinitionInit = new ArrayList<>(keyStmt.argument().size());
+            final Set<QName> possibleLeafQNamesForKey = new HashSet<>();
+            for (final EffectiveStatement<?, ?> effectiveStatement : substatements) {
+                if (effectiveStatement instanceof LeafSchemaNode) {
+                    possibleLeafQNamesForKey.add(((LeafSchemaNode) effectiveStatement).getQName());
+                }
+            }
+            for (final SchemaNodeIdentifier key : keyStmt.argument()) {
+                final QName keyQName = key.getLastComponent();
+
+                if (!possibleLeafQNamesForKey.contains(keyQName)) {
+                    throw new InferenceException(ref, "Key '%s' misses node '%s' in list '%s'",
+                        keyStmt.getDeclared().rawArgument(), keyQName.getLocalName(), ctx.getStatementArgument());
+                }
+                keyDefinitionInit.add(keyQName);
+            }
+
+            keyDefinition = ImmutableList.copyOf(keyDefinitionInit);
+        } else {
+            keyDefinition = ImmutableList.of();
+        }
+
+        final boolean configuration = ctx.isConfiguration();
+        final int flags = new FlagsBuilder()
+                .setHistory(ctx.getCopyHistory())
+                .setStatus(findFirstArgument(substatements, StatusEffectiveStatement.class, Status.CURRENT))
+                .setConfiguration(configuration)
+                .setUserOrdered(findFirstArgument(substatements, OrderedByEffectiveStatement.class, "system")
+                    .equals("user"))
+                .toFlags();
+        if (configuration && keyDefinition.isEmpty()) {
+            LOG.info("Configuration list {} does not define any keys in violation of RFC7950 section 7.8.2. While "
+                    + " this is fine with OpenDaylight, it can cause interoperability issues with other systems "
+                    + "[at {}]", ctx.getStatementArgument(), ref);
+        }
+
+        final Optional<ElementCountConstraint> elementCountConstraint =
+                EffectiveStmtUtils.createElementCountConstraint(substatements);
+        return original == null && !elementCountConstraint.isPresent()
+                ? new EmptyListEffectiveStatement(declared, path, flags, ctx, substatements, keyDefinition)
+                        : new RegularListEffectiveStatement(declared, path, flags, ctx, substatements, keyDefinition,
+                            elementCountConstraint.orElse(null), original);
+    }
+
+    @Override
+    protected final ListEffectiveStatement createEmptyEffective(
+            final StmtContext<QName, ListStatement, ListEffectiveStatement> ctx, final ListStatement declared) {
+        return createEffective(ctx, declared, ImmutableList.of());
     }
-}
\ No newline at end of file
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/EmptyListEffectiveStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/EmptyListEffectiveStatement.java
new file mode 100644 (file)
index 0000000..ff23838
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.rfc7950.stmt.list;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ListStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class EmptyListEffectiveStatement extends AbstractListEffectiveStatement {
+    EmptyListEffectiveStatement(final ListStatement declared, final SchemaPath path, final int flags,
+            final StmtContext<?, ?, ?> ctx, final ImmutableList<? extends EffectiveStatement<?, ?>> substatements,
+            final ImmutableList<QName> keyDefinition) {
+        super(declared, path, flags, ctx, substatements, keyDefinition);
+    }
+
+    @Override
+    public Optional<? extends SchemaNode> getOriginal() {
+        return Optional.empty();
+    }
+
+    @Override
+    public Optional<ElementCountConstraint> getElementCountConstraint() {
+        return Optional.empty();
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/ListEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/ListEffectiveStatementImpl.java
deleted file mode 100644 (file)
index 867543d..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.yangtools.yang.parser.rfc7950.stmt.list;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSet.Builder;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
-import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
-import org.opendaylight.yangtools.yang.model.api.UniqueConstraint;
-import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
-import org.opendaylight.yangtools.yang.model.api.stmt.KeyEffectiveStatement;
-import org.opendaylight.yangtools.yang.model.api.stmt.ListEffectiveStatement;
-import org.opendaylight.yangtools.yang.model.api.stmt.ListStatement;
-import org.opendaylight.yangtools.yang.model.api.stmt.OrderedByEffectiveStatement;
-import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
-import org.opendaylight.yangtools.yang.model.api.stmt.compat.ActionNodeContainerCompat;
-import org.opendaylight.yangtools.yang.model.api.stmt.compat.NotificationNodeContainerCompat;
-import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveMustConstraintAwareSimpleDataNodeContainer;
-import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.EffectiveStmtUtils;
-import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
-import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-final class ListEffectiveStatementImpl
-        extends AbstractEffectiveMustConstraintAwareSimpleDataNodeContainer<ListStatement>
-        implements ListEffectiveStatement, ListSchemaNode, DerivableSchemaNode,
-            ActionNodeContainerCompat<QName, ListStatement>, NotificationNodeContainerCompat<QName, ListStatement> {
-    private static final Logger LOG = LoggerFactory.getLogger(ListEffectiveStatementImpl.class);
-    private static final String ORDER_BY_USER_KEYWORD = "user";
-
-    private final boolean userOrdered;
-    private final ImmutableList<QName> keyDefinition;
-    private final ListSchemaNode original;
-    private final @NonNull ImmutableSet<ActionDefinition> actions;
-    private final @NonNull ImmutableSet<NotificationDefinition> notifications;
-    private final @NonNull ImmutableList<UniqueConstraint> uniqueConstraints;
-    private final ElementCountConstraint elementCountConstraint;
-
-    ListEffectiveStatementImpl(
-            final StmtContext<QName, ListStatement, EffectiveStatement<QName, ListStatement>> ctx) {
-        super(ctx);
-
-        this.original = (ListSchemaNode) ctx.getOriginalCtx().map(StmtContext::buildEffective).orElse(null);
-        this.userOrdered = findFirstEffectiveSubstatementArgument(OrderedByEffectiveStatement.class)
-                .map(ORDER_BY_USER_KEYWORD::equals).orElse(Boolean.FALSE).booleanValue();
-
-        // initKeyDefinition
-        final Optional<KeyEffectiveStatement> optKeyStmt = findFirstEffectiveSubstatement(KeyEffectiveStatement.class);
-        if (optKeyStmt.isPresent()) {
-            final KeyEffectiveStatement keyStmt = optKeyStmt.get();
-            final List<QName> keyDefinitionInit = new ArrayList<>(keyStmt.argument().size());
-            final Set<QName> possibleLeafQNamesForKey = new HashSet<>();
-            for (final EffectiveStatement<?, ?> effectiveStatement : effectiveSubstatements()) {
-                if (effectiveStatement instanceof LeafSchemaNode) {
-                    possibleLeafQNamesForKey.add(((LeafSchemaNode) effectiveStatement).getQName());
-                }
-            }
-            for (final SchemaNodeIdentifier key : keyStmt.argument()) {
-                final QName keyQName = key.getLastComponent();
-
-                if (!possibleLeafQNamesForKey.contains(keyQName)) {
-                    throw new InferenceException(ctx.getStatementSourceReference(),
-                            "Key '%s' misses node '%s' in list '%s'", keyStmt.getDeclared().rawArgument(),
-                            keyQName.getLocalName(), ctx.getStatementArgument());
-                }
-                keyDefinitionInit.add(keyQName);
-            }
-
-            this.keyDefinition = ImmutableList.copyOf(keyDefinitionInit);
-        } else {
-            this.keyDefinition = ImmutableList.of();
-        }
-
-        if (isConfiguration() && keyDefinition.isEmpty()) {
-            LOG.info("Configuration list {} does not define any keys in violation of RFC7950 section 7.8.2. While "
-                    + " this is fine with OpenDaylight, it can cause interoperability issues with other systems "
-                    + "[at {}]", ctx.getStatementArgument(), ctx.getStatementSourceReference());
-        }
-
-        this.uniqueConstraints = ImmutableList.copyOf(allSubstatementsOfType(UniqueConstraint.class));
-
-        final Builder<ActionDefinition> actionsBuilder = ImmutableSet.builder();
-        final Builder<NotificationDefinition> notificationsBuilder = ImmutableSet.builder();
-        for (final EffectiveStatement<?, ?> effectiveStatement : effectiveSubstatements()) {
-            if (effectiveStatement instanceof ActionDefinition) {
-                actionsBuilder.add((ActionDefinition) effectiveStatement);
-            }
-
-            if (effectiveStatement instanceof NotificationDefinition) {
-                notificationsBuilder.add((NotificationDefinition) effectiveStatement);
-            }
-        }
-
-        this.actions = actionsBuilder.build();
-        this.notifications = notificationsBuilder.build();
-        elementCountConstraint = EffectiveStmtUtils.createElementCountConstraint(this).orElse(null);
-    }
-
-    @Override
-    public Optional<ListSchemaNode> getOriginal() {
-        return Optional.ofNullable(original);
-    }
-
-    @Override
-    public List<QName> getKeyDefinition() {
-        return keyDefinition;
-    }
-
-    @Override
-    public Set<ActionDefinition> getActions() {
-        return actions;
-    }
-
-    @Override
-    public Set<NotificationDefinition> getNotifications() {
-        return notifications;
-    }
-
-    @Override
-    public Collection<UniqueConstraint> getUniqueConstraints() {
-        return uniqueConstraints;
-    }
-
-    @Override
-    public boolean isUserOrdered() {
-        return userOrdered;
-    }
-
-    @Override
-    public Optional<ElementCountConstraint> getElementCountConstraint() {
-        return Optional.ofNullable(elementCountConstraint);
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + Objects.hashCode(getQName());
-        result = prime * result + Objects.hashCode(getPath());
-        return result;
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        final ListEffectiveStatementImpl other = (ListEffectiveStatementImpl) obj;
-        return Objects.equals(getQName(), other.getQName()) && Objects.equals(getPath(), other.getPath());
-    }
-
-    @Override
-    public String toString() {
-        return "list " + getQName().getLocalName();
-    }
-}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/RegularListEffectiveStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/RegularListEffectiveStatement.java
new file mode 100644 (file)
index 0000000..9f00ec4
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.rfc7950.stmt.list;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ListStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class RegularListEffectiveStatement extends AbstractListEffectiveStatement {
+    private final ElementCountConstraint elementCountConstraint;
+    private final ListSchemaNode original;
+
+    RegularListEffectiveStatement(final ListStatement declared, final SchemaPath path, final int flags,
+            final StmtContext<?, ?, ?> ctx, final ImmutableList<? extends EffectiveStatement<?, ?>> substatements,
+            final ImmutableList<QName> keyDefinition, final ElementCountConstraint elementCountConstraint,
+            final ListSchemaNode original) {
+        super(declared, path, flags, ctx, substatements, keyDefinition);
+        this.elementCountConstraint = elementCountConstraint;
+        this.original = original;
+    }
+
+    @Override
+    public Optional<ListSchemaNode> getOriginal() {
+        return Optional.ofNullable(original);
+    }
+
+    @Override
+    public Optional<ElementCountConstraint> getElementCountConstraint() {
+        return Optional.ofNullable(elementCountConstraint);
+    }
+}