private IndexedNode fullLimitUpdateChild()

in src/main/java/com/google/firebase/database/core/view/filter/LimitedFilter.java [73:142]


  private IndexedNode fullLimitUpdateChild(
      IndexedNode oldIndexed,
      ChildKey childKey,
      Node childSnap,
      CompleteChildSource source,
      ChildChangeAccumulator optChangeAccumulator) {
    // TODO: rename all cache stuff etc to general snap terminology
    assert oldIndexed.getNode().getChildCount() == this.limit;
    NamedNode newChildNamedNode = new NamedNode(childKey, childSnap);
    NamedNode windowBoundary =
        this.reverse ? oldIndexed.getFirstChild() : oldIndexed.getLastChild();
    boolean inRange = rangedFilter.matches(newChildNamedNode);
    if (oldIndexed.getNode().hasChild(childKey)) {
      Node oldChildSnap = oldIndexed.getNode().getImmediateChild(childKey);
      NamedNode nextChild = source.getChildAfterChild(this.index, windowBoundary, this.reverse);
      while (nextChild != null
          && (nextChild.getName().equals(childKey)
              || oldIndexed.getNode().hasChild(nextChild.getName()))) {
        // There is a weird edge case where a node is updated as part of a merge in the
        // write tree,
        // but hasn't been applied to the limited filter yet. Ignore this next child
        // which will be
        // updated later in the limited filter...
        nextChild = source.getChildAfterChild(this.index, nextChild, this.reverse);
      }
      int compareNext =
          nextChild == null ? 1 : index.compare(nextChild, newChildNamedNode, this.reverse);
      boolean remainsInWindow = inRange && !childSnap.isEmpty() && compareNext >= 0;
      if (remainsInWindow) {
        if (optChangeAccumulator != null) {
          optChangeAccumulator.trackChildChange(
              Change.childChangedChange(childKey, childSnap, oldChildSnap));
        }
        return oldIndexed.updateChild(childKey, childSnap);
      } else {
        if (optChangeAccumulator != null) {
          optChangeAccumulator.trackChildChange(Change.childRemovedChange(childKey, oldChildSnap));
        }
        IndexedNode newIndexed = oldIndexed.updateChild(childKey, EmptyNode.Empty());
        boolean nextChildInRange = nextChild != null && rangedFilter.matches(nextChild);
        if (nextChildInRange) {
          if (optChangeAccumulator != null) {
            optChangeAccumulator.trackChildChange(
                Change.childAddedChange(nextChild.getName(), nextChild.getNode()));
          }
          return newIndexed.updateChild(nextChild.getName(), nextChild.getNode());
        } else {
          return newIndexed;
        }
      }
    } else if (childSnap.isEmpty()) {
      // we're deleting a node, but it was not in the window, so ignore it
      return oldIndexed;
    } else if (inRange) {
      if (this.index.compare(windowBoundary, newChildNamedNode, this.reverse) >= 0) {
        if (optChangeAccumulator != null) {
          optChangeAccumulator.trackChildChange(
              Change.childRemovedChange(windowBoundary.getName(), windowBoundary.getNode()));
          optChangeAccumulator.trackChildChange(Change.childAddedChange(childKey, childSnap));
        }
        return oldIndexed
            .updateChild(childKey, childSnap)
            .updateChild(windowBoundary.getName(), EmptyNode.Empty());
      } else {
        return oldIndexed;
      }
    } else {
      return oldIndexed;
    }
  }