| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951 |
- /*
- * 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.
- */
- #import "Firestore/Source/Model/FSTFieldValue.h"
- #import "FIRDocumentSnapshot.h"
- #import "FIRTimestamp.h"
- #import "Firestore/Source/API/FIRGeoPoint+Internal.h"
- #import "Firestore/Source/Model/FSTDocumentKey.h"
- #import "Firestore/Source/Util/FSTClasses.h"
- #include "Firestore/core/src/firebase/firestore/model/database_id.h"
- #include "Firestore/core/src/firebase/firestore/model/document_key.h"
- #include "Firestore/core/src/firebase/firestore/model/field_path.h"
- #include "Firestore/core/src/firebase/firestore/util/comparison.h"
- #include "Firestore/core/src/firebase/firestore/util/hard_assert.h"
- #include "Firestore/core/src/firebase/firestore/util/string_apple.h"
- namespace util = firebase::firestore::util;
- using firebase::firestore::model::DatabaseId;
- using firebase::firestore::model::FieldPath;
- using firebase::firestore::util::Comparator;
- using firebase::firestore::util::CompareMixedNumber;
- using firebase::firestore::util::DoubleBitwiseEquals;
- using firebase::firestore::util::DoubleBitwiseHash;
- using firebase::firestore::util::MakeStringView;
- using firebase::firestore::util::ReverseOrder;
- using firebase::firestore::util::WrapCompare;
- NS_ASSUME_NONNULL_BEGIN
- #pragma mark - FSTFieldValueOptions
- @implementation FSTFieldValueOptions
- - (instancetype)initWithServerTimestampBehavior:(FSTServerTimestampBehavior)serverTimestampBehavior
- timestampsInSnapshotsEnabled:(BOOL)timestampsInSnapshotsEnabled {
- self = [super init];
- if (self) {
- _serverTimestampBehavior = serverTimestampBehavior;
- _timestampsInSnapshotsEnabled = timestampsInSnapshotsEnabled;
- }
- return self;
- }
- @end
- #pragma mark - FSTFieldValue
- @interface FSTFieldValue ()
- - (NSComparisonResult)defaultCompare:(FSTFieldValue *)other;
- @end
- @implementation FSTFieldValue
- - (FSTTypeOrder)typeOrder {
- @throw FSTAbstractMethodException(); // NOLINT
- }
- - (id)value {
- @throw FSTAbstractMethodException(); // NOLINT
- }
- - (id)valueWithOptions:(FSTFieldValueOptions *)options {
- return [self value];
- }
- - (BOOL)isEqual:(id)other {
- @throw FSTAbstractMethodException(); // NOLINT
- }
- - (NSUInteger)hash {
- @throw FSTAbstractMethodException(); // NOLINT
- }
- - (NSComparisonResult)compare:(FSTFieldValue *)other {
- @throw FSTAbstractMethodException(); // NOLINT
- }
- - (NSString *)description {
- return [[self value] description];
- }
- - (NSComparisonResult)defaultCompare:(FSTFieldValue *)other {
- if (self.typeOrder > other.typeOrder) {
- return NSOrderedDescending;
- } else {
- HARD_ASSERT(self.typeOrder < other.typeOrder,
- "defaultCompare should not be used for values of same type.");
- return NSOrderedAscending;
- }
- }
- @end
- #pragma mark - FSTNullValue
- @implementation FSTNullValue
- + (instancetype)nullValue {
- static FSTNullValue *sharedInstance = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- sharedInstance = [[FSTNullValue alloc] init];
- });
- return sharedInstance;
- }
- - (FSTTypeOrder)typeOrder {
- return FSTTypeOrderNull;
- }
- - (id)value {
- return [NSNull null];
- }
- - (BOOL)isEqual:(id)other {
- return [other isKindOfClass:[self class]];
- }
- - (NSUInteger)hash {
- return 47;
- }
- - (NSComparisonResult)compare:(FSTFieldValue *)other {
- if ([other isKindOfClass:[self class]]) {
- return NSOrderedSame;
- } else {
- return [self defaultCompare:other];
- }
- }
- @end
- #pragma mark - FSTBooleanValue
- @interface FSTBooleanValue ()
- @property(nonatomic, assign, readonly) BOOL internalValue;
- @end
- @implementation FSTBooleanValue
- + (instancetype)trueValue {
- static FSTBooleanValue *sharedInstance = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- sharedInstance = [[FSTBooleanValue alloc] initWithValue:YES];
- });
- return sharedInstance;
- }
- + (instancetype)falseValue {
- static FSTBooleanValue *sharedInstance = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- sharedInstance = [[FSTBooleanValue alloc] initWithValue:NO];
- });
- return sharedInstance;
- }
- + (instancetype)booleanValue:(BOOL)value {
- return value ? [FSTBooleanValue trueValue] : [FSTBooleanValue falseValue];
- }
- - (id)initWithValue:(BOOL)value {
- self = [super init];
- if (self) {
- _internalValue = value;
- }
- return self;
- }
- - (FSTTypeOrder)typeOrder {
- return FSTTypeOrderBoolean;
- }
- - (id)value {
- return self.internalValue ? @YES : @NO;
- }
- - (BOOL)isEqual:(id)other {
- // Since we create shared instances for true / false, we can use reference equality.
- return self == other;
- }
- - (NSUInteger)hash {
- return self.internalValue ? 1231 : 1237;
- }
- - (NSComparisonResult)compare:(FSTFieldValue *)other {
- if ([other isKindOfClass:[FSTBooleanValue class]]) {
- return WrapCompare<bool>(self.internalValue, ((FSTBooleanValue *)other).internalValue);
- } else {
- return [self defaultCompare:other];
- }
- }
- @end
- #pragma mark - FSTNumberValue
- @implementation FSTNumberValue
- - (FSTTypeOrder)typeOrder {
- return FSTTypeOrderNumber;
- }
- - (NSComparisonResult)compare:(FSTFieldValue *)other {
- if (![other isKindOfClass:[FSTNumberValue class]]) {
- return [self defaultCompare:other];
- } else {
- if ([self isKindOfClass:[FSTDoubleValue class]]) {
- double thisDouble = ((FSTDoubleValue *)self).internalValue;
- if ([other isKindOfClass:[FSTDoubleValue class]]) {
- return WrapCompare(thisDouble, ((FSTDoubleValue *)other).internalValue);
- } else {
- HARD_ASSERT([other isKindOfClass:[FSTIntegerValue class]], "Unknown number value: %s",
- other);
- auto result = CompareMixedNumber(thisDouble, ((FSTIntegerValue *)other).internalValue);
- return static_cast<NSComparisonResult>(result);
- }
- } else {
- int64_t thisInt = ((FSTIntegerValue *)self).internalValue;
- if ([other isKindOfClass:[FSTIntegerValue class]]) {
- return WrapCompare(thisInt, ((FSTIntegerValue *)other).internalValue);
- } else {
- HARD_ASSERT([other isKindOfClass:[FSTDoubleValue class]], "Unknown number value: %s",
- other);
- double otherDouble = ((FSTDoubleValue *)other).internalValue;
- auto result = ReverseOrder(CompareMixedNumber(otherDouble, thisInt));
- return static_cast<NSComparisonResult>(result);
- }
- }
- }
- }
- @end
- #pragma mark - FSTIntegerValue
- @interface FSTIntegerValue ()
- @property(nonatomic, assign, readonly) int64_t internalValue;
- @end
- @implementation FSTIntegerValue
- + (instancetype)integerValue:(int64_t)value {
- return [[FSTIntegerValue alloc] initWithValue:value];
- }
- - (id)initWithValue:(int64_t)value {
- self = [super init];
- if (self) {
- _internalValue = value;
- }
- return self;
- }
- - (id)value {
- return @(self.internalValue);
- }
- - (BOOL)isEqual:(id)other {
- // NOTE: DoubleValue and LongValue instances may compare: the same, but that doesn't make them
- // equal via isEqual:
- return [other isKindOfClass:[FSTIntegerValue class]] &&
- self.internalValue == ((FSTIntegerValue *)other).internalValue;
- }
- - (NSUInteger)hash {
- return (((NSUInteger)self.internalValue) ^ (NSUInteger)(self.internalValue >> 32));
- }
- // NOTE: compare: is implemented in NumberValue.
- @end
- #pragma mark - FSTDoubleValue
- @interface FSTDoubleValue ()
- @property(nonatomic, assign, readonly) double internalValue;
- @end
- @implementation FSTDoubleValue
- + (instancetype)doubleValue:(double)value {
- // Normalize NaNs to match the behavior on the backend (which uses Double.doubletoLongBits()).
- if (isnan(value)) {
- return [FSTDoubleValue nanValue];
- }
- return [[FSTDoubleValue alloc] initWithValue:value];
- }
- + (instancetype)nanValue {
- static FSTDoubleValue *sharedInstance = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- sharedInstance = [[FSTDoubleValue alloc] initWithValue:NAN];
- });
- return sharedInstance;
- }
- - (id)initWithValue:(double)value {
- self = [super init];
- if (self) {
- _internalValue = value;
- }
- return self;
- }
- - (id)value {
- return @(self.internalValue);
- }
- - (BOOL)isEqual:(id)other {
- // NOTE: DoubleValue and LongValue instances may compare: the same, but that doesn't make them
- // equal via isEqual:
- // NOTE: isEqual: should compare NaN equal to itself and -0.0 not equal to 0.0.
- return [other isKindOfClass:[FSTDoubleValue class]] &&
- DoubleBitwiseEquals(self.internalValue, ((FSTDoubleValue *)other).internalValue);
- }
- - (NSUInteger)hash {
- return DoubleBitwiseHash(self.internalValue);
- }
- // NOTE: compare: is implemented in NumberValue.
- @end
- #pragma mark - FSTStringValue
- /**
- * Specialization of Comparator for NSStrings.
- */
- template <>
- struct Comparator<NSString *> {
- bool operator()(NSString *left, NSString *right) const {
- Comparator<absl::string_view> lessThan;
- return lessThan(MakeString(left), MakeString(right));
- }
- };
- @interface FSTStringValue ()
- @property(nonatomic, copy, readonly) NSString *internalValue;
- @end
- // TODO(b/37267885): Add truncation support
- @implementation FSTStringValue
- + (instancetype)stringValue:(NSString *)value {
- return [[FSTStringValue alloc] initWithValue:value];
- }
- - (id)initWithValue:(NSString *)value {
- self = [super init];
- if (self) {
- _internalValue = [value copy];
- }
- return self;
- }
- - (FSTTypeOrder)typeOrder {
- return FSTTypeOrderString;
- }
- - (id)value {
- return self.internalValue;
- }
- - (BOOL)isEqual:(id)other {
- return [other isKindOfClass:[FSTStringValue class]] &&
- [self.internalValue isEqualToString:((FSTStringValue *)other).internalValue];
- }
- - (NSUInteger)hash {
- return self.internalValue ? 1 : 0;
- }
- - (NSComparisonResult)compare:(FSTFieldValue *)other {
- if ([other isKindOfClass:[FSTStringValue class]]) {
- return WrapCompare(self.internalValue, ((FSTStringValue *)other).internalValue);
- } else {
- return [self defaultCompare:other];
- }
- }
- @end
- #pragma mark - FSTTimestampValue
- @interface FSTTimestampValue ()
- @property(nonatomic, strong, readonly) FIRTimestamp *internalValue;
- @end
- @implementation FSTTimestampValue
- + (instancetype)timestampValue:(FIRTimestamp *)value {
- return [[FSTTimestampValue alloc] initWithValue:value];
- }
- - (id)initWithValue:(FIRTimestamp *)value {
- self = [super init];
- if (self) {
- _internalValue = value; // FIRTimestamp is immutable.
- }
- return self;
- }
- - (FSTTypeOrder)typeOrder {
- return FSTTypeOrderTimestamp;
- }
- - (id)value {
- return self.internalValue;
- }
- - (id)valueWithOptions:(FSTFieldValueOptions *)options {
- if (options.timestampsInSnapshotsEnabled) {
- return self.value;
- } else {
- return [self.value dateValue];
- }
- }
- - (BOOL)isEqual:(id)other {
- return [other isKindOfClass:[FSTTimestampValue class]] &&
- [self.internalValue isEqual:((FSTTimestampValue *)other).internalValue];
- }
- - (NSUInteger)hash {
- return [self.internalValue hash];
- }
- - (NSComparisonResult)compare:(FSTFieldValue *)other {
- if ([other isKindOfClass:[FSTTimestampValue class]]) {
- return [self.internalValue compare:((FSTTimestampValue *)other).internalValue];
- } else if ([other isKindOfClass:[FSTServerTimestampValue class]]) {
- // Concrete timestamps come before server timestamps.
- return NSOrderedAscending;
- } else {
- return [self defaultCompare:other];
- }
- }
- @end
- #pragma mark - FSTServerTimestampValue
- @implementation FSTServerTimestampValue
- + (instancetype)serverTimestampValueWithLocalWriteTime:(FIRTimestamp *)localWriteTime
- previousValue:(nullable FSTFieldValue *)previousValue {
- return [[FSTServerTimestampValue alloc] initWithLocalWriteTime:localWriteTime
- previousValue:previousValue];
- }
- - (id)initWithLocalWriteTime:(FIRTimestamp *)localWriteTime
- previousValue:(nullable FSTFieldValue *)previousValue {
- self = [super init];
- if (self) {
- _localWriteTime = localWriteTime;
- _previousValue = previousValue;
- }
- return self;
- }
- - (FSTTypeOrder)typeOrder {
- return FSTTypeOrderTimestamp;
- }
- - (id)value {
- return [NSNull null];
- }
- - (id)valueWithOptions:(FSTFieldValueOptions *)options {
- switch (options.serverTimestampBehavior) {
- case FSTServerTimestampBehaviorNone:
- return [NSNull null];
- case FSTServerTimestampBehaviorEstimate:
- return [[FSTTimestampValue timestampValue:self.localWriteTime] valueWithOptions:options];
- case FSTServerTimestampBehaviorPrevious:
- return self.previousValue ? [self.previousValue valueWithOptions:options] : [NSNull null];
- default:
- HARD_FAIL("Unexpected server timestamp option: %s", options.serverTimestampBehavior);
- }
- }
- - (BOOL)isEqual:(id)other {
- return [other isKindOfClass:[FSTServerTimestampValue class]] &&
- [self.localWriteTime isEqual:((FSTServerTimestampValue *)other).localWriteTime];
- }
- - (NSUInteger)hash {
- return [self.localWriteTime hash];
- }
- - (NSString *)description {
- return [NSString stringWithFormat:@"<ServerTimestamp localTime=%@>", self.localWriteTime];
- }
- - (NSComparisonResult)compare:(FSTFieldValue *)other {
- if ([other isKindOfClass:[FSTServerTimestampValue class]]) {
- return [self.localWriteTime compare:((FSTServerTimestampValue *)other).localWriteTime];
- } else if ([other isKindOfClass:[FSTTimestampValue class]]) {
- // Server timestamps come after all concrete timestamps.
- return NSOrderedDescending;
- } else {
- return [self defaultCompare:other];
- }
- }
- @end
- #pragma mark - FSTGeoPointValue
- @interface FSTGeoPointValue ()
- @property(nonatomic, strong, readonly) FIRGeoPoint *internalValue;
- @end
- @implementation FSTGeoPointValue
- + (instancetype)geoPointValue:(FIRGeoPoint *)value {
- return [[FSTGeoPointValue alloc] initWithValue:value];
- }
- - (id)initWithValue:(FIRGeoPoint *)value {
- self = [super init];
- if (self) {
- _internalValue = value; // FIRGeoPoint is immutable.
- }
- return self;
- }
- - (FSTTypeOrder)typeOrder {
- return FSTTypeOrderGeoPoint;
- }
- - (id)value {
- return self.internalValue;
- }
- - (BOOL)isEqual:(id)other {
- return [other isKindOfClass:[FSTGeoPointValue class]] &&
- [self.internalValue isEqual:((FSTGeoPointValue *)other).internalValue];
- }
- - (NSUInteger)hash {
- return [self.internalValue hash];
- }
- - (NSComparisonResult)compare:(FSTFieldValue *)other {
- if ([other isKindOfClass:[FSTGeoPointValue class]]) {
- return [self.internalValue compare:((FSTGeoPointValue *)other).internalValue];
- } else {
- return [self defaultCompare:other];
- }
- }
- @end
- #pragma mark - FSTBlobValue
- static NSComparisonResult CompareBytes(NSData *left, NSData *right) {
- NSUInteger minLength = MIN(left.length, right.length);
- int result = memcmp(left.bytes, right.bytes, minLength);
- if (result < 0) {
- return NSOrderedAscending;
- } else if (result > 0) {
- return NSOrderedDescending;
- } else if (left.length < right.length) {
- return NSOrderedAscending;
- } else if (left.length > right.length) {
- return NSOrderedDescending;
- } else {
- return NSOrderedSame;
- }
- }
- @interface FSTBlobValue ()
- @property(nonatomic, copy, readonly) NSData *internalValue;
- @end
- // TODO(b/37267885): Add truncation support
- @implementation FSTBlobValue
- + (instancetype)blobValue:(NSData *)value {
- return [[FSTBlobValue alloc] initWithValue:value];
- }
- - (id)initWithValue:(NSData *)value {
- self = [super init];
- if (self) {
- _internalValue = [value copy];
- }
- return self;
- }
- - (FSTTypeOrder)typeOrder {
- return FSTTypeOrderBlob;
- }
- - (id)value {
- return self.internalValue;
- }
- - (BOOL)isEqual:(id)other {
- return [other isKindOfClass:[FSTBlobValue class]] &&
- [self.internalValue isEqual:((FSTBlobValue *)other).internalValue];
- }
- - (NSUInteger)hash {
- return [self.internalValue hash];
- }
- - (NSComparisonResult)compare:(FSTFieldValue *)other {
- if ([other isKindOfClass:[FSTBlobValue class]]) {
- return CompareBytes(self.internalValue, ((FSTBlobValue *)other).internalValue);
- } else {
- return [self defaultCompare:other];
- }
- }
- @end
- #pragma mark - FSTReferenceValue
- @interface FSTReferenceValue ()
- @property(nonatomic, strong, readonly) FSTDocumentKey *key;
- @end
- @implementation FSTReferenceValue
- + (instancetype)referenceValue:(FSTDocumentKey *)value databaseID:(const DatabaseId *)databaseID {
- return [[FSTReferenceValue alloc] initWithValue:value databaseID:databaseID];
- }
- - (id)initWithValue:(FSTDocumentKey *)value databaseID:(const DatabaseId *)databaseID {
- self = [super init];
- if (self) {
- _key = value;
- _databaseID = databaseID;
- }
- return self;
- }
- - (id)value {
- return self.key;
- }
- - (FSTTypeOrder)typeOrder {
- return FSTTypeOrderReference;
- }
- - (BOOL)isEqual:(id)other {
- if (other == self) {
- return YES;
- }
- if (![other isKindOfClass:[FSTReferenceValue class]]) {
- return NO;
- }
- FSTReferenceValue *otherRef = (FSTReferenceValue *)other;
- return self.key.key == otherRef.key.key && *self.databaseID == *otherRef.databaseID;
- }
- - (NSUInteger)hash {
- NSUInteger result = self.databaseID->Hash();
- result = 31 * result + [self.key hash];
- return result;
- }
- - (NSComparisonResult)compare:(FSTFieldValue *)other {
- if ([other isKindOfClass:[FSTReferenceValue class]]) {
- FSTReferenceValue *ref = (FSTReferenceValue *)other;
- NSComparisonResult cmp =
- WrapCompare(self.databaseID->project_id(), ref.databaseID->project_id());
- if (cmp != NSOrderedSame) {
- return cmp;
- }
- cmp = WrapCompare(self.databaseID->database_id(), ref.databaseID->database_id());
- return cmp != NSOrderedSame ? cmp : CompareKeys(self.key.key, ref.key.key);
- } else {
- return [self defaultCompare:other];
- }
- }
- @end
- #pragma mark - FSTObjectValue
- static const NSComparator StringComparator = ^NSComparisonResult(NSString *left, NSString *right) {
- return WrapCompare(left, right);
- };
- @interface FSTObjectValue ()
- @property(nonatomic, strong, readonly)
- FSTImmutableSortedDictionary<NSString *, FSTFieldValue *> *internalValue;
- @end
- @implementation FSTObjectValue
- + (instancetype)objectValue {
- static FSTObjectValue *sharedEmptyInstance = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- FSTImmutableSortedDictionary<NSString *, FSTFieldValue *> *empty =
- [FSTImmutableSortedDictionary dictionaryWithComparator:StringComparator];
- sharedEmptyInstance = [[FSTObjectValue alloc] initWithImmutableDictionary:empty];
- });
- return sharedEmptyInstance;
- }
- - (instancetype)initWithImmutableDictionary:
- (FSTImmutableSortedDictionary<NSString *, FSTFieldValue *> *)value {
- self = [super init];
- if (self) {
- _internalValue = value; // FSTImmutableSortedDictionary is immutable.
- }
- return self;
- }
- - (id)initWithDictionary:(NSDictionary<NSString *, FSTFieldValue *> *)value {
- FSTImmutableSortedDictionary<NSString *, FSTFieldValue *> *dictionary =
- [FSTImmutableSortedDictionary dictionaryWithDictionary:value comparator:StringComparator];
- return [self initWithImmutableDictionary:dictionary];
- }
- - (id)value {
- NSMutableDictionary *result = [NSMutableDictionary dictionary];
- [self.internalValue
- enumerateKeysAndObjectsUsingBlock:^(NSString *key, FSTFieldValue *obj, BOOL *stop) {
- result[key] = [obj value];
- }];
- return result;
- }
- - (id)valueWithOptions:(FSTFieldValueOptions *)options {
- NSMutableDictionary *result = [NSMutableDictionary dictionary];
- [self.internalValue
- enumerateKeysAndObjectsUsingBlock:^(NSString *key, FSTFieldValue *obj, BOOL *stop) {
- result[key] = [obj valueWithOptions:options];
- }];
- return result;
- }
- - (FSTTypeOrder)typeOrder {
- return FSTTypeOrderObject;
- }
- - (BOOL)isEqual:(id)other {
- if (other == self) {
- return YES;
- }
- if (![other isKindOfClass:[FSTObjectValue class]]) {
- return NO;
- }
- FSTObjectValue *otherObj = other;
- return [self.internalValue isEqual:otherObj.internalValue];
- }
- - (NSUInteger)hash {
- return [self.internalValue hash];
- }
- - (NSComparisonResult)compare:(FSTFieldValue *)other {
- if ([other isKindOfClass:[FSTObjectValue class]]) {
- FSTImmutableSortedDictionary *selfDict = self.internalValue;
- FSTImmutableSortedDictionary *otherDict = ((FSTObjectValue *)other).internalValue;
- NSEnumerator *enumerator1 = [selfDict keyEnumerator];
- NSEnumerator *enumerator2 = [otherDict keyEnumerator];
- NSString *key1 = [enumerator1 nextObject];
- NSString *key2 = [enumerator2 nextObject];
- while (key1 && key2) {
- NSComparisonResult keyCompare = [key1 compare:key2];
- if (keyCompare != NSOrderedSame) {
- return keyCompare;
- }
- NSComparisonResult valueCompare = [selfDict[key1] compare:otherDict[key2]];
- if (valueCompare != NSOrderedSame) {
- return valueCompare;
- }
- key1 = [enumerator1 nextObject];
- key2 = [enumerator2 nextObject];
- }
- // Only equal if both enumerators are exhausted.
- return WrapCompare(key1 != nil, key2 != nil);
- } else {
- return [self defaultCompare:other];
- }
- }
- - (nullable FSTFieldValue *)valueForPath:(const FieldPath &)fieldPath {
- FSTFieldValue *value = self;
- for (size_t i = 0, max = fieldPath.size(); value && i < max; i++) {
- if (![value isMemberOfClass:[FSTObjectValue class]]) {
- return nil;
- }
- NSString *fieldName = util::WrapNSStringNoCopy(fieldPath[i]);
- value = ((FSTObjectValue *)value).internalValue[fieldName];
- }
- return value;
- }
- - (FSTObjectValue *)objectBySettingValue:(FSTFieldValue *)value
- forPath:(const FieldPath &)fieldPath {
- HARD_ASSERT(fieldPath.size() > 0, "Cannot set value with an empty path");
- NSString *childName = util::WrapNSString(fieldPath.first_segment());
- if (fieldPath.size() == 1) {
- // Recursive base case:
- return [self objectBySettingValue:value forField:childName];
- } else {
- // Nested path. Recursively generate a new sub-object and then wrap a new FSTObjectValue
- // around the result.
- FSTFieldValue *child = [_internalValue objectForKey:childName];
- FSTObjectValue *childObject;
- if ([child isKindOfClass:[FSTObjectValue class]]) {
- childObject = (FSTObjectValue *)child;
- } else {
- // If the child is not found or is a primitive type, pretend as if an empty object lived
- // there.
- childObject = [FSTObjectValue objectValue];
- }
- FSTFieldValue *newChild = [childObject objectBySettingValue:value forPath:fieldPath.PopFirst()];
- return [self objectBySettingValue:newChild forField:childName];
- }
- }
- - (FSTObjectValue *)objectByDeletingPath:(const FieldPath &)fieldPath {
- HARD_ASSERT(fieldPath.size() > 0, "Cannot delete an empty path");
- NSString *childName = util::WrapNSString(fieldPath.first_segment());
- if (fieldPath.size() == 1) {
- return [[FSTObjectValue alloc]
- initWithImmutableDictionary:[_internalValue dictionaryByRemovingObjectForKey:childName]];
- } else {
- FSTFieldValue *child = _internalValue[childName];
- if ([child isKindOfClass:[FSTObjectValue class]]) {
- FSTObjectValue *newChild =
- [((FSTObjectValue *)child) objectByDeletingPath:fieldPath.PopFirst()];
- return [self objectBySettingValue:newChild forField:childName];
- } else {
- // If the child is not found or is a primitive type, make no modifications
- return self;
- }
- }
- }
- - (FSTObjectValue *)objectBySettingValue:(FSTFieldValue *)value forField:(NSString *)field {
- return [[FSTObjectValue alloc]
- initWithImmutableDictionary:[_internalValue dictionaryBySettingObject:value forKey:field]];
- }
- @end
- @interface FSTArrayValue ()
- @property(nonatomic, strong, readonly) NSArray<FSTFieldValue *> *internalValue;
- @end
- #pragma mark - FSTArrayValue
- @implementation FSTArrayValue
- - (id)initWithValueNoCopy:(NSArray<FSTFieldValue *> *)value {
- self = [super init];
- if (self) {
- // Does not copy, assumes the caller has already copied.
- _internalValue = value;
- }
- return self;
- }
- - (BOOL)isEqual:(id)other {
- if (other == self) {
- return YES;
- }
- if (![other isKindOfClass:[self class]]) {
- return NO;
- }
- // NSArray's isEqual does the right thing for our purposes.
- FSTArrayValue *otherArray = other;
- return [self.internalValue isEqual:otherArray.internalValue];
- }
- - (NSUInteger)hash {
- return [self.internalValue hash];
- }
- - (id)value {
- NSMutableArray *result = [NSMutableArray arrayWithCapacity:_internalValue.count];
- [self.internalValue enumerateObjectsUsingBlock:^(FSTFieldValue *obj, NSUInteger idx, BOOL *stop) {
- [result addObject:[obj value]];
- }];
- return result;
- }
- - (id)valueWithOptions:(FSTFieldValueOptions *)options {
- NSMutableArray *result = [NSMutableArray arrayWithCapacity:_internalValue.count];
- [self.internalValue enumerateObjectsUsingBlock:^(FSTFieldValue *obj, NSUInteger idx, BOOL *stop) {
- [result addObject:[obj valueWithOptions:options]];
- }];
- return result;
- }
- - (FSTTypeOrder)typeOrder {
- return FSTTypeOrderArray;
- }
- - (NSComparisonResult)compare:(FSTFieldValue *)other {
- if ([other isKindOfClass:[FSTArrayValue class]]) {
- NSArray<FSTFieldValue *> *selfArray = self.internalValue;
- NSArray<FSTFieldValue *> *otherArray = ((FSTArrayValue *)other).internalValue;
- NSUInteger minLength = MIN(selfArray.count, otherArray.count);
- for (NSUInteger i = 0; i < minLength; i++) {
- NSComparisonResult cmp = [selfArray[i] compare:otherArray[i]];
- if (cmp != NSOrderedSame) {
- return cmp;
- }
- }
- return WrapCompare<int64_t>(selfArray.count, otherArray.count);
- } else {
- return [self defaultCompare:other];
- }
- }
- @end
- NS_ASSUME_NONNULL_END
|