stringl.inc 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898
  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. {* TStringsEnumerator *}
  12. {****************************************************************************}
  13. constructor TStringsEnumerator.Create(AStrings: TStrings);
  14. begin
  15. inherited Create;
  16. FStrings := AStrings;
  17. FPosition := -1;
  18. end;
  19. function TStringsEnumerator.GetCurrent: String;
  20. begin
  21. Result := FStrings[FPosition];
  22. end;
  23. function TStringsEnumerator.MoveNext: Boolean;
  24. begin
  25. Inc(FPosition);
  26. Result := FPosition < FStrings.Count;
  27. end;
  28. {****************************************************************************}
  29. {* TStrings *}
  30. {****************************************************************************}
  31. // Function to quote text. Should move maybe to sysutils !!
  32. // Also, it is not clear at this point what exactly should be done.
  33. { //!! is used to mark unsupported things. }
  34. Function QuoteString (Const S : String; Const Quote : String) : String;
  35. Var
  36. I,J : Integer;
  37. begin
  38. J:=0;
  39. Result:=S;
  40. for i:=1 to length(s) do
  41. begin
  42. inc(j);
  43. if S[i]=Quote then
  44. begin
  45. System.Insert(Quote,Result,J);
  46. inc(j);
  47. end;
  48. end;
  49. Result:=Quote+Result+Quote;
  50. end;
  51. {
  52. For compatibility we can't add a Constructor to TSTrings to initialize
  53. the special characters. Therefore we add a routine which is called whenever
  54. the special chars are needed.
  55. }
  56. Procedure Tstrings.CheckSpecialChars;
  57. begin
  58. If Not FSpecialCharsInited then
  59. begin
  60. FQuoteChar:='"';
  61. FDelimiter:=',';
  62. FNameValueSeparator:='=';
  63. FLBS:=DefaultTextLineBreakStyle;
  64. FSpecialCharsInited:=true;
  65. FLineBreak:=sLineBreak;
  66. end;
  67. end;
  68. Function TStrings.GetSkipLastLineBreak : Boolean;
  69. begin
  70. CheckSpecialChars;
  71. Result:=FSkipLastLineBreak;
  72. end;
  73. procedure TStrings.SetSkipLastLineBreak(const AValue : Boolean);
  74. begin
  75. CheckSpecialChars;
  76. FSkipLastLineBreak:=AValue;
  77. end;
  78. Function TStrings.GetLBS : TTextLineBreakStyle;
  79. begin
  80. CheckSpecialChars;
  81. Result:=FLBS;
  82. end;
  83. Procedure TStrings.SetLBS (AValue : TTextLineBreakStyle);
  84. begin
  85. CheckSpecialChars;
  86. FLBS:=AValue;
  87. end;
  88. procedure TStrings.SetDelimiter(c:Char);
  89. begin
  90. CheckSpecialChars;
  91. FDelimiter:=c;
  92. end;
  93. Procedure TStrings.SetEncoding(const AEncoding: TEncoding);
  94. begin
  95. if (FEncoding<>nil) and not TEncoding.IsStandardEncoding(FEncoding) then
  96. FEncoding.Free;
  97. if TEncoding.IsStandardEncoding(AEncoding) then
  98. FEncoding:=AEncoding
  99. else if AEncoding<>nil then
  100. FEncoding:=AEncoding.Clone
  101. else
  102. FEncoding:=nil;
  103. end;
  104. Function TStrings.GetDelimiter : Char;
  105. begin
  106. CheckSpecialChars;
  107. Result:=FDelimiter;
  108. end;
  109. procedure TStrings.SetLineBreak(Const S : String);
  110. begin
  111. CheckSpecialChars;
  112. FLineBreak:=S;
  113. end;
  114. Function TStrings.GetLineBreak : String;
  115. begin
  116. CheckSpecialChars;
  117. Result:=FLineBreak;
  118. end;
  119. procedure TStrings.SetQuoteChar(c:Char);
  120. begin
  121. CheckSpecialChars;
  122. FQuoteChar:=c;
  123. end;
  124. Function TStrings.GetQuoteChar :Char;
  125. begin
  126. CheckSpecialChars;
  127. Result:=FQuoteChar;
  128. end;
  129. procedure TStrings.SetNameValueSeparator(c:Char);
  130. begin
  131. CheckSpecialChars;
  132. FNameValueSeparator:=c;
  133. end;
  134. Function TStrings.GetNameValueSeparator :Char;
  135. begin
  136. CheckSpecialChars;
  137. Result:=FNameValueSeparator;
  138. end;
  139. function TStrings.GetCommaText: string;
  140. Var
  141. C1,C2 : Char;
  142. FSD : Boolean;
  143. begin
  144. CheckSpecialChars;
  145. FSD:=StrictDelimiter;
  146. C1:=Delimiter;
  147. C2:=QuoteChar;
  148. Delimiter:=',';
  149. QuoteChar:='"';
  150. StrictDelimiter:=False;
  151. Try
  152. Result:=GetDelimitedText;
  153. Finally
  154. Delimiter:=C1;
  155. QuoteChar:=C2;
  156. StrictDelimiter:=FSD;
  157. end;
  158. end;
  159. Function TStrings.GetDelimitedText: string;
  160. Var
  161. I : integer;
  162. p : pchar;
  163. BreakChars : set of char;
  164. S : String;
  165. begin
  166. CheckSpecialChars;
  167. result:='';
  168. if StrictDelimiter then
  169. BreakChars:=[#0,QuoteChar,Delimiter]
  170. else
  171. BreakChars:=[#0..' ',QuoteChar,Delimiter];
  172. // Check for break characters and quote if required.
  173. For i:=0 to count-1 do
  174. begin
  175. S:=Strings[i];
  176. p:=pchar(S);
  177. //Quote strings that include BreakChars:
  178. while not(p^ in BreakChars) do
  179. inc(p);
  180. if (p<>pchar(S)+length(S)) then
  181. Result:=Result+QuoteString(S,QuoteChar)
  182. else
  183. Result:=Result+S;
  184. if I<Count-1 then
  185. Result:=Result+Delimiter;
  186. end;
  187. // Quote empty string:
  188. If (Length(Result)=0) and (Count=1) then
  189. Result:=QuoteChar+QuoteChar;
  190. end;
  191. procedure TStrings.GetNameValue(Index : Integer; Out AName,AValue : String);
  192. Var L : longint;
  193. begin
  194. CheckSpecialChars;
  195. AValue:=Strings[Index];
  196. L:=Pos(FNameValueSeparator,AValue);
  197. If L<>0 then
  198. begin
  199. AName:=Copy(AValue,1,L-1);
  200. System.Delete(AValue,1,L);
  201. end
  202. else
  203. AName:='';
  204. end;
  205. function TStrings.ExtractName(const s:String):String;
  206. var
  207. L: Longint;
  208. begin
  209. CheckSpecialChars;
  210. L:=Pos(FNameValueSeparator,S);
  211. If L<>0 then
  212. Result:=Copy(S,1,L-1)
  213. else
  214. Result:='';
  215. end;
  216. function TStrings.GetName(Index: Integer): string;
  217. Var
  218. V : String;
  219. begin
  220. GetNameValue(Index,Result,V);
  221. end;
  222. Function TStrings.GetValue(const Name: string): string;
  223. Var
  224. L : longint;
  225. N : String;
  226. begin
  227. Result:='';
  228. L:=IndexOfName(Name);
  229. If L<>-1 then
  230. GetNameValue(L,N,Result);
  231. end;
  232. Function TStrings.GetValueFromIndex(Index: Integer): string;
  233. Var
  234. N : String;
  235. begin
  236. GetNameValue(Index,N,Result);
  237. end;
  238. Procedure TStrings.SetValueFromIndex(Index: Integer; const Value: string);
  239. begin
  240. If (Value='') then
  241. Delete(Index)
  242. else
  243. begin
  244. If (Index<0) then
  245. Index:=Add('');
  246. CheckSpecialChars;
  247. Strings[Index]:=GetName(Index)+FNameValueSeparator+Value;
  248. end;
  249. end;
  250. procedure TStrings.ReadData(Reader: TReader);
  251. begin
  252. Reader.ReadListBegin;
  253. BeginUpdate;
  254. try
  255. Clear;
  256. while not Reader.EndOfList do
  257. Add(Reader.ReadString);
  258. finally
  259. EndUpdate;
  260. end;
  261. Reader.ReadListEnd;
  262. end;
  263. Procedure TStrings.SetDelimitedText(const AValue: string);
  264. var i,j:integer;
  265. aNotFirst:boolean;
  266. begin
  267. CheckSpecialChars;
  268. BeginUpdate;
  269. i:=1;
  270. j:=1;
  271. aNotFirst:=false;
  272. { Paraphrased from Delphi XE2 help:
  273. Strings must be separated by Delimiter characters or spaces.
  274. They may be enclosed in QuoteChars.
  275. QuoteChars in the string must be repeated to distinguish them from the QuoteChars enclosing the string.
  276. }
  277. try
  278. Clear;
  279. If StrictDelimiter then
  280. begin
  281. while i<=length(AValue) do begin
  282. // skip delimiter
  283. if aNotFirst and (i<=length(AValue)) and (AValue[i]=FDelimiter) then inc(i);
  284. // read next string
  285. if i<=length(AValue) then begin
  286. if AValue[i]=FQuoteChar then begin
  287. // next string is quoted
  288. j:=i+1;
  289. while (j<=length(AValue)) and
  290. ( (AValue[j]<>FQuoteChar) or
  291. ( (j+1<=length(AValue)) and (AValue[j+1]=FQuoteChar) ) ) do begin
  292. if (j<=length(AValue)) and (AValue[j]=FQuoteChar) then inc(j,2)
  293. else inc(j);
  294. end;
  295. // j is position of closing quote
  296. Add( StringReplace (Copy(AValue,i+1,j-i-1),
  297. FQuoteChar+FQuoteChar,FQuoteChar, [rfReplaceAll]));
  298. i:=j+1;
  299. end else begin
  300. // next string is not quoted; read until delimiter
  301. j:=i;
  302. while (j<=length(AValue)) and
  303. (AValue[j]<>FDelimiter) do inc(j);
  304. Add( Copy(AValue,i,j-i));
  305. i:=j;
  306. end;
  307. end else begin
  308. if aNotFirst then Add('');
  309. end;
  310. aNotFirst:=true;
  311. end;
  312. end
  313. else
  314. begin
  315. while i<=length(AValue) do begin
  316. // skip delimiter
  317. if aNotFirst and (i<=length(AValue)) and (AValue[i]=FDelimiter) then inc(i);
  318. // skip spaces
  319. while (i<=length(AValue)) and (Ord(AValue[i])<=Ord(' ')) do inc(i);
  320. // read next string
  321. if i<=length(AValue) then begin
  322. if AValue[i]=FQuoteChar then begin
  323. // next string is quoted
  324. j:=i+1;
  325. while (j<=length(AValue)) and
  326. ( (AValue[j]<>FQuoteChar) or
  327. ( (j+1<=length(AValue)) and (AValue[j+1]=FQuoteChar) ) ) do begin
  328. if (j<=length(AValue)) and (AValue[j]=FQuoteChar) then inc(j,2)
  329. else inc(j);
  330. end;
  331. // j is position of closing quote
  332. Add( StringReplace (Copy(AValue,i+1,j-i-1),
  333. FQuoteChar+FQuoteChar,FQuoteChar, [rfReplaceAll]));
  334. i:=j+1;
  335. end else begin
  336. // next string is not quoted; read until control character/space/delimiter
  337. j:=i;
  338. while (j<=length(AValue)) and
  339. (Ord(AValue[j])>Ord(' ')) and
  340. (AValue[j]<>FDelimiter) do inc(j);
  341. Add( Copy(AValue,i,j-i));
  342. i:=j;
  343. end;
  344. end else begin
  345. if aNotFirst then Add('');
  346. end;
  347. // skip spaces
  348. while (i<=length(AValue)) and (Ord(AValue[i])<=Ord(' ')) do inc(i);
  349. aNotFirst:=true;
  350. end;
  351. end;
  352. finally
  353. EndUpdate;
  354. end;
  355. end;
  356. Procedure TStrings.SetCommaText(const Value: string);
  357. Var
  358. C1,C2 : Char;
  359. begin
  360. CheckSpecialChars;
  361. C1:=Delimiter;
  362. C2:=QuoteChar;
  363. Delimiter:=',';
  364. QuoteChar:='"';
  365. Try
  366. SetDelimitedText(Value);
  367. Finally
  368. Delimiter:=C1;
  369. QuoteChar:=C2;
  370. end;
  371. end;
  372. Procedure TStrings.SetStringsAdapter(const Value: IStringsAdapter);
  373. begin
  374. end;
  375. Procedure TStrings.SetDefaultEncoding(const ADefaultEncoding: TEncoding);
  376. begin
  377. if (FDefaultEncoding<>nil) and not TEncoding.IsStandardEncoding(FDefaultEncoding) then
  378. FDefaultEncoding.Free;
  379. if TEncoding.IsStandardEncoding(ADefaultEncoding) then
  380. FDefaultEncoding:=ADefaultEncoding
  381. else if ADefaultEncoding<>nil then
  382. FDefaultEncoding:=ADefaultEncoding.Clone
  383. else
  384. FDefaultEncoding:=TEncoding.Default;
  385. end;
  386. Procedure TStrings.SetValue(const Name, Value: string);
  387. Var L : longint;
  388. begin
  389. CheckSpecialChars;
  390. L:=IndexOfName(Name);
  391. if L=-1 then
  392. Add (Name+FNameValueSeparator+Value)
  393. else
  394. Strings[L]:=Name+FNameValueSeparator+value;
  395. end;
  396. procedure TStrings.WriteData(Writer: TWriter);
  397. var
  398. i: Integer;
  399. begin
  400. Writer.WriteListBegin;
  401. for i := 0 to Count - 1 do
  402. Writer.WriteString(Strings[i]);
  403. Writer.WriteListEnd;
  404. end;
  405. procedure TStrings.DefineProperties(Filer: TFiler);
  406. var
  407. HasData: Boolean;
  408. begin
  409. if Assigned(Filer.Ancestor) then
  410. // Only serialize if string list is different from ancestor
  411. if Filer.Ancestor.InheritsFrom(TStrings) then
  412. HasData := not Equals(TStrings(Filer.Ancestor))
  413. else
  414. HasData := True
  415. else
  416. HasData := Count > 0;
  417. Filer.DefineProperty('Strings', @ReadData, @WriteData, HasData);
  418. end;
  419. Procedure TStrings.Error(const Msg: string; Data: Integer);
  420. begin
  421. Raise EStringListError.CreateFmt(Msg,[Data]) at get_caller_addr(get_frame), get_caller_frame(get_frame);
  422. end;
  423. Procedure TStrings.Error(const Msg: pstring; Data: Integer);
  424. begin
  425. Raise EStringListError.CreateFmt(Msg^,[Data]) at get_caller_addr(get_frame), get_caller_frame(get_frame);
  426. end;
  427. Function TStrings.GetCapacity: Integer;
  428. begin
  429. Result:=Count;
  430. end;
  431. Function TStrings.GetObject(Index: Integer): TObject;
  432. begin
  433. Result:=Nil;
  434. end;
  435. Function TStrings.GetTextStr: string;
  436. Var P : Pchar;
  437. I,L,NLS : Longint;
  438. S,NL : String;
  439. begin
  440. CheckSpecialChars;
  441. // Determine needed place
  442. if FLineBreak<>sLineBreak then
  443. NL:=FLineBreak
  444. else
  445. Case FLBS of
  446. tlbsLF : NL:=#10;
  447. tlbsCRLF : NL:=#13#10;
  448. tlbsCR : NL:=#13;
  449. end;
  450. L:=0;
  451. NLS:=Length(NL);
  452. For I:=0 to count-1 do
  453. L:=L+Length(Strings[I])+NLS;
  454. if SkipLastLineBreak then
  455. Dec(L,NLS);
  456. Setlength(Result,L);
  457. P:=Pointer(Result);
  458. For i:=0 To count-1 do
  459. begin
  460. S:=Strings[I];
  461. L:=Length(S);
  462. if L<>0 then
  463. System.Move(Pointer(S)^,P^,L);
  464. P:=P+L;
  465. if (I<Count-1) or Not SkipLastLineBreak then
  466. For L:=1 to NLS do
  467. begin
  468. P^:=NL[L];
  469. inc(P);
  470. end;
  471. end;
  472. end;
  473. Procedure TStrings.Put(Index: Integer; const S: string);
  474. Var Obj : TObject;
  475. begin
  476. Obj:=Objects[Index];
  477. Delete(Index);
  478. InsertObject(Index,S,Obj);
  479. end;
  480. Procedure TStrings.PutObject(Index: Integer; AObject: TObject);
  481. begin
  482. // Empty.
  483. end;
  484. Procedure TStrings.SetCapacity(NewCapacity: Integer);
  485. begin
  486. // Empty.
  487. end;
  488. Class Function TStrings.GetNextLine (Const Value : String; Var S : String; Var P : Integer) : Boolean;
  489. Var
  490. PS : PChar;
  491. IP,L : Integer;
  492. begin
  493. L:=Length(Value);
  494. S:='';
  495. Result:=False;
  496. If ((L-P)<0) then
  497. exit;
  498. if ((L-P)=0) and (not (value[P] in [#10,#13])) Then
  499. Begin
  500. s:=value[P];
  501. inc(P);
  502. Exit(True);
  503. End;
  504. PS:=PChar(Value)+P-1;
  505. IP:=P;
  506. While ((L-P)>=0) and (not (PS^ in [#10,#13])) do
  507. begin
  508. P:=P+1;
  509. Inc(PS);
  510. end;
  511. SetLength (S,P-IP);
  512. System.Move (Value[IP],Pointer(S)^,P-IP);
  513. If (P<=L) and (Value[P]=#13) then
  514. Inc(P);
  515. If (P<=L) and (Value[P]=#10) then
  516. Inc(P); // Point to character after #10(#13)
  517. Result:=True;
  518. end;
  519. Function TStrings.GetNextLineBreak (Const Value : String; Var S : String; Var P : Integer) : Boolean;
  520. Var
  521. PS,PC,PP : PChar;
  522. begin
  523. S:='';
  524. Result:=False;
  525. If ((Length(Value)-P)<0) then
  526. exit;
  527. PS:=@Value[P];
  528. PC:=PS;
  529. PP:=AnsiStrPos(PS,PChar(FLineBreak));
  530. // Stop on #0.
  531. While (PC^<>#0) and (PC<>PP) do
  532. Inc(PC);
  533. P:=P+(PC-PS)+Length(FLineBreak);
  534. SetString(S,PS,PC-PS);
  535. Result:=True;
  536. end;
  537. Procedure TStrings.DoSetTextStr(const Value: string; DoClear : Boolean);
  538. Var
  539. S : String;
  540. P : Integer;
  541. begin
  542. Try
  543. beginUpdate;
  544. if DoClear then
  545. Clear;
  546. P:=1;
  547. if FLineBreak=sLineBreak then
  548. begin
  549. While GetNextLine (Value,S,P) do
  550. Add(S)
  551. end
  552. else
  553. While GetNextLineBreak (Value,S,P) do
  554. Add(S);
  555. finally
  556. EndUpdate;
  557. end;
  558. end;
  559. Procedure TStrings.SetTextStr(const Value: string);
  560. begin
  561. CheckSpecialChars;
  562. DoSetTextStr(Value,True);
  563. end;
  564. Procedure TStrings.AddText(const S: string);
  565. begin
  566. CheckSpecialChars;
  567. DoSetTextStr(S,False);
  568. end;
  569. Procedure TStrings.SetUpdateState(Updating: Boolean);
  570. begin
  571. FPONotifyObservers(Self,ooChange,Nil);
  572. end;
  573. destructor TSTrings.Destroy;
  574. begin
  575. if (FEncoding<>nil) and not TEncoding.IsStandardEncoding(FEncoding) then
  576. FreeAndNil(FEncoding);
  577. if (FDefaultEncoding<>nil) and not TEncoding.IsStandardEncoding(FDefaultEncoding) then
  578. FreeAndNil(FDefaultEncoding);
  579. inherited destroy;
  580. end;
  581. constructor TStrings.Create;
  582. begin
  583. inherited Create;
  584. FDefaultEncoding:=TEncoding.Default;
  585. FEncoding:=nil;
  586. FWriteBOM:=True;
  587. end;
  588. Function TStrings.Add(const S: string): Integer;
  589. begin
  590. Result:=Count;
  591. Insert (Count,S);
  592. end;
  593. function TStrings.Add(const Fmt : string; const Args : Array of const): Integer;
  594. begin
  595. Result:=Add(Format(Fmt,Args));
  596. end;
  597. Function TStrings.AddObject(const S: string; AObject: TObject): Integer;
  598. begin
  599. Result:=Add(S);
  600. Objects[result]:=AObject;
  601. end;
  602. function TStrings.AddObject(const Fmt: string; Args : Array of const; AObject: TObject): Integer;
  603. begin
  604. Result:=AddObject(Format(Fmt,Args),AObject);
  605. end;
  606. Procedure TStrings.Append(const S: string);
  607. begin
  608. Add (S);
  609. end;
  610. Procedure TStrings.AddStrings(TheStrings: TStrings; ClearFirst : Boolean);
  611. begin
  612. beginupdate;
  613. try
  614. if ClearFirst then
  615. Clear;
  616. AddStrings(TheStrings);
  617. finally
  618. EndUpdate;
  619. end;
  620. end;
  621. Procedure TStrings.AddStrings(TheStrings: TStrings);
  622. Var Runner : longint;
  623. begin
  624. For Runner:=0 to TheStrings.Count-1 do
  625. self.AddObject (Thestrings[Runner],TheStrings.Objects[Runner]);
  626. end;
  627. Procedure TStrings.AddStrings(const TheStrings: array of string);
  628. Var Runner : longint;
  629. begin
  630. if Count + High(TheStrings)+1 > Capacity then
  631. Capacity := Count + High(TheStrings)+1;
  632. For Runner:=Low(TheStrings) to High(TheStrings) do
  633. self.Add(Thestrings[Runner]);
  634. end;
  635. Procedure TStrings.AddStrings(const TheStrings: array of string; ClearFirst : Boolean);
  636. begin
  637. beginupdate;
  638. try
  639. if ClearFirst then
  640. Clear;
  641. AddStrings(TheStrings);
  642. finally
  643. EndUpdate;
  644. end;
  645. end;
  646. Procedure TStrings.Assign(Source: TPersistent);
  647. Var
  648. S : TStrings;
  649. begin
  650. If Source is TStrings then
  651. begin
  652. S:=TStrings(Source);
  653. BeginUpdate;
  654. Try
  655. clear;
  656. FSpecialCharsInited:=S.FSpecialCharsInited;
  657. FQuoteChar:=S.FQuoteChar;
  658. FDelimiter:=S.FDelimiter;
  659. FNameValueSeparator:=S.FNameValueSeparator;
  660. FLBS:=S.FLBS;
  661. FLineBreak:=S.FLineBreak;
  662. FWriteBOM:=S.FWriteBOM;
  663. DefaultEncoding:=S.DefaultEncoding;
  664. SetEncoding(S.Encoding);
  665. AddStrings(S);
  666. finally
  667. EndUpdate;
  668. end;
  669. end
  670. else
  671. Inherited Assign(Source);
  672. end;
  673. Procedure TStrings.BeginUpdate;
  674. begin
  675. if FUpdateCount = 0 then SetUpdateState(true);
  676. inc(FUpdateCount);
  677. end;
  678. Procedure TStrings.EndUpdate;
  679. begin
  680. If FUpdateCount>0 then
  681. Dec(FUpdateCount);
  682. if FUpdateCount=0 then
  683. SetUpdateState(False);
  684. end;
  685. Function TStrings.Equals(Obj: TObject): Boolean;
  686. begin
  687. if Obj is TStrings then
  688. Result := Equals(TStrings(Obj))
  689. else
  690. Result := inherited Equals(Obj);
  691. end;
  692. Function TStrings.Equals(TheStrings: TStrings): Boolean;
  693. Var Runner,Nr : Longint;
  694. begin
  695. Result:=False;
  696. Nr:=Self.Count;
  697. if Nr<>TheStrings.Count then exit;
  698. For Runner:=0 to Nr-1 do
  699. If Strings[Runner]<>TheStrings[Runner] then exit;
  700. Result:=True;
  701. end;
  702. Procedure TStrings.Exchange(Index1, Index2: Integer);
  703. Var
  704. Obj : TObject;
  705. Str : String;
  706. begin
  707. beginUpdate;
  708. Try
  709. Obj:=Objects[Index1];
  710. Str:=Strings[Index1];
  711. Objects[Index1]:=Objects[Index2];
  712. Strings[Index1]:=Strings[Index2];
  713. Objects[Index2]:=Obj;
  714. Strings[Index2]:=Str;
  715. finally
  716. EndUpdate;
  717. end;
  718. end;
  719. function TStrings.GetEnumerator: TStringsEnumerator;
  720. begin
  721. Result:=TStringsEnumerator.Create(Self);
  722. end;
  723. Function TStrings.GetText: PChar;
  724. begin
  725. Result:=StrNew(Pchar(Self.Text));
  726. end;
  727. Function TStrings.DoCompareText(const s1,s2 : string) : PtrInt;
  728. begin
  729. result:=CompareText(s1,s2);
  730. end;
  731. Function TStrings.IndexOf(const S: string): Integer;
  732. begin
  733. Result:=0;
  734. While (Result<Count) and (DoCompareText(Strings[Result],S)<>0) do Result:=Result+1;
  735. if Result=Count then Result:=-1;
  736. end;
  737. Function TStrings.IndexOfName(const Name: string): Integer;
  738. Var
  739. len : longint;
  740. S : String;
  741. begin
  742. CheckSpecialChars;
  743. Result:=0;
  744. while (Result<Count) do
  745. begin
  746. S:=Strings[Result];
  747. len:=pos(FNameValueSeparator,S)-1;
  748. if (len>=0) and (DoCompareText(Name,Copy(S,1,Len))=0) then
  749. exit;
  750. inc(result);
  751. end;
  752. result:=-1;
  753. end;
  754. Function TStrings.IndexOfObject(AObject: TObject): Integer;
  755. begin
  756. Result:=0;
  757. While (Result<count) and (Objects[Result]<>AObject) do Result:=Result+1;
  758. If Result=Count then Result:=-1;
  759. end;
  760. Procedure TStrings.InsertObject(Index: Integer; const S: string;
  761. AObject: TObject);
  762. begin
  763. Insert (Index,S);
  764. Objects[Index]:=AObject;
  765. end;
  766. Procedure TStrings.LoadFromFile(const FileName: string);
  767. Var
  768. TheStream : TFileStream;
  769. begin
  770. TheStream:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
  771. try
  772. LoadFromStream(TheStream);
  773. finally
  774. TheStream.Free;
  775. end;
  776. end;
  777. Procedure TStrings.LoadFromFile(const FileName: string; AEncoding: TEncoding);
  778. Var
  779. TheStream : TFileStream;
  780. begin
  781. TheStream:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
  782. try
  783. LoadFromStream(TheStream,AEncoding);
  784. finally
  785. TheStream.Free;
  786. end;
  787. end;
  788. Procedure TStrings.LoadFromStream(Stream: TStream);
  789. {
  790. Borlands method is no good, since a pipe for
  791. instance doesn't have a size.
  792. So we must do it the hard way.
  793. }
  794. Const
  795. BufSize = 1024;
  796. MaxGrow = 1 shl 29;
  797. Var
  798. Buffer : AnsiString;
  799. BytesRead,
  800. BufLen,
  801. I,BufDelta : Longint;
  802. begin
  803. // reread into a buffer
  804. beginupdate;
  805. try
  806. Buffer:='';
  807. BufLen:=0;
  808. I:=1;
  809. Repeat
  810. BufDelta:=BufSize*I;
  811. SetLength(Buffer,BufLen+BufDelta);
  812. BytesRead:=Stream.Read(Buffer[BufLen+1],BufDelta);
  813. inc(BufLen,BufDelta);
  814. If I<MaxGrow then
  815. I:=I shl 1;
  816. Until BytesRead<>BufDelta;
  817. SetLength(Buffer, BufLen-BufDelta+BytesRead);
  818. SetTextStr(Buffer);
  819. SetLength(Buffer,0);
  820. finally
  821. EndUpdate;
  822. end;
  823. end;
  824. Procedure TStrings.LoadFromStream(Stream: TStream; AEncoding: TEncoding);
  825. {
  826. Borlands method is no good, since a pipe for
  827. instance doesn't have a size.
  828. So we must do it the hard way.
  829. }
  830. Const
  831. BufSize = 1024;
  832. MaxGrow = 1 shl 29;
  833. Var
  834. Buffer : TBytes;
  835. T : string;
  836. BytesRead,
  837. BufLen,
  838. I,BufDelta,
  839. PreambleLength : Longint;
  840. begin
  841. // reread into a buffer
  842. beginupdate;
  843. try
  844. SetLength(Buffer,0);
  845. BufLen:=0;
  846. I:=1;
  847. Repeat
  848. BufDelta:=BufSize*I;
  849. SetLength(Buffer,BufLen+BufDelta);
  850. BytesRead:=Stream.Read(Buffer[BufLen],BufDelta);
  851. inc(BufLen,BufDelta);
  852. If I<MaxGrow then
  853. I:=I shl 1;
  854. Until BytesRead<>BufDelta;
  855. SetLength(Buffer,BufLen-BufDelta+BytesRead);
  856. PreambleLength:=TEncoding.GetBufferEncoding(Buffer,AEncoding,FDefaultEncoding);
  857. T:=AEncoding.GetAnsiString(Buffer,PreambleLength,Length(Buffer)-PreambleLength);
  858. SetEncoding(AEncoding);
  859. SetLength(Buffer,0);
  860. SetTextStr(T);
  861. finally
  862. EndUpdate;
  863. end;
  864. end;
  865. Procedure TStrings.Move(CurIndex, NewIndex: Integer);
  866. Var
  867. Obj : TObject;
  868. Str : String;
  869. begin
  870. BeginUpdate;
  871. Try
  872. Obj:=Objects[CurIndex];
  873. Str:=Strings[CurIndex];
  874. Objects[CurIndex]:=Nil; // Prevent Delete from freeing.
  875. Delete(Curindex);
  876. InsertObject(NewIndex,Str,Obj);
  877. finally
  878. EndUpdate;
  879. end;
  880. end;
  881. Procedure TStrings.SaveToFile(const FileName: string);
  882. Var TheStream : TFileStream;
  883. begin
  884. TheStream:=TFileStream.Create(FileName,fmCreate);
  885. try
  886. SaveToStream(TheStream);
  887. finally
  888. TheStream.Free;
  889. end;
  890. end;
  891. Procedure TStrings.SaveToFile(const FileName: string; AEncoding: TEncoding);
  892. Var TheStream : TFileStream;
  893. begin
  894. TheStream:=TFileStream.Create(FileName,fmCreate);
  895. try
  896. SaveToStream(TheStream,AEncoding);
  897. finally
  898. TheStream.Free;
  899. end;
  900. end;
  901. Procedure TStrings.SaveToStream(Stream: TStream);
  902. Var
  903. S : String;
  904. begin
  905. if Encoding<>nil then
  906. SaveToStream(Stream,Encoding)
  907. else
  908. begin
  909. S:=Text;
  910. if S = '' then Exit;
  911. Stream.WriteBuffer(Pointer(S)^,Length(S));
  912. end;
  913. end;
  914. Procedure TStrings.SaveToStream(Stream: TStream; AEncoding: TEncoding);
  915. Var B : TBytes;
  916. begin
  917. if AEncoding=nil then
  918. AEncoding:=FDefaultEncoding;
  919. if FWriteBOM then
  920. begin
  921. B:=AEncoding.GetPreamble;
  922. if Length(B)>0 then
  923. Stream.WriteBuffer(B[0],Length(B));
  924. end;
  925. B:=AEncoding.GetAnsiBytes(Text);
  926. if Length(B)>0 then
  927. Stream.WriteBuffer(B[0],Length(B));
  928. end;
  929. Procedure TStrings.SetText(TheText: PChar);
  930. Var S : String;
  931. begin
  932. If TheText<>Nil then
  933. S:=StrPas(TheText)
  934. else
  935. S:='';
  936. SetTextStr(S);
  937. end;
  938. {****************************************************************************}
  939. {* TStringList *}
  940. {****************************************************************************}
  941. {$if not defined(FPC_TESTGENERICS)}
  942. procedure TStringList.ExchangeItemsInt(Index1, Index2: Integer);
  943. Var P1,P2 : Pointer;
  944. begin
  945. P1:=Pointer(Flist^[Index1].FString);
  946. P2:=Pointer(Flist^[Index1].FObject);
  947. Pointer(Flist^[Index1].Fstring):=Pointer(Flist^[Index2].Fstring);
  948. Pointer(Flist^[Index1].FObject):=Pointer(Flist^[Index2].FObject);
  949. Pointer(Flist^[Index2].Fstring):=P1;
  950. Pointer(Flist^[Index2].FObject):=P2;
  951. end;
  952. function TStringList.GetSorted: Boolean;
  953. begin
  954. Result:=FSortStyle in [sslUser,sslAuto];
  955. end;
  956. procedure TStringList.ExchangeItems(Index1, Index2: Integer);
  957. begin
  958. ExchangeItemsInt(Index1, Index2);
  959. end;
  960. procedure TStringList.Grow;
  961. Var
  962. NC : Integer;
  963. begin
  964. NC:=FCapacity;
  965. If NC>=256 then
  966. NC:=NC+(NC Div 4)
  967. else if NC=0 then
  968. NC:=4
  969. else
  970. NC:=NC*4;
  971. SetCapacity(NC);
  972. end;
  973. procedure TStringList.InternalClear(FromIndex: Integer; ClearOnly: Boolean);
  974. Var
  975. I: Integer;
  976. begin
  977. if FromIndex < FCount then
  978. begin
  979. if FOwnsObjects then
  980. begin
  981. For I:=FromIndex to FCount-1 do
  982. begin
  983. Flist^[I].FString:='';
  984. freeandnil(Flist^[i].FObject);
  985. end;
  986. end
  987. else
  988. begin
  989. For I:=FromIndex to FCount-1 do
  990. Flist^[I].FString:='';
  991. end;
  992. FCount:=FromIndex;
  993. end;
  994. if Not ClearOnly then
  995. SetCapacity(0);
  996. end;
  997. procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare
  998. );
  999. var
  1000. Pivot, vL, vR: Integer;
  1001. ExchangeProc: procedure(Left, Right: Integer) of object;
  1002. begin
  1003. //if ExchangeItems is override call that, else call (faster) ExchangeItemsInt
  1004. if TMethod(@Self.ExchangeItems).Code = CodePointer(@TStringList.ExchangeItems) then
  1005. ExchangeProc := @ExchangeItemsInt
  1006. else
  1007. ExchangeProc := @ExchangeItems;
  1008. if R - L <= 1 then begin // a little bit of time saver
  1009. if L < R then
  1010. if CompareFn(Self, L, R) > 0 then
  1011. ExchangeProc(L, R);
  1012. Exit;
  1013. end;
  1014. vL := L;
  1015. vR := R;
  1016. Pivot := L + Random(R - L); // they say random is best
  1017. while vL < vR do begin
  1018. while (vL < Pivot) and (CompareFn(Self, vL, Pivot) <= 0) do
  1019. Inc(vL);
  1020. while (vR > Pivot) and (CompareFn(Self, vR, Pivot) > 0) do
  1021. Dec(vR);
  1022. ExchangeProc(vL, vR);
  1023. if Pivot = vL then // swap pivot if we just hit it from one side
  1024. Pivot := vR
  1025. else if Pivot = vR then
  1026. Pivot := vL;
  1027. end;
  1028. if Pivot - 1 >= L then
  1029. QuickSort(L, Pivot - 1, CompareFn);
  1030. if Pivot + 1 <= R then
  1031. QuickSort(Pivot + 1, R, CompareFn);
  1032. end;
  1033. procedure TStringList.InsertItem(Index: Integer; const S: string);
  1034. begin
  1035. InsertItem(Index, S, nil);
  1036. end;
  1037. procedure TStringList.InsertItem(Index: Integer; const S: string; O: TObject);
  1038. begin
  1039. Changing;
  1040. If FCount=Fcapacity then Grow;
  1041. If Index<FCount then
  1042. System.Move (FList^[Index],FList^[Index+1],
  1043. (FCount-Index)*SizeOf(TStringItem));
  1044. Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
  1045. Flist^[Index].FString:=S;
  1046. Flist^[Index].FObject:=O;
  1047. Inc(FCount);
  1048. Changed;
  1049. end;
  1050. procedure TStringList.SetSorted(Value: Boolean);
  1051. begin
  1052. If Value then
  1053. SortStyle:=sslAuto
  1054. else
  1055. SortStyle:=sslNone
  1056. end;
  1057. procedure TStringList.Changed;
  1058. begin
  1059. If (FUpdateCount=0) Then
  1060. begin
  1061. If Assigned(FOnChange) then
  1062. FOnchange(Self);
  1063. FPONotifyObservers(Self,ooChange,Nil);
  1064. end;
  1065. end;
  1066. procedure TStringList.Changing;
  1067. begin
  1068. If FUpdateCount=0 then
  1069. if Assigned(FOnChanging) then
  1070. FOnchanging(Self);
  1071. end;
  1072. function TStringList.Get(Index: Integer): string;
  1073. begin
  1074. CheckIndex(Index);
  1075. Result:=Flist^[Index].FString;
  1076. end;
  1077. function TStringList.GetCapacity: Integer;
  1078. begin
  1079. Result:=FCapacity;
  1080. end;
  1081. function TStringList.GetCount: Integer;
  1082. begin
  1083. Result:=FCount;
  1084. end;
  1085. function TStringList.GetObject(Index: Integer): TObject;
  1086. begin
  1087. CheckIndex(Index);
  1088. Result:=Flist^[Index].FObject;
  1089. end;
  1090. procedure TStringList.Put(Index: Integer; const S: string);
  1091. begin
  1092. If Sorted then
  1093. Error(SSortedListError,0);
  1094. CheckIndex(Index);
  1095. Changing;
  1096. Flist^[Index].FString:=S;
  1097. Changed;
  1098. end;
  1099. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1100. begin
  1101. CheckIndex(Index);
  1102. Changing;
  1103. Flist^[Index].FObject:=AObject;
  1104. Changed;
  1105. end;
  1106. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1107. Var NewList : Pointer;
  1108. MSize : Longint;
  1109. begin
  1110. If (NewCapacity<0) then
  1111. Error (SListCapacityError,NewCapacity);
  1112. If NewCapacity>FCapacity then
  1113. begin
  1114. GetMem (NewList,NewCapacity*SizeOf(TStringItem));
  1115. If NewList=Nil then
  1116. Error (SListCapacityError,NewCapacity);
  1117. If Assigned(FList) then
  1118. begin
  1119. MSize:=FCapacity*Sizeof(TStringItem);
  1120. System.Move (FList^,NewList^,MSize);
  1121. FillWord (Pchar(NewList)[MSize],(NewCapacity-FCapacity)*(SizeOf(TStringItem) div SizeOf(Word)), 0);
  1122. FreeMem (Flist,MSize);
  1123. end;
  1124. Flist:=NewList;
  1125. FCapacity:=NewCapacity;
  1126. end
  1127. else if NewCapacity<FCapacity then
  1128. begin
  1129. if NewCapacity = 0 then
  1130. begin
  1131. if FCount > 0 then
  1132. InternalClear(0,True);
  1133. FreeMem(FList);
  1134. FList := nil;
  1135. end else
  1136. begin
  1137. InternalClear(NewCapacity,True);
  1138. GetMem(NewList, NewCapacity * SizeOf(TStringItem));
  1139. System.Move(FList^, NewList^, NewCapacity * SizeOf(TStringItem));
  1140. FreeMem(FList);
  1141. FList := NewList;
  1142. end;
  1143. FCapacity:=NewCapacity;
  1144. end;
  1145. end;
  1146. procedure TStringList.SetUpdateState(Updating: Boolean);
  1147. begin
  1148. If Updating then
  1149. Changing
  1150. else
  1151. Changed
  1152. end;
  1153. destructor TStringList.Destroy;
  1154. begin
  1155. InternalClear;
  1156. Inherited destroy;
  1157. end;
  1158. function TStringList.Add(const S: string): Integer;
  1159. begin
  1160. If Not (SortStyle=sslAuto) then
  1161. Result:=FCount
  1162. else
  1163. If Find (S,Result) then
  1164. Case DUplicates of
  1165. DupIgnore : Exit;
  1166. DupError : Error(SDuplicateString,0)
  1167. end;
  1168. InsertItem (Result,S);
  1169. end;
  1170. procedure TStringList.Clear;
  1171. begin
  1172. if FCount = 0 then Exit;
  1173. Changing;
  1174. InternalClear;
  1175. Changed;
  1176. end;
  1177. procedure TStringList.Delete(Index: Integer);
  1178. begin
  1179. CheckIndex(Index);
  1180. Changing;
  1181. Flist^[Index].FString:='';
  1182. if FOwnsObjects then
  1183. FreeAndNil(Flist^[Index].FObject);
  1184. Dec(FCount);
  1185. If Index<FCount then
  1186. System.Move(Flist^[Index+1],
  1187. Flist^[Index],
  1188. (Fcount-Index)*SizeOf(TStringItem));
  1189. Changed;
  1190. end;
  1191. procedure TStringList.Exchange(Index1, Index2: Integer);
  1192. begin
  1193. CheckIndex(Index1);
  1194. CheckIndex(Index2);
  1195. Changing;
  1196. ExchangeItemsInt(Index1,Index2);
  1197. changed;
  1198. end;
  1199. procedure TStringList.SetCaseSensitive(b : boolean);
  1200. begin
  1201. if b=FCaseSensitive then
  1202. Exit;
  1203. FCaseSensitive:=b;
  1204. if FSortStyle=sslAuto then
  1205. begin
  1206. FForceSort:=True;
  1207. try
  1208. Sort;
  1209. finally
  1210. FForceSort:=False;
  1211. end;
  1212. end;
  1213. end;
  1214. procedure TStringList.SetSortStyle(AValue: TStringsSortStyle);
  1215. begin
  1216. if FSortStyle=AValue then Exit;
  1217. if (AValue=sslAuto) then
  1218. Sort;
  1219. FSortStyle:=AValue;
  1220. end;
  1221. procedure TStringList.CheckIndex(AIndex: Integer);
  1222. begin
  1223. If (AIndex<0) or (AIndex>=FCount) then
  1224. Error(SListIndexError,AIndex);
  1225. end;
  1226. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1227. begin
  1228. if FCaseSensitive then
  1229. result:=AnsiCompareStr(s1,s2)
  1230. else
  1231. result:=AnsiCompareText(s1,s2);
  1232. end;
  1233. function TStringList.CompareStrings(const s1,s2 : string) : Integer;
  1234. begin
  1235. Result := DoCompareText(s1, s2);
  1236. end;
  1237. function TStringList.Find(const S: string; out Index: Integer): Boolean;
  1238. var
  1239. L, R, I: Integer;
  1240. CompareRes: PtrInt;
  1241. begin
  1242. Result := false;
  1243. Index:=-1;
  1244. if Not Sorted then
  1245. Raise EListError.Create(SErrFindNeedsSortedList);
  1246. // Use binary search.
  1247. L := 0;
  1248. R := Count - 1;
  1249. while (L<=R) do
  1250. begin
  1251. I := L + (R - L) div 2;
  1252. CompareRes := DoCompareText(S, Flist^[I].FString);
  1253. if (CompareRes>0) then
  1254. L := I+1
  1255. else begin
  1256. R := I-1;
  1257. if (CompareRes=0) then begin
  1258. Result := true;
  1259. if (Duplicates<>dupAccept) then
  1260. L := I; // forces end of while loop
  1261. end;
  1262. end;
  1263. end;
  1264. Index := L;
  1265. end;
  1266. function TStringList.IndexOf(const S: string): Integer;
  1267. begin
  1268. If Not Sorted then
  1269. Result:=Inherited indexOf(S)
  1270. else
  1271. // faster using binary search...
  1272. If Not Find (S,Result) then
  1273. Result:=-1;
  1274. end;
  1275. procedure TStringList.Insert(Index: Integer; const S: string);
  1276. begin
  1277. If SortStyle=sslAuto then
  1278. Error (SSortedListError,0)
  1279. else
  1280. begin
  1281. If (Index<0) or (Index>FCount) then
  1282. Error(SListIndexError,Index); // Cannot use CheckIndex, because there >= FCount...
  1283. InsertItem (Index,S);
  1284. end;
  1285. end;
  1286. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1287. begin
  1288. If (FForceSort or (Not (FSortStyle=sslAuto))) and (FCount>1) then
  1289. begin
  1290. Changing;
  1291. QuickSort(0,FCount-1, CompareFn);
  1292. Changed;
  1293. end;
  1294. end;
  1295. function StringListAnsiCompare(List: TStringList; Index1, Index: Integer): Integer;
  1296. begin
  1297. Result := List.DoCompareText(List.FList^[Index1].FString,
  1298. List.FList^[Index].FString);
  1299. end;
  1300. procedure TStringList.Sort;
  1301. begin
  1302. CustomSort(@StringListAnsiCompare);
  1303. end;
  1304. {$else}
  1305. { generics based implementation of TStringList follows }
  1306. function StringListAnsiCompare(List: TStringList; Index1, Index2: Integer): Integer;
  1307. begin
  1308. Result := List.DoCompareText(List.Strings[Index1], List.Strings[Index2]);
  1309. end;
  1310. constructor TStringList.Create;
  1311. begin
  1312. inherited;
  1313. FOwnsObjects:=false;
  1314. FMap := TFPStrObjMap.Create;
  1315. FMap.OnPtrCompare := @MapPtrCompare;
  1316. FOnCompareText := @DefaultCompareText;
  1317. NameValueSeparator:='=';
  1318. CheckSpecialChars;
  1319. end;
  1320. destructor TStringList.Destroy;
  1321. begin
  1322. FMap.Free;
  1323. inherited;
  1324. end;
  1325. function TStringList.GetDuplicates: TDuplicates;
  1326. begin
  1327. Result := FMap.Duplicates;
  1328. end;
  1329. function TStringList.GetSorted: boolean;
  1330. begin
  1331. Result := FMap.Sorted;
  1332. end;
  1333. procedure TStringList.SetDuplicates(NewDuplicates: TDuplicates);
  1334. begin
  1335. FMap.Duplicates := NewDuplicates;
  1336. end;
  1337. procedure TStringList.SetSorted(NewSorted: Boolean);
  1338. begin
  1339. FMap.Sorted := NewSorted;
  1340. end;
  1341. procedure TStringList.Changed;
  1342. begin
  1343. if FUpdateCount = 0 then
  1344. if Assigned(FOnChange) then
  1345. FOnChange(Self);
  1346. end;
  1347. procedure TStringList.Changing;
  1348. begin
  1349. if FUpdateCount = 0 then
  1350. if Assigned(FOnChanging) then
  1351. FOnChanging(Self);
  1352. end;
  1353. function TStringList.Get(Index: Integer): string;
  1354. begin
  1355. Result := FMap.Keys[Index];
  1356. end;
  1357. function TStringList.GetCapacity: Integer;
  1358. begin
  1359. Result := FMap.Capacity;
  1360. end;
  1361. function TStringList.GetCount: Integer;
  1362. begin
  1363. Result := FMap.Count;
  1364. end;
  1365. function TStringList.GetObject(Index: Integer): TObject;
  1366. begin
  1367. Result := FMap.Data[Index];
  1368. end;
  1369. procedure TStringList.Put(Index: Integer; const S: string);
  1370. begin
  1371. Changing;
  1372. FMap.Keys[Index] := S;
  1373. Changed;
  1374. end;
  1375. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1376. begin
  1377. Changing;
  1378. FMap.Data[Index] := AObject;
  1379. Changed;
  1380. end;
  1381. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1382. begin
  1383. FMap.Capacity := NewCapacity;
  1384. end;
  1385. procedure TStringList.SetUpdateState(Updating: Boolean);
  1386. begin
  1387. if Updating then
  1388. Changing
  1389. else
  1390. Changed
  1391. end;
  1392. function TStringList.Add(const S: string): Integer;
  1393. begin
  1394. Result := FMap.Add(S);
  1395. end;
  1396. procedure TStringList.Clear;
  1397. begin
  1398. if FMap.Count = 0 then exit;
  1399. Changing;
  1400. FMap.Clear;
  1401. Changed;
  1402. end;
  1403. procedure TStringList.Delete(Index: Integer);
  1404. begin
  1405. if (Index < 0) or (Index >= FMap.Count) then
  1406. Error(SListIndexError, Index);
  1407. Changing;
  1408. FMap.Delete(Index);
  1409. Changed;
  1410. end;
  1411. procedure TStringList.Exchange(Index1, Index2: Integer);
  1412. begin
  1413. if (Index1 < 0) or (Index1 >= FMap.Count) then
  1414. Error(SListIndexError, Index1);
  1415. if (Index2 < 0) or (Index2 >= FMap.Count) then
  1416. Error(SListIndexError, Index2);
  1417. Changing;
  1418. FMap.InternalExchange(Index1, Index2);
  1419. Changed;
  1420. end;
  1421. procedure TStringList.SetCaseSensitive(NewSensitive: Boolean);
  1422. begin
  1423. if NewSensitive <> FCaseSensitive then
  1424. begin
  1425. FCaseSensitive := NewSensitive;
  1426. if Sorted then
  1427. Sort;
  1428. end;
  1429. end;
  1430. function TStringList.MapPtrCompare(Key1, Key2: Pointer): Integer;
  1431. begin
  1432. Result := FOnCompareText(string(Key1^), string(Key2^));
  1433. end;
  1434. function TStringList.DefaultCompareText(const s1, s2: string): PtrInt;
  1435. begin
  1436. if FCaseSensitive then
  1437. Result := AnsiCompareStr(s1, s2)
  1438. else
  1439. Result := AnsiCompareText(s1, s2);
  1440. end;
  1441. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1442. begin
  1443. Result := FOnCompareText(s1, s2);
  1444. end;
  1445. function TStringList.Find(const S: string; var Index: Integer): Boolean;
  1446. begin
  1447. Result := FMap.Find(S, Index);
  1448. end;
  1449. function TStringList.IndexOf(const S: string): Integer;
  1450. begin
  1451. Result := FMap.IndexOf(S);
  1452. end;
  1453. procedure TStringList.Insert(Index: Integer; const S: string);
  1454. begin
  1455. if not Sorted and (0 <= Index) and (Index < FMap.Count) then
  1456. Changing;
  1457. FMap.InsertKey(Index, S);
  1458. Changed;
  1459. end;
  1460. procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  1461. var
  1462. I, J, Pivot: Integer;
  1463. begin
  1464. repeat
  1465. I := L;
  1466. J := R;
  1467. Pivot := (L + R) div 2;
  1468. repeat
  1469. while CompareFn(Self, I, Pivot) < 0 do Inc(I);
  1470. while CompareFn(Self, J, Pivot) > 0 do Dec(J);
  1471. if I <= J then
  1472. begin
  1473. FMap.InternalExchange(I, J); // No check, indices are correct.
  1474. if Pivot = I then
  1475. Pivot := J
  1476. else if Pivot = J then
  1477. Pivot := I;
  1478. Inc(I);
  1479. Dec(j);
  1480. end;
  1481. until I > J;
  1482. if L < J then
  1483. QuickSort(L,J, CompareFn);
  1484. L := I;
  1485. until I >= R;
  1486. end;
  1487. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1488. begin
  1489. if not Sorted and (FMap.Count > 1) then
  1490. begin
  1491. Changing;
  1492. QuickSort(0, FMap.Count-1, CompareFn);
  1493. Changed;
  1494. end;
  1495. end;
  1496. procedure TStringList.Sort;
  1497. begin
  1498. if not Sorted and (FMap.Count > 1) then
  1499. begin
  1500. Changing;
  1501. FMap.Sort;
  1502. Changed;
  1503. end;
  1504. end;
  1505. {$endif}