1/* 2 Copyright (C) 2000-2006 SKYRIX Software AG 3 Copyright (C) 2006 Helge Hess 4 5 This file is part of SOPE. 6 7 SOPE is free software; you can redistribute it and/or modify it under 8 the terms of the GNU Lesser General Public License as published by the 9 Free Software Foundation; either version 2, or (at your option) any 10 later version. 11 12 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY 13 WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 15 License for more details. 16 17 You should have received a copy of the GNU Lesser General Public 18 License along with SOPE; see the file COPYING. If not, write to the 19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 20 02111-1307, USA. 21*/ 22 23#include <DOM/DOMElement.h> 24#include <DOM/DOMNamedNodeMap.h> 25#include <DOM/DOMAttribute.h> 26#include <DOM/DOMDocument.h> 27#include <DOM/DOMNodeWalker.h> 28#include "DOMNode+QueryPath.h" 29#include "common.h" 30 31 32@interface _DOMElementAttrNamedNodeMap : NSObject < DOMNamedNodeMap > 33{ 34 NGDOMElement *element; /* non-retained */ 35} 36 37- (id)initWithElement:(id)_element; 38 39- (id)objectEnumerator; 40 41- (void)invalidate; 42 43@end /* _DOMElementAttrNamedNodeMap */ 44 45@interface NGDOMElement(Privates) 46- (NSUInteger)_numberOfAttributes; 47- (id)_attributeNodeAtIndex:(NSUInteger)_idx; 48- (id)attributeNode:(NSString *)_localName; 49- (id)attributeNode:(NSString *)_localName namespaceURI:(NSString *)_ns; 50@end 51 52static NSNull *null = nil; 53 54@implementation NGDOMElement 55 56- (id)initWithTagName:(NSString *)_tagName namespaceURI:(NSString *)_uri { 57 if (null == nil) 58 null = [[NSNull null] retain]; 59 60 if ((self = [super init])) { 61 self->tagName = [_tagName copy]; 62 self->namespaceURI = [_uri copy]; 63 } 64 return self; 65} 66- (id)initWithTagName:(NSString *)_tagName { 67 return [self initWithTagName:_tagName namespaceURI:nil]; 68} 69 70- (void)dealloc { 71 [self->attributes makeObjectsPerformSelector: 72 @selector(_domNodeForgetParentNode:) 73 withObject:self]; 74 75 [self->attrNodeMap invalidate]; 76 [self->attrNodeMap release]; 77 [self->keyToAttribute release]; 78 [self->attributes release]; 79 [self->tagName release]; 80 [self->namespaceURI release]; 81 [self->prefix release]; 82 [super dealloc]; 83} 84 85/* attributes */ 86 87- (NSString *)tagName { 88 return self->tagName; 89} 90- (NSString *)localName { 91 return self->tagName; 92} 93 94- (void)setPrefix:(NSString *)_prefix { 95 id old = self->prefix; 96 self->prefix = [_prefix copy]; 97 [old release]; 98} 99- (NSString *)prefix { 100 return self->prefix; 101} 102 103- (NSString *)namespaceURI { 104 return self->namespaceURI; 105} 106 107- (void)setLine:(NSInteger)_line { 108 self->line = _line; 109} 110- (NSUInteger)line { 111 return self->line; 112} 113 114/* lookup */ 115 116- (void)_walk_getElementsByTagName:(id)_walker { 117 id node; 118 119 node = [_walker currentNode]; 120 if ([node nodeType] != DOM_ELEMENT_NODE) 121 return; 122 123 if (![[node tagName] isEqualToString: 124 [(NSArray *)[_walker context] objectAtIndex:0]]) 125 /* tagname doesn't match */ 126 return; 127 128 [[(NSArray *)[_walker context] objectAtIndex:1] addObject:node]; 129} 130- (void)_walk_getElementsByTagNameAddAll:(id)_walker { 131 id node; 132 133 node = [_walker currentNode]; 134 if ([node nodeType] != DOM_ELEMENT_NODE) 135 return; 136 137 [(NSMutableArray *)[_walker context] addObject:node]; 138} 139- (id<NSObject,DOMNodeList>)getElementsByTagName:(NSString *)_tagName { 140 /* introduced in DOM2, should return a *live* list ! */ 141 NGDOMNodePreorderWalker *walker; 142 NSMutableArray *array; 143 SEL sel; 144 id ctx; 145 146 if (![self hasChildNodes]) 147 return nil; 148 149 if (_tagName == nil) 150 return nil; 151 152 array = [NSMutableArray arrayWithCapacity:4]; 153 154 if ([_tagName isEqualToString:@"*"]) { 155 _tagName = nil; 156 ctx = array; 157 sel = @selector(_walk_getElementsByTagNameAddAll:); 158 } 159 else { 160 ctx = [NSArray arrayWithObjects:_tagName, array, nil]; 161 sel = @selector(_walk_getElementsByTagName:); 162 } 163 164 walker = [[NGDOMNodePreorderWalker alloc] 165 initWithTarget:self selector:sel context:ctx]; 166 167 [walker walkNode:self]; 168 169 [walker release]; walker = nil; 170 return [[array copy] autorelease]; 171} 172- (id<NSObject,DOMNodeList>)getElementsByTagName:(NSString *)_tagName 173 namespaceURI:(NSString *)_uri 174{ 175 // TODO: implement 176 [self doesNotRecognizeSelector:_cmd]; 177 return nil; 178} 179 180/* element attributes */ 181 182- (void)_ensureAttrs { 183 if (self->attributes == nil) 184 self->attributes = [[NSMutableArray alloc] init]; 185 if (self->keyToAttribute == nil) 186 self->keyToAttribute = [[NSMutableDictionary alloc] init]; 187} 188 189- (void)_attributeSetChanged { 190} 191 192- (NSUInteger)_numberOfAttributes { 193 return [self->attributes count]; 194} 195- (id)_attributeNodeAtIndex:(NSUInteger)_idx { 196 if (_idx >= [self->attributes count]) 197 return nil; 198 return [self->attributes objectAtIndex:_idx]; 199} 200 201- (id)_keyForAttribute:(id<DOMAttr>)_attrNode { 202 return [_attrNode name]; 203} 204- (id)_nskeyForLocalName:(NSString *)attrName namespaceURI:(NSString *)nsURI { 205 id key; 206 207 if (attrName == nil) 208 return nil; 209 210 if (nsURI) { 211 id objs[2]; 212 213 objs[0] = attrName; 214 objs[1] = nsURI; 215 key = [NSArray arrayWithObjects:objs count:2]; 216 } 217 else 218 key = attrName; 219 220 return key; 221} 222- (id)_nskeyForAttribute:(id<DOMAttr>)_attrNode { 223 NSString *attrName; 224 225 if ((attrName = [_attrNode name]) == nil) { 226 NSLog(@"WARNING: attribute %@ has no valid attribute name !", _attrNode); 227 return nil; 228 } 229 230 return [self _nskeyForLocalName:attrName 231 namespaceURI:[_attrNode namespaceURI]]; 232} 233 234- (BOOL)hasAttribute:(NSString *)_attrName { 235 return [self hasAttribute:_attrName namespaceURI:[self namespaceURI]]; 236} 237 238- (void)setAttribute:(NSString *)_attrName value:(NSString *)_value { 239 [self setAttribute:_attrName namespaceURI:[self namespaceURI] value:_value]; 240 241#if 0 // ms: ?? 242 id node; 243 244 NSAssert1(_attrName, @"invalid attribute name '%@'", _attrName); 245 246 if ((node = [self->keyToAttribute objectForKey:_attrName]) == nil) { 247 /* create new node */ 248 node = [[self ownerDocument] createAttribute:_attrName]; 249 } 250 NSAssert(node, @"couldn't find/create node for attribute"); 251 252 node = [self setAttributeNode:node]; 253 254 [node setValue:_value]; 255#endif 256} 257- (id)attributeNode:(NSString *)_attrName { 258 return [self attributeNode:_attrName namespaceURI:[self namespaceURI]]; 259} 260- (NSString *)attribute:(NSString *)_attrName { 261 return [[self attributeNode:_attrName] value]; 262} 263 264- (BOOL)hasAttribute:(NSString *)_localName namespaceURI:(NSString *)_ns { 265 id objs[2]; 266 id key; 267 268 if ([_ns isEqualToString:@"*"]) { 269 /* match any namespace */ 270 NSEnumerator *e; 271 id attr; 272 273 if ((attr = [self->keyToAttribute objectForKey:_localName])) 274 return YES; 275 276 e = [self->keyToAttribute keyEnumerator]; 277 while ((key = [e nextObject])) { 278 if ([key isKindOfClass:[NSArray class]]) { 279 if ([[key objectAtIndex:0] isEqualToString:_localName]) 280 return YES; 281 } 282 } 283 return NO; 284 } 285 286 objs[0] = _localName; 287 objs[1] = _ns ? _ns : (NSString *)null; 288 key = [NSArray arrayWithObjects:objs count:2]; 289 290 return [self->keyToAttribute objectForKey:key] ? YES : NO; 291} 292 293- (void)setAttribute:(NSString *)_localName namespaceURI:(NSString *)_ns 294 value:(NSString *)_value 295{ 296 id key; 297 id node; 298 299 key = [self _nskeyForLocalName:_localName namespaceURI:_ns]; 300 NSAssert2(key, @"invalid (ns-)attribute name localName='%@', uri='%@'", 301 _localName, _ns); 302 303 if ((node = [self->keyToAttribute objectForKey:key]) == nil) { 304 /* create new node */ 305 node = [[self ownerDocument] createAttribute:_localName namespaceURI:_ns]; 306 } 307 NSAssert(node, @"couldn't find/create node for attribute"); 308 309 node = [self setAttributeNodeNS:node]; 310 311 [node setValue:_value]; 312} 313- (id)attributeNode:(NSString *)_localName namespaceURI:(NSString *)_ns { 314 id objs[2]; 315 id key; 316 317 if ([_ns isEqualToString:@"*"]) { 318 /* match any namespace */ 319 NSEnumerator *e; 320 id attr; 321 322 if ((attr = [self->keyToAttribute objectForKey:_localName])) 323 return attr; 324 325 e = [self->keyToAttribute keyEnumerator]; 326 while ((key = [e nextObject])) { 327 if ([key isKindOfClass:[NSArray class]]) { 328 if ([[key objectAtIndex:0] isEqualToString:_localName]) 329 return [self->keyToAttribute objectForKey:key]; 330 } 331 } 332 return nil; 333 } 334 335 objs[0] = _localName; 336 objs[1] = _ns ? _ns : (NSString *)null; 337 key = [NSArray arrayWithObjects:objs count:2]; 338 339 return [self->keyToAttribute objectForKey:key]; 340} 341- (NSString *)attribute:(NSString *)_localName namespaceURI:(NSString *)_ns { 342 return [[self attributeNode:_localName namespaceURI:_ns] value]; 343} 344 345- (id<NSObject, DOMAttr>)setAttributeNodeNS:(id<NSObject, DOMAttr>)_attrNode { 346 id key, oldNode; 347 348 if (_attrNode == nil) 349 /* invalid node parameters */ 350 return nil; 351 352 if ((key = [self _nskeyForAttribute:_attrNode]) == nil) 353 /* couldn't get key */ 354 return nil; 355 356 [self _ensureAttrs]; 357 358 /* check if the key is already added */ 359 360 if ((oldNode = [self->keyToAttribute objectForKey:key])) { 361 if (oldNode == _attrNode) { 362 /* already contained */ 363 // NSLog(@"node is already set !"); 364 return _attrNode; 365 } 366 367 /* replace existing node */ 368 [self->attributes replaceObjectAtIndex: 369 [self->attributes indexOfObject:oldNode] 370 withObject:_attrNode]; 371 [self->keyToAttribute setObject:_attrNode forKey:key]; 372 373 [(id)_attrNode _domNodeRegisterParentNode:self]; 374 [self _attributeSetChanged]; 375 376 return _attrNode; 377 } 378 else { 379 /* add node */ 380 381 NSAssert(self->keyToAttribute, @"missing keyToAttribute"); 382 NSAssert(self->attributes, @"missing attrs"); 383 384 [self->keyToAttribute setObject:_attrNode forKey:key]; 385 [self->attributes addObject:_attrNode]; 386 387 [(id)_attrNode _domNodeRegisterParentNode:self]; 388 [self _attributeSetChanged]; 389 390 // NSLog(@"added attr %@, elem %@", _attrNode, self); 391 392 return _attrNode; 393 } 394} 395 396- (void)removeAttribute:(NSString *)_attr namespaceURI:(NSString *)_uri { 397 id node; 398 id key; 399 400 key = [self _nskeyForLocalName:_attr namespaceURI:_uri]; 401 NSAssert2(key, @"invalid (ns-)attribute name '%@', '%@'", _attr, _uri); 402 403 node = [self->keyToAttribute objectForKey:key]; 404 405 [self removeAttributeNodeNS:node]; 406} 407- (id<NSObject,DOMAttr>)removeAttributeNodeNS:(id<NSObject,DOMAttr>)_attrNode { 408 id key, oldNode; 409 410 if (_attrNode == nil) 411 /* invalid node parameters */ 412 return nil; 413 414 if (self->attributes == nil) 415 /* no attributes are set up */ 416 return nil; 417 418 if ((key = [self _nskeyForAttribute:_attrNode]) == nil) 419 /* couldn't get key for node */ 420 return nil; 421 422 if ((oldNode = [self->keyToAttribute objectForKey:key])) { 423 /* the node's key exists */ 424 if (oldNode != _attrNode) { 425 /* the node has the same key, but isn't the same */ 426 return nil; 427 } 428 429 /* ok, found the node, let's remove ! */ 430 [[_attrNode retain] autorelease]; 431 [self->keyToAttribute removeObjectForKey:key]; 432 [self->attributes removeObjectIdenticalTo:_attrNode]; 433 434 [(id)_attrNode _domNodeForgetParentNode:self]; 435 [self _attributeSetChanged]; 436 437 return _attrNode; 438 } 439 else 440 /* no such attribute is stored */ 441 return nil; 442} 443 444- (id<NSObject,DOMAttr>)setAttributeNode:(id<NSObject,DOMAttr>)_attrNode { 445 [self doesNotRecognizeSelector:_cmd]; 446 return nil; 447} 448- (id<NSObject,DOMAttr>)removeAttributeNode:(id<NSObject,DOMAttr>)_attrNode { 449 [self doesNotRecognizeSelector:_cmd]; 450 return nil; 451} 452- (void)removeAttribute:(NSString *)_attr { 453 id node; 454 455 NSAssert1(_attr, @"invalid attribute name '%@'", _attr); 456 457 node = [self->keyToAttribute objectForKey:_attr]; 458 459 [self removeAttributeNode:node]; 460} 461 462/* node */ 463 464- (BOOL)_isValidChildNode:(id)_node { 465 switch ([_node nodeType]) { 466 case DOM_ELEMENT_NODE: 467 case DOM_TEXT_NODE: 468 case DOM_COMMENT_NODE: 469 case DOM_PROCESSING_INSTRUCTION_NODE: 470 case DOM_CDATA_SECTION_NODE: 471 case DOM_ENTITY_REFERENCE_NODE: 472 return YES; 473 474 default: 475 return NO; 476 } 477} 478 479- (DOMNodeType)nodeType { 480 return DOM_ELEMENT_NODE; 481} 482 483- (id<NSObject,DOMNamedNodeMap>)attributes { 484 /* returns a named-node-map */ 485 if (self->attrNodeMap == nil) { 486 self->attrNodeMap = 487 [[_DOMElementAttrNamedNodeMap alloc] initWithElement:self]; 488 } 489 return self->attrNodeMap; 490} 491 492/* parent node */ 493 494- (void)_domNodeRegisterParentNode:(id)_parent { 495 self->parent = _parent; 496} 497- (void)_domNodeForgetParentNode:(id)_parent { 498 if (_parent == self->parent) 499 /* the node's parent was deallocated */ 500 self->parent = nil; 501} 502- (id<NSObject,DOMNode>)parentNode { 503 return self->parent; 504} 505 506/* description */ 507 508- (NSString *)description { 509 return [NSString stringWithFormat: 510 @"<0x%p[%@]: name=%@ parent=%@ #attrs=%"PRIuPTR" #children=%"PRIuPTR">", 511 self, NSStringFromClass([self class]), 512 [self nodeName], 513 [[self parentNode] nodeName], 514 [self _numberOfAttributes], 515 [self hasChildNodes] ? [[self childNodes] length] : 0]; 516} 517 518/* QPValues */ 519 520- (NSException *)setQueryPathValue:(id)_value { 521 return [NSException exceptionWithName:@"QueryPathEvalException" 522 reason:@"cannot set query-path value on DOMElement !" 523 userInfo:nil]; 524} 525- (id)queryPathValue { 526 return [self childNodes]; 527} 528 529/* key/value coding */ 530 531- (id)valueForKey:(NSString *)_key { 532 if ([_key hasPrefix:@"/"]) 533 return [self lookupQueryPath:[_key substringFromIndex:1]]; 534 535 if ([_key hasPrefix:@"@"]) { 536 return [[self attributes] namedItem:[_key substringFromIndex:1] 537 namespaceURI:@"*"]; 538 } 539 540 return [super valueForKey:_key]; 541} 542 543@end /* NGDOMElement */ 544 545 546 547@implementation _DOMElementAttrNamedNodeMap 548 549- (id)initWithElement:(id)_element { 550 self->element = _element; 551 return self; 552} 553 554- (void)invalidate { 555 self->element = nil; 556} 557 558static inline void _checkValid(_DOMElementAttrNamedNodeMap *self) { 559 if (self->element == nil) { 560 NSCAssert(self->element, 561 @"named node map is invalid (element was deallocated) !"); 562 } 563} 564 565/* access */ 566 567static NSString *_XNSUri(NSString *_name) { 568 NSRange r1; 569 570 if (![_name hasPrefix:@"{"]) 571 return nil; 572 573 r1 = [_name rangeOfString:@"}"]; 574 if (r1.length == 0) 575 return nil; 576 577 r1.length = (r1.location - 2); 578 r1.location = 1; 579 return [_name substringWithRange:r1]; 580} 581static NSString *_XNSLocalName(NSString *_name) { 582 NSRange r; 583 584 r = [_name rangeOfString:@"}"]; 585 return r.length == 0 586 ? _name 587 : [_name substringFromIndex:(r.location + r.length)]; 588} 589 590- (NSUInteger)length { 591 _checkValid(self); 592 return [self->element _numberOfAttributes]; 593} 594- (id)objectAtIndex:(NSUInteger)_idx { 595 _checkValid(self); 596 return [self->element _attributeNodeAtIndex:_idx]; 597} 598 599- (IDOMNode)namedItem:(NSString *)_name { 600 NSString *nsuri; 601 _checkValid(self); 602 603 if ((nsuri = _XNSUri(_name))) 604 return [self namedItem:_XNSLocalName(_name) namespaceURI:nsuri]; 605 606 return [self->element attributeNode:_name]; 607} 608- (IDOMNode)setNamedItem:(IDOMNode)_node { 609 _checkValid(self); 610 611 // TODO: is the cast correct? 612 return [self->element setAttributeNode:(id<NSObject,DOMAttr>)_node]; 613} 614- (IDOMNode)removeNamedItem:(NSString *)_name { 615 NSString *nsuri; 616 id node; 617 618 _checkValid(self); 619 if ((nsuri = _XNSUri(_name))) 620 return [self removeNamedItem:_XNSLocalName(_name) namespaceURI:nsuri]; 621 622 if ((node = [self->element attributeNode:_name])) { 623 node = [node retain]; 624 [self->element removeAttribute:_name]; 625 return [node autorelease]; 626 } 627 else 628 return nil; 629} 630 631/* DOM2 access */ 632 633- (IDOMNode)namedItem:(NSString *)_name namespaceURI:(NSString *)_uri { 634 return [self->element attributeNode:_name namespaceURI:_uri]; 635} 636- (IDOMNode)setNamedItemNS:(IDOMNode)_node { 637 _checkValid(self); 638 // TODO: is the cast correct? 639 return [self->element setAttributeNodeNS:(id<NSObject,DOMAttr>)_node]; 640} 641- (IDOMNode)removeNamedItem:(NSString *)_name namespaceURI:(NSString *)_uri { 642 id node; 643 644 _checkValid(self); 645 if ((node = [self->element attributeNode:_name namespaceURI:_uri])) { 646 node = [node retain]; 647 [self->element removeAttribute:_name namespaceURI:_uri]; 648 return [node autorelease]; 649 } 650 else 651 return nil; 652} 653 654/* mimic NSArray */ 655 656- (NSUInteger)count { 657 _checkValid(self); 658 return [self->element _numberOfAttributes]; 659} 660 661- (id)objectEnumerator { 662 NSMutableArray *ma; 663 unsigned i, count; 664 665 _checkValid(self); 666 if ((count = [self->element _numberOfAttributes]) == 0) 667 return nil; 668 669 ma = [NSMutableArray arrayWithCapacity:count]; 670 671 for (i = 0; i < count; i++) 672 [ma addObject:[self->element _attributeNodeAtIndex:i]]; 673 674 return [ma objectEnumerator]; 675} 676 677/* mimic NSDictionary */ 678 679- (void)setObject:(id)_value forKey:(id)_key { 680 _checkValid(self); 681 [self takeValue:_value forKey:[_key stringValue]]; 682} 683- (id)objectForKey:(id)_key { 684 _checkValid(self); 685 return [self valueForKey:[_key stringValue]]; 686} 687 688/* KVC */ 689 690- (void)takeValue:(id)_value forKey:(NSString *)_key { 691 id node; 692 _checkValid(self); 693 694 if ((node = [self->element attributeNode:_key namespaceURI:@"*"])) { 695 [node setValue:[_value stringValue]]; 696 } 697 else { 698 [self->element setAttribute:_key namespaceURI:@"xhtml" 699 value:[_value stringValue]]; 700 } 701} 702- (id)valueForKey:(NSString *)_key { 703 id v; 704 _checkValid(self); 705 706 if ((v = [self namedItem:_key])) 707 return [v value]; 708 if ((v = [self namedItem:_key namespaceURI:@"*"])) 709 return [v value]; 710 711 return nil; 712} 713 714/* JSSupport */ 715 716- (id)_jsprop_length { 717 return [NSNumber numberWithInt:[self length]]; 718} 719 720- (id)_jsfunc_item:(NSArray *)_args { 721 unsigned count; 722 723 if ((count = [_args count]) == 0) return nil; 724 return [self objectAtIndex:[[_args objectAtIndex:0] intValue]]; 725} 726 727- (id)_jsfunc_getNamedItem:(NSArray *)_args { 728 unsigned count; 729 730 if ((count = [_args count]) == 0) return nil; 731 return [self namedItem:[[_args objectAtIndex:0] stringValue]]; 732} 733- (id)_jsfunc_getNamedItemNS:(NSArray *)_args { 734 unsigned count; 735 736 if ((count = [_args count]) == 0) return nil; 737 if (count == 1) 738 return [self namedItem:[[_args objectAtIndex:0] stringValue]]; 739 else { 740 return [self namedItem:[[_args objectAtIndex:1] stringValue] 741 namespaceURI:[[_args objectAtIndex:0] stringValue]]; 742 } 743} 744 745- (id)_jsfunc_setNamedItem:(NSArray *)_args { 746 unsigned i, count; 747 id last = nil; 748 749 for (i = 0, count = [_args count]; i < count; i++) 750 last = [self setNamedItem:[_args objectAtIndex:i]]; 751 return last; 752} 753- (id)_jsfunc_setNamedItemNS:(NSArray *)_args { 754 unsigned i, count; 755 id last = nil; 756 757 for (i = 0, count = [_args count]; i < count; i++) 758 last = [self setNamedItemNS:[_args objectAtIndex:i]]; 759 return last; 760} 761 762- (id)_jsfunc_removeNamedItem:(NSArray *)_args { 763 unsigned count; 764 765 if ((count = [_args count]) == 0) return nil; 766 return [self namedItem:[[_args objectAtIndex:0] stringValue]]; 767} 768- (id)_jsfunc_removeNamedItemNS:(NSArray *)_args { 769 unsigned count; 770 771 if ((count = [_args count]) == 0) return nil; 772 if (count == 1) 773 return [self removeNamedItem:[[_args objectAtIndex:0] stringValue]]; 774 else { 775 return [self removeNamedItem:[[_args objectAtIndex:1] stringValue] 776 namespaceURI:[[_args objectAtIndex:0] stringValue]]; 777 } 778} 779 780/* description */ 781 782- (NSString *)description { 783 NSMutableString *ms; 784 NSEnumerator *e; 785 id attr; 786 787 ms = [NSMutableString stringWithCapacity:1024]; 788 [ms appendFormat:@"<0x%p[%@]:", self, NSStringFromClass([self class])]; 789 [ms appendFormat:@" element=%@", self->element]; 790 791 [ms appendString:@" attributes:\n"]; 792 e = [self objectEnumerator]; 793 while ((attr = [e nextObject]) != nil) { 794 [ms appendString:[attr description]]; 795 [ms appendString:@"\n"]; 796 } 797 798 [ms appendString:@">"]; 799 return ms; 800} 801 802@end /* _DOMElementAttrNamedNodeMap */ 803