writer.inc 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305
  1. {%MainUnit classes.pp}
  2. {
  3. This file is part of the Free Component Library (FCL)
  4. Copyright (c) 1999-2000 by the Free Pascal development team
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {****************************************************************************}
  12. {* TBinaryObjectWriter *}
  13. {****************************************************************************}
  14. {$ifndef FPUNONE}
  15. {$IFNDEF FPC_HAS_TYPE_EXTENDED}
  16. procedure DoubleToExtended(d : double; e : pointer);
  17. var mant : qword;
  18. exp : smallint;
  19. sign : boolean;
  20. begin
  21. mant:=(qword(d) and $000FFFFFFFFFFFFF) shl 12;
  22. exp :=(qword(d) shr 52) and $7FF;
  23. sign:=(qword(d) and $8000000000000000)<>0;
  24. case exp of
  25. 0 : begin
  26. if mant<>0 then //denormalized value: hidden bit is 0. normalize it
  27. begin
  28. exp:=16383-1022;
  29. while (mant and $8000000000000000)=0 do
  30. begin
  31. dec(exp);
  32. mant:=mant shl 1;
  33. end;
  34. dec(exp); //don't shift, most significant bit is not hidden in extended
  35. end;
  36. end;
  37. 2047 : exp:=$7FFF //either infinity or NaN
  38. else
  39. begin
  40. inc(exp,16383-1023);
  41. mant:=(mant shr 1) or $8000000000000000; //unhide hidden bit
  42. end;
  43. end;
  44. if sign then exp:=exp or $8000;
  45. mant:=NtoLE(mant);
  46. exp:=NtoLE(word(exp));
  47. move(mant,pbyte(e)[0],8); //mantissa : bytes 0..7
  48. move(exp,pbyte(e)[8],2); //exponent and sign: bytes 8..9
  49. end;
  50. {$ENDIF}
  51. {$endif}
  52. procedure TBinaryObjectWriter.WriteWord(w : word); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  53. begin
  54. w:=NtoLE(w);
  55. Write(w,2);
  56. end;
  57. procedure TBinaryObjectWriter.WriteDWord(lw : longword); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  58. begin
  59. lw:=NtoLE(lw);
  60. Write(lw,4);
  61. end;
  62. procedure TBinaryObjectWriter.WriteQWord(qw : qword); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  63. begin
  64. qw:=NtoLE(qw);
  65. Write(qw,8);
  66. end;
  67. {$ifndef FPUNONE}
  68. procedure TBinaryObjectWriter.WriteExtended(e : extended); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  69. {$IFNDEF FPC_HAS_TYPE_EXTENDED}
  70. var ext : array[0..9] of byte;
  71. {$ENDIF}
  72. begin
  73. {$IFNDEF FPC_HAS_TYPE_EXTENDED}
  74. {$IFDEF FPC_DOUBLE_HILO_SWAPPED}
  75. { SwapDoubleHiLo defined in reader.inc }
  76. SwapDoubleHiLo(e);
  77. {$ENDIF FPC_DOUBLE_HILO_SWAPPED}
  78. DoubleToExtended(e,@(ext[0]));
  79. Write(ext[0],10);
  80. {$ELSE}
  81. Write(e,sizeof(e));
  82. {$ENDIF}
  83. end;
  84. {$endif}
  85. constructor TBinaryObjectWriter.Create(Stream: TStream; BufSize: Integer);
  86. begin
  87. inherited Create;
  88. If (Stream=Nil) then
  89. Raise EWriteError.Create(SEmptyStreamIllegalWriter);
  90. FStream := Stream;
  91. FBufSize := BufSize;
  92. GetMem(FBuffer, BufSize);
  93. end;
  94. destructor TBinaryObjectWriter.Destroy;
  95. begin
  96. // Flush all data which hasn't been written yet
  97. FlushBuffer;
  98. if Assigned(FBuffer) then
  99. FreeMem(FBuffer, FBufSize);
  100. inherited Destroy;
  101. end;
  102. procedure TBinaryObjectWriter.BeginCollection;
  103. begin
  104. WriteValue(vaCollection);
  105. end;
  106. procedure TBinaryObjectWriter.WriteSignature;
  107. begin
  108. if Version=TBinaryObjectReader.TBOVersion.boVersion1 then
  109. Write(FilerSignature1, SizeOf(FilerSignature1))
  110. else
  111. Write(FilerSignature, SizeOf(FilerSignature));
  112. end;
  113. procedure TBinaryObjectWriter.BeginComponent(Component: TComponent;
  114. Flags: TFilerFlags; ChildPos: Integer);
  115. var
  116. Prefix: Byte;
  117. begin
  118. { Only write the flags if they are needed! }
  119. if Flags <> [] then
  120. begin
  121. Prefix := TFilerFlagsInt(Flags) or $f0;
  122. Write(Prefix, 1);
  123. if ffChildPos in Flags then
  124. WriteInteger(ChildPos);
  125. end;
  126. if Version=TBinaryObjectReader.TBOVersion.boVersion1 then
  127. WriteString(Component.UnitName+TBinaryObjectReader.UnitnameSeparator+Component.ClassName)
  128. else
  129. WriteStr(Component.ClassName);
  130. WriteStr(Component.Name);
  131. end;
  132. procedure TBinaryObjectWriter.BeginList;
  133. begin
  134. WriteValue(vaList);
  135. end;
  136. procedure TBinaryObjectWriter.EndList;
  137. begin
  138. WriteValue(vaNull);
  139. end;
  140. procedure TBinaryObjectWriter.BeginProperty(const PropName: String);
  141. begin
  142. WriteStr(PropName);
  143. end;
  144. procedure TBinaryObjectWriter.EndProperty;
  145. begin
  146. end;
  147. procedure TBinaryObjectWriter.WriteBinary(const Buffer; Count: LongInt);
  148. begin
  149. WriteValue(vaBinary);
  150. WriteDWord(longword(Count));
  151. Write(Buffer, Count);
  152. end;
  153. procedure TBinaryObjectWriter.WriteBoolean(Value: Boolean);
  154. begin
  155. if Value then
  156. WriteValue(vaTrue)
  157. else
  158. WriteValue(vaFalse);
  159. end;
  160. {$ifndef FPUNONE}
  161. procedure TBinaryObjectWriter.WriteFloat(const Value: Extended);
  162. begin
  163. WriteValue(vaExtended);
  164. WriteExtended(Value);
  165. end;
  166. procedure TBinaryObjectWriter.WriteSingle(const Value: Single);
  167. begin
  168. WriteValue(vaSingle);
  169. WriteDWord(longword(Value));
  170. end;
  171. {$endif}
  172. procedure TBinaryObjectWriter.WriteCurrency(const Value: Currency);
  173. begin
  174. WriteValue(vaCurrency);
  175. WriteQWord(qword(Value));
  176. end;
  177. {$ifndef FPUNONE}
  178. procedure TBinaryObjectWriter.WriteDate(const Value: TDateTime);
  179. begin
  180. WriteValue(vaDate);
  181. WriteQWord(qword(Value));
  182. end;
  183. {$endif}
  184. procedure TBinaryObjectWriter.WriteIdent(const Ident: string);
  185. begin
  186. Case UpperCase(Ident) of
  187. 'NIL' : WriteValue(vaNil);
  188. 'FALSE' : WriteValue(vaFalse);
  189. 'TRUE' : WriteValue(vaTrue);
  190. 'NULL' : WriteValue(vaNull);
  191. else
  192. WriteValue(vaIdent);
  193. WriteStr(Ident);
  194. end;
  195. end;
  196. procedure TBinaryObjectWriter.WriteInteger(Value: Int64);
  197. var
  198. s: ShortInt;
  199. i: SmallInt;
  200. l: Longint;
  201. begin
  202. { Use the smallest possible integer type for the given value: }
  203. if (Value >= -128) and (Value <= 127) then
  204. begin
  205. WriteValue(vaInt8);
  206. s := Value;
  207. Write(s, 1);
  208. end else if (Value >= -32768) and (Value <= 32767) then
  209. begin
  210. WriteValue(vaInt16);
  211. i := Value;
  212. WriteWord(word(i));
  213. end else if (Value >= -$80000000) and (Value <= $7fffffff) then
  214. begin
  215. WriteValue(vaInt32);
  216. l := Value;
  217. WriteDWord(longword(l));
  218. end else
  219. begin
  220. WriteValue(vaInt64);
  221. WriteQWord(qword(Value));
  222. end;
  223. end;
  224. procedure TBinaryObjectWriter.WriteUInt64(Value: QWord);
  225. var
  226. s: ShortInt;
  227. i: SmallInt;
  228. l: Longint;
  229. begin
  230. { Use the smallest possible integer type for the given value: }
  231. if (Value <= 127) then
  232. begin
  233. WriteValue(vaInt8);
  234. s := Value;
  235. Write(s, 1);
  236. end else if (Value <= 32767) then
  237. begin
  238. WriteValue(vaInt16);
  239. i := Value;
  240. WriteWord(word(i));
  241. end else if (Value <= $7fffffff) then
  242. begin
  243. WriteValue(vaInt32);
  244. l := Value;
  245. WriteDWord(longword(l));
  246. end else
  247. begin
  248. WriteValue(vaQWord);
  249. WriteQWord(Value);
  250. end;
  251. end;
  252. procedure TBinaryObjectWriter.WriteMethodName(const Name: String);
  253. begin
  254. if Length(Name) > 0 then
  255. begin
  256. WriteValue(vaIdent);
  257. WriteStr(Name);
  258. end else
  259. WriteValue(vaNil);
  260. end;
  261. procedure TBinaryObjectWriter.WriteSet(Value: LongInt; SetType: Pointer);
  262. type
  263. tset = set of 0..31;
  264. var
  265. i: Integer;
  266. begin
  267. WriteValue(vaSet);
  268. for i := 0 to 31 do
  269. begin
  270. if (i in tset(Value)) then
  271. WriteStr(GetEnumName(PTypeInfo(SetType), i));
  272. end;
  273. WriteStr('');
  274. end;
  275. procedure TBinaryObjectWriter.WriteString(const Value: RawByteString);
  276. var
  277. i: Integer;
  278. b: byte;
  279. begin
  280. i := Length(Value);
  281. if i <= 255 then
  282. begin
  283. WriteValue(vaString);
  284. b := i;
  285. Write(b, 1);
  286. end else
  287. begin
  288. WriteValue(vaLString);
  289. WriteDWord(longword(i));
  290. end;
  291. if i > 0 then
  292. Write(Value[1], i);
  293. end;
  294. procedure TBinaryObjectWriter.WriteWideString(const Value: WideString);
  295. var len : longword;
  296. {$IFDEF ENDIAN_BIG}
  297. i : integer;
  298. ws : widestring;
  299. {$ENDIF}
  300. begin
  301. WriteValue(vaWString);
  302. len:=Length(Value);
  303. WriteDWord(len);
  304. if len > 0 then
  305. begin
  306. {$IFDEF ENDIAN_BIG}
  307. setlength(ws,len);
  308. for i:=1 to len do
  309. ws[i]:=widechar(SwapEndian(word(Value[i])));
  310. Write(ws[1], len*sizeof(widechar));
  311. {$ELSE}
  312. Write(Value[1], len*sizeof(widechar));
  313. {$ENDIF}
  314. end;
  315. end;
  316. procedure TBinaryObjectWriter.WriteUnicodeString(const Value: UnicodeString);
  317. var len : longword;
  318. {$IFDEF ENDIAN_BIG}
  319. i : integer;
  320. us : UnicodeString;
  321. {$ENDIF}
  322. begin
  323. WriteValue(vaUString);
  324. len:=Length(Value);
  325. WriteDWord(len);
  326. if len > 0 then
  327. begin
  328. {$IFDEF ENDIAN_BIG}
  329. setlength(us,len);
  330. for i:=1 to len do
  331. us[i]:=widechar(SwapEndian(word(Value[i])));
  332. Write(us[1], len*sizeof(UnicodeChar));
  333. {$ELSE}
  334. Write(Value[1], len*sizeof(UnicodeChar));
  335. {$ENDIF}
  336. end;
  337. end;
  338. procedure TBinaryObjectWriter.WriteVariant(const VarValue: Variant);
  339. begin
  340. { The variant manager will handle varbyref and vararray transparently for us
  341. }
  342. case (tvardata(VarValue).vtype and varTypeMask) of
  343. varEmpty:
  344. begin
  345. WriteValue(vaNil);
  346. end;
  347. varNull:
  348. begin
  349. WriteValue(vaNull);
  350. end;
  351. { all integer sizes must be split for big endian systems }
  352. varShortInt,varSmallInt,varInteger,varInt64:
  353. begin
  354. WriteInteger(VarValue);
  355. end;
  356. varQWord:
  357. begin
  358. WriteUInt64(VarValue);
  359. end;
  360. varBoolean:
  361. begin
  362. WriteBoolean(VarValue);
  363. end;
  364. varCurrency:
  365. begin
  366. WriteCurrency(VarValue);
  367. end;
  368. {$ifndef fpunone}
  369. varSingle:
  370. begin
  371. WriteSingle(VarValue);
  372. end;
  373. varDouble:
  374. begin
  375. WriteFloat(VarValue);
  376. end;
  377. varDate:
  378. begin
  379. WriteDate(VarValue);
  380. end;
  381. {$endif fpunone}
  382. varOleStr,varString:
  383. begin
  384. WriteWideString(VarValue);
  385. end;
  386. else
  387. raise EWriteError.CreateFmt(SUnsupportedPropertyVariantType, [Ord(tvardata(VarValue).vtype)]);
  388. end;
  389. end;
  390. procedure TBinaryObjectWriter.FlushBuffer;
  391. begin
  392. FStream.WriteBuffer(FBuffer^, FBufPos);
  393. FBufPos := 0;
  394. end;
  395. procedure TBinaryObjectWriter.Write(const Buffer; Count: Longint);
  396. var
  397. CopyNow: LongInt;
  398. SourceBuf: PAnsiChar;
  399. begin
  400. SourceBuf:=@Buffer;
  401. while Count > 0 do
  402. begin
  403. CopyNow := Count;
  404. if CopyNow > FBufSize - FBufPos then
  405. CopyNow := FBufSize - FBufPos;
  406. Move(SourceBuf^, PAnsiChar(FBuffer)[FBufPos], CopyNow);
  407. Dec(Count, CopyNow);
  408. Inc(FBufPos, CopyNow);
  409. inc(SourceBuf, CopyNow);
  410. if FBufPos = FBufSize then
  411. FlushBuffer;
  412. end;
  413. end;
  414. procedure TBinaryObjectWriter.WriteValue(Value: TValueType);
  415. var
  416. b: byte;
  417. begin
  418. b := byte(Value);
  419. Write(b, 1);
  420. end;
  421. procedure TBinaryObjectWriter.WriteStr(const Value: RawByteString);
  422. var
  423. i: integer;
  424. b: byte;
  425. begin
  426. i := Length(Value);
  427. if i > 255 then
  428. i := 255;
  429. b := i;
  430. Write(b, 1);
  431. if i > 0 then
  432. Write(Value[1], i);
  433. end;
  434. {****************************************************************************}
  435. {* TWriter *}
  436. {****************************************************************************}
  437. constructor TWriter.Create(ADriver: TAbstractObjectWriter);
  438. begin
  439. inherited Create;
  440. FDriver := ADriver;
  441. end;
  442. constructor TWriter.Create(Stream: TStream; BufSize: Integer);
  443. begin
  444. inherited Create;
  445. If (Stream=Nil) then
  446. Raise EWriteError.Create(SEmptyStreamIllegalWriter);
  447. FDriver := CreateDriver(Stream, BufSize);
  448. FDestroyDriver := True;
  449. end;
  450. destructor TWriter.Destroy;
  451. begin
  452. if FDestroyDriver then
  453. FDriver.Free;
  454. inherited Destroy;
  455. end;
  456. procedure TWriter.FlushBuffer;
  457. begin
  458. Driver.FlushBuffer;
  459. end;
  460. function TWriter.CreateDriver(Stream: TStream; BufSize: Integer): TAbstractObjectWriter;
  461. begin
  462. Result := TBinaryObjectWriter.Create(Stream, BufSize);
  463. end;
  464. Type
  465. TPosComponent = Class(TObject)
  466. FPos : Integer;
  467. FComponent : TComponent;
  468. Constructor Create(APos : Integer; AComponent : TComponent);
  469. end;
  470. Constructor TPosComponent.Create(APos : Integer; AComponent : TComponent);
  471. begin
  472. FPos:=APos;
  473. FComponent:=AComponent;
  474. end;
  475. // Used as argument for calls to TComponent.GetChildren:
  476. procedure TWriter.AddToAncestorList(Component: TComponent);
  477. begin
  478. FAncestors.AddObject(Component.Name,TPosComponent.Create(FAncestors.Count,Component));
  479. end;
  480. procedure TWriter.DefineProperty(const Name: string; ReadData: TReaderProc;
  481. AWriteData: TWriterProc; HasData: Boolean);
  482. begin
  483. if HasData and Assigned(AWriteData) then
  484. begin
  485. // Write the property name and then the data itself
  486. Driver.BeginProperty(FPropPath + Name);
  487. AWriteData(Self);
  488. Driver.EndProperty;
  489. end;
  490. end;
  491. procedure TWriter.DefineBinaryProperty(const Name: string; ReadData,
  492. AWriteData: TStreamProc; HasData: Boolean);
  493. begin
  494. if HasData and Assigned(AWriteData) then
  495. begin
  496. // Write the property name and then the data itself
  497. Driver.BeginProperty(FPropPath + Name);
  498. WriteBinary(AWriteData);
  499. Driver.EndProperty;
  500. end;
  501. end;
  502. procedure TWriter.Write(const Buffer; Count: Longint);
  503. begin
  504. //This should give an exception if write is not implemented (i.e. TTextObjectWriter)
  505. //but should work with TBinaryObjectWriter.
  506. Driver.Write(Buffer, Count);
  507. end;
  508. procedure TWriter.SetRoot(ARoot: TComponent);
  509. begin
  510. inherited SetRoot(ARoot);
  511. // Use the new root as lookup root too
  512. FLookupRoot := ARoot;
  513. end;
  514. procedure TWriter.WriteSignature;
  515. begin
  516. FDriver.WriteSignature;
  517. end;
  518. procedure TWriter.WriteBinary(AWriteData: TStreamProc);
  519. var
  520. MemBuffer: TMemoryStream;
  521. BufferSize: Longint;
  522. begin
  523. { First write the binary data into a memory stream, then copy this buffered
  524. stream into the writing destination. This is necessary as we have to know
  525. the size of the binary data in advance (we're assuming that seeking within
  526. the writer stream is not possible) }
  527. MemBuffer := TMemoryStream.Create;
  528. try
  529. AWriteData(MemBuffer);
  530. BufferSize := MemBuffer.Size;
  531. Driver.WriteBinary(MemBuffer.Memory^, BufferSize);
  532. finally
  533. MemBuffer.Free;
  534. end;
  535. end;
  536. procedure TWriter.WriteBoolean(Value: Boolean);
  537. begin
  538. Driver.WriteBoolean(Value);
  539. end;
  540. procedure TWriter.WriteChar(Value: AnsiChar);
  541. begin
  542. WriteString(Value);
  543. end;
  544. procedure TWriter.WriteWideChar(Value: WideChar);
  545. begin
  546. WriteWideString(Value);
  547. end;
  548. procedure TWriter.WriteCollection(Value: TCollection);
  549. var
  550. i: Integer;
  551. begin
  552. Driver.BeginCollection;
  553. if Assigned(Value) then
  554. for i := 0 to Value.Count - 1 do
  555. begin
  556. { Each collection item needs its own ListBegin/ListEnd tag, or else the
  557. reader wouldn't be able to know where an item ends and where the next
  558. one starts }
  559. WriteListBegin;
  560. WriteProperties(Value.Items[i]);
  561. WriteListEnd;
  562. end;
  563. WriteListEnd;
  564. end;
  565. procedure TWriter.DetermineAncestor(Component : TComponent);
  566. Var
  567. I : Integer;
  568. begin
  569. // Should be set only when we write an inherited with children.
  570. if Not Assigned(FAncestors) then
  571. exit;
  572. I:=FAncestors.IndexOf(Component.Name);
  573. If (I=-1) then
  574. begin
  575. FAncestor:=Nil;
  576. FAncestorPos:=-1;
  577. end
  578. else
  579. With TPosComponent(FAncestors.Objects[i]) do
  580. begin
  581. FAncestor:=FComponent;
  582. FAncestorPos:=FPos;
  583. end;
  584. end;
  585. procedure TWriter.DoFindAncestor(Component : TComponent);
  586. Var
  587. C : TComponent;
  588. begin
  589. if Assigned(FOnFindAncestor) then
  590. if (Ancestor=Nil) or (Ancestor is TComponent) then
  591. begin
  592. C:=TComponent(Ancestor);
  593. FOnFindAncestor(Self,Component,Component.Name,C,FRootAncestor);
  594. Ancestor:=C;
  595. end;
  596. end;
  597. procedure TWriter.WriteComponent(Component: TComponent);
  598. var
  599. SA : TPersistent;
  600. SR, SRA : TComponent;
  601. begin
  602. SR:=FRoot;
  603. SA:=FAncestor;
  604. SRA:=FRootAncestor;
  605. Try
  606. Component.FComponentState:=Component.FComponentState+[csWriting];
  607. Try
  608. // Possibly set ancestor.
  609. DetermineAncestor(Component);
  610. DoFindAncestor(Component); // Mainly for IDE when a parent form had an ancestor renamed...
  611. // Will call WriteComponentData.
  612. Component.WriteState(Self);
  613. FDriver.EndList;
  614. Finally
  615. Component.FComponentState:=Component.FComponentState-[csWriting];
  616. end;
  617. Finally
  618. FAncestor:=SA;
  619. FRoot:=SR;
  620. FRootAncestor:=SRA;
  621. end;
  622. end;
  623. procedure TWriter.WriteChildren(Component : TComponent);
  624. Var
  625. SRoot, SRootA : TComponent;
  626. SList : TStringList;
  627. SPos, I , SAncestorPos: Integer;
  628. begin
  629. // Write children list.
  630. // While writing children, the ancestor environment must be saved
  631. // This is recursive...
  632. SRoot:=FRoot;
  633. SRootA:=FRootAncestor;
  634. SList:=FAncestors;
  635. SPos:=FCurrentPos;
  636. SAncestorPos:=FAncestorPos;
  637. try
  638. FAncestors:=Nil;
  639. FCurrentPos:=0;
  640. FAncestorPos:=-1;
  641. if csInline in Component.ComponentState then
  642. FRoot:=Component;
  643. if (FAncestor is TComponent) then
  644. begin
  645. FAncestors:=TStringList.Create;
  646. if csInline in TComponent(FAncestor).ComponentState then
  647. FRootAncestor := TComponent(FAncestor);
  648. TComponent(FAncestor).GetChildren(@AddToAncestorList,FRootAncestor);
  649. FAncestors.Sorted:=True;
  650. end;
  651. try
  652. Component.GetChildren(@WriteComponent, FRoot);
  653. Finally
  654. If Assigned(Fancestors) then
  655. For I:=0 to FAncestors.Count-1 do
  656. FAncestors.Objects[i].Free;
  657. FreeAndNil(FAncestors);
  658. end;
  659. finally
  660. FAncestors:=Slist;
  661. FRoot:=SRoot;
  662. FRootAncestor:=SRootA;
  663. FCurrentPos:=SPos;
  664. FAncestorPos:=SAncestorPos;
  665. end;
  666. end;
  667. procedure TWriter.WriteComponentData(Instance: TComponent);
  668. var
  669. Flags: TFilerFlags;
  670. begin
  671. Flags := [];
  672. If (Assigned(FAncestor)) and //has ancestor
  673. (not (csInline in Instance.ComponentState) or // no inline component
  674. // .. or the inline component is inherited
  675. (csAncestor in Instance.Componentstate) and (FAncestors <> nil)) then
  676. Flags:=[ffInherited]
  677. else If csInline in Instance.ComponentState then
  678. Flags:=[ffInline];
  679. If (FAncestors<>Nil) and ((FCurrentPos<>FAncestorPos) or (FAncestor=Nil)) then
  680. Include(Flags,ffChildPos);
  681. FDriver.BeginComponent(Instance,Flags,FCurrentPos);
  682. If (FAncestors<>Nil) then
  683. Inc(FCurrentPos);
  684. WriteProperties(Instance);
  685. WriteListEnd;
  686. // Needs special handling of ancestor.
  687. If not IgnoreChildren then
  688. WriteChildren(Instance);
  689. end;
  690. procedure TWriter.WriteDescendent(ARoot: TComponent; AAncestor: TComponent);
  691. begin
  692. FRoot := ARoot;
  693. FAncestor := AAncestor;
  694. FRootAncestor := AAncestor;
  695. FLookupRoot := ARoot;
  696. WriteSignature;
  697. WriteComponent(ARoot);
  698. end;
  699. {$ifndef FPUNONE}
  700. procedure TWriter.WriteFloat(const Value: Extended);
  701. begin
  702. Driver.WriteFloat(Value);
  703. end;
  704. procedure TWriter.WriteSingle(const Value: Single);
  705. begin
  706. Driver.WriteSingle(Value);
  707. end;
  708. {$endif}
  709. procedure TWriter.WriteCurrency(const Value: Currency);
  710. begin
  711. Driver.WriteCurrency(Value);
  712. end;
  713. {$ifndef FPUNONE}
  714. procedure TWriter.WriteDate(const Value: TDateTime);
  715. begin
  716. Driver.WriteDate(Value);
  717. end;
  718. {$endif}
  719. procedure TWriter.WriteIdent(const Ident: string);
  720. begin
  721. Driver.WriteIdent(Ident);
  722. end;
  723. procedure TWriter.WriteInteger(Value: Longint);
  724. begin
  725. Driver.WriteInteger(Value);
  726. end;
  727. procedure TWriter.WriteInteger(Value: Int64);
  728. begin
  729. Driver.WriteInteger(Value);
  730. end;
  731. procedure TWriter.WriteSet(Value: Longint; SetType: Pointer);
  732. begin
  733. Driver.WriteSet(Value,SetType);
  734. end;
  735. procedure TWriter.WriteVariant(const VarValue: Variant);
  736. begin
  737. Driver.WriteVariant(VarValue);
  738. end;
  739. procedure TWriter.WriteListBegin;
  740. begin
  741. Driver.BeginList;
  742. end;
  743. procedure TWriter.WriteListEnd;
  744. begin
  745. Driver.EndList;
  746. end;
  747. procedure TWriter.WriteProperties(Instance: TPersistent);
  748. var PropCount,i : integer;
  749. PropList : PPropList;
  750. begin
  751. PropCount:=GetPropList(Instance,PropList);
  752. if PropCount>0 then
  753. try
  754. for i := 0 to PropCount-1 do
  755. if IsStoredProp(Instance,PropList^[i]) then
  756. WriteProperty(Instance,PropList^[i]);
  757. Finally
  758. Freemem(PropList);
  759. end;
  760. Instance.DefineProperties(Self);
  761. end;
  762. procedure TWriter.WriteProperty(Instance: TPersistent; PropInfo: Pointer);
  763. var
  764. HasAncestor: Boolean;
  765. PropType: PTypeInfo;
  766. Value, DefValue: LongInt;
  767. Ident: String;
  768. IntToIdentFn: TIntToIdent;
  769. {$ifndef FPUNONE}
  770. FloatValue, DefFloatValue: Extended;
  771. {$endif}
  772. MethodValue: TMethod;
  773. DefMethodValue: TMethod;
  774. WStrValue, WDefStrValue: WideString;
  775. StrValue, DefStrValue: String;
  776. UStrValue, UDefStrValue: UnicodeString;
  777. AncestorObj: TObject;
  778. C,Component: TComponent;
  779. ObjValue: TObject;
  780. SavedAncestor: TPersistent;
  781. SavedPropPath, Name: String;
  782. Int64Value, DefInt64Value: Int64;
  783. VarValue, DefVarValue : tvardata;
  784. BoolValue, DefBoolValue: boolean;
  785. Handled: Boolean;
  786. IntfValue: IInterface;
  787. CompRef: IInterfaceComponentReference;
  788. begin
  789. // do not stream properties without getter
  790. if not Assigned(PPropInfo(PropInfo)^.GetProc) then
  791. exit;
  792. // properties without setter are only allowed, if they are subcomponents
  793. PropType := PPropInfo(PropInfo)^.PropType;
  794. if not Assigned(PPropInfo(PropInfo)^.SetProc) then begin
  795. if PropType^.Kind<>tkClass then
  796. exit;
  797. ObjValue := TObject(GetObjectProp(Instance, PropInfo));
  798. if not (assigned(ObjValue) and
  799. ObjValue.InheritsFrom(TComponent) and
  800. (csSubComponent in TComponent(ObjValue).ComponentStyle)) then
  801. exit;
  802. end;
  803. { Check if the ancestor can be used }
  804. HasAncestor := Assigned(Ancestor) and ((Instance = Root) or
  805. (Instance.ClassType = Ancestor.ClassType));
  806. //writeln('TWriter.WriteProperty Name=',PropType^.Name,' Kind=',GetEnumName(TypeInfo(TTypeKind),ord(PropType^.Kind)),' HasAncestor=',HasAncestor);
  807. case PropType^.Kind of
  808. tkInteger, tkChar, tkEnumeration, tkSet, tkWChar:
  809. begin
  810. Value := GetOrdProp(Instance, PropInfo);
  811. if HasAncestor then
  812. DefValue := GetOrdProp(Ancestor, PropInfo)
  813. else
  814. DefValue := PPropInfo(PropInfo)^.Default;
  815. // writeln(PPropInfo(PropInfo)^.Name, ', HasAncestor=', ord(HasAncestor), ', Value=', Value, ', Default=', DefValue);
  816. if (Value <> DefValue) or (DefValue=longint($80000000)) then
  817. begin
  818. Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
  819. case PropType^.Kind of
  820. tkInteger:
  821. begin
  822. // Check if this integer has a string identifier
  823. IntToIdentFn := FindIntToIdent(PPropInfo(PropInfo)^.PropType);
  824. if Assigned(IntToIdentFn) and IntToIdentFn(Value, Ident) then
  825. // Integer can be written a human-readable identifier
  826. WriteIdent(Ident)
  827. else
  828. // Integer has to be written just as number
  829. WriteInteger(Value);
  830. end;
  831. tkChar:
  832. WriteChar(Chr(Value));
  833. tkWChar:
  834. WriteWideChar(WideChar(Value));
  835. tkSet:
  836. Driver.WriteSet(Value, GetTypeData(PropType)^.CompType);
  837. tkEnumeration:
  838. WriteIdent(GetEnumName(PropType, Value));
  839. end;
  840. Driver.EndProperty;
  841. end;
  842. end;
  843. {$ifndef FPUNONE}
  844. tkFloat:
  845. begin
  846. FloatValue := GetFloatProp(Instance, PropInfo);
  847. if HasAncestor then
  848. DefFloatValue := GetFloatProp(Ancestor, PropInfo)
  849. else
  850. begin
  851. DefValue :=PPropInfo(PropInfo)^.Default;
  852. DefFloatValue:=PSingle(@PPropInfo(PropInfo)^.Default)^;
  853. end;
  854. if (FloatValue<>DefFloatValue) or (not HasAncestor and (DefValue=longint($80000000))) then
  855. begin
  856. Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
  857. WriteFloat(FloatValue);
  858. Driver.EndProperty;
  859. end;
  860. end;
  861. {$endif}
  862. tkMethod:
  863. begin
  864. MethodValue := GetMethodProp(Instance, PropInfo);
  865. if HasAncestor then
  866. DefMethodValue := GetMethodProp(Ancestor, PropInfo)
  867. else begin
  868. DefMethodValue.Data := nil;
  869. DefMethodValue.Code := nil;
  870. end;
  871. Handled:=false;
  872. if Assigned(OnWriteMethodProperty) then
  873. OnWriteMethodProperty(Self,Instance,PPropInfo(PropInfo),MethodValue,
  874. DefMethodValue,Handled);
  875. if (not Handled) and
  876. (MethodValue.Code <> DefMethodValue.Code) and
  877. ((not Assigned(MethodValue.Code)) or
  878. ((Length(FLookupRoot.MethodName(MethodValue.Code)) > 0))) then
  879. begin
  880. Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
  881. if Assigned(MethodValue.Code) then
  882. Driver.WriteMethodName(FLookupRoot.MethodName(MethodValue.Code))
  883. else
  884. Driver.WriteMethodName('');
  885. Driver.EndProperty;
  886. end;
  887. end;
  888. tkSString, tkLString, tkAString:
  889. begin
  890. StrValue := GetStrProp(Instance, PropInfo);
  891. if HasAncestor then
  892. DefStrValue := GetStrProp(Ancestor, PropInfo)
  893. else
  894. begin
  895. DefValue :=PPropInfo(PropInfo)^.Default;
  896. SetLength(DefStrValue, 0);
  897. end;
  898. if (StrValue<>DefStrValue) or (not HasAncestor and (DefValue=longint($80000000))) then
  899. begin
  900. Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
  901. if Assigned(FOnWriteStringProperty) then
  902. FOnWriteStringProperty(Self,Instance,PropInfo,StrValue);
  903. WriteString(StrValue);
  904. Driver.EndProperty;
  905. end;
  906. end;
  907. tkWString:
  908. begin
  909. WStrValue := GetWideStrProp(Instance, PropInfo);
  910. if HasAncestor then
  911. WDefStrValue := GetWideStrProp(Ancestor, PropInfo)
  912. else
  913. begin
  914. DefValue :=PPropInfo(PropInfo)^.Default;
  915. SetLength(WDefStrValue, 0);
  916. end;
  917. if (WStrValue<>WDefStrValue) or (not HasAncestor and (DefValue=longint($80000000))) then
  918. begin
  919. Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
  920. WriteWideString(WStrValue);
  921. Driver.EndProperty;
  922. end;
  923. end;
  924. tkUString:
  925. begin
  926. UStrValue := GetUnicodeStrProp(Instance, PropInfo);
  927. if HasAncestor then
  928. UDefStrValue := GetUnicodeStrProp(Ancestor, PropInfo)
  929. else
  930. begin
  931. DefValue :=PPropInfo(PropInfo)^.Default;
  932. SetLength(UDefStrValue, 0);
  933. end;
  934. if (UStrValue<>UDefStrValue) or (not HasAncestor and (DefValue=longint($80000000))) then
  935. begin
  936. Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
  937. WriteUnicodeString(UStrValue);
  938. Driver.EndProperty;
  939. end;
  940. end;
  941. tkVariant:
  942. begin
  943. { Ensure that a Variant manager is installed }
  944. if not assigned(VarClearProc) then
  945. raise EWriteError.Create(SErrNoVariantSupport);
  946. VarValue := tvardata(GetVariantProp(Instance, PropInfo));
  947. if HasAncestor then
  948. DefVarValue := tvardata(GetVariantProp(Ancestor, PropInfo))
  949. else
  950. FillChar(DefVarValue,sizeof(DefVarValue),0);
  951. if (CompareByte(VarValue,DefVarValue,sizeof(VarValue)) <> 0) then
  952. begin
  953. Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
  954. { can't use variant() typecast, pulls in variants unit }
  955. WriteVariant(pvariant(@VarValue)^);
  956. Driver.EndProperty;
  957. end;
  958. end;
  959. tkClass:
  960. begin
  961. ObjValue := TObject(GetObjectProp(Instance, PropInfo));
  962. if HasAncestor then
  963. begin
  964. AncestorObj := TObject(GetObjectProp(Ancestor, PropInfo));
  965. if (AncestorObj is TComponent) and
  966. (ObjValue is TComponent) then
  967. begin
  968. //writeln('TWriter.WriteProperty AncestorObj=',TComponent(AncestorObj).Name,' OwnerFit=',TComponent(AncestorObj).Owner = FRootAncestor,' ',TComponent(ObjValue).Name,' OwnerFit=',TComponent(ObjValue).Owner = Root);
  969. if (AncestorObj<> ObjValue) and
  970. (TComponent(AncestorObj).Owner = FRootAncestor) and
  971. (TComponent(ObjValue).Owner = Root) and
  972. (UpperCase(TComponent(AncestorObj).Name) = UpperCase(TComponent(ObjValue).Name)) then
  973. begin
  974. // different components, but with the same name
  975. // treat it like an override
  976. AncestorObj := ObjValue;
  977. end;
  978. end;
  979. end else
  980. AncestorObj := nil;
  981. if not Assigned(ObjValue) then
  982. begin
  983. if ObjValue <> AncestorObj then
  984. begin
  985. Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
  986. Driver.WriteIdent('NIL');
  987. Driver.EndProperty;
  988. end
  989. end
  990. else if ObjValue.InheritsFrom(TPersistent) then
  991. begin
  992. { Subcomponents are streamed the same way as persistents }
  993. if ObjValue.InheritsFrom(TComponent)
  994. and ((not (csSubComponent in TComponent(ObjValue).ComponentStyle))
  995. or ((TComponent(ObjValue).Owner<>Instance) and (TComponent(ObjValue).Owner<>Nil))) then
  996. begin
  997. Component := TComponent(ObjValue);
  998. if (ObjValue <> AncestorObj)
  999. and not (csTransient in Component.ComponentStyle) then
  1000. begin
  1001. Name:= '';
  1002. C:= Component;
  1003. While (C<>Nil) and (C.Name<>'') do
  1004. begin
  1005. If (Name<>'') Then
  1006. Name:='.'+Name;
  1007. if C.Owner = LookupRoot then
  1008. begin
  1009. Name := C.Name+Name;
  1010. break;
  1011. end
  1012. else if C = LookupRoot then
  1013. begin
  1014. Name := 'Owner' + Name;
  1015. break;
  1016. end;
  1017. Name:=C.Name + Name;
  1018. C:= C.Owner;
  1019. end;
  1020. if (C=nil) and (Component.Owner=nil) then
  1021. if (Name<>'') then //foreign root
  1022. Name:=Name+'.Owner';
  1023. if Length(Name) > 0 then
  1024. begin
  1025. Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
  1026. WriteIdent(Name);
  1027. Driver.EndProperty;
  1028. end; // length Name>0
  1029. end; //(ObjValue <> AncestorObj)
  1030. end // ObjValue.InheritsFrom(TComponent)
  1031. else
  1032. begin
  1033. SavedAncestor := Ancestor;
  1034. SavedPropPath := FPropPath;
  1035. try
  1036. FPropPath := FPropPath + PPropInfo(PropInfo)^.Name + '.';
  1037. if HasAncestor then
  1038. Ancestor := TPersistent(GetObjectProp(Ancestor, PropInfo));
  1039. WriteProperties(TPersistent(ObjValue));
  1040. finally
  1041. Ancestor := SavedAncestor;
  1042. FPropPath := SavedPropPath;
  1043. end;
  1044. if ObjValue.InheritsFrom(TCollection) then
  1045. begin
  1046. if (not HasAncestor) or (not CollectionsEqual(TCollection(ObjValue),
  1047. TCollection(GetObjectProp(Ancestor, PropInfo)),root,rootancestor)) then
  1048. begin
  1049. Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
  1050. SavedPropPath := FPropPath;
  1051. try
  1052. SetLength(FPropPath, 0);
  1053. WriteCollection(TCollection(ObjValue));
  1054. finally
  1055. FPropPath := SavedPropPath;
  1056. Driver.EndProperty;
  1057. end;
  1058. end;
  1059. end // Tcollection
  1060. end;
  1061. end; // Inheritsfrom(TPersistent)
  1062. end;
  1063. tkInt64, tkQWord:
  1064. begin
  1065. Int64Value := GetInt64Prop(Instance, PropInfo);
  1066. if HasAncestor then
  1067. DefInt64Value := GetInt64Prop(Ancestor, PropInfo)
  1068. else
  1069. DefInt64Value := PPropInfo(PropInfo)^.Default;
  1070. if (Int64Value <> DefInt64Value) or (DefInt64Value=longint($80000000)) then
  1071. begin
  1072. Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
  1073. WriteInteger(Int64Value);
  1074. Driver.EndProperty;
  1075. end;
  1076. end;
  1077. tkBool:
  1078. begin
  1079. BoolValue := GetOrdProp(Instance, PropInfo)<>0;
  1080. if HasAncestor then
  1081. DefBoolValue := GetOrdProp(Ancestor, PropInfo)<>0
  1082. else
  1083. begin
  1084. DefBoolValue := PPropInfo(PropInfo)^.Default<>0;
  1085. DefValue:=PPropInfo(PropInfo)^.Default;
  1086. end;
  1087. // writeln(PPropInfo(PropInfo)^.Name, ', HasAncestor=', ord(HasAncestor), ', Value=', Value, ', Default=', DefBoolValue);
  1088. if (BoolValue<>DefBoolValue) or (DefValue=longint($80000000)) then
  1089. begin
  1090. Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
  1091. WriteBoolean(BoolValue);
  1092. Driver.EndProperty;
  1093. end;
  1094. end;
  1095. tkInterface:
  1096. begin
  1097. IntfValue := GetInterfaceProp(Instance, PropInfo);
  1098. if Assigned(IntfValue) and Supports(IntfValue, IInterfaceComponentReference, CompRef) then
  1099. begin
  1100. Component := CompRef.GetComponent;
  1101. if HasAncestor then
  1102. begin
  1103. AncestorObj := TObject(GetObjectProp(Ancestor, PropInfo));
  1104. if (AncestorObj is TComponent) then
  1105. begin
  1106. //writeln('TWriter.WriteProperty AncestorObj=',TComponent(AncestorObj).Name,' OwnerFit=',TComponent(AncestorObj).Owner = FRootAncestor,' ',TComponent(ObjValue).Name,' OwnerFit=',TComponent(ObjValue).Owner = Root);
  1107. if (AncestorObj<> Component) and
  1108. (TComponent(AncestorObj).Owner = FRootAncestor) and
  1109. (Component.Owner = Root) and
  1110. (UpperCase(TComponent(AncestorObj).Name) = UpperCase(Component.Name)) then
  1111. begin
  1112. // different components, but with the same name
  1113. // treat it like an override
  1114. AncestorObj := Component;
  1115. end;
  1116. end;
  1117. end else
  1118. AncestorObj := nil;
  1119. if not Assigned(Component) then
  1120. begin
  1121. if Component <> AncestorObj then
  1122. begin
  1123. Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
  1124. Driver.WriteIdent('NIL');
  1125. Driver.EndProperty;
  1126. end
  1127. end
  1128. else if ((not (csSubComponent in Component.ComponentStyle))
  1129. or ((Component.Owner<>Instance) and (Component.Owner<>Nil))) then
  1130. begin
  1131. if (Component <> AncestorObj)
  1132. and not (csTransient in Component.ComponentStyle) then
  1133. begin
  1134. Name:= '';
  1135. C:= Component;
  1136. While (C<>Nil) and (C.Name<>'') do
  1137. begin
  1138. If (Name<>'') Then
  1139. Name:='.'+Name;
  1140. if C.Owner = LookupRoot then
  1141. begin
  1142. Name := C.Name+Name;
  1143. break;
  1144. end
  1145. else if C = LookupRoot then
  1146. begin
  1147. Name := 'Owner' + Name;
  1148. break;
  1149. end;
  1150. Name:=C.Name + Name;
  1151. C:= C.Owner;
  1152. end;
  1153. if (C=nil) and (Component.Owner=nil) then
  1154. if (Name<>'') then //foreign root
  1155. Name:=Name+'.Owner';
  1156. if Length(Name) > 0 then
  1157. begin
  1158. Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
  1159. WriteIdent(Name);
  1160. Driver.EndProperty;
  1161. end; // length Name>0
  1162. end; //(Component <> AncestorObj)
  1163. end;
  1164. end; //Assigned(IntfValue) and Supports(IntfValue,..
  1165. //else write NIL ?
  1166. end;
  1167. end;
  1168. end;
  1169. procedure TWriter.WriteRootComponent(ARoot: TComponent);
  1170. begin
  1171. WriteDescendent(ARoot, nil);
  1172. end;
  1173. procedure TWriter.WriteString(const Value: String);
  1174. begin
  1175. Driver.WriteString(Value);
  1176. end;
  1177. procedure TWriter.WriteWideString(const Value: WideString);
  1178. begin
  1179. Driver.WriteWideString(Value);
  1180. end;
  1181. procedure TWriter.WriteUnicodeString(const Value: UnicodeString);
  1182. begin
  1183. Driver.WriteUnicodeString(Value);
  1184. end;
  1185. { TAbstractObjectWriter }
  1186. procedure TAbstractObjectWriter.FlushBuffer;
  1187. begin
  1188. // Do nothing
  1189. end;