Remove unused constructor
[yangtools.git] / yang / yang-parser-reactor / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / StatementContextBase.java
index 7ccad2490fb9955d9840aee4106436af6e0349b3..65656d4efa5ba85c3e4541b6cf033756f4170547 100644 (file)
@@ -8,12 +8,13 @@
 package org.opendaylight.yangtools.yang.parser.stmt.reactor;
 
 import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.base.Verify.verify;
+import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.Beta;
+import com.google.common.base.VerifyException;
 import com.google.common.collect.ImmutableCollection;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMultimap;
@@ -30,7 +31,6 @@ import java.util.Map.Entry;
 import java.util.Optional;
 import java.util.stream.Stream;
 import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
@@ -40,6 +40,7 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ImplicitParentAwareStatementSupport;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.ExecutionOrder;
 import org.opendaylight.yangtools.yang.parser.spi.meta.MutableStatement;
 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceKeyCriterion;
@@ -64,7 +65,7 @@ import org.slf4j.LoggerFactory;
  * @param <E> Effective Statement representation
  */
 public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
-        extends ReactorStmtCtx<A, D, E> {
+        extends ReactorStmtCtx<A, D, E> implements CopyHistory {
     /**
      * Event listener when an item is added to model namespace.
      */
@@ -95,34 +96,77 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
 
     private static final Logger LOG = LoggerFactory.getLogger(StatementContextBase.class);
 
-    private final CopyHistory copyHistory;
+    //
+    // {@link CopyHistory} encoded as a single byte. We still have 4 bits unused.
+    //
+    private static final byte COPY_LAST_TYPE_MASK        = 0x03;
+    private static final byte COPY_ADDED_BY_USES         = 0x04;
+    private static final byte COPY_ADDED_BY_AUGMENTATION = 0x08;
+    private static final byte COPY_ORIGINAL              = 0x00;
+
+    private final byte copyHistory;
+
+    static {
+        final int copyTypes = CopyType.values().length;
+        // This implies CopyType.ordinal() is <= COPY_TYPE_MASK
+        verify(copyTypes == COPY_LAST_TYPE_MASK + 1, "Unexpected %s CopyType values", copyTypes);
+    }
+
     // Note: this field can strictly be derived in InferredStatementContext, but it forms the basis of many of our
     //       operations, hence we want to keep it close by.
     private final @NonNull StatementDefinitionContext<A, D, E> definition;
 
+    // TODO: consider keying by Byte equivalent of ExecutionOrder
     private Multimap<ModelProcessingPhase, OnPhaseFinished> phaseListeners = ImmutableMultimap.of();
     private Multimap<ModelProcessingPhase, ContextMutation> phaseMutation = ImmutableMultimap.of();
 
     private List<StmtContext<?, ?, ?>> effectOfStatement = ImmutableList.of();
 
-    private @Nullable ModelProcessingPhase completedPhase;
+    /**
+     * {@link ModelProcessingPhase.ExecutionOrder} value of current {@link ModelProcessingPhase} of this statement.
+     */
+    private byte executionOrder;
 
     // Copy constructor used by subclasses to implement reparent()
     StatementContextBase(final StatementContextBase<A, D, E> original) {
         super(original);
         this.copyHistory = original.copyHistory;
         this.definition = original.definition;
-        this.completedPhase = original.completedPhase;
+        this.executionOrder = original.executionOrder;
     }
 
     StatementContextBase(final StatementDefinitionContext<A, D, E> def) {
         this.definition = requireNonNull(def);
-        this.copyHistory = CopyHistory.original();
+        this.copyHistory = COPY_ORIGINAL;
     }
 
-    StatementContextBase(final StatementDefinitionContext<A, D, E> def, final CopyHistory copyHistory) {
+    StatementContextBase(final StatementDefinitionContext<A, D, E> def, final CopyType copyType) {
         this.definition = requireNonNull(def);
-        this.copyHistory = requireNonNull(copyHistory);
+        this.copyHistory = (byte) copyFlags(copyType);
+    }
+
+    StatementContextBase(final StatementContextBase<A, D, E> prototype, final CopyType copyType) {
+        this.definition = prototype.definition;
+        this.copyHistory = (byte) (copyFlags(copyType) | prototype.copyHistory & ~COPY_LAST_TYPE_MASK);
+    }
+
+    private static int copyFlags(final CopyType copyType) {
+        return historyFlags(copyType) | copyType.ordinal();
+    }
+
+    private static byte historyFlags(final CopyType copyType) {
+        switch (copyType) {
+            case ADDED_BY_AUGMENTATION:
+                return COPY_ADDED_BY_AUGMENTATION;
+            case ADDED_BY_USES:
+                return COPY_ADDED_BY_USES;
+            case ADDED_BY_USES_AUGMENTATION:
+                return COPY_ADDED_BY_AUGMENTATION | COPY_ADDED_BY_USES;
+            case ORIGINAL:
+                return COPY_ORIGINAL;
+            default:
+                throw new VerifyException("Unhandled type " + copyType);
+        }
     }
 
     @Override
@@ -142,20 +186,43 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
         effectOfStatement.addAll(ctxs);
     }
 
+    //
+    // CopyHistory integration
+    //
+
     @Override
     public final CopyHistory history() {
-        return copyHistory;
+        return this;
+    }
+
+    @Override
+    public final boolean isAddedByUses() {
+        return (copyHistory & COPY_ADDED_BY_USES) != 0;
+    }
+
+    @Override
+    public final boolean isAugmenting() {
+        return (copyHistory & COPY_ADDED_BY_AUGMENTATION) != 0;
+    }
+
+    @Override
+    public final CopyType getLastOperation() {
+        return CopyType.values()[copyHistory & COPY_LAST_TYPE_MASK];
     }
 
+    //
+    // Inference completion tracking
+    //
+
     @Override
-    public final ModelProcessingPhase getCompletedPhase() {
-        return completedPhase;
+    final byte executionOrder() {
+        return executionOrder;
     }
 
     // FIXME: this should be propagated through a correct constructor
     @Deprecated
     final void setCompletedPhase(final ModelProcessingPhase completedPhase) {
-        this.completedPhase = completedPhase;
+        this.executionOrder = completedPhase.executionOrder();
     }
 
     @Override
@@ -257,10 +324,7 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
 
         final List<ReactorStmtCtx<?, ?, ?>> resized = beforeAddEffectiveStatement(effective, 1);
         final ReactorStmtCtx<?, ?, ?> stmt = (ReactorStmtCtx<?, ?, ?>) substatement;
-        final ModelProcessingPhase phase = completedPhase;
-        if (phase != null) {
-            ensureCompletedPhase(stmt, phase);
-        }
+        ensureCompletedExecution(stmt);
         resized.add(stmt);
         return resized;
     }
@@ -286,12 +350,11 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
     final List<ReactorStmtCtx<?, ?, ?>> addEffectiveSubstatementsImpl(final List<ReactorStmtCtx<?, ?, ?>> effective,
             final Collection<? extends Mutable<?, ?, ?>> statements) {
         final List<ReactorStmtCtx<?, ?, ?>> resized = beforeAddEffectiveStatement(effective, statements.size());
-        final Collection<? extends StatementContextBase<?, ?, ?>> casted =
-            (Collection<? extends StatementContextBase<?, ?, ?>>) statements;
-        final ModelProcessingPhase phase = completedPhase;
-        if (phase != null) {
-            for (StatementContextBase<?, ?, ?> stmt : casted) {
-                ensureCompletedPhase(stmt, phase);
+        final Collection<? extends ReactorStmtCtx<?, ?, ?>> casted =
+            (Collection<? extends ReactorStmtCtx<?, ?, ?>>) statements;
+        if (executionOrder != ExecutionOrder.NULL) {
+            for (ReactorStmtCtx<?, ?, ?> stmt : casted) {
+                ensureCompletedExecution(stmt, executionOrder);
             }
         }
 
@@ -304,17 +367,20 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
     // exposed for InferredStatementContext only
     final void ensureCompletedPhase(final Mutable<?, ?, ?> stmt) {
         verifyStatement(stmt);
-        final ModelProcessingPhase phase = completedPhase;
-        if (phase != null) {
-            ensureCompletedPhase((ReactorStmtCtx<?, ?, ?>) stmt, phase);
+        ensureCompletedExecution((ReactorStmtCtx<?, ?, ?>) stmt);
+    }
+
+    // Make sure target statement has transitioned at least to our phase (if we have one). This method is just before we
+    // take allow a statement to become our substatement. This is needed to ensure that every statement tree does not
+    // contain any statements which did not complete the same phase as the root statement.
+    private void ensureCompletedExecution(final ReactorStmtCtx<?, ?, ?> stmt) {
+        if (executionOrder != ExecutionOrder.NULL) {
+            ensureCompletedExecution(stmt, executionOrder);
         }
     }
 
-    // Make sure target statement has transitioned at least to specified phase. This method is just before we take
-    // allow a statement to become our substatement. This is needed to ensure that every statement tree does not contain
-    // any statements which did not complete the same phase as the root statement.
-    private static void ensureCompletedPhase(final ReactorStmtCtx<?, ?, ?> stmt, final ModelProcessingPhase phase) {
-        verify(stmt.tryToCompletePhase(phase), "Statement %s cannot complete phase %s", stmt, phase);
+    private static void ensureCompletedExecution(final ReactorStmtCtx<?, ?, ?> stmt, final byte executionOrder) {
+        verify(stmt.tryToCompletePhase(executionOrder), "Statement %s cannot complete phase %s", stmt, executionOrder);
     }
 
     private static void verifyStatement(final Mutable<?, ?, ?> stmt) {
@@ -323,8 +389,10 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
 
     private List<ReactorStmtCtx<?, ?, ?>> beforeAddEffectiveStatement(final List<ReactorStmtCtx<?, ?, ?>> effective,
             final int toAdd) {
-        // We cannot allow statement to be further mutated
-        verify(completedPhase != ModelProcessingPhase.EFFECTIVE_MODEL, "Cannot modify finished statement at %s",
+        // We cannot allow statement to be further mutated.
+        // TODO: we really want to say 'not NULL and not at or after EFFECTIVE_MODEL here. This will matter if we have
+        //       a phase after EFFECTIVE_MODEL
+        verify(executionOrder != ExecutionOrder.EFFECTIVE_MODEL, "Cannot modify finished statement at %s",
             sourceReference());
         return beforeAddEffectiveStatementUnsafe(effective, toAdd);
     }
@@ -358,32 +426,47 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
         return factory.createEffective(ctx, ctx.streamDeclared(), ctx.streamEffective());
     }
 
-    abstract Stream<? extends StmtContext<?, ?, ?>> streamDeclared();
+    /**
+     * Return a stream of declared statements which can be built into an {@link EffectiveStatement}, as per
+     * {@link StmtContext#buildEffective()} contract.
+     *
+     * @return Stream of supported declared statements.
+     */
+    // FIXME: we really want to unify this with streamEffective(), under its name
+    abstract Stream<? extends @NonNull StmtContext<?, ?, ?>> streamDeclared();
 
-    abstract Stream<? extends StmtContext<?, ?, ?>> streamEffective();
+    /**
+     * Return a stream of inferred statements which can be built into an {@link EffectiveStatement}, as per
+     * {@link StmtContext#buildEffective()} contract.
+     *
+     * @return Stream of supported effective statements.
+     */
+    // FIXME: this method is currently a misnomer, but unifying with streamDeclared() would make this accurate again
+    abstract Stream<? extends @NonNull StmtContext<?, ?, ?>> streamEffective();
 
     @Override
-    final boolean doTryToCompletePhase(final ModelProcessingPhase phase) {
-        final boolean finished = phaseMutation.isEmpty() ? true : runMutations(phase);
-        if (completeChildren(phase) && finished) {
-            onPhaseCompleted(phase);
+    final boolean doTryToCompletePhase(final byte targetOrder) {
+        final boolean finished = phaseMutation.isEmpty() ? true : runMutations(targetOrder);
+        if (completeChildren(targetOrder) && finished) {
+            onPhaseCompleted(targetOrder);
             return true;
         }
         return false;
     }
 
-    private boolean completeChildren(final ModelProcessingPhase phase) {
+    private boolean completeChildren(final byte targetOrder) {
         boolean finished = true;
         for (final StatementContextBase<?, ?, ?> child : mutableDeclaredSubstatements()) {
-            finished &= child.tryToCompletePhase(phase);
+            finished &= child.tryToCompletePhase(targetOrder);
         }
         for (final ReactorStmtCtx<?, ?, ?> child : effectiveChildrenToComplete()) {
-            finished &= child.tryToCompletePhase(phase);
+            finished &= child.tryToCompletePhase(targetOrder);
         }
         return finished;
     }
 
-    private boolean runMutations(final ModelProcessingPhase phase) {
+    private boolean runMutations(final byte targetOrder) {
+        final ModelProcessingPhase phase = verifyNotNull(ModelProcessingPhase.ofExecutionOrder(targetOrder));
         final Collection<ContextMutation> openMutations = phaseMutation.get(phase);
         return openMutations.isEmpty() ? true : runMutations(phase, openMutations);
     }
@@ -414,22 +497,73 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
     }
 
     /**
-     * Occurs on end of {@link ModelProcessingPhase} of source parsing.
+     * Occurs on end of {@link ModelProcessingPhase} of source parsing. This method must not be called with
+     * {@code executionOrder} equal to {@link ExecutionOrder#NULL}.
      *
-     * @param phase
-     *            that was to be completed (finished)
-     * @throws SourceException
-     *             when an error occurred in source parsing
+     * @param phase that was to be completed (finished)
+     * @throws SourceException when an error occurred in source parsing
      */
-    private void onPhaseCompleted(final ModelProcessingPhase phase) {
-        completedPhase = phase;
+    private void onPhaseCompleted(final byte completedOrder) {
+        executionOrder = completedOrder;
+        if (completedOrder == ExecutionOrder.EFFECTIVE_MODEL) {
+            // We have completed effective model, substatements are guaranteed not to change
+            summarizeSubstatementPolicy();
+        }
 
+        final ModelProcessingPhase phase = verifyNotNull(ModelProcessingPhase.ofExecutionOrder(completedOrder));
         final Collection<OnPhaseFinished> listeners = phaseListeners.get(phase);
         if (!listeners.isEmpty()) {
             runPhaseListeners(phase, listeners);
         }
     }
 
+    private void summarizeSubstatementPolicy() {
+        if (definition().support().copyPolicy() == CopyPolicy.EXACT_REPLICA || noSensitiveSubstatements()) {
+            setAllSubstatementsContextIndependent();
+        }
+    }
+
+    /**
+     * Determine whether any substatements are copy-sensitive as determined by {@link StatementSupport#copyPolicy()}.
+     * Only {@link CopyPolicy#CONTEXT_INDEPENDENT}, {@link CopyPolicy#EXACT_REPLICA} and {@link CopyPolicy#IGNORE} are
+     * copy-insensitive. Note that statements which are not {@link StmtContext#isSupportedToBuildEffective()} are all
+     * considered copy-insensitive.
+     *
+     * <p>
+     * Implementations are expected to call {@link #noSensitiveSubstatements()} to actually traverse substatement sets.
+     *
+     * @return True if no substatements require copy-sensitive handling
+     */
+    abstract boolean noSensitiveSubstatements();
+
+    /**
+     * Determine whether any of the provided substatements are context-sensitive for purposes of implementing
+     * {@link #noSensitiveSubstatements()}.
+     *
+     * @param substatements Substatements to check
+     * @return True if no substatements require context-sensitive handling
+     */
+    static boolean noSensitiveSubstatements(final Collection<? extends ReactorStmtCtx<?, ?, ?>> substatements) {
+        for (ReactorStmtCtx<?, ?, ?> stmt : substatements) {
+            if (stmt.isSupportedToBuildEffective()) {
+                if (!stmt.allSubstatementsContextIndependent()) {
+                    // This is a recursive property
+                    return false;
+                }
+
+                switch (stmt.definition().support().copyPolicy()) {
+                    case CONTEXT_INDEPENDENT:
+                    case EXACT_REPLICA:
+                    case IGNORE:
+                        break;
+                    default:
+                        return false;
+                }
+            }
+        }
+        return true;
+    }
+
     private void runPhaseListeners(final ModelProcessingPhase phase, final Collection<OnPhaseFinished> listeners) {
         final Iterator<OnPhaseFinished> listener = listeners.iterator();
         while (listener.hasNext()) {
@@ -543,10 +677,10 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
      * @throws NullPointerException if any of the arguments is null
      */
     void addPhaseCompletedListener(final ModelProcessingPhase phase, final OnPhaseFinished listener) {
-        checkNotNull(phase, "Statement context processing phase cannot be null at: %s", sourceReference());
-        checkNotNull(listener, "Statement context phase listener cannot be null at: %s", sourceReference());
+        requireNonNull(phase, "Statement context processing phase cannot be null");
+        requireNonNull(listener, "Statement context phase listener cannot be null");
 
-        ModelProcessingPhase finishedPhase = completedPhase;
+        ModelProcessingPhase finishedPhase = ModelProcessingPhase.ofExecutionOrder(executionOrder);
         while (finishedPhase != null) {
             if (phase.equals(finishedPhase)) {
                 listener.phaseFinished(this, finishedPhase);
@@ -567,12 +701,8 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
      * @throws IllegalStateException when the mutation was registered after phase was completed
      */
     final void addMutation(final ModelProcessingPhase phase, final ContextMutation mutation) {
-        ModelProcessingPhase finishedPhase = completedPhase;
-        while (finishedPhase != null) {
-            checkState(!phase.equals(finishedPhase), "Mutation registered after phase was completed at: %s",
-                sourceReference());
-            finishedPhase = finishedPhase.getPreviousPhase();
-        }
+        checkState(executionOrder < phase.executionOrder(), "Mutation registered after phase was completed at: %s",
+            sourceReference());
 
         if (phaseMutation.isEmpty()) {
             phaseMutation = newMultimap();
@@ -605,6 +735,8 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
         final StatementSupport<A, D, E> support = definition.support();
         final CopyPolicy policy = support.copyPolicy();
         switch (policy) {
+            case EXACT_REPLICA:
+                return replicaAsChildOf(parent);
             case CONTEXT_INDEPENDENT:
                 if (allSubstatementsContextIndependent()) {
                     return replicaAsChildOf(parent);
@@ -633,7 +765,7 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
         }
 
         parent.ensureCompletedPhase(copy);
-        return canReuseCurrent(copy) ? replicaAsChildOf(parent) : copy;
+        return canReuseCurrent(copy) ? this : copy;
     }
 
     private boolean canReuseCurrent(final ReactorStmtCtx<A, D, E> copy) {
@@ -643,17 +775,6 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
             && allSubstatementsContextIndependent();
     }
 
-    // FIXME: YANGTOOLS-1195: we really want to compute (and cache) the summary for substatements.
-    //
-    // For now we just check if there are any substatements, but we really want to ask:
-    //
-    //   Are all substatements (recursively) CONTEXT_INDEPENDENT as well?
-    //
-    // Which is something we want to compute once and store. This needs to be implemented.
-    private boolean allSubstatementsContextIndependent() {
-        return hasEmptySubstatements();
-    }
-
     @Override
     public final Mutable<?, ?, ?> childCopyOf(final StmtContext<?, ?, ?> stmt, final CopyType type,
             final QNameModule targetModule) {
@@ -700,13 +821,8 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
     }
 
     @Override
-    public final ReactorStmtCtx<A, D, E> replicaAsChildOf(final Mutable<?, ?, ?> parent) {
-        checkArgument(parent instanceof StatementContextBase, "Unsupported parent %s", parent);
-        return replicaAsChildOf((StatementContextBase<?, ?, ?>) parent);
-    }
-
-    final @NonNull ReplicaStatementContext<A, D, E> replicaAsChildOf(final StatementContextBase<?, ?, ?> stmt) {
-        return new ReplicaStatementContext<>(stmt, this);
+    final ReplicaStatementContext<A, D, E> replicaAsChildOf(final StatementContextBase<?, ?, ?> parent) {
+        return new ReplicaStatementContext<>(parent, this);
     }
 
     private static void checkEffectiveModelCompleted(final StmtContext<?, ?, ?> stmt) {
@@ -716,6 +832,7 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
     }
 
     @Beta
+    // FIXME: this information should be exposed as a well-known Namespace
     public final boolean hasImplicitParentSupport() {
         return definition.getFactory() instanceof ImplicitParentAwareStatementSupport;
     }