import org.opendaylight.yangtools.yang.parser.spi.source.ImplicitSubstatement;
import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter.ResumedStatement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Intermediate subclass of StatementContextBase facing the parser stream via implementation of ResumedStatement. This
*/
abstract class AbstractResumedStatement<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
extends StatementContextBase<A, D, E> implements ResumedStatement {
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractResumedStatement.class);
+
private final @NonNull StatementSourceReference statementDeclSource;
private final String rawArgument;
return effective.stream();
}
+ @Override
+ final int sweepSubstatements() {
+ // First we need to sweep all statements, which may trigger sweeps all across the place, for example:
+ // - 'effective' member sweeping a 'substatements' member
+ // - 'substatements' member sweeping a 'substatements' member which came before it during iteration
+ // We then iterate once again, counting what remains unswept
+ sweep(substatements);
+ sweep(effective);
+ final int count = countUnswept(substatements) + countUnswept(effective);
+ if (count != 0) {
+ LOG.debug("{} children left to sweep from {}", count, this);
+ }
+ substatements = null;
+ effective = null;
+ return count;
+ }
+
/**
* Lookup substatement by its offset in this statement.
*
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 A argument;
/**
- * Effective substatements, lazily materialized. This field can have three states:
+ * 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;
this.childCopyType = requireNonNull(childCopyType);
this.targetModule = targetModule;
this.originalCtx = prototype.getOriginalCtx().orElse(prototype);
+
+ // Mark prototype as blocking statement cleanup
+ prototype.incRef();
}
@Override
// 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 List<StatementContextBase<?, ?, ?>> ensureEffectiveSubstatements() {
+ accessSubstatements();
return substatements instanceof List ? castEffective(substatements)
: initializeSubstatements(castMaterialized(substatements));
}
// from prototype (which is already at ModelProcessingPhase.EFFECTIVE_MODEL).
if (substatements == null) {
return ImmutableList.of();
- } else if (substatements instanceof HashMap) {
+ }
+ accessSubstatements();
+ if (substatements instanceof HashMap) {
return castMaterialized(substatements).values();
} else {
return castEffective(substatements);
@Override
Stream<? extends StmtContext<?, ?, ?>> streamEffective() {
- // FIXME: YANGTOOLS-1184: do not force initialization
+ accessSubstatements();
return ensureEffectiveSubstatements().stream();
}
+ private void accessSubstatements() {
+ verify(substatements != SWEPT_SUBSTATEMENTS, "Attempted to access substatements of %s", this);
+ }
+
+ @Override
+ int sweepSubstatements() {
+ final Object local = substatements;
+ substatements = SWEPT_SUBSTATEMENTS;
+ int count = 0;
+ if (local != null) {
+ final List<StatementContextBase<?, ?, ?>> list = castEffective(local);
+ sweep(list);
+ count = countUnswept(list);
+ }
+ return count;
+ }
+
private List<StatementContextBase<?, ?, ?>> initializeSubstatements(
final Map<StmtContext<?, ?, ?>, StatementContextBase<?, ?, ?>> 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, materializedSchemaTree);
buffer.size());
ret.addAll((Collection) buffer);
substatements = ret;
+
+ prototype.decRef();
return ret;
}
*/
package org.opendaylight.yangtools.yang.parser.stmt.reactor;
+import static com.google.common.base.Verify.verifyNotNull;
+
import com.google.common.collect.ImmutableMap;
import java.util.HashMap;
import java.util.Map;
import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
abstract class NamespaceStorageSupport implements NamespaceStorageNode {
+ private static final Logger LOG = LoggerFactory.getLogger(NamespaceStorageSupport.class);
- private Map<Class<?>, Map<?,?>> namespaces = ImmutableMap.of();
+ private Map<Class<?>, Map<?, ?>> namespaces = ImmutableMap.of();
/**
* {@inheritDoc}
@SuppressWarnings("unchecked")
final <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getLocalNamespace(final Class<N> type) {
- return (Map<K, V>) namespaces.get(type);
+ return (Map<K, V>) accessNamespaces().get(type);
}
final <K, V, T extends K, U extends V, N extends IdentifierNamespace<K, V>> void addToNamespace(
@SuppressWarnings("unchecked")
@Override
public <K, V, N extends IdentifierNamespace<K, V>> V getFromLocalStorage(final Class<N> type, final K key) {
- final Map<K, V> localNamespace = (Map<K, V>) namespaces.get(type);
+ final Map<K, V> localNamespace = (Map<K, V>) accessNamespaces().get(type);
return localNamespace == null ? null : localNamespace.get(key);
}
@Override
public <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAllFromLocalStorage(final Class<N> type) {
@SuppressWarnings("unchecked")
- final Map<K, V> localNamespace = (Map<K, V>) namespaces.get(type);
+ final Map<K, V> localNamespace = (Map<K, V>) accessNamespaces().get(type);
return localNamespace;
}
+ @Override
+ public <K, V, N extends IdentifierNamespace<K, V>> V putToLocalStorage(final Class<N> type, final K key,
+ final V value) {
+ final V ret = ensureLocalNamespace(type).put(key, value);
+ onNamespaceElementAdded(type, key, value);
+ return ret;
+ }
+
+ @Override
+ public <K, V, N extends IdentifierNamespace<K, V>> V putToLocalStorageIfAbsent(final Class<N> type, final K key,
+ final V value) {
+ final V ret = ensureLocalNamespace(type).putIfAbsent(key, value);
+ if (ret == null) {
+ onNamespaceElementAdded(type, key, value);
+ }
+ return ret;
+ }
+
+ void sweepNamespaces() {
+ namespaces = null;
+ LOG.debug("Swept namespace storages of {}", this);
+ }
+
+ void sweepNamespaces(final Map<Class<?>, SweptNamespace> toWipe) {
+ switch (namespaces.size()) {
+ case 0:
+ namespaces = ImmutableMap.copyOf(toWipe);
+ return;
+ case 1:
+ namespaces = new HashMap<>(namespaces);
+ break;
+ default:
+ // No-op, we are ready
+ }
+
+ namespaces.putAll(toWipe);
+ LOG.debug("Trimmed namespace storages of {} to {}", this, namespaces.keySet());
+ }
+
+ private Map<Class<?>, Map<?, ?>> accessNamespaces() {
+ return verifyNotNull(namespaces, "Attempted to access swept namespaces of %s", this);
+ }
+
private <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> ensureLocalNamespace(final Class<N> type) {
@SuppressWarnings("unchecked")
- Map<K, V> ret = (Map<K,V>) namespaces.get(type);
+ Map<K, V> ret = (Map<K,V>) accessNamespaces().get(type);
if (ret == null) {
checkLocalNamespaceAllowed(type);
ret = new HashMap<>(1);
return ret;
}
-
- @Override
- public <K, V, N extends IdentifierNamespace<K, V>> V putToLocalStorage(final Class<N> type, final K key,
- final V value) {
- final V ret = ensureLocalNamespace(type).put(key, value);
- onNamespaceElementAdded(type, key, value);
- return ret;
- }
-
- @Override
- public <K, V, N extends IdentifierNamespace<K, V>> V putToLocalStorageIfAbsent(final Class<N> type, final K key,
- final V value) {
- final V ret = ensureLocalNamespace(type).putIfAbsent(key, value);
- if (ret == null) {
- onNamespaceElementAdded(type, key, value);
- }
- return ret;
- }
}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.stmt.reactor;
+
+import static com.google.common.base.Verify.verify;
+
+import com.google.common.base.VerifyException;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Real "core" reactor statement implementation of {@link Mutable}, supporting basic reactor lifecycle.
+ *
+ * @param <A> Argument type
+ * @param <D> Declared Statement representation
+ * @param <E> Effective Statement representation
+ */
+abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
+ extends NamespaceStorageSupport implements Mutable<A, D, E> {
+ private static final Logger LOG = LoggerFactory.getLogger(ReactorStmtCtx.class);
+
+ /**
+ * Substatement refcount tracking. This mechanics deals with retaining substatements for the purposes of
+ * instantiating their lazy copies in InferredStatementContext. It works in concert with {@link #buildEffective()}
+ * and {@link #buildDeclared()}: declared/effective statement views hold an implicit reference and refcount-based
+ * sweep is not activated until they are done (or this statement is not {@link #isSupportedToBuildEffective}).
+ *
+ * <p>
+ * Reference count is hierarchical in that parent references also pin down their child statements and do not allow
+ * them to be swept.
+ *
+ * <p>
+ * The counter's positive values are tracking incoming references via {@link #incRef()}/{@link #decRef()} methods.
+ * Once we transition to sweeping, this value becomes negative counting upwards to {@link #REFCOUNT_NONE} based on
+ * {@link #sweepOnChildDone()}. Once we reach that, we transition to {@link #REFCOUNT_SWEPT}.
+ */
+ private int refcount = REFCOUNT_NONE;
+ /**
+ * No outstanding references, this statement is a potential candidate for sweeping, provided it has populated its
+ * declared and effective views and {@link #parentRef} is known to be absent.
+ */
+ private static final int REFCOUNT_NONE = 0;
+ /**
+ * Reference count overflow or some other recoverable logic error. Do not rely on refcounts and do not sweep
+ * anything.
+ *
+ * <p>
+ * Note on value assignment:
+ * This allow our incRef() to naturally progress to being saturated. Others jump there directly.
+ * It also makes it it impossible to observe {@code Interger.MAX_VALUE} children, which we take advantage of for
+ * {@link #REFCOUNT_SWEEPING}.
+ */
+ private static final int REFCOUNT_DEFUNCT = Integer.MAX_VALUE;
+ /**
+ * This statement is being actively swept. This is a transient value set when we are sweeping our children, so that
+ * we prevent re-entering this statement.
+ *
+ * <p>
+ * Note on value assignment:
+ * The value is lower than any legal child refcount due to {@link #REFCOUNT_DEFUNCT} while still being higher than
+ * {@link #REFCOUNT_SWEPT}.
+ */
+ private static final int REFCOUNT_SWEEPING = -Integer.MAX_VALUE;
+ /**
+ * This statement, along with its entire subtree has been swept and we positively know all our children have reached
+ * this state. We {@link #sweepNamespaces()} upon reaching this state.
+ *
+ * <p>
+ * Note on value assignment:
+ * This is the lowest value observable, making it easier on checking others on equality.
+ */
+ private static final int REFCOUNT_SWEPT = Integer.MIN_VALUE;
+
+ /**
+ * Acquire a reference on this context. As long as there is at least one reference outstanding,
+ * {@link #buildEffective()} will not result in {@link #effectiveSubstatements()} being discarded.
+ *
+ * @throws VerifyException if {@link #effectiveSubstatements()} has already been discarded
+ */
+ final void incRef() {
+ final int current = refcount;
+ verify(current >= REFCOUNT_NONE, "Attempted to access reference count of %s", this);
+ if (current != REFCOUNT_DEFUNCT) {
+ // Note: can end up becoming REFCOUNT_DEFUNCT on overflow
+ refcount = current + 1;
+ } else {
+ LOG.debug("Disabled refcount increment of {}", this);
+ }
+ }
+
+ /**
+ * Release a reference on this context. This call may result in {@link #effectiveSubstatements()} becoming
+ * unavailable.
+ */
+ final void decRef() {
+ final int current = refcount;
+ if (current == REFCOUNT_DEFUNCT) {
+ // no-op
+ LOG.debug("Disabled refcount decrement of {}", this);
+ return;
+ }
+ if (current <= REFCOUNT_NONE) {
+ // Underflow, become defunct
+ LOG.warn("Statement refcount underflow, reference counting disabled for {}", this, new Throwable());
+ refcount = REFCOUNT_DEFUNCT;
+ return;
+ }
+
+ refcount = current - 1;
+ LOG.trace("Refcount {} on {}", refcount, this);
+ if (isSweepable()) {
+ // We are no longer guarded by effective instance
+ sweepOnDecrement();
+ }
+ }
+
+ final void releaseImplicitRef() {
+ if (refcount == REFCOUNT_NONE) {
+ sweepOnDecrement();
+ }
+ }
+
+ /**
+ * Sweep this statement context as a result of {@link #sweepSubstatements()}, i.e. when parent is also being swept.
+ */
+ private void sweep() {
+ if (isSweepable()) {
+ LOG.trace("Releasing {}", this);
+ sweepState();
+ }
+ }
+
+ static final void sweep(final Collection<? extends ReactorStmtCtx<?, ?, ?>> substatements) {
+ for (ReactorStmtCtx<?, ?, ?> stmt : substatements) {
+ stmt.sweep();
+ }
+ }
+
+ static final int countUnswept(final Collection<? extends ReactorStmtCtx<?, ?, ?>> substatements) {
+ int result = 0;
+ for (ReactorStmtCtx<?, ?, ?> stmt : substatements) {
+ if (stmt.refcount > REFCOUNT_NONE || !stmt.noImplictRef()) {
+ result++;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Implementation-specific sweep action. This is expected to perform a recursive {@link #sweep(Collection)} on all
+ * {@link #declaredSubstatements()} and {@link #effectiveSubstatements()} and report the result of the sweep
+ * operation.
+ *
+ * <p>
+ * {@link #effectiveSubstatements()} as well as namespaces may become inoperable as a result of this operation.
+ *
+ * @return True if the entire tree has been completely swept, false otherwise.
+ */
+ abstract int sweepSubstatements();
+
+ abstract boolean noImplictRef();
+
+ abstract @Nullable ReactorStmtCtx<?, ?, ?> parentStmtCtx();
+
+ // Called when this statement does not have an implicit reference and have reached REFCOUNT_NONE
+ private void sweepOnDecrement() {
+ LOG.trace("Sweeping on decrement {}", this);
+ final ReactorStmtCtx<?, ?, ?> parent = parentStmtCtx();
+ if (parent == null) {
+ // We are the top-level object and have lost a reference. Trigger sweep if possible and we are done.
+ sweepState();
+ return;
+ }
+
+ parent.sweepOnChildDecrement();
+ }
+
+ // Called from child when it has lost its final reference
+ private void sweepOnChildDecrement() {
+ if (isAwaitingChildren()) {
+ // We are a child for which our parent is waiting. Notify it and we are done.
+ sweepOnChildDone();
+ return;
+ }
+
+ // Check parent reference count
+ final int refs = refcount;
+ if (refs > REFCOUNT_NONE || refs <= REFCOUNT_SWEEPING || !noImplictRef()) {
+ // No-op
+ return;
+ }
+
+ // parent is potentially reclaimable
+ if (noParentRefs()) {
+ LOG.trace("Cleanup {} of parent {}", refcount, this);
+ sweepState();
+ }
+ }
+
+ // FIXME: cache the resolution of this
+ private boolean noParentRefs() {
+ final ReactorStmtCtx<?, ?, ?> parent = parentStmtCtx();
+ if (parent != null) {
+ final int refs = parent.refcount;
+ // FIXME: 'noImplicitRef' is too strict here?
+ if (refs > REFCOUNT_NONE || !parent.noImplictRef()) {
+ // parent with refcount or protected by views
+ return false;
+ }
+ if (refs < REFCOUNT_NONE) {
+ // parent is being swept already
+ return true;
+ }
+ // REFCOUNT_NONE and reclaimable, look forward
+ return parent.noParentRefs();
+ }
+ return true;
+ }
+
+ private boolean isAwaitingChildren() {
+ return refcount > REFCOUNT_SWEEPING && refcount < REFCOUNT_NONE;
+ }
+
+ private boolean isSweepable() {
+ return refcount == REFCOUNT_NONE && noImplictRef();
+ }
+
+ private void sweepOnChildDone() {
+ LOG.trace("Sweeping on child done {}", this);
+ final int current = refcount;
+ if (current >= REFCOUNT_NONE) {
+ // no-op, perhaps we want to handle some cases differently?
+ LOG.trace("Ignoring child sweep of {} for {}", this, current);
+ return;
+ }
+ verify(current != REFCOUNT_SWEPT, "Attempt to sweep a child of swept %s", this);
+
+ refcount = current + 1;
+ LOG.trace("Child refcount {}", refcount);
+ if (refcount == REFCOUNT_NONE) {
+ sweepDone();
+ sweepParent();
+ }
+ }
+
+ private void sweepParent() {
+ final ReactorStmtCtx<?, ?, ?> parent = parentStmtCtx();
+ LOG.trace("Propagating to parent {}", parent);
+ if (parent != null && parent.isAwaitingChildren()) {
+ parent.sweepOnChildDone();
+ }
+ }
+
+ private void sweepDone() {
+ LOG.trace("Sweep done for {}", this);
+ refcount = REFCOUNT_SWEPT;
+ sweepNamespaces();
+ }
+
+ private boolean sweepState() {
+ refcount = REFCOUNT_SWEEPING;
+ final int childRefs = sweepSubstatements();
+ if (childRefs == 0) {
+ sweepDone();
+ return true;
+ }
+ if (childRefs < 0 || childRefs >= REFCOUNT_DEFUNCT) {
+ LOG.warn("Negative child refcount {} cannot be stored, reference counting disabled for {}", childRefs, this,
+ new Throwable());
+ refcount = REFCOUNT_DEFUNCT;
+ } else {
+ LOG.trace("Still {} outstanding children of {}", childRefs, this);
+ refcount = -childRefs;
+ }
+ return false;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.stmt.reactor;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.Optional;
+import java.util.stream.Stream;
+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.StatementDefinition;
+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.source.StatementSourceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A replica of a different statement. It does not allow modification, but produces an effective statement from a
+ * designated source.
+ */
+final class ReplicaStatementContext<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
+ extends StatementContextBase<A, D, E> {
+ private static final Logger LOG = LoggerFactory.getLogger(ReplicaStatementContext.class);
+
+ private final StatementContextBase<?, ?, ?> parent;
+ private final StatementContextBase<A, D, E> source;
+
+ private final boolean haveRef;
+
+ ReplicaStatementContext(final StatementContextBase<?, ?, ?> parent, final StatementContextBase<A, D, E> source) {
+ super(source);
+ this.parent = requireNonNull(parent);
+ this.source = requireNonNull(source);
+ if (source.isSupportedToBuildEffective()) {
+ source.incRef();
+ haveRef = true;
+ } else {
+ setIsSupportedToBuildEffective(false);
+ haveRef = false;
+ }
+ }
+
+ @Override
+ E createEffective() {
+ return source.buildEffective();
+ }
+
+ @Override
+ public boolean isConfiguration() {
+ return source.isConfiguration();
+ }
+
+ @Override
+ public D buildDeclared() {
+ return source.buildDeclared();
+ }
+
+ @Override
+ public A argument() {
+ return source.argument();
+ }
+
+ @Override
+ public StatementSourceReference sourceReference() {
+ return source.sourceReference();
+ }
+
+ @Override
+ public String rawArgument() {
+ return source.rawArgument();
+ }
+
+ @Override
+ public Optional<StmtContext<A, D, E>> getOriginalCtx() {
+ return source.getOriginalCtx();
+ }
+
+ @Override
+ public Collection<? extends StatementContextBase<?, ?, ?>> mutableDeclaredSubstatements() {
+ return source.mutableDeclaredSubstatements();
+ }
+
+ @Override
+ public Collection<? extends Mutable<?, ?, ?>> mutableEffectiveSubstatements() {
+ return source.mutableEffectiveSubstatements();
+ }
+
+ @Override
+ boolean hasEmptySubstatements() {
+ return source.hasEmptySubstatements();
+ }
+
+ @Override
+ Iterable<StatementContextBase<?, ?, ?>> effectiveChildrenToComplete() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ int sweepSubstatements() {
+ if (haveRef) {
+ source.decRef();
+ }
+ return 0;
+ }
+
+ @Override
+ public Optional<StmtContext<A, D, E>> getPreviousCopyCtx() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef,
+ final String statementArg) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void addEffectiveSubstatement(final Mutable<?, ?, ?> substatement) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ void addEffectiveSubstatementsImpl(final Collection<? extends Mutable<?, ?, ?>> statements) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ Stream<? extends StmtContext<?, ?, ?>> streamDeclared() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ Stream<? extends StmtContext<?, ?, ?>> streamEffective() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ StatementContextBase<A, D, E> reparent(final StatementContextBase<?, ?, ?> newParent) {
+ throw new UnsupportedOperationException();
+ }
+
+ /*
+ * KEEP THINGS ORGANIZED!
+ *
+ * below methods exist in the same form in InferredStatementContext/SubstatementContext. If any adjustment is made
+ * here, make sure it is properly updated there.
+ */
+ @Override
+ @Deprecated
+ Optional<SchemaPath> schemaPath() {
+ return substatementGetSchemaPath();
+ }
+
+ @Override
+ public StatementContextBase<?, ?, ?> getParentContext() {
+ return parent;
+ }
+
+ @Override
+ public StorageNodeType getStorageNodeType() {
+ return StorageNodeType.STATEMENT_LOCAL;
+ }
+
+ @Override
+ public StatementContextBase<?, ?, ?> getParentNamespaceStorage() {
+ return parent;
+ }
+
+ @Override
+ public RootStatementContext<?, ?, ?> getRoot() {
+ return parent.getRoot();
+ }
+
+ @Override
+ protected boolean isIgnoringIfFeatures() {
+ return isIgnoringIfFeatures(parent);
+ }
+
+ @Override
+ protected boolean isIgnoringConfig() {
+ return isIgnoringConfig(parent);
+ }
+
+ @Override
+ protected boolean isParentSupportedByFeatures() {
+ return parent.isSupportedByFeatures();
+ }
+}
import static java.util.Objects.requireNonNull;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.util.ArrayList;
import java.util.Collection;
import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.SchemaTreeNamespace;
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.NamespaceStorageNode;
import org.opendaylight.yangtools.yang.parser.spi.meta.RootStmtContext;
import org.opendaylight.yangtools.yang.parser.spi.source.IncludedModuleContext;
import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Root statement class for a YANG source. All statements defined in that YANG source are mapped underneath an instance
*/
public final class RootStatementContext<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
extends AbstractResumedStatement<A, D, E> implements RootStmtContext.Mutable<A, D, E> {
-
public static final YangVersion DEFAULT_VERSION = YangVersion.VERSION_1;
+ private static final Logger LOG = LoggerFactory.getLogger(RootStatementContext.class);
+ // These namespaces are well-known and not needed after the root is cleaned up
+ private static final Map<Class<?>, SweptNamespace> SWEPT_NAMESPACES = ImmutableMap.of(
+ // FIXME: somehow these two end up being referenced post-sweep
+ // this probably means we have a cross-module reference we do not account for
+// GroupingNamespace.class, new SweptNamespace(GroupingNamespace.class),
+// TypeNamespace.class, new SweptNamespace(TypeNamespace.class),
+ SchemaTreeNamespace.class, new SweptNamespace(SchemaTreeNamespace.class));
+
private final @NonNull SourceSpecificContext sourceContext;
private final A argument;
StatementContextBase<A, D, E> reparent(final StatementContextBase<?, ?, ?> newParent) {
throw new UnsupportedOperationException("Root statement cannot be reparented to" + newParent);
}
+
+ @Override
+ void sweepNamespaces() {
+ LOG.trace("Sweeping root {}", this);
+ sweepNamespaces(SWEPT_NAMESPACES);
+ }
}
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;
* @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> {
+ extends ReactorStmtCtx<A, D, E> {
/**
* Event listener when an item is added to model namespace.
*/
}
private E loadEffective() {
- return effectiveInstance = definition.getFactory().createEffective(new BaseCurrentEffectiveStmtCtx<>(this),
- streamDeclared(), streamEffective());
+ // Creating an effective statement does not strictly require a declared instance -- there are statements like
+ // 'input', which are implicitly defined.
+ // Our implementation design makes an invariant assumption that buildDeclared() has been called by the time
+ // we attempt to create effective statement:
+ buildDeclared();
+
+ final E ret = effectiveInstance = createEffective();
+ // we have called createEffective(), substatements are no longer guarded by us. Let's see if we can clear up
+ // some residue.
+ releaseImplicitRef();
+ return ret;
+ }
+
+ // Exposed for ReplicaStatementContext
+ E createEffective() {
+ return definition.getFactory().createEffective(new BaseCurrentEffectiveStmtCtx<>(this), streamDeclared(),
+ streamEffective());
}
abstract Stream<? extends StmtContext<?, ?, ?>> streamDeclared();
@Override
public final StatementContextBase<A, D, E> replicaAsChildOf(final Mutable<?, ?, ?> parent) {
checkArgument(parent instanceof StatementContextBase, "Unsupported parent %s", parent);
- return this;
+ return replicaAsChildOf((StatementContextBase<?, ?, ?>) parent);
+ }
+
+ final @NonNull StatementContextBase<A, D, E> replicaAsChildOf(final StatementContextBase<?, ?, ?> stmt) {
+ return new ReplicaStatementContext<>(stmt, this);
}
private static void checkEffectiveModelCompleted(final StmtContext<?, ?, ?> stmt) {
*/
abstract boolean hasEmptySubstatements();
+ @Override
+ final boolean noImplictRef() {
+ return effectiveInstance != null || !isSupportedToBuildEffective();
+ }
+
+ @Override
+ final ReactorStmtCtx<?, ?, ?> parentStmtCtx() {
+ return getParentContext();
+ }
+
/**
* 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
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.stmt.reactor;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.VerifyException;
+import java.util.AbstractMap;
+import java.util.Set;
+
+/**
+ * Placeholder namespace map which does not allow access and acts as a sentinel for namespaces which have been
+ * explicitly removed from {@link NamespaceStorageSupport}.
+ */
+final class SweptNamespace extends AbstractMap<Object, Object> {
+ private final Class<?> name;
+
+ SweptNamespace(final Class<?> name) {
+ this.name = requireNonNull(name);
+ }
+
+ @Override
+ public Set<Entry<Object, Object>> entrySet() {
+ throw new VerifyException("Attempted to access swept namespace " + name);
+ }
+}