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=a331d6b6d91619da119fcc3323aa94dee5a478a5;hb=cd04351669d5921088a73a275954ba78336c1a44;hp=6903620c32347e7c9adc0b61a3c7ae917a2aee53;hpb=fc9cc1b7980a47ae746d51f28e3a2bca5a007545;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 6903620c32..a331d6b6d9 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,7 +8,6 @@ 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 java.util.Objects.requireNonNull; @@ -34,15 +33,17 @@ 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.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; @@ -128,14 +129,6 @@ public abstract class StatementContextBase, E return effectOfStatement; } - @Override - public void addAsEffectOfStatement(final StmtContext ctx) { - if (effectOfStatement.isEmpty()) { - effectOfStatement = new ArrayList<>(1); - } - effectOfStatement.add(ctx); - } - @Override public void addAsEffectOfStatement(final Collection> ctxs) { if (ctxs.isEmpty()) { @@ -165,7 +158,7 @@ public abstract class StatementContextBase, E } @Override - public final > void addToNs( + public final > void addToNs( final Class<@NonNull N> type, final T key, final U value) { addToNamespace(type, key, value); } @@ -292,11 +285,11 @@ public abstract class StatementContextBase, E final List> addEffectiveSubstatementsImpl(final List> effective, final Collection> statements) { final List> resized = beforeAddEffectiveStatement(effective, statements.size()); - final Collection> casted = - (Collection>) statements; + final Collection> casted = + (Collection>) statements; final ModelProcessingPhase phase = completedPhase; if (phase != null) { - for (StatementContextBase stmt : casted) { + for (ReactorStmtCtx stmt : casted) { ensureCompletedPhase(stmt, phase); } } @@ -345,15 +338,42 @@ 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); + } + + // 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> streamEffective(); + /** + * Return a stream of declared statements which can be built into an {@link EffectiveStatement}, as per + * {@link StmtContext#buildEffective()} contract. + * + * @return Stream of supported declared statements. + */ + // 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(); @Override final boolean doTryToCompletePhase(final ModelProcessingPhase phase) { @@ -416,6 +436,9 @@ public abstract class StatementContextBase, E */ private void onPhaseCompleted(final ModelProcessingPhase phase) { completedPhase = phase; + if (phase == ModelProcessingPhase.EFFECTIVE_MODEL) { + summarizeSubstatementPolicy(); + } final Collection listeners = phaseListeners.get(phase); if (!listeners.isEmpty()) { @@ -423,6 +446,53 @@ public abstract class StatementContextBase, E } } + 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()) { @@ -452,7 +522,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) { @@ -469,7 +539,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); @@ -495,7 +565,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, @@ -504,7 +574,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, @@ -514,7 +584,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", @@ -536,8 +606,8 @@ 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; while (finishedPhase != null) { @@ -590,27 +660,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: @@ -618,6 +688,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) { @@ -664,13 +754,8 @@ public abstract class StatementContextBase, E } @Override - public final ReactorStmtCtx replicaAsChildOf(final Mutable parent) { - checkArgument(parent instanceof StatementContextBase, "Unsupported parent %s", parent); - return replicaAsChildOf((StatementContextBase) parent); - } - - final @NonNull ReplicaStatementContext 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) {