]> git.saurik.com Git - apple/cf.git/blob - CFXMLTree.c
CF-550.13.tar.gz
[apple/cf.git] / CFXMLTree.c
1 /*
2 * Copyright (c) 2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /* CFXMLTree.c
25 Copyright (c) 1999-2009, Apple Inc. All rights reserved.
26 Responsibility: Chris Parker
27 */
28
29 #include "CFInternal.h"
30 #include <CoreFoundation/CFXMLParser.h>
31
32 /*************/
33 /* CFXMLTree */
34 /*************/
35
36 /* Creates a childless node from desc */
37 CFXMLTreeRef CFXMLTreeCreateWithNode(CFAllocatorRef allocator, CFXMLNodeRef node) {
38 CFTreeContext treeCtxt;
39 treeCtxt.version = 0;
40 treeCtxt.info = (void *)node;
41 treeCtxt.retain = CFRetain;
42 treeCtxt.release = CFRelease;
43 treeCtxt.copyDescription = CFCopyDescription;
44 return CFTreeCreate(allocator, &treeCtxt);
45 }
46
47 CFXMLNodeRef CFXMLTreeGetNode(CFXMLTreeRef xmlNode) {
48 CFTreeContext treeContext;
49 treeContext.version = 0;
50 CFTreeGetContext(xmlNode, &treeContext);
51 return (CFXMLNodeRef)treeContext.info;
52 }
53
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;
64 }
65 return true;
66 }
67
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);
71
72 CFDataRef CFXMLTreeCreateXMLData(CFAllocatorRef allocator, CFXMLTreeRef xmlTree) {
73 CFMutableStringRef xmlStr;
74 CFDataRef result;
75 CFStringEncoding encoding;
76
77 __CFGenericValidateType(xmlTree, CFTreeGetTypeID());
78
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;
84 } else {
85 encoding = kCFStringEncodingUTF8;
86 }
87 result = CFStringCreateExternalRepresentation(allocator, xmlStr, encoding, 0);
88 CFRelease(xmlStr);
89 return result;
90 }
91
92 static void _CFAppendXML(CFMutableStringRef str, CFXMLTreeRef tree) {
93 CFXMLTreeRef child;
94 _CFAppendXMLProlog(str, tree);
95 for (child = CFTreeGetFirstChild(tree); child; child = CFTreeGetNextSibling(child)) {
96 _CFAppendXML(str, child);
97 }
98 _CFAppendXMLEpilog(str, tree);
99 }
100
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);
104 }
105
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));
114 }
115 } else if (extID->systemID) {
116 CFStringAppendCString(str, " SYSTEM ", kCFStringEncodingASCII);
117 appendQuotedString(str, CFURLGetString(extID->systemID));
118 } else {
119 // Should never get here
120 }
121 }
122
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);
133 }
134 }
135 if (data->isEmpty) {
136 CFStringAppendCString(str, "/>", kCFStringEncodingASCII);
137 } else {
138 CFStringAppendCString(str, ">", kCFStringEncodingASCII);
139 }
140 }
141
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:
146 break;
147 case kCFXMLNodeTypeElement:
148 appendElementProlog(str, tree);
149 break;
150 case kCFXMLNodeTypeAttribute:
151 // Should never be encountered
152 break;
153 case kCFXMLNodeTypeProcessingInstruction: {
154 CFXMLProcessingInstructionInfo *data = (CFXMLProcessingInstructionInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree));
155 if (data->dataString) {
156 CFStringAppendFormat(str, NULL, CFSTR("<?%@ %@?>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree)), data->dataString);
157 } else {
158 CFStringAppendFormat(str, NULL, CFSTR("<?%@?>"));
159 }
160 break;
161 }
162 case kCFXMLNodeTypeComment:
163 CFStringAppendFormat(str, NULL, CFSTR("<!--%@-->"), CFXMLNodeGetString(CFXMLTreeGetNode(tree)));
164 break;
165 case kCFXMLNodeTypeText:
166 CFStringAppend(str, CFXMLNodeGetString(CFXMLTreeGetNode(tree)));
167 break;
168 case kCFXMLNodeTypeCDATASection:
169 CFStringAppendFormat(str, NULL, CFSTR("<![CDATA[%@]]>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree)));
170 break;
171 case kCFXMLNodeTypeDocumentFragment:
172 break;
173 case kCFXMLNodeTypeEntity: {
174 CFXMLEntityInfo *data = (CFXMLEntityInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree));
175 CFStringAppendCString(str, "<!ENTITY ", kCFStringEncodingASCII);
176 if (data->entityType == kCFXMLEntityTypeParameter) {
177 CFStringAppend(str, CFSTR("% "));
178 }
179 CFStringAppend(str, CFXMLNodeGetString(CFXMLTreeGetNode(tree)));
180 CFStringAppend(str, CFSTR(" "));
181 if (data->replacementText) {
182 appendQuotedString(str, data->replacementText);
183 CFStringAppendCString(str, ">", kCFStringEncodingASCII);
184 } else {
185 appendExternalID(str, &(data->entityID));
186 if (data->notationName) {
187 CFStringAppendFormat(str, NULL, CFSTR(" NDATA %@"), data->notationName);
188 }
189 CFStringAppendCString(str, ">", kCFStringEncodingASCII);
190 }
191 break;
192 }
193 case kCFXMLNodeTypeEntityReference:
194 {
195 CFXMLEntityTypeCode entityType = ((CFXMLEntityReferenceInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree)))->entityType;
196 if (entityType == kCFXMLEntityTypeParameter) {
197 CFStringAppendFormat(str, NULL, CFSTR("%%%@;"), CFXMLNodeGetString(CFXMLTreeGetNode(tree)));
198 } else {
199 CFStringAppendFormat(str, NULL, CFSTR("&%@;"), CFXMLNodeGetString(CFXMLTreeGetNode(tree)));
200 }
201 break;
202 }
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);
209 }
210 CFStringAppendCString(str, " [", kCFStringEncodingASCII);
211 break;
212 case kCFXMLNodeTypeWhitespace:
213 CFStringAppend(str, CFXMLNodeGetString(CFXMLTreeGetNode(tree)));
214 break;
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);
220 break;
221 }
222 case kCFXMLNodeTypeElementTypeDeclaration:
223 CFStringAppendFormat(str, NULL, CFSTR("<!ELEMENT %@ %@>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree)), ((CFXMLElementTypeDeclarationInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree)))->contentDescription);
224 break;
225 case kCFXMLNodeTypeAttributeListDeclaration: {
226 CFXMLAttributeListDeclarationInfo *attListData = (CFXMLAttributeListDeclarationInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree));
227 CFIndex idx;
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);
233 }
234 CFStringAppendCString(str, ">", kCFStringEncodingASCII);
235 break;
236 }
237 default:
238 CFAssert1(false, __kCFLogAssertion, "Encountered unexpected XMLDataTypeID %d", CFXMLNodeGetTypeCode(CFXMLTreeGetNode(tree)));
239 }
240 }
241
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));
252 } else {
253 CFStringAppendCString(str, "]", kCFStringEncodingASCII);
254 }
255 CFStringAppendCString(str, ">", kCFStringEncodingASCII);
256 }
257 }
258
259