/ CFXMLTree.c
CFXMLTree.c
  1  /*
  2   * Copyright (c) 2015 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-2014, Apple Inc. All rights reserved.
 26  	Responsibility: David Smith
 27  */
 28  
 29  #include "CFInternal.h"
 30  #include <CoreFoundation/CFXMLParser.h>
 31  
 32  #pragma GCC diagnostic push
 33  #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 34  
 35  /*************/
 36  /* CFXMLTree */
 37  /*************/
 38  
 39  /* Creates a childless node from desc */
 40  CFXMLTreeRef CFXMLTreeCreateWithNode(CFAllocatorRef allocator, CFXMLNodeRef node) {
 41      CFTreeContext treeCtxt;
 42      treeCtxt.version = 0;
 43      treeCtxt.info = (void *)node;
 44      treeCtxt.retain = CFRetain;
 45      treeCtxt.release = CFRelease;
 46      treeCtxt.copyDescription = CFCopyDescription;
 47      return CFTreeCreate(allocator, &treeCtxt);
 48  }
 49  
 50  CFXMLNodeRef CFXMLTreeGetNode(CFXMLTreeRef xmlNode) {
 51      CFTreeContext treeContext;
 52      treeContext.version = 0;
 53      CFTreeGetContext(xmlNode, &treeContext);
 54      return (CFXMLNodeRef)treeContext.info;
 55  }
 56  
 57  // We will probably ultimately want to export this under some public API name
 58  CF_PRIVATE Boolean CFXMLTreeEqual(CFXMLTreeRef xmlTree1, CFXMLTreeRef xmlTree2) {
 59      CFXMLNodeRef node1, node2;
 60      CFXMLTreeRef child1, child2;
 61      if (CFTreeGetChildCount(xmlTree1) != CFTreeGetChildCount(xmlTree2)) return false;
 62      node1 = CFXMLTreeGetNode(xmlTree1);
 63      node2 = CFXMLTreeGetNode(xmlTree2);
 64      if (!CFEqual(node1, node2)) return false;
 65      for (child1 = CFTreeGetFirstChild(xmlTree1), child2 = CFTreeGetFirstChild(xmlTree2); child1 && child2; child1 = CFTreeGetNextSibling(child1), child2 = CFTreeGetNextSibling(child2)) {
 66          if (!CFXMLTreeEqual(child1, child2)) return false;
 67      }
 68      return true;
 69  }
 70  
 71  static void _CFAppendXML(CFMutableStringRef str, CFXMLTreeRef tree);
 72  static void _CFAppendXMLProlog(CFMutableStringRef str, const CFXMLTreeRef node);
 73  static void _CFAppendXMLEpilog(CFMutableStringRef str, const CFXMLTreeRef node);
 74  
 75  CFDataRef CFXMLTreeCreateXMLData(CFAllocatorRef allocator, CFXMLTreeRef xmlTree) {
 76      CFMutableStringRef xmlStr; 
 77      CFDataRef result;
 78      CFStringEncoding encoding;
 79  
 80      __CFGenericValidateType(xmlTree, CFTreeGetTypeID());
 81      
 82      xmlStr = CFStringCreateMutable(allocator, 0);
 83      _CFAppendXML(xmlStr, xmlTree);
 84      if (CFXMLNodeGetTypeCode(CFXMLTreeGetNode(xmlTree)) == kCFXMLNodeTypeDocument) {
 85          const CFXMLDocumentInfo *docData = (CFXMLDocumentInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(xmlTree));
 86          encoding = docData ? docData->encoding : kCFStringEncodingUTF8;
 87      } else {
 88          encoding = kCFStringEncodingUTF8;
 89      }
 90      result = CFStringCreateExternalRepresentation(allocator, xmlStr, encoding, 0);
 91      CFRelease(xmlStr);
 92      return result;
 93  }
 94  
 95  static void _CFAppendXML(CFMutableStringRef str, CFXMLTreeRef tree) {
 96      CFXMLTreeRef child;
 97      _CFAppendXMLProlog(str, tree);
 98      for (child = CFTreeGetFirstChild(tree); child; child = CFTreeGetNextSibling(child)) {
 99          _CFAppendXML(str, child);
100      }
101      _CFAppendXMLEpilog(str, tree);
102  }
103  
104  CF_PRIVATE void appendQuotedString(CFMutableStringRef str, CFStringRef strToQuote) {
105      char quoteChar = CFStringFindWithOptions(strToQuote, CFSTR("\""), CFRangeMake(0, CFStringGetLength(strToQuote)), 0, NULL) ? '\'' : '\"';
106      CFStringAppendFormat(str, NULL, CFSTR("%c%@%c"), quoteChar, strToQuote, quoteChar);
107  }
108  
109  static void appendExternalID(CFMutableStringRef str, CFXMLExternalID *extID) {
110      if (extID->publicID) {
111          CFStringAppendCString(str, " PUBLIC ", kCFStringEncodingASCII);
112          appendQuotedString(str, extID->publicID);
113          if (extID->systemID) {
114              // 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
115              CFStringAppendCString(str, " ", kCFStringEncodingASCII);
116              appendQuotedString(str, CFURLGetString(extID->systemID));
117          }
118      } else if (extID->systemID) {
119          CFStringAppendCString(str, " SYSTEM ", kCFStringEncodingASCII);
120          appendQuotedString(str, CFURLGetString(extID->systemID));
121      } else {
122          // Should never get here
123      }
124  }
125  
126  static void appendElementProlog(CFMutableStringRef str, CFXMLTreeRef tree) {
127      const CFXMLElementInfo *data = (CFXMLElementInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree));
128      CFStringAppendFormat(str, NULL, CFSTR("<%@"), CFXMLNodeGetString(CFXMLTreeGetNode(tree)));
129      if (data->attributeOrder) {
130          CFIndex i, c = CFArrayGetCount(data->attributeOrder);
131          for (i = 0; i < c; i ++) {
132              CFStringRef attr = (CFStringRef)CFArrayGetValueAtIndex(data->attributeOrder, i);
133              CFStringRef value = (CFStringRef)CFDictionaryGetValue(data->attributes, attr);
134              CFStringAppendFormat(str, NULL, CFSTR(" %@="), attr);
135              appendQuotedString(str, value);
136          }
137      }
138      if (data->isEmpty) {
139          CFStringAppendCString(str, "/>", kCFStringEncodingASCII);
140      } else {
141          CFStringAppendCString(str, ">", kCFStringEncodingASCII);
142      }
143  }
144  
145  /* 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 */
146  static void _CFAppendXMLProlog(CFMutableStringRef str, const CFXMLTreeRef tree) {
147      switch (CFXMLNodeGetTypeCode(CFXMLTreeGetNode(tree))) {
148          case kCFXMLNodeTypeDocument:
149              break;
150          case kCFXMLNodeTypeElement:
151              appendElementProlog(str, tree);
152              break;
153          case kCFXMLNodeTypeAttribute:
154              // Should never be encountered
155              break;
156          case kCFXMLNodeTypeProcessingInstruction: {
157              CFXMLProcessingInstructionInfo *data = (CFXMLProcessingInstructionInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree));
158              if (data->dataString) {
159                  CFStringAppendFormat(str, NULL, CFSTR("<?%@ %@?>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree)), data->dataString);
160              } else {
161                  CFStringAppendFormat(str, NULL, CFSTR("<?%@?>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree)));
162              }
163              break;
164          }
165          case kCFXMLNodeTypeComment:
166              CFStringAppendFormat(str, NULL, CFSTR("<!--%@-->"), CFXMLNodeGetString(CFXMLTreeGetNode(tree)));
167              break;
168          case kCFXMLNodeTypeText:
169              CFStringAppend(str, CFXMLNodeGetString(CFXMLTreeGetNode(tree)));
170              break;
171          case kCFXMLNodeTypeCDATASection:
172              CFStringAppendFormat(str, NULL, CFSTR("<![CDATA[%@]]>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree)));
173              break;
174          case kCFXMLNodeTypeDocumentFragment:
175              break;
176          case kCFXMLNodeTypeEntity: {
177              CFXMLEntityInfo *data = (CFXMLEntityInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree));
178              CFStringAppendCString(str, "<!ENTITY ", kCFStringEncodingASCII);
179              if (data->entityType == kCFXMLEntityTypeParameter) {
180                  CFStringAppend(str, CFSTR("% "));
181              }
182              CFStringAppend(str, CFXMLNodeGetString(CFXMLTreeGetNode(tree)));
183              CFStringAppend(str, CFSTR(" "));
184              if (data->replacementText) {
185                  appendQuotedString(str, data->replacementText);
186                  CFStringAppendCString(str, ">", kCFStringEncodingASCII);
187              } else {
188                  appendExternalID(str, &(data->entityID));
189                  if (data->notationName) {
190                      CFStringAppendFormat(str, NULL, CFSTR(" NDATA %@"), data->notationName);
191                  }
192                  CFStringAppendCString(str, ">", kCFStringEncodingASCII);
193              }
194                  break;
195          }
196          case kCFXMLNodeTypeEntityReference:
197          {
198              CFXMLEntityTypeCode entityType = ((CFXMLEntityReferenceInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree)))->entityType;
199              if (entityType == kCFXMLEntityTypeParameter) {
200                  CFStringAppendFormat(str, NULL, CFSTR("%%%@;"), CFXMLNodeGetString(CFXMLTreeGetNode(tree)));
201              } else {
202                  CFStringAppendFormat(str, NULL, CFSTR("&%@;"), CFXMLNodeGetString(CFXMLTreeGetNode(tree)));
203              }
204              break;
205          }
206          case kCFXMLNodeTypeDocumentType:
207              CFStringAppendCString(str, "<!DOCTYPE ", kCFStringEncodingASCII);
208              CFStringAppend(str, CFXMLNodeGetString(CFXMLTreeGetNode(tree)));
209              if (CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree))) {
210                  CFXMLExternalID *extID = &((CFXMLDocumentTypeInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree)))->externalID;
211                  appendExternalID(str, extID);
212              }
213                  CFStringAppendCString(str, " [", kCFStringEncodingASCII);
214              break;
215          case kCFXMLNodeTypeWhitespace:
216              CFStringAppend(str, CFXMLNodeGetString(CFXMLTreeGetNode(tree)));
217              break;
218          case kCFXMLNodeTypeNotation: {
219              CFXMLNotationInfo *data = (CFXMLNotationInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree));
220              CFStringAppendFormat(str, NULL, CFSTR("<!NOTATION %@ "), CFXMLNodeGetString(CFXMLTreeGetNode(tree)));
221              appendExternalID(str, &(data->externalID));
222                  CFStringAppendCString(str, ">", kCFStringEncodingASCII);
223              break;
224          }
225          case kCFXMLNodeTypeElementTypeDeclaration:
226              CFStringAppendFormat(str, NULL, CFSTR("<!ELEMENT %@ %@>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree)), ((CFXMLElementTypeDeclarationInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree)))->contentDescription);
227              break;
228          case kCFXMLNodeTypeAttributeListDeclaration: {
229              CFXMLAttributeListDeclarationInfo *attListData = (CFXMLAttributeListDeclarationInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree));
230              CFIndex idx;
231              CFStringAppendCString(str, "<!ATTLIST ", kCFStringEncodingASCII);
232              CFStringAppend(str, CFXMLNodeGetString(CFXMLTreeGetNode(tree)));
233              for (idx = 0; idx < attListData->numberOfAttributes; idx ++) {
234                  CFXMLAttributeDeclarationInfo *attr = &(attListData->attributes[idx]);
235                  CFStringAppendFormat(str, NULL, CFSTR("\n\t%@ %@ %@"), attr->attributeName, attr->typeString, attr->defaultString);
236              }
237              CFStringAppendCString(str, ">", kCFStringEncodingASCII);
238              break;
239          }
240          default:
241              CFAssert1(false, __kCFLogAssertion, "Encountered unexpected XMLDataTypeID %d", CFXMLNodeGetTypeCode(CFXMLTreeGetNode(tree)));
242      }
243  }
244  
245  static void _CFAppendXMLEpilog(CFMutableStringRef str, CFXMLTreeRef tree) {
246      CFXMLNodeTypeCode typeID = CFXMLNodeGetTypeCode(CFXMLTreeGetNode(tree));
247      if (typeID == kCFXMLNodeTypeElement) {
248          if (((CFXMLElementInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree)))->isEmpty) return;
249          CFStringAppendFormat(str, NULL, CFSTR("</%@>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree)));
250      } else if (typeID == kCFXMLNodeTypeDocumentType) {
251          CFIndex len = CFStringGetLength(str);
252          if (CFStringHasSuffix(str, CFSTR(" ["))) {
253              // There were no in-line DTD elements
254              CFStringDelete(str, CFRangeMake(len-2, 2));
255          } else {
256              CFStringAppendCString(str, "]", kCFStringEncodingASCII);
257          }
258          CFStringAppendCString(str, ">", kCFStringEncodingASCII);
259      }
260  }
261  
262  #pragma GCC diagnostic pop