stringl.inc 27 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 1999-2000 by the Free Pascal development team
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. **********************************************************************}
  10. {****************************************************************************}
  11. {* TStrings *}
  12. {****************************************************************************}
  13. // Function to quote text. Should move maybe to sysutils !!
  14. // Also, it is not clear at this point what exactly should be done.
  15. { //!! is used to mark unsupported things. }
  16. Function QuoteString (Const S : String; Quote : String) : String;
  17. Var
  18. I,J : Integer;
  19. begin
  20. J:=0;
  21. Result:=S;
  22. for i:=1to length(s) do
  23. begin
  24. inc(j);
  25. if S[i]=Quote then
  26. begin
  27. System.Insert(Quote,Result,J);
  28. inc(j);
  29. end;
  30. end;
  31. Result:=Quote+Result+Quote;
  32. end;
  33. {
  34. For compatibility we can't add a Constructor to TSTrings to initialize
  35. the special characters. Therefore we add a routine which is called whenever
  36. the special chars are needed.
  37. }
  38. Procedure Tstrings.CheckSpecialChars;
  39. begin
  40. If Not FSpecialCharsInited then
  41. begin
  42. FQuoteChar:='"';
  43. FDelimiter:=',';
  44. FNameValueSeparator:='=';
  45. FSpecialCharsInited:=true;
  46. FLBS:=DefaultTextLineBreakStyle;
  47. end;
  48. end;
  49. Function TStrings.GetLBS : TTextLineBreakStyle;
  50. begin
  51. CheckSpecialChars;
  52. Result:=FLBS;
  53. end;
  54. Procedure TStrings.SetLBS (AValue : TTextLineBreakStyle);
  55. begin
  56. CheckSpecialChars;
  57. FLBS:=AValue;
  58. end;
  59. procedure TStrings.SetDelimiter(c:Char);
  60. begin
  61. CheckSpecialChars;
  62. FDelimiter:=c;
  63. end;
  64. procedure TStrings.SetQuoteChar(c:Char);
  65. begin
  66. CheckSpecialChars;
  67. FQuoteChar:=c;
  68. end;
  69. procedure TStrings.SetNameValueSeparator(c:Char);
  70. begin
  71. CheckSpecialChars;
  72. FNameValueSeparator:=c;
  73. end;
  74. function TStrings.GetCommaText: string;
  75. Var
  76. C1,C2 : Char;
  77. begin
  78. CheckSpecialChars;
  79. C1:=Delimiter;
  80. C2:=QuoteChar;
  81. Delimiter:=',';
  82. QuoteChar:='"';
  83. Try
  84. Result:=GetDelimitedText;
  85. Finally
  86. Delimiter:=C1;
  87. QuoteChar:=C2;
  88. end;
  89. end;
  90. Function TStrings.GetDelimitedText: string;
  91. Var
  92. I : integer;
  93. p : pchar;
  94. begin
  95. CheckSpecialChars;
  96. result:='';
  97. For i:=0 to count-1 do
  98. begin
  99. p:=pchar(strings[i]);
  100. while not(p^ in [#0..' ',QuoteChar,Delimiter]) do
  101. inc(p);
  102. // strings in list may contain #0
  103. if p<>pchar(strings[i])+length(strings[i]) then
  104. Result:=Result+QuoteString (Strings[I],QuoteChar)
  105. else
  106. result:=result+strings[i];
  107. if I<Count-1 then Result:=Result+Delimiter;
  108. end;
  109. If (Length(Result)=0)and(count=1) then
  110. Result:=QuoteChar+QuoteChar;
  111. end;
  112. procedure TStrings.GetNameValue(Index : Integer; Var AName,AValue : String);
  113. Var L : longint;
  114. begin
  115. CheckSpecialChars;
  116. AValue:=Strings[Index];
  117. L:=Pos(FNameValueSeparator,AValue);
  118. If L<>0 then
  119. begin
  120. AName:=Copy(AValue,1,L-1);
  121. System.Delete(AValue,1,L);
  122. end
  123. else
  124. AName:='';
  125. end;
  126. function TStrings.ExtractName(const s:String):String;
  127. var
  128. L: Longint;
  129. begin
  130. CheckSpecialChars;
  131. L:=Pos(FNameValueSeparator,S);
  132. If L<>0 then
  133. Result:=Copy(S,1,L-1)
  134. else
  135. Result:='';
  136. end;
  137. function TStrings.GetName(Index: Integer): string;
  138. Var
  139. V : String;
  140. begin
  141. GetNameValue(Index,Result,V);
  142. end;
  143. Function TStrings.GetValue(const Name: string): string;
  144. Var
  145. L : longint;
  146. N : String;
  147. begin
  148. Result:='';
  149. L:=IndexOfName(Name);
  150. If L<>-1 then
  151. GetNameValue(L,N,Result);
  152. end;
  153. Function TStrings.GetValueFromIndex(Index: Integer): string;
  154. Var
  155. N : String;
  156. begin
  157. GetNameValue(Index,N,Result);
  158. end;
  159. Procedure TStrings.SetValueFromIndex(Index: Integer; const Value: string);
  160. begin
  161. If (Value='') then
  162. Delete(Index)
  163. else
  164. begin
  165. If (Index<0) then
  166. Index:=Add('');
  167. CheckSpecialChars;
  168. Strings[Index]:=GetName(Index)+FNameValueSeparator+Value;
  169. end;
  170. end;
  171. procedure TStrings.ReadData(Reader: TReader);
  172. begin
  173. Reader.ReadListBegin;
  174. BeginUpdate;
  175. try
  176. Clear;
  177. while not Reader.EndOfList do
  178. Add(Reader.ReadString);
  179. finally
  180. EndUpdate;
  181. end;
  182. Reader.ReadListEnd;
  183. end;
  184. Procedure TStrings.SetDelimitedText(const AValue: string);
  185. var i,j:integer;
  186. aNotFirst:boolean;
  187. begin
  188. CheckSpecialChars;
  189. BeginUpdate;
  190. i:=1;
  191. aNotFirst:=false;
  192. try
  193. Clear;
  194. while i<=length(AValue) do begin
  195. // skip delimiter
  196. if aNotFirst and (i<=length(AValue)) and (AValue[i]=FDelimiter) then inc(i);
  197. // skip spaces
  198. while (i<=length(AValue)) and (Ord(AValue[i])<=Ord(' ')) do inc(i);
  199. // read next string
  200. if i<=length(AValue) then begin
  201. if AValue[i]=FQuoteChar then begin
  202. // next string is quoted
  203. j:=i+1;
  204. while (j<=length(AValue)) and
  205. ( (AValue[j]<>FQuoteChar) or
  206. ( (j+1<=length(AValue)) and (AValue[j+1]=FQuoteChar) ) ) do begin
  207. if (j<=length(AValue)) and (AValue[j]=FQuoteChar) then inc(j,2)
  208. else inc(j);
  209. end;
  210. // j is position of closing quote
  211. Add( StringReplace (Copy(AValue,i+1,j-i-1),
  212. FQuoteChar+FQuoteChar,FQuoteChar, [rfReplaceAll]));
  213. i:=j+1;
  214. end else begin
  215. // next string is not quoted
  216. j:=i;
  217. while (j<=length(AValue)) and
  218. (Ord(AValue[j])>Ord(' ')) and
  219. (AValue[j]<>FDelimiter) do inc(j);
  220. Add( Copy(AValue,i,j-i));
  221. i:=j;
  222. end;
  223. end else begin
  224. if aNotFirst then Add('');
  225. end;
  226. // skip spaces
  227. while (i<=length(AValue)) and (Ord(AValue[i])<=Ord(' ')) do inc(i);
  228. aNotFirst:=true;
  229. end;
  230. finally
  231. EndUpdate;
  232. end;
  233. end;
  234. Procedure TStrings.SetCommaText(const Value: string);
  235. Var
  236. C1,C2 : Char;
  237. begin
  238. CheckSpecialChars;
  239. C1:=Delimiter;
  240. C2:=QuoteChar;
  241. Delimiter:=',';
  242. QuoteChar:='"';
  243. Try
  244. SetDelimitedText(Value);
  245. Finally
  246. Delimiter:=C1;
  247. QuoteChar:=C2;
  248. end;
  249. end;
  250. Procedure TStrings.SetStringsAdapter(const Value: IStringsAdapter);
  251. begin
  252. end;
  253. Procedure TStrings.SetValue(const Name, Value: string);
  254. Var L : longint;
  255. begin
  256. CheckSpecialChars;
  257. L:=IndexOfName(Name);
  258. if L=-1 then
  259. Add (Name+FNameValueSeparator+Value)
  260. else
  261. Strings[L]:=Name+FNameValueSeparator+value;
  262. end;
  263. procedure TStrings.WriteData(Writer: TWriter);
  264. var
  265. i: Integer;
  266. begin
  267. Writer.WriteListBegin;
  268. for i := 0 to Count - 1 do
  269. Writer.WriteString(Strings[i]);
  270. Writer.WriteListEnd;
  271. end;
  272. procedure TStrings.DefineProperties(Filer: TFiler);
  273. var
  274. HasData: Boolean;
  275. begin
  276. if Assigned(Filer.Ancestor) then
  277. // Only serialize if string list is different from ancestor
  278. if Filer.Ancestor.InheritsFrom(TStrings) then
  279. HasData := not Equals(TStrings(Filer.Ancestor))
  280. else
  281. HasData := True
  282. else
  283. HasData := Count > 0;
  284. Filer.DefineProperty('Strings', @ReadData, @WriteData, HasData);
  285. end;
  286. Procedure TStrings.Error(const Msg: string; Data: Integer);
  287. begin
  288. Raise EStringListError.CreateFmt(Msg,[Data]) at get_caller_addr(get_frame);
  289. end;
  290. Procedure TStrings.Error(const Msg: pstring; Data: Integer);
  291. begin
  292. Raise EStringListError.CreateFmt(Msg^,[Data]) at get_caller_addr(get_frame);
  293. end;
  294. Function TStrings.GetCapacity: Integer;
  295. begin
  296. Result:=Count;
  297. end;
  298. Function TStrings.GetObject(Index: Integer): TObject;
  299. begin
  300. Result:=Nil;
  301. end;
  302. Function TStrings.GetTextStr: string;
  303. Var P : Pchar;
  304. I,L,NLS : Longint;
  305. S,NL : String;
  306. begin
  307. CheckSpecialChars;
  308. // Determine needed place
  309. Case FLBS of
  310. tlbsLF : NL:=#10;
  311. tlbsCRLF : NL:=#13#10;
  312. tlbsCR : NL:=#13;
  313. end;
  314. L:=0;
  315. NLS:=Length(NL);
  316. For I:=0 to count-1 do
  317. L:=L+Length(Strings[I])+NLS;
  318. Setlength(Result,L);
  319. P:=Pointer(Result);
  320. For i:=0 To count-1 do
  321. begin
  322. S:=Strings[I];
  323. L:=Length(S);
  324. if L<>0 then
  325. System.Move(Pointer(S)^,P^,L);
  326. P:=P+L;
  327. For L:=1 to NLS do
  328. begin
  329. P^:=NL[L];
  330. inc(P);
  331. end;
  332. end;
  333. end;
  334. Procedure TStrings.Put(Index: Integer; const S: string);
  335. Var Obj : TObject;
  336. begin
  337. Obj:=Objects[Index];
  338. Delete(Index);
  339. InsertObject(Index,S,Obj);
  340. end;
  341. Procedure TStrings.PutObject(Index: Integer; AObject: TObject);
  342. begin
  343. // Empty.
  344. end;
  345. Procedure TStrings.SetCapacity(NewCapacity: Integer);
  346. begin
  347. // Empty.
  348. end;
  349. Function GetNextLine (Const Value : String; Var S : String; Var P : Integer) : Boolean;
  350. Var
  351. PS : PChar;
  352. IP,L : Integer;
  353. begin
  354. L:=Length(Value);
  355. S:='';
  356. Result:=False;
  357. If ((L-P)<0) then
  358. exit;
  359. if ((L-P)=0) and (not (value[P] in [#10,#13])) Then
  360. Begin
  361. s:=value[P];
  362. inc(P);
  363. Exit(True);
  364. End;
  365. PS:=PChar(Value)+P-1;
  366. IP:=P;
  367. While ((L-P)>=0) and (not (PS^ in [#10,#13])) do
  368. begin
  369. P:=P+1;
  370. Inc(PS);
  371. end;
  372. SetLength (S,P-IP);
  373. System.Move (Value[IP],Pointer(S)^,P-IP);
  374. If (P<=L) and (Value[P]=#13) then
  375. Inc(P);
  376. If (P<=L) and (Value[P]=#10) then
  377. Inc(P); // Point to character after #10(#13)
  378. Result:=True;
  379. end;
  380. Procedure TStrings.SetTextStr(const Value: string);
  381. Var
  382. S : String;
  383. P : Integer;
  384. begin
  385. Try
  386. beginUpdate;
  387. Clear;
  388. P:=1;
  389. While GetNextLine (Value,S,P) do
  390. Add(S);
  391. finally
  392. EndUpdate;
  393. end;
  394. end;
  395. Procedure TStrings.SetUpdateState(Updating: Boolean);
  396. begin
  397. end;
  398. destructor TSTrings.Destroy;
  399. begin
  400. inherited destroy;
  401. end;
  402. Function TStrings.Add(const S: string): Integer;
  403. begin
  404. Result:=Count;
  405. Insert (Count,S);
  406. end;
  407. Function TStrings.AddObject(const S: string; AObject: TObject): Integer;
  408. begin
  409. Result:=Add(S);
  410. Objects[result]:=AObject;
  411. end;
  412. Procedure TStrings.Append(const S: string);
  413. begin
  414. Add (S);
  415. end;
  416. Procedure TStrings.AddStrings(TheStrings: TStrings);
  417. Var Runner : longint;
  418. begin
  419. try
  420. beginupdate;
  421. For Runner:=0 to TheStrings.Count-1 do
  422. self.AddObject (Thestrings[Runner],TheStrings.Objects[Runner]);
  423. finally
  424. EndUpdate;
  425. end;
  426. end;
  427. Procedure TStrings.Assign(Source: TPersistent);
  428. Var
  429. S : TStrings;
  430. begin
  431. If Source is TStrings then
  432. begin
  433. S:=TStrings(Source);
  434. BeginUpdate;
  435. Try
  436. clear;
  437. FSpecialCharsInited:=S.FSpecialCharsInited;
  438. FQuoteChar:=S.FQuoteChar;
  439. FDelimiter:=S.FDelimiter;
  440. FNameValueSeparator:=S.FNameValueSeparator;
  441. FLBS:=S.FLBS;
  442. AddStrings(S);
  443. finally
  444. EndUpdate;
  445. end;
  446. end
  447. else
  448. Inherited Assign(Source);
  449. end;
  450. Procedure TStrings.BeginUpdate;
  451. begin
  452. if FUpdateCount = 0 then SetUpdateState(true);
  453. inc(FUpdateCount);
  454. end;
  455. Procedure TStrings.EndUpdate;
  456. begin
  457. If FUpdateCount>0 then
  458. Dec(FUpdateCount);
  459. if FUpdateCount=0 then
  460. SetUpdateState(False);
  461. end;
  462. Function TStrings.Equals(TheStrings: TStrings): Boolean;
  463. Var Runner,Nr : Longint;
  464. begin
  465. Result:=False;
  466. Nr:=Self.Count;
  467. if Nr<>TheStrings.Count then exit;
  468. For Runner:=0 to Nr-1 do
  469. If Strings[Runner]<>TheStrings[Runner] then exit;
  470. Result:=True;
  471. end;
  472. Procedure TStrings.Exchange(Index1, Index2: Integer);
  473. Var
  474. Obj : TObject;
  475. Str : String;
  476. begin
  477. Try
  478. beginUpdate;
  479. Obj:=Objects[Index1];
  480. Str:=Strings[Index1];
  481. Objects[Index1]:=Objects[Index2];
  482. Strings[Index1]:=Strings[Index2];
  483. Objects[Index2]:=Obj;
  484. Strings[Index2]:=Str;
  485. finally
  486. EndUpdate;
  487. end;
  488. end;
  489. Function TStrings.GetText: PChar;
  490. begin
  491. Result:=StrNew(Pchar(Self.Text));
  492. end;
  493. Function TStrings.DoCompareText(const s1,s2 : string) : PtrInt;
  494. begin
  495. result:=CompareText(s1,s2);
  496. end;
  497. Function TStrings.IndexOf(const S: string): Integer;
  498. begin
  499. Result:=0;
  500. While (Result<Count) and (DoCompareText(Strings[Result],S)<>0) do Result:=Result+1;
  501. if Result=Count then Result:=-1;
  502. end;
  503. Function TStrings.IndexOfName(const Name: string): Integer;
  504. Var
  505. len : longint;
  506. S : String;
  507. begin
  508. CheckSpecialChars;
  509. Result:=0;
  510. while (Result<Count) do
  511. begin
  512. S:=Strings[Result];
  513. len:=pos(FNameValueSeparator,S)-1;
  514. if (len>0) and (DoCompareText(Name,Copy(S,1,Len))=0) then
  515. exit;
  516. inc(result);
  517. end;
  518. result:=-1;
  519. end;
  520. Function TStrings.IndexOfObject(AObject: TObject): Integer;
  521. begin
  522. Result:=0;
  523. While (Result<count) and (Objects[Result]<>AObject) do Result:=Result+1;
  524. If Result=Count then Result:=-1;
  525. end;
  526. Procedure TStrings.InsertObject(Index: Integer; const S: string;
  527. AObject: TObject);
  528. begin
  529. Insert (Index,S);
  530. Objects[Index]:=AObject;
  531. end;
  532. Procedure TStrings.LoadFromFile(const FileName: string);
  533. Var
  534. TheStream : TFileStream;
  535. begin
  536. TheStream:=TFileStream.Create(FileName,fmOpenRead);
  537. LoadFromStream(TheStream);
  538. TheStream.Free;
  539. end;
  540. Procedure TStrings.LoadFromStream(Stream: TStream);
  541. {
  542. Borlands method is no good, since a pipe for
  543. instance doesn't have a size.
  544. So we must do it the hard way.
  545. }
  546. Const
  547. BufSize = 1024;
  548. MaxGrow = 1 shl 29;
  549. Var
  550. Buffer : AnsiString;
  551. BytesRead,
  552. BufLen,
  553. I,BufDelta : Longint;
  554. begin
  555. // reread into a buffer
  556. try
  557. beginupdate;
  558. Buffer:='';
  559. BufLen:=0;
  560. I:=1;
  561. Repeat
  562. BufDelta:=BufSize*I;
  563. SetLength(Buffer,BufLen+BufDelta);
  564. BytesRead:=Stream.Read(Buffer[BufLen+1],BufDelta);
  565. inc(BufLen,BufDelta);
  566. If I<MaxGrow then
  567. I:=I shl 1;
  568. Until BytesRead<>BufDelta;
  569. SetLength(Buffer, BufLen-BufDelta+BytesRead);
  570. SetTextStr(Buffer);
  571. SetLength(Buffer,0);
  572. finally
  573. EndUpdate;
  574. end;
  575. end;
  576. Procedure TStrings.Move(CurIndex, NewIndex: Integer);
  577. Var
  578. Obj : TObject;
  579. Str : String;
  580. begin
  581. BeginUpdate;
  582. Obj:=Objects[CurIndex];
  583. Str:=Strings[CurIndex];
  584. Delete(Curindex);
  585. InsertObject(NewIndex,Str,Obj);
  586. EndUpdate;
  587. end;
  588. Procedure TStrings.SaveToFile(const FileName: string);
  589. Var TheStream : TFileStream;
  590. begin
  591. TheStream:=TFileStream.Create(FileName,fmCreate);
  592. SaveToStream(TheStream);
  593. TheStream.Free;
  594. end;
  595. Procedure TStrings.SaveToStream(Stream: TStream);
  596. Var
  597. S : String;
  598. begin
  599. S:=Text;
  600. Stream.WriteBuffer(Pointer(S)^,Length(S));
  601. end;
  602. Procedure TStrings.SetText(TheText: PChar);
  603. Var S : String;
  604. begin
  605. If TheText<>Nil then
  606. S:=StrPas(TheText)
  607. else
  608. S:='';
  609. SetTextStr(S);
  610. end;
  611. {****************************************************************************}
  612. {* TStringList *}
  613. {****************************************************************************}
  614. {$if defined(VER2_0) or not defined(FPC_TESTGENERICS)}
  615. Procedure TStringList.ExchangeItems(Index1, Index2: Integer);
  616. Var P1,P2 : Pointer;
  617. begin
  618. P1:=Pointer(Flist^[Index1].FString);
  619. P2:=Pointer(Flist^[Index1].FObject);
  620. Pointer(Flist^[Index1].Fstring):=Pointer(Flist^[Index2].Fstring);
  621. Pointer(Flist^[Index1].FObject):=Pointer(Flist^[Index2].FObject);
  622. Pointer(Flist^[Index2].Fstring):=P1;
  623. Pointer(Flist^[Index2].FObject):=P2;
  624. end;
  625. Procedure TStringList.Grow;
  626. Var
  627. NC : Integer;
  628. begin
  629. NC:=FCapacity;
  630. If NC>=256 then
  631. NC:=NC+(NC Div 4)
  632. else if NC=0 then
  633. NC:=4
  634. else
  635. NC:=NC*4;
  636. SetCapacity(NC);
  637. end;
  638. Procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  639. var
  640. Pivot, vL, vR: Integer;
  641. begin
  642. if R - L <= 1 then begin // a little bit of time saver
  643. if L < R then
  644. if CompareFn(Self, L, R) > 0 then
  645. ExchangeItems(L, R);
  646. Exit;
  647. end;
  648. vL := L;
  649. vR := R;
  650. Pivot := L + Random(R - L); // they say random is best
  651. while vL < vR do begin
  652. while (vL < Pivot) and (CompareFn(Self, vL, Pivot) <= 0) do
  653. Inc(vL);
  654. while (vR > Pivot) and (CompareFn(Self, vR, Pivot) > 0) do
  655. Dec(vR);
  656. ExchangeItems(vL, vR);
  657. if Pivot = vL then // swap pivot if we just hit it from one side
  658. Pivot := vR
  659. else if Pivot = vR then
  660. Pivot := vL;
  661. end;
  662. if Pivot - 1 >= L then
  663. QuickSort(L, Pivot - 1, CompareFn);
  664. if Pivot + 1 <= R then
  665. QuickSort(Pivot + 1, R, CompareFn);
  666. end;
  667. Procedure TStringList.InsertItem(Index: Integer; const S: string);
  668. begin
  669. Changing;
  670. If FCount=Fcapacity then Grow;
  671. If Index<FCount then
  672. System.Move (FList^[Index],FList^[Index+1],
  673. (FCount-Index)*SizeOf(TStringItem));
  674. Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
  675. Flist^[Index].FString:=S;
  676. Flist^[Index].Fobject:=Nil;
  677. Inc(FCount);
  678. Changed;
  679. end;
  680. Procedure TStringList.InsertItem(Index: Integer; const S: string; O: TObject);
  681. begin
  682. Changing;
  683. If FCount=Fcapacity then Grow;
  684. If Index<FCount then
  685. System.Move (FList^[Index],FList^[Index+1],
  686. (FCount-Index)*SizeOf(TStringItem));
  687. Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
  688. Flist^[Index].FString:=S;
  689. Flist^[Index].FObject:=O;
  690. Inc(FCount);
  691. Changed;
  692. end;
  693. Procedure TStringList.SetSorted(Value: Boolean);
  694. begin
  695. If FSorted<>Value then
  696. begin
  697. If Value then sort;
  698. FSorted:=VAlue
  699. end;
  700. end;
  701. Procedure TStringList.Changed;
  702. begin
  703. If (FUpdateCount=0) Then
  704. If Assigned(FOnChange) then
  705. FOnchange(Self);
  706. end;
  707. Procedure TStringList.Changing;
  708. begin
  709. If FUpdateCount=0 then
  710. if Assigned(FOnChanging) then
  711. FOnchanging(Self);
  712. end;
  713. Function TStringList.Get(Index: Integer): string;
  714. begin
  715. If (Index<0) or (INdex>=Fcount) then
  716. Error (SListIndexError,Index);
  717. Result:=Flist^[Index].FString;
  718. end;
  719. Function TStringList.GetCapacity: Integer;
  720. begin
  721. Result:=FCapacity;
  722. end;
  723. Function TStringList.GetCount: Integer;
  724. begin
  725. Result:=FCount;
  726. end;
  727. Function TStringList.GetObject(Index: Integer): TObject;
  728. begin
  729. If (Index<0) or (INdex>=Fcount) then
  730. Error (SListIndexError,Index);
  731. Result:=Flist^[Index].FObject;
  732. end;
  733. Procedure TStringList.Put(Index: Integer; const S: string);
  734. begin
  735. If Sorted then
  736. Error(SSortedListError,0);
  737. If (Index<0) or (INdex>=Fcount) then
  738. Error (SListIndexError,Index);
  739. Changing;
  740. Flist^[Index].FString:=S;
  741. Changed;
  742. end;
  743. Procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  744. begin
  745. If (Index<0) or (INdex>=Fcount) then
  746. Error (SListIndexError,Index);
  747. Changing;
  748. Flist^[Index].FObject:=AObject;
  749. Changed;
  750. end;
  751. Procedure TStringList.SetCapacity(NewCapacity: Integer);
  752. Var NewList : Pointer;
  753. MSize : Longint;
  754. begin
  755. If (NewCapacity<0) then
  756. Error (SListCapacityError,NewCapacity);
  757. If NewCapacity>FCapacity then
  758. begin
  759. GetMem (NewList,NewCapacity*SizeOf(TStringItem));
  760. If NewList=Nil then
  761. Error (SListCapacityError,NewCapacity);
  762. If Assigned(FList) then
  763. begin
  764. MSize:=FCapacity*Sizeof(TStringItem);
  765. System.Move (FList^,NewList^,MSize);
  766. FillWord (Pchar(NewList)[MSize],(NewCapacity-FCapacity)*WordRatio, 0);
  767. FreeMem (Flist,MSize);
  768. end;
  769. Flist:=NewList;
  770. FCapacity:=NewCapacity;
  771. end
  772. else if NewCapacity<FCapacity then
  773. begin
  774. if NewCapacity = 0 then
  775. begin
  776. FreeMem(FList);
  777. FList := nil;
  778. end else
  779. begin
  780. GetMem(NewList, NewCapacity * SizeOf(TStringItem));
  781. System.Move(FList^, NewList^, NewCapacity * SizeOf(TStringItem));
  782. FreeMem(FList);
  783. FList := NewList;
  784. end;
  785. FCapacity:=NewCapacity;
  786. end;
  787. end;
  788. Procedure TStringList.SetUpdateState(Updating: Boolean);
  789. begin
  790. If Updating then
  791. Changing
  792. else
  793. Changed
  794. end;
  795. destructor TStringList.Destroy;
  796. Var I : Longint;
  797. begin
  798. FOnChange:=Nil;
  799. FOnChanging:=Nil;
  800. // This will force a dereference. Can be done better...
  801. For I:=0 to FCount-1 do
  802. FList^[I].FString:='';
  803. FCount:=0;
  804. SetCapacity(0);
  805. Inherited destroy;
  806. end;
  807. Function TStringList.Add(const S: string): Integer;
  808. begin
  809. If Not Sorted then
  810. Result:=FCount
  811. else
  812. If Find (S,Result) then
  813. Case DUplicates of
  814. DupIgnore : Exit;
  815. DupError : Error(SDuplicateString,0)
  816. end;
  817. InsertItem (Result,S);
  818. end;
  819. Procedure TStringList.Clear;
  820. Var I : longint;
  821. begin
  822. if FCount = 0 then Exit;
  823. Changing;
  824. For I:=0 to FCount-1 do
  825. Flist^[I].FString:='';
  826. FCount:=0;
  827. SetCapacity(0);
  828. Changed;
  829. end;
  830. Procedure TStringList.Delete(Index: Integer);
  831. begin
  832. If (Index<0) or (Index>=FCount) then
  833. Error(SlistINdexError,Index);
  834. Changing;
  835. Flist^[Index].FString:='';
  836. Dec(FCount);
  837. If Index<FCount then
  838. System.Move(Flist^[Index+1],
  839. Flist^[Index],
  840. (Fcount-Index)*SizeOf(TStringItem));
  841. Changed;
  842. end;
  843. Procedure TStringList.Exchange(Index1, Index2: Integer);
  844. begin
  845. If (Index1<0) or (Index1>=FCount) then
  846. Error(SListIndexError,Index1);
  847. If (Index2<0) or (Index2>=FCount) then
  848. Error(SListIndexError,Index2);
  849. Changing;
  850. ExchangeItems(Index1,Index2);
  851. changed;
  852. end;
  853. procedure TStringList.SetCaseSensitive(b : boolean);
  854. begin
  855. if b<>FCaseSensitive then
  856. begin
  857. FCaseSensitive:=b;
  858. if FSorted then
  859. sort;
  860. end;
  861. end;
  862. Function TStringList.DoCompareText(const s1,s2 : string) : PtrInt;
  863. begin
  864. if FCaseSensitive then
  865. result:=AnsiCompareStr(s1,s2)
  866. else
  867. result:=AnsiCompareText(s1,s2);
  868. end;
  869. Function TStringList.Find(const S: string; var Index: Integer): Boolean;
  870. var
  871. L, R, I: Integer;
  872. CompareRes: PtrInt;
  873. begin
  874. Result := false;
  875. // Use binary search.
  876. L := 0;
  877. R := Count - 1;
  878. while (L<=R) do
  879. begin
  880. I := L + (R - L) div 2;
  881. CompareRes := DoCompareText(S, Flist^[I].FString);
  882. if (CompareRes>0) then
  883. L := I+1
  884. else begin
  885. R := I-1;
  886. if (CompareRes=0) then begin
  887. Result := true;
  888. if (Duplicates<>dupAccept) then
  889. L := I; // forces end of while loop
  890. end;
  891. end;
  892. end;
  893. Index := L;
  894. end;
  895. Function TStringList.IndexOf(const S: string): Integer;
  896. begin
  897. If Not Sorted then
  898. Result:=Inherited indexOf(S)
  899. else
  900. // faster using binary search...
  901. If Not Find (S,Result) then
  902. Result:=-1;
  903. end;
  904. Procedure TStringList.Insert(Index: Integer; const S: string);
  905. begin
  906. If Sorted then
  907. Error (SSortedListError,0)
  908. else
  909. If (Index<0) or (Index>FCount) then
  910. Error (SListIndexError,Index)
  911. else
  912. InsertItem (Index,S);
  913. end;
  914. Procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  915. begin
  916. If Not Sorted and (FCount>1) then
  917. begin
  918. Changing;
  919. QuickSort(0,FCount-1, CompareFn);
  920. Changed;
  921. end;
  922. end;
  923. function StringListAnsiCompare(List: TStringList; Index1, Index: Integer): Integer;
  924. begin
  925. Result := List.DoCompareText(List.FList^[Index1].FString,
  926. List.FList^[Index].FString);
  927. end;
  928. Procedure TStringList.Sort;
  929. begin
  930. CustomSort(@StringListAnsiCompare);
  931. end;
  932. {$else}
  933. { generics based implementation of TStringList follows }
  934. function StringListAnsiCompare(List: TStringList; Index1, Index2: Integer): Integer;
  935. begin
  936. Result := List.DoCompareText(List.Strings[Index1], List.Strings[Index2]);
  937. end;
  938. constructor TStringList.Create;
  939. begin
  940. inherited;
  941. FMap := TFPStrObjMap.Create;
  942. FMap.OnPtrCompare := @MapPtrCompare;
  943. FOnCompareText := @DefaultCompareText;
  944. end;
  945. destructor TStringList.Destroy;
  946. begin
  947. FMap.Free;
  948. inherited;
  949. end;
  950. function TStringList.GetDuplicates: TDuplicates;
  951. begin
  952. Result := FMap.Duplicates;
  953. end;
  954. function TStringList.GetSorted: boolean;
  955. begin
  956. Result := FMap.Sorted;
  957. end;
  958. procedure TStringList.SetDuplicates(NewDuplicates: TDuplicates);
  959. begin
  960. FMap.Duplicates := NewDuplicates;
  961. end;
  962. procedure TStringList.SetSorted(NewSorted: Boolean);
  963. begin
  964. FMap.Sorted := NewSorted;
  965. end;
  966. procedure TStringList.Changed;
  967. begin
  968. if FUpdateCount = 0 then
  969. if Assigned(FOnChange) then
  970. FOnChange(Self);
  971. end;
  972. procedure TStringList.Changing;
  973. begin
  974. if FUpdateCount = 0 then
  975. if Assigned(FOnChanging) then
  976. FOnChanging(Self);
  977. end;
  978. function TStringList.Get(Index: Integer): string;
  979. begin
  980. Result := FMap.Keys[Index];
  981. end;
  982. function TStringList.GetCapacity: Integer;
  983. begin
  984. Result := FMap.Capacity;
  985. end;
  986. function TStringList.GetCount: Integer;
  987. begin
  988. Result := FMap.Count;
  989. end;
  990. function TStringList.GetObject(Index: Integer): TObject;
  991. begin
  992. Result := FMap.Data[Index];
  993. end;
  994. procedure TStringList.Put(Index: Integer; const S: string);
  995. begin
  996. Changing;
  997. FMap.Keys[Index] := S;
  998. Changed;
  999. end;
  1000. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1001. begin
  1002. Changing;
  1003. FMap.Data[Index] := AObject;
  1004. Changed;
  1005. end;
  1006. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1007. begin
  1008. FMap.Capacity := NewCapacity;
  1009. end;
  1010. procedure TStringList.SetUpdateState(Updating: Boolean);
  1011. begin
  1012. if Updating then
  1013. Changing
  1014. else
  1015. Changed
  1016. end;
  1017. function TStringList.Add(const S: string): Integer;
  1018. begin
  1019. Result := FMap.Add(S);
  1020. end;
  1021. procedure TStringList.Clear;
  1022. begin
  1023. if FMap.Count = 0 then exit;
  1024. Changing;
  1025. FMap.Clear;
  1026. Changed;
  1027. end;
  1028. procedure TStringList.Delete(Index: Integer);
  1029. begin
  1030. if (Index < 0) or (Index >= FMap.Count) then
  1031. Error(SListIndexError, Index);
  1032. Changing;
  1033. FMap.Delete(Index);
  1034. Changed;
  1035. end;
  1036. procedure TStringList.Exchange(Index1, Index2: Integer);
  1037. begin
  1038. if (Index1 < 0) or (Index1 >= FMap.Count) then
  1039. Error(SListIndexError, Index1);
  1040. if (Index2 < 0) or (Index2 >= FMap.Count) then
  1041. Error(SListIndexError, Index2);
  1042. Changing;
  1043. FMap.InternalExchange(Index1, Index2);
  1044. Changed;
  1045. end;
  1046. procedure TStringList.SetCaseSensitive(NewSensitive: Boolean);
  1047. begin
  1048. if NewSensitive <> FCaseSensitive then
  1049. begin
  1050. FCaseSensitive := NewSensitive;
  1051. if Sorted then
  1052. Sort;
  1053. end;
  1054. end;
  1055. function TStringList.MapPtrCompare(Key1, Key2: Pointer): Integer;
  1056. begin
  1057. Result := FOnCompareText(string(Key1^), string(Key2^));
  1058. end;
  1059. function TStringList.DefaultCompareText(const s1, s2: string): PtrInt;
  1060. begin
  1061. if FCaseSensitive then
  1062. Result := AnsiCompareStr(s1, s2)
  1063. else
  1064. Result := AnsiCompareText(s1, s2);
  1065. end;
  1066. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1067. begin
  1068. Result := FOnCompareText(s1, s2);
  1069. end;
  1070. function TStringList.Find(const S: string; var Index: Integer): Boolean;
  1071. begin
  1072. Result := FMap.Find(S, Index);
  1073. end;
  1074. function TStringList.IndexOf(const S: string): Integer;
  1075. begin
  1076. Result := FMap.IndexOf(S);
  1077. end;
  1078. procedure TStringList.Insert(Index: Integer; const S: string);
  1079. begin
  1080. if not Sorted and (0 <= Index) and (Index < FMap.Count) then
  1081. Changing;
  1082. FMap.InsertKey(Index, S);
  1083. Changed;
  1084. end;
  1085. procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  1086. var
  1087. I, J, Pivot: Integer;
  1088. begin
  1089. repeat
  1090. I := L;
  1091. J := R;
  1092. Pivot := (L + R) div 2;
  1093. repeat
  1094. while CompareFn(Self, I, Pivot) < 0 do Inc(I);
  1095. while CompareFn(Self, J, Pivot) > 0 do Dec(J);
  1096. if I <= J then
  1097. begin
  1098. FMap.InternalExchange(I, J); // No check, indices are correct.
  1099. if Pivot = I then
  1100. Pivot := J
  1101. else if Pivot = J then
  1102. Pivot := I;
  1103. Inc(I);
  1104. Dec(j);
  1105. end;
  1106. until I > J;
  1107. if L < J then
  1108. QuickSort(L,J, CompareFn);
  1109. L := I;
  1110. until I >= R;
  1111. end;
  1112. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1113. begin
  1114. if not Sorted and (FMap.Count > 1) then
  1115. begin
  1116. Changing;
  1117. QuickSort(0, FMap.Count-1, CompareFn);
  1118. Changed;
  1119. end;
  1120. end;
  1121. procedure TStringList.Sort;
  1122. begin
  1123. if not Sorted and (FMap.Count > 1) then
  1124. begin
  1125. Changing;
  1126. FMap.Sort;
  1127. Changed;
  1128. end;
  1129. end;
  1130. {$endif}