stringl.inc 31 KB

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