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=02fb8d222e5f01c48c967bfd1d8b25fc280f421f;hb=4929fb6d63325fcbcbd72c16758a44ba19c3d20d;hp=89e2bbb805eb90a6dc7779b4c039daee50eb9e75;hpb=484bd5df7543f019d9fa46a676251b6897adf6fe;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 89e2bbb805..02fb8d222e 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 @@ -10,6 +10,7 @@ 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; @@ -18,7 +19,6 @@ 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; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import java.util.ArrayList; @@ -28,38 +28,47 @@ 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.Objects; import java.util.Optional; 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.model.api.YangStmtMapping; +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.meta.StatementSource; -import org.opendaylight.yangtools.yang.model.api.stmt.ConfigStatement; +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.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.StatementSourceReference; -import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter.ResumedStatement; 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; @@ -75,7 +84,7 @@ import org.slf4j.LoggerFactory; * @param Effective Statement representation */ public abstract class StatementContextBase, E extends EffectiveStatement> - extends NamespaceStorageSupport implements Mutable, ResumedStatement { + extends NamespaceStorageSupport implements Mutable { /** * Event listener when an item is added to model namespace. */ @@ -124,69 +133,57 @@ public abstract class StatementContextBase, E 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 @NonNull StatementDefinitionContext definition; - private final @NonNull StatementSourceReference statementDeclSource; - private final StmtContext originalCtx; - private final StmtContext prevCopyCtx; private final CopyHistory copyHistory; - private final String rawArgument; + // 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; private Multimap phaseListeners = ImmutableMultimap.of(); private Multimap phaseMutation = ImmutableMultimap.of(); - private List> effective = ImmutableList.of(); + private List> effective = ImmutableList.of(); private List> effectOfStatement = ImmutableList.of(); - private StatementMap substatements = StatementMap.empty(); private @Nullable ModelProcessingPhase completedPhase; - private @Nullable D declaredInstance; private @Nullable E effectiveInstance; - // Common state bits + // 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 booleans and + // Flag for InferredStatementContext. This is hiding in the alignment shadow created by above boolean. + private boolean substatementsInitialized; + + // Flags for use with SubstatementContext. These are hiding in the alignment shadow created by above boolean and // hence improve memory layout. private byte flags; - StatementContextBase(final StatementDefinitionContext def, final StatementSourceReference ref, - final String rawArgument) { - this.definition = requireNonNull(def); - this.statementDeclSource = requireNonNull(ref); - this.rawArgument = def.internArgument(rawArgument); - this.copyHistory = CopyHistory.original(); - this.originalCtx = null; - this.prevCopyCtx = null; - } + // 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; - StatementContextBase(final StatementDefinitionContext def, final StatementSourceReference ref, - final String rawArgument, final CopyType copyType) { - this.definition = requireNonNull(def); - this.statementDeclSource = requireNonNull(ref); - this.rawArgument = rawArgument; - this.copyHistory = CopyHistory.of(copyType, CopyHistory.original()); - this.originalCtx = null; - this.prevCopyCtx = null; + // Copy constructor used by subclasses to implement reparent() + StatementContextBase(final StatementContextBase 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 StatementContextBase original, final CopyType copyType) { - this.definition = original.definition; - this.statementDeclSource = original.statementDeclSource; - this.rawArgument = original.rawArgument; - this.copyHistory = CopyHistory.of(copyType, original.getCopyHistory()); - this.originalCtx = original.getOriginalCtx().orElse(original); - this.prevCopyCtx = original; + StatementContextBase(final StatementDefinitionContext def) { + this.definition = requireNonNull(def); + this.copyHistory = CopyHistory.original(); } - StatementContextBase(final StatementContextBase original) { - this.definition = original.definition; - this.statementDeclSource = original.statementDeclSource; - this.rawArgument = original.rawArgument; - this.copyHistory = original.getCopyHistory(); - this.originalCtx = original.getOriginalCtx().orElse(original); - this.prevCopyCtx = original; - this.substatements = original.substatements; - this.effective = original.effective; + StatementContextBase(final StatementDefinitionContext def, final CopyHistory copyHistory) { + this.definition = requireNonNull(def); + this.copyHistory = requireNonNull(copyHistory); } @Override @@ -260,23 +257,14 @@ public abstract class StatementContextBase, E return copyHistory; } - @Override - public Optional> getOriginalCtx() { - return Optional.ofNullable(originalCtx); - } - - @Override - public Optional> getPreviousCopyCtx() { - return Optional.ofNullable(prevCopyCtx); - } - @Override public ModelProcessingPhase getCompletedPhase() { return completedPhase; } - @Override - public void setCompletedPhase(final ModelProcessingPhase completedPhase) { + // FIXME: this should be propagated through a correct constructor + @Deprecated + final void setCompletedPhase(final ModelProcessingPhase completedPhase) { this.completedPhase = completedPhase; } @@ -292,33 +280,94 @@ public abstract class StatementContextBase, E public abstract RootStatementContext getRoot(); @Override - public StatementSource getStatementSource() { - return statementDeclSource.getStatementSource(); + 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 StatementSourceReference getStatementSourceReference() { - return statementDeclSource; + public final boolean isEnabledSemanticVersioning() { + return getRoot().isEnabledSemanticVersioningImpl(); } @Override - public final String rawStatementArgument() { - return rawArgument; + public StatementSource getStatementSource() { + return getStatementSourceReference().getStatementSource(); + } + + @Override + public final > Map getAllFromCurrentStmtCtxNamespace( + final Class type) { + return getLocalNamespace(type); } @Override - public Collection> declaredSubstatements() { - return substatements.values(); + 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 Collection> mutableDeclaredSubstatements() { - return substatements.values(); + public final > void addToNs( + final Class<@NonNull N> type, final T key, final U value) { + addToNamespace(type, key, value); } @Override - public Collection> effectiveSubstatements() { - return mutableEffectiveSubstatements(); + 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); } @Override @@ -374,7 +423,7 @@ public abstract class StatementContextBase, E return; } - 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())) { @@ -385,6 +434,19 @@ public abstract class StatementContextBase, E shrinkEffective(); } + // YANG example: RPC/action statements always have 'input' and 'output' defined + @Beta + public , Z extends EffectiveStatement> @NonNull Mutable + appendImplicitSubstatement(final StatementSupport support, final String rawArg) { + // 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); + support.onStatementAdded(ret); + addEffectiveSubstatement(ret); + return ret; + } + /** * Adds an effective statement to collection of substatements. * @@ -395,8 +457,15 @@ public abstract class StatementContextBase, E * if statement parameter is null */ public void addEffectiveSubstatement(final Mutable substatement) { + verifyStatement(substatement); beforeAddEffectiveStatement(1); - effective.add(substatement); + + final StatementContextBase stmt = (StatementContextBase) substatement; + final ModelProcessingPhase phase = completedPhase; + if (phase != null) { + ensureCompletedPhase(stmt, phase); + } + effective.add(stmt); } /** @@ -409,16 +478,58 @@ public abstract class StatementContextBase, E * if statement parameter is null */ public void addEffectiveSubstatements(final Collection> statements) { - if (statements.isEmpty()) { - return; + if (!statements.isEmpty()) { + statements.forEach(StatementContextBase::verifyStatement); + beforeAddEffectiveStatement(statements.size()); + doAddEffectiveSubstatements(statements); } + } + + // exposed for InferredStatementContext only + final void addInitialEffectiveSubstatements(final Collection> statements) { + verify(!substatementsInitialized, "Attempted to re-initialized statement {} with {}", this, statements); + substatementsInitialized = true; + + if (!statements.isEmpty()) { + statements.forEach(StatementContextBase::verifyStatement); + beforeAddEffectiveStatementUnsafe(statements.size()); + doAddEffectiveSubstatements(statements); + } + } + + private void doAddEffectiveSubstatements(final Collection> statements) { + final Collection> casted = + (Collection>) statements; + final ModelProcessingPhase phase = completedPhase; + if (phase != null) { + for (StatementContextBase stmt : casted) { + ensureCompletedPhase(stmt, phase); + } + } + + effective.addAll(casted); + } - statements.forEach(Objects::requireNonNull); - beforeAddEffectiveStatement(statements.size()); - effective.addAll(statements); + // 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 verifyStatement(final Mutable stmt) { + verify(stmt instanceof StatementContextBase, "Unexpected statement %s", stmt); } private void beforeAddEffectiveStatement(final int toAdd) { + // We cannot allow statement to be further mutated + verify(completedPhase != ModelProcessingPhase.EFFECTIVE_MODEL, "Cannot modify finished statement at %s", + getStatementSourceReference()); + beforeAddEffectiveStatementUnsafe(toAdd); + } + + private void beforeAddEffectiveStatementUnsafe(final int toAdd) { final ModelProcessingPhase inProgressPhase = getRoot().getSourceContext().getInProgressPhase(); checkState(inProgressPhase == ModelProcessingPhase.FULL_DECLARATION || inProgressPhase == ModelProcessingPhase.EFFECTIVE_MODEL, @@ -429,140 +540,90 @@ public abstract class StatementContextBase, E } } - /** - * Create a new substatement at the specified offset. - * - * @param offset Substatement offset - * @param def definition context - * @param ref source reference - * @param argument statement argument - * @param new substatement argument type - * @param new substatement declared type - * @param new substatement effective type - * @return A new substatement - */ - @SuppressWarnings("checkstyle:methodTypeParameterName") - public final , Z extends EffectiveStatement> - StatementContextBase createSubstatement(final int offset, - final StatementDefinitionContext def, final StatementSourceReference ref, - final String argument) { - final ModelProcessingPhase inProgressPhase = getRoot().getSourceContext().getInProgressPhase(); - checkState(inProgressPhase != ModelProcessingPhase.EFFECTIVE_MODEL, - "Declared statement cannot be added in effective phase at: %s", getStatementSourceReference()); - - final Optional> implicitParent = definition.getImplicitParentFor(def.getPublicView()); - if (implicitParent.isPresent()) { - return createImplicitParent(offset, implicitParent.get(), ref, argument).createSubstatement(offset, def, - ref, argument); - } - - final StatementContextBase ret = new SubstatementContext<>(this, def, ref, argument); - substatements = substatements.put(offset, ret); - def.onStatementAdded(ret); - return ret; + // These two exists only due to memory optimization, should live in AbstractResumedStatement + final boolean fullyDefined() { + return fullyDefined; } - private StatementContextBase createImplicitParent(final int offset, - final StatementSupport implicitParent, final StatementSourceReference ref, final String argument) { - final StatementDefinitionContext def = new StatementDefinitionContext<>(implicitParent); - return createSubstatement(offset, def, ImplicitSubstatement.of(ref), argument); + final void setFullyDefined() { + fullyDefined = true; } - public void appendImplicitStatement(final StatementSupport statementToAdd) { - createSubstatement(substatements.capacity(), new StatementDefinitionContext<>(statementToAdd), - ImplicitSubstatement.of(getStatementSourceReference()), null); + // These two exist only due to memory optimization, should live in InferredStatementContext + final boolean substatementsInitialized() { + return substatementsInitialized; } - /** - * Lookup substatement by its offset in this statement. - * - * @param offset Substatement offset - * @return Substatement, or null if substatement does not exist. - */ - final StatementContextBase lookupSubstatement(final int offset) { - return substatements.get(offset); + final void setSubstatementsInitialized() { + substatementsInitialized = true; } - final void setFullyDefined() { - this.fullyDefined = true; + @Override + public E buildEffective() { + final E existing; + return (existing = effectiveInstance) != null ? existing : loadEffective(); } - final void resizeSubstatements(final int expectedSize) { - substatements = substatements.ensureCapacity(expectedSize); + private E loadEffective() { + return effectiveInstance = definition.getFactory().createEffective(this); } - final void walkChildren(final ModelProcessingPhase phase) { - checkState(fullyDefined); - substatements.values().forEach(stmt -> { - stmt.walkChildren(phase); - stmt.endDeclared(phase); - }); + /** + * Try to execute current {@link ModelProcessingPhase} of source parsing. If the phase has already been executed, + * this method does nothing. + * + * @param phase to be executed (completed) + * @return true if phase was successfully completed + * @throws SourceException when an error occurred in source parsing + */ + final boolean tryToCompletePhase(final ModelProcessingPhase phase) { + return phase.isCompletedBy(completedPhase) || doTryToCompletePhase(phase); } - @Override - public D buildDeclared() { - checkArgument(completedPhase == ModelProcessingPhase.FULL_DECLARATION - || completedPhase == ModelProcessingPhase.EFFECTIVE_MODEL); - if (declaredInstance == null) { - declaredInstance = definition().getFactory().createDeclared(this); + private boolean doTryToCompletePhase(final ModelProcessingPhase phase) { + final boolean finished = phaseMutation.isEmpty() ? true : runMutations(phase); + if (completeChildren(phase) && finished) { + onPhaseCompleted(phase); + return true; } - return declaredInstance; + return false; } - @Override - public E buildEffective() { - if (effectiveInstance == null) { - effectiveInstance = definition().getFactory().createEffective(this); + private boolean completeChildren(final ModelProcessingPhase phase) { + boolean finished = true; + for (final StatementContextBase child : mutableDeclaredSubstatements()) { + finished &= child.tryToCompletePhase(phase); + } + for (final StatementContextBase child : effective) { + finished &= child.tryToCompletePhase(phase); } - return effectiveInstance; + return finished; } - /** - * tries to execute current {@link ModelProcessingPhase} of source parsing. - * - * @param phase - * to be executed (completed) - * @return if phase was successfully completed - * @throws SourceException - * when an error occurred in source parsing - */ - boolean tryToCompletePhase(final ModelProcessingPhase phase) { - - boolean finished = true; + private boolean runMutations(final ModelProcessingPhase phase) { final Collection openMutations = phaseMutation.get(phase); - if (!openMutations.isEmpty()) { - final Iterator it = openMutations.iterator(); - while (it.hasNext()) { - final ContextMutation current = it.next(); - if (current.isFinished()) { - it.remove(); - } else { - finished = false; - } - } + return openMutations.isEmpty() ? true : runMutations(phase, openMutations); + } - if (openMutations.isEmpty()) { - phaseMutation.removeAll(phase); - if (phaseMutation.isEmpty()) { - phaseMutation = ImmutableMultimap.of(); - } + private boolean runMutations(final ModelProcessingPhase phase, final Collection openMutations) { + boolean finished = true; + final Iterator it = openMutations.iterator(); + while (it.hasNext()) { + final ContextMutation current = it.next(); + if (current.isFinished()) { + it.remove(); + } else { + finished = false; } } - for (final StatementContextBase child : substatements.values()) { - finished &= child.tryToCompletePhase(phase); - } - for (final Mutable child : effective) { - if (child instanceof StatementContextBase) { - finished &= ((StatementContextBase) child).tryToCompletePhase(phase); + if (openMutations.isEmpty()) { + phaseMutation.removeAll(phase); + if (phaseMutation.isEmpty()) { + phaseMutation = ImmutableMultimap.of(); } } - - if (finished) { - onPhaseCompleted(phase); - return true; - } - return false; + return finished; } /** @@ -577,10 +638,12 @@ public abstract class StatementContextBase, E completedPhase = phase; final Collection listeners = phaseListeners.get(phase); - if (listeners.isEmpty()) { - return; + if (!listeners.isEmpty()) { + runPhaseListeners(phase, listeners); } + } + private void runPhaseListeners(final ModelProcessingPhase phase, final Collection listeners) { final Iterator listener = listeners.iterator(); while (listener.hasNext()) { final OnPhaseFinished next = listener.next(); @@ -601,7 +664,7 @@ public abstract class StatementContextBase, E * Ends declared section of current node. */ void endDeclared(final ModelProcessingPhase phase) { - definition().onDeclarationFinished(this, phase); + definition.onDeclarationFinished(this, phase); } /** @@ -615,7 +678,7 @@ public abstract class StatementContextBase, E @Override protected void checkLocalNamespaceAllowed(final Class> type) { - definition().checkNamespaceAllowed(type); + definition.checkNamespaceAllowed(type); } @Override @@ -697,7 +760,7 @@ public abstract class StatementContextBase, E @Override public StatementDefinition getPublicDefinition() { - return definition().getPublicView(); + return definition.getPublicView(); } @Override @@ -757,31 +820,62 @@ public abstract class StatementContextBase, E } @Override - public > void addContext(final Class namespace, + public > void addContext(final Class<@NonNull N> namespace, final KT key,final StmtContext stmt) { addContextToNamespace(namespace, key, stmt); } + @Override + public Optional> copyAsChildOf(final Mutable parent, final CopyType type, + final QNameModule targetModule) { + checkEffectiveModelCompleted(this); + + final StatementSupport support = definition.support(); + final CopyPolicy policy = support.applyCopyPolicy(this, parent, type, targetModule); + switch (policy) { + case CONTEXT_INDEPENDENT: + if (hasEmptySubstatements()) { + // This statement is context-independent and has no substatements -- hence it can be freely shared. + return Optional.of(this); + } + // 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)); + case IGNORE: + return Optional.empty(); + case REJECT: + throw new IllegalStateException("Statement " + support.getPublicView() + " should never be copied"); + default: + throw new IllegalStateException("Unhandled policy " + policy); + } + } + @Override public final Mutable childCopyOf(final StmtContext stmt, final CopyType type, final QNameModule targetModule) { - checkState(stmt.getCompletedPhase() == ModelProcessingPhase.EFFECTIVE_MODEL, - "Attempted to copy statement %s which has completed phase %s", stmt, stmt.getCompletedPhase()); - checkArgument(stmt instanceof SubstatementContext, "Unsupported statement %s", stmt); - return childCopyOf((SubstatementContext) stmt, type, targetModule); + checkEffectiveModelCompleted(stmt); + checkArgument(stmt instanceof StatementContextBase, "Unsupported statement %s", stmt); + return childCopyOf((StatementContextBase) stmt, type, targetModule); } private , Z extends EffectiveStatement> Mutable childCopyOf( - final SubstatementContext original, final CopyType type, final QNameModule targetModule) { + final StatementContextBase original, final CopyType type, final QNameModule targetModule) { final Optional> implicitParent = definition.getImplicitParentFor( original.getPublicDefinition()); - final SubstatementContext result; - final SubstatementContext copy; + final StatementContextBase result; + final InferredStatementContext copy; if (implicitParent.isPresent()) { final StatementDefinitionContext def = new StatementDefinitionContext<>(implicitParent.get()); - result = new SubstatementContext(this, def, original.getSourceReference(), + result = new SubstatementContext(this, def, original.getStatementSourceReference(), original.rawStatementArgument(), original.getStatementArgument(), type); final CopyType childCopyType; @@ -798,30 +892,20 @@ public abstract class StatementContextBase, E childCopyType = type; } - copy = new SubstatementContext<>(original, result, childCopyType, targetModule); + copy = new InferredStatementContext<>(result, original, childCopyType, type, targetModule); result.addEffectiveSubstatement(copy); } else { - result = copy = new SubstatementContext<>(original, this, type, targetModule); + result = copy = new InferredStatementContext<>(this, original, type, type, targetModule); } - original.definition().onStatementAdded(copy); - original.copyTo(copy, type, targetModule); + original.definition.onStatementAdded(copy); return result; } - @Override - public @NonNull StatementDefinition getDefinition() { - return getPublicDefinition(); - } - - @Override - public @NonNull StatementSourceReference getSourceReference() { - return getStatementSourceReference(); - } - - @Override - public boolean isFullyDefined() { - return fullyDefined; + private static void checkEffectiveModelCompleted(final StmtContext stmt) { + final ModelProcessingPhase phase = stmt.getCompletedPhase(); + checkState(phase == ModelProcessingPhase.EFFECTIVE_MODEL, + "Attempted to copy statement %s which has completed phase %s", stmt, phase); } @Beta @@ -843,11 +927,24 @@ public abstract class StatementContextBase, E original.getStatementSourceReference(), original.rawStatementArgument(), original.getStatementArgument(), type); - result.addEffectiveSubstatement(new SubstatementContext<>(original, result)); + result.addEffectiveSubstatement(original.reparent(result)); result.setCompletedPhase(original.getCompletedPhase()); return result; } + abstract StatementContextBase reparent(StatementContextBase newParent); + + /** + * Indicate that the set of substatements is empty. This is a preferred shortcut to substatement stream filtering. + * + * @return True if {@link #allSubstatements()} and {@link #allSubstatementsStream()} would return an empty stream. + */ + abstract boolean hasEmptySubstatements(); + + final boolean hasEmptyEffectiveSubstatements() { + return effective.isEmpty(); + } + /** * 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 @@ -871,11 +968,10 @@ public abstract class StatementContextBase, E return true; } - final StmtContext configStatement = StmtContextUtils.findFirstSubstatement(this, - ConfigStatement.class); final boolean isConfig; - if (configStatement != null) { - isConfig = configStatement.coerceStatementArgument(); + 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(), @@ -907,7 +1003,7 @@ public abstract class StatementContextBase, E if (fl != 0) { return fl == SET_IGNORE_CONFIG; } - if (definition().isIgnoringConfig() || parent.isIgnoringConfig()) { + if (definition.support().isIgnoringConfig() || parent.isIgnoringConfig()) { flags |= SET_IGNORE_CONFIG; return true; } @@ -928,7 +1024,7 @@ public abstract class StatementContextBase, E if (fl != 0) { return fl == SET_IGNORE_IF_FEATURE; } - if (definition().isIgnoringIfFeatures() || parent.isIgnoringIfFeatures()) { + if (definition.support().isIgnoringIfFeatures() || parent.isIgnoringIfFeatures()) { flags |= SET_IGNORE_IF_FEATURE; return true; } @@ -937,63 +1033,55 @@ public abstract class StatementContextBase, E return false; } - final void copyTo(final StatementContextBase target, final CopyType typeOfCopy, - @Nullable final QNameModule targetModule) { - final Collection> buffer = new ArrayList<>(substatements.size() + effective.size()); - - for (final Mutable stmtContext : substatements.values()) { - if (stmtContext.isSupportedByFeatures()) { - copySubstatement(stmtContext, target, typeOfCopy, targetModule, buffer); + // 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()); + } } } - for (final Mutable stmtContext : effective) { - copySubstatement(stmtContext, target, typeOfCopy, targetModule, buffer); - } - - target.addEffectiveSubstatements(buffer); + return Optional.ofNullable(local); } - private void copySubstatement(final Mutable stmtContext, final Mutable target, - final CopyType typeOfCopy, final QNameModule newQNameModule, final Collection> buffer) { - if (needToCopyByUses(stmtContext)) { - final Mutable copy = target.childCopyOf(stmtContext, typeOfCopy, newQNameModule); - LOG.debug("Copying substatement {} for {} as {}", stmtContext, this, copy); - buffer.add(copy); - } else if (isReusedByUses(stmtContext)) { - LOG.debug("Reusing substatement {} for {}", stmtContext, this); - buffer.add(stmtContext); - } else { - LOG.debug("Skipping statement {}", stmtContext); - } - } + @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(); - // FIXME: revise this, as it seems to be wrong - private static final ImmutableSet NOCOPY_FROM_GROUPING_SET = ImmutableSet.of( - YangStmtMapping.DESCRIPTION, - YangStmtMapping.REFERENCE, - YangStmtMapping.STATUS); - private static final ImmutableSet REUSED_DEF_SET = ImmutableSet.of( - YangStmtMapping.TYPE, - YangStmtMapping.TYPEDEF, - YangStmtMapping.USES); + 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); + } - private static boolean needToCopyByUses(final StmtContext stmtContext) { - final StatementDefinition def = stmtContext.getPublicDefinition(); - if (REUSED_DEF_SET.contains(def)) { - LOG.debug("Will reuse {} statement {}", def, stmtContext); - return false; + return parentPath.createChild(qname); } - if (NOCOPY_FROM_GROUPING_SET.contains(def)) { - return !YangStmtMapping.GROUPING.equals(stmtContext.coerceParentContext().getPublicDefinition()); + 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))) { - LOG.debug("Will copy {} statement {}", def, stmtContext); - return true; - } + return parentPath.createChild(((SchemaNodeIdentifier) argument).getNodeIdentifiers()); + } - private static boolean isReusedByUses(final StmtContext stmtContext) { - return REUSED_DEF_SET.contains(stmtContext.getPublicDefinition()); + // FIXME: this does not look right + return maybeParentPath.orElse(null); } @Override @@ -1002,6 +1090,6 @@ public abstract class StatementContextBase, E } protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) { - return toStringHelper.add("definition", definition).add("rawArgument", rawArgument); + return toStringHelper.add("definition", definition).add("rawArgument", rawStatementArgument()); } }