/* Make sure everything is NULL before initializing. (We usually just *initializeafterweusetheconstructorthattakesonlya *MacabAddressBook,sothesevariableswillmostlikelyalreadybe *NULL.
*/ if(records != nullptr)
{
sal_Int32 i;
for(i = 0; i < recordsSize; i++) delete records[i];
delete [] records;
}
if(header != nullptr) delete header;
/* We can handle both default record Address Book record types in *thismethod,thoughonlykABPersonRecordTypeiseverused.
*/
CFArrayRef allRecords; if(CFStringCompare(recordType, kABPersonRecordType, 0) == kCFCompareEqualTo)
allRecords = ABCopyArrayOfAllPeople(addressBook); else
allRecords = ABCopyArrayOfAllGroups(addressBook);
ABRecordRef record;
sal_Int32 i;
recordsSize = static_cast<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 = const_cast<ABRecordRef>(CFArrayGetValueAtIndex(allRecords, i));
records[i] = createMacabRecord(record, header, recordType);
}
currentRecord = recordsSize;
/* Inserts a MacabRecord at a given location. If there is already a *MacabRecordatthatlocation,returnit.
*/
MacabRecord *MacabRecords::insertRecord(MacabRecord *_newRecord, const sal_Int32 _location)
{
MacabRecord *oldRecord;
/* If the location is greater than the current allocated size of this *MacabRecords,allocatemorespace.
*/ 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.,itreferstowheretoplacethenextrecordifa *locationisnotgiven).
*/ if(_location >= currentRecord)
currentRecord = _location+1;
/* Insert a record at the next available place. */ void MacabRecords::insertRecord(MacabRecord *_newRecord)
{
insertRecord(_newRecord, currentRecord);
}
/* Create the lcl_CFTypes array -- we need this because there is no *waytogettheABTypeofanobjectfromtheobjectitself,andthe *functionABTypeOfPropertycan'thandlemultiplelevelsofdata *(e.g.,itcantellusthat"address"isoftype *kABDictionaryProperty,butitcannottellusthatallofthekeys *andvaluesinthedictionaryhavetypekABStringProperty.Onthe *otherhand,we_can_gettheCFTypeoutofanyobject. *Unfortunately,allinformationaboutCFTypeIDscomeswiththe *warningthattheychangebetweenreleases,sowebuildthem *ourselveshere.(Theonethatwecan'tbuildisformultivalues, *e.g.,kABMultiStringProperty.Alloftheseappeartohavethe *sametype:1,butthereisnofunctionthatI'vefoundtogive *usthatdynamicallyincasethatnumbereverchanges.
*/ void MacabRecords::bootstrap_CF_types()
{
lcl_CFTypes = {
{CFNumberGetTypeID(), kABIntegerProperty},
{CFStringGetTypeID(), kABStringProperty},
{CFDateGetTypeID(), kABDateProperty},
{CFArrayGetTypeID(), kABArrayProperty},
{CFDictionaryGetTypeID(), kABDictionaryProperty},
{CFDataGetTypeID(), kABDataProperty}};
}
/* This is based on the possible fields required in the mail merge template *insw.Ifthefieldspossibletherechange,itwouldbeoptimalto *changethesefieldsaswell.
*/ void MacabRecords::bootstrap_requiredProperties()
{
requiredProperties = {
kABTitleProperty, kABFirstNameProperty, kABLastNameProperty, kABOrganizationProperty,
kABAddressProperty, kABPhoneProperty, kABEmailProperty};
}
bool shouldSkipProperty(CFStringRef propertyName)
{ /* Skip Note property to help prevent the following exception: * *CoreData:error:Unhandlederroroccurredduringfaulting:ErrorDomain=NSCocoaErrorDomainCode=134092"(null)" * *whichoccursbecausewedonothaveanentitlementtoaccesscontactnotes. *Seehttps://developer.apple.com/documentation/bundleresources/entitlements/com.apple.developer.contacts.notes *formoredetails.
*/ if (CFStringCompare(propertyName, CFSTR("Note"), 0) == kCFCompareEqualTo) returntrue;
returnfalse;
}
/* Create the header for a given record type and a given array of records. *Becausethearrayofrecordsandtherecordtypearegiven,ifyouwant *to,youcanrunthismethodonthemembersofagroup,oronanyother *filteredlistofpeopleandgetaheaderrelevanttothem(e.g.,if *theyonlyhavehomeaddresses,theworkaddressfieldswon'tshowup).
*/
MacabHeader *MacabRecords::createHeaderForRecordType(const CFArrayRef _records, const CFStringRef _recordType) const
{ /* We have two types of properties for a given record type, nonrequired *andrequired.Requiredpropertiesareonesthatwillshowupwhether *ornottheyareempty.Nonrequiredpropertieswillonlyshowupif *atleastonerecordinthesethasthatpropertyfilled.Thereason *isthatsomeproperties,likethekABTitlePropertyarerequiredby *themailmergewizard(inmodulesw)butarebydefaultnotshownin *themacOSaddressbook,sotheywouldbeweededoutatthisstage *andnotshowniftheywerenotrequired. * *Note:withtheadditionofrequiredproperties,Iamnotsurethat *thismethodstillworksforkABGroupRecordType(sincetherequired *propertiesareallforkABPersonRecordType). * *Note:requiredpropertiesareconstructedinthemethod *bootstrap_requiredProperties()(above).
*/
CFArrayRef allProperties = ABCopyArrayOfPropertiesForRecordType(addressBook, _recordType);
CFStringRef *nonRequiredProperties;
sal_Int32 numRecords = static_cast<sal_Int32>(CFArrayGetCount(_records));
sal_Int32 numProperties = static_cast<sal_Int32>(CFArrayGetCount(allProperties));
sal_Int32 numNonRequiredProperties = numProperties - requiredProperties.size();
/* While searching through the properties for required properties, these *sal_Boolswillkeeptrackofwhatwehavefound.
*/ autoconst bFoundRequiredProperties = std::make_unique<bool[]>(requiredProperties.size());
/* We have three MacabHeaders: headerDataForProperty is where we *storetheresultofcreateHeaderForProperty(),whichreturna *MacabHeaderforasingleproperty.lcl_headeriswherewestore *theMacabHeaderthatweareconstructing.And,nonRequiredHeader *iswhereweconstructtheMacabHeaderfornon-requiredproperties, *sothatwecansortthembeforeaddingthemtolcl_header.
*/
MacabHeader *headerDataForProperty;
MacabHeader *lcl_header = new MacabHeader();
MacabHeader *nonRequiredHeader = new MacabHeader();
/* Other variables... */
sal_Int32 k;
ABRecordRef record;
CFStringRef property;
/* Allocate and initialize... */
nonRequiredProperties = new CFStringRef[numNonRequiredProperties];
k = 0; for(std::vector<CFStringRef>::size_type i = 0; i < requiredProperties.size(); i++)
bFoundRequiredProperties[i] = false;
if(!bFoundProperty)
{ /* 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-required 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(std::vector<CFStringRef>::size_type i = 0; i < requiredProperties.size(); i++)
{ if(bFoundRequiredProperties[i])
{ /* The order of these matters (we want all address properties *beforeanyphoneproperties,orelsethingswilllookweird), *sowegetallpossibilitiesforeachproperty,goingthrough *eachrecord,andthengoontothenextproperty. *(Note:thereasonthatwehavetogothroughallrecords *inthefirstplaceisthatpropertieslikeaddress,phone,and *e-mailaremulti-valuepropertieswithanunknownnumberof *values.Ausercouldspecifythirteendifferentkindsof *e-mailaddressesforoneofherorhiscontacts,andweneedto *getallofthem.
*/ for(sal_Int32 j = 0; j < numRecords; j++)
{
record = const_cast<ABRecordRef>(CFArrayGetValueAtIndex(_records, j));
headerDataForProperty = createHeaderForProperty(record,requiredProperties[i],_recordType,true); if(headerDataForProperty != nullptr)
{
(*lcl_header) += headerDataForProperty; delete headerDataForProperty;
}
}
} else
{ // Couldn't find a required property...
OSL_FAIL(OString("MacabRecords::createHeaderForRecordType: could not find required property: " +
OUStringToOString(CFStringToOUString(requiredProperties[i]), RTL_TEXTENCODING_ASCII_US)).getStr());
}
}
/* And now, non-required properties... */ for(sal_Int32 i = 0; i < numRecords; i++)
{
record = const_cast<ABRecordRef>(CFArrayGetValueAtIndex(_records, i));
/* Create a header for a single property. This method is recursive *becauseasinglepropertymightcontainseveralsub-propertiesthat *wealsowanttotreatsingly.
*/
MacabHeader *MacabRecords::createHeaderForProperty(const ABPropertyType _propertyType, const CFTypeRef _propertyValue, const CFStringRef _propertyName) const
{
macabfield **headerNames = nullptr;
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; if(headerNames[0]->value)
CFRetain(_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 *actuallyexists.
*/ if(_propertyValue != nullptr)
{
sal_Int32 i;
/* Go through each element, and - since each element is a scalar - *justcreateanewmacabfieldforit.
*/ for(i = 0; i < multiLength; i++)
{
multiLabel = ABMultiValueCopyLabelAtIndex(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue)), i);
localizedMultiLabel = ABCopyLocalizedPropertyOrLabel(multiLabel);
multiLabelString = CFStringToOUString(localizedMultiLabel);
CFRelease(multiLabel);
CFRelease(localizedMultiLabel);
headerNameString = multiPropertyString + ": " + 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 *actuallyexists.
*/ if(_propertyValue != nullptr)
{
sal_Int32 i,j,k;
// Total number of multi-array or multi-dictionary elements.
sal_Int32 multiLengthFirstLevel = ABMultiValueCount(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue)));
/* Total length, including the length of each element (e.g., if *thismulti-dictionarycontainsthreedictionaries,andeach *dictionaryhasfourelements,thisvariablewillbetwelve, *whereasmultiLengthFirstLevelwillbethree.
*/
sal_Int32 multiLengthSecondLevel = 0;
/* Go through each element - since each element can really *containanything,werunthismethodagainoneachelement *andstoretheresultingMacabHeader(inthemultiHeaders *array).Then,allwe'llhavetodoiscombinetheMacabHeaders *intoasingleone.
*/ for(i = 0; i < multiLengthFirstLevel; i++)
{ /* label */
multiLabel = ABMultiValueCopyLabelAtIndex(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue)), i);
multiValue = ABMultiValueCopyValueAtIndex(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue)), i);
std::unique_ptr<MacabHeader> hdr; if(multiValue && multiLabel)
{
localizedMultiLabel = ABCopyLocalizedPropertyOrLabel(multiLabel);
multiLabelString = multiPropertyString + ": " + fixLabel(CFStringToOUString(localizedMultiLabel));
CFRelease(multiLabel);
CFRelease(localizedMultiLabel);
multiLabel = OUStringToCFString(multiLabelString);
hdr.reset(createHeaderForProperty(multiType, multiValue, multiLabel)); if (!hdr)
hdr = std::make_unique<MacabHeader>();
multiLengthSecondLevel += hdr->getSize();
} else
{
hdr = std::make_unique<MacabHeader>();
} if(multiValue)
CFRelease(multiValue); if(multiLabel)
CFRelease(multiLabel);
multiHeaders.push_back(std::move(hdr));
}
/* We now have enough information to create our final MacabHeader. *Wegothrougheachfieldofeachheaderandaddittothe *headerNamesarray(whichiswhatisusedbelowtoconstruct *theMacabHeaderwereturn).
*/
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;
}
/* Dictionary */ case kABDictionaryProperty: /* For non-scalars, we can only get more information if the property *actuallyexists.
*/ if(_propertyValue != nullptr)
{ /* Assume all keys are strings */
sal_Int32 numRecords = static_cast<sal_Int32>(CFDictionaryGetCount(static_cast<CFDictionaryRef>(_propertyValue)));
/* The only method for getting info out of a CFDictionary, of both *keysandvalues,istoallofthemallatonce,sothese *variableswillholdthem.
*/
CFStringRef *dictKeys;
CFTypeRef *dictValues;
length = 0; /* Go through each element - assuming that the key is a string but *thatthevaluecouldbeanything.Sincethevaluecouldbe *anything,wecan'tassumethatitisscalar(itcouldevenbe *anotherdictionary),soweattempttogetitstypeusing *themethodgetABTypeFromCFTypeandthenrunthismethod *recursivelyonthatelement,storingtheMacabHeaderthat *results.Then,wejustcombinealloftheMacabHeadersinto *one.
*/ for(i = 0; i < numRecords; i++)
{
dictType = getABTypeFromCFType( CFGetTypeID(dictValues[i]) );
localizedDictKey = ABCopyLocalizedPropertyOrLabel(dictKeys[i]);
dictKeyString = CFStringToOUString(localizedDictKey);
dictLabelString = propertyNameString + ": " + 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 *headerNamesarray,which(attheendofthismethod)isused *tocreatetheMacabHeaderthatisreturned.
*/
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];
/* Array */ case kABArrayProperty: /* For non-scalars, we can only get more information if the property *actuallyexists.
*/ if(_propertyValue != nullptr)
{
sal_Int32 arrLength = static_cast<sal_Int32>(CFArrayGetCount(static_cast<CFArrayRef>(_propertyValue)));
sal_Int32 i,j,k;
CFTypeRef arrValue;
ABPropertyType arrType;
std::vector<std::unique_ptr<MacabHeader>> arrHeaders;
OUString propertyNameString = CFStringToOUString(_propertyName);
OUString arrLabelString;
CFStringRef arrLabel;
length = 0; /* Go through each element - since the elements here do not have *uniquekeysliketheonesindictionaries,wecreateaunique *keyoutoftheidoftheelementinthearray(thefirst *elementgetsa0ploppedontotheendofit,theseconda1... *Aswithdictionaries,theelementscouldbeanything,including *anotherarray,sowehavetorunthismethodrecursivelyon *eachelement,storingtheresultingMacabHeaderintoanarray, *whichwethencombineintooneMacabHeaderthatisreturned.
*/ for(i = 0; i < arrLength; i++)
{
arrValue = CFArrayGetValueAtIndex(static_cast<CFArrayRef>(_propertyValue), i);
arrType = getABTypeFromCFType( CFGetTypeID(arrValue) );
arrLabelString = propertyNameString + OUString::number(i);
arrLabel = OUStringToCFString(arrLabelString); auto hdr = std::unique_ptr<MacabHeader>(createHeaderForProperty(arrType, arrValue, arrLabel)); if (!hdr)
hdr = std::make_unique<MacabHeader>();
length += hdr->getSize();
CFRelease(arrLabel);
arrHeaders.push_back(std::move(hdr));
}
headerNames = new macabfield *[length]; for(i = 0, j = 0, k = 0; i < length; i++,k++)
{ while(arrHeaders[j]->getSize() == k)
{
j++;
k = 0;
}
/* If we succeeded at adding elements to the headerNames array, then *lengthwillnolongerbe0.Ifitis,createanewMacabHeader *outoftheheaderNames(afterweedingoutduplicateheaders),and *thenreturntheresult.Ifthelengthisstill0,returnNULL:we *failedtocreateaMacabHeaderoutofthisproperty.
*/ if(length != 0)
{
manageDuplicateHeaders(headerNames, length);
MacabHeader *headerResult = new MacabHeader(length, headerNames); for(sal_Int32 i = 0; i < length; ++i)
{ if(headerNames[i]->value)
CFRelease(headerNames[i]->value); delete headerNames[i];
} delete [] headerNames; return headerResult;
} else return nullptr;
}
/* Create a MacabRecord out of an ABRecord, using a given MacabHeader and *therecord'stype.Wegothrougheachpropertyforthisrecordtype *thenprocessitmuchlikeweprocessedtheheader(above),withtwo *exceptions:ifwecomeuponsomethingnotintheheader,weignoreit *(it'ssomethingwedon'twanttoadd),andoncewefindacorresponding *locationintheheader,westorethepropertyandthepropertytypein *amacabfield.(Fortheheader,westoredthepropertytypeandthename *ofthepropertyasaCFString.)
*/
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());
/* Inserts a given property into a MacabRecord. This method is recursive *becausepropertiescancontainmanysub-properties.
*/ void MacabRecords::insertPropertyIntoMacabRecord(const ABPropertyType _propertyType, MacabRecord *_abrecord, const MacabHeader *_header, const OUString& _propertyName, const CFTypeRef _propertyValue) const
{ /* If there is no value, return */ if(_propertyValue == nullptr) 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. *Inallothercases,thismethodiscalledrecursivelyuntila *scalartype,anerror,oranunknowntypearefound. *Becauseofthat,thefollowingchecksonlyoccurforthistype. *Westorewhetherwehavesuccessfullyplacedthisproperty *intotheMacabRecord(orwhetheranunrecoverableerroroccurred). *Then,wetryoverandoveragaintoplacethepropertyintothe *record.Therearethreepossibleresults: *1)Success! *2)Thereisalreadyapropertystoredatthecolumnofthisname, *inwhichcasewehaveaduplicateheader(seethemethod *manageDuplicateHeaders()).Ifthatisthecase,weaddanID *totheendofthecolumnnameinthesameformataswedoin *manageDuplicateHeaders()andtryagain. *3)Nocolumnofthisnameexistsintheheader.Inthiscase, *thereisnothingwecando:wehavefailedtoplacethis *propertyintotherecord.
*/ bool bPlaced = false;
OUString columnName = _propertyName;
sal_Int32 i = 1;
// A big safeguard to prevent two fields from having the same name. while(!bPlaced)
{
sal_Int32 columnNumber = _header->getColumnNumber(columnName);
bPlaced = true; if(columnNumber != -1)
{ // collision! A property already exists here! if(_abrecord->get(columnNumber) != nullptr)
{
bPlaced = false;
i++;
columnName = _propertyName + " (" + OUString::number(i) + ")";
}
/* Array */ case kABArrayProperty:
{ /* An array is basically just a list of anything, so all we do *isgothroughthearray,andrerunthismethodrecursively *oneachelement.
*/
sal_Int32 arrLength = static_cast<sal_Int32>(CFArrayGetCount(static_cast<CFArrayRef>(_propertyValue)));
sal_Int32 i;
OUString newPropertyName;
/* Going through each element... */ for(i = 0; i < arrLength; i++)
{ constvoid *arrValue = CFArrayGetValueAtIndex(static_cast<CFArrayRef>(_propertyValue), i);
newPropertyName = _propertyName + OUString::number(i);
insertPropertyIntoMacabRecord(_abrecord, _header, newPropertyName, arrValue);
CFRelease(arrValue);
}
} break;
/* Dictionary */ case kABDictionaryProperty:
{ /* A dictionary is basically a hashmap. Technically, it can *holdanyobjectasakeyandanyobjectasavalue. *Forourcase,weassumethatthekeyisastring(sothat *wecanusethekeytogetthecolumnnameandmatchitagainst *theheader),butwedon'tassumeanythingaboutthevalue,so *werunthismethodrecursively(or,rather,weruntheversion *ofthismethodforwhenwedon'tknowtheobject'stype)until *wehitascalarvalue.
*/
/* Unfortunately, the only way to get both keys and values out *ofadictionaryinCoreFoundationistogetthemallatonce,sowe *dothat.
*/
CFStringRef *dictKeys;
CFStringRef localizedDictKey;
CFTypeRef *dictValues;
dictKeys = static_cast<CFStringRef *>(malloc(sizeof(CFStringRef)*numRecords));
dictValues = static_cast<CFTypeRef *>(malloc(sizeof(CFTypeRef)*numRecords));
CFDictionaryGetKeysAndValues(static_cast<CFDictionaryRef>(_propertyValue), reinterpret_cast<constvoid **>(dictKeys), dictValues);
/* 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 *isalabelandavalue.Alllabelsarestrings *(kABStringProperty),andallvalueshavethesametype *(whichisthetypeofthemultivalueminus255,oras *AddressBook'slistofpropertytypeshasit,minus0x100. *Wejustgetthecorrecttype,thengothrougheachelement *andgetthelabelandvalueandprinttheminalist.
*/
/* Go through each element... */ for(i = 0; i < multiLength; i++)
{ /* Label and value */
multiLabel = ABMultiValueCopyLabelAtIndex(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue)), i);
multiValue = ABMultiValueCopyValueAtIndex(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue)), i);
/* Unhandled types */ case kABErrorInProperty: case kABDataProperty: default: /* An error, as far as I have seen, only shows up as a type *returnedbyafunctionfordictionarieswhenthedictionary *holdsmanytypesofvalues.Sincewedonotusethatfunction, *itshouldn'tcomeup.IhaveyettoseethekABDataProperty, *andIamnotsurehowtorepresentitasastringanyway, *sinceitappearstojustbeabunchofbytes.Assumably,if *thesebytesmadeupastring,thetypewouldbe *kABStringProperty.Ithinkthatthisisusedwhenwearenot *surewhatthetypeis(e.g.,itcouldbeastringoranumber). *Thatbeingthecase,Istilldon'tknowhowtorepresentit. *And,defaultshouldnevercomeup,sincewe'veexhaustedall *ofthepossibletypesforABPropertyType,but...justincase.
*/ break;
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.21Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-02)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.