private static void collectResults()

in litho-core/src/main/java/com/facebook/litho/LayoutState.java [531:932]


  private static void collectResults(
      final ComponentContext parentContext,
      final LithoLayoutResult result,
      final LithoNode node,
      final LayoutState layoutState,
      @Nullable RenderTreeNode parent,
      final @Nullable DiffNode parentDiffNode,
      final @Nullable DebugHierarchy.Node parentHierarchy) {
    final LayoutStateContext layoutStateContext = layoutState.getLayoutStateContext();

    if (layoutStateContext.isLayoutReleased()) {
      return;
    }

    final Component component = node.getTailComponent();
    final boolean isTracing = ComponentsSystrace.isTracing();

    final DebugHierarchy.Node hierarchy = getDebugHierarchy(parentHierarchy, node);

    // Early return if collecting results of a node holding a nested tree.
    if (result instanceof NestedTreeHolderResult) {
      // If the nested tree is defined, it has been resolved during a measure call during
      // layout calculation.
      if (isTracing) {
        ComponentsSystrace.beginSectionWithArgs("resolveNestedTree:" + component.getSimpleName())
            .arg("widthSpec", "EXACTLY " + result.getWidth())
            .arg("heightSpec", "EXACTLY " + result.getHeight())
            .arg("rootComponentId", node.getTailComponent().getId())
            .flush();
      }

      final int size = node.getComponentCount();
      final ComponentContext immediateParentContext;
      if (size == 1) {
        immediateParentContext = parentContext;
      } else {
        immediateParentContext = node.getComponentContextAt(1);
      }

      LithoLayoutResult nestedTree =
          Layout.create(
              layoutStateContext,
              Preconditions.checkNotNull(immediateParentContext),
              (NestedTreeHolderResult) result,
              SizeSpec.makeSizeSpec(result.getWidth(), EXACTLY),
              SizeSpec.makeSizeSpec(result.getHeight(), EXACTLY));

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

      if (nestedTree == null) {
        return;
      }

      // Account for position of the holder node.
      layoutState.mCurrentX += result.getX();
      layoutState.mCurrentY += result.getY();

      collectResults(
          parentContext,
          nestedTree,
          nestedTree.getNode(),
          layoutState,
          parent,
          parentDiffNode,
          hierarchy);

      layoutState.mCurrentX -= result.getX();
      layoutState.mCurrentY -= result.getY();

      return;
    }

    final ScopedComponentInfo tail = node.getTailScopedComponentInfo();
    final ComponentContext context = tail.getContext();
    final boolean shouldGenerateDiffTree = layoutState.mShouldGenerateDiffTree;
    final DiffNode diffNode;

    if (shouldGenerateDiffTree) {
      diffNode = createDiffNode(tail, parentDiffNode);
      if (parentDiffNode == null) {
        layoutState.mDiffTreeRoot = diffNode;
      }
    } else {
      diffNode = null;
    }

    // The last measured specs, and measurements need to be explicitly
    // set on the LayoutResult if measure was not called for it. This
    // will ensure layout diffing works in subsequent layouts for these
    // results.
    if (ComponentsConfiguration.alwaysWriteDiffNodes && !result.wasMeasured()) {
      final int widthSpec = exactly(result.getWidth());
      final int heightSpec = exactly(result.getHeight());
      result.setLastWidthSpec(widthSpec);
      result.setLastHeightSpec(heightSpec);
      result.setLastMeasuredWidth(result.getWidth());
      result.setLastMeasuredHeight(result.getHeight());
    }

    final @Nullable LithoRenderUnit hostRenderUnit = result.getHostRenderUnit();
    final boolean needsHostView = hostRenderUnit != null;

    final long currentHostMarker = layoutState.mCurrentHostMarker;
    final int currentHostOutputPosition = layoutState.mCurrentHostOutputPosition;

    final TransitionId currentTransitionId = layoutState.mCurrentTransitionId;
    final OutputUnitsAffinityGroup<AnimatableItem> currentLayoutOutputAffinityGroup =
        layoutState.mCurrentLayoutOutputAffinityGroup;

    layoutState.mCurrentTransitionId = getTransitionIdForNode(node);
    layoutState.mCurrentLayoutOutputAffinityGroup =
        layoutState.mCurrentTransitionId != null
            ? new OutputUnitsAffinityGroup<AnimatableItem>()
            : null;

    // 1. Insert a host LayoutOutput if we have some interactive content to be attached to.
    if (hostRenderUnit != null) {
      final int hostLayoutPosition =
          addHostRenderTreeNode(
              hostRenderUnit, parent, result, node, layoutState, diffNode, hierarchy);
      addCurrentAffinityGroupToTransitionMapping(layoutState);

      parent = layoutState.mMountableOutputs.get(hostLayoutPosition);

      layoutState.mCurrentLevel++;
      layoutState.mCurrentHostMarker = parent.getRenderUnit().getId();
      layoutState.mCurrentHostOutputPosition = hostLayoutPosition;
    }

    // We need to take into account flattening when setting duplicate parent state. The parent after
    // flattening may no longer exist. Therefore the value of duplicate parent state should only be
    // true if the path between us (inclusive) and our inner/root host (exclusive) all are
    // duplicate parent state.
    final boolean shouldDuplicateParentState = layoutState.mShouldDuplicateParentState;
    layoutState.mShouldDuplicateParentState =
        needsHostView
            || layoutState.isLayoutRoot(result)
            || (shouldDuplicateParentState && node.isDuplicateParentStateEnabled());

    // 2. Add background if defined.
    if (!layoutState.mShouldDisableDrawableOutputs) {
      final LithoRenderUnit backgroundRenderUnit = result.getBackgroundRenderUnit();
      if (backgroundRenderUnit != null) {
        final RenderTreeNode backgroundRenderTreeNode =
            addDrawableRenderTreeNode(
                backgroundRenderUnit,
                parent,
                result,
                layoutState,
                hierarchy,
                OutputUnitType.BACKGROUND,
                needsHostView);

        if (diffNode != null) {
          diffNode.setBackgroundOutput((LithoRenderUnit) backgroundRenderTreeNode.getRenderUnit());
        }
      }
    }

    // Generate the RenderTreeNode for the given node.
    final @Nullable RenderTreeNode contentRenderTreeNode =
        createContentRenderTreeNode(result, node, layoutState, parent);

    // 3. Now add the MountSpec (either View or Drawable) to the outputs.
    if (contentRenderTreeNode != null) {
      final LithoRenderUnit contentRenderUnit =
          (LithoRenderUnit) contentRenderTreeNode.getRenderUnit();
      final LayoutOutput contentLayoutOutput = contentRenderUnit.getLayoutOutput();
      final LithoLayoutData layoutData =
          (LithoLayoutData) Preconditions.checkNotNull(contentRenderTreeNode.getLayoutData());

      // Notify component about its final size.
      if (isTracing) {
        ComponentsSystrace.beginSection("onBoundsDefined:" + component.getSimpleName());
      }

      try {
        if (isMountSpec(component) && !isMountable(component)) {
          component.onBoundsDefined(
              context, result, (InterStagePropsContainer) layoutData.mLayoutData);
        }
      } catch (Exception e) {
        ComponentUtils.handleWithHierarchy(context, component, e);
      } finally {
        if (isTracing) {
          ComponentsSystrace.endSection();
        }
      }

      addRenderTreeNode(
          layoutState,
          contentRenderTreeNode,
          contentRenderUnit,
          contentLayoutOutput,
          OutputUnitType.CONTENT,
          !needsHostView ? layoutState.mCurrentTransitionId : null,
          parent);

      if (diffNode != null) {
        diffNode.setContentOutput(contentRenderUnit);
      }

      if (hierarchy != null) {
        contentLayoutOutput.setHierarchy(hierarchy.mutateType(OutputUnitType.CONTENT));
      }
    }

    // 4. Extract the Transitions.
    if (Layout.areTransitionsEnabled(context)) {
      final ArrayList<Transition> transitions = node.getTransitions();
      if (transitions != null) {
        for (int i = 0, size = transitions.size(); i < size; i++) {
          final Transition transition = transitions.get(i);
          if (layoutState.mTransitions == null) {
            layoutState.mTransitions = new ArrayList<>();
          }
          TransitionUtils.addTransitions(
              transition, layoutState.mTransitions, layoutState.mRootComponentName);
        }
      }

      final @Nullable Map<String, ScopedComponentInfo>
          scopedComponentInfosNeedingPreviousRenderData =
              node.getScopedComponentInfosNeedingPreviousRenderData();

      if (scopedComponentInfosNeedingPreviousRenderData != null) {

        if (layoutState.mScopedComponentInfosNeedingPreviousRenderData == null) {
          layoutState.mScopedComponentInfosNeedingPreviousRenderData = new ArrayList<>();
        }

        for (Map.Entry<String, ScopedComponentInfo> entry :
            scopedComponentInfosNeedingPreviousRenderData.entrySet()) {
          layoutState.mScopedComponentInfosNeedingPreviousRenderData.add(entry.getValue());
        }
      }
    }

    layoutState.mCurrentX += result.getX();
    layoutState.mCurrentY += result.getY();

    // We must process the nodes in order so that the layout state output order is correct.
    for (int i = 0, size = result.getChildCount(); i < size; i++) {
      final LithoLayoutResult child = result.getChildAt(i);
      collectResults(context, child, child.getNode(), layoutState, parent, diffNode, hierarchy);
    }

    layoutState.mCurrentX -= result.getX();
    layoutState.mCurrentY -= result.getY();

    // 5. Add border color if defined.
    final LithoRenderUnit borderRenderUnit = result.getBorderRenderUnit();
    if (borderRenderUnit != null) {
      final RenderTreeNode borderRenderTreeNode =
          addDrawableRenderTreeNode(
              borderRenderUnit,
              parent,
              result,
              layoutState,
              hierarchy,
              OutputUnitType.BORDER,
              needsHostView);

      if (diffNode != null) {
        diffNode.setBorderOutput((LithoRenderUnit) borderRenderTreeNode.getRenderUnit());
      }
    }

    // 6. Add foreground if defined.
    if (!layoutState.mShouldDisableDrawableOutputs) {
      final @Nullable LithoRenderUnit foregroundRenderUnit = result.getForegroundRenderUnit();
      if (foregroundRenderUnit != null) {
        final RenderTreeNode foregroundRenderTreeNode =
            addDrawableRenderTreeNode(
                foregroundRenderUnit,
                parent,
                result,
                layoutState,
                hierarchy,
                OutputUnitType.FOREGROUND,
                needsHostView);

        if (diffNode != null) {
          diffNode.setForegroundOutput((LithoRenderUnit) foregroundRenderTreeNode.getRenderUnit());
        }
      }
    }

    if (diffNode != null) {
      diffNode.setLastWidthSpec(result.getLastWidthSpec());
      diffNode.setLastHeightSpec(result.getLastHeightSpec());
      diffNode.setLastMeasuredWidth(result.getLastMeasuredWidth());
      diffNode.setLastMeasuredHeight(result.getLastMeasuredHeight());
      diffNode.setLayoutData(result.getLayoutData());
      diffNode.setMountable(result.getNode().getMountable());
    }

    // 7. Add VisibilityOutputs if any visibility-related event handlers are present.
    if (node.hasVisibilityHandlers()) {
      final VisibilityOutput visibilityOutput =
          createVisibilityOutput(
              result,
              node,
              layoutState,
              contentRenderTreeNode != null
                  ? contentRenderTreeNode
                  : needsHostView ? parent : null);

      layoutState.mVisibilityOutputs.add(visibilityOutput);

      if (diffNode != null) {
        diffNode.setVisibilityOutput(visibilityOutput);
      }
    }

    // 8. If we're in a testing environment, maintain an additional data structure with
    // information about nodes that we can query later.
    if (layoutState.mTestOutputs != null && !TextUtils.isEmpty(node.getTestKey())) {
      final TestOutput testOutput =
          createTestOutput(
              result,
              node,
              layoutState,
              contentRenderTreeNode != null
                  ? (LithoRenderUnit) contentRenderTreeNode.getRenderUnit()
                  : null);
      layoutState.mTestOutputs.add(testOutput);
    }

    // 9. Extract the Working Range registrations.
    List<WorkingRangeContainer.Registration> registrations = node.getWorkingRangeRegistrations();
    if (registrations != null && !registrations.isEmpty()) {
      if (layoutState.mWorkingRangeContainer == null) {
        layoutState.mWorkingRangeContainer = new WorkingRangeContainer();
      }

      for (WorkingRangeContainer.Registration registration : registrations) {
        layoutState.mWorkingRangeContainer.registerWorkingRange(
            registration.mName, registration.mWorkingRange, registration.mScopedComponentInfo);
      }
    }

    final List<Attachable> attachables = result.getNode().getAttachables();
    if (attachables != null) {
      if (layoutState.mAttachables == null) {
        layoutState.mAttachables = new ArrayList<>();
      }
      layoutState.mAttachables.addAll(attachables);
    }

    final Rect rect;
    if (contentRenderTreeNode != null) {
      rect = contentRenderTreeNode.getAbsoluteBounds(new Rect());
    } else {
      rect = new Rect();
      rect.left = layoutState.mCurrentX + result.getX();
      rect.top = layoutState.mCurrentY + result.getY();
      rect.right = rect.left + result.getWidth();
      rect.bottom = rect.top + result.getHeight();
    }

    for (int i = 0, size = node.getComponentCount(); i < size; i++) {
      final Component delegate = node.getComponentAt(i);
      final String delegateKey = node.getGlobalKeyAt(i);
      // Keep a list of the components we created during this layout calculation. If the layout is
      // valid, the ComponentTree will update the event handlers that have been created in the
      // previous ComponentTree with the new component dispatched, otherwise Section children
      // might not be accessing the correct props and state on the event handlers. The null
      // checkers cover tests, the scope and tree should not be null at this point of the layout
      // calculation.
      final ComponentContext delegateScopedContext = node.getComponentContextAt(i);
      if (delegateScopedContext != null && delegateScopedContext.getComponentTree() != null) {
        if (layoutState.mScopedComponentInfos != null) {
          layoutState.mScopedComponentInfos.add(delegateScopedContext.getScopedComponentInfo());
        }
      }
      if (delegateKey != null || delegate.hasHandle()) {
        Rect copyRect = new Rect(rect);
        if (delegateKey != null) {
          layoutState.mComponentKeyToBounds.put(delegateKey, copyRect);
        }
        if (delegate.hasHandle()) {
          layoutState.mComponentHandleToBounds.put(delegate.getHandle(), copyRect);
        }
      }
    }

    // All children for the given host have been added, restore the previous
    // host, level, and duplicate parent state value in the recursive queue.
    if (layoutState.mCurrentHostMarker != currentHostMarker) {
      layoutState.mCurrentHostMarker = currentHostMarker;
      layoutState.mCurrentHostOutputPosition = currentHostOutputPosition;
      layoutState.mCurrentLevel--;
    }
    layoutState.mShouldDuplicateParentState = shouldDuplicateParentState;

    addCurrentAffinityGroupToTransitionMapping(layoutState);
    layoutState.mCurrentTransitionId = currentTransitionId;
    layoutState.mCurrentLayoutOutputAffinityGroup = currentLayoutOutputAffinityGroup;
  }