// -*- mode:objc -*- // $Id: Tree.m,v 1.3 2004/03/18 19:50:40 ujwal Exp $ // /* ** Tree.m ** ** Copyright (c) 2002-2004 ** ** Author: Ujwal S. Setlur ** ** Project: iTerm ** ** Description: Implements a tree structure for bookmarks. ** Adapted from Apple's example code. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #import #define KEY_NAME @"Name" #define KEY_DESCRIPTION @"Description" #define KEY_IS_GROUP @"Group Node" #define KEY_ENTRIES @"Entries" #define KEY_DATA @"Data" @implementation NSArray (MyExtensions) - (BOOL) containsObjectIdenticalTo: (id)obj { return [self indexOfObjectIdenticalTo: obj]!=NSNotFound; } @end @implementation NSMutableArray (MyExtensions) - (void) insertObjectsFromArray:(NSArray *)array atIndex:(int)index { NSObject *entry = nil; NSEnumerator *enumerator = [array objectEnumerator]; while ((entry=[enumerator nextObject])) { [self insertObject:entry atIndex:index++]; } } @end @implementation TreeNode + (id) treeFromDictionary:(NSDictionary*)dict { return [[[TreeNode alloc] initFromDictionary:dict] autorelease]; } - (id)initWithData:(NSDictionary *)data parent:(TreeNode*)parent children:(NSArray*)children { self = [super init]; if (self==nil) return nil; nodeData = [[NSMutableDictionary alloc] initWithDictionary: data]; nodeChildren = [[NSMutableArray arrayWithArray:children] retain]; nodeParent = parent; return self; } - (id) initFromDictionary:(NSDictionary*)dict { // This is a convenience init method to return a tree root of a tree derived from an input dictionary. // The input dictionary for this example app is InitInfo.dict. Look at that file to understand the format. NSDictionary *data = [dict objectForKey: KEY_DATA]; NSEnumerator *entryEnum = [[dict objectForKey: KEY_ENTRIES] objectEnumerator]; id entry; TreeNode *child = nil; self = [self initWithData:data parent:nil children:[NSArray array]]; if (self==nil) return nil; if([dict objectForKey: KEY_IS_GROUP]) [self setIsLeaf: NO]; else [self setIsLeaf: YES]; while ((entry=[entryEnum nextObject])) { // if child is another group, recursively add the branch if ([[entry objectForKey: KEY_IS_GROUP] isEqualToString: @"Yes"]) { child = [TreeNode treeFromDictionary: entry]; } else { data = [entry objectForKey: KEY_DATA]; child = [[[TreeNode alloc] initWithData: data parent:nil children: [NSArray array]] autorelease]; [child setIsLeaf: YES]; } [self insertChild: child atIndex: [self numberOfChildren]]; } return self; } // return a dictionary representation of the node and its children - (NSDictionary *) dictionary { NSMutableDictionary *aDict; NSEnumerator *entryEnum; TreeNode *child; NSMutableArray *aMutableArray; aDict = [[NSMutableDictionary alloc] init]; if(nodeData) [aDict setObject: nodeData forKey: KEY_DATA]; if(!isLeaf) [aDict setObject: @"Yes" forKey: KEY_IS_GROUP]; // recursively encode the children aMutableArray = [NSMutableArray array]; entryEnum = [nodeChildren objectEnumerator]; while ((child = [entryEnum nextObject])) { [aMutableArray addObject: [child dictionary]]; } [aDict setObject: aMutableArray forKey: KEY_ENTRIES]; return ([aDict autorelease]); } - (void)dealloc { [nodeData release]; [nodeChildren release]; nodeData = nil; nodeChildren = nil; [super dealloc]; } // ================================================================ // Methods used to manage a node and its children. // ================================================================ - (void)setNodeData:(NSDictionary *)data { [nodeData release]; nodeData = [[NSMutableDictionary alloc] initWithDictionary: data]; } - (NSDictionary *)nodeData { return nodeData; } - (void)setNodeParent:(TreeNode*)parent { nodeParent = parent; } - (TreeNode*)nodeParent { return nodeParent; } - (BOOL)isGroup { return (!isLeaf); } - (BOOL) isLeaf { return (isLeaf); } - (void) setIsLeaf: (BOOL) flag { isLeaf = flag; } - (void)insertChild:(TreeNode*)child atIndex:(int)index { [nodeChildren insertObject:child atIndex:index]; [child setNodeParent: self]; } - (void)insertChildren:(NSArray*)children atIndex:(int)index { [nodeChildren insertObjectsFromArray: children atIndex: index]; [children makeObjectsPerformSelector:@selector(setNodeParent:) withObject:self]; } - (void)_removeChildrenIdenticalTo:(NSArray*)children { TreeNode *child; NSEnumerator *childEnumerator = [children objectEnumerator]; [children makeObjectsPerformSelector:@selector(setNodeParent:) withObject:nil]; while ((child=[childEnumerator nextObject])) { [nodeChildren removeObjectIdenticalTo:child]; } } - (void)removeChild:(TreeNode*)child { int index = [self indexOfChild: child]; if (index!=NSNotFound) { [self _removeChildrenIdenticalTo: [NSArray arrayWithObject: [self childAtIndex:index]]]; } } - (void)removeFromParent { [[self nodeParent] removeChild:self]; } - (int)indexOfChild:(TreeNode*)child { return [nodeChildren indexOfObject:child]; } - (int)indexOfChildIdenticalTo:(TreeNode*)child { return [nodeChildren indexOfObjectIdenticalTo:child]; } - (int)numberOfChildren { return [nodeChildren count]; } - (NSArray*)children { return [NSArray arrayWithArray: nodeChildren]; } - (TreeNode*)firstChild { return [nodeChildren objectAtIndex:0]; } - (TreeNode*)lastChild { return [nodeChildren lastObject]; } - (TreeNode*)childAtIndex:(int)index { return [nodeChildren objectAtIndex:index]; } - (BOOL)isDescendantOfNode:(TreeNode*)node { // returns YES if 'node' is an ancestor. // Walk up the tree, to see if any of our ancestors is 'node'. TreeNode *parent = self; while(parent) { if(parent==node) return YES; parent = [parent nodeParent]; } return NO; } - (BOOL)isDescendantOfNodeInArray:(NSArray*)nodes { // returns YES if any 'node' in the array 'nodes' is an ancestor of ours. // For each node in nodes, if node is an ancestor return YES. If none is an // ancestor, return NO. NSEnumerator *nodeEnum = [nodes objectEnumerator]; TreeNode *node = nil; while((node=[nodeEnum nextObject])) { if([self isDescendantOfNode:node]) return YES; } return NO; } - (void)recursiveSortChildren { [nodeChildren sortUsingSelector:@selector(compare:)]; [nodeChildren makeObjectsPerformSelector: @selector(recursiveSortChildren)]; } - (NSString*)description { // Return something that will be useful for debugging. return [NSString stringWithFormat: @"{%@}", nodeData]; } // Returns the minimum nodes from 'allNodes' required to cover the nodes in 'allNodes'. // This methods returns an array containing nodes from 'allNodes' such that no node in // the returned array has an ancestor in the returned array. // There are better ways to compute this, but this implementation should be efficient for our app. + (NSArray *) minimumNodeCoverFromNodesInArray: (NSArray *)allNodes { NSMutableArray *minimumCover = [NSMutableArray array]; NSMutableArray *nodeQueue = [NSMutableArray arrayWithArray:allNodes]; TreeNode *node = nil; while ([nodeQueue count]) { node = [nodeQueue objectAtIndex:0]; [nodeQueue removeObjectAtIndex:0]; while ( [node nodeParent] && [nodeQueue containsObjectIdenticalTo:[node nodeParent]] ) { [nodeQueue removeObjectIdenticalTo: node]; node = [node nodeParent]; } if (![node isDescendantOfNodeInArray: minimumCover]) [minimumCover addObject: node]; [nodeQueue removeObjectIdenticalTo: node]; } return minimumCover; } @end