mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-05-04 00:43:42 +02:00
1552 lines
28 KiB
PHP
1552 lines
28 KiB
PHP
{
|
|
This file is part of the Free Component Library (FCL)
|
|
Copyright (c) 1999-2000 by the Free Pascal development team
|
|
|
|
See the file COPYING.FPC, included in this distribution,
|
|
for details about the copyright.
|
|
|
|
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.
|
|
|
|
**********************************************************************}
|
|
|
|
{****************************************************************************}
|
|
{* TStringsEnumerator *}
|
|
{****************************************************************************}
|
|
|
|
constructor TStringsEnumerator.Create(AStrings: TStrings);
|
|
begin
|
|
inherited Create;
|
|
FStrings := AStrings;
|
|
FPosition := -1;
|
|
end;
|
|
|
|
function TStringsEnumerator.GetCurrent: String;
|
|
begin
|
|
Result := FStrings[FPosition];
|
|
end;
|
|
|
|
function TStringsEnumerator.MoveNext: Boolean;
|
|
begin
|
|
Inc(FPosition);
|
|
Result := FPosition < FStrings.Count;
|
|
end;
|
|
|
|
{****************************************************************************}
|
|
{* TStrings *}
|
|
{****************************************************************************}
|
|
|
|
// Function to quote text. Should move maybe to sysutils !!
|
|
// Also, it is not clear at this point what exactly should be done.
|
|
|
|
{ //!! is used to mark unsupported things. }
|
|
|
|
Function QuoteString (Const S : String; Quote : String) : String;
|
|
Var
|
|
I,J : Integer;
|
|
begin
|
|
J:=0;
|
|
Result:=S;
|
|
for i:=1to length(s) do
|
|
begin
|
|
inc(j);
|
|
if S[i]=Quote then
|
|
begin
|
|
System.Insert(Quote,Result,J);
|
|
inc(j);
|
|
end;
|
|
end;
|
|
Result:=Quote+Result+Quote;
|
|
end;
|
|
|
|
{
|
|
For compatibility we can't add a Constructor to TSTrings to initialize
|
|
the special characters. Therefore we add a routine which is called whenever
|
|
the special chars are needed.
|
|
}
|
|
|
|
Procedure Tstrings.CheckSpecialChars;
|
|
|
|
begin
|
|
If Not FSpecialCharsInited then
|
|
begin
|
|
FQuoteChar:='"';
|
|
FDelimiter:=',';
|
|
FNameValueSeparator:='=';
|
|
FSpecialCharsInited:=true;
|
|
FLBS:=DefaultTextLineBreakStyle;
|
|
end;
|
|
end;
|
|
|
|
Function TStrings.GetLBS : TTextLineBreakStyle;
|
|
begin
|
|
CheckSpecialChars;
|
|
Result:=FLBS;
|
|
end;
|
|
|
|
Procedure TStrings.SetLBS (AValue : TTextLineBreakStyle);
|
|
begin
|
|
CheckSpecialChars;
|
|
FLBS:=AValue;
|
|
end;
|
|
|
|
procedure TStrings.SetDelimiter(c:Char);
|
|
begin
|
|
CheckSpecialChars;
|
|
FDelimiter:=c;
|
|
end;
|
|
|
|
|
|
procedure TStrings.SetQuoteChar(c:Char);
|
|
begin
|
|
CheckSpecialChars;
|
|
FQuoteChar:=c;
|
|
end;
|
|
|
|
procedure TStrings.SetNameValueSeparator(c:Char);
|
|
begin
|
|
CheckSpecialChars;
|
|
FNameValueSeparator:=c;
|
|
end;
|
|
|
|
|
|
function TStrings.GetCommaText: string;
|
|
|
|
Var
|
|
C1,C2 : Char;
|
|
FSD : Boolean;
|
|
|
|
begin
|
|
CheckSpecialChars;
|
|
FSD:=StrictDelimiter;
|
|
C1:=Delimiter;
|
|
C2:=QuoteChar;
|
|
Delimiter:=',';
|
|
QuoteChar:='"';
|
|
StrictDelimiter:=False;
|
|
Try
|
|
Result:=GetDelimitedText;
|
|
Finally
|
|
Delimiter:=C1;
|
|
QuoteChar:=C2;
|
|
StrictDelimiter:=FSD;
|
|
end;
|
|
end;
|
|
|
|
|
|
Function TStrings.GetDelimitedText: string;
|
|
|
|
Var
|
|
I : integer;
|
|
p : pchar;
|
|
c : set of char;
|
|
S : String;
|
|
|
|
begin
|
|
CheckSpecialChars;
|
|
result:='';
|
|
if StrictDelimiter then
|
|
c:=[#0,Delimiter]
|
|
else
|
|
c:=[#0..' ',QuoteChar,Delimiter];
|
|
For i:=0 to count-1 do
|
|
begin
|
|
S:=Strings[i];
|
|
p:=pchar(S);
|
|
while not(p^ in c) do
|
|
inc(p);
|
|
// strings in list may contain #0
|
|
if (p<>pchar(S)+length(S)) and not StrictDelimiter then
|
|
Result:=Result+QuoteString(S,QuoteChar)
|
|
else
|
|
Result:=Result+S;
|
|
if I<Count-1 then
|
|
Result:=Result+Delimiter;
|
|
end;
|
|
If (Length(Result)=0) and (Count=1) then
|
|
Result:=QuoteChar+QuoteChar;
|
|
end;
|
|
|
|
procedure TStrings.GetNameValue(Index : Integer; Out AName,AValue : String);
|
|
|
|
Var L : longint;
|
|
|
|
begin
|
|
CheckSpecialChars;
|
|
AValue:=Strings[Index];
|
|
L:=Pos(FNameValueSeparator,AValue);
|
|
If L<>0 then
|
|
begin
|
|
AName:=Copy(AValue,1,L-1);
|
|
System.Delete(AValue,1,L);
|
|
end
|
|
else
|
|
AName:='';
|
|
end;
|
|
|
|
function TStrings.ExtractName(const s:String):String;
|
|
var
|
|
L: Longint;
|
|
begin
|
|
CheckSpecialChars;
|
|
L:=Pos(FNameValueSeparator,S);
|
|
If L<>0 then
|
|
Result:=Copy(S,1,L-1)
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TStrings.GetName(Index: Integer): string;
|
|
|
|
Var
|
|
V : String;
|
|
|
|
begin
|
|
GetNameValue(Index,Result,V);
|
|
end;
|
|
|
|
Function TStrings.GetValue(const Name: string): string;
|
|
|
|
Var
|
|
L : longint;
|
|
N : String;
|
|
|
|
begin
|
|
Result:='';
|
|
L:=IndexOfName(Name);
|
|
If L<>-1 then
|
|
GetNameValue(L,N,Result);
|
|
end;
|
|
|
|
Function TStrings.GetValueFromIndex(Index: Integer): string;
|
|
|
|
Var
|
|
N : String;
|
|
|
|
begin
|
|
GetNameValue(Index,N,Result);
|
|
end;
|
|
|
|
Procedure TStrings.SetValueFromIndex(Index: Integer; const Value: string);
|
|
|
|
begin
|
|
If (Value='') then
|
|
Delete(Index)
|
|
else
|
|
begin
|
|
If (Index<0) then
|
|
Index:=Add('');
|
|
CheckSpecialChars;
|
|
Strings[Index]:=GetName(Index)+FNameValueSeparator+Value;
|
|
end;
|
|
end;
|
|
|
|
procedure TStrings.ReadData(Reader: TReader);
|
|
begin
|
|
Reader.ReadListBegin;
|
|
BeginUpdate;
|
|
try
|
|
Clear;
|
|
while not Reader.EndOfList do
|
|
Add(Reader.ReadString);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
Reader.ReadListEnd;
|
|
end;
|
|
|
|
|
|
Procedure TStrings.SetDelimitedText(const AValue: string);
|
|
var i,j:integer;
|
|
aNotFirst:boolean;
|
|
begin
|
|
CheckSpecialChars;
|
|
BeginUpdate;
|
|
|
|
i:=1;
|
|
j:=1;
|
|
aNotFirst:=false;
|
|
|
|
try
|
|
Clear;
|
|
If StrictDelimiter then
|
|
begin
|
|
// Easier, faster loop.
|
|
While I<=Length(AValue) do
|
|
begin
|
|
If (AValue[I] in [FDelimiter,#0]) then
|
|
begin
|
|
Add(Copy(AValue,J,I-J));
|
|
J:=I+1;
|
|
end;
|
|
Inc(i);
|
|
end;
|
|
If (Length(AValue)>0) then
|
|
Add(Copy(AValue,J,I-J));
|
|
end
|
|
else
|
|
begin
|
|
while i<=length(AValue) do begin
|
|
// skip delimiter
|
|
if aNotFirst and (i<=length(AValue)) and (AValue[i]=FDelimiter) then inc(i);
|
|
|
|
// skip spaces
|
|
while (i<=length(AValue)) and (Ord(AValue[i])<=Ord(' ')) do inc(i);
|
|
|
|
// read next string
|
|
if i<=length(AValue) then begin
|
|
if AValue[i]=FQuoteChar then begin
|
|
// next string is quoted
|
|
j:=i+1;
|
|
while (j<=length(AValue)) and
|
|
( (AValue[j]<>FQuoteChar) or
|
|
( (j+1<=length(AValue)) and (AValue[j+1]=FQuoteChar) ) ) do begin
|
|
if (j<=length(AValue)) and (AValue[j]=FQuoteChar) then inc(j,2)
|
|
else inc(j);
|
|
end;
|
|
// j is position of closing quote
|
|
Add( StringReplace (Copy(AValue,i+1,j-i-1),
|
|
FQuoteChar+FQuoteChar,FQuoteChar, [rfReplaceAll]));
|
|
i:=j+1;
|
|
end else begin
|
|
// next string is not quoted
|
|
j:=i;
|
|
while (j<=length(AValue)) and
|
|
(Ord(AValue[j])>Ord(' ')) and
|
|
(AValue[j]<>FDelimiter) do inc(j);
|
|
Add( Copy(AValue,i,j-i));
|
|
i:=j;
|
|
end;
|
|
end else begin
|
|
if aNotFirst then Add('');
|
|
end;
|
|
|
|
// skip spaces
|
|
while (i<=length(AValue)) and (Ord(AValue[i])<=Ord(' ')) do inc(i);
|
|
|
|
aNotFirst:=true;
|
|
end;
|
|
end;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
Procedure TStrings.SetCommaText(const Value: string);
|
|
|
|
Var
|
|
C1,C2 : Char;
|
|
|
|
begin
|
|
CheckSpecialChars;
|
|
C1:=Delimiter;
|
|
C2:=QuoteChar;
|
|
Delimiter:=',';
|
|
QuoteChar:='"';
|
|
Try
|
|
SetDelimitedText(Value);
|
|
Finally
|
|
Delimiter:=C1;
|
|
QuoteChar:=C2;
|
|
end;
|
|
end;
|
|
|
|
|
|
Procedure TStrings.SetStringsAdapter(const Value: IStringsAdapter);
|
|
|
|
begin
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStrings.SetValue(const Name, Value: string);
|
|
|
|
Var L : longint;
|
|
|
|
begin
|
|
CheckSpecialChars;
|
|
L:=IndexOfName(Name);
|
|
if L=-1 then
|
|
Add (Name+FNameValueSeparator+Value)
|
|
else
|
|
Strings[L]:=Name+FNameValueSeparator+value;
|
|
end;
|
|
|
|
|
|
|
|
procedure TStrings.WriteData(Writer: TWriter);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Writer.WriteListBegin;
|
|
for i := 0 to Count - 1 do
|
|
Writer.WriteString(Strings[i]);
|
|
Writer.WriteListEnd;
|
|
end;
|
|
|
|
|
|
|
|
procedure TStrings.DefineProperties(Filer: TFiler);
|
|
var
|
|
HasData: Boolean;
|
|
begin
|
|
if Assigned(Filer.Ancestor) then
|
|
// Only serialize if string list is different from ancestor
|
|
if Filer.Ancestor.InheritsFrom(TStrings) then
|
|
HasData := not Equals(TStrings(Filer.Ancestor))
|
|
else
|
|
HasData := True
|
|
else
|
|
HasData := Count > 0;
|
|
Filer.DefineProperty('Strings', @ReadData, @WriteData, HasData);
|
|
end;
|
|
|
|
|
|
Procedure TStrings.Error(const Msg: string; Data: Integer);
|
|
begin
|
|
Raise EStringListError.CreateFmt(Msg,[Data]) at get_caller_addr(get_frame);
|
|
end;
|
|
|
|
|
|
Procedure TStrings.Error(const Msg: pstring; Data: Integer);
|
|
begin
|
|
Raise EStringListError.CreateFmt(Msg^,[Data]) at get_caller_addr(get_frame);
|
|
end;
|
|
|
|
|
|
Function TStrings.GetCapacity: Integer;
|
|
|
|
begin
|
|
Result:=Count;
|
|
end;
|
|
|
|
|
|
|
|
Function TStrings.GetObject(Index: Integer): TObject;
|
|
|
|
begin
|
|
Result:=Nil;
|
|
end;
|
|
|
|
|
|
|
|
Function TStrings.GetTextStr: string;
|
|
|
|
Var P : Pchar;
|
|
I,L,NLS : Longint;
|
|
S,NL : String;
|
|
|
|
begin
|
|
CheckSpecialChars;
|
|
// Determine needed place
|
|
Case FLBS of
|
|
tlbsLF : NL:=#10;
|
|
tlbsCRLF : NL:=#13#10;
|
|
tlbsCR : NL:=#13;
|
|
end;
|
|
L:=0;
|
|
NLS:=Length(NL);
|
|
For I:=0 to count-1 do
|
|
L:=L+Length(Strings[I])+NLS;
|
|
Setlength(Result,L);
|
|
P:=Pointer(Result);
|
|
For i:=0 To count-1 do
|
|
begin
|
|
S:=Strings[I];
|
|
L:=Length(S);
|
|
if L<>0 then
|
|
System.Move(Pointer(S)^,P^,L);
|
|
P:=P+L;
|
|
For L:=1 to NLS do
|
|
begin
|
|
P^:=NL[L];
|
|
inc(P);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStrings.Put(Index: Integer; const S: string);
|
|
|
|
Var Obj : TObject;
|
|
|
|
begin
|
|
Obj:=Objects[Index];
|
|
Delete(Index);
|
|
InsertObject(Index,S,Obj);
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStrings.PutObject(Index: Integer; AObject: TObject);
|
|
|
|
begin
|
|
// Empty.
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStrings.SetCapacity(NewCapacity: Integer);
|
|
|
|
begin
|
|
// Empty.
|
|
end;
|
|
|
|
Function GetNextLine (Const Value : String; Var S : String; Var P : Integer) : Boolean;
|
|
|
|
Var
|
|
PS : PChar;
|
|
IP,L : Integer;
|
|
|
|
begin
|
|
L:=Length(Value);
|
|
S:='';
|
|
Result:=False;
|
|
If ((L-P)<0) then
|
|
exit;
|
|
if ((L-P)=0) and (not (value[P] in [#10,#13])) Then
|
|
Begin
|
|
s:=value[P];
|
|
inc(P);
|
|
Exit(True);
|
|
End;
|
|
PS:=PChar(Value)+P-1;
|
|
IP:=P;
|
|
While ((L-P)>=0) and (not (PS^ in [#10,#13])) do
|
|
begin
|
|
P:=P+1;
|
|
Inc(PS);
|
|
end;
|
|
SetLength (S,P-IP);
|
|
System.Move (Value[IP],Pointer(S)^,P-IP);
|
|
If (P<=L) and (Value[P]=#13) then
|
|
Inc(P);
|
|
If (P<=L) and (Value[P]=#10) then
|
|
Inc(P); // Point to character after #10(#13)
|
|
Result:=True;
|
|
end;
|
|
|
|
Procedure TStrings.SetTextStr(const Value: string);
|
|
|
|
Var
|
|
S : String;
|
|
P : Integer;
|
|
|
|
begin
|
|
Try
|
|
beginUpdate;
|
|
Clear;
|
|
P:=1;
|
|
While GetNextLine (Value,S,P) do
|
|
Add(S);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStrings.SetUpdateState(Updating: Boolean);
|
|
|
|
begin
|
|
end;
|
|
|
|
|
|
|
|
destructor TSTrings.Destroy;
|
|
|
|
begin
|
|
inherited destroy;
|
|
end;
|
|
|
|
|
|
|
|
Function TStrings.Add(const S: string): Integer;
|
|
|
|
begin
|
|
Result:=Count;
|
|
Insert (Count,S);
|
|
end;
|
|
|
|
|
|
|
|
Function TStrings.AddObject(const S: string; AObject: TObject): Integer;
|
|
|
|
begin
|
|
Result:=Add(S);
|
|
Objects[result]:=AObject;
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStrings.Append(const S: string);
|
|
|
|
begin
|
|
Add (S);
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStrings.AddStrings(TheStrings: TStrings);
|
|
|
|
Var Runner : longint;
|
|
|
|
begin
|
|
try
|
|
beginupdate;
|
|
For Runner:=0 to TheStrings.Count-1 do
|
|
self.AddObject (Thestrings[Runner],TheStrings.Objects[Runner]);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStrings.Assign(Source: TPersistent);
|
|
|
|
Var
|
|
S : TStrings;
|
|
|
|
begin
|
|
If Source is TStrings then
|
|
begin
|
|
S:=TStrings(Source);
|
|
BeginUpdate;
|
|
Try
|
|
clear;
|
|
FSpecialCharsInited:=S.FSpecialCharsInited;
|
|
FQuoteChar:=S.FQuoteChar;
|
|
FDelimiter:=S.FDelimiter;
|
|
FNameValueSeparator:=S.FNameValueSeparator;
|
|
FLBS:=S.FLBS;
|
|
AddStrings(S);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end
|
|
else
|
|
Inherited Assign(Source);
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStrings.BeginUpdate;
|
|
|
|
begin
|
|
if FUpdateCount = 0 then SetUpdateState(true);
|
|
inc(FUpdateCount);
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStrings.EndUpdate;
|
|
|
|
begin
|
|
If FUpdateCount>0 then
|
|
Dec(FUpdateCount);
|
|
if FUpdateCount=0 then
|
|
SetUpdateState(False);
|
|
end;
|
|
|
|
|
|
|
|
Function TStrings.Equals(Obj: TObject): Boolean;
|
|
|
|
begin
|
|
if Obj is TStrings then
|
|
Result := Equals(TStrings(Obj))
|
|
else
|
|
Result := inherited Equals(Obj);
|
|
end;
|
|
|
|
|
|
|
|
Function TStrings.Equals(TheStrings: TStrings): Boolean;
|
|
|
|
Var Runner,Nr : Longint;
|
|
|
|
begin
|
|
Result:=False;
|
|
Nr:=Self.Count;
|
|
if Nr<>TheStrings.Count then exit;
|
|
For Runner:=0 to Nr-1 do
|
|
If Strings[Runner]<>TheStrings[Runner] then exit;
|
|
Result:=True;
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStrings.Exchange(Index1, Index2: Integer);
|
|
|
|
Var
|
|
Obj : TObject;
|
|
Str : String;
|
|
|
|
begin
|
|
Try
|
|
beginUpdate;
|
|
Obj:=Objects[Index1];
|
|
Str:=Strings[Index1];
|
|
Objects[Index1]:=Objects[Index2];
|
|
Strings[Index1]:=Strings[Index2];
|
|
Objects[Index2]:=Obj;
|
|
Strings[Index2]:=Str;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
|
|
function TStrings.GetEnumerator: TStringsEnumerator;
|
|
begin
|
|
Result:=TStringsEnumerator.Create(Self);
|
|
end;
|
|
|
|
|
|
Function TStrings.GetText: PChar;
|
|
begin
|
|
Result:=StrNew(Pchar(Self.Text));
|
|
end;
|
|
|
|
|
|
Function TStrings.DoCompareText(const s1,s2 : string) : PtrInt;
|
|
begin
|
|
result:=CompareText(s1,s2);
|
|
end;
|
|
|
|
|
|
Function TStrings.IndexOf(const S: string): Integer;
|
|
begin
|
|
Result:=0;
|
|
While (Result<Count) and (DoCompareText(Strings[Result],S)<>0) do Result:=Result+1;
|
|
if Result=Count then Result:=-1;
|
|
end;
|
|
|
|
|
|
Function TStrings.IndexOfName(const Name: string): Integer;
|
|
Var
|
|
len : longint;
|
|
S : String;
|
|
begin
|
|
CheckSpecialChars;
|
|
Result:=0;
|
|
while (Result<Count) do
|
|
begin
|
|
S:=Strings[Result];
|
|
len:=pos(FNameValueSeparator,S)-1;
|
|
if (len>0) and (DoCompareText(Name,Copy(S,1,Len))=0) then
|
|
exit;
|
|
inc(result);
|
|
end;
|
|
result:=-1;
|
|
end;
|
|
|
|
|
|
Function TStrings.IndexOfObject(AObject: TObject): Integer;
|
|
begin
|
|
Result:=0;
|
|
While (Result<count) and (Objects[Result]<>AObject) do Result:=Result+1;
|
|
If Result=Count then Result:=-1;
|
|
end;
|
|
|
|
|
|
Procedure TStrings.InsertObject(Index: Integer; const S: string;
|
|
AObject: TObject);
|
|
|
|
begin
|
|
Insert (Index,S);
|
|
Objects[Index]:=AObject;
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStrings.LoadFromFile(const FileName: string);
|
|
Var
|
|
TheStream : TFileStream;
|
|
begin
|
|
TheStream:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
|
|
try
|
|
LoadFromStream(TheStream);
|
|
finally
|
|
TheStream.Free;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStrings.LoadFromStream(Stream: TStream);
|
|
{
|
|
Borlands method is no good, since a pipe for
|
|
instance doesn't have a size.
|
|
So we must do it the hard way.
|
|
}
|
|
Const
|
|
BufSize = 1024;
|
|
MaxGrow = 1 shl 29;
|
|
|
|
Var
|
|
Buffer : AnsiString;
|
|
BytesRead,
|
|
BufLen,
|
|
I,BufDelta : Longint;
|
|
begin
|
|
// reread into a buffer
|
|
try
|
|
beginupdate;
|
|
Buffer:='';
|
|
BufLen:=0;
|
|
I:=1;
|
|
Repeat
|
|
BufDelta:=BufSize*I;
|
|
SetLength(Buffer,BufLen+BufDelta);
|
|
BytesRead:=Stream.Read(Buffer[BufLen+1],BufDelta);
|
|
inc(BufLen,BufDelta);
|
|
If I<MaxGrow then
|
|
I:=I shl 1;
|
|
Until BytesRead<>BufDelta;
|
|
SetLength(Buffer, BufLen-BufDelta+BytesRead);
|
|
SetTextStr(Buffer);
|
|
SetLength(Buffer,0);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
|
|
Procedure TStrings.Move(CurIndex, NewIndex: Integer);
|
|
Var
|
|
Obj : TObject;
|
|
Str : String;
|
|
begin
|
|
BeginUpdate;
|
|
Obj:=Objects[CurIndex];
|
|
Str:=Strings[CurIndex];
|
|
Delete(Curindex);
|
|
InsertObject(NewIndex,Str,Obj);
|
|
EndUpdate;
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStrings.SaveToFile(const FileName: string);
|
|
|
|
Var TheStream : TFileStream;
|
|
|
|
begin
|
|
TheStream:=TFileStream.Create(FileName,fmCreate);
|
|
try
|
|
SaveToStream(TheStream);
|
|
finally
|
|
TheStream.Free;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStrings.SaveToStream(Stream: TStream);
|
|
Var
|
|
S : String;
|
|
begin
|
|
S:=Text;
|
|
if S = '' then Exit;
|
|
Stream.WriteBuffer(Pointer(S)^,Length(S));
|
|
end;
|
|
|
|
|
|
|
|
|
|
Procedure TStrings.SetText(TheText: PChar);
|
|
|
|
Var S : String;
|
|
|
|
begin
|
|
If TheText<>Nil then
|
|
S:=StrPas(TheText)
|
|
else
|
|
S:='';
|
|
SetTextStr(S);
|
|
end;
|
|
|
|
|
|
{****************************************************************************}
|
|
{* TStringList *}
|
|
{****************************************************************************}
|
|
|
|
{$if defined(VER2_0) or not defined(FPC_TESTGENERICS)}
|
|
|
|
Procedure TStringList.ExchangeItems(Index1, Index2: Integer);
|
|
|
|
Var P1,P2 : Pointer;
|
|
|
|
begin
|
|
P1:=Pointer(Flist^[Index1].FString);
|
|
P2:=Pointer(Flist^[Index1].FObject);
|
|
Pointer(Flist^[Index1].Fstring):=Pointer(Flist^[Index2].Fstring);
|
|
Pointer(Flist^[Index1].FObject):=Pointer(Flist^[Index2].FObject);
|
|
Pointer(Flist^[Index2].Fstring):=P1;
|
|
Pointer(Flist^[Index2].FObject):=P2;
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStringList.Grow;
|
|
|
|
Var
|
|
NC : Integer;
|
|
|
|
begin
|
|
NC:=FCapacity;
|
|
If NC>=256 then
|
|
NC:=NC+(NC Div 4)
|
|
else if NC=0 then
|
|
NC:=4
|
|
else
|
|
NC:=NC*4;
|
|
SetCapacity(NC);
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
|
|
var
|
|
Pivot, vL, vR: Integer;
|
|
begin
|
|
if R - L <= 1 then begin // a little bit of time saver
|
|
if L < R then
|
|
if CompareFn(Self, L, R) > 0 then
|
|
ExchangeItems(L, R);
|
|
|
|
Exit;
|
|
end;
|
|
|
|
vL := L;
|
|
vR := R;
|
|
|
|
Pivot := L + Random(R - L); // they say random is best
|
|
|
|
while vL < vR do begin
|
|
while (vL < Pivot) and (CompareFn(Self, vL, Pivot) <= 0) do
|
|
Inc(vL);
|
|
|
|
while (vR > Pivot) and (CompareFn(Self, vR, Pivot) > 0) do
|
|
Dec(vR);
|
|
|
|
ExchangeItems(vL, vR);
|
|
|
|
if Pivot = vL then // swap pivot if we just hit it from one side
|
|
Pivot := vR
|
|
else if Pivot = vR then
|
|
Pivot := vL;
|
|
end;
|
|
|
|
if Pivot - 1 >= L then
|
|
QuickSort(L, Pivot - 1, CompareFn);
|
|
if Pivot + 1 <= R then
|
|
QuickSort(Pivot + 1, R, CompareFn);
|
|
end;
|
|
|
|
|
|
Procedure TStringList.InsertItem(Index: Integer; const S: string);
|
|
begin
|
|
Changing;
|
|
If FCount=Fcapacity then Grow;
|
|
If Index<FCount then
|
|
System.Move (FList^[Index],FList^[Index+1],
|
|
(FCount-Index)*SizeOf(TStringItem));
|
|
Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
|
|
Flist^[Index].FString:=S;
|
|
Flist^[Index].Fobject:=Nil;
|
|
Inc(FCount);
|
|
Changed;
|
|
end;
|
|
|
|
|
|
Procedure TStringList.InsertItem(Index: Integer; const S: string; O: TObject);
|
|
begin
|
|
Changing;
|
|
If FCount=Fcapacity then Grow;
|
|
If Index<FCount then
|
|
System.Move (FList^[Index],FList^[Index+1],
|
|
(FCount-Index)*SizeOf(TStringItem));
|
|
Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
|
|
Flist^[Index].FString:=S;
|
|
Flist^[Index].FObject:=O;
|
|
Inc(FCount);
|
|
Changed;
|
|
end;
|
|
|
|
|
|
Procedure TStringList.SetSorted(Value: Boolean);
|
|
|
|
begin
|
|
If FSorted<>Value then
|
|
begin
|
|
If Value then sort;
|
|
FSorted:=VAlue
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStringList.Changed;
|
|
|
|
begin
|
|
If (FUpdateCount=0) Then
|
|
If Assigned(FOnChange) then
|
|
FOnchange(Self);
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStringList.Changing;
|
|
|
|
begin
|
|
If FUpdateCount=0 then
|
|
if Assigned(FOnChanging) then
|
|
FOnchanging(Self);
|
|
end;
|
|
|
|
|
|
|
|
Function TStringList.Get(Index: Integer): string;
|
|
|
|
begin
|
|
If (Index<0) or (INdex>=Fcount) then
|
|
Error (SListIndexError,Index);
|
|
Result:=Flist^[Index].FString;
|
|
end;
|
|
|
|
|
|
|
|
Function TStringList.GetCapacity: Integer;
|
|
|
|
begin
|
|
Result:=FCapacity;
|
|
end;
|
|
|
|
|
|
|
|
Function TStringList.GetCount: Integer;
|
|
|
|
begin
|
|
Result:=FCount;
|
|
end;
|
|
|
|
|
|
|
|
Function TStringList.GetObject(Index: Integer): TObject;
|
|
|
|
begin
|
|
If (Index<0) or (INdex>=Fcount) then
|
|
Error (SListIndexError,Index);
|
|
Result:=Flist^[Index].FObject;
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStringList.Put(Index: Integer; const S: string);
|
|
|
|
begin
|
|
If Sorted then
|
|
Error(SSortedListError,0);
|
|
If (Index<0) or (INdex>=Fcount) then
|
|
Error (SListIndexError,Index);
|
|
Changing;
|
|
Flist^[Index].FString:=S;
|
|
Changed;
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStringList.PutObject(Index: Integer; AObject: TObject);
|
|
|
|
begin
|
|
If (Index<0) or (INdex>=Fcount) then
|
|
Error (SListIndexError,Index);
|
|
Changing;
|
|
Flist^[Index].FObject:=AObject;
|
|
Changed;
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStringList.SetCapacity(NewCapacity: Integer);
|
|
|
|
Var NewList : Pointer;
|
|
MSize : Longint;
|
|
|
|
begin
|
|
If (NewCapacity<0) then
|
|
Error (SListCapacityError,NewCapacity);
|
|
If NewCapacity>FCapacity then
|
|
begin
|
|
GetMem (NewList,NewCapacity*SizeOf(TStringItem));
|
|
If NewList=Nil then
|
|
Error (SListCapacityError,NewCapacity);
|
|
If Assigned(FList) then
|
|
begin
|
|
MSize:=FCapacity*Sizeof(TStringItem);
|
|
System.Move (FList^,NewList^,MSize);
|
|
FillWord (Pchar(NewList)[MSize],(NewCapacity-FCapacity)*WordRatio, 0);
|
|
FreeMem (Flist,MSize);
|
|
end;
|
|
Flist:=NewList;
|
|
FCapacity:=NewCapacity;
|
|
end
|
|
else if NewCapacity<FCapacity then
|
|
begin
|
|
if NewCapacity = 0 then
|
|
begin
|
|
FreeMem(FList);
|
|
FList := nil;
|
|
end else
|
|
begin
|
|
GetMem(NewList, NewCapacity * SizeOf(TStringItem));
|
|
System.Move(FList^, NewList^, NewCapacity * SizeOf(TStringItem));
|
|
FreeMem(FList);
|
|
FList := NewList;
|
|
end;
|
|
FCapacity:=NewCapacity;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStringList.SetUpdateState(Updating: Boolean);
|
|
|
|
begin
|
|
If Updating then
|
|
Changing
|
|
else
|
|
Changed
|
|
end;
|
|
|
|
|
|
|
|
destructor TStringList.Destroy;
|
|
|
|
Var I : Longint;
|
|
|
|
begin
|
|
FOnChange:=Nil;
|
|
FOnChanging:=Nil;
|
|
// This will force a dereference. Can be done better...
|
|
For I:=0 to FCount-1 do
|
|
FList^[I].FString:='';
|
|
FCount:=0;
|
|
SetCapacity(0);
|
|
Inherited destroy;
|
|
end;
|
|
|
|
|
|
|
|
Function TStringList.Add(const S: string): Integer;
|
|
|
|
begin
|
|
If Not Sorted then
|
|
Result:=FCount
|
|
else
|
|
If Find (S,Result) then
|
|
Case DUplicates of
|
|
DupIgnore : Exit;
|
|
DupError : Error(SDuplicateString,0)
|
|
end;
|
|
InsertItem (Result,S);
|
|
end;
|
|
|
|
Procedure TStringList.Clear;
|
|
|
|
Var I : longint;
|
|
|
|
begin
|
|
if FCount = 0 then Exit;
|
|
Changing;
|
|
if FOwnsObjects then
|
|
begin
|
|
For I:=0 to FCount-1 do
|
|
begin
|
|
Flist^[I].FString:='';
|
|
freeandnil(Flist^[i].FObject);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
For I:=0 to FCount-1 do
|
|
Flist^[I].FString:='';
|
|
end;
|
|
FCount:=0;
|
|
SetCapacity(0);
|
|
Changed;
|
|
end;
|
|
|
|
Procedure TStringList.Delete(Index: Integer);
|
|
|
|
begin
|
|
If (Index<0) or (Index>=FCount) then
|
|
Error(SlistINdexError,Index);
|
|
Changing;
|
|
Flist^[Index].FString:='';
|
|
if FOwnsObjects then
|
|
FreeAndNil(Flist^[Index].FObject);
|
|
Dec(FCount);
|
|
If Index<FCount then
|
|
System.Move(Flist^[Index+1],
|
|
Flist^[Index],
|
|
(Fcount-Index)*SizeOf(TStringItem));
|
|
Changed;
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStringList.Exchange(Index1, Index2: Integer);
|
|
|
|
begin
|
|
If (Index1<0) or (Index1>=FCount) then
|
|
Error(SListIndexError,Index1);
|
|
If (Index2<0) or (Index2>=FCount) then
|
|
Error(SListIndexError,Index2);
|
|
Changing;
|
|
ExchangeItems(Index1,Index2);
|
|
changed;
|
|
end;
|
|
|
|
|
|
procedure TStringList.SetCaseSensitive(b : boolean);
|
|
begin
|
|
if b<>FCaseSensitive then
|
|
begin
|
|
FCaseSensitive:=b;
|
|
if FSorted then
|
|
sort;
|
|
end;
|
|
end;
|
|
|
|
|
|
Function TStringList.DoCompareText(const s1,s2 : string) : PtrInt;
|
|
begin
|
|
if FCaseSensitive then
|
|
result:=AnsiCompareStr(s1,s2)
|
|
else
|
|
result:=AnsiCompareText(s1,s2);
|
|
end;
|
|
|
|
|
|
Function TStringList.Find(const S: string; Out Index: Integer): Boolean;
|
|
|
|
var
|
|
L, R, I: Integer;
|
|
CompareRes: PtrInt;
|
|
begin
|
|
Result := false;
|
|
// Use binary search.
|
|
L := 0;
|
|
R := Count - 1;
|
|
while (L<=R) do
|
|
begin
|
|
I := L + (R - L) div 2;
|
|
CompareRes := DoCompareText(S, Flist^[I].FString);
|
|
if (CompareRes>0) then
|
|
L := I+1
|
|
else begin
|
|
R := I-1;
|
|
if (CompareRes=0) then begin
|
|
Result := true;
|
|
if (Duplicates<>dupAccept) then
|
|
L := I; // forces end of while loop
|
|
end;
|
|
end;
|
|
end;
|
|
Index := L;
|
|
end;
|
|
|
|
|
|
|
|
Function TStringList.IndexOf(const S: string): Integer;
|
|
|
|
begin
|
|
If Not Sorted then
|
|
Result:=Inherited indexOf(S)
|
|
else
|
|
// faster using binary search...
|
|
If Not Find (S,Result) then
|
|
Result:=-1;
|
|
end;
|
|
|
|
|
|
|
|
Procedure TStringList.Insert(Index: Integer; const S: string);
|
|
|
|
begin
|
|
If Sorted then
|
|
Error (SSortedListError,0)
|
|
else
|
|
If (Index<0) or (Index>FCount) then
|
|
Error (SListIndexError,Index)
|
|
else
|
|
InsertItem (Index,S);
|
|
end;
|
|
|
|
|
|
Procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
|
|
|
|
begin
|
|
If Not Sorted and (FCount>1) then
|
|
begin
|
|
Changing;
|
|
QuickSort(0,FCount-1, CompareFn);
|
|
Changed;
|
|
end;
|
|
end;
|
|
|
|
function StringListAnsiCompare(List: TStringList; Index1, Index: Integer): Integer;
|
|
|
|
begin
|
|
Result := List.DoCompareText(List.FList^[Index1].FString,
|
|
List.FList^[Index].FString);
|
|
end;
|
|
|
|
Procedure TStringList.Sort;
|
|
|
|
begin
|
|
CustomSort(@StringListAnsiCompare);
|
|
end;
|
|
|
|
{$else}
|
|
|
|
{ generics based implementation of TStringList follows }
|
|
|
|
function StringListAnsiCompare(List: TStringList; Index1, Index2: Integer): Integer;
|
|
begin
|
|
Result := List.DoCompareText(List.Strings[Index1], List.Strings[Index2]);
|
|
end;
|
|
|
|
constructor TStringList.Create;
|
|
begin
|
|
inherited;
|
|
FOwnsObjects:=false;
|
|
FMap := TFPStrObjMap.Create;
|
|
FMap.OnPtrCompare := @MapPtrCompare;
|
|
FOnCompareText := @DefaultCompareText;
|
|
CheckSpecialChars;
|
|
end;
|
|
|
|
destructor TStringList.Destroy;
|
|
begin
|
|
FMap.Free;
|
|
inherited;
|
|
end;
|
|
|
|
function TStringList.GetDuplicates: TDuplicates;
|
|
begin
|
|
Result := FMap.Duplicates;
|
|
end;
|
|
|
|
function TStringList.GetSorted: boolean;
|
|
begin
|
|
Result := FMap.Sorted;
|
|
end;
|
|
|
|
procedure TStringList.SetDuplicates(NewDuplicates: TDuplicates);
|
|
begin
|
|
FMap.Duplicates := NewDuplicates;
|
|
end;
|
|
|
|
procedure TStringList.SetSorted(NewSorted: Boolean);
|
|
begin
|
|
FMap.Sorted := NewSorted;
|
|
end;
|
|
|
|
procedure TStringList.Changed;
|
|
begin
|
|
if FUpdateCount = 0 then
|
|
if Assigned(FOnChange) then
|
|
FOnChange(Self);
|
|
end;
|
|
|
|
procedure TStringList.Changing;
|
|
begin
|
|
if FUpdateCount = 0 then
|
|
if Assigned(FOnChanging) then
|
|
FOnChanging(Self);
|
|
end;
|
|
|
|
function TStringList.Get(Index: Integer): string;
|
|
begin
|
|
Result := FMap.Keys[Index];
|
|
end;
|
|
|
|
function TStringList.GetCapacity: Integer;
|
|
begin
|
|
Result := FMap.Capacity;
|
|
end;
|
|
|
|
function TStringList.GetCount: Integer;
|
|
begin
|
|
Result := FMap.Count;
|
|
end;
|
|
|
|
function TStringList.GetObject(Index: Integer): TObject;
|
|
begin
|
|
Result := FMap.Data[Index];
|
|
end;
|
|
|
|
procedure TStringList.Put(Index: Integer; const S: string);
|
|
begin
|
|
Changing;
|
|
FMap.Keys[Index] := S;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TStringList.PutObject(Index: Integer; AObject: TObject);
|
|
begin
|
|
Changing;
|
|
FMap.Data[Index] := AObject;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TStringList.SetCapacity(NewCapacity: Integer);
|
|
begin
|
|
FMap.Capacity := NewCapacity;
|
|
end;
|
|
|
|
procedure TStringList.SetUpdateState(Updating: Boolean);
|
|
begin
|
|
if Updating then
|
|
Changing
|
|
else
|
|
Changed
|
|
end;
|
|
|
|
function TStringList.Add(const S: string): Integer;
|
|
begin
|
|
Result := FMap.Add(S);
|
|
end;
|
|
|
|
procedure TStringList.Clear;
|
|
begin
|
|
if FMap.Count = 0 then exit;
|
|
Changing;
|
|
FMap.Clear;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TStringList.Delete(Index: Integer);
|
|
begin
|
|
if (Index < 0) or (Index >= FMap.Count) then
|
|
Error(SListIndexError, Index);
|
|
Changing;
|
|
FMap.Delete(Index);
|
|
Changed;
|
|
end;
|
|
|
|
procedure TStringList.Exchange(Index1, Index2: Integer);
|
|
begin
|
|
if (Index1 < 0) or (Index1 >= FMap.Count) then
|
|
Error(SListIndexError, Index1);
|
|
if (Index2 < 0) or (Index2 >= FMap.Count) then
|
|
Error(SListIndexError, Index2);
|
|
Changing;
|
|
FMap.InternalExchange(Index1, Index2);
|
|
Changed;
|
|
end;
|
|
|
|
procedure TStringList.SetCaseSensitive(NewSensitive: Boolean);
|
|
begin
|
|
if NewSensitive <> FCaseSensitive then
|
|
begin
|
|
FCaseSensitive := NewSensitive;
|
|
if Sorted then
|
|
Sort;
|
|
end;
|
|
end;
|
|
|
|
function TStringList.MapPtrCompare(Key1, Key2: Pointer): Integer;
|
|
begin
|
|
Result := FOnCompareText(string(Key1^), string(Key2^));
|
|
end;
|
|
|
|
function TStringList.DefaultCompareText(const s1, s2: string): PtrInt;
|
|
begin
|
|
if FCaseSensitive then
|
|
Result := AnsiCompareStr(s1, s2)
|
|
else
|
|
Result := AnsiCompareText(s1, s2);
|
|
end;
|
|
|
|
function TStringList.DoCompareText(const s1, s2: string): PtrInt;
|
|
begin
|
|
Result := FOnCompareText(s1, s2);
|
|
end;
|
|
|
|
function TStringList.Find(const S: string; var Index: Integer): Boolean;
|
|
begin
|
|
Result := FMap.Find(S, Index);
|
|
end;
|
|
|
|
function TStringList.IndexOf(const S: string): Integer;
|
|
begin
|
|
Result := FMap.IndexOf(S);
|
|
end;
|
|
|
|
procedure TStringList.Insert(Index: Integer; const S: string);
|
|
begin
|
|
if not Sorted and (0 <= Index) and (Index < FMap.Count) then
|
|
Changing;
|
|
FMap.InsertKey(Index, S);
|
|
Changed;
|
|
end;
|
|
|
|
procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
|
|
var
|
|
I, J, Pivot: Integer;
|
|
begin
|
|
repeat
|
|
I := L;
|
|
J := R;
|
|
Pivot := (L + R) div 2;
|
|
repeat
|
|
while CompareFn(Self, I, Pivot) < 0 do Inc(I);
|
|
while CompareFn(Self, J, Pivot) > 0 do Dec(J);
|
|
if I <= J then
|
|
begin
|
|
FMap.InternalExchange(I, J); // No check, indices are correct.
|
|
if Pivot = I then
|
|
Pivot := J
|
|
else if Pivot = J then
|
|
Pivot := I;
|
|
Inc(I);
|
|
Dec(j);
|
|
end;
|
|
until I > J;
|
|
if L < J then
|
|
QuickSort(L,J, CompareFn);
|
|
L := I;
|
|
until I >= R;
|
|
end;
|
|
|
|
procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
|
|
begin
|
|
if not Sorted and (FMap.Count > 1) then
|
|
begin
|
|
Changing;
|
|
QuickSort(0, FMap.Count-1, CompareFn);
|
|
Changed;
|
|
end;
|
|
end;
|
|
|
|
procedure TStringList.Sort;
|
|
begin
|
|
if not Sorted and (FMap.Count > 1) then
|
|
begin
|
|
Changing;
|
|
FMap.Sort;
|
|
Changed;
|
|
end;
|
|
end;
|
|
|
|
{$endif}
|
|
|