in firebase-database/src/main/java/com/google/firebase/database/core/view/filter/LimitedFilter.java [71:138]
private IndexedNode fullLimitUpdateChild(
IndexedNode oldIndexed,
ChildKey childKey,
Node childSnap,
CompleteChildSource source,
ChildChangeAccumulator optChangeAccumulator) {
// TODO: rename all cache stuff etc to general snap terminology
hardAssert(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;
}
}