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