Develop and Download Open Source Software

Browse Subversion Repository

Contents of /trunk/0.2.X/KissXML/DDXMLNode.m

Parent Directory Parent Directory | Revision Log Revision Log


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

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26