diff options
Diffstat (limited to 'AddressBook/GTMABAddressBook.m')
-rw-r--r-- | AddressBook/GTMABAddressBook.m | 1234 |
1 files changed, 0 insertions, 1234 deletions
diff --git a/AddressBook/GTMABAddressBook.m b/AddressBook/GTMABAddressBook.m deleted file mode 100644 index 9fce8ff..0000000 --- a/AddressBook/GTMABAddressBook.m +++ /dev/null @@ -1,1234 +0,0 @@ -// -// GTMABAddressBook.m -// -// Copyright 2008 Google 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 "GTMABAddressBook.h" -#import "GTMTypeCasting.h" - -#if GTM_IPHONE_SDK -#import <UIKit/UIKit.h> -#else // GTM_IPHONE_SDK -#import <Cocoa/Cocoa.h> -#endif // GTM_IPHONE_SDK - -NSString *const kGTMABUnknownPropertyName = @"UNKNOWN_PROPERTY"; - -typedef struct { - GTMABPropertyType pType; - Class class; -} TypeClassNameMap; - -@interface GTMABMultiValue () -- (unsigned long*)mutations; -@end - -@interface GTMABMutableMultiValue () -// Checks to see if a value is a valid type to be stored in this multivalue -- (BOOL)checkValueType:(id)value; -@end - -@interface GTMABMultiValueEnumerator : NSEnumerator { - @private - ABMultiValueRef ref_; // ref_ cached from enumeree_ - GTMABMultiValue *enumeree_; - unsigned long mutations_; - NSUInteger count_; - NSUInteger index_; - BOOL useLabels_; -} -+ (id)valueEnumeratorFor:(GTMABMultiValue*)enumeree; -+ (id)labelEnumeratorFor:(GTMABMultiValue*)enumeree; -- (id)initWithEnumeree:(GTMABMultiValue*)enumeree useLabels:(BOOL)useLabels; -- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state - objects:(id *)stackbuf - count:(NSUInteger)len; -@end - -@implementation GTMABAddressBook -+ (GTMABAddressBook *)addressBook { - return [[[self alloc] init] autorelease]; -} - -- (id)init { - if ((self = [super init])) { -#if GTM_IPHONE_SDK - CFErrorRef error = nil; - addressBook_ = ABAddressBookCreateWithOptions(NULL, &error); - if (error) { - _GTMDevLog(@"ABAddressBookCreate: %@", error); - CFRelease(error); - } -#else // GTM_IPHONE_SDK - addressBook_ = ABGetSharedAddressBook(); - CFRetain(addressBook_); -#endif // GTM_IPHONE_SDK - if (!addressBook_) { - // COV_NF_START - [self release]; - self = nil; - // COV_NF_END - } - } - return self; -} - -- (void)dealloc { - if (addressBook_) { - CFRelease(addressBook_); - } - [super dealloc]; -} - -- (BOOL)save { -#if GTM_IPHONE_SDK - CFErrorRef cfError = NULL; - bool wasGood = ABAddressBookSave(addressBook_, &cfError); - if (!wasGood) { - _GTMDevLog(@"Error in [%@ %@]: %@", - [self class], NSStringFromSelector(_cmd), cfError); - if (cfError) { - CFRelease(cfError); - } - } -#else // GTM_IPHONE_SDK - bool wasGood = ABSave(addressBook_); -#endif // GTM_IPHONE_SDK - return wasGood ? YES : NO; -} - -- (BOOL)hasUnsavedChanges { - bool hasUnsavedChanges; -#if GTM_IPHONE_SDK - hasUnsavedChanges = ABAddressBookHasUnsavedChanges(addressBook_); -#else // GTM_IPHONE_SDK - hasUnsavedChanges = ABHasUnsavedChanges(addressBook_); -#endif // GTM_IPHONE_SDK - return hasUnsavedChanges ? YES : NO; -} - -- (BOOL)addRecord:(GTMABRecord *)record { - // Note: we check for bad data here because of radar - // 6201258 Adding a NULL record using ABAddressBookAddRecord crashes - if (!record) return NO; -#if GTM_IPHONE_SDK - CFErrorRef cfError = NULL; - bool wasGood = ABAddressBookAddRecord(addressBook_, - [record recordRef], &cfError); - if (cfError) { - // COV_NF_START - _GTMDevLog(@"Error in [%@ %@]: %@", - [self class], NSStringFromSelector(_cmd), cfError); - CFRelease(cfError); - // COV_NF_END - } -#else // GTM_IPHONE_SDK - bool wasGood = ABAddRecord(addressBook_, [record recordRef]); -#endif // GTM_IPHONE_SDK - return wasGood ? YES : NO; -} - -- (BOOL)removeRecord:(GTMABRecord *)record { - // Note: we check for bad data here because of radar - // 6201276 Removing a NULL record using ABAddressBookRemoveRecord crashes - if (!record) return NO; -#if GTM_IPHONE_SDK - CFErrorRef cfError = NULL; - bool wasGood = ABAddressBookRemoveRecord(addressBook_, - [record recordRef], &cfError); - if (cfError) { - // COV_NF_START - _GTMDevLog(@"Error in [%@ %@]: %@", - [self class], NSStringFromSelector(_cmd), cfError); - CFRelease(cfError); - // COV_NF_END - } -#else // GTM_IPHONE_SDK - GTMABRecordID recID = [record recordID]; - ABRecordRef ref = ABCopyRecordForUniqueId(addressBook_, (CFStringRef)recID); - bool wasGood = NO; - if (ref) { - wasGood = ABRemoveRecord(addressBook_, [record recordRef]); - CFRelease(ref); - } -#endif // GTM_IPHONE_SDK - return wasGood ? YES : NO; -} - -- (NSArray *)people { -#if GTM_IPHONE_SDK - NSArray *people - = GTMCFAutorelease(ABAddressBookCopyArrayOfAllPeople(addressBook_)); -#else // GTM_IPHONE_SDK - NSArray *people - = GTMCFAutorelease(ABCopyArrayOfAllPeople(addressBook_)); -#endif // GTM_IPHONE_SDK - NSMutableArray *result = [NSMutableArray arrayWithCapacity:[people count]]; - id person; - for (person in people) { - [result addObject:[GTMABPerson recordWithRecord:person]]; - } - return result; -} - -- (NSArray *)groups { -#if GTM_IPHONE_SDK - NSArray *groups - = GTMCFAutorelease(ABAddressBookCopyArrayOfAllGroups(addressBook_)); -#else // GTM_IPHONE_SDK - NSArray *groups - = GTMCFAutorelease(ABCopyArrayOfAllGroups(addressBook_)); -#endif // GTM_IPHONE_SDK - NSMutableArray *result = [NSMutableArray arrayWithCapacity:[groups count]]; - id group; - for (group in groups) { - [result addObject:[GTMABGroup recordWithRecord:group]]; - } - return result; -} - -- (ABAddressBookRef)addressBookRef { - return addressBook_; -} - -- (GTMABPerson *)personForId:(GTMABRecordID)uniqueId { - GTMABPerson *person = nil; -#if GTM_IPHONE_SDK - ABRecordRef ref = ABAddressBookGetPersonWithRecordID(addressBook_, uniqueId); -#else // GTM_IPHONE_SDK - ABRecordRef ref = ABCopyRecordForUniqueId(addressBook_, - (CFStringRef)uniqueId); -#endif // GTM_IPHONE_SDK - if (ref) { - person = [GTMABPerson recordWithRecord:ref]; - } - return person; -} - -- (GTMABGroup *)groupForId:(GTMABRecordID)uniqueId { - GTMABGroup *group = nil; -#if GTM_IPHONE_SDK - ABRecordRef ref = ABAddressBookGetGroupWithRecordID(addressBook_, uniqueId); -#else // GTM_IPHONE_SDK - ABRecordRef ref = ABCopyRecordForUniqueId(addressBook_, - (CFStringRef)uniqueId); -#endif // GTM_IPHONE_SDK - if (ref) { - group = [GTMABGroup recordWithRecord:ref]; - } - return group; -} - -// Performs a prefix search on the composite names of people in an address book -// and returns an array of persons that match the search criteria. -- (NSArray *)peopleWithCompositeNameWithPrefix:(NSString *)prefix { -#if GTM_IPHONE_SDK - NSArray *people = - GTMCFAutorelease(ABAddressBookCopyPeopleWithName(addressBook_, - (CFStringRef)prefix)); - NSMutableArray *gtmPeople = [NSMutableArray arrayWithCapacity:[people count]]; - id person; - for (person in people) { - GTMABPerson *gtmPerson = [GTMABPerson recordWithRecord:person]; - [gtmPeople addObject:gtmPerson]; - } - return gtmPeople; -#else - // TODO(dmaclach): Change over to recordsMatchingSearchElement as an - // optimization? - // TODO(dmaclach): Make this match the way that the iPhone does it (by - // checking both first and last names) and adding unittests for all this. - NSArray *people = [self people]; - NSMutableArray *foundPeople = [NSMutableArray array]; - GTMABPerson *person; - for (person in people) { - NSString *compositeName = [person compositeName]; - NSRange range = [compositeName rangeOfString:prefix - options:(NSCaseInsensitiveSearch - | NSDiacriticInsensitiveSearch - | NSWidthInsensitiveSearch - | NSAnchoredSearch)]; - if (range.location != NSNotFound) { - [foundPeople addObject:person]; - } - } - return foundPeople; -#endif -} - -// Performs a prefix search on the composite names of groups in an address book -// and returns an array of groups that match the search criteria. -- (NSArray *)groupsWithCompositeNameWithPrefix:(NSString *)prefix { - NSArray *groups = [self groups]; - NSMutableArray *foundGroups = [NSMutableArray array]; - GTMABGroup *group; - for (group in groups) { - NSString *compositeName = [group compositeName]; - NSRange range = [compositeName rangeOfString:prefix - options:(NSCaseInsensitiveSearch - | NSDiacriticInsensitiveSearch - | NSWidthInsensitiveSearch - | NSAnchoredSearch)]; - if (range.location != NSNotFound) { - [foundGroups addObject:group]; - } - } - return foundGroups; -} - -+ (NSString *)localizedLabel:(NSString *)label { -#if GTM_IPHONE_SDK - return GTMCFAutorelease(ABAddressBookCopyLocalizedLabel((CFStringRef)label)); -#else // GTM_IPHONE_SDK - return GTMCFAutorelease(ABCopyLocalizedPropertyOrLabel((CFStringRef)label)); -#endif // GTM_IPHONE_SDK -} - -@end - -@implementation GTMABRecord -+ (id)recordWithRecord:(ABRecordRef)record { - return [[[self alloc] initWithRecord:record] autorelease]; -} - -- (id)initWithRecord:(ABRecordRef)record { - if ((self = [super init])) { - if ([self class] == [GTMABRecord class]) { - [self autorelease]; - [self doesNotRecognizeSelector:_cmd]; - } - if (!record) { - [self release]; - self = nil; - } else { - record_ = (ABRecordRef)CFRetain(record); - } - } - return self; -} - -- (NSUInteger)hash { - // This really isn't completely valid due to - // 6203836 ABRecords hash to their address - // but it's the best we can do without knowing what properties - // are in a record, and we don't have an API for that. - return CFHash(record_); -} - -- (BOOL)isEqual:(id)object { - // This really isn't completely valid due to - // 6203836 ABRecords hash to their address - // but it's the best we can do without knowing what properties - // are in a record, and we don't have an API for that. - return [object respondsToSelector:@selector(recordRef)] - && CFEqual(record_, [object recordRef]); -} - -- (void)dealloc { - if (record_) { - CFRelease(record_); - } - [super dealloc]; -} - -- (ABRecordRef)recordRef { - return record_; -} - -- (GTMABRecordID)recordID { -#if GTM_IPHONE_SDK - return ABRecordGetRecordID(record_); -#else // GTM_IPHONE_SDK - return GTMCFAutorelease(ABRecordCopyUniqueId(record_)); -#endif // GTM_IPHONE_SDK -} - -- (id)valueForProperty:(GTMABPropertyID)property { -#if GTM_IPHONE_SDK - id value = GTMCFAutorelease(ABRecordCopyValue(record_, property)); -#else // GTM_IPHONE_SDK - id value = GTMCFAutorelease(ABRecordCopyValue(record_, (CFStringRef)property)); -#endif // GTM_IPHONE_SDK - if (value) { - if ([[self class] typeOfProperty:property] & kABMultiValueMask) { - value = [[[GTMABMultiValue alloc] - initWithMultiValue:(ABMultiValueRef)value] autorelease]; - } - } - return value; -} - -- (BOOL)setValue:(id)value forProperty:(GTMABPropertyID)property { - if (!value) return NO; - // We check the type here because of - // Radar 6201046 ABRecordSetValue returns true even if you pass in a bad type - // for a value - TypeClassNameMap fullTypeMap[] = { - { kGTMABStringPropertyType, [NSString class] }, - { kGTMABIntegerPropertyType, [NSNumber class] }, - { kGTMABRealPropertyType, [NSNumber class] }, - { kGTMABDateTimePropertyType, [NSDate class] }, - { kGTMABDictionaryPropertyType, [NSDictionary class] }, - { kGTMABMultiStringPropertyType, [GTMABMultiValue class] }, - { kGTMABMultiRealPropertyType, [GTMABMultiValue class] }, - { kGTMABMultiDateTimePropertyType, [GTMABMultiValue class] }, - { kGTMABMultiDictionaryPropertyType, [GTMABMultiValue class] } - }; - GTMABPropertyType type = [[self class] typeOfProperty:property]; - BOOL wasFound = NO; - for (size_t i = 0; i < sizeof(fullTypeMap) / sizeof(TypeClassNameMap); ++i) { - if (fullTypeMap[i].pType == type) { - wasFound = YES; - if (![[value class] isSubclassOfClass:fullTypeMap[i].class]) { - return NO; - } - } - } - if (!wasFound) { - return NO; - } - if (type & kABMultiValueMask) { - value = (id)[value multiValueRef]; - } -#if GTM_IPHONE_SDK - CFErrorRef cfError = nil; - bool wasGood = ABRecordSetValue(record_, property, - (CFTypeRef)value, &cfError); - if (cfError) { - // COV_NF_START - _GTMDevLog(@"Error in [%@ %@]: %@", - [self class], NSStringFromSelector(_cmd), cfError); - CFRelease(cfError); - // COV_NF_END - } -#else // GTM_IPHONE_SDK - bool wasGood = ABRecordSetValue(record_, (CFStringRef)property, (CFTypeRef)value); -#endif // GTM_IPHONE_SDK - return wasGood ? YES : NO; -} - -- (BOOL)removeValueForProperty:(GTMABPropertyID)property { -#if GTM_IPHONE_SDK - CFErrorRef cfError = nil; - // We check to see if the value is in the property because of: - // Radar 6201005 ABRecordRemoveValue returns true for value that aren't - // in the record - id value = [self valueForProperty:property]; - bool wasGood = value && ABRecordRemoveValue(record_, property, &cfError); - if (cfError) { - // COV_NF_START - _GTMDevLog(@"Error in [%@ %@]: %@", - [self class], NSStringFromSelector(_cmd), cfError); - CFRelease(cfError); - // COV_NF_END - } -#else // GTM_IPHONE_SDK - id value = [self valueForProperty:property]; - bool wasGood = value && ABRecordRemoveValue(record_, (CFStringRef)property); -#endif // GTM_IPHONE_SDK - return wasGood ? YES : NO; -} - -// COV_NF_START -// All of these methods are to be overridden by their subclasses - -- (NSString *)compositeName { - [self doesNotRecognizeSelector:_cmd]; - return nil; -} - -+ (GTMABPropertyType)typeOfProperty:(GTMABPropertyID)property { - [self doesNotRecognizeSelector:_cmd]; - return kGTMABInvalidPropertyType; -} - -+ (NSString *)localizedPropertyName:(GTMABPropertyID)property { - [self doesNotRecognizeSelector:_cmd]; - return nil; -} -// COV_NF_END -@end - -@implementation GTMABPerson - -+ (GTMABPerson *)personWithFirstName:(NSString *)first - lastName:(NSString *)last { - GTMABPerson *person = [[[self alloc] init] autorelease]; - if (person) { - BOOL isGood = YES; - if (first) { - isGood = [person setValue:first - forProperty:kGTMABPersonFirstNameProperty]; - } - if (isGood && last) { - isGood = [person setValue:last forProperty:kGTMABPersonLastNameProperty]; - } - if (!isGood) { - // COV_NF_START - // Marked as NF because I don't know how to force an error - person = nil; - // COV_NF_END - } - } - return person; -} - -- (id)init { - ABRecordRef person = ABPersonCreate(); - self = [super initWithRecord:person]; - if (person) { - CFRelease(person); - } - return self; -} - -- (BOOL)setImageData:(NSData *)data { -#if GTM_IPHONE_SDK - CFErrorRef cfError = NULL; - bool wasGood = NO; - if (!data) { - wasGood = ABPersonRemoveImageData([self recordRef], &cfError); - } else { - // We verify that the data is good because of: - // Radar 6202868 ABPersonSetImageData should validate image data - UIImage *image = [UIImage imageWithData:data]; - wasGood = image && ABPersonSetImageData([self recordRef], - (CFDataRef)data, &cfError); - } - if (cfError) { - // COV_NF_START - _GTMDevLog(@"Error in [%@ %@]: %@", - [self class], NSStringFromSelector(_cmd), cfError); - CFRelease(cfError); - // COV_NF_END - } -#else // GTM_IPHONE_SDK - bool wasGood = YES; - if (data) { - NSImage *image = [[[NSImage alloc] initWithData:data] autorelease]; - wasGood = image != nil; - } - wasGood = wasGood && ABPersonSetImageData([self recordRef], (CFDataRef)data); -#endif // GTM_IPHONE_SDK - return wasGood ? YES : NO; -} - -- (GTMABImage *)image { - NSData *data = [self imageData]; -#if GTM_IPHONE_SDK - return [UIImage imageWithData:data]; -#else // GTM_IPHONE_SDK - return [[[NSImage alloc] initWithData:data] autorelease]; -#endif // GTM_IPHONE_SDK -} - -- (BOOL)setImage:(GTMABImage *)image { -#if GTM_IPHONE_SDK - NSData *data = UIImagePNGRepresentation(image); -#else // GTM_IPHONE_SDK - NSData *data = [image TIFFRepresentation]; -#endif // GTM_IPHONE_SDK - return [self setImageData:data]; -} - -- (NSData *)imageData { - return GTMCFAutorelease(ABPersonCopyImageData([self recordRef])); -} - -- (NSString *)compositeName { -#if GTM_IPHONE_SDK - return GTMCFAutorelease(ABRecordCopyCompositeName([self recordRef])); -#else // GTM_IPHONE_SDK - NSNumber *nsFlags = [self valueForProperty:kABPersonFlags]; - NSInteger flags = [nsFlags longValue]; - NSString *compositeName = nil; - if (flags & kABShowAsCompany) { - compositeName = [self valueForProperty:kABOrganizationProperty]; - } else { - NSString *firstName = [self valueForProperty:kGTMABPersonFirstNameProperty]; - NSString *lastName = [self valueForProperty:kGTMABPersonLastNameProperty]; - - if (firstName && lastName) { - GTMABPersonCompositeNameFormat format; - if (flags & kABFirstNameFirst) { - format = kABPersonCompositeNameFormatFirstNameFirst; - } else if (flags & kABLastNameFirst) { - format = kABPersonCompositeNameFormatLastNameFirst; - } else { - format = [[self class] compositeNameFormat]; - } - if (format == kABPersonCompositeNameFormatLastNameFirst) { - NSString *tempStr = lastName; - lastName = firstName; - firstName = tempStr; - } - compositeName = [NSString stringWithFormat:@"%@ %@", firstName, lastName]; - } else if (firstName) { - compositeName = firstName; - } else if (lastName) { - compositeName = lastName; - } else { - compositeName = @""; - } - } - - return compositeName; -#endif // GTM_IPHONE_SDK -} - -- (NSString *)description { -#if GTM_IPHONE_SDK - return [NSString stringWithFormat:@"%@ %@ %@ %d", - [self class], - [self valueForProperty:kGTMABPersonFirstNameProperty], - [self valueForProperty:kGTMABPersonLastNameProperty], - [self recordID]]; -#else // GTM_IPHONE_SDK - return [NSString stringWithFormat:@"%@ %@ %@ %@", - [self class], - [self valueForProperty:kGTMABPersonFirstNameProperty], - [self valueForProperty:kGTMABPersonLastNameProperty], - [self recordID]]; -#endif // GTM_IPHONE_SDK -} - -+ (NSString *)localizedPropertyName:(GTMABPropertyID)property { -#if GTM_IPHONE_SDK - return GTMCFAutorelease(ABPersonCopyLocalizedPropertyName(property)); -#else // GTM_IPHONE_SDK - return ABLocalizedPropertyOrLabel(property); -#endif // GTM_IPHONE_SDK -} - -+ (GTMABPersonCompositeNameFormat)compositeNameFormat { -#if GTM_IPHONE_SDK - #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0 - return ABPersonGetCompositeNameFormat(); - #else - return ABPersonGetCompositeNameFormatForRecord(NULL); - #endif // __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0 -#else // GTM_IPHONE_SDK - NSInteger nameOrdering - = [[ABAddressBook sharedAddressBook] defaultNameOrdering]; - return nameOrdering == kABFirstNameFirst ? - kABPersonCompositeNameFormatFirstNameFirst : - kABPersonCompositeNameFormatLastNameFirst; -#endif // GTM_IPHONE_SDK -} - -+ (GTMABPropertyType)typeOfProperty:(GTMABPropertyID)property { -#if GTM_IPHONE_SDK - return ABPersonGetTypeOfProperty(property); -#else // GTM_IPHONE_SDK - return ABTypeOfProperty([[GTMABAddressBook addressBook] addressBookRef], - (CFStringRef)kABPersonRecordType, - (CFStringRef)property); -#endif // GTM_IPHONE_SDK -} -@end - -@implementation GTMABGroup - -+ (GTMABGroup *)groupNamed:(NSString *)name { - GTMABGroup *group = [[[self alloc] init] autorelease]; - if (group) { - if (![group setValue:name forProperty:kABGroupNameProperty]) { - // COV_NF_START - // Can't get setValue to fail for me - group = nil; - // COV_NF_END - } - } - return group; -} - -- (id)init { - ABRecordRef group = ABGroupCreate(); - self = [super initWithRecord:group]; - if (group) { - CFRelease(group); - } - return self; -} - -- (NSArray *)members { - NSArray *people - = GTMCFAutorelease(ABGroupCopyArrayOfAllMembers([self recordRef])); - NSMutableArray *gtmPeople = [NSMutableArray arrayWithCapacity:[people count]]; - id person; - for (person in people) { - [gtmPeople addObject:[GTMABPerson recordWithRecord:(ABRecordRef)person]]; - } - return gtmPeople; -} - -- (BOOL)addMember:(GTMABPerson *)person { -#if GTM_IPHONE_SDK - CFErrorRef cfError = nil; - // We check for person because of - // Radar 6202860 Passing nil person into ABGroupAddMember crashes - bool wasGood = person && ABGroupAddMember([self recordRef], - [person recordRef], &cfError); - if (cfError) { - // COV_NF_START - _GTMDevLog(@"Error in [%@ %@]: %@", - [self class], NSStringFromSelector(_cmd), cfError); - CFRelease(cfError); - // COV_NF_END - } -#else // GTM_IPHONE_SDK - bool wasGood = person && ABGroupAddMember([self recordRef], - [person recordRef]); -#endif // GTM_IPHONE_SDK - return wasGood ? YES : NO; -} - -- (BOOL)removeMember:(GTMABPerson *)person { -#if GTM_IPHONE_SDK - CFErrorRef cfError = nil; - // We check for person because of - // Radar 6202860 Passing nil person into ABGroupAddMember crashes - // (I know this is remove, but it crashes there too) - bool wasGood = person && ABGroupRemoveMember([self recordRef], - [person recordRef], &cfError); - if (cfError) { - // COV_NF_START - _GTMDevLog(@"Error in [%@ %@]: %@", - [self class], NSStringFromSelector(_cmd), cfError); - CFRelease(cfError); - // COV_NF_END - } -#else // GTM_IPHONE_SDK - bool wasGood = person != nil; - if (wasGood) { - NSArray *array = GTMCFAutorelease(ABPersonCopyParentGroups([person recordRef])); - if ([array containsObject:[self recordRef]]) { - wasGood = ABGroupRemoveMember([self recordRef], - [person recordRef]); - } else { - wasGood = NO; - } - } -#endif // GTM_IPHONE_SDK - return wasGood ? YES : NO; -} - -- (NSString *)compositeName { -#if GTM_IPHONE_SDK - return GTMCFAutorelease(ABRecordCopyCompositeName([self recordRef])); -#else // GTM_IPHONE_SDK - return [self valueForProperty:kGTMABGroupNameProperty]; -#endif // GTM_IPHONE_SDK -} - -+ (GTMABPropertyType)typeOfProperty:(GTMABPropertyID)property { - GTMABPropertyType type = kGTMABInvalidPropertyType; - if (property == kABGroupNameProperty) { - type = kGTMABStringPropertyType; - } - return type; -} - -+ (NSString *)localizedPropertyName:(GTMABPropertyID)property { - NSString *name = kGTMABUnknownPropertyName; - if (property == kABGroupNameProperty) { - name = NSLocalizedStringFromTable(@"Name", - @"GTMABAddressBook", - @"name property"); - } - return name; -} - -- (NSString *)description { -#if GTM_IPHONE_SDK - return [NSString stringWithFormat:@"%@ %@ %d", - [self class], - [self valueForProperty:kABGroupNameProperty], - [self recordID]]; -#else // GTM_IPHONE_SDK - return [NSString stringWithFormat:@"%@ %@ %@", - [self class], - [self valueForProperty:kABGroupNameProperty], - [self recordID]]; -#endif // GTM_IPHONE_SDK -} -@end - -@implementation GTMABMultiValue -- (id)init { - // Call super init and release so we don't leak - [[super init] autorelease]; - [self doesNotRecognizeSelector:_cmd]; - return nil; // COV_NF_LINE -} - -- (id)initWithMultiValue:(ABMultiValueRef)multiValue { - if ((self = [super init])) { - if (!multiValue) { - [self release]; - self = nil; - } else { - multiValue_ = CFRetain(multiValue); - } - } - return self; -} - -- (id)copyWithZone:(NSZone *)zone { - return [[GTMABMultiValue alloc] initWithMultiValue:multiValue_]; -} - -- (id)mutableCopyWithZone:(NSZone *)zone { - return [[GTMABMutableMultiValue alloc] initWithMultiValue:multiValue_]; -} - -- (NSUInteger)hash { - // I'm implementing hash instead of using CFHash(multiValue_) because - // 6203854 ABMultiValues hash to their address - NSUInteger count = [self count]; - NSUInteger hash = 0; - for (NSUInteger i = 0; i < count; ++i) { - NSString *label = [self labelAtIndex:i]; - id value = [self valueAtIndex:i]; - hash += [label hash]; - hash += [value hash]; - } - return hash; -} - -- (BOOL)isEqual:(id)object { - // I'm implementing isEqual instea of using CFEquals(multiValue,...) because - // 6203854 ABMultiValues hash to their address - // and it appears CFEquals just calls through to hash to compare them. - BOOL isEqual = NO; - if ([object respondsToSelector:@selector(multiValueRef)]) { - isEqual = multiValue_ == [object multiValueRef]; - if (!isEqual) { - NSUInteger count = [self count]; - NSUInteger objCount = [(GTMABMultiValue *)object count]; - isEqual = count == objCount; - for (NSUInteger i = 0; isEqual && i < count; ++i) { - NSString *label = [self labelAtIndex:i]; - NSString *objLabel = [object labelAtIndex:i]; - isEqual = [label isEqual:objLabel]; - if (isEqual) { - id value = [self valueAtIndex:i]; - GTMABMultiValue *multiValueObject - = GTM_STATIC_CAST(GTMABMultiValue, object); - id objValue = [multiValueObject valueAtIndex:i]; - isEqual = [value isEqual:objValue]; - } - } - } - } - return isEqual; -} - -- (void)dealloc { - if (multiValue_) { - CFRelease(multiValue_); - } - [super dealloc]; -} - -- (ABMultiValueRef)multiValueRef { - return multiValue_; -} - -- (NSUInteger)count { -#if GTM_IPHONE_SDK - return ABMultiValueGetCount(multiValue_); -#else // GTM_IPHONE_SDK - return ABMultiValueCount(multiValue_); -#endif // GTM_IPHONE_SDK -} - -- (id)valueAtIndex:(NSUInteger)idx { - id value = nil; - if (idx < [self count]) { - value = GTMCFAutorelease(ABMultiValueCopyValueAtIndex(multiValue_, idx)); - ABPropertyType type = [self propertyType]; - if (type == kGTMABIntegerPropertyType - || type == kGTMABRealPropertyType - || type == kGTMABDictionaryPropertyType) { - // This is because of - // 6208390 Integer and real values don't work in ABMultiValueRefs - // Apparently they forget to add a ref count on int, real and - // dictionary values in ABMultiValueCopyValueAtIndex, although they do - // remember them for all other types. - // Once they fix this, this will lead to a leak, but I figure the leak - // is better than the crash. Our unittests will test to make sure that - // this is the case, and once we find a system that has this fixed, we - // can conditionalize this code. Look for testRadar6208390 in - // GTMABAddressBookTest.m - // Also, search for 6208390 below and fix the fast enumerator to actually - // be somewhat performant when this is fixed. -#ifndef __clang_analyzer__ - [value retain]; -#endif // __clang_analyzer__ - } - } - return value; -} - -- (NSString *)labelAtIndex:(NSUInteger)idx { - NSString *label = nil; - if (idx < [self count]) { - label = GTMCFAutorelease(ABMultiValueCopyLabelAtIndex(multiValue_, idx)); - } - return label; -} - -- (GTMABMultiValueIdentifier)identifierAtIndex:(NSUInteger)idx { - GTMABMultiValueIdentifier identifier = kGTMABMultiValueInvalidIdentifier; - if (idx < [self count]) { -#if GTM_IPHONE_SDK - identifier = ABMultiValueGetIdentifierAtIndex(multiValue_, idx); -#else // GTM_IPHONE_SDK - identifier = GTMCFAutorelease(ABMultiValueCopyIdentifierAtIndex(multiValue_, - idx)); -#endif // GTM_IPHONE_SDK - } - return identifier; -} - -- (NSUInteger)indexForIdentifier:(GTMABMultiValueIdentifier)identifier { -#if GTM_IPHONE_SDK - NSUInteger idx = ABMultiValueGetIndexForIdentifier(multiValue_, identifier); -#else // GTM_IPHONE_SDK - NSUInteger idx = ABMultiValueIndexForIdentifier(multiValue_, - (CFStringRef)identifier); -#endif // GTM_IPHONE_SDK - return idx == (NSUInteger)kCFNotFound ? (NSUInteger)NSNotFound : idx; -} - -- (GTMABPropertyType)propertyType { -#if GTM_IPHONE_SDK - return ABMultiValueGetPropertyType(multiValue_); -#else // GTM_IPHONE_SDK - return ABMultiValuePropertyType(multiValue_); -#endif // GTM_IPHONE_SDK -} - -- (id)valueForIdentifier:(GTMABMultiValueIdentifier)identifier { - return [self valueAtIndex:[self indexForIdentifier:identifier]]; -} - -- (NSString *)labelForIdentifier:(GTMABMultiValueIdentifier)identifier { - return [self labelAtIndex:[self indexForIdentifier:identifier]]; -} - -- (unsigned long*)mutations { - // We just need some constant non-zero value here so fast enumeration works. - // Dereferencing self should give us the isa which will stay constant - // over the enumeration. - return (unsigned long*)self; -} - -- (NSEnumerator *)valueEnumerator { - return [GTMABMultiValueEnumerator valueEnumeratorFor:self]; -} - -- (NSEnumerator *)labelEnumerator { - return [GTMABMultiValueEnumerator labelEnumeratorFor:self]; -} - -@end - -@implementation GTMABMutableMultiValue -+ (id)valueWithPropertyType:(GTMABPropertyType)type { - return [[[self alloc] initWithPropertyType:type] autorelease]; -} - -- (id)initWithPropertyType:(GTMABPropertyType)type { - ABMutableMultiValueRef ref = nil; - if (type != kGTMABInvalidPropertyType) { -#if GTM_IPHONE_SDK - ref = ABMultiValueCreateMutable(type); -#else // GTM_IPHONE_SDK - ref = ABMultiValueCreateMutable(); -#endif // GTM_IPHONE_SDK - } - self = [super initWithMultiValue:ref]; - if (ref) { - CFRelease(ref); - } - return self; -} - -- (id)initWithMultiValue:(ABMultiValueRef)multiValue { - ABMutableMultiValueRef ref = nil; - if (multiValue) { - ref = ABMultiValueCreateMutableCopy(multiValue); - } - self = [super initWithMultiValue:ref]; - if (ref) { - CFRelease(ref); - } - return self; -} - -- (id)initWithMutableMultiValue:(ABMutableMultiValueRef)multiValue { - return [super initWithMultiValue:multiValue]; -} - -- (BOOL)checkValueType:(id)value { - BOOL isGood = NO; - if (value) { - TypeClassNameMap singleValueTypeMap[] = { - { kGTMABStringPropertyType, [NSString class] }, - { kGTMABIntegerPropertyType, [NSNumber class] }, - { kGTMABRealPropertyType, [NSNumber class] }, - { kGTMABDateTimePropertyType, [NSDate class] }, - { kGTMABDictionaryPropertyType, [NSDictionary class] }, - }; - GTMABPropertyType type = [self propertyType] & ~kABMultiValueMask; -#if GTM_MACOS_SDK - // Since on the desktop mutables don't have a type UNTIL they have - // something in them, return YES if it's empty. - if ((type == 0) && ([self count] == 0)) return YES; -#endif // GTM_MACOS_SDK - for (size_t i = 0; - i < sizeof(singleValueTypeMap) / sizeof(TypeClassNameMap); ++i) { - if (singleValueTypeMap[i].pType == type) { - if ([[value class] isSubclassOfClass:singleValueTypeMap[i].class]) { - isGood = YES; - break; - } - } - } - } - return isGood; -} - -- (GTMABMultiValueIdentifier)addValue:(id)value withLabel:(CFStringRef)label { - GTMABMultiValueIdentifier identifier = kGTMABMultiValueInvalidIdentifier; - // We check label and value here because of - // radar 6202827 Passing nil info ABMultiValueAddValueAndLabel causes crash - bool wasGood = label && [self checkValueType:value]; - if (wasGood) { -#if GTM_IPHONE_SDK - wasGood = ABMultiValueAddValueAndLabel(multiValue_, - value, - label, - &identifier); -#else // GTM_IPHONE_SDK - wasGood = ABMultiValueAdd((ABMutableMultiValueRef)multiValue_, - value, - label, - (CFStringRef *)&identifier); -#endif // GTM_IPHONE_SDK - } - if (!wasGood) { - identifier = kGTMABMultiValueInvalidIdentifier; - } else { - mutations_++; - } - return identifier; -} - -- (GTMABMultiValueIdentifier)insertValue:(id)value - withLabel:(CFStringRef)label - atIndex:(NSUInteger)idx { - GTMABMultiValueIdentifier identifier = kGTMABMultiValueInvalidIdentifier; - // We perform a check here to ensure that we don't get bitten by - // Radar 6202807 ABMultiValueInsertValueAndLabelAtIndex allows you to insert - // values past end - NSUInteger count = [self count]; - // We check label and value here because of - // radar 6202827 Passing nil info ABMultiValueAddValueAndLabel causes crash - bool wasGood = idx <= count && label && [self checkValueType:value]; - if (wasGood) { -#if GTM_IPHONE_SDK - wasGood = ABMultiValueInsertValueAndLabelAtIndex(multiValue_, - value, - label, - idx, - &identifier); -#else // GTM_IPHONE_SDK - wasGood = ABMultiValueInsert((ABMutableMultiValueRef)multiValue_, - value, - label, - idx, - (CFStringRef *)&identifier); -#endif // GTM_IPHONE_SDK - } - if (!wasGood) { - identifier = kGTMABMultiValueInvalidIdentifier; - } else { - mutations_++; - } - return identifier; -} - -- (BOOL)removeValueAndLabelAtIndex:(NSUInteger)idx { - BOOL isGood = NO; - NSUInteger count = [self count]; - if (idx < count) { -#if GTM_IPHONE_SDK - bool wasGood = ABMultiValueRemoveValueAndLabelAtIndex(multiValue_, - idx); -#else // GTM_IPHONE_SDK - bool wasGood = ABMultiValueRemove((ABMutableMultiValueRef)multiValue_, - idx); -#endif // GTM_IPHONE_SDK - if (wasGood) { - mutations_++; - isGood = YES; - } - } - return isGood; -} - -- (BOOL)replaceValueAtIndex:(NSUInteger)idx withValue:(id)value { - BOOL isGood = NO; - NSUInteger count = [self count]; - if (idx < count && [self checkValueType:value]) { -#if GTM_IPHONE_SDK - bool goodReplace = ABMultiValueReplaceValueAtIndex(multiValue_, - value, idx); -#else // GTM_IPHONE_SDK - bool goodReplace - = ABMultiValueReplaceValue((ABMutableMultiValueRef)multiValue_, - (CFTypeRef)value, idx); -#endif // GTM_IPHONE_SDK - if (goodReplace) { - mutations_++; - isGood = YES; - } - } - return isGood; -} - -- (BOOL)replaceLabelAtIndex:(NSUInteger)idx withLabel:(CFStringRef)label { - BOOL isGood = NO; - NSUInteger count = [self count]; - if (idx < count) { -#if GTM_IPHONE_SDK - bool goodReplace = ABMultiValueReplaceLabelAtIndex(multiValue_, - label, idx); -#else // GTM_IPHONE_SDK - bool goodReplace - = ABMultiValueReplaceLabel((ABMutableMultiValueRef)multiValue_, - (CFTypeRef)label, idx); -#endif // GTM_IPHONE_SDK - if (goodReplace) { - mutations_++; - isGood = YES; - } - } - return isGood; -} - -- (unsigned long*)mutations { - return &mutations_; -} -@end - - -@implementation GTMABMultiValueEnumerator - -+ (id)valueEnumeratorFor:(GTMABMultiValue*)enumeree { - return [[[self alloc] initWithEnumeree:enumeree useLabels:NO] autorelease]; -} - -+ (id)labelEnumeratorFor:(GTMABMultiValue*)enumeree { - return [[[self alloc] initWithEnumeree:enumeree useLabels:YES] autorelease]; -} - -- (id)initWithEnumeree:(GTMABMultiValue*)enumeree useLabels:(BOOL)useLabels { - if ((self = [super init])) { - if (enumeree) { - enumeree_ = [enumeree retain]; - useLabels_ = useLabels; - } else { - // COV_NF_START - // Since this is a private class where the enumeree creates us - // there is no way we should ever get here. - [self release]; - self = nil; - // COV_NF_END - } - } - return self; -} - -- (void)dealloc { - [enumeree_ release]; - [super dealloc]; -} - -- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state - objects:(id *)stackbuf - count:(NSUInteger)len { - NSUInteger i; - if (!ref_) { - count_ = [enumeree_ count]; - ref_ = [enumeree_ multiValueRef]; - } - - for (i = 0; state->state < count_ && i < len; ++i, ++state->state) { - if (useLabels_) { - stackbuf[i] = GTMCFAutorelease(ABMultiValueCopyLabelAtIndex(ref_, - state->state)); - } else { - // TODO(dmaclach) Check this on Mac Desktop and use fast path if we can - // Yes this is slow, but necessary in light of radar 6208390 - // Once this is fixed we can go to something similar to the label - // case which should speed stuff up again. Hopefully anybody who wants - // real performance is willing to move down to the C API anyways. - stackbuf[i] = [enumeree_ valueAtIndex:state->state]; - } - } - - state->itemsPtr = stackbuf; - state->mutationsPtr = [enumeree_ mutations]; - return i; -} - -- (id)nextObject { - id value = nil; - if (!ref_) { - count_ = [enumeree_ count]; - mutations_ = *[enumeree_ mutations]; - ref_ = [enumeree_ multiValueRef]; - - } - if (mutations_ != *[enumeree_ mutations]) { - NSString *reason = [NSString stringWithFormat:@"*** Collection <%@> was " - "mutated while being enumerated", enumeree_]; - [[NSException exceptionWithName:NSGenericException - reason:reason - userInfo:nil] raise]; - } - if (index_ < count_) { - if (useLabels_) { - value = GTMCFAutorelease(ABMultiValueCopyLabelAtIndex(ref_, - index_)); - } else { - // TODO(dmaclach) Check this on Mac Desktop and use fast path if we can - // Yes this is slow, but necessary in light of radar 6208390 - // Once this is fixed we can go to something similar to the label - // case which should speed stuff up again. Hopefully anybody who wants - // real performance is willing to move down to the C API anyways. - value = [enumeree_ valueAtIndex:index_]; - } - index_ += 1; - } - return value; -} -@end - |