unit CollPArray;

(*****************************************************************************
 * Copyright 2003 by Matthew Greet
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation; either version 2.1 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 Lesser General Public License for more
 * details. (http://opensource.org/licenses/lgpl-license.php)
 * 
 * See http://www.warmachine.u-net.com/delphi_collections for updates and downloads.
 *
 * $Version: v1.0.3 $
 * $Revision: 1.2.1.2 $
 * $Log: D:\QVCS Repositories\Delphi Collections\CollPArray.qbt $
 * 
 *   Collection implementations based on TList.
 * 
 * Revision 1.2.1.2  by: Matthew Greet  Rev date: 12/06/04 20:08:30
 *   Capacity property.
 * 
 * Revision 1.2.1.1  by: Matthew Greet  Rev date: 14/02/04 17:46:10
 *   v1.0 branch.
 * 
 * Revision 1.2  by: Matthew Greet  Rev date: 28/04/03 15:07:14
 *   Correctly handles nil items.
 * 
 * Revision 1.1  by: Matthew Greet  Rev date: 06/04/03 10:43:16
 *   Added TPArrayMap and TExposedPArrayList.
 * 
 * Revision 1.0  by: Matthew Greet  Rev date: 01/03/03 10:50:02
 *   Initial revision.
 * 
 * FPC compatibility fixes by: UltraStar Deluxe Team
 *
 * $Endlog$
 *****************************************************************************)

{$IFDEF FPC}
  {$MODE Delphi}{$H+}
{$ENDIF}
 
interface

uses
    Classes,
    Collections;

type
    TPArrayBag = class(TAbstractBag)
    private
        FList: TList;
    protected
        function TrueAdd(const Item: ICollectable): Boolean; override;
        procedure TrueClear; override;
        function TrueRemove(const Item: ICollectable): ICollectable; override;
        function TrueRemoveAll(const Item: ICollectable): ICollection; override;
    public
        constructor Create(NaturalItemsOnly: Boolean); override;
        destructor Destroy; override;
        function GetCapacity: Integer; override;
        procedure SetCapacity(Value: Integer); override;
        function GetIterator: IIterator; override;
        function GetSize: Integer; override;
        function TrueContains(const Item: ICollectable): Boolean; override;
    end;

    TPArraySet = class(TAbstractSet)
    private
        FList: TList;
    protected
        function GetPosition(const Item: ICollectable): TCollectionPosition; override;
        procedure TrueAdd2(Position: TCollectionPosition; const Item: ICollectable); override;
        procedure TrueClear; override;
        function TrueGet(Position: TCollectionPosition): ICollectable; override;
        procedure TrueRemove2(Position: TCollectionPosition); override;
    public
        constructor Create(NaturalItemsOnly: Boolean); override;
        destructor Destroy; override;
        function GetCapacity: Integer; override;
        procedure SetCapacity(Value: Integer); override;
        function GetIterator: IIterator; override;
        function GetSize: Integer; override;
    end;

    TPArrayList = class(TAbstractList)
    private
        FList: TList;
    protected
        function TrueGetItem(Index: Integer): ICollectable; override;
        procedure TrueSetItem(Index: Integer; const Item: ICollectable); override;
        procedure TrueAppend(const Item: ICollectable); override;
        procedure TrueClear; override;
        function TrueDelete(Index: Integer): ICollectable; override;
        procedure TrueInsert(Index: Integer; const Item: ICollectable); override;
    public
        constructor Create(NaturalItemsOnly: Boolean); override;
        destructor Destroy; override;
        function GetCapacity: Integer; override;
        procedure SetCapacity(Value: Integer); override;
        function GetIterator: IIterator; override;
        function GetSize: Integer; override;
    end;

    TPArrayMap = class(TAbstractMap)
    private
        FList: TList;
    protected
        function GetAssociationIterator: IMapIterator; override;
        function GetKeyPosition(const Key: ICollectable): TCollectionPosition; override;
        procedure TrueClear; override;
        function TrueGet(Position: TCollectionPosition): IAssociation; override;
        function TruePut(Position: TCollectionPosition; const Association: IAssociation): IAssociation; override;
        function TrueRemove2(Position: TCollectionPosition): IAssociation; override;
    public
        constructor Create(NaturalItemsOnly: Boolean; NaturalKeysOnly: Boolean); override;
        destructor Destroy; override;
        function GetCapacity: Integer; override;
        procedure SetCapacity(Value: Integer); override;
        function GetSize: Integer; override;
    end;

    // Same as TPArrayList but raises method visibilities so items can be manually
    // appended or inserted without resetting sort flag.
    TExposedPArrayList = class(TPArrayList)
    public
        procedure TrueAppend(const Item: ICollectable); override;
        procedure TrueInsert(Index: Integer; const Item: ICollectable); override;
    end;


implementation

type
    TPArrayIterator = class(TAbstractIterator)
    private
        FList: TList;
        FIndex: Integer;
    protected
        constructor Create(List: TList; AllowRemove: Boolean);
        function TrueFirst: ICollectable; override;
        function TrueNext: ICollectable; override;
        procedure TrueRemove; override;
    end;

    TPArrayAssociationIterator = class(TAbstractAssociationIterator)
    private
        FList: TList;
        FIndex: Integer;
    protected
        constructor Create(List: TList; AllowRemove: Boolean);
        function TrueFirst: IAssociation; override;
        function TrueNext: IAssociation; override;
        procedure TrueRemove; override;
    end;

    TPArrayPosition = class(TCollectionPosition)
    private
        FIndex: Integer;
    public
        constructor Create(Found: Boolean; Index: Integer);
        property Index: Integer read FIndex;
    end;

constructor TPArrayBag.Create(NaturalItemsOnly: Boolean);
begin
    inherited Create(NaturalItemsOnly);
    FList := TList.Create;
end;

destructor TPArrayBag.Destroy;
begin
    FList.Free;
    inherited Destroy;
end;

function TPArrayBag.TrueAdd(const Item: ICollectable): Boolean;
begin
    FList.Add(Pointer(Item));
    Result := true;
    // Storing interface reference as a pointer does not update reference
    // count automatically, so this must be done manually
    if Item <> nil then
        Item._AddRef;
end;

procedure TPArrayBag.TrueClear;
var
    Item: ICollectable;
    I: Integer;
begin
    // Delete all interface references
    for I := 0 to FList.Count - 1 do
    begin
        Item := ICollectable(FList[I]);
        FList[I] := nil;
        // Storing interface reference as a pointer does not update reference
        // count automatically, so this must be done manually
        if Item <> nil then
            Item._Release;
    end;
    FList.Clear;
end;

function TPArrayBag.TrueContains(const Item: ICollectable): Boolean;
var
    I: Integer;
    Success: Boolean;
begin
    // Sequential search
    I := 0;
    Success := false;
    while (I < FList.Count) and not Success do
    begin
        Success := Comparator.Equals(Item, ICollectable(FList[I]));
        Inc(I);
    end;
    Result := Success;
end;

function TPArrayBag.TrueRemove(const Item: ICollectable): ICollectable;
var
    Item2: ICollectable;
    I: Integer;
    Found: Boolean;
begin
    // Sequential search
    I := 0;
    Found := false;
    Result := nil;
    while (I < FList.Count) and not Found do
    begin
        Item2 := ICollectable(FList[I]);
        if Comparator.Equals(Item, Item2) then
        begin
            Found := true;
            Result := Item2;
            FList.Delete(I);
            // Storing interface reference as a pointer does not update reference
            // count automatically, so this must be done manually
            if Item2 <> nil then
                Item2._Release;
        end
        else
            Inc(I);
    end;
end;

function TPArrayBag.TrueRemoveAll(const Item: ICollectable): ICollection;
var
    ResultCollection: TPArrayBag;
    Item2: ICollectable;
    I: Integer;
begin
    // Sequential search
    I := 0;
    ResultCollection := TPArrayBag.Create;
    while I < FList.Count do
    begin
        Item2 := ICollectable(FList[I]);
        if Comparator.Equals(Item, Item2) then
        begin
            ResultCollection.Add(Item2);
            FList.Delete(I);
            // Storing interface reference as a pointer does not update reference
            // count automatically, so this must be done manually
            if Item <> nil then
                Item._Release;
        end
        else
            Inc(I);
    end;
    Result := ResultCollection;
end;

function TPArrayBag.GetCapacity: Integer;
begin
    Result := FList.Capacity;
end;

procedure TPArrayBag.SetCapacity(Value: Integer);
begin
    FList.Capacity := Value;
end;

function TPArrayBag.GetIterator: IIterator;
begin
    Result := TPArrayIterator.Create(FList, true);
end;

function TPArrayBag.GetSize: Integer;
begin
    Result := FList.Count;
end;

constructor TPArraySet.Create(NaturalItemsOnly: Boolean);
begin
    inherited Create(NaturalItemsOnly);
    FList := TList.Create;
end;

destructor TPArraySet.Destroy;
begin
    FList.Free;
    inherited Destroy;
end;

function TPArraySet.GetPosition(const Item: ICollectable): TCollectionPosition;
var
    I: Integer;
    Success: Boolean;
begin
    // Sequential search
    I := 0;
    Success := false;
    while (I < FList.Count) do
    begin
        Success := Comparator.Equals(Item, ICollectable(FList[I]));
        if Success then
            break;
        Inc(I);
    end;
    Result := TPArrayPosition.Create(Success, I);
end;

procedure TPArraySet.TrueAdd2(Position: TCollectionPosition; const Item: ICollectable);
begin
    FList.Add(Pointer(Item));
    // Storing interface reference as a pointer does not update reference
    // count automatically, so this must be done manually
    Item._AddRef;
end;

procedure TPArraySet.TrueClear;
var
    Item: ICollectable;
    I: Integer;
begin
    // Delete all interface references
    for I := 0 to FList.Count - 1 do
    begin
        Item := ICollectable(FList[I]);
        FList[I] := nil;
        // Storing interface reference as a pointer does not update reference
        // count automatically, so this must be done manually
        Item._Release;
    end;
    FList.Clear;
end;

function TPArraySet.TrueGet(Position: TCollectionPosition): ICollectable;
begin
    Result := ICollectable(FList.Items[TPArrayPosition(Position).Index]);
end;

procedure TPArraySet.TrueRemove2(Position: TCollectionPosition);
var
    Item: ICollectable;
begin
    Item := ICollectable(FList[TPArrayPosition(Position).Index]);
    FList.Delete(TPArrayPosition(Position).Index);
    // Storing interface reference as a pointer does not update reference
    // count automatically, so this must be done manually
    Item._Release;
end;

function TPArraySet.GetCapacity: Integer;
begin
    Result := FList.Capacity;
end;

procedure TPArraySet.SetCapacity(Value: Integer);
begin
    FList.Capacity := Value;
end;

function TPArraySet.GetIterator: IIterator;
begin
    Result := TPArrayIterator.Create(FList, true);
end;

function TPArraySet.GetSize: Integer;
begin
    Result := FList.Count;
end;

constructor TPArrayList.Create(NaturalItemsOnly: Boolean);
begin
    inherited Create(NaturalItemsOnly);
    FList := TList.Create;
end;

destructor TPArrayList.Destroy;
begin
    FList.Free;
    inherited Destroy;
end;

function TPArrayList.TrueGetItem(Index: Integer): ICollectable;
begin
    Result := ICollectable(FList.Items[Index]);
end;

procedure TPArrayList.TrueSetItem(Index: Integer; const Item: ICollectable);
var
    OldItem: ICollectable;
begin
    OldItem := ICollectable(FList[Index]);
    FList[Index] := Pointer(Item);
    // Storing interface reference as a pointer does not update reference
    // count automatically, so this must be done manually
    if Item <> nil then
        Item._AddRef;
    if OldItem <> nil then
        OldItem._Release;
end;

procedure TPArrayList.TrueAppend(const Item: ICollectable);
begin
    FList.Add(Pointer(Item));
    // Storing interface reference as a pointer does not update reference
    // count automatically, so this must be done manually
    if Item <> nil then
        Item._AddRef;
end;

procedure TPArrayList.TrueClear;
var
    Item: ICollectable;
    I: Integer;
begin
    // Delete all interface references
    for I := 0 to FList.Count - 1 do
    begin
        Item := ICollectable(FList[I]);
        FList[I] := nil;
        // Storing interface reference as a pointer does not update reference
        // count automatically, so this must be done manually
        if Item <> nil then
            Item._Release;
    end;
    FList.Clear;
end;

function TPArrayList.TrueDelete(Index: Integer): ICollectable;
begin
    Result := ICollectable(FList[Index]);
    FList.Delete(Index);
    // Storing interface reference as a pointer does not update reference
    // count automatically, so this must be done manually
    if Result <> nil then
        Result._Release;
end;

procedure TPArrayList.TrueInsert(Index: Integer; const Item: ICollectable);
begin
    FList.Insert(Index, Pointer(Item));
    // Storing interface reference as a pointer does not update reference
    // count automatically, so this must be done manually
    if Item <> nil then
        Item._AddRef;
end;

function TPArrayList.GetCapacity: Integer;
begin
    Result := FList.Capacity;
end;

procedure TPArrayList.SetCapacity(Value: Integer);
begin
    FList.Capacity := Value;
end;

function TPArrayList.GetIterator: IIterator;
begin
    Result := TPArrayIterator.Create(FList, true);
end;

function TPArrayList.GetSize: Integer;
begin
    Result := FList.Count;
end;

constructor TPArrayMap.Create(NaturalItemsOnly: Boolean; NaturalKeysOnly: Boolean);
begin
    inherited Create(NaturalItemsOnly, NaturalKeysOnly);
    FList := TList.Create;
end;

destructor TPArrayMap.Destroy;
begin
    FList.Free;
    inherited Destroy;
end;

function TPArrayMap.GetAssociationIterator: IMapIterator;
begin
    Result := TPArrayAssociationIterator.Create(FList, true);
end;

function TPArrayMap.GetKeyPosition(const Key: ICollectable): TCollectionPosition;
var
    I: Integer;
    Success: Boolean;
begin
    // Sequential search
    I := 0;
    Success := false;
    while (I < FList.Count) do
    begin
        Success := KeyComparator.Equals(Key, IAssociation(FList[I]).GetKey);
        if Success then
            break;
        Inc(I);
    end;
    Result := TPArrayPosition.Create(Success, I);
end;

procedure TPArrayMap.TrueClear;
var
    Association: IAssociation;
    I: Integer;
begin
    // Delete all interface references
    for I := 0 to FList.Count - 1 do
    begin
        Association := IAssociation(FList[I]);
        FList[I] := nil;
        // Storing interface reference as a pointer does not update reference
        // count automatically, so this must be done manually
        Association._Release;
    end;
    FList.Clear;
end;

function TPArrayMap.TrueGet(Position: TCollectionPosition): IAssociation;
begin
    Result := IAssociation(FList.Items[TPArrayPosition(Position).Index]);
end;

function TPArrayMap.TruePut(Position: TCollectionPosition; const Association: IAssociation): IAssociation;
var
    OldAssociation: IAssociation;
    Index: Integer;
begin
    if Position.Found then
    begin
        Index := (Position as TPArrayPosition).Index;
        OldAssociation := IAssociation(FList[Index]);
        FList[Index] := Pointer(Association);
    end
    else
    begin
        OldAssociation := nil;
        FList.Add(Pointer(Association));
    end;
    Result := OldAssociation;
    // Storing interface reference as a pointer does not update reference
    // count automatically, so this must be done manually
    Association._AddRef;
    if OldAssociation <> nil then
        OldAssociation._Release;
end;

function TPArrayMap.TrueRemove2(Position: TCollectionPosition): IAssociation;
var
    OldAssociation: IAssociation;
begin
    OldAssociation := IAssociation(FList[TPArrayPosition(Position).Index]);
    FList.Delete(TPArrayPosition(Position).Index);
    Result := OldAssociation;
    // Storing interface reference as a pointer does not update reference
    // count automatically, so this must be done manually
    OldAssociation._Release;
end;

function TPArrayMap.GetCapacity: Integer;
begin
    Result := FList.Capacity;
end;

procedure TPArrayMap.SetCapacity(Value: Integer);
begin
    FList.Capacity := Value;
end;

function TPArrayMap.GetSize: Integer;
begin
    Result := FList.Count;
end;

procedure TExposedPArrayList.TrueAppend(const Item: ICollectable);
begin
    inherited TrueAppend(Item);
end;

procedure TExposedPArrayList.TrueInsert(Index: Integer; const Item: ICollectable);
begin
    inherited TrueInsert(Index, Item);
end;

{ TPArrayIterator }
constructor TPArrayIterator.Create(List: TList; AllowRemove: Boolean);
begin
    inherited Create(AllowRemove);
    FList := List;
    FIndex := -1;
end;

function TPArrayIterator.TrueFirst: ICollectable;
begin
    FIndex := 0;
    if FIndex < FList.Count then
        Result := ICollectable(FList[FIndex])
    else
        Result := nil;
end;

function TPArrayIterator.TrueNext: ICollectable;
begin
    Inc(FIndex);
    if FIndex < FList.Count then
        Result := ICollectable(FList[FIndex])
    else
        Result := nil;
end;

procedure TPArrayIterator.TrueRemove;
var
    Item: ICollectable;
begin
    Item := ICollectable(FList[FIndex]);
    FList.Delete(FIndex);
    Dec(FIndex);
    // Storing interface reference as a pointer does not update reference
    // count automatically, so this must be done manually
    Item._Release;
end;

{ TPArrayAssociationIterator }
constructor TPArrayAssociationIterator.Create(List: TList; AllowRemove: Boolean);
begin
    inherited Create(AllowRemove);
    FList := List;
    FIndex := -1;
end;

function TPArrayAssociationIterator.TrueFirst: IAssociation;
begin
    FIndex := 0;
    if FIndex < FList.Count then
        Result := IAssociation(FList[FIndex])
    else
        Result := nil;
end;

function TPArrayAssociationIterator.TrueNext: IAssociation;
begin
    Inc(FIndex);
    if FIndex < FList.Count then
        Result := IAssociation(FList[FIndex])
    else
        Result := nil;
end;

procedure TPArrayAssociationIterator.TrueRemove;
var
    Association: IAssociation;
begin
    Association := IAssociation(FList[FIndex]);
    FList.Delete(FIndex);
    Dec(FIndex);
    // Storing interface reference as a pointer does not update reference
    // count automatically, so this must be done manually
    Association._Release;
end;

{ TPArrayPosition }
constructor TPArrayPosition.Create(Found: Boolean; Index: Integer);
begin
    inherited Create(Found);
    FIndex := Index;
end;

end.