Use simple requireNonNull()
[yangtools.git] / yang / yang-parser-reactor / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / StatementContextBase.java
index cd07b95b1cb51e30d3054050f5d6434d6234f96a..a331d6b6d91619da119fcc3323aa94dee5a478a5 100644 (file)
@@ -8,7 +8,6 @@
 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 java.util.Objects.requireNonNull;
@@ -44,6 +43,7 @@ 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;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ParserNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementFactory;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport.CopyPolicy;
@@ -129,14 +129,6 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
         return effectOfStatement;
     }
 
-    @Override
-    public void addAsEffectOfStatement(final StmtContext<?, ?, ?> ctx) {
-        if (effectOfStatement.isEmpty()) {
-            effectOfStatement = new ArrayList<>(1);
-        }
-        effectOfStatement.add(ctx);
-    }
-
     @Override
     public void addAsEffectOfStatement(final Collection<? extends StmtContext<?, ?, ?>> ctxs) {
         if (ctxs.isEmpty()) {
@@ -293,11 +285,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 Collection<? extends ReactorStmtCtx<?, ?, ?>> casted =
+            (Collection<? extends ReactorStmtCtx<?, ?, ?>>) statements;
         final ModelProcessingPhase phase = completedPhase;
         if (phase != null) {
-            for (StatementContextBase<?, ?, ?> stmt : casted) {
+            for (ReactorStmtCtx<?, ?, ?> stmt : casted) {
                 ensureCompletedPhase(stmt, phase);
             }
         }
@@ -346,19 +338,42 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
         return effective.isEmpty() ? new ArrayList<>(toAdd) : effective;
     }
 
-
     @Override
     final E createEffective() {
-        final E result = definition.getFactory().createEffective(this, streamDeclared(), streamEffective());
+        final E result = createEffective(definition.getFactory());
         if (result instanceof MutableStatement) {
             getRoot().addMutableStmtToSeal((MutableStatement) result);
         }
         return result;
     }
 
-    abstract Stream<? extends StmtContext<?, ?, ?>> streamDeclared();
+    @NonNull E createEffective(final StatementFactory<A, D, E> factory) {
+        return createEffective(factory, this);
+    }
 
-    abstract Stream<? extends StmtContext<?, ?, ?>> streamEffective();
+    // Creates EffectiveStatement through full materialization
+    static <A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>> @NonNull E createEffective(
+            final StatementFactory<A, D, E> factory, final StatementContextBase<A, D, E> ctx) {
+        return factory.createEffective(ctx, ctx.streamDeclared(), ctx.streamEffective());
+    }
+
+    /**
+     * 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();
+
+    /**
+     * 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) {
@@ -421,6 +436,9 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
      */
     private void onPhaseCompleted(final ModelProcessingPhase phase) {
         completedPhase = phase;
+        if (phase == ModelProcessingPhase.EFFECTIVE_MODEL) {
+            summarizeSubstatementPolicy();
+        }
 
         final Collection<OnPhaseFinished> listeners = phaseListeners.get(phase);
         if (!listeners.isEmpty()) {
@@ -428,6 +446,53 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
         }
     }
 
+    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()) {
@@ -541,8 +606,8 @@ 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;
         while (finishedPhase != null) {
@@ -595,20 +660,27 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
     public Optional<? extends Mutable<?, ?, ?>> copyAsChildOf(final Mutable<?, ?, ?> parent, final CopyType type,
             final QNameModule targetModule) {
         checkEffectiveModelCompleted(this);
+        return Optional.ofNullable(copyAsChildOfImpl(parent, type, targetModule));
+    }
 
+    private ReactorStmtCtx<A, D, E> copyAsChildOfImpl(final Mutable<?, ?, ?> parent, final CopyType type,
+            final QNameModule targetModule) {
         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 (substatementsContextIndependent()) {
-                    return Optional.of(replicaAsChildOf(parent));
+                if (allSubstatementsContextIndependent()) {
+                    return replicaAsChildOf(parent);
                 }
 
                 // fall through
             case DECLARED_COPY:
-                return Optional.of(parent.childCopyOf(this, type, targetModule));
+                // FIXME: ugly cast
+                return (ReactorStmtCtx<A, D, E>) parent.childCopyOf(this, type, targetModule);
             case IGNORE:
-                return Optional.empty();
+                return null;
             case REJECT:
                 throw new IllegalStateException("Statement " + support.getPublicView() + " should never be copied");
             default:
@@ -616,35 +688,24 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
         }
     }
 
-    private boolean substatementsContextIndependent() {
-        // 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.
-        return hasEmptySubstatements();
-    }
-
-    // FIXME: YANGTOOLS-1195: this method is unused, but should be called from InferredStatementContext at the very
-    //        least. It should return @NonNull -- either 'E' or EffectiveStmtCtx.Current'. Perhaps its arguments need
-    //        to be adjusted, too.
-    final void asEffectiveChildOf(final Mutable<?, ?, ?> parent, final CopyType type, final QNameModule targetModule) {
-        checkEffectiveModelCompleted(this);
-
-        final StatementSupport<A, D, E> support = definition.support();
-        final StmtContext<?, ?, ?> effective = support.effectiveCopyOf(this, parent, type, targetModule);
-        if (effective == this) {
-            LOG.debug("Should reuse {}", this);
-            return;
+    @Override
+    final ReactorStmtCtx<?, ?, ?> asEffectiveChildOf(final StatementContextBase<?, ?, ?> parent, final CopyType type,
+            final QNameModule targetModule) {
+        final ReactorStmtCtx<A, D, E> copy = copyAsChildOfImpl(parent, type, targetModule);
+        if (copy == null) {
+            // The statement fizzled, this should never happen, perhaps a verify()?
+            return null;
         }
 
-        // FIXME: YANGTOOLS-1195: here is probably where we want to do some statement reuse: even if the parent is
-        //                        affected, some substatements may not -- in which case we want to reuse them. This
-        //                        probably needs to be a callout of some kind.
-        // FIXME: YANGTOOLS-1067: an incremental improvement to that is that if no substatements changed, we want to
-        //                        be reusing the entire List<EffectiveStatement> and pass that as substatements.
+        parent.ensureCompletedPhase(copy);
+        return canReuseCurrent(copy) ? this : copy;
+    }
+
+    private boolean canReuseCurrent(final ReactorStmtCtx<A, D, E> copy) {
+        // Defer to statement factory to see if we can reuse this object. If we can and have only context-independent
+        // substatements we can reuse the object. More complex cases are handled indirectly via the copy.
+        return definition.getFactory().canReuseCurrent(copy, this, buildEffective().effectiveSubstatements())
+            && allSubstatementsContextIndependent();
     }
 
     @Override
@@ -693,13 +754,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) {