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