FSTFieldValue.m 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887
  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 "Firestore/Source/Model/FSTFieldValue.h"
  17. #import "Firestore/Source/API/FIRGeoPoint+Internal.h"
  18. #import "Firestore/Source/API/FIRSnapshotOptions+Internal.h"
  19. #import "Firestore/Source/Core/FSTTimestamp.h"
  20. #import "Firestore/Source/Model/FSTDatabaseID.h"
  21. #import "Firestore/Source/Model/FSTDocumentKey.h"
  22. #import "Firestore/Source/Model/FSTPath.h"
  23. #import "Firestore/Source/Util/FSTAssert.h"
  24. #import "Firestore/Source/Util/FSTClasses.h"
  25. #import "Firestore/Source/Util/FSTComparison.h"
  26. NS_ASSUME_NONNULL_BEGIN
  27. #pragma mark - FSTFieldValueOptions
  28. @implementation FSTFieldValueOptions
  29. + (instancetype)optionsForSnapshotOptions:(FIRSnapshotOptions *)options {
  30. if (options.serverTimestampBehavior == FSTServerTimestampBehaviorNone) {
  31. static FSTFieldValueOptions *defaultInstance = nil;
  32. static dispatch_once_t onceToken;
  33. dispatch_once(&onceToken, ^{
  34. defaultInstance = [[FSTFieldValueOptions alloc]
  35. initWithServerTimestampBehavior:FSTServerTimestampBehaviorNone];
  36. });
  37. return defaultInstance;
  38. } else {
  39. return [[FSTFieldValueOptions alloc]
  40. initWithServerTimestampBehavior:options.serverTimestampBehavior];
  41. }
  42. }
  43. - (instancetype)initWithServerTimestampBehavior:
  44. (FSTServerTimestampBehavior)serverTimestampBehavior {
  45. self = [super init];
  46. if (self) {
  47. _serverTimestampBehavior = serverTimestampBehavior;
  48. }
  49. return self;
  50. }
  51. @end
  52. #pragma mark - FSTFieldValue
  53. @interface FSTFieldValue ()
  54. - (NSComparisonResult)defaultCompare:(FSTFieldValue *)other;
  55. @end
  56. @implementation FSTFieldValue
  57. - (FSTTypeOrder)typeOrder {
  58. @throw FSTAbstractMethodException(); // NOLINT
  59. }
  60. - (id)value {
  61. return [self valueWithOptions:[FSTFieldValueOptions
  62. optionsForSnapshotOptions:[FIRSnapshotOptions defaultOptions]]];
  63. }
  64. - (id)valueWithOptions:(FSTFieldValueOptions *)options {
  65. @throw FSTAbstractMethodException(); // NOLINT
  66. }
  67. - (BOOL)isEqual:(id)other {
  68. @throw FSTAbstractMethodException(); // NOLINT
  69. }
  70. - (NSUInteger)hash {
  71. @throw FSTAbstractMethodException(); // NOLINT
  72. }
  73. - (NSComparisonResult)compare:(FSTFieldValue *)other {
  74. @throw FSTAbstractMethodException(); // NOLINT
  75. }
  76. - (NSString *)description {
  77. return [[self value] description];
  78. }
  79. - (NSComparisonResult)defaultCompare:(FSTFieldValue *)other {
  80. if (self.typeOrder > other.typeOrder) {
  81. return NSOrderedDescending;
  82. } else {
  83. FSTAssert(self.typeOrder < other.typeOrder,
  84. @"defaultCompare should not be used for values of same type.");
  85. return NSOrderedAscending;
  86. }
  87. }
  88. @end
  89. #pragma mark - FSTNullValue
  90. @implementation FSTNullValue
  91. + (instancetype)nullValue {
  92. static FSTNullValue *sharedInstance = nil;
  93. static dispatch_once_t onceToken;
  94. dispatch_once(&onceToken, ^{
  95. sharedInstance = [[FSTNullValue alloc] init];
  96. });
  97. return sharedInstance;
  98. }
  99. - (FSTTypeOrder)typeOrder {
  100. return FSTTypeOrderNull;
  101. }
  102. - (id)valueWithOptions:(FSTFieldValueOptions *)options {
  103. return [NSNull null];
  104. }
  105. - (BOOL)isEqual:(id)other {
  106. return [other isKindOfClass:[self class]];
  107. }
  108. - (NSUInteger)hash {
  109. return 47;
  110. }
  111. - (NSComparisonResult)compare:(FSTFieldValue *)other {
  112. if ([other isKindOfClass:[self class]]) {
  113. return NSOrderedSame;
  114. } else {
  115. return [self defaultCompare:other];
  116. }
  117. }
  118. @end
  119. #pragma mark - FSTBooleanValue
  120. @interface FSTBooleanValue ()
  121. @property(nonatomic, assign, readonly) BOOL internalValue;
  122. @end
  123. @implementation FSTBooleanValue
  124. + (instancetype)trueValue {
  125. static FSTBooleanValue *sharedInstance = nil;
  126. static dispatch_once_t onceToken;
  127. dispatch_once(&onceToken, ^{
  128. sharedInstance = [[FSTBooleanValue alloc] initWithValue:YES];
  129. });
  130. return sharedInstance;
  131. }
  132. + (instancetype)falseValue {
  133. static FSTBooleanValue *sharedInstance = nil;
  134. static dispatch_once_t onceToken;
  135. dispatch_once(&onceToken, ^{
  136. sharedInstance = [[FSTBooleanValue alloc] initWithValue:NO];
  137. });
  138. return sharedInstance;
  139. }
  140. + (instancetype)booleanValue:(BOOL)value {
  141. return value ? [FSTBooleanValue trueValue] : [FSTBooleanValue falseValue];
  142. }
  143. - (id)initWithValue:(BOOL)value {
  144. self = [super init];
  145. if (self) {
  146. _internalValue = value;
  147. }
  148. return self;
  149. }
  150. - (FSTTypeOrder)typeOrder {
  151. return FSTTypeOrderBoolean;
  152. }
  153. - (id)valueWithOptions:(FSTFieldValueOptions *)options {
  154. return self.internalValue ? @YES : @NO;
  155. }
  156. - (BOOL)isEqual:(id)other {
  157. // Since we create shared instances for true / false, we can use reference equality.
  158. return self == other;
  159. }
  160. - (NSUInteger)hash {
  161. return self.internalValue ? 1231 : 1237;
  162. }
  163. - (NSComparisonResult)compare:(FSTFieldValue *)other {
  164. if ([other isKindOfClass:[FSTBooleanValue class]]) {
  165. return FSTCompareBools(self.internalValue, ((FSTBooleanValue *)other).internalValue);
  166. } else {
  167. return [self defaultCompare:other];
  168. }
  169. }
  170. @end
  171. #pragma mark - FSTNumberValue
  172. @implementation FSTNumberValue
  173. - (FSTTypeOrder)typeOrder {
  174. return FSTTypeOrderNumber;
  175. }
  176. - (NSComparisonResult)compare:(FSTFieldValue *)other {
  177. if (![other isKindOfClass:[FSTNumberValue class]]) {
  178. return [self defaultCompare:other];
  179. } else {
  180. if ([self isKindOfClass:[FSTDoubleValue class]]) {
  181. double thisDouble = ((FSTDoubleValue *)self).internalValue;
  182. if ([other isKindOfClass:[FSTDoubleValue class]]) {
  183. return FSTCompareDoubles(thisDouble, ((FSTDoubleValue *)other).internalValue);
  184. } else {
  185. FSTAssert([other isKindOfClass:[FSTIntegerValue class]], @"Unknown number value: %@",
  186. other);
  187. return FSTCompareMixed(thisDouble, ((FSTIntegerValue *)other).internalValue);
  188. }
  189. } else {
  190. int64_t thisInt = ((FSTIntegerValue *)self).internalValue;
  191. if ([other isKindOfClass:[FSTIntegerValue class]]) {
  192. return FSTCompareInt64s(thisInt, ((FSTIntegerValue *)other).internalValue);
  193. } else {
  194. FSTAssert([other isKindOfClass:[FSTDoubleValue class]], @"Unknown number value: %@", other);
  195. return -1 * FSTCompareMixed(((FSTDoubleValue *)other).internalValue, thisInt);
  196. }
  197. }
  198. }
  199. }
  200. @end
  201. #pragma mark - FSTIntegerValue
  202. @interface FSTIntegerValue ()
  203. @property(nonatomic, assign, readonly) int64_t internalValue;
  204. @end
  205. @implementation FSTIntegerValue
  206. + (instancetype)integerValue:(int64_t)value {
  207. return [[FSTIntegerValue alloc] initWithValue:value];
  208. }
  209. - (id)initWithValue:(int64_t)value {
  210. self = [super init];
  211. if (self) {
  212. _internalValue = value;
  213. }
  214. return self;
  215. }
  216. - (id)valueWithOptions:(FSTFieldValueOptions *)options {
  217. return @(self.internalValue);
  218. }
  219. - (BOOL)isEqual:(id)other {
  220. // NOTE: DoubleValue and LongValue instances may compare: the same, but that doesn't make them
  221. // equal via isEqual:
  222. return [other isKindOfClass:[FSTIntegerValue class]] &&
  223. self.internalValue == ((FSTIntegerValue *)other).internalValue;
  224. }
  225. - (NSUInteger)hash {
  226. return (((NSUInteger)self.internalValue) ^ (NSUInteger)(self.internalValue >> 32));
  227. }
  228. // NOTE: compare: is implemented in NumberValue.
  229. @end
  230. #pragma mark - FSTDoubleValue
  231. @interface FSTDoubleValue ()
  232. @property(nonatomic, assign, readonly) double internalValue;
  233. @end
  234. @implementation FSTDoubleValue
  235. + (instancetype)doubleValue:(double)value {
  236. // Normalize NaNs to match the behavior on the backend (which uses Double.doubletoLongBits()).
  237. if (isnan(value)) {
  238. return [FSTDoubleValue nanValue];
  239. }
  240. return [[FSTDoubleValue alloc] initWithValue:value];
  241. }
  242. + (instancetype)nanValue {
  243. static FSTDoubleValue *sharedInstance = nil;
  244. static dispatch_once_t onceToken;
  245. dispatch_once(&onceToken, ^{
  246. sharedInstance = [[FSTDoubleValue alloc] initWithValue:NAN];
  247. });
  248. return sharedInstance;
  249. }
  250. - (id)initWithValue:(double)value {
  251. self = [super init];
  252. if (self) {
  253. _internalValue = value;
  254. }
  255. return self;
  256. }
  257. - (id)valueWithOptions:(FSTFieldValueOptions *)options {
  258. return @(self.internalValue);
  259. }
  260. - (BOOL)isEqual:(id)other {
  261. // NOTE: DoubleValue and LongValue instances may compare: the same, but that doesn't make them
  262. // equal via isEqual:
  263. // NOTE: isEqual: should compare NaN equal to itself and -0.0 not equal to 0.0.
  264. return [other isKindOfClass:[FSTDoubleValue class]] &&
  265. FSTDoubleBitwiseEquals(self.internalValue, ((FSTDoubleValue *)other).internalValue);
  266. }
  267. - (NSUInteger)hash {
  268. return FSTDoubleBitwiseHash(self.internalValue);
  269. }
  270. // NOTE: compare: is implemented in NumberValue.
  271. @end
  272. #pragma mark - FSTStringValue
  273. @interface FSTStringValue ()
  274. @property(nonatomic, copy, readonly) NSString *internalValue;
  275. @end
  276. // TODO(b/37267885): Add truncation support
  277. @implementation FSTStringValue
  278. + (instancetype)stringValue:(NSString *)value {
  279. return [[FSTStringValue alloc] initWithValue:value];
  280. }
  281. - (id)initWithValue:(NSString *)value {
  282. self = [super init];
  283. if (self) {
  284. _internalValue = [value copy];
  285. }
  286. return self;
  287. }
  288. - (FSTTypeOrder)typeOrder {
  289. return FSTTypeOrderString;
  290. }
  291. - (id)valueWithOptions:(FSTFieldValueOptions *)options {
  292. return self.internalValue;
  293. }
  294. - (BOOL)isEqual:(id)other {
  295. return [other isKindOfClass:[FSTStringValue class]] &&
  296. [self.internalValue isEqualToString:((FSTStringValue *)other).internalValue];
  297. }
  298. - (NSUInteger)hash {
  299. return self.internalValue ? 1 : 0;
  300. }
  301. - (NSComparisonResult)compare:(FSTFieldValue *)other {
  302. if ([other isKindOfClass:[FSTStringValue class]]) {
  303. return FSTCompareStrings(self.internalValue, ((FSTStringValue *)other).internalValue);
  304. } else {
  305. return [self defaultCompare:other];
  306. }
  307. }
  308. @end
  309. #pragma mark - FSTTimestampValue
  310. @interface FSTTimestampValue ()
  311. @property(nonatomic, strong, readonly) FSTTimestamp *internalValue;
  312. @end
  313. @implementation FSTTimestampValue
  314. + (instancetype)timestampValue:(FSTTimestamp *)value {
  315. return [[FSTTimestampValue alloc] initWithValue:value];
  316. }
  317. - (id)initWithValue:(FSTTimestamp *)value {
  318. self = [super init];
  319. if (self) {
  320. _internalValue = value; // FSTTimestamp is immutable.
  321. }
  322. return self;
  323. }
  324. - (FSTTypeOrder)typeOrder {
  325. return FSTTypeOrderTimestamp;
  326. }
  327. - (id)valueWithOptions:(FSTFieldValueOptions *)options {
  328. // For developers, we expose Timestamps as Dates.
  329. return self.internalValue.approximateDateValue;
  330. }
  331. - (BOOL)isEqual:(id)other {
  332. return [other isKindOfClass:[FSTTimestampValue class]] &&
  333. [self.internalValue isEqual:((FSTTimestampValue *)other).internalValue];
  334. }
  335. - (NSUInteger)hash {
  336. return [self.internalValue hash];
  337. }
  338. - (NSComparisonResult)compare:(FSTFieldValue *)other {
  339. if ([other isKindOfClass:[FSTTimestampValue class]]) {
  340. return [self.internalValue compare:((FSTTimestampValue *)other).internalValue];
  341. } else if ([other isKindOfClass:[FSTServerTimestampValue class]]) {
  342. // Concrete timestamps come before server timestamps.
  343. return NSOrderedAscending;
  344. } else {
  345. return [self defaultCompare:other];
  346. }
  347. }
  348. @end
  349. #pragma mark - FSTServerTimestampValue
  350. @implementation FSTServerTimestampValue
  351. + (instancetype)serverTimestampValueWithLocalWriteTime:(FSTTimestamp *)localWriteTime
  352. previousValue:(nullable FSTFieldValue *)previousValue {
  353. return [[FSTServerTimestampValue alloc] initWithLocalWriteTime:localWriteTime
  354. previousValue:previousValue];
  355. }
  356. - (id)initWithLocalWriteTime:(FSTTimestamp *)localWriteTime
  357. previousValue:(nullable FSTFieldValue *)previousValue {
  358. self = [super init];
  359. if (self) {
  360. _localWriteTime = localWriteTime;
  361. _previousValue = previousValue;
  362. }
  363. return self;
  364. }
  365. - (FSTTypeOrder)typeOrder {
  366. return FSTTypeOrderTimestamp;
  367. }
  368. - (id)valueWithOptions:(FSTFieldValueOptions *)options {
  369. switch (options.serverTimestampBehavior) {
  370. case FSTServerTimestampBehaviorNone:
  371. return [NSNull null];
  372. case FSTServerTimestampBehaviorEstimate:
  373. return [self.localWriteTime approximateDateValue];
  374. case FSTServerTimestampBehaviorPrevious:
  375. return self.previousValue ? [self.previousValue valueWithOptions:options] : [NSNull null];
  376. default:
  377. FSTFail(@"Unexpected server timestamp option: %d", (int)options.serverTimestampBehavior);
  378. }
  379. }
  380. - (BOOL)isEqual:(id)other {
  381. return [other isKindOfClass:[FSTServerTimestampValue class]] &&
  382. [self.localWriteTime isEqual:((FSTServerTimestampValue *)other).localWriteTime];
  383. }
  384. - (NSUInteger)hash {
  385. return [self.localWriteTime hash];
  386. }
  387. - (NSString *)description {
  388. return [NSString stringWithFormat:@"<ServerTimestamp localTime=%@>", self.localWriteTime];
  389. }
  390. - (NSComparisonResult)compare:(FSTFieldValue *)other {
  391. if ([other isKindOfClass:[FSTServerTimestampValue class]]) {
  392. return [self.localWriteTime compare:((FSTServerTimestampValue *)other).localWriteTime];
  393. } else if ([other isKindOfClass:[FSTTimestampValue class]]) {
  394. // Server timestamps come after all concrete timestamps.
  395. return NSOrderedDescending;
  396. } else {
  397. return [self defaultCompare:other];
  398. }
  399. }
  400. @end
  401. #pragma mark - FSTGeoPointValue
  402. @interface FSTGeoPointValue ()
  403. @property(nonatomic, strong, readonly) FIRGeoPoint *internalValue;
  404. @end
  405. @implementation FSTGeoPointValue
  406. + (instancetype)geoPointValue:(FIRGeoPoint *)value {
  407. return [[FSTGeoPointValue alloc] initWithValue:value];
  408. }
  409. - (id)initWithValue:(FIRGeoPoint *)value {
  410. self = [super init];
  411. if (self) {
  412. _internalValue = value; // FIRGeoPoint is immutable.
  413. }
  414. return self;
  415. }
  416. - (FSTTypeOrder)typeOrder {
  417. return FSTTypeOrderGeoPoint;
  418. }
  419. - (id)valueWithOptions:(FSTFieldValueOptions *)options {
  420. return self.internalValue;
  421. }
  422. - (BOOL)isEqual:(id)other {
  423. return [other isKindOfClass:[FSTGeoPointValue class]] &&
  424. [self.internalValue isEqual:((FSTGeoPointValue *)other).internalValue];
  425. }
  426. - (NSUInteger)hash {
  427. return [self.internalValue hash];
  428. }
  429. - (NSComparisonResult)compare:(FSTFieldValue *)other {
  430. if ([other isKindOfClass:[FSTGeoPointValue class]]) {
  431. return [self.internalValue compare:((FSTGeoPointValue *)other).internalValue];
  432. } else {
  433. return [self defaultCompare:other];
  434. }
  435. }
  436. @end
  437. #pragma mark - FSTBlobValue
  438. @interface FSTBlobValue ()
  439. @property(nonatomic, copy, readonly) NSData *internalValue;
  440. @end
  441. // TODO(b/37267885): Add truncation support
  442. @implementation FSTBlobValue
  443. + (instancetype)blobValue:(NSData *)value {
  444. return [[FSTBlobValue alloc] initWithValue:value];
  445. }
  446. - (id)initWithValue:(NSData *)value {
  447. self = [super init];
  448. if (self) {
  449. _internalValue = [value copy];
  450. }
  451. return self;
  452. }
  453. - (FSTTypeOrder)typeOrder {
  454. return FSTTypeOrderBlob;
  455. }
  456. - (id)valueWithOptions:(FSTFieldValueOptions *)options {
  457. return self.internalValue;
  458. }
  459. - (BOOL)isEqual:(id)other {
  460. return [other isKindOfClass:[FSTBlobValue class]] &&
  461. [self.internalValue isEqual:((FSTBlobValue *)other).internalValue];
  462. }
  463. - (NSUInteger)hash {
  464. return [self.internalValue hash];
  465. }
  466. - (NSComparisonResult)compare:(FSTFieldValue *)other {
  467. if ([other isKindOfClass:[FSTBlobValue class]]) {
  468. return FSTCompareBytes(self.internalValue, ((FSTBlobValue *)other).internalValue);
  469. } else {
  470. return [self defaultCompare:other];
  471. }
  472. }
  473. @end
  474. #pragma mark - FSTReferenceValue
  475. @interface FSTReferenceValue ()
  476. @property(nonatomic, strong, readonly) FSTDocumentKey *key;
  477. @end
  478. @implementation FSTReferenceValue
  479. + (instancetype)referenceValue:(FSTDocumentKey *)value databaseID:(FSTDatabaseID *)databaseID {
  480. return [[FSTReferenceValue alloc] initWithValue:value databaseID:databaseID];
  481. }
  482. - (id)initWithValue:(FSTDocumentKey *)value databaseID:(FSTDatabaseID *)databaseID {
  483. self = [super init];
  484. if (self) {
  485. _key = value;
  486. _databaseID = databaseID;
  487. }
  488. return self;
  489. }
  490. - (id)valueWithOptions:(FSTFieldValueOptions *)options {
  491. return self.key;
  492. }
  493. - (FSTTypeOrder)typeOrder {
  494. return FSTTypeOrderReference;
  495. }
  496. - (BOOL)isEqual:(id)other {
  497. if (other == self) {
  498. return YES;
  499. }
  500. if (![other isKindOfClass:[FSTReferenceValue class]]) {
  501. return NO;
  502. }
  503. FSTReferenceValue *otherRef = (FSTReferenceValue *)other;
  504. return [self.key isEqualToKey:otherRef.key] &&
  505. [self.databaseID isEqualToDatabaseId:otherRef.databaseID];
  506. }
  507. - (NSUInteger)hash {
  508. NSUInteger result = [self.databaseID hash];
  509. result = 31 * result + [self.key hash];
  510. return result;
  511. }
  512. - (NSComparisonResult)compare:(FSTFieldValue *)other {
  513. if ([other isKindOfClass:[FSTReferenceValue class]]) {
  514. FSTReferenceValue *ref = (FSTReferenceValue *)other;
  515. NSComparisonResult cmp = [self.databaseID compare:ref.databaseID];
  516. return cmp != NSOrderedSame ? cmp : [self.key compare:ref.key];
  517. } else {
  518. return [self defaultCompare:other];
  519. }
  520. }
  521. @end
  522. #pragma mark - FSTObjectValue
  523. @interface FSTObjectValue ()
  524. @property(nonatomic, strong, readonly)
  525. FSTImmutableSortedDictionary<NSString *, FSTFieldValue *> *internalValue;
  526. @end
  527. @implementation FSTObjectValue
  528. + (instancetype)objectValue {
  529. static FSTObjectValue *sharedEmptyInstance = nil;
  530. static dispatch_once_t onceToken;
  531. dispatch_once(&onceToken, ^{
  532. FSTImmutableSortedDictionary<NSString *, FSTFieldValue *> *empty =
  533. [FSTImmutableSortedDictionary dictionaryWithComparator:FSTStringComparator];
  534. sharedEmptyInstance = [[FSTObjectValue alloc] initWithImmutableDictionary:empty];
  535. });
  536. return sharedEmptyInstance;
  537. }
  538. - (instancetype)initWithImmutableDictionary:
  539. (FSTImmutableSortedDictionary<NSString *, FSTFieldValue *> *)value {
  540. self = [super init];
  541. if (self) {
  542. _internalValue = value; // FSTImmutableSortedDictionary is immutable.
  543. }
  544. return self;
  545. }
  546. - (id)initWithDictionary:(NSDictionary<NSString *, FSTFieldValue *> *)value {
  547. FSTImmutableSortedDictionary<NSString *, FSTFieldValue *> *dictionary =
  548. [FSTImmutableSortedDictionary dictionaryWithDictionary:value comparator:FSTStringComparator];
  549. return [self initWithImmutableDictionary:dictionary];
  550. }
  551. - (id)valueWithOptions:(FSTFieldValueOptions *)options {
  552. NSMutableDictionary *result = [NSMutableDictionary dictionary];
  553. [self.internalValue
  554. enumerateKeysAndObjectsUsingBlock:^(NSString *key, FSTFieldValue *obj, BOOL *stop) {
  555. result[key] = [obj valueWithOptions:options];
  556. }];
  557. return result;
  558. }
  559. - (FSTTypeOrder)typeOrder {
  560. return FSTTypeOrderObject;
  561. }
  562. - (BOOL)isEqual:(id)other {
  563. if (other == self) {
  564. return YES;
  565. }
  566. if (![other isKindOfClass:[FSTObjectValue class]]) {
  567. return NO;
  568. }
  569. FSTObjectValue *otherObj = other;
  570. return [self.internalValue isEqual:otherObj.internalValue];
  571. }
  572. - (NSUInteger)hash {
  573. return [self.internalValue hash];
  574. }
  575. - (NSComparisonResult)compare:(FSTFieldValue *)other {
  576. if ([other isKindOfClass:[FSTObjectValue class]]) {
  577. FSTImmutableSortedDictionary *selfDict = self.internalValue;
  578. FSTImmutableSortedDictionary *otherDict = ((FSTObjectValue *)other).internalValue;
  579. NSEnumerator *enumerator1 = [selfDict keyEnumerator];
  580. NSEnumerator *enumerator2 = [otherDict keyEnumerator];
  581. NSString *key1 = [enumerator1 nextObject];
  582. NSString *key2 = [enumerator2 nextObject];
  583. while (key1 && key2) {
  584. NSComparisonResult keyCompare = [key1 compare:key2];
  585. if (keyCompare != NSOrderedSame) {
  586. return keyCompare;
  587. }
  588. NSComparisonResult valueCompare = [selfDict[key1] compare:otherDict[key2]];
  589. if (valueCompare != NSOrderedSame) {
  590. return valueCompare;
  591. }
  592. key1 = [enumerator1 nextObject];
  593. key2 = [enumerator2 nextObject];
  594. }
  595. // Only equal if both enumerators are exhausted.
  596. return FSTCompareBools(key1 != nil, key2 != nil);
  597. } else {
  598. return [self defaultCompare:other];
  599. }
  600. }
  601. - (nullable FSTFieldValue *)valueForPath:(FSTFieldPath *)fieldPath {
  602. FSTFieldValue *value = self;
  603. for (int i = 0, max = fieldPath.length; value && i < max; i++) {
  604. if (![value isMemberOfClass:[FSTObjectValue class]]) {
  605. return nil;
  606. }
  607. NSString *fieldName = fieldPath[i];
  608. value = ((FSTObjectValue *)value).internalValue[fieldName];
  609. }
  610. return value;
  611. }
  612. - (FSTObjectValue *)objectBySettingValue:(FSTFieldValue *)value forPath:(FSTFieldPath *)fieldPath {
  613. FSTAssert([fieldPath length] > 0, @"Cannot set value with an empty path");
  614. NSString *childName = [fieldPath firstSegment];
  615. if ([fieldPath length] == 1) {
  616. // Recursive base case:
  617. return [self objectBySettingValue:value forField:childName];
  618. } else {
  619. // Nested path. Recursively generate a new sub-object and then wrap a new FSTObjectValue around
  620. // the result.
  621. FSTFieldValue *child = [_internalValue objectForKey:childName];
  622. FSTObjectValue *childObject;
  623. if ([child isKindOfClass:[FSTObjectValue class]]) {
  624. childObject = (FSTObjectValue *)child;
  625. } else {
  626. // If the child is not found or is a primitive type, pretend as if an empty object lived
  627. // there.
  628. childObject = [FSTObjectValue objectValue];
  629. }
  630. FSTFieldValue *newChild =
  631. [childObject objectBySettingValue:value forPath:[fieldPath pathByRemovingFirstSegment]];
  632. return [self objectBySettingValue:newChild forField:childName];
  633. }
  634. }
  635. - (FSTObjectValue *)objectByDeletingPath:(FSTFieldPath *)fieldPath {
  636. FSTAssert([fieldPath length] > 0, @"Cannot delete an empty path");
  637. NSString *childName = [fieldPath firstSegment];
  638. if ([fieldPath length] == 1) {
  639. return [[FSTObjectValue alloc]
  640. initWithImmutableDictionary:[_internalValue dictionaryByRemovingObjectForKey:childName]];
  641. } else {
  642. FSTFieldValue *child = _internalValue[childName];
  643. if ([child isKindOfClass:[FSTObjectValue class]]) {
  644. FSTObjectValue *newChild =
  645. [((FSTObjectValue *)child) objectByDeletingPath:[fieldPath pathByRemovingFirstSegment]];
  646. return [self objectBySettingValue:newChild forField:childName];
  647. } else {
  648. // If the child is not found or is a primitive type, make no modifications
  649. return self;
  650. }
  651. }
  652. }
  653. - (FSTObjectValue *)objectBySettingValue:(FSTFieldValue *)value forField:(NSString *)field {
  654. return [[FSTObjectValue alloc]
  655. initWithImmutableDictionary:[_internalValue dictionaryBySettingObject:value forKey:field]];
  656. }
  657. @end
  658. @interface FSTArrayValue ()
  659. @property(nonatomic, strong, readonly) NSArray<FSTFieldValue *> *internalValue;
  660. @end
  661. #pragma mark - FSTArrayValue
  662. @implementation FSTArrayValue
  663. - (id)initWithValueNoCopy:(NSArray<FSTFieldValue *> *)value {
  664. self = [super init];
  665. if (self) {
  666. // Does not copy, assumes the caller has already copied.
  667. _internalValue = value;
  668. }
  669. return self;
  670. }
  671. - (BOOL)isEqual:(id)other {
  672. if (other == self) {
  673. return YES;
  674. }
  675. if (![other isKindOfClass:[self class]]) {
  676. return NO;
  677. }
  678. // NSArray's isEqual does the right thing for our purposes.
  679. FSTArrayValue *otherArray = other;
  680. return [self.internalValue isEqual:otherArray.internalValue];
  681. }
  682. - (NSUInteger)hash {
  683. return [self.internalValue hash];
  684. }
  685. - (id)valueWithOptions:(FSTFieldValueOptions *)options {
  686. NSMutableArray *result = [NSMutableArray arrayWithCapacity:_internalValue.count];
  687. [self.internalValue enumerateObjectsUsingBlock:^(FSTFieldValue *obj, NSUInteger idx, BOOL *stop) {
  688. [result addObject:[obj value]];
  689. }];
  690. return result;
  691. }
  692. - (FSTTypeOrder)typeOrder {
  693. return FSTTypeOrderArray;
  694. }
  695. - (NSComparisonResult)compare:(FSTFieldValue *)other {
  696. if ([other isKindOfClass:[FSTArrayValue class]]) {
  697. NSArray<FSTFieldValue *> *selfArray = self.internalValue;
  698. NSArray<FSTFieldValue *> *otherArray = ((FSTArrayValue *)other).internalValue;
  699. NSUInteger minLength = MIN(selfArray.count, otherArray.count);
  700. for (NSUInteger i = 0; i < minLength; i++) {
  701. NSComparisonResult cmp = [selfArray[i] compare:otherArray[i]];
  702. if (cmp != NSOrderedSame) {
  703. return cmp;
  704. }
  705. }
  706. return FSTCompareUIntegers(selfArray.count, otherArray.count);
  707. } else {
  708. return [self defaultCompare:other];
  709. }
  710. }
  711. @end
  712. NS_ASSUME_NONNULL_END