import java.util.Set;
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;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+/**
+ * Core reactor statement implementation of {@link Mutable}.
+ *
+ * @param <A> Argument type
+ * @param <D> Declared Statement representation
+ * @param <E> Effective Statement representation
+ */
public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
extends NamespaceStorageSupport implements Mutable<A, D, E>, ResumedStatement {
/**
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 @NonNull StatementDefinitionContext<A, D, E> definition;
private final @NonNull StatementSourceReference statementDeclSource;
private final StmtContext<?, ?, ?> originalCtx;
+ private final StmtContext<?, ?, ?> prevCopyCtx;
private final CopyHistory copyHistory;
private final String rawArgument;
private List<StmtContext<?, ?, ?>> 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<A, D, E> def, final StatementSourceReference ref,
final String rawArgument) {
this.definition = requireNonNull(def);
this.rawArgument = def.internArgument(rawArgument);
this.copyHistory = CopyHistory.original();
this.originalCtx = null;
+ this.prevCopyCtx = null;
}
StatementContextBase(final StatementDefinitionContext<A, D, E> def, final StatementSourceReference ref,
this.rawArgument = rawArgument;
this.copyHistory = CopyHistory.of(copyType, CopyHistory.original());
this.originalCtx = null;
+ this.prevCopyCtx = null;
}
StatementContextBase(final StatementContextBase<A, D, E> original, final CopyType copyType) {
this.rawArgument = original.rawArgument;
this.copyHistory = CopyHistory.of(copyType, original.getCopyHistory());
this.originalCtx = original.getOriginalCtx().orElse(original);
+ this.prevCopyCtx = original;
}
StatementContextBase(final StatementContextBase<A, D, E> original) {
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
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<QName> 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<QName> supportedFeatures = getFromNamespace(SupportedFeaturesNamespace.class,
- SupportedFeatures.SUPPORTED_FEATURES);
- final boolean ret = supportedFeatures == null || 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;
return Optional.ofNullable(originalCtx);
}
+ @Override
+ public Optional<? extends StmtContext<?, ?, ?>> getPreviousCopyCtx() {
+ return Optional.ofNullable(prevCopyCtx);
+ }
+
@Override
public ModelProcessingPhase getCompletedPhase() {
return completedPhase;
@Override
public abstract RootStatementContext<?, ?, ?> getRoot();
- /**
- * Returns the origin of the statement.
- *
- * @return origin of statement
- */
@Override
public StatementSource getStatementSource() {
return statementDeclSource.getStatementSource();
}
- /**
- * Returns a reference to statement source.
- *
- * @return reference of statement source
- */
@Override
public StatementSourceReference getStatementSourceReference() {
return statementDeclSource;
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(forRemoval = true)
public void removeStatementsFromEffectiveSubstatements(
final Collection<? extends StmtContext<?, ?, ?>> statements) {
if (!effective.isEmpty()) {
return (NamespaceBehaviourWithListeners<K, V, N>) behaviour;
}
- /**
- * See {@link StatementSupport#getPublicView()}.
- */
@Override
public StatementDefinition getPublicDefinition() {
return definition().getPublicView();
copy = new SubstatementContext<>(original, result, childCopyType, targetModule);
result.addEffectiveSubstatement(copy);
- original.definition().onStatementAdded(copy);
} else {
result = copy = new SubstatementContext<>(original, this, type, targetModule);
- original.definition().onStatementAdded(copy);
}
+ original.definition().onStatementAdded(copy);
original.copyTo(copy, type, targetModule);
return result;
}
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.
+ *
+ * <p>
+ * 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()}.
+ *
+ * <p>
+ * 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<Boolean, ?, ?> 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()}.
+ *
+ * <p>
+ * 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<Mutable<?, ?, ?>> buffer = new ArrayList<>(substatements.size() + effective.size());