blob: 25b33fc6ab845be30acac50f16ffe08ab9f98f02 [file] [log] [blame]
/* CFXMLNode.c
Copyright (c) 1998-2017, Apple Inc. and the Swift project authors
Portions Copyright (c) 2014-2017, Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception
See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
Responsibility: David Smith
*/
#include <CoreFoundation/CFXMLNode.h>
#include <CoreFoundation/CFPropertyList.h>
#include "CFInternal.h"
#include "CFXMLInputStream.h"
CF_INLINE Boolean _nullSafeCFEqual(CFTypeRef cf1, CFTypeRef cf2) {
if (cf1 && !cf2) return false;
if (cf2 && !cf1) return false;
if (cf1) return CFEqual(cf1, cf2);
return true;
}
static Boolean externalIDEqual(CFXMLExternalID *ext1, CFXMLExternalID *ext2) {
return _nullSafeCFEqual(ext1->systemID, ext2->systemID) && _nullSafeCFEqual(ext1->publicID, ext2->publicID);
}
static Boolean __CFXMLNodeEqual(CFTypeRef cf1, CFTypeRef cf2) {
CFXMLNodeRef desc1 = (CFXMLNodeRef)cf1, desc2 = (CFXMLNodeRef)cf2;
if (desc1 == desc2) return true;
if (!desc1 || !desc2) return false;
if (desc1->dataTypeID != desc2->dataTypeID) return false;
if ((desc1->dataString && !desc2->dataString) || (!desc1->dataString && desc2->dataString)) return false;
if (desc1->dataString && !CFEqual(desc1->dataString, desc2->dataString)) return false;
if ((desc1->additionalData && !desc2->additionalData) || (!desc1->additionalData && desc2->additionalData)) return false;
if (!desc1->additionalData) return true;
switch (desc1->dataTypeID) {
case kCFXMLNodeTypeDocument:{
CFURLRef url1, url2;
url1 = ((CFXMLDocumentInfo *)desc1->additionalData)->sourceURL;
url2 = ((CFXMLDocumentInfo *)desc2->additionalData)->sourceURL;
return _nullSafeCFEqual(url1, url2);
}
case kCFXMLNodeTypeElement: {
CFXMLElementInfo *elt1, *elt2;
elt1 = (CFXMLElementInfo *)desc1->additionalData;
elt2 = (CFXMLElementInfo *)desc2->additionalData;
if (elt1->isEmpty != elt2->isEmpty) return false;
if (elt1->attributes == elt2->attributes) return true;
if (!elt1->attributes) return (CFDictionaryGetCount(elt2->attributes) == 0);
if (!elt2->attributes) return (CFDictionaryGetCount(elt1->attributes) == 0);
return CFEqual(elt1->attributes, elt2->attributes);
}
case kCFXMLNodeTypeProcessingInstruction: {
CFStringRef str1, str2;
str1 = ((CFXMLProcessingInstructionInfo *)desc1->additionalData)->dataString;
str2 = ((CFXMLProcessingInstructionInfo *)desc2->additionalData)->dataString;
return _nullSafeCFEqual(str1, str2);
}
case kCFXMLNodeTypeEntity: {
CFXMLEntityInfo *data1, *data2;
data1 = (CFXMLEntityInfo *)desc1->additionalData;
data2 = (CFXMLEntityInfo *)desc2->additionalData;
if (data1->entityType != data2->entityType) return false;
if (!_nullSafeCFEqual(data1->replacementText, data2->replacementText)) return false;
if (!_nullSafeCFEqual(data1->notationName, data2->notationName)) return false;
return externalIDEqual(&data1->entityID, &data2->entityID);
}
case kCFXMLNodeTypeEntityReference: {
return ((CFXMLEntityReferenceInfo *)(desc1->additionalData))->entityType == ((CFXMLEntityReferenceInfo *)(desc2->additionalData))->entityType;
}
case kCFXMLNodeTypeNotation: {
CFXMLNotationInfo *data1, *data2;
data1 = (CFXMLNotationInfo *)desc1->additionalData;
data2 = (CFXMLNotationInfo *)desc2->additionalData;
return externalIDEqual(&(data1->externalID), &(data2->externalID));
}
case kCFXMLNodeTypeDocumentType: {
CFXMLDocumentTypeInfo *data1, *data2;
data1 = (CFXMLDocumentTypeInfo *)desc1->additionalData;
data2 = (CFXMLDocumentTypeInfo *)desc2->additionalData;
return externalIDEqual(&(data1->externalID), &(data2->externalID));
}
case kCFXMLNodeTypeElementTypeDeclaration: {
CFXMLElementTypeDeclarationInfo *d1 = (CFXMLElementTypeDeclarationInfo *)desc1->additionalData;
CFXMLElementTypeDeclarationInfo *d2 = (CFXMLElementTypeDeclarationInfo *)desc2->additionalData;
return _nullSafeCFEqual(d1->contentDescription, d2->contentDescription);
}
case kCFXMLNodeTypeAttributeListDeclaration: {
CFXMLAttributeListDeclarationInfo *attList1 = (CFXMLAttributeListDeclarationInfo *)desc1->additionalData;
CFXMLAttributeListDeclarationInfo *attList2 = (CFXMLAttributeListDeclarationInfo *)desc2->additionalData;
CFIndex idx;
if (attList1->numberOfAttributes != attList2->numberOfAttributes) return false;
for (idx = 0; idx < attList1->numberOfAttributes; idx ++) {
CFXMLAttributeDeclarationInfo *attr1 = &(attList1->attributes[idx]);
CFXMLAttributeDeclarationInfo *attr2 = &(attList2->attributes[idx]);
if (!_nullSafeCFEqual(attr1->attributeName, attr2->attributeName)) return false;
if (!_nullSafeCFEqual(attr1->typeString, attr2->typeString)) return false;
if (!_nullSafeCFEqual(attr1->defaultString, attr2->defaultString)) return false;
}
return true;
}
default:
return false;
}
return true;
}
static CFHashCode __CFXMLNodeHash(CFTypeRef cf) {
CFXMLNodeRef node = (CFXMLNodeRef)cf;
if (node->dataString) {
return CFHash(node->dataString);
}
if (node->dataTypeID == kCFXMLNodeTypeDocument) {
CFURLRef url = ((CFXMLDocumentInfo *)node->additionalData)->sourceURL;
return url ? CFHash(url) : (CFHashCode)cf;
} else {
CFAssert2(false, __kCFLogAssertion, "%s(): Saw unexpected XML type code %ld", __PRETTY_FUNCTION__, node->dataTypeID);
return CFHash(cf);
}
}
static CFStringRef __CFXMLNodeCopyDescription(CFTypeRef cf) {
struct __CFXMLNode *node = (struct __CFXMLNode *)cf;
return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("CFXMLNode %p>{typeID = %ld, string = %@}"), cf, (unsigned long)node->dataTypeID, node->dataString);
}
static void __CFXMLNodeDeallocate(CFTypeRef cf) {
struct __CFXMLNode *node = (struct __CFXMLNode *)cf;
if (node->dataString) CFRelease(node->dataString);
if (node->additionalData) {
switch (node->dataTypeID) {
case kCFXMLNodeTypeDocument:
if (((CFXMLDocumentInfo *)node->additionalData)->sourceURL) {
CFRelease(((CFXMLDocumentInfo *)node->additionalData)->sourceURL);
}
break;
case kCFXMLNodeTypeElement:
if (((CFXMLElementInfo *)node->additionalData)->attributes) {
CFRelease(((CFXMLElementInfo *)node->additionalData)->attributes);
CFRelease(((CFXMLElementInfo *)node->additionalData)->attributeOrder);
}
break;
case kCFXMLNodeTypeProcessingInstruction:
if (((CFXMLProcessingInstructionInfo *)node->additionalData)->dataString) {
CFRelease(((CFXMLProcessingInstructionInfo *)node->additionalData)->dataString);
}
break;
case kCFXMLNodeTypeEntity:
{
CFXMLEntityInfo *data = (CFXMLEntityInfo *)node->additionalData;
if (data->replacementText) CFRelease(data->replacementText);
if (data->entityID.systemID) CFRelease(data->entityID.systemID);
if (data->entityID.publicID) CFRelease(data->entityID.publicID);
if (data->notationName) CFRelease(data->notationName);
break;
}
case kCFXMLNodeTypeEntityReference:
{
// Do nothing; additionalData has no structure of its own, with dependent pieces to release. -- REW, 2/11/2000
break;
}
case kCFXMLNodeTypeDocumentType:
case kCFXMLNodeTypeNotation:
// We get away with this because CFXMLNotationInfo and CFXMLDocumentTypeInfo have identical formats
{
CFXMLNotationInfo *data = (CFXMLNotationInfo *)node->additionalData;
if (data->externalID.systemID) CFRelease(data->externalID.systemID);
if (data->externalID.publicID) CFRelease(data->externalID.publicID);
break;
}
case kCFXMLNodeTypeElementTypeDeclaration:
if (((CFXMLElementTypeDeclarationInfo *)node->additionalData)->contentDescription) {
CFRelease(((CFXMLElementTypeDeclarationInfo *)node->additionalData)->contentDescription);
}
break;
case kCFXMLNodeTypeAttributeListDeclaration:
{
CFXMLAttributeListDeclarationInfo *data = (CFXMLAttributeListDeclarationInfo *)node->additionalData;
CFIndex idx;
for (idx = 0; idx < data->numberOfAttributes; idx ++) {
CFRelease(data->attributes[idx].attributeName);
CFRelease(data->attributes[idx].typeString);
CFRelease(data->attributes[idx].defaultString);
}
CFAllocatorDeallocate(CFGetAllocator(node), data->attributes);
break;
}
default:
CFAssert2(false, __kCFLogAssertion, "%s(): Encountered unexpected typeID %ld (additionalData should be empty)", __PRETTY_FUNCTION__, node->dataTypeID);
}
}
}
static CFTypeID __kCFXMLNodeTypeID = _kCFRuntimeNotATypeID;
static const CFRuntimeClass __CFXMLNodeClass = {
0,
"CFXMLNode",
NULL, // init
NULL, // copy
__CFXMLNodeDeallocate,
__CFXMLNodeEqual,
__CFXMLNodeHash,
NULL, //
__CFXMLNodeCopyDescription
};
CFTypeID CFXMLNodeGetTypeID(void) {
static dispatch_once_t initOnce;
dispatch_once(&initOnce, ^{ __kCFXMLNodeTypeID = _CFRuntimeRegisterClass(&__CFXMLNodeClass); });
return __kCFXMLNodeTypeID;
}
CFXMLNodeRef CFXMLNodeCreateCopy(CFAllocatorRef alloc, CFXMLNodeRef origNode) {
return CFXMLNodeCreate(alloc, origNode->dataTypeID, origNode->dataString, origNode->additionalData, origNode->version);
}
static void _copyAddlDataForType(CFAllocatorRef alloc, CFXMLNodeTypeCode xmlType, const void *src, void *dest) {
switch(xmlType) {
case kCFXMLNodeTypeDocument: {
CFXMLDocumentInfo *srcData = (CFXMLDocumentInfo *)src;
CFXMLDocumentInfo *destData = (CFXMLDocumentInfo *)dest;
destData->sourceURL = srcData->sourceURL ? (CFURLRef)CFRetain(srcData->sourceURL) : NULL;
destData->encoding = srcData->encoding;
break;
}
case kCFXMLNodeTypeElement: {
CFXMLElementInfo *srcData = (CFXMLElementInfo *)src;
CFXMLElementInfo *destData = (CFXMLElementInfo *)dest;
if (srcData->attributes && CFDictionaryGetCount(srcData->attributes) != 0) {
destData->attributes = (CFDictionaryRef)CFPropertyListCreateDeepCopy(alloc, srcData->attributes, kCFPropertyListImmutable);
destData->attributeOrder = (CFArrayRef)CFPropertyListCreateDeepCopy(alloc, srcData->attributeOrder, kCFPropertyListImmutable);
} else {
destData->attributes = NULL;
destData->attributeOrder = NULL;
}
destData->isEmpty = srcData->isEmpty;
break;
}
case kCFXMLNodeTypeProcessingInstruction: {
CFXMLProcessingInstructionInfo *srcData = (CFXMLProcessingInstructionInfo *)src;
CFXMLProcessingInstructionInfo *destData = (CFXMLProcessingInstructionInfo *)dest;
destData->dataString = srcData->dataString ? (CFStringRef)CFStringCreateCopy(alloc, srcData->dataString) : NULL;
break;
}
case kCFXMLNodeTypeEntity:
{
CFXMLEntityInfo *sourceData = (CFXMLEntityInfo *)src;
CFXMLEntityInfo *destData = (CFXMLEntityInfo *)dest;
destData->entityType = sourceData->entityType;
destData->replacementText = sourceData->replacementText ? (CFStringRef)CFStringCreateCopy(alloc, sourceData->replacementText) : NULL;
destData->entityID.systemID = sourceData->entityID.systemID ? (CFURLRef)CFRetain(sourceData->entityID.systemID) : NULL;
destData->entityID.publicID = sourceData->entityID.publicID ? (CFStringRef)CFStringCreateCopy(alloc, sourceData->entityID.publicID) : NULL;
destData->notationName = sourceData->notationName ? (CFStringRef)CFStringCreateCopy(alloc, sourceData->notationName) : NULL;
break;
}
case kCFXMLNodeTypeEntityReference:
{
CFXMLEntityReferenceInfo *srcData = (CFXMLEntityReferenceInfo *)src;
CFXMLEntityReferenceInfo *destData = (CFXMLEntityReferenceInfo *)dest;
destData->entityType = srcData->entityType;
break;
}
case kCFXMLNodeTypeDocumentType:
case kCFXMLNodeTypeNotation:
{
// We can get away with this because the structures of CFXMLNotationInfo and CFXMLDocumentTypeInfo match. -- REW, 3/8/2000
CFXMLNotationInfo *srcData = (CFXMLNotationInfo *)src;
CFXMLNotationInfo *destData = (CFXMLNotationInfo *)dest;
destData->externalID.systemID = srcData->externalID.systemID ? (CFURLRef)CFRetain(srcData->externalID.systemID) : NULL;
destData->externalID.publicID = srcData->externalID.publicID ? (CFStringRef)CFStringCreateCopy(alloc, srcData->externalID.publicID) : NULL;
break;
}
case kCFXMLNodeTypeElementTypeDeclaration: {
CFXMLElementTypeDeclarationInfo *srcData = (CFXMLElementTypeDeclarationInfo *)src;
CFXMLElementTypeDeclarationInfo *destData = (CFXMLElementTypeDeclarationInfo *)dest;
destData->contentDescription = srcData->contentDescription ? (CFStringRef)CFStringCreateCopy(alloc, srcData->contentDescription) : NULL;
break;
}
case kCFXMLNodeTypeAttributeListDeclaration:
{
CFXMLAttributeListDeclarationInfo *sourceData = (CFXMLAttributeListDeclarationInfo *)src;
CFXMLAttributeListDeclarationInfo *destData = (CFXMLAttributeListDeclarationInfo *)dest;
CFIndex idx;
destData->numberOfAttributes = sourceData->numberOfAttributes;
destData->attributes = sourceData->numberOfAttributes ? (CFXMLAttributeDeclarationInfo *)CFAllocatorAllocate(alloc, sizeof(CFXMLAttributeDeclarationInfo)*sourceData->numberOfAttributes, 0) : NULL;
for (idx = 0; idx < sourceData->numberOfAttributes; idx ++) {
CFXMLAttributeDeclarationInfo sourceAttr = sourceData->attributes[idx];
CFXMLAttributeDeclarationInfo *destAttr = &(destData->attributes[idx]);
destAttr->attributeName = (CFStringRef)CFStringCreateCopy(alloc, sourceAttr.attributeName);
destAttr->typeString = (CFStringRef)CFStringCreateCopy(alloc, sourceAttr.typeString);
destAttr->defaultString = (CFStringRef)CFStringCreateCopy(alloc, sourceAttr.defaultString);
}
break;
}
default:
CFAssert2(false, __kCFLogAssertion, "%s(): Encountered unexpected typeID %ld (additionalData should be empty)", __PRETTY_FUNCTION__, xmlType);
}
}
// Designated initializer; all node creation (except the wonky one created by the parser) should happen here.
CFXMLNodeRef CFXMLNodeCreate(CFAllocatorRef alloc, CFXMLNodeTypeCode xmlType, CFStringRef dataString, const void *additionalData, CFIndex version) {
struct __CFXMLNode *node;
UInt32 extraSize;
// Add assertions checking xmlType against the presence/absence of additionalData & dataString
switch (xmlType) {
case kCFXMLNodeTypeDocument: extraSize = sizeof(CFXMLDocumentInfo); break;
case kCFXMLNodeTypeElement: extraSize = sizeof(CFXMLElementInfo); break;
case kCFXMLNodeTypeProcessingInstruction: extraSize = sizeof(CFXMLProcessingInstructionInfo); break;
case kCFXMLNodeTypeEntity: extraSize = sizeof(CFXMLEntityInfo); break;
case kCFXMLNodeTypeEntityReference: extraSize = sizeof(CFXMLEntityReferenceInfo); break;
case kCFXMLNodeTypeDocumentType: extraSize = sizeof(CFXMLDocumentTypeInfo); break;
case kCFXMLNodeTypeNotation: extraSize = sizeof(CFXMLNotationInfo); break;
case kCFXMLNodeTypeElementTypeDeclaration: extraSize = sizeof(CFXMLElementTypeDeclarationInfo); break;
case kCFXMLNodeTypeAttributeListDeclaration: extraSize = sizeof(CFXMLAttributeListDeclarationInfo); break;
default: extraSize = 0;
}
node = (struct __CFXMLNode *)_CFRuntimeCreateInstance(alloc, CFXMLNodeGetTypeID(), sizeof(struct __CFXMLNode) + extraSize - sizeof(CFRuntimeBase), NULL);
if (node) {
alloc = CFGetAllocator(node);
node->version = version;
node->dataTypeID = xmlType;
node->dataString = dataString ? (CFStringRef)CFStringCreateCopy(alloc, dataString) : NULL;
if (extraSize != 0) {
node->additionalData = (void *)((uint8_t *)node + sizeof(struct __CFXMLNode));
_copyAddlDataForType(alloc, xmlType, additionalData, node->additionalData);
} else {
node->additionalData = NULL;
}
}
return node;
}
CFXMLNodeTypeCode CFXMLNodeGetTypeCode(CFXMLNodeRef node) {
return node->dataTypeID;
}
CFStringRef CFXMLNodeGetString(CFXMLNodeRef node) {
return node->dataString;
}
const void *CFXMLNodeGetInfoPtr(CFXMLNodeRef node) {
return node->additionalData;
}
CFIndex CFXMLNodeGetVersion(CFXMLNodeRef node) {
return node->version;
}