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