From: Robert Varga Date: Sun, 12 Jan 2020 18:12:09 +0000 (+0100) Subject: Refactor ListEffectiveStatementImpl X-Git-Tag: v4.0.5~7 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=fb9318d77d36a605497c3d12a5fc602a0ed2265f;p=yangtools.git Refactor ListEffectiveStatementImpl 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 --- diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractDeclaredEffectiveStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractDeclaredEffectiveStatement.java index 5b9edf3206..95341eeb27 100644 --- a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractDeclaredEffectiveStatement.java +++ b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractDeclaredEffectiveStatement.java @@ -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 Argument type ({@link Void} if statement does not have argument.) + * @param Class representing declared version of this statement. + * @param Class representing effective version of this statement. + */ + public abstract static class WithSchemaTree, + E extends SchemaTreeAwareEffectiveStatement> extends AbstractDeclaredEffectiveStatement { + @Override + @SuppressWarnings("unchecked") + protected > Optional> getNamespaceContents( + final Class namespace) { + if (SchemaTreeAwareEffectiveStatement.Namespace.class.equals(namespace)) { + return Optional.of((Map) schemaTreeNamespace()); + } + return super.getNamespaceContents(namespace); + } + + /** + * Indexing support for {@link DataNodeContainer#findDataChildByName(QName)}. + */ + protected final Optional 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> schemaTreeNamespace(); + } + + /** + * Base stateless superclass form {@link DataTreeAwareEffectiveStatement}s. It maintains the contents of data tree + * namespace based of effective substatements. + * + * @param Argument type ({@link Void} if statement does not have argument.) + * @param Class representing declared version of this statement. + * @param Class representing effective version of this statement. + */ + public abstract static class WithDataTree, + E extends DataTreeAwareEffectiveStatement> extends WithSchemaTree { + @Override + @SuppressWarnings("unchecked") + protected > Optional> getNamespaceContents( + final Class namespace) { + if (DataTreeAwareEffectiveStatement.Namespace.class.equals(namespace)) { + return Optional.of((Map) dataTreeNamespace()); + } + return super.getNamespaceContents(namespace); + } + + protected abstract ImmutableMap> dataTreeNamespace(); + } + /** * A stateful version of {@link AbstractDeclaredEffectiveStatement}, which holds (and requires) a declared * statement. @@ -84,4 +156,72 @@ public abstract class AbstractDeclaredEffectiveStatement Argument type ({@link Void} if statement does not have argument.) + * @param Class representing declared version of this statement. + * @param Class representing effective version of this statement. + */ + public abstract static class DefaultWithSchemaTree, + E extends SchemaTreeAwareEffectiveStatement> extends WithSchemaTree { + private final @NonNull ImmutableMap> schemaTree; + private final @NonNull D declared; + + protected DefaultWithSchemaTree(final D declared, final StmtContext ctx, + final ImmutableList> substatements) { + this.declared = requireNonNull(declared); + this.schemaTree = AbstractSchemaEffectiveDocumentedNode.createSchemaTreeNamespace( + ctx.getStatementSourceReference(), substatements); + } + + @Override + public final D getDeclared() { + return declared; + } + + @Override + protected final ImmutableMap> schemaTreeNamespace() { + return schemaTree; + } + } + + /** + * Stateful version of {@link WithDataTree}. Schema tree and data tree namespaces are eagerly instantiated + * (and checked). + * + * @param Argument type ({@link Void} if statement does not have argument.) + * @param Class representing declared version of this statement. + * @param Class representing effective version of this statement. + */ + public abstract static class DefaultWithDataTree, + E extends DataTreeAwareEffectiveStatement> extends WithDataTree { + private final @NonNull ImmutableMap> schemaTree; + private final @NonNull ImmutableMap> dataTree; + private final @NonNull D declared; + + protected DefaultWithDataTree(final D declared, final StmtContext ctx, + final ImmutableList> 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> schemaTreeNamespace() { + return schemaTree; + } + + @Override + protected final ImmutableMap> dataTreeNamespace() { + return dataTree; + } + } } diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractSchemaEffectiveDocumentedNode.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractSchemaEffectiveDocumentedNode.java index b5a9757ea9..79ae09fee6 100644 --- a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractSchemaEffectiveDocumentedNode.java +++ b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractSchemaEffectiveDocumentedNode.java @@ -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> 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> 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> createSchemaTreeNamespace( + final StatementSourceReference ref, final Collection> substatements) { + final Map> schemaChildren = new LinkedHashMap<>(); + substatements.stream().filter(SchemaTreeEffectiveStatement.class::isInstance) + .forEach(child -> putChild(schemaChildren, (SchemaTreeEffectiveStatement) child, ref, "schema")); + return ImmutableMap.copyOf(schemaChildren); + } + + static @NonNull ImmutableMap> createDataTreeNamespace( + final StatementSourceReference ref, + final ImmutableMap> schemaTreeNamespace) { + final Map> 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 @NonNull ImmutableSet loadSet(final VarHandle vh, final @NonNull Class clazz) { final ImmutableSet computed = ImmutableSet.copyOf(allSubstatementsOfType(clazz)); diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/EffectiveStatementMixins.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/EffectiveStatementMixins.java index 46f09b90f8..1584c90d12 100644 --- a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/EffectiveStatementMixins.java +++ b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/EffectiveStatementMixins.java @@ -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) Collections2.filter(effectiveSubstatements(), type::isInstance); } + // FIXME: YANGTOOLS-1068: eliminate this once we can return collections default List filterEffectiveStatementsList(final Class 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 Set filterEffectiveStatementsSet(final Class type) { + return ImmutableSet.copyOf(filterEffectiveStatements(type)); + } + } + + /** + * Bridge between {@link EffectiveStatement} and {@link AugmentationTarget}. + * + * @param Argument type ({@link Void} if statement does not have argument.) + * @param Class representing declared version of this statement. + */ + public interface AugmentationTargetMixin> + extends Mixin, AugmentationTarget { + @Override + default Set getAvailableAugmentations() { + return filterEffectiveStatementsSet(AugmentationSchemaNode.class); } } @@ -70,6 +102,34 @@ public final class EffectiveStatementMixins { } } + /** + * Bridge between {@link EffectiveStatementWithFlags} and {@link ActionNodeContainer}. + * + * @param Argument type ({@link Void} if statement does not have argument.) + * @param Class representing declared version of this statement. + */ + public interface ActionNodeContainerMixin> + extends Mixin, ActionNodeContainer { + @Override + default Set getActions() { + return filterEffectiveStatementsSet(ActionDefinition.class); + } + } + + /** + * Bridge between {@link EffectiveStatementWithFlags} and {@link NotificationNodeContainer}. + * + * @param Argument type ({@link Void} if statement does not have argument.) + * @param Class representing declared version of this statement. + */ + public interface NotificationNodeContainerMixin> + extends Mixin, NotificationNodeContainer { + @Override + default Set 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 Argument type ({@link Void} if statement does not have argument.) + * @param Class representing declared version of this statement. + */ + public interface DataNodeContainerMixin> extends DataNodeContainer, Mixin { + @Override + default Set> 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 getChildNodes() { + return filterEffectiveStatements(DataSchemaNode.class); + } + + @Override + default Set getGroupings() { + return filterEffectiveStatementsSet(GroupingDefinition.class); + } + + @Override + default Set getUses() { + return filterEffectiveStatementsSet(UsesNode.class); + } + } + /** * Bridge between {@link EffectiveStatementWithFlags} and {@link DataSchemaNode}. * diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/EffectiveStmtUtils.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/EffectiveStmtUtils.java index 0b88f71393..d3fec27761 100644 --- a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/EffectiveStmtUtils.java +++ b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/EffectiveStmtUtils.java @@ -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> statements) { + checkUniqueNodes(ctx, statements, GroupingDefinition.class); + } + + public static void checkUniqueTypedefs(final StmtContext ctx, + final Collection> statements) { + final Set 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> statements) { + checkUniqueNodes(ctx, statements, UsesNode.class); + } + + private static void checkUniqueNodes(final StmtContext ctx, + final Collection> statements, final Class type) { + final Set 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 index 0000000000..4263f58a27 --- /dev/null +++ b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/AbstractListEffectiveStatement.java @@ -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 + implements ListEffectiveStatement, ListSchemaNode, DerivableSchemaNode, + ActionNodeContainerCompat, NotificationNodeContainerCompat, + DataSchemaNodeMixin, UserOrderedMixin, + DataNodeContainerMixin, WhenConditionMixin, + AugmentationTargetMixin, NotificationNodeContainerMixin, + ActionNodeContainerMixin, MustConstraintMixin { + 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> substatements, + final ImmutableList 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> 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 getKeyDefinition() { + return listFrom(keyDefinition, QName.class); + } + + @Override + public final Optional findDataChildByName(final QName name) { + return findDataSchemaNode(name); + } + + @Override + public final Collection 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 @NonNull ImmutableList listFrom(final Object obj, final Class type) { + if (obj instanceof ImmutableList) { + return (ImmutableList) obj; + } + verify(type.isInstance(obj), "Unexpected list value %s", obj); + return ImmutableList.of(type.cast(obj)); + } +} diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/AbstractListStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/AbstractListStatementSupport.java index f6d460f6f7..91d2e7750b 100644 --- a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/AbstractListStatementSupport.java +++ b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/AbstractListStatementSupport.java @@ -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> { +abstract class AbstractListStatementSupport extends BaseQNameStatementSupport { + 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> stmt) { + public final void onStatementAdded(final Mutable stmt) { stmt.coerceParentContext().addToNs(ChildSchemaNodeNamespace.class, stmt.coerceStatementArgument(), stmt); } @@ -41,8 +62,64 @@ abstract class AbstractListStatementSupport } @Override - public final EffectiveStatement createEffective( - final StmtContext> ctx) { - return new ListEffectiveStatementImpl(ctx); + protected final ListEffectiveStatement createEffective( + final StmtContext ctx, + final ListStatement declared, final ImmutableList> 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 keyDefinition; + final KeyEffectiveStatement keyStmt = findFirstStatement(substatements, KeyEffectiveStatement.class); + if (keyStmt != null) { + final List keyDefinitionInit = new ArrayList<>(keyStmt.argument().size()); + final Set 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 = + 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 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 index 0000000000..ff23838129 --- /dev/null +++ b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/EmptyListEffectiveStatement.java @@ -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> substatements, + final ImmutableList keyDefinition) { + super(declared, path, flags, ctx, substatements, keyDefinition); + } + + @Override + public Optional getOriginal() { + return Optional.empty(); + } + + @Override + public Optional 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 index 867543d425..0000000000 --- a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/ListEffectiveStatementImpl.java +++ /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 - implements ListEffectiveStatement, ListSchemaNode, DerivableSchemaNode, - ActionNodeContainerCompat, NotificationNodeContainerCompat { - 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 keyDefinition; - private final ListSchemaNode original; - private final @NonNull ImmutableSet actions; - private final @NonNull ImmutableSet notifications; - private final @NonNull ImmutableList uniqueConstraints; - private final ElementCountConstraint elementCountConstraint; - - ListEffectiveStatementImpl( - final StmtContext> 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 optKeyStmt = findFirstEffectiveSubstatement(KeyEffectiveStatement.class); - if (optKeyStmt.isPresent()) { - final KeyEffectiveStatement keyStmt = optKeyStmt.get(); - final List keyDefinitionInit = new ArrayList<>(keyStmt.argument().size()); - final Set 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 actionsBuilder = ImmutableSet.builder(); - final Builder 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 getOriginal() { - return Optional.ofNullable(original); - } - - @Override - public List getKeyDefinition() { - return keyDefinition; - } - - @Override - public Set getActions() { - return actions; - } - - @Override - public Set getNotifications() { - return notifications; - } - - @Override - public Collection getUniqueConstraints() { - return uniqueConstraints; - } - - @Override - public boolean isUserOrdered() { - return userOrdered; - } - - @Override - public Optional 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 index 0000000000..9f00ec4832 --- /dev/null +++ b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/RegularListEffectiveStatement.java @@ -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> substatements, + final ImmutableList keyDefinition, final ElementCountConstraint elementCountConstraint, + final ListSchemaNode original) { + super(declared, path, flags, ctx, substatements, keyDefinition); + this.elementCountConstraint = elementCountConstraint; + this.original = original; + } + + @Override + public Optional getOriginal() { + return Optional.ofNullable(original); + } + + @Override + public Optional getElementCountConstraint() { + return Optional.ofNullable(elementCountConstraint); + } +}