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