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