package jetbrains.mps.vcs.diff;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.model.SModel;
import java.util.List;
import jetbrains.mps.vcs.diff.changes.ModelChange;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import java.util.ArrayList;
import jetbrains.mps.RuntimeFlags;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.language.SProperty;
import jetbrains.mps.internal.collections.runtime.Sequence;
import org.jetbrains.mps.openapi.language.SReferenceLink;
import org.jetbrains.annotations.Nullable;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import org.jetbrains.mps.openapi.model.SNodeId;
import jetbrains.mps.vcs.diff.changes.AddRootChange;
import jetbrains.mps.vcs.diff.changes.DeleteRootChange;
import java.util.Map;
import org.jetbrains.mps.openapi.language.SContainmentLink;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import jetbrains.mps.internal.collections.runtime.MapSequence;
import java.util.HashMap;
import java.util.function.Function;
import jetbrains.mps.internal.collections.runtime.IListSequence;
import java.util.Objects;
import jetbrains.mps.util.LongestCommonSubsequenceFinder;
import jetbrains.mps.baseLanguage.tuples.runtime.Tuples;
import jetbrains.mps.vcs.diff.changes.NodeGroupChange;
import jetbrains.mps.baseLanguage.closures.runtime.Wrappers;
import jetbrains.mps.vcs.diff.changes.NodeIdChange;
import jetbrains.mps.baseLanguage.closures.runtime._FunctionTypes;
import jetbrains.mps.extapi.model.SModelBase;
import jetbrains.mps.vcs.diff.changes.DependencyChange;
import org.jetbrains.mps.openapi.model.SModelReference;
import jetbrains.mps.smodel.SModelOperations;
import jetbrains.mps.vcs.diff.changes.ImportedModelChange;
import jetbrains.mps.vcs.diff.changes.ModuleDependencyChange;
import org.jetbrains.mps.openapi.module.SModuleReference;
import java.util.Set;
import org.jetbrains.mps.openapi.language.SLanguage;
import java.util.HashSet;
import jetbrains.mps.vcs.diff.changes.UsedLanguageChange;
import jetbrains.mps.vcs.diff.changes.ChangeType;
import jetbrains.mps.vcs.diff.changes.EngagedLanguageChange;
import jetbrains.mps.extapi.model.ModelWithAttributes;
import jetbrains.mps.vcs.diff.changes.ModelAttributeChange;
import jetbrains.mps.baseLanguage.tuples.runtime.MultiTuple;
import jetbrains.mps.internal.collections.runtime.IMapping;

@GeneratedClass(node = "r:5744ed46-c83f-47cd-94ce-f24d1f92d6a1(jetbrains.mps.vcs.diff)/4652592318748341229", model = "r:5744ed46-c83f-47cd-94ce-f24d1f92d6a1(jetbrains.mps.vcs.diff)")
public final class ChangeSetBuilder {

  @NotNull
  private final SModel myOldModel;
  @NotNull
  private final SModel myNewModel;
  @NotNull
  private final ChangeSetImpl myChangeSet;
  @NotNull
  private final List<ModelChange> myNewChanges = ListSequence.fromList(new ArrayList<ModelChange>());
  private final boolean myTrackMovedNodes;
  private final boolean myRespectCommentedOutNodes;


  private ChangeSetBuilder(@NotNull SModel oldModel, @NotNull SModel newModel, boolean trackMovedNodes) {
    myChangeSet = new ChangeSetImpl(oldModel, newModel);
    myOldModel = oldModel;
    myNewModel = newModel;
    myTrackMovedNodes = trackMovedNodes;
    // FIXME this is to deal with MPS-35421 in a limited yet controlled/documented way.
    //      instead of the flag, we shall use different builders to collect changes in different operation modes.
    //      This applies to trackMovedNodes flag (and MovesAwareChangeSetBuilder) as well.
    //      Now I just record this value here, eventually shall pass it down to NodeGroupChange, where for now I just
    //      duplicate this logic.
    myRespectCommentedOutNodes = !(RuntimeFlags.isMergeDriverMode());
  }

  private ChangeSetBuilder(@NotNull ChangeSetImpl changeSet, boolean trackMovedNodes) {
    myChangeSet = changeSet;
    myOldModel = changeSet.getOldModel();
    myNewModel = changeSet.getNewModel();
    myTrackMovedNodes = trackMovedNodes;
    myRespectCommentedOutNodes = !(RuntimeFlags.isMergeDriverMode());
  }

  public void buildForProperty(@NotNull SNode oldNode, @NotNull SNode newNode, @NotNull SProperty property) {
    ListSequence.fromList(myNewChanges).addSequence(Sequence.fromIterable(DiffUtil.collectPropertyChanges(myChangeSet, oldNode, newNode, property)));
  }

  public void buildForReference(@NotNull SNode oldNode, @NotNull SNode newNode, @NotNull SReferenceLink role) {
    ListSequence.fromList(myNewChanges).addSequence(Sequence.fromIterable(DiffUtil.collectReferenceChanges(myChangeSet, oldNode, newNode, role)));
  }

  private void buildForRoot(@Nullable SNode oldNode, @Nullable SNode newNode) {
    if (SNodeOperations.getContainingRoot(oldNode) != oldNode) {
      oldNode = null;
    }
    if (SNodeOperations.getContainingRoot(newNode) != newNode) {
      newNode = null;
    }
    if (oldNode == null && newNode == null) {
      return;
    }
    buildChangeSetForNode(oldNode, newNode);
  }

  private void buildChangeSetForNode(@Nullable SNode oldNode, @Nullable SNode newNode) {
    if (myTrackMovedNodes) {
      MovesAwareChangeSetBuilder builder = new MovesAwareChangeSetBuilder(oldNode, newNode, myChangeSet);
      ListSequence.fromList(myNewChanges).addSequence(ListSequence.fromList(builder.getChanges()));
    } else {
      buildForNode(oldNode, newNode);
    }
  }

  @Nullable
  private SNode getOldNode(@NotNull SNodeId nodeId) {
    return myOldModel.getNode(nodeId);
  }

  @Nullable
  private SNode getNewNode(@NotNull SNodeId nodeId) {
    return myNewModel.getNode(nodeId);
  }

  public void buildForNode(@Nullable final SNode oldNode, @Nullable final SNode newNode) {

    assert oldNode != null || newNode != null;

    if (oldNode == null) {
      ListSequence.fromList(myNewChanges).addElement(new AddRootChange(myChangeSet, newNode.getNodeId()));
    } else if (newNode == null) {
      ListSequence.fromList(myNewChanges).addElement(new DeleteRootChange(myChangeSet, oldNode.getNodeId()));
    } else {
      ListSequence.fromList(myNewChanges).addSequence(Sequence.fromIterable(DiffUtil.collectNodeChanges(myChangeSet, oldNode, newNode)));

      final Map<SContainmentLink, List<SNode>> roleToOldChildCollection = getRoleToChildCollectionMap(oldNode);
      final Map<SContainmentLink, List<SNode>> roleToNewChildCollection = getRoleToChildCollectionMap(newNode);

      SetSequence.fromSet(MapSequence.fromMap(roleToOldChildCollection).keySet()).concat(SetSequence.fromSet(MapSequence.fromMap(roleToNewChildCollection).keySet())).distinct().visitAll((role) -> buildForNodeRole(roleToOldChildCollection.getOrDefault(role, ListSequence.fromList(new ArrayList<SNode>())), roleToNewChildCollection.getOrDefault(role, ListSequence.fromList(new ArrayList<SNode>())), oldNode.getNodeId(), newNode.getNodeId(), role));
    }
  }

  /*package*/ static Map<SContainmentLink, List<SNode>> getRoleToChildCollectionMap(SNode node) {
    final Map<SContainmentLink, List<SNode>> roleToChildCollection = new HashMap<SContainmentLink, List<SNode>>();
    // FIXME if(!myRespectCommentedOutNodes), but can't use it here as it's a static method with quite some uses
    //      What I need is to synchronise knowledge about whether to stick to single role or look into combination with
    //      respective ChildAttributes under smodelAttributes role throughout few classes, where this assumption is now
    //      scattered around and is not apparent.
    if (RuntimeFlags.isMergeDriverMode()) {
      // see MPS-35421, SNodeOperations.getContainingLinkInChildrenAndChildAttributesCollection() case
      ListSequence.fromList(SNodeOperations.getChildren(node)).visitAll((child) -> roleToChildCollection.computeIfAbsent(child.getContainmentLink(), (SContainmentLink link) -> ListSequence.fromList(new ArrayList<SNode>())).add(child));
    } else {
      ListSequence.fromList(SNodeOperations.getChildren(node)).visitAll((child) -> {
        SContainmentLink link = SNodeOperations.getContainingLinkInChildrenAndChildAttributesCollection(child);
        roleToChildCollection.computeIfAbsent(link, new Function<SContainmentLink, List<SNode>>() {
          public IListSequence<SNode> apply(SContainmentLink link) {
            return ListSequence.fromList(new ArrayList<SNode>());
          }
        }).add(child);
      });
    }
    return roleToChildCollection;
  }

  public void buildForNodeRole(final List<SNode> oldChildren, List<SNode> newChildren, final SNodeId oldParentId, final SNodeId newParentId, final SContainmentLink role) {

    final List<SNodeId> oldIds = ListSequence.fromList(oldChildren).select((n) -> n.getNodeId()).toList();
    final List<SNodeId> newIds = ListSequence.fromList(newChildren).select((n) -> n.getNodeId()).toList();

    if (Objects.equals(oldIds, newIds)) {
      for (SNode child : ListSequence.fromList(oldChildren)) {
        buildForNode(child, getNewNode(child.getNodeId()));
      }
      return;
    }

    LongestCommonSubsequenceFinder<SNodeId> finder = new LongestCommonSubsequenceFinder<SNodeId>(oldIds, newIds);
    List<Tuples._2<Integer, Integer>> commonIndices = finder.getCommonIndices();
    ListSequence.fromList(commonIndices).select((in) -> ListSequence.fromList(oldChildren).getElement((int) in._0())).visitAll((child) -> buildForNode(child, getNewNode(child.getNodeId())));

    // Finding inserts, deletions and replacements
    for (Tuples._2<Tuples._2<Integer, Integer>, Tuples._2<Integer, Integer>> indices : ListSequence.fromList(finder.getDifferentIndices())) {
      final List<SNodeId> oldIds1 = ListSequence.fromList(oldIds).page((int) indices._0()._0(), (int) indices._0()._1()).toList();
      List<SNodeId> newIds1 = ListSequence.fromList(newIds).page((int) indices._1()._0(), (int) indices._1()._1()).toList();
      final Map<SNodeId, SNodeId> newToOldMap = MapSequence.fromMap(new HashMap<SNodeId, SNodeId>());
      for (SNodeId oldNodeId : oldIds1) {
        for (SNodeId newNodeId : newIds1) {
          if (MapSequence.fromMap(newToOldMap).containsKey(newNodeId)) {
            continue;
          }
          if (DiffUtil.nodesDifferByIdOnly(myOldModel, getOldNode(oldNodeId), getNewNode(newNodeId))) {
            MapSequence.fromMap(newToOldMap).put(newNodeId, oldNodeId);
            break;
          }
        }
      }
      if (MapSequence.fromMap(newToOldMap).isEmpty()) {
        ListSequence.fromList(myNewChanges).addElement(new NodeGroupChange(myChangeSet, oldParentId, newParentId, role, (int) indices._0()._0(), (int) indices._0()._1(), (int) indices._1()._0(), (int) indices._1()._1()));
        continue;
      }
      List<SNodeId> newIds2 = ListSequence.fromList(newIds1).select((it) -> (MapSequence.fromMap(newToOldMap).containsKey(it) ? MapSequence.fromMap(newToOldMap).get(it) : it)).toList();
      LongestCommonSubsequenceFinder<SNodeId> finder1 = new LongestCommonSubsequenceFinder<SNodeId>(oldIds1, newIds2);
      List<Tuples._2<Integer, Integer>> commonIndices1 = finder1.getCommonIndices();
      for (Tuples._2<Tuples._2<Integer, Integer>, Tuples._2<Integer, Integer>> indices1 : ListSequence.fromList(finder1.getDifferentIndices())) {
        int oldStart1 = (int) indices1._0()._0();
        int oldEnd1 = (int) indices1._0()._1();
        int newStart1 = (int) indices1._1()._0();
        int newEnd1 = (int) indices1._1()._1();
        int oldStart = (oldStart1 < ListSequence.fromList(oldIds1).count() ? ListSequence.fromList(oldIds).indexOf(ListSequence.fromList(oldIds1).getElement(oldStart1)) : (int) indices._0()._1());
        int oldEnd = (oldEnd1 < ListSequence.fromList(oldIds1).count() ? ListSequence.fromList(oldIds).indexOf(ListSequence.fromList(oldIds1).getElement(oldEnd1)) : (int) indices._0()._1());
        int newStart;
        if (newStart1 < ListSequence.fromList(newIds2).count()) {
          final Wrappers._T<SNodeId> newNodeId = new Wrappers._T<SNodeId>(ListSequence.fromList(newIds2).getElement(newStart1));
          if (MapSequence.fromMap(newToOldMap).containsValue(newNodeId.value)) {
            newNodeId.value = check_nbyrtw_a0a0b0h0i0l0db(MapSequence.fromMap(newToOldMap).findFirst((it) -> Objects.equals(it.value(), newNodeId.value)));
          }
          newStart = ListSequence.fromList(newIds).indexOf(newNodeId.value);
        } else {
          newStart = (int) indices._1()._1();
        }
        int newEnd;
        if (newEnd1 < ListSequence.fromList(newIds2).count()) {
          final Wrappers._T<SNodeId> newNodeId = new Wrappers._T<SNodeId>(ListSequence.fromList(newIds2).getElement(newEnd1));
          if (MapSequence.fromMap(newToOldMap).containsValue(newNodeId.value)) {
            newNodeId.value = check_nbyrtw_a0a0b0j0i0l0db(MapSequence.fromMap(newToOldMap).findFirst((it) -> Objects.equals(it.value(), newNodeId.value)));
          }
          newEnd = ListSequence.fromList(newIds).indexOf(newNodeId.value);
        } else {
          newEnd = (int) indices._1()._1();
        }
        ListSequence.fromList(myNewChanges).addElement(new NodeGroupChange(myChangeSet, oldParentId, newParentId, role, oldStart, oldEnd, newStart, newEnd));
      }
      // Finding changes for children
      ListSequence.fromList(commonIndices1).select((in) -> ListSequence.fromList(oldIds1).getElement((int) in._0())).visitAll((final SNodeId oldNodeId) -> {
        SNodeId newNodeId = check_nbyrtw_a0a0a0a01a11a92(MapSequence.fromMap(newToOldMap).findFirst((it) -> Objects.equals(it.value(), oldNodeId)));
        assert newNodeId != null;
        assert ListSequence.fromList(oldIds).contains(oldNodeId) && ListSequence.fromList(newIds).contains(newNodeId);
        ListSequence.fromList(myNewChanges).addElement(new NodeIdChange(myChangeSet, oldParentId, newParentId, role, oldNodeId, newNodeId));
        buildForNode(getOldNode(oldNodeId), getNewNode(newNodeId));
      });
    }
  }

  private <D> void buildAddedAndDeletedDependencies(_FunctionTypes._return_P1_E0<? extends Iterable<D>, ? super SModelBase> referencesExtractor, final _FunctionTypes._return_P2_E0<? extends DependencyChange, ? super D, ? super Boolean> changeCreator) {
    Iterable<D> added;
    Iterable<D> deleted;
    {
      Tuples._2<Iterable<D>, Iterable<D>> _tmp_nbyrtw_c0fb = getAddedAndDeleted(referencesExtractor);
      added = _tmp_nbyrtw_c0fb._0();
      deleted = _tmp_nbyrtw_c0fb._1();
    }
    ListSequence.fromList(myNewChanges).addSequence(Sequence.fromIterable(added).select((r) -> changeCreator.invoke(r, false)));
    ListSequence.fromList(myNewChanges).addSequence(Sequence.fromIterable(deleted).select((r) -> changeCreator.invoke(r, true)));
  }

  private void buildForImports() {
    _FunctionTypes._return_P1_E0<? extends Iterable<SModelReference>, ? super SModelBase> importedModelsExtractor = (SModelBase model) -> SModelOperations.getImportedModelUIDs(model);
    _FunctionTypes._return_P2_E0<? extends ImportedModelChange, ? super SModelReference, ? super Boolean> changeCreator = (SModelReference mr, Boolean deleted) -> new ImportedModelChange(myChangeSet, mr, deleted);
    buildAddedAndDeletedDependencies(importedModelsExtractor, changeCreator);
  }

  private void buildForDependencies(final ModuleDependencyChange.DependencyType dependencyType, _FunctionTypes._return_P1_E0<? extends Iterable<SModuleReference>, ? super SModelBase> referencesExtractor) {
    _FunctionTypes._return_P2_E0<? extends ModuleDependencyChange, ? super SModuleReference, ? super Boolean> changeCreator = (SModuleReference mr, Boolean deleted) -> new ModuleDependencyChange(myChangeSet, mr, dependencyType, deleted);
    buildAddedAndDeletedDependencies(referencesExtractor, changeCreator);
  }

  private void buildForUsedLanguages() {
    SModelBase oldModel = (SModelBase) myOldModel;
    SModelBase newModel = (SModelBase) myNewModel;
    Set<SLanguage> oldLanguages = SetSequence.fromSetWithValues(new HashSet<SLanguage>(), oldModel.importedLanguageIds());
    Set<SLanguage> newLanguages = SetSequence.fromSetWithValues(new HashSet<SLanguage>(), newModel.importedLanguageIds());
    // deleted
    for (SLanguage lang : SetSequence.fromSet(oldLanguages).subtract(SetSequence.fromSet(newLanguages))) {
      int oldVersion = oldModel.getLanguageImportVersion(lang);
      UsedLanguageChange change = new UsedLanguageChange(myChangeSet, lang, oldVersion, ChangeType.DELETE);
      ListSequence.fromList(myNewChanges).addElement(change);
    }
    // added & changed
    for (SLanguage lang : SetSequence.fromSet(newLanguages)) {
      int newVersion = newModel.getLanguageImportVersion(lang);
      if (!(SetSequence.fromSet(oldLanguages).contains(lang))) {
        UsedLanguageChange change = new UsedLanguageChange(myChangeSet, lang, newVersion, ChangeType.ADD);
        ListSequence.fromList(myNewChanges).addElement(change);
      } else if (oldModel.getLanguageImportVersion(lang) != newVersion) {
        UsedLanguageChange change = new UsedLanguageChange(myChangeSet, lang, newVersion, ChangeType.CHANGE);
        ListSequence.fromList(myNewChanges).addElement(change);
      }
    }
  }

  private void buildForLanguagesEngagedOnGeneration() {
    buildAddedAndDeletedDependencies((SModelBase m) -> m.getLanguagesEngagedOnGeneration(), (SLanguage l, Boolean deleted) -> new EngagedLanguageChange(myChangeSet, deleted, l));
  }

  private void buildForMetadata() {
    buildForImports();

    buildForUsedLanguages();
    buildForDependencies(ModuleDependencyChange.DependencyType.USED_DEVKIT, (SModelBase model) -> model.importedDevkits());
    buildForLanguagesEngagedOnGeneration();

    if (myNewModel instanceof ModelWithAttributes && myOldModel instanceof ModelWithAttributes) {
      ModelWithAttributes newModel = (ModelWithAttributes) myNewModel;
      final ModelWithAttributes oldModel = (ModelWithAttributes) myOldModel;

      final Set<String> seenAttr = new HashSet<String>();
      newModel.forEachAttribute((String k, String v) -> {
        seenAttr.add(k);
        String oldv = oldModel.getAttribute(k);
        if (!(Objects.equals(v, oldv))) {
          ListSequence.fromList(myNewChanges).addElement(new ModelAttributeChange(myChangeSet, k, v));
        }
      });
      // check if any attribute has been deleted in the new version of the model
      oldModel.forEachAttribute((String k, String v) -> {
        if (!(seenAttr.contains(k))) {
          ListSequence.fromList(myNewChanges).addElement(new ModelAttributeChange(myChangeSet, k, null));
        }
      });
    }
  }

  private void build(boolean withOpposite) {
    Iterable<SNodeId> allRootIds = ListSequence.fromList(jetbrains.mps.lang.smodel.generator.smodelAdapter.SModelOperations.roots(myOldModel, null)).concat(ListSequence.fromList(jetbrains.mps.lang.smodel.generator.smodelAdapter.SModelOperations.roots(myNewModel, null))).select((n) -> n.getNodeId());
    for (SNodeId rootId : SetSequence.fromSetWithValues(new HashSet<SNodeId>(), allRootIds)) {
      buildForRoot(getOldNode(rootId), getNewNode(rootId));
    }

    buildForMetadata();
    commit();
    if (withOpposite) {
      myChangeSet.buildOppositeChangeSet();
    }
  }

  public void commit() {
    ListSequence.fromList(myNewChanges).visitAll((it) -> myChangeSet.add(it));
    ListSequence.fromList(myNewChanges).clear();
    myChangeSet.fillRootToChange();
  }

  public List<ModelChange> getNewChanges() {
    return myNewChanges;
  }

  private <D> Tuples._2<Iterable<D>, Iterable<D>> getAddedAndDeleted(Iterable<D> oldItems, Iterable<D> newItems) {
    Set<D> oldSet = SetSequence.fromSetWithValues(new HashSet<D>(), oldItems);
    Set<D> newSet = SetSequence.fromSetWithValues(new HashSet<D>(), newItems);
    return MultiTuple.<Iterable<D>,Iterable<D>>from(SetSequence.fromSet(newSet).subtract(SetSequence.fromSet(oldSet)), SetSequence.fromSet(oldSet).subtract(SetSequence.fromSet(newSet)));
  }

  private <D> Tuples._2<Iterable<D>, Iterable<D>> getAddedAndDeleted(_FunctionTypes._return_P1_E0<? extends Iterable<D>, ? super SModelBase> itemsExtractor) {
    return getAddedAndDeleted(itemsExtractor.invoke(as_nbyrtw_a0a0a0zb(myOldModel, SModelBase.class)), itemsExtractor.invoke(as_nbyrtw_a0b0a0zb(myNewModel, SModelBase.class)));
  }

  @NotNull
  public static ModelChangeSet buildChangeSet(@NotNull SModel oldModel, @NotNull SModel newModel) {
    return buildChangeSet(oldModel, newModel, false, false);
  }

  @NotNull
  public static ModelChangeSet buildChangeSet(@NotNull SModel oldModel, @NotNull SModel newModel, boolean withOpposite, boolean trackMovedNodes) {
    ChangeSetBuilder builder = new ChangeSetBuilder(oldModel, newModel, trackMovedNodes);
    builder.build(withOpposite);
    return builder.myChangeSet;
  }

  @NotNull
  public static ModelChangeSet buildChangeSet(@NotNull SModel oldModel, @NotNull SModel newModel, boolean withOpposite) {
    return buildChangeSet(oldModel, newModel, withOpposite, false);
  }

  public static boolean hasChangesForNodeId(@NotNull SModel oldModel, @NotNull SModel newModel, SNodeId nodeId) {
    SNode oldNode = oldModel.getNode(nodeId);
    SNode newNode = newModel.getNode(nodeId);
    return MovesAwareChangeSetBuilder.hasChanges(oldNode, newNode, new ChangeSetImpl(oldModel, newModel));
  }

  public static ModelChangeSet buildChangeSetForNode(@NotNull SModel oldModel, @NotNull SModel newModel, SNodeId rootId, boolean withOpposite, boolean trackMovedNodes) {
    ChangeSetBuilder builder = new ChangeSetBuilder(oldModel, newModel, trackMovedNodes);
    SNode oldNode = oldModel.getNode(rootId);
    SNode newNode = newModel.getNode(rootId);
    if ((oldNode != null) || (newNode != null)) {
      builder.buildChangeSetForNode(oldNode, newNode);
      builder.commit();
      if (withOpposite) {
        builder.myChangeSet.buildOppositeChangeSet();
      }
    }
    return builder.myChangeSet;
  }

  public static void rebuildChangeSet(ChangeSet changeSet, boolean trackMovedNodes) {
    ChangeSetImpl impl = (ChangeSetImpl) changeSet;
    impl.clear();
    impl.clearOppositeChangeSet();
    ChangeSetBuilder builder = new ChangeSetBuilder(impl, trackMovedNodes);
    builder.build(true);
  }

  public static void rebuildChangeSet(ChangeSet changeSet) {
    rebuildChangeSet(changeSet, false);
  }

  public static ChangeSetBuilder createBuilder(ChangeSet changeSet) {
    return new ChangeSetBuilder((ChangeSetImpl) changeSet, false);
  }
  private static SNodeId check_nbyrtw_a0a0b0h0i0l0db(IMapping<SNodeId, SNodeId> checkedDotOperand) {
    if (null != checkedDotOperand) {
      return checkedDotOperand.key();
    }
    return null;
  }
  private static SNodeId check_nbyrtw_a0a0b0j0i0l0db(IMapping<SNodeId, SNodeId> checkedDotOperand) {
    if (null != checkedDotOperand) {
      return checkedDotOperand.key();
    }
    return null;
  }
  private static SNodeId check_nbyrtw_a0a0a0a01a11a92(IMapping<SNodeId, SNodeId> checkedDotOperand) {
    if (null != checkedDotOperand) {
      return checkedDotOperand.key();
    }
    return null;
  }
  private static <T> T as_nbyrtw_a0a0a0zb(Object o, Class<T> type) {
    return (type.isInstance(o) ? (T) o : null);
  }
  private static <T> T as_nbyrtw_a0b0a0zb(Object o, Class<T> type) {
    return (type.isInstance(o) ? (T) o : null);
  }
}
