import com.google.common.base.MoreObjects;
import com.google.common.base.MoreObjects.ToStringHelper;
+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 org.eclipse.jdt.annotation.NonNull;
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);
action = null;
}
- private void applyAction() {
- checkState(!actionApplied);
- action.apply(ctx);
- actionApplied = true;
- }
-
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) {
checkState(action != null, "Action was not defined yet.");
if (removeSatisfied()) {
- applyAction();
+ if (!actionApplied) {
+ action.apply(ctx);
+ actionApplied = true;
+ }
return true;
}
return false;
}
@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,
}
@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) {
}
@Override
+ @Deprecated
public <E extends EffectiveStatement<?, ?>> Prerequisite<E> requiresEffective(
final StmtContext<?, ?, ? extends E> stmt) {
return requiresCtxImpl(stmt, EFFECTIVE_MODEL).transform(StmtContext::buildEffective);
}
@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,
}
@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) {
}
@Override
+ @Deprecated
public <N extends IdentifierNamespace<?, ?>> Prerequisite<Mutable<?, ?, ?>> mutatesNs(
final Mutable<?, ?, ?> context, final Class<N> namespace) {
return addMutation(new NamespaceMutation<>(contextImpl(context), namespace));
addReq(ret);
addMutation(ret);
- ret.hookOnto(context, namespace);
+ if (bootstraps == null) {
+ bootstraps = new ArrayList<>(1);
+ }
+ bootstraps.add(() -> ret.hookOnto(context, namespace));
return ret;
}
public void apply(final InferenceAction 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
public boolean phaseFinished(final StatementContextBase<?, ?, ?> context,
final ModelProcessingPhase finishedPhase) {
- return resolvePrereq((C) context);
+ return resolvePrereq((C) context) || tryApply();
}
}
@Override
public boolean phaseFinished(final StatementContextBase<?, ?, ?> context,
final ModelProcessingPhase finishedPhase) {
- return resolvePrereq((C) context);
+ return resolvePrereq((C) context) || tryApply();
}
@Override
}
}
+ /**
+ * 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 {
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()) {
- target.addMutation(modPhase, this);
- resolvePrereq((C) value);
+ // 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());