Files
firebase-ios-sdk/Firebase/Database/Snapshot/FLeafNode.m
T
2017-05-15 12:27:07 -07:00

251 lines
6.9 KiB
Objective-C

/*
* 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 "FLeafNode.h"
#import "FEmptyNode.h"
#import "FChildrenNode.h"
#import "FConstants.h"
#import "FImmutableSortedDictionary.h"
#import "FUtilities.h"
#import "FStringUtilities.h"
#import "FSnapshotUtilities.h"
@interface FLeafNode ()
@property (nonatomic, strong) id<FNode> priorityNode;
@property (nonatomic, strong) NSString *lazyHash;
@end
@implementation FLeafNode
@synthesize value;
@synthesize priorityNode;
- (id)initWithValue:(id)aValue {
self = [super init];
if (self) {
self.value = aValue;
self.priorityNode = [FEmptyNode emptyNode];
}
return self;
}
- (id)initWithValue:(id)aValue withPriority:(id<FNode>)aPriority {
self = [super init];
if (self) {
self.value = aValue;
[FSnapshotUtilities validatePriorityNode:aPriority];
self.priorityNode = aPriority;
}
return self;
}
#pragma mark -
#pragma mark FNode methods
- (BOOL) isLeafNode {
return YES;
}
- (id<FNode>) getPriority {
return self.priorityNode;
}
- (id<FNode>) updatePriority:(id<FNode>)aPriority {
return [[FLeafNode alloc] initWithValue:self.value withPriority:aPriority];
}
- (id<FNode>) getImmediateChild:(NSString *) childName {
if ([childName isEqualToString:@".priority"]) {
return self.priorityNode;
} else {
return [FEmptyNode emptyNode];
}
}
- (id<FNode>) getChild:(FPath *)path {
if (path.getFront == nil) {
return self;
} else if ([[path getFront] isEqualToString:@".priority"]) {
return [self getPriority];
} else {
return [FEmptyNode emptyNode];
}
}
- (BOOL)hasChild:(NSString *)childName {
return [childName isEqualToString:@".priority"] && ![self getPriority].isEmpty;
}
- (NSString *)predecessorChildKey:(NSString *)childKey
{
return nil;
}
- (id<FNode>) updateImmediateChild:(NSString *)childName withNewChild:(id<FNode>)newChildNode {
if ([childName isEqualToString:@".priority"]) {
return [self updatePriority:newChildNode];
} else if (newChildNode.isEmpty) {
return self;
} else {
FChildrenNode* childrenNode = [[FChildrenNode alloc] init];
childrenNode = [childrenNode updateImmediateChild:childName withNewChild:newChildNode];
childrenNode = [childrenNode updatePriority:self.priorityNode];
return childrenNode;
}
}
- (id<FNode>) updateChild:(FPath *)path withNewChild:(id<FNode>)newChildNode {
NSString* front = [path getFront];
if(front == nil) {
return newChildNode;
} else if (newChildNode.isEmpty && ![front isEqualToString:@".priority"]) {
return self;
} else {
NSAssert(![front isEqualToString:@".priority"] || path.length == 1, @".priority must be the last token in a path.");
return [self updateImmediateChild:front withNewChild:
[[FEmptyNode emptyNode] updateChild:[path popFront] withNewChild:newChildNode]];
}
}
- (id) val {
return [self valForExport:NO];
}
- (id) valForExport:(BOOL)exp {
if(exp && !self.getPriority.isEmpty) {
return @{kPayloadValue : self.value,
kPayloadPriority : [[self getPriority] val]};
}
else {
return self.value;
}
}
- (BOOL)isEqual:(id <FNode>)other {
if(other == self) {
return YES;
} else if (other.isLeafNode) {
FLeafNode *otherLeaf = other;
if ([FUtilities getJavascriptType:self.value] != [FUtilities getJavascriptType:otherLeaf.value]) {
return NO;
}
return [otherLeaf.value isEqual:self.value] && [otherLeaf.priorityNode isEqual:self.priorityNode];
} else {
return NO;
}
}
- (NSUInteger)hash {
return [self.value hash] * 17 + self.priorityNode.hash;
}
- (id <FNode>)withIndex:(id <FIndex>)index {
return self;
}
- (BOOL)isIndexed:(id <FIndex>)index {
return YES;
}
- (BOOL) isEmpty {
return NO;
}
- (int) numChildren {
return 0;
}
- (void) enumerateChildrenUsingBlock:(void (^)(NSString *, id<FNode>, BOOL *))block
{
// Nothing to iterate over
}
- (void) enumerateChildrenReverse:(BOOL)reverse usingBlock:(void (^)(NSString *, id<FNode>, BOOL *))block
{
// Nothing to iterate over
}
- (NSEnumerator *)childEnumerator
{
// Nothing to iterate over
return [@[] objectEnumerator];
}
- (NSString *) dataHash {
if (self.lazyHash == nil) {
NSMutableString *toHash = [[NSMutableString alloc] init];
[FSnapshotUtilities appendHashRepresentationForLeafNode:self toString:toHash hashVersion:FDataHashVersionV1];
self.lazyHash = [FStringUtilities base64EncodedSha1:toHash];
}
return self.lazyHash;
}
- (NSComparisonResult)compare:(id <FNode>)other {
if (other == [FEmptyNode emptyNode]) {
return NSOrderedDescending;
} else if ([other isKindOfClass:[FChildrenNode class]]) {
return NSOrderedAscending;
} else {
NSAssert(other.isLeafNode, @"Compared against unknown type of node.");
return [self compareToLeafNode:(FLeafNode*)other];
}
}
+ (NSArray*) valueTypeOrder {
static NSArray* valueOrder = nil;
static dispatch_once_t once;
dispatch_once(&once, ^{
valueOrder = @[kJavaScriptObject, kJavaScriptBoolean, kJavaScriptNumber, kJavaScriptString];
});
return valueOrder;
}
- (NSComparisonResult) compareToLeafNode:(FLeafNode*)other {
NSString* thisLeafType = [FUtilities getJavascriptType:self.value];
NSString* otherLeafType = [FUtilities getJavascriptType:other.value];
NSUInteger thisIndex = [[FLeafNode valueTypeOrder] indexOfObject:thisLeafType];
NSUInteger otherIndex = [[FLeafNode valueTypeOrder] indexOfObject:otherLeafType];
assert(thisIndex >= 0 && otherIndex >= 0);
if (otherIndex == thisIndex) {
// Same type. Compare values.
if (thisLeafType == kJavaScriptObject) {
// Deferred value nodes are all equal, but we should also never get to this point...
return NSOrderedSame;
} else if (thisLeafType == kJavaScriptString) {
return [self.value compare:other.value options:NSLiteralSearch];
} else {
return [self.value compare:other.value];
}
} else {
return thisIndex > otherIndex ? NSOrderedDescending : NSOrderedAscending;
}
}
- (NSString *) description {
return [[self valForExport:YES] description];
}
- (void) forEachChildDo:(fbt_bool_nsstring_node)action {
// There are no children, so there is nothing to do.
return;
}
@end