2 * Copyright (c) 2015 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 Copyright (c) 1998-2014, Apple Inc. All rights reserved.
26 Responsibility: David Smith
29 #include <CoreFoundation/CFXMLNode.h>
30 #include <CoreFoundation/CFPropertyList.h>
31 #include "CFInternal.h"
32 #include "CFXMLInputStream.h"
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
);
41 static Boolean
externalIDEqual(CFXMLExternalID
*ext1
, CFXMLExternalID
*ext2
) {
42 return _nullSafeCFEqual(ext1
->systemID
, ext2
->systemID
) && _nullSafeCFEqual(ext1
->publicID
, ext2
->publicID
);
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
:{
57 url1
= ((CFXMLDocumentInfo
*)desc1
->additionalData
)->sourceURL
;
58 url2
= ((CFXMLDocumentInfo
*)desc2
->additionalData
)->sourceURL
;
59 return _nullSafeCFEqual(url1
, url2
);
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
);
71 case kCFXMLNodeTypeProcessingInstruction
: {
72 CFStringRef str1
, str2
;
73 str1
= ((CFXMLProcessingInstructionInfo
*)desc1
->additionalData
)->dataString
;
74 str2
= ((CFXMLProcessingInstructionInfo
*)desc2
->additionalData
)->dataString
;
75 return _nullSafeCFEqual(str1
, str2
);
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
);
86 case kCFXMLNodeTypeEntityReference
: {
87 return ((CFXMLEntityReferenceInfo
*)(desc1
->additionalData
))->entityType
== ((CFXMLEntityReferenceInfo
*)(desc2
->additionalData
))->entityType
;
89 case kCFXMLNodeTypeNotation
: {
90 CFXMLNotationInfo
*data1
, *data2
;
91 data1
= (CFXMLNotationInfo
*)desc1
->additionalData
;
92 data2
= (CFXMLNotationInfo
*)desc2
->additionalData
;
93 return externalIDEqual(&(data1
->externalID
), &(data2
->externalID
));
95 case kCFXMLNodeTypeDocumentType
: {
96 CFXMLDocumentTypeInfo
*data1
, *data2
;
97 data1
= (CFXMLDocumentTypeInfo
*)desc1
->additionalData
;
98 data2
= (CFXMLDocumentTypeInfo
*)desc2
->additionalData
;
99 return externalIDEqual(&(data1
->externalID
), &(data2
->externalID
));
101 case kCFXMLNodeTypeElementTypeDeclaration
: {
102 CFXMLElementTypeDeclarationInfo
*d1
= (CFXMLElementTypeDeclarationInfo
*)desc1
->additionalData
;
103 CFXMLElementTypeDeclarationInfo
*d2
= (CFXMLElementTypeDeclarationInfo
*)desc2
->additionalData
;
104 return _nullSafeCFEqual(d1
->contentDescription
, d2
->contentDescription
);
106 case kCFXMLNodeTypeAttributeListDeclaration
: {
107 CFXMLAttributeListDeclarationInfo
*attList1
= (CFXMLAttributeListDeclarationInfo
*)desc1
->additionalData
;
108 CFXMLAttributeListDeclarationInfo
*attList2
= (CFXMLAttributeListDeclarationInfo
*)desc2
->additionalData
;
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;
126 static CFHashCode
__CFXMLNodeHash(CFTypeRef cf
) {
127 CFXMLNodeRef node
= (CFXMLNodeRef
)cf
;
128 if (node
->dataString
) {
129 return CFHash(node
->dataString
);
131 if (node
->dataTypeID
== kCFXMLNodeTypeDocument
) {
132 CFURLRef url
= ((CFXMLDocumentInfo
*)node
->additionalData
)->sourceURL
;
133 return url
? CFHash(url
) : (CFHashCode
)cf
;
135 CFAssert2(false, __kCFLogAssertion
, "%s(): Saw unexpected XML type code %d", __PRETTY_FUNCTION__
, node
->dataTypeID
);
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
);
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
);
155 case kCFXMLNodeTypeElement
:
156 if (((CFXMLElementInfo
*)node
->additionalData
)->attributes
) {
157 CFRelease(((CFXMLElementInfo
*)node
->additionalData
)->attributes
);
158 CFRelease(((CFXMLElementInfo
*)node
->additionalData
)->attributeOrder
);
161 case kCFXMLNodeTypeProcessingInstruction
:
162 if (((CFXMLProcessingInstructionInfo
*)node
->additionalData
)->dataString
) {
163 CFRelease(((CFXMLProcessingInstructionInfo
*)node
->additionalData
)->dataString
);
166 case kCFXMLNodeTypeEntity
:
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
);
175 case kCFXMLNodeTypeEntityReference
:
177 // Do nothing; additionalData has no structure of its own, with dependent pieces to release. -- REW, 2/11/2000
180 case kCFXMLNodeTypeDocumentType
:
181 case kCFXMLNodeTypeNotation
:
182 // We get away with this because CFXMLNotationInfo and CFXMLDocumentTypeInfo have identical formats
184 CFXMLNotationInfo
*data
= (CFXMLNotationInfo
*)node
->additionalData
;
185 if (data
->externalID
.systemID
) CFRelease(data
->externalID
.systemID
);
186 if (data
->externalID
.publicID
) CFRelease(data
->externalID
.publicID
);
189 case kCFXMLNodeTypeElementTypeDeclaration
:
190 if (((CFXMLElementTypeDeclarationInfo
*)node
->additionalData
)->contentDescription
) {
191 CFRelease(((CFXMLElementTypeDeclarationInfo
*)node
->additionalData
)->contentDescription
);
194 case kCFXMLNodeTypeAttributeListDeclaration
:
196 CFXMLAttributeListDeclarationInfo
*data
= (CFXMLAttributeListDeclarationInfo
*)node
->additionalData
;
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
);
203 CFAllocatorDeallocate(CFGetAllocator(node
), data
->attributes
);
207 CFAssert1(false, __kCFLogAssertion
, "%s(): Encountered unexpected typeID %d (additionalData should be empty)", node
->dataTypeID
);
212 static CFTypeID __kCFXMLNodeTypeID
= _kCFRuntimeNotATypeID
;
214 static const CFRuntimeClass __CFXMLNodeClass
= {
219 __CFXMLNodeDeallocate
,
223 __CFXMLNodeCopyDescription
226 CFTypeID
CFXMLNodeGetTypeID(void) {
227 static dispatch_once_t initOnce
;
228 dispatch_once(&initOnce
, ^{ __kCFXMLNodeTypeID
= _CFRuntimeRegisterClass(&__CFXMLNodeClass
); });
229 return __kCFXMLNodeTypeID
;
232 CFXMLNodeRef
CFXMLNodeCreateCopy(CFAllocatorRef alloc
, CFXMLNodeRef origNode
) {
233 return CFXMLNodeCreate(alloc
, origNode
->dataTypeID
, origNode
->dataString
, origNode
->additionalData
, origNode
->version
);
236 static void _copyAddlDataForType(CFAllocatorRef alloc
, CFXMLNodeTypeCode xmlType
, const void *src
, void *dest
) {
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
;
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
);
252 destData
->attributes
= NULL
;
253 destData
->attributeOrder
= NULL
;
255 destData
->isEmpty
= srcData
->isEmpty
;
258 case kCFXMLNodeTypeProcessingInstruction
: {
259 CFXMLProcessingInstructionInfo
*srcData
= (CFXMLProcessingInstructionInfo
*)src
;
260 CFXMLProcessingInstructionInfo
*destData
= (CFXMLProcessingInstructionInfo
*)dest
;
261 destData
->dataString
= srcData
->dataString
? (CFStringRef
)CFStringCreateCopy(alloc
, srcData
->dataString
) : NULL
;
264 case kCFXMLNodeTypeEntity
:
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
;
275 case kCFXMLNodeTypeEntityReference
:
277 CFXMLEntityReferenceInfo
*srcData
= (CFXMLEntityReferenceInfo
*)src
;
278 CFXMLEntityReferenceInfo
*destData
= (CFXMLEntityReferenceInfo
*)dest
;
279 destData
->entityType
= srcData
->entityType
;
282 case kCFXMLNodeTypeDocumentType
:
283 case kCFXMLNodeTypeNotation
:
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
;
292 case kCFXMLNodeTypeElementTypeDeclaration
: {
293 CFXMLElementTypeDeclarationInfo
*srcData
= (CFXMLElementTypeDeclarationInfo
*)src
;
294 CFXMLElementTypeDeclarationInfo
*destData
= (CFXMLElementTypeDeclarationInfo
*)dest
;
295 destData
->contentDescription
= srcData
->contentDescription
? (CFStringRef
)CFStringCreateCopy(alloc
, srcData
->contentDescription
) : NULL
;
298 case kCFXMLNodeTypeAttributeListDeclaration
:
300 CFXMLAttributeListDeclarationInfo
*sourceData
= (CFXMLAttributeListDeclarationInfo
*)src
;
301 CFXMLAttributeListDeclarationInfo
*destData
= (CFXMLAttributeListDeclarationInfo
*)dest
;
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
);
315 CFAssert2(false, __kCFLogAssertion
, "%s(): Encountered unexpected typeID %d (additionalData should be empty)", __PRETTY_FUNCTION__
, xmlType
);
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
;
323 // Add assertions checking xmlType against the presence/absence of additionalData & dataString
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;
337 node
= (struct __CFXMLNode
*)_CFRuntimeCreateInstance(alloc
, CFXMLNodeGetTypeID(), sizeof(struct __CFXMLNode
) + extraSize
- sizeof(CFRuntimeBase
), NULL
);
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
);
347 node
->additionalData
= NULL
;
353 CFXMLNodeTypeCode
CFXMLNodeGetTypeCode(CFXMLNodeRef node
) {
354 return node
->dataTypeID
;
357 CFStringRef
CFXMLNodeGetString(CFXMLNodeRef node
) {
358 return node
->dataString
;
361 const void *CFXMLNodeGetInfoPtr(CFXMLNodeRef node
) {
362 return node
->additionalData
;
365 CFIndex
CFXMLNodeGetVersion(CFXMLNodeRef node
) {
366 return node
->version
;