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=abf79fde20240d82562e8546a13e4257887dacef;hb=c16177dbef202fc36833c3ab86a9f5fa59c868ba;hp=7dc216dcd8540b0aa980e3ac62f7a10f1682f9a7;hpb=cac1831a621574716e403afe7997360308758fa6;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 7dc216dcd8..abf79fde20 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 @@ -7,9 +7,14 @@ */ 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 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.base.Preconditions; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMultimap; @@ -22,13 +27,13 @@ import java.util.Collections; import java.util.EnumMap; import java.util.EventListener; import java.util.Iterator; +import java.util.List; import java.util.Map.Entry; +import java.util.Objects; import java.util.Optional; import java.util.Set; -import javax.annotation.Nonnull; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; -import org.opendaylight.yangtools.util.OptionalBoolean; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.model.api.YangStmtMapping; @@ -37,8 +42,11 @@ 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.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.NamespaceBehaviour; @@ -59,6 +67,13 @@ import org.opendaylight.yangtools.yang.parser.stmt.reactor.NamespaceBehaviourWit import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Core reactor statement implementation of {@link Mutable}. + * + * @param Argument type + * @param Declared Statement representation + * @param Effective Statement representation + */ public abstract class StatementContextBase, E extends EffectiveStatement> extends NamespaceStorageSupport implements Mutable, ResumedStatement { /** @@ -91,46 +106,87 @@ public abstract class StatementContextBase, E private static final Logger LOG = LoggerFactory.getLogger(StatementContextBase.class); - private final StatementDefinitionContext definition; - private final StatementSourceReference statementDeclSource; + // 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 @NonNull StatementDefinitionContext definition; + private final @NonNull StatementSourceReference statementDeclSource; private final StmtContext originalCtx; + private final StmtContext prevCopyCtx; private final CopyHistory copyHistory; private final String rawArgument; private Multimap phaseListeners = ImmutableMultimap.of(); private Multimap phaseMutation = ImmutableMultimap.of(); - private Collection> effective = ImmutableList.of(); - private Collection> effectOfStatement = ImmutableList.of(); + private List> effective = ImmutableList.of(); + private List> effectOfStatement = ImmutableList.of(); private StatementMap substatements = StatementMap.empty(); - private boolean isSupportedToBuildEffective = true; private @Nullable ModelProcessingPhase completedPhase; private @Nullable D declaredInstance; private @Nullable E effectiveInstance; - // BooleanFields value - private byte supportedByFeatures; - + // Common state bits + private boolean isSupportedToBuildEffective = true; private boolean fullyDefined; + // Flags for use with SubstatementContext. These are hiding in the alignment shadow created by above booleans and + // hence improve memory layout. + private byte flags; + StatementContextBase(final StatementDefinitionContext def, final StatementSourceReference ref, final String rawArgument) { - this.definition = Preconditions.checkNotNull(def); - this.statementDeclSource = Preconditions.checkNotNull(ref); + this.definition = requireNonNull(def); + this.statementDeclSource = requireNonNull(ref); this.rawArgument = def.internArgument(rawArgument); this.copyHistory = CopyHistory.original(); this.originalCtx = null; + this.prevCopyCtx = null; + } + + 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; } StatementContextBase(final StatementContextBase original, final CopyType copyType) { - this.definition = Preconditions.checkNotNull(original.definition, - "Statement context definition cannot be null copying from: %s", original.getStatementSourceReference()); - this.statementDeclSource = Preconditions.checkNotNull(original.statementDeclSource, - "Statement context statementDeclSource cannot be null copying from: %s", - original.getStatementSourceReference()); + 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 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; } @Override @@ -160,46 +216,35 @@ public abstract class StatementContextBase, E @Override public boolean isSupportedByFeatures() { - if (OptionalBoolean.isPresent(supportedByFeatures)) { - return OptionalBoolean.get(supportedByFeatures); + final int fl = flags & SET_SUPPORTED_BY_FEATURES; + if (fl != 0) { + return fl == SET_SUPPORTED_BY_FEATURES; } - if (isIgnoringIfFeatures()) { - supportedByFeatures = OptionalBoolean.of(true); + flags |= SET_SUPPORTED_BY_FEATURES; return true; } - final boolean isParentSupported = isParentSupportedByFeatures(); /* - * If parent is not supported, then this context is also not supported. - * So we do not need to check if-features statements of this context and - * we can return false immediately. + * If parent is supported, we need to check if-features statements of this context. */ - if (!isParentSupported) { - supportedByFeatures = OptionalBoolean.of(false); - return false; + 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; + } } - /* - * If parent is supported, we need to check if-features statements of - * this context. - */ - // 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); - final boolean ret = supportedFeatures == null ? true - : StmtContextUtils.checkFeatureSupport(this, supportedFeatures); - - supportedByFeatures = OptionalBoolean.of(ret); - return ret; + // Either parent is not supported or this statement is not supported + flags |= HAVE_SUPPORTED_BY_FEATURES; + return false; } protected abstract boolean isParentSupportedByFeatures(); - protected abstract boolean isIgnoringIfFeatures(); - - protected abstract boolean isIgnoringConfig(); - @Override public boolean isSupportedToBuildEffective() { return isSupportedToBuildEffective; @@ -220,6 +265,11 @@ public abstract class StatementContextBase, E return Optional.ofNullable(originalCtx); } + @Override + public Optional> getPreviousCopyCtx() { + return Optional.ofNullable(prevCopyCtx); + } + @Override public ModelProcessingPhase getCompletedPhase() { return completedPhase; @@ -238,27 +288,14 @@ public abstract class StatementContextBase, E * * @return root context of statement */ - @Nonnull @Override public abstract RootStatementContext getRoot(); - /** - * Returns the origin of the statement. - * - * @return origin of statement - */ - @Nonnull @Override public StatementSource getStatementSource() { return statementDeclSource.getStatementSource(); } - /** - * Returns a reference to statement source. - * - * @return reference of statement source - */ - @Nonnull @Override public StatementSourceReference getStatementSourceReference() { return statementDeclSource; @@ -269,13 +306,11 @@ public abstract class StatementContextBase, E return rawArgument; } - @Nonnull @Override public Collection> declaredSubstatements() { return substatements.values(); } - @Nonnull @Override public Collection> mutableDeclaredSubstatements() { return substatements.values(); @@ -286,7 +321,6 @@ public abstract class StatementContextBase, E return mutableEffectiveSubstatements(); } - @Nonnull @Override public Collection> mutableEffectiveSubstatements() { if (effective instanceof ImmutableCollection) { @@ -296,6 +330,15 @@ public abstract class StatementContextBase, E return Collections.unmodifiableCollection(effective); } + /** + * Remove a set of statements from effective statements. + * + * @param statements statements to be removed + * @deprecated This method was used by EffectiveStatementBase to restore proper order of effects of uses statements. + * It is no longer used in that capacity and slated for removal. + */ + // FIXME: 5.0.0: remove this method + @Deprecated public void removeStatementsFromEffectiveSubstatements( final Collection> statements) { if (!effective.isEmpty()) { @@ -387,14 +430,14 @@ public abstract class StatementContextBase, E return; } - statements.forEach(Preconditions::checkNotNull); + statements.forEach(Objects::requireNonNull); beforeAddEffectiveStatement(statements.size()); effective.addAll(statements); } private void beforeAddEffectiveStatement(final int toAdd) { final ModelProcessingPhase inProgressPhase = getRoot().getSourceContext().getInProgressPhase(); - Preconditions.checkState(inProgressPhase == ModelProcessingPhase.FULL_DECLARATION + checkState(inProgressPhase == ModelProcessingPhase.FULL_DECLARATION || inProgressPhase == ModelProcessingPhase.EFFECTIVE_MODEL, "Effective statement cannot be added in declared phase at: %s", getStatementSourceReference()); @@ -421,7 +464,7 @@ public abstract class StatementContextBase, E final StatementDefinitionContext def, final StatementSourceReference ref, final String argument) { final ModelProcessingPhase inProgressPhase = getRoot().getSourceContext().getInProgressPhase(); - Preconditions.checkState(inProgressPhase != ModelProcessingPhase.EFFECTIVE_MODEL, + checkState(inProgressPhase != ModelProcessingPhase.EFFECTIVE_MODEL, "Declared statement cannot be added in effective phase at: %s", getStatementSourceReference()); final Optional> implicitParent = definition.getImplicitParentFor(def.getPublicView()); @@ -466,7 +509,7 @@ public abstract class StatementContextBase, E } final void walkChildren(final ModelProcessingPhase phase) { - Preconditions.checkState(fullyDefined); + checkState(fullyDefined); substatements.values().forEach(stmt -> { stmt.walkChildren(phase); stmt.endDeclared(phase); @@ -475,7 +518,7 @@ public abstract class StatementContextBase, E @Override public D buildDeclared() { - Preconditions.checkArgument(completedPhase == ModelProcessingPhase.FULL_DECLARATION + checkArgument(completedPhase == ModelProcessingPhase.FULL_DECLARATION || completedPhase == ModelProcessingPhase.EFFECTIVE_MODEL); if (declaredInstance == null) { declaredInstance = definition().getFactory().createDeclared(this); @@ -583,7 +626,7 @@ public abstract class StatementContextBase, E * * @return statement definition */ - protected final StatementDefinitionContext definition() { + protected final @NonNull StatementDefinitionContext definition() { return definition; } @@ -644,8 +687,8 @@ public abstract class StatementContextBase, E final > void selectMatch(final Class type, final NamespaceKeyCriterion criterion, final OnNamespaceItemAdded listener) { final Optional> optMatch = getFromNamespace(type, criterion); - Preconditions.checkState(optMatch.isPresent(), - "Failed to find a match for criterion %s in namespace %s node %s", criterion, type, this); + checkState(optMatch.isPresent(), "Failed to find a match for criterion %s in namespace %s node %s", criterion, + type, this); final Entry match = optMatch.get(); listener.namespaceItemAdded(StatementContextBase.this, type, match.getKey(), match.getValue()); } @@ -663,16 +706,12 @@ public abstract class StatementContextBase, E private > NamespaceBehaviourWithListeners getBehaviour( final Class type) { final NamespaceBehaviour behaviour = getBehaviourRegistry().getNamespaceBehaviour(type); - Preconditions.checkArgument(behaviour instanceof NamespaceBehaviourWithListeners, - "Namespace %s does not support listeners", type); + checkArgument(behaviour instanceof NamespaceBehaviourWithListeners, "Namespace %s does not support listeners", + type); return (NamespaceBehaviourWithListeners) behaviour; } - /** - * See {@link StatementSupport#getPublicView()}. - */ - @Nonnull @Override public StatementDefinition getPublicDefinition() { return definition().getPublicView(); @@ -696,10 +735,8 @@ public abstract class StatementContextBase, E * @throws NullPointerException if any of the arguments is null */ void addPhaseCompletedListener(final ModelProcessingPhase phase, final OnPhaseFinished listener) { - Preconditions.checkNotNull(phase, "Statement context processing phase cannot be null at: %s", - getStatementSourceReference()); - Preconditions.checkNotNull(listener, "Statement context phase listener cannot be null at: %s", - getStatementSourceReference()); + checkNotNull(phase, "Statement context processing phase cannot be null at: %s", getStatementSourceReference()); + checkNotNull(listener, "Statement context phase listener cannot be null at: %s", getStatementSourceReference()); ModelProcessingPhase finishedPhase = completedPhase; while (finishedPhase != null) { @@ -725,8 +762,8 @@ public abstract class StatementContextBase, E void addMutation(final ModelProcessingPhase phase, final ContextMutation mutation) { ModelProcessingPhase finishedPhase = completedPhase; while (finishedPhase != null) { - Preconditions.checkState(!phase.equals(finishedPhase), - "Mutation registered after phase was completed at: %s", getStatementSourceReference()); + checkState(!phase.equals(finishedPhase), "Mutation registered after phase was completed at: %s", + getStatementSourceReference()); finishedPhase = finishedPhase.getPreviousPhase(); } @@ -745,21 +782,48 @@ public abstract class StatementContextBase, E @Override public , Z extends EffectiveStatement> Mutable childCopyOf( final StmtContext stmt, final CopyType type, final QNameModule targetModule) { - Preconditions.checkState(stmt.getCompletedPhase() == ModelProcessingPhase.EFFECTIVE_MODEL, + checkState(stmt.getCompletedPhase() == ModelProcessingPhase.EFFECTIVE_MODEL, "Attempted to copy statement %s which has completed phase %s", stmt, stmt.getCompletedPhase()); - Preconditions.checkArgument(stmt instanceof SubstatementContext, "Unsupported statement %s", stmt); + checkArgument(stmt instanceof SubstatementContext, "Unsupported statement %s", stmt); final SubstatementContext original = (SubstatementContext)stmt; - final SubstatementContext copy = new SubstatementContext<>(original, this, type, targetModule); + final Optional> implicitParent = definition.getImplicitParentFor( + original.getPublicDefinition()); + + final SubstatementContext result; + final SubstatementContext copy; + + if (implicitParent.isPresent()) { + final StatementDefinitionContext def = new StatementDefinitionContext<>(implicitParent.get()); + result = new SubstatementContext(this, def, original.getSourceReference(), + original.rawStatementArgument(), original.getStatementArgument(), type); + + final CopyType childCopyType; + switch (type) { + case ADDED_BY_AUGMENTATION: + childCopyType = CopyType.ORIGINAL; + break; + case ADDED_BY_USES_AUGMENTATION: + childCopyType = CopyType.ADDED_BY_USES; + break; + case ADDED_BY_USES: + case ORIGINAL: + default: + childCopyType = type; + } + + copy = new SubstatementContext<>(original, result, childCopyType, targetModule); + result.addEffectiveSubstatement(copy); + } else { + result = copy = new SubstatementContext<>(original, this, type, targetModule); + } original.definition().onStatementAdded(copy); original.copyTo(copy, type, targetModule); - - return copy; + return result; } - @Override public @NonNull StatementDefinition getDefinition() { return getPublicDefinition(); @@ -775,6 +839,119 @@ public abstract class StatementContextBase, E return fullyDefined; } + @Beta + public final boolean hasImplicitParentSupport() { + return definition.getFactory() instanceof ImplicitParentAwareStatementSupport; + } + + @Beta + public final StatementContextBase wrapWithImplicit(final StatementContextBase original) { + final Optional> optImplicit = definition.getImplicitParentFor( + original.getPublicDefinition()); + if (!optImplicit.isPresent()) { + return original; + } + + final StatementDefinitionContext def = new StatementDefinitionContext<>(optImplicit.get()); + final CopyType type = original.getCopyHistory().getLastOperation(); + final SubstatementContext result = new SubstatementContext(original.getParentContext(), def, + original.getStatementSourceReference(), original.rawStatementArgument(), original.getStatementArgument(), + type); + + result.addEffectiveSubstatement(new SubstatementContext<>(original, result)); + result.setCompletedPhase(original.getCompletedPhase()); + return result; + } + + /** + * 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 StmtContext configStatement = StmtContextUtils.findFirstSubstatement(this, + ConfigStatement.class); + final boolean isConfig; + if (configStatement != null) { + isConfig = configStatement.coerceStatementArgument(); + 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().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().isIgnoringIfFeatures() || parent.isIgnoringIfFeatures()) { + flags |= SET_IGNORE_IF_FEATURE; + return true; + } + + flags |= HAVE_IGNORE_IF_FEATURE; + return false; + } + final void copyTo(final StatementContextBase target, final CopyType typeOfCopy, @Nullable final QNameModule targetModule) { final Collection> buffer = new ArrayList<>(substatements.size() + effective.size()); @@ -807,11 +984,11 @@ public abstract class StatementContextBase, E } // FIXME: revise this, as it seems to be wrong - private static final Set NOCOPY_FROM_GROUPING_SET = ImmutableSet.of( + private static final ImmutableSet NOCOPY_FROM_GROUPING_SET = ImmutableSet.of( YangStmtMapping.DESCRIPTION, YangStmtMapping.REFERENCE, YangStmtMapping.STATUS); - private static final Set REUSED_DEF_SET = ImmutableSet.of( + private static final ImmutableSet REUSED_DEF_SET = ImmutableSet.of( YangStmtMapping.TYPE, YangStmtMapping.TYPEDEF, YangStmtMapping.USES); @@ -823,7 +1000,7 @@ public abstract class StatementContextBase, E return false; } if (NOCOPY_FROM_GROUPING_SET.contains(def)) { - return !YangStmtMapping.GROUPING.equals(stmtContext.getParentContext().getPublicDefinition()); + return !YangStmtMapping.GROUPING.equals(stmtContext.coerceParentContext().getPublicDefinition()); } LOG.debug("Will copy {} statement {}", def, stmtContext);