getUpdatesWithData method

Iterable<DataDiffUpdate<T>> getUpdatesWithData()

Implementation

Iterable<DataDiffUpdate<T>> getUpdatesWithData() {
  final delegate = _mCallback;
  if (delegate is! IndexableItemDiffDelegate<T>) {
    throw Exception(
        "$delegate is not a IndexableItemDiffDelegate<$T>. call getUpdates() instead or implement IndexableItemDiffDelegate in your DiffDelegate ");
  }
  final updates = <DataDiffUpdate<T>>[];
  // track up to date current list size for moves
  // when a move is found, we record its position from the end of the list (which is
  // less likely to change since we iterate in reverse).
  // Later when we find the match of that move, we dispatch the update
  int currentListSize = _mOldListSize;
  // list of postponed moves
  final postponedUpdates = <_PostponedUpdate>[];
  // posX and posY are exclusive
  int posX = _mOldListSize;
  int posY = _mNewListSize;
  // iterate from end of the list to the beginning.
  // this just makes offsets easier since changes in the earlier indices has an effect
  // on the later indices.
  for (int diagonalIndex = _mDiagonals.length - 1;
      diagonalIndex >= 0;
      diagonalIndex--) {
    final _Diagonal diagonal = _mDiagonals[(diagonalIndex)];

    final int endX = diagonal.endX();
    final int endY = diagonal.endY();
    // dispatch removals and additions until we reach to that diagonal
    // first remove then add so that it can go into its place and we don't need
    // to offset values
    while (posX > endX) {
      posX--;
      // REMOVAL
      final int status = _mOldItemStatuses[posX];
      final item = delegate.getOldItemAtIndex(posX);
      if ((status & FLAG_MOVED) != 0) {
        final int newPos = status >> FLAG_OFFSET;
        // get postponed addition
        final _PostponedUpdate? postponedUpdate =
            getPostponedUpdate(postponedUpdates, newPos, false);
        if (postponedUpdate != null) {
          // this is an addition that was postponed. Now dispatch it.
          final int updatedNewPos =
              currentListSize - postponedUpdate.currentPos;
          updates
              .add(DataMove(from: posX, to: updatedNewPos - 1, data: item));
          if ((status & FLAG_MOVED_CHANGED) != 0) {
            updates.add(DataChange(
              position: updatedNewPos - 1,
              newData: delegate.getNewItemAtIndex(newPos),
              oldData: item,
            ));
          }
        } else {
          // first time we are seeing this, we'll see a matching addition
          postponedUpdates.add(_PostponedUpdate(
              posInOwnerList: posX,
              currentPos: currentListSize - posX - 1,
              removal: true));
        }
      } else {
        // simple removal
        updates.add(DataRemove(position: posX, data: item));
        currentListSize--;
      }
    }
    while (posY > endY) {
      posY--;
      // ADDITION
      final int status = _mNewItemStatuses[posY];
      final item = delegate.getNewItemAtIndex(posY);

      if ((status & FLAG_MOVED) != 0) {
        // this is a move not an addition.
        // see if this is postponed
        final int oldPos = status >> FLAG_OFFSET;
        // get postponed removal
        final _PostponedUpdate? postponedUpdate =
            getPostponedUpdate(postponedUpdates, oldPos, true);
        // empty size returns 0 for indexOf
        if (postponedUpdate == null) {
          // postpone it until we see the removal
          postponedUpdates.add(_PostponedUpdate(
              posInOwnerList: posY,
              currentPos: currentListSize - posX,
              removal: false));
        } else {
          // oldPosFromEnd = foundListSize - posX
          // we can find posX if we swap the list sizes
          // posX = listSize - oldPosFromEnd
          final int updatedOldPos =
              currentListSize - postponedUpdate.currentPos - 1;
          updates.add(DataMove(from: updatedOldPos, to: posX, data: item));
          if ((status & FLAG_MOVED_CHANGED) != 0) {
            updates.add(DataDiffUpdate.change(
                position: posX,
                oldData: delegate.getOldItemAtIndex(oldPos),
                newData: item));
          }
        }
      } else {
        // simple addition
        updates.add(DataInsert(position: posX, data: item));
        currentListSize++;
      }
    }
    // now dispatch updates for the diagonal
    posX = diagonal.x;
    posY = diagonal.y;
    for (int i = 0; i < diagonal.size; i++) {
      // dispatch changes
      if ((_mOldItemStatuses[posX] & FLAG_MASK) == FLAG_CHANGED) {
        updates.add(DataDiffUpdate.change(
            position: posX,
            oldData: delegate.getOldItemAtIndex(posX),
            newData: delegate.getNewItemAtIndex(posY)));
        //updates.add(Change(position: posX, payload: changePayload));
      }
      posX++;
      posY++;
    }
    // snap back for the next diagonal
    posX = diagonal.x;
    posY = diagonal.y;
  }
  return updates;
}