5c1fa6329c87aa8436bf697c53dd7697eb2e0432
[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.base.Verify;
12 import com.google.common.collect.HashMultimap;
13 import com.google.common.collect.ImmutableList;
14 import com.google.common.collect.ImmutableTable;
15 import com.google.common.collect.Multimap;
16 import com.google.common.collect.Table;
17 import java.net.URI;
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Objects;
24 import java.util.Optional;
25 import java.util.Set;
26 import javax.annotation.Nullable;
27 import org.opendaylight.yangtools.concepts.Mutable;
28 import org.opendaylight.yangtools.yang.common.QName;
29 import org.opendaylight.yangtools.yang.common.QNameModule;
30 import org.opendaylight.yangtools.yang.common.YangConstants;
31 import org.opendaylight.yangtools.yang.common.YangVersion;
32 import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
33 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
34 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
35 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
36 import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
37 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
38 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
39 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
40 import org.opendaylight.yangtools.yang.parser.spi.meta.MutableStatement;
41 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
42 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
43 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.StorageNodeType;
44 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementDefinitionNamespace;
45 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
46 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupportBundle;
47 import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToModuleContext;
48 import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToPrefixToModuleIdentifier;
49 import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToModuleIdentifier;
50 import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToNamespace;
51 import org.opendaylight.yangtools.yang.parser.spi.source.ImportedModuleContext;
52 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleIdentifierToModuleQName;
53 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
54 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModuleMap;
55 import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
56 import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinitionMap;
57 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
58 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
59 import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
60 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.BitsSpecificationImpl;
61 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Decimal64SpecificationImpl;
62 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.EnumSpecificationImpl;
63 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.IdentityRefSpecificationImpl;
64 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.InstanceIdentifierSpecificationImpl;
65 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.LeafrefSpecificationImpl;
66 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.ModelDefinedStatementDefinition;
67 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.TypeUtils;
68 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.UnionSpecificationImpl;
69 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.UnknownStatementImpl;
70 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils;
71 import org.opendaylight.yangtools.yang.parser.stmt.rfc7950.IdentityrefSpecificationRfc7950Support;
72 import org.opendaylight.yangtools.yang.parser.stmt.rfc7950.LeafrefSpecificationRfc7950Support;
73 import org.slf4j.Logger;
74 import org.slf4j.LoggerFactory;
75
76 public class SourceSpecificContext implements NamespaceStorageNode, NamespaceBehaviour.Registry, Mutable {
77
78     public enum PhaseCompletionProgress {
79         NO_PROGRESS,
80         PROGRESS,
81         FINISHED
82     }
83
84     private static final Logger LOG = LoggerFactory.getLogger(SourceSpecificContext.class);
85     private static final Table<YangVersion, String, StatementSupport<?, ?, ?>> BUILTIN_TYPE_SUPPORTS =
86             ImmutableTable.<YangVersion, String, StatementSupport<?, ?, ?>>builder()
87             .put(YangVersion.VERSION_1, TypeUtils.DECIMAL64, new Decimal64SpecificationImpl.Definition())
88             .put(YangVersion.VERSION_1, TypeUtils.UNION, new UnionSpecificationImpl.Definition())
89             .put(YangVersion.VERSION_1, TypeUtils.ENUMERATION, new EnumSpecificationImpl.Definition())
90             .put(YangVersion.VERSION_1, TypeUtils.LEAF_REF, new LeafrefSpecificationImpl.Definition())
91             .put(YangVersion.VERSION_1_1, TypeUtils.LEAF_REF, new LeafrefSpecificationRfc7950Support())
92             .put(YangVersion.VERSION_1, TypeUtils.BITS, new BitsSpecificationImpl.Definition())
93             .put(YangVersion.VERSION_1, TypeUtils.IDENTITY_REF, new IdentityRefSpecificationImpl.Definition())
94             .put(YangVersion.VERSION_1_1, TypeUtils.IDENTITY_REF, new IdentityrefSpecificationRfc7950Support())
95             .put(YangVersion.VERSION_1, TypeUtils.INSTANCE_IDENTIFIER, new InstanceIdentifierSpecificationImpl.Definition())
96                     .build();
97
98     private static final QName TYPE = YangStmtMapping.TYPE.getStatementName();
99
100     private final Multimap<ModelProcessingPhase, ModifierImpl> modifiers = HashMultimap.create();
101     private final QNameToStatementDefinitionMap qNameToStmtDefMap = new QNameToStatementDefinitionMap();
102     private final PrefixToModuleMap prefixToModuleMap = new PrefixToModuleMap();
103     private final BuildGlobalContext currentContext;
104     private final StatementStreamSource source;
105
106     /*
107      * "imported" namespaces in this source -- this points to RootStatementContexts of
108      * - modules imported via 'import' statement
109      * - parent module, declared via 'belongs-to' statement
110      */
111     private Collection<RootStatementContext<?, ?, ?>> importedNamespaces = ImmutableList.of();
112     private ModelProcessingPhase finishedPhase = ModelProcessingPhase.INIT;
113     private ModelProcessingPhase inProgressPhase;
114     private RootStatementContext<?, ?, ?> root;
115
116     SourceSpecificContext(final BuildGlobalContext currentContext, final StatementStreamSource source) {
117         this.currentContext = Preconditions.checkNotNull(currentContext);
118         this.source = Preconditions.checkNotNull(source);
119     }
120
121     boolean isEnabledSemanticVersioning(){
122         return currentContext.isEnabledSemanticVersioning();
123     }
124
125     ModelProcessingPhase getInProgressPhase() {
126         return inProgressPhase;
127     }
128
129     StatementContextBase<?, ?, ?> createDeclaredChild(final StatementContextBase<?, ?, ?> current, final int childId,
130             QName name, final String argument, final StatementSourceReference ref) {
131         if (current != null) {
132             // Fast path: we are entering a statement which was emitted in previous phase
133             final StatementContextBase<?, ?, ?> existing = current.lookupSubstatement(childId);
134             if (existing != null) {
135                 return existing;
136             }
137         }
138
139         // FIXME: BUG-7038: Refactor/clean up this special case
140         if (TYPE.equals(name)) {
141             SourceException.throwIfNull(argument, ref, "Type statement requires an argument");
142             if (TypeUtils.isYangTypeBodyStmtString(argument)) {
143                 name = QName.create(YangConstants.RFC6020_YIN_MODULE, argument);
144             } else {
145                 name = QName.create(YangConstants.RFC6020_YIN_MODULE, TYPE.getLocalName());
146             }
147         }
148
149         StatementDefinitionContext<?, ?, ?> def = currentContext.getStatementDefinition(getRootVersion(), name);
150         if (def == null) {
151             final StatementSupport<?, ?, ?> extension = qNameToStmtDefMap.get(name);
152             if (extension != null) {
153                 def = new StatementDefinitionContext<>(extension);
154             } else {
155                 // type-body-stmts
156                 def = resolveTypeBodyStmts(name.getLocalName(), getRootVersion());
157             }
158         } else if (current != null && current.definition().getRepresentingClass().equals(UnknownStatementImpl.class)) {
159             /*
160              * This code wraps statements encountered inside an extension so they do not get confused with regular
161              * statements.
162              *
163              * FIXME: BUG-7037: re-evaluate whether this is really needed, as this is a very expensive way of making
164              *        this work. We really should be peeking into the extension definition to find these nodes,
165              *        as otherwise we are not reusing definitions nor support for these nodes.
166              */
167             final QName qName = Utils.qNameFromArgument(current, name.getLocalName());
168             def = new StatementDefinitionContext<>(new UnknownStatementImpl.Definition(
169                 new ModelDefinedStatementDefinition(qName, argument != null)));
170         }
171
172         InferenceException.throwIfNull(def, ref, "Statement %s does not have type mapping defined.", name);
173         if (def.hasArgument()) {
174             SourceException.throwIfNull(argument, ref, "Statement %s requires an argument", name);
175         } else {
176             SourceException.throwIf(argument != null, ref, "Statement %s does not take argument", name);
177         }
178
179         if (current != null) {
180             return current.createSubstatement(childId, def, ref, argument);
181         }
182
183         /*
184          * If root is null or root version is other than default,
185          * we need to create new root.
186          */
187         if (root == null) {
188             root = new RootStatementContext<>(this, def, ref, argument);
189         } else if (!RootStatementContext.DEFAULT_VERSION.equals(root.getRootVersion())
190                 && inProgressPhase == ModelProcessingPhase.SOURCE_LINKAGE) {
191             root = new RootStatementContext<>(this, def, ref, argument, root.getRootVersion(), root.getRootIdentifier());
192         } else {
193             final QName rootStatement = root.definition().getStatementName();
194             final String rootArgument = root.rawStatementArgument();
195
196             Preconditions.checkState(Objects.equals(def.getStatementName(), rootStatement)
197                 && Objects.equals(argument, rootArgument),
198                 "Root statement was already defined as '%s %s'.", rootStatement, rootArgument);
199         }
200         return root;
201     }
202
203     RootStatementContext<?, ?, ?> getRoot() {
204         return root;
205     }
206
207     /**
208      * Return version of root statement context.
209      *
210      * @return version of root statement context
211      */
212     YangVersion getRootVersion() {
213         return root != null ? root.getRootVersion() : RootStatementContext.DEFAULT_VERSION;
214     }
215
216     DeclaredStatement<?> buildDeclared() {
217         return root.buildDeclared();
218     }
219
220     EffectiveStatement<?, ?> buildEffective() {
221         return root.buildEffective();
222     }
223
224     void startPhase(final ModelProcessingPhase phase) {
225         @Nullable final ModelProcessingPhase previousPhase = phase.getPreviousPhase();
226         Verify.verify(Objects.equals(previousPhase, finishedPhase),
227             "Phase sequencing violation: previous phase should be %s, source %s has %s", previousPhase, source,
228             finishedPhase);
229
230         final Collection<ModifierImpl> previousModifiers = modifiers.get(previousPhase);
231         Preconditions.checkState(previousModifiers.isEmpty(),
232             "Previous phase %s has unresolved modifiers %s in source %s",
233             previousPhase, previousModifiers, source);
234
235         inProgressPhase = phase;
236         LOG.debug("Source {} started phase {}", source, phase);
237     }
238
239     private void updateImportedNamespaces(final Class<?> type, final Object value) {
240         if (BelongsToModuleContext.class.isAssignableFrom(type) || ImportedModuleContext.class.isAssignableFrom(type)) {
241             if (importedNamespaces.isEmpty()) {
242                 importedNamespaces = new ArrayList<>(1);
243             }
244
245             Verify.verify(value instanceof RootStatementContext);
246             importedNamespaces.add((RootStatementContext<?, ?, ?>) value);
247         }
248     }
249
250     @Override
251     public <K, V, N extends IdentifierNamespace<K, V>> V putToLocalStorage(final Class<N> type, final K key,
252            final V value) {
253         // RootStatementContext takes care of IncludedModuleContext and the rest...
254         final V ret = getRoot().putToLocalStorage(type, key, value);
255         // FIXME: what about duplicates?
256         updateImportedNamespaces(type, value);
257         return ret;
258     }
259
260     @Override
261     public <K, V, N extends IdentifierNamespace<K, V>> V putToLocalStorageIfAbsent(final Class<N> type, final K key,
262            final V value) {
263         // RootStatementContext takes care of IncludedModuleContext and the rest...
264         final V ret = getRoot().putToLocalStorageIfAbsent(type, key, value);
265         if (ret == null) {
266             updateImportedNamespaces(type, value);
267         }
268         return ret;
269     }
270
271     @Override
272     public StorageNodeType getStorageNodeType() {
273         return StorageNodeType.SOURCE_LOCAL_SPECIAL;
274     }
275
276     @Override
277     public <K, V, N extends IdentifierNamespace<K, V>> V getFromLocalStorage(final Class<N> type, final K key) {
278         final V potentialLocal = getRoot().getFromLocalStorage(type, key);
279         if (potentialLocal != null) {
280             return potentialLocal;
281         }
282
283         for (final NamespaceStorageNode importedSource : importedNamespaces) {
284             final V potential = importedSource.getFromLocalStorage(type, key);
285             if (potential != null) {
286                 return potential;
287             }
288         }
289         return null;
290     }
291
292     @Nullable
293     @Override
294     public <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAllFromLocalStorage(final Class<N> type) {
295         final Map<K, V> potentialLocal = getRoot().getAllFromLocalStorage(type);
296         if (potentialLocal != null) {
297             return potentialLocal;
298         }
299
300         for (final NamespaceStorageNode importedSource : importedNamespaces) {
301             final Map<K, V> potential = importedSource.getAllFromLocalStorage(type);
302
303             if (potential != null) {
304                 return potential;
305             }
306         }
307         return null;
308     }
309
310     @Override
311     public <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviour<K, V, N> getNamespaceBehaviour(
312             final Class<N> type) {
313         return currentContext.getNamespaceBehaviour(type);
314     }
315
316     @Override
317     public NamespaceStorageNode getParentNamespaceStorage() {
318         return currentContext;
319     }
320
321     PhaseCompletionProgress tryToCompletePhase(final ModelProcessingPhase phase) throws SourceException {
322         final Collection<ModifierImpl> currentPhaseModifiers = modifiers.get(phase);
323
324         boolean hasProgressed = tryToProgress(currentPhaseModifiers);
325
326         Preconditions.checkNotNull(this.root, "Malformed source. Valid root element is missing.");
327         final boolean phaseCompleted = root.tryToCompletePhase(phase);
328
329         hasProgressed |= tryToProgress(currentPhaseModifiers);
330
331         if (phaseCompleted && currentPhaseModifiers.isEmpty()) {
332             finishedPhase = phase;
333             LOG.debug("Source {} finished phase {}", source, phase);
334             return PhaseCompletionProgress.FINISHED;
335
336         }
337
338         return hasProgressed ? PhaseCompletionProgress.PROGRESS : PhaseCompletionProgress.NO_PROGRESS;
339     }
340
341     private static boolean tryToProgress(final Collection<ModifierImpl> currentPhaseModifiers) {
342         boolean hasProgressed = false;
343
344         final Iterator<ModifierImpl> modifier = currentPhaseModifiers.iterator();
345         while (modifier.hasNext()) {
346             if (modifier.next().tryApply()) {
347                 modifier.remove();
348                 hasProgressed = true;
349             }
350         }
351
352         return hasProgressed;
353     }
354
355     ModelActionBuilder newInferenceAction(final ModelProcessingPhase phase) {
356         final ModifierImpl action = new ModifierImpl(phase);
357         modifiers.put(phase, action);
358         return action;
359     }
360
361     @Override
362     public String toString() {
363         return "SourceSpecificContext [source=" + source + ", current=" + inProgressPhase + ", finished="
364                 + finishedPhase + "]";
365     }
366
367     Optional<SourceException> failModifiers(final ModelProcessingPhase identifier) {
368         final List<SourceException> exceptions = new ArrayList<>();
369         for (final ModifierImpl mod : modifiers.get(identifier)) {
370             try {
371                 mod.failModifier();
372             } catch (final SourceException e) {
373                 exceptions.add(e);
374             }
375         }
376
377         if (exceptions.isEmpty()) {
378             return Optional.empty();
379         }
380
381         final String message = String.format("Yang model processing phase %s failed", identifier);
382         final InferenceException e = new InferenceException(message, root.getStatementSourceReference(),
383             exceptions.get(0));
384         exceptions.listIterator(1).forEachRemaining(e::addSuppressed);
385
386         return Optional.of(e);
387     }
388
389     void loadStatements() throws SourceException {
390         LOG.trace("Source {} loading statements for phase {}", source, inProgressPhase);
391
392         switch (inProgressPhase) {
393             case SOURCE_PRE_LINKAGE:
394                 source.writePreLinkage(new StatementContextWriter(this, inProgressPhase), stmtDef());
395                 break;
396             case SOURCE_LINKAGE:
397                 source.writeLinkage(new StatementContextWriter(this, inProgressPhase), stmtDef(), preLinkagePrefixes(), getRootVersion());
398                 break;
399             case STATEMENT_DEFINITION:
400                 source.writeLinkageAndStatementDefinitions(new StatementContextWriter(this, inProgressPhase), stmtDef(), prefixes(), getRootVersion());
401                 break;
402             case FULL_DECLARATION:
403                 source.writeFull(new StatementContextWriter(this, inProgressPhase), stmtDef(), prefixes(), getRootVersion());
404                 break;
405             default:
406                 break;
407         }
408     }
409
410     private static StatementDefinitionContext<?, ?, ?> resolveTypeBodyStmts(final String typeArgument,
411             final YangVersion version) {
412         StatementSupport<?, ?, ?> support = BUILTIN_TYPE_SUPPORTS.get(version, typeArgument);
413
414         if (support == null) {
415             support = BUILTIN_TYPE_SUPPORTS.get(YangVersion.VERSION_1, typeArgument);
416         }
417
418         return support == null ? null : new StatementDefinitionContext<>(support);
419     }
420
421     private PrefixToModule preLinkagePrefixes() {
422         final PrefixToModuleMap preLinkagePrefixes = new PrefixToModuleMap(true);
423         final Map<String, URI> prefixToNamespaceMap = getAllFromLocalStorage(ImpPrefixToNamespace.class);
424         if (prefixToNamespaceMap == null) {
425             //:FIXME if it is a submodule without any import, the map is null. Handle also submodules and includes...
426             return null;
427         }
428
429         prefixToNamespaceMap.forEach((key, value) -> preLinkagePrefixes.put(key, QNameModule.create(value, null)));
430         return preLinkagePrefixes;
431     }
432
433     private PrefixToModule prefixes() {
434         final Map<String, ModuleIdentifier> allPrefixes = getRoot().getAllFromNamespace(ImpPrefixToModuleIdentifier
435                 .class);
436         final Map<String, ModuleIdentifier> belongsToPrefixes = getRoot().getAllFromNamespace
437                 (BelongsToPrefixToModuleIdentifier.class);
438         if (belongsToPrefixes != null) {
439             allPrefixes.putAll(belongsToPrefixes);
440         }
441
442         allPrefixes.forEach((key, value) ->
443             prefixToModuleMap.put(key, getRoot().getFromNamespace(ModuleIdentifierToModuleQName.class, value)));
444
445         return prefixToModuleMap;
446     }
447
448     private QNameToStatementDefinition stmtDef() {
449         // regular YANG statements and extension supports added
450         final StatementSupportBundle supportsForPhase = currentContext.getSupportsForPhase(inProgressPhase);
451         qNameToStmtDefMap.putAll(supportsForPhase.getCommonDefinitions());
452         qNameToStmtDefMap.putAll(supportsForPhase.getDefinitionsSpecificForVersion(getRootVersion()));
453
454         // No further actions needed
455         if (inProgressPhase != ModelProcessingPhase.FULL_DECLARATION) {
456             return qNameToStmtDefMap;
457         }
458
459         // We need to any and all extension statements which have been declared in the context
460         final Map<QName, StatementSupport<?, ?, ?>> extensions = currentContext.getAllFromNamespace(
461                 StatementDefinitionNamespace.class);
462         if (extensions != null) {
463             extensions.forEach((qname, support) -> {
464                 final StatementSupport<?, ?, ?> existing = qNameToStmtDefMap.putIfAbsent(qname, support);
465                 if (existing != null) {
466                     LOG.debug("Source {} already defines statement {} as {}", source, qname, existing);
467                 } else {
468                     LOG.debug("Source {} defined statement {} as {}", source, qname, support);
469                 }
470             });
471         }
472
473         return qNameToStmtDefMap;
474     }
475
476     public Set<YangVersion> getSupportedVersions() {
477         return currentContext.getSupportedVersions();
478     }
479
480     void addMutableStmtToSeal(final MutableStatement mutableStatement) {
481         currentContext.addMutableStmtToSeal(mutableStatement);
482     }
483
484     Collection<ModuleIdentifier> getRequiredModules() {
485         return root.getRequiredModules();
486     }
487
488     ModuleIdentifier getRootIdentifier() {
489         return root.getRootIdentifier();
490     }
491 }