7adb0d6f1d8a8068a3dae535fcb9f7a4f9d6e9af
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / SourceSpecificContext.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.collect.HashMultimap;
12 import com.google.common.collect.ImmutableMap;
13 import com.google.common.collect.Multimap;
14 import java.net.URI;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import java.util.Objects;
22 import javax.annotation.Nullable;
23 import org.opendaylight.yangtools.concepts.Mutable;
24 import org.opendaylight.yangtools.yang.common.QName;
25 import org.opendaylight.yangtools.yang.common.QNameModule;
26 import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
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.model.api.meta.StatementDefinition;
31 import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionStatement;
32 import org.opendaylight.yangtools.yang.parser.spi.ExtensionNamespace;
33 import org.opendaylight.yangtools.yang.parser.spi.meta.ImportedNamespaceContext;
34 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
35 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
36 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
37 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
38 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
39 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.StorageNodeType;
40 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementDefinitionNamespace;
41 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
42 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
43 import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToPrefixToModuleIdentifier;
44 import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToModuleIdentifier;
45 import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToNamespace;
46 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleIdentifierToModuleQName;
47 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
48 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModuleMap;
49 import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
50 import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinitionMap;
51 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
52 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
53 import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
54 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.BitsSpecificationImpl;
55 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Decimal64SpecificationImpl;
56 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.EnumSpecificationImpl;
57 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.IdentityRefSpecificationImpl;
58 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.InstanceIdentifierSpecificationImpl;
59 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.LeafrefSpecificationImpl;
60 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.ModelDefinedStatementDefinition;
61 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.TypeUtils;
62 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.UnionSpecificationImpl;
63 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.UnknownStatementImpl;
64 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils;
65
66 public class SourceSpecificContext implements NamespaceStorageNode, NamespaceBehaviour.Registry, Mutable {
67
68     public enum PhaseCompletionProgress {
69         NO_PROGRESS,
70         PROGRESS,
71         FINISHED
72     }
73
74     private final StatementStreamSource source;
75     private final BuildGlobalContext currentContext;
76     private final Collection<NamespaceStorageNode> importedNamespaces = new ArrayList<>();
77     private final Multimap<ModelProcessingPhase, ModifierImpl> modifiers = HashMultimap.create();
78
79     private RootStatementContext<?, ?, ?> root;
80
81     private ModelProcessingPhase inProgressPhase;
82     private ModelProcessingPhase finishedPhase = ModelProcessingPhase.INIT;
83     private final QNameToStatementDefinitionMap qNameToStmtDefMap = new QNameToStatementDefinitionMap();
84     private final PrefixToModuleMap prefixToModuleMap = new PrefixToModuleMap();
85
86
87     SourceSpecificContext(final BuildGlobalContext currentContext, final StatementStreamSource source) {
88         this.source = source;
89         this.currentContext = currentContext;
90     }
91
92     public boolean isEnabledSemanticVersioning(){
93         return currentContext.isEnabledSemanticVersioning();
94     }
95
96     ModelProcessingPhase getInProgressPhase() {
97         return inProgressPhase;
98     }
99
100     StatementDefinitionContext<?, ?, ?> getDefinition(final QName name) {
101         return currentContext.getStatementDefinition(name);
102     }
103
104     ContextBuilder<?, ?, ?> createDeclaredChild(final StatementContextBase<?, ?, ?> current, final QName name,
105                                                 final StatementSourceReference ref) {
106         StatementDefinitionContext<?, ?, ?> def = getDefinition(name);
107
108         if (def == null) {
109             // unknown-stmts (from import, include or local-scope)
110             if (qNameToStmtDefMap.get(name) != null) {
111                 final StatementDefinition extension = currentContext.getFromNamespace(
112                     StatementDefinitionNamespace.class, name);
113                 SourceException.throwIfNull(extension, current.getStatementSourceReference(), "Extension %s not found",
114                     name);
115
116                 def = new StatementDefinitionContext<>(new UnknownStatementImpl.Definition(extension));
117             } else {
118                 // type-body-stmts
119                 def = resolveTypeBodyStmts(name.getLocalName());
120             }
121         } else if (current != null && current.definition().getRepresentingClass().equals(UnknownStatementImpl.class)) {
122             // FIXME: What's going on here?
123             final QName qName = Utils.qNameFromArgument(current, name.getLocalName());
124
125             def = new StatementDefinitionContext<>(new UnknownStatementImpl.Definition(
126                 new ModelDefinedStatementDefinition(qName)));
127         }
128
129         Preconditions.checkArgument(def != null, "Statement %s does not have type mapping defined.", name);
130         if (current == null) {
131             return createDeclaredRoot(def, ref);
132         }
133         return current.substatementBuilder(def, ref);
134     }
135
136     @SuppressWarnings({"rawtypes", "unchecked"})
137     private ContextBuilder<?, ?, ?> createDeclaredRoot(final StatementDefinitionContext<?, ?, ?> def,
138                                                        final StatementSourceReference ref) {
139         return new ContextBuilder(def, ref) {
140
141             @Override
142             public StatementContextBase build() {
143                 if (root == null) {
144                     root = new RootStatementContext(this, SourceSpecificContext.this);
145                 } else {
146                     Preconditions.checkState(root.getIdentifier().equals(createIdentifier()),
147                             "Root statement was already defined as %s.", root.getIdentifier());
148                 }
149                 root.resetLists();
150                 return root;
151             }
152
153         };
154     }
155
156     RootStatementContext<?, ?, ?> getRoot() {
157         return root;
158     }
159
160     DeclaredStatement<?> buildDeclared() {
161         return root.buildDeclared();
162     }
163
164     EffectiveStatement<?, ?> buildEffective() {
165         return root.buildEffective();
166     }
167
168     void startPhase(final ModelProcessingPhase phase) {
169         @Nullable
170         final ModelProcessingPhase previousPhase = phase.getPreviousPhase();
171         Preconditions.checkState(Objects.equals(previousPhase, finishedPhase));
172         Preconditions.checkState(modifiers.get(previousPhase).isEmpty());
173         inProgressPhase = phase;
174     }
175
176     @Override
177     public <K, V, N extends IdentifierNamespace<K, V>> void addToLocalStorage(final Class<N> type, final K key,
178            final V value) {
179         if (ImportedNamespaceContext.class.isAssignableFrom(type)) {
180             importedNamespaces.add((NamespaceStorageNode) value);
181         }
182         getRoot().addToLocalStorage(type, key, value);
183     }
184
185     @Override
186     public StorageNodeType getStorageNodeType() {
187         return StorageNodeType.SOURCE_LOCAL_SPECIAL;
188     }
189
190     @Override
191     public <K, V, N extends IdentifierNamespace<K, V>> V getFromLocalStorage(final Class<N> type, final K key) {
192         final V potentialLocal = getRoot().getFromLocalStorage(type, key);
193         if (potentialLocal != null) {
194             return potentialLocal;
195         }
196         for (final NamespaceStorageNode importedSource : importedNamespaces) {
197             final V potential = importedSource.getFromLocalStorage(type, key);
198             if (potential != null) {
199                 return potential;
200             }
201         }
202         return null;
203     }
204
205     @Nullable
206     @Override
207     public <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAllFromLocalStorage(final Class<N> type) {
208         final Map<K, V> potentialLocal = getRoot().getAllFromLocalStorage(type);
209
210         if (potentialLocal != null) {
211             return potentialLocal;
212         }
213
214         for (final NamespaceStorageNode importedSource : importedNamespaces) {
215             final Map<K, V> potential = importedSource.getAllFromLocalStorage(type);
216
217             if (potential != null) {
218                 return potential;
219             }
220         }
221         return null;
222     }
223
224     @Override
225     public <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviour<K, V, N> getNamespaceBehaviour(
226             final Class<N> type) {
227         return currentContext.getNamespaceBehaviour(type);
228     }
229
230     @Override
231     public NamespaceStorageNode getParentNamespaceStorage() {
232         return currentContext;
233     }
234
235     PhaseCompletionProgress tryToCompletePhase(final ModelProcessingPhase phase) throws SourceException {
236         final Collection<ModifierImpl> currentPhaseModifiers = modifiers.get(phase);
237
238         boolean hasProgressed = tryToProgress(currentPhaseModifiers);
239
240         Preconditions.checkNotNull(this.root, "Malformed source. Valid root element is missing.");
241         final boolean phaseCompleted = root.tryToCompletePhase(phase);
242
243         hasProgressed = (tryToProgress(currentPhaseModifiers) | hasProgressed);
244
245         if (phaseCompleted && (currentPhaseModifiers.isEmpty())) {
246             finishedPhase = phase;
247             return PhaseCompletionProgress.FINISHED;
248
249         }
250         if (hasProgressed) {
251             return PhaseCompletionProgress.PROGRESS;
252         }
253         return PhaseCompletionProgress.NO_PROGRESS;
254     }
255
256
257     private static boolean tryToProgress(final Collection<ModifierImpl> currentPhaseModifiers) {
258         boolean hasProgressed = false;
259
260         final Iterator<ModifierImpl> modifier = currentPhaseModifiers.iterator();
261         while (modifier.hasNext()) {
262             if (modifier.next().tryApply()) {
263                 modifier.remove();
264                 hasProgressed = true;
265             }
266         }
267
268         return hasProgressed;
269
270     }
271
272     ModelActionBuilder newInferenceAction(final ModelProcessingPhase phase) {
273         final ModifierImpl action = new ModifierImpl(phase);
274         modifiers.put(phase, action);
275         return action;
276     }
277
278     @Override
279     public String toString() {
280         return "SourceSpecificContext [source=" + source + ", current=" + inProgressPhase + ", finished="
281                 + finishedPhase + "]";
282     }
283
284     SourceException failModifiers(final ModelProcessingPhase identifier) {
285         final List<SourceException> exceptions = new ArrayList<>();
286         for (final ModifierImpl mod : modifiers.get(identifier)) {
287             try {
288                 mod.failModifier();
289             } catch (final SourceException e) {
290                 exceptions.add(e);
291             }
292         }
293
294         final String message = String.format("Yang model processing phase %s failed", identifier);
295         if (exceptions.isEmpty()) {
296             return new InferenceException(message, root.getStatementSourceReference());
297         }
298
299         final InferenceException e = new InferenceException(message, root.getStatementSourceReference(),
300             exceptions.get(0));
301         final Iterator<SourceException> it = exceptions.listIterator(1);
302         while (it.hasNext()) {
303             e.addSuppressed(it.next());
304         }
305
306         return e;
307     }
308
309     void loadStatements() throws SourceException {
310         switch (inProgressPhase) {
311             case SOURCE_PRE_LINKAGE:
312                 source.writePreLinkage(new StatementContextWriter(this, inProgressPhase), stmtDef());
313                 break;
314             case SOURCE_LINKAGE:
315                 source.writeLinkage(new StatementContextWriter(this, inProgressPhase), stmtDef(), preLinkagePrefixes());
316                 break;
317             case STATEMENT_DEFINITION:
318                 source.writeLinkageAndStatementDefinitions(new StatementContextWriter(this, inProgressPhase), stmtDef(), prefixes());
319                 break;
320             case FULL_DECLARATION:
321                 source.writeFull(new StatementContextWriter(this, inProgressPhase), stmtDef(), prefixes());
322                 break;
323             default:
324                 break;
325         }
326     }
327
328     private static StatementDefinitionContext<?, ?, ?> resolveTypeBodyStmts(final String typeArgument) {
329         switch (typeArgument) {
330             case TypeUtils.DECIMAL64:
331                 return new StatementDefinitionContext<>(new Decimal64SpecificationImpl.Definition());
332             case TypeUtils.UNION:
333                 return new StatementDefinitionContext<>(new UnionSpecificationImpl.Definition());
334             case TypeUtils.ENUMERATION:
335                 return new StatementDefinitionContext<>(new EnumSpecificationImpl.Definition());
336             case TypeUtils.LEAF_REF:
337                 return new StatementDefinitionContext<>(new LeafrefSpecificationImpl.Definition());
338             case TypeUtils.BITS:
339                 return new StatementDefinitionContext<>(new BitsSpecificationImpl.Definition());
340             case TypeUtils.IDENTITY_REF:
341                 return new StatementDefinitionContext<>(new IdentityRefSpecificationImpl.Definition());
342             case TypeUtils.INSTANCE_IDENTIFIER:
343                 return new StatementDefinitionContext<>(new InstanceIdentifierSpecificationImpl.Definition());
344             default:
345                 return null;
346         }
347     }
348
349     private PrefixToModule preLinkagePrefixes() {
350         final PrefixToModuleMap preLinkagePrefixes = new PrefixToModuleMap(true);
351         final Map<String, URI> prefixToNamespaceMap = getAllFromLocalStorage(ImpPrefixToNamespace.class);
352         if (prefixToNamespaceMap == null) {
353             //:FIXME if it is a submodule without any import, the map is null. Handle also submodules and includes...
354             return null;
355         }
356         for (final Entry<String, URI> prefixToNamespace : prefixToNamespaceMap.entrySet()) {
357             preLinkagePrefixes.put(prefixToNamespace.getKey(), QNameModule.create(prefixToNamespace.getValue(), null));
358         }
359
360         return preLinkagePrefixes;
361     }
362
363     private PrefixToModule prefixes() {
364         final Map<String, ModuleIdentifier> allPrefixes = getRoot().getAllFromNamespace(ImpPrefixToModuleIdentifier
365                 .class);
366         final Map<String, ModuleIdentifier> belongsToPrefixes = getRoot().getAllFromNamespace
367                 (BelongsToPrefixToModuleIdentifier.class);
368         if (belongsToPrefixes != null) {
369             allPrefixes.putAll(belongsToPrefixes);
370         }
371
372         for (final Entry<String, ModuleIdentifier> stringModuleIdentifierEntry : allPrefixes.entrySet()) {
373             final QNameModule namespace = getRoot().getFromNamespace(ModuleIdentifierToModuleQName.class,
374                 stringModuleIdentifierEntry.getValue());
375             prefixToModuleMap.put(stringModuleIdentifierEntry.getKey(), namespace);
376         }
377         return prefixToModuleMap;
378     }
379
380     private QNameToStatementDefinition stmtDef() {
381         // regular YANG statements and extension supports added
382         final ImmutableMap<QName, StatementSupport<?, ?, ?>> definitions = currentContext.getSupportsForPhase(
383                 inProgressPhase).getDefinitions();
384         for (final Entry<QName, StatementSupport<?, ?, ?>> entry : definitions.entrySet()) {
385             qNameToStmtDefMap.put(entry.getKey(), entry.getValue());
386         }
387
388         // extensions added
389         if (inProgressPhase.equals(ModelProcessingPhase.FULL_DECLARATION)) {
390             final Map<QName, StmtContext<?, ExtensionStatement, EffectiveStatement<QName, ExtensionStatement>>> extensions =
391                     currentContext.getAllFromNamespace(ExtensionNamespace.class);
392             if (extensions != null) {
393                 for (final Entry<QName, StmtContext<?, ExtensionStatement, EffectiveStatement<QName, ExtensionStatement>>> extension :
394                     extensions.entrySet()) {
395                     if(qNameToStmtDefMap.get(extension.getKey()) == null) {
396                         qNameToStmtDefMap.put((extension.getKey()),
397                         (StatementDefinition) ((StatementContextBase<?, ?, ?>) extension.getValue()).definition()
398                         .getFactory());
399                     }
400                 }
401             }
402         }
403
404         return qNameToStmtDefMap;
405     }
406 }