in src/main/java/com/google/firebase/database/core/view/ViewProcessor.java [197:290]
private ViewCache generateEventCacheAfterServerEvent(
ViewCache viewCache,
Path changePath,
WriteTreeRef writesCache,
NodeFilter.CompleteChildSource source,
ChildChangeAccumulator accumulator) {
CacheNode oldEventSnap = viewCache.getEventCache();
if (writesCache.shadowingWrite(changePath) != null) {
// we have a shadowing write, ignore changes
return viewCache;
} else {
IndexedNode newEventCache;
if (changePath.isEmpty()) {
// TODO: figure out how this plays with "sliding ack windows"
assert viewCache.getServerCache().isFullyInitialized()
: "If change path is empty, we must have complete server data";
Node nodeWithLocalWrites;
if (viewCache.getServerCache().isFiltered()) {
// We need to special case this, because we need to only apply writes to
// complete
// children, or we might end up raising events for incomplete children. If
// the server data
// is filtered deep writes cannot be guaranteed to be complete
Node serverCache = viewCache.getCompleteServerSnap();
Node completeChildren =
(serverCache instanceof ChildrenNode) ? serverCache : EmptyNode.Empty();
nodeWithLocalWrites = writesCache.calcCompleteEventChildren(completeChildren);
} else {
nodeWithLocalWrites =
writesCache.calcCompleteEventCache(viewCache.getCompleteServerSnap());
}
IndexedNode indexedNode = IndexedNode.from(nodeWithLocalWrites, this.filter.getIndex());
newEventCache =
this.filter.updateFullNode(
viewCache.getEventCache().getIndexedNode(), indexedNode, accumulator);
} else {
ChildKey childKey = changePath.getFront();
if (childKey.isPriorityChildName()) {
assert changePath.size() == 1 : "Can't have a priority with additional path components";
Node oldEventNode = oldEventSnap.getNode();
Node serverNode = viewCache.getServerCache().getNode();
// we might have overwrites for this priority
Node updatedPriority =
writesCache.calcEventCacheAfterServerOverwrite(changePath, oldEventNode, serverNode);
if (updatedPriority != null) {
newEventCache =
this.filter.updatePriority(oldEventSnap.getIndexedNode(), updatedPriority);
} else {
// priority didn't change, keep old node
newEventCache = oldEventSnap.getIndexedNode();
}
} else {
Path childChangePath = changePath.popFront();
// update child
Node newEventChild;
if (oldEventSnap.isCompleteForChild(childKey)) {
Node serverNode = viewCache.getServerCache().getNode();
Node eventChildUpdate =
writesCache.calcEventCacheAfterServerOverwrite(
changePath, oldEventSnap.getNode(), serverNode);
if (eventChildUpdate != null) {
newEventChild =
oldEventSnap
.getNode()
.getImmediateChild(childKey)
.updateChild(childChangePath, eventChildUpdate);
} else {
// Nothing changed, just keep the old child
newEventChild = oldEventSnap.getNode().getImmediateChild(childKey);
}
} else {
newEventChild = writesCache.calcCompleteChild(childKey, viewCache.getServerCache());
}
if (newEventChild != null) {
newEventCache =
this.filter.updateChild(
oldEventSnap.getIndexedNode(),
childKey,
newEventChild,
childChangePath,
source,
accumulator);
} else {
// no complete child available or no change
newEventCache = oldEventSnap.getIndexedNode();
}
}
}
return viewCache.updateEventSnap(
newEventCache,
oldEventSnap.isFullyInitialized() || changePath.isEmpty(),
this.filter.filtersNodes());
}
}