unit CacheTree;
// Base Tree Class,

interface

uses
  Classes, Math;

type
  // 基础树类 -- 合并项目, 导入Excel等树以此树为基础
  TCacheTree = class;
  TCacheNode = class;

  TCacheNodeList = class
  private
    FList: TList;
    function GetCount: Integer;
    function GetFirst: TCacheNode;
    function GetItems(AIndex: Integer): TCacheNode;
    function GetLast: TCacheNode;
    procedure SetItems(AIndex: Integer; const Value: TCacheNode);
  public
    constructor Create;
    destructor Destroy; override;

    function Add(ANode: TCacheNode): Integer;
    procedure Insert(AIndex: Integer; ANode: TCacheNode);
    procedure Delete(AIndex: Integer);
    function Remove(ANode: TCacheNode): Integer;
    procedure Clear;

    function IndexOf(ANode: TCacheNode): Integer;

    property Count: Integer read GetCount;
    property First: TCacheNode read GetFirst;
    property Last: TCacheNode read GetLast;
    property Items[AIndex: Integer]: TCacheNode read GetItems write SetItems; default;
  end;

  TCacheNode = class
  private
    FID: Integer;

    FParent: TCacheNode;
    FNextSibling: TCacheNode;
    FPreSibling: TCacheNode;

    FChildren: TCacheNodeList;
    FCacheTree: TCacheTree;
    function GetFirstChild: TCacheNode;
    function GetLastChild: TCacheNode;
    function GetNextSiblingID: Integer;
    function GetParentID: Integer;
    function GetPreSiblingID: Integer;

    function GetNodeID(ANode: TCacheNode): Integer;
  public
    constructor Create(ACacheTree: TCacheTree; AID: Integer);
    destructor Destroy; override;

    procedure InsertChild(AChildNode: TCacheNode);
    procedure InsertNextSibling(ANextSibling: TCacheNode);
    procedure InsertPreSibling(APreSibling: TCacheNode);
    procedure RemoveChild(AChildNode: TCacheNode);
    procedure DeleteChild(AIndex: Integer);
    procedure ClearChildren;

    property ID: Integer read FID;
    property ParentID: Integer read GetParentID;
    property PreSiblingID: Integer read GetPreSiblingID;
    property NextSiblingID: Integer read GetNextSiblingID;
    property Parent: TCacheNode read FParent write FParent;
    property NextSibling: TCacheNode read FNextSibling write FNextSibling;
    property PreSibling: TCacheNode read FPreSibling write FPreSibling;

    property FirstChild: TCacheNode read GetFirstChild;
    property LastChild: TCacheNode read GetLastChild;
    property Children: TCacheNodeList read FChildren;
  end;

  TCacheTree = class
  private
    FCacheNodes: TCacheNodeList;
    FRoot: TCacheNode;
    FNewNodeID: Integer;

    function GetFirstNode: TCacheNode;
    procedure SetNewNodeID(const Value: Integer);
  protected
    function GetNewNodeID: Integer;   
    function GetNewNode: TCacheNode; virtual;
  public
    constructor Create; virtual;
    destructor Destroy; override;

    function AddNode(AParent: TCacheNode; ANextSibling: TCacheNode = nil): TCacheNode;
    procedure DeleteNode(ANode: TCacheNode);
    procedure ClearTreeNodes;

    property Root: TCacheNode read FRoot;
    property FirstNode: TCacheNode read GetFirstNode;
    property CacheNodes: TCacheNodeList read FCacheNodes;
    property NewNodeID: Integer read FNewNodeID write SetNewNodeID;
  end;

implementation

{ TCacheNodeList }

function TCacheNodeList.Add(ANode: TCacheNode): Integer;
begin
  Result := FList.Add(ANode);
end;

procedure TCacheNodeList.Clear;
begin
  FList.Clear;
end;

constructor TCacheNodeList.Create;
begin
  FList := TList.Create;
end;

procedure TCacheNodeList.Delete(AIndex: Integer);
begin
  FList.Delete(AIndex);
end;

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

function TCacheNodeList.GetCount: Integer;
begin
  Result := FList.Count;
end;

function TCacheNodeList.GetFirst: TCacheNode;
begin
  Result := TCacheNode(FList.First);
end;

function TCacheNodeList.IndexOf(ANode: TCacheNode): Integer;
begin
  Result := FList.IndexOf(ANode);
end;

function TCacheNodeList.GetItems(AIndex: Integer): TCacheNode;
begin
  Result := TCacheNode(FList.Items[AIndex]);
end;

function TCacheNodeList.GetLast: TCacheNode;
begin
  Result := TCacheNode(FList.Last);
end;

procedure TCacheNodeList.Insert(AIndex: Integer; ANode: TCacheNode);
begin
  FList.Insert(AIndex, ANode);
end;

function TCacheNodeList.Remove(ANode: TCacheNode): Integer;
begin
  Result := FList.Remove(ANode);
end;

procedure TCacheNodeList.SetItems(AIndex: Integer;
  const Value: TCacheNode);
begin
  FList.Items[AIndex] := Value;
end;

{ TCacheNode }

procedure TCacheNode.ClearChildren;
var
  I: Integer;
begin
  for I := 0 to FChildren.Count - 1 do
  begin
    FChildren[I].ClearChildren;
    FChildren[I].Free;
  end;
  FChildren.Clear;
end;

constructor TCacheNode.Create(ACacheTree: TCacheTree; AID: Integer);
begin
  FChildren := TCacheNodeList.Create;
  FCacheTree := ACacheTree;
  FID := AID;
end;

procedure TCacheNode.DeleteChild(AIndex: Integer);
begin
  if (AIndex >= 0) and (AIndex < FChildren.Count) then
    RemoveChild(FChildren[AIndex]);
end;

destructor TCacheNode.Destroy;
begin
  FChildren.Free;
  inherited;
end;

function TCacheNode.GetFirstChild: TCacheNode;
begin
  if FChildren.Count > 0 then
    Result := FChildren.First
  else
    Result := nil;
end;

function TCacheNode.GetLastChild: TCacheNode;
begin
  if FChildren.Count > 0 then
    Result := FChildren.Last
  else
    Result := nil;
end;

function TCacheNode.GetNextSiblingID: Integer;
begin
  Result := GetNodeID(FNextSibling);
end;

function TCacheNode.GetNodeID(ANode: TCacheNode): Integer;
begin
  if Assigned(ANode) and (ANode <> nil) then
    Result := ANode.ID
  else
    Result := -1;
end;

function TCacheNode.GetParentID: Integer;
begin
  Result := GetNodeID(FParent);
end;

function TCacheNode.GetPreSiblingID: Integer;
begin
  Result := GetNodeID(FPreSibling);
end;

procedure TCacheNode.InsertChild(AChildNode: TCacheNode);
begin
  AChildNode.FParent := Self;
  AChildNode.FPreSibling := LastChild;
  if LastChild <> nil then
    LastChild.FNextSibling := AChildNode;
  FChildren.Add(AChildNode);
end;

procedure TCacheNode.InsertNextSibling(ANextSibling: TCacheNode);
begin
  if Assigned(FNextSibling) then
    FNextSibling.FPreSibling := ANextSibling;
  ANextSibling.FNextSibling := FNextSibling;
  FNextSibling := ANextSibling;
  ANextSibling.FPreSibling := Self;
  ANextSibling.FParent := FParent;
  FParent.Children.Insert(FParent.Children.IndexOf(Self) + 1, ANextSibling);
end;

procedure TCacheNode.InsertPreSibling(APreSibling: TCacheNode);
begin
  if Assigned(FPreSibling) then
    FPreSibling.FNextSibling := APreSibling;
  APreSibling.FPreSibling := FPreSibling;
  FPreSibling := APreSibling;
  APreSibling.FNextSibling := Self;
  APreSibling.FParent := FParent;
  FParent.Children.Insert(FParent.Children.IndexOf(Self), APreSibling);
end;

procedure TCacheNode.RemoveChild(AChildNode: TCacheNode);
begin
  if AChildNode.FPreSibling <> nil then
    AChildNode.FPreSibling.FNextSibling := AChildNode.FNextSibling;
  if AChildNode.FNextSibling <> nil then
    AChildNode.FNextSibling.FPreSibling := AChildNode.FPreSibling;
  FChildren.Remove(AChildNode);
end;

{ TCacheTree }

function TCacheTree.AddNode(AParent, ANextSibling: TCacheNode): TCacheNode;
begin
  Result := GetNewNode;
  if Assigned(ANextSibling) then
    ANextSibling.InsertPreSibling(Result)
  else if Assigned(AParent) then
    AParent.InsertChild(Result)
  else
    FRoot.InsertChild(Result);
end;

procedure TCacheTree.ClearTreeNodes;
begin
  FNewNodeID := 1;
  FRoot.ClearChildren;
  FCacheNodes.Clear;
end;

constructor TCacheTree.Create;
begin
  FCacheNodes := TCacheNodeList.Create;
  FRoot := TCacheNode.Create(Self, -1);
  FNewNodeID := 1;
end;

procedure TCacheTree.DeleteNode(ANode: TCacheNode);
begin
  ANode.FParent.RemoveChild(ANode);
  ANode.ClearChildren;
  FCacheNodes.Remove(ANode);
  ANode.Free;
end;

destructor TCacheTree.Destroy;
begin
  ClearTreeNodes;
  FRoot.Free;
  FCacheNodes.Free;
  inherited;
end;

function TCacheTree.GetFirstNode: TCacheNode;
begin
  if FCacheNodes.Count > 0 then
    Result := FCacheNodes.First
  else
    Result := nil;
end;

function TCacheTree.GetNewNode: TCacheNode;
begin
  Result := TCacheNode.Create(Self, GetNewNodeID);
  FCacheNodes.Add(Result);
end;

function TCacheTree.GetNewNodeID: Integer;
begin
  Result := FNewNodeID;
  Inc(FNewNodeID);
end;

procedure TCacheTree.SetNewNodeID(const Value: Integer);
begin
  FNewNodeID := Max(FNewNodeID, Value);
end;

end.
