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 occurred).
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