import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
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.NamespaceKeyCriterion;
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.meta.StmtContext.Mutable;
actionApplied = true;
}
- @SuppressWarnings({ "unchecked", "rawtypes" })
private <K, C extends StmtContext<?,?,?>, N extends StatementNamespace<K, ?, ?>> AbstractPrerequisite<C>
requiresCtxImpl(final StmtContext<?, ?, ?> context, final Class<N> namespace, final K key,
final ModelProcessingPhase phase) {
AddedToNamespace<C> addedToNs = new AddedToNamespace<>(phase);
addReq(addedToNs);
- contextImpl(context).onNamespaceItemAddedAction((Class) namespace, key, addedToNs);
+ contextImpl(context).onNamespaceItemAddedAction(namespace, key, addedToNs);
+ return addedToNs;
+ }
+
+ private <K, C extends StmtContext<?,?,?>, N extends StatementNamespace<K, ?, ?>> AbstractPrerequisite<C>
+ requiresCtxImpl(final StmtContext<?, ?, ?> context, final Class<N> namespace,
+ final NamespaceKeyCriterion<K> criterion, final ModelProcessingPhase phase) {
+ checkNotRegistered();
+
+ AddedToNamespace<C> addedToNs = new AddedToNamespace<>(phase);
+ addReq(addedToNs);
+ contextImpl(context).onNamespaceItemAddedAction(namespace, phase, criterion, addedToNs);
return addedToNs;
}
return requiresCtxImpl(context, namespace, key, phase);
}
+ @Nonnull
+ @Override
+ public <K, N extends StatementNamespace<K, ?, ?>> Prerequisite<StmtContext<?, ?, ?>> requiresCtx(
+ final StmtContext<?, ?, ?> context, final Class<N> namespace, final NamespaceKeyCriterion<K> criterion,
+ final ModelProcessingPhase phase) {
+ return requiresCtxImpl(context, namespace, criterion, phase);
+ }
+
@Nonnull
@Override
public <D extends DeclaredStatement<?>> Prerequisite<D> requiresDeclared(
*/
package org.opendaylight.yangtools.yang.parser.stmt.reactor;
+import static java.util.Objects.requireNonNull;
+
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import javax.annotation.Nonnull;
import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
abstract static class ValueAddedListener<K> {
private final NamespaceStorageNode ctxNode;
- private final K key;
- ValueAddedListener(final NamespaceStorageNode contextNode, final K key) {
- this.ctxNode = contextNode;
- this.key = key;
+ ValueAddedListener(final NamespaceStorageNode contextNode) {
+ this.ctxNode = requireNonNull(contextNode);
}
- NamespaceStorageNode getCtxNode() {
+ final NamespaceStorageNode getCtxNode() {
return ctxNode;
}
+ }
+
+ abstract static class KeyedValueAddedListener<K> extends ValueAddedListener<K> {
+ private final K key;
+
+ KeyedValueAddedListener(final NamespaceStorageNode contextNode, final K key) {
+ super(contextNode);
+ this.key = requireNonNull(key);
+ }
+
+ final K getKey() {
+ return key;
+ }
+
final <V> boolean isRequestedValue(final NamespaceBehaviour<K, ? , ?> behavior,
final NamespaceStorageNode storage, final V value) {
- return value == behavior.getFrom(ctxNode, key);
+ return value == behavior.getFrom(getCtxNode(), key);
}
abstract void onValueAdded(Object value);
}
+ abstract static class PredicateValueAddedListener<K, V> extends ValueAddedListener<K> {
+ PredicateValueAddedListener(final NamespaceStorageNode contextNode) {
+ super(contextNode);
+ }
+
+ abstract boolean onValueAdded(@Nonnull K key, @Nonnull V value);
+ }
+
protected final NamespaceBehaviour<K, V, N> delegate;
private final List<VirtualNamespaceContext<?, V, ?, K>> derivedNamespaces = new ArrayList<>();
this.delegate = delegate;
}
- abstract void addListener(K key, ValueAddedListener<K> listener);
+ abstract void addListener(KeyedValueAddedListener<K> listener);
+
+ abstract void addListener(PredicateValueAddedListener<K, V> listener);
@Override
public abstract void addTo(NamespaceStorageNode storage, K key, V value);
protected void notifyListeners(final NamespaceStorageNode storage,
- final Iterator<ValueAddedListener<K>> keyListeners, final V value) {
- List<ValueAddedListener<K>> toNotify = new ArrayList<>();
+ final Iterator<? extends KeyedValueAddedListener<K>> keyListeners, final V value) {
+ List<KeyedValueAddedListener<K>> toNotify = new ArrayList<>();
while (keyListeners.hasNext()) {
- final ValueAddedListener<K> listener = keyListeners.next();
+ final KeyedValueAddedListener<K> listener = keyListeners.next();
if (listener.isRequestedValue(this, storage, value)) {
keyListeners.remove();
toNotify.add(listener);
}
}
- for (ValueAddedListener<K> listener : toNotify) {
+ for (KeyedValueAddedListener<K> listener : toNotify) {
listener.onValueAdded(value);
}
}
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Optional;
import java.util.Set;
import javax.annotation.Nonnull;
import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.Registry;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceKeyCriterion;
import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceNotAvailableException;
import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
}
@Nonnull
- public final <K,V, KT extends K, N extends IdentifierNamespace<K, V>> V getFromNamespace(final Class<N> type,
+ public final <K, V, KT extends K, N extends IdentifierNamespace<K, V>> V getFromNamespace(final Class<N> type,
final KT key) throws NamespaceNotAvailableException {
return getBehaviourRegistry().getNamespaceBehaviour(type).getFrom(this, key);
}
+ public final <K, V, N extends IdentifierNamespace<K, V>> Optional<Entry<K, V>> getFromNamespace(
+ final Class<N> type, final NamespaceKeyCriterion<K> criterion) {
+ return getBehaviourRegistry().getNamespaceBehaviour(type).getFrom(this, criterion);
+ }
+
public final <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAllFromNamespace(final Class<N> type) {
return getBehaviourRegistry().getNamespaceBehaviour(type).getAllFrom(this);
}
package org.opendaylight.yangtools.yang.parser.stmt.reactor;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
extends NamespaceBehaviourWithListeners<K, V, N> {
// FIXME: Change this to Multimap, once issue with modules is resolved.
- private final List<ValueAddedListener<K>> listeners = new ArrayList<>();
+ private final List<KeyedValueAddedListener<K>> listeners = new ArrayList<>();
+
+ private final Collection<PredicateValueAddedListener<K, V>> predicateListeners = new ArrayList<>();
SimpleNamespaceContext(final NamespaceBehaviour<K, V, N> delegate) {
super(delegate);
}
@Override
- void addListener(final K key, final ValueAddedListener<K> listener) {
+ void addListener(final KeyedValueAddedListener<K> listener) {
listeners.add(listener);
}
- private Iterator<ValueAddedListener<K>> getMutableListeners(final K key) {
- return listeners.iterator();
+ @Override
+ void addListener(final PredicateValueAddedListener<K, V> listener) {
+ predicateListeners.add(listener);
}
@Override
public void addTo(final NamespaceStorageNode storage, final K key, final V value) {
delegate.addTo(storage, key, value);
- notifyListeners(storage, getMutableListeners(key), value);
+ notifyListeners(storage, listeners.iterator(), value);
+
+ final Iterator<PredicateValueAddedListener<K, V>> it = predicateListeners.iterator();
+ while (it.hasNext()) {
+ if (it.next().onValueAdded(key, value)) {
+ it.remove();
+ }
+ }
+
notifyDerivedNamespaces(storage, key, value);
}
}
import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToModuleContext;
import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToPrefixToModuleCtx;
-import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToModuleCtx;
import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToModuleCtx;
import org.opendaylight.yangtools.yang.parser.spi.source.ImportedModuleContext;
import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
private static boolean tryToProgress(final Collection<ModifierImpl> currentPhaseModifiers) {
boolean hasProgressed = false;
+ // Try making forward progress ...
final Iterator<ModifierImpl> modifier = currentPhaseModifiers.iterator();
while (modifier.hasNext()) {
if (modifier.next().tryApply()) {
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.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;
// 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 %s does not support listeners", type);
-
- final NamespaceBehaviourWithListeners<K, V, N> casted = (NamespaceBehaviourWithListeners<K, V, N>) behaviour;
- casted.addListener(key, new ValueAddedListener<K>(this, key) {
+ getBehaviour(type).addListener(new KeyedValueAddedListener<K>(this, key) {
@Override
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()}.
*/
final class VirtualNamespaceContext<K, V, N extends IdentifierNamespace<K, V>, D>
extends NamespaceBehaviourWithListeners<K, V, N> {
- private final Multimap<D, ValueAddedListener<K>> listeners = HashMultimap.create();
+ private final Multimap<D, KeyedValueAddedListener<K>> listeners = HashMultimap.create();
private final DerivedNamespaceBehaviour<K, V, D, N, ?> derivedDelegate;
VirtualNamespaceContext(final DerivedNamespaceBehaviour<K, V, D, N, ?> delegate) {
}
@Override
- void addListener(final K key, final ValueAddedListener<K> listener) {
- listeners.put(derivedDelegate.getSignificantKey(key), listener);
+ void addListener(final KeyedValueAddedListener<K> listener) {
+ listeners.put(derivedDelegate.getSignificantKey(listener.getKey()), listener);
+ }
+
+ @Override
+ void addListener(final PredicateValueAddedListener<K, V> listener) {
+ throw new UnsupportedOperationException("Virtual namespaces support only exact lookups");
}
void addedToSourceNamespace(final NamespaceStorageNode storage, final D key, final V value) {
@Nonnull <K, N extends StatementNamespace<K, ?, ?>> Prerequisite<StmtContext<?, ?, ?>> requiresCtx(
StmtContext<?, ?, ?> context, Class<N> namespace, K key, ModelProcessingPhase phase);
+ @Nonnull <K, N extends StatementNamespace<K, ?, ?>> Prerequisite<StmtContext<?, ?, ?>> requiresCtx(
+ StmtContext<?, ?, ?> context, Class<N> namespace, NamespaceKeyCriterion<K> criterion,
+ ModelProcessingPhase phase);
+
default @Nonnull <T extends Mutable<?, ?, ?>> Prerequisite<T> mutatesEffectiveCtx(final T stmt) {
return mutatesCtx(stmt, EFFECTIVE_MODEL);
}
import static java.util.Objects.requireNonNull;
+import com.google.common.base.Verify;
import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.opendaylight.yangtools.concepts.Identifiable;
*/
public abstract V getFrom(NamespaceStorageNode storage, K key);
+ /**
+ * Returns the key/value mapping best matching specified criterion.
+ *
+ * @param storage namespace storage
+ * @param criterion selection criterion
+ * @return Selected mapping, if available.
+ */
+ public final Optional<Entry<K, V>> getFrom(final NamespaceStorageNode storage,
+ final NamespaceKeyCriterion<K> criterion) {
+ final Map<K, V> mappings = getAllFrom(storage);
+ if (mappings == null) {
+ return Optional.empty();
+ }
+
+ Entry<K, V> match = null;
+ for (Entry<K, V> entry : mappings.entrySet()) {
+ final K key = entry.getKey();
+ if (criterion.match(key)) {
+ if (match != null) {
+ final K selected = criterion.select(match.getKey(), key);
+ if (selected.equals(match.getKey())) {
+ continue;
+ }
+
+ Verify.verify(selected == key, "Criterion %s selected invalid key %s from candidates [%s %s]",
+ selected, match.getKey(), key);
+ }
+
+ match = entry;
+ }
+ }
+
+ return Optional.ofNullable(match);
+ }
+
/**
* Returns all values of a keys of param class from model namespace storage.
*
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies, 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.spi.meta;
+
+import com.google.common.annotations.Beta;
+import javax.annotation.Nonnull;
+
+/**
+ * Namespace key matching criterion.
+ *
+ * @param <K> Key type
+ *
+ * @author Robert Varga
+ */
+@Beta
+public abstract class NamespaceKeyCriterion<K> {
+ /**
+ * Match a key against this criterion.
+ *
+ * @param key Key to be matched
+ * @return True if the key matches this criterion, false otherwise.
+ */
+ public abstract boolean match(@Nonnull K key);
+
+ /**
+ * Select the better match from two candidate keys.
+ *
+ * @param first First key
+ * @param second Second key
+ * @return Selected key, must be either first or second key, by identity.
+ */
+ public abstract K select(@Nonnull K first, @Nonnull K second);
+
+ @Override
+ public abstract String toString();
+
+ @Override
+ public final int hashCode() {
+ return super.hashCode();
+ }
+
+ @Override
+ public final boolean equals(final Object obj) {
+ return super.equals(obj);
+ }
+}