2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.parser.stmt.reactor;
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;
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;
57 * Core reactor statement implementation of {@link Mutable}.
59 * @param <A> Argument type
60 * @param <D> Declared Statement representation
61 * @param <E> Effective Statement representation
63 abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
64 extends ReactorStmtCtx<A, D, E> implements CopyHistory {
66 * Event listener when an item is added to model namespace.
68 interface OnNamespaceItemAdded extends EventListener {
70 * Invoked whenever a new item is added to a namespace.
72 void namespaceItemAdded(StatementContextBase<?, ?, ?> context, ParserNamespace<?, ?> namespace, Object key,
77 * Event listener when a parsing {@link ModelProcessingPhase} is completed.
79 interface OnPhaseFinished extends EventListener {
81 * Invoked whenever a processing phase has finished.
83 boolean phaseFinished(StatementContextBase<?, ?, ?> context, ModelProcessingPhase finishedPhase);
87 * Interface for all mutations within an {@link ModelActionBuilder.InferenceAction}.
89 interface ContextMutation {
94 private static final Logger LOG = LoggerFactory.getLogger(StatementContextBase.class);
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;
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;
106 private static final CopyType @NonNull [] COPY_TYPE_VALUES = CopyType.values();
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);
115 * 8 bits worth of instance storage. This is treated as a constant bit field with following structure:
124 * The four allocated fields are:
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>
131 * We still have two unused bits.
133 private final byte bitsAight;
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;
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();
143 private List<StmtContext<?, ?, ?>> effectOfStatement = ImmutableList.of();
146 * {@link ModelProcessingPhase.ExecutionOrder} value of current {@link ModelProcessingPhase} of this statement.
148 private byte executionOrder;
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).
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.
162 // Copy constructor used by subclasses to implement reparent()
163 StatementContextBase(final StatementContextBase<A, D, E> original) {
165 this.bitsAight = original.bitsAight;
166 this.definition = original.definition;
167 this.executionOrder = original.executionOrder;
170 StatementContextBase(final StatementDefinitionContext<A, D, E> def) {
171 this.definition = requireNonNull(def);
172 this.bitsAight = COPY_ORIGINAL;
175 StatementContextBase(final StatementDefinitionContext<A, D, E> def, final CopyType copyType) {
176 this.definition = requireNonNull(def);
177 this.bitsAight = (byte) copyFlags(copyType);
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);
187 private static int copyFlags(final CopyType copyType) {
188 return historyFlags(copyType) | copyType.ordinal();
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;
201 public final Collection<? extends StmtContext<?, ?, ?>> getEffectOfStatement() {
202 return effectOfStatement;
206 public final void addAsEffectOfStatement(final Collection<? extends StmtContext<?, ?, ?>> ctxs) {
207 if (ctxs.isEmpty()) {
211 if (effectOfStatement.isEmpty()) {
212 effectOfStatement = new ArrayList<>(ctxs.size());
214 effectOfStatement.addAll(ctxs);
218 // CopyHistory integration
222 public final CopyHistory history() {
227 @Deprecated(since = "7.0.9", forRemoval = true)
228 public final boolean isAddedByUses() {
229 return (bitsAight & COPY_ADDED_BY_USES) != 0;
233 @Deprecated(since = "8.0.0")
234 public final boolean isAugmenting() {
235 return (bitsAight & COPY_ADDED_BY_AUGMENTATION) != 0;
239 public final CopyType getLastOperation() {
240 return COPY_TYPE_VALUES[bitsAight & COPY_LAST_TYPE_MASK];
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];
249 // Inference completion tracking
253 final byte executionOrder() {
254 return executionOrder;
257 // FIXME: this should be propagated through a correct constructor
259 final void setCompletedPhase(final ModelProcessingPhase completedPhase) {
260 executionOrder = completedPhase.executionOrder();
264 public final <K, V> void addToNs(final ParserNamespace<K, V> type, final K key, final V value) {
265 addToNamespace(type, key, value);
268 static final Collection<? extends Mutable<?, ?, ?>> mutableEffectiveSubstatements(
269 final List<ReactorStmtCtx<?, ?, ?>> effective) {
270 return effective instanceof ImmutableCollection ? effective : Collections.unmodifiableCollection(effective);
273 private static List<ReactorStmtCtx<?, ?, ?>> shrinkEffective(final List<ReactorStmtCtx<?, ?, ?>> effective) {
274 return effective.isEmpty() ? ImmutableList.of() : effective;
277 static final List<ReactorStmtCtx<?, ?, ?>> removeStatementFromEffectiveSubstatements(
278 final List<ReactorStmtCtx<?, ?, ?>> effective, final StatementDefinition statementDef) {
279 if (effective.isEmpty()) {
283 final Iterator<? extends StmtContext<?, ?, ?>> iterator = effective.iterator();
284 while (iterator.hasNext()) {
285 final StmtContext<?, ?, ?> next = iterator.next();
286 if (statementDef.equals(next.publicDefinition())) {
291 return shrinkEffective(effective);
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);
301 if (effective.isEmpty()) {
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())) {
313 return shrinkEffective(effective);
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);
322 final var ret = new UndeclaredStmtCtx<>(this, support, arg);
323 support.onStatementAdded(ret);
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);
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);
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);
350 public final void addEffectiveSubstatements(final Collection<? extends Mutable<?, ?, ?>> statements) {
351 if (!statements.isEmpty()) {
352 statements.forEach(StatementContextBase::verifyStatement);
353 addEffectiveSubstatementsImpl(statements);
357 abstract void addEffectiveSubstatementsImpl(Collection<? extends Mutable<?, ?, ?>> statements);
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);
370 resized.addAll(casted);
374 abstract Iterator<ReactorStmtCtx<?, ?, ?>> effectiveChildrenToComplete();
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);
385 private static void ensureCompletedExecution(final ReactorStmtCtx<?, ?, ?> stmt, final byte executionOrder) {
386 verify(stmt.tryToCompletePhase(executionOrder), "Statement %s cannot complete phase %s", stmt, executionOrder);
389 // exposed for InferredStatementContext only
390 static final ReactorStmtCtx<?, ?, ?> verifyStatement(final Mutable<?, ?, ?> stmt) {
391 if (stmt instanceof ReactorStmtCtx<?, ?, ?> reactorStmt) {
394 throw new VerifyException("Unexpected statement " + stmt);
397 private List<ReactorStmtCtx<?, ?, ?>> beforeAddEffectiveStatement(final List<ReactorStmtCtx<?, ?, ?>> effective,
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",
404 return beforeAddEffectiveStatementUnsafe(effective, toAdd);
407 final List<ReactorStmtCtx<?, ?, ?>> beforeAddEffectiveStatementUnsafe(final List<ReactorStmtCtx<?, ?, ?>> effective,
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());
414 return effective.isEmpty() ? new ArrayList<>(toAdd) : effective;
418 final E createEffective() {
419 final E result = createEffective(definition.getFactory());
420 if (result instanceof MutableStatement mutable) {
421 getRoot().addMutableStmtToSeal(mutable);
426 abstract @NonNull E createEffective(@NonNull StatementFactory<A, D, E> factory);
429 * Return a stream of declared statements which can be built into an {@link EffectiveStatement}, as per
430 * {@link StmtContext#buildEffective()} contract.
432 * @return Stream of supported declared statements.
434 // FIXME: we really want to unify this with streamEffective(), under its name
435 abstract Stream<? extends @NonNull ReactorStmtCtx<?, ?, ?>> streamDeclared();
438 * Return a stream of inferred statements which can be built into an {@link EffectiveStatement}, as per
439 * {@link StmtContext#buildEffective()} contract.
441 * @return Stream of supported effective statements.
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();
447 final boolean doTryToCompletePhase(final byte targetOrder) {
448 final boolean finished = phaseMutation.isEmpty() || runMutations(targetOrder);
449 if (completeChildren(targetOrder) && finished) {
450 onPhaseCompleted(targetOrder);
456 private boolean completeChildren(final byte targetOrder) {
457 boolean finished = true;
458 for (final StatementContextBase<?, ?, ?> child : mutableDeclaredSubstatements()) {
459 finished &= child.tryToCompletePhase(targetOrder);
461 final var it = effectiveChildrenToComplete();
462 while (it.hasNext()) {
463 finished &= it.next().tryToCompletePhase(targetOrder);
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);
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()) {
486 if (openMutations.isEmpty()) {
487 phaseMutation.removeAll(phase);
488 cleanupPhaseMutation();
493 private void cleanupPhaseMutation() {
494 if (phaseMutation.isEmpty()) {
495 phaseMutation = ImmutableMultimap.of();
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}.
503 * @param phase that was to be completed (finished)
504 * @throws SourceException when an error occurred in source parsing
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();
513 final ModelProcessingPhase phase = verifyNotNull(ModelProcessingPhase.ofExecutionOrder(completedOrder));
514 final Collection<OnPhaseFinished> listeners = phaseListeners.get(phase);
515 if (!listeners.isEmpty()) {
516 runPhaseListeners(phase, listeners);
520 private void summarizeSubstatementPolicy() {
521 if (definition().support().copyPolicy() == CopyPolicy.EXACT_REPLICA || noSensitiveSubstatements()) {
522 setAllSubstatementsContextIndependent();
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.
533 * Implementations are expected to call {@link #noSensitiveSubstatements()} to actually traverse substatement sets.
535 * @return True if no substatements require copy-sensitive handling
537 abstract boolean noSensitiveSubstatements();
540 * Determine whether any of the provided substatements are context-sensitive for purposes of implementing
541 * {@link #noSensitiveSubstatements()}.
543 * @param substatements Substatements to check
544 * @return True if no substatements require context-sensitive handling
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
554 switch (stmt.definition().support().copyPolicy()) {
555 case CONTEXT_INDEPENDENT:
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)) {
576 if (listeners.isEmpty()) {
577 phaseListeners.removeAll(phase);
578 if (phaseListeners.isEmpty()) {
579 phaseListeners = ImmutableMultimap.of();
585 final StatementDefinitionContext<A, D, E> definition() {
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);
599 access.addListener(key, new KeyedValueAddedListener<>(this) {
601 void onValueAdded(final K key, final V value) {
602 listener.namespaceItemAdded(StatementContextBase.this, namespace, key, value);
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);
613 LOG.debug("Listener on {} criterion {} found a pre-existing match: {}", namespace, criterion, entry);
614 waitForPhase(entry.getValue(), access, phase, criterion, listener);
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);
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);
634 throw new IllegalStateException("Failed to find a match for criterion %s in namespace %s node %s"
635 .formatted(criterion, access.namespace(), this));
637 listener.namespaceItemAdded(this, access.namespace(), match.getKey(), match.getValue());
642 private static <T> Multimap<ModelProcessingPhase, T> newMultimap() {
643 return Multimaps.newListMultimap(new EnumMap<>(ModelProcessingPhase.class), () -> new ArrayList<>(1));
647 * Adds {@link OnPhaseFinished} listener for a {@link ModelProcessingPhase} end. If the base has already completed
648 * the listener is notified immediately.
650 * @param phase requested completion phase
651 * @param listener listener to invoke
652 * @throws NullPointerException if any of the arguments is null
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");
658 ModelProcessingPhase finishedPhase = ModelProcessingPhase.ofExecutionOrder(executionOrder);
659 while (finishedPhase != null) {
660 if (phase.equals(finishedPhase)) {
661 listener.phaseFinished(this, finishedPhase);
664 finishedPhase = finishedPhase.getPreviousPhase();
666 if (phaseListeners.isEmpty()) {
667 phaseListeners = newMultimap();
670 phaseListeners.put(phase, listener);
674 * Adds a {@link ContextMutation} to a {@link ModelProcessingPhase}.
676 * @throws IllegalStateException when the mutation was registered after phase was completed
678 final void addMutation(final ModelProcessingPhase phase, final ContextMutation mutation) {
679 checkState(executionOrder < phase.executionOrder(), "Mutation registered after phase was completed at: %s",
682 if (phaseMutation.isEmpty()) {
683 phaseMutation = newMultimap();
685 phaseMutation.put(phase, mutation);
688 final void removeMutation(final ModelProcessingPhase phase, final ContextMutation mutation) {
689 if (!phaseMutation.isEmpty()) {
690 phaseMutation.remove(phase, mutation);
691 cleanupPhaseMutation();
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));
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();
708 return replicaAsChildOf(parent);
709 case CONTEXT_INDEPENDENT:
710 if (allSubstatementsContextIndependent()) {
711 return replicaAsChildOf(parent);
717 return (ReactorStmtCtx<A, D, E>) parent.childCopyOf(this, type, targetModule);
721 throw new IllegalStateException("Statement " + support.getPublicView() + " should never be copied");
723 throw new IllegalStateException("Unhandled policy " + policy);
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);
732 // The statement fizzled, this should never happen, perhaps a verify()?
736 parent.ensureCompletedExecution(copy);
737 return canReuseCurrent(copy) ? this : copy;
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();
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);
756 throw new IllegalArgumentException("Unsupported statement " + stmt);
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());
764 final StatementContextBase<X, Y, Z> result;
765 final InferredStatementContext<X, Y, Z> copy;
767 if (implicitParent.isPresent()) {
768 result = new UndeclaredStmtCtx(this, implicitParent.orElseThrow(), original, type);
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;
775 copy = new InferredStatementContext<>(result, original, childCopyType, type, targetModule);
776 result.addEffectiveSubstatement(copy);
777 result.definition.onStatementAdded(result);
779 result = copy = new InferredStatementContext<>(this, original, type, type, targetModule);
782 original.definition.onStatementAdded(copy);
787 final ReplicaStatementContext<A, D, E> replicaAsChildOf(final StatementContextBase<?, ?, ?> parent) {
788 return new ReplicaStatementContext<>(parent, this);
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);
798 public final boolean hasImplicitParentSupport() {
799 return definition.getFactory() instanceof ImplicitParentAwareStatementSupport;
803 public final StmtContext<?, ?, ?> wrapWithImplicit(final StmtContext<?, ?, ?> original) {
804 final var optImplicit = definition.getImplicitParentFor(this, original.publicDefinition());
805 if (optImplicit.isEmpty()) {
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());
814 throw new IllegalArgumentException("Unsupported original " + original);
817 abstract StatementContextBase<A, D, E> reparent(StatementContextBase<?, ?, ?> newParent);
820 * Indicate that the set of substatements is empty. This is a preferred shortcut to substatement stream filtering.
822 * @return True if {@link #allSubstatements()} and {@link #allSubstatementsStream()} would return an empty stream.
824 abstract boolean hasEmptySubstatements();