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