import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Verify.verify;
+import static com.google.common.base.Verify.verifyNotNull;
import static java.util.Objects.requireNonNull;
import com.google.common.collect.HashMultimap;
private static final Logger LOG = LoggerFactory.getLogger(SourceSpecificContext.class);
+ // TODO: consider keying by Byte equivalent of ExecutionOrder
private final Multimap<ModelProcessingPhase, ModifierImpl> modifiers = HashMultimap.create();
private final QNameToStatementDefinitionMap qnameToStmtDefMap = new QNameToStatementDefinitionMap();
private final PrefixToModuleMap prefixToModuleMap = new PrefixToModuleMap();
* - parent module, declared via 'belongs-to' statement
*/
private Collection<RootStatementContext<?, ?, ?>> importedNamespaces = ImmutableList.of();
+ private RootStatementContext<?, ?, ?> root;
+ // TODO: consider using ExecutionOrder byte for these two
private ModelProcessingPhase finishedPhase = ModelProcessingPhase.INIT;
private ModelProcessingPhase inProgressPhase;
- private RootStatementContext<?, ?, ?> root;
SourceSpecificContext(final BuildGlobalContext globalContext, final StatementStreamSource source) {
this.globalContext = requireNonNull(globalContext);
return globalContext;
}
- PhaseCompletionProgress tryToCompletePhase(final ModelProcessingPhase phase) {
+ PhaseCompletionProgress tryToCompletePhase(final byte executionOrder) {
+ final ModelProcessingPhase phase = verifyNotNull(ModelProcessingPhase.ofExecutionOrder(executionOrder));
final Collection<ModifierImpl> currentPhaseModifiers = modifiers.get(phase);
boolean hasProgressed = tryToProgress(currentPhaseModifiers);
final boolean phaseCompleted = requireNonNull(root, "Malformed source. Valid root element is missing.")
- .tryToCompletePhase(phase);
+ .tryToCompletePhase(executionOrder);
hasProgressed |= tryToProgress(currentPhaseModifiers);
+ // TODO: use executionOrder instead?
if (phaseCompleted && currentPhaseModifiers.isEmpty()) {
finishedPhase = phase;
LOG.debug("Source {} finished phase {}", source, phase);
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Verify.verify;
+import static com.google.common.base.Verify.verifyNotNull;
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.Beta;
import java.util.Optional;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
import org.opendaylight.yangtools.yang.parser.spi.meta.ImplicitParentAwareStatementSupport;
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.ModelProcessingPhase.ExecutionOrder;
import org.opendaylight.yangtools.yang.parser.spi.meta.MutableStatement;
import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceKeyCriterion;
// operations, hence we want to keep it close by.
private final @NonNull StatementDefinitionContext<A, D, E> definition;
+ // TODO: consider keying by Byte equivalent of ExecutionOrder
private Multimap<ModelProcessingPhase, OnPhaseFinished> phaseListeners = ImmutableMultimap.of();
private Multimap<ModelProcessingPhase, ContextMutation> phaseMutation = ImmutableMultimap.of();
private List<StmtContext<?, ?, ?>> effectOfStatement = ImmutableList.of();
- private @Nullable ModelProcessingPhase completedPhase;
+ /**
+ * {@link ModelProcessingPhase.ExecutionOrder} value of current {@link ModelProcessingPhase} of this statement.
+ */
+ private byte executionOrder;
// Copy constructor used by subclasses to implement reparent()
StatementContextBase(final StatementContextBase<A, D, E> original) {
super(original);
this.copyHistory = original.copyHistory;
this.definition = original.definition;
- this.completedPhase = original.completedPhase;
+ this.executionOrder = original.executionOrder;
}
StatementContextBase(final StatementDefinitionContext<A, D, E> def) {
}
@Override
- public final ModelProcessingPhase getCompletedPhase() {
- return completedPhase;
+ final byte executionOrder() {
+ return executionOrder;
}
// FIXME: this should be propagated through a correct constructor
@Deprecated
final void setCompletedPhase(final ModelProcessingPhase completedPhase) {
- this.completedPhase = completedPhase;
+ this.executionOrder = completedPhase.executionOrder();
}
@Override
final List<ReactorStmtCtx<?, ?, ?>> resized = beforeAddEffectiveStatement(effective, 1);
final ReactorStmtCtx<?, ?, ?> stmt = (ReactorStmtCtx<?, ?, ?>) substatement;
- final ModelProcessingPhase phase = completedPhase;
- if (phase != null) {
- ensureCompletedPhase(stmt, phase);
- }
+ ensureCompletedExecution(stmt);
resized.add(stmt);
return resized;
}
final List<ReactorStmtCtx<?, ?, ?>> resized = beforeAddEffectiveStatement(effective, statements.size());
final Collection<? extends ReactorStmtCtx<?, ?, ?>> casted =
(Collection<? extends ReactorStmtCtx<?, ?, ?>>) statements;
- final ModelProcessingPhase phase = completedPhase;
- if (phase != null) {
+ if (executionOrder != ExecutionOrder.NULL) {
for (ReactorStmtCtx<?, ?, ?> stmt : casted) {
- ensureCompletedPhase(stmt, phase);
+ ensureCompletedExecution(stmt, executionOrder);
}
}
// exposed for InferredStatementContext only
final void ensureCompletedPhase(final Mutable<?, ?, ?> stmt) {
verifyStatement(stmt);
- final ModelProcessingPhase phase = completedPhase;
- if (phase != null) {
- ensureCompletedPhase((ReactorStmtCtx<?, ?, ?>) stmt, phase);
+ ensureCompletedExecution((ReactorStmtCtx<?, ?, ?>) stmt);
+ }
+
+ // Make sure target statement has transitioned at least to our phase (if we have one). This method is just before we
+ // take allow a statement to become our substatement. This is needed to ensure that every statement tree does not
+ // contain any statements which did not complete the same phase as the root statement.
+ private void ensureCompletedExecution(final ReactorStmtCtx<?, ?, ?> stmt) {
+ if (executionOrder != ExecutionOrder.NULL) {
+ ensureCompletedExecution(stmt, executionOrder);
}
}
- // Make sure target statement has transitioned at least to specified phase. This method is just before we take
- // allow a statement to become our substatement. This is needed to ensure that every statement tree does not contain
- // any statements which did not complete the same phase as the root statement.
- private static void ensureCompletedPhase(final ReactorStmtCtx<?, ?, ?> stmt, final ModelProcessingPhase phase) {
- verify(stmt.tryToCompletePhase(phase), "Statement %s cannot complete phase %s", stmt, phase);
+ private static void ensureCompletedExecution(final ReactorStmtCtx<?, ?, ?> stmt, final byte executionOrder) {
+ verify(stmt.tryToCompletePhase(executionOrder), "Statement %s cannot complete phase %s", stmt, executionOrder);
}
private static void verifyStatement(final Mutable<?, ?, ?> stmt) {
private List<ReactorStmtCtx<?, ?, ?>> beforeAddEffectiveStatement(final List<ReactorStmtCtx<?, ?, ?>> effective,
final int toAdd) {
- // We cannot allow statement to be further mutated
- verify(completedPhase != ModelProcessingPhase.EFFECTIVE_MODEL, "Cannot modify finished statement at %s",
+ // We cannot allow statement to be further mutated.
+ // TODO: we really want to say 'not NULL and not at or after EFFECTIVE_MODEL here. This will matter if we have
+ // a phase after EFFECTIVE_MODEL
+ verify(executionOrder != ExecutionOrder.EFFECTIVE_MODEL, "Cannot modify finished statement at %s",
sourceReference());
return beforeAddEffectiveStatementUnsafe(effective, toAdd);
}
abstract Stream<? extends @NonNull StmtContext<?, ?, ?>> streamEffective();
@Override
- final boolean doTryToCompletePhase(final ModelProcessingPhase phase) {
- final boolean finished = phaseMutation.isEmpty() ? true : runMutations(phase);
- if (completeChildren(phase) && finished) {
- onPhaseCompleted(phase);
+ final boolean doTryToCompletePhase(final byte targetOrder) {
+ final boolean finished = phaseMutation.isEmpty() ? true : runMutations(targetOrder);
+ if (completeChildren(targetOrder) && finished) {
+ onPhaseCompleted(targetOrder);
return true;
}
return false;
}
- private boolean completeChildren(final ModelProcessingPhase phase) {
+ private boolean completeChildren(final byte targetOrder) {
boolean finished = true;
for (final StatementContextBase<?, ?, ?> child : mutableDeclaredSubstatements()) {
- finished &= child.tryToCompletePhase(phase);
+ finished &= child.tryToCompletePhase(targetOrder);
}
for (final ReactorStmtCtx<?, ?, ?> child : effectiveChildrenToComplete()) {
- finished &= child.tryToCompletePhase(phase);
+ finished &= child.tryToCompletePhase(targetOrder);
}
return finished;
}
- private boolean runMutations(final ModelProcessingPhase phase) {
+ private boolean runMutations(final byte targetOrder) {
+ final ModelProcessingPhase phase = verifyNotNull(ModelProcessingPhase.ofExecutionOrder(targetOrder));
final Collection<ContextMutation> openMutations = phaseMutation.get(phase);
return openMutations.isEmpty() ? true : runMutations(phase, openMutations);
}
}
/**
- * Occurs on end of {@link ModelProcessingPhase} of source parsing.
+ * Occurs on end of {@link ModelProcessingPhase} of source parsing. This method must not be called with
+ * {@code executionOrder} equal to {@link ExecutionOrder#NULL}.
*
- * @param phase
- * that was to be completed (finished)
- * @throws SourceException
- * when an error occurred in source parsing
+ * @param phase that was to be completed (finished)
+ * @throws SourceException when an error occurred in source parsing
*/
- private void onPhaseCompleted(final ModelProcessingPhase phase) {
- completedPhase = phase;
- if (phase == ModelProcessingPhase.EFFECTIVE_MODEL) {
+ private void onPhaseCompleted(final byte completedOrder) {
+ executionOrder = completedOrder;
+ if (completedOrder == ExecutionOrder.EFFECTIVE_MODEL) {
+ // We have completed effective model, substatements are guaranteed not to change
summarizeSubstatementPolicy();
}
+ final ModelProcessingPhase phase = verifyNotNull(ModelProcessingPhase.ofExecutionOrder(completedOrder));
final Collection<OnPhaseFinished> listeners = phaseListeners.get(phase);
if (!listeners.isEmpty()) {
runPhaseListeners(phase, listeners);
requireNonNull(phase, "Statement context processing phase cannot be null");
requireNonNull(listener, "Statement context phase listener cannot be null");
- ModelProcessingPhase finishedPhase = completedPhase;
+ ModelProcessingPhase finishedPhase = ModelProcessingPhase.ofExecutionOrder(executionOrder);
while (finishedPhase != null) {
if (phase.equals(finishedPhase)) {
listener.phaseFinished(this, finishedPhase);
* @throws IllegalStateException when the mutation was registered after phase was completed
*/
final void addMutation(final ModelProcessingPhase phase, final ContextMutation mutation) {
- ModelProcessingPhase finishedPhase = completedPhase;
- while (finishedPhase != null) {
- checkState(!phase.equals(finishedPhase), "Mutation registered after phase was completed at: %s",
- sourceReference());
- finishedPhase = finishedPhase.getPreviousPhase();
- }
+ checkState(executionOrder < phase.executionOrder(), "Mutation registered after phase was completed at: %s",
+ sourceReference());
if (phaseMutation.isEmpty()) {
phaseMutation = newMultimap();
*/
package org.opendaylight.yangtools.yang.parser.spi.meta;
+import static com.google.common.base.Verify.verify;
import static java.util.Objects.requireNonNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
+// FIXME: YANGTOOLS-1150: this should go into yang-reactor-api
@NonNullByDefault
public enum ModelProcessingPhase {
INIT(),
/**
* Preliminary cross-source relationship resolution phase which collects available module names and module
* namespaces. It is necessary in order to correct resolution of unknown statements used in linkage phase (e.g.
- * semantic version of yang modules).
+ * semantic version of YANG modules).
*/
- SOURCE_PRE_LINKAGE(INIT),
+ SOURCE_PRE_LINKAGE(INIT, ExecutionOrder.SOURCE_PRE_LINKAGE),
/**
* Cross-source relationship resolution phase.
* At end of this phase all source related contexts should be bind to their imports and includes to allow
* visibility of custom defined statements in subsequent phases.
*/
- SOURCE_LINKAGE(SOURCE_PRE_LINKAGE),
- STATEMENT_DEFINITION(SOURCE_LINKAGE),
- FULL_DECLARATION(STATEMENT_DEFINITION),
- EFFECTIVE_MODEL(FULL_DECLARATION);
+ SOURCE_LINKAGE(SOURCE_PRE_LINKAGE, ExecutionOrder.SOURCE_LINKAGE),
+ STATEMENT_DEFINITION(SOURCE_LINKAGE, ExecutionOrder.STATEMENT_DEFINITION),
+ FULL_DECLARATION(STATEMENT_DEFINITION, ExecutionOrder.FULL_DECLARATION),
+ EFFECTIVE_MODEL(FULL_DECLARATION, ExecutionOrder.EFFECTIVE_MODEL);
+
+ /**
+ * The concept of phase execution order, expressed as non-negative values.
+ */
+ public static final class ExecutionOrder {
+ /**
+ * Equivalent of a {@code null} {@link ModelProcessingPhase}.
+ */
+ public static final byte NULL = 0;
+ /**
+ * Corresponds to {@link ModelProcessingPhase#INIT}.
+ */
+ public static final byte INIT = 1;
+ /**
+ * Corresponds to {@link ModelProcessingPhase#SOURCE_PRE_LINKAGE}.
+ */
+ public static final byte SOURCE_PRE_LINKAGE = 2;
+ /**
+ * Corresponds to {@link ModelProcessingPhase#SOURCE_LINKAGE}.
+ */
+ public static final byte SOURCE_LINKAGE = 3;
+ /**
+ * Corresponds to {@link ModelProcessingPhase#STATEMENT_DEFINITION}.
+ */
+ public static final byte STATEMENT_DEFINITION = 4;
+ /**
+ * Corresponds to {@link ModelProcessingPhase#FULL_DECLARATION}.
+ */
+ public static final byte FULL_DECLARATION = 5;
+ /**
+ * Corresponds to {@link ModelProcessingPhase#EFFECTIVE_MODEL}.
+ */
+ public static final byte EFFECTIVE_MODEL = 6;
+
+ private ExecutionOrder() {
+ // Hidden on purpose
+ }
+ }
+
+ /**
+ * Members of this enum at their {@link #executionOrder} offset, with {@code 0} being reserved as {@code null}.
+ */
+ private static final ModelProcessingPhase[] BY_EXECUTION_ORDER;
+
+ // BY_EXECUTION_ORDER initialization. The array has a semantic tie-in on ExectionOrder values, which has to follow
+ // its rules. Since we are one-time indexing, let's make a thorough job of it and verify that everything is declared
+ // as it should be.
+ static {
+ final ModelProcessingPhase[] values = values();
+ final ModelProcessingPhase[] tmp = new ModelProcessingPhase[values.length + 1];
+
+ for (ModelProcessingPhase phase : values) {
+ final byte offset = phase.executionOrder;
+ verify(offset > 0, "Invalid execution order in %s", phase);
+
+ final ModelProcessingPhase existing = tmp[offset];
+ verify(existing == null, "Execution order %s clash with %s", offset, existing);
+ verify(tmp[offset - 1] == phase.previousPhase, "Illegal previous phase of %s", phase);
+ tmp[offset] = phase;
+ }
+
+ BY_EXECUTION_ORDER = tmp;
+ }
private final @Nullable ModelProcessingPhase previousPhase;
+ private final byte executionOrder;
@SuppressFBWarnings(value = "NP_STORE_INTO_NONNULL_FIELD",
justification = "https://github.com/spotbugs/spotbugs/issues/743")
+ // For INIT only
ModelProcessingPhase() {
previousPhase = null;
+ executionOrder = ExecutionOrder.INIT;
}
- ModelProcessingPhase(final ModelProcessingPhase previousPhase) {
+ ModelProcessingPhase(final ModelProcessingPhase previousPhase, final int executionOrder) {
this.previousPhase = requireNonNull(previousPhase);
+ this.executionOrder = (byte) executionOrder;
}
/**
public boolean isCompletedBy(final @Nullable ModelProcessingPhase other) {
return other != null && ordinal() <= other.ordinal();
}
+
+ /**
+ * Return the execution order, which is a value in range {@code 1..127}.
+ *
+ * @return Execution order
+ */
+ public byte executionOrder() {
+ return executionOrder;
+ }
+
+ /**
+ * Return the {@link ModelProcessingPhase} corresponding to a {@link ExecutionOrder} value.
+ *
+ * @param executionOrder Execution order
+ * @return Corresponding value, or null for {@link ExecutionOrder#NULL}
+ * @throws IllegalArgumentException if the execution order is invalid
+ */
+ public static @Nullable ModelProcessingPhase ofExecutionOrder(final byte executionOrder) {
+ try {
+ return BY_EXECUTION_ORDER[executionOrder];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
}