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@
24 Copyright (c) 1999-2009, Apple Inc. All rights reserved.
25 Responsibility: Chris Parker
28 #include "CFInternal.h"
29 #include <CoreFoundation/CFXMLParser.h>
35 /* Creates a childless node from desc */
36 CFXMLTreeRef
CFXMLTreeCreateWithNode(CFAllocatorRef allocator
, CFXMLNodeRef node
) {
37 CFTreeContext treeCtxt
;
39 treeCtxt
.info
= (void *)node
;
40 treeCtxt
.retain
= CFRetain
;
41 treeCtxt
.release
= CFRelease
;
42 treeCtxt
.copyDescription
= CFCopyDescription
;
43 return CFTreeCreate(allocator
, &treeCtxt
);
46 CFXMLNodeRef
CFXMLTreeGetNode(CFXMLTreeRef xmlNode
) {
47 CFTreeContext treeContext
;
48 treeContext
.version
= 0;
49 CFTreeGetContext(xmlNode
, &treeContext
);
50 return (CFXMLNodeRef
)treeContext
.info
;
53 // We will probably ultimately want to export this under some public API name
54 __private_extern__ Boolean
CFXMLTreeEqual(CFXMLTreeRef xmlTree1
, CFXMLTreeRef xmlTree2
) {
55 CFXMLNodeRef node1
, node2
;
56 CFXMLTreeRef child1
, child2
;
57 if (CFTreeGetChildCount(xmlTree1
) != CFTreeGetChildCount(xmlTree2
)) return false;
58 node1
= CFXMLTreeGetNode(xmlTree1
);
59 node2
= CFXMLTreeGetNode(xmlTree2
);
60 if (!CFEqual(node1
, node2
)) return false;
61 for (child1
= CFTreeGetFirstChild(xmlTree1
), child2
= CFTreeGetFirstChild(xmlTree2
); child1
&& child2
; child1
= CFTreeGetNextSibling(child1
), child2
= CFTreeGetNextSibling(child2
)) {
62 if (!CFXMLTreeEqual(child1
, child2
)) return false;
67 static void _CFAppendXML(CFMutableStringRef str
, CFXMLTreeRef tree
);
68 static void _CFAppendXMLProlog(CFMutableStringRef str
, const CFXMLTreeRef node
);
69 static void _CFAppendXMLEpilog(CFMutableStringRef str
, const CFXMLTreeRef node
);
71 CFDataRef
CFXMLTreeCreateXMLData(CFAllocatorRef allocator
, CFXMLTreeRef xmlTree
) {
72 CFMutableStringRef xmlStr
;
74 CFStringEncoding encoding
;
76 __CFGenericValidateType(xmlTree
, CFTreeGetTypeID());
78 xmlStr
= CFStringCreateMutable(allocator
, 0);
79 _CFAppendXML(xmlStr
, xmlTree
);
80 if (CFXMLNodeGetTypeCode(CFXMLTreeGetNode(xmlTree
)) == kCFXMLNodeTypeDocument
) {
81 const CFXMLDocumentInfo
*docData
= (CFXMLDocumentInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(xmlTree
));
82 encoding
= docData
? docData
->encoding
: kCFStringEncodingUTF8
;
84 encoding
= kCFStringEncodingUTF8
;
86 result
= CFStringCreateExternalRepresentation(allocator
, xmlStr
, encoding
, 0);
91 static void _CFAppendXML(CFMutableStringRef str
, CFXMLTreeRef tree
) {
93 _CFAppendXMLProlog(str
, tree
);
94 for (child
= CFTreeGetFirstChild(tree
); child
; child
= CFTreeGetNextSibling(child
)) {
95 _CFAppendXML(str
, child
);
97 _CFAppendXMLEpilog(str
, tree
);
100 __private_extern__
void appendQuotedString(CFMutableStringRef str
, CFStringRef strToQuote
) {
101 char quoteChar
= CFStringFindWithOptions(strToQuote
, CFSTR("\""), CFRangeMake(0, CFStringGetLength(strToQuote
)), 0, NULL
) ? '\'' : '\"';
102 CFStringAppendFormat(str
, NULL
, CFSTR("%c%@%c"), quoteChar
, strToQuote
, quoteChar
);
105 static void appendExternalID(CFMutableStringRef str
, CFXMLExternalID
*extID
) {
106 if (extID
->publicID
) {
107 CFStringAppendCString(str
, " PUBLIC ", kCFStringEncodingASCII
);
108 appendQuotedString(str
, extID
->publicID
);
109 if (extID
->systemID
) {
110 // 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
111 CFStringAppendCString(str
, " ", kCFStringEncodingASCII
);
112 appendQuotedString(str
, CFURLGetString(extID
->systemID
));
114 } else if (extID
->systemID
) {
115 CFStringAppendCString(str
, " SYSTEM ", kCFStringEncodingASCII
);
116 appendQuotedString(str
, CFURLGetString(extID
->systemID
));
118 // Should never get here
122 static void appendElementProlog(CFMutableStringRef str
, CFXMLTreeRef tree
) {
123 const CFXMLElementInfo
*data
= (CFXMLElementInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
));
124 CFStringAppendFormat(str
, NULL
, CFSTR("<%@"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
125 if (data
->attributeOrder
) {
126 CFIndex i
, c
= CFArrayGetCount(data
->attributeOrder
);
127 for (i
= 0; i
< c
; i
++) {
128 CFStringRef attr
= (CFStringRef
)CFArrayGetValueAtIndex(data
->attributeOrder
, i
);
129 CFStringRef value
= (CFStringRef
)CFDictionaryGetValue(data
->attributes
, attr
);
130 CFStringAppendFormat(str
, NULL
, CFSTR(" %@="), attr
);
131 appendQuotedString(str
, value
);
135 CFStringAppendCString(str
, "/>", kCFStringEncodingASCII
);
137 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);
141 /* 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 */
142 static void _CFAppendXMLProlog(CFMutableStringRef str
, const CFXMLTreeRef tree
) {
143 switch (CFXMLNodeGetTypeCode(CFXMLTreeGetNode(tree
))) {
144 case kCFXMLNodeTypeDocument
:
146 case kCFXMLNodeTypeElement
:
147 appendElementProlog(str
, tree
);
149 case kCFXMLNodeTypeAttribute
:
150 // Should never be encountered
152 case kCFXMLNodeTypeProcessingInstruction
: {
153 CFXMLProcessingInstructionInfo
*data
= (CFXMLProcessingInstructionInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
));
154 if (data
->dataString
) {
155 CFStringAppendFormat(str
, NULL
, CFSTR("<?%@ %@?>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)), data
->dataString
);
157 CFStringAppendFormat(str
, NULL
, CFSTR("<?%@?>"));
161 case kCFXMLNodeTypeComment
:
162 CFStringAppendFormat(str
, NULL
, CFSTR("<!--%@-->"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
164 case kCFXMLNodeTypeText
:
165 CFStringAppend(str
, CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
167 case kCFXMLNodeTypeCDATASection
:
168 CFStringAppendFormat(str
, NULL
, CFSTR("<![CDATA[%@]]>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
170 case kCFXMLNodeTypeDocumentFragment
:
172 case kCFXMLNodeTypeEntity
: {
173 CFXMLEntityInfo
*data
= (CFXMLEntityInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
));
174 CFStringAppendCString(str
, "<!ENTITY ", kCFStringEncodingASCII
);
175 if (data
->entityType
== kCFXMLEntityTypeParameter
) {
176 CFStringAppend(str
, CFSTR("% "));
178 CFStringAppend(str
, CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
179 CFStringAppend(str
, CFSTR(" "));
180 if (data
->replacementText
) {
181 appendQuotedString(str
, data
->replacementText
);
182 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);
184 appendExternalID(str
, &(data
->entityID
));
185 if (data
->notationName
) {
186 CFStringAppendFormat(str
, NULL
, CFSTR(" NDATA %@"), data
->notationName
);
188 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);
192 case kCFXMLNodeTypeEntityReference
:
194 CFXMLEntityTypeCode entityType
= ((CFXMLEntityReferenceInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
)))->entityType
;
195 if (entityType
== kCFXMLEntityTypeParameter
) {
196 CFStringAppendFormat(str
, NULL
, CFSTR("%%%@;"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
198 CFStringAppendFormat(str
, NULL
, CFSTR("&%@;"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
202 case kCFXMLNodeTypeDocumentType
:
203 CFStringAppendCString(str
, "<!DOCTYPE ", kCFStringEncodingASCII
);
204 CFStringAppend(str
, CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
205 if (CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
))) {
206 CFXMLExternalID
*extID
= &((CFXMLDocumentTypeInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
)))->externalID
;
207 appendExternalID(str
, extID
);
209 CFStringAppendCString(str
, " [", kCFStringEncodingASCII
);
211 case kCFXMLNodeTypeWhitespace
:
212 CFStringAppend(str
, CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
214 case kCFXMLNodeTypeNotation
: {
215 CFXMLNotationInfo
*data
= (CFXMLNotationInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
));
216 CFStringAppendFormat(str
, NULL
, CFSTR("<!NOTATION %@ "), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
217 appendExternalID(str
, &(data
->externalID
));
218 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);
221 case kCFXMLNodeTypeElementTypeDeclaration
:
222 CFStringAppendFormat(str
, NULL
, CFSTR("<!ELEMENT %@ %@>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)), ((CFXMLElementTypeDeclarationInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
)))->contentDescription
);
224 case kCFXMLNodeTypeAttributeListDeclaration
: {
225 CFXMLAttributeListDeclarationInfo
*attListData
= (CFXMLAttributeListDeclarationInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
));
227 CFStringAppendCString(str
, "<!ATTLIST ", kCFStringEncodingASCII
);
228 CFStringAppend(str
, CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
229 for (idx
= 0; idx
< attListData
->numberOfAttributes
; idx
++) {
230 CFXMLAttributeDeclarationInfo
*attr
= &(attListData
->attributes
[idx
]);
231 CFStringAppendFormat(str
, NULL
, CFSTR("\n\t%@ %@ %@"), attr
->attributeName
, attr
->typeString
, attr
->defaultString
);
233 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);
237 CFAssert1(false, __kCFLogAssertion
, "Encountered unexpected XMLDataTypeID %d", CFXMLNodeGetTypeCode(CFXMLTreeGetNode(tree
)));
241 static void _CFAppendXMLEpilog(CFMutableStringRef str
, CFXMLTreeRef tree
) {
242 CFXMLNodeTypeCode typeID
= CFXMLNodeGetTypeCode(CFXMLTreeGetNode(tree
));
243 if (typeID
== kCFXMLNodeTypeElement
) {
244 if (((CFXMLElementInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
)))->isEmpty
) return;
245 CFStringAppendFormat(str
, NULL
, CFSTR("</%@>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
246 } else if (typeID
== kCFXMLNodeTypeDocumentType
) {
247 CFIndex len
= CFStringGetLength(str
);
248 if (CFStringHasSuffix(str
, CFSTR(" ["))) {
249 // There were no in-line DTD elements
250 CFStringDelete(str
, CFRangeMake(len
-2, 2));
252 CFStringAppendCString(str
, "]", kCFStringEncodingASCII
);
254 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);