diff options
Diffstat (limited to 'connectivity/source/drivers/macab/MacabRecords.cxx')
-rw-r--r-- | connectivity/source/drivers/macab/MacabRecords.cxx | 1215 |
1 files changed, 0 insertions, 1215 deletions
diff --git a/connectivity/source/drivers/macab/MacabRecords.cxx b/connectivity/source/drivers/macab/MacabRecords.cxx deleted file mode 100644 index a92e2313e9..0000000000 --- a/connectivity/source/drivers/macab/MacabRecords.cxx +++ /dev/null @@ -1,1215 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/************************************************************************* - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright 2000, 2010 Oracle and/or its affiliates. - * - * OpenOffice.org - a multi-platform office productivity suite - * - * This file is part of OpenOffice.org. - * - * OpenOffice.org is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 3 - * only, as published by the Free Software Foundation. - * - * OpenOffice.org is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License version 3 for more details - * (a copy is included in the LICENSE file that accompanied this code). - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with OpenOffice.org. If not, see - * <http://www.openoffice.org/license.html> - * for a copy of the LGPLv3 License. - * - ************************************************************************/ - -// MARKER(update_precomp.py): autogen include statement, do not remove -#include "precompiled_connectivity.hxx" - -#include "MacabRecords.hxx" -#include "MacabRecord.hxx" -#include "MacabHeader.hxx" -#include "macabutilities.hxx" - -#include <premac.h> -#include <Carbon/Carbon.h> -#include <AddressBook/ABAddressBookC.h> -#include <postmac.h> -#include <com/sun/star/util/DateTime.hpp> - -using namespace connectivity::macab; -using namespace com::sun::star::util; - -// ------------------------------------------------------------------------- -MacabRecords::MacabRecords(const ABAddressBookRef _addressBook, MacabHeader *_header, MacabRecord **_records, sal_Int32 _numRecords) -{ - /* Variables passed in... */ - header = _header; - recordsSize = _numRecords; - currentRecord = _numRecords; - records = _records; - addressBook = _addressBook; - - /* Default variables... */ - recordType = kABPersonRecordType; - - /* Variables constructed... */ - bootstrap_CF_types(); - bootstrap_requiredProperties(); -} - -// ------------------------------------------------------------------------- -/* Creates a MacabRecords from another: copies the length, name, and - * address book of the original, but the header or the records themselves. - * The idea is that the only reason to copy a MacabRecords is to create - * a filtered version of it, which can have the same length (to avoid - * resizing) and will work from the same base addressbook, but might have - * entirey different values and even (possibly in the future) a different - * header. - */ -MacabRecords::MacabRecords(const MacabRecords *_copy) -{ - /* Variables passed in... */ - recordsSize = _copy->recordsSize; - addressBook = _copy->addressBook; - m_sName = _copy->m_sName; - - /* Default variables... */ - currentRecord = 0; - header = NULL; - records = new MacabRecord *[recordsSize]; - recordType = kABPersonRecordType; - - /* Variables constructed... */ - bootstrap_CF_types(); - bootstrap_requiredProperties(); -} - -// ------------------------------------------------------------------------- -MacabRecords::MacabRecords(const ABAddressBookRef _addressBook) -{ - /* Variables passed in... */ - addressBook = _addressBook; - - /* Default variables... */ - recordsSize = 0; - currentRecord = 0; - records = NULL; - header = NULL; - recordType = kABPersonRecordType; - - /* Variables constructed... */ - bootstrap_CF_types(); - bootstrap_requiredProperties(); -} - -// ------------------------------------------------------------------------- -void MacabRecords::initialize() -{ - - /* Make sure everything is NULL before initializing. (We usually just - * initialize after we use the constructor that takes only a - * MacabAddressBook, so these variables will most likely already be - * NULL. - */ - if(records != NULL) - { - sal_Int32 i; - - for(i = 0; i < recordsSize; i++) - delete records[i]; - - delete [] records; - } - - if(header != NULL) - delete header; - - /* We can handle both default record Address Book record types in - * this method, though only kABPersonRecordType is ever used. - */ - CFArrayRef allRecords; - if(CFStringCompare(recordType, kABPersonRecordType, 0) == kCFCompareEqualTo) - allRecords = ABCopyArrayOfAllPeople(addressBook); - else - allRecords = ABCopyArrayOfAllGroups(addressBook); - - ABRecordRef record; - sal_Int32 i; - recordsSize = (sal_Int32) CFArrayGetCount(allRecords); - records = new MacabRecord *[recordsSize]; - - /* First, we create the header... */ - header = createHeaderForRecordType(allRecords, recordType); - - /* Then, we create each of the records... */ - for(i = 0; i < recordsSize; i++) - { - record = (ABRecordRef) CFArrayGetValueAtIndex(allRecords, i); - records[i] = createMacabRecord(record, header, recordType); - } - currentRecord = recordsSize; - - CFRelease(allRecords); -} - -// ------------------------------------------------------------------------- -MacabRecords::~MacabRecords() -{ -} - -// ------------------------------------------------------------------------- -void MacabRecords::setHeader(MacabHeader *_header) -{ - if(header != NULL) - delete header; - header = _header; -} - -// ------------------------------------------------------------------------- -MacabHeader *MacabRecords::getHeader() const -{ - return header; -} - -// ------------------------------------------------------------------------- -/* Inserts a MacabRecord at a given location. If there is already a - * MacabRecord at that location, return it. - */ -MacabRecord *MacabRecords::insertRecord(MacabRecord *_newRecord, const sal_Int32 _location) -{ - MacabRecord *oldRecord; - - /* If the location is greater than the current allocated size of this - * MacabRecords, allocate more space. - */ - if(_location >= recordsSize) - { - sal_Int32 i; - MacabRecord **newRecordsArray = new MacabRecord *[_location+1]; - for(i = 0; i < recordsSize; i++) - { - newRecordsArray[i] = records[i]; - } - delete [] records; - records = newRecordsArray; - } - - /* Remember: currentRecord refers to one above the highest existing - * record (i.e., it refers to where to place the next record if a - * location is not given). - */ - if(_location >= currentRecord) - currentRecord = _location+1; - - oldRecord = records[_location]; - records[_location] = _newRecord; - return oldRecord; -} - -// ------------------------------------------------------------------------- -/* Insert a record at the next available place. */ -void MacabRecords::insertRecord(MacabRecord *_newRecord) -{ - insertRecord(_newRecord, currentRecord); -} - -// ------------------------------------------------------------------------- -MacabRecord *MacabRecords::getRecord(const sal_Int32 _location) const -{ - if(_location >= recordsSize) - return NULL; - return records[_location]; -} - -// ------------------------------------------------------------------------- -macabfield *MacabRecords::getField(const sal_Int32 _recordNumber, const sal_Int32 _columnNumber) const -{ - if(_recordNumber >= recordsSize) - return NULL; - - MacabRecord *record = records[_recordNumber]; - - if(_columnNumber < 0 || _columnNumber >= record->getSize()) - return NULL; - - return record->get(_columnNumber); -} - -// ------------------------------------------------------------------------- -macabfield *MacabRecords::getField(const sal_Int32 _recordNumber, const ::rtl::OUString _columnName) const -{ - if(header != NULL) - { - sal_Int32 columnNumber = header->getColumnNumber(_columnName); - if(columnNumber == -1) - return NULL; - - return getField(_recordNumber, columnNumber); - } - else - { - // error: shouldn't access field with null header! - return NULL; - } -} - -// ------------------------------------------------------------------------- -sal_Int32 MacabRecords::getFieldNumber(const ::rtl::OUString _columnName) const -{ - if(header != NULL) - return header->getColumnNumber(_columnName); - else - // error: shouldn't access field with null header! - return -1; -} - -// ------------------------------------------------------------------------- -/* Create the lcl_CFTypes array -- we need this because there is no - * way to get the ABType of an object from the object itself, and the - * function ABTypeOfProperty can't handle multiple levels of data - * (e.g., it can tell us that "address" is of type - * kABDictionaryProperty, but it cannot tell us that all of the keys - * and values in the dictionary have type kABStringProperty. On the - * other hand, we _can_ get the CFType out of any object. - * Unfortunately, all information about CFTypeIDs comes with the - * warning that they change between releases, so we build them - * ourselves here. (The one that we can't build is for multivalues, - * e.g., kABMultiStringProperty. All of these appear to have the - * same type: 1, but there is no function that I've found to give - * us that dynamically in case that number ever changes. - */ -void MacabRecords::bootstrap_CF_types() -{ - lcl_CFTypesLength = 6; - lcl_CFTypes = new lcl_CFType[lcl_CFTypesLength]; - - lcl_CFTypes[0].cf = CFNumberGetTypeID(); - lcl_CFTypes[0].ab = kABIntegerProperty; - - lcl_CFTypes[1].cf = CFStringGetTypeID(); - lcl_CFTypes[1].ab = kABStringProperty; - - lcl_CFTypes[2].cf = CFDateGetTypeID(); - lcl_CFTypes[2].ab = kABDateProperty; - - lcl_CFTypes[3].cf = CFArrayGetTypeID(); - lcl_CFTypes[3].ab = kABArrayProperty; - - lcl_CFTypes[4].cf = CFDictionaryGetTypeID(); - lcl_CFTypes[4].ab = kABDictionaryProperty; - - lcl_CFTypes[5].cf = CFDataGetTypeID(); - lcl_CFTypes[5].ab = kABDataProperty; -} - -// ------------------------------------------------------------------------- -/* This is based on the possible fields required in the mail merge template - * in sw. If the fields possible there change, it would be optimal to - * change these fields as well. - */ -void MacabRecords::bootstrap_requiredProperties() -{ - numRequiredProperties = 7; - requiredProperties = new CFStringRef[numRequiredProperties]; - requiredProperties[0] = kABTitleProperty; - requiredProperties[1] = kABFirstNameProperty; - requiredProperties[2] = kABLastNameProperty; - requiredProperties[3] = kABOrganizationProperty; - requiredProperties[4] = kABAddressProperty; - requiredProperties[5] = kABPhoneProperty; - requiredProperties[6] = kABEmailProperty; -} - -// ------------------------------------------------------------------------- -/* Create the header for a given record type and a given array of records. - * Because the array of records and the record type are given, if you want - * to, you can run this method on the members of a group, or on any other - * filtered list of people and get a header relevant to them (e.g., if - * they only have home addresses, the work address fields won't show up). - */ -MacabHeader *MacabRecords::createHeaderForRecordType(const CFArrayRef _records, const CFStringRef _recordType) const -{ - /* We have two types of properties for a given record type, nonrequired - * and required. Required properties are ones that will show up whether - * or not they are empty. Nonrequired properties will only show up if - * at least one record in the set has that property filled. The reason - * is that some properties, like the kABTitleProperty are required by - * the mail merge wizard (in module sw) but are by default not shown in - * the Mac OS X address book, so they would be weeded out at this stage - * and not shown if they were not required. - * - * Note: with the addition of required properties, I am not sure that - * this method still works for kABGroupRecordType (since the required - * properites are all for kABPersonRecordType). - * - * Note: required properties are constructed in the method - * bootstrap_requiredProperties() (above). - */ - CFArrayRef allProperties = ABCopyArrayOfPropertiesForRecordType(addressBook, _recordType); - CFStringRef *nonRequiredProperties; - sal_Int32 numRecords = (sal_Int32) CFArrayGetCount(_records); - sal_Int32 numProperties = (sal_Int32) CFArrayGetCount(allProperties); - sal_Int32 numNonRequiredProperties = numProperties - numRequiredProperties; - - /* While searching through the properties for required properties, these - * sal_Bools will keep track of what we have found. - */ - sal_Bool bFoundProperty; - sal_Bool bFoundRequiredProperties[numRequiredProperties]; - - - /* We have three MacabHeaders: headerDataForProperty is where we - * store the result of createHeaderForProperty(), which return a - * MacabHeader for a single property. lcl_header is where we store - * the MacabHeader that we are constructing. And, nonRequiredHeader - * is where we construct the MacabHeader for non-required properties, - * so that we can sort them before adding them to lcl_header. - */ - MacabHeader *headerDataForProperty; - MacabHeader *lcl_header = new MacabHeader(); - MacabHeader *nonRequiredHeader = new MacabHeader(); - - /* Other variables... */ - sal_Int32 i, j, k; - ABRecordRef record; - CFStringRef property; - - - /* Allocate and initialize... */ - nonRequiredProperties = new CFStringRef[numNonRequiredProperties]; - k = 0; - for(i = 0; i < numRequiredProperties; i++) - bFoundRequiredProperties[i] = sal_False; - - /* Determine the non-required properties... */ - for(i = 0; i < numProperties; i++) - { - property = (CFStringRef) CFArrayGetValueAtIndex(allProperties, i); - bFoundProperty = sal_False; - for(j = 0; j < numRequiredProperties; j++) - { - if(CFEqual(property, requiredProperties[j])) - { - bFoundProperty = sal_True; - bFoundRequiredProperties[j] = sal_True; - break; - } - } - - if(bFoundProperty == sal_False) - { - /* If we have found too many non-required properties */ - if(k == numNonRequiredProperties) - { - k++; // so that the OSL_ENSURE below fails - break; - } - nonRequiredProperties[k] = property; - k++; - } - } - - // Somehow, we got too many or too few non-requird properties... - // Most likely, one of the required properties no longer exists, which - // we also test later. - OSL_ENSURE(k == numNonRequiredProperties, "MacabRecords::createHeaderForRecordType: Found an unexpected number of non-required properties"); - - /* Fill the header with required properties first... */ - for(i = 0; i < numRequiredProperties; i++) - { - if(bFoundRequiredProperties[i] == sal_True) - { - /* The order of these matters (we want all address properties - * before any phone properties, or else things will look weird), - * so we get all possibilitities for each property, going through - * each record, and then go onto the next property. - * (Note: the reason that we have to go through all records - * in the first place is that properties like address, phone, and - * e-mail are multi-value properties with an unknown number of - * values. A user could specify thirteen different kinds of - * e-mail addresses for one of her or his contacts, and we need to - * get all of them. - */ - for(j = 0; j < numRecords; j++) - { - record = (ABRecordRef) CFArrayGetValueAtIndex(_records, j); - headerDataForProperty = createHeaderForProperty(record,requiredProperties[i],_recordType,sal_True); - if(headerDataForProperty != NULL) - { - (*lcl_header) += headerDataForProperty; - delete headerDataForProperty; - } - } - } - else - { - // Couldn't find a required property... - OSL_FAIL(::rtl::OString("MacabRecords::createHeaderForRecordType: could not find required property: ") + - ::rtl::OUStringToOString(CFStringToOUString(requiredProperties[i]), RTL_TEXTENCODING_ASCII_US)); - } - } - - /* And now, non-required properties... */ - for(i = 0; i < numRecords; i++) - { - record = (ABRecordRef) CFArrayGetValueAtIndex(_records, i); - - for(j = 0; j < numNonRequiredProperties; j++) - { - property = nonRequiredProperties[j]; - headerDataForProperty = createHeaderForProperty(record,property,_recordType,sal_False); - if(headerDataForProperty != NULL) - { - (*nonRequiredHeader) += headerDataForProperty; - delete headerDataForProperty; - } - } - - } - nonRequiredHeader->sortRecord(); - - (*lcl_header) += nonRequiredHeader; - delete nonRequiredHeader; - - CFRelease(allProperties); - delete [] nonRequiredProperties; - - return lcl_header; -} - -// ------------------------------------------------------------------------- -/* Create a header for a single property. Basically, this method gets - * the property's value and type and then calls another method of - * the same name to do the dirty work. - */ -MacabHeader *MacabRecords::createHeaderForProperty(const ABRecordRef _record, const CFStringRef _propertyName, const CFStringRef _recordType, const sal_Bool _isPropertyRequired) const -{ - // local variables - CFStringRef localizedPropertyName; - CFTypeRef propertyValue; - ABPropertyType propertyType; - MacabHeader *result; - - /* Get the property's value */ - propertyValue = ABRecordCopyValue(_record,_propertyName); - if(propertyValue == NULL && _isPropertyRequired == sal_False) - return NULL; - - propertyType = ABTypeOfProperty(addressBook, _recordType, _propertyName); - localizedPropertyName = ABCopyLocalizedPropertyOrLabel(_propertyName); - - result = createHeaderForProperty(propertyType, propertyValue, localizedPropertyName); - - if(propertyValue != NULL) - CFRelease(propertyValue); - - return result; -} - -// ------------------------------------------------------------------------- -/* Create a header for a single property. This method is recursive - * because a single property might contain several sub-properties that - * we also want to treat singly. - */ -MacabHeader *MacabRecords::createHeaderForProperty(const ABPropertyType _propertyType, const CFTypeRef _propertyValue, const CFStringRef _propertyName) const -{ - macabfield **headerNames = NULL; - sal_Int32 length = 0; - - switch(_propertyType) - { - /* Scalars */ - case kABStringProperty: - case kABRealProperty: - case kABIntegerProperty: - case kABDateProperty: - length = 1; - headerNames = new macabfield *[1]; - headerNames[0] = new macabfield; - headerNames[0]->value = _propertyName; - headerNames[0]->type = _propertyType; - break; - - /* Multi-scalars */ - case kABMultiIntegerProperty: - case kABMultiDateProperty: - case kABMultiStringProperty: - case kABMultiRealProperty: - case kABMultiDataProperty: - /* For non-scalars, we can only get more information if the property - * actually exists. - */ - if(_propertyValue != NULL) - { - sal_Int32 i; - - sal_Int32 multiLength = ABMultiValueCount((ABMutableMultiValueRef) _propertyValue); - CFStringRef multiLabel, localizedMultiLabel; - ::rtl::OUString multiLabelString; - ::rtl::OUString multiPropertyString; - ::rtl::OUString headerNameString; - ABPropertyType multiType = (ABPropertyType) (ABMultiValuePropertyType((ABMutableMultiValueRef) _propertyValue) - 0x100); - - length = multiLength; - headerNames = new macabfield *[multiLength]; - multiPropertyString = CFStringToOUString(_propertyName); - - /* Go through each element, and - since each element is a scalar - - * just create a new macabfield for it. - */ - for(i = 0; i < multiLength; i++) - { - multiLabel = ABMultiValueCopyLabelAtIndex((ABMutableMultiValueRef) _propertyValue, i); - localizedMultiLabel = ABCopyLocalizedPropertyOrLabel(multiLabel); - multiLabelString = CFStringToOUString(localizedMultiLabel); - CFRelease(multiLabel); - CFRelease(localizedMultiLabel); - headerNameString = multiPropertyString + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(": ")) + fixLabel(multiLabelString); - headerNames[i] = new macabfield; - headerNames[i]->value = OUStringToCFString(headerNameString); - headerNames[i]->type = multiType; - } - } - break; - - /* Multi-array or dictionary */ - case kABMultiArrayProperty: - case kABMultiDictionaryProperty: - /* For non-scalars, we can only get more information if the property - * actually exists. - */ - if(_propertyValue != NULL) - { - sal_Int32 i,j,k; - - // Total number of multi-array or multi-dictionary elements. - sal_Int32 multiLengthFirstLevel = ABMultiValueCount((ABMutableMultiValueRef) _propertyValue); - - /* Total length, including the length of each element (e.g., if - * this multi-dictionary contains three dictionaries, and each - * dictionary has four elements, this variable will be twelve, - * whereas multiLengthFirstLevel will be three. - */ - sal_Int32 multiLengthSecondLevel = 0; - - CFStringRef multiLabel, localizedMultiLabel; - CFTypeRef multiValue; - ::rtl::OUString multiLabelString; - ::rtl::OUString multiPropertyString; - MacabHeader **multiHeaders = new MacabHeader *[multiLengthFirstLevel]; - ABPropertyType multiType = (ABPropertyType) (ABMultiValuePropertyType((ABMutableMultiValueRef) _propertyValue) - 0x100); - - multiPropertyString = CFStringToOUString(_propertyName); - - /* Go through each element - since each element can really - * contain anything, we run this method again on each element - * and store the resulting MacabHeader (in the multiHeaders - * array). Then, all we'll have to do is combine the MacabHeaders - * into a single one. - */ - for(i = 0; i < multiLengthFirstLevel; i++) - { - /* label */ - multiLabel = ABMultiValueCopyLabelAtIndex((ABMutableMultiValueRef) _propertyValue, i); - multiValue = ABMultiValueCopyValueAtIndex((ABMutableMultiValueRef) _propertyValue, i); - if(multiValue && multiLabel) - { - localizedMultiLabel = ABCopyLocalizedPropertyOrLabel(multiLabel); - multiLabelString = multiPropertyString + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(": ")) + fixLabel(CFStringToOUString(localizedMultiLabel)); - CFRelease(multiLabel); - CFRelease(localizedMultiLabel); - multiLabel = OUStringToCFString(multiLabelString); - multiHeaders[i] = createHeaderForProperty(multiType, multiValue, multiLabel); - if (!multiHeaders[i]) - multiHeaders[i] = new MacabHeader(); - multiLengthSecondLevel += multiHeaders[i]->getSize(); - } - else - { - multiHeaders[i] = new MacabHeader(); - } - if(multiValue) - CFRelease(multiValue); - } - - /* We now have enough information to create our final MacabHeader. - * We go through each field of each header and add it to the - * headerNames array (which is what is used below to construct - * the MacabHeader we return). - */ - length = multiLengthSecondLevel; - headerNames = new macabfield *[multiLengthSecondLevel]; - - for(i = 0, j = 0, k = 0; i < multiLengthSecondLevel; i++,k++) - { - while(multiHeaders[j]->getSize() == k) - { - j++; - k = 0; - } - - headerNames[i] = multiHeaders[j]->copy(k); - } - for(i = 0; i < multiLengthFirstLevel; i++) - delete multiHeaders[i]; - - delete [] multiHeaders; - } - break; - - /* Dictionary */ - case kABDictionaryProperty: - /* For non-scalars, we can only get more information if the property - * actually exists. - */ - if(_propertyValue != NULL) - { - /* Assume all keys are strings */ - sal_Int32 numRecords = (sal_Int32) CFDictionaryGetCount((CFDictionaryRef) _propertyValue); - - /* The only method for getting info out of a CFDictionary, of both - * keys and values, is to all of them all at once, so these - * variables will hold them. - */ - CFStringRef *dictKeys; - CFTypeRef *dictValues; - - sal_Int32 i,j,k; - ::rtl::OUString dictKeyString, propertyNameString; - ABPropertyType dictType; - MacabHeader **dictHeaders = new MacabHeader *[numRecords]; - ::rtl::OUString dictLabelString; - CFStringRef dictLabel, localizedDictKey; - - /* Get the keys and values */ - dictKeys = (CFStringRef *) malloc(sizeof(CFStringRef)*numRecords); - dictValues = (CFTypeRef *) malloc(sizeof(CFTypeRef)*numRecords); - CFDictionaryGetKeysAndValues((CFDictionaryRef) _propertyValue, (const void **) dictKeys, (const void **) dictValues); - - propertyNameString = CFStringToOUString(_propertyName); - - length = 0; - /* Go through each element - assuming that the key is a string but - * that the value could be anything. Since the value could be - * anything, we can't assume that it is scalar (it could even be - * another dictionary), so we attempt to get its type using - * the method getABTypeFromCFType and then run this method - * recursively on that element, storing the MacabHeader that - * results. Then, we just combine all of the MacabHeaders into - * one. - */ - for(i = 0; i < numRecords; i++) - { - dictType = (ABPropertyType) getABTypeFromCFType( CFGetTypeID(dictValues[i]) ); - localizedDictKey = ABCopyLocalizedPropertyOrLabel(dictKeys[i]); - dictKeyString = CFStringToOUString(localizedDictKey); - dictLabelString = propertyNameString + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(": ")) + fixLabel(dictKeyString); - dictLabel = OUStringToCFString(dictLabelString); - dictHeaders[i] = createHeaderForProperty(dictType, dictValues[i], dictLabel); - if (!dictHeaders[i]) - dictHeaders[i] = new MacabHeader(); - length += dictHeaders[i]->getSize(); - CFRelease(dictLabel); - CFRelease(localizedDictKey); - } - - /* Combine all of the macabfields in each MacabHeader into the - * headerNames array, which (at the end of this method) is used - * to create the MacabHeader that is returned. - */ - headerNames = new macabfield *[length]; - for(i = 0, j = 0, k = 0; i < length; i++,k++) - { - while(dictHeaders[j]->getSize() == k) - { - j++; - k = 0; - } - - headerNames[i] = dictHeaders[j]->copy(k); - } - - for(i = 0; i < numRecords; i++) - delete dictHeaders[i]; - - delete [] dictHeaders; - free(dictKeys); - free(dictValues); - } - break; - - /* Array */ - case kABArrayProperty: - /* For non-scalars, we can only get more information if the property - * actually exists. - */ - if(_propertyValue != NULL) - { - sal_Int32 arrLength = (sal_Int32) CFArrayGetCount( (CFArrayRef) _propertyValue); - sal_Int32 i,j,k; - CFTypeRef arrValue; - ABPropertyType arrType; - MacabHeader **arrHeaders = new MacabHeader *[arrLength]; - ::rtl::OUString propertyNameString = CFStringToOUString(_propertyName); - ::rtl::OUString arrLabelString; - CFStringRef arrLabel; - - length = 0; - /* Go through each element - since the elements here do not have - * unique keys like the ones in dictionaries, we create a unique - * key out of the id of the element in the array (the first - * element gets a 0 plopped onto the end of it, the second a 1... - * As with dictionaries, the elements could be anything, including - * another array, so we have to run this method recursively on - * each element, storing the resulting MacabHeader into an array, - * which we then combine into one MacabHeader that is returned. - */ - for(i = 0; i < arrLength; i++) - { - arrValue = (CFTypeRef) CFArrayGetValueAtIndex( (CFArrayRef) _propertyValue, i); - arrType = (ABPropertyType) getABTypeFromCFType( CFGetTypeID(arrValue) ); - arrLabelString = propertyNameString + ::rtl::OUString::valueOf(i); - arrLabel = OUStringToCFString(arrLabelString); - arrHeaders[i] = createHeaderForProperty(arrType, arrValue, arrLabel); - if (!arrHeaders[i]) - arrHeaders[i] = new MacabHeader(); - length += arrHeaders[i]->getSize(); - CFRelease(arrLabel); - } - - headerNames = new macabfield *[length]; - for(i = 0, j = 0, k = 0; i < length; i++,k++) - { - while(arrHeaders[j]->getSize() == k) - { - j++; - k = 0; - } - - headerNames[i] = arrHeaders[j]->copy(k); - } - for(i = 0; i < arrLength; i++) - delete arrHeaders[i]; - - delete [] arrHeaders; - } - break; - - default: - break; - - } - - /* If we succeeded at adding elements to the headerNames array, then - * length will no longer be 0. If it is, create a new MacabHeader - * out of the headerNames (after weeding out duplicate headers), and - * then return the result. If the length is still 0, return NULL: we - * failed to create a MacabHeader out of this property. - */ - if(length != 0) - { - manageDuplicateHeaders(headerNames, length); - MacabHeader *headerResult = new MacabHeader(length, headerNames); - delete [] headerNames; - return headerResult; - } - else - return NULL; -} - -// ------------------------------------------------------------------------- -void MacabRecords::manageDuplicateHeaders(macabfield **_headerNames, const sal_Int32 _length) const -{ - /* If we have two cases of, say, phone: home, this makes it: - * phone: home (1) - * phone: home (2) - */ - sal_Int32 i, j; - sal_Int32 count; - for(i = _length-1; i >= 0; i--) - { - count = 1; - for( j = i-1; j >= 0; j--) - { - if(CFEqual(_headerNames[i]->value, _headerNames[j]->value)) - { - count++; - } - } - - // duplicate! - if(count != 1) - { - // There is probably a better way to do this... - ::rtl::OUString newName = CFStringToOUString((CFStringRef) _headerNames[i]->value); - CFRelease(_headerNames[i]->value); - newName += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" (")) + ::rtl::OUString::valueOf(count) + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(")")); - _headerNames[i]->value = OUStringToCFString(newName); - } - } -} - -// ------------------------------------------------------------------------- -/* Create a MacabRecord out of an ABRecord, using a given MacabHeader and - * the record's type. We go through each property for this record type - * then process it much like we processed the header (above), with two - * exceptions: if we come upon something not in the header, we ignore it - * (it's something we don't want to add), and once we find a corresponding - * location in the header, we store the property and the property type in - * a macabfield. (For the header, we stored the property type and the name - * of the property as a CFString.) - */ -MacabRecord *MacabRecords::createMacabRecord(const ABRecordRef _abrecord, const MacabHeader *_header, const CFStringRef _recordType) const -{ - /* The new record that we will create... */ - MacabRecord *macabRecord = new MacabRecord(_header->getSize()); - - CFArrayRef recordProperties = ABCopyArrayOfPropertiesForRecordType(addressBook, _recordType); - sal_Int32 numProperties = (sal_Int32) CFArrayGetCount(recordProperties); - - sal_Int32 i; - - CFTypeRef propertyValue; - ABPropertyType propertyType; - - CFStringRef propertyName, localizedPropertyName; - ::rtl::OUString propertyNameString; - for(i = 0; i < numProperties; i++) - { - propertyName = (CFStringRef) CFArrayGetValueAtIndex(recordProperties, i); - localizedPropertyName = ABCopyLocalizedPropertyOrLabel(propertyName); - propertyNameString = CFStringToOUString(localizedPropertyName); - CFRelease(localizedPropertyName); - - /* Get the property's value */ - propertyValue = ABRecordCopyValue(_abrecord,propertyName); - if(propertyValue != NULL) - { - propertyType = ABTypeOfProperty(addressBook, _recordType, propertyName); - if(propertyType != kABErrorInProperty) - insertPropertyIntoMacabRecord(propertyType, macabRecord, _header, propertyNameString, propertyValue); - - CFRelease(propertyValue); - } - } - CFRelease(recordProperties); - return macabRecord; -} - -// ------------------------------------------------------------------------- -/* Inserts a given property into a MacabRecord. This method calls another - * method by the same name after getting the property type (it only - * receives the property value). It is called when we aren't given the - * property's type already. - */ -void MacabRecords::insertPropertyIntoMacabRecord(MacabRecord *_abrecord, const MacabHeader *_header, const ::rtl::OUString _propertyName, const CFTypeRef _propertyValue) const -{ - CFTypeID cf_type = CFGetTypeID(_propertyValue); - ABPropertyType ab_type = getABTypeFromCFType( cf_type ); - - if(ab_type != kABErrorInProperty) - insertPropertyIntoMacabRecord(ab_type, _abrecord, _header, _propertyName, _propertyValue); -} - -// ------------------------------------------------------------------------- -/* Inserts a given property into a MacabRecord. This method is recursive - * because properties can contain many sub-properties. - */ -void MacabRecords::insertPropertyIntoMacabRecord(const ABPropertyType _propertyType, MacabRecord *_abrecord, const MacabHeader *_header, const ::rtl::OUString _propertyName, const CFTypeRef _propertyValue) const -{ - /* If there is no value, return */ - if(_propertyValue == NULL) - return; - - /* The main switch statement */ - switch(_propertyType) - { - /* Scalars */ - case kABStringProperty: - case kABRealProperty: - case kABIntegerProperty: - case kABDateProperty: - { - /* Only scalars actually insert a property into the MacabRecord. - * In all other cases, this method is called recursively until a - * scalar type, an error, or an unknown type are found. - * Because of that, the following checks only occur for this type. - * We store whether we have successfully placed this property - * into the MacabRecord (or whether an unrecoverable error occurred). - * Then, we try over and over again to place the property into the - * record. There are three possible results: - * 1) Success! - * 2) There is already a property stored at the column of this name, - * in which case we have a duplicate header (see the method - * manageDuplicateHeaders()). If that is the case, we add an ID - * to the end of the column name in the same format as we do in - * manageDuplicateHeaders() and try again. - * 3) No column of this name exists in the header. In this case, - * there is nothing we can do: we have failed to place this - * property into the record. - */ - sal_Bool bPlaced = sal_False; - ::rtl::OUString columnName = ::rtl::OUString(_propertyName); - sal_Int32 i = 1; - - // A big safeguard to prevent two fields from having the same name. - while(bPlaced != sal_True) - { - sal_Int32 columnNumber = _header->getColumnNumber(columnName); - bPlaced = sal_True; - if(columnNumber != -1) - { - // collision! A property already exists here! - if(_abrecord->get(columnNumber) != NULL) - { - bPlaced = sal_False; - i++; - columnName = ::rtl::OUString(_propertyName) + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" (")) + ::rtl::OUString::valueOf(i) + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(")")); - } - - // success! - else - { - _abrecord->insertAtColumn(_propertyValue, _propertyType, columnNumber); - } - } - } - } - break; - - /* Array */ - case kABArrayProperty: - { - /* An array is basically just a list of anything, so all we do - * is go through the array, and rerun this method recursively - * on each element. - */ - sal_Int32 arrLength = (sal_Int32) CFArrayGetCount( (CFArrayRef) _propertyValue); - sal_Int32 i; - const void *arrValue; - ::rtl::OUString newPropertyName; - - /* Going through each element... */ - for(i = 0; i < arrLength; i++) - { - arrValue = CFArrayGetValueAtIndex( (CFArrayRef) _propertyValue, i); - newPropertyName = _propertyName + ::rtl::OUString::valueOf(i); - insertPropertyIntoMacabRecord(_abrecord, _header, newPropertyName, arrValue); - CFRelease(arrValue); - } - - } - break; - - /* Dictionary */ - case kABDictionaryProperty: - { - /* A dictionary is basically a hashmap. Technically, it can - * hold any object as a key and any object as a value. - * For our case, we assume that the key is a string (so that - * we can use the key to get the column name and match it against - * the header), but we don't assume anything about the value, so - * we run this method recursively (or, rather, we run the version - * of this method for when we don't know the object's type) until - * we hit a scalar value. - */ - - sal_Int32 numRecords = (sal_Int32) CFDictionaryGetCount((CFDictionaryRef) _propertyValue); - ::rtl::OUString dictKeyString; - sal_Int32 i; - ::rtl::OUString newPropertyName; - - /* Unfortunately, the only way to get both keys and values out - * of a dictionary in Carbon is to get them all at once, so we - * do that. - */ - CFStringRef *dictKeys; - CFStringRef localizedDictKey; - CFTypeRef *dictValues; - dictKeys = (CFStringRef *) malloc(sizeof(CFStringRef)*numRecords); - dictValues = (CFTypeRef *) malloc(sizeof(CFTypeRef)*numRecords); - CFDictionaryGetKeysAndValues((CFDictionaryRef) _propertyValue, (const void **) dictKeys, (const void **) dictValues); - - /* Going through each element... */ - for(i = 0; i < numRecords; i++) - { - localizedDictKey = ABCopyLocalizedPropertyOrLabel(dictKeys[i]); - dictKeyString = CFStringToOUString(localizedDictKey); - CFRelease(localizedDictKey); - newPropertyName = _propertyName + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(": ")) + fixLabel(dictKeyString); - insertPropertyIntoMacabRecord(_abrecord, _header, newPropertyName, dictValues[i]); - } - - free(dictKeys); - free(dictValues); - } - break; - - /* Multivalue */ - case kABMultiIntegerProperty: - case kABMultiDateProperty: - case kABMultiStringProperty: - case kABMultiRealProperty: - case kABMultiDataProperty: - case kABMultiDictionaryProperty: - case kABMultiArrayProperty: - { - /* All scalar multivalues are handled in the same way. Each element - * is a label and a value. All labels are strings - * (kABStringProperty), and all values have the same type - * (which is the type of the multivalue minus 255, or as - * Carbon's list of property types has it, minus 0x100. - * We just get the correct type, then go through each element - * and get the label and value and print them in a list. - */ - - sal_Int32 i; - sal_Int32 multiLength = ABMultiValueCount((ABMutableMultiValueRef) _propertyValue); - CFStringRef multiLabel, localizedMultiLabel; - CFTypeRef multiValue; - ::rtl::OUString multiLabelString, newPropertyName; - ABPropertyType multiType = (ABPropertyType) (ABMultiValuePropertyType((ABMutableMultiValueRef) _propertyValue) - 0x100); - - /* Go through each element... */ - for(i = 0; i < multiLength; i++) - { - /* Label and value */ - multiLabel = ABMultiValueCopyLabelAtIndex((ABMutableMultiValueRef) _propertyValue, i); - multiValue = ABMultiValueCopyValueAtIndex((ABMutableMultiValueRef) _propertyValue, i); - - localizedMultiLabel = ABCopyLocalizedPropertyOrLabel(multiLabel); - multiLabelString = CFStringToOUString(localizedMultiLabel); - newPropertyName = _propertyName + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(": ")) + fixLabel(multiLabelString); - insertPropertyIntoMacabRecord(multiType, _abrecord, _header, newPropertyName, multiValue); - - /* free our variables */ - CFRelease(multiLabel); - CFRelease(localizedMultiLabel); - CFRelease(multiValue); - } - } - break; - - /* Unhandled types */ - case kABErrorInProperty: - case kABDataProperty: - default: - /* An error, as far as I have seen, only shows up as a type - * returned by a function for dictionaries when the dictionary - * holds many types of values. Since we do not use that function, - * it shouldn't come up. I have yet to see the kABDataProperty, - * and I am not sure how to represent it as a string anyway, - * since it appears to just be a bunch of bytes. Assumably, if - * these bytes made up a string, the type would be - * kABStringProperty. I think that this is used when we are not - * sure what the type is (e.g., it could be a string or a number). - * That being the case, I still don't know how to represent it. - * And, default should never come up, since we've exhausted all - * of the possible types for ABPropertyType, but... just in case. - */ - break; - } - -} - -// ------------------------------------------------------------------------- -ABPropertyType MacabRecords::getABTypeFromCFType(const CFTypeID cf_type ) const -{ - sal_Int32 i; - for(i = 0; i < lcl_CFTypesLength; i++) - { - /* A match! */ - if(lcl_CFTypes[i].cf == (sal_Int32) cf_type) - { - return (ABPropertyType) lcl_CFTypes[i].ab; - } - } - return kABErrorInProperty; -} - -// ------------------------------------------------------------------------- -sal_Int32 MacabRecords::size() const -{ - return currentRecord; -} - -// ------------------------------------------------------------------------- -MacabRecords *MacabRecords::begin() -{ - return this; -} - -// ------------------------------------------------------------------------- -MacabRecords::iterator::iterator () -{ -} - -// ------------------------------------------------------------------------- -MacabRecords::iterator::~iterator () -{ -} - -// ------------------------------------------------------------------------- -MacabRecords::iterator& MacabRecords::iterator::operator= (MacabRecords *_records) -{ - id = 0; - records = _records; - return *this; -} - -// ------------------------------------------------------------------------- -void MacabRecords::iterator::operator++ () -{ - id++; -} - -// ------------------------------------------------------------------------- -sal_Bool MacabRecords::iterator::operator!= (const sal_Int32 i) const -{ - return(id != i); -} - -// ------------------------------------------------------------------------- -sal_Bool MacabRecords::iterator::operator== (const sal_Int32 i) const -{ - return(id == i); -} - -// ------------------------------------------------------------------------- -MacabRecord *MacabRecords::iterator::operator* () const -{ - return records->getRecord(id); -} - -// ------------------------------------------------------------------------- -sal_Int32 MacabRecords::end() const -{ - return currentRecord; -} - -// ------------------------------------------------------------------------- -void MacabRecords::swap(const sal_Int32 _id1, const sal_Int32 _id2) -{ - MacabRecord *swapRecord = records[_id1]; - - records[_id1] = records[_id2]; - records[_id2] = swapRecord; -} - -// ------------------------------------------------------------------------- -void MacabRecords::setName(const ::rtl::OUString _sName) -{ - m_sName = _sName; -} - -// ------------------------------------------------------------------------- -::rtl::OUString MacabRecords::getName() const -{ - return m_sName; -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |