import java.util.EnumMap;
import java.util.EventListener;
import java.util.Iterator;
+import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nonnull;
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.opendaylight.yangtools.yang.parser.spi.meta.NamespaceKeyCriterion;
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.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.SupportedFeaturesNamespace;
import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace.SupportedFeatures;
-import org.opendaylight.yangtools.yang.parser.stmt.reactor.NamespaceBehaviourWithListeners.ValueAddedListener;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.NamespaceBehaviourWithListeners.KeyedValueAddedListener;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.NamespaceBehaviourWithListeners.PredicateValueAddedListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
- extends NamespaceStorageSupport implements StmtContext.Mutable<A, D, E> {
+ extends NamespaceStorageSupport implements Mutable<A, D, E> {
/**
- * event listener when an item is added to model namespace.
+ * Event listener when an item is added to model namespace.
*/
interface OnNamespaceItemAdded extends EventListener {
/**
- * @throws SourceException
+ * Invoked whenever a new item is added to a namespace.
*/
void namespaceItemAdded(StatementContextBase<?, ?, ?> context, Class<?> namespace, Object key, Object value);
}
/**
- * event listener when a parsing {@link ModelProcessingPhase} is completed.
+ * Event listener when a parsing {@link ModelProcessingPhase} is completed.
*/
interface OnPhaseFinished extends EventListener {
/**
- * @throws SourceException
+ * Invoked whenever a processing phase has finished.
*/
boolean phaseFinished(StatementContextBase<?, ?, ?> context, ModelProcessingPhase phase);
}
/**
- * interface for all mutations within an {@link ModelActionBuilder.InferenceAction}.
+ * Interface for all mutations within an {@link ModelActionBuilder.InferenceAction}.
*/
interface ContextMutation {
public abstract StatementContextBase<?, ?, ?> getParentContext();
/**
+ * Returns the model root for this statement.
+ *
* @return root context of statement
*/
@Nonnull
public abstract RootStatementContext<?, ?, ?> getRoot();
/**
+ * Returns the origin of the statement.
+ *
* @return origin of statement
*/
@Nonnull
}
/**
+ * Returns a reference to statement source.
+ *
* @return reference of statement source
*/
@Nonnull
return Collections.unmodifiableCollection(effective);
}
- public void removeStatementsFromEffectiveSubstatements(final Collection<? extends StmtContext<?, ?, ?>> substatements) {
+ public void removeStatementsFromEffectiveSubstatements(
+ final Collection<? extends StmtContext<?, ?, ?>> substatements) {
if (!effective.isEmpty()) {
effective.removeAll(substatements);
shrinkEffective();
}
/**
- * Removes a statement context from the effective substatements
- * based on its statement definition (i.e statement keyword) and raw (in String form) statement argument.
- * The statement context is removed only if both statement definition and statement argument match with
- * one of the effective substatements' statement definition and argument.
+ * Removes a statement context from the effective substatements based on its statement definition (i.e statement
+ * keyword) and raw (in String form) statement argument. The statement context is removed only if both statement
+ * definition and statement argument match with one of the effective substatements' statement definition
+ * and argument.
*
+ * <p>
* If the statementArg parameter is null, the statement context is removed based only on its statement definition.
*
* @param statementDef statement definition of the statement context to remove
}
/**
- * adds effective statement to collection of substatements
+ * Adds an effective statement to collection of substatements.
*
* @param substatement substatement
* @throws IllegalStateException
}
/**
- * adds effective statement to collection of substatements
+ * Adds an effective statement to collection of substatements.
*
* @param substatements substatements
* @throws IllegalStateException
* @param argument statement argument
* @return A new substatement
*/
- public final <CA, CD extends DeclaredStatement<CA>, CE extends EffectiveStatement<CA, CD>> StatementContextBase<CA, CD, CE> createSubstatement(
- final int offset, final StatementDefinitionContext<CA, CD, CE> def, final StatementSourceReference ref,
- final String argument) {
+ @SuppressWarnings("checkstyle:methodTypeParameterName")
+ public final <CA, CD extends DeclaredStatement<CA>, CE extends EffectiveStatement<CA, CD>>
+ StatementContextBase<CA, CD, CE> createSubstatement(final int offset,
+ final StatementDefinitionContext<CA, CD, CE> def, final StatementSourceReference ref,
+ final String argument) {
final ModelProcessingPhase inProgressPhase = getRoot().getSourceContext().getInProgressPhase();
Preconditions.checkState(inProgressPhase != ModelProcessingPhase.EFFECTIVE_MODEL,
"Declared statement cannot be added in effective phase at: %s", getStatementSourceReference());
- final Optional<StatementContextBase<?, ?, ?>> implicitStatement = definition.beforeSubStatementCreated(this,
- offset, def, ref, argument);
- if (implicitStatement.isPresent()) {
- return implicitStatement.get().createSubstatement(offset, def, ref, argument);
+ final Optional<StatementSupport<?, ?, ?>> implicitParent = definition.getImplicitParentFor(def.getPublicView());
+ if (implicitParent.isPresent()) {
+ return createImplicitParent(offset, implicitParent.get(), ref, argument).createSubstatement(offset, def,
+ ref, argument);
}
final StatementContextBase<CA, CD, CE> ret = new SubstatementContext<>(this, def, ref, argument);
return ret;
}
+ 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);
+ }
+
/**
* Lookup substatement by its offset in this statement.
*
/**
* Ends declared section of current node.
- *
- * @param ref
- * @throws SourceException
*/
void endDeclared(final StatementSourceReference ref, final ModelProcessingPhase phase) {
definition().onDeclarationFinished(this, phase);
}
/**
+ * Return the context in which this statement was defined.
+ *
* @return statement definition
*/
protected final StatementDefinitionContext<A, D, E> definition() {
// definition().onNamespaceElementAdded(this, type, key, value);
}
- <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceItemAddedAction(final Class<N> type, final K key,
- final OnNamespaceItemAdded listener) throws SourceException {
+ final <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceItemAddedAction(final Class<N> type, final K key,
+ final OnNamespaceItemAdded listener) {
final Object potential = getFromNamespace(type, key);
if (potential != null) {
LOG.trace("Listener on {} key {} satisfied immediately", type, key);
return;
}
- final NamespaceBehaviour<K, V, N> behaviour = getBehaviourRegistry().getNamespaceBehaviour(type);
- Preconditions.checkArgument(behaviour instanceof NamespaceBehaviourWithListeners,
- "Namespace {} does not support listeners", type);
-
- final NamespaceBehaviourWithListeners<K, V, N> casted = (NamespaceBehaviourWithListeners<K, V, N>) behaviour;
- casted.addValueListener(new ValueAddedListener<K>(this, key) {
+ getBehaviour(type).addListener(new KeyedValueAddedListener<K>(this, key) {
@Override
- void onValueAdded(final Object key, final Object value) {
+ void onValueAdded(final Object value) {
listener.namespaceItemAdded(StatementContextBase.this, type, key, value);
}
});
}
+ final <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceItemAddedAction(final Class<N> type,
+ final ModelProcessingPhase phase, final NamespaceKeyCriterion<K> criterion,
+ final OnNamespaceItemAdded listener) {
+ final Optional<Entry<K, V>> existing = getFromNamespace(type, criterion);
+ if (existing.isPresent()) {
+ final Entry<K, V> entry = existing.get();
+ LOG.debug("Listener on {} criterion {} found a pre-existing match: {}", type, criterion, entry);
+ waitForPhase(entry.getValue(), type, phase, criterion, listener);
+ return;
+ }
+
+ final NamespaceBehaviourWithListeners<K, V, N> behaviour = getBehaviour(type);
+ behaviour.addListener(new PredicateValueAddedListener<K, V>(this) {
+ @Override
+ boolean onValueAdded(final K key, final V value) {
+ if (criterion.match(key)) {
+ LOG.debug("Listener on {} criterion {} matched added key {}", type, criterion, key);
+ waitForPhase(value, type, phase, criterion, listener);
+ return true;
+ }
+
+ return false;
+ }
+ });
+ }
+
+ final <K, V, N extends IdentifierNamespace<K, V>> void selectMatch(final Class<N> type,
+ final NamespaceKeyCriterion<K> criterion, final OnNamespaceItemAdded listener) {
+ final Optional<Entry<K, V>> optMatch = getFromNamespace(type, criterion);
+ Preconditions.checkState(optMatch.isPresent(),
+ "Failed to find a match for criterion %s in namespace %s node %s", criterion, type, this);
+ final Entry<K, V> match = optMatch.get();
+ listener.namespaceItemAdded(StatementContextBase.this, type, match.getKey(), match.getValue());
+ }
+
+ final <K, V, N extends IdentifierNamespace<K, V>> void waitForPhase(final Object value, final Class<N> type,
+ final ModelProcessingPhase phase, final NamespaceKeyCriterion<K> criterion,
+ final OnNamespaceItemAdded listener) {
+ ((StatementContextBase<?, ? ,?>) value).addPhaseCompletedListener(phase,
+ (context, completedPhase) -> {
+ selectMatch(type, criterion, listener);
+ return true;
+ });
+ }
+
+ private <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviourWithListeners<K, V, N> getBehaviour(
+ final Class<N> type) {
+ final NamespaceBehaviour<K, V, N> behaviour = getBehaviourRegistry().getNamespaceBehaviour(type);
+ Preconditions.checkArgument(behaviour instanceof NamespaceBehaviourWithListeners,
+ "Namespace %s does not support listeners", type);
+
+ return (NamespaceBehaviourWithListeners<K, V, N>) behaviour;
+ }
+
/**
* See {@link StatementSupport#getPublicView()}.
*/
}
/**
- * adds {@link OnPhaseFinished} listener for a {@link ModelProcessingPhase} end
+ * Adds {@link OnPhaseFinished} listener for a {@link ModelProcessingPhase} end. If the base has already completed
+ * the listener is notified immediately.
*
- * @throws SourceException
+ * @param phase requested completion phase
+ * @param listener listener to invoke
+ * @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",
}
/**
- * adds {@link ContextMutation} to {@link ModelProcessingPhase}
+ * Adds a {@link ContextMutation} to a {@link ModelProcessingPhase}.
*
* @throws IllegalStateException
* when the mutation was registered after phase was completed
void addMutation(final ModelProcessingPhase phase, final ContextMutation mutation) {
ModelProcessingPhase finishedPhase = completedPhase;
while (finishedPhase != null) {
- if (phase.equals(finishedPhase)) {
- throw new IllegalStateException("Mutation registered after phase was completed at: " +
- getStatementSourceReference());
- }
+ Preconditions.checkState(!phase.equals(finishedPhase),
+ "Mutation registered after phase was completed at: %s", getStatementSourceReference());
finishedPhase = finishedPhase.getPreviousPhase();
}