PageRenderTime 75ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 1ms

/XMPP_Demo/Vendor/KissXML/DDXMLNode.m

https://gitlab.com/praveenvelanati/ios-demo
Objective C | 1976 lines | 1179 code | 351 blank | 446 comment | 272 complexity | 3fe4e12eabe36eff0e2827734f355414 MD5 | raw file
  1. #import "DDXMLPrivate.h"
  2. #import "NSString+DDXML.h"
  3. #import <libxml/xpath.h>
  4. #import <libxml/xpathInternals.h>
  5. #if ! __has_feature(objc_arc)
  6. #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
  7. #endif
  8. /**
  9. * Welcome to KissXML.
  10. *
  11. * The project page has documentation if you have questions.
  12. * https://github.com/robbiehanson/KissXML
  13. *
  14. * If you're new to the project you may wish to read the "Getting Started" wiki.
  15. * https://github.com/robbiehanson/KissXML/wiki/GettingStarted
  16. *
  17. * KissXML provides a drop-in replacement for Apple's NSXML class cluster.
  18. * The goal is to get the exact same behavior as the NSXML classes.
  19. *
  20. * For API Reference, see Apple's excellent documentation,
  21. * either via Xcode's Mac OS X documentation, or via the web:
  22. *
  23. * https://github.com/robbiehanson/KissXML/wiki/Reference
  24. **/
  25. @implementation DDXMLNode
  26. static void MyErrorHandler(void * userData, xmlErrorPtr error);
  27. #if DDXML_DEBUG_MEMORY_ISSUES
  28. static CFMutableDictionaryRef zombieTracker;
  29. static dispatch_queue_t zombieQueue;
  30. static void RecursiveMarkZombiesFromNode(xmlNodePtr node);
  31. static void RecursiveMarkZombiesFromDoc(xmlDocPtr doc);
  32. static void MarkZombies(void *xmlPtr);
  33. static void MarkBirth(void *xmlPtr, DDXMLNode *wrapper);
  34. static void MarkDeath(void *xmlPtr, DDXMLNode *wrapper);
  35. #endif
  36. /**
  37. * From Apple's Documentation:
  38. *
  39. * The runtime sends initialize to each class in a program exactly one time just before the class,
  40. * or any class that inherits from it, is sent its first message from within the program. (Thus the method may
  41. * never be invoked if the class is not used.) The runtime sends the initialize message to classes
  42. * in a thread-safe manner. Superclasses receive this message before their subclasses.
  43. *
  44. * The method may also be called directly (assumably by accident), hence the safety mechanism.
  45. **/
  46. + (void)initialize
  47. {
  48. static dispatch_once_t onceToken;
  49. dispatch_once(&onceToken, ^{
  50. // Redirect error output to our own function (don't clog up the console)
  51. initGenericErrorDefaultFunc(NULL);
  52. xmlSetStructuredErrorFunc(NULL, MyErrorHandler);
  53. // Tell libxml not to keep ignorable whitespace (such as node indentation, formatting, etc).
  54. // NSXML ignores such whitespace.
  55. // This also has the added benefit of taking up less RAM when parsing formatted XML documents.
  56. xmlKeepBlanksDefault(0);
  57. #if DDXML_DEBUG_MEMORY_ISSUES
  58. {
  59. zombieTracker = CFDictionaryCreateMutable(NULL, 0, NULL, &kCFTypeDictionaryValueCallBacks);
  60. zombieQueue = dispatch_queue_create("DDXMLZombieQueue", NULL);
  61. }
  62. #endif
  63. });
  64. }
  65. + (id)elementWithName:(NSString *)name
  66. {
  67. return [[DDXMLElement alloc] initWithName:name];
  68. }
  69. + (id)elementWithName:(NSString *)name stringValue:(NSString *)string
  70. {
  71. return [[DDXMLElement alloc] initWithName:name stringValue:string];
  72. }
  73. + (id)elementWithName:(NSString *)name children:(NSArray *)children attributes:(NSArray *)attributes
  74. {
  75. DDXMLElement *result = [[DDXMLElement alloc] initWithName:name];
  76. [result setChildren:children];
  77. [result setAttributes:attributes];
  78. return result;
  79. }
  80. + (id)elementWithName:(NSString *)name URI:(NSString *)URI
  81. {
  82. return [[DDXMLElement alloc] initWithName:name URI:URI];
  83. }
  84. + (id)attributeWithName:(NSString *)name stringValue:(NSString *)stringValue
  85. {
  86. xmlAttrPtr attr = xmlNewProp(NULL, [name xmlChar], [stringValue xmlChar]);
  87. if (attr == NULL) return nil;
  88. return [[DDXMLAttributeNode alloc] initWithAttrPrimitive:attr owner:nil];
  89. }
  90. + (id)attributeWithName:(NSString *)name URI:(NSString *)URI stringValue:(NSString *)stringValue
  91. {
  92. xmlAttrPtr attr = xmlNewProp(NULL, [name xmlChar], [stringValue xmlChar]);
  93. if (attr == NULL) return nil;
  94. DDXMLAttributeNode *result = [[DDXMLAttributeNode alloc] initWithAttrPrimitive:attr owner:nil];
  95. [result setURI:URI];
  96. return result;
  97. }
  98. + (id)namespaceWithName:(NSString *)name stringValue:(NSString *)stringValue
  99. {
  100. // If the user passes a nil or empty string name, they are trying to create a default namespace
  101. const xmlChar *xmlName = [name length] > 0 ? [name xmlChar] : NULL;
  102. xmlNsPtr ns = xmlNewNs(NULL, [stringValue xmlChar], xmlName);
  103. if (ns == NULL) return nil;
  104. return [[DDXMLNamespaceNode alloc] initWithNsPrimitive:ns nsParent:NULL owner:nil];
  105. }
  106. + (id)processingInstructionWithName:(NSString *)name stringValue:(NSString *)stringValue
  107. {
  108. xmlNodePtr procInst = xmlNewPI([name xmlChar], [stringValue xmlChar]);
  109. if (procInst == NULL) return nil;
  110. return [[DDXMLNode alloc] initWithPrimitive:(xmlKindPtr)procInst owner:nil];
  111. }
  112. + (id)commentWithStringValue:(NSString *)stringValue
  113. {
  114. xmlNodePtr comment = xmlNewComment([stringValue xmlChar]);
  115. if (comment == NULL) return nil;
  116. return [[DDXMLNode alloc] initWithPrimitive:(xmlKindPtr)comment owner:nil];
  117. }
  118. + (id)textWithStringValue:(NSString *)stringValue
  119. {
  120. xmlNodePtr text = xmlNewText([stringValue xmlChar]);
  121. if (text == NULL) return nil;
  122. return [[DDXMLNode alloc] initWithPrimitive:(xmlKindPtr)text owner:nil];
  123. }
  124. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  125. #pragma mark Init, Dealloc
  126. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  127. + (id)nodeWithUnknownPrimitive:(xmlKindPtr)kindPtr owner:(DDXMLNode *)owner
  128. {
  129. if (kindPtr->type == XML_DOCUMENT_NODE)
  130. {
  131. return [DDXMLDocument nodeWithDocPrimitive:(xmlDocPtr)kindPtr owner:owner];
  132. }
  133. else if (kindPtr->type == XML_ELEMENT_NODE)
  134. {
  135. return [DDXMLElement nodeWithElementPrimitive:(xmlNodePtr)kindPtr owner:owner];
  136. }
  137. else if (kindPtr->type == XML_NAMESPACE_DECL)
  138. {
  139. // Todo: This may be a problem...
  140. return [DDXMLNamespaceNode nodeWithNsPrimitive:(xmlNsPtr)kindPtr nsParent:NULL owner:owner];
  141. }
  142. else if (kindPtr->type == XML_ATTRIBUTE_NODE)
  143. {
  144. return [DDXMLAttributeNode nodeWithAttrPrimitive:(xmlAttrPtr)kindPtr owner:owner];
  145. }
  146. else
  147. {
  148. return [DDXMLNode nodeWithPrimitive:kindPtr owner:owner];
  149. }
  150. }
  151. /**
  152. * Returns a DDXML wrapper object for the given primitive node.
  153. * The given node MUST be non-NULL and of the proper type.
  154. **/
  155. + (id)nodeWithPrimitive:(xmlKindPtr)kindPtr owner:(DDXMLNode *)owner
  156. {
  157. return [[DDXMLNode alloc] initWithPrimitive:kindPtr owner:owner];
  158. }
  159. /**
  160. * Returns a DDXML wrapper object for the given primitive node.
  161. * The given node MUST be non-NULL and of the proper type.
  162. **/
  163. - (id)initWithPrimitive:(xmlKindPtr)kindPtr owner:(DDXMLNode *)inOwner
  164. {
  165. if ((self = [super init]))
  166. {
  167. genericPtr = kindPtr;
  168. owner = inOwner;
  169. #if DDXML_DEBUG_MEMORY_ISSUES
  170. MarkBirth(genericPtr, self);
  171. #endif
  172. }
  173. return self;
  174. }
  175. /**
  176. * This method shouldn't be used.
  177. * To maintain compatibility with Apple, we return an invalid node.
  178. **/
  179. - (id)init
  180. {
  181. self = [super init];
  182. if ([self isKindOfClass:[DDXMLInvalidNode class]])
  183. {
  184. return self;
  185. }
  186. else
  187. {
  188. return [[DDXMLInvalidNode alloc] init];
  189. }
  190. }
  191. - (void)dealloc
  192. {
  193. #if DDXML_DEBUG_MEMORY_ISSUES
  194. MarkDeath(genericPtr, self);
  195. #endif
  196. // We also check if genericPtr is NULL.
  197. // This may be the case if, e.g., DDXMLElement calls [self release] from it's init method.
  198. if ((owner == nil) && (genericPtr != NULL))
  199. {
  200. if (IsXmlNsPtr(genericPtr))
  201. {
  202. #if DDXML_DEBUG_MEMORY_ISSUES
  203. MarkZombies(genericPtr);
  204. #endif
  205. xmlFreeNs((xmlNsPtr)genericPtr);
  206. }
  207. else if (IsXmlAttrPtr(genericPtr))
  208. {
  209. #if DDXML_DEBUG_MEMORY_ISSUES
  210. MarkZombies(genericPtr);
  211. #endif
  212. xmlFreeProp((xmlAttrPtr)genericPtr);
  213. }
  214. else if (IsXmlDtdPtr(genericPtr))
  215. {
  216. #if DDXML_DEBUG_MEMORY_ISSUES
  217. MarkZombies(genericPtr);
  218. #endif
  219. xmlFreeDtd((xmlDtdPtr)genericPtr);
  220. }
  221. else if (IsXmlDocPtr(genericPtr))
  222. {
  223. xmlDocPtr doc = (xmlDocPtr)genericPtr;
  224. #if DDXML_DEBUG_MEMORY_ISSUES
  225. RecursiveMarkZombiesFromDoc(doc);
  226. #endif
  227. xmlFreeDoc(doc);
  228. }
  229. else if (IsXmlNodePtr(genericPtr))
  230. {
  231. xmlNodePtr node = (xmlNodePtr)genericPtr;
  232. #if DDXML_DEBUG_MEMORY_ISSUES
  233. RecursiveMarkZombiesFromNode(node);
  234. #endif
  235. xmlFreeNode(node);
  236. }
  237. else
  238. {
  239. NSAssert1(NO, @"Cannot free unknown node type: %i", genericPtr->type);
  240. }
  241. }
  242. }
  243. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  244. #pragma mark Copying
  245. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  246. - (id)copyWithZone:(NSZone *)zone
  247. {
  248. #if DDXML_DEBUG_MEMORY_ISSUES
  249. DDXMLNotZombieAssert();
  250. #endif
  251. if (IsXmlDocPtr(genericPtr))
  252. {
  253. xmlDocPtr copyDocPtr = xmlCopyDoc((xmlDocPtr)genericPtr, 1);
  254. if (copyDocPtr == NULL) return nil;
  255. return [[DDXMLDocument alloc] initWithDocPrimitive:copyDocPtr owner:nil];
  256. }
  257. if (IsXmlNodePtr(genericPtr))
  258. {
  259. xmlNodePtr copyNodePtr = xmlCopyNode((xmlNodePtr)genericPtr, 1);
  260. if (copyNodePtr == NULL) return nil;
  261. if ([self isKindOfClass:[DDXMLElement class]])
  262. return [[DDXMLElement alloc] initWithElementPrimitive:copyNodePtr owner:nil];
  263. else
  264. return [[DDXMLNode alloc] initWithPrimitive:(xmlKindPtr)copyNodePtr owner:nil];
  265. }
  266. if (IsXmlAttrPtr(genericPtr))
  267. {
  268. xmlAttrPtr copyAttrPtr = xmlCopyProp(NULL, (xmlAttrPtr)genericPtr);
  269. if (copyAttrPtr == NULL) return nil;
  270. return [[DDXMLAttributeNode alloc] initWithAttrPrimitive:copyAttrPtr owner:nil];
  271. }
  272. if (IsXmlNsPtr(genericPtr))
  273. {
  274. xmlNsPtr copyNsPtr = xmlCopyNamespace((xmlNsPtr)genericPtr);
  275. if (copyNsPtr == NULL) return nil;
  276. return [[DDXMLNamespaceNode alloc] initWithNsPrimitive:copyNsPtr nsParent:NULL owner:nil];
  277. }
  278. if (IsXmlDtdPtr(genericPtr))
  279. {
  280. xmlDtdPtr copyDtdPtr = xmlCopyDtd((xmlDtdPtr)genericPtr);
  281. if (copyDtdPtr == NULL) return nil;
  282. return [[DDXMLNode alloc] initWithPrimitive:(xmlKindPtr)copyDtdPtr owner:nil];
  283. }
  284. return nil;
  285. }
  286. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  287. #pragma mark Equality
  288. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  289. - (BOOL)isEqual:(id)anObject
  290. {
  291. // DDXMLNode, DDXMLElement, and DDXMLDocument are simply light-weight wrappers atop a libxml structure.
  292. //
  293. // To provide maximum speed and thread-safety,
  294. // multiple DDXML wrapper objects may be created that wrap the same underlying libxml node.
  295. //
  296. // Thus equality is simply a matter of what underlying libxml node DDXML is wrapping.
  297. if ([anObject class] == [self class])
  298. {
  299. DDXMLNode *aNode = (DDXMLNode *)anObject;
  300. return (genericPtr == aNode->genericPtr);
  301. }
  302. return NO;
  303. }
  304. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  305. #pragma mark Properties
  306. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  307. - (DDXMLNodeKind)kind
  308. {
  309. #if DDXML_DEBUG_MEMORY_ISSUES
  310. DDXMLNotZombieAssert();
  311. #endif
  312. if (genericPtr != NULL)
  313. return genericPtr->type;
  314. else
  315. return DDXMLInvalidKind;
  316. }
  317. - (void)setName:(NSString *)name
  318. {
  319. // Note: DDXMLNamespaceNode overrides this method
  320. #if DDXML_DEBUG_MEMORY_ISSUES
  321. DDXMLNotZombieAssert();
  322. #endif
  323. // The xmlNodeSetName function works for both nodes and attributes
  324. xmlNodeSetName((xmlNodePtr)genericPtr, [name xmlChar]);
  325. }
  326. - (NSString *)name
  327. {
  328. // Note: DDXMLNamespaceNode overrides this method
  329. // Note: DDXMLAttributeNode overrides this method
  330. #if DDXML_DEBUG_MEMORY_ISSUES
  331. DDXMLNotZombieAssert();
  332. #endif
  333. const xmlChar *xmlName = ((xmlStdPtr)genericPtr)->name;
  334. if (xmlName == NULL)
  335. {
  336. return nil;
  337. }
  338. NSString *name = [NSString stringWithUTF8String:(const char *)xmlName];
  339. if (IsXmlNodePtr(genericPtr))
  340. {
  341. xmlNodePtr node = (xmlNodePtr)genericPtr;
  342. NSRange range = [name rangeOfString:@":"];
  343. if (range.length == 0)
  344. {
  345. if (node->ns && node->ns->prefix)
  346. {
  347. return [NSString stringWithFormat:@"%s:%@", node->ns->prefix, name];
  348. }
  349. }
  350. }
  351. return name;
  352. }
  353. - (void)setStringValue:(NSString *)string
  354. {
  355. // Note: DDXMLNamespaceNode overrides this method
  356. // Note: DDXMLAttributeNode overrides this method
  357. #if DDXML_DEBUG_MEMORY_ISSUES
  358. DDXMLNotZombieAssert();
  359. #endif
  360. if (IsXmlNodePtr(genericPtr))
  361. {
  362. xmlStdPtr node = (xmlStdPtr)genericPtr;
  363. // Setting the content of a node erases any existing child nodes.
  364. // Therefore, we need to remove them properly first.
  365. [[self class] removeAllChildrenFromNode:(xmlNodePtr)node];
  366. xmlChar *escapedString = xmlEncodeSpecialChars(node->doc, [string xmlChar]);
  367. xmlNodeSetContent((xmlNodePtr)node, escapedString);
  368. xmlFree(escapedString);
  369. }
  370. }
  371. /**
  372. * Returns the content of the receiver as a string value.
  373. *
  374. * If the receiver is a node object of element kind, the content is that of any text-node children.
  375. * This method recursively visits elements nodes and concatenates their text nodes in document order with
  376. * no intervening spaces.
  377. **/
  378. - (NSString *)stringValue
  379. {
  380. // Note: DDXMLNamespaceNode overrides this method
  381. // Note: DDXMLAttributeNode overrides this method
  382. #if DDXML_DEBUG_MEMORY_ISSUES
  383. DDXMLNotZombieAssert();
  384. #endif
  385. if (IsXmlNodePtr(genericPtr))
  386. {
  387. xmlChar *content = xmlNodeGetContent((xmlNodePtr)genericPtr);
  388. NSString *result = [NSString stringWithUTF8String:(const char *)content];
  389. xmlFree(content);
  390. return result;
  391. }
  392. return nil;
  393. }
  394. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  395. #pragma mark Tree Navigation
  396. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  397. /**
  398. * Returns the index of the receiver identifying its position relative to its sibling nodes.
  399. * The first child node of a parent has an index of zero.
  400. **/
  401. - (NSUInteger)index
  402. {
  403. // Note: DDXMLNamespaceNode overrides this method
  404. #if DDXML_DEBUG_MEMORY_ISSUES
  405. DDXMLNotZombieAssert();
  406. #endif
  407. NSUInteger result = 0;
  408. xmlStdPtr node = ((xmlStdPtr)genericPtr)->prev;
  409. while (node != NULL)
  410. {
  411. result++;
  412. node = node->prev;
  413. }
  414. return result;
  415. }
  416. /**
  417. * Returns the nesting level of the receiver within the tree hierarchy.
  418. * The root element of a document has a nesting level of one.
  419. **/
  420. - (NSUInteger)level
  421. {
  422. // Note: DDXMLNamespaceNode overrides this method
  423. #if DDXML_DEBUG_MEMORY_ISSUES
  424. DDXMLNotZombieAssert();
  425. #endif
  426. NSUInteger result = 0;
  427. xmlNodePtr currentNode = ((xmlStdPtr)genericPtr)->parent;
  428. while (currentNode != NULL)
  429. {
  430. result++;
  431. currentNode = currentNode->parent;
  432. }
  433. return result;
  434. }
  435. /**
  436. * Returns the DDXMLDocument object containing the root element and representing the XML document as a whole.
  437. * If the receiver is a standalone node (that is, a node at the head of a detached branch of the tree), this
  438. * method returns nil.
  439. **/
  440. - (DDXMLDocument *)rootDocument
  441. {
  442. // Note: DDXMLNamespaceNode overrides this method
  443. #if DDXML_DEBUG_MEMORY_ISSUES
  444. DDXMLNotZombieAssert();
  445. #endif
  446. xmlStdPtr node = (xmlStdPtr)genericPtr;
  447. if (node == NULL || node->doc == NULL)
  448. return nil;
  449. else
  450. return [DDXMLDocument nodeWithDocPrimitive:node->doc owner:self];
  451. }
  452. /**
  453. * Returns the parent node of the receiver.
  454. *
  455. * Document nodes and standalone nodes (that is, the root of a detached branch of a tree) have no parent, and
  456. * sending this message to them returns nil. A one-to-one relationship does not always exists between a parent and
  457. * its children; although a namespace or attribute node cannot be a child, it still has a parent element.
  458. **/
  459. - (DDXMLNode *)parent
  460. {
  461. // Note: DDXMLNamespaceNode overrides this method
  462. #if DDXML_DEBUG_MEMORY_ISSUES
  463. DDXMLNotZombieAssert();
  464. #endif
  465. xmlStdPtr node = (xmlStdPtr)genericPtr;
  466. if (node->parent == NULL)
  467. return nil;
  468. else
  469. return [DDXMLNode nodeWithUnknownPrimitive:(xmlKindPtr)node->parent owner:self];
  470. }
  471. /**
  472. * Returns the number of child nodes the receiver has.
  473. * For performance reasons, use this method instead of getting the count from the array returned by children.
  474. **/
  475. - (NSUInteger)childCount
  476. {
  477. // Note: DDXMLNamespaceNode overrides this method
  478. #if DDXML_DEBUG_MEMORY_ISSUES
  479. DDXMLNotZombieAssert();
  480. #endif
  481. if (!IsXmlDocPtr(genericPtr) && !IsXmlNodePtr(genericPtr) && !IsXmlDtdPtr(genericPtr))
  482. {
  483. return 0;
  484. }
  485. NSUInteger result = 0;
  486. xmlNodePtr child = ((xmlStdPtr)genericPtr)->children;
  487. while (child != NULL)
  488. {
  489. result++;
  490. child = child->next;
  491. }
  492. return result;
  493. }
  494. /**
  495. * Returns an immutable array containing the child nodes of the receiver (as DDXMLNode objects).
  496. **/
  497. - (NSArray *)children
  498. {
  499. // Note: DDXMLNamespaceNode overrides this method
  500. #if DDXML_DEBUG_MEMORY_ISSUES
  501. DDXMLNotZombieAssert();
  502. #endif
  503. if (!IsXmlDocPtr(genericPtr) && !IsXmlNodePtr(genericPtr) && !IsXmlDtdPtr(genericPtr))
  504. {
  505. return nil;
  506. }
  507. NSMutableArray *result = [NSMutableArray array];
  508. xmlNodePtr child = ((xmlStdPtr)genericPtr)->children;
  509. while (child != NULL)
  510. {
  511. [result addObject:[DDXMLNode nodeWithUnknownPrimitive:(xmlKindPtr)child owner:self]];
  512. child = child->next;
  513. }
  514. return [result copy];
  515. }
  516. /**
  517. * Returns the child node of the receiver at the specified location.
  518. * Returns a DDXMLNode object or nil if the receiver has no children.
  519. *
  520. * If the receive has children and index is out of bounds, an exception is raised.
  521. *
  522. * The receiver should be a DDXMLNode object representing a document, element, or document type declaration.
  523. * The returned node object can represent an element, comment, text, or processing instruction.
  524. **/
  525. - (DDXMLNode *)childAtIndex:(NSUInteger)index
  526. {
  527. // Note: DDXMLNamespaceNode overrides this method
  528. #if DDXML_DEBUG_MEMORY_ISSUES
  529. DDXMLNotZombieAssert();
  530. #endif
  531. if (!IsXmlDocPtr(genericPtr) && !IsXmlNodePtr(genericPtr) && !IsXmlDtdPtr(genericPtr))
  532. {
  533. return nil;
  534. }
  535. NSUInteger i = 0;
  536. xmlNodePtr child = ((xmlStdPtr)genericPtr)->children;
  537. if (child == NULL)
  538. {
  539. // NSXML doesn't raise an exception if there are no children
  540. return nil;
  541. }
  542. while (child != NULL)
  543. {
  544. if (i == index)
  545. {
  546. return [DDXMLNode nodeWithUnknownPrimitive:(xmlKindPtr)child owner:self];
  547. }
  548. i++;
  549. child = child->next;
  550. }
  551. // NSXML version uses this same assertion
  552. DDXMLAssert(NO, @"index (%u) beyond bounds (%u)", (unsigned)index, (unsigned)i);
  553. return nil;
  554. }
  555. /**
  556. * Returns the previous DDXMLNode object that is a sibling node to the receiver.
  557. *
  558. * This object will have an index value that is one less than the receiverÕs.
  559. * If there are no more previous siblings (that is, other child nodes of the receiverÕs parent) the method returns nil.
  560. **/
  561. - (DDXMLNode *)previousSibling
  562. {
  563. // Note: DDXMLNamespaceNode overrides this method
  564. #if DDXML_DEBUG_MEMORY_ISSUES
  565. DDXMLNotZombieAssert();
  566. #endif
  567. xmlStdPtr node = (xmlStdPtr)genericPtr;
  568. if (node->prev == NULL)
  569. return nil;
  570. else
  571. return [DDXMLNode nodeWithUnknownPrimitive:(xmlKindPtr)node->prev owner:self];
  572. }
  573. /**
  574. * Returns the next DDXMLNode object that is a sibling node to the receiver.
  575. *
  576. * This object will have an index value that is one more than the receiverÕs.
  577. * If there are no more subsequent siblings (that is, other child nodes of the receiverÕs parent) the
  578. * method returns nil.
  579. **/
  580. - (DDXMLNode *)nextSibling
  581. {
  582. // Note: DDXMLNamespaceNode overrides this method
  583. #if DDXML_DEBUG_MEMORY_ISSUES
  584. DDXMLNotZombieAssert();
  585. #endif
  586. xmlStdPtr node = (xmlStdPtr)genericPtr;
  587. if (node->next == NULL)
  588. return nil;
  589. else
  590. return [DDXMLNode nodeWithUnknownPrimitive:(xmlKindPtr)node->next owner:self];
  591. }
  592. /**
  593. * Returns the previous DDXMLNode object in document order.
  594. *
  595. * You use this method to ÒwalkÓ backward through the tree structure representing an XML document or document section.
  596. * (Use nextNode to traverse the tree in the opposite direction.) Document order is the natural order that XML
  597. * constructs appear in markup text. If you send this message to the first node in the tree (that is, the root element),
  598. * nil is returned. DDXMLNode bypasses namespace and attribute nodes when it traverses a tree in document order.
  599. **/
  600. - (DDXMLNode *)previousNode
  601. {
  602. // Note: DDXMLNamespaceNode overrides this method
  603. // Note: DDXMLAttributeNode overrides this method
  604. #if DDXML_DEBUG_MEMORY_ISSUES
  605. DDXMLNotZombieAssert();
  606. #endif
  607. // If the node has a previous sibling,
  608. // then we need the last child of the last child of the last child etc
  609. // Note: Try to accomplish this task without creating dozens of intermediate wrapper objects
  610. xmlStdPtr node = (xmlStdPtr)genericPtr;
  611. xmlStdPtr previousSibling = node->prev;
  612. if (previousSibling != NULL)
  613. {
  614. if (previousSibling->last != NULL)
  615. {
  616. xmlNodePtr lastChild = previousSibling->last;
  617. while (lastChild->last != NULL)
  618. {
  619. lastChild = lastChild->last;
  620. }
  621. return [DDXMLNode nodeWithUnknownPrimitive:(xmlKindPtr)lastChild owner:self];
  622. }
  623. else
  624. {
  625. // The previous sibling has no children, so the previous node is simply the previous sibling
  626. return [DDXMLNode nodeWithUnknownPrimitive:(xmlKindPtr)previousSibling owner:self];
  627. }
  628. }
  629. // If there are no previous siblings, then the previous node is simply the parent
  630. // Note: rootNode.parent == docNode
  631. if (node->parent == NULL || node->parent->type == XML_DOCUMENT_NODE)
  632. return nil;
  633. else
  634. return [DDXMLNode nodeWithUnknownPrimitive:(xmlKindPtr)node->parent owner:self];
  635. }
  636. /**
  637. * Returns the next DDXMLNode object in document order.
  638. *
  639. * You use this method to ÒwalkÓ forward through the tree structure representing an XML document or document section.
  640. * (Use previousNode to traverse the tree in the opposite direction.) Document order is the natural order that XML
  641. * constructs appear in markup text. If you send this message to the last node in the tree, nil is returned.
  642. * DDXMLNode bypasses namespace and attribute nodes when it traverses a tree in document order.
  643. **/
  644. - (DDXMLNode *)nextNode
  645. {
  646. // Note: DDXMLNamespaceNode overrides this method
  647. // Note: DDXMLAttributeNode overrides this method
  648. #if DDXML_DEBUG_MEMORY_ISSUES
  649. DDXMLNotZombieAssert();
  650. #endif
  651. // If the node has children, then next node is the first child
  652. DDXMLNode *firstChild = [self childAtIndex:0];
  653. if (firstChild)
  654. return firstChild;
  655. // If the node has a next sibling, then next node is the same as next sibling
  656. DDXMLNode *nextSibling = [self nextSibling];
  657. if (nextSibling)
  658. return nextSibling;
  659. // There are no children, and no more siblings, so we need to get the next sibling of the parent.
  660. // If that is nil, we need to get the next sibling of the grandparent, etc.
  661. // Note: Try to accomplish this task without creating dozens of intermediate wrapper objects
  662. xmlNodePtr parent = ((xmlStdPtr)genericPtr)->parent;
  663. while (parent != NULL)
  664. {
  665. xmlNodePtr parentNextSibling = parent->next;
  666. if (parentNextSibling != NULL)
  667. return [DDXMLNode nodeWithUnknownPrimitive:(xmlKindPtr)parentNextSibling owner:self];
  668. else
  669. parent = parent->parent;
  670. }
  671. return nil;
  672. }
  673. /**
  674. * Detaches the receiver from its parent node.
  675. *
  676. * This method is applicable to DDXMLNode objects representing elements, text, comments, processing instructions,
  677. * attributes, and namespaces. Once the node object is detached, you can add it as a child node of another parent.
  678. **/
  679. - (void)detach
  680. {
  681. // Note: DDXMLNamespaceNode overrides this method
  682. // Note: DDXMLAttributeNode overrides this method
  683. #if DDXML_DEBUG_MEMORY_ISSUES
  684. DDXMLNotZombieAssert();
  685. #endif
  686. xmlStdPtr node = (xmlStdPtr)genericPtr;
  687. if (node->parent != NULL)
  688. {
  689. if (IsXmlNodePtr(genericPtr))
  690. {
  691. [[self class] detachChild:(xmlNodePtr)node];
  692. owner = nil;
  693. }
  694. }
  695. }
  696. - (xmlStdPtr)_XPathPreProcess:(NSMutableString *)result
  697. {
  698. // This is a private/internal method
  699. // Note: DDXMLNamespaceNode overrides this method
  700. // Note: DDXMLAttributeNode overrides this method
  701. return (xmlStdPtr)genericPtr;
  702. }
  703. - (NSString *)XPath
  704. {
  705. #if DDXML_DEBUG_MEMORY_ISSUES
  706. DDXMLNotZombieAssert();
  707. #endif
  708. NSMutableString *result = [NSMutableString stringWithCapacity:25];
  709. // Examples:
  710. // /rootElement[1]/subElement[4]/thisNode[2]
  711. // topElement/thisNode[2]
  712. xmlStdPtr node = [self _XPathPreProcess:result];
  713. // Note: rootNode.parent == docNode
  714. while ((node != NULL) && (node->type != XML_DOCUMENT_NODE))
  715. {
  716. if ((node->parent == NULL) && (node->doc == NULL))
  717. {
  718. // We're at the top of the heirarchy, and there is no xml document.
  719. // Thus we don't use a leading '/', and we don't need an index.
  720. [result insertString:[NSString stringWithFormat:@"%s", node->name] atIndex:0];
  721. }
  722. else
  723. {
  724. // Find out what index this node is.
  725. // If it's the first node with this name, the index is 1.
  726. // If there are previous siblings with the same name, the index is greater than 1.
  727. int index = 1;
  728. xmlStdPtr prevNode = node->prev;
  729. while (prevNode != NULL)
  730. {
  731. if (xmlStrEqual(node->name, prevNode->name))
  732. {
  733. index++;
  734. }
  735. prevNode = prevNode->prev;
  736. }
  737. [result insertString:[NSString stringWithFormat:@"/%s[%i]", node->name, index] atIndex:0];
  738. }
  739. node = (xmlStdPtr)node->parent;
  740. }
  741. return [result copy];
  742. }
  743. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  744. #pragma mark QNames
  745. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  746. /**
  747. * Returns the local name of the receiver.
  748. *
  749. * The local name is the part of a node name that follows a namespace-qualifying colon or the full name if
  750. * there is no colon. For example, ÒchapterÓ is the local name in the qualified name Òacme:chapterÓ.
  751. **/
  752. - (NSString *)localName
  753. {
  754. // Note: DDXMLNamespaceNode overrides this method
  755. // Zombie test occurs in [self name]
  756. return [[self class] localNameForName:[self name]];
  757. }
  758. /**
  759. * Returns the prefix of the receiverÕs name.
  760. *
  761. * The prefix is the part of a namespace-qualified name that precedes the colon.
  762. * For example, ÒacmeÓ is the local name in the qualified name Òacme:chapterÓ.
  763. * This method returns an empty string if the receiverÕs name is not qualified by a namespace.
  764. **/
  765. - (NSString *)prefix
  766. {
  767. // Note: DDXMLNamespaceNode overrides this method
  768. // Zombie test occurs in [self name]
  769. return [[self class] prefixForName:[self name]];
  770. }
  771. /**
  772. * Sets the URI identifying the source of this document.
  773. * Pass nil to remove the current URI.
  774. **/
  775. - (void)setURI:(NSString *)URI
  776. {
  777. // Note: DDXMLNamespaceNode overrides this method
  778. // Note: DDXMLAttributeNode overrides this method
  779. #if DDXML_DEBUG_MEMORY_ISSUES
  780. DDXMLNotZombieAssert();
  781. #endif
  782. if (IsXmlNodePtr(genericPtr))
  783. {
  784. xmlNodePtr node = (xmlNodePtr)genericPtr;
  785. if (node->ns != NULL)
  786. {
  787. [[self class] removeNamespace:node->ns fromNode:node];
  788. }
  789. if (URI)
  790. {
  791. // Create a new xmlNsPtr, add it to the nsDef list, and make ns point to it
  792. xmlNsPtr ns = xmlNewNs(NULL, [URI xmlChar], NULL);
  793. ns->next = node->nsDef;
  794. node->nsDef = ns;
  795. node->ns = ns;
  796. }
  797. }
  798. }
  799. /**
  800. * Returns the URI associated with the receiver.
  801. *
  802. * A nodeÕs URI is derived from its namespace or a documentÕs URI; for documents, the URI comes either from the
  803. * parsed XML or is explicitly set. You cannot change the URI for a particular node other for than a namespace
  804. * or document node.
  805. **/
  806. - (NSString *)URI
  807. {
  808. // Note: DDXMLNamespaceNode overrides this method
  809. // Note: DDXMLAttributeNode overrides this method
  810. #if DDXML_DEBUG_MEMORY_ISSUES
  811. DDXMLNotZombieAssert();
  812. #endif
  813. if (IsXmlNodePtr(genericPtr))
  814. {
  815. xmlNodePtr node = (xmlNodePtr)genericPtr;
  816. if (node->ns != NULL)
  817. {
  818. return [NSString stringWithUTF8String:((const char *)node->ns->href)];
  819. }
  820. }
  821. return nil;
  822. }
  823. + (void)getHasPrefix:(BOOL *)hasPrefixPtr localName:(NSString **)localNamePtr forName:(NSString *)name
  824. {
  825. // This is a private/internal method
  826. if (name)
  827. {
  828. NSRange range = [name rangeOfString:@":"];
  829. if (range.length != 0)
  830. {
  831. if (hasPrefixPtr) *hasPrefixPtr = range.location > 0;
  832. if (localNamePtr) *localNamePtr = [name substringFromIndex:(range.location + range.length)];
  833. }
  834. else
  835. {
  836. if (hasPrefixPtr) *hasPrefixPtr = NO;
  837. if (localNamePtr) *localNamePtr = name;
  838. }
  839. }
  840. else
  841. {
  842. if (hasPrefixPtr) *hasPrefixPtr = NO;
  843. if (localNamePtr) *localNamePtr = nil;
  844. }
  845. }
  846. + (void)getPrefix:(NSString **)prefixPtr localName:(NSString **)localNamePtr forName:(NSString *)name
  847. {
  848. // This is a private/internal method
  849. if (name)
  850. {
  851. NSRange range = [name rangeOfString:@":"];
  852. if (range.length != 0)
  853. {
  854. if (prefixPtr) *prefixPtr = [name substringToIndex:range.location];
  855. if (localNamePtr) *localNamePtr = [name substringFromIndex:(range.location + range.length)];
  856. }
  857. else
  858. {
  859. if (prefixPtr) *prefixPtr = @"";
  860. if (localNamePtr) *localNamePtr = name;
  861. }
  862. }
  863. else
  864. {
  865. if (prefixPtr) *prefixPtr = @"";
  866. if (localNamePtr) *localNamePtr = nil;
  867. }
  868. }
  869. /**
  870. * Returns the local name from the specified qualified name.
  871. *
  872. * Examples:
  873. * "a:node" -> "node"
  874. * "a:a:node" -> "a:node"
  875. * "node" -> "node"
  876. * nil - > nil
  877. **/
  878. + (NSString *)localNameForName:(NSString *)name
  879. {
  880. // This is a public/API method
  881. NSString *localName;
  882. [self getPrefix:NULL localName:&localName forName:name];
  883. return localName;
  884. }
  885. /**
  886. * Extracts the prefix from the given name.
  887. * If name is nil, or has no prefix, an empty string is returned.
  888. *
  889. * Examples:
  890. * "a:deusty.com" -> "a"
  891. * "a:a:deusty.com" -> "a"
  892. * "node" -> ""
  893. * nil -> ""
  894. **/
  895. + (NSString *)prefixForName:(NSString *)name
  896. {
  897. // This is a public/API method
  898. NSString *prefix;
  899. [self getPrefix:&prefix localName:NULL forName:name];
  900. return prefix;
  901. }
  902. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  903. #pragma mark Output
  904. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  905. - (NSString *)description
  906. {
  907. // Zombie test occurs in XMLStringWithOptions:
  908. return [self XMLStringWithOptions:0];
  909. }
  910. - (NSString *)XMLString
  911. {
  912. // Zombie test occurs in XMLStringWithOptions:
  913. return [self XMLStringWithOptions:0];
  914. }
  915. - (NSString *)XMLStringWithOptions:(NSUInteger)options
  916. {
  917. #if DDXML_DEBUG_MEMORY_ISSUES
  918. DDXMLNotZombieAssert();
  919. #endif
  920. // xmlSaveNoEmptyTags:
  921. // Global setting, asking the serializer to not output empty tags
  922. // as <empty/> but <empty></empty>. those two forms are undistinguishable
  923. // once parsed.
  924. // Disabled by default
  925. if (options & DDXMLNodeCompactEmptyElement)
  926. xmlSaveNoEmptyTags = 0;
  927. else
  928. xmlSaveNoEmptyTags = 1;
  929. int format = 0;
  930. if (options & DDXMLNodePrettyPrint)
  931. {
  932. format = 1;
  933. xmlIndentTreeOutput = 1;
  934. }
  935. int dumpCnt;
  936. xmlBufferPtr bufferPtr = xmlBufferCreate();
  937. if (IsXmlNsPtr(genericPtr))
  938. dumpCnt = xmlNodeDump(bufferPtr, NULL, (xmlNodePtr)genericPtr, 0, format);
  939. else
  940. dumpCnt = xmlNodeDump(bufferPtr, ((xmlStdPtr)genericPtr)->doc, (xmlNodePtr)genericPtr, 0, format);
  941. if (dumpCnt < 0)
  942. {
  943. return nil;
  944. }
  945. if ([self kind] == DDXMLTextKind)
  946. {
  947. NSString *result = [NSString stringWithUTF8String:(const char *)bufferPtr->content];
  948. xmlBufferFree(bufferPtr);
  949. return result;
  950. }
  951. else
  952. {
  953. NSMutableString *resTmp = [NSMutableString stringWithUTF8String:(const char *)bufferPtr->content];
  954. CFStringTrimWhitespace((__bridge CFMutableStringRef)resTmp);
  955. xmlBufferFree(bufferPtr);
  956. return [resTmp copy];
  957. }
  958. }
  959. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  960. #pragma mark XPath/XQuery
  961. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  962. - (NSArray *)nodesForXPath:(NSString *)xpath error:(NSError **)error
  963. {
  964. #if DDXML_DEBUG_MEMORY_ISSUES
  965. DDXMLNotZombieAssert();
  966. #endif
  967. xmlXPathContextPtr xpathCtx;
  968. xmlXPathObjectPtr xpathObj;
  969. BOOL isTempDoc = NO;
  970. xmlDocPtr doc;
  971. if (IsXmlDocPtr(genericPtr))
  972. {
  973. doc = (xmlDocPtr)genericPtr;
  974. }
  975. else if (IsXmlNodePtr(genericPtr))
  976. {
  977. doc = ((xmlNodePtr)genericPtr)->doc;
  978. if(doc == NULL)
  979. {
  980. isTempDoc = YES;
  981. doc = xmlNewDoc(NULL);
  982. xmlDocSetRootElement(doc, (xmlNodePtr)genericPtr);
  983. }
  984. }
  985. else
  986. {
  987. return nil;
  988. }
  989. xpathCtx = xmlXPathNewContext(doc);
  990. xpathCtx->node = (xmlNodePtr)genericPtr;
  991. xmlNodePtr rootNode = (doc)->children;
  992. if(rootNode != NULL)
  993. {
  994. xmlNsPtr ns = rootNode->nsDef;
  995. while(ns != NULL)
  996. {
  997. xmlXPathRegisterNs(xpathCtx, ns->prefix, ns->href);
  998. ns = ns->next;
  999. }
  1000. }
  1001. xpathObj = xmlXPathEvalExpression([xpath xmlChar], xpathCtx);
  1002. NSArray *result;
  1003. if(xpathObj == NULL)
  1004. {
  1005. if(error) *error = [[self class] lastError];
  1006. result = nil;
  1007. }
  1008. else
  1009. {
  1010. if(error) *error = nil;
  1011. int count = xmlXPathNodeSetGetLength(xpathObj->nodesetval);
  1012. if(count == 0)
  1013. {
  1014. result = [NSArray array];
  1015. }
  1016. else
  1017. {
  1018. NSMutableArray *mResult = [NSMutableArray arrayWithCapacity:count];
  1019. int i;
  1020. for (i = 0; i < count; i++)
  1021. {
  1022. xmlNodePtr node = xpathObj->nodesetval->nodeTab[i];
  1023. [mResult addObject:[DDXMLNode nodeWithUnknownPrimitive:(xmlKindPtr)node owner:self]];
  1024. }
  1025. result = mResult;
  1026. }
  1027. }
  1028. if(xpathObj) xmlXPathFreeObject(xpathObj);
  1029. if(xpathCtx) xmlXPathFreeContext(xpathCtx);
  1030. if (isTempDoc)
  1031. {
  1032. xmlUnlinkNode((xmlNodePtr)genericPtr);
  1033. xmlFreeDoc(doc);
  1034. // xmlUnlinkNode doesn't remove the doc ptr
  1035. [[self class] recursiveStripDocPointersFromNode:(xmlNodePtr)genericPtr];
  1036. }
  1037. return result;
  1038. }
  1039. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1040. #pragma mark Private API
  1041. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1042. // ---------- MEMORY MANAGEMENT ARCHITECTURE ----------
  1043. //
  1044. // KissXML is designed to be read-access thread-safe.
  1045. // It is not write-access thread-safe as this would require significant overhead.
  1046. //
  1047. // What exactly does read-access thread-safe mean?
  1048. // It means that multiple threads can safely read from the same xml structure,
  1049. // so long as none of them attempt to alter the xml structure (add/remove nodes, change attributes, etc).
  1050. //
  1051. // This read-access thread-safety includes parsed xml structures as well as xml structures created by you.
  1052. // Let's walk through a few examples to get a deeper understanding.
  1053. //
  1054. //
  1055. //
  1056. // Example #1 - Parallel processing of children
  1057. //
  1058. // DDXMLElement *root = [[DDXMLElement alloc] initWithXMLString:str error:nil];
  1059. // NSArray *children = [root children];
  1060. //
  1061. // dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  1062. // dispatch_apply([children count], q, ^(size_t i) {
  1063. // DDXMLElement *child = [children objectAtIndex:i];
  1064. // <process child>
  1065. // });
  1066. //
  1067. //
  1068. //
  1069. // Example #2 - Asynchronous child processing
  1070. //
  1071. // DDXMLElement *root = [[DDXMLElement alloc] initWithXMLString:str error:nil];
  1072. // DDXMLElement *child = [root elementForName:@"starbucks"];
  1073. //
  1074. // dispatch_async(queue, ^{
  1075. // <process child>
  1076. // });
  1077. //
  1078. // [root release];
  1079. //
  1080. // You may have noticed that we possibly released the root node before the child was processed.
  1081. // Is this safe?
  1082. //
  1083. // The answer is YES.
  1084. // The child node retains a reference to the root node,
  1085. // so the xml tree heirarchy won't be freed until you're done using all associated nodes.
  1086. //
  1087. //
  1088. //
  1089. /**
  1090. * Returns whether or not the node has a parent.
  1091. * Use this method instead of parent when you only need to ensure parent is nil.
  1092. * This prevents the unnecessary creation of a parent node wrapper.
  1093. **/
  1094. - (BOOL)_hasParent
  1095. {
  1096. // This is a private/internal method
  1097. // Note: DDXMLNamespaceNode overrides this method
  1098. xmlStdPtr node = (xmlStdPtr)genericPtr;
  1099. return (node->parent != NULL);
  1100. }
  1101. + (void)stripDocPointersFromAttr:(xmlAttrPtr)attr
  1102. {
  1103. xmlNodePtr child = attr->children;
  1104. while (child != NULL)
  1105. {
  1106. child->doc = NULL;
  1107. child = child->next;
  1108. }
  1109. attr->doc = NULL;
  1110. }
  1111. + (void)recursiveStripDocPointersFromNode:(xmlNodePtr)node
  1112. {
  1113. xmlAttrPtr attr = node->properties;
  1114. while (attr != NULL)
  1115. {
  1116. [self stripDocPointersFromAttr:attr];
  1117. attr = attr->next;
  1118. }
  1119. xmlNodePtr child = node->children;
  1120. while (child != NULL)
  1121. {
  1122. [self recursiveStripDocPointersFromNode:child];
  1123. child = child->next;
  1124. }
  1125. node->doc = NULL;
  1126. }
  1127. /**
  1128. * If node->ns is pointing to the given ns, the pointer is nullified.
  1129. * The same goes for any attributes and childrend of node.
  1130. **/
  1131. + (void)recursiveStripNamespace:(xmlNsPtr)ns fromNode:(xmlNodePtr)node
  1132. {
  1133. if (node->ns == ns)
  1134. {
  1135. node->ns = NULL;
  1136. }
  1137. xmlAttrPtr attr = node->properties;
  1138. while (attr)
  1139. {
  1140. if (attr->ns == ns)
  1141. {
  1142. attr->ns = NULL;
  1143. }
  1144. attr = attr->next;
  1145. }
  1146. xmlNodePtr child = node->children;
  1147. while (child)
  1148. {
  1149. [self recursiveStripNamespace:ns fromNode:child];
  1150. child = child->next;
  1151. }
  1152. }
  1153. /**
  1154. * If node or any of its attributes or children are referencing the the given old namespace,
  1155. * they are migrated to reference the new namespace instead.
  1156. *
  1157. * If newNs is NULL, and a reference to oldNs is found, then oldNs is copied,
  1158. * and the copy is used for the remainder of the recursion.
  1159. *
  1160. * This method makes copies of oldNs as needed so that oldNs can be cleanly detached from the tree.
  1161. **/
  1162. + (void)recursiveMigrateNamespace:(xmlNsPtr)oldNs to:(xmlNsPtr)newNs node:(xmlNodePtr)node
  1163. {
  1164. // Do we need to copy old namespace?
  1165. if (newNs == NULL)
  1166. {
  1167. // A copy needs to be made if:
  1168. // * node->ns == oldNs
  1169. // * attr->ns == oldNs
  1170. //
  1171. // Remember: The namespaces in node->nsDef are owned by node.
  1172. // That's not what we're migrating.
  1173. BOOL needsCopy = (node->ns == oldNs);
  1174. if (!needsCopy)
  1175. {
  1176. xmlAttrPtr attr = node->properties;
  1177. while (attr)
  1178. {
  1179. if (attr->ns == oldNs)
  1180. {
  1181. needsCopy = YES;
  1182. break;
  1183. }
  1184. attr = attr->next;
  1185. }
  1186. }
  1187. if (needsCopy)
  1188. {
  1189. // Copy oldNs, and place at the end of the node's namspace list
  1190. newNs = xmlNewNs(NULL, oldNs->href, oldNs->prefix);
  1191. if (node->nsDef == NULL)
  1192. {
  1193. node->nsDef = newNs;
  1194. }
  1195. else
  1196. {
  1197. xmlNsPtr lastNs = node->nsDef;
  1198. while (lastNs->next)
  1199. {
  1200. lastNs = lastNs->next;
  1201. }
  1202. lastNs->next = newNs;
  1203. }
  1204. }
  1205. }
  1206. // Migrate node & attributes
  1207. if (newNs)
  1208. {
  1209. if (node->ns == oldNs)
  1210. {
  1211. node->ns = newNs;
  1212. }
  1213. xmlAttrPtr attr = node->properties;
  1214. while (attr)
  1215. {
  1216. if (attr->ns == oldNs)
  1217. {
  1218. attr->ns = newNs;
  1219. }
  1220. attr = attr->next;
  1221. }
  1222. }
  1223. // Migrate children
  1224. xmlNodePtr child = node->children;
  1225. while (child)
  1226. {
  1227. [self recursiveMigrateNamespace:oldNs to:newNs node:child];
  1228. child = child->next;
  1229. }
  1230. }
  1231. /**
  1232. * If node has a default namespace allocated outside the given root,
  1233. * this method copies the namespace so that the given root can be cleanly detached from its tree.
  1234. **/
  1235. + (void)recursiveFixDefaultNamespacesInNode:(xmlNodePtr)node withNewRoot:(xmlNodePtr)rootNode
  1236. {
  1237. NSAssert(rootNode != NULL, @"Must specify rootNode.");
  1238. // Step 1 of 3
  1239. //
  1240. // Copy our namespace.
  1241. // It's important to do this first (before the other steps).
  1242. // This way attributes and children can reference our copy. (prevents multiple copies throughout tree)
  1243. xmlNsPtr nodeNs = node->ns;
  1244. if (nodeNs)
  1245. {
  1246. // Does the namespace reside within the new root somewhere?
  1247. // We can find out by searching for it in nsDef lists up to the given root.
  1248. BOOL nsResidesWithinNewRoot = NO;
  1249. xmlNodePtr treeNode = node;
  1250. while (treeNode)
  1251. {
  1252. xmlNsPtr treeNs = treeNode->nsDef;
  1253. while (treeNs)
  1254. {
  1255. if (treeNs == nodeNs)
  1256. {
  1257. nsResidesWithinNewRoot = YES;
  1258. break;
  1259. }
  1260. treeNs = treeNs->next;
  1261. }
  1262. if (nsResidesWithinNewRoot || treeNode == rootNode)
  1263. treeNode = NULL;
  1264. else
  1265. treeNode = treeNode->parent;
  1266. }
  1267. if (!nsResidesWithinNewRoot)
  1268. {
  1269. // Create a copy of the namespace, add to nsDef list, and then set as ns
  1270. xmlNsPtr nodeNsCopy = xmlNewNs(NULL, nodeNs->href, nodeNs->prefix);
  1271. nodeNsCopy->next = node->nsDef;
  1272. node->nsDef = nodeNsCopy;
  1273. node->ns = nodeNsCopy;
  1274. }
  1275. }
  1276. // Step 2 of 3
  1277. //
  1278. // If any attributes are referencing namespaces outside the new root,
  1279. // copy the namespaces into node, and have the attributes reference the copy.
  1280. xmlAttrPtr attr = node->properties;
  1281. while (attr)
  1282. {
  1283. xmlNsPtr attrNs = attr->ns;
  1284. while (attrNs)
  1285. {
  1286. BOOL nsResidesWithinNewRoot = NO;
  1287. xmlNodePtr treeNode = node;
  1288. while (treeNode)
  1289. {
  1290. xmlNsPtr treeNs = treeNode->nsDef;
  1291. while (treeNs)
  1292. {
  1293. if (treeNs == attrNs)
  1294. {
  1295. nsResidesWithinNewRoot = YES;
  1296. break;
  1297. }
  1298. treeNs = treeNs->next;
  1299. }
  1300. if (nsResidesWithinNewRoot || treeNode == rootNode)
  1301. treeNode = NULL;
  1302. else
  1303. treeNode = treeNode->parent;
  1304. }
  1305. if (!nsResidesWithinNewRoot)
  1306. {
  1307. // Create a copy of the namespace, add to node's nsDef list, and then set as attribute's ns
  1308. xmlNsPtr attrNsCopy = xmlNewNs(NULL, attrNs->href, attrNs->prefix);
  1309. attrNsCopy->next = node->nsDef;
  1310. node->nsDef = attrNsCopy;
  1311. attr->ns = attrNsCopy;
  1312. }
  1313. attrNs = attrNs->next;
  1314. }
  1315. attr = attr->next;
  1316. }
  1317. // Step 3 of 3
  1318. //
  1319. // Copy namespaces into children
  1320. xmlNodePtr childNode = node->children;
  1321. while (childNode)
  1322. {
  1323. [self recursiveFixDefaultNamespacesInNode:childNode withNewRoot:rootNode];
  1324. childNode = childNode->next;
  1325. }
  1326. }
  1327. /**
  1328. * Detaches the given namespace from the given node.
  1329. * The namespace's surrounding next pointers are properly updated to remove the namespace from the node's nsDef list.
  1330. * Then the namespace's parent and next pointers are destroyed.
  1331. **/
  1332. + (void)detachNamespace:(xmlNsPtr)ns fromNode:(xmlNodePtr)node
  1333. {
  1334. // If node, or any of node's attributes are referring to this namespace,
  1335. // then we need to nullify those references.
  1336. //
  1337. // However, if any children are referring to this namespace,
  1338. // then we instruct those children to make copies.
  1339. if (node->ns == ns)
  1340. {
  1341. node->ns = NULL;
  1342. }
  1343. xmlAttrPtr attr = node->properties;
  1344. while (attr)
  1345. {
  1346. if (attr->ns == ns)
  1347. {
  1348. attr->ns = NULL;
  1349. }
  1350. attr = attr->next;
  1351. }
  1352. xmlNodePtr child = node->children;
  1353. while (child)
  1354. {
  1355. [self recursiveMigrateNamespace:ns to:NULL node:child];
  1356. child = child->next;
  1357. }
  1358. // Now detach namespace from the namespace list.
  1359. //
  1360. // Namespace nodes have no previous pointer, so we have to search for the node
  1361. xmlNsPtr previousNs = NULL;
  1362. xmlNsPtr currentNs = node->nsDef;
  1363. while (currentNs != NULL)
  1364. {
  1365. if (currentNs == ns)
  1366. {
  1367. if (previousNs == NULL)
  1368. node->nsDef = currentNs->next;
  1369. else
  1370. previousNs->next = currentNs->next;
  1371. break;
  1372. }
  1373. previousNs = currentNs;
  1374. currentNs = currentNs->next;
  1375. }
  1376. // Nullify pointers
  1377. ns->next = NULL;
  1378. }
  1379. /**
  1380. * Removes the given namespace from the given node.
  1381. * The namespace's surrounding next pointers are properly updated to remove the namespace from the nsDef list.
  1382. * Then the namespace is freed if it's no longer being referenced.
  1383. * Otherwise, it's nsParent and next pointers are destroyed.
  1384. **/
  1385. + (void)removeNamespace:(xmlNsPtr)ns fromNode:(xmlNodePtr)node
  1386. {
  1387. #if DDXML_DEBUG_MEMORY_ISSUES
  1388. MarkZombies(ns);
  1389. #endif
  1390. [self detachNamespace:ns fromNode:node];
  1391. xmlFreeNs(ns);
  1392. }
  1393. /**
  1394. * Removes all namespaces from the given node.
  1395. * All namespaces are either freed, or their nsParent and next pointers are properly destroyed.
  1396. * Upon return, the given node's nsDef pointer is NULL.
  1397. **/
  1398. + (void)removeAllNamespacesFromNode:(xmlNodePtr)node
  1399. {
  1400. xmlNsPtr ns = node->nsDef;
  1401. while (ns != NULL)
  1402. {
  1403. xmlNsPtr nextNs = ns->next;
  1404. #if DDXML_DEBUG_MEMORY_ISSUES
  1405. MarkZombies(ns);
  1406. #endif
  1407. xmlFreeNs(ns);
  1408. ns = nextNs;
  1409. }
  1410. node->nsDef = NULL;
  1411. node->ns = NULL;
  1412. }
  1413. /**
  1414. * Detaches the given attribute from its parent node.
  1415. * The attribute's surrounding prev/next pointers are properly updated to remove the attribute from the attr list.
  1416. * Then, if the clean flag is YES, the attribute's parent, prev, next and doc pointers are set to null.
  1417. **/
  1418. + (void)detachAttribute:(xmlAttrPtr)attr andClean:(BOOL)clean
  1419. {
  1420. xmlNodePtr parent = attr->parent;
  1421. // Update the surrounding prev/next pointers
  1422. if (attr->prev == NULL)
  1423. {
  1424. if (attr->next == NULL)
  1425. {
  1426. parent->properties = NULL;
  1427. }
  1428. else
  1429. {
  1430. parent->properties = attr->next;
  1431. attr->next->prev = NULL;
  1432. }
  1433. }
  1434. else
  1435. {
  1436. if (attr->next == NULL)
  1437. {
  1438. attr->prev->next = NULL;
  1439. }
  1440. else
  1441. {
  1442. attr->prev->next = attr->next;
  1443. attr->next->prev = attr->prev;
  1444. }
  1445. }
  1446. if (clean)
  1447. {
  1448. // Nullify pointers
  1449. attr->parent = NULL;
  1450. attr->prev = NULL;
  1451. attr->next = NULL;
  1452. attr->ns = NULL;
  1453. if (attr->doc != NULL) [self stripDocPointersFromAttr:attr];
  1454. }
  1455. }
  1456. /**
  1457. * Detaches the given attribute from its parent node.
  1458. * The attribute's surrounding prev/next pointers are properly updated to remove the attribute from the attr list.
  1459. * Then the attribute's parent, prev, next and doc pointers are destroyed.
  1460. **/
  1461. + (void)detachAttribute:(xmlAttrPtr)attr
  1462. {
  1463. [self detachAttribute:attr andClean:YES];
  1464. }
  1465. /**
  1466. * Removes and free's the given attribute from its parent node.
  1467. * The attribute's surrounding prev/next pointers are properly updated to remove the attribute from the attr list.
  1468. **/
  1469. + (void)removeAttribute:(xmlAttrPtr)attr
  1470. {
  1471. #if DDXML_DEBUG_MEMORY_ISSUES
  1472. MarkZombies(attr);
  1473. #endif
  1474. // We perform a bit of optimization here.
  1475. // No need to bother nullifying pointers since we're about to free the node anyway.
  1476. [self detachAttribute:attr andClean:NO];
  1477. xmlFreeProp(attr);
  1478. }
  1479. /**
  1480. * Removes and frees all attributes from the given node.
  1481. * Upon return, the given node's properties pointer is NULL.
  1482. **/
  1483. + (void)removeAllAttributesFromNode:(xmlNodePtr)node
  1484. {
  1485. xmlAttrPtr attr = node->properties;
  1486. while (attr != NULL)
  1487. {
  1488. xmlAttrPtr nextAttr = attr->next;
  1489. #if DDXML_DEBUG_MEMORY_ISSUES
  1490. MarkZombies(attr);
  1491. #endif
  1492. xmlFreeProp(attr);
  1493. attr = nextAttr;
  1494. }
  1495. node->properties = NULL;
  1496. }
  1497. /**
  1498. * Detaches the given child from its parent.
  1499. * The child's surrounding prev/next pointers are properly updated to remove the child from the node's children list.
  1500. * Then, if the clean flag is YES, the child's parent, prev, next and doc pointers are set to null.
  1501. **/
  1502. + (void)detachChild:(xmlNodePtr)child andClean:(BOOL)clean andFixNamespaces:(BOOL)fixNamespaces
  1503. {
  1504. xmlNodePtr parent = child->parent;
  1505. // Update the surrounding prev/next pointers
  1506. if (child->prev == NULL)
  1507. {
  1508. if (child->next == NULL)
  1509. {
  1510. parent->children = NULL;
  1511. parent->last = NULL;
  1512. }
  1513. else
  1514. {
  1515. parent->children = child->next;
  1516. child->next->prev = NULL;
  1517. }
  1518. }
  1519. else
  1520. {
  1521. if (child->next == NULL)
  1522. {
  1523. parent->last = child->prev;
  1524. child->prev->next = NULL;
  1525. }
  1526. else
  1527. {
  1528. child->prev->next = child->next;
  1529. child->next->prev = child->prev;
  1530. }
  1531. }
  1532. if (fixNamespaces)
  1533. {
  1534. // Fix namesapces (namespace references that now point outside tree)
  1535. // Note: This must be done before we nullify pointers so we can search up the tree.
  1536. [self recursiveFixDefaultNamespacesInNode:child withNewRoot:child];
  1537. }
  1538. if (clean)
  1539. {
  1540. // Nullify pointers
  1541. child->parent = NULL;
  1542. child->prev = NULL;
  1543. child->next = NULL;
  1544. if (child->doc != NULL) [self recursiveStripDocPointersFromNode:child];
  1545. }
  1546. }
  1547. /**
  1548. * Detaches the given child from its parent.
  1549. * The child's surrounding prev/next pointers are properly updated to remove the child from the node's children list.
  1550. * Then the child's parent, prev, next and doc pointers are set to null.
  1551. **/
  1552. + (void)detachChild:(xmlNodePtr)child
  1553. {
  1554. [self detachChild:child andClean:YES andFixNamespaces:YES];
  1555. }
  1556. /**
  1557. * Removes the given child from its parent node.
  1558. * The child's surrounding prev/next pointers are properly updated to remove the child from the node's children list.
  1559. * Then the child is recursively freed if it's no longer being referenced.
  1560. * Otherwise, it's parent, prev, next and doc pointers are destroyed.
  1561. *
  1562. * During the recursive free, subnodes still being referenced are properly handled.
  1563. **/
  1564. + (void)removeChild:(xmlNodePtr)child
  1565. {
  1566. #if DDXML_DEBUG_MEMORY_ISSUES
  1567. RecursiveMarkZombiesFromNode(child);
  1568. #endif
  1569. // We perform a bit of optimization here.
  1570. // No need to bother nullifying pointers since we're about to free the node anyway.
  1571. [self detachChild:child andClean:NO andFixNamespaces:NO];
  1572. xmlFreeNode(child);
  1573. }
  1574. /**
  1575. * Removes all children from the given node.
  1576. * All children are either recursively freed, or their parent, prev, next and doc pointers are properly destroyed.
  1577. * Upon return, the given node's children pointer is NULL.
  1578. *
  1579. * During the recursive free, subnodes still being referenced are properly handled.
  1580. **/
  1581. + (void)removeAllChildrenFromNode:(xmlNodePtr)node
  1582. {
  1583. xmlNodePtr child = node->children;
  1584. while (child != NULL)
  1585. {
  1586. xmlNodePtr nextChild = child->next;
  1587. #if DDXML_DEBUG_MEMORY_ISSUES
  1588. RecursiveMarkZombiesFromNode(child);
  1589. #endif
  1590. xmlFreeNode(child);
  1591. child = nextChild;
  1592. }
  1593. node->children = NULL;
  1594. node->last = NULL;
  1595. }
  1596. /**
  1597. * Returns the last error encountered by libxml.
  1598. * Errors are caught in the MyErrorHandler method within DDXMLDocument.
  1599. **/
  1600. + (NSError *)lastError
  1601. {
  1602. NSValue *lastErrorValue = [[[NSThread currentThread] threadDictionary] objectForKey:DDLastErrorKey];
  1603. if(lastErrorValue)
  1604. {
  1605. xmlError lastError;
  1606. [lastErrorValue getValue:&lastError];
  1607. int errCode = lastError.code;
  1608. NSString *errMsg = [[NSString stringWithFormat:@"%s", lastError.message] stringByTrimming];
  1609. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1610. return [NSError errorWithDomain:@"DDXMLErrorDomain" code:errCode userInfo:info];
  1611. }
  1612. else
  1613. {
  1614. return nil;
  1615. }
  1616. }
  1617. static void MyErrorHandler(void * userData, xmlErrorPtr error)
  1618. {
  1619. // This method is called by libxml when an error occurs.
  1620. // We register for this error in the initialize method below.
  1621. // Extract error message and store in the current thread's dictionary.
  1622. // This ensure's thread safey, and easy access for all other DDXML classes.
  1623. if (error == NULL)
  1624. {
  1625. [[[NSThread currentThread] threadDictionar