mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-09 22:36:01 +05:45
267 lines
7.8 KiB
Objective-C
267 lines
7.8 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 "FirebaseDatabase/Sources/Snapshot/FLeafNode.h"
|
|
#import "FirebaseDatabase/Sources/Constants/FConstants.h"
|
|
#import "FirebaseDatabase/Sources/Snapshot/FChildrenNode.h"
|
|
#import "FirebaseDatabase/Sources/Snapshot/FEmptyNode.h"
|
|
#import "FirebaseDatabase/Sources/Snapshot/FSnapshotUtilities.h"
|
|
#import "FirebaseDatabase/Sources/Utilities/FStringUtilities.h"
|
|
#import "FirebaseDatabase/Sources/Utilities/FUtilities.h"
|
|
#import "FirebaseDatabase/Sources/third_party/FImmutableSortedDictionary/FImmutableSortedDictionary/FImmutableSortedDictionary.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
|