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;
}