1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "MacabRecords.hxx"
22 #include "MacabRecord.hxx"
23 #include "MacabHeader.hxx"
24 #include "macabutilities.hxx"
27 #include <Carbon/Carbon.h>
28 #include <AddressBook/ABAddressBookC.h>
30 #include <com/sun/star/util/DateTime.hpp>
32 using namespace connectivity::macab
;
33 using namespace com::sun::star::util
;
37 void manageDuplicateHeaders(macabfield
**_headerNames
, const sal_Int32 _length
)
39 /* If we have two cases of, say, phone: home, this makes it:
45 for(i
= _length
-1; i
>= 0; i
--)
48 for( j
= i
-1; j
>= 0; j
--)
50 if(CFEqual(_headerNames
[i
]->value
, _headerNames
[j
]->value
))
59 // There is probably a better way to do this...
60 OUString newName
= CFStringToOUString(static_cast<CFStringRef
>(_headerNames
[i
]->value
));
61 CFRelease(_headerNames
[i
]->value
);
62 newName
+= " (" + OUString::number(count
) + ")";
63 _headerNames
[i
]->value
= OUStringToCFString(newName
);
70 MacabRecords::MacabRecords(const ABAddressBookRef _addressBook
, MacabHeader
*_header
, MacabRecord
**_records
, sal_Int32 _numRecords
)
72 /* Variables passed in... */
74 recordsSize
= _numRecords
;
75 currentRecord
= _numRecords
;
77 addressBook
= _addressBook
;
79 /* Default variables... */
80 recordType
= kABPersonRecordType
;
82 /* Variables constructed... */
84 bootstrap_requiredProperties();
88 /* Creates a MacabRecords from another: copies the length, name, and
89 * address book of the original, but the header or the records themselves.
90 * The idea is that the only reason to copy a MacabRecords is to create
91 * a filtered version of it, which can have the same length (to avoid
92 * resizing) and will work from the same base addressbook, but might have
93 * entirey different values and even (possibly in the future) a different
96 MacabRecords::MacabRecords(const MacabRecords
*_copy
)
98 /* Variables passed in... */
99 recordsSize
= _copy
->recordsSize
;
100 addressBook
= _copy
->addressBook
;
101 m_sName
= _copy
->m_sName
;
103 /* Default variables... */
106 records
= new MacabRecord
*[recordsSize
];
107 recordType
= kABPersonRecordType
;
109 /* Variables constructed... */
110 bootstrap_CF_types();
111 bootstrap_requiredProperties();
115 MacabRecords::MacabRecords(const ABAddressBookRef _addressBook
)
117 /* Variables passed in... */
118 addressBook
= _addressBook
;
120 /* Default variables... */
125 recordType
= kABPersonRecordType
;
127 /* Variables constructed... */
128 bootstrap_CF_types();
129 bootstrap_requiredProperties();
133 void MacabRecords::initialize()
136 /* Make sure everything is NULL before initializing. (We usually just
137 * initialize after we use the constructor that takes only a
138 * MacabAddressBook, so these variables will most likely already be
145 for(i
= 0; i
< recordsSize
; i
++)
154 /* We can handle both default record Address Book record types in
155 * this method, though only kABPersonRecordType is ever used.
157 CFArrayRef allRecords
;
158 if(CFStringCompare(recordType
, kABPersonRecordType
, 0) == kCFCompareEqualTo
)
159 allRecords
= ABCopyArrayOfAllPeople(addressBook
);
161 allRecords
= ABCopyArrayOfAllGroups(addressBook
);
165 recordsSize
= (sal_Int32
) CFArrayGetCount(allRecords
);
166 records
= new MacabRecord
*[recordsSize
];
168 /* First, we create the header... */
169 header
= createHeaderForRecordType(allRecords
, recordType
);
171 /* Then, we create each of the records... */
172 for(i
= 0; i
< recordsSize
; i
++)
174 record
= const_cast<ABRecordRef
>(CFArrayGetValueAtIndex(allRecords
, i
));
175 records
[i
] = createMacabRecord(record
, header
, recordType
);
177 currentRecord
= recordsSize
;
179 CFRelease(allRecords
);
183 MacabRecords::~MacabRecords()
188 void MacabRecords::setHeader(MacabHeader
*_header
)
196 MacabHeader
*MacabRecords::getHeader() const
202 /* Inserts a MacabRecord at a given location. If there is already a
203 * MacabRecord at that location, return it.
205 MacabRecord
*MacabRecords::insertRecord(MacabRecord
*_newRecord
, const sal_Int32 _location
)
207 MacabRecord
*oldRecord
;
209 /* If the location is greater than the current allocated size of this
210 * MacabRecords, allocate more space.
212 if(_location
>= recordsSize
)
215 MacabRecord
**newRecordsArray
= new MacabRecord
*[_location
+1];
216 for(i
= 0; i
< recordsSize
; i
++)
218 newRecordsArray
[i
] = records
[i
];
221 records
= newRecordsArray
;
224 /* Remember: currentRecord refers to one above the highest existing
225 * record (i.e., it refers to where to place the next record if a
226 * location is not given).
228 if(_location
>= currentRecord
)
229 currentRecord
= _location
+1;
231 oldRecord
= records
[_location
];
232 records
[_location
] = _newRecord
;
237 /* Insert a record at the next available place. */
238 void MacabRecords::insertRecord(MacabRecord
*_newRecord
)
240 insertRecord(_newRecord
, currentRecord
);
244 MacabRecord
*MacabRecords::getRecord(const sal_Int32 _location
) const
246 if(_location
>= recordsSize
)
248 return records
[_location
];
252 macabfield
*MacabRecords::getField(const sal_Int32 _recordNumber
, const sal_Int32 _columnNumber
) const
254 if(_recordNumber
>= recordsSize
)
257 MacabRecord
*record
= records
[_recordNumber
];
259 if(_columnNumber
< 0 || _columnNumber
>= record
->getSize())
262 return record
->get(_columnNumber
);
266 macabfield
*MacabRecords::getField(const sal_Int32 _recordNumber
, const OUString
& _columnName
) const
270 sal_Int32 columnNumber
= header
->getColumnNumber(_columnName
);
271 if(columnNumber
== -1)
274 return getField(_recordNumber
, columnNumber
);
278 // error: shouldn't access field with null header!
284 sal_Int32
MacabRecords::getFieldNumber(const OUString
& _columnName
) const
287 return header
->getColumnNumber(_columnName
);
289 // error: shouldn't access field with null header!
294 /* Create the lcl_CFTypes array -- we need this because there is no
295 * way to get the ABType of an object from the object itself, and the
296 * function ABTypeOfProperty can't handle multiple levels of data
297 * (e.g., it can tell us that "address" is of type
298 * kABDictionaryProperty, but it cannot tell us that all of the keys
299 * and values in the dictionary have type kABStringProperty. On the
300 * other hand, we _can_ get the CFType out of any object.
301 * Unfortunately, all information about CFTypeIDs comes with the
302 * warning that they change between releases, so we build them
303 * ourselves here. (The one that we can't build is for multivalues,
304 * e.g., kABMultiStringProperty. All of these appear to have the
305 * same type: 1, but there is no function that I've found to give
306 * us that dynamically in case that number ever changes.
308 void MacabRecords::bootstrap_CF_types()
310 lcl_CFTypesLength
= 6;
311 lcl_CFTypes
= new lcl_CFType
[lcl_CFTypesLength
];
313 lcl_CFTypes
[0].cf
= CFNumberGetTypeID();
314 lcl_CFTypes
[0].ab
= kABIntegerProperty
;
316 lcl_CFTypes
[1].cf
= CFStringGetTypeID();
317 lcl_CFTypes
[1].ab
= kABStringProperty
;
319 lcl_CFTypes
[2].cf
= CFDateGetTypeID();
320 lcl_CFTypes
[2].ab
= kABDateProperty
;
322 lcl_CFTypes
[3].cf
= CFArrayGetTypeID();
323 lcl_CFTypes
[3].ab
= kABArrayProperty
;
325 lcl_CFTypes
[4].cf
= CFDictionaryGetTypeID();
326 lcl_CFTypes
[4].ab
= kABDictionaryProperty
;
328 lcl_CFTypes
[5].cf
= CFDataGetTypeID();
329 lcl_CFTypes
[5].ab
= kABDataProperty
;
333 /* This is based on the possible fields required in the mail merge template
334 * in sw. If the fields possible there change, it would be optimal to
335 * change these fields as well.
337 void MacabRecords::bootstrap_requiredProperties()
339 numRequiredProperties
= 7;
340 requiredProperties
= new CFStringRef
[numRequiredProperties
];
341 requiredProperties
[0] = kABTitleProperty
;
342 requiredProperties
[1] = kABFirstNameProperty
;
343 requiredProperties
[2] = kABLastNameProperty
;
344 requiredProperties
[3] = kABOrganizationProperty
;
345 requiredProperties
[4] = kABAddressProperty
;
346 requiredProperties
[5] = kABPhoneProperty
;
347 requiredProperties
[6] = kABEmailProperty
;
351 /* Create the header for a given record type and a given array of records.
352 * Because the array of records and the record type are given, if you want
353 * to, you can run this method on the members of a group, or on any other
354 * filtered list of people and get a header relevant to them (e.g., if
355 * they only have home addresses, the work address fields won't show up).
357 MacabHeader
*MacabRecords::createHeaderForRecordType(const CFArrayRef _records
, const CFStringRef _recordType
) const
359 /* We have two types of properties for a given record type, nonrequired
360 * and required. Required properties are ones that will show up whether
361 * or not they are empty. Nonrequired properties will only show up if
362 * at least one record in the set has that property filled. The reason
363 * is that some properties, like the kABTitleProperty are required by
364 * the mail merge wizard (in module sw) but are by default not shown in
365 * the Mac OS X address book, so they would be weeded out at this stage
366 * and not shown if they were not required.
368 * Note: with the addition of required properties, I am not sure that
369 * this method still works for kABGroupRecordType (since the required
370 * properites are all for kABPersonRecordType).
372 * Note: required properties are constructed in the method
373 * bootstrap_requiredProperties() (above).
375 CFArrayRef allProperties
= ABCopyArrayOfPropertiesForRecordType(addressBook
, _recordType
);
376 CFStringRef
*nonRequiredProperties
;
377 sal_Int32 numRecords
= (sal_Int32
) CFArrayGetCount(_records
);
378 sal_Int32 numProperties
= (sal_Int32
) CFArrayGetCount(allProperties
);
379 sal_Int32 numNonRequiredProperties
= numProperties
- numRequiredProperties
;
381 /* While searching through the properties for required properties, these
382 * sal_Bools will keep track of what we have found.
385 bool bFoundRequiredProperties
[numRequiredProperties
];
388 /* We have three MacabHeaders: headerDataForProperty is where we
389 * store the result of createHeaderForProperty(), which return a
390 * MacabHeader for a single property. lcl_header is where we store
391 * the MacabHeader that we are constructing. And, nonRequiredHeader
392 * is where we construct the MacabHeader for non-required properties,
393 * so that we can sort them before adding them to lcl_header.
395 MacabHeader
*headerDataForProperty
;
396 MacabHeader
*lcl_header
= new MacabHeader();
397 MacabHeader
*nonRequiredHeader
= new MacabHeader();
399 /* Other variables... */
402 CFStringRef property
;
405 /* Allocate and initialize... */
406 nonRequiredProperties
= new CFStringRef
[numNonRequiredProperties
];
408 for(i
= 0; i
< numRequiredProperties
; i
++)
409 bFoundRequiredProperties
[i
] = false;
411 /* Determine the non-required properties... */
412 for(i
= 0; i
< numProperties
; i
++)
414 property
= static_cast<CFStringRef
>(CFArrayGetValueAtIndex(allProperties
, i
));
415 bFoundProperty
= false;
416 for(j
= 0; j
< numRequiredProperties
; j
++)
418 if(CFEqual(property
, requiredProperties
[j
]))
420 bFoundProperty
= true;
421 bFoundRequiredProperties
[j
] = true;
428 /* If we have found too many non-required properties */
429 if(k
== numNonRequiredProperties
)
431 k
++; // so that the OSL_ENSURE below fails
434 nonRequiredProperties
[k
] = property
;
439 // Somehow, we got too many or too few non-requird properties...
440 // Most likely, one of the required properties no longer exists, which
441 // we also test later.
442 OSL_ENSURE(k
== numNonRequiredProperties
, "MacabRecords::createHeaderForRecordType: Found an unexpected number of non-required properties");
444 /* Fill the header with required properties first... */
445 for(i
= 0; i
< numRequiredProperties
; i
++)
447 if(bFoundRequiredProperties
[i
])
449 /* The order of these matters (we want all address properties
450 * before any phone properties, or else things will look weird),
451 * so we get all possibilitities for each property, going through
452 * each record, and then go onto the next property.
453 * (Note: the reason that we have to go through all records
454 * in the first place is that properties like address, phone, and
455 * e-mail are multi-value properties with an unknown number of
456 * values. A user could specify thirteen different kinds of
457 * e-mail addresses for one of her or his contacts, and we need to
460 for(j
= 0; j
< numRecords
; j
++)
462 record
= const_cast<ABRecordRef
>(CFArrayGetValueAtIndex(_records
, j
));
463 headerDataForProperty
= createHeaderForProperty(record
,requiredProperties
[i
],_recordType
,true);
464 if(headerDataForProperty
!= NULL
)
466 (*lcl_header
) += headerDataForProperty
;
467 delete headerDataForProperty
;
473 // Couldn't find a required property...
474 OSL_FAIL(OString(OString("MacabRecords::createHeaderForRecordType: could not find required property: ") +
475 OUStringToOString(CFStringToOUString(requiredProperties
[i
]), RTL_TEXTENCODING_ASCII_US
)).getStr());
479 /* And now, non-required properties... */
480 for(i
= 0; i
< numRecords
; i
++)
482 record
= const_cast<ABRecordRef
>(CFArrayGetValueAtIndex(_records
, i
));
484 for(j
= 0; j
< numNonRequiredProperties
; j
++)
486 property
= nonRequiredProperties
[j
];
487 headerDataForProperty
= createHeaderForProperty(record
,property
,_recordType
,false);
488 if(headerDataForProperty
!= NULL
)
490 (*nonRequiredHeader
) += headerDataForProperty
;
491 delete headerDataForProperty
;
496 nonRequiredHeader
->sortRecord();
498 (*lcl_header
) += nonRequiredHeader
;
499 delete nonRequiredHeader
;
501 CFRelease(allProperties
);
502 delete [] nonRequiredProperties
;
508 /* Create a header for a single property. Basically, this method gets
509 * the property's value and type and then calls another method of
510 * the same name to do the dirty work.
512 MacabHeader
*MacabRecords::createHeaderForProperty(const ABRecordRef _record
, const CFStringRef _propertyName
, const CFStringRef _recordType
, const bool _isPropertyRequired
) const
515 CFStringRef localizedPropertyName
;
516 CFTypeRef propertyValue
;
517 ABPropertyType propertyType
;
520 /* Get the property's value */
521 propertyValue
= ABRecordCopyValue(_record
,_propertyName
);
522 if(propertyValue
== NULL
&& !_isPropertyRequired
)
525 propertyType
= ABTypeOfProperty(addressBook
, _recordType
, _propertyName
);
526 localizedPropertyName
= ABCopyLocalizedPropertyOrLabel(_propertyName
);
528 result
= createHeaderForProperty(propertyType
, propertyValue
, localizedPropertyName
);
530 if(propertyValue
!= NULL
)
531 CFRelease(propertyValue
);
537 /* Create a header for a single property. This method is recursive
538 * because a single property might contain several sub-properties that
539 * we also want to treat singly.
541 MacabHeader
*MacabRecords::createHeaderForProperty(const ABPropertyType _propertyType
, const CFTypeRef _propertyValue
, const CFStringRef _propertyName
) const
543 macabfield
**headerNames
= NULL
;
544 sal_Int32 length
= 0;
546 switch(_propertyType
)
549 case kABStringProperty
:
550 case kABRealProperty
:
551 case kABIntegerProperty
:
552 case kABDateProperty
:
554 headerNames
= new macabfield
*[1];
555 headerNames
[0] = new macabfield
;
556 headerNames
[0]->value
= _propertyName
;
557 headerNames
[0]->type
= _propertyType
;
561 case kABMultiIntegerProperty
:
562 case kABMultiDateProperty
:
563 case kABMultiStringProperty
:
564 case kABMultiRealProperty
:
565 case kABMultiDataProperty
:
566 /* For non-scalars, we can only get more information if the property
569 if(_propertyValue
!= NULL
)
573 sal_Int32 multiLength
= ABMultiValueCount(static_cast<ABMutableMultiValueRef
>(const_cast<void *>(_propertyValue
)));
574 CFStringRef multiLabel
, localizedMultiLabel
;
575 OUString multiLabelString
;
576 OUString multiPropertyString
;
577 OUString headerNameString
;
578 ABPropertyType multiType
= (ABPropertyType
) (ABMultiValuePropertyType(static_cast<ABMutableMultiValueRef
>(const_cast<void *>(_propertyValue
))) - 0x100);
580 length
= multiLength
;
581 headerNames
= new macabfield
*[multiLength
];
582 multiPropertyString
= CFStringToOUString(_propertyName
);
584 /* Go through each element, and - since each element is a scalar -
585 * just create a new macabfield for it.
587 for(i
= 0; i
< multiLength
; i
++)
589 multiLabel
= ABMultiValueCopyLabelAtIndex(static_cast<ABMutableMultiValueRef
>(const_cast<void *>(_propertyValue
)), i
);
590 localizedMultiLabel
= ABCopyLocalizedPropertyOrLabel(multiLabel
);
591 multiLabelString
= CFStringToOUString(localizedMultiLabel
);
592 CFRelease(multiLabel
);
593 CFRelease(localizedMultiLabel
);
594 headerNameString
= multiPropertyString
+ ": " + fixLabel(multiLabelString
);
595 headerNames
[i
] = new macabfield
;
596 headerNames
[i
]->value
= OUStringToCFString(headerNameString
);
597 headerNames
[i
]->type
= multiType
;
602 /* Multi-array or dictionary */
603 case kABMultiArrayProperty
:
604 case kABMultiDictionaryProperty
:
605 /* For non-scalars, we can only get more information if the property
608 if(_propertyValue
!= NULL
)
612 // Total number of multi-array or multi-dictionary elements.
613 sal_Int32 multiLengthFirstLevel
= ABMultiValueCount(static_cast<ABMutableMultiValueRef
>(const_cast<void *>(_propertyValue
)));
615 /* Total length, including the length of each element (e.g., if
616 * this multi-dictionary contains three dictionaries, and each
617 * dictionary has four elements, this variable will be twelve,
618 * whereas multiLengthFirstLevel will be three.
620 sal_Int32 multiLengthSecondLevel
= 0;
622 CFStringRef multiLabel
, localizedMultiLabel
;
623 CFTypeRef multiValue
;
624 OUString multiLabelString
;
625 OUString multiPropertyString
;
626 MacabHeader
**multiHeaders
= new MacabHeader
*[multiLengthFirstLevel
];
627 ABPropertyType multiType
= (ABPropertyType
) (ABMultiValuePropertyType(static_cast<ABMutableMultiValueRef
>(const_cast<void *>(_propertyValue
))) - 0x100);
629 multiPropertyString
= CFStringToOUString(_propertyName
);
631 /* Go through each element - since each element can really
632 * contain anything, we run this method again on each element
633 * and store the resulting MacabHeader (in the multiHeaders
634 * array). Then, all we'll have to do is combine the MacabHeaders
637 for(i
= 0; i
< multiLengthFirstLevel
; i
++)
640 multiLabel
= ABMultiValueCopyLabelAtIndex(static_cast<ABMutableMultiValueRef
>(const_cast<void *>(_propertyValue
)), i
);
641 multiValue
= ABMultiValueCopyValueAtIndex(static_cast<ABMutableMultiValueRef
>(const_cast<void *>(_propertyValue
)), i
);
642 if(multiValue
&& multiLabel
)
644 localizedMultiLabel
= ABCopyLocalizedPropertyOrLabel(multiLabel
);
645 multiLabelString
= multiPropertyString
+ ": " + fixLabel(CFStringToOUString(localizedMultiLabel
));
646 CFRelease(multiLabel
);
647 CFRelease(localizedMultiLabel
);
648 multiLabel
= OUStringToCFString(multiLabelString
);
649 multiHeaders
[i
] = createHeaderForProperty(multiType
, multiValue
, multiLabel
);
650 if (!multiHeaders
[i
])
651 multiHeaders
[i
] = new MacabHeader();
652 multiLengthSecondLevel
+= multiHeaders
[i
]->getSize();
656 multiHeaders
[i
] = new MacabHeader();
659 CFRelease(multiValue
);
662 /* We now have enough information to create our final MacabHeader.
663 * We go through each field of each header and add it to the
664 * headerNames array (which is what is used below to construct
665 * the MacabHeader we return).
667 length
= multiLengthSecondLevel
;
668 headerNames
= new macabfield
*[multiLengthSecondLevel
];
670 for(i
= 0, j
= 0, k
= 0; i
< multiLengthSecondLevel
; i
++,k
++)
672 while(multiHeaders
[j
]->getSize() == k
)
678 headerNames
[i
] = multiHeaders
[j
]->copy(k
);
680 for(i
= 0; i
< multiLengthFirstLevel
; i
++)
681 delete multiHeaders
[i
];
683 delete [] multiHeaders
;
688 case kABDictionaryProperty
:
689 /* For non-scalars, we can only get more information if the property
692 if(_propertyValue
!= NULL
)
694 /* Assume all keys are strings */
695 sal_Int32 numRecords
= (sal_Int32
) CFDictionaryGetCount(static_cast<CFDictionaryRef
>(_propertyValue
));
697 /* The only method for getting info out of a CFDictionary, of both
698 * keys and values, is to all of them all at once, so these
699 * variables will hold them.
701 CFStringRef
*dictKeys
;
702 CFTypeRef
*dictValues
;
705 OUString dictKeyString
, propertyNameString
;
706 ABPropertyType dictType
;
707 MacabHeader
**dictHeaders
= new MacabHeader
*[numRecords
];
708 OUString dictLabelString
;
709 CFStringRef dictLabel
, localizedDictKey
;
711 /* Get the keys and values */
712 dictKeys
= static_cast<CFStringRef
*>(malloc(sizeof(CFStringRef
)*numRecords
));
713 dictValues
= static_cast<CFTypeRef
*>(malloc(sizeof(CFTypeRef
)*numRecords
));
714 CFDictionaryGetKeysAndValues(static_cast<CFDictionaryRef
>(_propertyValue
), reinterpret_cast<const void **>(dictKeys
), (const void **) dictValues
);
716 propertyNameString
= CFStringToOUString(_propertyName
);
719 /* Go through each element - assuming that the key is a string but
720 * that the value could be anything. Since the value could be
721 * anything, we can't assume that it is scalar (it could even be
722 * another dictionary), so we attempt to get its type using
723 * the method getABTypeFromCFType and then run this method
724 * recursively on that element, storing the MacabHeader that
725 * results. Then, we just combine all of the MacabHeaders into
728 for(i
= 0; i
< numRecords
; i
++)
730 dictType
= (ABPropertyType
) getABTypeFromCFType( CFGetTypeID(dictValues
[i
]) );
731 localizedDictKey
= ABCopyLocalizedPropertyOrLabel(dictKeys
[i
]);
732 dictKeyString
= CFStringToOUString(localizedDictKey
);
733 dictLabelString
= propertyNameString
+ ": " + fixLabel(dictKeyString
);
734 dictLabel
= OUStringToCFString(dictLabelString
);
735 dictHeaders
[i
] = createHeaderForProperty(dictType
, dictValues
[i
], dictLabel
);
737 dictHeaders
[i
] = new MacabHeader();
738 length
+= dictHeaders
[i
]->getSize();
739 CFRelease(dictLabel
);
740 CFRelease(localizedDictKey
);
743 /* Combine all of the macabfields in each MacabHeader into the
744 * headerNames array, which (at the end of this method) is used
745 * to create the MacabHeader that is returned.
747 headerNames
= new macabfield
*[length
];
748 for(i
= 0, j
= 0, k
= 0; i
< length
; i
++,k
++)
750 while(dictHeaders
[j
]->getSize() == k
)
756 headerNames
[i
] = dictHeaders
[j
]->copy(k
);
759 for(i
= 0; i
< numRecords
; i
++)
760 delete dictHeaders
[i
];
762 delete [] dictHeaders
;
769 case kABArrayProperty
:
770 /* For non-scalars, we can only get more information if the property
773 if(_propertyValue
!= NULL
)
775 sal_Int32 arrLength
= (sal_Int32
) CFArrayGetCount(static_cast<CFArrayRef
>(_propertyValue
));
778 ABPropertyType arrType
;
779 MacabHeader
**arrHeaders
= new MacabHeader
*[arrLength
];
780 OUString propertyNameString
= CFStringToOUString(_propertyName
);
781 OUString arrLabelString
;
782 CFStringRef arrLabel
;
785 /* Go through each element - since the elements here do not have
786 * unique keys like the ones in dictionaries, we create a unique
787 * key out of the id of the element in the array (the first
788 * element gets a 0 plopped onto the end of it, the second a 1...
789 * As with dictionaries, the elements could be anything, including
790 * another array, so we have to run this method recursively on
791 * each element, storing the resulting MacabHeader into an array,
792 * which we then combine into one MacabHeader that is returned.
794 for(i
= 0; i
< arrLength
; i
++)
796 arrValue
= (CFTypeRef
) CFArrayGetValueAtIndex(static_cast<CFArrayRef
>(_propertyValue
), i
);
797 arrType
= (ABPropertyType
) getABTypeFromCFType( CFGetTypeID(arrValue
) );
798 arrLabelString
= propertyNameString
+ OUString::number(i
);
799 arrLabel
= OUStringToCFString(arrLabelString
);
800 arrHeaders
[i
] = createHeaderForProperty(arrType
, arrValue
, arrLabel
);
802 arrHeaders
[i
] = new MacabHeader();
803 length
+= arrHeaders
[i
]->getSize();
807 headerNames
= new macabfield
*[length
];
808 for(i
= 0, j
= 0, k
= 0; i
< length
; i
++,k
++)
810 while(arrHeaders
[j
]->getSize() == k
)
816 headerNames
[i
] = arrHeaders
[j
]->copy(k
);
818 for(i
= 0; i
< arrLength
; i
++)
819 delete arrHeaders
[i
];
821 delete [] arrHeaders
;
830 /* If we succeeded at adding elements to the headerNames array, then
831 * length will no longer be 0. If it is, create a new MacabHeader
832 * out of the headerNames (after weeding out duplicate headers), and
833 * then return the result. If the length is still 0, return NULL: we
834 * failed to create a MacabHeader out of this property.
838 manageDuplicateHeaders(headerNames
, length
);
839 MacabHeader
*headerResult
= new MacabHeader(length
, headerNames
);
840 delete [] headerNames
;
848 /* Create a MacabRecord out of an ABRecord, using a given MacabHeader and
849 * the record's type. We go through each property for this record type
850 * then process it much like we processed the header (above), with two
851 * exceptions: if we come upon something not in the header, we ignore it
852 * (it's something we don't want to add), and once we find a corresponding
853 * location in the header, we store the property and the property type in
854 * a macabfield. (For the header, we stored the property type and the name
855 * of the property as a CFString.)
857 MacabRecord
*MacabRecords::createMacabRecord(const ABRecordRef _abrecord
, const MacabHeader
*_header
, const CFStringRef _recordType
) const
859 /* The new record that we will create... */
860 MacabRecord
*macabRecord
= new MacabRecord(_header
->getSize());
862 CFArrayRef recordProperties
= ABCopyArrayOfPropertiesForRecordType(addressBook
, _recordType
);
863 sal_Int32 numProperties
= (sal_Int32
) CFArrayGetCount(recordProperties
);
867 CFTypeRef propertyValue
;
868 ABPropertyType propertyType
;
870 CFStringRef propertyName
, localizedPropertyName
;
871 OUString propertyNameString
;
872 for(i
= 0; i
< numProperties
; i
++)
874 propertyName
= static_cast<CFStringRef
>(CFArrayGetValueAtIndex(recordProperties
, i
));
875 localizedPropertyName
= ABCopyLocalizedPropertyOrLabel(propertyName
);
876 propertyNameString
= CFStringToOUString(localizedPropertyName
);
877 CFRelease(localizedPropertyName
);
879 /* Get the property's value */
880 propertyValue
= ABRecordCopyValue(_abrecord
,propertyName
);
881 if(propertyValue
!= NULL
)
883 propertyType
= ABTypeOfProperty(addressBook
, _recordType
, propertyName
);
884 if(propertyType
!= kABErrorInProperty
)
885 insertPropertyIntoMacabRecord(propertyType
, macabRecord
, _header
, propertyNameString
, propertyValue
);
887 CFRelease(propertyValue
);
890 CFRelease(recordProperties
);
895 /* Inserts a given property into a MacabRecord. This method calls another
896 * method by the same name after getting the property type (it only
897 * receives the property value). It is called when we aren't given the
898 * property's type already.
900 void MacabRecords::insertPropertyIntoMacabRecord(MacabRecord
*_abrecord
, const MacabHeader
*_header
, const OUString
& _propertyName
, const CFTypeRef _propertyValue
) const
902 CFTypeID cf_type
= CFGetTypeID(_propertyValue
);
903 ABPropertyType ab_type
= getABTypeFromCFType( cf_type
);
905 if(ab_type
!= kABErrorInProperty
)
906 insertPropertyIntoMacabRecord(ab_type
, _abrecord
, _header
, _propertyName
, _propertyValue
);
910 /* Inserts a given property into a MacabRecord. This method is recursive
911 * because properties can contain many sub-properties.
913 void MacabRecords::insertPropertyIntoMacabRecord(const ABPropertyType _propertyType
, MacabRecord
*_abrecord
, const MacabHeader
*_header
, const OUString
& _propertyName
, const CFTypeRef _propertyValue
) const
915 /* If there is no value, return */
916 if(_propertyValue
== NULL
)
919 /* The main switch statement */
920 switch(_propertyType
)
923 case kABStringProperty
:
924 case kABRealProperty
:
925 case kABIntegerProperty
:
926 case kABDateProperty
:
928 /* Only scalars actually insert a property into the MacabRecord.
929 * In all other cases, this method is called recursively until a
930 * scalar type, an error, or an unknown type are found.
931 * Because of that, the following checks only occur for this type.
932 * We store whether we have successfully placed this property
933 * into the MacabRecord (or whether an unrecoverable error occurred).
934 * Then, we try over and over again to place the property into the
935 * record. There are three possible results:
937 * 2) There is already a property stored at the column of this name,
938 * in which case we have a duplicate header (see the method
939 * manageDuplicateHeaders()). If that is the case, we add an ID
940 * to the end of the column name in the same format as we do in
941 * manageDuplicateHeaders() and try again.
942 * 3) No column of this name exists in the header. In this case,
943 * there is nothing we can do: we have failed to place this
944 * property into the record.
946 bool bPlaced
= false;
947 OUString columnName
= _propertyName
;
950 // A big safeguard to prevent two fields from having the same name.
953 sal_Int32 columnNumber
= _header
->getColumnNumber(columnName
);
955 if(columnNumber
!= -1)
957 // collision! A property already exists here!
958 if(_abrecord
->get(columnNumber
) != NULL
)
962 columnName
= _propertyName
+ " (" + OUString::number(i
) + ")";
968 _abrecord
->insertAtColumn(_propertyValue
, _propertyType
, columnNumber
);
976 case kABArrayProperty
:
978 /* An array is basically just a list of anything, so all we do
979 * is go through the array, and rerun this method recursively
982 sal_Int32 arrLength
= (sal_Int32
) CFArrayGetCount(static_cast<CFArrayRef
>(_propertyValue
));
984 OUString newPropertyName
;
986 /* Going through each element... */
987 for(i
= 0; i
< arrLength
; i
++)
989 const void *arrValue
= CFArrayGetValueAtIndex(static_cast<CFArrayRef
>(_propertyValue
), i
);
990 newPropertyName
= _propertyName
+ OUString::number(i
);
991 insertPropertyIntoMacabRecord(_abrecord
, _header
, newPropertyName
, arrValue
);
999 case kABDictionaryProperty
:
1001 /* A dictionary is basically a hashmap. Technically, it can
1002 * hold any object as a key and any object as a value.
1003 * For our case, we assume that the key is a string (so that
1004 * we can use the key to get the column name and match it against
1005 * the header), but we don't assume anything about the value, so
1006 * we run this method recursively (or, rather, we run the version
1007 * of this method for when we don't know the object's type) until
1008 * we hit a scalar value.
1011 sal_Int32 numRecords
= (sal_Int32
) CFDictionaryGetCount(static_cast<CFDictionaryRef
>(_propertyValue
));
1012 OUString dictKeyString
;
1014 OUString newPropertyName
;
1016 /* Unfortunately, the only way to get both keys and values out
1017 * of a dictionary in Carbon is to get them all at once, so we
1020 CFStringRef
*dictKeys
;
1021 CFStringRef localizedDictKey
;
1022 CFTypeRef
*dictValues
;
1023 dictKeys
= static_cast<CFStringRef
*>(malloc(sizeof(CFStringRef
)*numRecords
));
1024 dictValues
= static_cast<CFTypeRef
*>(malloc(sizeof(CFTypeRef
)*numRecords
));
1025 CFDictionaryGetKeysAndValues(static_cast<CFDictionaryRef
>(_propertyValue
), reinterpret_cast<const void **>(dictKeys
), (const void **) dictValues
);
1027 /* Going through each element... */
1028 for(i
= 0; i
< numRecords
; i
++)
1030 localizedDictKey
= ABCopyLocalizedPropertyOrLabel(dictKeys
[i
]);
1031 dictKeyString
= CFStringToOUString(localizedDictKey
);
1032 CFRelease(localizedDictKey
);
1033 newPropertyName
= _propertyName
+ ": " + fixLabel(dictKeyString
);
1034 insertPropertyIntoMacabRecord(_abrecord
, _header
, newPropertyName
, dictValues
[i
]);
1043 case kABMultiIntegerProperty
:
1044 case kABMultiDateProperty
:
1045 case kABMultiStringProperty
:
1046 case kABMultiRealProperty
:
1047 case kABMultiDataProperty
:
1048 case kABMultiDictionaryProperty
:
1049 case kABMultiArrayProperty
:
1051 /* All scalar multivalues are handled in the same way. Each element
1052 * is a label and a value. All labels are strings
1053 * (kABStringProperty), and all values have the same type
1054 * (which is the type of the multivalue minus 255, or as
1055 * Carbon's list of property types has it, minus 0x100.
1056 * We just get the correct type, then go through each element
1057 * and get the label and value and print them in a list.
1061 sal_Int32 multiLength
= ABMultiValueCount(static_cast<ABMutableMultiValueRef
>(const_cast<void *>(_propertyValue
)));
1062 CFStringRef multiLabel
, localizedMultiLabel
;
1063 CFTypeRef multiValue
;
1064 OUString multiLabelString
, newPropertyName
;
1065 ABPropertyType multiType
= (ABPropertyType
) (ABMultiValuePropertyType(static_cast<ABMutableMultiValueRef
>(const_cast<void *>(_propertyValue
))) - 0x100);
1067 /* Go through each element... */
1068 for(i
= 0; i
< multiLength
; i
++)
1070 /* Label and value */
1071 multiLabel
= ABMultiValueCopyLabelAtIndex(static_cast<ABMutableMultiValueRef
>(const_cast<void *>(_propertyValue
)), i
);
1072 multiValue
= ABMultiValueCopyValueAtIndex(static_cast<ABMutableMultiValueRef
>(const_cast<void *>(_propertyValue
)), i
);
1074 localizedMultiLabel
= ABCopyLocalizedPropertyOrLabel(multiLabel
);
1075 multiLabelString
= CFStringToOUString(localizedMultiLabel
);
1076 newPropertyName
= _propertyName
+ ": " + fixLabel(multiLabelString
);
1077 insertPropertyIntoMacabRecord(multiType
, _abrecord
, _header
, newPropertyName
, multiValue
);
1079 /* free our variables */
1080 CFRelease(multiLabel
);
1081 CFRelease(localizedMultiLabel
);
1082 CFRelease(multiValue
);
1087 /* Unhandled types */
1088 case kABErrorInProperty
:
1089 case kABDataProperty
:
1091 /* An error, as far as I have seen, only shows up as a type
1092 * returned by a function for dictionaries when the dictionary
1093 * holds many types of values. Since we do not use that function,
1094 * it shouldn't come up. I have yet to see the kABDataProperty,
1095 * and I am not sure how to represent it as a string anyway,
1096 * since it appears to just be a bunch of bytes. Assumably, if
1097 * these bytes made up a string, the type would be
1098 * kABStringProperty. I think that this is used when we are not
1099 * sure what the type is (e.g., it could be a string or a number).
1100 * That being the case, I still don't know how to represent it.
1101 * And, default should never come up, since we've exhausted all
1102 * of the possible types for ABPropertyType, but... just in case.
1110 ABPropertyType
MacabRecords::getABTypeFromCFType(const CFTypeID cf_type
) const
1113 for(i
= 0; i
< lcl_CFTypesLength
; i
++)
1116 if(lcl_CFTypes
[i
].cf
== (sal_Int32
) cf_type
)
1118 return (ABPropertyType
) lcl_CFTypes
[i
].ab
;
1121 return kABErrorInProperty
;
1125 sal_Int32
MacabRecords::size() const
1127 return currentRecord
;
1131 MacabRecords
*MacabRecords::begin()
1137 MacabRecords::iterator::iterator ()
1142 MacabRecords::iterator::~iterator ()
1147 MacabRecords::iterator
& MacabRecords::iterator::operator= (MacabRecords
*_records
)
1155 void MacabRecords::iterator::operator++ ()
1161 bool MacabRecords::iterator::operator!= (const sal_Int32 i
) const
1167 bool MacabRecords::iterator::operator== (const sal_Int32 i
) const
1173 MacabRecord
*MacabRecords::iterator::operator* () const
1175 return records
->getRecord(id
);
1179 sal_Int32
MacabRecords::end() const
1181 return currentRecord
;
1185 void MacabRecords::swap(const sal_Int32 _id1
, const sal_Int32 _id2
)
1187 MacabRecord
*swapRecord
= records
[_id1
];
1189 records
[_id1
] = records
[_id2
];
1190 records
[_id2
] = swapRecord
;
1194 void MacabRecords::setName(const OUString
& _sName
)
1200 OUString
MacabRecords::getName() const
1205 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */