2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 Copyright 1999-2002, Apple, Inc. All rights reserved.
27 Responsibility: Chris Parker
30 #include "CFInternal.h"
31 #include <CoreFoundation/CFXMLParser.h>
37 /* Creates a childless node from desc */
38 CFXMLTreeRef
CFXMLTreeCreateWithNode(CFAllocatorRef allocator
, CFXMLNodeRef node
) {
39 CFTreeContext treeCtxt
;
41 treeCtxt
.info
= (void *)node
;
42 treeCtxt
.retain
= CFRetain
;
43 treeCtxt
.release
= CFRelease
;
44 treeCtxt
.copyDescription
= CFCopyDescription
;
45 return CFTreeCreate(allocator
, &treeCtxt
);
48 CFXMLNodeRef
CFXMLTreeGetNode(CFXMLTreeRef xmlNode
) {
49 CFTreeContext treeContext
;
50 treeContext
.version
= 0;
51 CFTreeGetContext(xmlNode
, &treeContext
);
52 return (CFXMLNodeRef
)treeContext
.info
;
55 // We will probably ultimately want to export this under some public API name
56 __private_extern__ Boolean
CFXMLTreeEqual(CFXMLTreeRef xmlTree1
, CFXMLTreeRef xmlTree2
) {
57 CFXMLNodeRef node1
, node2
;
58 CFXMLTreeRef child1
, child2
;
59 if (CFTreeGetChildCount(xmlTree1
) != CFTreeGetChildCount(xmlTree2
)) return false;
60 node1
= CFXMLTreeGetNode(xmlTree1
);
61 node2
= CFXMLTreeGetNode(xmlTree2
);
62 if (!CFEqual(node1
, node2
)) return false;
63 for (child1
= CFTreeGetFirstChild(xmlTree1
), child2
= CFTreeGetFirstChild(xmlTree2
); child1
&& child2
; child1
= CFTreeGetNextSibling(child1
), child2
= CFTreeGetNextSibling(child2
)) {
64 if (!CFXMLTreeEqual(child1
, child2
)) return false;
69 static void _CFAppendXML(CFMutableStringRef str
, CFXMLTreeRef tree
);
70 static void _CFAppendXMLProlog(CFMutableStringRef str
, const CFXMLTreeRef node
);
71 static void _CFAppendXMLEpilog(CFMutableStringRef str
, const CFXMLTreeRef node
);
73 CFDataRef
CFXMLTreeCreateXMLData(CFAllocatorRef allocator
, CFXMLTreeRef xmlTree
) {
74 CFMutableStringRef xmlStr
;
76 CFStringEncoding encoding
;
78 __CFGenericValidateType(xmlTree
, CFTreeGetTypeID());
80 xmlStr
= CFStringCreateMutable(allocator
, 0);
81 _CFAppendXML(xmlStr
, xmlTree
);
82 if (CFXMLNodeGetTypeCode(CFXMLTreeGetNode(xmlTree
)) == kCFXMLNodeTypeDocument
) {
83 const CFXMLDocumentInfo
*docData
= CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(xmlTree
));
84 encoding
= docData
? docData
->encoding
: kCFStringEncodingUTF8
;
86 encoding
= kCFStringEncodingUTF8
;
88 result
= CFStringCreateExternalRepresentation(allocator
, xmlStr
, encoding
, 0);
93 static void _CFAppendXML(CFMutableStringRef str
, CFXMLTreeRef tree
) {
95 _CFAppendXMLProlog(str
, tree
);
96 for (child
= CFTreeGetFirstChild(tree
); child
; child
= CFTreeGetNextSibling(child
)) {
97 _CFAppendXML(str
, child
);
99 _CFAppendXMLEpilog(str
, tree
);
102 __private_extern__
void appendQuotedString(CFMutableStringRef str
, CFStringRef strToQuote
) {
103 char quoteChar
= CFStringFindWithOptions(strToQuote
, CFSTR("\""), CFRangeMake(0, CFStringGetLength(strToQuote
)), 0, NULL
) ? '\'' : '\"';
104 CFStringAppendFormat(str
, NULL
, CFSTR("%c%@%c"), quoteChar
, strToQuote
, quoteChar
);
107 static void appendExternalID(CFMutableStringRef str
, CFXMLExternalID
*extID
) {
108 if (extID
->publicID
) {
109 CFStringAppendCString(str
, " PUBLIC ", kCFStringEncodingASCII
);
110 appendQuotedString(str
, extID
->publicID
);
111 if (extID
->systemID
) {
112 // 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
113 CFStringAppendCString(str
, " ", kCFStringEncodingASCII
);
114 appendQuotedString(str
, CFURLGetString(extID
->systemID
));
116 } else if (extID
->systemID
) {
117 CFStringAppendCString(str
, " SYSTEM ", kCFStringEncodingASCII
);
118 appendQuotedString(str
, CFURLGetString(extID
->systemID
));
120 // Should never get here
124 static void appendElementProlog(CFMutableStringRef str
, CFXMLTreeRef tree
) {
125 const CFXMLElementInfo
*data
= CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
));
126 CFStringAppendFormat(str
, NULL
, CFSTR("<%@"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
127 if (data
->attributeOrder
) {
128 CFIndex i
, c
= CFArrayGetCount(data
->attributeOrder
);
129 for (i
= 0; i
< c
; i
++) {
130 CFStringRef attr
= CFArrayGetValueAtIndex(data
->attributeOrder
, i
);
131 CFStringRef value
= CFDictionaryGetValue(data
->attributes
, attr
);
132 CFStringAppendFormat(str
, NULL
, CFSTR(" %@="), attr
);
133 appendQuotedString(str
, value
);
137 CFStringAppendCString(str
, "/>", kCFStringEncodingASCII
);
139 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);
143 /* 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 */
144 static void _CFAppendXMLProlog(CFMutableStringRef str
, const CFXMLTreeRef tree
) {
145 switch (CFXMLNodeGetTypeCode(CFXMLTreeGetNode(tree
))) {
146 case kCFXMLNodeTypeDocument
:
148 case kCFXMLNodeTypeElement
:
149 appendElementProlog(str
, tree
);
151 case kCFXMLNodeTypeAttribute
:
152 // Should never be encountered
154 case kCFXMLNodeTypeProcessingInstruction
: {
155 CFXMLProcessingInstructionInfo
*data
= (CFXMLProcessingInstructionInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
));
156 if (data
->dataString
) {
157 CFStringAppendFormat(str
, NULL
, CFSTR("<?%@ %@?>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)), data
->dataString
);
159 CFStringAppendFormat(str
, NULL
, CFSTR("<?%@?>"));
163 case kCFXMLNodeTypeComment
:
164 CFStringAppendFormat(str
, NULL
, CFSTR("<!--%@-->"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
166 case kCFXMLNodeTypeText
:
167 CFStringAppend(str
, CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
169 case kCFXMLNodeTypeCDATASection
:
170 CFStringAppendFormat(str
, NULL
, CFSTR("<![CDATA[%@]]>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
172 case kCFXMLNodeTypeDocumentFragment
:
174 case kCFXMLNodeTypeEntity
: {
175 CFXMLEntityInfo
*data
= (CFXMLEntityInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
));
176 CFStringAppendCString(str
, "<!ENTITY ", kCFStringEncodingASCII
);
177 if (data
->entityType
== kCFXMLEntityTypeParameter
) {
178 CFStringAppend(str
, CFSTR("% "));
180 CFStringAppend(str
, CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
181 CFStringAppend(str
, CFSTR(" "));
182 if (data
->replacementText
) {
183 appendQuotedString(str
, data
->replacementText
);
184 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);
186 appendExternalID(str
, &(data
->entityID
));
187 if (data
->notationName
) {
188 CFStringAppendFormat(str
, NULL
, CFSTR(" NDATA %@"), data
->notationName
);
190 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);
194 case kCFXMLNodeTypeEntityReference
:
196 CFXMLEntityTypeCode entityType
= ((CFXMLEntityReferenceInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
)))->entityType
;
197 if (entityType
== kCFXMLEntityTypeParameter
) {
198 CFStringAppendFormat(str
, NULL
, CFSTR("%%%@;"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
200 CFStringAppendFormat(str
, NULL
, CFSTR("&%@;"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
204 case kCFXMLNodeTypeDocumentType
:
205 CFStringAppendCString(str
, "<!DOCTYPE ", kCFStringEncodingASCII
);
206 CFStringAppend(str
, CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
207 if (CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
))) {
208 CFXMLExternalID
*extID
= &((CFXMLDocumentTypeInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
)))->externalID
;
209 appendExternalID(str
, extID
);
211 CFStringAppendCString(str
, " [", kCFStringEncodingASCII
);
213 case kCFXMLNodeTypeWhitespace
:
214 CFStringAppend(str
, CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
216 case kCFXMLNodeTypeNotation
: {
217 CFXMLNotationInfo
*data
= (CFXMLNotationInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
));
218 CFStringAppendFormat(str
, NULL
, CFSTR("<!NOTATION %@ "), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
219 appendExternalID(str
, &(data
->externalID
));
220 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);
223 case kCFXMLNodeTypeElementTypeDeclaration
:
224 CFStringAppendFormat(str
, NULL
, CFSTR("<!ELEMENT %@ %@>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)), ((CFXMLElementTypeDeclarationInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
)))->contentDescription
);
226 case kCFXMLNodeTypeAttributeListDeclaration
: {
227 CFXMLAttributeListDeclarationInfo
*attListData
= (CFXMLAttributeListDeclarationInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
));
229 CFStringAppendCString(str
, "<!ATTLIST ", kCFStringEncodingASCII
);
230 CFStringAppend(str
, CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
231 for (idx
= 0; idx
< attListData
->numberOfAttributes
; idx
++) {
232 CFXMLAttributeDeclarationInfo
*attr
= &(attListData
->attributes
[idx
]);
233 CFStringAppendFormat(str
, NULL
, CFSTR("\n\t%@ %@ %@"), attr
->attributeName
, attr
->typeString
, attr
->defaultString
);
235 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);
239 CFAssert1(false, __kCFLogAssertion
, "Encountered unexpected XMLDataTypeID %d", CFXMLNodeGetTypeCode(CFXMLTreeGetNode(tree
)));
243 static void _CFAppendXMLEpilog(CFMutableStringRef str
, CFXMLTreeRef tree
) {
244 CFXMLNodeTypeCode typeID
= CFXMLNodeGetTypeCode(CFXMLTreeGetNode(tree
));
245 if (typeID
== kCFXMLNodeTypeElement
) {
246 if (((CFXMLElementInfo
*)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree
)))->isEmpty
) return;
247 CFStringAppendFormat(str
, NULL
, CFSTR("</%@>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree
)));
248 } else if (typeID
== kCFXMLNodeTypeDocumentType
) {
249 CFIndex len
= CFStringGetLength(str
);
250 if (CFStringHasSuffix(str
, CFSTR(" ["))) {
251 // There were no in-line DTD elements
252 CFStringDelete(str
, CFRangeMake(len
-2, 2));
254 CFStringAppendCString(str
, "]", kCFStringEncodingASCII
);
256 CFStringAppendCString(str
, ">", kCFStringEncodingASCII
);