*/
package org.opendaylight.yangtools.yang.parser.stmt.reactor;
+import static com.google.common.base.Verify.verify;
import static java.util.Objects.requireNonNull;
+import com.google.common.base.VerifyException;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
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.model.api.SchemaPath;
import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.spi.SchemaTreeNamespace;
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.InferenceException;
import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.OnDemandSchemaTreeStorageNode;
import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.StorageNodeType;
import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextDefaults;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
extends StatementContextBase<A, D, E> implements OnDemandSchemaTreeStorageNode {
private static final Logger LOG = LoggerFactory.getLogger(InferredStatementContext.class);
+ // Sentinel object for 'substatements'
+ private static final Object SWEPT_SUBSTATEMENTS = new Object();
+
private final @NonNull StatementContextBase<A, D, E> prototype;
private final @NonNull StatementContextBase<?, ?, ?> parent;
private final @NonNull StmtContext<A, D, E> originalCtx;
private final QNameModule targetModule;
private final A argument;
+ /**
+ * Effective substatements, lazily materialized. This field can have four states:
+ * <ul>
+ * <li>it can be {@code null}, in which case no materialization has taken place</li>
+ * <li>it can be a {@link HashMap}, in which case partial materialization has taken place</li>
+ * <li>it can be a {@link List}, in which case full materialization has taken place</li>
+ * <li>it can be {@link SWEPT_SUBSTATEMENTS}, in which case materialized state is no longer available</li>
+ * </ul>
+ */
+ private Object substatements;
+
private InferredStatementContext(final InferredStatementContext<A, D, E> original,
final StatementContextBase<?, ?, ?> parent) {
super(original);
this.prototype = original.prototype;
this.originalCtx = original.originalCtx;
this.argument = original.argument;
- setSubstatementsInitialized();
+ // Substatements are initialized here
+ this.substatements = ImmutableList.of();
}
InferredStatementContext(final StatementContextBase<?, ?, ?> parent, final StatementContextBase<A, D, E> prototype,
final CopyType myCopyType, final CopyType childCopyType, final QNameModule targetModule) {
- super(prototype.definition(), CopyHistory.of(myCopyType, prototype.getCopyHistory()));
+ super(prototype.definition(), CopyHistory.of(myCopyType, prototype.history()));
this.parent = requireNonNull(parent);
this.prototype = requireNonNull(prototype);
- this.argument = targetModule == null ? prototype.getStatementArgument()
+ this.argument = targetModule == null ? prototype.argument()
: prototype.definition().adaptArgumentValue(prototype, targetModule);
this.childCopyType = requireNonNull(childCopyType);
this.targetModule = targetModule;
this.originalCtx = prototype.getOriginalCtx().orElse(prototype);
- // Note: substatements from prototype are initialized lazily through ensureSubstatements()
+ // Mark prototype as blocking statement cleanup
+ prototype.incRef();
}
@Override
return ImmutableList.of();
}
+ @Override
+ public Collection<? extends Mutable<?, ?, ?>> mutableEffectiveSubstatements() {
+ return mutableEffectiveSubstatements(ensureEffectiveSubstatements());
+ }
+
@Override
public Iterable<? extends StmtContext<?, ?, ?>> allSubstatements() {
// No need to concat with declared
}
@Override
- public StatementSourceReference getStatementSourceReference() {
- return originalCtx.getStatementSourceReference();
+ public StatementSourceReference sourceReference() {
+ return originalCtx.sourceReference();
}
@Override
- public String rawStatementArgument() {
- return originalCtx.rawStatementArgument();
+ public String rawArgument() {
+ return originalCtx.rawArgument();
}
@Override
}
@Override
- public D buildDeclared() {
+ public D declared() {
/*
* Share original instance of declared statement between all effective statements which have been copied or
* derived from this original declared statement.
*/
- return originalCtx.buildDeclared();
+ return originalCtx.declared();
}
@Override
- public Collection<? extends Mutable<?, ?, ?>> mutableEffectiveSubstatements() {
- ensureSubstatements();
- return super.mutableEffectiveSubstatements();
+ public void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef) {
+ substatements = removeStatementFromEffectiveSubstatements(ensureEffectiveSubstatements(), statementDef);
+ }
+
+ @Override
+ public void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef,
+ final String statementArg) {
+ substatements = removeStatementFromEffectiveSubstatements(ensureEffectiveSubstatements(), statementDef,
+ statementArg);
}
@Override
public void addEffectiveSubstatement(final Mutable<?, ?, ?> substatement) {
- ensureSubstatements();
- super.addEffectiveSubstatement(substatement);
+ substatements = addEffectiveSubstatement(ensureEffectiveSubstatements(), substatement);
}
@Override
- public void addEffectiveSubstatements(final Collection<? extends Mutable<?, ?, ?>> statements) {
- ensureSubstatements();
- super.addEffectiveSubstatements(statements);
+ void addEffectiveSubstatementsImpl(final Collection<? extends Mutable<?, ?, ?>> statements) {
+ substatements = addEffectiveSubstatementsImpl(ensureEffectiveSubstatements(), statements);
}
@Override
- public void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef,
- final String statementArg) {
- ensureSubstatements();
- super.removeStatementFromEffectiveSubstatements(statementDef, statementArg);
+ InferredStatementContext<A, D, E> reparent(final StatementContextBase<?, ?, ?> newParent) {
+ return new InferredStatementContext<>(this, newParent);
}
@Override
- public void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef) {
- ensureSubstatements();
- super.removeStatementFromEffectiveSubstatements(statementDef);
+ boolean hasEmptySubstatements() {
+ if (substatements == null) {
+ return prototype.hasEmptySubstatements();
+ }
+ return substatements instanceof HashMap ? false : ((List<?>) substatements).isEmpty();
}
@Override
- InferredStatementContext<A, D, E> reparent(final StatementContextBase<?, ?, ?> newParent) {
- return new InferredStatementContext<>(this, newParent);
+ public <X, Z extends EffectiveStatement<X, ?>> @NonNull Optional<X> findSubstatementArgument(
+ final @NonNull Class<Z> type) {
+ if (substatements instanceof List) {
+ return StmtContextDefaults.findSubstatementArgument(this, type);
+ }
+
+ final Optional<X> templateArg = prototype.findSubstatementArgument(type);
+ if (templateArg.isEmpty()) {
+ return templateArg;
+ }
+ if (SchemaTreeEffectiveStatement.class.isAssignableFrom(type)) {
+ // X is known to be QName
+ return (Optional<X>) templateArg.map(template -> ((QName) template).bindTo(targetModule));
+ }
+ return templateArg;
}
@Override
- boolean hasEmptySubstatements() {
- ensureSubstatements();
- return hasEmptyEffectiveSubstatements();
+ public boolean hasSubstatement(final @NonNull Class<? extends EffectiveStatement<?, ?>> type) {
+ return substatements instanceof List ? StmtContextDefaults.hasSubstatement(prototype, type)
+ // We do not allow deletion of partially-materialized statements, hence this is accurate
+ : prototype.hasSubstatement(type);
}
@Override
- public <D extends DeclaredStatement<QName>, E extends EffectiveStatement<QName, D>>
- StmtContext<QName, D, E> requestSchemaTreeChild(final QName qname) {
- LOG.debug("Materializing on lookup of {}", qname);
- // FIXME: YANGTOOLS-1160: we do not want to force full materialization here
- ensureSubstatements();
+ public <Y extends DeclaredStatement<QName>, Z extends EffectiveStatement<QName, Y>>
+ StmtContext<QName, Y, Z> requestSchemaTreeChild(final QName qname) {
+ if (substatements instanceof List) {
+ // We have performed materialization, hence we have triggered creation of all our schema tree child
+ // statements.
+ return null;
+ }
- // Now we have to do a lookup as we do not have access to the namespace being populated (yet). Here we are
- // bypassing additional checks and talk directly to superclass to get the statements.
- for (StmtContext<?, ?, ?> stmt : super.mutableEffectiveSubstatements()) {
- if (stmt.producesEffective(SchemaTreeEffectiveStatement.class)
- && qname.equals(stmt.coerceStatementArgument())) {
- return (StmtContext<QName, D, E>) stmt;
- }
+ final QName templateQName = qname.bindTo(StmtContextUtils.getRootModuleQName(prototype));
+ LOG.debug("Materializing child {} from {}", qname, templateQName);
+
+ final StmtContext<?, ?, ?> template;
+ if (prototype instanceof InferredStatementContext) {
+ // Note: we need to access namespace here, as the target statement may have already been populated, in which
+ // case we want to obtain the statement in local namespace storage.
+ template = (StmtContext) ((InferredStatementContext<?, ?, ?>) prototype).getFromNamespace(
+ SchemaTreeNamespace.class, templateQName);
+ } else {
+ template = prototype.allSubstatementsStream()
+ .filter(stmt -> stmt.producesEffective(SchemaTreeEffectiveStatement.class)
+ && templateQName.equals(stmt.argument()))
+ .findAny()
+ .orElse(null);
+ }
+
+ if (template == null) {
+ // We do not have a template, this child does not exist. It may be added later, but that is someone else's
+ // responsibility.
+ LOG.debug("Child {} does not have a template", qname);
+ return null;
}
- return null;
+
+ @SuppressWarnings("unchecked")
+ final Mutable<QName, Y, Z> ret = (Mutable<QName, Y, Z>) copySubstatement((Mutable<?, ?, ?>) template)
+ .orElseThrow(
+ () -> new InferenceException(this, "Failed to materialize child %s template %s", qname, template));
+ ensureCompletedPhase(ret);
+ addMaterialized(template, ret);
+
+ LOG.debug("Child {} materialized", qname);
+ return ret;
}
// Instantiate this statement's effective substatements. Note this method has side-effects in namespaces and overall
// BuildGlobalContext, hence it must be called at most once.
- private void ensureSubstatements() {
- if (!substatementsInitialized()) {
- initializeSubstatements();
+ private List<ReactorStmtCtx<?, ?, ?>> ensureEffectiveSubstatements() {
+ accessSubstatements();
+ return substatements instanceof List ? castEffective(substatements)
+ : initializeSubstatements(castMaterialized(substatements));
+ }
+
+ @Override
+ Iterable<ReactorStmtCtx<?, ?, ?>> effectiveChildrenToComplete() {
+ // When we have not initialized, there are no statements to catch up: we will catch up when we are copying
+ // from prototype (which is already at ModelProcessingPhase.EFFECTIVE_MODEL).
+ if (substatements == null) {
+ return ImmutableList.of();
+ }
+ accessSubstatements();
+ if (substatements instanceof HashMap) {
+ return castMaterialized(substatements).values();
+ } else {
+ return castEffective(substatements);
+ }
+ }
+
+ @Override
+ Stream<? extends StmtContext<?, ?, ?>> streamDeclared() {
+ return Stream.empty();
+ }
+
+ @Override
+ Stream<? extends StmtContext<?, ?, ?>> streamEffective() {
+ accessSubstatements();
+ return ensureEffectiveSubstatements().stream();
+ }
+
+ private void accessSubstatements() {
+ verify(substatements != SWEPT_SUBSTATEMENTS, "Attempted to access substatements of %s", this);
+ }
+
+ @Override
+ void markNoParentRef() {
+ final Object local = substatements;
+ if (local != null) {
+ markNoParentRef(castEffective(local));
+ }
+ }
+
+ @Override
+ int sweepSubstatements() {
+ final Object local = substatements;
+ substatements = SWEPT_SUBSTATEMENTS;
+ int count = 0;
+ if (local != null) {
+ final List<ReactorStmtCtx<?, ?, ?>> list = castEffective(local);
+ sweep(list);
+ count = countUnswept(list);
}
+ return count;
}
- private void initializeSubstatements() {
+ private List<ReactorStmtCtx<?, ?, ?>> initializeSubstatements(
+ final Map<StmtContext<?, ?, ?>, ReactorStmtCtx<?, ?, ?>> materializedSchemaTree) {
final Collection<? extends StatementContextBase<?, ?, ?>> declared = prototype.mutableDeclaredSubstatements();
final Collection<? extends Mutable<?, ?, ?>> effective = prototype.mutableEffectiveSubstatements();
- final List<Mutable<?, ?, ?>> buffer = new ArrayList<>(declared.size() + effective.size());
+ final List<Mutable<?, ?, ?>> buffer = new ArrayList<>(declared.size() + effective.size());
for (final Mutable<?, ?, ?> stmtContext : declared) {
if (stmtContext.isSupportedByFeatures()) {
- copySubstatement(stmtContext, buffer);
+ copySubstatement(stmtContext, buffer, materializedSchemaTree);
}
}
for (final Mutable<?, ?, ?> stmtContext : effective) {
- copySubstatement(stmtContext, buffer);
+ copySubstatement(stmtContext, buffer, materializedSchemaTree);
}
- // We are bypassing usual safeties here, as this is not introducing new statements but rather just materializing
- // them when the need has arised.
- addInitialEffectiveSubstatements(buffer);
+ final List<ReactorStmtCtx<?, ?, ?>> ret = beforeAddEffectiveStatementUnsafe(ImmutableList.of(), buffer.size());
+ ret.addAll((Collection) buffer);
+ substatements = ret;
+
+ prototype.decRef();
+ return ret;
}
// Statement copy mess starts here
YangStmtMapping.TYPEDEF,
YangStmtMapping.USES);
- private void copySubstatement(final Mutable<?, ?, ?> substatement, final Collection<Mutable<?, ?, ?>> buffer) {
- final StatementDefinition def = substatement.getPublicDefinition();
+ private void copySubstatement(final Mutable<?, ?, ?> substatement, final Collection<Mutable<?, ?, ?>> buffer,
+ final Map<StmtContext<?, ?, ?>, ReactorStmtCtx<?, ?, ?>> materializedSchemaTree) {
+ final StatementDefinition def = substatement.publicDefinition();
// FIXME: YANGTOOLS-652: formerly known as "isReusedByUses"
if (REUSED_DEF_SET.contains(def)) {
- LOG.debug("Reusing substatement {} for {}", substatement, this);
- buffer.add(substatement);
+ LOG.trace("Reusing substatement {} for {}", substatement, this);
+ buffer.add(substatement.replicaAsChildOf(this));
return;
}
- substatement.copyAsChildOf(this, childCopyType, targetModule).ifPresent(buffer::add);
+ // Consult materialized substatements. We are in a copy operation and will end up throwing materialized
+ // statements away -- hence we do not perform Map.remove() to save ourselves a mutation operation.
+ //
+ // We could also perform a Map.containsKey() and perform a bulk add, but that would mean the statement order
+ // against parent would change -- and we certainly do not want that to happen.
+ final ReactorStmtCtx<?, ?, ?> materialized = findMaterialized(materializedSchemaTree, substatement);
+ if (materialized == null) {
+ copySubstatement(substatement).ifPresent(copy -> {
+ ensureCompletedPhase(copy);
+ buffer.add(copy);
+ });
+ } else {
+ buffer.add(materialized);
+ }
+ }
+
+ private Optional<? extends Mutable<?, ?, ?>> copySubstatement(final Mutable<?, ?, ?> substatement) {
+ return substatement.copyAsChildOf(this, childCopyType, targetModule);
+ }
+
+ private void addMaterialized(final StmtContext<?, ?, ?> template, final Mutable<?, ?, ?> copy) {
+ final HashMap<StmtContext<?, ?, ?>, ReactorStmtCtx<?, ?, ?>> materializedSchemaTree;
+ if (substatements == null) {
+ // Lazy initialization of backing map. We do not expect this to be used often or multiple times -- each hit
+ // here means an inference along schema tree, such as deviate/augment. HashMap requires power-of-two and
+ // defaults to 0.75 load factor -- we therefore size it to 4, i.e. next two inserts will not cause a
+ // resizing operation.
+ materializedSchemaTree = new HashMap<>(4);
+ substatements = materializedSchemaTree;
+ } else {
+ verify(substatements instanceof HashMap, "Unexpected substatements %s", substatements);
+ materializedSchemaTree = castMaterialized(substatements);
+ }
+
+ final StmtContext<?, ?, ?> existing = materializedSchemaTree.put(template,
+ (StatementContextBase<?, ?, ?>) copy);
+ if (existing != null) {
+ throw new VerifyException(
+ "Unexpected duplicate request for " + copy.argument() + " previous result was " + existing);
+ }
+ }
+
+ private static @Nullable ReactorStmtCtx<?, ?, ?> findMaterialized(
+ final Map<StmtContext<?, ?, ?>, ReactorStmtCtx<?, ?, ?>> materializedSchemaTree,
+ final StmtContext<?, ?, ?> template) {
+ return materializedSchemaTree == null ? null : materializedSchemaTree.get(template);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static List<ReactorStmtCtx<?, ?, ?>> castEffective(final Object substatements) {
+ return (List<ReactorStmtCtx<?, ?, ?>>) substatements;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static HashMap<StmtContext<?, ?, ?>, ReactorStmtCtx<?, ?, ?>> castMaterialized(final Object substatements) {
+ return (HashMap<StmtContext<?, ?, ?>, ReactorStmtCtx<?, ?, ?>>) substatements;
}
// Statement copy mess ends here
*/
@Override
@Deprecated
- public Optional<SchemaPath> getSchemaPath() {
+ public Optional<SchemaPath> schemaPath() {
return substatementGetSchemaPath();
}
@Override
- public A getStatementArgument() {
+ public A argument() {
return argument;
}
}
@Override
- public boolean isConfiguration() {
+ public boolean effectiveConfig() {
return isConfiguration(parent);
}