home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1995 August / NEBULA.mdf / Apps / DevTools / MachOViewer / Source / SortedList.m < prev    next >
Encoding:
Text File  |  1994-05-29  |  19.5 KB  |  655 lines

  1.  
  2. /*###########################################################
  3.     Written by Don McGregor  Version 1.0, Feb 1992.
  4.  
  5.     This code is free for any use.  GNU copyleft
  6.     rules apply.  This code has no warrantability
  7.     of fitness for a particular use, I am not 
  8.     responsible for any damage to data that might
  9.     be caused by bugs, etc, etc. 
  10.     
  11.     I encourage others to examine and improve on this
  12.     code, and to post the improvements to the net.
  13.     
  14.     Don McGregor, mcgregdr@conan.ie.orst.edu (for now).
  15.  
  16.     $Log:    SortedList.m,v $
  17. Revision 1.4  94/05/29  15:00:38  ediger
  18. change some printf format specs to eliminate compiler warnings
  19.  
  20. Revision 1.3  93/10/28  23:10:29  ediger
  21. corrected comparison between two UNSIGNED_LONG keys.  didn't do it at all
  22. correctly due to sloppy, cut-n-paste-without thinking style.
  23.  
  24. Revision 1.2  93/10/27  23:45:02  ediger
  25. SortedList that handles "unsigned longs" as one of the built-in data
  26. types it sorts on.  Still doesn't sort correctly, though.
  27.  
  28.  
  29.  ###########################################################*/
  30.  
  31. #import "SortedList.h"
  32.  
  33. @implementation SortedList
  34.  
  35. static char rcsident[] = "$Id";
  36.  
  37. + initialize
  38. {
  39.   /*-----------------------------------------------------------
  40.       Sets the class version; useful for unarchiving several
  41.       older versions of the object as time improves it.
  42.   -----------------------------------------------------------*/
  43.   
  44.   [SortedList setVersion:CURRENT_SORTED_LIST_VERSION];
  45.   
  46.   return self;
  47. }
  48.  
  49. - initCount:(unsigned int)numSlots
  50. {
  51.   /*-----------------------------------------------------------
  52.       Initialize the data and internal structures.  This should
  53.       not be used to reinitialize an already alloc'd and init'd
  54.       instance; instead, free the list first and then create
  55.       a new one.
  56.       
  57.       This is the designated initializer for the class.
  58.     -----------------------------------------------------------*/
  59.     
  60.     [super initCount:numSlots];
  61.                     
  62.     sortOrder         = ASCENDING;
  63.     caseSensitive     = YES;
  64.     keyDataType        = -1;        //causes error if not set
  65.                         //to something else
  66.       
  67.     return self;
  68. }
  69.  
  70. - setKeyMethod:(SEL)theMethod
  71. {
  72.   /*-----------------------------------------------------------
  73.       set the method used to sort the key by.  This uses the SEL to 
  74.       do that, so the user can set an arbitrary method by which
  75.       to sort, rather than forcing a particular method name to be
  76.       used on the objects that make up the sortedList.
  77.       
  78.       This also checks to make sure all the objects currently in
  79.       the list can respond to the message.  If any object can't,
  80.       nil is returned instead of self.  (The key method is still
  81.       reset, though.)  
  82.       
  83.       if the accessor method is new the SortedList is also completely
  84.        resorted when this method is called.  Since there is a new 
  85.       accessor method, the list order might be completely different.
  86.     -----------------------------------------------------------*/
  87.    int    i, listSize;
  88.    BOOL    uhOh     = NO;
  89.  
  90.    if(keyMethod != theMethod)
  91.      {
  92.        keyMethod = theMethod;
  93.    
  94.        listSize = [self count];  //Could have just directly gotten inst. var...
  95.    
  96.        for(i = 0; i < listSize; i++)
  97.            {
  98.                if(!([[self objectAt:i] respondsTo:keyMethod]))
  99.              uhOh = YES;
  100.            }
  101.      
  102.        if(uhOh == YES)        //Return nil, punt on trying to reorder list
  103.          return nil;
  104.      
  105.        [self insertionSort];      //new accessor method, new order too.        
  106.    }
  107.    
  108.    return self;
  109. }
  110.  
  111. - (SEL)keyMethod
  112. {
  113.   /*-----------------------------------------------------------
  114.       Returns the method by which the sortedList finds the
  115.       key the list is sorted by.
  116.     -----------------------------------------------------------*/
  117.  
  118.    return keyMethod;
  119. }
  120.  
  121. - setSortOrder:(int)theSortOrder
  122. {
  123.   /*-----------------------------------------------------------
  124.       Just sets the way in which the sortedList is maintained,
  125.       either ascending order (1,2,3,4...) or descending (5,4,3,2,1).
  126.       
  127.       Also resorts the list if this is a change from the prior
  128.       sort order.  This can be done by simply reversing the order
  129.       of the list; no fancy sorts needed.  This does it by removing
  130.       the top object and putting it at the end size times.
  131.       
  132.       Can't use addObject: in the for loop, even though it looks
  133.       like a good place for it; that method calls insertObject:at:
  134.       internally and sends the message to self.  that blows up 
  135.       when the message gets sent to this object.
  136.     -----------------------------------------------------------*/
  137.     int    i, size;
  138.     id    tempObj;
  139.     
  140.     if(sortOrder != theSortOrder)
  141.         {
  142.             sortOrder = theSortOrder;
  143.         size = [self count];
  144.         
  145.         for(i = 0; i < size; i++)
  146.           {
  147.               tempObj = [self removeObjectAt:0];
  148.             [super insertObject:tempObj at:(size - 1)];
  149.           }
  150.     }
  151.     
  152.     return self;
  153. }
  154.  
  155. - (int)sortOrder
  156. {
  157.   /*-----------------------------------------------------------
  158.       Returns the sort order.
  159.   -----------------------------------------------------------*/
  160.   
  161.   return sortOrder;
  162. }
  163.  
  164. -setCaseSensitive:(BOOL)isIt
  165. {
  166.   /*-----------------------------------------------------------
  167.      Whether or not compares on strings are case sensitive
  168.   -----------------------------------------------------------*/
  169.   
  170.   caseSensitive = isIt;
  171.   
  172.   return self;
  173. }
  174.  
  175. - (BOOL)caseSensitive
  176. {
  177.   return caseSensitive;
  178. }
  179.  
  180.  
  181. - setKeyDataType:(int)theKeyDataType
  182. {
  183.   /*-----------------------------------------------------------
  184.       sets a flag that tells of the type of data contained the
  185.       list is sorted by.  Right now this is  DOUBLE, FLOAT, INT, 
  186.       or ATOM (NXAtoms, the NeXT version of strings).
  187.     -----------------------------------------------------------*/
  188.     
  189.     keyDataType = theKeyDataType;
  190.     
  191.     return self;
  192. }
  193.  
  194. - (int)keyDataType
  195. {
  196.   /*-----------------------------------------------------------
  197.      Returns the type of data the list is sorted by.
  198.   -----------------------------------------------------------*/
  199.   
  200.   return keyDataType;
  201. }
  202.  
  203. - printKeyValues
  204. {
  205.   /*-----------------------------------------------------------
  206.       Prints out the key values of the objects in the sortedList,
  207.       in the order they are stored in the list.  This is really
  208.       just an internal debugging sort of thing, but most people
  209.       wind up using something like this sooner or later.
  210.       
  211.       This method should not be called in any production code.
  212.   -----------------------------------------------------------*/
  213.   
  214.   int    count, i;
  215.   id    listObject;
  216.   
  217.   printf("---------------\n");
  218.   count = [self count];
  219.   
  220.   for(i = 0; i < count; i++)
  221.     {
  222.   
  223.       listObject = [self objectAt:i];
  224.     
  225.       switch(keyDataType)
  226.             {
  227.                   case DOUBLE:    
  228.                          printf("%d: %g, for object %p\n", i, 
  229.                       ((double(*)())[listObject methodFor:keyMethod])(), listObject);
  230.                      break;
  231.             case FLOAT:    
  232.                          printf("%d: %g, for object %p\n", i, 
  233.                       ((float(*)())[listObject methodFor:keyMethod])(), listObject);
  234.                      break;
  235.             case INT:    
  236.                          printf("%d: %d, for object %p\n", i, 
  237.                       ((int(*)())[listObject methodFor:keyMethod])(), listObject);
  238.                      break;
  239.  
  240.             case UNSIGNED_LONG:    
  241.                          printf("%lx: %lx, for object %p\n", i, 
  242.                       ((int(*)())[listObject methodFor:keyMethod])(), listObject);
  243.                      break;
  244.  
  245.                   
  246.             case ATOM:         printf("%d: %s, for object %p\n", i,
  247.                          (char *)[[self objectAt:i] perform:keyMethod], listObject);
  248.                      break;
  249.                      
  250.             default:         [self error:"attempt print contents of SortedList when the key value of object is of unknown type"];
  251.                          break;
  252.         }
  253.         
  254.    }
  255.    
  256.  printf("---------------\n");
  257.  
  258.  
  259.  return self;
  260. }
  261.  
  262.  
  263. - addObject:anObject
  264. {
  265.   /*-----------------------------------------------------------
  266.      Adds an object to the sortedList.  Uses a binary search to
  267.      find the insert point.
  268.      
  269.      By convention, the "top" of the list is at 0, and the "bottom"
  270.      of the list is at the last slot.  Conceptually this is a 
  271.      top-to bottom arrangement, a convention followed in naming
  272.      the variables here.
  273.      
  274.      If the object does not respond to the accessor method, we
  275.      should catch it here and punt big-time.
  276.    -----------------------------------------------------------*/
  277.    
  278.    int    topPtr = 0, bottomPtr, pivot;
  279.    int    compareResult;
  280.    
  281.    bottomPtr     = [self count] - 1;
  282.    pivot     = (int)((topPtr + bottomPtr)/2);
  283.    
  284.        //Make sure the new object can respond to the accessor
  285.        //method.
  286.     
  287.    if(!([anObject respondsTo:keyMethod]))
  288.       {
  289.        [self error:"attempt to add object to SortedList without correct accesor method.  The object was of Class %s\n", [anObject name]];
  290.         return nil;
  291.       }
  292.  
  293.        //empty list; insert and get out of here
  294.     
  295.    if(bottomPtr == -1)    
  296.      {
  297.        [super insertObject:anObject at:0];
  298.        return self;
  299.      }  
  300.    
  301.    
  302.        //The real interest.  Do a binary search until the insertion pt
  303.     //is found.
  304.     
  305.    compareResult = [self compare:[self objectAt:pivot] to:anObject];
  306.  
  307.    do
  308.      {      
  309.        if(compareResult > 0)
  310.                   bottomPtr     = pivot - 1;
  311.          else
  312.             topPtr     = pivot + 1;
  313.             
  314.        pivot = (int)((topPtr + bottomPtr)/2);
  315.     compareResult = [self compare:[self objectAt:pivot] to:anObject];
  316.      }
  317.    while ((compareResult != 0) && (topPtr < bottomPtr));
  318.    
  319.        //Insert the new object
  320.     
  321.    if(compareResult >= 0)
  322.       [super insertObject:anObject at:pivot];
  323.      else 
  324.          [super insertObject:anObject at:pivot+1];
  325.  
  326.   return self;
  327. }
  328.  
  329. - addObjectIfAbsent:anObject
  330. {
  331.   /*-----------------------------------------------------------
  332.      More overrides of List methods.  We need to make sure all additions
  333.      are done through the addObject: method to ensure it all stays
  334.      sorted.
  335.   -----------------------------------------------------------*/
  336.   
  337.  if([self indexOf:anObject] == NX_NOT_IN_LIST)
  338.      {
  339.        [self addObject:anObject];
  340.      }
  341.  return self;
  342. }
  343.  
  344. - (BOOL)isEqual:anObject
  345. {
  346.   /*-----------------------------------------------------------
  347.      Similar to the list op,which compares the to lists for equality.
  348.      We also need to check for the new instance vars declared
  349.      in this object, and make sure this is a SortedList rather
  350.      than just a List.
  351.   -----------------------------------------------------------*/
  352.   
  353.   if([self class] != [anObject class])
  354.      return NO;
  355.   
  356.   if(([anObject sortOrder]     != [self sortOrder])      ||
  357.      ([anObject keyMethod]     != [self keyMethod])      ||
  358.      ([anObject caseSensitive]     != [self caseSensitive]) ||
  359.      ([anObject keyDataType]     != [self keyDataType]))
  360.         return NO;
  361.  
  362.    return [super isEqual:anObject];
  363. }
  364.   
  365.  
  366.  
  367.  
  368.  
  369. - (int)compare:thisObject to:thatObject
  370. {
  371.   /*-----------------------------------------------------------
  372.      Compares the values of the keys of thisObject and thatObject.
  373.      
  374.      returns a number larger than zero if the first object's key is 
  375.      larger than the other object's, a number smaller than zero if  
  376.      the first object is less than the second, and zero if they have 
  377.      the same key values.  For complex sorts, "larger" means that it
  378.      comes after the object when sorted in ascending order.
  379.      
  380.      This can deal with ints, doubles, floats, and atoms at this point.
  381.      Feel free to expand it to other types.   
  382.      
  383.      There is a built-in capability to expand this to other comparision
  384.      (sorting) operations, perhaps with complex structs or multiple
  385.      keys.  If the return type is not one of the pre-defined (and fairly
  386.      common) types such as int, double, or atom, the program
  387.      sends the SEL message to thisObject with an argument of thatObject.
  388.      The OBJECTS IN THE LIST should respond to this message and return
  389.      the results of a comparision operation.  Eg,
  390.      
  391.      result = [thisObject compareTo:thatObject];
  392.      return result;
  393.      
  394.      where the compareTo: method takes the other object as an arg,
  395.      and does the possibly complex comparision.  If thisObject is
  396.      "greater" than thatObject, by whatever criterion, the method
  397.      should return an int greater than zero.  If thisObject is "less"
  398.      than thatObject, it should return a negative integer, and if the
  399.      two are equal zero should be returned.
  400.      
  401.      The ATOM type is case sensitive by default.  The default string
  402.      ordering table is used.  You should be using NXUniqueString to to
  403.      the atoms before passing them in, and the should be null-terminated.
  404.      
  405.     -----------------------------------------------------------*/
  406.    
  407.        //protos for ptr to function that returns a <type> and takes the usual
  408.     //objc parameters: an id and a SEL.
  409.    typedef    double (*keyValueFunctionDouble)(id, SEL);
  410.    typedef    int    (*keyValueFunctionInt)(id, SEL);
  411.    typedef    NXAtom (*keyValueFunctionAtom)(id, SEL);
  412.    typedef    float  (*keyValueFunctionFloat)(id, SEL);
  413.    typedef    unsigned long    (*keyValueFunctionUnsignedLong)(id, SEL);
  414.    
  415.        //pointer to key value function for each of the objects passed in.
  416.     //A function pointer for each type that the sortedList can hold.
  417.         
  418.    keyValueFunctionDouble    thisObjectKeyFunctionDouble, 
  419.                    thatObjectKeyFunctionDouble;
  420.                 
  421.    keyValueFunctionInt        thisObjectKeyFunctionInt, 
  422.                    thatObjectKeyFunctionInt;
  423.                                 
  424.    keyValueFunctionUnsignedLong        thisObjectKeyFunctionUnsignedLong, 
  425.                    thatObjectKeyFunctionUnsignedLong;
  426.                                 
  427.    keyValueFunctionFloat    thisObjectKeyFunctionFloat, 
  428.                    thatObjectKeyFunctionFloat;                                
  429.             
  430.    double        compareResults;        //difference between two numerics
  431.    int            orderFlag;
  432.    
  433.    
  434.        //This can be either ascending or descending.  To do with compares we'll
  435.     //just use a flag, -1 or 1, and multiply that by the integer result.
  436.     //that'll flip the comparision results depending on asc or desc.
  437.     
  438.    orderFlag = (sortOrder == ASCENDING) ? 1 : -1;
  439.       
  440.    switch(keyDataType)
  441.      {
  442.          case DOUBLE:    
  443.              thisObjectKeyFunctionDouble = (keyValueFunctionDouble)[thisObject methodFor:keyMethod];
  444.                thatObjectKeyFunctionDouble = (keyValueFunctionDouble)[thatObject methodFor:keyMethod];
  445.      
  446.              compareResults =    ( thisObjectKeyFunctionDouble(thisObject, keyMethod) -
  447.                               thatObjectKeyFunctionDouble(thatObject, keyMethod));
  448.             
  449.                 //return -1, 0, or 1.
  450.             if(compareResults < 0) return orderFlag * (-1);
  451.             if(compareResults > 0) return orderFlag * 1;
  452.             return 0;
  453.             
  454.             break;
  455.             
  456.      case FLOAT:    
  457.              thisObjectKeyFunctionFloat = (keyValueFunctionFloat)[thisObject methodFor:keyMethod];
  458.                thatObjectKeyFunctionFloat = (keyValueFunctionFloat)[thatObject methodFor:keyMethod];
  459.              compareResults =    (double)( thisObjectKeyFunctionFloat(thisObject, keyMethod) -
  460.                               thatObjectKeyFunctionFloat(thatObject, keyMethod));
  461.             
  462.                 //return -1, 0, or 1.
  463.             if(compareResults < 0) return orderFlag * (-1);
  464.             if(compareResults > 0) return orderFlag * 1;
  465.             return 0;
  466.             
  467.             break;
  468.  
  469.     case INT:    
  470.              thisObjectKeyFunctionInt = (keyValueFunctionInt)[thisObject methodFor:keyMethod];
  471.                thatObjectKeyFunctionInt = (keyValueFunctionInt)[thatObject methodFor:keyMethod];
  472.      
  473.              compareResults =    (double)( thisObjectKeyFunctionInt(thisObject, keyMethod) -
  474.                               thatObjectKeyFunctionInt(thatObject, keyMethod));
  475.             
  476.                 //return -1, 0, or 1.
  477.             if(compareResults < 0) return orderFlag * (-1);
  478.             if(compareResults > 0) return orderFlag * 1;
  479.             return 0;
  480.             
  481.             break;
  482.             
  483.     case UNSIGNED_LONG:    
  484.              thisObjectKeyFunctionUnsignedLong
  485.             = (keyValueFunctionUnsignedLong)[thisObject methodFor:keyMethod];
  486.                thatObjectKeyFunctionUnsignedLong
  487.             = (keyValueFunctionUnsignedLong)[thatObject methodFor:keyMethod];
  488.      
  489.             //return -1, 0, or 1.
  490.             if(thisObjectKeyFunctionUnsignedLong(thisObject, keyMethod)
  491.             < thatObjectKeyFunctionUnsignedLong(thatObject, keyMethod))
  492.                  return orderFlag * (-1);
  493.  
  494.             if(thisObjectKeyFunctionUnsignedLong(thisObject, keyMethod)
  495.             > thatObjectKeyFunctionUnsignedLong(thatObject, keyMethod))
  496.                  return orderFlag * 1;
  497.  
  498.             return 0;
  499.             
  500.             break;
  501.             
  502.      case ATOM:    
  503.              return orderFlag * NXOrderStrings((unsigned char*)[thisObject perform:keyMethod],
  504.                            (unsigned char *)[thatObject perform:keyMethod],
  505.                           caseSensitive, -1, NULL);
  506.             break;
  507.             
  508.      case OTHER:    
  509.              return orderFlag * (int)[thisObject perform:keyMethod with:thisObject];
  510.              break;
  511.             
  512.     default:    [self error:"unspecified type of comparision operator\n"];
  513.     }
  514.     
  515.     return 0;        //foo-faw to fool the compiler; should never get here
  516. }
  517.  
  518.  
  519. - insertionSort;
  520. {
  521.   /*-----------------------------------------------------------
  522.      do a simple insertion sort.  This is good for files that
  523.      are "alomst sorted", as I suspect that many data sets
  524.      will be in this application.  Besides, it's easy to program
  525.      and I'm lazy right now :-)
  526.      
  527.      I'm sure someone can do something more sophisticated 
  528.      than this.
  529.   -----------------------------------------------------------*/
  530.   
  531.   int    i, j, dataSize, compareResult;
  532.   id    listEl;
  533.   
  534.   dataSize = [self count];
  535.   
  536.   for(i = 1; i < dataSize; i++)
  537.     {
  538.       j = i-1;
  539.       listEl = [self objectAt:i];
  540.       
  541.       while(((compareResult = [self compare:[self objectAt:j] to:listEl]) > 0) &&
  542.                         (j >=1))
  543.     j--;
  544.       
  545.       if(compareResult > 0)
  546.         {
  547.             [super removeObjectAt:i];
  548.           [super insertObject:listEl at:j];
  549.     }
  550.     
  551.    }
  552.    
  553.  return self;
  554. }
  555.         
  556.     
  557. -copy
  558. {
  559.   /*-----------------------------------------------------------
  560.      An override to keep things hunky-dory with all the instance 
  561.      vars.
  562.   -----------------------------------------------------------*/
  563.   
  564.   id    newList;
  565.   
  566.   newList = [super copy];
  567.   
  568.   [newList setKeyMethod:    [self keyMethod]];
  569.   [newList setSortOrder:    [self sortOrder]];
  570.   [newList setCaseSensitive:    [self caseSensitive]];
  571.   [newList setKeyDataType:    [self keyDataType]];
  572.   
  573.   return newList;
  574. }
  575.   
  576. -copyFromZone:(NXZone*)zone
  577. {
  578.   /*-----------------------------------------------------------
  579.       Ditto above method.
  580.   -----------------------------------------------------------*/
  581.   
  582.   id    newList;
  583.   
  584.   newList = [super copyFromZone:zone];
  585.   
  586.   [newList setKeyMethod:    [self keyMethod]];
  587.   [newList setSortOrder:    [self sortOrder]];
  588.   [newList setCaseSensitive:    [self caseSensitive]];
  589.   [newList setKeyDataType:    [self keyDataType]];
  590.   
  591.   return newList;
  592. }
  593.  
  594.  
  595. /*-----------------------------------------------------------  
  596.          Methods that return a rutime error if called 
  597.   -----------------------------------------------------------*/
  598.  
  599. - insertObject:anObject at:(unsigned int)index
  600. {
  601.   [self error:" objects should not be sent insertObject:at: messages.\n"];
  602.     
  603.   return self;    //foo-faw to keep the compiler happy 'bout return types
  604. }
  605.  
  606. - replaceObjectAt:(unsigned int)index with:newObject 
  607. {
  608.   [self error:"%s objects should not be sent replaceObjectAt:with: messages.\n"];
  609.     
  610.   return self;    //foo-faw to keep the compiler happy 'bout return types
  611.  
  612. }
  613.  
  614. - replaceObject:anObject with:newObject
  615. {
  616.   [self error:"%s objects should not be sent replaceObject:with: messages.\n"];
  617.     
  618.   return self;    //foo-faw to keep the compiler happy 'bout return types
  619.  
  620. }
  621.  
  622. /*-----------------------------------------------------------
  623.                     Archiving methods
  624. -----------------------------------------------------------*/
  625.  
  626. -write:(NXTypedStream*)stream
  627. {
  628.   /*-----------------------------------------------------------
  629.      Write out the instance variables and the superclass
  630.      object structure.
  631.   -----------------------------------------------------------*/
  632.   
  633.   [super write:stream];
  634.   NXWriteTypes(stream, "ic:i", &sortOrder, 
  635.           &caseSensitive, &keyMethod, &keyDataType);
  636.             
  637.   return self;
  638. }
  639.  
  640. - read:(NXTypedStream*)stream
  641. {
  642.   /*-----------------------------------------------------------
  643.     Read the object back in
  644.   -----------------------------------------------------------*/
  645.   
  646.   [super read:stream];
  647.   NXReadTypes(stream, "ic:i", &sortOrder, 
  648.           &caseSensitive, &keyMethod, &keyDataType);
  649.  
  650.   return self;
  651. }
  652.  
  653.   
  654. @end
  655.