in src/main/java/com/google/firebase/database/core/SyncTree.java [396:498]
public List<? extends Event> addEventRegistration(
@NotNull final EventRegistration eventRegistration) {
return persistenceManager.runInTransaction(
new Callable<List<? extends Event>>() {
@Override
public List<? extends Event> call() {
final QuerySpec query = eventRegistration.getQuerySpec();
Path path = query.getPath();
Node serverCacheNode = null;
boolean foundAncestorDefaultView = false;
// Any covering writes will necessarily be at the root, so really all we need to find is
// the server cache. Consider optimizing this once there's a better understanding of
// what actual behavior will be.
// for (Map.Entry<QuerySpec, View> entry: views.entrySet()) {
{
ImmutableTree<SyncPoint> tree = syncPointTree;
Path currentPath = path;
while (!tree.isEmpty()) {
SyncPoint currentSyncPoint = tree.getValue();
if (currentSyncPoint != null) {
serverCacheNode =
serverCacheNode != null
? serverCacheNode
: currentSyncPoint.getCompleteServerCache(currentPath);
foundAncestorDefaultView =
foundAncestorDefaultView || currentSyncPoint.hasCompleteView();
}
ChildKey front =
currentPath.isEmpty() ? ChildKey.fromString("") : currentPath.getFront();
tree = tree.getChild(front);
currentPath = currentPath.popFront();
}
}
SyncPoint syncPoint = syncPointTree.get(path);
if (syncPoint == null) {
syncPoint = new SyncPoint(persistenceManager);
syncPointTree = syncPointTree.set(path, syncPoint);
} else {
foundAncestorDefaultView = foundAncestorDefaultView || syncPoint.hasCompleteView();
serverCacheNode =
serverCacheNode != null
? serverCacheNode
: syncPoint.getCompleteServerCache(Path.getEmptyPath());
}
persistenceManager.setQueryActive(query);
CacheNode serverCache;
if (serverCacheNode != null) {
serverCache =
new CacheNode(IndexedNode.from(serverCacheNode, query.getIndex()), true, false);
} else {
// Hit persistence
CacheNode persistentServerCache = persistenceManager.serverCache(query);
if (persistentServerCache.isFullyInitialized()) {
serverCache = persistentServerCache;
} else {
serverCacheNode = EmptyNode.Empty();
ImmutableTree<SyncPoint> subtree = syncPointTree.subtree(path);
for (Map.Entry<ChildKey, ImmutableTree<SyncPoint>> child : subtree.getChildren()) {
SyncPoint childSyncPoint = child.getValue().getValue();
if (childSyncPoint != null) {
Node completeCache = childSyncPoint.getCompleteServerCache(Path.getEmptyPath());
if (completeCache != null) {
serverCacheNode =
serverCacheNode.updateImmediateChild(child.getKey(), completeCache);
}
}
}
// Fill the node with any available children we have
for (NamedNode child : persistentServerCache.getNode()) {
if (!serverCacheNode.hasChild(child.getName())) {
serverCacheNode =
serverCacheNode.updateImmediateChild(child.getName(), child.getNode());
}
}
serverCache =
new CacheNode(
IndexedNode.from(serverCacheNode, query.getIndex()), false, false);
}
}
boolean viewAlreadyExists = syncPoint.viewExistsForQuery(query);
if (!viewAlreadyExists && !query.loadsAllData()) {
// We need to track a tag for this query
assert !queryToTagMap.containsKey(query) : "View does not exist but we have a tag";
Tag tag = getNextQueryTag();
queryToTagMap.put(query, tag);
tagToQueryMap.put(tag, query);
}
WriteTreeRef writesCache = pendingWriteTree.childWrites(path);
List<? extends Event> events =
syncPoint.addEventRegistration(eventRegistration, writesCache, serverCache);
if (!viewAlreadyExists && !foundAncestorDefaultView) {
View view = syncPoint.viewForQuery(query);
setupListener(query, view);
}
return events;
}
});
}