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) 1999-2014, Apple Inc. All rights reserved.
26 Responsibility: David Smith
29 #include "CFInternal.h"
30 #include <CoreFoundation/CFXMLParser.h>
32 #pragma GCC diagnostic push
33 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
39 /* Creates a childless node from desc */
40 CFXMLTreeRef
CFXMLTreeCreateWithNode(CFAllocatorRef allocator
, CFXMLNodeRef node
) {
41 CFTreeContext treeCtxt
;
43 treeCtxt
.info
= (void *)node
;
44 treeCtxt
.retain
= CFRetain
;
45 treeCtxt
.release
= CFRelease
;
46 treeCtxt
.copyDescription
= CFCopyDescription
;
47 return CFTreeCreate(allocator
, &treeCtxt
);
50 CFXMLNodeRef
CFXMLTreeGetNode(CFXMLTreeRef xmlNode
) {
51 CFTreeContext treeContext
;
52 treeContext
.version
= 0;
53 CFTreeGetContext(xmlNode
, &treeContext
);
54 return (CFXMLNodeRef
)treeContext
.info
;
57 // We will probably ultimately want to export this under some public API name
58 CF_PRIVATE Boolean
CFXMLTreeEqual(CFXMLTreeRef xmlTree1
, CFXMLTreeRef xmlTree2
) {
59 CFXMLNodeRef node1
, node2
;
60 CFXMLTreeRef child1
, child2
;
61 if (CFTreeGetChildCount(xmlTree1
) != CFTreeGetChildCount(xmlTree2
)) return false;
62 node1
= CFXMLTreeGetNode(xmlTree1
);
63 node2
= CFXMLTreeGetNode(xmlTree2
);
64 if (!CFEqual(node1
, node2
)) return false;
65 for (child1
= CFTreeGetFirstChild(xmlTree1
), child2
= CFTreeGetFirstChild(xmlTree2
); child1
&& child2
; child1
= CFTreeGetNextSibling(child1
), child2
= CFTreeGetNextSibling(child2
)) {
66 if (!CFXMLTreeEqual(child1
, child2
)) return false;
71 static void _CFAppendXML(CFMutableStringRef str
, CFXMLTreeRef tree
);
72 static void _CFAppendXMLProlog(CFMutableStringRef str
, const CFXMLTreeRef node
);
73 static void _CFAppendXMLEpilog(CFMutableStringRef str
, const CFXMLTreeRef node
);
75 CFDataRef
CFXMLTreeCreateXMLData(CFAllocatorRef allocator
, CFXMLTreeRef xmlTree
) {
76 CFMutableStringRef xmlStr
;
78 CFStringEncoding encoding
;
80 __CFGenericValidateType(xmlTree
, CFTreeGetTypeID());
82 xmlStr
= CFStringCreateMutable(allocator
, 0);
83 _CFAppendXML(xmlStr
, xmlTree
);
84 if (CFXMLNodeGetTypeCode(CFXMLTreeGetNode(xmlTree
)) == kCFXMLNodeTypeDocument
) {
85 const CFXMLDocumentInfo
*docData
= (CFXMLDocumentInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(xmlTree
));
86 encoding
= docData
? docData
->encoding
: kCFStringEncodingUTF8
;
88 encoding
= kCFStringEncodingUTF8
;
90 result
= CFStringCreateExternalRepresentation(allocator
, xmlStr
, encoding
, 0);
95 static void _CFAppendXML(CFMutableStringRef str
, CFXMLTreeRef tree
) {
97 _CFAppendXMLProlog(str
, tree
);
98 for (child
= CFTreeGetFirstChild(tree
); child
; child
= CFTreeGetNextSibling(child
)) {
99 _CFAppendXML(str
, child
);
101 _CFAppendXMLEpilog(str
, tree
);
104 CF_PRIVATE
void appendQuotedString(CFMutableStringRef str
, CFStringRef strToQuote
) {
105 char quoteChar
= CFStringFindWithOptions(strToQuote
, CFSTR("\""), CFRangeMake(0, CFStringGetLength(strToQuote
)), 0, NULL
) ? '\'' : '\"';
106 CFStringAppendFormat(str
, NULL
, CFSTR("%c%@%c"), quoteChar
, strToQuote
, quoteChar
);
109 static void appendExternalID(CFMutableStringRef str
, CFXMLExternalID
*extID
) {
110 if (extID
->publicID
) {
111 CFStringAppendCString(str
, " PUBLIC ", kCFStringEncodingASCII
);
112 appendQuotedString(str
, extID
->publicID
);
113 if (extID
->systemID
) {
114 // Technically, for externalIDs, systemID must not be NULL. However, by testing for a NULL systemID, we can use this to emit publicIDs, too. REW, 2/15/2000
115 CFStringAppendCString(str
, " ", kCFStringEncodingASCII
);
116 appendQuotedString(str
, CFURLGetString(extID
->systemID
));
118 } else if (extID
->systemID
) {
119 CFStringAppendCString(str
, " SYSTEM ", kCFStringEncodingASCII
);
120 appendQuotedString(str
, CFURLGetString(extID
->systemID
));
122 // Should never get here
126 static void appendElementProlog(CFMutableStringRef str
, CFXMLTreeRef tree
) {
127 const CFXMLElementInfo
*data
= (CFXMLElementInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
));
128 CFStringAppendFormat(str
, NULL
, CFSTR("<%@"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
129 if (data
->attributeOrder
) {
130 CFIndex i
, c
= CFArrayGetCount(data
->attributeOrder
);
131 for (i
= 0; i
< c
; i
++) {
132 CFStringRef attr
= (CFStringRef
)CFArrayGetValueAtIndex(data
->attributeOrder
, i
);
133 CFStringRef value
= (CFStringRef
)CFDictionaryGetValue(data
->attributes
, attr
);
134 CFStringAppendFormat(str
, NULL
, CFSTR(" %@="), attr
);
135 appendQuotedString(str
, value
);
139 CFStringAppendCString(str
, "/>", kCFStringEncodingASCII
);
141 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);
145 /* Although named "prolog", for leafs of the tree, this is the only XML generation function called. This is why Comments, Processing Instructions, etc. generate their XML during this function. REW, 2/11/2000 */
146 static void _CFAppendXMLProlog(CFMutableStringRef str
, const CFXMLTreeRef tree
) {
147 switch (CFXMLNodeGetTypeCode(CFXMLTreeGetNode(tree
))) {
148 case kCFXMLNodeTypeDocument
:
150 case kCFXMLNodeTypeElement
:
151 appendElementProlog(str
, tree
);
153 case kCFXMLNodeTypeAttribute
:
154 // Should never be encountered
156 case kCFXMLNodeTypeProcessingInstruction
: {
157 CFXMLProcessingInstructionInfo
*data
= (CFXMLProcessingInstructionInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
));
158 if (data
->dataString
) {
159 CFStringAppendFormat(str
, NULL
, CFSTR("<?%@ %@?>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)), data
->dataString
);
161 CFStringAppendFormat(str
, NULL
, CFSTR("<?%@?>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
165 case kCFXMLNodeTypeComment
:
166 CFStringAppendFormat(str
, NULL
, CFSTR("<!--%@-->"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
168 case kCFXMLNodeTypeText
:
169 CFStringAppend(str
, CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
171 case kCFXMLNodeTypeCDATASection
:
172 CFStringAppendFormat(str
, NULL
, CFSTR("<![CDATA[%@]]>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
174 case kCFXMLNodeTypeDocumentFragment
:
176 case kCFXMLNodeTypeEntity
: {
177 CFXMLEntityInfo
*data
= (CFXMLEntityInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
));
178 CFStringAppendCString(str
, "<!ENTITY ", kCFStringEncodingASCII
);
179 if (data
->entityType
== kCFXMLEntityTypeParameter
) {
180 CFStringAppend(str
, CFSTR("% "));
182 CFStringAppend(str
, CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
183 CFStringAppend(str
, CFSTR(" "));
184 if (data
->replacementText
) {
185 appendQuotedString(str
, data
->replacementText
);
186 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);
188 appendExternalID(str
, &(data
->entityID
));
189 if (data
->notationName
) {
190 CFStringAppendFormat(str
, NULL
, CFSTR(" NDATA %@"), data
->notationName
);
192 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);
196 case kCFXMLNodeTypeEntityReference
:
198 CFXMLEntityTypeCode entityType
= ((CFXMLEntityReferenceInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
)))->entityType
;
199 if (entityType
== kCFXMLEntityTypeParameter
) {
200 CFStringAppendFormat(str
, NULL
, CFSTR("%%%@;"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
202 CFStringAppendFormat(str
, NULL
, CFSTR("&%@;"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
206 case kCFXMLNodeTypeDocumentType
:
207 CFStringAppendCString(str
, "<!DOCTYPE ", kCFStringEncodingASCII
);
208 CFStringAppend(str
, CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
209 if (CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
))) {
210 CFXMLExternalID
*extID
= &((CFXMLDocumentTypeInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
)))->externalID
;
211 appendExternalID(str
, extID
);
213 CFStringAppendCString(str
, " [", kCFStringEncodingASCII
);
215 case kCFXMLNodeTypeWhitespace
:
216 CFStringAppend(str
, CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
218 case kCFXMLNodeTypeNotation
: {
219 CFXMLNotationInfo
*data
= (CFXMLNotationInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
));
220 CFStringAppendFormat(str
, NULL
, CFSTR("<!NOTATION %@ "), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
221 appendExternalID(str
, &(data
->externalID
));
222 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);
225 case kCFXMLNodeTypeElementTypeDeclaration
:
226 CFStringAppendFormat(str
, NULL
, CFSTR("<!ELEMENT %@ %@>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)), ((CFXMLElementTypeDeclarationInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
)))->contentDescription
);
228 case kCFXMLNodeTypeAttributeListDeclaration
: {
229 CFXMLAttributeListDeclarationInfo
*attListData
= (CFXMLAttributeListDeclarationInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
));
231 CFStringAppendCString(str
, "<!ATTLIST ", kCFStringEncodingASCII
);
232 CFStringAppend(str
, CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
233 for (idx
= 0; idx
< attListData
->numberOfAttributes
; idx
++) {
234 CFXMLAttributeDeclarationInfo
*attr
= &(attListData
->attributes
[idx
]);
235 CFStringAppendFormat(str
, NULL
, CFSTR("\n\t%@ %@ %@"), attr
->attributeName
, attr
->typeString
, attr
->defaultString
);
237 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);
241 CFAssert1(false, __kCFLogAssertion
, "Encountered unexpected XMLDataTypeID %d", CFXMLNodeGetTypeCode(CFXMLTreeGetNode(tree
)));
245 static void _CFAppendXMLEpilog(CFMutableStringRef str
, CFXMLTreeRef tree
) {
246 CFXMLNodeTypeCode typeID
= CFXMLNodeGetTypeCode(CFXMLTreeGetNode(tree
));
247 if (typeID
== kCFXMLNodeTypeElement
) {
248 if (((CFXMLElementInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
)))->isEmpty
) return;
249 CFStringAppendFormat(str
, NULL
, CFSTR("</%@>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
250 } else if (typeID
== kCFXMLNodeTypeDocumentType
) {
251 CFIndex len
= CFStringGetLength(str
);
252 if (CFStringHasSuffix(str
, CFSTR(" ["))) {
253 // There were no in-line DTD elements
254 CFStringDelete(str
, CFRangeMake(len
-2, 2));
256 CFStringAppendCString(str
, "]", kCFStringEncodingASCII
);
258 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);
262 #pragma GCC diagnostic pop