aboutsummaryrefslogtreecommitdiff
path: root/AddressBook/GTMABAddressBook.m
diff options
context:
space:
mode:
Diffstat (limited to 'AddressBook/GTMABAddressBook.m')
-rw-r--r--AddressBook/GTMABAddressBook.m1234
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
-