*/
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.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
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;
final class ModifierImpl implements ModelActionBuilder {
private static final Logger LOG = LoggerFactory.getLogger(ModifierImpl.class);
- private final InferenceContext ctx = new InferenceContext() { };
-
private final Set<AbstractPrerequisite<?>> unsatisfied = new HashSet<>(1);
private final Set<AbstractPrerequisite<?>> mutations = new HashSet<>(1);
+ private final InferenceContext ctx = new InferenceContext() { };
+ private List<Runnable> bootstraps;
private InferenceAction action;
- private boolean actionApplied = false;
+ private boolean actionApplied;
private <D> AbstractPrerequisite<D> addReq(final AbstractPrerequisite<D> prereq) {
LOG.trace("Modifier {} adding prerequisite {}", this, prereq);
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() {
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();
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();
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();
final K key, final ModelProcessingPhase phase) {
checkNotRegistered();
- PhaseModificationInNamespace<C> mod = createModification(phase);
+ final PhaseModificationInNamespace<C> mod = new PhaseModificationInNamespace<>(EFFECTIVE_MODEL);
+ addReq(mod);
+ addMutation(mod);
contextImpl(context).onNamespaceItemAddedAction((Class) namespace, key, mod);
return mod;
}
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,
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,
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,
return mutatesCtxImpl(context, namespace, key, EFFECTIVE_MODEL);
}
- @Nonnull
@Override
- @SuppressWarnings({ "rawtypes", "unchecked" })
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) {
- final Iterator<K> it = keys.iterator();
- Preconditions.checkArgument(it.hasNext(), "Namespace %s keys may not be empty", namespace);
checkNotRegistered();
- final PhaseModificationInNamespace<Mutable<?, ?, E>> ret = createModification(EFFECTIVE_MODEL);
- contextImpl(context).onNamespaceItemAddedAction((Class) namespace, it.next(),
- (parent, ns, foundKey, foundValue) -> {
- checkResult((Mutable<?, ?, E>)foundValue, namespace, it, ret);
- });
- return ret;
- }
-
- @SuppressWarnings({ "rawtypes", "unchecked" })
- private <K, C extends Mutable<?, ?, ?>, N extends IdentifierNamespace<K, ? extends StmtContext<?, ?, ?>>>
- void mutateNextKey(final StmtContext<?, ?, ?> context, final Class<N> namespace,
- final Iterator<K> it, final AbstractPrerequisite<C> result) {
- final PhaseModificationInNamespace<C> mod = createModification(EFFECTIVE_MODEL);
- contextImpl(context).onNamespaceItemAddedAction((Class) namespace, it.next(),
- (parent, ns, foundKey, foundValue) -> {
- checkResult((C) foundValue, namespace, it, result);
- mod.resolvePrereq((C) foundValue);
- });
- }
-
- private <C extends Mutable<?, ?, ?>> PhaseModificationInNamespace<C> createModification(
- final ModelProcessingPhase phase) {
- final PhaseModificationInNamespace<C> ret = new PhaseModificationInNamespace<>(EFFECTIVE_MODEL);
+ final PhaseModificationInNamespacePath<Mutable<?, ?, E>, K, N> ret = new PhaseModificationInNamespacePath<>(
+ EFFECTIVE_MODEL, keys);
addReq(ret);
addMutation(ret);
- return ret;
- }
- private <K, C extends Mutable<?, ?, ?>, N extends IdentifierNamespace<K, ? extends StmtContext<?, ?, ?>>>
- void checkResult(final C context, final Class<N> namespace, final Iterator<K> it,
- final AbstractPrerequisite<C> result) {
- if (it.hasNext()) {
- mutateNextKey(context, namespace, it, result);
- } else {
- result.resolvePrereq(context);
+ if (bootstraps == null) {
+ bootstraps = new ArrayList<>(1);
}
+ bootstraps.add(() -> 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);
+ if (bootstraps != null) {
+ bootstraps.forEach(Runnable::run);
+ bootstraps = null;
+ }
}
private abstract class AbstractPrerequisite<T> implements Prerequisite<T> {
@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() {
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));
}
}
}
- 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);
}
}
- 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;
@Override
public boolean phaseFinished(final StatementContextBase<?, ?, ?> context,
final ModelProcessingPhase finishedPhase) {
- return resolvePrereq((C) context);
+ return resolvePrereq((C) context) || tryApply();
}
@Override
}
}
- 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;
}
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);
+ }
+ }
}