FChildrenNode.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "FirebaseDatabase/Sources/Snapshot/FChildrenNode.h"
  17. #import "FirebaseDatabase/Sources/Constants/FConstants.h"
  18. #import "FirebaseDatabase/Sources/FMaxNode.h"
  19. #import "FirebaseDatabase/Sources/FNamedNode.h"
  20. #import "FirebaseDatabase/Sources/FPriorityIndex.h"
  21. #import "FirebaseDatabase/Sources/FTransformedEnumerator.h"
  22. #import "FirebaseDatabase/Sources/Snapshot/FEmptyNode.h"
  23. #import "FirebaseDatabase/Sources/Snapshot/FSnapshotUtilities.h"
  24. #import "FirebaseDatabase/Sources/Utilities/FStringUtilities.h"
  25. #import "FirebaseDatabase/Sources/Utilities/FUtilities.h"
  26. @interface FChildrenNode ()
  27. @property(nonatomic, strong) NSString *lazyHash;
  28. @end
  29. @implementation FChildrenNode
  30. // Note: The only reason we allow nil priority is to for EmptyNode, since we
  31. // can't use EmptyNode as the priority of EmptyNode. We might want to consider
  32. // making EmptyNode its own class instead of an empty ChildrenNode.
  33. - (id)init {
  34. return [self
  35. initWithPriority:nil
  36. children:[FImmutableSortedDictionary
  37. dictionaryWithComparator:[FUtilities
  38. keyComparator]]];
  39. }
  40. - (id)initWithChildren:(FImmutableSortedDictionary *)someChildren {
  41. return [self initWithPriority:nil children:someChildren];
  42. }
  43. - (id)initWithPriority:(id<FNode>)aPriority
  44. children:(FImmutableSortedDictionary *)someChildren {
  45. if (someChildren.isEmpty && aPriority != nil && ![aPriority isEmpty]) {
  46. [NSException raise:NSInvalidArgumentException
  47. format:@"Can't create empty node with priority!"];
  48. }
  49. self = [super init];
  50. if (self) {
  51. self.children = someChildren;
  52. self.priorityNode = aPriority;
  53. }
  54. return self;
  55. }
  56. - (NSString *)description {
  57. return [[self valForExport:YES] description];
  58. }
  59. #pragma mark -
  60. #pragma mark FNode methods
  61. - (BOOL)isLeafNode {
  62. return NO;
  63. }
  64. - (id<FNode>)getPriority {
  65. if (self.priorityNode) {
  66. return self.priorityNode;
  67. } else {
  68. return [FEmptyNode emptyNode];
  69. }
  70. }
  71. - (id<FNode>)updatePriority:(id<FNode>)aPriority {
  72. if ([self.children isEmpty]) {
  73. return [FEmptyNode emptyNode];
  74. } else {
  75. return [[FChildrenNode alloc] initWithPriority:aPriority
  76. children:self.children];
  77. }
  78. }
  79. - (id<FNode>)getImmediateChild:(NSString *)childName {
  80. if ([childName isEqualToString:@".priority"]) {
  81. return [self getPriority];
  82. } else {
  83. id<FNode> child = [self.children objectForKey:childName];
  84. return (child == nil) ? [FEmptyNode emptyNode] : child;
  85. }
  86. }
  87. - (id<FNode>)getChild:(FPath *)path {
  88. NSString *front = [path getFront];
  89. if (front == nil) {
  90. return self;
  91. } else {
  92. return [[self getImmediateChild:front] getChild:[path popFront]];
  93. }
  94. }
  95. - (BOOL)hasChild:(NSString *)childName {
  96. return ![self getImmediateChild:childName].isEmpty;
  97. }
  98. - (id<FNode>)updateImmediateChild:(NSString *)childName
  99. withNewChild:(id<FNode>)newChildNode {
  100. NSAssert(newChildNode != nil, @"Should always be passing nodes.");
  101. if ([childName isEqualToString:@".priority"]) {
  102. return [self updatePriority:newChildNode];
  103. } else {
  104. FImmutableSortedDictionary *newChildren;
  105. if (newChildNode.isEmpty) {
  106. newChildren = [self.children removeObjectForKey:childName];
  107. } else {
  108. newChildren = [self.children setObject:newChildNode
  109. forKey:childName];
  110. }
  111. if (newChildren.isEmpty) {
  112. return [FEmptyNode emptyNode];
  113. } else {
  114. return [[FChildrenNode alloc] initWithPriority:self.getPriority
  115. children:newChildren];
  116. }
  117. }
  118. }
  119. - (id<FNode>)updateChild:(FPath *)path withNewChild:(id<FNode>)newChildNode {
  120. NSString *front = [path getFront];
  121. if (front == nil) {
  122. return newChildNode;
  123. } else {
  124. NSAssert(![front isEqualToString:@".priority"] || path.length == 1,
  125. @".priority must be the last token in a path.");
  126. id<FNode> newImmediateChild =
  127. [[self getImmediateChild:front] updateChild:[path popFront]
  128. withNewChild:newChildNode];
  129. return [self updateImmediateChild:front withNewChild:newImmediateChild];
  130. }
  131. }
  132. - (BOOL)isEmpty {
  133. return [self.children isEmpty];
  134. }
  135. - (int)numChildren {
  136. return [self.children count];
  137. }
  138. - (id)val {
  139. return [self valForExport:NO];
  140. }
  141. - (id)valForExport:(BOOL)exp {
  142. if ([self isEmpty]) {
  143. return [NSNull null];
  144. }
  145. __block int numKeys = 0;
  146. __block NSInteger maxKey = 0;
  147. __block BOOL allIntegerKeys = YES;
  148. NSMutableDictionary *obj =
  149. [[NSMutableDictionary alloc] initWithCapacity:[self.children count]];
  150. [self enumerateChildrenUsingBlock:^(NSString *key, id<FNode> childNode,
  151. BOOL *stop) {
  152. [obj setObject:[childNode valForExport:exp] forKey:key];
  153. numKeys++;
  154. // If we already found a string key, don't bother with any of this
  155. if (!allIntegerKeys) {
  156. return;
  157. }
  158. // Treat leading zeroes that are not exactly "0" as strings
  159. NSString *firstChar = [key substringWithRange:NSMakeRange(0, 1)];
  160. if ([firstChar isEqualToString:@"0"] && [key length] > 1) {
  161. allIntegerKeys = NO;
  162. } else {
  163. NSNumber *keyAsNum = [FUtilities intForString:key];
  164. if (keyAsNum != nil) {
  165. NSInteger keyAsInt = [keyAsNum integerValue];
  166. if (keyAsInt > maxKey) {
  167. maxKey = keyAsInt;
  168. }
  169. } else {
  170. allIntegerKeys = NO;
  171. }
  172. }
  173. }];
  174. if (!exp && allIntegerKeys && maxKey < 2 * numKeys) {
  175. // convert to an array
  176. NSMutableArray *array =
  177. [[NSMutableArray alloc] initWithCapacity:maxKey + 1];
  178. for (int i = 0; i <= maxKey; ++i) {
  179. NSString *keyString = [NSString stringWithFormat:@"%i", i];
  180. id child = obj[keyString];
  181. if (child != nil) {
  182. [array addObject:child];
  183. } else {
  184. [array addObject:[NSNull null]];
  185. }
  186. }
  187. return array;
  188. } else {
  189. if (exp && [self getPriority] != nil && !self.getPriority.isEmpty) {
  190. obj[kPayloadPriority] = [self.getPriority val];
  191. }
  192. return obj;
  193. }
  194. }
  195. - (NSString *)dataHash {
  196. if (self.lazyHash == nil) {
  197. NSMutableString *toHash = [[NSMutableString alloc] init];
  198. if (!self.getPriority.isEmpty) {
  199. [toHash appendString:@"priority:"];
  200. [FSnapshotUtilities
  201. appendHashRepresentationForLeafNode:(FLeafNode *)
  202. self.getPriority
  203. toString:toHash
  204. hashVersion:FDataHashVersionV1];
  205. [toHash appendString:@":"];
  206. }
  207. __block BOOL sawPriority = NO;
  208. [self enumerateChildrenUsingBlock:^(NSString *key, id<FNode> node,
  209. BOOL *stop) {
  210. sawPriority = sawPriority || [[node getPriority] isEmpty];
  211. *stop = sawPriority;
  212. }];
  213. if (sawPriority) {
  214. NSMutableArray *array = [NSMutableArray array];
  215. [self enumerateChildrenUsingBlock:^(NSString *key, id<FNode> node,
  216. BOOL *stop) {
  217. FNamedNode *namedNode = [[FNamedNode alloc] initWithName:key
  218. andNode:node];
  219. [array addObject:namedNode];
  220. }];
  221. [array sortUsingComparator:^NSComparisonResult(
  222. FNamedNode *namedNode1, FNamedNode *namedNode2) {
  223. return
  224. [[FPriorityIndex priorityIndex] compareNamedNode:namedNode1
  225. toNamedNode:namedNode2];
  226. }];
  227. [array enumerateObjectsUsingBlock:^(FNamedNode *namedNode,
  228. NSUInteger idx, BOOL *stop) {
  229. NSString *childHash = [namedNode.node dataHash];
  230. if (![childHash isEqualToString:@""]) {
  231. [toHash appendFormat:@":%@:%@", namedNode.name, childHash];
  232. }
  233. }];
  234. } else {
  235. [self enumerateChildrenUsingBlock:^(NSString *key, id<FNode> node,
  236. BOOL *stop) {
  237. NSString *childHash = [node dataHash];
  238. if (![childHash isEqualToString:@""]) {
  239. [toHash appendFormat:@":%@:%@", key, childHash];
  240. }
  241. }];
  242. }
  243. self.lazyHash = [toHash isEqualToString:@""]
  244. ? @""
  245. : [FStringUtilities base64EncodedSha1:toHash];
  246. }
  247. return self.lazyHash;
  248. }
  249. - (NSComparisonResult)compare:(id<FNode>)other {
  250. // children nodes come last, unless this is actually an empty node, then we
  251. // come first.
  252. if (self.isEmpty) {
  253. if (other.isEmpty) {
  254. return NSOrderedSame;
  255. } else {
  256. return NSOrderedAscending;
  257. }
  258. } else if (other.isLeafNode || other.isEmpty) {
  259. return NSOrderedDescending;
  260. } else if (other == [FMaxNode maxNode]) {
  261. return NSOrderedAscending;
  262. } else {
  263. // Must be another node with children.
  264. return NSOrderedSame;
  265. }
  266. }
  267. - (BOOL)isEqual:(id<FNode>)other {
  268. if (other == self) {
  269. return YES;
  270. } else if (other == nil) {
  271. return NO;
  272. } else if (other.isLeafNode) {
  273. return NO;
  274. } else if (self.isEmpty && [other isEmpty]) {
  275. // Empty nodes do not have priority
  276. return YES;
  277. } else {
  278. FChildrenNode *otherChildrenNode = other;
  279. if (![self.getPriority isEqual:other.getPriority]) {
  280. return NO;
  281. } else if (self.children.count == otherChildrenNode.children.count) {
  282. __block BOOL equal = YES;
  283. [self enumerateChildrenUsingBlock:^(NSString *key, id<FNode> node,
  284. BOOL *stop) {
  285. id<FNode> child = [otherChildrenNode getImmediateChild:key];
  286. if (![child isEqual:node]) {
  287. equal = NO;
  288. *stop = YES;
  289. }
  290. }];
  291. return equal;
  292. } else {
  293. return NO;
  294. }
  295. }
  296. }
  297. - (NSUInteger)hash {
  298. __block NSUInteger hashCode = 0;
  299. [self enumerateChildrenUsingBlock:^(NSString *key, id<FNode> node,
  300. BOOL *stop) {
  301. hashCode = 31 * hashCode + key.hash;
  302. hashCode = 17 * hashCode + node.hash;
  303. }];
  304. return 17 * hashCode + self.priorityNode.hash;
  305. }
  306. - (void)enumerateChildrenAndPriorityUsingBlock:(void (^)(NSString *, id<FNode>,
  307. BOOL *))block {
  308. if ([self.getPriority isEmpty]) {
  309. [self enumerateChildrenUsingBlock:block];
  310. } else {
  311. __block BOOL passedPriorityKey = NO;
  312. [self enumerateChildrenUsingBlock:^(NSString *key, id<FNode> node,
  313. BOOL *stop) {
  314. if (!passedPriorityKey &&
  315. [FUtilities compareKey:key
  316. toKey:@".priority"] == NSOrderedDescending) {
  317. passedPriorityKey = YES;
  318. BOOL stopAfterPriority = NO;
  319. block(@".priority", [self getPriority], &stopAfterPriority);
  320. if (stopAfterPriority)
  321. return;
  322. }
  323. block(key, node, stop);
  324. }];
  325. }
  326. }
  327. - (void)enumerateChildrenUsingBlock:(void (^)(NSString *, id<FNode>,
  328. BOOL *))block {
  329. [self.children enumerateKeysAndObjectsUsingBlock:block];
  330. }
  331. - (void)enumerateChildrenReverse:(BOOL)reverse
  332. usingBlock:
  333. (void (^)(NSString *, id<FNode>, BOOL *))block {
  334. [self.children enumerateKeysAndObjectsReverse:reverse usingBlock:block];
  335. }
  336. - (NSEnumerator *)childEnumerator {
  337. return [[FTransformedEnumerator alloc]
  338. initWithEnumerator:self.children.keyEnumerator
  339. andTransform:^id(NSString *key) {
  340. return [FNamedNode nodeWithName:key
  341. node:[self getImmediateChild:key]];
  342. }];
  343. }
  344. - (NSString *)predecessorChildKey:(NSString *)childKey {
  345. return [self.children getPredecessorKey:childKey];
  346. }
  347. #pragma mark -
  348. #pragma mark FChildrenNode specific methods
  349. - (id)childrenGetter:(id)key {
  350. return [self.children objectForKey:key];
  351. }
  352. - (FNamedNode *)firstChild {
  353. NSString *childKey = self.children.minKey;
  354. if (childKey) {
  355. return
  356. [[FNamedNode alloc] initWithName:childKey
  357. andNode:[self getImmediateChild:childKey]];
  358. } else {
  359. return nil;
  360. }
  361. }
  362. - (FNamedNode *)lastChild {
  363. NSString *childKey = self.children.maxKey;
  364. if (childKey) {
  365. return
  366. [[FNamedNode alloc] initWithName:childKey
  367. andNode:[self getImmediateChild:childKey]];
  368. } else {
  369. return nil;
  370. }
  371. }
  372. @end