stringl.inc 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246
  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.GetMissingNameValueSeparatorAction: TMissingNameValueSeparatorAction;
  160. begin
  161. CheckSpecialChars;
  162. Result:=FMissingNameValueSeparatorAction;
  163. end;
  164. Function TStrings.GetDelimitedText: string;
  165. Var
  166. I : integer;
  167. p : pchar;
  168. BreakChars : set of char;
  169. S : String;
  170. doQuote : Boolean;
  171. begin
  172. CheckSpecialChars;
  173. result:='';
  174. if StrictDelimiter then
  175. BreakChars:=[#0,QuoteChar,Delimiter]
  176. else
  177. BreakChars:=[#0..' ',QuoteChar,Delimiter];
  178. // Check for break characters and quote if required.
  179. For i:=0 to count-1 do
  180. begin
  181. S:=Strings[i];
  182. doQuote:=FAlwaysQuote;
  183. If not DoQuote then
  184. begin
  185. p:=pchar(S);
  186. //Quote strings that include BreakChars:
  187. while not(p^ in BreakChars) do
  188. inc(p);
  189. DoQuote:=(p<>pchar(S)+length(S));
  190. end;
  191. if DoQuote then
  192. Result:=Result+QuoteString(S,QuoteChar)
  193. else
  194. Result:=Result+S;
  195. if I<Count-1 then
  196. Result:=Result+Delimiter;
  197. end;
  198. // Quote empty string:
  199. If (Length(Result)=0) and (Count=1) then
  200. Result:=QuoteChar+QuoteChar;
  201. end;
  202. procedure TStrings.GetNameValue(Index : Integer; Out AName,AValue : String);
  203. Var L : longint;
  204. begin
  205. aName:='';
  206. CheckSpecialChars;
  207. AValue:=Strings[Index];
  208. L:=Pos(FNameValueSeparator,AValue);
  209. If L<>0 then
  210. begin
  211. AName:=Copy(AValue,1,L-1);
  212. System.Delete(AValue,1,L);
  213. end
  214. else
  215. case FMissingNameValueSeparatorAction of
  216. mnvaValue : ;
  217. mnvaName :
  218. begin
  219. aName:=aValue;
  220. aValue:='';
  221. end;
  222. mnvaEmpty :
  223. aValue:='';
  224. mnvaError :
  225. Raise EStringListError.CreateFmt(SErrNoNameValuePairAt,[Index]);
  226. end;
  227. end;
  228. function TStrings.ExtractName(const s:String):String;
  229. var
  230. L: Longint;
  231. begin
  232. CheckSpecialChars;
  233. L:=Pos(FNameValueSeparator,S);
  234. If L<>0 then
  235. Result:=Copy(S,1,L-1)
  236. else
  237. Result:='';
  238. end;
  239. procedure TStrings.Filter(aFilter: TStringsFilterMethod; aList: TStrings);
  240. var
  241. S : string;
  242. begin
  243. for S in self do
  244. if aFilter(S) then
  245. aList.Add(S);
  246. end;
  247. procedure TStrings.ForEach(aCallback: TStringsForeachMethod);
  248. var
  249. S : String;
  250. begin
  251. for S in self do
  252. aCallBack(S);
  253. end;
  254. procedure TStrings.ForEach(aCallback: TStringsForeachMethodEx);
  255. var
  256. i: integer;
  257. begin
  258. for i:=0 to Count-1 do
  259. aCallBack(Strings[i],i);
  260. end;
  261. procedure TStrings.ForEach(aCallback: TStringsForeachMethodExObj);
  262. var
  263. i: integer;
  264. begin
  265. for i:=0 to Count-1 do
  266. aCallback(Strings[i],i,Objects[i]);
  267. end;
  268. function TStrings.Filter(aFilter: TStringsFilterMethod): TStrings;
  269. begin
  270. Result:=TStringsClass(Self.ClassType).Create;
  271. try
  272. Filter(aFilter,Result);
  273. except
  274. FreeAndNil(Result);
  275. Raise;
  276. end;
  277. end;
  278. procedure TStrings.Fill(const aValue: String; aStart, aEnd: Integer);
  279. var
  280. i: integer;
  281. begin
  282. if aEnd<0 then
  283. aEnd:=Self.Count+aEnd;
  284. if aEnd>=Count then
  285. aEnd:=Count-1;
  286. for i:=aStart to aEnd do
  287. Strings[i]:=aValue;
  288. end;
  289. Procedure TStrings.Map(aMap: TStringsMapMethod; aList : TStrings);
  290. Var
  291. S : String;
  292. begin
  293. For S in self do
  294. aList.Add(aMap(S));
  295. end;
  296. Function TStrings.Map(aMap: TStringsMapMethod) : TStrings;
  297. begin
  298. Result:=TStringsClass(Self.ClassType).Create;
  299. try
  300. Map(aMap,Result);
  301. except
  302. FreeAndNil(Result);
  303. Raise;
  304. end;
  305. end;
  306. function TStrings.Reduce(aReduceMethod: TStringsReduceMethod; const startingValue: string): string;
  307. var
  308. S : String;
  309. begin
  310. Result:=startingValue;
  311. for S in self do
  312. Result:=aReduceMethod(Result, S);
  313. end;
  314. Function TStrings.Reverse : TStrings;
  315. begin
  316. Result:=TStringsClass(Self.ClassType).Create;
  317. try
  318. Reverse(Result);
  319. except
  320. FreeAndNil(Result);
  321. Raise;
  322. end;
  323. end;
  324. Procedure TStrings.Reverse(aList : TStrings);
  325. Var
  326. I : Integer;
  327. begin
  328. for I:=Count-1 downto 0 do
  329. aList.Add(Strings[i]);
  330. end;
  331. Procedure TStrings.Slice(fromIndex: integer; aList : TStrings);
  332. var
  333. i: integer;
  334. begin
  335. for i:=fromIndex to Count-1 do
  336. aList.Add(Self[i]);
  337. end;
  338. Function TStrings.Slice(fromIndex: integer) : TStrings;
  339. begin
  340. Result:=TStringsClass(Self.ClassType).Create;
  341. try
  342. Slice(FromIndex,Result);
  343. except
  344. FreeAndNil(Result);
  345. Raise;
  346. end;
  347. end;
  348. function TStrings.GetName(Index: Integer): string;
  349. Var
  350. V : String;
  351. begin
  352. GetNameValue(Index,Result,V);
  353. end;
  354. function TStrings.GetTrailingLineBreak: Boolean;
  355. begin
  356. Result:=Not SkipLastLineBreak;
  357. end;
  358. Function TStrings.GetValue(const Name: string): string;
  359. Var
  360. L : longint;
  361. N : String;
  362. begin
  363. Result:='';
  364. L:=IndexOfName(Name);
  365. If L<>-1 then
  366. GetNameValue(L,N,Result);
  367. end;
  368. Function TStrings.GetValueFromIndex(Index: Integer): string;
  369. Var
  370. N : String;
  371. begin
  372. GetNameValue(Index,N,Result);
  373. end;
  374. Procedure TStrings.SetValueFromIndex(Index: Integer; const Value: string);
  375. begin
  376. If (Value='') then
  377. Delete(Index)
  378. else
  379. begin
  380. If (Index<0) then
  381. Index:=Add('');
  382. CheckSpecialChars;
  383. Strings[Index]:=GetName(Index)+FNameValueSeparator+Value;
  384. end;
  385. end;
  386. procedure TStrings.ReadData(Reader: TReader);
  387. begin
  388. Reader.ReadListBegin;
  389. BeginUpdate;
  390. try
  391. Clear;
  392. while not Reader.EndOfList do
  393. Add(Reader.ReadString);
  394. finally
  395. EndUpdate;
  396. end;
  397. Reader.ReadListEnd;
  398. end;
  399. Procedure TStrings.SetDelimitedText(const AValue: string);
  400. var
  401. len,i,j: SizeInt;
  402. aNotFirst:boolean;
  403. begin
  404. CheckSpecialChars;
  405. BeginUpdate;
  406. i:=1;
  407. j:=1;
  408. aNotFirst:=false;
  409. { Paraphrased from Delphi XE2 help:
  410. Strings must be separated by Delimiter characters or spaces.
  411. They may be enclosed in QuoteChars.
  412. QuoteChars in the string must be repeated to distinguish them from the QuoteChars enclosing the string.
  413. }
  414. try
  415. Clear;
  416. len:=length(AValue);
  417. If StrictDelimiter then
  418. begin
  419. while i<=Len do begin
  420. // skip delimiter
  421. if aNotFirst and (i<=len) and (AValue[i]=FDelimiter) then
  422. inc(i);
  423. // read next string
  424. if i<=len then begin
  425. if AValue[i]=FQuoteChar then begin
  426. // next string is quoted
  427. j:=i+1;
  428. while (j<=len) and
  429. ( (AValue[j]<>FQuoteChar) or
  430. ( (j+1<=len) and (AValue[j+1]=FQuoteChar) ) ) do begin
  431. if (j<=len) and (AValue[j]=FQuoteChar) then inc(j,2)
  432. else inc(j);
  433. end;
  434. // j is position of closing quote
  435. Add( StringReplace (Copy(AValue,i+1,j-i-1),
  436. FQuoteChar+FQuoteChar,FQuoteChar, [rfReplaceAll]));
  437. i:=j+1;
  438. end else begin
  439. // next string is not quoted; read until delimiter
  440. j:=i;
  441. while (j<=len) and
  442. (AValue[j]<>FDelimiter) do inc(j);
  443. Add( Copy(AValue,i,j-i));
  444. i:=j;
  445. end;
  446. end else begin
  447. if aNotFirst then Add('');
  448. end;
  449. aNotFirst:=true;
  450. end;
  451. end
  452. else
  453. begin
  454. while i<=len do begin
  455. // skip delimiter
  456. if aNotFirst and (i<=len) and (AValue[i]=FDelimiter) then inc(i);
  457. // skip spaces
  458. while (i<=len) and (Ord(AValue[i])<=Ord(' ')) do inc(i);
  459. // read next string
  460. if i<=len then begin
  461. if AValue[i]=FQuoteChar then begin
  462. // next string is quoted
  463. j:=i+1;
  464. while (j<=len) and
  465. ( (AValue[j]<>FQuoteChar) or
  466. ( (j+1<=len) and (AValue[j+1]=FQuoteChar) ) ) do begin
  467. if (j<=len) and (AValue[j]=FQuoteChar) then inc(j,2)
  468. else inc(j);
  469. end;
  470. // j is position of closing quote
  471. Add( StringReplace (Copy(AValue,i+1,j-i-1),
  472. FQuoteChar+FQuoteChar,FQuoteChar, [rfReplaceAll]));
  473. i:=j+1;
  474. end else begin
  475. // next string is not quoted; read until control character/space/delimiter
  476. j:=i;
  477. while (j<=len) and
  478. (Ord(AValue[j])>Ord(' ')) and
  479. (AValue[j]<>FDelimiter) do inc(j);
  480. Add( Copy(AValue,i,j-i));
  481. i:=j;
  482. end;
  483. end else begin
  484. if aNotFirst then Add('');
  485. end;
  486. // skip spaces
  487. while (i<=len) and (Ord(AValue[i])<=Ord(' ')) do inc(i);
  488. aNotFirst:=true;
  489. end;
  490. end;
  491. finally
  492. EndUpdate;
  493. end;
  494. end;
  495. Procedure TStrings.SetCommaText(const Value: string);
  496. Var
  497. C1,C2 : Char;
  498. begin
  499. CheckSpecialChars;
  500. C1:=Delimiter;
  501. C2:=QuoteChar;
  502. Delimiter:=',';
  503. QuoteChar:='"';
  504. Try
  505. SetDelimitedText(Value);
  506. Finally
  507. Delimiter:=C1;
  508. QuoteChar:=C2;
  509. end;
  510. end;
  511. procedure TStrings.SetMissingNameValueSeparatorAction(AValue: TMissingNameValueSeparatorAction);
  512. begin
  513. CheckSpecialChars;
  514. FMissingNameValueSeparatorAction:=aValue;
  515. end;
  516. Procedure TStrings.SetStringsAdapter(const Value: IStringsAdapter);
  517. begin
  518. end;
  519. procedure TStrings.SetTrailingLineBreak(AValue: Boolean);
  520. begin
  521. SkipLastLineBreak:=Not aValue;
  522. end;
  523. Procedure TStrings.SetDefaultEncoding(const ADefaultEncoding: TEncoding);
  524. begin
  525. if (FDefaultEncoding<>nil) and not TEncoding.IsStandardEncoding(FDefaultEncoding) then
  526. FDefaultEncoding.Free;
  527. if TEncoding.IsStandardEncoding(ADefaultEncoding) then
  528. FDefaultEncoding:=ADefaultEncoding
  529. else if ADefaultEncoding<>nil then
  530. FDefaultEncoding:=ADefaultEncoding.Clone
  531. else
  532. FDefaultEncoding:=TEncoding.Default;
  533. end;
  534. Procedure TStrings.SetValue(const Name, Value: string);
  535. Var L : longint;
  536. begin
  537. CheckSpecialChars;
  538. L:=IndexOfName(Name);
  539. if L=-1 then
  540. Add (Name+FNameValueSeparator+Value)
  541. else
  542. Strings[L]:=Name+FNameValueSeparator+value;
  543. end;
  544. procedure TStrings.WriteData(Writer: TWriter);
  545. var
  546. i: Integer;
  547. begin
  548. Writer.WriteListBegin;
  549. for i := 0 to Count - 1 do
  550. Writer.WriteString(Strings[i]);
  551. Writer.WriteListEnd;
  552. end;
  553. procedure TStrings.DefineProperties(Filer: TFiler);
  554. var
  555. HasData: Boolean;
  556. begin
  557. if Assigned(Filer.Ancestor) then
  558. // Only serialize if string list is different from ancestor
  559. if Filer.Ancestor.InheritsFrom(TStrings) then
  560. HasData := not Equals(TStrings(Filer.Ancestor))
  561. else
  562. HasData := True
  563. else
  564. HasData := Count > 0;
  565. Filer.DefineProperty('Strings', @ReadData, @WriteData, HasData);
  566. end;
  567. Procedure TStrings.Error(const Msg: string; Data: Integer);
  568. begin
  569. Raise EStringListError.CreateFmt(Msg,[Data]) at get_caller_addr(get_frame), get_caller_frame(get_frame);
  570. end;
  571. Procedure TStrings.Error(const Msg: pstring; Data: Integer);
  572. begin
  573. Raise EStringListError.CreateFmt(Msg^,[Data]) at get_caller_addr(get_frame), get_caller_frame(get_frame);
  574. end;
  575. Function TStrings.GetCapacity: Integer;
  576. begin
  577. Result:=Count;
  578. end;
  579. Function TStrings.GetObject(Index: Integer): TObject;
  580. begin
  581. Result:=Nil;
  582. end;
  583. Function TStrings.GetTextStr: string;
  584. Var P : Pchar;
  585. I,L,NLS : SizeInt;
  586. S,NL : String;
  587. begin
  588. CheckSpecialChars;
  589. // Determine needed place
  590. if FLineBreak<>sLineBreak then
  591. NL:=FLineBreak
  592. else
  593. Case FLBS of
  594. tlbsLF : NL:=#10;
  595. tlbsCRLF : NL:=#13#10;
  596. tlbsCR : NL:=#13;
  597. end;
  598. L:=0;
  599. NLS:=Length(NL);
  600. For I:=0 to count-1 do
  601. L:=L+Length(Strings[I])+NLS;
  602. if SkipLastLineBreak then
  603. Dec(L,NLS);
  604. Setlength(Result,L);
  605. P:=Pointer(Result);
  606. For i:=0 To count-1 do
  607. begin
  608. S:=Strings[I];
  609. L:=Length(S);
  610. if L<>0 then
  611. System.Move(Pointer(S)^,P^,L);
  612. P:=P+L;
  613. if (I<Count-1) or Not SkipLastLineBreak then
  614. For L:=1 to NLS do
  615. begin
  616. P^:=NL[L];
  617. inc(P);
  618. end;
  619. end;
  620. end;
  621. Procedure TStrings.Put(Index: Integer; const S: string);
  622. Var Obj : TObject;
  623. begin
  624. Obj:=Objects[Index];
  625. Delete(Index);
  626. InsertObject(Index,S,Obj);
  627. end;
  628. Procedure TStrings.PutObject(Index: Integer; AObject: TObject);
  629. begin
  630. // Empty.
  631. end;
  632. Procedure TStrings.SetCapacity(NewCapacity: Integer);
  633. begin
  634. // Empty.
  635. end;
  636. Class Function TStrings.GetNextLine (Const Value : String; Var S : String; Var P : SizeInt) : Boolean;
  637. var
  638. LengthOfValue: SizeInt;
  639. StartPos, FuturePos: SizeInt;
  640. begin
  641. LengthOfValue := Length(Value);
  642. StartPos := P;
  643. if (StartPos <= 0) or (StartPos > LengthOfValue) then // True for LengthOfValue <= 0
  644. begin
  645. S := '';
  646. Exit(False);
  647. end;
  648. FuturePos := StartPos;
  649. while (FuturePos <= LengthOfValue) and not (Value[FuturePos] in [#10, #13]) do
  650. Inc(FuturePos);
  651. // If we use S := Copy(Value, StartPos, FuturePos - StartPos); then compiler
  652. // generate TempS := Copy(...); S := TempS to eliminate side effects and
  653. // implicit "try finally" for TempS finalization
  654. // When we use SetString then no TempS, no try finally generated,
  655. // but we must check case when Value and S is same (side effects)
  656. if Pointer(S) = Pointer(Value) then
  657. System.Delete(S, FuturePos, High(FuturePos))
  658. else
  659. begin
  660. SetString(S, @Value[StartPos], FuturePos - StartPos);
  661. if (FuturePos <= LengthOfValue) and (Value[FuturePos] = #13) then
  662. Inc(FuturePos);
  663. if (FuturePos <= LengthOfValue) and (Value[FuturePos] = #10) then
  664. Inc(FuturePos);
  665. end;
  666. P := FuturePos;
  667. Result := True;
  668. end;
  669. Function TStrings.GetNextLineBreak (Const Value : String; Var S : String; Var P : SizeInt) : Boolean;
  670. var
  671. StartPos, FuturePos: SizeInt;
  672. begin
  673. StartPos := P;
  674. if (StartPos <= 0) or (StartPos > Length(Value)) then // True for Length <= 0
  675. begin
  676. S := '';
  677. Exit(False);
  678. end;
  679. FuturePos := Pos(FLineBreak, Value, StartPos); // Use PosEx in old RTL
  680. // Why we don't use Copy but use SetString read in GetNextLine
  681. if FuturePos = 0 then // No line breaks
  682. begin
  683. FuturePos := Length(Value) + 1;
  684. if Pointer(S) = Pointer(Value) then
  685. // Nothing to do
  686. else
  687. SetString(S, @Value[StartPos], FuturePos - StartPos)
  688. end
  689. else
  690. if Pointer(S) = Pointer(Value) then
  691. System.Delete(S, FuturePos, High(FuturePos))
  692. else
  693. begin
  694. SetString(S, @Value[StartPos], FuturePos - StartPos);
  695. Inc(FuturePos, Length(FLineBreak));
  696. end;
  697. P := FuturePos;
  698. Result := True;
  699. end;
  700. {$IF (SizeOf(Integer) < SizeOf(SizeInt)) }
  701. class function TStrings.GetNextLine(const Value: string; var S: string; var P: Integer) : Boolean;
  702. var
  703. LP: SizeInt;
  704. begin
  705. LP := P;
  706. Result := GetNextLine(Value, S, LP);
  707. P := LP;
  708. end;
  709. function TStrings.GetNextLineBreak(const Value: string; var S: string; var P: Integer) : Boolean;
  710. var
  711. LP: SizeInt;
  712. begin
  713. LP := P;
  714. Result := GetNextLineBreak(Value, S, LP);
  715. P := LP;
  716. end;
  717. {$IFEND}
  718. Procedure TStrings.DoSetTextStr(const Value: string; DoClear : Boolean);
  719. Var
  720. S : String;
  721. P : SizeInt;
  722. begin
  723. Try
  724. beginUpdate;
  725. if DoClear then
  726. Clear;
  727. P:=1;
  728. if FLineBreak=sLineBreak then
  729. begin
  730. While GetNextLine (Value,S,P) do
  731. Add(S)
  732. end
  733. else
  734. While GetNextLineBreak (Value,S,P) do
  735. Add(S);
  736. finally
  737. EndUpdate;
  738. end;
  739. end;
  740. Procedure TStrings.SetTextStr(const Value: string);
  741. begin
  742. CheckSpecialChars;
  743. DoSetTextStr(Value,True);
  744. end;
  745. Procedure TStrings.AddText(const S: string);
  746. begin
  747. CheckSpecialChars;
  748. DoSetTextStr(S,False);
  749. end;
  750. procedure TStrings.AddCommaText(const S: String);
  751. var
  752. L: TStringList;
  753. begin
  754. L := TStringList.Create;
  755. try
  756. L.CommaText := S;
  757. AddStrings(L);
  758. finally
  759. L.Free;
  760. end;
  761. end;
  762. procedure TStrings.AddDelimitedText(const S: String; ADelimiter: Char;
  763. AStrictDelimiter: Boolean);
  764. var
  765. L: TStringList;
  766. begin
  767. L := TStringList.Create;
  768. try
  769. L.Delimiter := ADelimiter;
  770. L.StrictDelimiter := AStrictDelimiter;
  771. L.DelimitedText := S;
  772. AddStrings(L);
  773. finally
  774. L.Free;
  775. end;
  776. end;
  777. procedure TStrings.AddDelimitedText(const S: String);
  778. begin
  779. AddDelimitedText(S, FDelimiter, FStrictDelimiter);
  780. end;
  781. Procedure TStrings.SetUpdateState(Updating: Boolean);
  782. begin
  783. FPONotifyObservers(Self,ooChange,Nil);
  784. end;
  785. destructor TSTrings.Destroy;
  786. begin
  787. if (FEncoding<>nil) and not TEncoding.IsStandardEncoding(FEncoding) then
  788. FreeAndNil(FEncoding);
  789. if (FDefaultEncoding<>nil) and not TEncoding.IsStandardEncoding(FDefaultEncoding) then
  790. FreeAndNil(FDefaultEncoding);
  791. inherited destroy;
  792. end;
  793. constructor TStrings.Create;
  794. begin
  795. inherited Create;
  796. FDefaultEncoding:=TEncoding.Default;
  797. FEncoding:=nil;
  798. FWriteBOM:=True;
  799. FAlwaysQuote:=False;
  800. end;
  801. Function TStrings.Add(const S: string): Integer;
  802. begin
  803. Result:=Count;
  804. Insert (Count,S);
  805. end;
  806. function TStrings.Add(const Fmt : string; const Args : Array of const): Integer;
  807. begin
  808. Result:=Add(Format(Fmt,Args));
  809. end;
  810. Function TStrings.AddObject(const S: string; AObject: TObject): Integer;
  811. begin
  812. Result:=Add(S);
  813. Objects[result]:=AObject;
  814. end;
  815. function TStrings.AddObject(const Fmt: string; Args : Array of const; AObject: TObject): Integer;
  816. begin
  817. Result:=AddObject(Format(Fmt,Args),AObject);
  818. end;
  819. function TStrings.AddPair(const AName, AValue: string): TStrings;
  820. begin
  821. Result:=AddPair(AName,AValue,Nil);
  822. end;
  823. function TStrings.AddPair(const AName, AValue: string; AObject: TObject): TStrings;
  824. begin
  825. Result := Self;
  826. AddObject(Concat(AName, NameValueSeparator, AValue), AObject);
  827. end;
  828. Procedure TStrings.Append(const S: string);
  829. begin
  830. Add (S);
  831. end;
  832. Procedure TStrings.AddStrings(TheStrings: TStrings; ClearFirst : Boolean);
  833. Var Runner : longint;
  834. begin
  835. beginupdate;
  836. try
  837. if ClearFirst then
  838. Clear;
  839. if Count + TheStrings.Count > Capacity then
  840. Capacity := Count + TheStrings.Count;
  841. For Runner:=0 to TheStrings.Count-1 do
  842. self.AddObject (Thestrings[Runner],TheStrings.Objects[Runner]);
  843. finally
  844. EndUpdate;
  845. end;
  846. end;
  847. Procedure TStrings.AddStrings(TheStrings: TStrings);
  848. begin
  849. AddStrings(TheStrings, False);
  850. end;
  851. Procedure TStrings.AddStrings(const TheStrings: array of string);
  852. begin
  853. AddStrings(TheStrings, False);
  854. end;
  855. Procedure TStrings.AddStrings(const TheStrings: array of string; ClearFirst : Boolean);
  856. Var Runner : longint;
  857. begin
  858. beginupdate;
  859. try
  860. if ClearFirst then
  861. Clear;
  862. if Count + High(TheStrings)+1 > Capacity then
  863. Capacity := Count + High(TheStrings)+1;
  864. For Runner:=Low(TheStrings) to High(TheStrings) do
  865. self.Add(Thestrings[Runner]);
  866. finally
  867. EndUpdate;
  868. end;
  869. end;
  870. Procedure TStrings.Assign(Source: TPersistent);
  871. Var
  872. S : TStrings;
  873. begin
  874. If Source is TStrings then
  875. begin
  876. S:=TStrings(Source);
  877. BeginUpdate;
  878. Try
  879. clear;
  880. FSpecialCharsInited:=S.FSpecialCharsInited;
  881. FQuoteChar:=S.FQuoteChar;
  882. FDelimiter:=S.FDelimiter;
  883. FNameValueSeparator:=S.FNameValueSeparator;
  884. FLBS:=S.FLBS;
  885. FLineBreak:=S.FLineBreak;
  886. FWriteBOM:=S.FWriteBOM;
  887. DefaultEncoding:=S.DefaultEncoding;
  888. SetEncoding(S.Encoding);
  889. AddStrings(S);
  890. finally
  891. EndUpdate;
  892. end;
  893. end
  894. else
  895. Inherited Assign(Source);
  896. end;
  897. Procedure TStrings.BeginUpdate;
  898. begin
  899. if FUpdateCount = 0 then SetUpdateState(true);
  900. inc(FUpdateCount);
  901. end;
  902. Procedure TStrings.EndUpdate;
  903. begin
  904. If FUpdateCount>0 then
  905. Dec(FUpdateCount);
  906. if FUpdateCount=0 then
  907. SetUpdateState(False);
  908. end;
  909. Function TStrings.Equals(Obj: TObject): Boolean;
  910. begin
  911. if Obj is TStrings then
  912. Result := Equals(TStrings(Obj))
  913. else
  914. Result := inherited Equals(Obj);
  915. end;
  916. Function TStrings.Equals(TheStrings: TStrings): Boolean;
  917. Var Runner,Nr : Longint;
  918. begin
  919. Result:=False;
  920. Nr:=Self.Count;
  921. if Nr<>TheStrings.Count then exit;
  922. For Runner:=0 to Nr-1 do
  923. If Strings[Runner]<>TheStrings[Runner] then exit;
  924. Result:=True;
  925. end;
  926. Procedure TStrings.Exchange(Index1, Index2: Integer);
  927. Var
  928. Obj : TObject;
  929. Str : String;
  930. begin
  931. beginUpdate;
  932. Try
  933. Obj:=Objects[Index1];
  934. Str:=Strings[Index1];
  935. Objects[Index1]:=Objects[Index2];
  936. Strings[Index1]:=Strings[Index2];
  937. Objects[Index2]:=Obj;
  938. Strings[Index2]:=Str;
  939. finally
  940. EndUpdate;
  941. end;
  942. end;
  943. function TStrings.GetEnumerator: TStringsEnumerator;
  944. begin
  945. Result:=TStringsEnumerator.Create(Self);
  946. end;
  947. Function TStrings.GetText: PChar;
  948. begin
  949. Result:=StrNew(Pchar(Self.Text));
  950. end;
  951. Function TStrings.DoCompareText(const s1,s2 : string) : PtrInt;
  952. begin
  953. result:=CompareText(s1,s2);
  954. end;
  955. Function TStrings.IndexOf(const S: string): Integer;
  956. begin
  957. Result:=0;
  958. While (Result<Count) and (DoCompareText(Strings[Result],S)<>0) do Result:=Result+1;
  959. if Result=Count then Result:=-1;
  960. end;
  961. function TStrings.IndexOf(const S: string; aStart: Integer): Integer;
  962. begin
  963. if aStart<0 then
  964. begin
  965. aStart:=Count+aStart;
  966. if aStart<0 then
  967. aStart:=0;
  968. end;
  969. Result:=aStart;
  970. While (Result<Count) and (DoCompareText(Strings[Result],S)<>0) do Result:=Result+1;
  971. if Result=Count then Result:=-1;
  972. end;
  973. Function TStrings.IndexOfName(const Name: string): Integer;
  974. Var
  975. len : longint;
  976. S : String;
  977. begin
  978. CheckSpecialChars;
  979. Result:=0;
  980. while (Result<Count) do
  981. begin
  982. S:=Strings[Result];
  983. len:=pos(FNameValueSeparator,S)-1;
  984. if (len>=0) and (DoCompareText(Name,Copy(S,1,Len))=0) then
  985. exit;
  986. inc(result);
  987. end;
  988. result:=-1;
  989. end;
  990. Function TStrings.IndexOfObject(AObject: TObject): Integer;
  991. begin
  992. Result:=0;
  993. While (Result<count) and (Objects[Result]<>AObject) do Result:=Result+1;
  994. If Result=Count then Result:=-1;
  995. end;
  996. Procedure TStrings.InsertObject(Index: Integer; const S: string;
  997. AObject: TObject);
  998. begin
  999. Insert (Index,S);
  1000. Objects[Index]:=AObject;
  1001. end;
  1002. function TStrings.LastIndexOf(const S: string): Integer;
  1003. begin
  1004. Result:=LastIndexOf(S,Count-1);
  1005. end;
  1006. function TStrings.LastIndexOf(const S: string; aStart : Integer): Integer;
  1007. begin
  1008. if aStart<0 then
  1009. begin
  1010. aStart:=Count+aStart;
  1011. if aStart<0 then
  1012. aStart:=0;
  1013. end;
  1014. Result:=aStart;
  1015. if Result>=Count-1 then
  1016. Result:=Count-1;
  1017. While (Result>=0) and (DoCompareText(Strings[Result],S)<>0) do
  1018. Result:=Result-1;
  1019. end;
  1020. Procedure TStrings.LoadFromFile(const FileName: string);
  1021. begin
  1022. LoadFromFile(FileName,False)
  1023. end;
  1024. Procedure TStrings.LoadFromFile(const FileName: string; IgnoreEncoding : Boolean);
  1025. Var
  1026. TheStream : TFileStream;
  1027. begin
  1028. TheStream:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
  1029. try
  1030. LoadFromStream(TheStream, IgnoreEncoding);
  1031. finally
  1032. TheStream.Free;
  1033. end;
  1034. end;
  1035. Procedure TStrings.LoadFromFile(const FileName: string; AEncoding: TEncoding);
  1036. Var
  1037. TheStream : TFileStream;
  1038. begin
  1039. TheStream:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
  1040. try
  1041. LoadFromStream(TheStream,AEncoding);
  1042. finally
  1043. TheStream.Free;
  1044. end;
  1045. end;
  1046. Procedure TStrings.LoadFromStream(Stream: TStream);
  1047. begin
  1048. LoadFromStream(Stream,False);
  1049. end;
  1050. Const
  1051. LoadBufSize = 1024;
  1052. LoadMaxGrow = MaxInt Div 2;
  1053. Procedure TStrings.LoadFromStream(Stream: TStream; IgnoreEncoding : Boolean);
  1054. {
  1055. Borlands method is no good, since a pipe for
  1056. instance doesn't have a size.
  1057. So we must do it the hard way.
  1058. }
  1059. Var
  1060. Buffer : AnsiString;
  1061. BufLen : SizeInt;
  1062. BytesRead, I, BufDelta : Longint;
  1063. begin
  1064. if not IgnoreEncoding then
  1065. begin
  1066. LoadFromStream(Stream,Nil);
  1067. Exit;
  1068. end;
  1069. // reread into a buffer
  1070. beginupdate;
  1071. try
  1072. Buffer:='';
  1073. BufLen:=0;
  1074. I:=1;
  1075. Repeat
  1076. BufDelta:=LoadBufSize*I;
  1077. SetLength(Buffer,BufLen+BufDelta);
  1078. BytesRead:=Stream.Read(Buffer[BufLen+1],BufDelta);
  1079. inc(BufLen,BufDelta);
  1080. If I<LoadMaxGrow then
  1081. I:=I shl 1;
  1082. Until BytesRead<>BufDelta;
  1083. SetLength(Buffer, BufLen-BufDelta+BytesRead);
  1084. SetTextStr(Buffer);
  1085. SetLength(Buffer,0);
  1086. finally
  1087. EndUpdate;
  1088. end;
  1089. end;
  1090. Procedure TStrings.LoadFromStream(Stream: TStream; AEncoding: TEncoding);
  1091. {
  1092. Borlands method is no good, since a pipe for
  1093. instance doesn't have a size.
  1094. So we must do it the hard way.
  1095. }
  1096. Var
  1097. Buffer : TBytes;
  1098. T : string;
  1099. BufLen : SizeInt;
  1100. BytesRead, I, BufDelta, PreambleLength : Longint;
  1101. begin
  1102. // reread into a buffer
  1103. beginupdate;
  1104. try
  1105. SetLength(Buffer,0);
  1106. BufLen:=0;
  1107. I:=1;
  1108. Repeat
  1109. BufDelta:=LoadBufSize*I;
  1110. SetLength(Buffer,BufLen+BufDelta);
  1111. BytesRead:=Stream.Read(Buffer[BufLen],BufDelta);
  1112. inc(BufLen,BufDelta);
  1113. If I<LoadMaxGrow then
  1114. I:=I shl 1;
  1115. Until BytesRead<>BufDelta;
  1116. SetLength(Buffer,BufLen-BufDelta+BytesRead);
  1117. PreambleLength:=TEncoding.GetBufferEncoding(Buffer,AEncoding,FDefaultEncoding);
  1118. T:=AEncoding.GetAnsiString(Buffer,PreambleLength,Length(Buffer)-PreambleLength);
  1119. SetEncoding(AEncoding);
  1120. SetLength(Buffer,0);
  1121. SetTextStr(T);
  1122. finally
  1123. EndUpdate;
  1124. end;
  1125. end;
  1126. Procedure TStrings.Move(CurIndex, NewIndex: Integer);
  1127. Var
  1128. Obj : TObject;
  1129. Str : String;
  1130. begin
  1131. BeginUpdate;
  1132. Try
  1133. Obj:=Objects[CurIndex];
  1134. Str:=Strings[CurIndex];
  1135. Objects[CurIndex]:=Nil; // Prevent Delete from freeing.
  1136. Delete(Curindex);
  1137. InsertObject(NewIndex,Str,Obj);
  1138. finally
  1139. EndUpdate;
  1140. end;
  1141. end;
  1142. function TStrings.Pop: string;
  1143. var
  1144. C : Integer;
  1145. begin
  1146. Result:='';
  1147. C:=Count-1;
  1148. if (C>=0) then
  1149. begin
  1150. Result:=Strings[C];
  1151. Delete(C);
  1152. end;
  1153. end;
  1154. function TStrings.Shift: String;
  1155. begin
  1156. Result:='';
  1157. if (Count > 0) then
  1158. begin
  1159. Result:=Strings[0];
  1160. Delete(0);
  1161. end;
  1162. end;
  1163. Procedure TStrings.SaveToFile(const FileName: string);
  1164. Var TheStream : TFileStream;
  1165. begin
  1166. TheStream:=TFileStream.Create(FileName,fmCreate);
  1167. try
  1168. SaveToStream(TheStream);
  1169. finally
  1170. TheStream.Free;
  1171. end;
  1172. end;
  1173. Procedure TStrings.SaveToFile(const FileName: string; AEncoding: TEncoding);
  1174. Var TheStream : TFileStream;
  1175. begin
  1176. TheStream:=TFileStream.Create(FileName,fmCreate);
  1177. try
  1178. SaveToStream(TheStream,AEncoding);
  1179. finally
  1180. TheStream.Free;
  1181. end;
  1182. end;
  1183. Procedure TStrings.SaveToStream(Stream: TStream);
  1184. Var
  1185. S : String;
  1186. begin
  1187. if Encoding<>nil then
  1188. SaveToStream(Stream,Encoding)
  1189. else
  1190. begin
  1191. S:=Text;
  1192. if S = '' then Exit;
  1193. Stream.WriteBuffer(Pointer(S)^,Length(S));
  1194. end;
  1195. end;
  1196. Procedure TStrings.SaveToStream(Stream: TStream; AEncoding: TEncoding);
  1197. Var B : TBytes;
  1198. begin
  1199. if AEncoding=nil then
  1200. AEncoding:=FDefaultEncoding;
  1201. if FWriteBOM then
  1202. begin
  1203. B:=AEncoding.GetPreamble;
  1204. if Length(B)>0 then
  1205. Stream.WriteBuffer(B[0],Length(B));
  1206. end;
  1207. B:=AEncoding.GetAnsiBytes(Text);
  1208. if Length(B)>0 then
  1209. Stream.WriteBuffer(B[0],Length(B));
  1210. end;
  1211. Procedure TStrings.SetText(TheText: PChar);
  1212. Var S : String;
  1213. begin
  1214. If TheText<>Nil then
  1215. S:=StrPas(TheText)
  1216. else
  1217. S:='';
  1218. SetTextStr(S);
  1219. end;
  1220. {****************************************************************************}
  1221. {* TStringList *}
  1222. {****************************************************************************}
  1223. {$if not defined(FPC_TESTGENERICS)}
  1224. procedure TStringList.ExchangeItemsInt(Index1, Index2: Integer);
  1225. Var P1,P2 : Pointer;
  1226. begin
  1227. P1:=Pointer(Flist^[Index1].FString);
  1228. P2:=Pointer(Flist^[Index1].FObject);
  1229. Pointer(Flist^[Index1].Fstring):=Pointer(Flist^[Index2].Fstring);
  1230. Pointer(Flist^[Index1].FObject):=Pointer(Flist^[Index2].FObject);
  1231. Pointer(Flist^[Index2].Fstring):=P1;
  1232. Pointer(Flist^[Index2].FObject):=P2;
  1233. end;
  1234. function TStringList.GetSorted: Boolean;
  1235. begin
  1236. Result:=FSortStyle in [sslUser,sslAuto];
  1237. end;
  1238. procedure TStringList.ExchangeItems(Index1, Index2: Integer);
  1239. begin
  1240. ExchangeItemsInt(Index1, Index2);
  1241. end;
  1242. procedure TStringList.Grow;
  1243. Var
  1244. NC : Integer;
  1245. begin
  1246. NC:=FCapacity;
  1247. If NC>=256 then
  1248. NC:=NC+(NC Div 4)
  1249. else if NC=0 then
  1250. NC:=4
  1251. else
  1252. NC:=NC*4;
  1253. SetCapacity(NC);
  1254. end;
  1255. procedure TStringList.InternalClear(FromIndex: Integer; ClearOnly: Boolean);
  1256. Var
  1257. I: Integer;
  1258. begin
  1259. if FromIndex < FCount then
  1260. begin
  1261. if FOwnsObjects then
  1262. begin
  1263. For I:=FromIndex to FCount-1 do
  1264. begin
  1265. Flist^[I].FString:='';
  1266. freeandnil(Flist^[i].FObject);
  1267. end;
  1268. end
  1269. else
  1270. begin
  1271. For I:=FromIndex to FCount-1 do
  1272. Flist^[I].FString:='';
  1273. end;
  1274. FCount:=FromIndex;
  1275. end;
  1276. if Not ClearOnly then
  1277. SetCapacity(0);
  1278. end;
  1279. procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare
  1280. );
  1281. var
  1282. Pivot, vL, vR: Integer;
  1283. ExchangeProc: procedure(Left, Right: Integer) of object;
  1284. begin
  1285. //if ExchangeItems is override call that, else call (faster) ExchangeItemsInt
  1286. if TMethod(@Self.ExchangeItems).Code = CodePointer(@TStringList.ExchangeItems) then
  1287. ExchangeProc := @ExchangeItemsInt
  1288. else
  1289. ExchangeProc := @ExchangeItems;
  1290. if R - L <= 1 then begin // a little bit of time saver
  1291. if L < R then
  1292. if CompareFn(Self, L, R) > 0 then
  1293. ExchangeProc(L, R);
  1294. Exit;
  1295. end;
  1296. vL := L;
  1297. vR := R;
  1298. Pivot := L + Random(R - L); // they say random is best
  1299. while vL < vR do begin
  1300. while (vL < Pivot) and (CompareFn(Self, vL, Pivot) <= 0) do
  1301. Inc(vL);
  1302. while (vR > Pivot) and (CompareFn(Self, vR, Pivot) > 0) do
  1303. Dec(vR);
  1304. ExchangeProc(vL, vR);
  1305. if Pivot = vL then // swap pivot if we just hit it from one side
  1306. Pivot := vR
  1307. else if Pivot = vR then
  1308. Pivot := vL;
  1309. end;
  1310. if Pivot - 1 >= L then
  1311. QuickSort(L, Pivot - 1, CompareFn);
  1312. if Pivot + 1 <= R then
  1313. QuickSort(Pivot + 1, R, CompareFn);
  1314. end;
  1315. procedure TStringList.InsertItem(Index: Integer; const S: string);
  1316. begin
  1317. InsertItem(Index, S, nil);
  1318. end;
  1319. procedure TStringList.InsertItem(Index: Integer; const S: string; O: TObject);
  1320. begin
  1321. Changing;
  1322. If FCount=Fcapacity then Grow;
  1323. If Index<FCount then
  1324. System.Move (FList^[Index],FList^[Index+1],
  1325. (FCount-Index)*SizeOf(TStringItem));
  1326. Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
  1327. Flist^[Index].FString:=S;
  1328. Flist^[Index].FObject:=O;
  1329. Inc(FCount);
  1330. Changed;
  1331. end;
  1332. procedure TStringList.SetSorted(Value: Boolean);
  1333. begin
  1334. If Value then
  1335. SortStyle:=sslAuto
  1336. else
  1337. SortStyle:=sslNone
  1338. end;
  1339. procedure TStringList.Changed;
  1340. begin
  1341. If (FUpdateCount=0) Then
  1342. begin
  1343. If Assigned(FOnChange) then
  1344. FOnchange(Self);
  1345. FPONotifyObservers(Self,ooChange,Nil);
  1346. end;
  1347. end;
  1348. procedure TStringList.Changing;
  1349. begin
  1350. If FUpdateCount=0 then
  1351. if Assigned(FOnChanging) then
  1352. FOnchanging(Self);
  1353. end;
  1354. function TStringList.Get(Index: Integer): string;
  1355. begin
  1356. CheckIndex(Index);
  1357. Result:=Flist^[Index].FString;
  1358. end;
  1359. function TStringList.GetCapacity: Integer;
  1360. begin
  1361. Result:=FCapacity;
  1362. end;
  1363. function TStringList.GetCount: Integer;
  1364. begin
  1365. Result:=FCount;
  1366. end;
  1367. function TStringList.GetObject(Index: Integer): TObject;
  1368. begin
  1369. CheckIndex(Index);
  1370. Result:=Flist^[Index].FObject;
  1371. end;
  1372. procedure TStringList.Put(Index: Integer; const S: string);
  1373. begin
  1374. If Sorted then
  1375. Error(SSortedListError,0);
  1376. CheckIndex(Index);
  1377. Changing;
  1378. Flist^[Index].FString:=S;
  1379. Changed;
  1380. end;
  1381. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1382. begin
  1383. CheckIndex(Index);
  1384. Changing;
  1385. Flist^[Index].FObject:=AObject;
  1386. Changed;
  1387. end;
  1388. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1389. Var NewList : Pointer;
  1390. MSize : Longint;
  1391. begin
  1392. If (NewCapacity<0) then
  1393. Error (SListCapacityError,NewCapacity);
  1394. If NewCapacity>FCapacity then
  1395. begin
  1396. GetMem (NewList,NewCapacity*SizeOf(TStringItem));
  1397. If NewList=Nil then
  1398. Error (SListCapacityError,NewCapacity);
  1399. If Assigned(FList) then
  1400. begin
  1401. MSize:=FCapacity*Sizeof(TStringItem);
  1402. System.Move (FList^,NewList^,MSize);
  1403. FillWord (Pchar(NewList)[MSize],(NewCapacity-FCapacity)*(SizeOf(TStringItem) div SizeOf(Word)), 0);
  1404. FreeMem (Flist,MSize);
  1405. end;
  1406. Flist:=NewList;
  1407. FCapacity:=NewCapacity;
  1408. end
  1409. else if NewCapacity<FCapacity then
  1410. begin
  1411. if NewCapacity = 0 then
  1412. begin
  1413. if FCount > 0 then
  1414. InternalClear(0,True);
  1415. FreeMem(FList);
  1416. FList := nil;
  1417. end else
  1418. begin
  1419. InternalClear(NewCapacity,True);
  1420. GetMem(NewList, NewCapacity * SizeOf(TStringItem));
  1421. System.Move(FList^, NewList^, NewCapacity * SizeOf(TStringItem));
  1422. FreeMem(FList);
  1423. FList := NewList;
  1424. end;
  1425. FCapacity:=NewCapacity;
  1426. end;
  1427. end;
  1428. procedure TStringList.SetUpdateState(Updating: Boolean);
  1429. begin
  1430. If Updating then
  1431. Changing
  1432. else
  1433. Changed
  1434. end;
  1435. destructor TStringList.Destroy;
  1436. begin
  1437. InternalClear;
  1438. Inherited destroy;
  1439. end;
  1440. function TStringList.Add(const S: string): Integer;
  1441. begin
  1442. If Not (SortStyle=sslAuto) then
  1443. Result:=FCount
  1444. else
  1445. If Find (S,Result) then
  1446. Case DUplicates of
  1447. DupIgnore : Exit;
  1448. DupError : Error(SDuplicateString,0)
  1449. end;
  1450. InsertItem (Result,S);
  1451. end;
  1452. procedure TStringList.Clear;
  1453. begin
  1454. if FCount = 0 then Exit;
  1455. Changing;
  1456. InternalClear;
  1457. Changed;
  1458. end;
  1459. procedure TStringList.Delete(Index: Integer);
  1460. begin
  1461. CheckIndex(Index);
  1462. Changing;
  1463. Flist^[Index].FString:='';
  1464. if FOwnsObjects then
  1465. FreeAndNil(Flist^[Index].FObject);
  1466. Dec(FCount);
  1467. If Index<FCount then
  1468. System.Move(Flist^[Index+1],
  1469. Flist^[Index],
  1470. (Fcount-Index)*SizeOf(TStringItem));
  1471. Changed;
  1472. end;
  1473. procedure TStringList.Exchange(Index1, Index2: Integer);
  1474. begin
  1475. CheckIndex(Index1);
  1476. CheckIndex(Index2);
  1477. Changing;
  1478. ExchangeItemsInt(Index1,Index2);
  1479. changed;
  1480. end;
  1481. procedure TStringList.SetCaseSensitive(b : boolean);
  1482. begin
  1483. if b=FCaseSensitive then
  1484. Exit;
  1485. FCaseSensitive:=b;
  1486. if FSortStyle=sslAuto then
  1487. begin
  1488. FForceSort:=True;
  1489. try
  1490. Sort;
  1491. finally
  1492. FForceSort:=False;
  1493. end;
  1494. end;
  1495. end;
  1496. procedure TStringList.SetSortStyle(AValue: TStringsSortStyle);
  1497. begin
  1498. if FSortStyle=AValue then Exit;
  1499. if (AValue=sslAuto) then
  1500. Sort;
  1501. FSortStyle:=AValue;
  1502. end;
  1503. procedure TStringList.CheckIndex(AIndex: Integer);
  1504. begin
  1505. If (AIndex<0) or (AIndex>=FCount) then
  1506. Error(SListIndexError,AIndex);
  1507. end;
  1508. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1509. begin
  1510. if FCaseSensitive then
  1511. result:=AnsiCompareStr(s1,s2)
  1512. else
  1513. result:=AnsiCompareText(s1,s2);
  1514. end;
  1515. function TStringList.CompareStrings(const s1,s2 : string) : Integer;
  1516. begin
  1517. Result := DoCompareText(s1, s2);
  1518. end;
  1519. function TStringList.Find(const S: string; out Index: Integer): Boolean;
  1520. var
  1521. L, R, I: Integer;
  1522. CompareRes: PtrInt;
  1523. begin
  1524. Result := false;
  1525. Index:=-1;
  1526. if Not Sorted then
  1527. Raise EListError.Create(SErrFindNeedsSortedList);
  1528. // Use binary search.
  1529. L := 0;
  1530. R := Count - 1;
  1531. while (L<=R) do
  1532. begin
  1533. I := L + (R - L) div 2;
  1534. CompareRes := DoCompareText(S, Flist^[I].FString);
  1535. if (CompareRes>0) then
  1536. L := I+1
  1537. else begin
  1538. R := I-1;
  1539. if (CompareRes=0) then begin
  1540. Result := true;
  1541. if (Duplicates<>dupAccept) then
  1542. L := I; // forces end of while loop
  1543. end;
  1544. end;
  1545. end;
  1546. Index := L;
  1547. end;
  1548. function TStringList.IndexOf(const S: string): Integer;
  1549. begin
  1550. If Not Sorted then
  1551. Result:=Inherited indexOf(S)
  1552. else
  1553. // faster using binary search...
  1554. If Not Find (S,Result) then
  1555. Result:=-1;
  1556. end;
  1557. procedure TStringList.Insert(Index: Integer; const S: string);
  1558. begin
  1559. If SortStyle=sslAuto then
  1560. Error (SSortedListError,0)
  1561. else
  1562. begin
  1563. If (Index<0) or (Index>FCount) then
  1564. Error(SListIndexError,Index); // Cannot use CheckIndex, because there >= FCount...
  1565. InsertItem (Index,S);
  1566. end;
  1567. end;
  1568. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1569. begin
  1570. If (FForceSort or (Not (FSortStyle=sslAuto))) and (FCount>1) then
  1571. begin
  1572. Changing;
  1573. QuickSort(0,FCount-1, CompareFn);
  1574. Changed;
  1575. end;
  1576. end;
  1577. function StringListAnsiCompare(List: TStringList; Index1, Index: Integer): Integer;
  1578. begin
  1579. Result := List.DoCompareText(List.FList^[Index1].FString,
  1580. List.FList^[Index].FString);
  1581. end;
  1582. procedure TStringList.Sort;
  1583. begin
  1584. CustomSort(@StringListAnsiCompare);
  1585. end;
  1586. {$else}
  1587. { generics based implementation of TStringList follows }
  1588. function StringListAnsiCompare(List: TStringList; Index1, Index2: Integer): Integer;
  1589. begin
  1590. Result := List.DoCompareText(List.Strings[Index1], List.Strings[Index2]);
  1591. end;
  1592. constructor TStringList.Create;
  1593. begin
  1594. inherited;
  1595. FOwnsObjects:=false;
  1596. FMap := TFPStrObjMap.Create;
  1597. FMap.OnPtrCompare := @MapPtrCompare;
  1598. FOnCompareText := @DefaultCompareText;
  1599. NameValueSeparator:='=';
  1600. CheckSpecialChars;
  1601. end;
  1602. destructor TStringList.Destroy;
  1603. begin
  1604. FMap.Free;
  1605. inherited;
  1606. end;
  1607. function TStringList.GetDuplicates: TDuplicates;
  1608. begin
  1609. Result := FMap.Duplicates;
  1610. end;
  1611. function TStringList.GetSorted: boolean;
  1612. begin
  1613. Result := FMap.Sorted;
  1614. end;
  1615. procedure TStringList.SetDuplicates(NewDuplicates: TDuplicates);
  1616. begin
  1617. FMap.Duplicates := NewDuplicates;
  1618. end;
  1619. procedure TStringList.SetSorted(NewSorted: Boolean);
  1620. begin
  1621. FMap.Sorted := NewSorted;
  1622. end;
  1623. procedure TStringList.Changed;
  1624. begin
  1625. if FUpdateCount = 0 then
  1626. if Assigned(FOnChange) then
  1627. FOnChange(Self);
  1628. end;
  1629. procedure TStringList.Changing;
  1630. begin
  1631. if FUpdateCount = 0 then
  1632. if Assigned(FOnChanging) then
  1633. FOnChanging(Self);
  1634. end;
  1635. function TStringList.Get(Index: Integer): string;
  1636. begin
  1637. Result := FMap.Keys[Index];
  1638. end;
  1639. function TStringList.GetCapacity: Integer;
  1640. begin
  1641. Result := FMap.Capacity;
  1642. end;
  1643. function TStringList.GetCount: Integer;
  1644. begin
  1645. Result := FMap.Count;
  1646. end;
  1647. function TStringList.GetObject(Index: Integer): TObject;
  1648. begin
  1649. Result := FMap.Data[Index];
  1650. end;
  1651. procedure TStringList.Put(Index: Integer; const S: string);
  1652. begin
  1653. Changing;
  1654. FMap.Keys[Index] := S;
  1655. Changed;
  1656. end;
  1657. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1658. begin
  1659. Changing;
  1660. FMap.Data[Index] := AObject;
  1661. Changed;
  1662. end;
  1663. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1664. begin
  1665. FMap.Capacity := NewCapacity;
  1666. end;
  1667. procedure TStringList.SetUpdateState(Updating: Boolean);
  1668. begin
  1669. if Updating then
  1670. Changing
  1671. else
  1672. Changed
  1673. end;
  1674. function TStringList.Add(const S: string): Integer;
  1675. begin
  1676. Result := FMap.Add(S);
  1677. end;
  1678. procedure TStringList.Clear;
  1679. begin
  1680. if FMap.Count = 0 then exit;
  1681. Changing;
  1682. FMap.Clear;
  1683. Changed;
  1684. end;
  1685. procedure TStringList.Delete(Index: Integer);
  1686. begin
  1687. if (Index < 0) or (Index >= FMap.Count) then
  1688. Error(SListIndexError, Index);
  1689. Changing;
  1690. FMap.Delete(Index);
  1691. Changed;
  1692. end;
  1693. procedure TStringList.Exchange(Index1, Index2: Integer);
  1694. begin
  1695. if (Index1 < 0) or (Index1 >= FMap.Count) then
  1696. Error(SListIndexError, Index1);
  1697. if (Index2 < 0) or (Index2 >= FMap.Count) then
  1698. Error(SListIndexError, Index2);
  1699. Changing;
  1700. FMap.InternalExchange(Index1, Index2);
  1701. Changed;
  1702. end;
  1703. procedure TStringList.SetCaseSensitive(NewSensitive: Boolean);
  1704. begin
  1705. if NewSensitive <> FCaseSensitive then
  1706. begin
  1707. FCaseSensitive := NewSensitive;
  1708. if Sorted then
  1709. Sort;
  1710. end;
  1711. end;
  1712. function TStringList.MapPtrCompare(Key1, Key2: Pointer): Integer;
  1713. begin
  1714. Result := FOnCompareText(string(Key1^), string(Key2^));
  1715. end;
  1716. function TStringList.DefaultCompareText(const s1, s2: string): PtrInt;
  1717. begin
  1718. if FCaseSensitive then
  1719. Result := AnsiCompareStr(s1, s2)
  1720. else
  1721. Result := AnsiCompareText(s1, s2);
  1722. end;
  1723. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1724. begin
  1725. Result := FOnCompareText(s1, s2);
  1726. end;
  1727. function TStringList.Find(const S: string; var Index: Integer): Boolean;
  1728. begin
  1729. Result := FMap.Find(S, Index);
  1730. end;
  1731. function TStringList.IndexOf(const S: string): Integer;
  1732. begin
  1733. Result := FMap.IndexOf(S);
  1734. end;
  1735. procedure TStringList.Insert(Index: Integer; const S: string);
  1736. begin
  1737. if not Sorted and (0 <= Index) and (Index < FMap.Count) then
  1738. Changing;
  1739. FMap.InsertKey(Index, S);
  1740. Changed;
  1741. end;
  1742. procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  1743. var
  1744. I, J, Pivot: Integer;
  1745. begin
  1746. repeat
  1747. I := L;
  1748. J := R;
  1749. Pivot := (L + R) div 2;
  1750. repeat
  1751. while CompareFn(Self, I, Pivot) < 0 do Inc(I);
  1752. while CompareFn(Self, J, Pivot) > 0 do Dec(J);
  1753. if I <= J then
  1754. begin
  1755. FMap.InternalExchange(I, J); // No check, indices are correct.
  1756. if Pivot = I then
  1757. Pivot := J
  1758. else if Pivot = J then
  1759. Pivot := I;
  1760. Inc(I);
  1761. Dec(j);
  1762. end;
  1763. until I > J;
  1764. if L < J then
  1765. QuickSort(L,J, CompareFn);
  1766. L := I;
  1767. until I >= R;
  1768. end;
  1769. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1770. begin
  1771. if not Sorted and (FMap.Count > 1) then
  1772. begin
  1773. Changing;
  1774. QuickSort(0, FMap.Count-1, CompareFn);
  1775. Changed;
  1776. end;
  1777. end;
  1778. procedure TStringList.Sort;
  1779. begin
  1780. if not Sorted and (FMap.Count > 1) then
  1781. begin
  1782. Changing;
  1783. FMap.Sort;
  1784. Changed;
  1785. end;
  1786. end;
  1787. {$endif}