X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=yang%2Fyang-parser-reactor%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fyang%2Fparser%2Fstmt%2Freactor%2FStatementContextBase.java;h=65656d4efa5ba85c3e4541b6cf033756f4170547;hb=5ef3027640586a5e291c18414cade544a89953f7;hp=46697255a863e15ac2faa37f435c253ea7ed971e;hpb=246158dab180ba6dc585c97cf2e786e842a2e688;p=yangtools.git 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 46697255a8..65656d4efa 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 @@ -8,12 +8,13 @@ package org.opendaylight.yangtools.yang.parser.stmt.reactor; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Verify.verify; +import static com.google.common.base.Verify.verifyNotNull; import static java.util.Objects.requireNonNull; import com.google.common.annotations.Beta; +import com.google.common.base.VerifyException; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMultimap; @@ -30,19 +31,21 @@ import java.util.Map.Entry; import java.util.Optional; import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNull; -import org.eclipse.jdt.annotation.Nullable; 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.IdentifierNamespace; import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition; import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory; import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType; import org.opendaylight.yangtools.yang.parser.spi.meta.ImplicitParentAwareStatementSupport; import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder; import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase; +import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.ExecutionOrder; +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; @@ -62,7 +65,7 @@ import org.slf4j.LoggerFactory; * @param Effective Statement representation */ public abstract class StatementContextBase, E extends EffectiveStatement> - extends ReactorStmtCtx { + extends ReactorStmtCtx implements CopyHistory { /** * Event listener when an item is added to model namespace. */ @@ -93,47 +96,82 @@ public abstract class StatementContextBase, E private static final Logger LOG = LoggerFactory.getLogger(StatementContextBase.class); - private final CopyHistory copyHistory; + // + // {@link CopyHistory} encoded as a single byte. We still have 4 bits unused. + // + private static final byte COPY_LAST_TYPE_MASK = 0x03; + private static final byte COPY_ADDED_BY_USES = 0x04; + private static final byte COPY_ADDED_BY_AUGMENTATION = 0x08; + private static final byte COPY_ORIGINAL = 0x00; + + private final byte copyHistory; + + static { + final int copyTypes = CopyType.values().length; + // This implies CopyType.ordinal() is <= COPY_TYPE_MASK + verify(copyTypes == COPY_LAST_TYPE_MASK + 1, "Unexpected %s CopyType values", copyTypes); + } + // Note: this field can strictly be derived in InferredStatementContext, but it forms the basis of many of our // operations, hence we want to keep it close by. private final @NonNull StatementDefinitionContext definition; + // TODO: consider keying by Byte equivalent of ExecutionOrder private Multimap phaseListeners = ImmutableMultimap.of(); private Multimap phaseMutation = ImmutableMultimap.of(); private List> effectOfStatement = ImmutableList.of(); - private @Nullable ModelProcessingPhase completedPhase; + /** + * {@link ModelProcessingPhase.ExecutionOrder} value of current {@link ModelProcessingPhase} of this statement. + */ + private byte executionOrder; // Copy constructor used by subclasses to implement reparent() StatementContextBase(final StatementContextBase original) { super(original); this.copyHistory = original.copyHistory; this.definition = original.definition; - this.completedPhase = original.completedPhase; + this.executionOrder = original.executionOrder; } StatementContextBase(final StatementDefinitionContext def) { this.definition = requireNonNull(def); - this.copyHistory = CopyHistory.original(); + this.copyHistory = COPY_ORIGINAL; } - StatementContextBase(final StatementDefinitionContext def, final CopyHistory copyHistory) { + StatementContextBase(final StatementDefinitionContext def, final CopyType copyType) { this.definition = requireNonNull(def); - this.copyHistory = requireNonNull(copyHistory); + this.copyHistory = (byte) copyFlags(copyType); } - @Override - public Collection> getEffectOfStatement() { - return effectOfStatement; + StatementContextBase(final StatementContextBase prototype, final CopyType copyType) { + this.definition = prototype.definition; + this.copyHistory = (byte) (copyFlags(copyType) | prototype.copyHistory & ~COPY_LAST_TYPE_MASK); } - @Override - public void addAsEffectOfStatement(final StmtContext ctx) { - if (effectOfStatement.isEmpty()) { - effectOfStatement = new ArrayList<>(1); + private static int copyFlags(final CopyType copyType) { + return historyFlags(copyType) | copyType.ordinal(); + } + + private static byte historyFlags(final CopyType copyType) { + switch (copyType) { + case ADDED_BY_AUGMENTATION: + return COPY_ADDED_BY_AUGMENTATION; + case ADDED_BY_USES: + return COPY_ADDED_BY_USES; + case ADDED_BY_USES_AUGMENTATION: + return COPY_ADDED_BY_AUGMENTATION | COPY_ADDED_BY_USES; + case ORIGINAL: + return COPY_ORIGINAL; + default: + throw new VerifyException("Unhandled type " + copyType); } - effectOfStatement.add(ctx); + } + + @Override + public Collection> getEffectOfStatement() { + return effectOfStatement; } @Override @@ -148,42 +186,64 @@ public abstract class StatementContextBase, E effectOfStatement.addAll(ctxs); } + // + // CopyHistory integration + // + @Override - public CopyHistory history() { - return copyHistory; + public final CopyHistory history() { + return this; } @Override - public ModelProcessingPhase getCompletedPhase() { - return completedPhase; + public final boolean isAddedByUses() { + return (copyHistory & COPY_ADDED_BY_USES) != 0; + } + + @Override + public final boolean isAugmenting() { + return (copyHistory & COPY_ADDED_BY_AUGMENTATION) != 0; + } + + @Override + public final CopyType getLastOperation() { + return CopyType.values()[copyHistory & COPY_LAST_TYPE_MASK]; + } + + // + // Inference completion tracking + // + + @Override + final byte executionOrder() { + return executionOrder; } // FIXME: this should be propagated through a correct constructor @Deprecated final void setCompletedPhase(final ModelProcessingPhase completedPhase) { - this.completedPhase = completedPhase; + this.executionOrder = completedPhase.executionOrder(); } @Override - public final > void addToNs( + public final > void addToNs( final Class<@NonNull N> type, final T key, final U value) { addToNamespace(type, key, value); } static final Collection> mutableEffectiveSubstatements( - final List> effective) { + final List> effective) { return effective instanceof ImmutableCollection ? effective : Collections.unmodifiableCollection(effective); } - private static List> shrinkEffective( - final List> effective) { + private static List> shrinkEffective(final List> effective) { return effective.isEmpty() ? ImmutableList.of() : effective; } public abstract void removeStatementFromEffectiveSubstatements(StatementDefinition statementDef); - static final List> removeStatementFromEffectiveSubstatements( - final List> effective, final StatementDefinition statementDef) { + static final List> removeStatementFromEffectiveSubstatements( + final List> effective, final StatementDefinition statementDef) { if (effective.isEmpty()) { return effective; } @@ -214,8 +274,8 @@ public abstract class StatementContextBase, E public abstract void removeStatementFromEffectiveSubstatements(StatementDefinition statementDef, String statementArg); - static final List> removeStatementFromEffectiveSubstatements( - final List> effective, final StatementDefinition statementDef, + static final List> removeStatementFromEffectiveSubstatements( + final List> effective, final StatementDefinition statementDef, final String statementArg) { if (statementArg == null) { return removeStatementFromEffectiveSubstatements(effective, statementDef); @@ -225,7 +285,7 @@ public abstract class StatementContextBase, E return effective; } - final Iterator> iterator = effective.iterator(); + final Iterator> iterator = effective.iterator(); while (iterator.hasNext()) { final Mutable next = iterator.next(); if (statementDef.equals(next.publicDefinition()) && statementArg.equals(next.rawArgument())) { @@ -258,16 +318,13 @@ public abstract class StatementContextBase, E */ public abstract void addEffectiveSubstatement(Mutable substatement); - final List> addEffectiveSubstatement( - final List> effective, final Mutable substatement) { + final List> addEffectiveSubstatement(final List> effective, + final Mutable substatement) { verifyStatement(substatement); - final List> resized = beforeAddEffectiveStatement(effective, 1); - final StatementContextBase stmt = (StatementContextBase) substatement; - final ModelProcessingPhase phase = completedPhase; - if (phase != null) { - ensureCompletedPhase(stmt, phase); - } + final List> resized = beforeAddEffectiveStatement(effective, 1); + final ReactorStmtCtx stmt = (ReactorStmtCtx) substatement; + ensureCompletedExecution(stmt); resized.add(stmt); return resized; } @@ -290,16 +347,14 @@ public abstract class StatementContextBase, E abstract void addEffectiveSubstatementsImpl(Collection> statements); - final List> addEffectiveSubstatementsImpl( - final List> effective, + final List> addEffectiveSubstatementsImpl(final List> effective, final Collection> statements) { - final List> resized = beforeAddEffectiveStatement(effective, statements.size()); - final Collection> casted = - (Collection>) statements; - final ModelProcessingPhase phase = completedPhase; - if (phase != null) { - for (StatementContextBase stmt : casted) { - ensureCompletedPhase(stmt, phase); + final List> resized = beforeAddEffectiveStatement(effective, statements.size()); + final Collection> casted = + (Collection>) statements; + if (executionOrder != ExecutionOrder.NULL) { + for (ReactorStmtCtx stmt : casted) { + ensureCompletedExecution(stmt, executionOrder); } } @@ -307,39 +362,43 @@ public abstract class StatementContextBase, E return resized; } - abstract Iterable> effectiveChildrenToComplete(); + abstract Iterable> effectiveChildrenToComplete(); // exposed for InferredStatementContext only final void ensureCompletedPhase(final Mutable stmt) { verifyStatement(stmt); - final ModelProcessingPhase phase = completedPhase; - if (phase != null) { - ensureCompletedPhase((StatementContextBase) stmt, phase); + ensureCompletedExecution((ReactorStmtCtx) stmt); + } + + // Make sure target statement has transitioned at least to our phase (if we have one). This method is just before we + // take allow a statement to become our substatement. This is needed to ensure that every statement tree does not + // contain any statements which did not complete the same phase as the root statement. + private void ensureCompletedExecution(final ReactorStmtCtx stmt) { + if (executionOrder != ExecutionOrder.NULL) { + ensureCompletedExecution(stmt, executionOrder); } } - // Make sure target statement has transitioned at least to specified phase. This method is just before we take - // allow a statement to become our substatement. This is needed to ensure that every statement tree does not contain - // any statements which did not complete the same phase as the root statement. - private static void ensureCompletedPhase(final StatementContextBase stmt, - final ModelProcessingPhase phase) { - verify(stmt.tryToCompletePhase(phase), "Statement %s cannot complete phase %s", stmt, phase); + private static void ensureCompletedExecution(final ReactorStmtCtx stmt, final byte executionOrder) { + verify(stmt.tryToCompletePhase(executionOrder), "Statement %s cannot complete phase %s", stmt, executionOrder); } private static void verifyStatement(final Mutable stmt) { - verify(stmt instanceof StatementContextBase, "Unexpected statement %s", stmt); + verify(stmt instanceof ReactorStmtCtx, "Unexpected statement %s", stmt); } - private List> beforeAddEffectiveStatement( - final List> effective, final int toAdd) { - // We cannot allow statement to be further mutated - verify(completedPhase != ModelProcessingPhase.EFFECTIVE_MODEL, "Cannot modify finished statement at %s", + private List> beforeAddEffectiveStatement(final List> effective, + final int toAdd) { + // We cannot allow statement to be further mutated. + // TODO: we really want to say 'not NULL and not at or after EFFECTIVE_MODEL here. This will matter if we have + // a phase after EFFECTIVE_MODEL + verify(executionOrder != ExecutionOrder.EFFECTIVE_MODEL, "Cannot modify finished statement at %s", sourceReference()); return beforeAddEffectiveStatementUnsafe(effective, toAdd); } - final List> beforeAddEffectiveStatementUnsafe( - final List> effective, final int toAdd) { + final List> beforeAddEffectiveStatementUnsafe(final List> effective, + final int toAdd) { final ModelProcessingPhase inProgressPhase = getRoot().getSourceContext().getInProgressPhase(); checkState(inProgressPhase == ModelProcessingPhase.FULL_DECLARATION || inProgressPhase == ModelProcessingPhase.EFFECTIVE_MODEL, @@ -348,49 +407,66 @@ public abstract class StatementContextBase, E return effective.isEmpty() ? new ArrayList<>(toAdd) : effective; } - // Exposed for ReplicaStatementContext @Override - E createEffective() { - return definition.getFactory().createEffective(this, streamDeclared(), streamEffective()); + final E createEffective() { + final E result = createEffective(definition.getFactory()); + if (result instanceof MutableStatement) { + getRoot().addMutableStmtToSeal((MutableStatement) result); + } + return result; } - abstract Stream> streamDeclared(); + @NonNull E createEffective(final StatementFactory factory) { + return createEffective(factory, this); + } - abstract Stream> streamEffective(); + // 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()); + } /** - * Try to execute current {@link ModelProcessingPhase} of source parsing. If the phase has already been executed, - * this method does nothing. + * Return a stream of declared statements which can be built into an {@link EffectiveStatement}, as per + * {@link StmtContext#buildEffective()} contract. * - * @param phase to be executed (completed) - * @return true if phase was successfully completed - * @throws SourceException when an error occurred in source parsing + * @return Stream of supported declared statements. */ - final boolean tryToCompletePhase(final ModelProcessingPhase phase) { - return phase.isCompletedBy(completedPhase) || doTryToCompletePhase(phase); - } + // FIXME: we really want to unify this with streamEffective(), under its name + abstract Stream> streamDeclared(); + + /** + * Return a stream of inferred statements which can be built into an {@link EffectiveStatement}, as per + * {@link StmtContext#buildEffective()} contract. + * + * @return Stream of supported effective statements. + */ + // FIXME: this method is currently a misnomer, but unifying with streamDeclared() would make this accurate again + abstract Stream> streamEffective(); - private boolean doTryToCompletePhase(final ModelProcessingPhase phase) { - final boolean finished = phaseMutation.isEmpty() ? true : runMutations(phase); - if (completeChildren(phase) && finished) { - onPhaseCompleted(phase); + @Override + final boolean doTryToCompletePhase(final byte targetOrder) { + final boolean finished = phaseMutation.isEmpty() ? true : runMutations(targetOrder); + if (completeChildren(targetOrder) && finished) { + onPhaseCompleted(targetOrder); return true; } return false; } - private boolean completeChildren(final ModelProcessingPhase phase) { + private boolean completeChildren(final byte targetOrder) { boolean finished = true; for (final StatementContextBase child : mutableDeclaredSubstatements()) { - finished &= child.tryToCompletePhase(phase); + finished &= child.tryToCompletePhase(targetOrder); } - for (final StatementContextBase child : effectiveChildrenToComplete()) { - finished &= child.tryToCompletePhase(phase); + for (final ReactorStmtCtx child : effectiveChildrenToComplete()) { + finished &= child.tryToCompletePhase(targetOrder); } return finished; } - private boolean runMutations(final ModelProcessingPhase phase) { + private boolean runMutations(final byte targetOrder) { + final ModelProcessingPhase phase = verifyNotNull(ModelProcessingPhase.ofExecutionOrder(targetOrder)); final Collection openMutations = phaseMutation.get(phase); return openMutations.isEmpty() ? true : runMutations(phase, openMutations); } @@ -421,22 +497,73 @@ public abstract class StatementContextBase, E } /** - * Occurs on end of {@link ModelProcessingPhase} of source parsing. + * Occurs on end of {@link ModelProcessingPhase} of source parsing. This method must not be called with + * {@code executionOrder} equal to {@link ExecutionOrder#NULL}. * - * @param phase - * that was to be completed (finished) - * @throws SourceException - * when an error occurred in source parsing + * @param phase that was to be completed (finished) + * @throws SourceException when an error occurred in source parsing */ - private void onPhaseCompleted(final ModelProcessingPhase phase) { - completedPhase = phase; + private void onPhaseCompleted(final byte completedOrder) { + executionOrder = completedOrder; + if (completedOrder == ExecutionOrder.EFFECTIVE_MODEL) { + // We have completed effective model, substatements are guaranteed not to change + summarizeSubstatementPolicy(); + } + final ModelProcessingPhase phase = verifyNotNull(ModelProcessingPhase.ofExecutionOrder(completedOrder)); final Collection listeners = phaseListeners.get(phase); if (!listeners.isEmpty()) { runPhaseListeners(phase, listeners); } } + private void summarizeSubstatementPolicy() { + if (definition().support().copyPolicy() == CopyPolicy.EXACT_REPLICA || noSensitiveSubstatements()) { + setAllSubstatementsContextIndependent(); + } + } + + /** + * Determine whether any substatements are copy-sensitive as determined by {@link StatementSupport#copyPolicy()}. + * Only {@link CopyPolicy#CONTEXT_INDEPENDENT}, {@link CopyPolicy#EXACT_REPLICA} and {@link CopyPolicy#IGNORE} are + * copy-insensitive. Note that statements which are not {@link StmtContext#isSupportedToBuildEffective()} are all + * considered copy-insensitive. + * + *

+ * Implementations are expected to call {@link #noSensitiveSubstatements()} to actually traverse substatement sets. + * + * @return True if no substatements require copy-sensitive handling + */ + abstract boolean noSensitiveSubstatements(); + + /** + * Determine whether any of the provided substatements are context-sensitive for purposes of implementing + * {@link #noSensitiveSubstatements()}. + * + * @param substatements Substatements to check + * @return True if no substatements require context-sensitive handling + */ + static boolean noSensitiveSubstatements(final Collection> substatements) { + for (ReactorStmtCtx stmt : substatements) { + if (stmt.isSupportedToBuildEffective()) { + if (!stmt.allSubstatementsContextIndependent()) { + // This is a recursive property + return false; + } + + switch (stmt.definition().support().copyPolicy()) { + case CONTEXT_INDEPENDENT: + case EXACT_REPLICA: + case IGNORE: + break; + default: + return false; + } + } + } + return true; + } + private void runPhaseListeners(final ModelProcessingPhase phase, final Collection listeners) { final Iterator listener = listeners.iterator(); while (listener.hasNext()) { @@ -466,7 +593,7 @@ public abstract class StatementContextBase, E return definition; } - final > void onNamespaceItemAddedAction(final Class type, final K key, + final > void onNamespaceItemAddedAction(final Class type, final K key, final OnNamespaceItemAdded listener) { final Object potential = getFromNamespace(type, key); if (potential != null) { @@ -483,7 +610,7 @@ public abstract class StatementContextBase, E }); } - final > void onNamespaceItemAddedAction(final Class type, + final > void onNamespaceItemAddedAction(final Class type, final ModelProcessingPhase phase, final NamespaceKeyCriterion criterion, final OnNamespaceItemAdded listener) { final Optional> existing = getFromNamespace(type, criterion); @@ -509,7 +636,7 @@ public abstract class StatementContextBase, E }); } - final > void selectMatch(final Class type, + final > void selectMatch(final Class type, final NamespaceKeyCriterion criterion, final OnNamespaceItemAdded listener) { final Optional> optMatch = getFromNamespace(type, criterion); checkState(optMatch.isPresent(), "Failed to find a match for criterion %s in namespace %s node %s", criterion, @@ -518,7 +645,7 @@ public abstract class StatementContextBase, E listener.namespaceItemAdded(StatementContextBase.this, type, match.getKey(), match.getValue()); } - final > void waitForPhase(final Object value, final Class type, + final > void waitForPhase(final Object value, final Class type, final ModelProcessingPhase phase, final NamespaceKeyCriterion criterion, final OnNamespaceItemAdded listener) { ((StatementContextBase) value).addPhaseCompletedListener(phase, @@ -528,7 +655,7 @@ public abstract class StatementContextBase, E }); } - private > NamespaceBehaviourWithListeners getBehaviour( + private > NamespaceBehaviourWithListeners getBehaviour( final Class type) { final NamespaceBehaviour behaviour = getBehaviourRegistry().getNamespaceBehaviour(type); checkArgument(behaviour instanceof NamespaceBehaviourWithListeners, "Namespace %s does not support listeners", @@ -550,10 +677,10 @@ public abstract class StatementContextBase, E * @throws NullPointerException if any of the arguments is null */ void addPhaseCompletedListener(final ModelProcessingPhase phase, final OnPhaseFinished listener) { - checkNotNull(phase, "Statement context processing phase cannot be null at: %s", sourceReference()); - checkNotNull(listener, "Statement context phase listener cannot be null at: %s", sourceReference()); + requireNonNull(phase, "Statement context processing phase cannot be null"); + requireNonNull(listener, "Statement context phase listener cannot be null"); - ModelProcessingPhase finishedPhase = completedPhase; + ModelProcessingPhase finishedPhase = ModelProcessingPhase.ofExecutionOrder(executionOrder); while (finishedPhase != null) { if (phase.equals(finishedPhase)) { listener.phaseFinished(this, finishedPhase); @@ -574,12 +701,8 @@ public abstract class StatementContextBase, E * @throws IllegalStateException when the mutation was registered after phase was completed */ final void addMutation(final ModelProcessingPhase phase, final ContextMutation mutation) { - ModelProcessingPhase finishedPhase = completedPhase; - while (finishedPhase != null) { - checkState(!phase.equals(finishedPhase), "Mutation registered after phase was completed at: %s", - sourceReference()); - finishedPhase = finishedPhase.getPreviousPhase(); - } + checkState(executionOrder < phase.executionOrder(), "Mutation registered after phase was completed at: %s", + sourceReference()); if (phaseMutation.isEmpty()) { phaseMutation = newMultimap(); @@ -604,27 +727,27 @@ 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.applyCopyPolicy(this, parent, type, targetModule); + final CopyPolicy policy = support.copyPolicy(); switch (policy) { + case EXACT_REPLICA: + return replicaAsChildOf(parent); case CONTEXT_INDEPENDENT: - if (hasEmptySubstatements()) { - // This statement is context-independent and has no substatements -- hence it can be freely shared. - return Optional.of(replicaAsChildOf(parent)); + if (allSubstatementsContextIndependent()) { + return replicaAsChildOf(parent); } - // FIXME: YANGTOOLS-694: filter out all context-independent substatements, eliminate fall-through + // fall through case DECLARED_COPY: - // FIXME: YANGTOOLS-694: this is still to eager, we really want to copy as a lazily-instantiated - // context, so that we can support building an effective statement without copying - // anything -- we will typically end up not being inferred against. In that case, - // this slim context should end up dealing with differences at buildContext() - // time. This is a YANGTOOLS-1067 prerequisite (which will deal with what can and - // cannot be shared across instances). - 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: @@ -632,6 +755,26 @@ public abstract class StatementContextBase, E } } + @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; + } + + parent.ensureCompletedPhase(copy); + return canReuseCurrent(copy) ? this : copy; + } + + 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(); + } + @Override public final Mutable childCopyOf(final StmtContext stmt, final CopyType type, final QNameModule targetModule) { @@ -678,13 +821,8 @@ public abstract class StatementContextBase, E } @Override - public final StatementContextBase replicaAsChildOf(final Mutable parent) { - checkArgument(parent instanceof StatementContextBase, "Unsupported parent %s", parent); - return replicaAsChildOf((StatementContextBase) parent); - } - - final @NonNull StatementContextBase replicaAsChildOf(final StatementContextBase stmt) { - return new ReplicaStatementContext<>(stmt, this); + final ReplicaStatementContext replicaAsChildOf(final StatementContextBase parent) { + return new ReplicaStatementContext<>(parent, this); } private static void checkEffectiveModelCompleted(final StmtContext stmt) { @@ -694,6 +832,7 @@ public abstract class StatementContextBase, E } @Beta + // FIXME: this information should be exposed as a well-known Namespace public final boolean hasImplicitParentSupport() { return definition.getFactory() instanceof ImplicitParentAwareStatementSupport; }