ComponentKit/TransactionalDataSources/Common/CKDataSourceChangeset.mm (199 lines of code) (raw):

/* * Copyright (c) 2014-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * */ #import "CKDataSourceChangeset.h" #import "CKDataSourceChangesetInternal.h" #import <UIKit/UICollectionView.h> #import <UIKit/UITableView.h> #import <ComponentKit/RCEqualityHelpers.h> #import <ComponentKit/CKMacros.h> #import <RenderCore/RCAssert.h> #import "CKIndexSetDescription.h" @implementation CKDataSourceChangeset - (instancetype)initWithUpdatedItems:(NSDictionary *)updatedItems removedItems:(NSSet *)removedItems removedSections:(NSIndexSet *)removedSections movedItems:(NSDictionary *)movedItems insertedSections:(NSIndexSet *)insertedSections insertedItems:(NSDictionary *)insertedItems { return [self initWithOriginName:@"not_set" updatedItems:updatedItems removedItems:removedItems removedSections:removedSections movedItems:movedItems insertedSections:insertedSections insertedItems:insertedItems]; } - (instancetype)initWithOriginName:(NSString *)originName updatedItems:(NSDictionary *)updatedItems removedItems:(NSSet *)removedItems removedSections:(NSIndexSet *)removedSections movedItems:(NSDictionary *)movedItems insertedSections:(NSIndexSet *)insertedSections insertedItems:(NSDictionary *)insertedItems { if (self = [super init]) { _originName = [originName copy] ?: @"nil"; _updatedItems = [updatedItems copy] ?: @{}; _removedItems = [removedItems copy] ?: [NSSet set]; _removedSections = [removedSections copy] ?: [NSIndexSet indexSet]; _movedItems = [movedItems copy] ?: @{}; _insertedSections = [insertedSections copy] ?: [NSIndexSet indexSet]; _insertedItems = [insertedItems copy] ?: @{}; } return self; } - (NSString *)description { return CK::changesetDescription(self); } - (BOOL)isEmpty { return (_insertedSections.count == 0 && _removedSections.count == 0 && _updatedItems.count == 0 && _movedItems.count == 0 && _insertedItems.count == 0 && _removedItems.count == 0); } - (BOOL)isEqual:(id)object { if (![object isKindOfClass:[CKDataSourceChangeset class]]) { return NO; } else { CKDataSourceChangeset *obj = (CKDataSourceChangeset *)object; return [_updatedItems isEqualToDictionary:obj.updatedItems] && [_removedItems isEqualToSet:obj.removedItems] && [_removedSections isEqualToIndexSet:obj.removedSections] && [_movedItems isEqualToDictionary:obj.movedItems] && [_insertedSections isEqualToIndexSet:obj.insertedSections] && [_insertedItems isEqualToDictionary:obj.insertedItems]; } } - (NSUInteger)hash { NSUInteger hashes[6] = { [_updatedItems hash], [_removedItems hash], [_removedSections hash], [_movedItems hash], [_insertedSections hash], [_insertedItems hash] }; return RCIntegerArrayHash(hashes, CK_ARRAY_COUNT(hashes)); } @end @implementation CKDataSourceChangesetBuilder { NSDictionary *_updatedItems; NSSet *_removedItems; NSIndexSet *_removedSections; NSDictionary *_movedItems; NSIndexSet *_insertedSections; NSDictionary *_insertedItems; NSString *_originName; } - (instancetype)initWithOriginName:(NSString *)originName { self = [super init]; if (self) { _originName = originName; } return self; } + (instancetype)dataSourceChangeset { return [CKDataSourceChangesetBuilder dataSourceChangesetWithOriginName:@"changeset_builder_default"]; } + (instancetype)dataSourceChangesetWithOriginName:(NSString *)originName { CKDataSourceChangesetBuilder * builder = [[self alloc] initWithOriginName:originName]; return builder; } - (instancetype)withUpdatedItems:(NSDictionary *)updatedItems { _updatedItems = updatedItems; return self;} - (instancetype)withRemovedItems:(NSSet *)removedItems { _removedItems = removedItems; return self; } - (instancetype)withRemovedSections:(NSIndexSet *)removedSections { _removedSections = removedSections; return self; } - (instancetype)withMovedItems:(NSDictionary *)movedItems { _movedItems = movedItems; return self; } - (instancetype)withInsertedSections:(NSIndexSet *)insertedSections { _insertedSections = insertedSections; return self; } - (instancetype)withInsertedItems:(NSDictionary *)insertedItems { _insertedItems = insertedItems; return self; } - (CKDataSourceChangeset *)build { return [[CKDataSourceChangeset alloc] initWithOriginName:_originName updatedItems:_updatedItems removedItems:_removedItems removedSections:_removedSections movedItems:_movedItems insertedSections:_insertedSections insertedItems:_insertedItems]; } @end namespace CK { static auto withNewLineIfNotEmpty(NSString const* s) -> NSString * { return s.length > 0 ? [s stringByAppendingString:@"\n"] : @""; } auto itemsByIndexPathDescription(NSDictionary<NSIndexPath *, NSObject *> * const items, NSString * const title) -> NSString * { if (items.count == 0) { return @""; } auto description = [NSMutableString new]; [description appendFormat:@" %@: {\n", title]; auto itemStrings = static_cast<NSMutableArray <NSString *> *>([NSMutableArray new]); const auto sortedIps = [[items allKeys] sortedArrayUsingSelector:@selector(compare:)]; for (NSIndexPath * const ip in sortedIps) { const auto itemStr = [NSString stringWithFormat:@" (%ld-%ld): %@", (long)ip.section, (long)ip.item, items[ip]]; [itemStrings addObject:itemStr]; } [description appendString:[itemStrings componentsJoinedByString:@",\n"]]; [description appendString:@"\n }\n"]; return description; } static auto movedItemsDescription(NSDictionary<NSIndexPath *, NSIndexPath *> * const ips) -> NSString * { if (ips.count == 0) { return @""; } auto description = [NSMutableString new]; [description appendString:@" Moved Items: {\n"]; auto ipStrings = static_cast<NSMutableArray <NSString *> *>([NSMutableArray new]); const auto sortedIps = [[ips allKeys] sortedArrayUsingSelector:@selector(compare:)]; for (NSIndexPath * const ip in sortedIps) { const auto ipStr = [NSString stringWithFormat:@" (%ld-%ld) → (%ld-%ld)", (long)ip.section, (long)ip.item, (long)ips[ip].section, (long)ips[ip].item]; [ipStrings addObject:ipStr]; } [description appendString:[ipStrings componentsJoinedByString:@",\n"]]; [description appendString:@"\n }\n"]; return description; } static auto removedItemsDescription(NSSet<NSIndexPath *> const* ips) -> NSString * { if (ips.count == 0) { return @""; } auto description = [NSMutableString new]; [description appendString:@" Removed Items: {\n"]; auto items = static_cast<NSMutableArray<NSString *> *>([NSMutableArray new]); const auto sortedIps = [[ips allObjects] sortedArrayUsingSelector:@selector(compare:)]; for (NSIndexPath * const ip : sortedIps) { const auto ipStr = [NSString stringWithFormat:@" (%ld-%ld)", (long)ip.section, (long)ip.item]; [items addObject:ipStr]; } [description appendString:[items componentsJoinedByString:@",\n"]]; [description appendString:@"\n }\n"]; return description; } auto changesetDescription(const CKDataSourceChangeset *const changeset) -> NSString * { if (changeset.isEmpty) { return @""; } auto description = [NSMutableString new]; [description appendString:@"{\n"]; [description appendString:itemsByIndexPathDescription(changeset.updatedItems, @"Updated Items")]; [description appendString:removedItemsDescription(changeset.removedItems)]; [description appendString:withNewLineIfNotEmpty(indexSetDescription(changeset.removedSections, @"Removed Sections", 2))]; [description appendString:movedItemsDescription(changeset.movedItems)]; [description appendString:withNewLineIfNotEmpty(indexSetDescription(changeset.insertedSections, @"Inserted Sections", 2))]; [description appendString:itemsByIndexPathDescription(changeset.insertedItems, @"Inserted Items")]; [description appendString:@"}"]; return description; } }