Migrate openconfig-model-api to bnd-parent
[yangtools.git] / parser / yang-parser-reactor / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / StatementContextBase.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.checkArgument;
11 import static com.google.common.base.Preconditions.checkState;
12 import static com.google.common.base.Verify.verify;
13 import static com.google.common.base.Verify.verifyNotNull;
14 import static java.util.Objects.requireNonNull;
15
16 import com.google.common.base.VerifyException;
17 import com.google.common.collect.ImmutableCollection;
18 import com.google.common.collect.ImmutableList;
19 import com.google.common.collect.ImmutableMultimap;
20 import com.google.common.collect.Multimap;
21 import com.google.common.collect.Multimaps;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.EnumMap;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Optional;
29 import java.util.stream.Stream;
30 import org.eclipse.jdt.annotation.NonNull;
31 import org.eclipse.jdt.annotation.Nullable;
32 import org.opendaylight.yangtools.yang.common.QNameModule;
33 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
34 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
35 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
36 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory;
37 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
38 import org.opendaylight.yangtools.yang.parser.spi.meta.ImplicitParentAwareStatementSupport;
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.ModelProcessingPhase.ExecutionOrder;
42 import org.opendaylight.yangtools.yang.parser.spi.meta.MutableStatement;
43 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceKeyCriterion;
44 import org.opendaylight.yangtools.yang.parser.spi.meta.ParserNamespace;
45 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementFactory;
46 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
47 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport.CopyPolicy;
48 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
49 import org.opendaylight.yangtools.yang.parser.spi.meta.UndeclaredStatementFactory;
50 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
51 import org.opendaylight.yangtools.yang.parser.stmt.reactor.NamespaceAccess.KeyedValueAddedListener;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 /**
56  * Core reactor statement implementation of {@link Mutable}.
57  *
58  * @param <A> Argument type
59  * @param <D> Declared Statement representation
60  * @param <E> Effective Statement representation
61  */
62 abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
63         extends ReactorStmtCtx<A, D, E> implements CopyHistory {
64     /**
65      * Event listener when an item is added to model namespace.
66      */
67     interface OnNamespaceItemAdded {
68         /**
69          * Invoked whenever a new item is added to a namespace.
70          */
71         void namespaceItemAdded(StatementContextBase<?, ?, ?> context, ParserNamespace<?, ?> namespace, Object key,
72             Object value);
73     }
74
75     /**
76      * Event listener when a parsing {@link ModelProcessingPhase} is completed.
77      */
78     interface OnPhaseFinished {
79         /**
80          * Invoked whenever a processing phase has finished.
81          */
82         boolean phaseFinished(StatementContextBase<?, ?, ?> context, ModelProcessingPhase finishedPhase);
83     }
84
85     /**
86      * Interface for all mutations within an {@link ModelActionBuilder.InferenceAction}.
87      */
88     interface ContextMutation {
89
90         boolean isFinished();
91     }
92
93     private static final Logger LOG = LoggerFactory.getLogger(StatementContextBase.class);
94
95     // Bottom 4 bits, encoding a CopyHistory, aight?
96     private static final byte COPY_ORIGINAL              = 0x00;
97     private static final byte COPY_LAST_TYPE_MASK        = 0x03;
98     @Deprecated(since = "7.0.9", forRemoval = true)
99     private static final byte COPY_ADDED_BY_USES         = 0x04;
100     private static final byte COPY_ADDED_BY_AUGMENTATION = 0x08;
101
102     // Top four bits, of which we define the topmost two to 0. We use the bottom two to encode last CopyType, aight?
103     private static final int COPY_CHILD_TYPE_SHIFT       = 4;
104
105     private static final CopyType @NonNull [] COPY_TYPE_VALUES = CopyType.values();
106
107     static {
108         final int copyTypes = COPY_TYPE_VALUES.length;
109         // This implies CopyType.ordinal() is <= COPY_TYPE_MASK
110         verify(copyTypes == COPY_LAST_TYPE_MASK + 1, "Unexpected %s CopyType values", copyTypes);
111     }
112
113     /**
114      * 8 bits worth of instance storage. This is treated as a constant bit field with following structure:
115      * <pre>
116      *   <code>
117      * |7|6|5|4|3|2|1|0|
118      * |0 0|cct|a|u|lst|
119      *   </code>
120      * </pre>
121      *
122      * <p>
123      * The four allocated fields are:
124      * <ul>
125      *   <li>{@code lst}, encoding the four states corresponding to {@link CopyHistory#getLastOperation()}</li>
126      *   <li>{@code u}, encoding {@link #isAddedByUses()}</li>
127      *   <li>{@code a}, encoding {@link #isAugmenting()}</li>
128      *   <li>{@code cct} encoding {@link #childCopyType()}</li>
129      * </ul>
130      * We still have two unused bits.
131      */
132     private final byte bitsAight;
133
134     // Note: this field can strictly be derived in InferredStatementContext, but it forms the basis of many of our
135     //       operations, hence we want to keep it close by.
136     private final @NonNull StatementDefinitionContext<A, D, E> definition;
137
138     // TODO: consider keying by Byte equivalent of ExecutionOrder
139     private Multimap<ModelProcessingPhase, OnPhaseFinished> phaseListeners = ImmutableMultimap.of();
140     private Multimap<ModelProcessingPhase, ContextMutation> phaseMutation = ImmutableMultimap.of();
141
142     private List<StmtContext<?, ?, ?>> effectOfStatement = ImmutableList.of();
143
144     /**
145      * {@link ModelProcessingPhase.ExecutionOrder} value of current {@link ModelProcessingPhase} of this statement.
146      */
147     private byte executionOrder;
148
149     // TODO: we a single byte of alignment shadow left, we should think how we can use it to cache information we build
150     //       during InferredStatementContext.tryToReusePrototype(). We usually end up being routed to
151     //       copyAsChildOfImpl() -- which performs an eager instantiation and checks for changes afterwards. We should
152     //       be able to capture how parent scope affects the copy in a few bits. If we can do that, than we can reap
153     //       the benefits by just examining new parent context and old parent context contribution to the state. If
154     //       their impact is the same, we can skip instantiation of statements and directly reuse them (individually,
155     //       or as a complete file).
156     //
157     //       Whatever we end up tracking, we need to track two views of that -- for the statement itself
158     //       (sans substatements) and a summary of substatements. I think it should be possible to get this working
159     //       with 2x5bits -- we have up to 15 mutable bits available if we share the field with implicitDeclaredFlag.
160
161     // Copy constructor used by subclasses to implement reparent()
162     StatementContextBase(final StatementContextBase<A, D, E> original) {
163         super(original);
164         this.bitsAight = original.bitsAight;
165         this.definition = original.definition;
166         this.executionOrder = original.executionOrder;
167     }
168
169     StatementContextBase(final StatementDefinitionContext<A, D, E> def) {
170         this.definition = requireNonNull(def);
171         this.bitsAight = COPY_ORIGINAL;
172     }
173
174     StatementContextBase(final StatementDefinitionContext<A, D, E> def, final CopyType copyType) {
175         this.definition = requireNonNull(def);
176         this.bitsAight = (byte) copyFlags(copyType);
177     }
178
179     StatementContextBase(final StatementContextBase<A, D, E> prototype, final CopyType copyType,
180             final CopyType childCopyType) {
181         this.definition = prototype.definition;
182         this.bitsAight = (byte) (copyFlags(copyType)
183             | prototype.bitsAight & ~COPY_LAST_TYPE_MASK | childCopyType.ordinal() << COPY_CHILD_TYPE_SHIFT);
184     }
185
186     private static int copyFlags(final CopyType copyType) {
187         return historyFlags(copyType) | copyType.ordinal();
188     }
189
190     private static byte historyFlags(final CopyType copyType) {
191         return switch (copyType) {
192             case ADDED_BY_AUGMENTATION -> COPY_ADDED_BY_AUGMENTATION;
193             case ADDED_BY_USES -> COPY_ADDED_BY_USES;
194             case ADDED_BY_USES_AUGMENTATION -> COPY_ADDED_BY_AUGMENTATION | COPY_ADDED_BY_USES;
195             case ORIGINAL -> COPY_ORIGINAL;
196         };
197     }
198
199     @Override
200     public final Collection<? extends StmtContext<?, ?, ?>> getEffectOfStatement() {
201         return effectOfStatement;
202     }
203
204     @Override
205     public final void addAsEffectOfStatement(final Collection<? extends StmtContext<?, ?, ?>> ctxs) {
206         if (ctxs.isEmpty()) {
207             return;
208         }
209
210         if (effectOfStatement.isEmpty()) {
211             effectOfStatement = new ArrayList<>(ctxs.size());
212         }
213         effectOfStatement.addAll(ctxs);
214     }
215
216     //
217     // CopyHistory integration
218     //
219
220     @Override
221     public final CopyHistory history() {
222         return this;
223     }
224
225     @Override
226     @Deprecated(since = "7.0.9", forRemoval = true)
227     public final boolean isAddedByUses() {
228         return (bitsAight & COPY_ADDED_BY_USES) != 0;
229     }
230
231     @Override
232     @Deprecated(since = "8.0.0")
233     public final boolean isAugmenting() {
234         return (bitsAight & COPY_ADDED_BY_AUGMENTATION) != 0;
235     }
236
237     @Override
238     public final CopyType getLastOperation() {
239         return COPY_TYPE_VALUES[bitsAight & COPY_LAST_TYPE_MASK];
240     }
241
242     // This method exists only for space optimization of InferredStatementContext
243     final CopyType childCopyType() {
244         return COPY_TYPE_VALUES[bitsAight >> COPY_CHILD_TYPE_SHIFT & COPY_LAST_TYPE_MASK];
245     }
246
247     //
248     // Inference completion tracking
249     //
250
251     @Override
252     final byte executionOrder() {
253         return executionOrder;
254     }
255
256     // FIXME: this should be propagated through a correct constructor
257     @Deprecated
258     final void setCompletedPhase(final ModelProcessingPhase completedPhase) {
259         executionOrder = completedPhase.executionOrder();
260     }
261
262     @Override
263     public final <K, V> void addToNs(final ParserNamespace<K, V> type, final K key, final V value) {
264         addToNamespace(type, key, value);
265     }
266
267     static final Collection<? extends Mutable<?, ?, ?>> mutableEffectiveSubstatements(
268             final List<ReactorStmtCtx<?, ?, ?>> effective) {
269         return effective instanceof ImmutableCollection ? effective : Collections.unmodifiableCollection(effective);
270     }
271
272     private static List<ReactorStmtCtx<?, ?, ?>> shrinkEffective(final List<ReactorStmtCtx<?, ?, ?>> effective) {
273         return effective.isEmpty() ? ImmutableList.of() : effective;
274     }
275
276     static final List<ReactorStmtCtx<?, ?, ?>> removeStatementFromEffectiveSubstatements(
277             final List<ReactorStmtCtx<?, ?, ?>> effective, final StatementDefinition statementDef) {
278         if (effective.isEmpty()) {
279             return effective;
280         }
281
282         final Iterator<? extends StmtContext<?, ?, ?>> iterator = effective.iterator();
283         while (iterator.hasNext()) {
284             final StmtContext<?, ?, ?> next = iterator.next();
285             if (statementDef.equals(next.publicDefinition())) {
286                 iterator.remove();
287             }
288         }
289
290         return shrinkEffective(effective);
291     }
292
293     static final List<ReactorStmtCtx<?, ?, ?>> removeStatementFromEffectiveSubstatements(
294             final List<ReactorStmtCtx<?, ?, ?>> effective, final StatementDefinition statementDef,
295             final String statementArg) {
296         if (statementArg == null) {
297             return removeStatementFromEffectiveSubstatements(effective, statementDef);
298         }
299
300         if (effective.isEmpty()) {
301             return effective;
302         }
303
304         final Iterator<ReactorStmtCtx<?, ?, ?>> iterator = effective.iterator();
305         while (iterator.hasNext()) {
306             final Mutable<?, ?, ?> next = iterator.next();
307             if (statementDef.equals(next.publicDefinition()) && statementArg.equals(next.rawArgument())) {
308                 iterator.remove();
309             }
310         }
311
312         return shrinkEffective(effective);
313     }
314
315     @Override
316     public final <X, Y extends DeclaredStatement<X>, Z extends EffectiveStatement<X, Y>>
317             Mutable<X, Y, Z> createUndeclaredSubstatement(final StatementSupport<X, Y, Z> support, final X arg) {
318         requireNonNull(support);
319         checkArgument(support instanceof UndeclaredStatementFactory, "Unsupported statement support %s", support);
320
321         final var ret = new UndeclaredStmtCtx<>(this, support, arg);
322         support.onStatementAdded(ret);
323         return ret;
324     }
325
326     final List<ReactorStmtCtx<?, ?, ?>> addEffectiveSubstatement(final List<ReactorStmtCtx<?, ?, ?>> effective,
327             final Mutable<?, ?, ?> substatement) {
328         final ReactorStmtCtx<?, ?, ?> stmt = verifyStatement(substatement);
329         final List<ReactorStmtCtx<?, ?, ?>> resized = beforeAddEffectiveStatement(effective, 1);
330         ensureCompletedExecution(stmt);
331         resized.add(stmt);
332         return resized;
333     }
334
335     static final void afterAddEffectiveSubstatement(final Mutable<?, ?, ?> substatement) {
336         // Undeclared statements still need to have 'onDeclarationFinished()' triggered
337         if (substatement instanceof UndeclaredStmtCtx<?, ?, ?> undeclared) {
338             finishDeclaration(undeclared);
339         }
340     }
341
342     // Split out to keep generics working without a warning
343     private static <X, Y extends DeclaredStatement<X>, Z extends EffectiveStatement<X, Y>> void finishDeclaration(
344             final @NonNull UndeclaredStmtCtx<X, Y, Z> substatement) {
345         substatement.definition().onDeclarationFinished(substatement, ModelProcessingPhase.FULL_DECLARATION);
346     }
347
348     @Override
349     public final void addEffectiveSubstatements(final Collection<? extends Mutable<?, ?, ?>> statements) {
350         if (!statements.isEmpty()) {
351             statements.forEach(StatementContextBase::verifyStatement);
352             addEffectiveSubstatementsImpl(statements);
353         }
354     }
355
356     abstract void addEffectiveSubstatementsImpl(Collection<? extends Mutable<?, ?, ?>> statements);
357
358     final List<ReactorStmtCtx<?, ?, ?>> addEffectiveSubstatementsImpl(final List<ReactorStmtCtx<?, ?, ?>> effective,
359             final Collection<? extends Mutable<?, ?, ?>> statements) {
360         final List<ReactorStmtCtx<?, ?, ?>> resized = beforeAddEffectiveStatement(effective, statements.size());
361         final Collection<? extends ReactorStmtCtx<?, ?, ?>> casted =
362             (Collection<? extends ReactorStmtCtx<?, ?, ?>>) statements;
363         if (executionOrder != ExecutionOrder.NULL) {
364             for (ReactorStmtCtx<?, ?, ?> stmt : casted) {
365                 ensureCompletedExecution(stmt, executionOrder);
366             }
367         }
368
369         resized.addAll(casted);
370         return resized;
371     }
372
373     abstract Iterator<ReactorStmtCtx<?, ?, ?>> effectiveChildrenToComplete();
374
375     // Make sure target statement has transitioned at least to our phase (if we have one). This method is just before we
376     // take allow a statement to become our substatement. This is needed to ensure that every statement tree does not
377     // contain any statements which did not complete the same phase as the root statement.
378     final void ensureCompletedExecution(final ReactorStmtCtx<?, ?, ?> stmt) {
379         if (executionOrder != ExecutionOrder.NULL) {
380             ensureCompletedExecution(stmt, executionOrder);
381         }
382     }
383
384     private static void ensureCompletedExecution(final ReactorStmtCtx<?, ?, ?> stmt, final byte executionOrder) {
385         verify(stmt.tryToCompletePhase(executionOrder), "Statement %s cannot complete phase %s", stmt, executionOrder);
386     }
387
388     // exposed for InferredStatementContext only
389     static final ReactorStmtCtx<?, ?, ?> verifyStatement(final Mutable<?, ?, ?> stmt) {
390         if (stmt instanceof ReactorStmtCtx<?, ?, ?> reactorStmt) {
391             return reactorStmt;
392         }
393         throw new VerifyException("Unexpected statement " + stmt);
394     }
395
396     private List<ReactorStmtCtx<?, ?, ?>> beforeAddEffectiveStatement(final List<ReactorStmtCtx<?, ?, ?>> effective,
397             final int toAdd) {
398         // We cannot allow statement to be further mutated.
399         // TODO: we really want to say 'not NULL and not at or after EFFECTIVE_MODEL here. This will matter if we have
400         //       a phase after EFFECTIVE_MODEL
401         verify(executionOrder != ExecutionOrder.EFFECTIVE_MODEL, "Cannot modify finished statement at %s",
402             sourceReference());
403         return beforeAddEffectiveStatementUnsafe(effective, toAdd);
404     }
405
406     final List<ReactorStmtCtx<?, ?, ?>> beforeAddEffectiveStatementUnsafe(final List<ReactorStmtCtx<?, ?, ?>> effective,
407             final int toAdd) {
408         final ModelProcessingPhase inProgressPhase = getRoot().getSourceContext().getInProgressPhase();
409         checkState(inProgressPhase == ModelProcessingPhase.FULL_DECLARATION
410                 || inProgressPhase == ModelProcessingPhase.EFFECTIVE_MODEL,
411                 "Effective statement cannot be added in declared phase at: %s", sourceReference());
412
413         return effective.isEmpty() ? new ArrayList<>(toAdd) : effective;
414     }
415
416     @Override
417     final E createEffective() {
418         final E result = createEffective(definition.getFactory());
419         if (result instanceof MutableStatement mutable) {
420             getRoot().addMutableStmtToSeal(mutable);
421         }
422         return result;
423     }
424
425     abstract @NonNull E createEffective(@NonNull StatementFactory<A, D, E> factory);
426
427     /**
428      * Return a stream of declared statements which can be built into an {@link EffectiveStatement}, as per
429      * {@link StmtContext#buildEffective()} contract.
430      *
431      * @return Stream of supported declared statements.
432      */
433     // FIXME: we really want to unify this with streamEffective(), under its name
434     abstract Stream<? extends @NonNull ReactorStmtCtx<?, ?, ?>> streamDeclared();
435
436     /**
437      * Return a stream of inferred statements which can be built into an {@link EffectiveStatement}, as per
438      * {@link StmtContext#buildEffective()} contract.
439      *
440      * @return Stream of supported effective statements.
441      */
442     // FIXME: this method is currently a misnomer, but unifying with streamDeclared() would make this accurate again
443     abstract @NonNull Stream<? extends @NonNull ReactorStmtCtx<?, ?, ?>> streamEffective();
444
445     @Override
446     final boolean doTryToCompletePhase(final byte targetOrder) {
447         final boolean finished = phaseMutation.isEmpty() || runMutations(targetOrder);
448         if (completeChildren(targetOrder) && finished) {
449             onPhaseCompleted(targetOrder);
450             return true;
451         }
452         return false;
453     }
454
455     private boolean completeChildren(final byte targetOrder) {
456         boolean finished = true;
457         for (final StatementContextBase<?, ?, ?> child : mutableDeclaredSubstatements()) {
458             finished &= child.tryToCompletePhase(targetOrder);
459         }
460         final var it = effectiveChildrenToComplete();
461         while (it.hasNext()) {
462             finished &= it.next().tryToCompletePhase(targetOrder);
463         }
464         return finished;
465     }
466
467     private boolean runMutations(final byte targetOrder) {
468         final ModelProcessingPhase phase = verifyNotNull(ModelProcessingPhase.ofExecutionOrder(targetOrder));
469         final Collection<ContextMutation> openMutations = phaseMutation.get(phase);
470         return openMutations.isEmpty() || runMutations(phase, openMutations);
471     }
472
473     private boolean runMutations(final ModelProcessingPhase phase, final Collection<ContextMutation> openMutations) {
474         boolean finished = true;
475         final Iterator<ContextMutation> it = openMutations.iterator();
476         while (it.hasNext()) {
477             final ContextMutation current = it.next();
478             if (current.isFinished()) {
479                 it.remove();
480             } else {
481                 finished = false;
482             }
483         }
484
485         if (openMutations.isEmpty()) {
486             phaseMutation.removeAll(phase);
487             cleanupPhaseMutation();
488         }
489         return finished;
490     }
491
492     private void cleanupPhaseMutation() {
493         if (phaseMutation.isEmpty()) {
494             phaseMutation = ImmutableMultimap.of();
495         }
496     }
497
498     /**
499      * Occurs on end of {@link ModelProcessingPhase} of source parsing. This method must not be called with
500      * {@code executionOrder} equal to {@link ExecutionOrder#NULL}.
501      *
502      * @param phase that was to be completed (finished)
503      * @throws SourceException when an error occurred in source parsing
504      */
505     private void onPhaseCompleted(final byte completedOrder) {
506         executionOrder = completedOrder;
507         if (completedOrder == ExecutionOrder.EFFECTIVE_MODEL) {
508             // We have completed effective model, substatements are guaranteed not to change
509             summarizeSubstatementPolicy();
510         }
511
512         final ModelProcessingPhase phase = verifyNotNull(ModelProcessingPhase.ofExecutionOrder(completedOrder));
513         final Collection<OnPhaseFinished> listeners = phaseListeners.get(phase);
514         if (!listeners.isEmpty()) {
515             runPhaseListeners(phase, listeners);
516         }
517     }
518
519     private void summarizeSubstatementPolicy() {
520         if (definition().support().copyPolicy() == CopyPolicy.EXACT_REPLICA || noSensitiveSubstatements()) {
521             setAllSubstatementsContextIndependent();
522         }
523     }
524
525     /**
526      * Determine whether any substatements are copy-sensitive as determined by {@link StatementSupport#copyPolicy()}.
527      * Only {@link CopyPolicy#CONTEXT_INDEPENDENT}, {@link CopyPolicy#EXACT_REPLICA} and {@link CopyPolicy#IGNORE} are
528      * copy-insensitive. Note that statements which are not {@link StmtContext#isSupportedToBuildEffective()} are all
529      * considered copy-insensitive.
530      *
531      * <p>
532      * Implementations are expected to call {@link #noSensitiveSubstatements()} to actually traverse substatement sets.
533      *
534      * @return True if no substatements require copy-sensitive handling
535      */
536     abstract boolean noSensitiveSubstatements();
537
538     /**
539      * Determine whether any of the provided substatements are context-sensitive for purposes of implementing
540      * {@link #noSensitiveSubstatements()}.
541      *
542      * @param substatements Substatements to check
543      * @return True if no substatements require context-sensitive handling
544      */
545     static boolean noSensitiveSubstatements(final Collection<? extends ReactorStmtCtx<?, ?, ?>> substatements) {
546         for (ReactorStmtCtx<?, ?, ?> stmt : substatements) {
547             if (stmt.isSupportedToBuildEffective()) {
548                 if (!stmt.allSubstatementsContextIndependent()) {
549                     // This is a recursive property
550                     return false;
551                 }
552
553                 switch (stmt.definition().support().copyPolicy()) {
554                     case CONTEXT_INDEPENDENT:
555                     case EXACT_REPLICA:
556                     case IGNORE:
557                         break;
558                     default:
559                         return false;
560                 }
561             }
562         }
563         return true;
564     }
565
566     private void runPhaseListeners(final ModelProcessingPhase phase, final Collection<OnPhaseFinished> listeners) {
567         final Iterator<OnPhaseFinished> listener = listeners.iterator();
568         while (listener.hasNext()) {
569             final OnPhaseFinished next = listener.next();
570             if (next.phaseFinished(this, phase)) {
571                 listener.remove();
572             }
573         }
574
575         if (listeners.isEmpty()) {
576             phaseListeners.removeAll(phase);
577             if (phaseListeners.isEmpty()) {
578                 phaseListeners = ImmutableMultimap.of();
579             }
580         }
581     }
582
583     @Override
584     final StatementDefinitionContext<A, D, E> definition() {
585         return definition;
586     }
587
588     final <K, V> void onNamespaceItemAddedAction(final ParserNamespace<K, V> namespace, final K key,
589             final OnNamespaceItemAdded listener) {
590         final var access = accessNamespace(namespace);
591         final var potential = access.valueFrom(this, key);
592         if (potential != null) {
593             LOG.trace("Listener on {} key {} satisfied immediately", namespace, key);
594             listener.namespaceItemAdded(this, namespace, key, potential);
595             return;
596         }
597
598         access.addListener(key, new KeyedValueAddedListener<>(this) {
599             @Override
600             void onValueAdded(final K key, final V value) {
601                 listener.namespaceItemAdded(StatementContextBase.this, namespace, key, value);
602             }
603         });
604     }
605
606     final <K, V> void onNamespaceItemAddedAction(final ParserNamespace<K, V> namespace,
607             final ModelProcessingPhase phase, final NamespaceKeyCriterion<K> criterion,
608             final OnNamespaceItemAdded listener) {
609         final var access = accessNamespace(namespace);
610         final var entry = access.entryFrom(this, criterion);
611         if (entry != null) {
612             LOG.debug("Listener on {} criterion {} found a pre-existing match: {}", namespace, criterion, entry);
613             waitForPhase(entry.getValue(), access, phase, criterion, listener);
614             return;
615         }
616
617         access.addListener((key, value) -> {
618             if (criterion.match(key)) {
619                 LOG.debug("Listener on {} criterion {} matched added key {}", namespace, criterion, key);
620                 waitForPhase(value, access, phase, criterion, listener);
621                 return true;
622             }
623             return false;
624         });
625     }
626
627     private <K, V> void waitForPhase(final Object value, final NamespaceAccess<K, V> access,
628             final ModelProcessingPhase phase, final NamespaceKeyCriterion<K> criterion,
629             final OnNamespaceItemAdded listener) {
630         ((StatementContextBase<?, ?, ?>) value).addPhaseCompletedListener(phase, (context, phaseCompleted) -> {
631             final var match = access.entryFrom(this, criterion);
632             if (match == null) {
633                 throw new IllegalStateException("Failed to find a match for criterion %s in namespace %s node %s"
634                     .formatted(criterion, access.namespace(), this));
635             }
636             listener.namespaceItemAdded(this, access.namespace(), match.getKey(), match.getValue());
637             return true;
638         });
639     }
640
641     private static <T> Multimap<ModelProcessingPhase, T> newMultimap() {
642         return Multimaps.newListMultimap(new EnumMap<>(ModelProcessingPhase.class), () -> new ArrayList<>(1));
643     }
644
645     /**
646      * Adds {@link OnPhaseFinished} listener for a {@link ModelProcessingPhase} end. If the base has already completed
647      * the listener is notified immediately.
648      *
649      * @param phase requested completion phase
650      * @param listener listener to invoke
651      * @throws NullPointerException if any of the arguments is null
652      */
653     void addPhaseCompletedListener(final ModelProcessingPhase phase, final OnPhaseFinished listener) {
654         requireNonNull(phase, "Statement context processing phase cannot be null");
655         requireNonNull(listener, "Statement context phase listener cannot be null");
656
657         ModelProcessingPhase finishedPhase = ModelProcessingPhase.ofExecutionOrder(executionOrder);
658         while (finishedPhase != null) {
659             if (phase.equals(finishedPhase)) {
660                 listener.phaseFinished(this, finishedPhase);
661                 return;
662             }
663             finishedPhase = finishedPhase.getPreviousPhase();
664         }
665         if (phaseListeners.isEmpty()) {
666             phaseListeners = newMultimap();
667         }
668
669         phaseListeners.put(phase, listener);
670     }
671
672     /**
673      * Adds a {@link ContextMutation} to a {@link ModelProcessingPhase}.
674      *
675      * @throws IllegalStateException when the mutation was registered after phase was completed
676      */
677     final void addMutation(final ModelProcessingPhase phase, final ContextMutation mutation) {
678         checkState(executionOrder < phase.executionOrder(), "Mutation registered after phase was completed at: %s",
679             sourceReference());
680
681         if (phaseMutation.isEmpty()) {
682             phaseMutation = newMultimap();
683         }
684         phaseMutation.put(phase, mutation);
685     }
686
687     final void removeMutation(final ModelProcessingPhase phase, final ContextMutation mutation) {
688         if (!phaseMutation.isEmpty()) {
689             phaseMutation.remove(phase, mutation);
690             cleanupPhaseMutation();
691         }
692     }
693
694     @Override
695     public final Optional<Mutable<A, D, E>> copyAsChildOf(final Mutable<?, ?, ?> parent, final CopyType type,
696             final QNameModule targetModule) {
697         checkEffectiveModelCompleted(this);
698         return Optional.ofNullable(copyAsChildOfImpl(parent, type, targetModule));
699     }
700
701     private @Nullable ReactorStmtCtx<A, D, E> copyAsChildOfImpl(final Mutable<?, ?, ?> parent, final CopyType type,
702             final QNameModule targetModule) {
703         final StatementSupport<A, D, E> support = definition.support();
704         final CopyPolicy policy = support.copyPolicy();
705         switch (policy) {
706             case EXACT_REPLICA:
707                 return replicaAsChildOf(parent);
708             case CONTEXT_INDEPENDENT:
709                 if (allSubstatementsContextIndependent()) {
710                     return replicaAsChildOf(parent);
711                 }
712
713                 // fall through
714             case DECLARED_COPY:
715                 // FIXME: ugly cast
716                 return (ReactorStmtCtx<A, D, E>) parent.childCopyOf(this, type, targetModule);
717             case IGNORE:
718                 return null;
719             case REJECT:
720                 throw new IllegalStateException("Statement " + support.getPublicView() + " should never be copied");
721             default:
722                 throw new IllegalStateException("Unhandled policy " + policy);
723         }
724     }
725
726     @Override
727     final ReactorStmtCtx<?, ?, ?> asEffectiveChildOf(final StatementContextBase<?, ?, ?> parent, final CopyType type,
728             final QNameModule targetModule) {
729         if (!isSupportedToBuildEffective() || !isSupportedByFeatures()) {
730             // Do not create effective copies, as they cannot be built anyway
731             return null;
732         }
733
734         final var copy = copyAsChildOfImpl(parent, type, targetModule);
735         if (copy == null) {
736             // The statement fizzled, this should never happen, perhaps a verify()?
737             return null;
738         }
739
740         parent.ensureCompletedExecution(copy);
741         return canReuseCurrent(copy) ? this : copy;
742     }
743
744     private boolean canReuseCurrent(final @NonNull ReactorStmtCtx<A, D, E> copy) {
745         // Defer to statement factory to see if we can reuse this object. If we can and have only context-independent
746         // substatements we can reuse the object. More complex cases are handled indirectly via the copy.
747         return definition.getFactory().canReuseCurrent(copy, this, buildEffective().effectiveSubstatements())
748             && allSubstatementsContextIndependent();
749     }
750
751     @Override
752     public final Mutable<?, ?, ?> childCopyOf(final StmtContext<?, ?, ?> stmt, final CopyType type,
753             final QNameModule targetModule) {
754         checkEffectiveModelCompleted(stmt);
755         if (stmt instanceof StatementContextBase<?, ?, ?> base) {
756             return childCopyOf(base, type, targetModule);
757         } else if (stmt instanceof ReplicaStatementContext<?, ?, ?> replica) {
758             return replica.replicaAsChildOf(this);
759         } else {
760             throw new IllegalArgumentException("Unsupported statement " + stmt);
761         }
762     }
763
764     private <X, Y extends DeclaredStatement<X>, Z extends EffectiveStatement<X, Y>> Mutable<X, Y, Z> childCopyOf(
765             final StatementContextBase<X, Y, Z> original, final CopyType type, final QNameModule targetModule) {
766         final var implicitParent = definition.getImplicitParentFor(this, original.publicDefinition());
767
768         final StatementContextBase<X, Y, Z> result;
769         final InferredStatementContext<X, Y, Z> copy;
770
771         if (implicitParent.isPresent()) {
772             result = new UndeclaredStmtCtx(this, implicitParent.orElseThrow(), original, type);
773
774             final CopyType childCopyType = switch (type) {
775                 case ADDED_BY_AUGMENTATION -> CopyType.ORIGINAL;
776                 case ADDED_BY_USES_AUGMENTATION -> CopyType.ADDED_BY_USES;
777                 case ADDED_BY_USES, ORIGINAL -> type;
778             };
779             copy = new InferredStatementContext<>(result, original, childCopyType, type, targetModule);
780             result.addEffectiveSubstatement(copy);
781             result.definition.onStatementAdded(result);
782         } else {
783             result = copy = new InferredStatementContext<>(this, original, type, type, targetModule);
784         }
785
786         original.definition.onStatementAdded(copy);
787         return result;
788     }
789
790     @Override
791     final ReplicaStatementContext<A, D, E> replicaAsChildOf(final StatementContextBase<?, ?, ?> parent) {
792         return new ReplicaStatementContext<>(parent, this);
793     }
794
795     private static void checkEffectiveModelCompleted(final StmtContext<?, ?, ?> stmt) {
796         final ModelProcessingPhase phase = stmt.getCompletedPhase();
797         checkState(phase == ModelProcessingPhase.EFFECTIVE_MODEL,
798                 "Attempted to copy statement %s which has completed phase %s", stmt, phase);
799     }
800
801     @Override
802     public final boolean hasImplicitParentSupport() {
803         return definition.getFactory() instanceof ImplicitParentAwareStatementSupport;
804     }
805
806     @Override
807     public final StmtContext<?, ?, ?> wrapWithImplicit(final StmtContext<?, ?, ?> original) {
808         final var optImplicit = definition.getImplicitParentFor(this, original.publicDefinition());
809         if (optImplicit.isEmpty()) {
810             return original;
811         }
812         if (original instanceof StatementContextBase<?, ?, ?> origBase) {
813             final var result = new UndeclaredStmtCtx<>(origBase, optImplicit.orElseThrow());
814             result.addEffectiveSubstatement(origBase.reparent(result));
815             result.setCompletedPhase(original.getCompletedPhase());
816             return result;
817         }
818         throw new IllegalArgumentException("Unsupported original " + original);
819     }
820
821     abstract StatementContextBase<A, D, E> reparent(StatementContextBase<?, ?, ?> newParent);
822
823     /**
824      * Indicate that the set of substatements is empty. This is a preferred shortcut to substatement stream filtering.
825      *
826      * @return True if {@link #allSubstatements()} and {@link #allSubstatementsStream()} would return an empty stream.
827      */
828     abstract boolean hasEmptySubstatements();
829 }