Do not force materialization when not needed
[yangtools.git] / yang / yang-parser-spi / src / main / java / org / opendaylight / yangtools / yang / parser / spi / meta / AbstractStatementSupport.java
index 0622463b8212cde180b5b7c2dfdcfef60eae35bc..f8ddbf3e0d3619f3d391776f325e787b1e502c4b 100644 (file)
@@ -12,13 +12,14 @@ import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.VerifyException;
+import java.util.Collection;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
-import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.Current;
 
 /**
  * Class providing necessary support for processing a YANG statement. This class is intended to be subclassed
@@ -30,17 +31,192 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
  */
 public abstract class AbstractStatementSupport<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
         implements StatementDefinition, StatementFactory<A, D, E>, StatementSupport<A, D, E> {
+    /**
+     * A baseline class for implementing the {@link StatementFactory#canReuseCurrent(Current, Current, Collection)}
+     * contract in a manner which is consistent with a statement's {@link CopyPolicy}.
+     *
+     * @param <A> Argument type
+     * @param <D> Declared Statement representation
+     */
+    public abstract static class StatementPolicy<A, D extends DeclaredStatement<A>> implements Immutable {
+        final @NonNull CopyPolicy copyPolicy;
+
+        StatementPolicy(final CopyPolicy copyPolicy) {
+            this.copyPolicy = requireNonNull(copyPolicy);
+        }
+
+        /**
+         * Return an {@link StatementPolicy} for {@link CopyPolicy#CONTEXT_INDEPENDENT}.
+         *
+         * @param <A> Argument type
+         * @param <D> Declared Statement representation
+         * @return Context-independent policy
+         */
+        @SuppressWarnings("unchecked")
+        public static final <A, D extends DeclaredStatement<A>> @NonNull StatementPolicy<A, D> contextIndependent() {
+            return (StatementPolicy<A, D>) AlwaysReuse.CONTEXT_INDEPENDENT;
+        }
+
+        /**
+         * Return an {@link StatementPolicy} for {@link CopyPolicy#IGNORE}.
+         *
+         * @param <A> Argument type
+         * @param <D> Declared Statement representation
+         * @return Ignoring policy
+         */
+        @SuppressWarnings("unchecked")
+        public static final <A, D extends DeclaredStatement<A>> @NonNull StatementPolicy<A, D> ignore() {
+            return (StatementPolicy<A, D>) AlwaysFail.IGNORE;
+        }
+
+        /**
+         * Return an {@link StatementPolicy} for {@link CopyPolicy#REJECT}.
+         *
+         * @param <A> Argument type
+         * @param <D> Declared Statement representation
+         * @return Rejecting statement policy
+         */
+        @SuppressWarnings("unchecked")
+        public static final <A, D extends DeclaredStatement<A>> @NonNull StatementPolicy<A, D> reject() {
+            return (StatementPolicy<A, D>) AlwaysFail.REJECT;
+        }
+
+        /**
+         * Return an {@link StatementPolicy} for {@link CopyPolicy#DECLARED_COPY}, deferring to a
+         * {@link StatementEquality} for individual decisions.
+         *
+         * @param <A> Argument type
+         * @param <D> Declared Statement representation
+         * @param equality {@link StatementEquality} to apply to effective statements
+         * @return Rejecting statement policy
+         */
+        public static final <A, D extends DeclaredStatement<A>> @NonNull StatementPolicy<A, D> copyDeclared(
+                final @NonNull StatementEquality<A, D> equality) {
+            return new EqualSemantics<>(equality);
+        }
+
+        abstract boolean canReuseCurrent(@NonNull Current<A, D> copy, @NonNull Current<A, D> current,
+            @NonNull Collection<? extends EffectiveStatement<?, ?>> substatements);
+
+        @Deprecated
+        @SuppressWarnings("unchecked")
+        static <A, D extends DeclaredStatement<A>> StatementPolicy<A, D> compat(final CopyPolicy copyPolicy) {
+            switch (copyPolicy) {
+                case CONTEXT_INDEPENDENT:
+                    return contextIndependent();
+                case DECLARED_COPY:
+                    return (StatementPolicy<A, D>) AlwaysCopy.DECLARED_COPY;
+                case IGNORE:
+                    return ignore();
+                case REJECT:
+                    return reject();
+                default:
+                    throw new IllegalStateException("Unsupported policy " + copyPolicy);
+            }
+        }
+
+        private static final class AlwaysCopy<A, D extends DeclaredStatement<A>> extends StatementPolicy<A, D> {
+            @Deprecated
+            static final @NonNull AlwaysCopy<?, ?> DECLARED_COPY = new AlwaysCopy<>(CopyPolicy.DECLARED_COPY);
+
+            AlwaysCopy(final CopyPolicy copyPolicy) {
+                super(copyPolicy);
+            }
+
+            @Override
+            boolean canReuseCurrent(final Current<A, D> copy, final Current<A, D> current,
+                    final Collection<? extends EffectiveStatement<?, ?>> substatements) {
+                return false;
+            }
+        }
+
+        private static final class AlwaysReuse<A, D extends DeclaredStatement<A>> extends StatementPolicy<A, D> {
+            static final @NonNull AlwaysReuse<?, ?> CONTEXT_INDEPENDENT =
+                new AlwaysReuse<>(CopyPolicy.CONTEXT_INDEPENDENT);
+
+            private AlwaysReuse(final CopyPolicy copyPolicy) {
+                super(copyPolicy);
+            }
 
+            @Override
+            boolean canReuseCurrent(final Current<A, D> copy, final Current<A, D> current,
+                    final Collection<? extends EffectiveStatement<?, ?>> substatements) {
+                return true;
+            }
+        }
+
+        private static final class AlwaysFail<A, D extends DeclaredStatement<A>> extends StatementPolicy<A, D> {
+            static final @NonNull AlwaysFail<?, ?> IGNORE = new AlwaysFail<>(CopyPolicy.IGNORE);
+            static final @NonNull AlwaysFail<?, ?> REJECT = new AlwaysFail<>(CopyPolicy.REJECT);
+
+            private AlwaysFail(final CopyPolicy copyPolicy) {
+                super(copyPolicy);
+            }
+
+            @Override
+            boolean canReuseCurrent(final Current<A, D> copy, final Current<A, D> current,
+                    final Collection<? extends EffectiveStatement<?, ?>> substatements) {
+                throw new VerifyException("This implementation should never be invoked");
+            }
+        }
+
+        private static final class EqualSemantics<A, D extends DeclaredStatement<A>> extends StatementPolicy<A, D> {
+            private final @NonNull StatementEquality<A, D> equality;
+
+            EqualSemantics(final @NonNull StatementEquality<A, D> equality) {
+                super(CopyPolicy.DECLARED_COPY);
+                this.equality = requireNonNull(equality);
+            }
+
+            @Override
+            boolean canReuseCurrent(final Current<A, D> copy, final Current<A, D> current,
+                    final Collection<? extends EffectiveStatement<?, ?>> substatements) {
+                return equality.canReuseCurrent(copy, current, substatements);
+            }
+        }
+    }
+
+    /**
+     * Abstract base class for comparators associated with statements with a {@link CopyPolicy#DECLARED_COPY} copy
+     * policy.
+     *
+     * @param <A> Argument type
+     * @param <D> Declared Statement representation
+     */
+    @FunctionalInterface
+    public interface StatementEquality<A, D extends DeclaredStatement<A>> {
+        /**
+         * Determine whether {@code current} statement has the same semantics as the provided copy. See the contract
+         * specification of {@link StatementFactory#canReuseCurrent(Current, Current, Collection)}.
+         *
+         * @param copy Copy of current effective context
+         * @param current Current effective context
+         * @param substatements Current effective substatements
+         * @return True if {@code current} can be reused in place of {@code copy}, false if the copy needs to be used.
+         */
+        boolean canReuseCurrent(@NonNull Current<A, D> copy, @NonNull Current<A, D> current,
+            @NonNull Collection<? extends EffectiveStatement<?, ?>> substatements);
+    }
+
+    private final @NonNull StatementPolicy<A, D> policy;
     private final @NonNull StatementDefinition type;
     private final @NonNull CopyPolicy copyPolicy;
 
     @Beta
-    protected AbstractStatementSupport(final StatementDefinition publicDefinition, final CopyPolicy copyPolicy) {
+    protected AbstractStatementSupport(final StatementDefinition publicDefinition, final StatementPolicy<A, D> policy) {
         this.type = requireNonNull(publicDefinition);
-        this.copyPolicy = requireNonNull(copyPolicy);
+        this.policy = requireNonNull(policy);
+        this.copyPolicy = policy.copyPolicy;
         checkArgument(publicDefinition != this);
     }
 
+    @Beta
+    @Deprecated
+    // FIXME: remove this constructor
+    protected AbstractStatementSupport(final StatementDefinition publicDefinition, final CopyPolicy copyPolicy) {
+        this(publicDefinition, StatementPolicy.compat(copyPolicy));
+    }
+
     @Override
     public final StatementDefinition getPublicView() {
         return type;
@@ -52,18 +228,9 @@ public abstract class AbstractStatementSupport<A, D extends DeclaredStatement<A>
     }
 
     @Override
-    public final StmtContext<?, ?, ?> effectiveCopyOf(final StmtContext<?, ?, ?> stmt, final Mutable<?, ?, ?> parent,
-            final CopyType copyType, final QNameModule targetModule) {
-        switch (copyPolicy) {
-            case CONTEXT_INDEPENDENT:
-                return stmt;
-            case DECLARED_COPY:
-                // FIXME: YANGTOOLS-1195: this is too harsh, we need to make a callout to subclass methods so they
-                //                        actually examine the differences.
-                return parent.childCopyOf(stmt, copyType, targetModule);
-            default:
-                throw new VerifyException("Attempted to apply " + copyPolicy);
-        }
+    public final boolean canReuseCurrent(final Current<A, D> copy, final Current<A, D> current,
+             final Collection<? extends EffectiveStatement<?, ?>> substatements) {
+        return policy.canReuseCurrent(copy, current, substatements);
     }
 
     @Override