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