/ CFXMLNode.c
CFXMLNode.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  /*	CFXMLNode.c
 25  	Copyright (c) 1998-2014, Apple Inc. All rights reserved.
 26  	Responsibility: David Smith
 27  */
 28  
 29  #include <CoreFoundation/CFXMLNode.h>
 30  #include <CoreFoundation/CFPropertyList.h>
 31  #include "CFInternal.h"
 32  #include "CFXMLInputStream.h"
 33  
 34  CF_INLINE Boolean _nullSafeCFEqual(CFTypeRef cf1, CFTypeRef cf2) {
 35      if (cf1 && !cf2) return false;
 36      if (cf2 && !cf1) return false;
 37      if (cf1) return CFEqual(cf1, cf2);
 38      return true;
 39  }
 40  
 41  static Boolean externalIDEqual(CFXMLExternalID *ext1, CFXMLExternalID *ext2) {
 42      return _nullSafeCFEqual(ext1->systemID, ext2->systemID) && _nullSafeCFEqual(ext1->publicID, ext2->publicID);
 43  }
 44  
 45  static Boolean __CFXMLNodeEqual(CFTypeRef  cf1, CFTypeRef  cf2) {
 46      CFXMLNodeRef desc1 = (CFXMLNodeRef)cf1, desc2 = (CFXMLNodeRef)cf2;
 47      if (desc1 == desc2) return true;
 48      if (!desc1 || !desc2) return false;
 49      if (desc1->dataTypeID != desc2->dataTypeID) return false;
 50      if ((desc1->dataString && !desc2->dataString) || (!desc1->dataString && desc2->dataString)) return false;
 51      if (desc1->dataString && !CFEqual(desc1->dataString, desc2->dataString)) return false;
 52      if ((desc1->additionalData && !desc2->additionalData) || (!desc1->additionalData && desc2->additionalData)) return false;
 53      if (!desc1->additionalData) return true;
 54      switch (desc1->dataTypeID) {
 55          case kCFXMLNodeTypeDocument:{
 56              CFURLRef url1, url2;
 57              url1 = ((CFXMLDocumentInfo *)desc1->additionalData)->sourceURL;
 58              url2 = ((CFXMLDocumentInfo *)desc2->additionalData)->sourceURL;
 59              return _nullSafeCFEqual(url1, url2);
 60          }
 61          case kCFXMLNodeTypeElement: {
 62              CFXMLElementInfo *elt1, *elt2;
 63              elt1 = (CFXMLElementInfo *)desc1->additionalData;
 64              elt2 = (CFXMLElementInfo *)desc2->additionalData;
 65              if (elt1->isEmpty != elt2->isEmpty) return false;
 66              if (elt1->attributes == elt2->attributes) return true;
 67              if (!elt1->attributes) return (CFDictionaryGetCount(elt2->attributes) == 0);
 68              if (!elt2->attributes) return (CFDictionaryGetCount(elt1->attributes) == 0);
 69              return CFEqual(elt1->attributes, elt2->attributes);
 70          }
 71          case kCFXMLNodeTypeProcessingInstruction: {
 72              CFStringRef str1, str2;
 73              str1 = ((CFXMLProcessingInstructionInfo *)desc1->additionalData)->dataString;
 74              str2 = ((CFXMLProcessingInstructionInfo *)desc2->additionalData)->dataString;
 75              return _nullSafeCFEqual(str1, str2);
 76          }
 77          case kCFXMLNodeTypeEntity: {
 78              CFXMLEntityInfo *data1, *data2;
 79              data1 = (CFXMLEntityInfo *)desc1->additionalData;
 80              data2 = (CFXMLEntityInfo *)desc2->additionalData;
 81              if (data1->entityType != data2->entityType) return false;
 82              if (!_nullSafeCFEqual(data1->replacementText, data2->replacementText)) return false;
 83              if (!_nullSafeCFEqual(data1->notationName, data2->notationName)) return false;
 84              return externalIDEqual(&data1->entityID, &data2->entityID);
 85          }
 86          case kCFXMLNodeTypeEntityReference: {
 87              return ((CFXMLEntityReferenceInfo *)(desc1->additionalData))->entityType == ((CFXMLEntityReferenceInfo *)(desc2->additionalData))->entityType;
 88          }
 89          case kCFXMLNodeTypeNotation: {
 90              CFXMLNotationInfo *data1, *data2;
 91              data1 = (CFXMLNotationInfo *)desc1->additionalData;
 92              data2 = (CFXMLNotationInfo *)desc2->additionalData;
 93              return externalIDEqual(&(data1->externalID), &(data2->externalID));
 94          }
 95          case kCFXMLNodeTypeDocumentType: {
 96              CFXMLDocumentTypeInfo *data1, *data2;
 97              data1 = (CFXMLDocumentTypeInfo *)desc1->additionalData;
 98              data2 = (CFXMLDocumentTypeInfo *)desc2->additionalData;
 99              return externalIDEqual(&(data1->externalID), &(data2->externalID));
100          }
101          case kCFXMLNodeTypeElementTypeDeclaration: {
102              CFXMLElementTypeDeclarationInfo *d1 = (CFXMLElementTypeDeclarationInfo *)desc1->additionalData;
103              CFXMLElementTypeDeclarationInfo *d2 = (CFXMLElementTypeDeclarationInfo *)desc2->additionalData;
104              return _nullSafeCFEqual(d1->contentDescription, d2->contentDescription);
105          }
106          case kCFXMLNodeTypeAttributeListDeclaration: {
107              CFXMLAttributeListDeclarationInfo *attList1 = (CFXMLAttributeListDeclarationInfo *)desc1->additionalData;
108              CFXMLAttributeListDeclarationInfo *attList2 = (CFXMLAttributeListDeclarationInfo *)desc2->additionalData;
109              CFIndex idx;
110              if (attList1->numberOfAttributes != attList2->numberOfAttributes) return false;
111              for (idx = 0; idx < attList1->numberOfAttributes; idx ++) {
112                  CFXMLAttributeDeclarationInfo *attr1 = &(attList1->attributes[idx]);
113                  CFXMLAttributeDeclarationInfo *attr2 = &(attList2->attributes[idx]);
114                  if (!_nullSafeCFEqual(attr1->attributeName, attr2->attributeName)) return false;
115                  if (!_nullSafeCFEqual(attr1->typeString, attr2->typeString)) return false;
116                  if (!_nullSafeCFEqual(attr1->defaultString, attr2->defaultString)) return false;
117              }
118              return true;
119          }
120          default:
121              return false;
122      }
123      return true;
124  }
125  
126  static CFHashCode __CFXMLNodeHash(CFTypeRef  cf) {
127      CFXMLNodeRef node = (CFXMLNodeRef)cf;
128      if (node->dataString) {
129          return CFHash(node->dataString);
130      }
131      if (node->dataTypeID == kCFXMLNodeTypeDocument) {
132          CFURLRef url = ((CFXMLDocumentInfo *)node->additionalData)->sourceURL;
133          return url ? CFHash(url) : (CFHashCode)cf;
134      } else {
135          CFAssert2(false, __kCFLogAssertion, "%s(): Saw unexpected XML type code %d", __PRETTY_FUNCTION__, node->dataTypeID);
136          return CFHash(cf);
137      }
138  }
139  
140  static CFStringRef __CFXMLNodeCopyDescription(CFTypeRef  cf) {
141      struct __CFXMLNode *node = (struct __CFXMLNode *)cf;
142      return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("CFXMLNode %p>{typeID = %ld, string = %@}"), cf, (unsigned long)node->dataTypeID, node->dataString);
143  }
144  
145  static void __CFXMLNodeDeallocate(CFTypeRef  cf) {
146      struct __CFXMLNode *node = (struct __CFXMLNode *)cf;
147      if (node->dataString) CFRelease(node->dataString);
148      if (node->additionalData) {
149          switch (node->dataTypeID) {
150              case kCFXMLNodeTypeDocument:
151                  if (((CFXMLDocumentInfo *)node->additionalData)->sourceURL) {
152                      CFRelease(((CFXMLDocumentInfo *)node->additionalData)->sourceURL);
153                  }
154                  break;
155              case kCFXMLNodeTypeElement:
156                  if (((CFXMLElementInfo *)node->additionalData)->attributes) {
157                      CFRelease(((CFXMLElementInfo *)node->additionalData)->attributes);
158                      CFRelease(((CFXMLElementInfo *)node->additionalData)->attributeOrder);
159                  }
160                  break;
161              case kCFXMLNodeTypeProcessingInstruction:
162                  if (((CFXMLProcessingInstructionInfo *)node->additionalData)->dataString) {
163                      CFRelease(((CFXMLProcessingInstructionInfo *)node->additionalData)->dataString);
164                  }
165                  break;
166              case kCFXMLNodeTypeEntity:
167              {
168                  CFXMLEntityInfo *data = (CFXMLEntityInfo *)node->additionalData;
169                  if (data->replacementText) CFRelease(data->replacementText);
170                  if (data->entityID.systemID) CFRelease(data->entityID.systemID);
171                  if (data->entityID.publicID) CFRelease(data->entityID.publicID);
172                  if (data->notationName) CFRelease(data->notationName);
173                  break;
174              }
175              case kCFXMLNodeTypeEntityReference:
176              {
177                  // Do nothing; additionalData has no structure of its own, with dependent pieces to release.  -- REW, 2/11/2000
178                  break;
179              }
180              case kCFXMLNodeTypeDocumentType:
181              case kCFXMLNodeTypeNotation:
182                  // We get away with this because CFXMLNotationInfo and CFXMLDocumentTypeInfo have identical formats
183              {
184                  CFXMLNotationInfo *data = (CFXMLNotationInfo *)node->additionalData;
185                  if (data->externalID.systemID) CFRelease(data->externalID.systemID);
186                  if (data->externalID.publicID) CFRelease(data->externalID.publicID);
187                  break;
188              }
189              case kCFXMLNodeTypeElementTypeDeclaration:
190                  if (((CFXMLElementTypeDeclarationInfo *)node->additionalData)->contentDescription) {
191                      CFRelease(((CFXMLElementTypeDeclarationInfo *)node->additionalData)->contentDescription);
192                  }
193                  break;
194              case kCFXMLNodeTypeAttributeListDeclaration:
195              {
196                  CFXMLAttributeListDeclarationInfo *data = (CFXMLAttributeListDeclarationInfo *)node->additionalData;
197                  CFIndex idx;
198                  for (idx = 0; idx < data->numberOfAttributes; idx ++) {
199                      CFRelease(data->attributes[idx].attributeName);
200                      CFRelease(data->attributes[idx].typeString);
201                      CFRelease(data->attributes[idx].defaultString);
202                  }
203                  CFAllocatorDeallocate(CFGetAllocator(node), data->attributes);
204                  break;
205              }
206              default:
207                  CFAssert1(false, __kCFLogAssertion, "%s(): Encountered unexpected typeID %d (additionalData should be empty)", node->dataTypeID);
208          }
209      }
210  }
211  
212  static CFTypeID __kCFXMLNodeTypeID = _kCFRuntimeNotATypeID;
213  
214  static const CFRuntimeClass __CFXMLNodeClass = {
215      0,
216      "CFXMLNode",
217      NULL,      // init
218      NULL,      // copy
219      __CFXMLNodeDeallocate,
220      __CFXMLNodeEqual,
221      __CFXMLNodeHash,
222      NULL,      // 
223      __CFXMLNodeCopyDescription
224  };
225  
226  CFTypeID CFXMLNodeGetTypeID(void) {
227      static dispatch_once_t initOnce;
228      dispatch_once(&initOnce, ^{ __kCFXMLNodeTypeID = _CFRuntimeRegisterClass(&__CFXMLNodeClass); });
229      return __kCFXMLNodeTypeID;
230  }
231  
232  CFXMLNodeRef CFXMLNodeCreateCopy(CFAllocatorRef alloc, CFXMLNodeRef origNode) {
233      return CFXMLNodeCreate(alloc, origNode->dataTypeID, origNode->dataString, origNode->additionalData, origNode->version);
234  }
235  
236  static void _copyAddlDataForType(CFAllocatorRef alloc, CFXMLNodeTypeCode xmlType, const void *src, void *dest) {
237      switch(xmlType) {
238          case kCFXMLNodeTypeDocument: {
239              CFXMLDocumentInfo *srcData = (CFXMLDocumentInfo *)src;
240              CFXMLDocumentInfo *destData = (CFXMLDocumentInfo *)dest;
241              destData->sourceURL = srcData->sourceURL ? (CFURLRef)CFRetain(srcData->sourceURL) : NULL;
242              destData->encoding = srcData->encoding;
243              break;
244          }
245          case kCFXMLNodeTypeElement: {
246              CFXMLElementInfo *srcData = (CFXMLElementInfo *)src;
247              CFXMLElementInfo *destData = (CFXMLElementInfo *)dest;
248              if (srcData->attributes && CFDictionaryGetCount(srcData->attributes) != 0) {
249                  destData->attributes =  (CFDictionaryRef)CFPropertyListCreateDeepCopy(alloc, srcData->attributes, kCFPropertyListImmutable);
250                  destData->attributeOrder = (CFArrayRef)CFPropertyListCreateDeepCopy(alloc, srcData->attributeOrder, kCFPropertyListImmutable);
251              } else {
252                  destData->attributes = NULL;
253                  destData->attributeOrder = NULL;
254              }
255              destData->isEmpty = srcData->isEmpty;
256              break;
257          }
258          case kCFXMLNodeTypeProcessingInstruction: {
259              CFXMLProcessingInstructionInfo *srcData = (CFXMLProcessingInstructionInfo *)src;
260              CFXMLProcessingInstructionInfo *destData = (CFXMLProcessingInstructionInfo *)dest;
261              destData->dataString = srcData->dataString ? (CFStringRef)CFStringCreateCopy(alloc, srcData->dataString) : NULL;
262              break;
263          }
264          case kCFXMLNodeTypeEntity:
265          {
266              CFXMLEntityInfo *sourceData = (CFXMLEntityInfo *)src;
267              CFXMLEntityInfo *destData = (CFXMLEntityInfo *)dest;
268              destData->entityType = sourceData->entityType;
269              destData->replacementText = sourceData->replacementText ? (CFStringRef)CFStringCreateCopy(alloc, sourceData->replacementText) : NULL;
270              destData->entityID.systemID = sourceData->entityID.systemID ? (CFURLRef)CFRetain(sourceData->entityID.systemID) : NULL;
271              destData->entityID.publicID = sourceData->entityID.publicID ? (CFStringRef)CFStringCreateCopy(alloc, sourceData->entityID.publicID) : NULL;
272              destData->notationName = sourceData->notationName ? (CFStringRef)CFStringCreateCopy(alloc, sourceData->notationName) : NULL;
273              break;
274          }
275          case kCFXMLNodeTypeEntityReference:
276          {
277              CFXMLEntityReferenceInfo *srcData = (CFXMLEntityReferenceInfo *)src;
278              CFXMLEntityReferenceInfo *destData = (CFXMLEntityReferenceInfo *)dest;
279              destData->entityType = srcData->entityType;
280              break;
281          }
282          case kCFXMLNodeTypeDocumentType:
283          case kCFXMLNodeTypeNotation:
284          {
285              // We can get away with this because the structures of CFXMLNotationInfo and CFXMLDocumentTypeInfo match.  -- REW, 3/8/2000
286              CFXMLNotationInfo *srcData = (CFXMLNotationInfo *)src;
287              CFXMLNotationInfo *destData = (CFXMLNotationInfo *)dest;
288              destData->externalID.systemID = srcData->externalID.systemID ? (CFURLRef)CFRetain(srcData->externalID.systemID) : NULL;
289              destData->externalID.publicID = srcData->externalID.publicID ? (CFStringRef)CFStringCreateCopy(alloc, srcData->externalID.publicID) : NULL;
290              break;
291          }
292          case kCFXMLNodeTypeElementTypeDeclaration: {
293              CFXMLElementTypeDeclarationInfo *srcData = (CFXMLElementTypeDeclarationInfo *)src;
294              CFXMLElementTypeDeclarationInfo *destData = (CFXMLElementTypeDeclarationInfo *)dest;
295              destData->contentDescription = srcData->contentDescription ? (CFStringRef)CFStringCreateCopy(alloc, srcData->contentDescription) : NULL;
296              break;
297          }
298          case kCFXMLNodeTypeAttributeListDeclaration:
299          {
300              CFXMLAttributeListDeclarationInfo *sourceData = (CFXMLAttributeListDeclarationInfo *)src;
301              CFXMLAttributeListDeclarationInfo *destData = (CFXMLAttributeListDeclarationInfo *)dest;
302              CFIndex idx;
303              destData->numberOfAttributes = sourceData->numberOfAttributes;
304              destData->attributes = sourceData->numberOfAttributes ? (CFXMLAttributeDeclarationInfo *)CFAllocatorAllocate(alloc, sizeof(CFXMLAttributeDeclarationInfo)*sourceData->numberOfAttributes, 0) : NULL;
305              for (idx = 0; idx < sourceData->numberOfAttributes; idx ++) {
306                  CFXMLAttributeDeclarationInfo sourceAttr = sourceData->attributes[idx];
307                  CFXMLAttributeDeclarationInfo *destAttr = &(destData->attributes[idx]);
308                  destAttr->attributeName = (CFStringRef)CFStringCreateCopy(alloc, sourceAttr.attributeName);
309                  destAttr->typeString = (CFStringRef)CFStringCreateCopy(alloc, sourceAttr.typeString);
310                  destAttr->defaultString = (CFStringRef)CFStringCreateCopy(alloc, sourceAttr.defaultString);
311              }
312              break;
313          }
314          default:
315              CFAssert2(false, __kCFLogAssertion, "%s(): Encountered unexpected typeID %d (additionalData should be empty)", __PRETTY_FUNCTION__, xmlType);
316      }
317  }
318  
319  // Designated initializer; all node creation (except the wonky one created by the parser) should happen here.
320  CFXMLNodeRef CFXMLNodeCreate(CFAllocatorRef alloc, CFXMLNodeTypeCode xmlType, CFStringRef dataString, const void *additionalData, CFIndex version) {
321      struct __CFXMLNode *node;
322      UInt32 extraSize;
323      // Add assertions checking xmlType against the presence/absence of additionalData & dataString
324      switch (xmlType) {
325          case kCFXMLNodeTypeDocument: extraSize = sizeof(CFXMLDocumentInfo); break;
326          case kCFXMLNodeTypeElement: extraSize = sizeof(CFXMLElementInfo); break;
327          case kCFXMLNodeTypeProcessingInstruction: extraSize = sizeof(CFXMLProcessingInstructionInfo); break;
328          case kCFXMLNodeTypeEntity: extraSize = sizeof(CFXMLEntityInfo); break;
329          case kCFXMLNodeTypeEntityReference: extraSize = sizeof(CFXMLEntityReferenceInfo); break;
330          case kCFXMLNodeTypeDocumentType: extraSize = sizeof(CFXMLDocumentTypeInfo); break;
331          case kCFXMLNodeTypeNotation: extraSize = sizeof(CFXMLNotationInfo); break;
332          case kCFXMLNodeTypeElementTypeDeclaration: extraSize = sizeof(CFXMLElementTypeDeclarationInfo); break;
333          case kCFXMLNodeTypeAttributeListDeclaration: extraSize = sizeof(CFXMLAttributeListDeclarationInfo); break;
334          default: extraSize = 0;
335      }
336  
337      node = (struct __CFXMLNode *)_CFRuntimeCreateInstance(alloc, CFXMLNodeGetTypeID(), sizeof(struct __CFXMLNode) + extraSize - sizeof(CFRuntimeBase), NULL);
338      if (node) {
339          alloc = CFGetAllocator(node);
340          node->version = version;
341          node->dataTypeID = xmlType;
342          node->dataString = dataString ? (CFStringRef)CFStringCreateCopy(alloc, dataString) : NULL;
343          if (extraSize != 0) {
344              node->additionalData = (void *)((uint8_t *)node + sizeof(struct __CFXMLNode));
345              _copyAddlDataForType(alloc, xmlType, additionalData, node->additionalData);
346          } else {
347              node->additionalData = NULL;
348          }
349      }
350      return node;
351  }
352  
353  CFXMLNodeTypeCode CFXMLNodeGetTypeCode(CFXMLNodeRef node) {
354      return node->dataTypeID;
355  }
356  
357  CFStringRef CFXMLNodeGetString(CFXMLNodeRef node) {
358      return node->dataString;
359  }
360  
361  const void *CFXMLNodeGetInfoPtr(CFXMLNodeRef node) {
362      return node->additionalData;
363  }
364  
365  CFIndex CFXMLNodeGetVersion(CFXMLNodeRef node) {
366      return node->version;
367  }
368  
369