update emoji autocorrect entries from po-files
[LibreOffice.git] / connectivity / source / drivers / macab / MacabRecords.cxx
blob56a27ff1c47804e509f47e821faaa40b24b7f793
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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"
26 #include <premac.h>
27 #include <Carbon/Carbon.h>
28 #include <AddressBook/ABAddressBookC.h>
29 #include <postmac.h>
30 #include <com/sun/star/util/DateTime.hpp>
32 using namespace connectivity::macab;
33 using namespace com::sun::star::util;
35 namespace {
37 void manageDuplicateHeaders(macabfield **_headerNames, const sal_Int32 _length)
39 /* If we have two cases of, say, phone: home, this makes it:
40 * phone: home (1)
41 * phone: home (2)
43 sal_Int32 i, j;
44 sal_Int32 count;
45 for(i = _length-1; i >= 0; i--)
47 count = 1;
48 for( j = i-1; j >= 0; j--)
50 if(CFEqual(_headerNames[i]->value, _headerNames[j]->value))
52 count++;
56 // duplicate!
57 if(count != 1)
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... */
73 header = _header;
74 recordsSize = _numRecords;
75 currentRecord = _numRecords;
76 records = _records;
77 addressBook = _addressBook;
79 /* Default variables... */
80 recordType = kABPersonRecordType;
82 /* Variables constructed... */
83 bootstrap_CF_types();
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
94 * header.
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... */
104 currentRecord = 0;
105 header = NULL;
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... */
121 recordsSize = 0;
122 currentRecord = 0;
123 records = NULL;
124 header = NULL;
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
139 * NULL.
141 if(records != NULL)
143 sal_Int32 i;
145 for(i = 0; i < recordsSize; i++)
146 delete records[i];
148 delete [] records;
151 if(header != NULL)
152 delete header;
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);
160 else
161 allRecords = ABCopyArrayOfAllGroups(addressBook);
163 ABRecordRef record;
164 sal_Int32 i;
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)
190 if(header != NULL)
191 delete header;
192 header = _header;
196 MacabHeader *MacabRecords::getHeader() const
198 return header;
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)
214 sal_Int32 i;
215 MacabRecord **newRecordsArray = new MacabRecord *[_location+1];
216 for(i = 0; i < recordsSize; i++)
218 newRecordsArray[i] = records[i];
220 delete [] records;
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;
233 return oldRecord;
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)
247 return NULL;
248 return records[_location];
252 macabfield *MacabRecords::getField(const sal_Int32 _recordNumber, const sal_Int32 _columnNumber) const
254 if(_recordNumber >= recordsSize)
255 return NULL;
257 MacabRecord *record = records[_recordNumber];
259 if(_columnNumber < 0 || _columnNumber >= record->getSize())
260 return NULL;
262 return record->get(_columnNumber);
266 macabfield *MacabRecords::getField(const sal_Int32 _recordNumber, const OUString& _columnName) const
268 if(header != NULL)
270 sal_Int32 columnNumber = header->getColumnNumber(_columnName);
271 if(columnNumber == -1)
272 return NULL;
274 return getField(_recordNumber, columnNumber);
276 else
278 // error: shouldn't access field with null header!
279 return NULL;
284 sal_Int32 MacabRecords::getFieldNumber(const OUString& _columnName) const
286 if(header != NULL)
287 return header->getColumnNumber(_columnName);
288 else
289 // error: shouldn't access field with null header!
290 return -1;
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.
384 bool bFoundProperty;
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... */
400 sal_Int32 i, j, k;
401 ABRecordRef record;
402 CFStringRef property;
405 /* Allocate and initialize... */
406 nonRequiredProperties = new CFStringRef[numNonRequiredProperties];
407 k = 0;
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;
422 break;
426 if(!bFoundProperty)
428 /* If we have found too many non-required properties */
429 if(k == numNonRequiredProperties)
431 k++; // so that the OSL_ENSURE below fails
432 break;
434 nonRequiredProperties[k] = property;
435 k++;
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
458 * get all of them.
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;
471 else
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;
504 return lcl_header;
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
514 // local variables
515 CFStringRef localizedPropertyName;
516 CFTypeRef propertyValue;
517 ABPropertyType propertyType;
518 MacabHeader *result;
520 /* Get the property's value */
521 propertyValue = ABRecordCopyValue(_record,_propertyName);
522 if(propertyValue == NULL && !_isPropertyRequired)
523 return NULL;
525 propertyType = ABTypeOfProperty(addressBook, _recordType, _propertyName);
526 localizedPropertyName = ABCopyLocalizedPropertyOrLabel(_propertyName);
528 result = createHeaderForProperty(propertyType, propertyValue, localizedPropertyName);
530 if(propertyValue != NULL)
531 CFRelease(propertyValue);
533 return result;
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)
548 /* Scalars */
549 case kABStringProperty:
550 case kABRealProperty:
551 case kABIntegerProperty:
552 case kABDateProperty:
553 length = 1;
554 headerNames = new macabfield *[1];
555 headerNames[0] = new macabfield;
556 headerNames[0]->value = _propertyName;
557 headerNames[0]->type = _propertyType;
558 break;
560 /* Multi-scalars */
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
567 * actually exists.
569 if(_propertyValue != NULL)
571 sal_Int32 i;
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;
600 break;
602 /* Multi-array or dictionary */
603 case kABMultiArrayProperty:
604 case kABMultiDictionaryProperty:
605 /* For non-scalars, we can only get more information if the property
606 * actually exists.
608 if(_propertyValue != NULL)
610 sal_Int32 i,j,k;
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
635 * into a single one.
637 for(i = 0; i < multiLengthFirstLevel; i++)
639 /* label */
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();
654 else
656 multiHeaders[i] = new MacabHeader();
658 if(multiValue)
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)
674 j++;
675 k = 0;
678 headerNames[i] = multiHeaders[j]->copy(k);
680 for(i = 0; i < multiLengthFirstLevel; i++)
681 delete multiHeaders[i];
683 delete [] multiHeaders;
685 break;
687 /* Dictionary */
688 case kABDictionaryProperty:
689 /* For non-scalars, we can only get more information if the property
690 * actually exists.
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;
704 sal_Int32 i,j,k;
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);
718 length = 0;
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
726 * one.
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);
736 if (!dictHeaders[i])
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)
752 j++;
753 k = 0;
756 headerNames[i] = dictHeaders[j]->copy(k);
759 for(i = 0; i < numRecords; i++)
760 delete dictHeaders[i];
762 delete [] dictHeaders;
763 free(dictKeys);
764 free(dictValues);
766 break;
768 /* Array */
769 case kABArrayProperty:
770 /* For non-scalars, we can only get more information if the property
771 * actually exists.
773 if(_propertyValue != NULL)
775 sal_Int32 arrLength = (sal_Int32) CFArrayGetCount(static_cast<CFArrayRef>(_propertyValue));
776 sal_Int32 i,j,k;
777 CFTypeRef arrValue;
778 ABPropertyType arrType;
779 MacabHeader **arrHeaders = new MacabHeader *[arrLength];
780 OUString propertyNameString = CFStringToOUString(_propertyName);
781 OUString arrLabelString;
782 CFStringRef arrLabel;
784 length = 0;
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);
801 if (!arrHeaders[i])
802 arrHeaders[i] = new MacabHeader();
803 length += arrHeaders[i]->getSize();
804 CFRelease(arrLabel);
807 headerNames = new macabfield *[length];
808 for(i = 0, j = 0, k = 0; i < length; i++,k++)
810 while(arrHeaders[j]->getSize() == k)
812 j++;
813 k = 0;
816 headerNames[i] = arrHeaders[j]->copy(k);
818 for(i = 0; i < arrLength; i++)
819 delete arrHeaders[i];
821 delete [] arrHeaders;
823 break;
825 default:
826 break;
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.
836 if(length != 0)
838 manageDuplicateHeaders(headerNames, length);
839 MacabHeader *headerResult = new MacabHeader(length, headerNames);
840 delete [] headerNames;
841 return headerResult;
843 else
844 return NULL;
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);
865 sal_Int32 i;
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);
891 return macabRecord;
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)
917 return;
919 /* The main switch statement */
920 switch(_propertyType)
922 /* Scalars */
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:
936 * 1) Success!
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;
948 sal_Int32 i = 1;
950 // A big safeguard to prevent two fields from having the same name.
951 while(!bPlaced)
953 sal_Int32 columnNumber = _header->getColumnNumber(columnName);
954 bPlaced = true;
955 if(columnNumber != -1)
957 // collision! A property already exists here!
958 if(_abrecord->get(columnNumber) != NULL)
960 bPlaced = false;
961 i++;
962 columnName = _propertyName + " (" + OUString::number(i) + ")";
965 // success!
966 else
968 _abrecord->insertAtColumn(_propertyValue, _propertyType, columnNumber);
973 break;
975 /* Array */
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
980 * on each element.
982 sal_Int32 arrLength = (sal_Int32) CFArrayGetCount(static_cast<CFArrayRef>(_propertyValue));
983 sal_Int32 i;
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);
992 CFRelease(arrValue);
996 break;
998 /* Dictionary */
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;
1013 sal_Int32 i;
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
1018 * do that.
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]);
1037 free(dictKeys);
1038 free(dictValues);
1040 break;
1042 /* Multivalue */
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.
1060 sal_Int32 i;
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);
1085 break;
1087 /* Unhandled types */
1088 case kABErrorInProperty:
1089 case kABDataProperty:
1090 default:
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.
1104 break;
1110 ABPropertyType MacabRecords::getABTypeFromCFType(const CFTypeID cf_type ) const
1112 sal_Int32 i;
1113 for(i = 0; i < lcl_CFTypesLength; i++)
1115 /* A match! */
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()
1133 return this;
1137 MacabRecords::iterator::iterator ()
1142 MacabRecords::iterator::~iterator ()
1147 MacabRecords::iterator& MacabRecords::iterator::operator= (MacabRecords *_records)
1149 id = 0;
1150 records = _records;
1151 return *this;
1155 void MacabRecords::iterator::operator++ ()
1157 id++;
1161 bool MacabRecords::iterator::operator!= (const sal_Int32 i) const
1163 return(id != i);
1167 bool MacabRecords::iterator::operator== (const sal_Int32 i) const
1169 return(id == i);
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)
1196 m_sName = _sName;
1200 OUString MacabRecords::getName() const
1202 return m_sName;
1205 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */