Develop and Download Open Source Software

Browse Subversion Repository

Contents of /trunk/0.2.X/KissXML/DDXMLElement.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: 18082 byte(s)
kissxmlアップデート
1 #import "DDXMLPrivate.h"
2 #import "NSString+DDXML.h"
3
4
5 @implementation DDXMLElement
6
7 /**
8 * Returns a DDXML wrapper object for the given primitive node.
9 * The given node MUST be non-NULL and of the proper type.
10 **/
11 + (id)nodeWithElementPrimitive:(xmlNodePtr)node freeOnDealloc:(BOOL)flag
12 {
13 return [[[DDXMLElement alloc] initWithElementPrimitive:node freeOnDealloc:flag] autorelease];
14 }
15
16 - (id)initWithElementPrimitive:(xmlNodePtr)node freeOnDealloc:(BOOL)flag
17 {
18 self = [super initWithPrimitive:(xmlKindPtr)node freeOnDealloc:flag];
19 return self;
20 }
21
22 + (id)nodeWithPrimitive:(xmlKindPtr)kindPtr freeOnDealloc:(BOOL)flag
23 {
24 // Promote initializers which use proper parameter types to enable compiler to catch more mistakes
25 NSAssert(NO, @"Use nodeWithElementPrimitive:freeOnDealloc:");
26
27 return nil;
28 }
29
30 - (id)initWithPrimitive:(xmlKindPtr)kindPtr freeOnDealloc:(BOOL)flag
31 {
32 // Promote initializers which use proper parameter types to enable compiler to catch more mistakes.
33 NSAssert(NO, @"Use initWithElementPrimitive:freeOnDealloc:");
34
35 [self release];
36 return nil;
37 }
38
39 - (id)initWithName:(NSString *)name
40 {
41 // Note: Make every guarantee that genericPtr is not null
42
43 xmlNodePtr node = xmlNewNode(NULL, [name xmlChar]);
44 if (node == NULL)
45 {
46 [self release];
47 return nil;
48 }
49
50 return [self initWithElementPrimitive:node freeOnDealloc:YES];
51 }
52
53 - (id)initWithName:(NSString *)name URI:(NSString *)URI
54 {
55 // Note: Make every guarantee that genericPtr is not null
56
57 xmlNodePtr node = xmlNewNode(NULL, [name xmlChar]);
58 if (node == NULL)
59 {
60 [self release];
61 return nil;
62 }
63
64 DDXMLElement *result = [self initWithElementPrimitive:node freeOnDealloc:YES];
65 [result setURI:URI];
66
67 return result;
68 }
69
70 - (id)initWithName:(NSString *)name stringValue:(NSString *)string
71 {
72 // Note: Make every guarantee that genericPtr is not null
73
74 xmlNodePtr node = xmlNewNode(NULL, [name xmlChar]);
75 if (node == NULL)
76 {
77 [self release];
78 return nil;
79 }
80
81 DDXMLElement *result = [self initWithElementPrimitive:node freeOnDealloc:YES];
82 [result setStringValue:string];
83
84 return result;
85 }
86
87 - (id)initWithXMLString:(NSString *)string error:(NSError **)error
88 {
89 DDXMLDocument *doc = [[DDXMLDocument alloc] initWithXMLString:string options:0 error:error];
90 if (doc == nil)
91 {
92 [self release];
93 return nil;
94 }
95
96 DDXMLElement *result = [doc rootElement];
97 [result detach];
98 [doc release];
99
100 [self release];
101 return [result retain];
102 }
103
104 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
105 #pragma mark Elements by name
106 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
107
108 /**
109 * Returns the child element nodes (as DDXMLElement objects) of the receiver that have a specified name.
110 *
111 * If name is a qualified name, then this method invokes elementsForLocalName:URI: with the URI parameter set to
112 * the URI associated with the prefix. Otherwise comparison is based on string equality of the qualified or
113 * non-qualified name.
114 **/
115 - (NSArray *)elementsForName:(NSString *)name
116 {
117 if (name == nil) return [NSArray array];
118
119 // We need to check to see if name has a prefix.
120 // If it does have a prefix, we need to figure out what the corresponding URI is for that prefix,
121 // and then search for any elements that have the same name (including prefix) OR have the same URI.
122 // Otherwise we loop through the children as usual and do a string compare on the name
123
124 NSString *prefix = [[self class] prefixForName:name];
125 if ([prefix length] > 0)
126 {
127 xmlNodePtr node = (xmlNodePtr)genericPtr;
128 xmlNsPtr ns = xmlSearchNs(node->doc, node, [prefix xmlChar]);
129 if (ns != NULL)
130 {
131 NSString *uri = [NSString stringWithUTF8String:((const char *)ns->href)];
132 return [self elementsForName:name uri:uri];
133 }
134
135 // Note: We used xmlSearchNs instead of resolveNamespaceForName: because
136 // we want to avoid creating wrapper objects when possible.
137 }
138
139 return [self elementsForName:name uri:nil];
140 }
141
142 - (NSArray *)elementsForLocalName:(NSString *)localName URI:(NSString *)URI
143 {
144 if (localName == nil) return [NSArray array];
145
146 // We need to figure out what the prefix is for this URI.
147 // Then we search for elements that are named prefix:localName OR (named localName AND have the given URI).
148
149 NSString *prefix = [self resolvePrefixForNamespaceURI:URI];
150 if (prefix != nil)
151 {
152 NSString *name = [NSString stringWithFormat:@"%@:%@", prefix, localName];
153
154 return [self elementsForName:name uri:URI];
155 }
156 else
157 {
158 return [self elementsForName:localName uri:URI];
159 }
160 }
161
162 /**
163 * Helper method elementsForName and elementsForLocalName:URI: so work isn't duplicated.
164 * The name parameter is required, URI is optional.
165 **/
166 - (NSArray *)elementsForName:(NSString *)name uri:(NSString *)uri
167 {
168 // Supplied: name, !uri : match: name
169 // Supplied: p:name, uri : match: p:name || (name && uri)
170 // Supplied: name, uri : match: name && uri
171
172 NSMutableArray *result = [NSMutableArray array];
173
174 xmlNodePtr node = (xmlNodePtr)genericPtr;
175
176 BOOL hasPrefix = [[[self class] prefixForName:name] length] > 0;
177 NSString *localName = [[self class] localNameForName:name];
178
179 xmlNodePtr child = node->children;
180 while (child != NULL)
181 {
182 if (child->type == XML_ELEMENT_NODE)
183 {
184 BOOL match = NO;
185 if (uri == nil)
186 {
187 match = xmlStrEqual(child->name, [name xmlChar]);
188 }
189 else
190 {
191 BOOL nameMatch = xmlStrEqual(child->name, [name xmlChar]);
192 BOOL localNameMatch = xmlStrEqual(child->name, [localName xmlChar]);
193
194 BOOL uriMatch = NO;
195 if (child->ns != NULL)
196 {
197 uriMatch = xmlStrEqual(child->ns->href, [uri xmlChar]);
198 }
199
200 if (hasPrefix)
201 match = nameMatch || (localNameMatch && uriMatch);
202 else
203 match = nameMatch && uriMatch;
204 }
205
206 if (match)
207 {
208 [result addObject:[DDXMLElement nodeWithElementPrimitive:child freeOnDealloc:NO]];
209 }
210 }
211
212 child = child->next;
213 }
214
215 return result;
216 }
217
218 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
219 #pragma mark Attributes
220 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
221
222 - (BOOL)hasAttributeWithName:(NSString *)name
223 {
224 xmlAttrPtr attr = ((xmlNodePtr)genericPtr)->properties;
225 if (attr != NULL)
226 {
227 const xmlChar *xmlName = [name xmlChar];
228
229 do
230 {
231 if (xmlStrEqual(attr->name, xmlName))
232 {
233 return YES;
234 }
235 attr = attr->next;
236
237 } while (attr != NULL);
238 }
239
240 return NO;
241 }
242
243 - (void)addAttribute:(DDXMLNode *)attribute
244 {
245 // NSXML version uses this same assertion
246 DDXMLAssert([attribute hasParent] == NO, @"Cannot add an attribute with a parent; detach or copy first");
247 DDXMLAssert(IsXmlAttrPtr(attribute->genericPtr), @"Not an attribute");
248
249 [self removeAttributeForName:[attribute name]];
250
251 // xmlNodePtr xmlAddChild(xmlNodePtr parent, xmlNodePtr cur)
252 // Add a new node to @parent, at the end of the child (or property) list merging
253 // adjacent TEXT nodes (in which case @cur is freed). If the new node is ATTRIBUTE, it is added
254 // into properties instead of children. If there is an attribute with equal name, it is first destroyed.
255
256 xmlAddChild((xmlNodePtr)genericPtr, (xmlNodePtr)attribute->genericPtr);
257
258 // The attribute is now part of the xml tree heirarchy
259 attribute->freeOnDealloc = NO;
260 }
261
262 - (void)removeAttribute:(xmlAttrPtr)attr
263 {
264 [[self class] removeAttribute:attr fromNode:(xmlNodePtr)genericPtr];
265 }
266
267 - (void)removeAllAttributes
268 {
269 [[self class] removeAllAttributesFromNode:(xmlNodePtr)genericPtr];
270 }
271
272 - (void)removeAttributeForName:(NSString *)name
273 {
274 xmlAttrPtr attr = ((xmlNodePtr)genericPtr)->properties;
275 if (attr != NULL)
276 {
277 const xmlChar *xmlName = [name xmlChar];
278
279 do
280 {
281 if (xmlStrEqual(attr->name, xmlName))
282 {
283 [self removeAttribute:attr];
284 return;
285 }
286 attr = attr->next;
287
288 } while(attr != NULL);
289 }
290 }
291
292 - (NSArray *)attributes
293 {
294 NSMutableArray *result = [NSMutableArray array];
295
296 xmlAttrPtr attr = ((xmlNodePtr)genericPtr)->properties;
297 while (attr != NULL)
298 {
299 [result addObject:[DDXMLAttributeNode nodeWithAttrPrimitive:attr freeOnDealloc:NO]];
300
301 attr = attr->next;
302 }
303
304 return result;
305 }
306
307 - (DDXMLNode *)attributeForName:(NSString *)name
308 {
309 const xmlChar *attrName = [name xmlChar];
310
311 xmlAttrPtr attr = ((xmlNodePtr)genericPtr)->properties;
312 while (attr != NULL)
313 {
314 if (attr->ns && attr->ns->prefix)
315 {
316 // If the attribute name was originally something like "xml:quack",
317 // then attr->name is "quack" and attr->ns->prefix is "xml".
318 //
319 // So if the user is searching for "xml:quack" we need to take the prefix into account.
320 // Note that "xml:quack" is what would be printed if we output the attribute.
321
322 if (xmlStrQEqual(attr->ns->prefix, attr->name, attrName))
323 {
324 return [DDXMLAttributeNode nodeWithAttrPrimitive:attr freeOnDealloc:NO];
325 }
326 }
327 else
328 {
329 if (xmlStrEqual(attr->name, attrName))
330 {
331 return [DDXMLAttributeNode nodeWithAttrPrimitive:attr freeOnDealloc:NO];
332 }
333 }
334
335 attr = attr->next;
336 }
337 return nil;
338 }
339
340 /**
341 * Sets the list of attributes for the element.
342 * Any previously set attributes are removed.
343 **/
344 - (void)setAttributes:(NSArray *)attributes
345 {
346 [self removeAllAttributes];
347
348 NSUInteger i;
349 for (i = 0; i < [attributes count]; i++)
350 {
351 DDXMLNode *attribute = [attributes objectAtIndex:i];
352 [self addAttribute:attribute];
353
354 // Note: The addAttributes method properly sets the freeOnDealloc ivar.
355 }
356 }
357
358 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
359 #pragma mark Namespaces
360 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
361
362 - (void)addNamespace:(DDXMLNode *)namespace
363 {
364 // NSXML version uses this same assertion
365 DDXMLAssert([namespace hasParent] == NO, @"Cannot add a namespace with a parent; detach or copy first");
366 DDXMLAssert(IsXmlNsPtr(namespace->genericPtr), @"Not a namespace");
367
368 xmlNodePtr node = (xmlNodePtr)genericPtr;
369 xmlNsPtr ns = (xmlNsPtr)namespace->genericPtr;
370
371 // Beware: [namespace prefix] does NOT return what you might expect. Use [namespace name] instead.
372
373 NSString *namespaceName = [namespace name];
374
375 [self removeNamespaceForPrefix:namespaceName];
376
377 xmlNsPtr currentNs = node->nsDef;
378 if (currentNs == NULL)
379 {
380 node->nsDef = ns;
381 }
382 else
383 {
384 while (currentNs->next != NULL)
385 {
386 currentNs = currentNs->next;
387 }
388
389 currentNs->next = ns;
390 }
391
392 // The namespace is now part of the xml tree heirarchy
393 namespace->freeOnDealloc = NO;
394
395 if ([namespace isKindOfClass:[DDXMLNamespaceNode class]])
396 {
397 DDXMLNamespaceNode *ddNamespace = (DDXMLNamespaceNode *)namespace;
398
399 // The xmlNs structure doesn't contain a reference to the parent, so we manage our own reference
400 [ddNamespace setNsParentPtr:node];
401 }
402
403 // Did we just add a default namespace
404 if ([namespaceName isEqualToString:@""])
405 {
406 node->ns = ns;
407
408 // Note: The removeNamespaceForPrefix method above properly handled removing any previous default namespace
409 }
410 }
411
412 - (void)removeNamespace:(xmlNsPtr)ns
413 {
414 [[self class] removeNamespace:ns fromNode:(xmlNodePtr)genericPtr];
415 }
416
417 - (void)removeAllNamespaces
418 {
419 [[self class] removeAllNamespacesFromNode:(xmlNodePtr)genericPtr];
420 }
421
422 - (void)removeNamespaceForPrefix:(NSString *)name
423 {
424 // If name is nil or the empty string, the user is wishing to remove the default namespace
425 const xmlChar *xmlName = [name length] > 0 ? [name xmlChar] : NULL;
426
427 xmlNsPtr ns = ((xmlNodePtr)genericPtr)->nsDef;
428 while (ns != NULL)
429 {
430 if (xmlStrEqual(ns->prefix, xmlName))
431 {
432 [self removeNamespace:ns];
433 break;
434 }
435 ns = ns->next;
436 }
437
438 // Note: The removeNamespace method properly handles the situation where the namespace is the default namespace
439 }
440
441 - (NSArray *)namespaces
442 {
443 NSMutableArray *result = [NSMutableArray array];
444
445 xmlNsPtr ns = ((xmlNodePtr)genericPtr)->nsDef;
446 while (ns != NULL)
447 {
448 [result addObject:[DDXMLNamespaceNode nodeWithNsPrimitive:ns nsParent:(xmlNodePtr)genericPtr freeOnDealloc:NO]];
449
450 ns = ns->next;
451 }
452
453 return result;
454 }
455
456 - (DDXMLNode *)namespaceForPrefix:(NSString *)prefix
457 {
458 // If the prefix is nil or the empty string, the user is requesting the default namespace
459
460 if ([prefix length] == 0)
461 {
462 // Requesting the default namespace
463 xmlNsPtr ns = ((xmlNodePtr)genericPtr)->ns;
464 if (ns != NULL)
465 {
466 return [DDXMLNamespaceNode nodeWithNsPrimitive:ns nsParent:(xmlNodePtr)genericPtr freeOnDealloc:NO];
467 }
468 }
469 else
470 {
471 xmlNsPtr ns = ((xmlNodePtr)genericPtr)->nsDef;
472 while (ns != NULL)
473 {
474 if (xmlStrEqual(ns->prefix, [prefix xmlChar]))
475 {
476 return [DDXMLNamespaceNode nodeWithNsPrimitive:ns nsParent:(xmlNodePtr)genericPtr freeOnDealloc:NO];
477 }
478 ns = ns->next;
479 }
480 }
481
482 return nil;
483 }
484
485 - (void)setNamespaces:(NSArray *)namespaces
486 {
487 [self removeAllNamespaces];
488
489 NSUInteger i;
490 for (i = 0; i < [namespaces count]; i++)
491 {
492 DDXMLNode *namespace = [namespaces objectAtIndex:i];
493 [self addNamespace:namespace];
494
495 // Note: The addNamespace method properly sets the freeOnDealloc ivar.
496 }
497 }
498
499 /**
500 * Recursively searches the given node for the given namespace
501 **/
502 + (DDXMLNode *)resolveNamespaceForPrefix:(NSString *)prefix atNode:(xmlNodePtr)nodePtr
503 {
504 if (nodePtr == NULL) return nil;
505
506 xmlNsPtr ns = nodePtr->nsDef;
507 while (ns != NULL)
508 {
509 if (xmlStrEqual(ns->prefix, [prefix xmlChar]))
510 {
511 return [DDXMLNamespaceNode nodeWithNsPrimitive:ns nsParent:nodePtr freeOnDealloc:NO];
512 }
513 ns = ns->next;
514 }
515
516 return [self resolveNamespaceForPrefix:prefix atNode:nodePtr->parent];
517 }
518
519 /**
520 * Returns the namespace node with the prefix matching the given qualified name.
521 * Eg: You pass it "a:dog", it returns the namespace (defined in this node or parent nodes) that has the "a" prefix.
522 **/
523 - (DDXMLNode *)resolveNamespaceForName:(NSString *)name
524 {
525 // If the user passes nil or an empty string for name, they're looking for the default namespace.
526 if ([name length] == 0)
527 {
528 return [[self class] resolveNamespaceForPrefix:nil atNode:(xmlNodePtr)genericPtr];
529 }
530
531 NSString *prefix = [[self class] prefixForName:name];
532
533 if ([prefix length] > 0)
534 {
535 // Unfortunately we can't use xmlSearchNs because it returns an xmlNsPtr.
536 // This gives us mostly what we want, except we also need to know the nsParent.
537 // So we do the recursive search ourselves.
538
539 return [[self class] resolveNamespaceForPrefix:prefix atNode:(xmlNodePtr)genericPtr];
540 }
541
542 return nil;
543 }
544
545 /**
546 * Recursively searches the given node for a namespace with the given URI, and a set prefix.
547 **/
548 + (NSString *)resolvePrefixForURI:(NSString *)uri atNode:(xmlNodePtr)nodePtr
549 {
550 if (nodePtr == NULL) return nil;
551
552 xmlNsPtr ns = nodePtr->nsDef;
553 while (ns != NULL)
554 {
555 if (xmlStrEqual(ns->href, [uri xmlChar]))
556 {
557 if (ns->prefix != NULL)
558 {
559 return [NSString stringWithUTF8String:((const char *)ns->prefix)];
560 }
561 }
562 ns = ns->next;
563 }
564
565 return [self resolvePrefixForURI:uri atNode:nodePtr->parent];
566 }
567
568 /**
569 * Returns the prefix associated with the specified URI.
570 * Returns a string that is the matching prefix or nil if it finds no matching prefix.
571 **/
572 - (NSString *)resolvePrefixForNamespaceURI:(NSString *)namespaceURI
573 {
574 // We can't use xmlSearchNsByHref because it will return xmlNsPtr's with NULL prefixes.
575 // We're looking for a definitive prefix for the given URI.
576
577 return [[self class] resolvePrefixForURI:namespaceURI atNode:(xmlNodePtr)genericPtr];
578 }
579
580 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
581 #pragma mark Children
582 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
583
584 - (void)addChild:(DDXMLNode *)child
585 {
586 // NSXML version uses these same assertions
587 DDXMLAssert([child hasParent] == NO, @"Cannot add a child that has a parent; detach or copy first");
588 DDXMLAssert(IsXmlNodePtr(child->genericPtr),
589 @"Elements can only have text, elements, processing instructions, and comments as children");
590
591 xmlAddChild((xmlNodePtr)genericPtr, (xmlNodePtr)child->genericPtr);
592
593 // The node is now part of the xml tree heirarchy
594 child->freeOnDealloc = NO;
595 }
596
597 - (void)insertChild:(DDXMLNode *)child atIndex:(NSUInteger)index
598 {
599 // NSXML version uses these same assertions
600 DDXMLAssert([child hasParent] == NO, @"Cannot add a child that has a parent; detach or copy first");
601 DDXMLAssert(IsXmlNodePtr(child->genericPtr),
602 @"Elements can only have text, elements, processing instructions, and comments as children");
603
604 NSUInteger i = 0;
605
606 xmlNodePtr childNodePtr = ((xmlNodePtr)genericPtr)->children;
607 while (childNodePtr != NULL)
608 {
609 // Ignore all but element, comment, text, or processing instruction nodes
610 if (IsXmlNodePtr(childNodePtr))
611 {
612 if (i == index)
613 {
614 xmlAddPrevSibling(childNodePtr, (xmlNodePtr)child->genericPtr);
615 child->freeOnDealloc = NO;
616 return;
617 }
618
619 i++;
620 }
621 childNodePtr = childNodePtr->next;
622 }
623
624 if (i == index)
625 {
626 xmlAddChild((xmlNodePtr)genericPtr, (xmlNodePtr)child->genericPtr);
627 child->freeOnDealloc = NO;
628 return;
629 }
630
631 // NSXML version uses this same assertion
632 DDXMLAssert(NO, @"index (%u) beyond bounds (%u)", (unsigned)index, (unsigned)++i);
633 }
634
635 - (void)removeChildAtIndex:(NSUInteger)index
636 {
637 NSUInteger i = 0;
638
639 xmlNodePtr child = ((xmlNodePtr)genericPtr)->children;
640 while (child != NULL)
641 {
642 // Ignore all but element, comment, text, or processing instruction nodes
643 if (IsXmlNodePtr(child))
644 {
645 if (i == index)
646 {
647 [DDXMLNode removeChild:child fromNode:(xmlNodePtr)genericPtr];
648 return;
649 }
650
651 i++;
652 }
653 child = child->next;
654 }
655 }
656
657 - (void)setChildren:(NSArray *)children
658 {
659 [DDXMLNode removeAllChildrenFromNode:(xmlNodePtr)genericPtr];
660
661 NSUInteger i;
662 for (i = 0; i < [children count]; i++)
663 {
664 DDXMLNode *child = [children objectAtIndex:i];
665 [self addChild:child];
666
667 // Note: The addChild method properly sets the freeOnDealloc ivar.
668 }
669 }
670
671 @end

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