/ CFXMLNode.c
CFXMLNode.c
1 /* 2 * Copyright (c) 2015 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 /* CFXMLNode.c 25 Copyright (c) 1998-2014, Apple Inc. All rights reserved. 26 Responsibility: David Smith 27 */ 28 29 #include <CoreFoundation/CFXMLNode.h> 30 #include <CoreFoundation/CFPropertyList.h> 31 #include "CFInternal.h" 32 #include "CFXMLInputStream.h" 33 34 CF_INLINE Boolean _nullSafeCFEqual(CFTypeRef cf1, CFTypeRef cf2) { 35 if (cf1 && !cf2) return false; 36 if (cf2 && !cf1) return false; 37 if (cf1) return CFEqual(cf1, cf2); 38 return true; 39 } 40 41 static Boolean externalIDEqual(CFXMLExternalID *ext1, CFXMLExternalID *ext2) { 42 return _nullSafeCFEqual(ext1->systemID, ext2->systemID) && _nullSafeCFEqual(ext1->publicID, ext2->publicID); 43 } 44 45 static Boolean __CFXMLNodeEqual(CFTypeRef cf1, CFTypeRef cf2) { 46 CFXMLNodeRef desc1 = (CFXMLNodeRef)cf1, desc2 = (CFXMLNodeRef)cf2; 47 if (desc1 == desc2) return true; 48 if (!desc1 || !desc2) return false; 49 if (desc1->dataTypeID != desc2->dataTypeID) return false; 50 if ((desc1->dataString && !desc2->dataString) || (!desc1->dataString && desc2->dataString)) return false; 51 if (desc1->dataString && !CFEqual(desc1->dataString, desc2->dataString)) return false; 52 if ((desc1->additionalData && !desc2->additionalData) || (!desc1->additionalData && desc2->additionalData)) return false; 53 if (!desc1->additionalData) return true; 54 switch (desc1->dataTypeID) { 55 case kCFXMLNodeTypeDocument:{ 56 CFURLRef url1, url2; 57 url1 = ((CFXMLDocumentInfo *)desc1->additionalData)->sourceURL; 58 url2 = ((CFXMLDocumentInfo *)desc2->additionalData)->sourceURL; 59 return _nullSafeCFEqual(url1, url2); 60 } 61 case kCFXMLNodeTypeElement: { 62 CFXMLElementInfo *elt1, *elt2; 63 elt1 = (CFXMLElementInfo *)desc1->additionalData; 64 elt2 = (CFXMLElementInfo *)desc2->additionalData; 65 if (elt1->isEmpty != elt2->isEmpty) return false; 66 if (elt1->attributes == elt2->attributes) return true; 67 if (!elt1->attributes) return (CFDictionaryGetCount(elt2->attributes) == 0); 68 if (!elt2->attributes) return (CFDictionaryGetCount(elt1->attributes) == 0); 69 return CFEqual(elt1->attributes, elt2->attributes); 70 } 71 case kCFXMLNodeTypeProcessingInstruction: { 72 CFStringRef str1, str2; 73 str1 = ((CFXMLProcessingInstructionInfo *)desc1->additionalData)->dataString; 74 str2 = ((CFXMLProcessingInstructionInfo *)desc2->additionalData)->dataString; 75 return _nullSafeCFEqual(str1, str2); 76 } 77 case kCFXMLNodeTypeEntity: { 78 CFXMLEntityInfo *data1, *data2; 79 data1 = (CFXMLEntityInfo *)desc1->additionalData; 80 data2 = (CFXMLEntityInfo *)desc2->additionalData; 81 if (data1->entityType != data2->entityType) return false; 82 if (!_nullSafeCFEqual(data1->replacementText, data2->replacementText)) return false; 83 if (!_nullSafeCFEqual(data1->notationName, data2->notationName)) return false; 84 return externalIDEqual(&data1->entityID, &data2->entityID); 85 } 86 case kCFXMLNodeTypeEntityReference: { 87 return ((CFXMLEntityReferenceInfo *)(desc1->additionalData))->entityType == ((CFXMLEntityReferenceInfo *)(desc2->additionalData))->entityType; 88 } 89 case kCFXMLNodeTypeNotation: { 90 CFXMLNotationInfo *data1, *data2; 91 data1 = (CFXMLNotationInfo *)desc1->additionalData; 92 data2 = (CFXMLNotationInfo *)desc2->additionalData; 93 return externalIDEqual(&(data1->externalID), &(data2->externalID)); 94 } 95 case kCFXMLNodeTypeDocumentType: { 96 CFXMLDocumentTypeInfo *data1, *data2; 97 data1 = (CFXMLDocumentTypeInfo *)desc1->additionalData; 98 data2 = (CFXMLDocumentTypeInfo *)desc2->additionalData; 99 return externalIDEqual(&(data1->externalID), &(data2->externalID)); 100 } 101 case kCFXMLNodeTypeElementTypeDeclaration: { 102 CFXMLElementTypeDeclarationInfo *d1 = (CFXMLElementTypeDeclarationInfo *)desc1->additionalData; 103 CFXMLElementTypeDeclarationInfo *d2 = (CFXMLElementTypeDeclarationInfo *)desc2->additionalData; 104 return _nullSafeCFEqual(d1->contentDescription, d2->contentDescription); 105 } 106 case kCFXMLNodeTypeAttributeListDeclaration: { 107 CFXMLAttributeListDeclarationInfo *attList1 = (CFXMLAttributeListDeclarationInfo *)desc1->additionalData; 108 CFXMLAttributeListDeclarationInfo *attList2 = (CFXMLAttributeListDeclarationInfo *)desc2->additionalData; 109 CFIndex idx; 110 if (attList1->numberOfAttributes != attList2->numberOfAttributes) return false; 111 for (idx = 0; idx < attList1->numberOfAttributes; idx ++) { 112 CFXMLAttributeDeclarationInfo *attr1 = &(attList1->attributes[idx]); 113 CFXMLAttributeDeclarationInfo *attr2 = &(attList2->attributes[idx]); 114 if (!_nullSafeCFEqual(attr1->attributeName, attr2->attributeName)) return false; 115 if (!_nullSafeCFEqual(attr1->typeString, attr2->typeString)) return false; 116 if (!_nullSafeCFEqual(attr1->defaultString, attr2->defaultString)) return false; 117 } 118 return true; 119 } 120 default: 121 return false; 122 } 123 return true; 124 } 125 126 static CFHashCode __CFXMLNodeHash(CFTypeRef cf) { 127 CFXMLNodeRef node = (CFXMLNodeRef)cf; 128 if (node->dataString) { 129 return CFHash(node->dataString); 130 } 131 if (node->dataTypeID == kCFXMLNodeTypeDocument) { 132 CFURLRef url = ((CFXMLDocumentInfo *)node->additionalData)->sourceURL; 133 return url ? CFHash(url) : (CFHashCode)cf; 134 } else { 135 CFAssert2(false, __kCFLogAssertion, "%s(): Saw unexpected XML type code %d", __PRETTY_FUNCTION__, node->dataTypeID); 136 return CFHash(cf); 137 } 138 } 139 140 static CFStringRef __CFXMLNodeCopyDescription(CFTypeRef cf) { 141 struct __CFXMLNode *node = (struct __CFXMLNode *)cf; 142 return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("CFXMLNode %p>{typeID = %ld, string = %@}"), cf, (unsigned long)node->dataTypeID, node->dataString); 143 } 144 145 static void __CFXMLNodeDeallocate(CFTypeRef cf) { 146 struct __CFXMLNode *node = (struct __CFXMLNode *)cf; 147 if (node->dataString) CFRelease(node->dataString); 148 if (node->additionalData) { 149 switch (node->dataTypeID) { 150 case kCFXMLNodeTypeDocument: 151 if (((CFXMLDocumentInfo *)node->additionalData)->sourceURL) { 152 CFRelease(((CFXMLDocumentInfo *)node->additionalData)->sourceURL); 153 } 154 break; 155 case kCFXMLNodeTypeElement: 156 if (((CFXMLElementInfo *)node->additionalData)->attributes) { 157 CFRelease(((CFXMLElementInfo *)node->additionalData)->attributes); 158 CFRelease(((CFXMLElementInfo *)node->additionalData)->attributeOrder); 159 } 160 break; 161 case kCFXMLNodeTypeProcessingInstruction: 162 if (((CFXMLProcessingInstructionInfo *)node->additionalData)->dataString) { 163 CFRelease(((CFXMLProcessingInstructionInfo *)node->additionalData)->dataString); 164 } 165 break; 166 case kCFXMLNodeTypeEntity: 167 { 168 CFXMLEntityInfo *data = (CFXMLEntityInfo *)node->additionalData; 169 if (data->replacementText) CFRelease(data->replacementText); 170 if (data->entityID.systemID) CFRelease(data->entityID.systemID); 171 if (data->entityID.publicID) CFRelease(data->entityID.publicID); 172 if (data->notationName) CFRelease(data->notationName); 173 break; 174 } 175 case kCFXMLNodeTypeEntityReference: 176 { 177 // Do nothing; additionalData has no structure of its own, with dependent pieces to release. -- REW, 2/11/2000 178 break; 179 } 180 case kCFXMLNodeTypeDocumentType: 181 case kCFXMLNodeTypeNotation: 182 // We get away with this because CFXMLNotationInfo and CFXMLDocumentTypeInfo have identical formats 183 { 184 CFXMLNotationInfo *data = (CFXMLNotationInfo *)node->additionalData; 185 if (data->externalID.systemID) CFRelease(data->externalID.systemID); 186 if (data->externalID.publicID) CFRelease(data->externalID.publicID); 187 break; 188 } 189 case kCFXMLNodeTypeElementTypeDeclaration: 190 if (((CFXMLElementTypeDeclarationInfo *)node->additionalData)->contentDescription) { 191 CFRelease(((CFXMLElementTypeDeclarationInfo *)node->additionalData)->contentDescription); 192 } 193 break; 194 case kCFXMLNodeTypeAttributeListDeclaration: 195 { 196 CFXMLAttributeListDeclarationInfo *data = (CFXMLAttributeListDeclarationInfo *)node->additionalData; 197 CFIndex idx; 198 for (idx = 0; idx < data->numberOfAttributes; idx ++) { 199 CFRelease(data->attributes[idx].attributeName); 200 CFRelease(data->attributes[idx].typeString); 201 CFRelease(data->attributes[idx].defaultString); 202 } 203 CFAllocatorDeallocate(CFGetAllocator(node), data->attributes); 204 break; 205 } 206 default: 207 CFAssert1(false, __kCFLogAssertion, "%s(): Encountered unexpected typeID %d (additionalData should be empty)", node->dataTypeID); 208 } 209 } 210 } 211 212 static CFTypeID __kCFXMLNodeTypeID = _kCFRuntimeNotATypeID; 213 214 static const CFRuntimeClass __CFXMLNodeClass = { 215 0, 216 "CFXMLNode", 217 NULL, // init 218 NULL, // copy 219 __CFXMLNodeDeallocate, 220 __CFXMLNodeEqual, 221 __CFXMLNodeHash, 222 NULL, // 223 __CFXMLNodeCopyDescription 224 }; 225 226 CFTypeID CFXMLNodeGetTypeID(void) { 227 static dispatch_once_t initOnce; 228 dispatch_once(&initOnce, ^{ __kCFXMLNodeTypeID = _CFRuntimeRegisterClass(&__CFXMLNodeClass); }); 229 return __kCFXMLNodeTypeID; 230 } 231 232 CFXMLNodeRef CFXMLNodeCreateCopy(CFAllocatorRef alloc, CFXMLNodeRef origNode) { 233 return CFXMLNodeCreate(alloc, origNode->dataTypeID, origNode->dataString, origNode->additionalData, origNode->version); 234 } 235 236 static void _copyAddlDataForType(CFAllocatorRef alloc, CFXMLNodeTypeCode xmlType, const void *src, void *dest) { 237 switch(xmlType) { 238 case kCFXMLNodeTypeDocument: { 239 CFXMLDocumentInfo *srcData = (CFXMLDocumentInfo *)src; 240 CFXMLDocumentInfo *destData = (CFXMLDocumentInfo *)dest; 241 destData->sourceURL = srcData->sourceURL ? (CFURLRef)CFRetain(srcData->sourceURL) : NULL; 242 destData->encoding = srcData->encoding; 243 break; 244 } 245 case kCFXMLNodeTypeElement: { 246 CFXMLElementInfo *srcData = (CFXMLElementInfo *)src; 247 CFXMLElementInfo *destData = (CFXMLElementInfo *)dest; 248 if (srcData->attributes && CFDictionaryGetCount(srcData->attributes) != 0) { 249 destData->attributes = (CFDictionaryRef)CFPropertyListCreateDeepCopy(alloc, srcData->attributes, kCFPropertyListImmutable); 250 destData->attributeOrder = (CFArrayRef)CFPropertyListCreateDeepCopy(alloc, srcData->attributeOrder, kCFPropertyListImmutable); 251 } else { 252 destData->attributes = NULL; 253 destData->attributeOrder = NULL; 254 } 255 destData->isEmpty = srcData->isEmpty; 256 break; 257 } 258 case kCFXMLNodeTypeProcessingInstruction: { 259 CFXMLProcessingInstructionInfo *srcData = (CFXMLProcessingInstructionInfo *)src; 260 CFXMLProcessingInstructionInfo *destData = (CFXMLProcessingInstructionInfo *)dest; 261 destData->dataString = srcData->dataString ? (CFStringRef)CFStringCreateCopy(alloc, srcData->dataString) : NULL; 262 break; 263 } 264 case kCFXMLNodeTypeEntity: 265 { 266 CFXMLEntityInfo *sourceData = (CFXMLEntityInfo *)src; 267 CFXMLEntityInfo *destData = (CFXMLEntityInfo *)dest; 268 destData->entityType = sourceData->entityType; 269 destData->replacementText = sourceData->replacementText ? (CFStringRef)CFStringCreateCopy(alloc, sourceData->replacementText) : NULL; 270 destData->entityID.systemID = sourceData->entityID.systemID ? (CFURLRef)CFRetain(sourceData->entityID.systemID) : NULL; 271 destData->entityID.publicID = sourceData->entityID.publicID ? (CFStringRef)CFStringCreateCopy(alloc, sourceData->entityID.publicID) : NULL; 272 destData->notationName = sourceData->notationName ? (CFStringRef)CFStringCreateCopy(alloc, sourceData->notationName) : NULL; 273 break; 274 } 275 case kCFXMLNodeTypeEntityReference: 276 { 277 CFXMLEntityReferenceInfo *srcData = (CFXMLEntityReferenceInfo *)src; 278 CFXMLEntityReferenceInfo *destData = (CFXMLEntityReferenceInfo *)dest; 279 destData->entityType = srcData->entityType; 280 break; 281 } 282 case kCFXMLNodeTypeDocumentType: 283 case kCFXMLNodeTypeNotation: 284 { 285 // We can get away with this because the structures of CFXMLNotationInfo and CFXMLDocumentTypeInfo match. -- REW, 3/8/2000 286 CFXMLNotationInfo *srcData = (CFXMLNotationInfo *)src; 287 CFXMLNotationInfo *destData = (CFXMLNotationInfo *)dest; 288 destData->externalID.systemID = srcData->externalID.systemID ? (CFURLRef)CFRetain(srcData->externalID.systemID) : NULL; 289 destData->externalID.publicID = srcData->externalID.publicID ? (CFStringRef)CFStringCreateCopy(alloc, srcData->externalID.publicID) : NULL; 290 break; 291 } 292 case kCFXMLNodeTypeElementTypeDeclaration: { 293 CFXMLElementTypeDeclarationInfo *srcData = (CFXMLElementTypeDeclarationInfo *)src; 294 CFXMLElementTypeDeclarationInfo *destData = (CFXMLElementTypeDeclarationInfo *)dest; 295 destData->contentDescription = srcData->contentDescription ? (CFStringRef)CFStringCreateCopy(alloc, srcData->contentDescription) : NULL; 296 break; 297 } 298 case kCFXMLNodeTypeAttributeListDeclaration: 299 { 300 CFXMLAttributeListDeclarationInfo *sourceData = (CFXMLAttributeListDeclarationInfo *)src; 301 CFXMLAttributeListDeclarationInfo *destData = (CFXMLAttributeListDeclarationInfo *)dest; 302 CFIndex idx; 303 destData->numberOfAttributes = sourceData->numberOfAttributes; 304 destData->attributes = sourceData->numberOfAttributes ? (CFXMLAttributeDeclarationInfo *)CFAllocatorAllocate(alloc, sizeof(CFXMLAttributeDeclarationInfo)*sourceData->numberOfAttributes, 0) : NULL; 305 for (idx = 0; idx < sourceData->numberOfAttributes; idx ++) { 306 CFXMLAttributeDeclarationInfo sourceAttr = sourceData->attributes[idx]; 307 CFXMLAttributeDeclarationInfo *destAttr = &(destData->attributes[idx]); 308 destAttr->attributeName = (CFStringRef)CFStringCreateCopy(alloc, sourceAttr.attributeName); 309 destAttr->typeString = (CFStringRef)CFStringCreateCopy(alloc, sourceAttr.typeString); 310 destAttr->defaultString = (CFStringRef)CFStringCreateCopy(alloc, sourceAttr.defaultString); 311 } 312 break; 313 } 314 default: 315 CFAssert2(false, __kCFLogAssertion, "%s(): Encountered unexpected typeID %d (additionalData should be empty)", __PRETTY_FUNCTION__, xmlType); 316 } 317 } 318 319 // Designated initializer; all node creation (except the wonky one created by the parser) should happen here. 320 CFXMLNodeRef CFXMLNodeCreate(CFAllocatorRef alloc, CFXMLNodeTypeCode xmlType, CFStringRef dataString, const void *additionalData, CFIndex version) { 321 struct __CFXMLNode *node; 322 UInt32 extraSize; 323 // Add assertions checking xmlType against the presence/absence of additionalData & dataString 324 switch (xmlType) { 325 case kCFXMLNodeTypeDocument: extraSize = sizeof(CFXMLDocumentInfo); break; 326 case kCFXMLNodeTypeElement: extraSize = sizeof(CFXMLElementInfo); break; 327 case kCFXMLNodeTypeProcessingInstruction: extraSize = sizeof(CFXMLProcessingInstructionInfo); break; 328 case kCFXMLNodeTypeEntity: extraSize = sizeof(CFXMLEntityInfo); break; 329 case kCFXMLNodeTypeEntityReference: extraSize = sizeof(CFXMLEntityReferenceInfo); break; 330 case kCFXMLNodeTypeDocumentType: extraSize = sizeof(CFXMLDocumentTypeInfo); break; 331 case kCFXMLNodeTypeNotation: extraSize = sizeof(CFXMLNotationInfo); break; 332 case kCFXMLNodeTypeElementTypeDeclaration: extraSize = sizeof(CFXMLElementTypeDeclarationInfo); break; 333 case kCFXMLNodeTypeAttributeListDeclaration: extraSize = sizeof(CFXMLAttributeListDeclarationInfo); break; 334 default: extraSize = 0; 335 } 336 337 node = (struct __CFXMLNode *)_CFRuntimeCreateInstance(alloc, CFXMLNodeGetTypeID(), sizeof(struct __CFXMLNode) + extraSize - sizeof(CFRuntimeBase), NULL); 338 if (node) { 339 alloc = CFGetAllocator(node); 340 node->version = version; 341 node->dataTypeID = xmlType; 342 node->dataString = dataString ? (CFStringRef)CFStringCreateCopy(alloc, dataString) : NULL; 343 if (extraSize != 0) { 344 node->additionalData = (void *)((uint8_t *)node + sizeof(struct __CFXMLNode)); 345 _copyAddlDataForType(alloc, xmlType, additionalData, node->additionalData); 346 } else { 347 node->additionalData = NULL; 348 } 349 } 350 return node; 351 } 352 353 CFXMLNodeTypeCode CFXMLNodeGetTypeCode(CFXMLNodeRef node) { 354 return node->dataTypeID; 355 } 356 357 CFStringRef CFXMLNodeGetString(CFXMLNodeRef node) { 358 return node->dataString; 359 } 360 361 const void *CFXMLNodeGetInfoPtr(CFXMLNodeRef node) { 362 return node->additionalData; 363 } 364 365 CFIndex CFXMLNodeGetVersion(CFXMLNodeRef node) { 366 return node->version; 367 } 368 369