private static ChangeSet generateChangeSetRecursive()

in litho-sections-core/src/main/java/com/facebook/litho/sections/ChangeSetState.java [166:342]


  private static ChangeSet generateChangeSetRecursive(
      SectionContext sectionContext,
      @Nullable Section currentRoot,
      @Nullable Section newRoot,
      List<Section> removedComponents,
      SectionsDebugLogger sectionsDebugLogger,
      String sectionTreeTag,
      String currentPrefix,
      String newPrefix,
      String thread,
      boolean enableStats) {

    boolean currentRootIsNull = currentRoot == null;
    boolean newRootIsNull = newRoot == null;

    if (currentRootIsNull && newRootIsNull) {
      throw new IllegalStateException("Both currentRoot and newRoot are null.");
    }

    if (newRootIsNull) {
      // The new tree doesn't have this component. We only need to remove all its children from
      // the list.
      final int currentItemsCount = currentRoot.getCount();
      removedComponents.add(currentRoot);
      final ChangeSet changeSet =
          ChangeSet.acquireChangeSet(currentRoot.getCount(), newRoot, enableStats);

      for (int i = 0; i < currentItemsCount; i++) {
        changeSet.addChange(Change.remove(0));
      }

      return changeSet;
    }

    final SectionLifecycle lifecycle = newRoot;
    final String updateCurrentPrefix = updatePrefix(currentRoot, currentPrefix);
    final String updateNewPrefix = updatePrefix(newRoot, newPrefix);

    // Components both exist and don't need to update.
    if (!currentRootIsNull && !lifecycle.shouldComponentUpdate(currentRoot, newRoot)) {
      final ChangeSet changeSet =
          ChangeSet.acquireChangeSet(currentRoot.getCount(), newRoot, enableStats);
      newRoot.setCount(changeSet.getCount());
      sectionsDebugLogger.logShouldUpdate(
          sectionTreeTag,
          currentRoot,
          newRoot,
          updateCurrentPrefix,
          updateNewPrefix,
          false,
          thread);
      return changeSet;
    }

    sectionsDebugLogger.logShouldUpdate(
        sectionTreeTag, currentRoot, newRoot, updateCurrentPrefix, updateNewPrefix, true, thread);

    // Component(s) can generate changeSets and will generate the changeset.
    // Add the startCount to the changeSet.
    if (lifecycle.isDiffSectionSpec()) {
      final boolean isTracing = ComponentsSystrace.isTracing();
      if (isTracing) {
        ComponentsSystrace.beginSectionWithArgs("generateChangeSet")
            .arg("current_root", currentRootIsNull ? "<null>" : currentRoot.getKey())
            .arg("update_prefix", updateCurrentPrefix)
            .flush();
      }

      final ChangeSet changeSet =
          ChangeSet.acquireChangeSet(
              currentRootIsNull ? 0 : currentRoot.getCount(), newRoot, enableStats);
      final SectionContext newRootScopedContext = newRoot.getScopedContext();
      final SectionContext currentRootScopedContext =
          currentRoot == null ? null : currentRoot.getScopedContext();
      lifecycle.generateChangeSet(
          newRootScopedContext,
          changeSet,
          currentRootScopedContext,
          currentRoot,
          newRootScopedContext,
          newRoot);
      newRoot.setCount(changeSet.getCount());

      if (isTracing) {
        ComponentsSystrace.endSection();
      }

      return changeSet;
    }

    ChangeSet resultChangeSet = ChangeSet.acquireChangeSet(newRoot, enableStats);

    final Map<String, Pair<Section, Integer>> currentChildren = acquireChildrenMap(currentRoot);
    final Map<String, Pair<Section, Integer>> newChildren = acquireChildrenMap(newRoot);

    final List<Section> currentChildrenList;
    if (currentRoot == null || currentRoot.getChildren() == null) {
      currentChildrenList = sEmptyList;
    } else {
      currentChildrenList = new ArrayList<>(currentRoot.getChildren());
    }

    final List<Section> newChildrenList;
    if (newRoot.getChildren() == null) {
      newChildrenList = sEmptyList;
    } else {
      newChildrenList = newRoot.getChildren();
    }

    // Determine Move Changes.
    // Index of a section that was detected as moved.
    // Components that have swapped order with this one in the new list will be moved.
    int sectionToSwapIndex = -1;
    int swapToIndex = -1;

    for (int i = 0; i < newChildrenList.size(); i++) {
      final String key = newChildrenList.get(i).getGlobalKey();

      if (currentChildren.containsKey(key)) {
        final Pair<Section, Integer> valueAndPosition = currentChildren.get(key);
        final Section current = valueAndPosition.first;
        final int currentIndex = valueAndPosition.second;

        // We found something that swapped order with the moved section.
        if (sectionToSwapIndex > currentIndex) {

          for (int c = 0; c < current.getCount(); c++) {
            resultChangeSet.addChange(
                Change.move(getPreviousChildrenCount(currentChildrenList, key), swapToIndex));
          }

          // Place this section in the correct order in the current children list.
          currentChildrenList.remove(currentIndex);
          currentChildrenList.add(sectionToSwapIndex, current);
          for (int j = 0, size = currentChildrenList.size(); j < size; j++) {
            final Section section = currentChildrenList.get(j);
            final Pair<Section, Integer> valueAndIndex =
                currentChildren.get(section.getGlobalKey());

            if (valueAndIndex.second != j) {
              currentChildren.put(section.getGlobalKey(), new Pair<>(valueAndIndex.first, j));
            }
          }
        } else if (currentIndex > sectionToSwapIndex) { // We found something that was moved.
          sectionToSwapIndex = currentIndex;
          swapToIndex =
              getPreviousChildrenCount(currentChildrenList, key)
                  + currentChildrenList.get(sectionToSwapIndex).getCount()
                  - 1;
        }
      }
    }

    final SparseArray<ChangeSet> changeSets =
        generateChildrenChangeSets(
            sectionContext,
            currentChildren,
            newChildren,
            currentChildrenList,
            newChildrenList,
            removedComponents,
            sectionsDebugLogger,
            sectionTreeTag,
            updateCurrentPrefix,
            updateNewPrefix,
            thread,
            enableStats);

    for (int i = 0, size = changeSets.size(); i < size; i++) {
      ChangeSet changeSet = changeSets.valueAt(i);
      resultChangeSet = ChangeSet.merge(resultChangeSet, changeSet);
    }

    newRoot.setCount(resultChangeSet.getCount());

    return resultChangeSet;
  }