stringl.inc 45 KB

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