2 * Copyright (c) 2009 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-2009, Apple Inc. All rights reserved.
26 Responsibility: Chris Parker
29 #include "CFInternal.h"
30 #include <CoreFoundation/CFXMLParser.h>
36 /* Creates a childless node from desc */
37 CFXMLTreeRef
CFXMLTreeCreateWithNode(CFAllocatorRef allocator
, CFXMLNodeRef node
) {
38 CFTreeContext treeCtxt
;
40 treeCtxt
.info
= (void *)node
;
41 treeCtxt
.retain
= CFRetain
;
42 treeCtxt
.release
= CFRelease
;
43 treeCtxt
.copyDescription
= CFCopyDescription
;
44 return CFTreeCreate(allocator
, &treeCtxt
);
47 CFXMLNodeRef
CFXMLTreeGetNode(CFXMLTreeRef xmlNode
) {
48 CFTreeContext treeContext
;
49 treeContext
.version
= 0;
50 CFTreeGetContext(xmlNode
, &treeContext
);
51 return (CFXMLNodeRef
)treeContext
.info
;
54 // We will probably ultimately want to export this under some public API name
55 __private_extern__ Boolean
CFXMLTreeEqual(CFXMLTreeRef xmlTree1
, CFXMLTreeRef xmlTree2
) {
56 CFXMLNodeRef node1
, node2
;
57 CFXMLTreeRef child1
, child2
;
58 if (CFTreeGetChildCount(xmlTree1
) != CFTreeGetChildCount(xmlTree2
)) return false;
59 node1
= CFXMLTreeGetNode(xmlTree1
);
60 node2
= CFXMLTreeGetNode(xmlTree2
);
61 if (!CFEqual(node1
, node2
)) return false;
62 for (child1
= CFTreeGetFirstChild(xmlTree1
), child2
= CFTreeGetFirstChild(xmlTree2
); child1
&& child2
; child1
= CFTreeGetNextSibling(child1
), child2
= CFTreeGetNextSibling(child2
)) {
63 if (!CFXMLTreeEqual(child1
, child2
)) return false;
68 static void _CFAppendXML(CFMutableStringRef str
, CFXMLTreeRef tree
);
69 static void _CFAppendXMLProlog(CFMutableStringRef str
, const CFXMLTreeRef node
);
70 static void _CFAppendXMLEpilog(CFMutableStringRef str
, const CFXMLTreeRef node
);
72 CFDataRef
CFXMLTreeCreateXMLData(CFAllocatorRef allocator
, CFXMLTreeRef xmlTree
) {
73 CFMutableStringRef xmlStr
;
75 CFStringEncoding encoding
;
77 __CFGenericValidateType(xmlTree
, CFTreeGetTypeID());
79 xmlStr
= CFStringCreateMutable(allocator
, 0);
80 _CFAppendXML(xmlStr
, xmlTree
);
81 if (CFXMLNodeGetTypeCode(CFXMLTreeGetNode(xmlTree
)) == kCFXMLNodeTypeDocument
) {
82 const CFXMLDocumentInfo
*docData
= (CFXMLDocumentInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(xmlTree
));
83 encoding
= docData
? docData
->encoding
: kCFStringEncodingUTF8
;
85 encoding
= kCFStringEncodingUTF8
;
87 result
= CFStringCreateExternalRepresentation(allocator
, xmlStr
, encoding
, 0);
92 static void _CFAppendXML(CFMutableStringRef str
, CFXMLTreeRef tree
) {
94 _CFAppendXMLProlog(str
, tree
);
95 for (child
= CFTreeGetFirstChild(tree
); child
; child
= CFTreeGetNextSibling(child
)) {
96 _CFAppendXML(str
, child
);
98 _CFAppendXMLEpilog(str
, tree
);
101 __private_extern__
void appendQuotedString(CFMutableStringRef str
, CFStringRef strToQuote
) {
102 char quoteChar
= CFStringFindWithOptions(strToQuote
, CFSTR("\""), CFRangeMake(0, CFStringGetLength(strToQuote
)), 0, NULL
) ? '\'' : '\"';
103 CFStringAppendFormat(str
, NULL
, CFSTR("%c%@%c"), quoteChar
, strToQuote
, quoteChar
);
106 static void appendExternalID(CFMutableStringRef str
, CFXMLExternalID
*extID
) {
107 if (extID
->publicID
) {
108 CFStringAppendCString(str
, " PUBLIC ", kCFStringEncodingASCII
);
109 appendQuotedString(str
, extID
->publicID
);
110 if (extID
->systemID
) {
111 // 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
112 CFStringAppendCString(str
, " ", kCFStringEncodingASCII
);
113 appendQuotedString(str
, CFURLGetString(extID
->systemID
));
115 } else if (extID
->systemID
) {
116 CFStringAppendCString(str
, " SYSTEM ", kCFStringEncodingASCII
);
117 appendQuotedString(str
, CFURLGetString(extID
->systemID
));
119 // Should never get here
123 static void appendElementProlog(CFMutableStringRef str
, CFXMLTreeRef tree
) {
124 const CFXMLElementInfo
*data
= (CFXMLElementInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
));
125 CFStringAppendFormat(str
, NULL
, CFSTR("<%@"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
126 if (data
->attributeOrder
) {
127 CFIndex i
, c
= CFArrayGetCount(data
->attributeOrder
);
128 for (i
= 0; i
< c
; i
++) {
129 CFStringRef attr
= (CFStringRef
)CFArrayGetValueAtIndex(data
->attributeOrder
, i
);
130 CFStringRef value
= (CFStringRef
)CFDictionaryGetValue(data
->attributes
, attr
);
131 CFStringAppendFormat(str
, NULL
, CFSTR(" %@="), attr
);
132 appendQuotedString(str
, value
);
136 CFStringAppendCString(str
, "/>", kCFStringEncodingASCII
);
138 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);
142 /* 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 */
143 static void _CFAppendXMLProlog(CFMutableStringRef str
, const CFXMLTreeRef tree
) {
144 switch (CFXMLNodeGetTypeCode(CFXMLTreeGetNode(tree
))) {
145 case kCFXMLNodeTypeDocument
:
147 case kCFXMLNodeTypeElement
:
148 appendElementProlog(str
, tree
);
150 case kCFXMLNodeTypeAttribute
:
151 // Should never be encountered
153 case kCFXMLNodeTypeProcessingInstruction
: {
154 CFXMLProcessingInstructionInfo
*data
= (CFXMLProcessingInstructionInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
));
155 if (data
->dataString
) {
156 CFStringAppendFormat(str
, NULL
, CFSTR("<?%@ %@?>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)), data
->dataString
);
158 CFStringAppendFormat(str
, NULL
, CFSTR("<?%@?>"));
162 case kCFXMLNodeTypeComment
:
163 CFStringAppendFormat(str
, NULL
, CFSTR("<!--%@-->"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
165 case kCFXMLNodeTypeText
:
166 CFStringAppend(str
, CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
168 case kCFXMLNodeTypeCDATASection
:
169 CFStringAppendFormat(str
, NULL
, CFSTR("<![CDATA[%@]]>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
171 case kCFXMLNodeTypeDocumentFragment
:
173 case kCFXMLNodeTypeEntity
: {
174 CFXMLEntityInfo
*data
= (CFXMLEntityInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
));
175 CFStringAppendCString(str
, "<!ENTITY ", kCFStringEncodingASCII
);
176 if (data
->entityType
== kCFXMLEntityTypeParameter
) {
177 CFStringAppend(str
, CFSTR("% "));
179 CFStringAppend(str
, CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
180 CFStringAppend(str
, CFSTR(" "));
181 if (data
->replacementText
) {
182 appendQuotedString(str
, data
->replacementText
);
183 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);
185 appendExternalID(str
, &(data
->entityID
));
186 if (data
->notationName
) {
187 CFStringAppendFormat(str
, NULL
, CFSTR(" NDATA %@"), data
->notationName
);
189 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);
193 case kCFXMLNodeTypeEntityReference
:
195 CFXMLEntityTypeCode entityType
= ((CFXMLEntityReferenceInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
)))->entityType
;
196 if (entityType
== kCFXMLEntityTypeParameter
) {
197 CFStringAppendFormat(str
, NULL
, CFSTR("%%%@;"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
199 CFStringAppendFormat(str
, NULL
, CFSTR("&%@;"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
203 case kCFXMLNodeTypeDocumentType
:
204 CFStringAppendCString(str
, "<!DOCTYPE ", kCFStringEncodingASCII
);
205 CFStringAppend(str
, CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
206 if (CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
))) {
207 CFXMLExternalID
*extID
= &((CFXMLDocumentTypeInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
)))->externalID
;
208 appendExternalID(str
, extID
);
210 CFStringAppendCString(str
, " [", kCFStringEncodingASCII
);
212 case kCFXMLNodeTypeWhitespace
:
213 CFStringAppend(str
, CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
215 case kCFXMLNodeTypeNotation
: {
216 CFXMLNotationInfo
*data
= (CFXMLNotationInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
));
217 CFStringAppendFormat(str
, NULL
, CFSTR("<!NOTATION %@ "), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
218 appendExternalID(str
, &(data
->externalID
));
219 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);
222 case kCFXMLNodeTypeElementTypeDeclaration
:
223 CFStringAppendFormat(str
, NULL
, CFSTR("<!ELEMENT %@ %@>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)), ((CFXMLElementTypeDeclarationInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
)))->contentDescription
);
225 case kCFXMLNodeTypeAttributeListDeclaration
: {
226 CFXMLAttributeListDeclarationInfo
*attListData
= (CFXMLAttributeListDeclarationInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
));
228 CFStringAppendCString(str
, "<!ATTLIST ", kCFStringEncodingASCII
);
229 CFStringAppend(str
, CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
230 for (idx
= 0; idx
< attListData
->numberOfAttributes
; idx
++) {
231 CFXMLAttributeDeclarationInfo
*attr
= &(attListData
->attributes
[idx
]);
232 CFStringAppendFormat(str
, NULL
, CFSTR("\n\t%@ %@ %@"), attr
->attributeName
, attr
->typeString
, attr
->defaultString
);
234 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);
238 CFAssert1(false, __kCFLogAssertion
, "Encountered unexpected XMLDataTypeID %d", CFXMLNodeGetTypeCode(CFXMLTreeGetNode(tree
)));
242 static void _CFAppendXMLEpilog(CFMutableStringRef str
, CFXMLTreeRef tree
) {
243 CFXMLNodeTypeCode typeID
= CFXMLNodeGetTypeCode(CFXMLTreeGetNode(tree
));
244 if (typeID
== kCFXMLNodeTypeElement
) {
245 if (((CFXMLElementInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
)))->isEmpty
) return;
246 CFStringAppendFormat(str
, NULL
, CFSTR("</%@>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
247 } else if (typeID
== kCFXMLNodeTypeDocumentType
) {
248 CFIndex len
= CFStringGetLength(str
);
249 if (CFStringHasSuffix(str
, CFSTR(" ["))) {
250 // There were no in-line DTD elements
251 CFStringDelete(str
, CFRangeMake(len
-2, 2));
253 CFStringAppendCString(str
, "]", kCFStringEncodingASCII
);
255 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);