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 // -------------------------------------------------------------------------
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  */
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 // -------------------------------------------------------------------------
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 // -------------------------------------------------------------------------
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 // -------------------------------------------------------------------------
155 MacabRecords::~MacabRecords()
156 {
157 }
158 
159 // -------------------------------------------------------------------------
160 void MacabRecords::setHeader(MacabHeader *_header)
161 {
162 	if(header != NULL)
163 		delete header;
164 	header = _header;
165 }
166 
167 // -------------------------------------------------------------------------
168 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  */
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. */
210 void MacabRecords::insertRecord(MacabRecord *_newRecord)
211 {
212 	insertRecord(_newRecord, currentRecord);
213 }
214 
215 // -------------------------------------------------------------------------
216 MacabRecord *MacabRecords::getRecord(const sal_Int32 _location) const
217 {
218 	if(_location >= recordsSize)
219 		return NULL;
220 	return records[_location];
221 }
222 
223 // -------------------------------------------------------------------------
224 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 // -------------------------------------------------------------------------
238 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 // -------------------------------------------------------------------------
256 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  */
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  */
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  */
329 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  */
484 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  */
513 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 // -------------------------------------------------------------------------
820 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  */
861 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  */
904 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  */
917 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 // -------------------------------------------------------------------------
1115 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 // -------------------------------------------------------------------------
1130 sal_Int32 MacabRecords::size() const
1131 {
1132 	return currentRecord;
1133 }
1134 
1135 // -------------------------------------------------------------------------
1136 MacabRecords *MacabRecords::begin()
1137 {
1138 	return this;
1139 }
1140 
1141 // -------------------------------------------------------------------------
1142 MacabRecords::iterator::iterator ()
1143 {
1144 }
1145 
1146 // -------------------------------------------------------------------------
1147 MacabRecords::iterator::~iterator ()
1148 {
1149 }
1150 
1151 // -------------------------------------------------------------------------
1152 void MacabRecords::iterator::operator= (MacabRecords *_records)
1153 {
1154 	id = 0;
1155 	records = _records;
1156 }
1157 
1158 // -------------------------------------------------------------------------
1159 void MacabRecords::iterator::operator++ ()
1160 {
1161 	id++;
1162 }
1163 
1164 // -------------------------------------------------------------------------
1165 sal_Bool MacabRecords::iterator::operator!= (const sal_Int32 i) const
1166 {
1167 	return(id != i);
1168 }
1169 
1170 // -------------------------------------------------------------------------
1171 sal_Bool MacabRecords::iterator::operator== (const sal_Int32 i) const
1172 {
1173 	return(id == i);
1174 }
1175 
1176 // -------------------------------------------------------------------------
1177 MacabRecord *MacabRecords::iterator::operator* () const
1178 {
1179 	return records->getRecord(id);
1180 }
1181 
1182 // -------------------------------------------------------------------------
1183 sal_Int32 MacabRecords::end() const
1184 {
1185 	return currentRecord;
1186 }
1187 
1188 // -------------------------------------------------------------------------
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 // -------------------------------------------------------------------------
1198 void MacabRecords::setName(const ::rtl::OUString _sName)
1199 {
1200 	m_sName = _sName;
1201 }
1202 
1203 // -------------------------------------------------------------------------
1204 ::rtl::OUString MacabRecords::getName() const
1205 {
1206 	return m_sName;
1207 }
1208 
1209