Files
firebase-ios-sdk/FirebaseDatabase/Sources/Snapshot/FIndexedNode.m
T

218 lines
7.7 KiB
Objective-C
Raw Normal View History

2017-05-15 12:27:07 -07:00
/*
* 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.
*/
2020-06-22 07:58:52 -07:00
#import "FirebaseDatabase/Sources/Snapshot/FIndexedNode.h"
2017-05-15 12:27:07 -07:00
2020-06-22 07:58:52 -07:00
#import "FirebaseDatabase/Sources/FIndex.h"
#import "FirebaseDatabase/Sources/FKeyIndex.h"
#import "FirebaseDatabase/Sources/FPriorityIndex.h"
#import "FirebaseDatabase/Sources/Snapshot/FChildrenNode.h"
#import "FirebaseDatabase/Sources/third_party/FImmutableSortedDictionary/FImmutableSortedDictionary/FImmutableSortedSet.h"
2017-05-15 12:27:07 -07:00
static FImmutableSortedSet *FALLBACK_INDEX;
@interface FIndexedNode ()
2019-08-05 13:27:47 -07:00
@property(nonatomic, strong) id<FNode> node;
2017-05-15 12:27:07 -07:00
/**
2019-08-05 13:27:47 -07:00
* The indexed set is initialized lazily to prevent creation when it is not
* needed
2017-05-15 12:27:07 -07:00
*/
2019-08-05 13:27:47 -07:00
@property(nonatomic, strong) FImmutableSortedSet *indexed;
@property(nonatomic, strong) id<FIndex> index;
2017-05-15 12:27:07 -07:00
@end
@implementation FIndexedNode
+ (FImmutableSortedSet *)fallbackIndex {
static FImmutableSortedSet *fallbackIndex;
static dispatch_once_t once;
dispatch_once(&once, ^{
2019-08-05 13:27:47 -07:00
fallbackIndex = [[FImmutableSortedSet alloc] init];
2017-05-15 12:27:07 -07:00
});
return fallbackIndex;
}
2019-08-05 13:27:47 -07:00
+ (FIndexedNode *)indexedNodeWithNode:(id<FNode>)node {
return [[FIndexedNode alloc] initWithNode:node
index:[FPriorityIndex priorityIndex]];
2017-05-15 12:27:07 -07:00
}
2019-08-05 13:27:47 -07:00
+ (FIndexedNode *)indexedNodeWithNode:(id<FNode>)node index:(id<FIndex>)index {
2017-05-15 12:27:07 -07:00
return [[FIndexedNode alloc] initWithNode:node index:index];
}
2019-08-05 13:27:47 -07:00
- (id)initWithNode:(id<FNode>)node index:(id<FIndex>)index {
2017-05-15 12:27:07 -07:00
// Initialize indexed lazily
return [self initWithNode:node index:index indexed:nil];
}
2019-08-05 13:27:47 -07:00
- (id)initWithNode:(id<FNode>)node
index:(id<FIndex>)index
indexed:(FImmutableSortedSet *)indexed {
2017-05-15 12:27:07 -07:00
self = [super init];
if (self != nil) {
self->_node = node;
self->_index = index;
self->_indexed = indexed;
}
return self;
}
2019-08-05 13:27:47 -07:00
- (void)ensureIndexed {
2017-05-15 12:27:07 -07:00
if (!self.indexed) {
if ([self.index isEqual:[FKeyIndex keyIndex]]) {
self.indexed = [FIndexedNode fallbackIndex];
} else {
__block BOOL sawChild = NO;
2019-08-05 13:27:47 -07:00
[self.node enumerateChildrenUsingBlock:^(
NSString *key, id<FNode> node, BOOL *stop) {
sawChild = sawChild || [self.index isDefinedOn:node];
*stop = sawChild;
2017-05-15 12:27:07 -07:00
}];
if (sawChild) {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
2019-08-05 13:27:47 -07:00
[self.node enumerateChildrenUsingBlock:^(
NSString *key, id<FNode> node, BOOL *stop) {
FNamedNode *namedNode =
[[FNamedNode alloc] initWithName:key andNode:node];
dict[namedNode] = [NSNull null];
2017-05-15 12:27:07 -07:00
}];
2019-08-05 13:27:47 -07:00
// Make sure to assign index here, because the comparator will
// be retained and using self will cause a cycle
2017-05-15 12:27:07 -07:00
id<FIndex> index = self.index;
2019-08-05 13:27:47 -07:00
self.indexed = [FImmutableSortedSet
setWithKeysFromDictionary:dict
comparator:^NSComparisonResult(
FNamedNode *namedNode1,
FNamedNode *namedNode2) {
return [index compareNamedNode:namedNode1
toNamedNode:namedNode2];
}];
2017-05-15 12:27:07 -07:00
} else {
self.indexed = [FIndexedNode fallbackIndex];
}
}
}
}
2019-08-05 13:27:47 -07:00
- (BOOL)hasIndex:(id<FIndex>)index {
2017-05-15 12:27:07 -07:00
return [self.index isEqual:index];
}
2019-08-05 13:27:47 -07:00
- (FIndexedNode *)updateChild:(NSString *)key
withNewChild:(id<FNode>)newChildNode {
id<FNode> newNode = [self.node updateImmediateChild:key
withNewChild:newChildNode];
if (self.indexed == [FIndexedNode fallbackIndex] &&
![self.index isDefinedOn:newChildNode]) {
2017-05-15 12:27:07 -07:00
// doesn't affect the index, no need to create an index
2019-08-05 13:27:47 -07:00
return [[FIndexedNode alloc] initWithNode:newNode
index:self.index
indexed:[FIndexedNode fallbackIndex]];
2017-05-15 12:27:07 -07:00
} else if (!self.indexed || self.indexed == [FIndexedNode fallbackIndex]) {
// No need to index yet, index lazily
return [[FIndexedNode alloc] initWithNode:newNode index:self.index];
} else {
id<FNode> oldChild = [self.node getImmediateChild:key];
2019-08-05 13:27:47 -07:00
FImmutableSortedSet *newIndexed = [self.indexed
removeObject:[FNamedNode nodeWithName:key node:oldChild]];
2017-05-15 12:27:07 -07:00
if (![newChildNode isEmpty]) {
2019-08-05 13:27:47 -07:00
newIndexed = [newIndexed
addObject:[FNamedNode nodeWithName:key node:newChildNode]];
2017-05-15 12:27:07 -07:00
}
2019-08-05 13:27:47 -07:00
return [[FIndexedNode alloc] initWithNode:newNode
index:self.index
indexed:newIndexed];
2017-05-15 12:27:07 -07:00
}
}
2019-08-05 13:27:47 -07:00
- (FIndexedNode *)updatePriority:(id<FNode>)priority {
return
[[FIndexedNode alloc] initWithNode:[self.node updatePriority:priority]
index:self.index
indexed:self.indexed];
2017-05-15 12:27:07 -07:00
}
2019-08-05 13:27:47 -07:00
- (FNamedNode *)firstChild {
2017-05-15 12:27:07 -07:00
if (![self.node isKindOfClass:[FChildrenNode class]]) {
return nil;
} else {
[self ensureIndexed];
if (self.indexed == [FIndexedNode fallbackIndex]) {
return [((FChildrenNode *)self.node) firstChild];
} else {
return self.indexed.firstObject;
}
}
}
2019-08-05 13:27:47 -07:00
- (FNamedNode *)lastChild {
2017-05-15 12:27:07 -07:00
if (![self.node isKindOfClass:[FChildrenNode class]]) {
return nil;
} else {
[self ensureIndexed];
if (self.indexed == [FIndexedNode fallbackIndex]) {
return [((FChildrenNode *)self.node) lastChild];
} else {
return self.indexed.lastObject;
}
}
}
2019-08-05 13:27:47 -07:00
- (NSString *)predecessorForChildKey:(NSString *)childKey
childNode:(id<FNode>)childNode
index:(id<FIndex>)index {
2017-05-15 12:27:07 -07:00
if (![self.index isEqual:index]) {
2019-08-05 13:27:47 -07:00
[NSException raise:NSInvalidArgumentException
format:@"Index not available in IndexedNode!"];
2017-05-15 12:27:07 -07:00
}
[self ensureIndexed];
if (self.indexed == [FIndexedNode fallbackIndex]) {
return [self.node predecessorChildKey:childKey];
} else {
2019-08-05 13:27:47 -07:00
FNamedNode *node = [self.indexed
predecessorEntry:[FNamedNode nodeWithName:childKey node:childNode]];
2017-05-15 12:27:07 -07:00
return node.name;
}
}
2019-08-05 13:27:47 -07:00
- (void)enumerateChildrenReverse:(BOOL)reverse
usingBlock:
(void (^)(NSString *, id<FNode>, BOOL *))block {
2017-05-15 12:27:07 -07:00
[self ensureIndexed];
if (self.indexed == [FIndexedNode fallbackIndex]) {
[self.node enumerateChildrenReverse:reverse usingBlock:block];
} else {
2019-08-05 13:27:47 -07:00
[self.indexed
enumerateObjectsReverse:reverse
usingBlock:^(FNamedNode *namedNode, BOOL *stop) {
block(namedNode.name, namedNode.node, stop);
}];
2017-05-15 12:27:07 -07:00
}
}
2019-08-05 13:27:47 -07:00
- (NSEnumerator *)childEnumerator {
2017-05-15 12:27:07 -07:00
[self ensureIndexed];
if (self.indexed == [FIndexedNode fallbackIndex]) {
return [self.node childEnumerator];
} else {
return [self.indexed objectEnumerator];
}
}
@end