unit Collect; { Collection classes for Delphi 2.0 Alin Flaider, 1996 aflaidar@datalog.ro } interface uses Windows, Classes, Sysutils; const coIndexError = -1; { Index out of range } coOverflow = -2; { Overflow } coUnderflow = -3; { Underflow } type CollException = class(Exception); TCollection = class( TObject) private { return item at index position } function At( Index : integer) : Pointer; { replace item at index position} procedure AtPut( Index : integer; Item : Pointer); protected It : PPointerList; { array of pointers } Limit : integer; { Current Allocated size of array} Delta : integer; {Number of items by which the collection grows when full} { deletes item at index position } procedure AtDelete (Index : integer); { generates CollException } procedure Error (Code,Info : Integer); virtual; { destroys specified Item; override this method if Item is not a descendant of TObject } procedure FreeItem (Item : Pointer); virtual; public Count : integer; {Current Number of Items} constructor create(aLimit, aDelta : integer); {before deallocating object it disposes all items and the storage array} destructor destroy; override; {inserts Item at specified position } procedure AtInsert( Index : integer; Item : Pointer); {deletes and disposes Item at specified position} procedure AtFree(Index: Integer); {deletes Item} procedure Delete( Item : Pointer); {deletes all Items without disposing them } procedure DeleteAll; {formerly Free, renamed to Clear to avoid bypassing inherited TObject.Free; deletes and disposes Item } procedure Clear(Item: Pointer); {finds first item that satisfies condition specified in function Test( Item: pointer): boolean} function FirstThat( Test : Pointer) : Pointer; {finds last item that satisfies condition specified in function Test( Item: pointer): boolean} function LastThat( Test : Pointer) : Pointer; {calls procedure Action( Item: pointer) for each item in collection} procedure ForEach( Action : Pointer); {disposes all items; set counter to zero} procedure FreeAll; {finds position of Item using a linear search} function IndexOf( Item : Pointer) : integer; virtual; {inserts Item at the end of collection} procedure Insert( Item : Pointer); virtual; {packs collection by removing nil Items} procedure Pack; {expands array of pointers } procedure SetLimit( aLimit : integer);virtual; {direct access to items through position} property Items[Index: integer]: pointer read At write AtPut; default; end; TSortedCollection = class(TCollection) Duplicates: boolean; {if true, rejects item whose key already exists} {override this method to specify relation bewtween two keys 1 if Key1 comes after Key2, -1 if Key1 comes before Key2, 0 if Key1 is equivalent to Key2} function Compare (Key1,Key2 : Pointer): Integer; virtual; abstract; {returns key of Item} function KeyOf (Item : Pointer): Pointer; virtual; {finds index of item by calling Search} function IndexOf (Item : Pointer): integer; virtual; {finds item required position and performs insertion } procedure Insert (Item : Pointer); virtual; {finds index of item by performing an optimised search} function Search (key : Pointer; Var Index : integer) : Boolean; virtual; end; implementation constructor TCollection.Create(ALimit, ADelta: Integer); begin inherited Create; Limit:= 0; Delta:=aDelta; Count:=0; It := nil; SetLimit( ALimit); end; destructor TCollection.Destroy; begin FreeAll; SetLimit(0); inherited Destroy; end; function TCollection.At(Index: Integer): Pointer; begin If Index > pred(Count) then begin Error(coIndexError,0); Result :=nil; end else Result := It^[Index]; end; procedure TCollection.AtPut(Index: Integer; Item: Pointer); begin if (Index < 0) or (Index >= Count) then Error(coIndexError,0) else It^[Index] := Item; end; procedure TCollection.AtDelete(Index: Integer); var p: pointer; begin if (Index < 0) or (Index >= Count) then begin Error(coIndexError,0); exit; end; if Index < pred(Count) then move( It^[succ(Index)], It^[Index], (count-index)*sizeof(pointer)); Dec(Count); end; procedure TCollection.AtInsert( Index: integer; Item: pointer); var i : integer; begin if (Index < 0) or ( Index > Count) then begin Error(coIndexError,0); exit; end; if Limit = Count then begin if Delta = 0 then begin Error(coOverFlow,0); exit; end; SetLimit( Limit+Delta); end; If Index <> Count then {move compensates for overlaps} move( It^[Index], It^[Index+1], (count - index)*sizeof(pointer)); It^[Index] := Item; Inc(Count); end; procedure TCollection.Delete( Item: pointer); begin AtDelete(Indexof(Item)); end; procedure TCollection.DeleteAll; begin Count:=0 end; procedure TCollection.Error(Code, Info: Integer); begin case Code of coIndexError: raise CollException.Create('Collection error; wrong index: '+IntToStr(Info)); coOverflow: raise CollException.Create('Collection overflow - cannot grow!'); coUnderflow: raise CollException.Create('Collection underflow - cannot shrink!'); end end; function TCollection.FirstThat(Test: Pointer): Pointer; type tTestFunc = function( p : pointer) : Boolean; var i : integer; begin Result := nil; for i := 0 to pred(count) do if tTestFunc(test)(It^[i]) then begin Result := It[i]; break end end; procedure TCollection.ForEach(Action: Pointer); type tActionProc = procedure(p : pointer); var i : integer; begin for i := 0 to pred(Count) do tActionProc(Action)(It^[i]); end; procedure TCollection.Clear(Item: Pointer); begin Delete(Item); FreeItem(Item); end; procedure TCollection.FreeAll; var i : integer; begin for I := 0 to Count - 1 do FreeItem(At(I)); Count := 0; end; procedure TCollection.FreeItem(Item: Pointer); begin if Item <> nil then TObject(Item).Free; end; function TCollection.IndexOf(Item: Pointer): integer; var i : integer; begin Result := -1; for i := 0 to pred(count) do if Item = It^[i] then begin Result := i; break end end; procedure TCollection.Insert(Item: Pointer); begin AtInsert(Count,Item); end; function TCollection.LastThat(Test: Pointer): pointer; type tTestFunc = function( p : pointer) : Boolean; var i : integer; begin Result := nil; for i := pred(count) downto 1 do if tTestFunc(test)(It^[i]) then begin Result := It^[i]; break end end; procedure TCollection.Pack; var i: integer; begin for i := pred(count) downto 0 do if It^[i] = nil then AtDelete(i); end; procedure TCollection.SetLimit(ALimit: Integer); begin if (ALimit < Count) then Error( coUnderFlow , 0); if ALimit <> Limit then begin ReallocMem( It, ALimit* SizeOf(Pointer)); Limit := ALimit; end; end; function TSortedCollection.IndexOf(Item: Pointer): Integer; var i: Integer; begin IndexOf := -1; if Search(KeyOf(Item), i) then begin if Duplicates then while (i < Count) and (Item <> It^[I]) do Inc(i); if i < Count then IndexOf := i; end; end; procedure TSortedCollection.Insert(Item: Pointer); var i : integer; begin if not Search(KeyOf(Item), I) or Duplicates then AtInsert(I, Item); end; function TSortedCollection.KeyOf(Item: Pointer): Pointer; begin Result := Item; end; function TSortedCollection.Search; var L, H, I, C: Integer; begin Search := False; L := 0; H := Count - 1; while L <= H do begin I := (L + H) shr 1; C := Compare(KeyOf(It^[I]), Key); if C < 0 then L := I + 1 else begin H := I - 1; if C = 0 then begin Search := True; if not Duplicates then L := I; end; end; end; Index := L; end; procedure TCollection.AtFree(Index: Integer); var Item: Pointer; begin Item := At(Index); AtDelete(Index); FreeItem(Item); end; end.