FImmutableTree.m 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  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 "FImmutableTree.h"
  17. #import "FImmutableSortedDictionary.h"
  18. #import "FPath.h"
  19. #import "FUtilities.h"
  20. @interface FImmutableTree ()
  21. @property (nonatomic, strong, readwrite) id value;
  22. /**
  23. * Maps NSString -> FImmutableTree<T>, where <T> is type of value.
  24. */
  25. @property (nonatomic, strong, readwrite) FImmutableSortedDictionary *children;
  26. @end
  27. @implementation FImmutableTree
  28. @synthesize value;
  29. @synthesize children;
  30. - (id) initWithValue:(id)aValue {
  31. self = [super init];
  32. if (self) {
  33. self.value = aValue;
  34. self.children = [FImmutableTree emptyChildren];
  35. }
  36. return self;
  37. }
  38. - (id) initWithValue:(id)aValue children:(FImmutableSortedDictionary *)childrenMap {
  39. self = [super init];
  40. if (self) {
  41. self.value = aValue;
  42. self.children = childrenMap;
  43. }
  44. return self;
  45. }
  46. + (FImmutableSortedDictionary *) emptyChildren {
  47. static dispatch_once_t emptyChildrenToken;
  48. static FImmutableSortedDictionary *emptyChildren;
  49. dispatch_once(&emptyChildrenToken, ^{
  50. emptyChildren = [FImmutableSortedDictionary dictionaryWithComparator:[FUtilities stringComparator]];
  51. });
  52. return emptyChildren;
  53. }
  54. + (FImmutableTree *) empty {
  55. static dispatch_once_t emptyImmutableTreeToken;
  56. static FImmutableTree *emptyTree = nil;
  57. dispatch_once(&emptyImmutableTreeToken, ^{
  58. emptyTree = [[FImmutableTree alloc] initWithValue:nil];
  59. });
  60. return emptyTree;
  61. }
  62. - (BOOL) isEmpty {
  63. return self.value == nil && [self.children isEmpty];
  64. }
  65. /**
  66. * Given a path and a predicate, return the first node and the path to that node where the predicate returns true
  67. * // TODO Do a perf test. If we're creating a bunch of FTuplePathValue objects on the way back out, it may be better to pass down a pathSoFar FPath
  68. */
  69. - (FTuplePathValue *) findRootMostMatchingPath:(FPath *)relativePath predicate:(BOOL (^)(id value))predicate {
  70. if (self.value != nil && predicate(self.value)) {
  71. return [[FTuplePathValue alloc] initWithPath:[FPath empty] value:self.value];
  72. } else {
  73. if ([relativePath isEmpty]) {
  74. return nil;
  75. } else {
  76. NSString *front = [relativePath getFront];
  77. FImmutableTree *child = [self.children get:front];
  78. if (child != nil) {
  79. FTuplePathValue *childExistingPathAndValue = [child findRootMostMatchingPath:[relativePath popFront] predicate:predicate];
  80. if (childExistingPathAndValue != nil) {
  81. FPath *fullPath = [[[FPath alloc] initWith:front] child:childExistingPathAndValue.path];
  82. return [[FTuplePathValue alloc] initWithPath:fullPath value:childExistingPathAndValue.value];
  83. } else {
  84. return nil;
  85. }
  86. } else {
  87. // No child matching path
  88. return nil;
  89. }
  90. }
  91. }
  92. }
  93. /**
  94. * Find, if it exists, the shortest subpath of the given path that points a defined value in the tree
  95. */
  96. - (FTuplePathValue *) findRootMostValueAndPath:(FPath *)relativePath {
  97. return [self findRootMostMatchingPath:relativePath predicate:^BOOL(__unsafe_unretained id value){
  98. return YES;
  99. }];
  100. }
  101. - (id) rootMostValueOnPath:(FPath *)path {
  102. return [self rootMostValueOnPath:path matching:^BOOL(id value) {
  103. return YES;
  104. }];
  105. }
  106. - (id) rootMostValueOnPath:(FPath *)path matching:(BOOL (^)(id))predicate {
  107. if (self.value != nil && predicate(self.value)) {
  108. return self.value;
  109. } else if (path.isEmpty) {
  110. return nil;
  111. } else {
  112. return [[self.children get:path.getFront] rootMostValueOnPath:[path popFront] matching:predicate];
  113. }
  114. }
  115. - (id) leafMostValueOnPath:(FPath *)path {
  116. return [self leafMostValueOnPath:path matching:^BOOL(id value) {
  117. return YES;
  118. }];
  119. }
  120. - (id) leafMostValueOnPath:(FPath *)relativePath matching:(BOOL (^)(id))predicate {
  121. __block id currentValue = self.value;
  122. __block FImmutableTree *currentTree = self;
  123. [relativePath enumerateComponentsUsingBlock:^(NSString *key, BOOL *stop) {
  124. currentTree = [currentTree.children get:key];
  125. if (currentTree == nil) {
  126. *stop = YES;
  127. } else {
  128. id treeValue = currentTree.value;
  129. if (treeValue != nil && predicate(treeValue)) {
  130. currentValue = treeValue;
  131. }
  132. }
  133. }];
  134. return currentValue;
  135. }
  136. - (BOOL) containsValueMatching:(BOOL (^)(id))predicate {
  137. if (self.value != nil && predicate(self.value)) {
  138. return YES;
  139. } else {
  140. __block BOOL found = NO;
  141. [self.children enumerateKeysAndObjectsUsingBlock:^(NSString *key, FImmutableTree *subtree, BOOL *stop) {
  142. found = [subtree containsValueMatching:predicate];
  143. if (found) *stop = YES;
  144. }];
  145. return found;
  146. }
  147. }
  148. - (FImmutableTree *) subtreeAtPath:(FPath *)relativePath {
  149. if ([relativePath isEmpty]) {
  150. return self;
  151. } else {
  152. NSString *front = [relativePath getFront];
  153. FImmutableTree *childTree = [self.children get:front];
  154. if (childTree != nil) {
  155. return [childTree subtreeAtPath:[relativePath popFront]];
  156. } else {
  157. return [FImmutableTree empty];
  158. }
  159. }
  160. }
  161. /**
  162. * Sets a value at the specified path
  163. */
  164. - (FImmutableTree *) setValue:(id)newValue atPath:(FPath *)relativePath {
  165. if ([relativePath isEmpty]) {
  166. return [[FImmutableTree alloc] initWithValue:newValue children:self.children];
  167. } else {
  168. NSString *front = [relativePath getFront];
  169. FImmutableTree *child = [self.children get:front];
  170. if (child == nil) {
  171. child = [FImmutableTree empty];
  172. }
  173. FImmutableTree *newChild = [child setValue:newValue atPath:[relativePath popFront]];
  174. FImmutableSortedDictionary *newChildren = [self.children insertKey:front withValue:newChild];
  175. return [[FImmutableTree alloc] initWithValue:self.value children:newChildren];
  176. }
  177. }
  178. /**
  179. * Remove the value at the specified path
  180. */
  181. - (FImmutableTree *) removeValueAtPath:(FPath *)relativePath {
  182. if ([relativePath isEmpty]) {
  183. if ([self.children isEmpty]) {
  184. return [FImmutableTree empty];
  185. } else {
  186. return [[FImmutableTree alloc] initWithValue:nil children:self.children];
  187. }
  188. } else {
  189. NSString *front = [relativePath getFront];
  190. FImmutableTree *child = [self.children get:front];
  191. if (child) {
  192. FImmutableTree *newChild = [child removeValueAtPath:[relativePath popFront]];
  193. FImmutableSortedDictionary *newChildren;
  194. if ([newChild isEmpty]) {
  195. newChildren = [self.children removeKey:front];
  196. } else {
  197. newChildren = [self.children insertKey:front withValue:newChild];
  198. }
  199. if (self.value == nil && [newChildren isEmpty]) {
  200. return [FImmutableTree empty];
  201. } else {
  202. return [[FImmutableTree alloc] initWithValue:self.value children:newChildren];
  203. }
  204. } else {
  205. return self;
  206. }
  207. }
  208. }
  209. /**
  210. * Gets a value from the tree
  211. */
  212. - (id) valueAtPath:(FPath *)relativePath {
  213. if ([relativePath isEmpty]) {
  214. return self.value;
  215. } else {
  216. NSString *front = [relativePath getFront];
  217. FImmutableTree *child = [self.children get:front];
  218. if (child) {
  219. return [child valueAtPath:[relativePath popFront]];
  220. } else {
  221. return nil;
  222. }
  223. }
  224. }
  225. /**
  226. * Replaces the subtree at the specified path with the given new tree
  227. */
  228. - (FImmutableTree *) setTree:(FImmutableTree *)newTree atPath:(FPath *)relativePath {
  229. if ([relativePath isEmpty]) {
  230. return newTree;
  231. } else {
  232. NSString *front = [relativePath getFront];
  233. FImmutableTree *child = [self.children get:front];
  234. if (child == nil) {
  235. child = [FImmutableTree empty];
  236. }
  237. FImmutableTree *newChild = [child setTree:newTree atPath:[relativePath popFront]];
  238. FImmutableSortedDictionary *newChildren;
  239. if ([newChild isEmpty]) {
  240. newChildren = [self.children removeKey:front];
  241. } else {
  242. newChildren = [self.children insertKey:front withValue:newChild];
  243. }
  244. return [[FImmutableTree alloc] initWithValue:self.value children:newChildren];
  245. }
  246. }
  247. /**
  248. * Performs a depth first fold on this tree. Transforms a tree into a single value, given a function that operates on
  249. * the path to a node, an optional current value, and a map of the child names to folded subtrees
  250. */
  251. - (id) foldWithBlock:(id (^)(FPath *path, id value, NSDictionary *foldedChildren))block {
  252. return [self foldWithPathSoFar:[FPath empty] withBlock:block];
  253. }
  254. /**
  255. * Recursive helper for public facing foldWithBlock: method
  256. */
  257. - (id) foldWithPathSoFar:(FPath *)pathSoFar withBlock:(id (^)(FPath *path, id value, NSDictionary *foldedChildren))block {
  258. __block NSMutableDictionary *accum = [[NSMutableDictionary alloc] init];
  259. [self.children enumerateKeysAndObjectsUsingBlock:^(NSString *childKey, FImmutableTree *childTree, BOOL *stop) {
  260. accum[childKey] = [childTree foldWithPathSoFar:[pathSoFar childFromString:childKey] withBlock:block];
  261. }];
  262. return block(pathSoFar, self.value, accum);
  263. }
  264. /**
  265. * Find the first matching value on the given path. Return the result of applying block to it.
  266. */
  267. - (id) findOnPath:(FPath *)path andApplyBlock:(id (^)(FPath *path, id value))block {
  268. return [self findOnPath:path pathSoFar:[FPath empty] andApplyBlock:block];
  269. }
  270. - (id) findOnPath:(FPath *)pathToFollow pathSoFar:(FPath *)pathSoFar andApplyBlock:(id (^)(FPath *path, id value))block {
  271. id result = self.value ? block(pathSoFar, self.value) : nil;
  272. if (result != nil) {
  273. return result;
  274. } else {
  275. if ([pathToFollow isEmpty]) {
  276. return nil;
  277. } else {
  278. NSString *front = [pathToFollow getFront];
  279. FImmutableTree *nextChild = [self.children get:front];
  280. if (nextChild != nil) {
  281. return [nextChild findOnPath:[pathToFollow popFront] pathSoFar:[pathSoFar childFromString:front] andApplyBlock:block];
  282. } else {
  283. return nil;
  284. }
  285. }
  286. }
  287. }
  288. /**
  289. * Call the block on each value along the path for as long as that function returns true
  290. * @return The path to the deepest location inspected
  291. */
  292. - (FPath *) forEachOnPath:(FPath *)path whileBlock:(BOOL (^)(FPath *, id))block {
  293. return [self forEachOnPath:path pathSoFar:[FPath empty] whileBlock:block];
  294. }
  295. - (FPath *) forEachOnPath:(FPath *)pathToFollow pathSoFar:(FPath *)pathSoFar whileBlock:(BOOL (^)(FPath *, id))block {
  296. if ([pathToFollow isEmpty]) {
  297. if (self.value) {
  298. block(pathSoFar, self.value);
  299. }
  300. return pathSoFar;
  301. } else {
  302. BOOL shouldContinue = YES;
  303. if (self.value) {
  304. shouldContinue = block(pathSoFar, self.value);
  305. }
  306. if (shouldContinue) {
  307. NSString *front = [pathToFollow getFront];
  308. FImmutableTree *nextChild = [self.children get:front];
  309. if (nextChild) {
  310. return [nextChild forEachOnPath:[pathToFollow popFront] pathSoFar:[pathSoFar childFromString:front] whileBlock:block];
  311. } else {
  312. return pathSoFar;
  313. }
  314. } else {
  315. return pathSoFar;
  316. }
  317. }
  318. }
  319. - (FImmutableTree *) forEachOnPath:(FPath *)path performBlock:(void (^)(FPath *path, id value))block {
  320. return [self forEachOnPath:path pathSoFar:[FPath empty] performBlock:block];
  321. }
  322. - (FImmutableTree *) forEachOnPath:(FPath *)pathToFollow pathSoFar:(FPath *)pathSoFar performBlock:(void (^)(FPath *path, id value))block {
  323. if ([pathToFollow isEmpty]) {
  324. return self;
  325. } else {
  326. if (self.value) {
  327. block(pathSoFar, self.value);
  328. }
  329. NSString *front = [pathToFollow getFront];
  330. FImmutableTree *nextChild = [self.children get:front];
  331. if (nextChild) {
  332. return [nextChild forEachOnPath:[pathToFollow popFront] pathSoFar:[pathSoFar childFromString:front] performBlock:block];
  333. } else {
  334. return [FImmutableTree empty];
  335. }
  336. }
  337. }
  338. /**
  339. * Calls the given block for each node in the tree that has a value. Called in depth-first order
  340. */
  341. - (void) forEach:(void (^)(FPath *path, id value))block {
  342. [self forEachPathSoFar:[FPath empty] withBlock:block];
  343. }
  344. - (void) forEachPathSoFar:(FPath *)pathSoFar withBlock:(void (^)(FPath *path, id value))block {
  345. [self.children enumerateKeysAndObjectsUsingBlock:^(NSString *childKey, FImmutableTree *childTree, BOOL *stop) {
  346. [childTree forEachPathSoFar:[pathSoFar childFromString:childKey] withBlock:block];
  347. }];
  348. if (self.value) {
  349. block(pathSoFar, self.value);
  350. }
  351. }
  352. - (void) forEachChild:(void (^)(NSString *childKey, id childValue))block {
  353. [self.children enumerateKeysAndObjectsUsingBlock:^(NSString *childKey, FImmutableTree *childTree, BOOL *stop) {
  354. if (childTree.value) {
  355. block(childKey, childTree.value);
  356. }
  357. }];
  358. }
  359. - (BOOL)isEqual:(id)object {
  360. if (![object isKindOfClass:[FImmutableTree class]]) {
  361. return NO;
  362. }
  363. FImmutableTree *other = (FImmutableTree *)object;
  364. return (self.value == other.value || [self.value isEqual:other.value]) && [self.children isEqual:other.children];
  365. }
  366. - (NSUInteger)hash {
  367. return self.children.hash * 31 + [self.value hash];
  368. }
  369. - (NSString *) description {
  370. NSMutableString *string = [[NSMutableString alloc] init];
  371. [string appendString:@"FImmutableTree { value="];
  372. [string appendString:(self.value ? [self.value description] : @"<nil>")];
  373. [string appendString:@", children={"];
  374. [self.children enumerateKeysAndObjectsUsingBlock:^(NSString *childKey, FImmutableTree *childTree, BOOL *stop) {
  375. [string appendString:@" "];
  376. [string appendString:childKey];
  377. [string appendString:@"="];
  378. [string appendString:[childTree.value description]];
  379. }];
  380. [string appendString:@" } }"];
  381. return [NSString stringWithString:string];
  382. }
  383. - (NSString *) debugDescription {
  384. return [self description];
  385. }
  386. @end