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