/* Allows access to a dictionary in an ordered fashion. */!!

inherit(Dictionary, #OrderedDictionary, #(orderKeys, /* collection to maintain the keys in order */), 2, 1)!!

now(class(OrderedDictionary))!!

now(OrderedDictionary)!!

/* Adds an Association to the Dictionary. */
Def addAssoc(self, anAssoc | idx)
{ if not(assocAt(self, key(anAssoc)))
  then add(orderKeys, key(anAssoc));
  endif;
  addAssoc(self:ancestor, anAssoc);
}!!

/* Evaluates the block over each of the
  receiver's Associations. */
 Def assocsDo(self, aBlock)
{ ^do(orderKeys,
  {using(aKey) eval(aBlock, assocAt(self, aKey));
  });
}!!

/* Enumerates over the elements in the
  OrderedDictionary. */
Def do(self, aBlock)
{^do(orderKeys,
  {using(aKey) eval(aBlock, self[aKey]);
  });
}!!

/* Copies elements into larger collection and swap with the old
  collection. */
Def grow(self | keys)
{ keys := orderKeys;
  grow(self:ancestor);
  orderKeys := keys;
}!!

/* Initializes the KeyedCollection by
  setting the tally instance variable
  to 0. */
 Def init(self)
{ init(self:ancestor);
  orderKeys := new(OrderedCollection, size(self));
}!!

/* Return the keys in proper order. */
Def keys(self)
{ ^copy(orderKeys);
}!!

/* Evaluates the one argument block over
  the keys of the Dictionary. */
 Def keysDo(self, aBlock)
{ ^do(orderKeys,
  {using(aKey) eval(aBlock, aKey);
  });
}!!

/* Replaces a current element or creates a new one. The put
  method for this class is identical to the add method except for
  the order of its arguments. */
Def put(self, anElement, aKey | keys val)
{ keys := orderKeys;
  if not(assocAt(self, aKey))
  then add(orderKeys, aKey);
  endif;
  val := put(self:ancestor, anElement, aKey);
  orderKeys := keys;
  ^val;
}!!

/* Removes the element with the specified key from the
  Dictionary.  If there is no element corresponding to aKey, then
  the 0-argument block is executed. The removeUsing method
  returns the removed key or the value of the block. */
Def removeUsing(self, aKey, aBlock | key idx)
{ if not(key := removeUsing(self:ancestor, aKey, { nil }))
  then ^eval(aBlock);
  endif;
  if class(orderKeys) == OrderedCollection
  then remove(orderKeys, find(orderKeys, aKey));
  else remove(orderKeys, aKey)
  endif;
  ^aKey;
}!!

/* Re-sorts self according to newCompareBlock,
  returns self. */
Def setCompareBlock(self, newCompareBlock)
{ setCompareBlock(orderKeys, newCompareBlock);
}!!

/* Set the class of the order for the keys. */
Def setOrderClass(self, aClass | newColl)
{ newColl := new(aClass, size(orderKeys));
  do(orderKeys,
  {using(aKey) add(newColl, aKey);
  });
  orderKeys := newColl;
}!!
