Define a feature-parent
[yangtools.git] / parser / yang-parser-reactor / 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 static com.google.common.base.Preconditions.checkState;
11 import static com.google.common.base.Verify.verify;
12 import static com.google.common.base.Verify.verifyNotNull;
13 import static java.util.Objects.requireNonNull;
14
15 import com.google.common.collect.HashMultimap;
16 import com.google.common.collect.ImmutableList;
17 import com.google.common.collect.Multimap;
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.Map.Entry;
24 import java.util.Objects;
25 import java.util.Optional;
26 import org.eclipse.jdt.annotation.NonNull;
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.UnresolvedQName.Unqualified;
31 import org.opendaylight.yangtools.yang.common.YangVersion;
32 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
33 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
34 import org.opendaylight.yangtools.yang.model.api.meta.StatementSourceException;
35 import org.opendaylight.yangtools.yang.model.api.meta.StatementSourceReference;
36 import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
37 import org.opendaylight.yangtools.yang.parser.spi.ParserNamespaces;
38 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
39 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
40 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
41 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceKeyCriterion;
42 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceStorage;
43 import org.opendaylight.yangtools.yang.parser.spi.meta.ParserNamespace;
44 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementDefinitions;
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.meta.StmtContextUtils;
48 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixResolver;
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.StatementStreamSource;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55
56 final class SourceSpecificContext implements NamespaceStorage, Mutable {
57     enum PhaseCompletionProgress {
58         NO_PROGRESS,
59         PROGRESS,
60         FINISHED
61     }
62
63     private static final class SupportedStatements extends NamespaceAccess<QName, StatementSupport<?, ?, ?>> {
64         private final QNameToStatementDefinitionMap statementDefinitions;
65
66         SupportedStatements(final QNameToStatementDefinitionMap statementDefinitions) {
67             this.statementDefinitions = requireNonNull(statementDefinitions);
68         }
69
70         @Override
71         ParserNamespace<QName, StatementSupport<?, ?, ?>> namespace() {
72             return StatementSupport.NAMESPACE;
73         }
74
75         @Override
76         StatementSupport<?, ?, ?> valueFrom(final NamespaceStorage storage, final QName key) {
77             return statementDefinitions.getSupport(key);
78         }
79
80         @Override
81         void valueTo(final NamespaceStorage storage, final QName key, final StatementSupport<?, ?, ?> value) {
82             throw uoe();
83         }
84
85         @Override
86         Map<QName, StatementSupport<?, ?, ?>> allFrom(final NamespaceStorage storage) {
87             throw uoe();
88         }
89
90         @Override
91         Entry<QName, StatementSupport<?, ?, ?>> entryFrom(final NamespaceStorage storage,
92                 final NamespaceKeyCriterion<QName> criterion) {
93             throw uoe();
94         }
95
96         @Override
97         void addListener(final QName key, final KeyedValueAddedListener<QName, StatementSupport<?, ?, ?>> listener) {
98             throw uoe();
99         }
100
101         @Override
102         void addListener(final PredicateValueAddedListener<QName, StatementSupport<?, ?, ?>> listener) {
103             throw uoe();
104         }
105
106         private static UnsupportedOperationException uoe() {
107             return new UnsupportedOperationException("StatementSupportNamespace is immutable");
108         }
109     }
110
111     private static final Logger LOG = LoggerFactory.getLogger(SourceSpecificContext.class);
112
113     // TODO: consider keying by Byte equivalent of ExecutionOrder
114     private final Multimap<ModelProcessingPhase, ModifierImpl> modifiers = HashMultimap.create();
115     private final QNameToStatementDefinitionMap qnameToStmtDefMap = new QNameToStatementDefinitionMap();
116     private final @NonNull SupportedStatements statementSupports = new SupportedStatements(qnameToStmtDefMap);
117     private final HashMapPrefixResolver prefixToModuleMap = new HashMapPrefixResolver();
118     private final @NonNull BuildGlobalContext globalContext;
119
120     // Freed as soon as we complete ModelProcessingPhase.EFFECTIVE_MODEL
121     private StatementStreamSource source;
122
123     /*
124      * "imported" namespaces in this source -- this points to RootStatementContexts of
125      * - modules imported via 'import' statement
126      * - parent module, declared via 'belongs-to' statement
127      */
128     private List<RootStatementContext<?, ?, ?>> importedNamespaces = ImmutableList.of();
129     private RootStatementContext<?, ?, ?> root;
130     // TODO: consider using ExecutionOrder byte for these two
131     private ModelProcessingPhase finishedPhase = ModelProcessingPhase.INIT;
132     private ModelProcessingPhase inProgressPhase;
133
134     // If not null, do not add anything to modifiers, but record it here.
135     private List<Entry<ModelProcessingPhase, ModifierImpl>> delayedModifiers;
136
137     SourceSpecificContext(final BuildGlobalContext globalContext, final StatementStreamSource source) {
138         this.globalContext = requireNonNull(globalContext);
139         this.source = requireNonNull(source);
140     }
141
142     @NonNull BuildGlobalContext globalContext() {
143         return globalContext;
144     }
145
146     ModelProcessingPhase getInProgressPhase() {
147         return inProgressPhase;
148     }
149
150     AbstractResumedStatement<?, ?, ?> createDeclaredChild(final AbstractResumedStatement<?, ?, ?> current,
151             final int childId, final QName name, final String argument, final StatementSourceReference ref) {
152         StatementDefinitionContext<?, ?, ?> def = globalContext.getStatementDefinition(getRootVersion(), name);
153         if (def == null) {
154             def = globalContext.getModelDefinedStatementDefinition(name);
155             if (def == null) {
156                 final StatementSupport<?, ?, ?> extension = qnameToStmtDefMap.getSupport(name);
157                 if (extension != null) {
158                     def = new StatementDefinitionContext<>(extension);
159                     globalContext.putModelDefinedStatementDefinition(name, def);
160                 }
161             }
162         } else if (current != null) {
163             def = current.definition().overrideDefinition(def);
164         }
165
166         if (InferenceException.throwIfNull(def, ref, "Statement %s does not have type mapping defined.", name)
167                 .getArgumentDefinition().isPresent()) {
168             SourceException.throwIfNull(argument, ref, "Statement %s requires an argument", name);
169         } else {
170             SourceException.throwIf(argument != null, ref, "Statement %s does not take argument", name);
171         }
172
173         /*
174          * If the current statement definition has argument specific
175          * sub-definitions, get argument specific sub-definition based on given
176          * argument (e.g. type statement need to be specialized based on its
177          * argument).
178          */
179         if (def.hasArgumentSpecificSubDefinitions()) {
180             def = def.getSubDefinitionSpecificForArgument(argument);
181         }
182
183         if (current != null) {
184             return current.createSubstatement(childId, def, ref, argument);
185         }
186
187         /*
188          * If root is null or root version is other than default,
189          * we need to create new root.
190          */
191         if (root == null) {
192             root = new RootStatementContext<>(this, def, ref, argument);
193         } else if (!RootStatementContext.DEFAULT_VERSION.equals(root.yangVersion())
194                 && inProgressPhase == ModelProcessingPhase.SOURCE_LINKAGE) {
195             root = new RootStatementContext<>(this, def, ref, argument, root.yangVersion(),
196                     root.getRootIdentifier());
197         } else {
198             final QName rootStatement = root.definition().getStatementName();
199             final String rootArgument = root.rawArgument();
200
201             checkState(Objects.equals(def.getStatementName(), rootStatement) && Objects.equals(argument, rootArgument),
202                 "Root statement was already defined as '%s %s'.", rootStatement, rootArgument);
203         }
204         return root;
205     }
206
207     @NonNull SourceIdentifier identifySource() {
208         final var arg = root.getArgument();
209         verify(arg instanceof Unqualified, "Unexpected argument %s", arg);
210         final var unqualified = (Unqualified) arg;
211
212         final var module = root.namespaceItem(ParserNamespaces.MODULECTX_TO_QNAME, root);
213         if (module != null) {
214             // creates SourceIdentifier for a module
215             return new SourceIdentifier(unqualified, module.revision());
216         }
217
218         // creates SourceIdentifier for a submodule
219         return new SourceIdentifier(unqualified,
220             StmtContextUtils.getLatestRevision(root.declaredSubstatements()).orElse(null));
221     }
222
223     @NonNull DeclaredStatement<?> declaredRoot() {
224         return root.declared();
225     }
226
227     @NonNull EffectiveStatement<?, ?> effectiveRoot() {
228         return root.buildEffective();
229     }
230
231     /**
232      * Return version of root statement context.
233      *
234      * @return version of root statement context
235      */
236     private YangVersion getRootVersion() {
237         return root != null ? root.yangVersion() : RootStatementContext.DEFAULT_VERSION;
238     }
239
240     void startPhase(final ModelProcessingPhase phase) {
241         final ModelProcessingPhase previousPhase = phase.getPreviousPhase();
242         verify(Objects.equals(previousPhase, finishedPhase),
243             "Phase sequencing violation: previous phase should be %s, source %s has %s", previousPhase, source,
244             finishedPhase);
245
246         final Collection<ModifierImpl> previousModifiers = modifiers.get(previousPhase);
247         checkState(previousModifiers.isEmpty(), "Previous phase %s has unresolved modifiers %s in source %s",
248             previousPhase, previousModifiers, source);
249
250         inProgressPhase = phase;
251         LOG.debug("Source {} started phase {}", source, phase);
252     }
253
254     private void updateImportedNamespaces(final ParserNamespace<?, ?> type, final Object value) {
255         if (ParserNamespaces.BELONGSTO_PREFIX_TO_MODULECTX.equals(type)
256             || ParserNamespaces.IMPORTED_MODULE.equals(type)) {
257             verify(value instanceof RootStatementContext, "Unexpected imported value %s", value);
258
259             if (importedNamespaces.isEmpty()) {
260                 importedNamespaces = new ArrayList<>(1);
261             }
262             importedNamespaces.add((RootStatementContext<?, ?, ?>) value);
263         }
264     }
265
266     @Override
267     public <K, V> V putToLocalStorage(final ParserNamespace<K, V> type, final K key, final V value) {
268         // RootStatementContext takes care of IncludedModuleContext and the rest...
269         final V ret = root.putToLocalStorage(type, key, value);
270         // FIXME: what about duplicates?
271         updateImportedNamespaces(type, value);
272         return ret;
273     }
274
275     @Override
276     public <K, V> V putToLocalStorageIfAbsent(final ParserNamespace<K, V> type, final K key, final V value) {
277         // RootStatementContext takes care of IncludedModuleContext and the rest...
278         final V ret = root.putToLocalStorageIfAbsent(type, key, value);
279         if (ret == null) {
280             updateImportedNamespaces(type, value);
281         }
282         return ret;
283     }
284
285     @Override
286     public StorageType getStorageType() {
287         return StorageType.SOURCE_LOCAL_SPECIAL;
288     }
289
290     @Override
291     public <K, V> V getFromLocalStorage(final ParserNamespace<K, V> type, final K key) {
292         final V potentialLocal = root.getFromLocalStorage(type, key);
293         if (potentialLocal != null) {
294             return potentialLocal;
295         }
296
297         for (final NamespaceStorage importedSource : importedNamespaces) {
298             final V potential = importedSource.getFromLocalStorage(type, key);
299             if (potential != null) {
300                 return potential;
301             }
302         }
303         return null;
304     }
305
306     @Override
307     public <K, V> Map<K, V> getAllFromLocalStorage(final ParserNamespace<K, V> type) {
308         final Map<K, V> potentialLocal = root.getAllFromLocalStorage(type);
309         if (potentialLocal != null) {
310             return potentialLocal;
311         }
312
313         for (final NamespaceStorage importedSource : importedNamespaces) {
314             final Map<K, V> potential = importedSource.getAllFromLocalStorage(type);
315
316             if (potential != null) {
317                 return potential;
318             }
319         }
320         return null;
321     }
322
323     <K, V> NamespaceAccess<K, V> accessNamespace(final ParserNamespace<K, V> type) {
324         if (StatementSupport.NAMESPACE.equals(type)) {
325             @SuppressWarnings("unchecked")
326             final var ret = (NamespaceAccess<K, V>) statementSupports;
327             return ret;
328         }
329         return globalContext.accessNamespace(type);
330     }
331
332     @Override
333     public GlobalStorage getParentStorage() {
334         return globalContext;
335     }
336
337     PhaseCompletionProgress tryToCompletePhase(final byte executionOrder) {
338         final ModelProcessingPhase phase = verifyNotNull(ModelProcessingPhase.ofExecutionOrder(executionOrder));
339         final Collection<ModifierImpl> currentPhaseModifiers = modifiers.get(phase);
340
341         boolean hasProgressed = tryToProgress(currentPhaseModifiers);
342         final boolean phaseCompleted = requireNonNull(root, "Malformed source. Valid root element is missing.")
343                 .tryToCompletePhase(executionOrder);
344
345         hasProgressed |= tryToProgress(currentPhaseModifiers);
346
347         // TODO: use executionOrder instead?
348         if (phaseCompleted && currentPhaseModifiers.isEmpty()) {
349             finishedPhase = phase;
350             LOG.debug("Source {} finished phase {}", source, phase);
351             if (phase == ModelProcessingPhase.EFFECTIVE_MODEL) {
352                 // We have the effective model acquired, which is the final phase of source interaction.
353                 LOG.trace("Releasing source {}", source);
354                 source = null;
355             }
356             return PhaseCompletionProgress.FINISHED;
357         }
358
359         return hasProgressed ? PhaseCompletionProgress.PROGRESS : PhaseCompletionProgress.NO_PROGRESS;
360     }
361
362     private boolean tryToProgress(final Collection<ModifierImpl> currentPhaseModifiers) {
363         boolean hasProgressed = false;
364
365         // We are about to iterate over the modifiers and invoke callbacks. Those callbacks can end up circling back
366         // and modifying the same collection. This asserts that modifiers should not be modified.
367         delayedModifiers = List.of();
368
369         // Try making forward progress ...
370         final Iterator<ModifierImpl> modifier = currentPhaseModifiers.iterator();
371         while (modifier.hasNext()) {
372             if (modifier.next().tryApply()) {
373                 modifier.remove();
374                 hasProgressed = true;
375             }
376         }
377
378         // We have finished iterating, if we have any delayed modifiers, put them back. This may seem as if we want
379         // to retry the loop, but we do not have to, as we will be circling back anyway.
380         //
381         // The thing is, we are inherently single-threaded and therefore if we observe non-empty delayedModifiers, the
382         // only way that could happen is through a callback, which in turn means we have made progress.
383         if (!delayedModifiers.isEmpty()) {
384             verify(hasProgressed, "Delayed modifiers encountered without making progress in %s", this);
385             for (Entry<ModelProcessingPhase, ModifierImpl> entry : delayedModifiers) {
386                 modifiers.put(entry.getKey(), entry.getValue());
387             }
388         }
389         delayedModifiers = null;
390
391         return hasProgressed;
392     }
393
394     @NonNull ModelActionBuilder newInferenceAction(final @NonNull ModelProcessingPhase phase) {
395         final ModifierImpl action = new ModifierImpl();
396
397         if (delayedModifiers != null) {
398             if (delayedModifiers.isEmpty()) {
399                 delayedModifiers = new ArrayList<>(2);
400             }
401             delayedModifiers.add(Map.entry(phase,action));
402         } else {
403             modifiers.put(phase, action);
404         }
405
406         return action;
407     }
408
409     @Override
410     public String toString() {
411         return "SourceSpecificContext [source=" + source + ", current=" + inProgressPhase + ", finished="
412                 + finishedPhase + "]";
413     }
414
415     Optional<StatementSourceException> failModifiers(final ModelProcessingPhase identifier) {
416         final var exceptions = new ArrayList<StatementSourceException>();
417         for (var mod : modifiers.get(identifier)) {
418             try {
419                 mod.failModifier();
420             } catch (StatementSourceException e) {
421                 exceptions.add(e);
422             }
423         }
424
425         return switch (exceptions.size()) {
426             case 0 -> Optional.empty();
427             case 1 -> Optional.of(exceptions.get(0));
428             default -> {
429                 final var ex = new InferenceException("Yang model processing phase " + identifier + " failed", root,
430                     exceptions.get(0));
431                 exceptions.listIterator(1).forEachRemaining(ex::addSuppressed);
432                 yield Optional.of(ex);
433             }
434         };
435     }
436
437     void loadStatements() {
438         LOG.trace("Source {} loading statements for phase {}", source, inProgressPhase);
439
440         switch (inProgressPhase) {
441             case SOURCE_PRE_LINKAGE:
442                 source.writePreLinkage(new StatementContextWriter(this, inProgressPhase), stmtDef());
443                 break;
444             case SOURCE_LINKAGE:
445                 source.writeLinkage(new StatementContextWriter(this, inProgressPhase), stmtDef(), preLinkagePrefixes(),
446                     getRootVersion());
447                 break;
448             case STATEMENT_DEFINITION:
449                 source.writeLinkageAndStatementDefinitions(new StatementContextWriter(this, inProgressPhase), stmtDef(),
450                     prefixes(), getRootVersion());
451                 break;
452             case FULL_DECLARATION:
453                 source.writeFull(new StatementContextWriter(this, inProgressPhase), stmtDef(), prefixes(),
454                     getRootVersion());
455                 break;
456             default:
457                 break;
458         }
459     }
460
461     private PrefixResolver preLinkagePrefixes() {
462         final HashMapPrefixResolver preLinkagePrefixes = new HashMapPrefixResolver();
463         final var prefixToNamespaceMap = getAllFromLocalStorage(ParserNamespaces.IMP_PREFIX_TO_NAMESPACE);
464         if (prefixToNamespaceMap == null) {
465             //:FIXME if it is a submodule without any import, the map is null. Handle also submodules and includes...
466             return null;
467         }
468
469         prefixToNamespaceMap.forEach((key, value) -> preLinkagePrefixes.put(key, QNameModule.of(value)));
470         return preLinkagePrefixes;
471     }
472
473     private PrefixResolver prefixes() {
474         final var allImports = root.namespace(ParserNamespaces.IMPORT_PREFIX_TO_MODULECTX);
475         if (allImports != null) {
476             allImports.forEach((key, value) ->
477                 prefixToModuleMap.put(key, root.namespaceItem(ParserNamespaces.MODULECTX_TO_QNAME, value)));
478         }
479
480         final var allBelongsTo = root.namespace(ParserNamespaces.BELONGSTO_PREFIX_TO_MODULECTX);
481         if (allBelongsTo != null) {
482             allBelongsTo.forEach((key, value) ->
483                 prefixToModuleMap.put(key, root.namespaceItem(ParserNamespaces.MODULECTX_TO_QNAME, value)));
484         }
485
486         return prefixToModuleMap;
487     }
488
489     private QNameToStatementDefinition stmtDef() {
490         // regular YANG statements and extension supports added
491         final StatementSupportBundle supportsForPhase = globalContext.getSupportsForPhase(inProgressPhase);
492         qnameToStmtDefMap.putAll(supportsForPhase.getCommonDefinitions());
493         qnameToStmtDefMap.putAll(supportsForPhase.getDefinitionsSpecificForVersion(getRootVersion()));
494
495         // No further actions needed
496         if (inProgressPhase != ModelProcessingPhase.FULL_DECLARATION) {
497             return qnameToStmtDefMap;
498         }
499
500         // We need to any and all extension statements which have been declared in the context
501         final Map<QName, StatementSupport<?, ?, ?>> extensions = globalContext.getNamespace(
502             StatementDefinitions.NAMESPACE);
503         if (extensions != null) {
504             extensions.forEach((qname, support) -> {
505                 final StatementSupport<?, ?, ?> existing = qnameToStmtDefMap.putIfAbsent(qname, support);
506                 if (existing != null) {
507                     LOG.debug("Source {} already defines statement {} as {}", source, qname, existing);
508                 } else {
509                     LOG.debug("Source {} defined statement {} as {}", source, qname, support);
510                 }
511             });
512         }
513
514         return qnameToStmtDefMap;
515     }
516
517     Collection<SourceIdentifier> getRequiredSources() {
518         return root.getRequiredSources();
519     }
520
521     SourceIdentifier getRootIdentifier() {
522         return root.getRootIdentifier();
523     }
524 }