1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_connectivity.hxx" 26 27 #include "MacabRecords.hxx" 28 #include "MacabRecord.hxx" 29 #include "MacabHeader.hxx" 30 #include "macabutilities.hxx" 31 32 #include <premac.h> 33 #include <Carbon/Carbon.h> 34 #include <AddressBook/ABAddressBookC.h> 35 #include <postmac.h> 36 #include <com/sun/star/util/DateTime.hpp> 37 38 using namespace connectivity::macab; 39 using namespace com::sun::star::util; 40 41 // ------------------------------------------------------------------------- 42 MacabRecords::MacabRecords(const ABAddressBookRef _addressBook, MacabHeader *_header, MacabRecord **_records, sal_Int32 _numRecords) 43 { 44 /* Variables passed in... */ 45 header = _header; 46 recordsSize = _numRecords; 47 currentRecord = _numRecords; 48 records = _records; 49 addressBook = _addressBook; 50 51 /* Default variables... */ 52 recordType = kABPersonRecordType; 53 54 /* Variables constructed... */ 55 bootstrap_CF_types(); 56 bootstrap_requiredProperties(); 57 } 58 59 // ------------------------------------------------------------------------- 60 /* Creates a MacabRecords from another: copies the length, name, and 61 * address book of the original, but the header or the records themselves. 62 * The idea is that the only reason to copy a MacabRecords is to create 63 * a filtered version of it, which can have the same length (to avoid 64 * resizing) and will work from the same base addressbook, but might have 65 * entirey different values and even (possibly in the future) a different 66 * header. 67 */ 68 MacabRecords::MacabRecords(const MacabRecords *_copy) 69 { 70 /* Variables passed in... */ 71 recordsSize = _copy->recordsSize; 72 addressBook = _copy->addressBook; 73 m_sName = _copy->m_sName; 74 75 /* Default variables... */ 76 currentRecord = 0; 77 header = NULL; 78 records = new MacabRecord *[recordsSize]; 79 recordType = kABPersonRecordType; 80 81 /* Variables constructed... */ 82 bootstrap_CF_types(); 83 bootstrap_requiredProperties(); 84 } 85 86 // ------------------------------------------------------------------------- 87 MacabRecords::MacabRecords(const ABAddressBookRef _addressBook) 88 { 89 /* Variables passed in... */ 90 addressBook = _addressBook; 91 92 /* Default variables... */ 93 recordsSize = 0; 94 currentRecord = 0; 95 records = NULL; 96 header = NULL; 97 recordType = kABPersonRecordType; 98 99 /* Variables constructed... */ 100 bootstrap_CF_types(); 101 bootstrap_requiredProperties(); 102 } 103 104 // ------------------------------------------------------------------------- 105 void MacabRecords::initialize() 106 { 107 108 /* Make sure everything is NULL before initializing. (We usually just 109 * initialize after we use the constructor that takes only a 110 * MacabAddressBook, so these variables will most likely already be 111 * NULL. 112 */ 113 if(records != NULL) 114 { 115 sal_Int32 i; 116 117 for(i = 0; i < recordsSize; i++) 118 delete records[i]; 119 120 delete [] records; 121 } 122 123 if(header != NULL) 124 delete header; 125 126 /* We can handle both default record Address Book record types in 127 * this method, though only kABPersonRecordType is ever used. 128 */ 129 CFArrayRef allRecords; 130 if(CFStringCompare(recordType, kABPersonRecordType, 0) == kCFCompareEqualTo) 131 allRecords = ABCopyArrayOfAllPeople(addressBook); 132 else 133 allRecords = ABCopyArrayOfAllGroups(addressBook); 134 135 ABRecordRef record; 136 sal_Int32 i; 137 recordsSize = (sal_Int32) CFArrayGetCount(allRecords); 138 records = new MacabRecord *[recordsSize]; 139 140 /* First, we create the header... */ 141 header = createHeaderForRecordType(allRecords, recordType); 142 143 /* Then, we create each of the records... */ 144 for(i = 0; i < recordsSize; i++) 145 { 146 record = (ABRecordRef) CFArrayGetValueAtIndex(allRecords, i); 147 records[i] = createMacabRecord(record, header, recordType); 148 } 149 currentRecord = recordsSize; 150 151 CFRelease(allRecords); 152 } 153 154 // ------------------------------------------------------------------------- 155 MacabRecords::~MacabRecords() 156 { 157 } 158 159 // ------------------------------------------------------------------------- 160 void MacabRecords::setHeader(MacabHeader *_header) 161 { 162 if(header != NULL) 163 delete header; 164 header = _header; 165 } 166 167 // ------------------------------------------------------------------------- 168 MacabHeader *MacabRecords::getHeader() const 169 { 170 return header; 171 } 172 173 // ------------------------------------------------------------------------- 174 /* Inserts a MacabRecord at a given location. If there is already a 175 * MacabRecord at that location, return it. 176 */ 177 MacabRecord *MacabRecords::insertRecord(MacabRecord *_newRecord, const sal_Int32 _location) 178 { 179 MacabRecord *oldRecord; 180 181 /* If the location is greater than the current allocated size of this 182 * MacabRecords, allocate more space. 183 */ 184 if(_location >= recordsSize) 185 { 186 sal_Int32 i; 187 MacabRecord **newRecordsArray = new MacabRecord *[_location+1]; 188 for(i = 0; i < recordsSize; i++) 189 { 190 newRecordsArray[i] = records[i]; 191 } 192 delete [] records; 193 records = newRecordsArray; 194 } 195 196 /* Remember: currentRecord refers to one above the highest existing 197 * record (i.e., it refers to where to place the next record if a 198 * location is not given). 199 */ 200 if(_location >= currentRecord) 201 currentRecord = _location+1; 202 203 oldRecord = records[_location]; 204 records[_location] = _newRecord; 205 return oldRecord; 206 } 207 208 // ------------------------------------------------------------------------- 209 /* Insert a record at the next available place. */ 210 void MacabRecords::insertRecord(MacabRecord *_newRecord) 211 { 212 insertRecord(_newRecord, currentRecord); 213 } 214 215 // ------------------------------------------------------------------------- 216 MacabRecord *MacabRecords::getRecord(const sal_Int32 _location) const 217 { 218 if(_location >= recordsSize) 219 return NULL; 220 return records[_location]; 221 } 222 223 // ------------------------------------------------------------------------- 224 macabfield *MacabRecords::getField(const sal_Int32 _recordNumber, const sal_Int32 _columnNumber) const 225 { 226 if(_recordNumber >= recordsSize) 227 return NULL; 228 229 MacabRecord *record = records[_recordNumber]; 230 231 if(_columnNumber < 0 || _columnNumber >= record->getSize()) 232 return NULL; 233 234 return record->get(_columnNumber); 235 } 236 237 // ------------------------------------------------------------------------- 238 macabfield *MacabRecords::getField(const sal_Int32 _recordNumber, const ::rtl::OUString _columnName) const 239 { 240 if(header != NULL) 241 { 242 sal_Int32 columnNumber = header->getColumnNumber(_columnName); 243 if(columnNumber == -1) 244 return NULL; 245 246 return getField(_recordNumber, columnNumber); 247 } 248 else 249 { 250 // error: shouldn't access field with null header! 251 return NULL; 252 } 253 } 254 255 // ------------------------------------------------------------------------- 256 sal_Int32 MacabRecords::getFieldNumber(const ::rtl::OUString _columnName) const 257 { 258 if(header != NULL) 259 return header->getColumnNumber(_columnName); 260 else 261 // error: shouldn't access field with null header! 262 return -1; 263 } 264 265 // ------------------------------------------------------------------------- 266 /* Create the lcl_CFTypes array -- we need this because there is no 267 * way to get the ABType of an object from the object itself, and the 268 * function ABTypeOfProperty can't handle multiple levels of data 269 * (e.g., it can tell us that "address" is of type 270 * kABDictionaryProperty, but it cannot tell us that all of the keys 271 * and values in the dictionary have type kABStringProperty. On the 272 * other hand, we _can_ get the CFType out of any object. 273 * Unfortunately, all information about CFTypeIDs comes with the 274 * warning that they change between releases, so we build them 275 * ourselves here. (The one that we can't build is for multivalues, 276 * e.g., kABMultiStringProperty. All of these appear to have the 277 * same type: 1, but there is no function that I've found to give 278 * us that dynamically in case that number ever changes. 279 */ 280 void MacabRecords::bootstrap_CF_types() 281 { 282 lcl_CFTypesLength = 6; 283 lcl_CFTypes = new lcl_CFType[lcl_CFTypesLength]; 284 285 lcl_CFTypes[0].cf = CFNumberGetTypeID(); 286 lcl_CFTypes[0].ab = kABIntegerProperty; 287 288 lcl_CFTypes[1].cf = CFStringGetTypeID(); 289 lcl_CFTypes[1].ab = kABStringProperty; 290 291 lcl_CFTypes[2].cf = CFDateGetTypeID(); 292 lcl_CFTypes[2].ab = kABDateProperty; 293 294 lcl_CFTypes[3].cf = CFArrayGetTypeID(); 295 lcl_CFTypes[3].ab = kABArrayProperty; 296 297 lcl_CFTypes[4].cf = CFDictionaryGetTypeID(); 298 lcl_CFTypes[4].ab = kABDictionaryProperty; 299 300 lcl_CFTypes[5].cf = CFDataGetTypeID(); 301 lcl_CFTypes[5].ab = kABDataProperty; 302 } 303 304 // ------------------------------------------------------------------------- 305 /* This is based on the possible fields required in the mail merge template 306 * in sw. If the fields possible there change, it would be optimal to 307 * change these fields as well. 308 */ 309 void MacabRecords::bootstrap_requiredProperties() 310 { 311 numRequiredProperties = 7; 312 requiredProperties = new CFStringRef[numRequiredProperties]; 313 requiredProperties[0] = kABTitleProperty; 314 requiredProperties[1] = kABFirstNameProperty; 315 requiredProperties[2] = kABLastNameProperty; 316 requiredProperties[3] = kABOrganizationProperty; 317 requiredProperties[4] = kABAddressProperty; 318 requiredProperties[5] = kABPhoneProperty; 319 requiredProperties[6] = kABEmailProperty; 320 } 321 322 // ------------------------------------------------------------------------- 323 /* Create the header for a given record type and a given array of records. 324 * Because the array of records and the record type are given, if you want 325 * to, you can run this method on the members of a group, or on any other 326 * filtered list of people and get a header relevant to them (e.g., if 327 * they only have home addresses, the work address fields won't show up). 328 */ 329 MacabHeader *MacabRecords::createHeaderForRecordType(const CFArrayRef _records, const CFStringRef _recordType) const 330 { 331 /* We have two types of properties for a given record type, nonrequired 332 * and required. Required properties are ones that will show up whether 333 * or not they are empty. Nonrequired properties will only show up if 334 * at least one record in the set has that property filled. The reason 335 * is that some properties, like the kABTitleProperty are required by 336 * the mail merge wizard (in module sw) but are by default not shown in 337 * the Mac OS X address book, so they would be weeded out at this stage 338 * and not shown if they were not required. 339 * 340 * Note: with the addition of required properties, I am not sure that 341 * this method still works for kABGroupRecordType (since the required 342 * properites are all for kABPersonRecordType). 343 * 344 * Note: required properties are constructed in the method 345 * bootstrap_requiredProperties() (above). 346 */ 347 CFArrayRef allProperties = ABCopyArrayOfPropertiesForRecordType(addressBook, _recordType); 348 CFStringRef *nonRequiredProperties; 349 sal_Int32 numRecords = (sal_Int32) CFArrayGetCount(_records); 350 sal_Int32 numProperties = (sal_Int32) CFArrayGetCount(allProperties); 351 sal_Int32 numNonRequiredProperties = numProperties - numRequiredProperties; 352 353 /* While searching through the properties for required properties, these 354 * sal_Bools will keep track of what we have found. 355 */ 356 sal_Bool bFoundProperty; 357 sal_Bool bFoundRequiredProperties[numRequiredProperties]; 358 359 360 /* We have three MacabHeaders: headerDataForProperty is where we 361 * store the result of createHeaderForProperty(), which return a 362 * MacabHeader for a single property. lcl_header is where we store 363 * the MacabHeader that we are constructing. And, nonRequiredHeader 364 * is where we construct the MacabHeader for non-required properties, 365 * so that we can sort them before adding them to lcl_header. 366 */ 367 MacabHeader *headerDataForProperty; 368 MacabHeader *lcl_header = new MacabHeader(); 369 MacabHeader *nonRequiredHeader = new MacabHeader(); 370 371 /* Other variables... */ 372 sal_Int32 i, j, k; 373 ABRecordRef record; 374 CFStringRef property; 375 376 377 /* Allocate and initialize... */ 378 nonRequiredProperties = new CFStringRef[numNonRequiredProperties]; 379 k = 0; 380 for(i = 0; i < numRequiredProperties; i++) 381 bFoundRequiredProperties[i] = sal_False; 382 383 /* Determine the non-required properties... */ 384 for(i = 0; i < numProperties; i++) 385 { 386 property = (CFStringRef) CFArrayGetValueAtIndex(allProperties, i); 387 bFoundProperty = sal_False; 388 for(j = 0; j < numRequiredProperties; j++) 389 { 390 if(CFEqual(property, requiredProperties[j])) 391 { 392 bFoundProperty = sal_True; 393 bFoundRequiredProperties[j] = sal_True; 394 break; 395 } 396 } 397 398 if(bFoundProperty == sal_False) 399 { 400 /* If we have found too many non-required properties */ 401 if(k == numNonRequiredProperties) 402 { 403 k++; // so that the OSL_ENSURE below fails 404 break; 405 } 406 nonRequiredProperties[k] = property; 407 k++; 408 } 409 } 410 411 // Somehow, we got too many or too few non-requird properties... 412 // Most likely, one of the required properties no longer exists, which 413 // we also test later. 414 OSL_ENSURE(k == numNonRequiredProperties, "MacabRecords::createHeaderForRecordType: Found an unexpected number of non-required properties"); 415 416 /* Fill the header with required properties first... */ 417 for(i = 0; i < numRequiredProperties; i++) 418 { 419 if(bFoundRequiredProperties[i] == sal_True) 420 { 421 /* The order of these matters (we want all address properties 422 * before any phone properties, or else things will look weird), 423 * so we get all possibilitities for each property, going through 424 * each record, and then go onto the next property. 425 * (Note: the reason that we have to go through all records 426 * in the first place is that properties like address, phone, and 427 * e-mail are multi-value properties with an unknown number of 428 * values. A user could specify thirteen different kinds of 429 * e-mail addresses for one of her or his contacts, and we need to 430 * get all of them. 431 */ 432 for(j = 0; j < numRecords; j++) 433 { 434 record = (ABRecordRef) CFArrayGetValueAtIndex(_records, j); 435 headerDataForProperty = createHeaderForProperty(record,requiredProperties[i],_recordType,sal_True); 436 if(headerDataForProperty != NULL) 437 { 438 (*lcl_header) += headerDataForProperty; 439 delete headerDataForProperty; 440 } 441 } 442 } 443 else 444 { 445 // Couldn't find a required property... 446 OSL_ENSURE(false, ::rtl::OString("MacabRecords::createHeaderForRecordType: could not find required property: ") + 447 ::rtl::OUStringToOString(CFStringToOUString(requiredProperties[i]), RTL_TEXTENCODING_ASCII_US)); 448 } 449 } 450 451 /* And now, non-required properties... */ 452 for(i = 0; i < numRecords; i++) 453 { 454 record = (ABRecordRef) CFArrayGetValueAtIndex(_records, i); 455 456 for(j = 0; j < numNonRequiredProperties; j++) 457 { 458 property = nonRequiredProperties[j]; 459 headerDataForProperty = createHeaderForProperty(record,property,_recordType,sal_False); 460 if(headerDataForProperty != NULL) 461 { 462 (*nonRequiredHeader) += headerDataForProperty; 463 delete headerDataForProperty; 464 } 465 } 466 467 } 468 nonRequiredHeader->sortRecord(); 469 470 (*lcl_header) += nonRequiredHeader; 471 delete nonRequiredHeader; 472 473 CFRelease(allProperties); 474 delete [] nonRequiredProperties; 475 476 return lcl_header; 477 } 478 479 // ------------------------------------------------------------------------- 480 /* Create a header for a single property. Basically, this method gets 481 * the property's value and type and then calls another method of 482 * the same name to do the dirty work. 483 */ 484 MacabHeader *MacabRecords::createHeaderForProperty(const ABRecordRef _record, const CFStringRef _propertyName, const CFStringRef _recordType, const sal_Bool _isPropertyRequired) const 485 { 486 // local variables 487 CFStringRef localizedPropertyName; 488 CFTypeRef propertyValue; 489 ABPropertyType propertyType; 490 MacabHeader *result; 491 492 /* Get the property's value */ 493 propertyValue = ABRecordCopyValue(_record,_propertyName); 494 if(propertyValue == NULL && _isPropertyRequired == sal_False) 495 return NULL; 496 497 propertyType = ABTypeOfProperty(addressBook, _recordType, _propertyName); 498 localizedPropertyName = ABCopyLocalizedPropertyOrLabel(_propertyName); 499 500 result = createHeaderForProperty(propertyType, propertyValue, localizedPropertyName); 501 502 if(propertyValue != NULL) 503 CFRelease(propertyValue); 504 505 return result; 506 } 507 508 // ------------------------------------------------------------------------- 509 /* Create a header for a single property. This method is recursive 510 * because a single property might contain several sub-properties that 511 * we also want to treat singly. 512 */ 513 MacabHeader *MacabRecords::createHeaderForProperty(const ABPropertyType _propertyType, const CFTypeRef _propertyValue, const CFStringRef _propertyName) const 514 { 515 macabfield **headerNames = NULL; 516 sal_Int32 length = 0; 517 518 switch(_propertyType) 519 { 520 /* Scalars */ 521 case kABStringProperty: 522 case kABRealProperty: 523 case kABIntegerProperty: 524 case kABDateProperty: 525 length = 1; 526 headerNames = new macabfield *[1]; 527 headerNames[0] = new macabfield; 528 headerNames[0]->value = _propertyName; 529 headerNames[0]->type = _propertyType; 530 break; 531 532 /* Multi-scalars */ 533 case kABMultiIntegerProperty: 534 case kABMultiDateProperty: 535 case kABMultiStringProperty: 536 case kABMultiRealProperty: 537 case kABMultiDataProperty: 538 /* For non-scalars, we can only get more information if the property 539 * actually exists. 540 */ 541 if(_propertyValue != NULL) 542 { 543 sal_Int32 i; 544 545 sal_Int32 multiLength = ABMultiValueCount((ABMutableMultiValueRef) _propertyValue); 546 CFStringRef multiLabel, localizedMultiLabel; 547 ::rtl::OUString multiLabelString; 548 ::rtl::OUString multiPropertyString; 549 ::rtl::OUString headerNameString; 550 ABPropertyType multiType = (ABPropertyType) (ABMultiValuePropertyType((ABMutableMultiValueRef) _propertyValue) - 0x100); 551 552 length = multiLength; 553 headerNames = new macabfield *[multiLength]; 554 multiPropertyString = CFStringToOUString(_propertyName); 555 556 /* Go through each element, and - since each element is a scalar - 557 * just create a new macabfield for it. 558 */ 559 for(i = 0; i < multiLength; i++) 560 { 561 multiLabel = ABMultiValueCopyLabelAtIndex((ABMutableMultiValueRef) _propertyValue, i); 562 localizedMultiLabel = ABCopyLocalizedPropertyOrLabel(multiLabel); 563 multiLabelString = CFStringToOUString(localizedMultiLabel); 564 CFRelease(multiLabel); 565 CFRelease(localizedMultiLabel); 566 headerNameString = multiPropertyString + ::rtl::OUString::createFromAscii(": ") + fixLabel(multiLabelString); 567 headerNames[i] = new macabfield; 568 headerNames[i]->value = OUStringToCFString(headerNameString); 569 headerNames[i]->type = multiType; 570 } 571 } 572 break; 573 574 /* Multi-array or dictionary */ 575 case kABMultiArrayProperty: 576 case kABMultiDictionaryProperty: 577 /* For non-scalars, we can only get more information if the property 578 * actually exists. 579 */ 580 if(_propertyValue != NULL) 581 { 582 sal_Int32 i,j,k; 583 584 // Total number of multi-array or multi-dictionary elements. 585 sal_Int32 multiLengthFirstLevel = ABMultiValueCount((ABMutableMultiValueRef) _propertyValue); 586 587 /* Total length, including the length of each element (e.g., if 588 * this multi-dictionary contains three dictionaries, and each 589 * dictionary has four elements, this variable will be twelve, 590 * whereas multiLengthFirstLevel will be three. 591 */ 592 sal_Int32 multiLengthSecondLevel = 0; 593 594 CFStringRef multiLabel, localizedMultiLabel; 595 CFTypeRef multiValue; 596 ::rtl::OUString multiLabelString; 597 ::rtl::OUString multiPropertyString; 598 MacabHeader **multiHeaders = new MacabHeader *[multiLengthFirstLevel]; 599 ABPropertyType multiType = (ABPropertyType) (ABMultiValuePropertyType((ABMutableMultiValueRef) _propertyValue) - 0x100); 600 601 multiPropertyString = CFStringToOUString(_propertyName); 602 603 /* Go through each element - since each element can really 604 * contain anything, we run this method again on each element 605 * and store the resulting MacabHeader (in the multiHeaders 606 * array). Then, all we'll have to do is combine the MacabHeaders 607 * into a single one. 608 */ 609 for(i = 0; i < multiLengthFirstLevel; i++) 610 { 611 /* label */ 612 multiLabel = ABMultiValueCopyLabelAtIndex((ABMutableMultiValueRef) _propertyValue, i); 613 multiValue = ABMultiValueCopyValueAtIndex((ABMutableMultiValueRef) _propertyValue, i); 614 if(multiValue && multiLabel) 615 { 616 localizedMultiLabel = ABCopyLocalizedPropertyOrLabel(multiLabel); 617 multiLabelString = multiPropertyString + ::rtl::OUString::createFromAscii(": ") + fixLabel(CFStringToOUString(localizedMultiLabel)); 618 CFRelease(multiLabel); 619 CFRelease(localizedMultiLabel); 620 multiLabel = OUStringToCFString(multiLabelString); 621 multiHeaders[i] = createHeaderForProperty(multiType, multiValue, multiLabel); 622 if (!multiHeaders[i]) 623 multiHeaders[i] = new MacabHeader(); 624 multiLengthSecondLevel += multiHeaders[i]->getSize(); 625 } 626 else 627 { 628 multiHeaders[i] = new MacabHeader(); 629 } 630 if(multiValue) 631 CFRelease(multiValue); 632 } 633 634 /* We now have enough information to create our final MacabHeader. 635 * We go through each field of each header and add it to the 636 * headerNames array (which is what is used below to construct 637 * the MacabHeader we return). 638 */ 639 length = multiLengthSecondLevel; 640 headerNames = new macabfield *[multiLengthSecondLevel]; 641 642 for(i = 0, j = 0, k = 0; i < multiLengthSecondLevel; i++,k++) 643 { 644 while(multiHeaders[j]->getSize() == k) 645 { 646 j++; 647 k = 0; 648 } 649 650 headerNames[i] = multiHeaders[j]->copy(k); 651 } 652 for(i = 0; i < multiLengthFirstLevel; i++) 653 delete multiHeaders[i]; 654 655 delete [] multiHeaders; 656 } 657 break; 658 659 /* Dictionary */ 660 case kABDictionaryProperty: 661 /* For non-scalars, we can only get more information if the property 662 * actually exists. 663 */ 664 if(_propertyValue != NULL) 665 { 666 /* Assume all keys are strings */ 667 sal_Int32 numRecords = (sal_Int32) CFDictionaryGetCount((CFDictionaryRef) _propertyValue); 668 669 /* The only method for getting info out of a CFDictionary, of both 670 * keys and values, is to all of them all at once, so these 671 * variables will hold them. 672 */ 673 CFStringRef *dictKeys; 674 CFTypeRef *dictValues; 675 676 sal_Int32 i,j,k; 677 ::rtl::OUString dictKeyString, propertyNameString; 678 ABPropertyType dictType; 679 MacabHeader **dictHeaders = new MacabHeader *[numRecords]; 680 ::rtl::OUString dictLabelString; 681 CFStringRef dictLabel, localizedDictKey; 682 683 /* Get the keys and values */ 684 dictKeys = (CFStringRef *) malloc(sizeof(CFStringRef)*numRecords); 685 dictValues = (CFTypeRef *) malloc(sizeof(CFTypeRef)*numRecords); 686 CFDictionaryGetKeysAndValues((CFDictionaryRef) _propertyValue, (const void **) dictKeys, (const void **) dictValues); 687 688 propertyNameString = CFStringToOUString(_propertyName); 689 690 length = 0; 691 /* Go through each element - assuming that the key is a string but 692 * that the value could be anything. Since the value could be 693 * anything, we can't assume that it is scalar (it could even be 694 * another dictionary), so we attempt to get its type using 695 * the method getABTypeFromCFType and then run this method 696 * recursively on that element, storing the MacabHeader that 697 * results. Then, we just combine all of the MacabHeaders into 698 * one. 699 */ 700 for(i = 0; i < numRecords; i++) 701 { 702 dictType = (ABPropertyType) getABTypeFromCFType( CFGetTypeID(dictValues[i]) ); 703 localizedDictKey = ABCopyLocalizedPropertyOrLabel(dictKeys[i]); 704 dictKeyString = CFStringToOUString(localizedDictKey); 705 dictLabelString = propertyNameString + ::rtl::OUString::createFromAscii(": ") + fixLabel(dictKeyString); 706 dictLabel = OUStringToCFString(dictLabelString); 707 dictHeaders[i] = createHeaderForProperty(dictType, dictValues[i], dictLabel); 708 if (!dictHeaders[i]) 709 dictHeaders[i] = new MacabHeader(); 710 length += dictHeaders[i]->getSize(); 711 CFRelease(dictLabel); 712 CFRelease(localizedDictKey); 713 } 714 715 /* Combine all of the macabfields in each MacabHeader into the 716 * headerNames array, which (at the end of this method) is used 717 * to create the MacabHeader that is returned. 718 */ 719 headerNames = new macabfield *[length]; 720 for(i = 0, j = 0, k = 0; i < length; i++,k++) 721 { 722 while(dictHeaders[j]->getSize() == k) 723 { 724 j++; 725 k = 0; 726 } 727 728 headerNames[i] = dictHeaders[j]->copy(k); 729 } 730 731 for(i = 0; i < numRecords; i++) 732 delete dictHeaders[i]; 733 734 delete [] dictHeaders; 735 free(dictKeys); 736 free(dictValues); 737 } 738 break; 739 740 /* Array */ 741 case kABArrayProperty: 742 /* For non-scalars, we can only get more information if the property 743 * actually exists. 744 */ 745 if(_propertyValue != NULL) 746 { 747 sal_Int32 arrLength = (sal_Int32) CFArrayGetCount( (CFArrayRef) _propertyValue); 748 sal_Int32 i,j,k; 749 CFTypeRef arrValue; 750 ABPropertyType arrType; 751 MacabHeader **arrHeaders = new MacabHeader *[arrLength]; 752 ::rtl::OUString propertyNameString = CFStringToOUString(_propertyName); 753 ::rtl::OUString arrLabelString; 754 CFStringRef arrLabel; 755 756 length = 0; 757 /* Go through each element - since the elements here do not have 758 * unique keys like the ones in dictionaries, we create a unique 759 * key out of the id of the element in the array (the first 760 * element gets a 0 plopped onto the end of it, the second a 1... 761 * As with dictionaries, the elements could be anything, including 762 * another array, so we have to run this method recursively on 763 * each element, storing the resulting MacabHeader into an array, 764 * which we then combine into one MacabHeader that is returned. 765 */ 766 for(i = 0; i < arrLength; i++) 767 { 768 arrValue = (CFTypeRef) CFArrayGetValueAtIndex( (CFArrayRef) _propertyValue, i); 769 arrType = (ABPropertyType) getABTypeFromCFType( CFGetTypeID(arrValue) ); 770 arrLabelString = propertyNameString + ::rtl::OUString::valueOf(i); 771 arrLabel = OUStringToCFString(arrLabelString); 772 arrHeaders[i] = createHeaderForProperty(arrType, arrValue, arrLabel); 773 if (!arrHeaders[i]) 774 arrHeaders[i] = new MacabHeader(); 775 length += arrHeaders[i]->getSize(); 776 CFRelease(arrLabel); 777 } 778 779 headerNames = new macabfield *[length]; 780 for(i = 0, j = 0, k = 0; i < length; i++,k++) 781 { 782 while(arrHeaders[j]->getSize() == k) 783 { 784 j++; 785 k = 0; 786 } 787 788 headerNames[i] = arrHeaders[j]->copy(k); 789 } 790 for(i = 0; i < arrLength; i++) 791 delete arrHeaders[i]; 792 793 delete [] arrHeaders; 794 } 795 break; 796 797 default: 798 break; 799 800 } 801 802 /* If we succeeded at adding elements to the headerNames array, then 803 * length will no longer be 0. If it is, create a new MacabHeader 804 * out of the headerNames (after weeding out duplicate headers), and 805 * then return the result. If the length is still 0, return NULL: we 806 * failed to create a MacabHeader out of this property. 807 */ 808 if(length != 0) 809 { 810 manageDuplicateHeaders(headerNames, length); 811 MacabHeader *headerResult = new MacabHeader(length, headerNames); 812 delete [] headerNames; 813 return headerResult; 814 } 815 else 816 return NULL; 817 } 818 819 // ------------------------------------------------------------------------- 820 void MacabRecords::manageDuplicateHeaders(macabfield **_headerNames, const sal_Int32 _length) const 821 { 822 /* If we have two cases of, say, phone: home, this makes it: 823 * phone: home (1) 824 * phone: home (2) 825 */ 826 sal_Int32 i, j; 827 sal_Int32 count; 828 for(i = _length-1; i >= 0; i--) 829 { 830 count = 1; 831 for( j = i-1; j >= 0; j--) 832 { 833 if(CFEqual(_headerNames[i]->value, _headerNames[j]->value)) 834 { 835 count++; 836 } 837 } 838 839 // duplicate! 840 if(count != 1) 841 { 842 // There is probably a better way to do this... 843 ::rtl::OUString newName = CFStringToOUString((CFStringRef) _headerNames[i]->value); 844 CFRelease(_headerNames[i]->value); 845 newName += ::rtl::OUString::createFromAscii(" (") + ::rtl::OUString::valueOf(count) + ::rtl::OUString::createFromAscii(")"); 846 _headerNames[i]->value = OUStringToCFString(newName); 847 } 848 } 849 } 850 851 // ------------------------------------------------------------------------- 852 /* Create a MacabRecord out of an ABRecord, using a given MacabHeader and 853 * the record's type. We go through each property for this record type 854 * then process it much like we processed the header (above), with two 855 * exceptions: if we come upon something not in the header, we ignore it 856 * (it's something we don't want to add), and once we find a corresponding 857 * location in the header, we store the property and the property type in 858 * a macabfield. (For the header, we stored the property type and the name 859 * of the property as a CFString.) 860 */ 861 MacabRecord *MacabRecords::createMacabRecord(const ABRecordRef _abrecord, const MacabHeader *_header, const CFStringRef _recordType) const 862 { 863 /* The new record that we will create... */ 864 MacabRecord *macabRecord = new MacabRecord(_header->getSize()); 865 866 CFArrayRef recordProperties = ABCopyArrayOfPropertiesForRecordType(addressBook, _recordType); 867 sal_Int32 numProperties = (sal_Int32) CFArrayGetCount(recordProperties); 868 869 sal_Int32 i; 870 871 CFTypeRef propertyValue; 872 ABPropertyType propertyType; 873 874 CFStringRef propertyName, localizedPropertyName; 875 ::rtl::OUString propertyNameString; 876 for(i = 0; i < numProperties; i++) 877 { 878 propertyName = (CFStringRef) CFArrayGetValueAtIndex(recordProperties, i); 879 localizedPropertyName = ABCopyLocalizedPropertyOrLabel(propertyName); 880 propertyNameString = CFStringToOUString(localizedPropertyName); 881 CFRelease(localizedPropertyName); 882 883 /* Get the property's value */ 884 propertyValue = ABRecordCopyValue(_abrecord,propertyName); 885 if(propertyValue != NULL) 886 { 887 propertyType = ABTypeOfProperty(addressBook, _recordType, propertyName); 888 if(propertyType != kABErrorInProperty) 889 insertPropertyIntoMacabRecord(propertyType, macabRecord, _header, propertyNameString, propertyValue); 890 891 CFRelease(propertyValue); 892 } 893 } 894 CFRelease(recordProperties); 895 return macabRecord; 896 } 897 898 // ------------------------------------------------------------------------- 899 /* Inserts a given property into a MacabRecord. This method calls another 900 * method by the same name after getting the property type (it only 901 * receives the property value). It is called when we aren't given the 902 * property's type already. 903 */ 904 void MacabRecords::insertPropertyIntoMacabRecord(MacabRecord *_abrecord, const MacabHeader *_header, const ::rtl::OUString _propertyName, const CFTypeRef _propertyValue) const 905 { 906 CFTypeID cf_type = CFGetTypeID(_propertyValue); 907 ABPropertyType ab_type = getABTypeFromCFType( cf_type ); 908 909 if(ab_type != kABErrorInProperty) 910 insertPropertyIntoMacabRecord(ab_type, _abrecord, _header, _propertyName, _propertyValue); 911 } 912 913 // ------------------------------------------------------------------------- 914 /* Inserts a given property into a MacabRecord. This method is recursive 915 * because properties can contain many sub-properties. 916 */ 917 void MacabRecords::insertPropertyIntoMacabRecord(const ABPropertyType _propertyType, MacabRecord *_abrecord, const MacabHeader *_header, const ::rtl::OUString _propertyName, const CFTypeRef _propertyValue) const 918 { 919 /* If there is no value, return */ 920 if(_propertyValue == NULL) 921 return; 922 923 /* The main switch statement */ 924 switch(_propertyType) 925 { 926 /* Scalars */ 927 case kABStringProperty: 928 case kABRealProperty: 929 case kABIntegerProperty: 930 case kABDateProperty: 931 { 932 /* Only scalars actually insert a property into the MacabRecord. 933 * In all other cases, this method is called recursively until a 934 * scalar type, an error, or an unknown type are found. 935 * Because of that, the following checks only occur for this type. 936 * We store whether we have successfully placed this property 937 * into the MacabRecord (or whether an unrecoverable error occured). 938 * Then, we try over and over again to place the property into the 939 * record. There are three possible results: 940 * 1) Success! 941 * 2) There is already a property stored at the column of this name, 942 * in which case we have a duplicate header (see the method 943 * manageDuplicateHeaders()). If that is the case, we add an ID 944 * to the end of the column name in the same format as we do in 945 * manageDuplicateHeaders() and try again. 946 * 3) No column of this name exists in the header. In this case, 947 * there is nothing we can do: we have failed to place this 948 * property into the record. 949 */ 950 sal_Bool bPlaced = sal_False; 951 ::rtl::OUString columnName = ::rtl::OUString(_propertyName); 952 sal_Int32 i = 1; 953 954 // A big safeguard to prevent two fields from having the same name. 955 while(bPlaced != sal_True) 956 { 957 sal_Int32 columnNumber = _header->getColumnNumber(columnName); 958 bPlaced = sal_True; 959 if(columnNumber != -1) 960 { 961 // collision! A property already exists here! 962 if(_abrecord->get(columnNumber) != NULL) 963 { 964 bPlaced = sal_False; 965 i++; 966 columnName = ::rtl::OUString(_propertyName) + ::rtl::OUString::createFromAscii(" (") + ::rtl::OUString::valueOf(i) + ::rtl::OUString::createFromAscii(")"); 967 } 968 969 // success! 970 else 971 { 972 _abrecord->insertAtColumn(_propertyValue, _propertyType, columnNumber); 973 } 974 } 975 } 976 } 977 break; 978 979 /* Array */ 980 case kABArrayProperty: 981 { 982 /* An array is basically just a list of anything, so all we do 983 * is go through the array, and rerun this method recursively 984 * on each element. 985 */ 986 sal_Int32 arrLength = (sal_Int32) CFArrayGetCount( (CFArrayRef) _propertyValue); 987 sal_Int32 i; 988 const void *arrValue; 989 ::rtl::OUString newPropertyName; 990 991 /* Going through each element... */ 992 for(i = 0; i < arrLength; i++) 993 { 994 arrValue = CFArrayGetValueAtIndex( (CFArrayRef) _propertyValue, i); 995 newPropertyName = _propertyName + ::rtl::OUString::valueOf(i); 996 insertPropertyIntoMacabRecord(_abrecord, _header, newPropertyName, arrValue); 997 CFRelease(arrValue); 998 } 999 1000 } 1001 break; 1002 1003 /* Dictionary */ 1004 case kABDictionaryProperty: 1005 { 1006 /* A dictionary is basically a hashmap. Technically, it can 1007 * hold any object as a key and any object as a value. 1008 * For our case, we assume that the key is a string (so that 1009 * we can use the key to get the column name and match it against 1010 * the header), but we don't assume anything about the value, so 1011 * we run this method recursively (or, rather, we run the version 1012 * of this method for when we don't know the object's type) until 1013 * we hit a scalar value. 1014 */ 1015 1016 sal_Int32 numRecords = (sal_Int32) CFDictionaryGetCount((CFDictionaryRef) _propertyValue); 1017 ::rtl::OUString dictKeyString; 1018 sal_Int32 i; 1019 ::rtl::OUString newPropertyName; 1020 1021 /* Unfortunately, the only way to get both keys and values out 1022 * of a dictionary in Carbon is to get them all at once, so we 1023 * do that. 1024 */ 1025 CFStringRef *dictKeys; 1026 CFStringRef localizedDictKey; 1027 CFTypeRef *dictValues; 1028 dictKeys = (CFStringRef *) malloc(sizeof(CFStringRef)*numRecords); 1029 dictValues = (CFTypeRef *) malloc(sizeof(CFTypeRef)*numRecords); 1030 CFDictionaryGetKeysAndValues((CFDictionaryRef) _propertyValue, (const void **) dictKeys, (const void **) dictValues); 1031 1032 /* Going through each element... */ 1033 for(i = 0; i < numRecords; i++) 1034 { 1035 localizedDictKey = ABCopyLocalizedPropertyOrLabel(dictKeys[i]); 1036 dictKeyString = CFStringToOUString(localizedDictKey); 1037 CFRelease(localizedDictKey); 1038 newPropertyName = _propertyName + ::rtl::OUString::createFromAscii(": ") + fixLabel(dictKeyString); 1039 insertPropertyIntoMacabRecord(_abrecord, _header, newPropertyName, dictValues[i]); 1040 } 1041 1042 free(dictKeys); 1043 free(dictValues); 1044 } 1045 break; 1046 1047 /* Multivalue */ 1048 case kABMultiIntegerProperty: 1049 case kABMultiDateProperty: 1050 case kABMultiStringProperty: 1051 case kABMultiRealProperty: 1052 case kABMultiDataProperty: 1053 case kABMultiDictionaryProperty: 1054 case kABMultiArrayProperty: 1055 { 1056 /* All scalar multivalues are handled in the same way. Each element 1057 * is a label and a value. All labels are strings 1058 * (kABStringProperty), and all values have the same type 1059 * (which is the type of the multivalue minus 255, or as 1060 * Carbon's list of property types has it, minus 0x100. 1061 * We just get the correct type, then go through each element 1062 * and get the label and value and print them in a list. 1063 */ 1064 1065 sal_Int32 i; 1066 sal_Int32 multiLength = ABMultiValueCount((ABMutableMultiValueRef) _propertyValue); 1067 CFStringRef multiLabel, localizedMultiLabel; 1068 CFTypeRef multiValue; 1069 ::rtl::OUString multiLabelString, newPropertyName; 1070 ABPropertyType multiType = (ABPropertyType) (ABMultiValuePropertyType((ABMutableMultiValueRef) _propertyValue) - 0x100); 1071 1072 /* Go through each element... */ 1073 for(i = 0; i < multiLength; i++) 1074 { 1075 /* Label and value */ 1076 multiLabel = ABMultiValueCopyLabelAtIndex((ABMutableMultiValueRef) _propertyValue, i); 1077 multiValue = ABMultiValueCopyValueAtIndex((ABMutableMultiValueRef) _propertyValue, i); 1078 1079 localizedMultiLabel = ABCopyLocalizedPropertyOrLabel(multiLabel); 1080 multiLabelString = CFStringToOUString(localizedMultiLabel); 1081 newPropertyName = _propertyName + ::rtl::OUString::createFromAscii(": ") + fixLabel(multiLabelString); 1082 insertPropertyIntoMacabRecord(multiType, _abrecord, _header, newPropertyName, multiValue); 1083 1084 /* free our variables */ 1085 CFRelease(multiLabel); 1086 CFRelease(localizedMultiLabel); 1087 CFRelease(multiValue); 1088 } 1089 } 1090 break; 1091 1092 /* Unhandled types */ 1093 case kABErrorInProperty: 1094 case kABDataProperty: 1095 default: 1096 /* An error, as far as I have seen, only shows up as a type 1097 * returned by a function for dictionaries when the dictionary 1098 * holds many types of values. Since we do not use that function, 1099 * it shouldn't come up. I have yet to see the kABDataProperty, 1100 * and I am not sure how to represent it as a string anyway, 1101 * since it appears to just be a bunch of bytes. Assumably, if 1102 * these bytes made up a string, the type would be 1103 * kABStringProperty. I think that this is used when we are not 1104 * sure what the type is (e.g., it could be a string or a number). 1105 * That being the case, I still don't know how to represent it. 1106 * And, default should never come up, since we've exhausted all 1107 * of the possible types for ABPropertyType, but... just in case. 1108 */ 1109 break; 1110 } 1111 1112 } 1113 1114 // ------------------------------------------------------------------------- 1115 ABPropertyType MacabRecords::getABTypeFromCFType(const CFTypeID cf_type ) const 1116 { 1117 sal_Int32 i; 1118 for(i = 0; i < lcl_CFTypesLength; i++) 1119 { 1120 /* A match! */ 1121 if(lcl_CFTypes[i].cf == (sal_Int32) cf_type) 1122 { 1123 return (ABPropertyType) lcl_CFTypes[i].ab; 1124 } 1125 } 1126 return kABErrorInProperty; 1127 } 1128 1129 // ------------------------------------------------------------------------- 1130 sal_Int32 MacabRecords::size() const 1131 { 1132 return currentRecord; 1133 } 1134 1135 // ------------------------------------------------------------------------- 1136 MacabRecords *MacabRecords::begin() 1137 { 1138 return this; 1139 } 1140 1141 // ------------------------------------------------------------------------- 1142 MacabRecords::iterator::iterator () 1143 { 1144 } 1145 1146 // ------------------------------------------------------------------------- 1147 MacabRecords::iterator::~iterator () 1148 { 1149 } 1150 1151 // ------------------------------------------------------------------------- 1152 void MacabRecords::iterator::operator= (MacabRecords *_records) 1153 { 1154 id = 0; 1155 records = _records; 1156 } 1157 1158 // ------------------------------------------------------------------------- 1159 void MacabRecords::iterator::operator++ () 1160 { 1161 id++; 1162 } 1163 1164 // ------------------------------------------------------------------------- 1165 sal_Bool MacabRecords::iterator::operator!= (const sal_Int32 i) const 1166 { 1167 return(id != i); 1168 } 1169 1170 // ------------------------------------------------------------------------- 1171 sal_Bool MacabRecords::iterator::operator== (const sal_Int32 i) const 1172 { 1173 return(id == i); 1174 } 1175 1176 // ------------------------------------------------------------------------- 1177 MacabRecord *MacabRecords::iterator::operator* () const 1178 { 1179 return records->getRecord(id); 1180 } 1181 1182 // ------------------------------------------------------------------------- 1183 sal_Int32 MacabRecords::end() const 1184 { 1185 return currentRecord; 1186 } 1187 1188 // ------------------------------------------------------------------------- 1189 void MacabRecords::swap(const sal_Int32 _id1, const sal_Int32 _id2) 1190 { 1191 MacabRecord *swapRecord = records[_id1]; 1192 1193 records[_id1] = records[_id2]; 1194 records[_id2] = swapRecord; 1195 } 1196 1197 // ------------------------------------------------------------------------- 1198 void MacabRecords::setName(const ::rtl::OUString _sName) 1199 { 1200 m_sName = _sName; 1201 } 1202 1203 // ------------------------------------------------------------------------- 1204 ::rtl::OUString MacabRecords::getName() const 1205 { 1206 return m_sName; 1207 } 1208 1209