Simplify tableView:didClickTableColumn:.
[MacTF.git] / XMLTree.m
blobb0e7aaa477e63bf091cd3e039eddf14193343a33
1 #import "XMLTree.h"\r
2 \r
3 @interface XMLTree (PrivateAPI)\r
4 \r
5 \r
6 @end // End Private API\r
7 \r
8 \r
9 \r
10 @implementation XMLTree\r
14 +(XMLTree *)treeWithURL:(NSURL *)url\r
15 {\r
16     return [[[XMLTree alloc] initWithURL:url] autorelease];\r
17 }   // end treeWithURL\r
22 +(XMLTree *)treeWithCFXMLTreeRef:(CFXMLTreeRef)ref\r
23 {\r
24     return [[[XMLTree alloc] initWithCFXMLTreeRef:ref] autorelease];\r
25 }   // end treeWithCFXMLTreeRef\r
28 +(XMLTree *)treeWithData:(NSData *)data\r
29 {\r
30     return [[[XMLTree alloc] initWithData:data] autorelease];\r
31 }   // end treeWithData\r
36 -(XMLTree *)init\r
37 {\r
38     if( (self = [super init]) == nil )\r
39         return nil;\r
41     _tree = NULL;\r
42     _node = NULL;\r
44     return self;\r
45 }   // end init\r
50 -(XMLTree *)initWithCFXMLTreeRef:(CFXMLTreeRef)ref\r
51 {\r
52     if( [self init] == nil )\r
53         return nil;\r
55     // Clean up?\r
56     if( _tree != NULL )\r
57         CFRelease( _tree );\r
58     if( _node != NULL )\r
59         CFRelease( _node );\r
61     // Make sure ref is something\r
62     if( ref ){\r
63         _tree = ref;\r
64         _node = CFXMLTreeGetNode( _tree );\r
65         \r
66         CFRetain( _tree );\r
67         CFRetain( _node );\r
68     }   // end if: valid ref\r
69     else{\r
70         [self release];\r
71         self = nil;\r
72     }   // end else: no good\r
73     \r
74     return self;\r
75 }       // end initWithCFXMLTreeRef:\r
82 -(XMLTree *)initWithURL:(NSURL *)url\r
83 {\r
84     CFXMLTreeRef tree;\r
86     if( !url )\r
87         return nil;\r
88     \r
89     tree = CFXMLTreeCreateWithDataFromURL(\r
90                                            kCFAllocatorDefault,\r
91                                            (CFURLRef)url,\r
92                                            kCFXMLParserSkipWhitespace, \r
93                                            NULL ); //CFIndex\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
98     return new;\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
114     CFXMLTreeRef tree;\r
116     if( !inData )\r
117         return nil;\r
118     \r
119     tree = CFXMLTreeCreateFromData(\r
120                                    kCFAllocatorDefault,\r
121                                     (CFDataRef)inData,\r
122                                     (CFURLRef)url,\r
123                                     kCFXMLParserSkipWhitespace,\r
124                                     NULL);\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
130     return new;\r
132 }   // end initWithData:withResolvingURL:\r
141 -(void)dealloc\r
143     //NSLog( @"dealloc %@", self );\r
145     if( _tree != NULL )\r
146         CFRelease( _tree );\r
148     if( _node != NULL )\r
149         CFRelease( _node );\r
151     _tree = NULL;\r
152     _node = NULL;\r
153     [super dealloc]; //added TWB\r
155 }       // end dealloc\r
161 /* ********  A B O U T   P A R E N T  ******** */\r
165 -(XMLTree *)parent\r
167     CFTreeRef  parent;\r
168     XMLTree   *returnVal;\r
169     \r
170     if( _tree == NULL )\r
171         return nil;\r
173     parent    = CFTreeGetParent( _tree );\r
174     returnVal = nil;\r
176     if( parent ){\r
177         CFRetain( parent );\r
178         returnVal = [XMLTree treeWithCFXMLTreeRef:parent];\r
179         CFRelease( parent );\r
180         parent = NULL;\r
181     }   // end if: got parent\r
183     return returnVal;\r
184 }       // end 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
198     CFTypeID  resultID;\r
199     id        result;\r
200     \r
201     NSLog(@"The path function is still being developed. It doesn't work yet. Sorry.");\r
202     return nil;\r
203     \r
206     // Call C-code\r
207     resultRef = XMLTreeXPath( (CFMutableStringRef)[xpath mutableCopy], _tree );\r
209     // Not null?\r
210     if( resultRef != NULL ){\r
212         // What kind of object was returned?\r
213         resultID  = CFGetTypeID( resultRef );\r
215         // String?\r
216         if( resultID == CFStringGetTypeID()  ){\r
217     \r
218             result = [NSString stringWithString:(NSString *)resultRef];\r
219         }       // end if: string\r
220     \r
221         // Else, tree?\r
222         else if( resultID == CFTreeGetTypeID() ){\r
223     \r
224             result = [XMLTree treeWithCFXMLTreeRef:(CFXMLTreeRef)resultRef];\r
225         }       // end else: tree\r
226     \r
227         // Else error\r
228         else{\r
229             NSLog(@"Error. Expected tree or string. Contact developer (rob@iharder.net)");\r
230         }       // end else\r
232         // Release result ref (part of contract with XMLTreeXPath function)\r
233         CFRelease( resultRef );\r
234         \r
235     }   // end if: not null\r
237     return result;\r
238 }       // end xpath:\r
247 -(int)count\r
249     if( _tree == NULL )\r
250         return -1;\r
251     \r
252     return CFTreeGetChildCount( _tree );\r
253 }       // end count\r
259 -(XMLTree *)childAtIndex:(int)index\r
261     CFXMLTreeRef child;\r
262     \r
263     if( _tree == NULL )\r
264         return nil;\r
266     if( index >= CFTreeGetChildCount( _tree ) )\r
267         return nil;\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
273     \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
289     int           childCount;\r
290     int           i;\r
292     if( _tree == NULL )\r
293         return nil;\r
295     childCount = CFTreeGetChildCount( _tree );\r
296     returnVal  = nil;\r
297     \r
298     for( i = 0; i < childCount; i++ ){\r
299         \r
300         childTree = CFTreeGetChildAtIndex(_tree, i );\r
301         CFRetain( childTree );\r
302         \r
303         childNode = CFXMLTreeGetNode( childTree );\r
304         CFRetain( childNode );\r
305         \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
317         if( returnVal )\r
318             break;\r
319         \r
320     }   // end for: each child\r
322     return returnVal;\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
339     int           childCount;\r
340     int           i;\r
342     if( _tree == NULL )\r
343         return nil;\r
345     childCount = CFTreeGetChildCount( _tree );\r
346     returnVal  = nil;\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
359         // Name matches?\r
360         if( CFStringCompare( (CFStringRef)name, childName, NULL ) == kCFCompareEqualTo ){\r
362             // Has attributes?\r
363             if( CFXMLNodeGetTypeCode( childNode ) == kCFXMLNodeTypeElement ){\r
364                 \r
365                 eInfo = *(CFXMLElementInfo *)CFXMLNodeGetInfoPtr(childNode);                \r
366                 childAttrVal = (CFStringRef)CFDictionaryGetValue(\r
367                                                                  eInfo.attributes,\r
368                                                                  attrName );\r
369                 CFRetain( childAttrVal );\r
370                 \r
371                 // Attribute matches\r
372                 if( CFStringCompare(\r
373                                     (CFStringRef)attrVal,\r
374                                     childAttrVal,\r
375                                     NULL ) == kCFCompareEqualTo ){\r
376                     \r
377                     returnVal = [XMLTree treeWithCFXMLTreeRef:childTree];\r
378                 }       // end if: match\r
380                 CFRelease( childAttrVal );\r
381                 \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
389         if( returnVal )\r
390             break;\r
392     }   // end for: each child\r
394     return returnVal;\r
395 }       // end childNamed:\r
404 -(XMLTree *)descendentNamed:(NSString *)name\r
406     CFXMLTreeRef       descTree;\r
407     XMLTree           *returnVal;\r
409     if( _tree == NULL )\r
410         return nil;\r
411     \r
412     descTree = XMLTreeDescendentNamed( (CFStringRef)name, _tree );\r
413     \r
414     if( descTree == NULL )\r
415         return nil;\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
422     return returnVal;\r
423 }       // end descendentNamed:\r
427 /* ********  A B O U T   S E L F  ******** */\r
432 -(NSString *)name\r
434     if( _node == NULL )\r
435         return nil;\r
437     return [NSString stringWithString:(NSString *)CFXMLNodeGetString(_node)];\r
438 }       // end name\r
444 /*!\r
445   @discussion\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
448  <pre>\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
465  };\r
466  </pre>\r
467  */\r
468 -(CFXMLNodeTypeCode)type\r
470     return CFXMLNodeGetTypeCode(_node);\r
471 }       // end type\r
475 -(NSDictionary *)attributes\r
477     CFXMLElementInfo eInfo;\r
479     if( CFXMLNodeGetTypeCode( _node ) != kCFXMLNodeTypeElement )\r
480         return nil;\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
496         return nil;\r
498     return [[[[[self attributes] objectForKey:name] description] retain] autorelease];\r
499 }       // end attributeNamed:\r
505 -(NSString *)description\r
507     NSMutableString *descr;\r
508     \r
509     descr = [NSMutableString string];\r
511     //NSLog( @"Description for type %d", CFXMLNodeGetTypeCode(_node) );\r
512     \r
513     switch( CFXMLNodeGetTypeCode(_node) ){\r
515         case kCFXMLNodeTypeDocument:\r
516         case kCFXMLNodeTypeElement:\r
517             XMLTreeDescription( (CFMutableStringRef)descr, _tree );\r
518             break;\r
519             \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
533         default:\r
534             [descr appendString:(NSString *)CFXMLNodeGetString(_node)];\r
535     }   // end switch\r
537     return descr;\r
538 }       // end description\r
543 -(NSString *)xml\r
545     CFDataRef  xmlData;\r
547     if( _tree == NULL )\r
548         return nil;\r
550     xmlData = CFXMLTreeCreateXMLData(\r
551                                      kCFAllocatorDefault,\r
552                                      _tree );\r
553     if( xmlData == NULL )\r
554         return nil;\r
556     \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
561     return string;\r
563 }       // end xml\r
568 @end // End implementation\r
574 CFStringRef XMLTreeDescription( CFMutableStringRef descr, CFXMLTreeRef tree )\r
576     CFXMLTreeRef childTree;\r
577     CFXMLNodeRef childNode;\r
578     int childCount;\r
579     int i;\r
581     childCount = CFTreeGetChildCount( tree );\r
583     for( i = 0; i < childCount; i++ ){\r
585         childTree = CFTreeGetChildAtIndex( tree, i );\r
586         CFRetain( childTree );\r
587         \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
595                 break;\r
597             case kCFXMLNodeTypeElement:\r
598                 XMLTreeDescription( descr, childTree );\r
599                 break;\r
601             default:\r
602                 break;\r
603         }       // end switch: node type\r
605         CFRelease( childTree );\r
606         CFRelease( childNode );\r
607     }   // end for\r
609     return descr;\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
621     int childCount;\r
622     int i;\r
623     \r
624     childCount = CFTreeGetChildCount( tree );\r
625     returnVal  = NULL;\r
626     descTree   = NULL;\r
628     for( i = 0; i < childCount; i++ ){\r
630         childTree = CFTreeGetChildAtIndex( tree, i );\r
631         CFRetain( childTree );\r
632         \r
633         childNode = CFXMLTreeGetNode( childTree );\r
634         CFRetain( childNode );\r
635         \r
636         childName = CFXMLNodeGetString( childNode );\r
637         CFRetain( childName );\r
639         // Is this it?\r
640         if( CFStringCompare( name, childName, NULL ) == kCFCompareEqualTo ){\r
641             returnVal = childTree;\r
642             CFRetain( returnVal );\r
643         }       // end if: found it\r
644         \r
645         // Else if child is an element, search recursively\r
646         else if( CFXMLNodeGetTypeCode( childNode ) == kCFXMLNodeTypeElement ){\r
648             descTree = XMLTreeDescendentNamed( name, childTree );\r
649                     \r
650                 // Got a match?\r
651             if( descTree != NULL ){\r
652                 returnVal = descTree; // Alread +1 retain count\r
653             }   // end if: got match\r
654                         \r
655         }       // end if: element node type\r
656         CFRelease(childTree); //TWB\r
657         CFRelease(childNode); //TWB\r
658         CFRelease(childName); //TWB\r
659         \r
660     }   // end for\r
661     \r
662     return returnVal;\r
663 }       // end XMLTreeDescendentNamed:\r
667 CFTypeRef XMLTreeXPath( CFMutableStringRef xpath, CFXMLTreeRef tree )\r
669     CFTypeRef returnRef;\r
670     CFXMLNodeRef node;\r
671     \r
673     returnRef = NULL;\r
674     node      = CFXMLTreeGetNode( tree );\r
675     CFRetain( node );\r
677     // Are "we" who we're looking for?\r
678     if( xpath == NULL || CFStringGetLength( xpath ) == 0 ){\r
679         \r
680         returnRef = tree;\r
681     }   // end if: looking for us\r
682     \r
683     // Attribute?\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
704     else{\r
706         BOOL        searchDescendents = NO;\r
707         CFStringRef firstWord = NULL;\r
708         CFRange     nextSlash;\r
709         CFRange     nextBracket;\r
710         CFRange     nextAt;\r
711         CFRange     firstDelimiter;\r
713         // Descendent or child?\r
714         searchDescendents = CFStringHasPrefix( xpath, (CFStringRef)@"//" );\r
715         \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
724         \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
732             ||\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
737                                                      xpath,\r
738                                                      CFRangeMake(0,\r
739                                                                  firstDelimiter.location) );\r
740             \r
741             // Strip word out of xpath\r
742             CFStringDelete( xpath, CFRangeMake(0,firstDelimiter.location) );\r
744             // Get child tree\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
753         \r
754         \r
756     }   // end else: not attribute\r
758     // Release stuff\r
759     CFRelease( node ); //not retained any more - TWB\r
761     return returnRef;\r
762 }       // end XMLTreeXPath\r