Developer Area

root/apps/iphone/superbook/trunk/Classes/DotTelABMapper.m @ 461

Revision 461, 24.3 kB (checked in by henri, 5 years ago)

Changed to use NSLocalizedString and rewrote the strings localization file

Line 
1//
2//  DotTelABMapper.m
3//  LocateThem
4//
5//  Created by Henri Asseily on 5/27/09.
6/*
7 Copyright (c) 2008-2009, Telnic Ltd. All rights reserved.
8 
9 Redistribution and use in source and binary forms, with or without modification,
10 are permitted provided that the following conditions are met:
11 
12 Redistributions of source code must retain the above copyright notice, this list of conditions
13 and the following disclaimer. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the documentation and/or other
15 materials provided with the distribution.
16 Neither the name of the Telnic Ltd. nor the names of its contributors may be used to endorse or
17 promote products derived from this software without specific prior written permission.
18 THIS DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
19 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20 AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
24 IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27//
28
29#import "DotTelABMapper.h"
30
31// Define initialization and assignment of any ABMultiValue field that contains CFStringRefs
32// Saves a lot of typing
33#define SETNAPTR_INITIALIZATION NSError *err; \
34                                                                CFErrorRef cferr = (CFErrorRef)err; \
35                                                                NSString *service; \
36                                                                service = [[rec serviceTypeArray] objectAtIndex:0]; \
37                                                                NSString *lih; \
38                                                                if ([[rec lihArray] count] == 0) \
39                                                                        lih = @""; \
40                                                                else \
41                                                                        lih = [[rec lihArray] objectAtIndex:0]; \
42                                                                ABMultiValueRef allexisting; \
43                                                                ABMutableMultiValueRef allupdated; \
44                                                                CFStringRef abLabel; \
45                                                                CFStringRef abValue
46
47#define SETNAPTR_ASSIGN(kValType)       BOOL exists = NO; \
48                                                                        for (CFIndex i = 0; i < ABMultiValueGetCount(allupdated); i++) { \
49                                                                                CFStringRef aVal = (CFStringRef)ABMultiValueCopyValueAtIndex(allupdated, i); \
50                                                                                if (aVal) { \
51                                                                                        if ([self record:(NSString *)abValue isEqualToRecord:(NSString *)aVal forProperty:kValType]) { \
52                                                                                                exists = YES; \
53                                                                                                CFRelease(aVal); \
54                                                                                                break; \
55                                                                                        } \
56                                                                                        CFRelease(aVal); \
57                                                                                } \
58                                                                        } \
59                                                                        if (!exists) { \
60                                                                                ABMultiValueAddValueAndLabel(allupdated, abValue, abLabel, NULL); \
61                                                                                BOOL res = ABRecordSetValue(abRecord, kValType, allupdated, &cferr); \
62                                                                                if (res) [updatedProperties addObject:[NSNumber numberWithInt:kValType]]; \
63                                                                        } \
64                                                                        CFRelease(allupdated); \
65                                                                        return NULL
66
67@interface DotTelABMapper (PrivateMethods)
68
69- (NSError *)setNaptrField:(RecordNaptr *)field;
70- (NSError *)setTxtField:(RecordTxt *)field;
71- (NSError *)setLocField:(RecordLoc *)field;
72
73@end
74
75NSString * const DotTelABMapperErrorDomain = @"DotTelABMapperErrorDomain";
76static NSDictionary *naptrSelectors;
77static NSDictionary *txtSelectors;
78static NSDictionary *ccMapper;
79
80@implementation DotTelABMapper
81
82@synthesize updatedProperties;
83
84#pragma mark ---- designated initializer ----
85- (DotTelABMapper *)initWithABRecord:(ABRecordRef)rec {
86        if (rec == NULL) {
87                return NULL;
88        }
89        self = [super init];
90        if (abRecord) {
91                CFRelease(abRecord);
92        }
93        abRecord = CFRetain(rec);
94        if (updatedProperties) {
95                [updatedProperties release];
96        }
97        updatedProperties = [[NSMutableArray array] retain];
98       
99        NSDictionary *selectorsMapper = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"ABMapperSelectors"
100                                                                                                                                                                                                                           ofType:@"plist"]];
101        txtSelectors    = [[selectorsMapper objectForKey:@"txt"] retain];
102        naptrSelectors  = [[selectorsMapper objectForKey:@"naptr"] retain];
103        ccMapper = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"CountryCodeMapper"
104                                                                                                                                                                                  ofType:@"plist"]];
105        return self;
106}
107
108- (DotTelABMapper *)init {
109        // fail
110        return NULL;
111}
112
113#pragma mark ---- mapping entry methods ----
114
115- (NSError *)setTelName:(NSString *)telname {
116        // Set a new telname or update it if it exists and is different
117        BOOL fieldExists        = NO;
118        BOOL fieldChanged       = NO;
119        CFStringRef telUrlValue;
120        ABMultiValueRef allExistingUrls;
121        ABMutableMultiValueRef allUrls;
122        allExistingUrls = ABRecordCopyValue(abRecord, kABPersonURLProperty);
123        if (!allExistingUrls) { // No URLs, so telname doesn't exist. Make a new property to hold the telname
124                allUrls = ABMultiValueCreateMutable(kABMultiStringPropertyType);
125        } else {                // There are URLs, let's find the telname
126                allUrls = ABMultiValueCreateMutableCopy(allExistingUrls);
127                CFRelease(allExistingUrls);
128                for (CFIndex j = 0; j < ABMultiValueGetCount(allUrls); j++) {
129                        telUrlValue = ABMultiValueCopyValueAtIndex(allUrls, j);
130                        if ([(NSString *)telUrlValue hasSuffix:@".tel"] || [(NSString *)telUrlValue hasSuffix:@".tel/"]) {
131                                fieldExists = YES;
132                                // Found a telname. If it's different, update it
133                                if (![telname isEqualToString:(NSString *)telUrlValue]) {
134                                        ABMultiValueReplaceValueAtIndex(allUrls, (CFStringRef)telname, j);
135                                        fieldChanged = YES;
136                                }
137                                CFRelease(telUrlValue);
138                                break;
139                        }
140                        CFRelease(telUrlValue);
141                }
142        }
143        // If telname doesn't exist, create a new one
144        if (!fieldExists) {
145                ABMultiValueAddValueAndLabel(allUrls, (CFStringRef)telname, CFSTR(".tel"), NULL);
146                fieldChanged = YES;
147        }
148        NSError *err = nil;
149        CFErrorRef cferr = (CFErrorRef)err;
150        if (fieldChanged) {
151                BOOL res = ABRecordSetValue(abRecord, kABPersonURLProperty, allUrls, &cferr);
152                if (res) {
153                        //[updatedProperties addObject:[NSNumber numberWithInt:kABPersonURLProperty]];  // Always show the .tel url
154                }
155        }
156        [updatedProperties addObject:[NSNumber numberWithInt:kABPersonURLProperty]];    // as said above, always show .tel url
157        CFRelease(allUrls);
158
159        return err;
160}
161
162- (NSError *)setField:(id)field {
163        if ([field isKindOfClass:[RecordNaptr class]])
164                return [self setNaptrField:(RecordNaptr *)field];
165        if ([field isKindOfClass:[RecordTxt class]])
166                return [self setTxtField:(RecordTxt *)field];
167        if ([field isKindOfClass:[RecordLoc class]])
168                return [self setLocField:(RecordLoc *)field];
169       
170        // wrong class
171        NSError *err = [NSError errorWithDomain:DotTelABMapperErrorDomain code:kDotTelABMapperUnknownRecordType userInfo:nil];
172        return err;
173}
174
175#pragma mark ---- private naptr/txt/loc dispatch methods ---
176
177- (NSError *)setNaptrField:(RecordNaptr *)field {
178        if ([field isTerminal]) {
179                NSString *primaryType = [[field serviceTypeArray] objectAtIndex:0];
180                SEL parseMethod = NSSelectorFromString([naptrSelectors objectForKey:primaryType]);
181                if (parseMethod == (SEL)0)
182                        return NULL;
183                return ((NSError *)[self performSelector:parseMethod withObject:field]);
184        }
185        return NULL;
186}
187
188- (NSError *)setTxtField:(RecordTxt *)field {
189        if (field.isKeyword) {
190                NSArray *kv = field.keysAndValues;
191                NSString *primaryType = [kv objectAtIndex:0];
192                SEL parseMethod = NSSelectorFromString([txtSelectors objectForKey:primaryType]);
193                if (parseMethod == (SEL)0)
194                        return NULL;
195                return ((NSError *)[self performSelector:parseMethod withObject:kv]);
196        }
197        return NULL;
198}
199
200- (NSError *)setLocField:(RecordLoc *)field {
201        // Do nothing for now
202        // We could do reverse geoloc and grab the actual address, but it's probably
203        // not what the .tel owner intended
204        return NULL;
205}
206
207#pragma mark ---- private mapping methods ---
208
209- (NSError *)setNaptrNone:(RecordNaptr *)rec {
210        return NULL;
211}
212
213- (NSError *)setNaptrPhone:(RecordNaptr *)rec {
214        SETNAPTR_INITIALIZATION;
215       
216        // ==== Begin parsing ====
217        allexisting = ABRecordCopyValue(abRecord, kABPersonPhoneProperty);
218        if (!allexisting) {
219                allupdated = ABMultiValueCreateMutable(kABMultiStringPropertyType);
220        } else {
221                allupdated = ABMultiValueCreateMutableCopy(allexisting);
222                CFRelease(allexisting);
223        }
224        abValue = (CFStringRef)[[[rec uriContent] componentsSeparatedByString:@":"] objectAtIndex:1];
225        if ([lih isEqualToString:@"x-mobile"]) {
226                abLabel = kABPersonPhoneMobileLabel;
227        } else if ([lih isEqualToString:@"x-main"]) {
228                abLabel = kABPersonPhoneMainLabel;
229        } else if ([lih isEqualToString:@"x-home"]) {
230                abLabel = kABHomeLabel;
231        } else if ([lih isEqualToString:@"x-work"]) {
232                abLabel = kABWorkLabel;
233        } else {
234                abLabel = kABOtherLabel;
235        }
236        // ==== End parsing ====
237       
238        SETNAPTR_ASSIGN(kABPersonPhoneProperty);
239}
240
241- (NSError *)setNaptrFax:(RecordNaptr *)rec {
242        SETNAPTR_INITIALIZATION;
243       
244        // ==== Begin parsing ====
245        allexisting = ABRecordCopyValue(abRecord, kABPersonPhoneProperty);
246        if (!allexisting) {
247                allupdated = ABMultiValueCreateMutable(kABMultiStringPropertyType);
248        } else {
249                allupdated = ABMultiValueCreateMutableCopy(allexisting);
250                CFRelease(allexisting);
251        }
252        abValue = (CFStringRef)[[[rec uriContent] componentsSeparatedByString:@":"] objectAtIndex:1];
253        if ([lih isEqualToString:@"x-main"]) {
254                abLabel = (CFStringRef)NSLocalizedString(@"Main Fax", @"'Main Fax' label");
255        } else if ([lih isEqualToString:@"x-home"]) {
256                abLabel = kABPersonPhoneHomeFAXLabel;
257        }  else {
258                abLabel = kABPersonPhoneWorkFAXLabel;
259        }
260        // ==== End parsing ====
261       
262        SETNAPTR_ASSIGN(kABPersonPhoneProperty);
263}
264
265- (NSError *)setNaptrEmail:(RecordNaptr *)rec {
266        SETNAPTR_INITIALIZATION;
267       
268        // ==== Begin parsing ====
269        allexisting = ABRecordCopyValue(abRecord, kABPersonEmailProperty);
270        if (!allexisting) {
271                allupdated = ABMultiValueCreateMutable(kABMultiStringPropertyType);
272        } else {
273                allupdated = ABMultiValueCreateMutableCopy(allexisting);
274                CFRelease(allexisting);
275        }
276        abValue = (CFStringRef)[[[rec uriContent] componentsSeparatedByString:@":"] objectAtIndex:1];
277        if ([lih isEqualToString:@"x-main"]) {
278                abLabel = (CFStringRef)NSLocalizedString(@"main", @"Label 'Main' for contact info");
279        } else if ([lih isEqualToString:@"x-home"]) {
280                abLabel = kABHomeLabel;
281        } else if ([lih isEqualToString:@"x-work"]) {
282                abLabel = kABWorkLabel;
283        } else {
284                abLabel = kABOtherLabel;
285        }
286        // ==== End parsing ====
287       
288        SETNAPTR_ASSIGN(kABPersonEmailProperty);
289}
290
291- (NSError *)setNaptrUrl:(RecordNaptr *)rec {
292        SETNAPTR_INITIALIZATION;
293       
294        // ==== Begin parsing ====
295        allexisting = ABRecordCopyValue(abRecord, kABPersonURLProperty);
296        if (!allexisting) {
297                allupdated = ABMultiValueCreateMutable(kABMultiStringPropertyType);
298        } else {
299                allupdated = ABMultiValueCreateMutableCopy(allexisting);
300                CFRelease(allexisting);
301        }
302
303        abValue = (CFStringRef)[rec uriContent];
304        if ([lih isEqualToString:@"x-main"]) {
305                abLabel = kABPersonHomePageLabel;
306        } else if ([lih isEqualToString:@"x-home"]) {
307                abLabel = kABPersonHomePageLabel;
308        } else if ([lih isEqualToString:@"x-work"]) {
309                abLabel = kABWorkLabel;
310        } else {
311                abLabel = kABOtherLabel;
312        }
313        // ==== End parsing ====
314       
315        SETNAPTR_ASSIGN(kABPersonURLProperty);
316}
317
318- (NSError *)setNaptrUrlOther:(RecordNaptr *)rec {
319        SETNAPTR_INITIALIZATION;
320       
321        // ==== Begin parsing ====
322        allexisting = ABRecordCopyValue(abRecord, kABPersonURLProperty);
323        if (!allexisting) {
324                allupdated = ABMultiValueCreateMutable(kABMultiStringPropertyType);
325        } else {
326                allupdated = ABMultiValueCreateMutableCopy(allexisting);
327                CFRelease(allexisting);
328        }
329
330        abValue = (CFStringRef)[rec uriContent];
331        NSString *servicePostfix = [[service componentsSeparatedByString:@":"] objectAtIndex:1];
332        if ([lih isEqualToString:@"x-main"]) {
333                abLabel = (CFStringRef)[servicePostfix stringByAppendingFormat:@" %@",NSLocalizedString(@"main", @"Label 'Main' for contact info")];
334        } else if ([lih isEqualToString:@"x-home"]) {
335                abLabel = (CFStringRef)[servicePostfix stringByAppendingFormat:@" %@",(NSString *)kABHomeLabel];
336        } else if ([lih isEqualToString:@"x-work"]) {
337                abLabel = (CFStringRef)[servicePostfix stringByAppendingFormat:@" %@",(NSString *)kABWorkLabel];
338        } else {
339                abLabel = (CFStringRef)[servicePostfix stringByAppendingFormat:@" %@",(NSString *)kABOtherLabel];
340        }
341        // ==== End parsing ====
342       
343        SETNAPTR_ASSIGN(kABPersonURLProperty);
344}
345
346- (NSError *)setNaptrIM:(RecordNaptr *)rec {
347        // NOTE: The simulator does NOT support IM fields. It silently discards them.
348        // IM fields are dictionaries, not strings. They need special handling.
349        SETNAPTR_INITIALIZATION;
350
351        CFMutableDictionaryRef anIM = (CFMutableDictionaryRef)[NSMutableDictionary dictionaryWithCapacity:2];   // IM dictionary
352
353        // ==== Begin parsing ====
354        allexisting = ABRecordCopyValue(abRecord, kABPersonInstantMessageProperty);
355        if (!allexisting) {
356                allupdated = ABMultiValueCreateMutable(kABMultiDictionaryPropertyType);
357        } else {
358                allupdated = ABMultiValueCreateMutableCopy(allexisting);
359                CFRelease(allexisting);
360        }
361
362        abValue = (CFStringRef)[[[rec uriContent] componentsSeparatedByString:@":"] objectAtIndex:1];
363        if ([lih isEqualToString:@"x-main"]) {
364                abLabel = (CFStringRef)NSLocalizedString(@"main", @"Label 'Main' for contact info");
365        } else if ([lih isEqualToString:@"x-home"]) {
366                abLabel = kABHomeLabel;
367        } else if ([lih isEqualToString:@"x-work"]) {
368                abLabel = kABWorkLabel;
369        } else {
370                abLabel = kABOtherLabel;
371        }
372        NSString *servicePostfix = [[service componentsSeparatedByString:@":"] objectAtIndex:1];
373        if ([servicePostfix isEqualToString:@"aim"]) {
374                CFDictionaryAddValue(anIM, kABPersonInstantMessageServiceKey, kABPersonInstantMessageServiceAIM);
375        } else if ([servicePostfix isEqualToString:@"icq"]) {
376                CFDictionaryAddValue(anIM, kABPersonInstantMessageServiceKey, kABPersonInstantMessageServiceICQ);
377        } else if ([servicePostfix isEqualToString:@"ymsgr"]) {
378                CFDictionaryAddValue(anIM, kABPersonInstantMessageServiceKey, kABPersonInstantMessageServiceYahoo);
379        } else if ([servicePostfix isEqualToString:@"msnim"]) {
380                CFDictionaryAddValue(anIM, kABPersonInstantMessageServiceKey, kABPersonInstantMessageServiceMSN);
381        } else if ([servicePostfix isEqualToString:@"xmpp"]) {
382                CFDictionaryAddValue(anIM, kABPersonInstantMessageServiceKey, kABPersonInstantMessageServiceJabber);
383        } else if ([servicePostfix isEqualToString:@"gtalk"]) {
384                CFDictionaryAddValue(anIM, kABPersonInstantMessageServiceKey, @"GoogleTalk");
385        } else {
386                CFDictionaryAddValue(anIM, kABPersonInstantMessageServiceKey, (CFStringRef)servicePostfix);
387        }
388        CFDictionaryAddValue(anIM, kABPersonInstantMessageUsernameKey, abValue);
389        // ==== End parsing ====
390       
391        // ==== Begin assignment ====
392        // Check if an IM with the same service type and label already exists. If so, we replace it.
393        // This is different behavior than phone numbers and urls (i.e. CFStringRefs)
394        // Note that we only replace the first one of the same type and label
395        CFIndex existingIMServiceIndex = kCFNotFound;
396        for (CFIndex i = 0; i < ABMultiValueGetCount(allupdated); i++) {
397                CFDictionaryRef existingIM = (CFDictionaryRef)ABMultiValueCopyValueAtIndex(allupdated, i);
398                if (existingIM) {
399                        if (CFDictionaryGetValue(anIM, kABPersonInstantMessageServiceKey) == CFDictionaryGetValue(existingIM, kABPersonInstantMessageServiceKey)) {
400                                // Found a matching IM service type
401                                CFStringRef existingLabel = (CFStringRef)ABMultiValueCopyLabelAtIndex(allupdated, i);
402                                if (existingLabel) {
403                                        if (CFStringCompare(abLabel, existingLabel, 0) == kCFCompareEqualTo) {
404                                                // Found a matching label on top of the matching IM service type
405                                                ABMultiValueReplaceValueAtIndex(allupdated, anIM, i);
406                                                existingIMServiceIndex = i;
407                                                CFRelease(existingLabel);
408                                                CFRelease(existingIM);
409                                                break;
410                                        } else {
411                                                // There was a label but it didn't match, don't replace anything
412                                                CFRelease(existingLabel);
413                                        }
414                                } else {
415                                        // No label, but the service type matches. Let's replace and update the label
416                                        ABMultiValueReplaceValueAtIndex(allupdated, anIM, i);
417                                        ABMultiValueReplaceLabelAtIndex(allupdated, abLabel, i);
418                                        existingIMServiceIndex = i;
419                                        CFRelease(existingIM);
420                                        break;                                 
421                                }
422                        }
423                        // At this point, the IM service type didn't match. Keep looping to look for a possible match
424                        CFRelease(existingIM);
425                }
426        }
427        if (existingIMServiceIndex == kCFNotFound) {
428                // Didn't find a matching IM service type and label
429                ABMultiValueAddValueAndLabel(allupdated, anIM, abLabel, NULL);
430                BOOL res = ABRecordSetValue(abRecord, kABPersonInstantMessageProperty, allupdated, &cferr);
431                if (res) {
432                        [updatedProperties addObject:[NSNumber numberWithInt:kABPersonInstantMessageProperty]];
433                }
434               
435        }
436        CFRelease(allupdated);
437        return NULL;
438}
439
440- (NSError *)setTxtNL:(NSArray *)kv {
441        // Name Parsing
442        // Discard the label
443        /*
444         "s" = "Salutation";    //    e.g. "Mr", "Mrs", etc
445         "fn" = "First Name";   //    e.g. "Adam"
446         "ln" = "Last Name";    //    e.g. "Smith"
447         "nn" = "Nickname";             //    e.g. "Ade"
448         */
449        CFErrorRef err = NULL;
450        NSUInteger i, count = [kv count];
451        for (i = 0; i < count-1; i=i+2) {
452                if (err) {
453                        return (NSError *)err;
454                }
455                NSString *k = [kv objectAtIndex:i];             // key
456                NSString *v = [kv objectAtIndex:i+1];   // value
457               
458                if ([k isEqualToString:@"fn"]) {        // kABPersonFirstNameProperty
459                        if (ABRecordSetValue(abRecord, kABPersonFirstNameProperty, (CFStringRef)v, &err))
460                                [updatedProperties addObject:[NSNumber numberWithInt:kABPersonFirstNameProperty]];
461                        continue;
462                }
463                if ([k isEqualToString:@"ln"]) {        // kABPersonLastNameProperty
464                        if (ABRecordSetValue(abRecord, kABPersonLastNameProperty, (CFStringRef)v, &err))
465                                [updatedProperties addObject:[NSNumber numberWithInt:kABPersonLastNameProperty]];
466                        continue;
467                }
468                if ([k isEqualToString:@"nn"]) {        // kABPersonNicknameProperty
469                        if (ABRecordSetValue(abRecord, kABPersonNicknameProperty, (CFStringRef)v, &err))
470                                [updatedProperties addObject:[NSNumber numberWithInt:kABPersonNicknameProperty]];
471                        continue;
472                }
473                if ([k isEqualToString:@"s"]) {         // kABPersonPrefixProperty
474                        if (ABRecordSetValue(abRecord, kABPersonPrefixProperty, (CFStringRef)v, &err))
475                                [updatedProperties addObject:[NSNumber numberWithInt:kABPersonPrefixProperty]];
476                        continue;
477                }
478        }
479        return NULL;
480}
481
482- (NSError *)setTxtBI:(NSArray *)kv {
483        return NULL;
484}
485
486- (NSError *)setTxtPA:(NSArray *)kv {
487        // Used for both "pa" and "bpa"
488        /*
489         "a1" = "Address Line 1";   //    e.g. "8 Wilfred Street"
490         "a2" = "Address Line 2";   //    e.g. "Victoria"
491         "a3" = "Address Line 3";   //    e.g. "Westminster"
492         "tc" = "City";     //    e.g. "London"
493         "sp" = "State";    //    e.g. "Hampshire"
494         "pc" = "Postal Code";     //    e.g. "SW1E 6PL"
495         "c" = "Country";       //    e.g. "Scotland"
496        */
497       
498        // Create the address dictionary
499        NSMutableDictionary *newAddress = [NSMutableDictionary dictionaryWithCapacity:5];
500        NSString *primaryType  = [kv objectAtIndex:0];
501        // NSString *primaryValue = [kv objectAtIndex:1];       // unused
502
503        // Map the pa and bpa to home and work respectively
504        CFStringRef primaryLabel;
505        if ([primaryType isEqualToString:@"pa"]) {
506                primaryLabel = kABHomeLabel;
507        } else if ([primaryType isEqualToString:@"bpa"]) {
508                primaryLabel = kABWorkLabel;
509        } else {
510                return NULL;
511        }
512
513        // Grab all types and values and build the newAddress
514        NSUInteger i, count = [kv count];
515        NSMutableString *addressStreetFull = nil;
516        for (i = 0; i < count-1; i=i+2) {
517                NSString *k = [kv objectAtIndex:i];             // key
518                NSString *v = [kv objectAtIndex:i+1];   // value
519               
520                // Concatenate the address lines
521                if ([k isEqualToString:@"a1"]) {
522                        if (addressStreetFull) {
523                                // if it exists (it shouldn't), prepend a1
524                                [addressStreetFull insertString:[NSString stringWithFormat:@"%@\n", v] atIndex:0];
525                        } else {
526                                addressStreetFull = [NSMutableString stringWithString:v];
527                        }
528                        continue;
529                }
530                if ([k isEqualToString:@"a2"] || [k isEqualToString:@"a3"]) {
531                        if (addressStreetFull) {
532                                [addressStreetFull appendFormat:@"\n%@", v];
533                        } else {
534                                addressStreetFull = [NSMutableString stringWithString:v];
535                        }
536                        continue;
537                }
538                if ([k isEqualToString:@"tc"]) {        // kABPersonAddressCityKey
539                        [newAddress setObject:v forKey:(NSString *)kABPersonAddressCityKey];
540                        continue;
541                }
542                if ([k isEqualToString:@"sp"]) {        // kABPersonAddressStateKey
543                        [newAddress setObject:v forKey:(NSString *)kABPersonAddressStateKey];
544                        continue;
545                }
546                if ([k isEqualToString:@"pc"]) {        // kABPersonAddressZIPKey
547                        [newAddress setObject:v forKey:(NSString *)kABPersonAddressZIPKey];
548                        continue;
549                }
550                if ([k isEqualToString:@"c"]) { // kABPersonAddressCountryKey
551                        [newAddress setObject:v forKey:(NSString *)kABPersonAddressCountryKey];
552                        NSString *countryCode = [ccMapper objectForKey:v];
553                        if (countryCode) {
554                                [newAddress setObject:countryCode forKey:(NSString *)kABPersonAddressCountryCodeKey];
555                        }
556                        continue;
557                }
558        }
559        // Add the concatenated address line
560        // kABPersonAddressStreetKey
561        if (addressStreetFull) {
562                [newAddress setObject:addressStreetFull forKey:(NSString *)kABPersonAddressStreetKey];
563        }
564       
565        // Now match to an existing address and replace if found
566        // Otherwise create a new one
567               
568        BOOL fieldExists        = NO;
569        BOOL fieldChanged       = NO;
570        ABMultiValueRef existingaddresses;      // All existing addresses
571        ABMutableMultiValueRef addresses;       // All addresses
572        CFDictionaryRef anAddress;                      // An existing address
573        CFStringRef aLabel;                                     // Label for that address
574        existingaddresses = ABRecordCopyValue(abRecord, kABPersonAddressProperty);
575        if (!existingaddresses) {       // No addresses. Make a new property to hold the new address
576                addresses = ABMultiValueCreateMutable(kABMultiDictionaryPropertyType);
577        } else {                // There are addresses, let's find the first matching one that has the proper label
578                addresses = ABMultiValueCreateMutableCopy(existingaddresses);
579                CFRelease(existingaddresses);
580                for (CFIndex j = 0; j < ABMultiValueGetCount(addresses); j++) {
581                        anAddress = ABMultiValueCopyValueAtIndex(addresses, j);
582                        aLabel = ABMultiValueCopyLabelAtIndex(addresses, j);
583                        if (CFStringCompare(primaryLabel, aLabel, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
584                                fieldExists = YES;
585                                // Found a matching address. Replace it without checking if it's the same
586                                // It would be better to check similarity and not replace if equal, but the cost is high
587                                ABMultiValueReplaceValueAtIndex(addresses, newAddress, j);
588                                fieldChanged = YES;
589                        }
590                        CFRelease(anAddress);
591                        if (aLabel)
592                                CFRelease(aLabel);
593                        if (fieldExists)
594                                break;
595                }
596        }
597        // If field doesn't exist, create a new one
598        if (!fieldExists) {
599                ABMultiValueAddValueAndLabel(addresses, (CFDictionaryRef)newAddress, primaryLabel, NULL);
600                fieldChanged = YES;
601        }
602        NSError *err;
603        CFErrorRef cferr = (CFErrorRef)err;
604        if (fieldChanged) {
605                BOOL res = ABRecordSetValue(abRecord, kABPersonAddressProperty, addresses, &cferr);
606                if (res) {
607                        [updatedProperties addObject:[NSNumber numberWithInt:kABPersonAddressProperty]];
608                }
609        }
610        CFRelease(addresses);
611       
612        return NULL;
613}
614
615#pragma mark ---- utility methods ----
616
617- (BOOL)record:(NSString *)aRec isEqualToRecord:(NSString *)otherRec forProperty:(ABPropertyID)property {
618        if (property == kABPersonPhoneProperty)
619                return [self phone:aRec isEqualToPhone:otherRec];
620        return [aRec isEqualToString:otherRec];
621}
622
623- (BOOL)phone:(NSString *)aPhone isEqualToPhone:(NSString *)otherPhone {
624        char *p1 = (char *)[aPhone cStringUsingEncoding:NSUTF8StringEncoding];
625        char *p2 = (char *)[otherPhone cStringUsingEncoding:NSUTF8StringEncoding];
626        BOOL areEqual = TRUE;
627        int i = 0;
628        int j = 0;
629        while ((char)p1[i] != '\0') {
630                if ((p1[i] == ' ') || (p1[i] == '-') || (p1[i] == '(') || (p1[i] == ')')) {
631                        i++;
632                        //NSLog(@"Skipped p1 char '%c' at index %d", p1[i], i);
633                        continue;
634                }
635                if (p2[j] == '\0') {
636                        //NSLog(@"End of p2 at %d, shorter than p1 at %d", j, i);
637                        areEqual = FALSE;
638                        break;
639                }
640                while (p2[j] != '\0') {
641                        if ((p2[j] == ' ') || (p2[j] == '-') || (p2[j] == '(') || (p2[j] == ')')) {
642                                //NSLog(@"Skipped p2 char '%c' at index %d", p2[j], i);
643                                j++;
644                                continue;
645                        }
646                        if(p1[i] != p2[j]) {
647                                areEqual = FALSE;
648                                //NSLog(@"Failed compare of p1[%d] '%c' and p2[%d] '%c'", i, p1[i], j, p2[j]);
649                                break;
650                        }
651                        //NSLog(@"Success compare of p1[%d] '%c' and p2[%d] '%c'", i, p1[i], j, p2[j]);
652                        i++;
653                        j++;
654                        break;
655                }
656                if (!areEqual)  // we already have a miss
657                        break;
658        }
659        if (areEqual) { // we are equal up to end of p1. Check that p2 isn't longer
660                while (p2[j] != '\0') {
661                        if ((p2[j] == ' ') || (p2[j] == '-') || (p2[j] == '(') || (p2[j] == ')')) {
662                                //NSLog(@"Skipped p2 char '%c' at index %d, after p1 end", p1[i], i);
663                                j++;
664                                continue;
665                        }
666                        //NSLog(@"Extra character(s) for p2 starting at index %d: '%c'", j, p2[j]);
667                        areEqual = FALSE;
668                        break;
669                }
670        }
671        //NSLog(@"Compared %@ and %@, result: %d", aPhone, otherPhone, (int)areEqual);
672        return areEqual;
673}
674
675#pragma mark ---- cleanup ----
676
677- (void)dealloc {
678        CFRelease(abRecord);
679        [naptrSelectors release];
680        [txtSelectors release];
681        [super dealloc];
682}
683
684@end
Note: See TracBrowser for help on using the browser.
Telnic
Search This Site
Partners
Neustar
ICANN
Main site | WHOIS | Sell .tel | FAQ | Archived Site | About Telnic | Contact Us