{ rxsortmemds unit

  Copyright (C) 2005-2021 Lagunov Aleksey alexs75@yandex.ru and Typhon team
  original conception from rx library for Delphi (c)

  This library is free software; you can redistribute it and/or modify it
  under the terms of the GNU Library General Public License as published by
  the Free Software Foundation; either version 2 of the License, or (at your
  option) any later version with the following modification:

  As a special exception, the copyright holders of this library give you
  permission to link this library with independent modules to produce an
  executable, regardless of the license terms of these independent modules,and
  to copy and distribute the resulting executable under terms of your choice,
  provided that you also meet, for each linked independent module, the terms
  and conditions of the license of that module. An independent module is a
  module which is not derived from or based on this library. If you modify
  this library, you may extend this exception to your version of the library,
  but you are not obligated to do so. If you do not wish to do so, delete this
  exception statement from your version.

  This program 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 Library General Public License
  for more details.

  You should have received a copy of the GNU Library General Public License
  along with this library; if not, write to the Free Software Foundation,
  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
}

unit rxsortcsvdataset;

{$I rx.inc}

interface

uses
  Classes, SysUtils, DB, RxDBGrid, csvdataset, TypInfo;

type

  { TcsvdatasetSortEngine }

  TcsvdatasetSortEngine = class(TRxDBGridSortEngine)
  protected
  public
    procedure Sort(FieldName: string; ADataSet:TDataSet; Asc:boolean; SortOptions:TRxSortEngineOptions);override;
    procedure SortList(ListField:string; ADataSet:TDataSet; Asc: array of boolean; SortOptions: TRxSortEngineOptions);override;
    function SortOnFields(const FieldName: string; ADataSet: TDataSet; CaseInsensitive: Boolean = True; Descending: Boolean = False):Boolean;
    function SortOnFieldsEx(const FieldNames: string; ADataSet: TDataSet; CaseInsensitive: Boolean; Asc: array of boolean):Boolean;
  end;

implementation
//uses rxmemds;

procedure TcsvdatasetSortEngine.Sort(FieldName: string; ADataSet: TDataSet; Asc: boolean; SortOptions: TRxSortEngineOptions);
begin
  if Assigned(ADataSet) then
    SortOnFields(FieldName, (ADataSet as Tcsvdataset), seoCaseInsensitiveSort in SortOptions, not Asc);
end;

procedure TcsvdatasetSortEngine.SortList(ListField: string; ADataSet: TDataSet; Asc: array of boolean; SortOptions: TRxSortEngineOptions);
begin
  if Assigned(ADataSet) then
    SortOnFieldsEx(ListField, (ADataSet as Tcsvdataset), seoCaseInsensitiveSort in SortOptions, Asc);
end;

function TcsvdatasetSortEngine.SortOnFields(const FieldName: string; ADataSet: TDataSet; CaseInsensitive: Boolean = True; Descending: Boolean = False):Boolean;
var
  i: Integer;
  vIndexDefs: TIndexDefs;
  vIndexName: String;
  vIndexOptions: TIndexOptions;
  vField: TField;
begin
  {
   (ADataset as TCSVDataset).MaxIndexesCount:=(2*((ADataset as TCSVDataset).FieldDefs.Count)+3); //Put this in your code, before opening dataset, because it cannot be performed on active dataset!
  }
  Result := False;
  vField := (ADataset as TCSVDataset).Fields.FindField(FieldName);
  //If invalid field name, exit.
  if vField = nil then Exit;
  //if invalid field type, exit.
  if {(vField is TObjectField) or} (vField is TBlobField) or
    {(vField is TAggregateField) or} (vField is TVariantField)
     or (vField is TBinaryField) then Exit;
  //Get IndexDefs and IndexName using RTTI
   if IsPublishedProp((ADataset as TCSVDataset), 'IndexDefs') then
     vIndexDefs := GetObjectProp((ADataset as TCSVDataset), 'IndexDefs') as TIndexDefs
   else
     Exit;
   if IsPublishedProp((ADataset as TCSVDataset), 'IndexName') then
     vIndexName := GetStrProp((ADataset as TCSVDataset), 'IndexName')
   else
     Exit;
  //Ensure IndexDefs is up-to-date
  (ADataset as TCSVDataset).IndexDefs.Update;
  //If an ascending index is already in use, switch to a descending index
  if vIndexName = FieldName + '__IdxA'
  then
    begin
      vIndexName := FieldName + '__IdxD';
      vIndexOptions := [ixDescending];
    end
  else
    begin
      vIndexName := FieldName + '__IdxA';
      vIndexOptions := [];
    end;
  //Look for existing index
  for i := 0 to Pred((ADataset as TCSVDataset).IndexDefs.Count) do
  begin
    if vIndexDefs[i].Name = vIndexName then
      begin
        Result := True;
        Break
      end;  //if
  end; // for
  //If existing index not found, create one
  if not Result then
      begin
        if vIndexName=FieldName + '__IdxD' then
          (ADataset as TCSVDataset).AddIndex(vIndexName, FieldName, vIndexOptions, FieldName)
        else
          (ADataset as TCSVDataset).AddIndex(vIndexName, FieldName, vIndexOptions);
        Result := True;
      end; // if not
  //Set the index
  SetStrProp((ADataset as Tcsvdataset), 'IndexName', vIndexName);
end;   

function TcsvdatasetSortEngine.SortOnFieldsEx(const FieldNames: string; ADataSet: TDataSet; CaseInsensitive: Boolean; Asc: array of boolean):Boolean;
var
  i: Integer;
  myFieldsList: TStringList;
  myDescFieldsList: TStringList;
  vFields: string;
  vDescFields: string;
  vIndexName: String;
  vIndexOptions: TIndexOptions;
  vIndexDefs: TIndexDefs;
begin
  Result := False;

  try
    myFieldsList:=TStringList.Create;
    myFieldsList.Delimiter:=char(';');

    myDescFieldsList:=TStringList.Create;
    myDescFieldsList.Delimiter:=char(';');

    myFieldsList.DelimitedText:=FieldNames;
    for i:=0 to myFieldsList.Count-1 do begin
      if (Asc[i]=False) then myDescFieldsList.Add(myFieldsList[i]);
    end;

    //Get IndexDefs and IndexName using RTTI
     if IsPublishedProp((ADataset as TCSVDataset), 'IndexDefs') then
       vIndexDefs := GetObjectProp((ADataset as TCSVDataset), 'IndexDefs') as TIndexDefs
     else
       Exit;
     if IsPublishedProp((ADataset as TCSVDataset), 'IndexName') then
       vIndexName := GetStrProp((ADataset as TCSVDataset), 'IndexName')
     else

     //Ensure IndexDefs is up-to-date
     (ADataset as TCSVDataset).IndexDefs.Update;

    //Set the indexes
    vIndexName:='Mixed_Index';
    vIndexOptions := [];
    VFields:=FieldNames;
    vDescFields:=myDescFieldsList.DelimitedText;

    //Look for existing mixed index and delete it.
    for i := 0 to Pred((ADataset as TCSVDataset).IndexDefs.Count) do begin
      if (vIndexDefs[i].Name = vIndexName) then begin
             (ADataset as TCSVDataset).IndexDefs.Delete(i);
      end;  //if
    end; // for

    //create mixed index
    begin
      (ADataset as TCSVDataset).AddIndex(vIndexName, vFields, vIndexOptions, vDescFields)
    end;

    SetStrProp((ADataset as Tcsvdataset), 'IndexName', vIndexName);
    Result:= True;
  finally
    myFieldsList.Free;
    myDescFieldsList.Free;
  end;
end;
                                                      

initialization
  RegisterRxDBGridSortEngine(TcsvdatasetSortEngine, 'Tcsvdataset');
end.

