ftp.nice.ch/pub/next/developer/resources/classes/misckit/MiscKit.1.10.0.s.gnutar.gz#/MiscKit/Source/MiscKit/MiscGraphNode.m

This is MiscGraphNode.m in view mode; [Download] [Up]

/************************************************************************
  CLASS:			MiscGraphNode

	See the header file for more information on this class.

  This object is included in the MiscKit by permission from the author
  and its use is governed by the MiscKit license, found in the file
  "LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
  for a list of all applicable permissions and restrictions.
  
  Copyright (c) 1995 by author.  All rights reserved.
  
 ************************************************************************/

#import <objc/List.h>
#import <misckit/MiscAnnouncer.h>
#import <misckit/MiscGraphNode.h>

// The following #defines are for archiving instances of MiscGraphNode. If
// the ivars that you would like to archive change, bump up the version
// and make the appropriate changes to the read: method so all previous
// versions will be unarchivable.

#define MISCGRAPHNODE_VERSION 0
#define MGN_CLASSNAME "MiscGraphNode"
 
 
@implementation MiscGraphNode 

+ initialize
{
	// Sets the current version of this class. This is used so you can make
	// sure that all previous versions of MiscGraphNode will be unarchivable.

	if (self == [MiscGraphNode class])
		[self setVersion: MISCGRAPHNODE_VERSION];
		
	return self;
}


- init
{
	if (!(self = [super init]))
		return nil;
	
	children = nil;		// the default, which is to be a leaf
	parents = [ [List alloc] init];
	announcer = [ [MiscAnnouncer alloc] initOwner: self];

	return self;
}


- free
{
  	// send willFree: message to all users and listeners
	[self announce: @selector (willFree:)];

	// send all children a removeParent: message while removing them
	// from our list. 
	while ([children count] > 0)
		if ([ [children objectAt: 0] respondsTo: @selector (removeParent:)])
			[ [children removeObjectAt: 0] removeParent: self];

	if (children)
		children = [children free];

	// Tell all our parents that we cannot be their child anymore.
	while ([parents count] > 0)
		if ([ [parents objectAt: 0] respondsTo: @selector (removeChild:)])
			[ [parents removeObjectAt: 0] removeChild: self];
				
	if (parents != nil)
		parents = [parents free];
				
	[announcer free];			
	return [super free];
}


- (BOOL)isLeaf
{	
	// If the list of children is nil, then we are definitely a leaf. If the
	// list is empty then you are considered to be a non-leaf node with zero
	// children.

	if ([self children] == nil)
		return YES;
		
	return NO;
}


- setLeaf: (BOOL)isALeaf
{
	// Sets whether the node is a leaf. If so, then you cannot have any children. 
  	int  i;
  
	if (isALeaf)
	{	// if you had children, you must give them all up
		if (children)
		{
			// tell them to remove you as their parent
			for (i=0; i<[children count]; i++)
				if ([ [children objectAt: i] respondsTo: @selector (removeParent:)])
					[ [children objectAt: i] removeParent: self];
			
			children = [children free];
		 }
	 }
	else
	{	// you are a node, so init list if needed
	 	if (children == nil)
			children = [ [List alloc] init];
	 }
	 
	 return self;
}
	
		
- announce: (SEL)announcement
{
	// Central announcement method which just uses the announcer object. When the 
	// foundation kit is in full use though, we could use NSNotifications.

	[announcer announce: announcement];
	return self;
}


- children
{
	// Returns the list of children the node has. If the node is a leaf node, then nil
	// will be returned.

	return children;
}


- addChild: aChild
{
	// Adds aChild to the list of children. If aChild is successfully added, self
	// is returned, else nil. If the child is added successfully, the users and 
	// listeners are sent a didAddChild: message and the child is sent an 
	// addParent: message if it responds.

	if ([self children] == nil)	
		children = [ [List alloc] init];
		
	if ([children addObject: aChild] == nil)
		return nil;
	
	if ([aChild respondsTo: @selector (addParent:)])
		[aChild addParent: self];
		
	[self announce: @selector (didAddChild:)];
	
	return self;
}



- addChildren: theChildren
{
  int  i; 
  int  childrenAdded = 0;
	
	// stop all announcements so addChild: will not send out anything
	[announcer setSendAnnouncements: NO];
	
	for (i=0; i<[theChildren count]; i++)
		if ([self addChild: [theChildren objectAt: i] ] != nil)
			childrenAdded++;
	
	[announcer setSendAnnouncements: YES];
	
	// send out notification depending upon the number of children added
	
	switch (childrenAdded)
	{
	 	case 0:
			break;
		case 1:
			[self announce: @selector (didAddChild:)];
			break;
		default:
			[self announce: @selector (didAddChildren:)];
	 }
	 
	return self;
}


- removeChild: aChild
{
	// Removes aChild from the children list. If aChild is removed successfully
	// self is return, else nil. Also sends the child a removeParent:
	// message if it repsonds. 

	if ([ [self children] removeObject: aChild] == nil)
		return nil;
		
	if ([aChild respondsTo: @selector (removeParent:)])
		[aChild removeParent: self];
		
	[self announce: @selector (didRemoveChild:)];
			
	return self;
}


- removeChildren: theChildren
{
  int  i; 
  int  childrenRemoved = 0;
	
	// stop all announcements so removeChild: will not send out anything
	[announcer setSendAnnouncements: NO];
	
	for (i=0; i<[theChildren count]; i++)
		if ([self removeChild: [theChildren objectAt: i] ] != nil)
			childrenRemoved++;
	
	[announcer setSendAnnouncements: YES];
	
	// send out notification depending upon the number of children added
	
	switch (childrenRemoved)
	{
	 	case 0:
			break;
		case 1:
			[self announce: @selector (didRemoveChild:)];
			break;
		default:
			[self announce: @selector (didRemoveChildren:)];
	 }
	 
	return self;
}


- parents
{
	// Returns the list of parents. It will always return a list, and if there are
	// no parents, it's count will be zero.

	return parents;
}



- addParent: aParent
{	
	if ([ [self parents] addObject: aParent] == nil)
		return nil;

	[self announce: @selector (didAddParent:)];	 	
	return self;
}


- removeParent: aParent
{
	// Removes aParent from the parent list, then checks to see if it should
	// free itself. The node is freed when it has no more users or parents.

	if ([ [self parents] removeObject: aParent] == nil)
		return nil;
	
	[self announce: @selector (didRemoveParent:)];
	[self checkForFree];
		
	return self;
}


- checkForFree
{
	// As long as we are supposed to garbage collect, check to see if we have
	// either parents or users. If we have neither, then we are no longer needed
	// and should free ourselves.

	if ([self garbageCollect] == NO)
		return self;
	
	if ([announcer numUsers] == 0 && [ [self parents] count] == 0)
		return [self free];

	return self;
}


- (BOOL)garbageCollect
{
	// By default, the MiscGraphNode will do it's own garbage collection. If you
	// have a sublcass and this is not the behavior you want, then override
	// to return NO.

	return YES;
}


// Methods for keeping track of dependancies between the nodes and other 
// objects. The following are from the IKDependency protocol. The messages
// are just passed to the announcer which keeps track of the users and 
// listeners.

- addUser: aUser
{
	[announcer addUser: aUser];
	return self;
}


- removeUser: aUser
{
	// Since possbile GC depends upon users and parents, you have to check just in
	// case the user you are removing is the last one.

	[announcer removeUser: aUser];
	[self checkForFree];
	return self;
}


- addListener: aListener
{
	[announcer addListener: aListener];
	return self;
}


- removeListener: aListener
{
	[announcer removeListener: aListener];
	return self;
}


- awake
{
	[super awake];
	
	announcer = [ [MiscAnnouncer alloc] initOwner: self];

	// I suppose this case should never be true, but just in case
	if (parents == nil)
		parents = [ [List alloc] init];
		
	return self;
}


- read:(NXTypedStream *)stream
{
  int  version;
  
	[super read: stream];

	version = NXTypedStreamClassVersion (stream, MGN_CLASSNAME);
	
	switch (version)
	{
	  case 0:
	  	children = NXReadObject (stream);
		parents = NXReadObject (stream);
	    break;

	  default:
	    break;
	 }
	 	
	return self;
}


- write:(NXTypedStream *)stream
{
	[super write: stream];
	
	NXWriteObject (stream, children);
	NXWriteObject (stream, parents);
	
	return self;
}

@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.