Define ExecutionOrder
[yangtools.git] / yang / yang-parser-spi / src / main / java / org / opendaylight / yangtools / yang / parser / spi / meta / ModelProcessingPhase.java
index 1f7922ab708ca0145497869b6d05a3e555fafa5d..8da5502607f96969216d2815bddc14a9e9c13ddb 100644 (file)
@@ -7,18 +7,24 @@
  */
 package org.opendaylight.yangtools.yang.parser.spi.meta;
 
-import javax.annotation.Nullable;
+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(null),
+    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).
+     * 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).
      */
-    SOURCE_PRE_LINKAGE(INIT),
+    SOURCE_PRE_LINKAGE(INIT, ExecutionOrder.SOURCE_PRE_LINKAGE),
 
     /**
      * Cross-source relationship resolution phase.
@@ -31,18 +37,132 @@ public enum ModelProcessingPhase {
      * 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);
 
-    private final ModelProcessingPhase previousPhase;
+    /**
+     * 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;
 
-    ModelProcessingPhase(@Nullable ModelProcessingPhase previous) {
-        this.previousPhase = previous;
+        private ExecutionOrder() {
+            // Hidden on purpose
+        }
     }
 
-    public ModelProcessingPhase getPreviousPhase() {
+    /**
+     * 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, final int executionOrder) {
+        this.previousPhase = requireNonNull(previousPhase);
+        this.executionOrder = (byte) executionOrder;
+    }
+
+    /**
+     * Return the preceding phase, or null if this phase is the first one.
+     *
+     * @return Preceding phase, if there is one
+     */
+    public @Nullable ModelProcessingPhase getPreviousPhase() {
         return previousPhase;
     }
+
+    /**
+     * Determine whether this processing phase is implied to have completed by completion of some other phase.
+     * Algebraically this means that other is not null and is either this phase or its {@link #getPreviousPhase()} chain
+     * contains this phase.
+     *
+     * @param other Other phase
+     * @return True if this phase completes no later than specified phase.
+     */
+    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);
+        }
+    }
 }