import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNull;
@Override
public StatementSupport<?, ?, ?> getFrom(final NamespaceStorageNode storage, final QName key) {
- return statementDefinitions.get(key);
+ return statementDefinitions.getSupport(key);
}
@Override
public Map<QName, StatementSupport<?, ?, ?>> getAllFrom(final NamespaceStorageNode storage) {
- throw new UnsupportedOperationException("StatementSupportNamespace is immutable");
+ throw uoe();
}
@Override
public void addTo(final NamespaceStorageNode storage, final QName key, final StatementSupport<?, ?, ?> value) {
- throw new UnsupportedOperationException("StatementSupportNamespace is immutable");
+ throw uoe();
+ }
+
+ private static UnsupportedOperationException uoe() {
+ return new UnsupportedOperationException("StatementSupportNamespace is immutable");
}
}
private ModelProcessingPhase finishedPhase = ModelProcessingPhase.INIT;
private ModelProcessingPhase inProgressPhase;
+ // If not null, do not add anything to modifiers, but record it here.
+ private List<Entry<ModelProcessingPhase, ModifierImpl>> delayedModifiers;
+
SourceSpecificContext(final BuildGlobalContext globalContext, final StatementStreamSource source) {
this.globalContext = requireNonNull(globalContext);
this.source = requireNonNull(source);
if (def == null) {
def = globalContext.getModelDefinedStatementDefinition(name);
if (def == null) {
- final StatementSupport<?, ?, ?> extension = qnameToStmtDefMap.get(name);
+ final StatementSupport<?, ?, ?> extension = qnameToStmtDefMap.getSupport(name);
if (extension != null) {
def = new StatementDefinitionContext<>(extension);
globalContext.putModelDefinedStatementDefinition(name, def);
return hasProgressed ? PhaseCompletionProgress.PROGRESS : PhaseCompletionProgress.NO_PROGRESS;
}
- private static boolean tryToProgress(final Collection<ModifierImpl> currentPhaseModifiers) {
+ private boolean tryToProgress(final Collection<ModifierImpl> currentPhaseModifiers) {
boolean hasProgressed = false;
+ // We are about to iterate over the modifiers and invoke callbacks. Those callbacks can end up circling back
+ // and modifying the same collection. This asserts that modifiers should not be modified.
+ delayedModifiers = List.of();
+
// Try making forward progress ...
final Iterator<ModifierImpl> modifier = currentPhaseModifiers.iterator();
while (modifier.hasNext()) {
}
}
+ // We have finished iterating, if we have any delayed modifiers, put them back. This may seem as if we want
+ // to retry the loop, but we do not have to, as we will be circling back anyway.
+ //
+ // The thing is, we are inherently single-threaded and therefore if we observe non-empty delayedModifiers, the
+ // only way that could happen is through a callback, which in turn means we have made progress.
+ if (!delayedModifiers.isEmpty()) {
+ verify(hasProgressed, "Delayed modifiers encountered without making progress in %s", this);
+ for (Entry<ModelProcessingPhase, ModifierImpl> entry : delayedModifiers) {
+ modifiers.put(entry.getKey(), entry.getValue());
+ }
+ }
+ delayedModifiers = null;
+
return hasProgressed;
}
@NonNull ModelActionBuilder newInferenceAction(final @NonNull ModelProcessingPhase phase) {
final ModifierImpl action = new ModifierImpl();
- modifiers.put(phase, action);
+
+ if (delayedModifiers != null) {
+ if (delayedModifiers.isEmpty()) {
+ delayedModifiers = new ArrayList<>(2);
+ }
+ delayedModifiers.add(Map.entry(phase,action));
+ } else {
+ modifiers.put(phase, action);
+ }
+
return action;
}
}
}
- if (exceptions.isEmpty()) {
- return Optional.empty();
+ switch (exceptions.size()) {
+ case 0:
+ return Optional.empty();
+ case 1:
+ return Optional.of(exceptions.get(0));
+ default:
+ final String message = String.format("Yang model processing phase %s failed", identifier);
+ final InferenceException ex = new InferenceException(message, root, exceptions.get(0));
+ exceptions.listIterator(1).forEachRemaining(ex::addSuppressed);
+ return Optional.of(ex);
}
-
- final String message = String.format("Yang model processing phase %s failed", identifier);
- final InferenceException e = new InferenceException(message, root, exceptions.get(0));
- exceptions.listIterator(1).forEachRemaining(e::addSuppressed);
-
- return Optional.of(e);
}
void loadStatements() {