Merge "Bug 2983 - Throws ResultAlreadySet instead of IllegalStateException"
[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             return (NamespaceBehaviourWithListeners<K, V, N>) potential;
107         }
108         throw new NamespaceNotAvailableException("Namespace " + type + " is not available in phase " + currentPhase);
109     }
110
111     public StatementDefinitionContext<?, ?, ?> getStatementDefinition(QName name) {
112         StatementDefinitionContext<?, ?, ?> potential = definitions.get(name);
113         if(potential == null) {
114             StatementSupport<?, ?, ?> potentialRaw = supports.get(currentPhase).getStatementDefinition(name);
115             if(potentialRaw != null) {
116                 potential = new StatementDefinitionContext<>(potentialRaw);
117                 definitions.put(name, potential);
118             }
119         }
120         return potential;
121     }
122
123     public EffectiveModelContext build() throws SourceException, ReactorException {
124         for(ModelProcessingPhase phase : PHASE_EXECUTION_ORDER) {
125             startPhase(phase);
126             loadPhaseStatements();
127             completePhaseActions();
128             endPhase(phase);
129         }
130         return transform();
131     }
132
133     private EffectiveModelContext transform() {
134         Preconditions.checkState(finishedPhase == ModelProcessingPhase.EFFECTIVE_MODEL);
135         List<DeclaredStatement<?>> rootStatements = new ArrayList<>();
136         for(SourceSpecificContext source : sources) {
137             DeclaredStatement<?> root = source.getRoot().buildDeclared();
138             rootStatements.add(root);
139         }
140         return new EffectiveModelContext(rootStatements);
141     }
142
143     public EffectiveSchemaContext buildEffective() throws SourceException, ReactorException {
144         for(ModelProcessingPhase phase : PHASE_EXECUTION_ORDER) {
145             startPhase(phase);
146             loadPhaseStatements();
147             completePhaseActions();
148             endPhase(phase);
149         }
150         return transformEffective();
151     }
152
153     private EffectiveSchemaContext transformEffective() {
154         Preconditions.checkState(finishedPhase == ModelProcessingPhase.EFFECTIVE_MODEL);
155         List<DeclaredStatement<?>> rootStatements = new ArrayList<>();
156         List<EffectiveStatement<?,?>> rootEffectiveStatements = new ArrayList<>();
157
158         for(SourceSpecificContext source : sources) {
159             DeclaredStatement<?> root = source.getRoot().buildDeclared();
160             rootStatements.add(root);
161
162             EffectiveStatement<?,?> rootEffective = source.getRoot().buildEffective();
163             rootEffectiveStatements.add(rootEffective);
164         }
165
166         return new EffectiveSchemaContext(rootStatements,rootEffectiveStatements);
167     }
168
169     private void startPhase(ModelProcessingPhase phase) {
170         Preconditions.checkState(Objects.equals(finishedPhase, phase.getPreviousPhase()));
171         for(SourceSpecificContext source : sources) {
172             source.startPhase(phase);
173         }
174         currentPhase = phase;
175     }
176
177     private  void loadPhaseStatements() throws SourceException {
178         Preconditions.checkState(currentPhase != null);
179         for(SourceSpecificContext source : sources) {
180             source.loadStatements();
181         }
182     }
183
184     private  void completePhaseActions() throws ReactorException {
185         Preconditions.checkState(currentPhase != null);
186         List<SourceSpecificContext> sourcesToProgress = Lists.newArrayList(sources);
187         try {
188             boolean progressing = true;
189             while(progressing) {
190                 // We reset progressing to false.
191                 progressing = false;
192                 Iterator<SourceSpecificContext> currentSource = sourcesToProgress.iterator();
193                 while(currentSource.hasNext()) {
194                     PhaseCompletionProgress sourceProgress = currentSource.next().tryToCompletePhase(currentPhase);
195                     switch (sourceProgress) {
196                         case FINISHED:
197                             currentSource.remove();
198                             // Fallback to progress, since we were able to make progress in computation
199                         case PROGRESS:
200                             progressing = true;
201                             break;
202                         case NO_PROGRESS:
203                             // Noop
204                             break;
205                         default:
206                            throw new IllegalStateException("Unsupported phase progress " + sourceProgress);
207                     }
208                 }
209             }
210         } catch (SourceException e) {
211             throw Throwables.propagate(e);
212         }
213         if(!sourcesToProgress.isEmpty()) {
214             SomeModifiersUnresolvedException buildFailure = new SomeModifiersUnresolvedException(currentPhase);
215                 for(SourceSpecificContext failedSource : sourcesToProgress) {
216                     SourceException sourceEx = failedSource.failModifiers(currentPhase);
217                     buildFailure.addSuppressed(sourceEx);
218                 }
219                 throw buildFailure;
220         }
221     }
222
223     private  void endPhase(ModelProcessingPhase phase) {
224         Preconditions.checkState(currentPhase == phase);
225         finishedPhase = currentPhase;
226     }
227
228     public Set<SourceSpecificContext> getSources() {
229         return sources;
230     }
231
232 }