3 @interface XMLTree (PrivateAPI)
\r
6 @end // End Private API
\r
10 @implementation XMLTree
\r
14 +(XMLTree *)treeWithURL:(NSURL *)url
\r
16 return [[[XMLTree alloc] initWithURL:url] autorelease];
\r
17 } // end treeWithURL
\r
22 +(XMLTree *)treeWithCFXMLTreeRef:(CFXMLTreeRef)ref
\r
24 return [[[XMLTree alloc] initWithCFXMLTreeRef:ref] autorelease];
\r
25 } // end treeWithCFXMLTreeRef
\r
28 +(XMLTree *)treeWithData:(NSData *)data
\r
30 return [[[XMLTree alloc] initWithData:data] autorelease];
\r
31 } // end treeWithData
\r
38 if( (self = [super init]) == nil )
\r
50 -(XMLTree *)initWithCFXMLTreeRef:(CFXMLTreeRef)ref
\r
52 if( [self init] == nil )
\r
61 // Make sure ref is something
\r
64 _node = CFXMLTreeGetNode( _tree );
\r
68 } // end if: valid ref
\r
72 } // end else: no good
\r
75 } // end initWithCFXMLTreeRef:
\r
82 -(XMLTree *)initWithURL:(NSURL *)url
\r
89 tree = CFXMLTreeCreateWithDataFromURL(
\r
90 kCFAllocatorDefault,
\r
92 kCFXMLParserSkipWhitespace,
\r
94 // Defer to the ...with tree... init
\r
95 XMLTree *new = [self initWithCFXMLTreeRef:tree];
\r
96 if( tree != NULL ) //because it is retained by us after creating it with
\r
97 CFRelease(tree); //CFXMLTreeCreateWithDataFromURL() and we're finished with it -TWB
\r
100 } // end initWithURL
\r
104 - (XMLTree *)initWithData:(NSData *)inData
\r
106 return [self initWithData:inData withResolvingURL:nil];
\r
107 } // end initWithData
\r
111 - (XMLTree *)initWithData:(NSData *)inData withResolvingURL:(NSURL *)url
\r
119 tree = CFXMLTreeCreateFromData(
\r
120 kCFAllocatorDefault,
\r
123 kCFXMLParserSkipWhitespace,
\r
126 // Defer to the ...with tree... init
\r
127 XMLTree *new = [self initWithCFXMLTreeRef:tree];
\r
128 if( tree != NULL ) //because it is retained by us after creating it with
\r
129 CFRelease(tree); //CFXMLTreeCreateFromData() and we're finished with it - TWB
\r
132 } // end initWithData:withResolvingURL:
\r
143 //NSLog( @"dealloc %@", self );
\r
145 if( _tree != NULL )
\r
146 CFRelease( _tree );
\r
148 if( _node != NULL )
\r
149 CFRelease( _node );
\r
153 [super dealloc]; //added TWB
\r
161 /* ******** A B O U T P A R E N T ******** */
\r
168 XMLTree *returnVal;
\r
170 if( _tree == NULL )
\r
173 parent = CFTreeGetParent( _tree );
\r
177 CFRetain( parent );
\r
178 returnVal = [XMLTree treeWithCFXMLTreeRef:parent];
\r
179 CFRelease( parent );
\r
181 } // end if: got parent
\r
190 /* ******** A B O U T C H I L D R E N ******** */
\r
195 -(id)xpath:(NSString *)xpath
\r
197 CFTypeRef resultRef;
\r
201 NSLog(@"The path function is still being developed. It doesn't work yet. Sorry.");
\r
207 resultRef = XMLTreeXPath( (CFMutableStringRef)[xpath mutableCopy], _tree );
\r
210 if( resultRef != NULL ){
\r
212 // What kind of object was returned?
\r
213 resultID = CFGetTypeID( resultRef );
\r
216 if( resultID == CFStringGetTypeID() ){
\r
218 result = [NSString stringWithString:(NSString *)resultRef];
\r
219 } // end if: string
\r
222 else if( resultID == CFTreeGetTypeID() ){
\r
224 result = [XMLTree treeWithCFXMLTreeRef:(CFXMLTreeRef)resultRef];
\r
225 } // end else: tree
\r
229 NSLog(@"Error. Expected tree or string. Contact developer (rob@iharder.net)");
\r
232 // Release result ref (part of contract with XMLTreeXPath function)
\r
233 CFRelease( resultRef );
\r
235 } // end if: not null
\r
249 if( _tree == NULL )
\r
252 return CFTreeGetChildCount( _tree );
\r
259 -(XMLTree *)childAtIndex:(int)index
\r
261 CFXMLTreeRef child;
\r
263 if( _tree == NULL )
\r
266 if( index >= CFTreeGetChildCount( _tree ) )
\r
269 child = CFTreeGetChildAtIndex(_tree, index);
\r
270 // Don't need to retain or release child. I think.
\r
271 // In keeping with your policy on interpreting "No assumptions can be made about how long the reference is valid"
\r
272 // in the CF docs, we should retain then release it, but it works without, so let's not :) TWB
\r
274 return [XMLTree treeWithCFXMLTreeRef:child];
\r
275 } // end childAtIndex:
\r
283 -(XMLTree *)childNamed:(NSString *)name
\r
285 CFXMLTreeRef childTree;
\r
286 CFXMLNodeRef childNode;
\r
287 CFStringRef childName;
\r
288 XMLTree *returnVal;
\r
292 if( _tree == NULL )
\r
295 childCount = CFTreeGetChildCount( _tree );
\r
298 for( i = 0; i < childCount; i++ ){
\r
300 childTree = CFTreeGetChildAtIndex(_tree, i );
\r
301 CFRetain( childTree );
\r
303 childNode = CFXMLTreeGetNode( childTree );
\r
304 CFRetain( childNode );
\r
306 childName = CFXMLNodeGetString( childNode );
\r
307 CFRetain( childName );
\r
309 //NSLog(@"checking child name: %@", childName );
\r
310 if( CFStringCompare( (CFStringRef)name, childName, NULL ) == kCFCompareEqualTo )
\r
311 returnVal = [XMLTree treeWithCFXMLTreeRef:childTree];
\r
313 CFRelease( childTree );
\r
314 CFRelease( childNode );
\r
315 CFRelease( childName );
\r
320 } // end for: each child
\r
323 } // end childNamed:
\r
329 -(XMLTree *)childNamed:(NSString *)name
\r
330 withAttribute:(NSString *)attrName
\r
331 equalTo:(NSString *)attrVal
\r
333 CFXMLTreeRef childTree;
\r
334 CFXMLNodeRef childNode;
\r
335 CFStringRef childName;
\r
336 CFStringRef childAttrVal;
\r
337 CFXMLElementInfo eInfo;
\r
338 XMLTree *returnVal;
\r
342 if( _tree == NULL )
\r
345 childCount = CFTreeGetChildCount( _tree );
\r
348 for( i = 0; i < childCount; i++ ){
\r
350 childTree = CFTreeGetChildAtIndex(_tree, i );
\r
351 CFRetain( childTree );
\r
353 childNode = CFXMLTreeGetNode( childTree );
\r
354 CFRetain( childNode );
\r
356 childName = CFXMLNodeGetString( childNode );
\r
357 CFRetain( childName );
\r
360 if( CFStringCompare( (CFStringRef)name, childName, NULL ) == kCFCompareEqualTo ){
\r
363 if( CFXMLNodeGetTypeCode( childNode ) == kCFXMLNodeTypeElement ){
\r
365 eInfo = *(CFXMLElementInfo *)CFXMLNodeGetInfoPtr(childNode);
\r
366 childAttrVal = (CFStringRef)CFDictionaryGetValue(
\r
369 CFRetain( childAttrVal );
\r
371 // Attribute matches
\r
372 if( CFStringCompare(
\r
373 (CFStringRef)attrVal,
\r
375 NULL ) == kCFCompareEqualTo ){
\r
377 returnVal = [XMLTree treeWithCFXMLTreeRef:childTree];
\r
380 CFRelease( childAttrVal );
\r
382 } // end if: has attributes
\r
383 } // end if: name matches
\r
385 CFRelease( childTree );
\r
386 CFRelease( childNode );
\r
387 CFRelease( childName );
\r
392 } // end for: each child
\r
395 } // end childNamed:
\r
404 -(XMLTree *)descendentNamed:(NSString *)name
\r
406 CFXMLTreeRef descTree;
\r
407 XMLTree *returnVal;
\r
409 if( _tree == NULL )
\r
412 descTree = XMLTreeDescendentNamed( (CFStringRef)name, _tree );
\r
414 if( descTree == NULL )
\r
417 // descTree will have a +1 retain count that we
\r
418 // are responsible for releasing (see comments on that function).
\r
419 returnVal = [XMLTree treeWithCFXMLTreeRef:descTree];
\r
420 CFRelease( descTree );
\r
423 } // end descendentNamed:
\r
427 /* ******** A B O U T S E L F ******** */
\r
434 if( _node == NULL )
\r
437 return [NSString stringWithString:(NSString *)CFXMLNodeGetString(_node)];
\r
446 Returns the node type, as defined by Apple's XML parser.
\r
447 The values will be one of the following constants:
\r
449 enum CFXMLNodeTypeCode {
\r
450 kCFXMLNodeTypeDocument = 1,
\r
451 kCFXMLNodeTypeElement = 2,
\r
452 kCFXMLNodeTypeAttribute = 3,
\r
453 kCFXMLNodeTypeProcessingInstruction = 4,
\r
454 kCFXMLNodeTypeComment = 5,
\r
455 kCFXMLNodeTypeText = 6,
\r
456 kCFXMLNodeTypeCDATASection = 7,
\r
457 kCFXMLNodeTypeDocumentFragment = 8,
\r
458 kCFXMLNodeTypeEntity = 9,
\r
459 kCFXMLNodeTypeEntityReference = 10,
\r
460 kCFXMLNodeTypeDocumentType = 11,
\r
461 kCFXMLNodeTypeWhitespace = 12,
\r
462 kCFXMLNodeTypeNotation = 13,
\r
463 kCFXMLNodeTypeElementTypeDeclaration = 14,
\r
464 kCFXMLNodeTypeAttributeListDeclaration = 15
\r
468 -(CFXMLNodeTypeCode)type
\r
470 return CFXMLNodeGetTypeCode(_node);
\r
475 -(NSDictionary *)attributes
\r
477 CFXMLElementInfo eInfo;
\r
479 if( CFXMLNodeGetTypeCode( _node ) != kCFXMLNodeTypeElement )
\r
482 eInfo = *(CFXMLElementInfo *)CFXMLNodeGetInfoPtr(_node);
\r
484 return [[(NSDictionary *)eInfo.attributes retain] autorelease];
\r
485 } // end attributes
\r
493 -(NSString *)attributeNamed:(NSString *)name
\r
495 if( _tree == NULL )
\r
498 return [[[[[self attributes] objectForKey:name] description] retain] autorelease];
\r
499 } // end attributeNamed:
\r
505 -(NSString *)description
\r
507 NSMutableString *descr;
\r
509 descr = [NSMutableString string];
\r
511 //NSLog( @"Description for type %d", CFXMLNodeGetTypeCode(_node) );
\r
513 switch( CFXMLNodeGetTypeCode(_node) ){
\r
515 case kCFXMLNodeTypeDocument:
\r
516 case kCFXMLNodeTypeElement:
\r
517 XMLTreeDescription( (CFMutableStringRef)descr, _tree );
\r
520 case kCFXMLNodeTypeProcessingInstruction:
\r
521 case kCFXMLNodeTypeAttribute:
\r
522 case kCFXMLNodeTypeComment:
\r
523 case kCFXMLNodeTypeText:
\r
524 case kCFXMLNodeTypeCDATASection:
\r
525 case kCFXMLNodeTypeDocumentFragment:
\r
526 case kCFXMLNodeTypeEntity:
\r
527 case kCFXMLNodeTypeEntityReference:
\r
528 case kCFXMLNodeTypeDocumentType:
\r
529 case kCFXMLNodeTypeWhitespace:
\r
530 case kCFXMLNodeTypeNotation:
\r
531 case kCFXMLNodeTypeElementTypeDeclaration:
\r
532 case kCFXMLNodeTypeAttributeListDeclaration:
\r
534 [descr appendString:(NSString *)CFXMLNodeGetString(_node)];
\r
538 } // end description
\r
547 if( _tree == NULL )
\r
550 xmlData = CFXMLTreeCreateXMLData(
\r
551 kCFAllocatorDefault,
\r
553 if( xmlData == NULL )
\r
557 NSString *string = [[[NSString alloc] initWithData:(NSData *)xmlData
\r
558 encoding:NSASCIIStringEncoding] autorelease];
\r
559 if( xmlData != NULL ) //because it is retained by us after creating it with
\r
560 CFRelease(xmlData); //CFXMLTreeCreateXMLData() and we're finished with it - TWB
\r
568 @end // End implementation
\r
574 CFStringRef XMLTreeDescription( CFMutableStringRef descr, CFXMLTreeRef tree )
\r
576 CFXMLTreeRef childTree;
\r
577 CFXMLNodeRef childNode;
\r
581 childCount = CFTreeGetChildCount( tree );
\r
583 for( i = 0; i < childCount; i++ ){
\r
585 childTree = CFTreeGetChildAtIndex( tree, i );
\r
586 CFRetain( childTree );
\r
588 childNode = CFXMLTreeGetNode( childTree );
\r
589 CFRetain( childNode );
\r
591 switch( CFXMLNodeGetTypeCode( childNode ) ){
\r
593 case kCFXMLNodeTypeText:
\r
594 CFStringAppend( descr, CFXMLNodeGetString( childNode ) );
\r
597 case kCFXMLNodeTypeElement:
\r
598 XMLTreeDescription( descr, childTree );
\r
603 } // end switch: node type
\r
605 CFRelease( childTree );
\r
606 CFRelease( childNode );
\r
610 } // end XMLTreeDescription
\r
614 CFXMLTreeRef XMLTreeDescendentNamed( CFStringRef name, CFXMLTreeRef tree )
\r
616 CFXMLTreeRef childTree;
\r
617 CFXMLTreeRef descTree;
\r
618 CFXMLNodeRef childNode;
\r
619 CFStringRef childName;
\r
620 CFXMLTreeRef returnVal;
\r
624 childCount = CFTreeGetChildCount( tree );
\r
628 for( i = 0; i < childCount; i++ ){
\r
630 childTree = CFTreeGetChildAtIndex( tree, i );
\r
631 CFRetain( childTree );
\r
633 childNode = CFXMLTreeGetNode( childTree );
\r
634 CFRetain( childNode );
\r
636 childName = CFXMLNodeGetString( childNode );
\r
637 CFRetain( childName );
\r
640 if( CFStringCompare( name, childName, NULL ) == kCFCompareEqualTo ){
\r
641 returnVal = childTree;
\r
642 CFRetain( returnVal );
\r
643 } // end if: found it
\r
645 // Else if child is an element, search recursively
\r
646 else if( CFXMLNodeGetTypeCode( childNode ) == kCFXMLNodeTypeElement ){
\r
648 descTree = XMLTreeDescendentNamed( name, childTree );
\r
651 if( descTree != NULL ){
\r
652 returnVal = descTree; // Alread +1 retain count
\r
653 } // end if: got match
\r
655 } // end if: element node type
\r
656 CFRelease(childTree); //TWB
\r
657 CFRelease(childNode); //TWB
\r
658 CFRelease(childName); //TWB
\r
663 } // end XMLTreeDescendentNamed:
\r
667 CFTypeRef XMLTreeXPath( CFMutableStringRef xpath, CFXMLTreeRef tree )
\r
669 CFTypeRef returnRef;
\r
674 node = CFXMLTreeGetNode( tree );
\r
677 // Are "we" who we're looking for?
\r
678 if( xpath == NULL || CFStringGetLength( xpath ) == 0 ){
\r
681 } // end if: looking for us
\r
684 else if( CFStringHasPrefix( xpath, (CFStringRef)@"@" ) ){
\r
686 // Must be an element node to make any sense
\r
687 if( CFXMLNodeGetTypeCode( node ) == kCFXMLNodeTypeElement ){
\r
689 CFXMLElementInfo eInfo;
\r
690 CFDictionaryRef attr;
\r
692 // Retrieve attribute dictionary
\r
693 eInfo = *(CFXMLElementInfo *)CFXMLNodeGetInfoPtr(node);
\r
694 attr = (CFDictionaryRef)eInfo.attributes;
\r
696 // Shave off '@' sign
\r
697 CFStringTrim( xpath, (CFStringRef)@"@" );
\r
699 returnRef = CFDictionaryGetValue( attr, xpath );
\r
700 } // end if: element node
\r
701 } // end if: attribute
\r
703 // Else, not looking at an attribute endpoint
\r
706 BOOL searchDescendents = NO;
\r
707 CFStringRef firstWord = NULL;
\r
709 CFRange nextBracket;
\r
711 CFRange firstDelimiter;
\r
713 // Descendent or child?
\r
714 searchDescendents = CFStringHasPrefix( xpath, (CFStringRef)@"//" );
\r
716 // Strip leading slashes
\r
717 CFStringTrim( xpath, (CFStringRef)@"/" );
\r
719 // Get location of next delimiters
\r
720 nextSlash = CFStringFind( xpath, (CFStringRef)@"/", NULL );
\r
721 nextBracket = CFStringFind( xpath, (CFStringRef)@"[", NULL );
\r
722 nextAt = CFStringFind( xpath, (CFStringRef)@"@", NULL );
\r
723 firstDelimiter = nextSlash;
\r
725 if( nextBracket.location < firstDelimiter.location )
\r
726 firstDelimiter = nextBracket;
\r
727 if( nextAt.location < firstDelimiter.location )
\r
728 firstDelimiter = nextAt;
\r
730 // Easiest case, slash or @ without a test
\r
731 if( firstDelimiter.location == nextSlash.location
\r
733 firstDelimiter.location == nextAt.location ){
\r
735 // Get first word (it's the node we need to retrieve
\r
736 firstWord = CFStringCreateWithSubstring( NULL,
\r
739 firstDelimiter.location) );
\r
741 // Strip word out of xpath
\r
742 CFStringDelete( xpath, CFRangeMake(0,firstDelimiter.location) );
\r
745 CFXMLTreeRef subTree = XMLTreeDescendentNamed( firstWord, subTree );
\r
747 // Call recursively with the rest of the xpath
\r
748 returnRef = XMLTreeXPath( xpath, subTree );
\r
750 // Release sub tree in accordance with XMLTreeDescendentNamed(...) function.
\r
751 CFRelease( subTree );
\r
752 } // end if: slash or @
\r
756 } // end else: not attribute
\r
759 CFRelease( node ); //not retained any more - TWB
\r
762 } // end XMLTreeXPath
\r