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