Bug 2366 - new parser API - implementation of declared statements
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / BuildGlobalContext.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.stmt.reactor;
9
10 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.EffectiveSchemaContext;
11
12 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
13 import com.google.common.base.Preconditions;
14 import com.google.common.base.Throwables;
15 import com.google.common.collect.ImmutableList;
16 import com.google.common.collect.Lists;
17 import java.util.ArrayList;
18 import java.util.HashMap;
19 import java.util.HashSet;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Objects;
24 import java.util.Set;
25 import javax.annotation.Nonnull;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
28 import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
29 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
30 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
31 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
32 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.StorageNodeType;
33 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceNotAvailableException;
34 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
35 import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
36 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
37 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupportBundle;
38 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
39 import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
40 import org.opendaylight.yangtools.yang.parser.stmt.reactor.SourceSpecificContext.PhaseCompletionProgress;
41
42 class BuildGlobalContext extends NamespaceStorageSupport implements NamespaceBehaviour.Registry {
43
44     private static final List<ModelProcessingPhase> PHASE_EXECUTION_ORDER = ImmutableList.<ModelProcessingPhase>builder()
45             .add(ModelProcessingPhase.SOURCE_LINKAGE)
46             .add(ModelProcessingPhase.STATEMENT_DEFINITION)
47             .add(ModelProcessingPhase.FULL_DECLARATION)
48             .add(ModelProcessingPhase.EFFECTIVE_MODEL)
49             .build();
50
51     private final Map<QName,StatementDefinitionContext<?,?,?>> definitions = new HashMap<>();
52     private final Map<Class<?>,NamespaceBehaviourWithListeners<?, ?, ?>> namespaces = new HashMap<>();
53
54
55     private final Map<ModelProcessingPhase,StatementSupportBundle> supports;
56     private final Set<SourceSpecificContext> sources = new HashSet<>();
57
58     private ModelProcessingPhase currentPhase;
59     private ModelProcessingPhase finishedPhase;
60
61     public BuildGlobalContext(Map<ModelProcessingPhase, StatementSupportBundle> supports) {
62         super();
63         this.supports = supports;
64     }
65
66     public StatementSupportBundle getSupportsForPhase(ModelProcessingPhase currentPhase) {
67         return supports.get(currentPhase);
68     }
69
70     public void addSource(@Nonnull StatementStreamSource source) {
71         sources.add(new SourceSpecificContext(this,source));
72     }
73
74     @Override
75     public StorageNodeType getStorageNodeType() {
76         return StorageNodeType.GLOBAL;
77     }
78
79     @Override
80     public NamespaceStorageNode getParentNamespaceStorage() {
81         return null;
82     }
83
84     @Override
85     public NamespaceBehaviour.Registry getBehaviourRegistry() {
86         return this;
87     }
88
89     @Override
90     public <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviourWithListeners<K, V, N> getNamespaceBehaviour(Class<N> type) {
91         NamespaceBehaviourWithListeners<?, ?, ?> potential = namespaces.get(type);
92         if (potential == null) {
93             NamespaceBehaviour<K, V, N> potentialRaw = supports.get(currentPhase).getNamespaceBehaviour(type);
94             if(potentialRaw != null) {
95                 potential = new NamespaceBehaviourWithListeners<>(potentialRaw);
96                 namespaces.put(type, potential);
97             }
98         }
99         if (potential != null) {
100             Preconditions.checkState(type.equals(potential.getIdentifier()));
101
102             /*
103              * Safe cast, previous checkState checks equivalence of key from
104              * which type argument are derived
105              */
106             @SuppressWarnings("unchecked")
107             NamespaceBehaviourWithListeners<K, V, N> casted = (NamespaceBehaviourWithListeners<K, V, N>) potential;
108             return casted;
109         }
110         throw new NamespaceNotAvailableException("Namespace " + type + "is not available in phase " + currentPhase);
111     }
112
113     public StatementDefinitionContext<?, ?, ?> getStatementDefinition(QName name) {
114         StatementDefinitionContext<?, ?, ?> potential = definitions.get(name);
115         if(potential == null) {
116             StatementSupport<?, ?, ?> potentialRaw = supports.get(currentPhase).getStatementDefinition(name);
117             if(potentialRaw != null) {
118                 potential = new StatementDefinitionContext<>(potentialRaw);
119                 definitions.put(name, potential);
120             }
121         }
122         return potential;
123     }
124
125     public EffectiveModelContext build() throws SourceException, ReactorException {
126         for(ModelProcessingPhase phase : PHASE_EXECUTION_ORDER) {
127             startPhase(phase);
128             loadPhaseStatements();
129             completePhaseActions();
130             endPhase(phase);
131         }
132         return transform();
133     }
134
135     private EffectiveModelContext transform() {
136         Preconditions.checkState(finishedPhase == ModelProcessingPhase.EFFECTIVE_MODEL);
137         List<DeclaredStatement<?>> rootStatements = new ArrayList<>();
138         for(SourceSpecificContext source : sources) {
139             DeclaredStatement<?> root = source.getRoot().buildDeclared();
140             rootStatements.add(root);
141         }
142         return new EffectiveModelContext(rootStatements);
143     }
144
145     public EffectiveSchemaContext buildEffective() throws SourceException, ReactorException {
146         for(ModelProcessingPhase phase : PHASE_EXECUTION_ORDER) {
147             startPhase(phase);
148             loadPhaseStatements();
149             completePhaseActions();
150             endPhase(phase);
151         }
152         return transformEffective();
153     }
154
155     private EffectiveSchemaContext transformEffective() {
156         Preconditions.checkState(finishedPhase == ModelProcessingPhase.EFFECTIVE_MODEL);
157         List<DeclaredStatement<?>> rootStatements = new ArrayList<>();
158         List<EffectiveStatement<?,?>> rootEffectiveStatements = new ArrayList<>();
159
160         for(SourceSpecificContext source : sources) {
161             DeclaredStatement<?> root = source.getRoot().buildDeclared();
162             rootStatements.add(root);
163
164             EffectiveStatement<?,?> rootEffective = source.getRoot().buildEffective();
165             rootEffectiveStatements.add(rootEffective);
166         }
167
168         return new EffectiveSchemaContext(rootStatements,rootEffectiveStatements);
169     }
170
171     private void startPhase(ModelProcessingPhase phase) {
172         Preconditions.checkState(Objects.equals(finishedPhase, phase.getPreviousPhase()));
173         for(SourceSpecificContext source : sources) {
174             source.startPhase(phase);
175         }
176         currentPhase = phase;
177     }
178
179     private  void loadPhaseStatements() throws SourceException {
180         Preconditions.checkState(currentPhase != null);
181         for(SourceSpecificContext source : sources) {
182             source.loadStatements();
183         }
184     }
185
186     private  void completePhaseActions() throws ReactorException {
187         Preconditions.checkState(currentPhase != null);
188         ArrayList<SourceSpecificContext> sourcesToProgress = Lists.newArrayList(sources);
189         try {
190             boolean progressing = true;
191             while(progressing) {
192                 // We reset progressing to false.
193                 progressing = false;
194                 Iterator<SourceSpecificContext> currentSource = sourcesToProgress.iterator();
195                 while(currentSource.hasNext()) {
196                     PhaseCompletionProgress sourceProgress = currentSource.next().tryToCompletePhase(currentPhase);
197                     switch (sourceProgress) {
198                         case FINISHED:
199                             currentSource.remove();
200                         case PROGRESS:
201                             progressing = true;
202                         case NO_PROGRESS:
203                             // Noop;
204                     }
205                 }
206             }
207         } catch (SourceException e) {
208             throw Throwables.propagate(e);
209         }
210         if(!sourcesToProgress.isEmpty()) {
211             SomeModifiersUnresolvedException buildFailure = new SomeModifiersUnresolvedException(currentPhase);
212                 for(SourceSpecificContext failedSource : sourcesToProgress) {
213                     SourceException sourceEx = failedSource.failModifiers(currentPhase);
214                     buildFailure.addSuppressed(sourceEx);
215                 }
216                 throw buildFailure;
217         }
218     }
219
220     private  void endPhase(ModelProcessingPhase phase) {
221         Preconditions.checkState(currentPhase == phase);
222         finishedPhase = currentPhase;
223     }
224
225     public Set<SourceSpecificContext> getSources() {
226         return sources;
227     }
228
229 }