FChildrenNode.m 12 KB

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