stringl.inc 27 KB

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