123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525 |
- ////////////////////////////////////////////////////////////////////////////
- //
- // Copyright 2014 Realm Inc.
- //
- // 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 "RLMResults_Private.hpp"
- #import "RLMAccessor.hpp"
- #import "RLMArray_Private.hpp"
- #import "RLMCollection_Private.hpp"
- #import "RLMObjectSchema_Private.hpp"
- #import "RLMObjectStore.h"
- #import "RLMObject_Private.hpp"
- #import "RLMObservation.hpp"
- #import "RLMProperty_Private.h"
- #import "RLMQueryUtil.hpp"
- #import "RLMRealm_Private.hpp"
- #import "RLMSchema_Private.h"
- #import "RLMThreadSafeReference_Private.hpp"
- #import "RLMUtil.hpp"
- #import "results.hpp"
- #import "shared_realm.hpp"
- #import <objc/message.h>
- #import <realm/table_view.hpp>
- using namespace realm;
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wincomplete-implementation"
- @implementation RLMNotificationToken
- @end
- #pragma clang diagnostic pop
- @interface RLMResults () <RLMThreadConfined_Private>
- @end
- //
- // RLMResults implementation
- //
- @implementation RLMResults {
- RLMRealm *_realm;
- RLMClassInfo *_info;
- }
- - (instancetype)initPrivate {
- self = [super init];
- return self;
- }
- - (instancetype)initWithResults:(Results)results {
- if (self = [super init]) {
- _results = std::move(results);
- }
- return self;
- }
- static void assertKeyPathIsNotNested(NSString *keyPath) {
- if ([keyPath rangeOfString:@"."].location != NSNotFound) {
- @throw RLMException(@"Nested key paths are not supported yet for KVC collection operators.");
- }
- }
- void RLMThrowResultsError(NSString *aggregateMethod) {
- try {
- throw;
- }
- catch (realm::InvalidTransactionException const&) {
- @throw RLMException(@"Cannot modify Results outside of a write transaction.");
- }
- catch (realm::IncorrectThreadException const&) {
- @throw RLMException(@"Realm accessed from incorrect thread.");
- }
- catch (realm::Results::InvalidatedException const&) {
- @throw RLMException(@"RLMResults has been invalidated.");
- }
- catch (realm::Results::DetatchedAccessorException const&) {
- @throw RLMException(@"Object has been invalidated.");
- }
- catch (realm::Results::IncorrectTableException const& e) {
- @throw RLMException(@"Object of type '%s' does not match RLMResults type '%s'.",
- e.actual.data(), e.expected.data());
- }
- catch (realm::Results::OutOfBoundsIndexException const& e) {
- @throw RLMException(@"Index %zu is out of bounds (must be less than %zu).",
- e.requested, e.valid_count);
- }
- catch (realm::Results::UnsupportedColumnTypeException const& e) {
- @throw RLMException(@"%@ is not supported for %s%s property '%s'.",
- aggregateMethod,
- string_for_property_type(e.property_type),
- is_nullable(e.property_type) ? "?" : "",
- e.column_name.data());
- }
- catch (std::exception const& e) {
- @throw RLMException(e);
- }
- }
- - (instancetype)initWithObjectInfo:(RLMClassInfo&)info
- results:(realm::Results&&)results {
- if (self = [super init]) {
- _results = std::move(results);
- _realm = info.realm;
- _info = &info;
- }
- return self;
- }
- + (instancetype)resultsWithObjectInfo:(RLMClassInfo&)info
- results:(realm::Results&&)results {
- return [[self alloc] initWithObjectInfo:info results:std::move(results)];
- }
- + (instancetype)emptyDetachedResults {
- return [[self alloc] initPrivate];
- }
- - (instancetype)subresultsWithResults:(realm::Results)results {
- return [self.class resultsWithObjectInfo:*_info results:std::move(results)];
- }
- static inline void RLMResultsValidateInWriteTransaction(__unsafe_unretained RLMResults *const ar) {
- ar->_realm->_realm->verify_thread();
- ar->_realm->_realm->verify_in_write();
- }
- - (BOOL)isInvalidated {
- return translateRLMResultsErrors([&] { return !_results.is_valid(); });
- }
- - (NSUInteger)count {
- return translateRLMResultsErrors([&] { return _results.size(); });
- }
- - (RLMPropertyType)type {
- return translateRLMResultsErrors([&] {
- return static_cast<RLMPropertyType>(_results.get_type() & ~realm::PropertyType::Nullable);
- });
- }
- - (BOOL)isOptional {
- return translateRLMResultsErrors([&] {
- return is_nullable(_results.get_type());
- });
- }
- - (NSString *)objectClassName {
- return translateRLMResultsErrors([&] {
- if (_info && _results.get_type() == realm::PropertyType::Object) {
- return _info->rlmObjectSchema.className;
- }
- return (NSString *)nil;
- });
- }
- - (RLMClassInfo *)objectInfo {
- return _info;
- }
- - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
- objects:(__unused __unsafe_unretained id [])buffer
- count:(NSUInteger)len {
- if (!_info) {
- return 0;
- }
- return RLMFastEnumerate(state, len, self);
- }
- - (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat, ... {
- va_list args;
- va_start(args, predicateFormat);
- NSUInteger index = [self indexOfObjectWhere:predicateFormat args:args];
- va_end(args);
- return index;
- }
- - (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args {
- return [self indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:predicateFormat
- arguments:args]];
- }
- - (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate {
- if (_results.get_mode() == Results::Mode::Empty) {
- return NSNotFound;
- }
- return translateRLMResultsErrors([&] {
- if (_results.get_type() != realm::PropertyType::Object) {
- @throw RLMException(@"Querying is currently only implemented for arrays of Realm Objects");
- }
- return RLMConvertNotFound(_results.index_of(RLMPredicateToQuery(predicate, _info->rlmObjectSchema, _realm.schema, _realm.group)));
- });
- }
- - (id)objectAtIndex:(NSUInteger)index {
- RLMAccessorContext ctx(_realm, *_info);
- return translateRLMResultsErrors([&] {
- return _results.get(ctx, index);
- });
- }
- - (id)firstObject {
- if (!_info) {
- return nil;
- }
- RLMAccessorContext ctx(_realm, *_info);
- return translateRLMResultsErrors([&] {
- return _results.first(ctx);
- });
- }
- - (id)lastObject {
- if (!_info) {
- return nil;
- }
- RLMAccessorContext ctx(_realm, *_info);
- return translateRLMResultsErrors([&] {
- return _results.last(ctx);
- });
- }
- - (NSUInteger)indexOfObject:(RLMObject *)object {
- if (!_info || !object || (!object->_realm && !object.invalidated)) {
- return NSNotFound;
- }
- RLMAccessorContext ctx(_realm, *_info);
- return translateRLMResultsErrors([&] {
- return RLMConvertNotFound(_results.index_of(ctx, object));
- });
- }
- - (id)valueForKeyPath:(NSString *)keyPath {
- if ([keyPath characterAtIndex:0] != '@') {
- return [super valueForKeyPath:keyPath];
- }
- if ([keyPath isEqualToString:@"@count"]) {
- return @(self.count);
- }
- NSRange operatorRange = [keyPath rangeOfString:@"." options:NSLiteralSearch];
- NSUInteger keyPathLength = keyPath.length;
- NSUInteger separatorIndex = operatorRange.location != NSNotFound ? operatorRange.location : keyPathLength;
- NSString *operatorName = [keyPath substringWithRange:NSMakeRange(1, separatorIndex - 1)];
- SEL opSelector = NSSelectorFromString([NSString stringWithFormat:@"_%@ForKeyPath:", operatorName]);
- if (![self respondsToSelector:opSelector]) {
- @throw RLMException(@"Unsupported KVC collection operator found in key path '%@'", keyPath);
- }
- if (separatorIndex >= keyPathLength - 1) {
- @throw RLMException(@"Missing key path for KVC collection operator %@ in key path '%@'",
- operatorName, keyPath);
- }
- NSString *operatorKeyPath = [keyPath substringFromIndex:separatorIndex + 1];
- return ((id(*)(id, SEL, id))objc_msgSend)(self, opSelector, operatorKeyPath);
- }
- - (id)valueForKey:(NSString *)key {
- if (!_info) {
- return @[];
- }
- return translateRLMResultsErrors([&] {
- return RLMCollectionValueForKey(_results, key, _realm, *_info);
- });
- }
- - (void)setValue:(id)value forKey:(NSString *)key {
- translateRLMResultsErrors([&] { RLMResultsValidateInWriteTransaction(self); });
- RLMCollectionSetValueForKey(self, key, value);
- }
- - (NSNumber *)_aggregateForKeyPath:(NSString *)keyPath
- method:(util::Optional<Mixed> (Results::*)(size_t))method
- methodName:(NSString *)methodName returnNilForEmpty:(BOOL)returnNilForEmpty {
- assertKeyPathIsNotNested(keyPath);
- return [self aggregate:keyPath method:method methodName:methodName returnNilForEmpty:returnNilForEmpty];
- }
- - (NSNumber *)_minForKeyPath:(NSString *)keyPath {
- return [self _aggregateForKeyPath:keyPath method:&Results::min methodName:@"@min" returnNilForEmpty:YES];
- }
- - (NSNumber *)_maxForKeyPath:(NSString *)keyPath {
- return [self _aggregateForKeyPath:keyPath method:&Results::max methodName:@"@max" returnNilForEmpty:YES];
- }
- - (NSNumber *)_sumForKeyPath:(NSString *)keyPath {
- return [self _aggregateForKeyPath:keyPath method:&Results::sum methodName:@"@sum" returnNilForEmpty:NO];
- }
- - (NSNumber *)_avgForKeyPath:(NSString *)keyPath {
- assertKeyPathIsNotNested(keyPath);
- return [self averageOfProperty:keyPath];
- }
- - (NSArray *)_unionOfObjectsForKeyPath:(NSString *)keyPath {
- assertKeyPathIsNotNested(keyPath);
- return translateRLMResultsErrors([&] {
- return RLMCollectionValueForKey(_results, keyPath, _realm, *_info);
- });
- }
- - (NSArray *)_distinctUnionOfObjectsForKeyPath:(NSString *)keyPath {
- return [NSSet setWithArray:[self _unionOfObjectsForKeyPath:keyPath]].allObjects;
- }
- - (NSArray *)_unionOfArraysForKeyPath:(NSString *)keyPath {
- assertKeyPathIsNotNested(keyPath);
- if ([keyPath isEqualToString:@"self"]) {
- @throw RLMException(@"self is not a valid key-path for a KVC array collection operator as 'unionOfArrays'.");
- }
- return translateRLMResultsErrors([&] {
- NSMutableArray *flatArray = [NSMutableArray new];
- for (id<NSFastEnumeration> array in RLMCollectionValueForKey(_results, keyPath, _realm, *_info)) {
- for (id value in array) {
- [flatArray addObject:value];
- }
- }
- return flatArray;
- });
- }
- - (NSArray *)_distinctUnionOfArraysForKeyPath:(__unused NSString *)keyPath {
- return [NSSet setWithArray:[self _unionOfArraysForKeyPath:keyPath]].allObjects;
- }
- - (RLMResults *)objectsWhere:(NSString *)predicateFormat, ... {
- va_list args;
- va_start(args, predicateFormat);
- RLMResults *results = [self objectsWhere:predicateFormat args:args];
- va_end(args);
- return results;
- }
- - (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args {
- return [self objectsWithPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]];
- }
- - (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate {
- return translateRLMResultsErrors([&] {
- if (_results.get_mode() == Results::Mode::Empty) {
- return self;
- }
- if (_results.get_type() != realm::PropertyType::Object) {
- @throw RLMException(@"Querying is currently only implemented for arrays of Realm Objects");
- }
- auto query = RLMPredicateToQuery(predicate, _info->rlmObjectSchema, _realm.schema, _realm.group);
- return [self subresultsWithResults:_results.filter(std::move(query))];
- });
- }
- - (RLMResults *)sortedResultsUsingKeyPath:(NSString *)keyPath ascending:(BOOL)ascending {
- return [self sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:keyPath ascending:ascending]]];
- }
- - (RLMResults *)sortedResultsUsingDescriptors:(NSArray<RLMSortDescriptor *> *)properties {
- if (properties.count == 0) {
- return self;
- }
- return translateRLMResultsErrors([&] {
- if (_results.get_mode() == Results::Mode::Empty) {
- return self;
- }
- return [self subresultsWithResults:_results.sort(RLMSortDescriptorsToKeypathArray(properties))];
- });
- }
- - (RLMResults *)distinctResultsUsingKeyPaths:(NSArray<NSString *> *)keyPaths {
- for (NSString *keyPath in keyPaths) {
- if ([keyPath rangeOfString:@"@"].location != NSNotFound) {
- @throw RLMException(@"Cannot distinct on keypath '%@': KVC collection operators are not supported.", keyPath);
- }
- }
-
- return translateRLMResultsErrors([&] {
- if (_results.get_mode() == Results::Mode::Empty) {
- return self;
- }
-
- std::vector<std::string> keyPathsVector;
- for (NSString *keyPath in keyPaths) {
- keyPathsVector.push_back(keyPath.UTF8String);
- }
-
- return [self subresultsWithResults:_results.distinct(keyPathsVector)];
- });
- }
- - (id)objectAtIndexedSubscript:(NSUInteger)index {
- return [self objectAtIndex:index];
- }
- - (id)aggregate:(NSString *)property method:(util::Optional<Mixed> (Results::*)(size_t))method
- methodName:(NSString *)methodName returnNilForEmpty:(BOOL)returnNilForEmpty {
- if (_results.get_mode() == Results::Mode::Empty) {
- return returnNilForEmpty ? nil : @0;
- }
- size_t column = 0;
- if (self.type == RLMPropertyTypeObject || ![property isEqualToString:@"self"]) {
- column = _info->tableColumn(property);
- }
- auto value = translateRLMResultsErrors([&] { return (_results.*method)(column); }, methodName);
- return value ? RLMMixedToObjc(*value) : nil;
- }
- - (id)minOfProperty:(NSString *)property {
- return [self aggregate:property method:&Results::min
- methodName:@"minOfProperty" returnNilForEmpty:YES];
- }
- - (id)maxOfProperty:(NSString *)property {
- return [self aggregate:property method:&Results::max
- methodName:@"maxOfProperty" returnNilForEmpty:YES];
- }
- - (id)sumOfProperty:(NSString *)property {
- return [self aggregate:property method:&Results::sum
- methodName:@"sumOfProperty" returnNilForEmpty:NO];
- }
- - (id)averageOfProperty:(NSString *)property {
- if (_results.get_mode() == Results::Mode::Empty) {
- return nil;
- }
- size_t column = 0;
- if (self.type == RLMPropertyTypeObject || ![property isEqualToString:@"self"]) {
- column = _info->tableColumn(property);
- }
- auto value = translateRLMResultsErrors([&] { return _results.average(column); }, @"averageOfProperty");
- return value ? @(*value) : nil;
- }
- - (void)deleteObjectsFromRealm {
- if (self.type != RLMPropertyTypeObject) {
- @throw RLMException(@"Cannot delete objects from RLMResults<%@>: only RLMObjects can be deleted.",
- RLMTypeToString(self.type));
- }
- return translateRLMResultsErrors([&] {
- if (_results.get_mode() == Results::Mode::Table) {
- RLMResultsValidateInWriteTransaction(self);
- RLMClearTable(*_info);
- }
- else {
- RLMTrackDeletions(_realm, [&] { _results.clear(); });
- }
- });
- }
- - (NSString *)description {
- return RLMDescriptionWithMaxDepth(@"RLMResults", self, RLMDescriptionMaxDepth);
- }
- - (realm::TableView)tableView {
- return translateRLMResultsErrors([&] { return _results.get_tableview(); });
- }
- - (RLMFastEnumerator *)fastEnumerator {
- return translateRLMResultsErrors([&] {
- return [[RLMFastEnumerator alloc] initWithResults:_results collection:self
- realm:_realm classInfo:*_info];
- });
- }
- // The compiler complains about the method's argument type not matching due to
- // it not having the generic type attached, but it doesn't seem to be possible
- // to actually include the generic type
- // http://www.openradar.me/radar?id=6135653276319744
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wmismatched-parameter-types"
- - (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMResults *, RLMCollectionChange *, NSError *))block {
- [_realm verifyNotificationsAreSupported:true];
- return RLMAddNotificationBlock(self, _results, block, true);
- }
- #pragma clang diagnostic pop
- - (BOOL)isAttached
- {
- return !!_realm;
- }
- #pragma mark - Thread Confined Protocol Conformance
- - (std::unique_ptr<realm::ThreadSafeReferenceBase>)makeThreadSafeReference {
- return std::make_unique<realm::ThreadSafeReference<Results>>(_realm->_realm->obtain_thread_safe_reference(_results));
- }
- - (id)objectiveCMetadata {
- return nil;
- }
- + (instancetype)objectWithThreadSafeReference:(std::unique_ptr<realm::ThreadSafeReferenceBase>)reference
- metadata:(__unused id)metadata
- realm:(RLMRealm *)realm {
- REALM_ASSERT_DEBUG(dynamic_cast<realm::ThreadSafeReference<Results> *>(reference.get()));
- auto results_reference = static_cast<realm::ThreadSafeReference<Results> *>(reference.get());
- Results results = realm->_realm->resolve_thread_safe_reference(std::move(*results_reference));
- return [RLMResults resultsWithObjectInfo:realm->_info[RLMStringDataToNSString(results.get_object_type())]
- results:std::move(results)];
- }
- @end
- @implementation RLMLinkingObjects
- - (NSString *)description {
- return RLMDescriptionWithMaxDepth(@"RLMLinkingObjects", self, RLMDescriptionMaxDepth);
- }
- @end
|