From b0ee110abbdefb8c84e032ae4a6ae73d8df99ba9 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Mon, 18 Jan 2021 07:56:56 +0100 Subject: [PATCH] Do not force materialization when not needed When an InferredStatementContext was not forced to materialize during inference, i.e. there was no movement in its substatements, we can potentially reuse the same EffectiveStatement instance. Refactor applyCopyPolicy() to provide the statement-specific facility to decide which way the semantics goes. Based on this feedback, the InferredStatementContext is improved to skip instantiations when instructed to do so by the support. AbstractStatementSupport is retrofitted to provide this functionality through EffectiveComparator and its subclasses. Backwards compatibility is maintained via choosing a conservative comparator based on copy policy. KeyStatementSupport is converted to take advantage of these facilities. JIRA: YANGTOOLS-1195 Change-Id: Idcea43f5ee121598eba324bac2a2edc70b11eaaa Signed-off-by: miroslav.kovac Signed-off-by: Michal Banik Signed-off-by: Robert Varga --- .../reactor/InferredStatementContext.java | 87 +++++++- .../parser/stmt/reactor/ReactorStmtCtx.java | 14 ++ .../stmt/reactor/ReplicaStatementContext.java | 6 + .../stmt/reactor/StatementContextBase.java | 79 ++++--- .../rfc7950/stmt/BaseStatementSupport.java | 6 +- .../rfc7950/stmt/key/KeyStatementSupport.java | 12 +- .../spi/meta/AbstractStatementSupport.java | 199 ++++++++++++++++-- .../spi/meta/ForwardingStatementSupport.java | 8 +- .../parser/spi/meta/StatementFactory.java | 20 ++ .../parser/spi/meta/StatementSupport.java | 32 +-- 10 files changed, 372 insertions(+), 91 deletions(-) diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/InferredStatementContext.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/InferredStatementContext.java index fb691e3c31..d6c83d1157 100644 --- a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/InferredStatementContext.java +++ b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/InferredStatementContext.java @@ -13,12 +13,16 @@ import static java.util.Objects.requireNonNull; import com.google.common.base.VerifyException; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Streams; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; @@ -36,6 +40,7 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType; import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException; import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.OnDemandSchemaTreeStorageNode; import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.StorageNodeType; +import org.opendaylight.yangtools.yang.parser.spi.meta.StatementFactory; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils; import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference; @@ -178,6 +183,76 @@ final class InferredStatementContext, E extend return new InferredStatementContext<>(this, newParent); } + @Override + E createEffective(final StatementFactory factory) { + // If we have not materialized we do not have a difference in effective substatements, hence we can forward + // towards the source of the statement. + return substatements == null ? tryToReusePrototype(factory) : super.createEffective(factory); + } + + private @NonNull E tryToReusePrototype(final StatementFactory factory) { + final E origEffective = prototype.buildEffective(); + final Collection> origSubstatements = + origEffective.effectiveSubstatements(); + + // First check if we can reuse the entire prototype + if (!factory.canReuseCurrent(this, prototype, origSubstatements)) { + // FIXME: YANGTOOLS-1067: an incremental improvement here is that we reuse statements that are not affected + // by us changing parent. For example: if our SchemaPath changed, but the namespace + // remained the same, 'key' statement should get reused. + // Fall back to full instantiation + return super.createEffective(factory); + } + + // No substatements to deal with, we can freely reuse the original + if (origSubstatements.isEmpty()) { + LOG.debug("Reusing empty: {}", origEffective); + substatements = ImmutableList.of(); + prototype.decRef(); + return origEffective; + } + + // We can reuse this statement let's see if all the statements agree + final List, Mutable>> declared = prototype.streamDeclared() + .filter(StmtContext::isSupportedByFeatures) + .map(sub -> effectiveCopy((ReactorStmtCtx) sub)) + .filter(Objects::nonNull) + .collect(Collectors.toUnmodifiableList()); + final List, Mutable>> effective = prototype.streamEffective() + .map(sub -> effectiveCopy((ReactorStmtCtx) sub)) + .filter(Objects::nonNull) + .collect(Collectors.toUnmodifiableList()); + + // We no longer need the prototype's substatements, but we may need to retain ours + prototype.decRef(); + if (haveRef()) { + substatements = Streams.concat(declared.stream(), effective.stream()) + .map(Entry::getValue) + .collect(ImmutableList.toImmutableList()); + } else { + // This should immediately get swept anyway. Should we use a poison object? + substatements = List.of(); + } + + if (allReused(declared) && allReused(effective)) { + LOG.debug("Reusing after substatement check: {}", origEffective); + return origEffective; + } + + // Values are the effective copies, hence this efficienly deals with recursion. + return factory.createEffective(this, declared.stream().map(Entry::getValue), + effective.stream().map(Entry::getValue)); + } + + private static boolean allReused(final List, Mutable>> entries) { + for (Entry, Mutable> entry : entries) { + if (entry.getKey() != entry.getValue()) { + return false; + } + } + return true; + } + @Override boolean hasEmptySubstatements() { if (substatements == null) { @@ -348,6 +423,16 @@ final class InferredStatementContext, E extend YangStmtMapping.TYPEDEF, YangStmtMapping.USES); + private Map.Entry, Mutable> effectiveCopy(final ReactorStmtCtx stmt) { + // FIXME: YANGTOOLS-652: formerly known as "isReusedByUses" + if (REUSED_DEF_SET.contains(stmt.definition().getPublicView())) { + return Map.entry(stmt, stmt.replicaAsChildOf(this)); + } + + final ReactorStmtCtx effective = stmt.asEffectiveChildOf(this, childCopyType, targetModule); + return effective == null ? null : Map.entry(stmt, effective); + } + private void copySubstatement(final Mutable substatement, final Collection> buffer, final Map, ReactorStmtCtx> materializedSchemaTree) { final StatementDefinition def = substatement.publicDefinition(); @@ -376,7 +461,7 @@ final class InferredStatementContext, E extend } private Optional> copySubstatement(final Mutable substatement) { - // FIXME: YANGTOOLS-1195: this is not exactly what we want to do here, because we are deling with two different + // FIXME: YANGTOOLS-1195: this is not exactly what we want to do here, because we are dealing with two different // requests: copy for inference purposes (this method), while we also copy for purposes // of buildEffective() -- in which case we want to probably invoke asEffectiveChildOf() // or similar diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ReactorStmtCtx.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ReactorStmtCtx.java index d8283e8882..c8f3c02663 100644 --- a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ReactorStmtCtx.java +++ b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ReactorStmtCtx.java @@ -19,6 +19,7 @@ import java.util.Set; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.common.YangVersion; import org.opendaylight.yangtools.yang.model.api.SchemaPath; import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement; @@ -31,6 +32,7 @@ import org.opendaylight.yangtools.yang.model.api.stmt.RefineStatement; import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier; import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement; import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; +import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType; import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.Current; import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException; import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder; @@ -306,6 +308,9 @@ abstract class ReactorStmtCtx, E extends Effec // definition().onNamespaceElementAdded(this, type, key, value); } + abstract @Nullable ReactorStmtCtx asEffectiveChildOf(StatementContextBase parent, CopyType type, + QNameModule targetModule); + // // // Statement build entry points -- both public and package-private. @@ -605,6 +610,15 @@ abstract class ReactorStmtCtx, E extends Effec } } + /** + * Return {@code true} if this context has an outstanding reference. + * + * @return True if this context has an outstanding reference. + */ + final boolean haveRef() { + return refcount > REFCOUNT_NONE; + } + private void lastDecRef() { if (noImplictRef()) { // We are no longer guarded by effective instance diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ReplicaStatementContext.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ReplicaStatementContext.java index c870df6b4e..f93b39ed53 100644 --- a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ReplicaStatementContext.java +++ b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ReplicaStatementContext.java @@ -112,6 +112,12 @@ final class ReplicaStatementContext, E extends return source.copyAsChildOf(newParent, type, targetModule); } + @Override + ReactorStmtCtx asEffectiveChildOf(final StatementContextBase newParent, final CopyType type, + final QNameModule targetModule) { + return source.asEffectiveChildOf(newParent, type, targetModule); + } + @Override StatementDefinitionContext definition() { return source.definition(); diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java index cd07b95b1c..7232a0bc08 100644 --- a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java +++ b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java @@ -44,6 +44,7 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.MutableStatement; import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour; import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceKeyCriterion; import org.opendaylight.yangtools.yang.parser.spi.meta.ParserNamespace; +import org.opendaylight.yangtools.yang.parser.spi.meta.StatementFactory; import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace; import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport; import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport.CopyPolicy; @@ -346,16 +347,25 @@ public abstract class StatementContextBase, E return effective.isEmpty() ? new ArrayList<>(toAdd) : effective; } - @Override final E createEffective() { - final E result = definition.getFactory().createEffective(this, streamDeclared(), streamEffective()); + final E result = createEffective(definition.getFactory()); if (result instanceof MutableStatement) { getRoot().addMutableStmtToSeal((MutableStatement) result); } return result; } + @NonNull E createEffective(final StatementFactory factory) { + return createEffective(factory, this); + } + + // Creates EffectiveStatement through full materialization + static , E extends EffectiveStatement> @NonNull E createEffective( + final StatementFactory factory, final StatementContextBase ctx) { + return factory.createEffective(ctx, ctx.streamDeclared(), ctx.streamEffective()); + } + abstract Stream> streamDeclared(); abstract Stream> streamEffective(); @@ -595,20 +605,25 @@ public abstract class StatementContextBase, E public Optional> copyAsChildOf(final Mutable parent, final CopyType type, final QNameModule targetModule) { checkEffectiveModelCompleted(this); + return Optional.ofNullable(copyAsChildOfImpl(parent, type, targetModule)); + } + private ReactorStmtCtx copyAsChildOfImpl(final Mutable parent, final CopyType type, + final QNameModule targetModule) { final StatementSupport support = definition.support(); final CopyPolicy policy = support.copyPolicy(); switch (policy) { case CONTEXT_INDEPENDENT: - if (substatementsContextIndependent()) { - return Optional.of(replicaAsChildOf(parent)); + if (allSubstatementsContextIndependent()) { + return replicaAsChildOf(parent); } // fall through case DECLARED_COPY: - return Optional.of(parent.childCopyOf(this, type, targetModule)); + // FIXME: ugly cast + return (ReactorStmtCtx) parent.childCopyOf(this, type, targetModule); case IGNORE: - return Optional.empty(); + return null; case REJECT: throw new IllegalStateException("Statement " + support.getPublicView() + " should never be copied"); default: @@ -616,35 +631,35 @@ public abstract class StatementContextBase, E } } - private boolean substatementsContextIndependent() { - // FIXME: YANGTOOLS-1195: we really want to compute (and cache) the summary for substatements. - // - // For now we just check if there are any substatements, but we really want to ask: - // - // Are all substatements (recursively) CONTEXT_INDEPENDENT as well? - // - // Which is something we want to compute once and store. This needs to be implemented. - return hasEmptySubstatements(); - } + @Override + final ReactorStmtCtx asEffectiveChildOf(final StatementContextBase parent, final CopyType type, + final QNameModule targetModule) { + final ReactorStmtCtx copy = copyAsChildOfImpl(parent, type, targetModule); + if (copy == null) { + // The statement fizzled, this should never happen, perhaps a verify()? + return null; + } - // FIXME: YANGTOOLS-1195: this method is unused, but should be called from InferredStatementContext at the very - // least. It should return @NonNull -- either 'E' or EffectiveStmtCtx.Current'. Perhaps its arguments need - // to be adjusted, too. - final void asEffectiveChildOf(final Mutable parent, final CopyType type, final QNameModule targetModule) { - checkEffectiveModelCompleted(this); + parent.ensureCompletedPhase(copy); + return canReuseCurrent(copy) ? replicaAsChildOf(parent) : copy; + } - final StatementSupport support = definition.support(); - final StmtContext effective = support.effectiveCopyOf(this, parent, type, targetModule); - if (effective == this) { - LOG.debug("Should reuse {}", this); - return; - } + private boolean canReuseCurrent(final ReactorStmtCtx copy) { + // Defer to statement factory to see if we can reuse this object. If we can and have only context-independent + // substatements we can reuse the object. More complex cases are handled indirectly via the copy. + return definition.getFactory().canReuseCurrent(copy, this, buildEffective().effectiveSubstatements()) + && allSubstatementsContextIndependent(); + } - // FIXME: YANGTOOLS-1195: here is probably where we want to do some statement reuse: even if the parent is - // affected, some substatements may not -- in which case we want to reuse them. This - // probably needs to be a callout of some kind. - // FIXME: YANGTOOLS-1067: an incremental improvement to that is that if no substatements changed, we want to - // be reusing the entire List and pass that as substatements. + // FIXME: YANGTOOLS-1195: we really want to compute (and cache) the summary for substatements. + // + // For now we just check if there are any substatements, but we really want to ask: + // + // Are all substatements (recursively) CONTEXT_INDEPENDENT as well? + // + // Which is something we want to compute once and store. This needs to be implemented. + private boolean allSubstatementsContextIndependent() { + return hasEmptySubstatements(); } @Override diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/BaseStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/BaseStatementSupport.java index e60164a92a..964cf3b6ce 100644 --- a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/BaseStatementSupport.java +++ b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/BaseStatementSupport.java @@ -37,11 +37,15 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext; @Beta public abstract class BaseStatementSupport, E extends EffectiveStatement> extends AbstractStatementSupport { - + @Deprecated protected BaseStatementSupport(final StatementDefinition publicDefinition, final CopyPolicy copyPolicy) { super(publicDefinition, copyPolicy); } + protected BaseStatementSupport(final StatementDefinition publicDefinition, final StatementPolicy policy) { + super(publicDefinition, policy); + } + @Override public final D createDeclared(final StmtContext ctx) { final ImmutableList> substatements = ctx.declaredSubstatements().stream() diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/key/KeyStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/key/KeyStatementSupport.java index 27aa30050e..b33d565f56 100644 --- a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/key/KeyStatementSupport.java +++ b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/key/KeyStatementSupport.java @@ -54,13 +54,14 @@ public final class KeyStatementSupport */ private static final Splitter KEY_ARG_SPLITTER = Splitter.on(SEP).omitEmptyStrings(); - private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder( - YangStmtMapping.KEY) - .build(); + private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = + SubstatementValidator.builder(YangStmtMapping.KEY).build(); private static final KeyStatementSupport INSTANCE = new KeyStatementSupport(); private KeyStatementSupport() { - super(YangStmtMapping.KEY, CopyPolicy.DECLARED_COPY); + super(YangStmtMapping.KEY, StatementPolicy.copyDeclared( + // Identity comparison is sufficient because adaptArgumentValue() is careful about reuse. + (copy, current, substatements) -> copy.getArgument() == current.getArgument())); } public static KeyStatementSupport getInstance() { @@ -97,8 +98,7 @@ public final class KeyStatementSupport } } - // This makes sure we reuse the collection when a grouping is - // instantiated in the same module + // This makes sure we reuse the collection when a grouping is instantiated in the same module. return replaced ? builder.build() : ctx.argument(); } diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/AbstractStatementSupport.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/AbstractStatementSupport.java index 0622463b82..f8ddbf3e0d 100644 --- a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/AbstractStatementSupport.java +++ b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/AbstractStatementSupport.java @@ -12,13 +12,14 @@ import static java.util.Objects.requireNonNull; import com.google.common.annotations.Beta; import com.google.common.base.VerifyException; +import java.util.Collection; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; -import org.opendaylight.yangtools.yang.common.QNameModule; +import org.opendaylight.yangtools.concepts.Immutable; 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.StatementDefinition; -import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable; +import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.Current; /** * Class providing necessary support for processing a YANG statement. This class is intended to be subclassed @@ -30,17 +31,192 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable; */ public abstract class AbstractStatementSupport, E extends EffectiveStatement> implements StatementDefinition, StatementFactory, StatementSupport { + /** + * A baseline class for implementing the {@link StatementFactory#canReuseCurrent(Current, Current, Collection)} + * contract in a manner which is consistent with a statement's {@link CopyPolicy}. + * + * @param Argument type + * @param Declared Statement representation + */ + public abstract static class StatementPolicy> implements Immutable { + final @NonNull CopyPolicy copyPolicy; + + StatementPolicy(final CopyPolicy copyPolicy) { + this.copyPolicy = requireNonNull(copyPolicy); + } + + /** + * Return an {@link StatementPolicy} for {@link CopyPolicy#CONTEXT_INDEPENDENT}. + * + * @param Argument type + * @param Declared Statement representation + * @return Context-independent policy + */ + @SuppressWarnings("unchecked") + public static final > @NonNull StatementPolicy contextIndependent() { + return (StatementPolicy) AlwaysReuse.CONTEXT_INDEPENDENT; + } + + /** + * Return an {@link StatementPolicy} for {@link CopyPolicy#IGNORE}. + * + * @param Argument type + * @param Declared Statement representation + * @return Ignoring policy + */ + @SuppressWarnings("unchecked") + public static final > @NonNull StatementPolicy ignore() { + return (StatementPolicy) AlwaysFail.IGNORE; + } + + /** + * Return an {@link StatementPolicy} for {@link CopyPolicy#REJECT}. + * + * @param Argument type + * @param Declared Statement representation + * @return Rejecting statement policy + */ + @SuppressWarnings("unchecked") + public static final > @NonNull StatementPolicy reject() { + return (StatementPolicy) AlwaysFail.REJECT; + } + + /** + * Return an {@link StatementPolicy} for {@link CopyPolicy#DECLARED_COPY}, deferring to a + * {@link StatementEquality} for individual decisions. + * + * @param Argument type + * @param Declared Statement representation + * @param equality {@link StatementEquality} to apply to effective statements + * @return Rejecting statement policy + */ + public static final > @NonNull StatementPolicy copyDeclared( + final @NonNull StatementEquality equality) { + return new EqualSemantics<>(equality); + } + + abstract boolean canReuseCurrent(@NonNull Current copy, @NonNull Current current, + @NonNull Collection> substatements); + + @Deprecated + @SuppressWarnings("unchecked") + static > StatementPolicy compat(final CopyPolicy copyPolicy) { + switch (copyPolicy) { + case CONTEXT_INDEPENDENT: + return contextIndependent(); + case DECLARED_COPY: + return (StatementPolicy) AlwaysCopy.DECLARED_COPY; + case IGNORE: + return ignore(); + case REJECT: + return reject(); + default: + throw new IllegalStateException("Unsupported policy " + copyPolicy); + } + } + + private static final class AlwaysCopy> extends StatementPolicy { + @Deprecated + static final @NonNull AlwaysCopy DECLARED_COPY = new AlwaysCopy<>(CopyPolicy.DECLARED_COPY); + + AlwaysCopy(final CopyPolicy copyPolicy) { + super(copyPolicy); + } + + @Override + boolean canReuseCurrent(final Current copy, final Current current, + final Collection> substatements) { + return false; + } + } + + private static final class AlwaysReuse> extends StatementPolicy { + static final @NonNull AlwaysReuse CONTEXT_INDEPENDENT = + new AlwaysReuse<>(CopyPolicy.CONTEXT_INDEPENDENT); + + private AlwaysReuse(final CopyPolicy copyPolicy) { + super(copyPolicy); + } + @Override + boolean canReuseCurrent(final Current copy, final Current current, + final Collection> substatements) { + return true; + } + } + + private static final class AlwaysFail> extends StatementPolicy { + static final @NonNull AlwaysFail IGNORE = new AlwaysFail<>(CopyPolicy.IGNORE); + static final @NonNull AlwaysFail REJECT = new AlwaysFail<>(CopyPolicy.REJECT); + + private AlwaysFail(final CopyPolicy copyPolicy) { + super(copyPolicy); + } + + @Override + boolean canReuseCurrent(final Current copy, final Current current, + final Collection> substatements) { + throw new VerifyException("This implementation should never be invoked"); + } + } + + private static final class EqualSemantics> extends StatementPolicy { + private final @NonNull StatementEquality equality; + + EqualSemantics(final @NonNull StatementEquality equality) { + super(CopyPolicy.DECLARED_COPY); + this.equality = requireNonNull(equality); + } + + @Override + boolean canReuseCurrent(final Current copy, final Current current, + final Collection> substatements) { + return equality.canReuseCurrent(copy, current, substatements); + } + } + } + + /** + * Abstract base class for comparators associated with statements with a {@link CopyPolicy#DECLARED_COPY} copy + * policy. + * + * @param Argument type + * @param Declared Statement representation + */ + @FunctionalInterface + public interface StatementEquality> { + /** + * Determine whether {@code current} statement has the same semantics as the provided copy. See the contract + * specification of {@link StatementFactory#canReuseCurrent(Current, Current, Collection)}. + * + * @param copy Copy of current effective context + * @param current Current effective context + * @param substatements Current effective substatements + * @return True if {@code current} can be reused in place of {@code copy}, false if the copy needs to be used. + */ + boolean canReuseCurrent(@NonNull Current copy, @NonNull Current current, + @NonNull Collection> substatements); + } + + private final @NonNull StatementPolicy policy; private final @NonNull StatementDefinition type; private final @NonNull CopyPolicy copyPolicy; @Beta - protected AbstractStatementSupport(final StatementDefinition publicDefinition, final CopyPolicy copyPolicy) { + protected AbstractStatementSupport(final StatementDefinition publicDefinition, final StatementPolicy policy) { this.type = requireNonNull(publicDefinition); - this.copyPolicy = requireNonNull(copyPolicy); + this.policy = requireNonNull(policy); + this.copyPolicy = policy.copyPolicy; checkArgument(publicDefinition != this); } + @Beta + @Deprecated + // FIXME: remove this constructor + protected AbstractStatementSupport(final StatementDefinition publicDefinition, final CopyPolicy copyPolicy) { + this(publicDefinition, StatementPolicy.compat(copyPolicy)); + } + @Override public final StatementDefinition getPublicView() { return type; @@ -52,18 +228,9 @@ public abstract class AbstractStatementSupport } @Override - public final StmtContext effectiveCopyOf(final StmtContext stmt, final Mutable parent, - final CopyType copyType, final QNameModule targetModule) { - switch (copyPolicy) { - case CONTEXT_INDEPENDENT: - return stmt; - case DECLARED_COPY: - // FIXME: YANGTOOLS-1195: this is too harsh, we need to make a callout to subclass methods so they - // actually examine the differences. - return parent.childCopyOf(stmt, copyType, targetModule); - default: - throw new VerifyException("Attempted to apply " + copyPolicy); - } + public final boolean canReuseCurrent(final Current copy, final Current current, + final Collection> substatements) { + return policy.canReuseCurrent(copy, current, substatements); } @Override diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ForwardingStatementSupport.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ForwardingStatementSupport.java index 827a97bdc9..0d15f1451a 100644 --- a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ForwardingStatementSupport.java +++ b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ForwardingStatementSupport.java @@ -9,8 +9,8 @@ package org.opendaylight.yangtools.yang.parser.spi.meta; import com.google.common.annotations.Beta; import com.google.common.collect.ForwardingObject; +import java.util.Collection; import java.util.stream.Stream; -import org.opendaylight.yangtools.yang.common.QNameModule; 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.StatementDefinition; @@ -97,8 +97,8 @@ public abstract class ForwardingStatementSupport effectiveCopyOf(final StmtContext stmt, final Mutable parent, - final CopyType copyType, final QNameModule targetModule) { - return delegate().effectiveCopyOf(stmt, parent, copyType, targetModule); + public boolean canReuseCurrent(final Current copy, final Current current, + final Collection> substatements) { + return delegate().canReuseCurrent(copy, current, substatements); } } diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementFactory.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementFactory.java index f929529c63..793c0e3534 100644 --- a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementFactory.java +++ b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementFactory.java @@ -7,10 +7,12 @@ */ package org.opendaylight.yangtools.yang.parser.spi.meta; +import java.util.Collection; import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement; import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; +import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.Current; /** * An entity capable of creating {@link DeclaredStatement} and {@link EffectiveStatement} instances for a particular @@ -38,4 +40,22 @@ public interface StatementFactory, E extends E @NonNull E createEffective(EffectiveStmtCtx.@NonNull Current stmt, Stream> declaredSubstatements, Stream> effectiveSubstatements); + + /** + * Determine reactor copy behaviour of a statement instance. Implementations classes are required to determine + * their operations with regard to their statements being replicated into different contexts -- potentially sharing + * instantiations. + * + *

+ * Implementations are examine {@code copy} as to whether it would result in the same semantics as {@code current} + * does, provided that {@code current}'s {@code substatements} are properly propagated. + * + * @param copy Copy of current effective context + * @param current Current effective context + * @param substatements Current effective substatements + * @return True if the differences between {@code copy} and {@code current} do not affect this statement's effective + * semantics. + */ + boolean canReuseCurrent(@NonNull Current copy, @NonNull Current current, + @NonNull Collection> substatements); } diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementSupport.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementSupport.java index 419c28c60d..05e5f47f4c 100644 --- a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementSupport.java +++ b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementSupport.java @@ -17,7 +17,6 @@ import org.opendaylight.yangtools.yang.model.api.meta.ArgumentDefinition; 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.StatementDefinition; -import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable; import org.opendaylight.yangtools.yang.parser.spi.source.SourceException; /** @@ -175,34 +174,6 @@ public interface StatementSupport, E extends E */ @NonNull CopyPolicy copyPolicy(); - /** - * Determine reactor copy behaviour of a statement instance. Statement support classes are required to determine - * their operations with regard to their statements being replicated into different contexts, so that - * {@link Mutable} instances are not created when it is evident they are superfluous. - * - *

- * The copy operation has three intrinsic parts: - *

    - *
  • target {@code parent}, i.e. new parent statement for the copy. This determines things like default value - * of the {@code config} statement and similar
  • - *
  • copy operation type
  • - *
  • {@code target module}, which defines the default namespace for the statement copy. This might not be always - * present, in which case the namespace is retained from the source. As an example, {@code uses} changes - * the default namespace to parent's namespace, whereas {@code augment} does not.
  • - *
- * - *

- * Implementations should return the context to use -- returning {@code stmt} if there is no change or a copy of it. - * - * @param stmt Context of statement to be copied statement - * @param parent Parent statement context - * @param copyType Type of copy being performed - * @param targetModule Target module, if present - * @return StmtContext holding the effective state - */ - @NonNull StmtContext effectiveCopyOf(StmtContext stmt, Mutable parent, CopyType copyType, - @Nullable QNameModule targetModule); - /** * Given a raw string representation of an argument, try to use a shared representation. * @@ -272,8 +243,7 @@ public interface StatementSupport, E extends E /** * Statement context copy policy, indicating how should reactor handle statement copy operations. Every statement - * copied by the reactor is subject to policy check done by - * {@link StatementSupport#effectiveCopyOf(StmtContext, Mutable, CopyType, QNameModule)}. + * copied by the reactor is subject to this policy. */ enum CopyPolicy { /** -- 2.36.6