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

   Written by:  R. Andrew McCallum <mccallum@cs.rochester.edu>
   Dept. of Computer Science, U. of Rochester, Rochester, NY  14627

   This file is part of the GNU Objective-C Collection 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 <coll/Collection.h>
#include <coll/CollectionPrivate.h>
#include <objc/objc-api.h>
#include <stdarg.h>
#include <coll/Bag.h>		/* for -includesSameContents: */
#include <coll/Array.h>		/* for -safeWithElementsCall: */

@implementation Collection

+ initialize
{
  if (self == [Collection class])
    {
      [self setVersion:-1];	// alpha release;
    }
  return self;
}

// INITIALIZING AND FREEING;

// This is the designated initializer of this class;
- initEncoding:(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 initEncoding:@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 initEncoding:[aCollection contentEncoding]];
  [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)
    {
      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)
    {
      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) includesSameContents: (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])
    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;
}

// Deal with this in IndexedCollection also? ;
- (int) compare: anObject
{
  void *enumStateSelf, *enumStateOther;

  if ([self isEqual:anObject])
    return 0;
  else
    {
      // How should Collections be ordered? ;;
      
    }
  [self notImplemented:_cmd];
  return 0;
}

// Is this what we want?;
- (BOOL) isEqual: anObject
{
  if (self == anObject) 
    return YES;
  if ([anObject class] == [self class]
      && [self count] != [anObject count] 
      && [self includesSameContents: 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;
    }
  [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;
    }
  [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
{
  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 *) contentEncoding
{
  /* 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;
}

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

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

- withElementsCall: (void(*)(elt))aFunc whileTrue: (BOOL*)flag
{
  void *enumState = 0;
  elt e;

  while (*flag && [self getNextElement:&e withEnumState:&enumState])
    aFunc(e);
  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 addContentsOf:self];
  [tmp withElementsCall:aFunc];
  [tmp free];
  return self;
}

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

// COPYING;

- allocCopy
{
  return object_copy(self);
}

// 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] 
	    initEncoding:[self contentEncoding]];
}

- 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];
  if (CONTAINS_OBJECTS)
    [self withElementsCall:
	  LAMBDA(void, (elt e), {[newColl addElement:[e.id_u copy]];})
	  ];
  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;
}

/* Remember you can also get more flexible options when creating deep copies
   by doing something like: 
   id newColl = [self emptyCopyAs:[Bag class]];
   [self withElementsCall:
         LAMBDA(void, (elt e), {[newColl addObject:[e.id_u copy]];})];
*/

- 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);
    }
  [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);
    }
  [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));
    }
  [self withElementsCall:doIt];
  return self;
}

/***********************************************************************
shallowCopySelectByPerforming: (SEL)aBoolSel  can be done like this:
  id newColl = [self emptyCopy];
  [self withObjectsTrueByCalling:LAMBDA_BOOL_PERFORM(aBoolSel)
        call:LAMBDA(void, (id o), {[newColl addObject:o];})];

copySelectByPerforming: (SEL)aBoolSel  can be done like this:
  id newColl = [self emptyCopy];
  [self withObjectsTrueByCalling:LAMBDA_BOOL_PERFORM(aBoolSel)
        call:LAMBDA(void, (id o), {[newColl addObject:[o copy]];})];

***********************************************************************/




// 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);
  return detectedElement;
}

- (elt) detectElementByCalling: (BOOL(*)(elt))aFunc
{
  elt err(arglist_t argFrame)
    {
      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);
    }
  [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
{
  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
{
  return [self minElement].id_u;
}



// OBJECT-COMPATIBLE MESSAGE NAMES

// ADDING;

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

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

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

  // 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
{
  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);
    }
  return [self removeElement:oldObject ifAbsentCall:elt_err].id_u;
}

- removeAllOccurrencesOfObject: oldObject
{
  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);
    }
  return [self replaceElement:oldObject with: newObject 
	       ifAbsentCall:elt_err].id_u;
}
  
- replaceObject: oldObject with: newObject
{
  return [self replaceElement:oldObject with:newObject].id_u;
}

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

// TESTING;

- (BOOL) includesObject: anObject
{
  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 withElementsCall:doIt];
  return self;
}

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

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

- 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)
    {
      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)
    {
      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 contentEncoding], anElement);
  return self;
}

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

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

- 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;
}

@end


@implementation Collection (ArchivingHelpers)

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

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

- _writeContents: (TypedStream*)aStream
{
  unsigned int count = [self count];
  const char *encoding = [self contentEncoding];
  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 contentEncoding];

  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
