| 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 |