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