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