/* * Copyright 2017 Google * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #import "FEventGenerator.h" #import "FNode.h" #import "FIRDatabaseQuery_Private.h" #import "FQueryParams.h" #import "FQuerySpec.h" #import "FChange.h" #import "FNamedNode.h" #import "FEventRegistration.h" #import "FEvent.h" #import "FDataEvent.h" @interface FEventGenerator () @property (nonatomic, strong) FQuerySpec *query; @end /** * An EventGenerator is used to convert "raw" changes (fb.core.view.Change) as computed by the * CacheDiffer into actual events (fb.core.view.Event) that can be raised. See generateEventsForChanges() * for details. */ @implementation FEventGenerator - (id)initWithQuery:(FQuerySpec *)query { self = [super init]; if (self) { self.query = query; } return self; } /** * Given a set of raw changes (no moved events, and prevName not specified yet), and a set of EventRegistrations that * should be notified of these changes, generate the actual events to be raised. * * Notes: * - child_moved events will be synthesized at this time for any child_changed events that affect our index * - prevName will be calculated based on the index ordering * * @param changes NSArray of FChange, not necessarily in order. * @param registrations is NSArray of FEventRegistration. * @return NSArray of FEvent. */ - (NSArray *) generateEventsForChanges:(NSArray *)changes eventCache:(FIndexedNode *)eventCache eventRegistrations:(NSArray *)registrations { NSMutableArray *events = [[NSMutableArray alloc] init]; // child_moved is index-specific, so check all our child_changed events to see if we need to materialize // child_moved events with this view's index NSMutableArray *moves = [[NSMutableArray alloc] init]; for (FChange *change in changes) { if (change.type == FIRDataEventTypeChildChanged && [self.query.index indexedValueChangedBetween:change.oldIndexedNode.node and:change.indexedNode.node]) { FChange *moveChange = [[FChange alloc] initWithType:FIRDataEventTypeChildMoved indexedNode:change.indexedNode childKey:change.childKey oldIndexedNode:nil]; [moves addObject:moveChange]; } } [self generateEvents:events forType:FIRDataEventTypeChildRemoved changes:changes eventCache:eventCache eventRegistrations:registrations]; [self generateEvents:events forType:FIRDataEventTypeChildAdded changes:changes eventCache:eventCache eventRegistrations:registrations]; [self generateEvents:events forType:FIRDataEventTypeChildMoved changes:moves eventCache:eventCache eventRegistrations:registrations]; [self generateEvents:events forType:FIRDataEventTypeChildChanged changes:changes eventCache:eventCache eventRegistrations:registrations]; [self generateEvents:events forType:FIRDataEventTypeValue changes:changes eventCache:eventCache eventRegistrations:registrations]; return events; } - (void) generateEvents:(NSMutableArray *)events forType:(FIRDataEventType)eventType changes:(NSArray *)changes eventCache:(FIndexedNode *)eventCache eventRegistrations:(NSArray *)registrations { NSMutableArray *filteredChanges = [[NSMutableArray alloc] init]; for (FChange *change in changes) { if (change.type == eventType) { [filteredChanges addObject:change]; } } id index = self.query.index; [filteredChanges sortUsingComparator:^NSComparisonResult(FChange *one, FChange *two) { if (one.childKey == nil || two.childKey == nil) { @throw [[NSException alloc] initWithName:@"InternalInconsistencyError" reason:@"Should only compare child_ events" userInfo:nil]; } return [index compareKey:one.childKey andNode:one.indexedNode.node toOtherKey:two.childKey andNode:two.indexedNode.node]; }]; for (FChange *change in filteredChanges) { for (id registration in registrations) { if ([registration responseTo:eventType]) { id event = [self generateEventForChange:change registration:registration eventCache:eventCache]; [events addObject:event]; } } } } - (id) generateEventForChange:(FChange *)change registration:(id)registration eventCache:(FIndexedNode *)eventCache { FChange *materializedChange; if (change.type == FIRDataEventTypeValue || change.type == FIRDataEventTypeChildRemoved) { materializedChange = change; } else { NSString *prevChildKey = [eventCache predecessorForChildKey:change.childKey childNode:change.indexedNode.node index:self.query.index]; materializedChange = [change changeWithPrevKey:prevChildKey]; } return [registration createEventFrom:materializedChange query:self.query]; } @end