Define ExecutionOrder
[yangtools.git] / yang / yang-parser-spi / src / main / java / org / opendaylight / yangtools / yang / parser / spi / meta / ModelProcessingPhase.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.parser.spi.meta;
9
10 import static com.google.common.base.Verify.verify;
11 import static java.util.Objects.requireNonNull;
12
13 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
14 import org.eclipse.jdt.annotation.NonNullByDefault;
15 import org.eclipse.jdt.annotation.Nullable;
16
17 // FIXME: YANGTOOLS-1150: this should go into yang-reactor-api
18 @NonNullByDefault
19 public enum ModelProcessingPhase {
20     INIT(),
21
22     /**
23      * Preliminary cross-source relationship resolution phase which collects available module names and module
24      * namespaces. It is necessary in order to correct resolution of unknown statements used in linkage phase (e.g.
25      * semantic version of YANG modules).
26      */
27     SOURCE_PRE_LINKAGE(INIT, ExecutionOrder.SOURCE_PRE_LINKAGE),
28
29     /**
30      * Cross-source relationship resolution phase.
31      *
32      * <p>
33      * In this phase of processing only statements which affects cross-source relationship (e.g. imports / includes)
34      * are processed.
35      *
36      * <p>
37      * At end of this phase all source related contexts should be bind to their imports and includes to allow
38      * visibility of custom defined statements in subsequent phases.
39      */
40     SOURCE_LINKAGE(SOURCE_PRE_LINKAGE, ExecutionOrder.SOURCE_LINKAGE),
41     STATEMENT_DEFINITION(SOURCE_LINKAGE, ExecutionOrder.STATEMENT_DEFINITION),
42     FULL_DECLARATION(STATEMENT_DEFINITION, ExecutionOrder.FULL_DECLARATION),
43     EFFECTIVE_MODEL(FULL_DECLARATION, ExecutionOrder.EFFECTIVE_MODEL);
44
45     /**
46      * The concept of phase execution order, expressed as non-negative values.
47      */
48     public static final class ExecutionOrder {
49         /**
50          * Equivalent of a {@code null} {@link ModelProcessingPhase}.
51          */
52         public static final byte NULL                 = 0;
53         /**
54          * Corresponds to {@link ModelProcessingPhase#INIT}.
55          */
56         public static final byte INIT                 = 1;
57         /**
58          * Corresponds to {@link ModelProcessingPhase#SOURCE_PRE_LINKAGE}.
59          */
60         public static final byte SOURCE_PRE_LINKAGE   = 2;
61         /**
62          * Corresponds to {@link ModelProcessingPhase#SOURCE_LINKAGE}.
63          */
64         public static final byte SOURCE_LINKAGE       = 3;
65         /**
66          * Corresponds to {@link ModelProcessingPhase#STATEMENT_DEFINITION}.
67          */
68         public static final byte STATEMENT_DEFINITION = 4;
69         /**
70          * Corresponds to {@link ModelProcessingPhase#FULL_DECLARATION}.
71          */
72         public static final byte FULL_DECLARATION     = 5;
73         /**
74          * Corresponds to {@link ModelProcessingPhase#EFFECTIVE_MODEL}.
75          */
76         public static final byte EFFECTIVE_MODEL      = 6;
77
78         private ExecutionOrder() {
79             // Hidden on purpose
80         }
81     }
82
83     /**
84      * Members of this enum at their {@link #executionOrder} offset, with {@code 0} being reserved as {@code null}.
85      */
86     private static final ModelProcessingPhase[] BY_EXECUTION_ORDER;
87
88     // BY_EXECUTION_ORDER initialization. The array has a semantic tie-in on ExectionOrder values, which has to follow
89     // its rules. Since we are one-time indexing, let's make a thorough job of it and verify that everything is declared
90     // as it should be.
91     static {
92         final ModelProcessingPhase[] values = values();
93         final ModelProcessingPhase[] tmp = new ModelProcessingPhase[values.length + 1];
94
95         for (ModelProcessingPhase phase : values) {
96             final byte offset = phase.executionOrder;
97             verify(offset > 0, "Invalid execution order in %s", phase);
98
99             final ModelProcessingPhase existing = tmp[offset];
100             verify(existing == null, "Execution order %s clash with %s", offset, existing);
101             verify(tmp[offset - 1] == phase.previousPhase, "Illegal previous phase of %s", phase);
102             tmp[offset] = phase;
103         }
104
105         BY_EXECUTION_ORDER = tmp;
106     }
107
108     private final @Nullable ModelProcessingPhase previousPhase;
109     private final byte executionOrder;
110
111     @SuppressFBWarnings(value = "NP_STORE_INTO_NONNULL_FIELD",
112         justification = "https://github.com/spotbugs/spotbugs/issues/743")
113     // For INIT only
114     ModelProcessingPhase() {
115         previousPhase = null;
116         executionOrder = ExecutionOrder.INIT;
117     }
118
119     ModelProcessingPhase(final ModelProcessingPhase previousPhase, final int executionOrder) {
120         this.previousPhase = requireNonNull(previousPhase);
121         this.executionOrder = (byte) executionOrder;
122     }
123
124     /**
125      * Return the preceding phase, or null if this phase is the first one.
126      *
127      * @return Preceding phase, if there is one
128      */
129     public @Nullable ModelProcessingPhase getPreviousPhase() {
130         return previousPhase;
131     }
132
133     /**
134      * Determine whether this processing phase is implied to have completed by completion of some other phase.
135      * Algebraically this means that other is not null and is either this phase or its {@link #getPreviousPhase()} chain
136      * contains this phase.
137      *
138      * @param other Other phase
139      * @return True if this phase completes no later than specified phase.
140      */
141     public boolean isCompletedBy(final @Nullable ModelProcessingPhase other) {
142         return other != null && ordinal() <= other.ordinal();
143     }
144
145     /**
146      * Return the execution order, which is a value in range {@code 1..127}.
147      *
148      * @return Execution order
149      */
150     public byte executionOrder() {
151         return executionOrder;
152     }
153
154     /**
155      * Return the {@link ModelProcessingPhase} corresponding to a {@link ExecutionOrder} value.
156      *
157      * @param executionOrder Execution order
158      * @return Corresponding value, or null for {@link ExecutionOrder#NULL}
159      * @throws IllegalArgumentException if the execution order is invalid
160      */
161     public static @Nullable ModelProcessingPhase ofExecutionOrder(final byte executionOrder) {
162         try {
163             return BY_EXECUTION_ORDER[executionOrder];
164         } catch (ArrayIndexOutOfBoundsException e) {
165             throw new IllegalArgumentException(e);
166         }
167     }
168 }