Fire AbstractPrerequisite listeners as soon as they resolve
[yangtools.git] / yang / yang-parser-reactor / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / ModifierImpl.java
index bf77d868050dcfb44e2a8e653dd8f447eb94eae0..5edde2ffd80a3b391b6c6d86331cce5b11fb27a7 100644 (file)
@@ -7,18 +7,20 @@
  */
 package org.opendaylight.yangtools.yang.parser.stmt.reactor;
 
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
 import static org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.EFFECTIVE_MODEL;
 import static org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.FULL_DECLARATION;
 
 import com.google.common.base.MoreObjects;
 import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.common.base.Preconditions;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Set;
 import java.util.function.Function;
-import javax.annotation.Nonnull;
+import org.eclipse.jdt.annotation.NonNull;
 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.IdentifierNamespace;
@@ -51,14 +53,14 @@ final class ModifierImpl implements ModelActionBuilder {
         return prereq;
     }
 
-    private <T> AbstractPrerequisite<T> addMutation(final AbstractPrerequisite<T> mutation) {
+    private <T> @NonNull AbstractPrerequisite<T> addMutation(final @NonNull AbstractPrerequisite<T> mutation) {
         LOG.trace("Modifier {} adding mutation {}", this, mutation);
         mutations.add(mutation);
         return mutation;
     }
 
     private void checkNotRegistered() {
-        Preconditions.checkState(action == null, "Action was already registered.");
+        checkState(action == null, "Action was already registered.");
     }
 
     private boolean removeSatisfied() {
@@ -84,13 +86,7 @@ final class ModifierImpl implements ModelActionBuilder {
         action = null;
     }
 
-    private void applyAction() {
-        Preconditions.checkState(!actionApplied);
-        action.apply(ctx);
-        actionApplied = true;
-    }
-
-    private <K, C extends StmtContext<?, ?, ?>, N extends StatementNamespace<K, ?, ?>> AbstractPrerequisite<C>
+    private <K, C extends StmtContext<?, ?, ?>, N extends StatementNamespace<K, ?, ?>> @NonNull AbstractPrerequisite<C>
             requiresCtxImpl(final StmtContext<?, ?, ?> context, final Class<N> namespace, final K key,
                     final ModelProcessingPhase phase)  {
         checkNotRegistered();
@@ -101,7 +97,7 @@ final class ModifierImpl implements ModelActionBuilder {
         return addedToNs;
     }
 
-    private <K, C extends StmtContext<?, ?, ?>, N extends StatementNamespace<K, ?, ?>> AbstractPrerequisite<C>
+    private <K, C extends StmtContext<?, ?, ?>, N extends StatementNamespace<K, ?, ?>> @NonNull AbstractPrerequisite<C>
             requiresCtxImpl(final StmtContext<?, ?, ?> context, final Class<N> namespace,
                     final NamespaceKeyCriterion<K> criterion, final ModelProcessingPhase phase)  {
         checkNotRegistered();
@@ -112,7 +108,7 @@ final class ModifierImpl implements ModelActionBuilder {
         return addedToNs;
     }
 
-    private <C extends StmtContext<?, ?, ?>> AbstractPrerequisite<C> requiresCtxImpl(final C context,
+    private <C extends StmtContext<?, ?, ?>> @NonNull AbstractPrerequisite<C> requiresCtxImpl(final C context,
             final ModelProcessingPhase phase) {
         checkNotRegistered();
 
@@ -128,7 +124,7 @@ final class ModifierImpl implements ModelActionBuilder {
                     final K key, final ModelProcessingPhase phase) {
         checkNotRegistered();
 
-        PhaseModificationInNamespace<C> mod = new PhaseModificationInNamespace<>(phase);
+        final PhaseModificationInNamespace<C> mod = new PhaseModificationInNamespace<>(EFFECTIVE_MODEL);
         addReq(mod);
         addMutation(mod);
         contextImpl(context).onNamespaceItemAddedAction((Class) namespace, key, mod);
@@ -136,62 +132,60 @@ final class ModifierImpl implements ModelActionBuilder {
     }
 
     private static StatementContextBase<?, ?, ?> contextImpl(final Object value) {
-        Preconditions.checkArgument(value instanceof StatementContextBase,
-            "Supplied context %s is not provided by this reactor.", value);
+        checkArgument(value instanceof StatementContextBase, "Supplied context %s is not provided by this reactor.",
+            value);
         return StatementContextBase.class.cast(value);
     }
 
     boolean tryApply() {
-        Preconditions.checkState(action != null, "Action was not defined yet.");
+        checkState(action != null, "Action was not defined yet.");
 
         if (removeSatisfied()) {
-            applyAction();
+            if (!actionApplied) {
+                action.apply(ctx);
+                actionApplied = true;
+            }
             return true;
         }
         return false;
     }
 
-    @Nonnull
     @Override
     public <C extends Mutable<?, ?, ?>, T extends C> Prerequisite<C> mutatesCtx(final T context,
             final ModelProcessingPhase phase) {
         return addMutation(new PhaseMutation<>(contextImpl(context), phase));
     }
 
-    @Nonnull
     @Override
-    public <A,D extends DeclaredStatement<A>,E extends EffectiveStatement<A, D>>
+    public <A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
             AbstractPrerequisite<StmtContext<A, D, E>> requiresCtx(final StmtContext<A, D, E> context,
                     final ModelProcessingPhase phase) {
         return requiresCtxImpl(context, phase);
     }
 
 
-    @Nonnull
     @Override
     public <K, N extends StatementNamespace<K, ?, ?>> Prerequisite<StmtContext<?, ?, ?>> requiresCtx(
-            final StmtContext<?, ?, ?> context, final Class<N> namespace, final K key,
+            final StmtContext<?, ?, ?> context, final Class<@NonNull N> namespace, final K key,
             final ModelProcessingPhase phase) {
         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) {
+            final StmtContext<?, ?, ?> context, final Class<@NonNull N> namespace,
+            final NamespaceKeyCriterion<K> criterion, final ModelProcessingPhase phase) {
         return requiresCtxImpl(context, namespace, criterion, phase);
     }
 
-    @Nonnull
     @Override
     public <D extends DeclaredStatement<?>> Prerequisite<D> requiresDeclared(
             final StmtContext<?, ? extends D, ?> context) {
         return requiresCtxImpl(context, FULL_DECLARATION).transform(StmtContext::buildDeclared);
     }
 
-    @Nonnull
     @Override
+    @Deprecated
     public <K, D extends DeclaredStatement<?>, N extends StatementNamespace<K, ? extends D, ?>> Prerequisite<D>
             requiresDeclared(final StmtContext<?, ?, ?> context, final Class<N> namespace, final K key) {
         final AbstractPrerequisite<StmtContext<?, D, ?>> rawContext = requiresCtxImpl(context, namespace, key,
@@ -199,23 +193,23 @@ final class ModifierImpl implements ModelActionBuilder {
         return rawContext.transform(StmtContext::buildDeclared);
     }
 
-    @Nonnull
     @Override
+    @Deprecated
     public <K, D extends DeclaredStatement<?>, N extends StatementNamespace<K, ? extends D, ?>>
             AbstractPrerequisite<StmtContext<?, D, ?>> requiresDeclaredCtx(final StmtContext<?, ?, ?> context,
                     final Class<N> namespace, final K key) {
         return requiresCtxImpl(context, namespace, key, FULL_DECLARATION);
     }
 
-    @Nonnull
     @Override
+    @Deprecated
     public <E extends EffectiveStatement<?, ?>> Prerequisite<E> requiresEffective(
             final StmtContext<?, ?, ? extends E> stmt) {
         return requiresCtxImpl(stmt, EFFECTIVE_MODEL).transform(StmtContext::buildEffective);
     }
 
-    @Nonnull
     @Override
+    @Deprecated
     public <K, E extends EffectiveStatement<?, ?>, N extends StatementNamespace<K, ?, ? extends E>> Prerequisite<E>
             requiresEffective(final StmtContext<?, ?, ?> context, final Class<N> namespace, final K key) {
         final AbstractPrerequisite<StmtContext<?, ?, E>> rawContext = requiresCtxImpl(context, namespace, key,
@@ -223,22 +217,21 @@ final class ModifierImpl implements ModelActionBuilder {
         return rawContext.transform(StmtContext::buildEffective);
     }
 
-    @Nonnull
     @Override
+    @Deprecated
     public <K, E extends EffectiveStatement<?, ?>, N extends StatementNamespace<K, ?, ? extends E>>
             AbstractPrerequisite<StmtContext<?, ?, E>> requiresEffectiveCtx(final StmtContext<?, ?, ?> context,
                     final Class<N> namespace, final K key) {
         return requiresCtxImpl(contextImpl(context), namespace, key, EFFECTIVE_MODEL);
     }
 
-    @Nonnull
     @Override
+    @Deprecated
     public <N extends IdentifierNamespace<?, ?>> Prerequisite<Mutable<?, ?, ?>> mutatesNs(
             final Mutable<?, ?, ?> context, final Class<N> namespace) {
         return addMutation(new NamespaceMutation<>(contextImpl(context), namespace));
     }
 
-    @Nonnull
     @Override
     public <K, E extends EffectiveStatement<?, ?>, N extends IdentifierNamespace<K, ? extends StmtContext<?, ?, ?>>>
             AbstractPrerequisite<Mutable<?, ?, E>> mutatesEffectiveCtx(final StmtContext<?, ?, ?> context,
@@ -246,11 +239,26 @@ final class ModifierImpl implements ModelActionBuilder {
         return mutatesCtxImpl(context, namespace, key, EFFECTIVE_MODEL);
     }
 
+    @Override
+    public <K, E extends EffectiveStatement<?, ?>, N extends IdentifierNamespace<K, ? extends StmtContext<?, ?, ?>>>
+            AbstractPrerequisite<Mutable<?, ?, E>> mutatesEffectiveCtxPath(final StmtContext<?, ?, ?> context,
+                    final Class<N> namespace, final Iterable<K> keys) {
+        checkNotRegistered();
+
+        final PhaseModificationInNamespacePath<Mutable<?, ?, E>, K, N> ret = new PhaseModificationInNamespacePath<>(
+                EFFECTIVE_MODEL, keys);
+        addReq(ret);
+        addMutation(ret);
+
+        ret.hookOnto(context, namespace);
+        return ret;
+    }
+
     @Override
     @SuppressWarnings("checkstyle:hiddenField")
     public void apply(final InferenceAction action) {
-        Preconditions.checkState(this.action == null, "Action already defined to %s", this.action);
-        this.action = Preconditions.checkNotNull(action);
+        checkState(this.action == null, "Action already defined to %s", this.action);
+        this.action = requireNonNull(action);
     }
 
     private abstract class AbstractPrerequisite<T> implements Prerequisite<T> {
@@ -260,9 +268,9 @@ final class ModifierImpl implements ModelActionBuilder {
         @Override
         @SuppressWarnings("checkstyle:hiddenField")
         public final T resolve(final InferenceContext ctx) {
-            Preconditions.checkState(done);
-            Preconditions.checkArgument(ctx == ModifierImpl.this.ctx);
-            return value;
+            checkState(done);
+            checkArgument(ctx == ModifierImpl.this.ctx);
+            return verifyNotNull(value, "Attempted to access unavailable prerequisite %s", this);
         }
 
         final boolean isDone() {
@@ -276,7 +284,7 @@ final class ModifierImpl implements ModelActionBuilder {
             return isApplied();
         }
 
-        final <O> Prerequisite<O> transform(final Function<? super T, O> transformation) {
+        final <O> @NonNull Prerequisite<O> transform(final Function<? super T, O> transformation) {
             return context -> transformation.apply(resolve(context));
         }
 
@@ -290,7 +298,7 @@ final class ModifierImpl implements ModelActionBuilder {
         }
     }
 
-    private class PhaseMutation<C> extends AbstractPrerequisite<C> implements ContextMutation {
+    private final class PhaseMutation<C> extends AbstractPrerequisite<C> implements ContextMutation {
         @SuppressWarnings("unchecked")
         PhaseMutation(final StatementContextBase<?, ?, ?> context, final ModelProcessingPhase phase) {
             context.addMutation(phase, this);
@@ -303,24 +311,24 @@ final class ModifierImpl implements ModelActionBuilder {
         }
     }
 
-    private class PhaseFinished<C extends StmtContext<?, ?, ?>> extends AbstractPrerequisite<C>
+    private final class PhaseFinished<C extends StmtContext<?, ?, ?>> extends AbstractPrerequisite<C>
             implements OnPhaseFinished {
         @SuppressWarnings("unchecked")
         @Override
         public boolean phaseFinished(final StatementContextBase<?, ?, ?> context,
                 final ModelProcessingPhase finishedPhase) {
-            return resolvePrereq((C) context);
+            return resolvePrereq((C) context) || tryApply();
         }
     }
 
-    private class NamespaceMutation<N extends IdentifierNamespace<?, ?>>
+    private final class NamespaceMutation<N extends IdentifierNamespace<?, ?>>
             extends AbstractPrerequisite<Mutable<?, ?, ?>>  {
         NamespaceMutation(final StatementContextBase<?, ?, ?> ctx, final Class<N> namespace) {
             resolvePrereq(ctx);
         }
     }
 
-    private class AddedToNamespace<C extends StmtContext<?, ?, ?>> extends AbstractPrerequisite<C>
+    private final class AddedToNamespace<C extends StmtContext<?, ?, ?>> extends AbstractPrerequisite<C>
             implements OnNamespaceItemAdded, OnPhaseFinished {
         private final ModelProcessingPhase phase;
 
@@ -338,7 +346,7 @@ final class ModifierImpl implements ModelActionBuilder {
         @Override
         public boolean phaseFinished(final StatementContextBase<?, ?, ?> context,
                 final ModelProcessingPhase finishedPhase) {
-            return resolvePrereq((C) context);
+            return resolvePrereq((C) context) || tryApply();
         }
 
         @Override
@@ -347,12 +355,12 @@ final class ModifierImpl implements ModelActionBuilder {
         }
     }
 
-    private class PhaseModificationInNamespace<C extends Mutable<?, ?, ?>> extends AbstractPrerequisite<C>
+    private final class PhaseModificationInNamespace<C extends Mutable<?, ?, ?>> extends AbstractPrerequisite<C>
             implements OnNamespaceItemAdded, ContextMutation {
         private final ModelProcessingPhase modPhase;
 
         PhaseModificationInNamespace(final ModelProcessingPhase phase) {
-            Preconditions.checkArgument(phase != null, "Model processing phase must not be null");
+            checkArgument(phase != null, "Model processing phase must not be null");
             this.modPhase = phase;
         }
 
@@ -370,4 +378,74 @@ final class ModifierImpl implements ModelActionBuilder {
             return isApplied();
         }
     }
+
+    /**
+     * This similar to {@link PhaseModificationInNamespace}, but allows recursive descent until it finds the real
+     * target. The mechanics is driven as a sequence of prerequisites along a path: first we hook onto namespace to
+     * give us the first step. When it does, we hook onto the first item to provide us the second step and so on.
+     */
+    private final class PhaseModificationInNamespacePath<C extends Mutable<?, ?, ?>, K,
+            N extends IdentifierNamespace<K, ? extends StmtContext<?, ?, ?>>> extends AbstractPrerequisite<C>
+            implements OnNamespaceItemAdded, ContextMutation {
+        private final ModelProcessingPhase modPhase;
+        private final Iterable<K> keys;
+        private final Iterator<K> it;
+
+        PhaseModificationInNamespacePath(final ModelProcessingPhase phase, final Iterable<K> keys) {
+            this.modPhase = requireNonNull(phase);
+            this.keys = requireNonNull(keys);
+            it = keys.iterator();
+        }
+
+        @Override
+        public boolean isFinished() {
+            return isApplied();
+        }
+
+        @Override
+        public void namespaceItemAdded(final StatementContextBase<?, ?, ?> context, final Class<?> namespace,
+                final Object key, final Object value) {
+            LOG.debug("Action for {} got key {}", keys, key);
+
+            final StatementContextBase<?, ?, ?> target = contextImpl(value);
+            if (!target.isSupportedByFeatures()) {
+                LOG.debug("Key {} in {} is not supported", key, keys);
+                resolvePrereq(null);
+                action.prerequisiteUnavailable(this);
+                return;
+            }
+
+            // Hook onto target: we either have a modification of the target itself or one of its children.
+            target.addMutation(modPhase, this);
+            // We have completed the context -> target step, hence we are no longer directly blocking context from
+            // making forward progress.
+            context.removeMutation(modPhase, this);
+
+            if (!it.hasNext()) {
+                // Last step: we are done
+                if (resolvePrereq((C) value)) {
+                    tryApply();
+                }
+                return;
+            }
+
+            // Make sure target's storage notifies us when the next step becomes available.
+            hookOnto(target, namespace, it.next());
+        }
+
+        @Override
+        ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+            return super.addToStringAttributes(toStringHelper).add("phase", modPhase).add("keys", keys);
+        }
+
+        void hookOnto(final StmtContext<?, ?, ?> context, final Class<?> namespace) {
+            checkArgument(it.hasNext(), "Namespace %s keys may not be empty", namespace);
+            hookOnto(contextImpl(context), namespace, it.next());
+        }
+
+        @SuppressWarnings("unchecked")
+        private void hookOnto(final StatementContextBase<?, ?, ?> context, final Class<?> namespace, final K key) {
+            context.onNamespaceItemAddedAction((Class) namespace, requireNonNull(key), this);
+        }
+    }
 }