MiTec/Common/MiTeC_Lists.pas

840 lines
19 KiB
ObjectPascal
Raw Normal View History

2024-01-02 00:01:59 +01:00
{*******************************************************}
{ MiTeC Lists }
{ }
{ Copyright (c) 1997-2021 Michal Mutl }
{ }
{*******************************************************}
{$INCLUDE Compilers.inc}
unit MiTeC_Lists;
interface
uses {$IFDEF RAD9PLUS}
System.Variants, System.SysUtils, System.Classes, WinAPI.Windows, System.SyncObjs;
{$ELSE}
Variants, SysUtils, Classes, Windows, SyncObjs;
{$ENDIF}
type
TIntegerList = class(TPersistent)
private
FList: TList;
FDuplicates: TDuplicates;
FSorted: Boolean;
procedure SetSorted(AValue: Boolean);
procedure QuickSort(AL,AR: integer);
function Find(AValue: int64; var AIndex: integer): Boolean;
protected
function GetCount: integer;
function GetItem(AIndex: integer): int64;
procedure SetItem(AIndex: integer; AValue: int64); virtual;
procedure Sort; virtual;
public
constructor Create;
destructor Destroy; override;
function Add(AValue: int64): integer; virtual;
procedure AddIntegers(AList: TIntegerList); virtual;
procedure Assign(ASource: TPersistent); override;
procedure AssignTo(ADest: TPersistent); override;
procedure Clear; virtual;
procedure Delete(AIndex: integer); virtual;
function Equals(AList: TIntegerList): Boolean; reintroduce;
procedure Exchange(AIndex1, AIndex2: integer); virtual;
function IndexOf(AValue: int64): integer; virtual;
procedure Insert(AIndex: integer; AValue: int64); virtual;
procedure Move(ACurIndex, ANewIndex: integer); virtual;
function Average: double;
property Duplicates: TDuplicates read FDuplicates write FDuplicates;
property Count: integer read GetCount;
property Items[Index: integer]: int64 read GetItem write SetItem; default;
property Sorted: Boolean read FSorted write SetSorted;
end;
TThreadStringList = class
private
FLock: TCriticalSection;
FData: TStringList;
function GetCommaText: string;
function GetText: string;
procedure SetCommaText(const Value: string);
procedure SetText(const Value: string);
public
constructor Create;
destructor Destroy; override;
procedure Lock;
procedure Unlock;
procedure Add(const AValue: string);
procedure Delete(AIndex: integer);
procedure DeleteLast;
procedure Clear;
function Get(AIndex: integer): string;
function GetLast: string;
function GetName(AIndex: integer): string;
function GetValue(AIndex: integer): string; overload;
function GetValue(AName: string): string; overload;
function Count: integer;
procedure LoadFromStream(AStream: TStream);
procedure LoadFromFile(const AFilename: string);
procedure SaveToStream(AStream: TStream);
procedure SaveToFile(const AFilename: string);
procedure LoadFromDelimitedString(AValue: string; ADelimiter: char);
procedure LoadFromStrings(AValue: TStrings);
procedure AddStrings(AValue: TStrings);
property Text: string read GetText write SetText;
property CommaText: string read GetCommaText write SetCommaText;
property StringList: TStringList read FData;
end;
{$IFDEF D7PLUS}
TValueRecord = record
AsString: string;
AsDouble: Double;
AsDateTime: TDateTime;
AsInteger: integer;
AsNumberString: string;
end;
TValueList = class(TStringList)
private
function GetValueAsDate(Name: string): TDatetime;
function GetValueAsDateTime(Name: string): TDatetime;
function GetValueAsDouble(Name: string): double;
function GetValueAsInt(Name: string): integer;
function GetValueAsInt64(Name: string): int64;
function GetValueAsString(Name: string): string;
procedure SetValueAsDate(Name: string; const Value: TDatetime);
procedure SetValueAsDateTime(Name: string; const Value: TDatetime);
procedure SetValueAsDouble(Name: string; const Value: double);
procedure SetValueAsInt(Name: string; const Value: integer);
procedure SetValueAsInt64(Name: string; const Value: int64);
procedure SetValueAsString(Name: string; const Value: string);
function GetValue(AName: string): TValueRecord;
public
constructor Create;
property Val[AName: string]: TValueRecord read GetValue;
property ValueAsString[Name: string]: string read GetValueAsString write SetValueAsString;
property ValueAsInt[Name: string]: integer read GetValueAsInt write SetValueAsInt;
property ValueAsInt64[Name: string]: int64 read GetValueAsInt64 write SetValueAsInt64;
property ValueAsDouble[Name: string]: double read GetValueAsDouble write SetValueAsDouble;
property ValueAsDate[Name: string]: TDatetime read GetValueAsDate write SetValueAsDate;
property ValueAsDateTime[Name: string]: TDatetime read GetValueAsDateTime write SetValueAsDateTime;
function ValueExists(AName: string): boolean;
end;
{$ENDIF}
TNamedIntegerList = class(TStringList)
protected
function GetValue(AIndex: integer): integer;
procedure PutValue(AIndex: integer; AValue: integer);
public
function Add(const AName: string; AValue: integer): integer; reintroduce;
function IndexOfInt(AValue: integer): integer;
procedure Insert(AIndex: integer; const AName: string; AValue: integer); reintroduce;
property IntValues[AIndex: integer]: integer read GetValue write PutValue; default;
end;
TNamedFloatList = class(TStringList)
private
FDF: string;
protected
function GetValue(AIndex: integer): double;
procedure PutValue(AIndex: integer; AValue: double);
public
constructor Create(ADoubleFormat: string = '%1.5f');
function Add(const AName: string; AValue: double): integer; reintroduce;
function IndexOfFloat(AValue: double): integer;
procedure Insert(AIndex: integer; const AName: string; AValue: double); reintroduce;
property FloatValues[AIndex: integer]: double read GetValue write PutValue; default;
property DoubleFormat: string read FDF;
end;
implementation
uses MiTeC_StrUtils;
function Quote(Source: string): string;
begin
Result:=Source;
if Copy(Source,1,1)='''' then
Exit;
Result:=''''+FastStringReplace(Source,'''','''''')+'''';
end;
function Dequote(Source: string): string;
begin
Result:=Source;
if Length(Source)>1 then begin
if (Source[1]='''') and (Source[Length(Source)]='''') then
Result:=Copy(Source,2,Length(Source)-2);
Result:=FastStringReplace(Result,'''''','''');
end;
end;
{$IFDEF D7PLUS}
{ TValueList }
constructor TValueList.Create;
begin
inherited Create;
NameValueSeparator:=#30;
end;
function TValueList.GetValue(AName: string): TValueRecord;
begin
Result.AsString:=ValueAsString[AName];
Result.AsDouble:=ValueAsDouble[AName];
Result.AsDateTime:=ValueAsDatetime[AName];
Result.AsInteger:=ValueAsInt[AName];
Result.AsNumberString:=StringReplace(ValueAsString[AName],{$IFDEF RAD8PLUS}FormatSettings.{$ENDIF}ThousandSeparator,'',[rfReplaceAll,rfIgnoreCase]);
Result.AsNumberString:=StringReplace(Result.AsNumberString,{$IFDEF RAD8PLUS}FormatSettings.{$ENDIF}DecimalSeparator,'.',[rfReplaceAll,rfIgnoreCase]);
end;
function TValueList.GetValueAsDate(Name: string): TDatetime;
begin
Result:=StrToDateTimeDef(Dequote(Values[Name]),0);
end;
function TValueList.GetValueAsDateTime(Name: string): TDatetime;
begin
Result:=StrToDateTimeDef(Dequote(Values[Name]),0);
end;
function TValueList.GetValueAsDouble(Name: string): double;
begin
Result:=StrToFloatDef(TrimAll(Dequote(Values[Name])),0);
end;
function TValueList.GetValueAsInt(Name: string): integer;
begin
Result:=Round(StrToFloatDef(TrimAll(Dequote(Values[Name])),0));
end;
function TValueList.GetValueAsInt64(Name: string): int64;
begin
Result:=Round(StrToFloatDef(TrimAll(Dequote(Values[Name])),0));
end;
function TValueList.GetValueAsString(Name: string): string;
var
idx: integer;
begin
idx:=IndexOfName(Name)+1;
Result:=Values[Name];
while (idx<Count) and (Pos(NameValueSeparator,Strings[idx])=0) do begin
Result:=Result+#13#10+Strings[idx];
Inc(idx);
end;
Result:=Dequote(Result);
end;
procedure TValueList.SetValueAsDate(Name: string; const Value: TDatetime);
begin
if IndexOfName(Name)=-1 then
Add(Format('%s%s%s',[Name,NameValueSeparator,Quote(DateToStr(Value))]))
else
Values[Name]:=Quote(DateToStr(Value));
end;
procedure TValueList.SetValueAsDateTime(Name: string; const Value: TDatetime);
begin
if IndexOfName(Name)=-1 then
Add(Format('%s%s%s',[Name,NameValueSeparator,Quote(DateTimeToStr(Value))]))
else
Values[Name]:=Quote(DateTimeToStr(Value));
end;
procedure TValueList.SetValueAsDouble(Name: string; const Value: double);
begin
if IndexOfName(Name)=-1 then
Add(Format('%s%s%s',[Name,NameValueSeparator,Quote(Format('%n',[Value]))]))
else
Values[Name]:=Quote(Format('%n',[Value]));
end;
procedure TValueList.SetValueAsInt(Name: string; const Value: integer);
begin
if IndexOfName(Name)=-1 then
Add(Format('%s%s%s',[Name,NameValueSeparator,Quote(Format('%1.0n',[Value/1]))]))
else
Values[Name]:=Quote(Format('%1.0n',[Value/1]));
end;
procedure TValueList.SetValueAsInt64(Name: string; const Value: int64);
begin
if IndexOfName(Name)=-1 then
Add(Format('%s%s%s',[Name,NameValueSeparator,Quote(Format('%1.0n',[Value/1]))]))
else
Values[Name]:=Quote(Format('%1.0n',[Value/1]));
end;
procedure TValueList.SetValueAsString(Name: string; const Value: string);
begin
if IndexOfName(Name)=-1 then
Add(Format('%s%s%s',[Name,NameValueSeparator,Quote(Value)]))
else
Values[Name]:=Quote(Value);
end;
function TValueList.ValueExists(AName: string): boolean;
begin
Result:=IndexOfName(AName)>-1;
end;
{$ENDIF}
{ TNamedFloatList }
function TNamedFloatList.Add(const AName: string; AValue: double): integer;
begin
Result:=inherited Add(AName+'='+Format(FDF,[AValue]));
end;
constructor TNamedFloatList.Create;
begin
inherited Create;
FDF:=ADoubleFormat;
end;
function TNamedFloatList.GetValue(AIndex: integer): double;
begin
Result:=StrToFloat(Values[Names[AIndex]]);
end;
function TNamedFloatList.IndexOfFloat(AValue: double): integer;
var
P: integer;
s: string;
begin
for Result:=0 to Count-1 do begin
s:=Strings[Result];
P:=AnsiPos('=',s);
if (P<>0) and (AnsiCompareText(Trim(Copy(s,P+1,32)),Format(FDF,[AValue]))=0) then
Exit;
end;
Result:=-1;
end;
procedure TNamedFloatList.Insert(AIndex: integer; const AName: string;
AValue: double);
begin
inherited Insert(AIndex,AName+'='+Format(FDF,[AValue]));
end;
procedure TNamedFloatList.PutValue(AIndex: integer; AValue: double);
begin
inherited Values[Names[AIndex]]:=Format(FDF,[AValue]);
end;
{ TNamedIntegerList }
function TNamedIntegerList.Add(const AName: string;
AValue: integer): integer;
begin
Result:=inherited Add(AName+'='+IntToStr(AValue));
end;
function TNamedIntegerList.GetValue(AIndex: integer): integer;
begin
Result:=StrToInt(Values[Names[AIndex]]);
end;
function TNamedIntegerList.IndexOfInt(AValue: integer): integer;
var
P: integer;
s: string;
begin
for Result:=0 to Count-1 do begin
s:=Strings[Result];
P:=AnsiPos('=',s);
if (P<>0) and (AnsiCompareText(Trim(Copy(s,P+1,32)),IntToStr(AValue))=0) then
Exit;
end;
Result:=-1;
end;
procedure TNamedIntegerList.Insert(AIndex: integer; const AName: string;
AValue: integer);
begin
inherited Insert(AIndex,AName+'='+IntToStr(AValue));
end;
procedure TNamedIntegerList.PutValue(AIndex, AValue: integer);
begin
inherited Values[Names[AIndex]]:=IntToStr(AValue);
end;
{ TThreadStringList }
procedure TThreadStringList.Add(const AValue: string);
begin
Lock;
try
FData.Add(AValue);
finally
Unlock;
end;
end;
procedure TThreadStringList.AddStrings(AValue: TStrings);
begin
Lock;
try
FData.AddStrings(AValue);
finally
Unlock;
end;
end;
procedure TThreadStringList.Clear;
begin
Lock;
try
FData.Clear;
finally
Unlock;
end;
end;
function TThreadStringList.Count: integer;
begin
Lock;
try
Result:=FData.Count;
finally
Unlock;
end;
end;
constructor TThreadStringList.Create;
begin
FLock:=TCriticalSection.Create;
FData:=TStringList.Create;
end;
procedure TThreadStringList.Delete(AIndex: integer);
begin
Lock;
try
FData.Delete(AIndex);
finally
Unlock;
end;
end;
procedure TThreadStringList.DeleteLast;
begin
Lock;
try
if FData.Count>0 then
FData.Delete(FData.Count-1);
finally
Unlock;
end;
end;
destructor TThreadStringList.Destroy;
begin
FData.Free;
FLock.Free;
inherited;
end;
function TThreadStringList.Get(AIndex: integer): string;
begin
Lock;
try
Result:=FData[AIndex];
finally
Unlock;
end;
end;
function TThreadStringList.GetCommaText: string;
begin
Lock;
try
Result:=FData.CommaText;
finally
Unlock;
end;
end;
function TThreadStringList.GetLast: string;
begin
Result:='';
Lock;
try
if FData.Count>0 then
Result:=FData[FData.Count-1];
finally
Unlock;
end;
end;
function TThreadStringList.GetName(AIndex: integer): string;
begin
Lock;
try
Result:=FData.Names[AIndex];
finally
Unlock;
end;
end;
function TThreadStringList.GetText: string;
begin
Lock;
try
Result:=FData.Text;
finally
Unlock;
end;
end;
function TThreadStringList.GetValue(AIndex: integer): string;
begin
Lock;
try
Result:=FData.ValueFromIndex[AIndex];
finally
Unlock;
end;
end;
function TThreadStringList.GetValue(AName: string): string;
begin
Lock;
try
Result:=FData.Values[AName];
finally
Unlock;
end;
end;
procedure TThreadStringList.LoadFromDelimitedString(AValue: string;
ADelimiter: char);
begin
Lock;
try
FData.Delimiter:=ADelimiter;
FData.StrictDelimiter:=True;
FData.DelimitedText:=AValue;
finally
Unlock;
end;
end;
procedure TThreadStringList.LoadFromFile(const AFilename: string);
begin
Lock;
try
FData.LoadFromFile(AFilename);
finally
Unlock;
end;
end;
procedure TThreadStringList.LoadFromStream(AStream: TStream);
begin
Lock;
try
FData.LoadFromStream(AStream);
finally
Unlock;
end;
end;
procedure TThreadStringList.LoadFromStrings(AValue: TStrings);
begin
Lock;
try
FData.Text:=AValue.Text;
finally
Unlock;
end;
end;
procedure TThreadStringList.Lock;
begin
FLock.Enter;
end;
procedure TThreadStringList.SaveToFile(const AFilename: string);
begin
Lock;
try
FData.SaveToFile(AFilename);
finally
Unlock;
end;
end;
procedure TThreadStringList.SaveToStream(AStream: TStream);
begin
Lock;
try
FData.SaveToStream(AStream);
finally
Unlock;
end;
end;
procedure TThreadStringList.SetCommaText(const Value: string);
begin
Lock;
try
FData.CommaText:=Value;
finally
Unlock;
end;
end;
procedure TThreadStringList.SetText(const Value: string);
begin
Lock;
try
FData.Text:=Value;
finally
Unlock;
end;
end;
procedure TThreadStringList.Unlock;
begin
FLock.Leave;
end;
{ TIntegerList }
constructor TIntegerList.Create;
begin
inherited Create;
FList:=TList.Create;
FDuplicates:=dupAccept;
FSorted:=False;
end;
destructor TIntegerList.Destroy;
begin
Clear;
FList.Free;
inherited Destroy;
end;
procedure TIntegerList.AddIntegers(AList: TIntegerList);
var
i: integer;
begin
for i:=0 to Pred(AList.Count) do
Add(AList[i]);
end;
procedure TIntegerList.Assign(ASource: TPersistent);
begin
if ASource is TIntegerList then begin
Clear;
AddIntegers(TIntegerList(ASource));
end else
inherited Assign(ASource);
end;
procedure TIntegerList.SetSorted(AValue: Boolean);
begin
if FSorted<>AValue then begin
if AValue then
Sort;
FSorted:=AValue;
end;
end;
function TIntegerList.GetCount: integer;
begin
Result:=FList.Count;
end;
function TIntegerList.GetItem(AIndex: integer): int64;
begin
Result:=PInt64(FList.Items[AIndex])^;
end;
procedure TIntegerList.SetItem(AIndex: integer; AValue: int64);
begin
PInt64(FList.Items[AIndex])^:=AValue;
end;
function TIntegerList.Add(AValue:int64): integer;
var
p: PInt64;
begin
if FDuplicates<>dupAccept then begin
Result:=IndexOf(AValue);
if Result>=0 then begin
if FDuplicates=dupIgnore then
Exit;
if FDuplicates=dupError then
raise EListError.CreateFmt('Value %d already exists in the no duplicates list',[AValue]);
end;
end;
New(p);
p^:=AValue;
FList.Add(p);
if Sorted then begin
Sorted:=False;
Sorted:=True;
end;
Result:=IndexOf(AValue);
end;
procedure TIntegerList.Clear;
var
i: integer;
begin
for i:=0 to Pred(FList.Count) do
Dispose(PInt64(FList.Items[i]));
FList.Clear;
end;
procedure TIntegerList.Delete(AIndex: integer);
begin
Dispose(PInt64(FList.Items[AIndex]));
FList.Delete(AIndex);
end;
function TIntegerList.Equals(AList: TIntegerList): Boolean;
var
i,c: integer;
begin
c:=GetCount;
if c<>AList.GetCount then
Result:=False
else begin
i:=0;
while (i<c) and (GetItem(i)=AList.GetItem(i)) do
Inc(i);
Result:=i=c;
end;
end;
procedure TIntegerList.Exchange(AIndex1, AIndex2: integer);
begin
FList.Exchange(AIndex1,AIndex2);
end;
{ List must be sorted }
function TIntegerList.Find(AValue: int64; var AIndex:integer): Boolean;
var
l,h,i: integer;
begin
Result:=False;
l:=0;
h:=Count-1;
while l<=h do begin
i:=(l+h)shr 1;
if PInt64(FList[i])^<AValue then
l:=i+1
else begin
h:=i-1;
if PInt64(FList[i])^=AValue then begin
Result:=True;
if Duplicates<>dupAccept then
l:=i;
end;
end;
end;
AIndex:=l;
end;
function TIntegerList.IndexOf(AValue: int64): integer;
var
i: integer;
begin
Result:=-1;
if not Sorted then begin
for i:=0 to FList.Count-1 do
if PInt64(FList[i])^=AValue then begin
Result:=i;
Break;
end;
end else if Find(AValue,i) then
Result:=i;
end;
procedure TIntegerList.Insert(AIndex: integer; AValue: int64);
var
p: PInt64;
begin
New(p);
p^:=AValue;
FList.Insert(AIndex,p);
end;
procedure TIntegerList.Move(ACurIndex, ANewIndex: integer);
begin
FList.Move(ACurIndex,ANewIndex);
end;
procedure TIntegerList.QuickSort(AL, AR: integer);
var
i,j: integer;
p: PInt64;
begin
i:=AL;
j:=AR;
p:=PInt64(FList[(AL+AR) shr 1]);
repeat
while PInt64(FList[i])^<p^ do
Inc(i);
while PInt64(FList[j])^>p^ do
Dec(j);
if i<=j then begin
FList.Exchange(i,j);
Inc(i);
Dec(j);
end;
until i>j;
if AL<j then
QuickSort(AL,j);
if i<AR then
QuickSort(i,AR);
end;
procedure TIntegerList.Sort;
begin
if not Sorted and (FList.Count>1)
then QuickSort(0,FList.Count-1);
end;
procedure TIntegerList.AssignTo(ADest: TPersistent);
var
i: integer;
s: TStrings;
begin
if ADest is TStrings then begin
s:=TStrings(ADest);
s.Clear;
for i:=0 to Count-1 do
s.Add(IntToStr(Items[i]));
end else
inherited AssignTo(ADest);
end;
function TIntegerList.Average: double;
var
p: PInt64;
begin
Result:=0;
for p in FList do
Result:=Result+p^;
Result:=Result/FList.Count;
end;
end.