fpc/rtl/objpas/classes/reader.inc
michael 93400f276c Merged revisions 9263-10571 via svnmerge from
svn+ssh://svn.freepascal.org/FPC/svn/fpc/branches/cleanroom

................
  r9269 | michael | 2007-11-17 13:58:31 +0100 (Sat, 17 Nov 2007) | 1 line
  
  * Cleaned initial list of tained routines
................
  r9270 | michael | 2007-11-17 14:00:25 +0100 (Sat, 17 Nov 2007) | 1 line
  
  * Test routines for cleanroom implementation
................
  r9271 | michael | 2007-11-17 14:04:43 +0100 (Sat, 17 Nov 2007) | 1 line
  
  DoVarClearArray also tainted
................
  r9272 | michael | 2007-11-17 15:25:04 +0100 (Sat, 17 Nov 2007) | 1 line
  
  * Removed possibly tainted code
................
  r9276 | Almindor | 2007-11-17 21:29:16 +0100 (Sat, 17 Nov 2007) | 2 lines
  
  * initial cleanroom implementation of TStringList.Find
................
  r9277 | Almindor | 2007-11-17 21:32:44 +0100 (Sat, 17 Nov 2007) | 2 lines
  
  * also commit forgotten part for "where would it instert" in case of sorted stringlist
................
  r9295 | michael | 2007-11-19 21:07:10 +0100 (Mon, 19 Nov 2007) | 1 line
  
  * More tests
................
  r9307 | michael | 2007-11-21 08:43:56 +0100 (Wed, 21 Nov 2007) | 1 line
  
  * More tests and reorganization per unit
................
  r9308 | michael | 2007-11-21 08:47:58 +0100 (Wed, 21 Nov 2007) | 1 line
  
  * More reorganization of files
................
  r9310 | michael | 2007-11-21 21:05:40 +0100 (Wed, 21 Nov 2007) | 1 line
  
  * Completed tccollection tests
................
  r9322 | marco | 2007-11-24 15:40:18 +0100 (Sat, 24 Nov 2007) | 1 line
  
   * getnamepath first version. Tests not run yet (fpcunit)
................
  r9337 | michael | 2007-11-27 09:21:31 +0100 (Tue, 27 Nov 2007) | 1 line
  
  * Removed TFPlist.Assign and TFPList.Extract
................
  r9340 | michael | 2007-11-27 22:33:07 +0100 (Tue, 27 Nov 2007) | 1 line
  
  Removed HandleSafeCallException 
................
  r9343 | Almindor | 2007-11-28 11:23:00 +0100 (Wed, 28 Nov 2007) | 2 lines
  
  * add cleanroom quicksort implementation [tested very little]
................
  r9344 | Almindor | 2007-11-28 11:25:54 +0100 (Wed, 28 Nov 2007) | 2 lines
  
  * update quicksort to use ExchangeItems instead of manual swap
................
  r9359 | vincents | 2007-11-30 20:10:03 +0100 (Fri, 30 Nov 2007) | 1 line
  
  + clean room implementation of HandleSafeCallException; compiles, but not tested.
................
  r9387 | michael | 2007-12-03 14:24:32 +0100 (Mon, 03 Dec 2007) | 1 line
  
  * Clean-room implementation of TParser by Giulio Bernardi
................
  r9396 | michael | 2007-12-05 21:36:41 +0100 (Wed, 05 Dec 2007) | 5 lines
  
  * Patch from Giulio Bernardi:
   - Fixes token positioning after HexToBinary 
   - Support for certain malformed negative integer values
................
  r9399 | michael | 2007-12-06 16:53:41 +0100 (Thu, 06 Dec 2007) | 1 line
  
  * More tests for classes unit
................
  r9401 | michael | 2007-12-06 21:58:16 +0100 (Thu, 06 Dec 2007) | 1 line
  
  * Added additional tests for collection streaming. Restructured
................
  r9402 | michael | 2007-12-06 22:35:56 +0100 (Thu, 06 Dec 2007) | 1 line
  
  * All compiles again, resolving references not quite yet done
................
  r9434 | michael | 2007-12-12 21:24:57 +0100 (Wed, 12 Dec 2007) | 1 line
  
  * New FindNestedComponent routine
................
  r9466 | michael | 2007-12-15 23:44:41 +0100 (Sat, 15 Dec 2007) | 1 line
  
  * Fixed all tests
................
  r9468 | michael | 2007-12-16 01:00:01 +0100 (Sun, 16 Dec 2007) | 1 line
  
  * Fixed reader fixup of references
................
  r9491 | joost | 2007-12-18 21:46:54 +0100 (Tue, 18 Dec 2007) | 3 lines
  
   * Implemented TWriter.WriteComponent
   * Implemented TWriter.WriteComponentData
   * Implemented TWriter.WriteDescendent
................
  r9492 | joost | 2007-12-18 21:56:32 +0100 (Tue, 18 Dec 2007) | 1 line
  
   * The BinaryObjectWriter of fpc stores TValueTypes as a byte, fixed the test for that
................
  r9566 | michael | 2007-12-29 15:53:32 +0100 (Sat, 29 Dec 2007) | 1 line
  
  * Clean (and complete) implementation of T(FP)List.Assign
................
  r9567 | michael | 2007-12-29 16:02:19 +0100 (Sat, 29 Dec 2007) | 1 line
  
  * Additional tests for reference resolving and TList.Assign
................
  r9568 | michael | 2007-12-29 16:12:33 +0100 (Sat, 29 Dec 2007) | 1 line
  
  * Cleanroom implementation of extract
................
  r9750 | yury | 2008-01-14 13:07:17 +0100 (Mon, 14 Jan 2008) | 1 line
  
  * My cleanroom implementation of DoVarClearArray.
................
  r10271 | michael | 2008-02-10 15:52:37 +0100 (Sun, 10 Feb 2008) | 1 line
  
  * Correct implementation committed
................
  r10273 | michael | 2008-02-10 17:08:59 +0100 (Sun, 10 Feb 2008) | 1 line
  
  * Added DecodeSoundexInt
................
  r10352 | vincents | 2008-02-18 08:23:18 +0100 (Mon, 18 Feb 2008) | 1 line
  
  + TStringList.Grow, used algorithm from TFPList.Expand
................
  r10353 | vincents | 2008-02-18 10:21:58 +0100 (Mon, 18 Feb 2008) | 1 line
  
  * use new TStringList.Grow implementation from trunk
................
  r10354 | vincents | 2008-02-18 10:23:07 +0100 (Mon, 18 Feb 2008) | 1 line
  
  * fixed TList tests
................
  r10355 | vincents | 2008-02-18 16:43:35 +0100 (Mon, 18 Feb 2008) | 1 line
  
  * fixed hint in test and removed session information from lpi
................
  r10356 | vincents | 2008-02-18 21:58:29 +0100 (Mon, 18 Feb 2008) | 1 line
  
  + implemented TStringList.Find
................
  r10358 | vincents | 2008-02-19 15:02:17 +0100 (Tue, 19 Feb 2008) | 1 line
  
  * fixed TTestTComponentNotifies test
................
  r10359 | vincents | 2008-02-19 15:48:43 +0100 (Tue, 19 Feb 2008) | 1 line
  
  * fixed memleak in TWriter.WriteProperties
................
  r10360 | vincents | 2008-02-19 15:49:20 +0100 (Tue, 19 Feb 2008) | 1 line
  
  + initial implementation of TReader.ReadCollection (needs further testing)
................
  r10364 | vincents | 2008-02-19 23:05:49 +0100 (Tue, 19 Feb 2008) | 1 line
  
  + TDataset.SetFieldValues (untested)
................
  r10365 | vincents | 2008-02-20 09:03:16 +0100 (Wed, 20 Feb 2008) | 1 line
  
  * initilize critical section used by resolving references
................
  r10366 | vincents | 2008-02-20 09:38:03 +0100 (Wed, 20 Feb 2008) | 2 lines
  
  * fixed resolve references test
  * removed unused variable
................
  r10369 | vincents | 2008-02-20 17:04:51 +0100 (Wed, 20 Feb 2008) | 1 line
  
  + initial version of TReader.FindComponentClass, works with a simple LCL application
................
  r10370 | michael | 2008-02-20 20:48:36 +0100 (Wed, 20 Feb 2008) | 1 line
  
  * Added tcollection stream read tests
................
  r10373 | vincents | 2008-02-21 00:33:10 +0100 (Thu, 21 Feb 2008) | 1 line
  
  * TReader.FindComponentClass: also search in FieldTables of parent classes.
................
  r10374 | michael | 2008-02-21 11:00:04 +0100 (Thu, 21 Feb 2008) | 1 line
  
  * Fix voor ResolveReferences
................
  r10376 | vincents | 2008-02-21 19:37:55 +0100 (Thu, 21 Feb 2008) | 1 line
  
  * reduced hints
................
  r10377 | vincents | 2008-02-22 14:56:22 +0100 (Fri, 22 Feb 2008) | 1 line
  
  * add check for valid NewIndex in TFPList.Move, so that an invalid NewIndex doesn't lead to memleak
................
  r10378 | vincents | 2008-02-22 15:16:56 +0100 (Fri, 22 Feb 2008) | 1 line
  
  * fixed TReader.ReadCollection in case more than one property was streamed
................
  r10379 | vincents | 2008-02-22 15:35:44 +0100 (Fri, 22 Feb 2008) | 3 lines
  
  + added another test for writing collections (shows how it should be written and thus read
  + added a test for a writing an enum with default value 
................
  r10380 | vincents | 2008-02-22 15:36:14 +0100 (Fri, 22 Feb 2008) | 1 line
  
  * fixed memleak
................
  r10381 | vincents | 2008-02-23 20:03:00 +0100 (Sat, 23 Feb 2008) | 1 line
  
  * fixed AV when streaming a component without published properties
................
  r10390 | michael | 2008-02-25 21:34:10 +0100 (Mon, 25 Feb 2008) | 1 line
  
  * Clean version of searchbuf inserted
................
  r10393 | vincents | 2008-02-26 23:06:14 +0100 (Tue, 26 Feb 2008) | 1 line
  
  * fixed TDataset.SetFieldValues
................
  r10398 | michael | 2008-02-27 21:58:49 +0100 (Wed, 27 Feb 2008) | 1 line
  
  * Added test for streaming 2 components
................
  r10400 | vincents | 2008-02-28 00:51:08 +0100 (Thu, 28 Feb 2008) | 1 line
  
  * improved tests for streaming components with owned subcomponents
................
  r10403 | vincents | 2008-02-28 22:19:32 +0100 (Thu, 28 Feb 2008) | 1 line
  
  * fixed writing child components
................
  r10441 | florian | 2008-03-04 20:11:46 +0100 (Tue, 04 Mar 2008) | 3 lines
  
  Initialized merge tracking via "svnmerge" with revisions "1-9261" from 
  http://svn.freepascal.org/svn/fpc/trunk
................
  r10444 | joost | 2008-03-05 11:31:07 +0100 (Wed, 05 Mar 2008) | 30 lines
  
  Merged revisions 9783,9786,9788,9814,9822,9825,9837-9850,9852,9854-9856,9863-9864,9867,9885,9895 via svnmerge from 
  svn+ssh://joost@svn.freepascal.org/FPC/svn/fpc/trunk
  
  ........
    r9783 | joost | 2008-01-18 23:52:13 +0100 (Fri, 18 Jan 2008) | 1 line
    
     * DigestTestREport makes it possible to write the unittest results to a testsuite-digest
  ........
    r9786 | joost | 2008-01-19 00:40:44 +0100 (Sat, 19 Jan 2008) | 1 line
    
     * Added dependency on paszlib to fcl-fpcunit
  ........
    r9788 | jonas | 2008-01-19 01:20:49 +0100 (Sat, 19 Jan 2008) | 2 lines
    
      + also add fpc-unit dependency on paszlib to build dependencies
  ........
    r9854 | joost | 2008-01-21 17:26:20 +0100 (Mon, 21 Jan 2008) | 2 lines
    
     * Added Comment and Category properties to TDigestResultsWriter
     * Write Comment and Category to digest.cfg
  ........
    r9885 | joost | 2008-01-23 22:56:34 +0100 (Wed, 23 Jan 2008) | 1 line
    
     * Write RelSrcDir to digest.cfg
  ........
    r9895 | joost | 2008-01-24 18:02:47 +0100 (Thu, 24 Jan 2008) | 1 line
    
     * Add dash between hostname and date in digest-tarfile
  ........
................
  r10445 | joost | 2008-03-05 11:47:26 +0100 (Wed, 05 Mar 2008) | 9 lines
  
  Merged revisions 10431 via svnmerge from 
  svn+ssh://joost@svn.freepascal.org/FPC/svn/fpc/trunk
  
  ........
    r10431 | joost | 2008-03-02 18:08:16 +0100 (Sun, 02 Mar 2008) | 1 line
    
     * Set Modified to false when te state of a dataset changes
  ........
................
  r10446 | joost | 2008-03-05 15:34:38 +0100 (Wed, 05 Mar 2008) | 9 lines
  
  Merged revisions 10350 via svnmerge from 
  svn+ssh://joost@svn.freepascal.org/FPC/svn/fpc/trunk
  
  ........
    r10350 | joost | 2008-02-17 22:14:26 +0100 (Sun, 17 Feb 2008) | 1 line
    
     * Fixed bug #8464
  ........
................
  r10490 | Almindor | 2008-03-15 11:18:42 +0100 (Sat, 15 Mar 2008) | 3 lines
  
  * add TDataLink.CalcFirstRecord cleanroom implementation (blind)
  * add TField.RefreshLookupList cleanroom implementation (blind)
................
  r10491 | Almindor | 2008-03-15 11:29:54 +0100 (Sat, 15 Mar 2008) | 2 lines
  
  * fix compilation of the TField.RefreshLookuplist;
................
  r10510 | Almindor | 2008-03-20 18:57:22 +0100 (Thu, 20 Mar 2008) | 2 lines
  
  * implement cleanroom TDataset.CalculateFields
................
  r10511 | Almindor | 2008-03-20 19:16:55 +0100 (Thu, 20 Mar 2008) | 2 lines
  
  * add TDataSet.EnableControls cleanroom implementation
................
  r10512 | Almindor | 2008-03-20 19:27:27 +0100 (Thu, 20 Mar 2008) | 2 lines
  
  * add TField.CalcLookupValue cleanroom implementation
................
  r10513 | Almindor | 2008-03-20 19:30:23 +0100 (Thu, 20 Mar 2008) | 2 lines
  
  * fix potential bug in cleanroom TField.RefreshLookupList
................
  r10514 | Almindor | 2008-03-20 19:33:13 +0100 (Thu, 20 Mar 2008) | 2 lines
  
  * add forgotten function call in TDataset.CalculateFields
................
  r10515 | Almindor | 2008-03-20 19:37:19 +0100 (Thu, 20 Mar 2008) | 2 lines
  
  * fix potential bug in cleanroom TDataLink.CalcFirstRecord
................
  r10531 | Almindor | 2008-03-22 10:57:40 +0100 (Sat, 22 Mar 2008) | 2 lines
  
  * implement cleanroom TDataSet.DataEvent
................
  r10534 | Almindor | 2008-03-22 21:30:02 +0100 (Sat, 22 Mar 2008) | 2 lines
  
  * fix cleanroom TDataset.DataEvent, make it call all connected datasources
................
  r10537 | michael | 2008-03-23 11:19:05 +0100 (Sun, 23 Mar 2008) | 6 lines
  
  * Fixed some issues:
    - Memleak in TReader.ReadPropValue. FFixups was re-allocated in beginreferences !
    - FPC behaves different from Delphi if no Default value is declared, it assumes a
      default of ord(TEnum)=0, same for sets.
    - Fixed MemLeak when a reference was resolved, Removed item was not freed.
................
  r10547 | Almindor | 2008-03-24 10:57:28 +0100 (Mon, 24 Mar 2008) | 2 lines
  
  * first fix to cleanroom TDataSet.DataEvent only 6 tests fail now :)
................
  r10553 | joost | 2008-03-24 19:58:33 +0100 (Mon, 24 Mar 2008) | 9 lines
  
  Merged revisions 10470 via svnmerge from 
  svn+ssh://joost@svn.freepascal.org/FPC/svn/fpc/trunk
  
  ........
    r10470 | joost | 2008-03-09 21:11:17 +0100 (Sun, 09 Mar 2008) | 1 line
    
     * Set TDataSet.InternalCalcFields if there are InternalCalcFields
  ........
................
  r10555 | joost | 2008-03-25 12:06:12 +0100 (Tue, 25 Mar 2008) | 9 lines
  
  Merged revisions 10519 via svnmerge from 
  svn+ssh://joost@svn.freepascal.org/FPC/svn/fpc/trunk
  
  ........
    r10519 | joost | 2008-03-21 14:38:44 +0100 (Fri, 21 Mar 2008) | 1 line
    
     * Fix for ValueOfKey for multiple-fields keys
  ........
................
  r10565 | Almindor | 2008-03-25 18:28:58 +0100 (Tue, 25 Mar 2008) | 2 lines
  
  * fix cleanroom TDataLink.CalcFirstRecord (passes tests now)
................

git-svn-id: trunk@10572 -
2008-03-27 20:15:57 +00:00

1513 lines
37 KiB
PHP

{
This file is part of the Free Component Library (FCL)
Copyright (c) 1999-2000 by the Free Pascal development team
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
{****************************************************************************}
{* TBinaryObjectReader *}
{****************************************************************************}
{$ifndef FPUNONE}
{$IFNDEF FPC_HAS_TYPE_EXTENDED}
function ExtendedToDouble(e : pointer) : double;
var mant : qword;
exp : smallint;
sign : boolean;
d : qword;
begin
move(pbyte(e)[0],mant,8); //mantissa : bytes 0..7
move(pbyte(e)[8],exp,2); //exponent and sign: bytes 8..9
mant:=LEtoN(mant);
exp:=LEtoN(word(exp));
sign:=(exp and $8000)<>0;
if sign then exp:=exp and $7FFF;
case exp of
0 : mant:=0; //if denormalized, value is too small for double,
//so it's always zero
$7FFF : exp:=2047 //either infinity or NaN
else
begin
dec(exp,16383-1023);
if (exp>=-51) and (exp<=0) then //can be denormalized
begin
mant:=mant shr (-exp);
exp:=0;
end
else
if (exp<-51) or (exp>2046) then //exponent too large.
begin
Result:=0;
exit;
end
else //normalized value
mant:=mant shl 1; //hide most significant bit
end;
end;
d:=word(exp);
d:=d shl 52;
mant:=mant shr 12;
d:=d or mant;
if sign then d:=d or $8000000000000000;
Result:=pdouble(@d)^;
end;
{$ENDIF}
{$endif}
function TBinaryObjectReader.ReadWord : word; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
begin
Read(Result,2);
Result:=LEtoN(Result);
end;
function TBinaryObjectReader.ReadDWord : longword; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
begin
Read(Result,4);
Result:=LEtoN(Result);
end;
function TBinaryObjectReader.ReadQWord : qword; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
begin
Read(Result,8);
Result:=LEtoN(Result);
end;
{$ifndef FPUNONE}
function TBinaryObjectReader.ReadExtended : extended; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
{$IFNDEF FPC_HAS_TYPE_EXTENDED}
var ext : array[0..9] of byte;
{$ENDIF}
begin
{$IFNDEF FPC_HAS_TYPE_EXTENDED}
Read(ext[0],10);
Result:=ExtendedToDouble(@(ext[0]));
{$ELSE}
Read(Result,sizeof(Result));
{$ENDIF}
end;
{$endif}
constructor TBinaryObjectReader.Create(Stream: TStream; BufSize: Integer);
begin
inherited Create;
If (Stream=Nil) then
Raise EReadError.Create(SEmptyStreamIllegalReader);
FStream := Stream;
FBufSize := BufSize;
GetMem(FBuffer, BufSize);
end;
destructor TBinaryObjectReader.Destroy;
begin
{ Seek back the amount of bytes that we didn't process until now: }
FStream.Seek(Integer(FBufPos) - Integer(FBufEnd), soFromCurrent);
if Assigned(FBuffer) then
FreeMem(FBuffer, FBufSize);
inherited Destroy;
end;
function TBinaryObjectReader.ReadValue: TValueType;
var
b: byte;
begin
Read(b, 1);
Result := TValueType(b);
end;
function TBinaryObjectReader.NextValue: TValueType;
begin
Result := ReadValue;
{ We only 'peek' at the next value, so seek back to unget the read value: }
Dec(FBufPos);
end;
procedure TBinaryObjectReader.BeginRootComponent;
var
Signature: LongInt;
begin
{ Read filer signature }
Read(Signature, 4);
if Signature <> LongInt({$ifdef FPC_SUPPORTS_UNALIGNED}unaligned{$endif}(FilerSignature)) then
raise EReadError.Create(SInvalidImage);
end;
procedure TBinaryObjectReader.BeginComponent(var Flags: TFilerFlags;
var AChildPos: Integer; var CompClassName, CompName: String);
var
Prefix: Byte;
ValueType: TValueType;
begin
{ Every component can start with a special prefix: }
Flags := [];
if (Byte(NextValue) and $f0) = $f0 then
begin
Prefix := Byte(ReadValue);
Flags := TFilerFlags(longint(Prefix and $0f));
if ffChildPos in Flags then
begin
ValueType := ReadValue;
case ValueType of
vaInt8:
AChildPos := ReadInt8;
vaInt16:
AChildPos := ReadInt16;
vaInt32:
AChildPos := ReadInt32;
else
raise EReadError.Create(SInvalidPropertyValue);
end;
end;
end;
CompClassName := ReadStr;
CompName := ReadStr;
end;
function TBinaryObjectReader.BeginProperty: String;
begin
Result := ReadStr;
end;
procedure TBinaryObjectReader.ReadBinary(const DestData: TMemoryStream);
var
BinSize: LongInt;
begin
BinSize:=LongInt(ReadDWord);
DestData.Size := BinSize;
Read(DestData.Memory^, BinSize);
end;
{$ifndef FPUNONE}
function TBinaryObjectReader.ReadFloat: Extended;
begin
Result:=ReadExtended;
end;
function TBinaryObjectReader.ReadSingle: Single;
begin
Result:=single(ReadDWord);
end;
{$endif}
function TBinaryObjectReader.ReadCurrency: Currency;
begin
Result:=currency(ReadQWord);
end;
{$ifndef FPUNONE}
function TBinaryObjectReader.ReadDate: TDateTime;
begin
Result:=TDateTime(ReadQWord);
end;
{$endif}
function TBinaryObjectReader.ReadIdent(ValueType: TValueType): String;
var
i: Byte;
begin
case ValueType of
vaIdent:
begin
Read(i, 1);
SetLength(Result, i);
Read(Pointer(@Result[1])^, i);
end;
vaNil:
Result := 'nil';
vaFalse:
Result := 'False';
vaTrue:
Result := 'True';
vaNull:
Result := 'Null';
end;
end;
function TBinaryObjectReader.ReadInt8: ShortInt;
begin
Read(Result, 1);
end;
function TBinaryObjectReader.ReadInt16: SmallInt;
begin
Result:=SmallInt(ReadWord);
end;
function TBinaryObjectReader.ReadInt32: LongInt;
begin
Result:=LongInt(ReadDWord);
end;
function TBinaryObjectReader.ReadInt64: Int64;
begin
Result:=Int64(ReadQWord);
end;
function TBinaryObjectReader.ReadSet(EnumType: Pointer): Integer;
var
Name: String;
Value: Integer;
begin
try
Result := 0;
while True do
begin
Name := ReadStr;
if Length(Name) = 0 then
break;
Value := GetEnumValue(PTypeInfo(EnumType), Name);
if Value = -1 then
raise EReadError.Create(SInvalidPropertyValue);
Result := Result or (1 shl Value);
end;
except
SkipSetBody;
raise;
end;
end;
function TBinaryObjectReader.ReadStr: String;
var
i: Byte;
begin
Read(i, 1);
SetLength(Result, i);
if i > 0 then
Read(Pointer(@Result[1])^, i);
end;
function TBinaryObjectReader.ReadString(StringType: TValueType): String;
var
b: Byte;
i: Integer;
begin
case StringType of
vaString:
begin
Read(b, 1);
i := b;
end;
vaLString:
i:=ReadDWord;
end;
SetLength(Result, i);
if i > 0 then
Read(Pointer(@Result[1])^, i);
end;
function TBinaryObjectReader.ReadWideString: WideString;
var
len: DWord;
{$IFDEF ENDIAN_BIG}
i : integer;
{$ENDIF}
begin
len := ReadDWord;
SetLength(Result, len);
if (len > 0) then
begin
Read(Pointer(@Result[1])^, len*2);
{$IFDEF ENDIAN_BIG}
for i:=1 to len do
Result[i]:=widechar(SwapEndian(word(Result[i])));
{$ENDIF}
end;
end;
procedure TBinaryObjectReader.SkipComponent(SkipComponentInfos: Boolean);
var
Flags: TFilerFlags;
Dummy: Integer;
CompClassName, CompName: String;
begin
if SkipComponentInfos then
{ Skip prefix, component class name and component object name }
BeginComponent(Flags, Dummy, CompClassName, CompName);
{ Skip properties }
while NextValue <> vaNull do
SkipProperty;
ReadValue;
{ Skip children }
while NextValue <> vaNull do
SkipComponent(True);
ReadValue;
end;
procedure TBinaryObjectReader.SkipValue;
procedure SkipBytes(Count: LongInt);
var
Dummy: array[0..1023] of Byte;
SkipNow: Integer;
begin
while Count > 0 do
begin
if Count > 1024 then
SkipNow := 1024
else
SkipNow := Count;
Read(Dummy, SkipNow);
Dec(Count, SkipNow);
end;
end;
var
Count: LongInt;
begin
case ReadValue of
vaNull, vaFalse, vaTrue, vaNil: ;
vaList:
begin
while NextValue <> vaNull do
SkipValue;
ReadValue;
end;
vaInt8:
SkipBytes(1);
vaInt16:
SkipBytes(2);
vaInt32:
SkipBytes(4);
vaExtended:
SkipBytes(10);
vaString, vaIdent:
ReadStr;
vaBinary, vaLString:
begin
Count:=LongInt(ReadDWord);
SkipBytes(Count);
end;
vaWString:
begin
Count:=LongInt(ReadDWord);
SkipBytes(Count*sizeof(widechar));
end;
vaSet:
SkipSetBody;
vaCollection:
begin
while NextValue <> vaNull do
begin
{ Skip the order value if present }
if NextValue in [vaInt8, vaInt16, vaInt32] then
SkipValue;
SkipBytes(1);
while NextValue <> vaNull do
SkipProperty;
ReadValue;
end;
ReadValue;
end;
vaSingle:
{$ifndef FPUNONE}
SkipBytes(Sizeof(Single));
{$else}
SkipBytes(4);
{$endif}
{!!!: vaCurrency:
SkipBytes(SizeOf(Currency));}
vaDate, vaInt64:
SkipBytes(8);
end;
end;
{ private methods }
procedure TBinaryObjectReader.Read(var Buf; Count: LongInt);
var
CopyNow: LongInt;
Dest: Pointer;
begin
Dest := @Buf;
while Count > 0 do
begin
if FBufPos >= FBufEnd then
begin
FBufEnd := FStream.Read(FBuffer^, FBufSize);
if FBufEnd = 0 then
raise EReadError.Create(SReadError);
FBufPos := 0;
end;
CopyNow := FBufEnd - FBufPos;
if CopyNow > Count then
CopyNow := Count;
Move(PChar(FBuffer)[FBufPos], Dest^, CopyNow);
Inc(FBufPos, CopyNow);
Inc(Dest, CopyNow);
Dec(Count, CopyNow);
end;
end;
procedure TBinaryObjectReader.SkipProperty;
begin
{ Skip property name, then the property value }
ReadStr;
SkipValue;
end;
procedure TBinaryObjectReader.SkipSetBody;
begin
while Length(ReadStr) > 0 do;
end;
{****************************************************************************}
{* TREADER *}
{****************************************************************************}
type
TFieldInfo = packed record
FieldOffset: LongWord;
ClassTypeIndex: Word;
Name: ShortString;
end;
PFieldClassTable = ^TFieldClassTable;
TFieldClassTable =
{$ifndef FPC_REQUIRES_PROPER_ALIGNMENT}
packed
{$endif FPC_REQUIRES_PROPER_ALIGNMENT}
record
Count: Word;
Entries: array[Word] of TPersistentClass;
end;
PFieldTable = ^TFieldTable;
TFieldTable =
{$ifndef FPC_REQUIRES_PROPER_ALIGNMENT}
packed
{$endif FPC_REQUIRES_PROPER_ALIGNMENT}
record
FieldCount: Word;
ClassTable: PFieldClassTable;
// Fields: array[Word] of TFieldInfo; Elements have variant size!
end;
function GetFieldClass(Instance: TObject; const ClassName: string): TPersistentClass;
var
UClassName: String;
ClassType: TClass;
ClassTable: PFieldClassTable;
i: Integer;
{ FieldTable: PFieldTable; }
begin
// At first, try to locate the class in the class tables
UClassName := UpperCase(ClassName);
ClassType := Instance.ClassType;
while ClassType <> TPersistent do
begin
{ FieldTable := PFieldTable((Pointer(ClassType) + vmtFieldTable)^); }
ClassTable := PFieldTable((Pointer(ClassType) + vmtFieldTable)^)^.ClassTable;
if Assigned(ClassTable) then
for i := 0 to ClassTable^.Count - 1 do
begin
Result := ClassTable^.Entries[i];
if UpperCase(Result.ClassName) = UClassName then
exit;
end;
// Try again with the parent class type
ClassType := ClassType.ClassParent;
end;
Result := Classes.GetClass(ClassName);
end;
constructor TReader.Create(Stream: TStream; BufSize: Integer);
begin
inherited Create;
If (Stream=Nil) then
Raise EReadError.Create(SEmptyStreamIllegalReader);
FDriver := CreateDriver(Stream, BufSize);
end;
destructor TReader.Destroy;
begin
FDriver.Free;
inherited Destroy;
end;
function TReader.CreateDriver(Stream: TStream; BufSize: Integer): TAbstractObjectReader;
begin
Result := TBinaryObjectReader.Create(Stream, BufSize);
end;
procedure TReader.BeginReferences;
begin
FLoaded := TList.Create;
end;
procedure TReader.CheckValue(Value: TValueType);
begin
if FDriver.NextValue <> Value then
raise EReadError.Create(SInvalidPropertyValue)
else
FDriver.ReadValue;
end;
procedure TReader.DefineProperty(const Name: String; AReadData: TReaderProc;
WriteData: TWriterProc; HasData: Boolean);
begin
if Assigned(AReadData) and (UpperCase(Name) = UpperCase(FPropName)) then
begin
AReadData(Self);
SetLength(FPropName, 0);
end;
end;
procedure TReader.DefineBinaryProperty(const Name: String;
AReadData, WriteData: TStreamProc; HasData: Boolean);
var
MemBuffer: TMemoryStream;
begin
if Assigned(AReadData) and (UpperCase(Name) = UpperCase(FPropName)) then
begin
{ Check if the next property really is a binary property}
if FDriver.NextValue <> vaBinary then
begin
FDriver.SkipValue;
FCanHandleExcepts := True;
raise EReadError.Create(SInvalidPropertyValue);
end else
FDriver.ReadValue;
MemBuffer := TMemoryStream.Create;
try
FDriver.ReadBinary(MemBuffer);
FCanHandleExcepts := True;
AReadData(MemBuffer);
finally
MemBuffer.Free;
end;
SetLength(FPropName, 0);
end;
end;
function TReader.EndOfList: Boolean;
begin
Result := FDriver.NextValue = vaNull;
end;
procedure TReader.EndReferences;
begin
FLoaded.Free;
FLoaded := nil;
end;
function TReader.Error(const Message: String): Boolean;
begin
Result := False;
if Assigned(FOnError) then
FOnError(Self, Message, Result);
end;
function TReader.FindMethod(ARoot: TComponent; const AMethodName: String): Pointer;
var
ErrorResult: Boolean;
begin
Result := ARoot.MethodAddress(AMethodName);
ErrorResult := Result = nil;
{ always give the OnFindMethod callback a chance to locate the method }
if Assigned(FOnFindMethod) then
FOnFindMethod(Self, AMethodName, Result, ErrorResult);
if ErrorResult then
raise EReadError.Create(SInvalidPropertyValue);
end;
procedure TReader.DoFixupReferences;
Var
R,RN : TLocalUnresolvedReference;
G : TUnresolvedInstance;
Ref : String;
C : TComponent;
P : integer;
L : TLinkedList;
begin
If Assigned(FFixups) then
begin
L:=TLinkedList(FFixups);
R:=TLocalUnresolvedReference(L.Root);
While (R<>Nil) do
begin
RN:=TLocalUnresolvedReference(R.Next);
Ref:=R.FRelative;
If Assigned(FOnReferenceName) then
FOnReferenceName(Self,Ref);
C:=FindNestedComponent(R.FRoot,Ref);
If Assigned(C) then
SetObjectProp(R.FInstance,R.FPropInfo,C)
else
begin
P:=Pos('.',R.FRelative);
If (P<>0) then
begin
G:=AddToResolveList(R.FInstance);
G.Addreference(R.FRoot,R.FPropInfo,Copy(R.FRelative,1,P-1),Copy(R.FRelative,P+1,Length(R.FRelative)-P));
end;
end;
L.RemoveItem(R);
R:=RN;
end;
FreeAndNil(FFixups);
end;
end;
procedure TReader.FixupReferences;
var
i: Integer;
begin
DoFixupReferences;
GlobalFixupReferences;
for i := 0 to FLoaded.Count - 1 do
TComponent(FLoaded[I]).Loaded;
end;
function TReader.NextValue: TValueType;
begin
Result := FDriver.NextValue;
end;
procedure TReader.Read(var Buf; Count: LongInt);
begin
//This should give an exception if read is not implemented (i.e. TTextObjectReader)
//but should work with TBinaryObjectReader.
Driver.Read(Buf, Count);
end;
procedure TReader.PropertyError;
begin
FDriver.SkipValue;
raise EReadError.CreateFmt(SUnknownProperty,[FPropName]);
end;
function TReader.ReadBoolean: Boolean;
var
ValueType: TValueType;
begin
ValueType := FDriver.ReadValue;
if ValueType = vaTrue then
Result := True
else if ValueType = vaFalse then
Result := False
else
raise EReadError.Create(SInvalidPropertyValue);
end;
function TReader.ReadChar: Char;
var
s: String;
begin
s := ReadString;
if Length(s) = 1 then
Result := s[1]
else
raise EReadError.Create(SInvalidPropertyValue);
end;
function TReader.ReadWideChar: WideChar;
var
W: WideString;
begin
W := ReadWideString;
if Length(W) = 1 then
Result := W[1]
else
raise EReadError.Create(SInvalidPropertyValue);
end;
procedure TReader.ReadCollection(Collection: TCollection);
var
Item: TCollectionItem;
begin
Collection.BeginUpdate;
Collection.Clear;
while not EndOfList do begin
ReadListBegin;
Item := Collection.Add;
while NextValue<>vaNull do
ReadProperty(Item);
ReadListEnd;
end;
Collection.EndUpdate;
ReadListEnd;
end;
function TReader.ReadComponent(Component: TComponent): TComponent;
var
Flags: TFilerFlags;
function Recover(var Component: TComponent): Boolean;
begin
Result := False;
if ExceptObject.InheritsFrom(Exception) then
begin
if not ((ffInherited in Flags) or Assigned(Component)) then
Component.Free;
Component := nil;
FDriver.SkipComponent(False);
Result := Error(Exception(ExceptObject).Message);
end;
end;
var
CompClassName, Name: String;
n, ChildPos: Integer;
SavedParent, SavedLookupRoot: TComponent;
ComponentClass: TComponentClass;
C, NewComponent: TComponent;
SubComponents: TList;
begin
FDriver.BeginComponent(Flags, ChildPos, CompClassName, Name);
SavedParent := Parent;
SavedLookupRoot := FLookupRoot;
SubComponents := nil;
try
Result := Component;
if not Assigned(Result) then
try
if ffInherited in Flags then
begin
{ Try to locate the existing ancestor component }
if Assigned(FLookupRoot) then
Result := FLookupRoot.FindComponent(Name)
else
Result := nil;
if not Assigned(Result) then
begin
if Assigned(FOnAncestorNotFound) then
FOnAncestorNotFound(Self, Name,
FindComponentClass(CompClassName), Result);
if not Assigned(Result) then
raise EReadError.CreateFmt(SAncestorNotFound, [Name]);
end;
Parent := Result.GetParentComponent;
if not Assigned(Parent) then
Parent := Root;
end else
begin
Result := nil;
ComponentClass := FindComponentClass(CompClassName);
if Assigned(FOnCreateComponent) then
FOnCreateComponent(Self, ComponentClass, Result);
if not Assigned(Result) then
begin
NewComponent := TComponent(ComponentClass.NewInstance);
if ffInline in Flags then
NewComponent.FComponentState :=
NewComponent.FComponentState + [csLoading, csInline];
NewComponent.Create(Owner);
{ Don't set Result earlier because else we would come in trouble
with the exception recover mechanism! (Result should be NIL if
an error occured) }
Result := NewComponent;
end;
Include(Result.FComponentState, csLoading);
end;
except
if not Recover(Result) then
raise;
end;
if Assigned(Result) then
try
Include(Result.FComponentState, csLoading);
{ create list of subcomponents and set loading}
SubComponents := TList.Create;
for n := 0 to Result.ComponentCount - 1 do
begin
C := Result.Components[n];
if csSubcomponent in C.ComponentStyle
then begin
SubComponents.Add(C);
Include(C.FComponentState, csLoading);
end;
end;
if not (ffInherited in Flags) then
try
Result.SetParentComponent(Parent);
if Assigned(FOnSetName) then
FOnSetName(Self, Result, Name);
Result.Name := Name;
if FindGlobalComponent(Name) = Result then
Include(Result.FComponentState, csInline);
except
if not Recover(Result) then
raise;
end;
if not Assigned(Result) then
exit;
if csInline in Result.ComponentState then
FLookupRoot := Result;
{ Read the component state }
Include(Result.FComponentState, csReading);
for n := 0 to Subcomponents.Count - 1 do
Include(TComponent(Subcomponents[n]).FComponentState, csReading);
Result.ReadState(Self);
Exclude(Result.FComponentState, csReading);
for n := 0 to Subcomponents.Count - 1 do
Exclude(TComponent(Subcomponents[n]).FComponentState, csReading);
if ffChildPos in Flags then
Parent.SetChildOrder(Result, ChildPos);
{ Add component to list of loaded components, if necessary }
if (not ((ffInherited in Flags) or (csInline in Result.ComponentState))) or
(FLoaded.IndexOf(Result) < 0)
then begin
for n := 0 to Subcomponents.Count - 1 do
FLoaded.Add(Subcomponents[n]);
FLoaded.Add(Result);
end;
except
if ((ffInherited in Flags) or Assigned(Component)) then
Result.Free;
raise;
end;
finally
Parent := SavedParent;
FLookupRoot := SavedLookupRoot;
Subcomponents.Free;
end;
end;
procedure TReader.ReadData(Instance: TComponent);
var
SavedOwner, SavedParent: TComponent;
begin
try
{ Read properties }
while not EndOfList do
ReadProperty(Instance);
ReadListEnd;
{ Read children }
SavedOwner := Owner;
SavedParent := Parent;
try
Owner := Instance.GetChildOwner;
if not Assigned(Owner) then
Owner := Root;
Parent := Instance.GetChildParent;
while not EndOfList do
ReadComponent(nil);
ReadListEnd;
finally
Owner := SavedOwner;
Parent := SavedParent;
end;
{ Fixup references if necessary (normally only if this is the root) }
DoFixupReferences;
finally
FreeAndNil(FFixups);
end;
end;
{$ifndef FPUNONE}
function TReader.ReadFloat: Extended;
begin
if FDriver.NextValue = vaExtended then
begin
ReadValue;
Result := FDriver.ReadFloat
end else
Result := ReadInteger;
end;
function TReader.ReadSingle: Single;
begin
if FDriver.NextValue = vaSingle then
begin
FDriver.ReadValue;
Result := FDriver.ReadSingle;
end else
Result := ReadInteger;
end;
{$endif}
function TReader.ReadCurrency: Currency;
begin
if FDriver.NextValue = vaCurrency then
begin
FDriver.ReadValue;
Result := FDriver.ReadCurrency;
end else
Result := ReadInteger;
end;
{$ifndef FPUNONE}
function TReader.ReadDate: TDateTime;
begin
if FDriver.NextValue = vaDate then
begin
FDriver.ReadValue;
Result := FDriver.ReadDate;
end else
Result := ReadInteger;
end;
{$endif}
function TReader.ReadIdent: String;
var
ValueType: TValueType;
begin
ValueType := FDriver.ReadValue;
if ValueType in [vaIdent, vaNil, vaFalse, vaTrue, vaNull] then
Result := FDriver.ReadIdent(ValueType)
else
raise EReadError.Create(SInvalidPropertyValue);
end;
function TReader.ReadInteger: LongInt;
begin
case FDriver.ReadValue of
vaInt8:
Result := FDriver.ReadInt8;
vaInt16:
Result := FDriver.ReadInt16;
vaInt32:
Result := FDriver.ReadInt32;
else
raise EReadError.Create(SInvalidPropertyValue);
end;
end;
function TReader.ReadInt64: Int64;
begin
if FDriver.NextValue = vaInt64 then
begin
FDriver.ReadValue;
Result := FDriver.ReadInt64;
end else
Result := ReadInteger;
end;
procedure TReader.ReadListBegin;
begin
CheckValue(vaList);
end;
procedure TReader.ReadListEnd;
begin
CheckValue(vaNull);
end;
procedure TReader.ReadProperty(AInstance: TPersistent);
var
Path: String;
Instance: TPersistent;
DotPos, NextPos: PChar;
PropInfo: PPropInfo;
Obj: TObject;
Name: String;
Skip: Boolean;
Handled: Boolean;
OldPropName: String;
function HandleMissingProperty(IsPath: Boolean): boolean;
begin
Result:=true;
if Assigned(OnPropertyNotFound) then begin
// user defined property error handling
OldPropName:=FPropName;
Handled:=false;
Skip:=false;
OnPropertyNotFound(Self,Instance,FPropName,IsPath,Handled,Skip);
if Handled and (not Skip) and (OldPropName<>FPropName) then
// try alias property
PropInfo := GetPropInfo(Instance.ClassInfo, FPropName);
if Skip then begin
FDriver.SkipValue;
Result:=false;
exit;
end;
end;
end;
begin
try
Path := FDriver.BeginProperty;
try
Instance := AInstance;
FCanHandleExcepts := True;
DotPos := PChar(Path);
while True do
begin
NextPos := StrScan(DotPos, '.');
if Assigned(NextPos) then
FPropName := Copy(String(DotPos), 1, Integer(NextPos - DotPos))
else
begin
FPropName := DotPos;
break;
end;
DotPos := NextPos + 1;
PropInfo := GetPropInfo(Instance.ClassInfo, FPropName);
if not Assigned(PropInfo) then begin
if not HandleMissingProperty(true) then exit;
if not Assigned(PropInfo) then
PropertyError;
end;
if PropInfo^.PropType^.Kind = tkClass then
Obj := TObject(GetObjectProp(Instance, PropInfo))
else
Obj := nil;
if not (Obj is TPersistent) then
begin
{ All path elements must be persistent objects! }
FDriver.SkipValue;
raise EReadError.Create(SInvalidPropertyPath);
end;
Instance := TPersistent(Obj);
end;
PropInfo := GetPropInfo(Instance.ClassInfo, FPropName);
if Assigned(PropInfo) then
ReadPropValue(Instance, PropInfo)
else
begin
FCanHandleExcepts := False;
Instance.DefineProperties(Self);
FCanHandleExcepts := True;
if Length(FPropName) > 0 then begin
if not HandleMissingProperty(false) then exit;
if not Assigned(PropInfo) then
PropertyError;
end;
end;
except
on e: Exception do
begin
SetLength(Name, 0);
if AInstance.InheritsFrom(TComponent) then
Name := TComponent(AInstance).Name;
if Length(Name) = 0 then
Name := AInstance.ClassName;
raise EReadError.CreateFmt(SPropertyException,
[Name, DotSep, Path, e.Message]);
end;
end;
except
on e: Exception do
if not FCanHandleExcepts or not Error(E.Message) then
raise;
end;
end;
procedure TReader.ReadPropValue(Instance: TPersistent; PropInfo: Pointer);
const
NullMethod: TMethod = (Code: nil; Data: nil);
var
PropType: PTypeInfo;
Value: LongInt;
{ IdentToIntFn: TIdentToInt; }
Ident: String;
Method: TMethod;
Handled: Boolean;
TmpStr: String;
begin
if not Assigned(PPropInfo(PropInfo)^.SetProc) then
raise EReadError.Create(SReadOnlyProperty);
PropType := PPropInfo(PropInfo)^.PropType;
case PropType^.Kind of
tkInteger:
if FDriver.NextValue = vaIdent then
begin
Ident := ReadIdent;
if GlobalIdentToInt(Ident,Value) then
SetOrdProp(Instance, PropInfo, Value)
else
raise EReadError.Create(SInvalidPropertyValue);
end else
SetOrdProp(Instance, PropInfo, ReadInteger);
tkBool:
SetOrdProp(Instance, PropInfo, Ord(ReadBoolean));
tkChar:
SetOrdProp(Instance, PropInfo, Ord(ReadChar));
tkWChar:
SetOrdProp(Instance, PropInfo, Ord(ReadWideChar));
tkEnumeration:
begin
Value := GetEnumValue(PropType, ReadIdent);
if Value = -1 then
raise EReadError.Create(SInvalidPropertyValue);
SetOrdProp(Instance, PropInfo, Value);
end;
{$ifndef FPUNONE}
tkFloat:
SetFloatProp(Instance, PropInfo, ReadFloat);
{$endif}
tkSet:
begin
CheckValue(vaSet);
SetOrdProp(Instance, PropInfo,
FDriver.ReadSet(GetTypeData(PropType)^.CompType));
end;
tkMethod:
if FDriver.NextValue = vaNil then
begin
FDriver.ReadValue;
SetMethodProp(Instance, PropInfo, NullMethod);
end else
begin
Handled:=false;
Ident:=ReadIdent;
if Assigned(OnSetMethodProperty) then
OnSetMethodProperty(Self,Instance,PPropInfo(PropInfo),Ident,
Handled);
if not Handled then begin
Method.Code := FindMethod(Root, Ident);
Method.Data := Root;
if Assigned(Method.Code) then
SetMethodProp(Instance, PropInfo, Method);
end;
end;
tkSString, tkLString, tkAString:
begin
TmpStr:=ReadString;
if Assigned(FOnReadStringProperty) then
FOnReadStringProperty(Self,Instance,PropInfo,TmpStr);
SetStrProp(Instance, PropInfo, TmpStr);
end;
tkWstring:
SetWideStrProp(Instance,PropInfo,ReadWideString);
{!!!: tkVariant}
tkClass:
case FDriver.NextValue of
vaNil:
begin
FDriver.ReadValue;
SetOrdProp(Instance, PropInfo, 0)
end;
vaCollection:
begin
FDriver.ReadValue;
ReadCollection(TCollection(GetObjectProp(Instance, PropInfo)));
end
else
begin
If Not Assigned(FFixups) then
FFixups:=TLinkedList.Create(TLocalUnresolvedReference);
With TLocalUnresolvedReference(TLinkedList(FFixups).Add) do
begin
FInstance:=Instance;
FRoot:=Root;
FPropInfo:=PropInfo;
FRelative:=ReadIdent;
end;
end;
end;
tkInt64, tkQWord: SetInt64Prop(Instance, PropInfo, ReadInt64);
else
raise EReadError.CreateFmt(SUnknownPropertyType, [Ord(PropType^.Kind)]);
end;
end;
function TReader.ReadRootComponent(ARoot: TComponent): TComponent;
var
Dummy, i: Integer;
Flags: TFilerFlags;
CompClassName, CompName, ResultName: String;
begin
FDriver.BeginRootComponent;
Result := nil;
{!!!: GlobalNameSpace.BeginWrite; // Loading from stream adds to name space
try}
try
FDriver.BeginComponent(Flags, Dummy, CompClassName, CompName);
if not Assigned(ARoot) then
begin
{ Read the class name and the object name and create a new object: }
Result := TComponentClass(FindClass(CompClassName)).Create(nil);
Result.Name := CompName;
end else
begin
Result := ARoot;
if not (csDesigning in Result.ComponentState) then
begin
Result.FComponentState :=
Result.FComponentState + [csLoading, csReading];
{ We need an unique name }
i := 0;
{ Don't use Result.Name directly, as this would influence
FindGlobalComponent in successive loop runs }
ResultName := CompName;
while Assigned(FindGlobalComponent(ResultName)) do
begin
Inc(i);
ResultName := CompName + '_' + IntToStr(i);
end;
Result.Name := ResultName;
end;
end;
FRoot := Result;
FLookupRoot := Result;
if Assigned(GlobalLoaded) then
FLoaded := GlobalLoaded
else
FLoaded := TList.Create;
try
if FLoaded.IndexOf(FRoot) < 0 then
FLoaded.Add(FRoot);
FOwner := FRoot;
FRoot.FComponentState := FRoot.FComponentState + [csLoading, csReading];
FRoot.ReadState(Self);
Exclude(FRoot.FComponentState, csReading);
if not Assigned(GlobalLoaded) then
for i := 0 to FLoaded.Count - 1 do
TComponent(FLoaded[i]).Loaded;
finally
if not Assigned(GlobalLoaded) then
FLoaded.Free;
FLoaded := nil;
end;
GlobalFixupReferences;
except
RemoveFixupReferences(ARoot, '');
if not Assigned(ARoot) then
Result.Free;
raise;
end;
{finally
GlobalNameSpace.EndWrite;
end;}
end;
procedure TReader.ReadComponents(AOwner, AParent: TComponent;
Proc: TReadComponentsProc);
var
Component: TComponent;
begin
Root := AOwner;
Owner := AOwner;
Parent := AParent;
BeginReferences;
try
while not EndOfList do
begin
FDriver.BeginRootComponent;
Component := ReadComponent(nil);
if Assigned(Proc) then
Proc(Component);
end;
ReadListEnd;
FixupReferences;
finally
EndReferences;
end;
end;
function TReader.ReadString: String;
var
StringType: TValueType;
begin
StringType := FDriver.ReadValue;
if StringType in [vaString, vaLString] then
Result := FDriver.ReadString(StringType)
else if StringType in [vaWString,vaUTF8String] then
Result:= FDriver.ReadWidestring
else
raise EReadError.Create(SInvalidPropertyValue);
end;
function TReader.ReadWideString: WideString;
var
s: String;
i: Integer;
begin
if NextValue in [vaWString,vaUTF8String] then
begin
ReadValue;
Result := FDriver.ReadWideString
end
else begin
//data probable from ObjectTextToBinary
s := ReadString;
setlength(result,length(s));
for i:= 1 to length(s) do begin
result[i]:= widechar(ord(s[i])); //no code conversion
end;
end;
end;
function TReader.ReadValue: TValueType;
begin
Result := FDriver.ReadValue;
end;
procedure TReader.CopyValue(Writer: TWriter);
procedure CopyBytes(Count: Integer);
{ var
Buffer: array[0..1023] of Byte; }
begin
{!!!: while Count > 1024 do
begin
FDriver.Read(Buffer, 1024);
Writer.Driver.Write(Buffer, 1024);
Dec(Count, 1024);
end;
if Count > 0 then
begin
FDriver.Read(Buffer, Count);
Writer.Driver.Write(Buffer, Count);
end;}
end;
{var
s: String;
Count: LongInt; }
begin
case FDriver.NextValue of
vaNull:
Writer.WriteIdent('NULL');
vaFalse:
Writer.WriteIdent('FALSE');
vaTrue:
Writer.WriteIdent('TRUE');
vaNil:
Writer.WriteIdent('NIL');
{!!!: vaList, vaCollection:
begin
Writer.WriteValue(FDriver.ReadValue);
while not EndOfList do
CopyValue(Writer);
ReadListEnd;
Writer.WriteListEnd;
end;}
vaInt8, vaInt16, vaInt32:
Writer.WriteInteger(ReadInteger);
{$ifndef FPUNONE}
vaExtended:
Writer.WriteFloat(ReadFloat);
{$endif}
{!!!: vaString:
Writer.WriteStr(ReadStr);}
vaIdent:
Writer.WriteIdent(ReadIdent);
{!!!: vaBinary, vaLString, vaWString:
begin
Writer.WriteValue(FDriver.ReadValue);
FDriver.Read(Count, SizeOf(Count));
Writer.Driver.Write(Count, SizeOf(Count));
CopyBytes(Count);
end;}
{!!!: vaSet:
Writer.WriteSet(ReadSet);}
{$ifndef FPUNONE}
vaSingle:
Writer.WriteSingle(ReadSingle);
{$endif}
{!!!: vaCurrency:
Writer.WriteCurrency(ReadCurrency);}
{$ifndef FPUNONE}
vaDate:
Writer.WriteDate(ReadDate);
{$endif}
vaInt64:
Writer.WriteInteger(ReadInt64);
end;
end;
function TReader.FindComponentClass(const AClassName: String): TComponentClass;
var
PersistentClass: TPersistentClass;
UClassName: shortstring;
procedure FindInFieldTable(RootComponent: TComponent);
var
FieldClassTable: PFieldClassTable;
Entry: TPersistentClass;
i: Integer;
ComponentClassType: TClass;
begin
ComponentClassType := RootComponent.ClassType;
// it is not necessary to look in the FieldTable of TComponent,
// because TComponent doesn't have published properties that are
// descendants of TComponent
while ComponentClassType<>TComponent do begin
FieldClassTable :=
PFieldTable((Pointer(RootComponent.ClassType)+vmtFieldTable)^)^.ClassTable;
if assigned(FieldClassTable) then begin
for i := 0 to FieldClassTable^.Count -1 do begin
Entry := FieldClassTable^.Entries[i];
//writeln(format('Looking for %s in field table of class %s. Found %s',
//[AClassName, ComponentClassType.ClassName, Entry.ClassName]));
if (UpperCase(Entry.ClassName)=UClassName) and
(Entry.InheritsFrom(TComponent)) then begin
Result := TComponentClass(Entry);
Exit;
end;
end;
end;
// look in parent class
ComponentClassType := ComponentClassType.ClassParent;
end;
end;
begin
Result := nil;
UClassName:=UpperCase(AClassName);
FindInFieldTable(Root);
if (Result=nil) and assigned(LookupRoot) and (LookupRoot<>Root) then
FindInFieldTable(LookupRoot);
if (Result=nil) then begin
PersistentClass := GetClass(AClassName);
if PersistentClass.InheritsFrom(TComponent) then
Result := TComponentClass(PersistentClass);
end;
if (Result=nil) and assigned(OnFindComponentClass) then
OnFindComponentClass(Self, AClassName, Result);
if (Result=nil) or (not Result.InheritsFrom(TComponent)) then
raise EClassNotFound.CreateFmt(SClassNotFound, [AClassName]);
end;