stringl.inc 45 KB

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