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=28f6f860812fa7552d754616729d96014d2aac66;hpb=85fcb950bd57a2dc9106309621b666cb3b448d11;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 28f6f86081..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,14 +8,11 @@ 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; import com.google.common.annotations.Beta; -import com.google.common.base.MoreObjects; -import com.google.common.base.MoreObjects.ToStringHelper; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMultimap; @@ -28,49 +25,31 @@ import java.util.EnumMap; import java.util.EventListener; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Map.Entry; import java.util.Optional; -import java.util.Set; import java.util.stream.Stream; 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; 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.stmt.AugmentStatement; -import org.opendaylight.yangtools.yang.model.api.stmt.ConfigEffectiveStatement; -import org.opendaylight.yangtools.yang.model.api.stmt.DeviationStatement; -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.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.InferenceException; 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.NamespaceBehaviour.Registry; import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceKeyCriterion; -import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceNotAvailableException; +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; 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.ImplicitSubstatement; import org.opendaylight.yangtools.yang.parser.spi.source.SourceException; -import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace; -import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace.SupportedFeatures; import org.opendaylight.yangtools.yang.parser.stmt.reactor.NamespaceBehaviourWithListeners.KeyedValueAddedListener; import org.opendaylight.yangtools.yang.parser.stmt.reactor.NamespaceBehaviourWithListeners.PredicateValueAddedListener; import org.slf4j.Logger; @@ -84,7 +63,7 @@ import org.slf4j.LoggerFactory; * @param Effective Statement representation */ public abstract class StatementContextBase, E extends EffectiveStatement> - extends NamespaceStorageSupport implements Mutable { + extends ReactorStmtCtx { /** * Event listener when an item is added to model namespace. */ @@ -115,24 +94,6 @@ public abstract class StatementContextBase, E private static final Logger LOG = LoggerFactory.getLogger(StatementContextBase.class); - // Flag bit assignments - private static final int IS_SUPPORTED_BY_FEATURES = 0x01; - private static final int HAVE_SUPPORTED_BY_FEATURES = 0x02; - private static final int IS_IGNORE_IF_FEATURE = 0x04; - private static final int HAVE_IGNORE_IF_FEATURE = 0x08; - // Note: these four are related - private static final int IS_IGNORE_CONFIG = 0x10; - private static final int HAVE_IGNORE_CONFIG = 0x20; - private static final int IS_CONFIGURATION = 0x40; - private static final int HAVE_CONFIGURATION = 0x80; - - // Have-and-set flag constants, also used as masks - private static final int SET_SUPPORTED_BY_FEATURES = HAVE_SUPPORTED_BY_FEATURES | IS_SUPPORTED_BY_FEATURES; - private static final int SET_CONFIGURATION = HAVE_CONFIGURATION | IS_CONFIGURATION; - // Note: implies SET_CONFIGURATION, allowing fewer bit operations to be performed - private static final int SET_IGNORE_CONFIG = HAVE_IGNORE_CONFIG | IS_IGNORE_CONFIG | SET_CONFIGURATION; - private static final int SET_IGNORE_IF_FEATURE = HAVE_IGNORE_IF_FEATURE | IS_IGNORE_IF_FEATURE; - private final CopyHistory copyHistory; // 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. @@ -144,33 +105,13 @@ public abstract class StatementContextBase, E private List> effectOfStatement = ImmutableList.of(); private @Nullable ModelProcessingPhase completedPhase; - private @Nullable E effectiveInstance; - - // Master flag controlling whether this context can yield an effective statement - // FIXME: investigate the mechanics that are being supported by this, as it would be beneficial if we can get rid - // of this flag -- eliminating the initial alignment shadow used by below gap-filler fields. - private boolean isSupportedToBuildEffective = true; - - // Flag for use with AbstractResumedStatement. This is hiding in the alignment shadow created by above boolean - private boolean fullyDefined; - - // Flags for use with SubstatementContext. These are hiding in the alignment shadow created by above boolean and - // hence improve memory layout. - private byte flags; - - // SchemaPath cache for use with SubstatementContext and InferredStatementContext. This hurts RootStatementContext - // a bit in terms of size -- but those are only a few and SchemaPath is on its way out anyway. - private volatile SchemaPath schemaPath; // Copy constructor used by subclasses to implement reparent() StatementContextBase(final StatementContextBase original) { + super(original); this.copyHistory = original.copyHistory; this.definition = original.definition; - - this.isSupportedToBuildEffective = original.isSupportedToBuildEffective; - this.fullyDefined = original.fullyDefined; this.completedPhase = original.completedPhase; - this.flags = original.flags; } StatementContextBase(final StatementDefinitionContext def) { @@ -188,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()) { @@ -209,53 +142,12 @@ public abstract class StatementContextBase, E } @Override - public boolean isSupportedByFeatures() { - final int fl = flags & SET_SUPPORTED_BY_FEATURES; - if (fl != 0) { - return fl == SET_SUPPORTED_BY_FEATURES; - } - if (isIgnoringIfFeatures()) { - flags |= SET_SUPPORTED_BY_FEATURES; - return true; - } - - /* - * If parent is supported, we need to check if-features statements of this context. - */ - if (isParentSupportedByFeatures()) { - // If the set of supported features has not been provided, all features are supported by default. - final Set supportedFeatures = getFromNamespace(SupportedFeaturesNamespace.class, - SupportedFeatures.SUPPORTED_FEATURES); - if (supportedFeatures == null || StmtContextUtils.checkFeatureSupport(this, supportedFeatures)) { - flags |= SET_SUPPORTED_BY_FEATURES; - return true; - } - } - - // Either parent is not supported or this statement is not supported - flags |= HAVE_SUPPORTED_BY_FEATURES; - return false; - } - - protected abstract boolean isParentSupportedByFeatures(); - - @Override - public boolean isSupportedToBuildEffective() { - return isSupportedToBuildEffective; - } - - @Override - public void setIsSupportedToBuildEffective(final boolean isSupportedToBuildEffective) { - this.isSupportedToBuildEffective = isSupportedToBuildEffective; - } - - @Override - public CopyHistory getCopyHistory() { + public final CopyHistory history() { return copyHistory; } @Override - public ModelProcessingPhase getCompletedPhase() { + public final ModelProcessingPhase getCompletedPhase() { return completedPhase; } @@ -266,116 +158,24 @@ public abstract class StatementContextBase, E } @Override - public abstract StatementContextBase getParentContext(); - - /** - * Returns the model root for this statement. - * - * @return root context of statement - */ - @Override - public abstract RootStatementContext getRoot(); - - @Override - public final @NonNull Registry getBehaviourRegistry() { - return getRoot().getBehaviourRegistryImpl(); - } - - @Override - public final YangVersion getRootVersion() { - return getRoot().getRootVersionImpl(); - } - - @Override - public final void setRootVersion(final YangVersion version) { - getRoot().setRootVersionImpl(version); - } - - @Override - public final void addMutableStmtToSeal(final MutableStatement mutableStatement) { - getRoot().addMutableStmtToSealImpl(mutableStatement); - } - - @Override - public final void addRequiredSource(final SourceIdentifier dependency) { - getRoot().addRequiredSourceImpl(dependency); - } - - @Override - public final void setRootIdentifier(final SourceIdentifier identifier) { - getRoot().setRootIdentifierImpl(identifier); - } - - @Override - public final boolean isEnabledSemanticVersioning() { - return getRoot().isEnabledSemanticVersioningImpl(); - } - - @Override - public final > Map getAllFromCurrentStmtCtxNamespace( - final Class type) { - return getLocalNamespace(type); - } - - @Override - public final > Map getAllFromNamespace(final Class type) { - return getNamespace(type); - } - - /** - * Associate a value with a key within a namespace. - * - * @param type Namespace type - * @param key Key - * @param value value - * @param namespace key type - * @param namespace value type - * @param namespace type - * @param key type - * @param value type - * @throws NamespaceNotAvailableException when the namespace is not available. - */ - @Override - public final > void addToNs( + public final > void addToNs( final Class<@NonNull N> type, final T key, final U value) { addToNamespace(type, key, value); } - @Override - public abstract Collection> mutableDeclaredSubstatements(); - - /** - * Return a value associated with specified key within a namespace. - * - * @param type Namespace type - * @param key Key - * @param namespace key type - * @param namespace value type - * @param namespace type - * @param key type - * @return Value, or null if there is no element - * @throws NamespaceNotAvailableException when the namespace is not available. - */ - @Override - public final > V getFromNamespace( - final Class<@NonNull N> type, final T key) { - return getBehaviourRegistry().getNamespaceBehaviour(type).getFrom(this, key); - } - 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; } @@ -383,7 +183,7 @@ public abstract class StatementContextBase, E final Iterator> iterator = effective.iterator(); while (iterator.hasNext()) { final StmtContext next = iterator.next(); - if (statementDef.equals(next.getPublicDefinition())) { + if (statementDef.equals(next.publicDefinition())) { iterator.remove(); } } @@ -406,8 +206,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); @@ -417,10 +217,10 @@ 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.getPublicDefinition()) && statementArg.equals(next.rawStatementArgument())) { + if (statementDef.equals(next.publicDefinition()) && statementArg.equals(next.rawArgument())) { iterator.remove(); } } @@ -435,7 +235,7 @@ public abstract class StatementContextBase, E // FIXME: YANGTOOLS-652: This does not need to be a SubstatementContext, in can be a specialized // StatementContextBase subclass. final Mutable ret = new SubstatementContext<>(this, new StatementDefinitionContext<>(support), - ImplicitSubstatement.of(getStatementSourceReference()), rawArg); + ImplicitSubstatement.of(sourceReference()), rawArg); support.onStatementAdded(ret); addEffectiveSubstatement(ret); return ret; @@ -450,12 +250,12 @@ 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 List> resized = beforeAddEffectiveStatement(effective, 1); + final ReactorStmtCtx stmt = (ReactorStmtCtx) substatement; final ModelProcessingPhase phase = completedPhase; if (phase != null) { ensureCompletedPhase(stmt, phase); @@ -482,15 +282,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 List> resized = beforeAddEffectiveStatement(effective, statements.size()); + final Collection> casted = + (Collection>) statements; final ModelProcessingPhase phase = completedPhase; if (phase != null) { - for (StatementContextBase stmt : casted) { + for (ReactorStmtCtx stmt : casted) { ensureCompletedPhase(stmt, phase); } } @@ -499,84 +298,85 @@ 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); + ensureCompletedPhase((ReactorStmtCtx) stmt, phase); } } // 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) { + private static void ensureCompletedPhase(final ReactorStmtCtx stmt, final ModelProcessingPhase phase) { verify(stmt.tryToCompletePhase(phase), "Statement %s cannot complete phase %s", stmt, phase); } 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) { + 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", - getStatementSourceReference()); + 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, - "Effective statement cannot be added in declared phase at: %s", getStatementSourceReference()); + "Effective statement cannot be added in declared phase at: %s", sourceReference()); return effective.isEmpty() ? new ArrayList<>(toAdd) : effective; } - // These two exists only due to memory optimization, should live in AbstractResumedStatement - final boolean fullyDefined() { - return fullyDefined; - } - - final void setFullyDefined() { - fullyDefined = true; - } - @Override - public E buildEffective() { - final E existing; - return (existing = effectiveInstance) != null ? existing : loadEffective(); + final E createEffective() { + final E result = createEffective(definition.getFactory()); + if (result instanceof MutableStatement) { + getRoot().addMutableStmtToSeal((MutableStatement) result); + } + return result; } - private E loadEffective() { - return effectiveInstance = definition.getFactory().createEffective(new BaseCurrentEffectiveStmtCtx<>(this), - streamDeclared(), streamEffective()); + @NonNull E createEffective(final StatementFactory factory) { + return createEffective(factory, this); } - abstract Stream> streamDeclared(); + // 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(); /** - * Try to execute current {@link ModelProcessingPhase} of source parsing. If the phase has already been executed, - * this method does nothing. + * Return a stream of inferred 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 effective statements. */ - final boolean tryToCompletePhase(final ModelProcessingPhase phase) { - return phase.isCompletedBy(completedPhase) || doTryToCompletePhase(phase); - } + // 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) { + @Override + final boolean doTryToCompletePhase(final ModelProcessingPhase phase) { final boolean finished = phaseMutation.isEmpty() ? true : runMutations(phase); if (completeChildren(phase) && finished) { onPhaseCompleted(phase); @@ -590,7 +390,7 @@ public abstract class StatementContextBase, E for (final StatementContextBase child : mutableDeclaredSubstatements()) { finished &= child.tryToCompletePhase(phase); } - for (final StatementContextBase child : effectiveChildrenToComplete()) { + for (final ReactorStmtCtx child : effectiveChildrenToComplete()) { finished &= child.tryToCompletePhase(phase); } return finished; @@ -636,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()) { @@ -643,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()) { @@ -667,27 +517,12 @@ public abstract class StatementContextBase, E definition.onDeclarationFinished(this, phase); } - /** - * Return the context in which this statement was defined. - * - * @return statement definition - */ - protected final @NonNull StatementDefinitionContext definition() { - return definition; - } - - @Override - protected void checkLocalNamespaceAllowed(final Class> type) { - definition.checkNamespaceAllowed(type); - } - @Override - protected > void onNamespaceElementAdded(final Class type, final K key, - final V value) { - // definition().onNamespaceElementAdded(this, type, key, value); + final StatementDefinitionContext definition() { + 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) { @@ -704,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); @@ -730,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, @@ -739,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, @@ -749,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", @@ -758,16 +593,6 @@ public abstract class StatementContextBase, E return (NamespaceBehaviourWithListeners) behaviour; } - @Override - public final StatementDefinition publicDefinition() { - return definition.getPublicView(); - } - - @Override - public final ModelActionBuilder newInferenceAction(final ModelProcessingPhase phase) { - return getRoot().getSourceContext().newInferenceAction(phase); - } - private static Multimap newMultimap() { return Multimaps.newListMultimap(new EnumMap<>(ModelProcessingPhase.class), () -> new ArrayList<>(1)); } @@ -781,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", getStatementSourceReference()); - checkNotNull(listener, "Statement context phase listener cannot be null at: %s", getStatementSourceReference()); + requireNonNull(phase, "Statement context processing phase cannot be null"); + requireNonNull(listener, "Statement context phase listener cannot be null"); ModelProcessingPhase finishedPhase = completedPhase; while (finishedPhase != null) { @@ -808,7 +633,7 @@ public abstract class StatementContextBase, E ModelProcessingPhase finishedPhase = completedPhase; while (finishedPhase != null) { checkState(!phase.equals(finishedPhase), "Mutation registered after phase was completed at: %s", - getStatementSourceReference()); + sourceReference()); finishedPhase = finishedPhase.getPreviousPhase(); } @@ -835,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(this); + 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: @@ -863,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) { @@ -874,15 +719,15 @@ public abstract class StatementContextBase, E private , Z extends EffectiveStatement> Mutable childCopyOf( final StatementContextBase original, final CopyType type, final QNameModule targetModule) { final Optional> implicitParent = definition.getImplicitParentFor( - original.getPublicDefinition()); + original.publicDefinition()); final StatementContextBase result; final InferredStatementContext copy; if (implicitParent.isPresent()) { final StatementDefinitionContext def = new StatementDefinitionContext<>(implicitParent.get()); - result = new SubstatementContext(this, def, original.getStatementSourceReference(), - original.rawStatementArgument(), original.getStatementArgument(), type); + result = new SubstatementContext(this, def, original.sourceReference(), original.rawArgument(), + original.argument(), type); final CopyType childCopyType; switch (type) { @@ -908,6 +753,11 @@ public abstract class StatementContextBase, E return result; } + @Override + final ReplicaStatementContext replicaAsChildOf(final StatementContextBase parent) { + return new ReplicaStatementContext<>(parent, this); + } + private static void checkEffectiveModelCompleted(final StmtContext stmt) { final ModelProcessingPhase phase = stmt.getCompletedPhase(); checkState(phase == ModelProcessingPhase.EFFECTIVE_MODEL, @@ -922,16 +772,15 @@ public abstract class StatementContextBase, E @Beta public final StatementContextBase wrapWithImplicit(final StatementContextBase original) { final Optional> optImplicit = definition.getImplicitParentFor( - original.getPublicDefinition()); + original.publicDefinition()); if (optImplicit.isEmpty()) { return original; } final StatementDefinitionContext def = new StatementDefinitionContext<>(optImplicit.get()); - final CopyType type = original.getCopyHistory().getLastOperation(); + final CopyType type = original.history().getLastOperation(); final SubstatementContext result = new SubstatementContext(original.getParentContext(), def, - original.getStatementSourceReference(), original.rawStatementArgument(), original.getStatementArgument(), - type); + original.sourceReference(), original.rawArgument(), original.argument(), type); result.addEffectiveSubstatement(original.reparent(result)); result.setCompletedPhase(original.getCompletedPhase()); @@ -946,152 +795,4 @@ public abstract class StatementContextBase, E * @return True if {@link #allSubstatements()} and {@link #allSubstatementsStream()} would return an empty stream. */ abstract boolean hasEmptySubstatements(); - - /** - * Config statements are not all that common which means we are performing a recursive search towards the root - * every time {@link #isConfiguration()} is invoked. This is quite expensive because it causes a linear search - * for the (usually non-existent) config statement. - * - *

- * This method maintains a resolution cache, so once we have returned a result, we will keep on returning the same - * result without performing any lookups, solely to support {@link SubstatementContext#isConfiguration()}. - * - *

- * Note: use of this method implies that {@link #isIgnoringConfig()} is realized with - * {@link #isIgnoringConfig(StatementContextBase)}. - */ - final boolean isConfiguration(final StatementContextBase parent) { - final int fl = flags & SET_CONFIGURATION; - if (fl != 0) { - return fl == SET_CONFIGURATION; - } - if (isIgnoringConfig(parent)) { - // Note: SET_CONFIGURATION has been stored in flags - return true; - } - - final boolean isConfig; - final Optional optConfig = findSubstatementArgument(ConfigEffectiveStatement.class); - if (optConfig.isPresent()) { - isConfig = optConfig.orElseThrow(); - if (isConfig) { - // Validity check: if parent is config=false this cannot be a config=true - InferenceException.throwIf(!parent.isConfiguration(), getStatementSourceReference(), - "Parent node has config=false, this node must not be specifed as config=true"); - } - } else { - // If "config" statement is not specified, the default is the same as the parent's "config" value. - isConfig = parent.isConfiguration(); - } - - // Resolved, make sure we cache this return - flags |= isConfig ? SET_CONFIGURATION : HAVE_CONFIGURATION; - return isConfig; - } - - protected abstract boolean isIgnoringConfig(); - - /** - * This method maintains a resolution cache for ignore config, so once we have returned a result, we will - * keep on returning the same result without performing any lookups. Exists only to support - * {@link SubstatementContext#isIgnoringConfig()}. - * - *

- * Note: use of this method implies that {@link #isConfiguration()} is realized with - * {@link #isConfiguration(StatementContextBase)}. - */ - final boolean isIgnoringConfig(final StatementContextBase parent) { - final int fl = flags & SET_IGNORE_CONFIG; - if (fl != 0) { - return fl == SET_IGNORE_CONFIG; - } - if (definition.support().isIgnoringConfig() || parent.isIgnoringConfig()) { - flags |= SET_IGNORE_CONFIG; - return true; - } - - flags |= HAVE_IGNORE_CONFIG; - return false; - } - - protected abstract boolean isIgnoringIfFeatures(); - - /** - * This method maintains a resolution cache for ignore if-feature, so once we have returned a result, we will - * keep on returning the same result without performing any lookups. Exists only to support - * {@link SubstatementContext#isIgnoringIfFeatures()}. - */ - final boolean isIgnoringIfFeatures(final StatementContextBase parent) { - final int fl = flags & SET_IGNORE_IF_FEATURE; - if (fl != 0) { - return fl == SET_IGNORE_IF_FEATURE; - } - if (definition.support().isIgnoringIfFeatures() || parent.isIgnoringIfFeatures()) { - flags |= SET_IGNORE_IF_FEATURE; - return true; - } - - flags |= HAVE_IGNORE_IF_FEATURE; - return false; - } - - // Exists only to support {SubstatementContext,InferredStatementContext}.getSchemaPath() - @Deprecated - final @NonNull Optional substatementGetSchemaPath() { - SchemaPath local = schemaPath; - if (local == null) { - synchronized (this) { - local = schemaPath; - if (local == null) { - schemaPath = local = createSchemaPath(coerceParentContext()); - } - } - } - - return Optional.ofNullable(local); - } - - @Deprecated - private SchemaPath createSchemaPath(final Mutable parent) { - final Optional maybeParentPath = parent.getSchemaPath(); - verify(maybeParentPath.isPresent(), "Parent %s does not have a SchemaPath", parent); - final SchemaPath parentPath = maybeParentPath.get(); - - if (StmtContextUtils.isUnknownStatement(this)) { - return parentPath.createChild(getPublicDefinition().getStatementName()); - } - final Object argument = getStatementArgument(); - if (argument instanceof QName) { - final QName qname = (QName) argument; - if (producesDeclared(UsesStatement.class)) { - return maybeParentPath.orElse(null); - } - - return parentPath.createChild(qname); - } - if (argument instanceof String) { - // FIXME: This may yield illegal argument exceptions - final Optional> originalCtx = getOriginalCtx(); - final QName qname = StmtContextUtils.qnameFromArgument(originalCtx.orElse(this), (String) argument); - return parentPath.createChild(qname); - } - if (argument instanceof SchemaNodeIdentifier - && (producesDeclared(AugmentStatement.class) || producesDeclared(RefineStatement.class) - || producesDeclared(DeviationStatement.class))) { - - return parentPath.createChild(((SchemaNodeIdentifier) argument).getNodeIdentifiers()); - } - - // FIXME: this does not look right - return maybeParentPath.orElse(null); - } - - @Override - public final String toString() { - return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString(); - } - - protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) { - return toStringHelper.add("definition", definition).add("rawArgument", rawStatementArgument()); - } }