Rework EffectiveStatementBase statement order restoration
[yangtools.git] / yang / yang-parser-reactor / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / StatementContextBase.java
index c4307a107f93d027139b76547c405d77d1d76293..08ab75f794bf91d83fdf7482f6b173ff4e5cc375 100644 (file)
@@ -7,9 +7,14 @@
  */
 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 java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableCollection;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMultimap;
@@ -22,12 +27,13 @@ import java.util.Collections;
 import java.util.EnumMap;
 import java.util.EventListener;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.util.OptionalBoolean;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
@@ -39,6 +45,7 @@ import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
 import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory;
 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.NamespaceBehaviour;
@@ -91,22 +98,23 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
 
     private static final Logger LOG = LoggerFactory.getLogger(StatementContextBase.class);
 
-    private final StatementDefinitionContext<A, D, E> definition;
-    private final StatementSourceReference statementDeclSource;
+    private final @NonNull StatementDefinitionContext<A, D, E> definition;
+    private final @NonNull StatementSourceReference statementDeclSource;
     private final StmtContext<?, ?, ?> originalCtx;
+    private final StmtContext<?, ?, ?> prevCopyCtx;
     private final CopyHistory copyHistory;
     private final String rawArgument;
 
     private Multimap<ModelProcessingPhase, OnPhaseFinished> phaseListeners = ImmutableMultimap.of();
     private Multimap<ModelProcessingPhase, ContextMutation> phaseMutation = ImmutableMultimap.of();
-    private Collection<Mutable<?, ?, ?>> effective = ImmutableList.of();
-    private Collection<StmtContext<?, ?, ?>> effectOfStatement = ImmutableList.of();
+    private List<Mutable<?, ?, ?>> effective = ImmutableList.of();
+    private List<StmtContext<?, ?, ?>> effectOfStatement = ImmutableList.of();
     private StatementMap substatements = StatementMap.empty();
 
     private boolean isSupportedToBuildEffective = true;
-    private ModelProcessingPhase completedPhase = null;
-    private D declaredInstance;
-    private E effectiveInstance;
+    private @Nullable ModelProcessingPhase completedPhase;
+    private @Nullable D declaredInstance;
+    private @Nullable E effectiveInstance;
 
     // BooleanFields value
     private byte supportedByFeatures;
@@ -115,22 +123,42 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
 
     StatementContextBase(final StatementDefinitionContext<A, D, E> def, final StatementSourceReference ref,
             final String rawArgument) {
-        this.definition = Preconditions.checkNotNull(def);
-        this.statementDeclSource = Preconditions.checkNotNull(ref);
+        this.definition = requireNonNull(def);
+        this.statementDeclSource = requireNonNull(ref);
         this.rawArgument = def.internArgument(rawArgument);
         this.copyHistory = CopyHistory.original();
         this.originalCtx = null;
+        this.prevCopyCtx = null;
+    }
+
+    StatementContextBase(final StatementDefinitionContext<A, D, E> def, final StatementSourceReference ref,
+        final String rawArgument, final CopyType copyType) {
+        this.definition = requireNonNull(def);
+        this.statementDeclSource = requireNonNull(ref);
+        this.rawArgument = rawArgument;
+        this.copyHistory = CopyHistory.of(copyType, CopyHistory.original());
+        this.originalCtx = null;
+        this.prevCopyCtx = null;
     }
 
     StatementContextBase(final StatementContextBase<A, D, E> original, final CopyType copyType) {
-        this.definition = Preconditions.checkNotNull(original.definition,
-                "Statement context definition cannot be null copying from: %s", original.getStatementSourceReference());
-        this.statementDeclSource = Preconditions.checkNotNull(original.statementDeclSource,
-                "Statement context statementDeclSource cannot be null copying from: %s",
-                original.getStatementSourceReference());
+        this.definition = original.definition;
+        this.statementDeclSource = original.statementDeclSource;
         this.rawArgument = original.rawArgument;
         this.copyHistory = CopyHistory.of(copyType, original.getCopyHistory());
         this.originalCtx = original.getOriginalCtx().orElse(original);
+        this.prevCopyCtx = original;
+    }
+
+    StatementContextBase(final StatementContextBase<A, D, E> original) {
+        this.definition = original.definition;
+        this.statementDeclSource = original.statementDeclSource;
+        this.rawArgument = original.rawArgument;
+        this.copyHistory = original.getCopyHistory();
+        this.originalCtx = original.getOriginalCtx().orElse(original);
+        this.prevCopyCtx = original;
+        this.substatements = original.substatements;
+        this.effective = original.effective;
     }
 
     @Override
@@ -187,9 +215,7 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
         // If the set of supported features has not been provided, all features are supported by default.
         final Set<QName> supportedFeatures = getFromNamespace(SupportedFeaturesNamespace.class,
                 SupportedFeatures.SUPPORTED_FEATURES);
-        final boolean ret = supportedFeatures == null ? true
-                : StmtContextUtils.checkFeatureSupport(this, supportedFeatures);
-
+        final boolean ret = supportedFeatures == null || StmtContextUtils.checkFeatureSupport(this, supportedFeatures);
         supportedByFeatures = OptionalBoolean.of(ret);
         return ret;
     }
@@ -220,6 +246,11 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
         return Optional.ofNullable(originalCtx);
     }
 
+    @Override
+    public Optional<? extends StmtContext<?, ?, ?>> getPreviousCopyCtx() {
+        return Optional.ofNullable(prevCopyCtx);
+    }
+
     @Override
     public ModelProcessingPhase getCompletedPhase() {
         return completedPhase;
@@ -238,7 +269,6 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
      *
      * @return root context of statement
      */
-    @Nonnull
     @Override
     public abstract RootStatementContext<?, ?, ?> getRoot();
 
@@ -247,7 +277,6 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
      *
      * @return origin of statement
      */
-    @Nonnull
     @Override
     public StatementSource getStatementSource() {
         return statementDeclSource.getStatementSource();
@@ -258,7 +287,6 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
      *
      * @return reference of statement source
      */
-    @Nonnull
     @Override
     public StatementSourceReference getStatementSourceReference() {
         return statementDeclSource;
@@ -269,13 +297,11 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
         return rawArgument;
     }
 
-    @Nonnull
     @Override
     public Collection<? extends StmtContext<?, ?, ?>> declaredSubstatements() {
         return substatements.values();
     }
 
-    @Nonnull
     @Override
     public Collection<? extends Mutable<?, ?, ?>> mutableDeclaredSubstatements() {
         return substatements.values();
@@ -286,7 +312,6 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
         return mutableEffectiveSubstatements();
     }
 
-    @Nonnull
     @Override
     public Collection<? extends Mutable<?, ?, ?>> mutableEffectiveSubstatements() {
         if (effective instanceof ImmutableCollection) {
@@ -296,6 +321,15 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
         return Collections.unmodifiableCollection(effective);
     }
 
+    /**
+     * Remove a set of statements from effective statements.
+     *
+     * @param statements statements to be removed
+     * @deprecated This method was used by EffectiveStatementBase to restore proper order of effects of uses statements.
+     *             It is no longer used in that capacity and slated for removal.
+     */
+    // FIXME: 5.0.0: remove this method
+    @Deprecated
     public void removeStatementsFromEffectiveSubstatements(
             final Collection<? extends StmtContext<?, ?, ?>> statements) {
         if (!effective.isEmpty()) {
@@ -387,14 +421,14 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
             return;
         }
 
-        statements.forEach(Preconditions::checkNotNull);
+        statements.forEach(Objects::requireNonNull);
         beforeAddEffectiveStatement(statements.size());
         effective.addAll(statements);
     }
 
     private void beforeAddEffectiveStatement(final int toAdd) {
         final ModelProcessingPhase inProgressPhase = getRoot().getSourceContext().getInProgressPhase();
-        Preconditions.checkState(inProgressPhase == ModelProcessingPhase.FULL_DECLARATION
+        checkState(inProgressPhase == ModelProcessingPhase.FULL_DECLARATION
                 || inProgressPhase == ModelProcessingPhase.EFFECTIVE_MODEL,
                 "Effective statement cannot be added in declared phase at: %s", getStatementSourceReference());
 
@@ -410,15 +444,18 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
      * @param def definition context
      * @param ref source reference
      * @param argument statement argument
+     * @param <X> new substatement argument type
+     * @param <Y> new substatement declared type
+     * @param <Z> new substatement effective type
      * @return A new substatement
      */
     @SuppressWarnings("checkstyle:methodTypeParameterName")
-    public final <CA, CD extends DeclaredStatement<CA>, CE extends EffectiveStatement<CA, CD>>
-            StatementContextBase<CA, CD, CE> createSubstatement(final int offset,
-                    final StatementDefinitionContext<CA, CD, CE> def, final StatementSourceReference ref,
+    public final <X, Y extends DeclaredStatement<X>, Z extends EffectiveStatement<X, Y>>
+            StatementContextBase<X, Y, Z> createSubstatement(final int offset,
+                    final StatementDefinitionContext<X, Y, Z> def, final StatementSourceReference ref,
                     final String argument) {
         final ModelProcessingPhase inProgressPhase = getRoot().getSourceContext().getInProgressPhase();
-        Preconditions.checkState(inProgressPhase != ModelProcessingPhase.EFFECTIVE_MODEL,
+        checkState(inProgressPhase != ModelProcessingPhase.EFFECTIVE_MODEL,
                 "Declared statement cannot be added in effective phase at: %s", getStatementSourceReference());
 
         final Optional<StatementSupport<?, ?, ?>> implicitParent = definition.getImplicitParentFor(def.getPublicView());
@@ -427,7 +464,7 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
                     ref, argument);
         }
 
-        final StatementContextBase<CA, CD, CE> ret = new SubstatementContext<>(this, def, ref, argument);
+        final StatementContextBase<X, Y, Z> ret = new SubstatementContext<>(this, def, ref, argument);
         substatements = substatements.put(offset, ret);
         def.onStatementAdded(ret);
         return ret;
@@ -439,6 +476,11 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
         return createSubstatement(offset, def, ImplicitSubstatement.of(ref), argument);
     }
 
+    public void appendImplicitStatement(final StatementSupport<?, ?, ?> statementToAdd) {
+        createSubstatement(substatements.capacity(), new StatementDefinitionContext<>(statementToAdd),
+                ImplicitSubstatement.of(getStatementSourceReference()), null);
+    }
+
     /**
      * Lookup substatement by its offset in this statement.
      *
@@ -453,8 +495,12 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
         this.fullyDefined = true;
     }
 
+    final void resizeSubstatements(final int expectedSize) {
+        substatements = substatements.ensureCapacity(expectedSize);
+    }
+
     final void walkChildren(final ModelProcessingPhase phase) {
-        Preconditions.checkState(fullyDefined);
+        checkState(fullyDefined);
         substatements.values().forEach(stmt -> {
             stmt.walkChildren(phase);
             stmt.endDeclared(phase);
@@ -463,7 +509,7 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
 
     @Override
     public D buildDeclared() {
-        Preconditions.checkArgument(completedPhase == ModelProcessingPhase.FULL_DECLARATION
+        checkArgument(completedPhase == ModelProcessingPhase.FULL_DECLARATION
                 || completedPhase == ModelProcessingPhase.EFFECTIVE_MODEL);
         if (declaredInstance == null) {
             declaredInstance = definition().getFactory().createDeclared(this);
@@ -486,7 +532,7 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
      *            to be executed (completed)
      * @return if phase was successfully completed
      * @throws SourceException
-     *             when an error occured in source parsing
+     *             when an error occurred in source parsing
      */
     boolean tryToCompletePhase(final ModelProcessingPhase phase) {
 
@@ -571,7 +617,7 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
      *
      * @return statement definition
      */
-    protected final StatementDefinitionContext<A, D, E> definition() {
+    protected final @NonNull StatementDefinitionContext<A, D, E> definition() {
         return definition;
     }
 
@@ -632,8 +678,8 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
     final <K, V, N extends IdentifierNamespace<K, V>> void selectMatch(final Class<N> type,
             final NamespaceKeyCriterion<K> criterion, final OnNamespaceItemAdded listener) {
         final Optional<Entry<K, V>> optMatch = getFromNamespace(type, criterion);
-        Preconditions.checkState(optMatch.isPresent(),
-            "Failed to find a match for criterion %s in namespace %s node %s", criterion, type, this);
+        checkState(optMatch.isPresent(), "Failed to find a match for criterion %s in namespace %s node %s", criterion,
+            type, this);
         final Entry<K, V> match = optMatch.get();
         listener.namespaceItemAdded(StatementContextBase.this, type, match.getKey(), match.getValue());
     }
@@ -651,8 +697,8 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
     private <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviourWithListeners<K, V, N> getBehaviour(
             final Class<N> type) {
         final NamespaceBehaviour<K, V, N> behaviour = getBehaviourRegistry().getNamespaceBehaviour(type);
-        Preconditions.checkArgument(behaviour instanceof NamespaceBehaviourWithListeners,
-            "Namespace %s does not support listeners", type);
+        checkArgument(behaviour instanceof NamespaceBehaviourWithListeners, "Namespace %s does not support listeners",
+            type);
 
         return (NamespaceBehaviourWithListeners<K, V, N>) behaviour;
     }
@@ -660,7 +706,6 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
     /**
      * See {@link StatementSupport#getPublicView()}.
      */
-    @Nonnull
     @Override
     public StatementDefinition getPublicDefinition() {
         return definition().getPublicView();
@@ -684,10 +729,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) {
-        Preconditions.checkNotNull(phase, "Statement context processing phase cannot be null at: %s",
-                getStatementSourceReference());
-        Preconditions.checkNotNull(listener, "Statement context phase listener cannot be null at: %s",
-                getStatementSourceReference());
+        checkNotNull(phase, "Statement context processing phase cannot be null at: %s", getStatementSourceReference());
+        checkNotNull(listener, "Statement context phase listener cannot be null at: %s", getStatementSourceReference());
 
         ModelProcessingPhase finishedPhase = completedPhase;
         while (finishedPhase != null) {
@@ -713,8 +756,8 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
     void addMutation(final ModelProcessingPhase phase, final ContextMutation mutation) {
         ModelProcessingPhase finishedPhase = completedPhase;
         while (finishedPhase != null) {
-            Preconditions.checkState(!phase.equals(finishedPhase),
-                "Mutation registered after phase was completed at: %s", getStatementSourceReference());
+            checkState(!phase.equals(finishedPhase), "Mutation registered after phase was completed at: %s",
+                getStatementSourceReference());
             finishedPhase = finishedPhase.getPreviousPhase();
         }
 
@@ -733,20 +776,48 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
     @Override
     public <X, Y extends DeclaredStatement<X>, Z extends EffectiveStatement<X, Y>> Mutable<X, Y, Z> childCopyOf(
             final StmtContext<X, Y, Z> stmt, final CopyType type, final QNameModule targetModule) {
-        Preconditions.checkState(stmt.getCompletedPhase() == ModelProcessingPhase.EFFECTIVE_MODEL,
+        checkState(stmt.getCompletedPhase() == ModelProcessingPhase.EFFECTIVE_MODEL,
                 "Attempted to copy statement %s which has completed phase %s", stmt, stmt.getCompletedPhase());
 
-        Preconditions.checkArgument(stmt instanceof SubstatementContext, "Unsupported statement %s", stmt);
+        checkArgument(stmt instanceof SubstatementContext, "Unsupported statement %s", stmt);
 
         final SubstatementContext<X, Y, Z> original = (SubstatementContext<X, Y, Z>)stmt;
-        final SubstatementContext<X, Y, Z> copy = new SubstatementContext<>(original, this, type, targetModule);
+        final Optional<StatementSupport<?, ?, ?>> implicitParent = definition.getImplicitParentFor(
+            original.getPublicDefinition());
 
-        original.definition().onStatementAdded(copy);
-        original.copyTo(copy, type, targetModule);
+        final SubstatementContext<X, Y, Z> result;
+        final SubstatementContext<X, Y, Z> copy;
 
-        return copy;
-    }
+        if (implicitParent.isPresent()) {
+            final StatementDefinitionContext<?, ?, ?> def = new StatementDefinitionContext<>(implicitParent.get());
+            result = new SubstatementContext(this, def, original.getSourceReference(),
+                original.rawStatementArgument(), original.getStatementArgument(), type);
+
+            final CopyType childCopyType;
+            switch (type) {
+                case ADDED_BY_AUGMENTATION:
+                    childCopyType = CopyType.ORIGINAL;
+                    break;
+                case ADDED_BY_USES_AUGMENTATION:
+                    childCopyType = CopyType.ADDED_BY_USES;
+                    break;
+                case ADDED_BY_USES:
+                case ORIGINAL:
+                default:
+                    childCopyType = type;
+            }
+
+            copy = new SubstatementContext<>(original, result, childCopyType, targetModule);
+            result.addEffectiveSubstatement(copy);
+            original.definition().onStatementAdded(copy);
+        } else {
+            result = copy = new SubstatementContext<>(original, this, type, targetModule);
+            original.definition().onStatementAdded(copy);
+        }
 
+        original.copyTo(copy, type, targetModule);
+        return result;
+    }
 
     @Override
     public @NonNull StatementDefinition getDefinition() {
@@ -763,6 +834,30 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
         return fullyDefined;
     }
 
+    @Beta
+    public final boolean hasImplicitParentSupport() {
+        return definition.getFactory() instanceof ImplicitParentAwareStatementSupport;
+    }
+
+    @Beta
+    public final StatementContextBase<?, ?, ?> wrapWithImplicit(final StatementContextBase<?, ?, ?> original) {
+        final Optional<StatementSupport<?, ?, ?>> optImplicit = definition.getImplicitParentFor(
+            original.getPublicDefinition());
+        if (!optImplicit.isPresent()) {
+            return original;
+        }
+
+        final StatementDefinitionContext<?, ?, ?> def = new StatementDefinitionContext<>(optImplicit.get());
+        final CopyType type = original.getCopyHistory().getLastOperation();
+        final SubstatementContext<?, ?, ?> result = new SubstatementContext(original.getParentContext(), def,
+            original.getStatementSourceReference(), original.rawStatementArgument(), original.getStatementArgument(),
+            type);
+
+        result.addEffectiveSubstatement(new SubstatementContext<>(original, result));
+        result.setCompletedPhase(original.getCompletedPhase());
+        return result;
+    }
+
     final void copyTo(final StatementContextBase<?, ?, ?> target, final CopyType typeOfCopy,
             @Nullable final QNameModule targetModule) {
         final Collection<Mutable<?, ?, ?>> buffer = new ArrayList<>(substatements.size() + effective.size());
@@ -784,7 +879,7 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
             final CopyType typeOfCopy, final QNameModule newQNameModule, final Collection<Mutable<?, ?, ?>> buffer) {
         if (needToCopyByUses(stmtContext)) {
             final Mutable<?, ?, ?> copy = target.childCopyOf(stmtContext, typeOfCopy, newQNameModule);
-            LOG.debug("Copying substatement {} for {} as", stmtContext, this, copy);
+            LOG.debug("Copying substatement {} for {} as {}", stmtContext, this, copy);
             buffer.add(copy);
         } else if (isReusedByUses(stmtContext)) {
             LOG.debug("Reusing substatement {} for {}", stmtContext, this);
@@ -795,11 +890,11 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
     }
 
     // FIXME: revise this, as it seems to be wrong
-    private static final Set<YangStmtMapping> NOCOPY_FROM_GROUPING_SET = ImmutableSet.of(
+    private static final ImmutableSet<YangStmtMapping> NOCOPY_FROM_GROUPING_SET = ImmutableSet.of(
         YangStmtMapping.DESCRIPTION,
         YangStmtMapping.REFERENCE,
         YangStmtMapping.STATUS);
-    private static final Set<YangStmtMapping> REUSED_DEF_SET = ImmutableSet.of(
+    private static final ImmutableSet<YangStmtMapping> REUSED_DEF_SET = ImmutableSet.of(
         YangStmtMapping.TYPE,
         YangStmtMapping.TYPEDEF,
         YangStmtMapping.USES);
@@ -811,7 +906,7 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
             return false;
         }
         if (NOCOPY_FROM_GROUPING_SET.contains(def)) {
-            return !YangStmtMapping.GROUPING.equals(stmtContext.getParentContext().getPublicDefinition());
+            return !YangStmtMapping.GROUPING.equals(stmtContext.coerceParentContext().getPublicDefinition());
         }
 
         LOG.debug("Will copy {} statement {}", def, stmtContext);