ftp.nice.ch/pub/next/developer/resources/libraries/libobjects.0.1.0.s.tar.gz#/libobjects-0.1.0/Collection.m

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

/* Implementation for Objective-C Collection object
   Copyright (C) 1993,1994 Free Software Foundation, Inc.

   Written by:  R. Andrew McCallum <mccallum@gnu.ai.mit.edu>
   Date: May 1993

   This file is part of the GNU Objective C Class Library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
   
   This library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ 

#include <objects/Collection.h>
#include <objects/CollectionPrivate.h>
#include <stdarg.h>
#include <objects/Bag.h>		/* for -contentsEqual: */
#include <objects/Array.h>		/* for -safeWithElementsCall: */
#include <objects/Coder.h>

@implementation Collection

+ initialize
{
  if (self == [Collection class])
    {
      [self setVersion:0];	// beta release;
    }
  return self;
}

// INITIALIZING AND FREEING;

// This is the designated initializer of this class;
- initWithType:(const char *)contentEncoding
{
  [super init];
  if (!elt_get_comparison_function(contentEncoding))
    [self error:"There is no elt comparison function for type encoding %s",
	  contentEncoding];
  return self;
}

- init
{
  // default contents are objects;
  return [self initWithType:@encode(id)];
}

/* Subclasses can override this for efficiency.  For example, Array can 
   init itself with enough capacity to hold aCollection. */
- initWithContentsOf: (id <Collecting>)aCollection
{
  [self initWithType:[aCollection contentType]];
  [self addContentsOf:aCollection];
  return self;
}

- free
{
  // ?;
  return [super free];
}

/* May be inefficient.  Could be overridden; */
- freeObjects
{
  if (CONTAINS_OBJECTS)
    [self safeMakeObjectsPerform:@selector(free)];
  [self empty];
  return self;
}

// ADDING;

- addElement: (elt)newElement
{
  return [self subclassResponsibility:_cmd];
}

- addElementIfAbsent: (elt)newElement
{
  if (![self includesElement:newElement])
    return [self addElement:newElement];
  return nil;
}

- addContentsOf: (id <Collecting>)aCollection
{
  id (*addElementImp)(id,SEL,elt) = (id(*)(id,SEL,elt))
    objc_msg_lookup(self, @selector(addElement:));

  void doIt(elt e) 
    {
      (*addElementImp)(self, @selector(addElement:), e);
    }

  [aCollection withElementsCall:doIt];
  return self;
}

- addContentsOfIfAbsent: (id <Collecting>)aCollection
{
  id (*addElementImp)(id,SEL,elt) = (id(*)(id,SEL,elt))
    objc_msg_lookup(self, @selector(addElement:));
  BOOL (*includesElementImp)(id,SEL,elt) = (BOOL(*)(id,SEL,elt))
    objc_msg_lookup(self, @selector(includesElement:));

  void doIt(elt e) 
    {
      if (!((*includesElementImp)(self, @selector(includesElement), e)))
	(*addElementImp)(self, @selector(addElement:), e);
    }

  [aCollection withElementsCall:doIt];
  return self;
}

- addElementsCount: (unsigned)count, ...
{
  va_list ap;

  // could use objc_msg_lookup here also;
  va_start(ap, count);
  while (count--)
    [self addElement:va_arg(ap, elt)];
  va_end(ap);
  return self;
}


// REMOVING AND REPLACING;

- (elt) removeElement: (elt)oldElement
{
  elt err(arglist_t argFrame)
    {
      return ELEMENT_NOT_FOUND_ERROR(oldElement);
    }
  return [self removeElement:oldElement ifAbsentCall:err];
}

- (elt) removeElement: (elt)oldElement ifAbsentCall: (elt(*)(arglist_t))excFunc
{
  return [self subclassResponsibility:_cmd];
}
  

- removeAllOccurrencesOfElement: (elt)oldElement
{
  BOOL (*includesElementImp)(id,SEL,elt) = (BOOL(*)(id,SEL,elt))
    objc_msg_lookup(self, @selector(includesElement:));
  elt (*removeElementImp)(id,SEL,elt) = (elt(*)(id,SEL,elt))
    objc_msg_lookup(self, @selector(removeElement:));

  while ((*includesElementImp)(self, @selector(includesElement:), oldElement))
    {
      (*removeElementImp)(self, @selector(removeElement:), oldElement);
    }
  return self;
}

- removeContentsIn: (id <Collecting>)aCollection
{
  BOOL (*includesElementImp)(id,SEL,elt) = (BOOL(*)(id,SEL,elt))
    objc_msg_lookup(self, @selector(includesElement:));
  elt (*removeElementImp)(id,SEL,elt) = (elt(*)(id,SEL,elt))
    objc_msg_lookup(self, @selector(removeElement:));

  void doIt(elt e)
    {
      if ((*includesElementImp)(self, @selector(includesElement:), e))
	(*removeElementImp)(self, @selector(removeElement:), e);
    }

  [aCollection withElementsCall:doIt];
  return self;
}

- removeContentsNotIn: (id <Collecting>)aCollection
{
  BOOL (*includesElementImp)(id,SEL,elt) = (BOOL(*)(id,SEL,elt))
    objc_msg_lookup(aCollection, @selector(includesElement:));
  elt (*removeElementImp)(id,SEL,elt) = (elt(*)(id,SEL,elt))
    objc_msg_lookup(self, @selector(removeElement:));

  void doIt(elt e)
    {
      if (!(*includesElementImp)(aCollection, @selector(includesElement:), e))
	(*removeElementImp)(self, @selector(removeElement:), e);
    }

  [self safeWithElementsCall:doIt];
  return self;
}

// remember this has to be overridden for IndexedCollection's;
- (elt) replaceElement: (elt )oldElement with: (elt )newElement
    ifAbsentCall: (elt(*)(arglist_t))excFunc
{
  elt err(arglist_t argFrame)
    {
      RETURN_BY_CALLING_EXCEPTION_FUNCTION(excFunc);
    }
  elt ret;
  ret = [self removeElement:oldElement ifAbsentCall:err];
  [self addElement:newElement];
  return ret;
}

- (elt) replaceElement: (elt )oldElement with: (elt)newElement
{
  elt err(arglist_t argFrame)
    {
      return ELEMENT_NOT_FOUND_ERROR(oldElement);
    }
  return [self replaceElement:oldElement with:newElement
	       ifAbsentCall:err];
}

- replaceAllOccurrencesOfElement: (elt)oldElement with: (elt)newElement
{
  BOOL (*includesElementImp)(id,SEL,elt) = (BOOL(*)(id,SEL,elt))
    objc_msg_lookup(self, @selector(includesElement:));
  elt (*replaceElementImp)(id,SEL,elt,elt) = (elt(*)(id,SEL,elt,elt))
    objc_msg_lookup(self, @selector(replaceElement:with:));

  if (ELEMENTS_EQUAL(oldElement, newElement))
    return self;
  while ((*includesElementImp)(self,@selector(includesElement:),oldElement))
    (*replaceElementImp)(self,@selector(replaceElement:with:),
			 oldElement, newElement);
  return self;
}

/* This is pretty inefficient?  Try to come up with something better. */
- uniqueContents
{
  // Use objc_msg_lookup here also;
  void doIt(elt e)
    {
      while ([self occurrencesOfElement:e] > 1)
	[self removeElement:e];
    }
  [self safeWithElementsCall:doIt];
  return self;
}

/* This must work without sending any messages to content objects.
   Content objects already may be -free'd when this is executed. */
- empty
{
  [self subclassResponsibility:_cmd];
  return self;
}

// TESTING;

- (BOOL) isEmpty
{
  return ([self count] == 0);
}

// Potentially inefficient, may be overridden in subclasses;
- (BOOL) includesElement: (elt)anElement
{
  int (*cf)(elt,elt) = [self comparisonFunction];
  BOOL test(elt e)
    {
      if (!((*cf)(anElement, e)))
	return YES;
      else
	return NO;
    }

  return [self trueForAnyElementsByCalling:test];
}

- (BOOL) isSubsetOf: (id <Collecting>)aCollection
{
  BOOL test(elt e) 
    { 
      return [aCollection includesElement:e]; 
    }
  return [self trueForAllElementsByCalling:test];
}
 
- (BOOL) contentsEqual: (id <Collecting>)aCollection
{
  id bag;
  BOOL flag;

  // Could use objc_msg_lookup here also;
  void doIt(elt e)
    {
      if ([bag includesElement:e])
	[bag removeElement:e];
      else
	flag = NO;
    }

  if ([self count] != [aCollection count]
      || ([self comparisonFunction] != [aCollection comparisonFunction]))
    return NO;
  bag = [[Bag alloc] initWithContentsOf:aCollection];
  flag = YES;
  [self withElementsCall:doIt whileTrue:&flag];
  if ((!flag) || [bag count])
    flag = NO;
  else
    flag = YES;
  [bag free];
  return flag;
}

/* This is what I'd like the -compare: implementation in Object.m to 
   look like. */
- (int) _objectCompare: anObject
{
  if (self == anObject)
    return 0;
  if (self > anObject)
    return 1;
  else
    return -1;
}

// Fix this ;
// How do we want to compare unordered contents?? ;
- (int) compareContentsOf: (id <Collecting>)aCollection
{
  if ([self contentsEqual:aCollection])
    return 0;
  if (self > aCollection)
    return 1;
  return -1;
}

// Deal with this in IndexedCollection also ;
// How do we want to compare collections? ;
- (int) compare: anObject
{
  if ([self isEqual:anObject])
    return 0;
  if (self > anObject)
    return 1;
  return -1;
}

- (BOOL) isEqual: anObject
{
  if (self == anObject) 
    return YES;
  if ([anObject class] == [self class]
      && [self contentsEqual: anObject] )
    return YES;
  else
    return NO;
}

- (BOOL) isDisjointFrom: (id <Collecting>)aCollection
{
  // Use objc_msg_lookup here also;
  BOOL flag = YES;
  void doIt(elt e)
    {
      if (![aCollection includesElement:e])
	flag = NO;
    }
  [self withElementsCall:doIt whileTrue:&flag];
  return !flag;
}

- (BOOL) trueForAllElementsByCalling: (BOOL(*)(elt))aFunc
{
  BOOL flag = YES;
  void doIt(elt e)
    {
      if (!((*aFunc)(e)))
	flag = NO;
    }
  [self withElementsCall:doIt whileTrue:&flag];
  return flag;
}

- (BOOL) trueForAllObjectsByCalling: (BOOL(*)(id))aFunc
{
  BOOL flag = YES;
  void doIt(elt e)
    {
      if (!((*aFunc)(e.id_u)))
	flag = NO;
    }
  CHECK_CONTAINS_OBJECTS_ERROR();
  [self withElementsCall:doIt whileTrue:&flag];
  return flag;
}

- (BOOL) trueForAnyElementsByCalling: (BOOL(*)(elt))aFunc;
{
  BOOL flag = YES;
  void doIt(elt e)
    {
      if ((*aFunc)(e))
	flag = NO;
    }
  [self withElementsCall:doIt whileTrue:&flag];
  return !flag;
}

- (BOOL) trueForAnyObjectsByCalling: (BOOL(*)(id))aFunc;
{
  BOOL flag = YES;
  void doIt(elt e)
    {
      if ((*aFunc)(e.id_u))
	flag = NO;
    }
  CHECK_CONTAINS_OBJECTS_ERROR();
  [self withElementsCall:doIt whileTrue:&flag];
  return !flag;
}

// Inefficient, so should be overridden in subclasses;
- (unsigned) count
{
  unsigned n = 0;
  void doIt(elt e)
    {
      n++;
    }
  [self withElementsCall:doIt];
  return n;
}

- (unsigned) occurrencesOfElement: (elt)anElement
{
  unsigned count = 0;
  int (*cf)(elt,elt) = [self comparisonFunction];
  void doIt(elt e) 
    {
      if (!((*cf)(anElement, e)))
	count++;
    }
  [self withElementsCall:doIt];
  return count;
}

- (unsigned) occurrencesOfObject: anObject
{
  CHECK_CONTAINS_OBJECTS_ERROR();
  return [self occurrencesOfElement:anObject];
}

- (BOOL) contentsAreObjects
{
  return CONTAINS_OBJECTS;
}

/* The two default implementations below are used by the node collection 
   objects: the collections whose contents are required to be objects
   conforming to some <...Comprising> protocol. */

/* some subclasses will have to override this for correctness */
- (const char *) contentType
{
  /* objects are the default */
  return @encode(id);
}

/* some subclasses will have to override this for correctness */
- (int(*)(elt,elt)) comparisonFunction
{
  /* objects are the default */
  return elt_compare_objects;
}

// ENUMERATING;

- (BOOL) getNextElement:(elt *)anElementPtr withEnumState: (void**)enumState
{
  [self subclassResponsibility:_cmd];
  return NO;
}

- (void*) newEnumState
{
  return (void*)0;
}

- freeEnumState: (void**)enumState
{
  *enumState = (void*)0;
  return self;
}

// Getting objects one at a time.  Pass *enumState == 0 to start.;
- (BOOL) getNextObject:(id *)anObjectPtr withEnumState: (void**)enumState;
{
  elt o;

  CHECK_CONTAINS_OBJECTS_ERROR();
  if ([self getNextElement:&o withEnumState:enumState])
    {
      *anObjectPtr = o.id_u;
      return YES;
    }
  return NO;
}

- withElementsCall: (void(*)(elt))aFunc whileTrue: (BOOL*)flag
{
  void *enumState = [self newEnumState];
  elt e;

  while (*flag && [self getNextElement:&e withEnumState:&enumState])
    (*aFunc)(e);
  [self freeEnumState:&enumState];
  return self;
}

- withElementsCall: (void(*)(elt))aFunc
{
  BOOL flag = YES;
  return [self withElementsCall:aFunc whileTrue:&flag];
}

- safeWithElementsCall: (void(*)(elt))aFunc
{
  id tmp = [[Array alloc] initWithContentsOf:self];
  [tmp withElementsCall:aFunc];
  [tmp free];
  return self;
}

- safeWithElementsCall: (void(*)(elt))aFunc whileTrue: (BOOL*)flag
{
  id tmp = [[Array alloc] initWithContentsOf:self];
  [tmp withElementsCall:aFunc whileTrue:flag];
  [tmp free];
  return self;
}

// COPYING;

- allocCopy
{
#if NeXT_runtime
  return object_copy(self, 0);
#else
  return object_copy(self);
#endif
}

// the copy to be filled by -shallowCopyAs: etc... ;
- emptyCopy
{
  // This will copy all instance vars;
  // Subclasses will have to change instance vars like Array's _contents_array;
  return [self allocCopy];
}

// the copy to be filled by -shallowCopyAs: etc... ;
- emptyCopyAs: (id <Collecting>)aCollectionClass
{
  if (aCollectionClass == [self species])
    return [self emptyCopy];
  else
    return [[(id)aCollectionClass alloc] 
	    initWithType:[self contentType]];
}

- shallowCopy
{
  return [self shallowCopyAs:[self species]];
}

- shallowCopyAs: (id <Collecting>)aCollectionClass
{
  id newColl = [self emptyCopyAs:aCollectionClass];
  [newColl addContentsOf:self];
  return newColl;
}

/* We can avoid the ugly [self safeWithElementsCall:doIt];
   in -deepen with something like this instead.
   This fits with a scheme in which we get rid of the -deepen method, an
   idea that I like since calling deepen on an object that has not just
   been -shallowCopy'ed can cause major memory leakage. */
- copyAs: (id <Collecting>)aCollectionClass
{
  id newColl = [self emptyCopyAs:aCollectionClass];
  void addCopy(elt e)
    {
      [newColl addElement:[e.id_u copy]];
    }
  if (CONTAINS_OBJECTS)
    [self withElementsCall:addCopy];
  else
    [newColl addContentsOf:self];
  return newColl;
}

- copy
{
  return [self copyAs:[self species]];
}

// This method shouldn't be necessary any more;
// (Yuck---replaceElement: is inefficient for many subclasses;
// Also, override this in KeyedCollection and IndexedCollection;)
- deepen
{
  // could use objc_msg_lookup here too;
  void doIt(elt o)
    {
      [self replaceElement:o with:[[o.id_u shallowCopy] deepen]];
    }

  [self error:"Collections don't use -deepen.  Use -copy instead."];
  if (!CONTAINS_OBJECTS)
    return self;
  [self safeWithElementsCall:doIt];
  return self;
}

- species
{
  return [self class];
}


// FILTERED ENUMERATING;

- withElementsTrueByCalling: (BOOL(*)(elt))testFunc 
    call: (void(*)(elt))destFunc
{
  void doIt(elt e)
    {
      if ((*testFunc)(e))
	(*destFunc)(e);
    }
  [self withElementsCall:doIt];
  return self;
}

- withObjectsTrueByCalling: (BOOL(*)(id))testFunc 
    call: (void(*)(id))destFunc
{
  void doIt(elt e)
    {
      if ((*testFunc)(e.id_u))
	(*destFunc)(e.id_u);
    }
  CHECK_CONTAINS_OBJECTS_ERROR();
  [self withElementsCall:doIt];
  return self;
}

- withElementsFalseByCalling: (BOOL(*)(elt))testFunc 
    call: (void(*)(elt))destFunc
{
  void doIt(elt e)
    {
      if (!(*testFunc)(e))
	(*destFunc)(e);
    }
  [self withElementsCall:doIt];
  return self;
}

- withObjectsFalseByCalling: (BOOL(*)(id))testFunc 
    call: (void(*)(id))destFunc
{
  void doIt(elt e)
    {
      if (!(*testFunc)(e.id_u))
	(*destFunc)(e.id_u);
    }
  CHECK_CONTAINS_OBJECTS_ERROR();
  [self withElementsCall:doIt];
  return self;
}

- withElementsTransformedByCalling: (elt(*)(elt))transFunc
    call: (void(*)(elt))destFunc
{
  void doIt(elt e)
    {
      (*destFunc)((*transFunc)(e));
    }
  [self withElementsCall:doIt];
  return self;
}

- withObjectsTransformedByCalling: (id(*)(id))transFunc
    call: (void(*)(id))destFunc
{
  void doIt(elt e)
    {
      (*destFunc)((*transFunc)(e.id_u));
    }
  CHECK_CONTAINS_OBJECTS_ERROR();
  [self withElementsCall:doIt];
  return self;
}


// NON-COPYING ENUMERATORS;

- (elt) detectElementByCalling: (BOOL(*)(elt))aFunc 
    ifNoneCall: (elt(*)(arglist_t))excFunc
{
  BOOL flag = YES;
  elt detectedElement;
  void doIt(elt e)
    {
      if ((*aFunc)(e)) {
	flag = NO;
	detectedElement = e;
      }
    }
  [self withElementsCall:doIt whileTrue:&flag];
  if (flag)
    RETURN_BY_CALLING_EXCEPTION_FUNCTION(excFunc);
  else
    return detectedElement;
}

- (elt) detectElementByCalling: (BOOL(*)(elt))aFunc
{
  elt err(arglist_t argFrame)
    {
      return NO_ELEMENT_FOUND_ERROR();
    }
  return [self detectElementByCalling:aFunc ifNoneCall:err];
}

- (elt) maxElementByCalling: (int(*)(elt,elt))aFunc
{
  elt max;
  BOOL firstTime = YES;
  void doIt(elt e)
    {
      if (firstTime)
	{
	  firstTime = NO;
	  max = e;
	}
      else
	{
	  if ((*aFunc)(e,max) > 0)
	    max = e;
	}
    }
  if (![self count])
    NO_ELEMENT_FOUND_ERROR();
  [self withElementsCall:doIt];
  return max;
}

- (elt) maxElement
{
  return [self maxElementByCalling:COMPARISON_FUNCTION];
}

- (elt) minElementByCalling: (int(*)(elt,elt))aFunc
{
  elt min;
  BOOL firstTime = YES;
  void doIt(elt e)
    {
      if (firstTime)
	{
	  firstTime = NO;
	  min = e;
	}
      else
	{
	  if ((*aFunc)(e,min) < 0)
	    min = e;
	}
    }
  if (![self count])
    NO_ELEMENT_FOUND_ERROR();
  [self withElementsCall:doIt];
  return min;
}

- (elt) minElement
{
  return [self minElementByCalling:COMPARISON_FUNCTION];
}

- (elt) injectElement: (elt)initialData byCalling: (elt(*)(elt,elt))aFunc
{
  void doIt(elt e)
    {
	initialData = (*aFunc)(initialData, e);
    }
  [self withElementsCall:doIt];
  return initialData;
}

- injectObject: (id)initialObject byCalling: (id(*)(id,id))aFunc
{
  void doIt(elt e)
    {
	initialObject = (*aFunc)(initialObject, e.id_u);
    }
  CHECK_CONTAINS_OBJECTS_ERROR();
  [self withElementsCall:doIt];
  return initialObject;
}


- maxObjectByCalling: (int(*)(id,id))aFunc
{
  id max;
  BOOL firstTime = YES;
  void doIt(id e)
    {
      if (firstTime)
	{
	  firstTime = NO;
	  max = e;
	}
      else
	{
	  if ((*aFunc)(e,max) > 0)
	    max = e;
	}
    }
  CHECK_CONTAINS_OBJECTS_ERROR();
  if (![self count])
    NO_ELEMENT_FOUND_ERROR();
  [self withObjectsCall:doIt];
  return max;
}

- maxObject
{
  CHECK_CONTAINS_OBJECTS_ERROR();
  return [self maxElement].id_u;
}

- minObjectByCalling: (int(*)(id,id))aFunc
{
  id min;
  BOOL firstTime = YES;
  void doIt(id e)
    {
      if (firstTime)
	{
	  firstTime = NO;
	  min = e;
	}
      else
	{
	  if ((*aFunc)(e,min) < 0)
	    min = e;
	}
    }
  CHECK_CONTAINS_OBJECTS_ERROR();
  if (![self count])
    NO_ELEMENT_FOUND_ERROR();
  [self withObjectsCall:doIt];
  return min;
}

- minObject
{
  CHECK_CONTAINS_OBJECTS_ERROR();
  return [self minElement].id_u;
}



// OBJECT-COMPATIBLE MESSAGE NAMES

// ADDING;

- addObject: newObject
{
  CHECK_CONTAINS_OBJECTS_ERROR();
  return [self addElement:newObject];
}

- addObjectIfAbsent: newObject
{
  CHECK_CONTAINS_OBJECTS_ERROR();
  return [self addElementIfAbsent:newObject];
}

- addObjectsCount: (unsigned)count, ...
{
  va_list ap;

  CHECK_CONTAINS_OBJECTS_ERROR();
  // could use objc_msg_lookup here also;
  va_start(ap, count);
  while (count--)
    [self addElement:va_arg(ap, id)];
  va_end(ap);
  return self;
}

// REMOVING AND REPLACING;

- removeObject: oldObject
{
  CHECK_CONTAINS_OBJECTS_ERROR();
  return [self removeElement:oldObject].id_u;
}

- removeObject: oldObject ifAbsentCall: (id(*)(arglist_t))excFunc
{
  elt elt_err(arglist_t argFrame)
    {
      RETURN_BY_CALLING_EXCEPTION_FUNCTION(excFunc);
    }
  CHECK_CONTAINS_OBJECTS_ERROR();
  return [self removeElement:oldObject ifAbsentCall:elt_err].id_u;
}

- removeAllOccurrencesOfObject: oldObject
{
  CHECK_CONTAINS_OBJECTS_ERROR();
  return [self removeAllOccurrencesOfElement:oldObject];
}

- replaceObject: oldObject with: newObject
    ifAbsentCall: (id(*)(arglist_t))excFunc
{
  elt elt_err(arglist_t argFrame)
    {
      RETURN_BY_CALLING_EXCEPTION_FUNCTION(excFunc);
    }
  CHECK_CONTAINS_OBJECTS_ERROR();
  return [self replaceElement:oldObject with: newObject 
	       ifAbsentCall:elt_err].id_u;
}
  
- replaceObject: oldObject with: newObject
{
  CHECK_CONTAINS_OBJECTS_ERROR();
  return [self replaceElement:oldObject with:newObject].id_u;
}

- replaceAllOccurrencesOfObject: oldObject with: newObject
{
  CHECK_CONTAINS_OBJECTS_ERROR();
  return [self replaceAllOccurrencesOfElement:oldObject with:newObject];
}

// TESTING;

- (BOOL) includesObject: anObject
{
  CHECK_CONTAINS_OBJECTS_ERROR();
  return [self includesElement:anObject];
}


// ENUMERATING

- withObjectsCall: (void(*)(id))aFunc
{
  void doIt(elt e)
    {
      (*aFunc)(e.id_u);
    }
  CHECK_CONTAINS_OBJECTS_ERROR();
  return [self withElementsCall:doIt];
}

- safeWithObjectsCall: (void(*)(id))aFunc
{
  void doIt(elt e)
    {
      (*aFunc)(e.id_u);
    }
  CHECK_CONTAINS_OBJECTS_ERROR();
  return [self safeWithElementsCall:doIt];
}

- withObjectsCall: (void(*)(id))aFunc whileTrue:(BOOL *)flag
{
  void doIt(elt e)
    {
      (*aFunc)(e.id_u);
    }
  CHECK_CONTAINS_OBJECTS_ERROR();
  return [self withElementsCall:doIt whileTrue:flag];
}

- safeWithObjectsCall: (void(*)(id))aFunc whileTrue:(BOOL *)flag
{
  void doIt(elt e)
    {
      (*aFunc)(e.id_u);
    }
  CHECK_CONTAINS_OBJECTS_ERROR();
  return [self safeWithElementsCall:doIt whileTrue:flag];
}

- makeObjectsPerform: (SEL)aSel
{
  void doIt(elt e)
    {
      [e.id_u perform:aSel];
    }

  CHECK_CONTAINS_OBJECTS_ERROR();
  [self withElementsCall:doIt];
  return self;
}

- safeMakeObjectsPerform: (SEL)aSel
{
  void doIt(elt e)
    {
      [e.id_u perform:aSel];
    }

  CHECK_CONTAINS_OBJECTS_ERROR();
  [self safeWithElementsCall:doIt];
  return self;
}

- safeMakeObjectsPerform: (SEL)aSel with: argObject
{
  void doIt(elt e)
    {
      [e.id_u perform:aSel with:argObject];
    }

  CHECK_CONTAINS_OBJECTS_ERROR();
  [self safeWithElementsCall:doIt];
  return self;
}

- makeObjectsPerform: (SEL)aSel with: argObject
{
  void doIt(elt e)
    {
      /* xxx change these to objc runtime functions,
	 in case object doesn't responds to "perform" methods. */
      [e.id_u perform:aSel with:argObject];
    }

  CHECK_CONTAINS_OBJECTS_ERROR();
  [self withElementsCall:doIt];
  return self;
}

/* xxx What about adding "-askObjectsPerform: (SEL)aSel with: argObject"
   that doesn't perform is object doesn't respond to aSel */

- withObjectsPerform: (SEL)aSel in: selObject
{
  id (*aSelImp)(id,SEL,id) = (id(*)(id,SEL,id))
    objc_msg_lookup(selObject, aSel);
  void doIt(elt e)
    {
      aSelImp(selObject, aSel, e.id_u);
    }

  CHECK_CONTAINS_OBJECTS_ERROR();
  [self withElementsCall:doIt];
  return self;
}

- safeWithObjectsPerform: (SEL)aSel in: selObject
{
  id (*aSelImp)(id,SEL,id) = (id(*)(id,SEL,id))
    objc_msg_lookup(selObject, aSel);
  void doIt(elt e)
    {
      aSelImp(selObject, aSel, e.id_u);
    }

  CHECK_CONTAINS_OBJECTS_ERROR();
  [self safeWithElementsCall:doIt];
  return self;
}

- withObjectsPerform: (SEL)aSel in: selObject with: argObject
{
  id (*aSelImp)(id,SEL,id,id) = (id(*)(id,SEL,id,id))
    objc_msg_lookup(selObject, aSel);
  void doIt(elt e)
    {
      aSelImp(selObject, aSel, e.id_u, argObject);
    }

  CHECK_CONTAINS_OBJECTS_ERROR();
  [self withElementsCall:doIt];
  return self;
}

- safeWithObjectsPerform: (SEL)aSel in: selObject with: argObject
{
  id (*aSelImp)(id,SEL,id,id) = (id(*)(id,SEL,id,id))
    objc_msg_lookup(selObject, aSel);
  void doIt(elt e)
    {
      aSelImp(selObject, aSel, e.id_u, argObject);
    }

  CHECK_CONTAINS_OBJECTS_ERROR();
  [self safeWithElementsCall:doIt];
  return self;
}


// NON-COPYING ENUMERATORS;

- detectObjectByCalling: (BOOL(*)(id))aFunc 
{
  id err(arglist_t argFrame)
    {
      return NO_ELEMENT_FOUND_ERROR();
    }
  return [self detectObjectByCalling:aFunc ifNoneCall:err];
}

- detectObjectByCalling: (BOOL(*)(id))aFunc 
    ifNoneCall: (id(*)(arglist_t))excFunc
{
  elt err(arglist_t argFrame)
    {
      RETURN_BY_CALLING_EXCEPTION_FUNCTION(excFunc);
    }
  BOOL test(elt e)
    {
      return (*aFunc)(e.id_u);
    }
  CHECK_CONTAINS_OBJECTS_ERROR();
  return [self detectElementByCalling:test ifNoneCall:err].id_u;
}

// This printing stuff will change when we get Stream objects;

- printElement: (elt)anElement
{
  elt_fprintf_elt(stdout, [self contentType], anElement);
  return self;
}

- printForDebugger
{
  void doIt(elt e)
    {
      [self printElement:e];
      printf(" ");
    }
  [self withElementsCall:doIt];
  printf(" :%s\n", [self name]);
  return self;
}

- _libobjectsMethodNotYetImplemented: (SEL)aSel
{
  [self error:"method %s in libobjects not yet implemented.\n\
Contact mccallum@gnu.ai.mit.edu (R. Andrew McCallum)\n\
for info about latest version.",
   sel_get_name(aSel)];
  return self;
}

- (const char *) libobjectsLicense
{
  const char *licenseString = 
    "Copyright (C) 1993,1994,1994 Free Software Foundation, Inc.\n"
    "\n"
    "Written by:  R. Andrew McCallum <mccallum@gnu.ai.mit.edu>\n"
    "Date: May 1993\n"
    "\n"
    "This object is part of the GNU Objective C Class Library.\n"
    "\n"
    "This library is free software; you can redistribute it and/or\n"
    "modify it under the terms of the GNU Library General Public\n"
    "License as published by the Free Software Foundation; either\n"
    "version 2 of the License, or (at your option) any later version.\n"
    "\n"
    "This library is distributed in the hope that it will be useful,\n"
    "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
    "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n"
    "Library General Public License for more details.\n"
    "\n"
    "You should have received a copy of the GNU Library General Public\n"
    "License along with this library; if not, write to the Free\n"
    "Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n";
  return licenseString;
}

- write: (TypedStream*)aStream
{
  [super write: aStream];
  [self _writeInit:aStream];
  [self _writeContents:aStream];
  return self;
}

- read: (TypedStream*)aStream
{
  [super read: aStream];
  [self _readInit:aStream];
  [self _readContents:aStream];
  return self;
}

- (void) encodeWithCoder: (Coder*)aCoder
{
  [self _encodeCollectionWithCoder:aCoder];
  [self _encodeContentsWithCoder:aCoder];
}

+ newWithCoder: (Coder*)aCoder
{
  id newCollection = [self _newCollectionWithCoder:aCoder];
  [newCollection _decodeContentsWithCoder:aCoder];
  return newCollection;
}

@end


@implementation Collection (ArchivingHelpers)

- (void) _encodeCollectionWithCoder: (Coder*) aCoder
{
  [super encodeWithCoder:aCoder];
  // there are no instance vars;
  return;
}

+ _newCollectionWithCoder: (Coder*) aCoder
{
  // there are no instance vars;
  return [super newWithCoder:aCoder];
}

- _writeInit: (TypedStream*)aStream
{
  // there are no instance vars;
  return self;
}

- _readInit: (TypedStream*)aStream
{
  // there are no instance vars;
  return self;
}

- (void) _encodeContentsWithCoder: (Coder*)aCoder
{
  unsigned int count = [self count];
  const char *encoding = [self contentType];
  void archiveElement(elt e)
    {
      [aCoder encodeValueOfType:encoding
	      at:elt_get_ptr_to_member(encoding, &e)
	      withName:"Collection element"];
    }

  [aCoder encodeValueOfSimpleType:@encode(unsigned)
	  at:&count
	  withName:"Collection element count"];
  [self withElementsCall:archiveElement];
}

- (void) _decodeContentsWithCoder: (Coder*)aCoder
{
  unsigned int count, i;
  elt newElement;  
  const char *encoding = [self contentType];

  [aCoder decodeValueOfSimpleType:@encode(unsigned)
	  at:&count
	  withName:NULL];
  for (i = 0; i < count; i++)
    {
      [aCoder decodeValueOfType:encoding
	      at:elt_get_ptr_to_member(encoding, &newElement)
	      withName:NULL];
      [self addElement:newElement];
    }
}

- _writeContents: (TypedStream*)aStream
{
  unsigned int count = [self count];
  const char *encoding = [self contentType];
  void archiveElement(elt e)
    {
      objc_write_type(aStream, encoding,
		      elt_get_ptr_to_member(encoding, &e));
    }

  objc_write_type(aStream, @encode(unsigned int), &count);
  [self withElementsCall:archiveElement];
  return self;
}

- _readContents: (TypedStream*)aStream
{
  unsigned int count, i;
  elt newElement;  
  const char *encoding = [self contentType];

  objc_read_type(aStream, @encode(unsigned int), &count);
  for (i = 0; i < count; i++)
    {
      objc_read_type(aStream, encoding, 
		     elt_get_ptr_to_member(encoding, &newElement));
      [self addElement:newElement];
    }
  return self;
}

@end

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