BUG-4688: Add flexible match support to NamespaceStorageSupport
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / StatementContextBase.java
index 1807b101de3833d82ee1f62e73722a249acea9cc..d009eb78dba486b89a922155f69c16b5e6729a4c 100644 (file)
@@ -22,6 +22,7 @@ import java.util.Collections;
 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;
@@ -40,6 +41,7 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
 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;
@@ -50,7 +52,8 @@ 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;
 
@@ -567,8 +570,8 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
         // 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);
@@ -576,12 +579,7 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
             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);
@@ -589,6 +587,60 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
         });
     }
 
+    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()}.
      */