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