1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: MacabRecords.cxx,v $
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"
40 #include <Carbon/Carbon.h>
41 #include <AddressBook/ABAddressBookC.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... */
53 recordsSize
= _numRecords
;
54 currentRecord
= _numRecords
;
56 addressBook
= _addressBook
;
58 /* Default variables... */
59 recordType
= kABPersonRecordType
;
61 /* Variables constructed... */
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
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... */
85 records
= new MacabRecord
*[recordsSize
];
86 recordType
= kABPersonRecordType
;
88 /* Variables constructed... */
90 bootstrap_requiredProperties();
93 // -------------------------------------------------------------------------
94 MacabRecords::MacabRecords(const ABAddressBookRef _addressBook
)
96 /* Variables passed in... */
97 addressBook
= _addressBook
;
99 /* Default variables... */
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
124 for(i
= 0; i
< recordsSize
; i
++)
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
);
140 allRecords
= ABCopyArrayOfAllGroups(addressBook
);
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
)
174 // -------------------------------------------------------------------------
175 MacabHeader
*MacabRecords::getHeader() const
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
)
194 MacabRecord
**newRecordsArray
= new MacabRecord
*[_location
+1];
195 for(i
= 0; i
< recordsSize
; i
++)
197 newRecordsArray
[i
] = records
[i
];
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
;
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
)
227 return records
[_location
];
230 // -------------------------------------------------------------------------
231 macabfield
*MacabRecords::getField(const sal_Int32 _recordNumber
, const sal_Int32 _columnNumber
) const
233 if(_recordNumber
>= recordsSize
)
236 MacabRecord
*record
= records
[_recordNumber
];
238 if(_columnNumber
< 0 || _columnNumber
>= record
->getSize())
241 return record
->get(_columnNumber
);
244 // -------------------------------------------------------------------------
245 macabfield
*MacabRecords::getField(const sal_Int32 _recordNumber
, const ::rtl::OUString _columnName
) const
249 sal_Int32 columnNumber
= header
->getColumnNumber(_columnName
);
250 if(columnNumber
== -1)
253 return getField(_recordNumber
, columnNumber
);
257 // error: shouldn't access field with null header!
262 // -------------------------------------------------------------------------
263 sal_Int32
MacabRecords::getFieldNumber(const ::rtl::OUString _columnName
) const
266 return header
->getColumnNumber(_columnName
);
268 // error: shouldn't access field with null header!
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... */
381 CFStringRef property
;
384 /* Allocate and initialize... */
385 nonRequiredProperties
= new CFStringRef
[numNonRequiredProperties
];
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
;
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
413 nonRequiredProperties
[k
] = property
;
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
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
;
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
;
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
494 CFStringRef localizedPropertyName
;
495 CFTypeRef propertyValue
;
496 ABPropertyType propertyType
;
499 /* Get the property's value */
500 propertyValue
= ABRecordCopyValue(_record
,_propertyName
);
501 if(propertyValue
== NULL
&& _isPropertyRequired
== sal_False
)
504 propertyType
= ABTypeOfProperty(addressBook
, _recordType
, _propertyName
);
505 localizedPropertyName
= ABCopyLocalizedPropertyOrLabel(_propertyName
);
507 result
= createHeaderForProperty(propertyType
, propertyValue
, localizedPropertyName
);
509 if(propertyValue
!= NULL
)
510 CFRelease(propertyValue
);
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
)
528 case kABStringProperty
:
529 case kABRealProperty
:
530 case kABIntegerProperty
:
531 case kABDateProperty
:
533 headerNames
= new macabfield
*[1];
534 headerNames
[0] = new macabfield
;
535 headerNames
[0]->value
= _propertyName
;
536 headerNames
[0]->type
= _propertyType
;
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
548 if(_propertyValue
!= NULL
)
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
;
581 /* Multi-array or dictionary */
582 case kABMultiArrayProperty
:
583 case kABMultiDictionaryProperty
:
584 /* For non-scalars, we can only get more information if the property
587 if(_propertyValue
!= NULL
)
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
616 for(i
= 0; i
< multiLengthFirstLevel
; i
++)
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();
635 multiHeaders
[i
] = new MacabHeader();
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
)
657 headerNames
[i
] = multiHeaders
[j
]->copy(k
);
659 for(i
= 0; i
< multiLengthFirstLevel
; i
++)
660 delete multiHeaders
[i
];
662 delete [] multiHeaders
;
667 case kABDictionaryProperty
:
668 /* For non-scalars, we can only get more information if the property
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
;
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
);
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
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
);
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
)
735 headerNames
[i
] = dictHeaders
[j
]->copy(k
);
738 for(i
= 0; i
< numRecords
; i
++)
739 delete dictHeaders
[i
];
741 delete [] dictHeaders
;
748 case kABArrayProperty
:
749 /* For non-scalars, we can only get more information if the property
752 if(_propertyValue
!= NULL
)
754 sal_Int32 arrLength
= (sal_Int32
) CFArrayGetCount( (CFArrayRef
) _propertyValue
);
757 ABPropertyType arrType
;
758 MacabHeader
**arrHeaders
= new MacabHeader
*[arrLength
];
759 ::rtl::OUString propertyNameString
= CFStringToOUString(_propertyName
);
760 ::rtl::OUString arrLabelString
;
761 CFStringRef arrLabel
;
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
);
781 arrHeaders
[i
] = new MacabHeader();
782 length
+= arrHeaders
[i
]->getSize();
786 headerNames
= new macabfield
*[length
];
787 for(i
= 0, j
= 0, k
= 0; i
< length
; i
++,k
++)
789 while(arrHeaders
[j
]->getSize() == k
)
795 headerNames
[i
] = arrHeaders
[j
]->copy(k
);
797 for(i
= 0; i
< arrLength
; i
++)
798 delete arrHeaders
[i
];
800 delete [] arrHeaders
;
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.
817 manageDuplicateHeaders(headerNames
, length
);
818 MacabHeader
*headerResult
= new MacabHeader(length
, headerNames
);
819 delete [] headerNames
;
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:
835 for(i
= _length
-1; i
>= 0; i
--)
838 for( j
= i
-1; j
>= 0; j
--)
840 if(CFEqual(_headerNames
[i
]->value
, _headerNames
[j
]->value
))
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
);
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
);
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
)
930 /* The main switch statement */
931 switch(_propertyType
)
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:
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
);
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
);
966 if(columnNumber
!= -1)
968 // collision! A property already exists here!
969 if(_abrecord
->get(columnNumber
) != NULL
)
973 columnName
= ::rtl::OUString(_propertyName
) + ::rtl::OUString::createFromAscii(" (") + ::rtl::OUString::valueOf(i
) + ::rtl::OUString::createFromAscii(")");
979 _abrecord
->insertAtColumn(_propertyValue
, _propertyType
, columnNumber
);
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
993 sal_Int32 arrLength
= (sal_Int32
) CFArrayGetCount( (CFArrayRef
) _propertyValue
);
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
);
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
;
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
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
]);
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.
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
);
1099 /* Unhandled types */
1100 case kABErrorInProperty
:
1101 case kABDataProperty
:
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.
1121 // -------------------------------------------------------------------------
1122 ABPropertyType
MacabRecords::getABTypeFromCFType(const CFTypeID cf_type
) const
1125 for(i
= 0; i
< lcl_CFTypesLength
; i
++)
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()
1148 // -------------------------------------------------------------------------
1149 MacabRecords::iterator::iterator ()
1153 // -------------------------------------------------------------------------
1154 MacabRecords::iterator::~iterator ()
1158 // -------------------------------------------------------------------------
1159 void MacabRecords::iterator::operator= (MacabRecords
*_records
)
1165 // -------------------------------------------------------------------------
1166 void MacabRecords::iterator::operator++ ()
1171 // -------------------------------------------------------------------------
1172 sal_Bool
MacabRecords::iterator::operator!= (const sal_Int32 i
) const
1177 // -------------------------------------------------------------------------
1178 sal_Bool
MacabRecords::iterator::operator== (const sal_Int32 i
) const
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
)
1210 // -------------------------------------------------------------------------
1211 ::rtl::OUString
MacabRecords::getName() const