fpc/fcl/classes/reader.inc
2005-02-14 17:13:06 +00:00

1317 lines
32 KiB
PHP

{
$Id$
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 *}
{****************************************************************************}
constructor TBinaryObjectReader.Create(Stream: TStream; BufSize: Integer);
begin
inherited Create;
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;
begin
Result := vaNull; { Necessary in FPC as TValueType is larger than 1 byte! }
Read(Result, 1);
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(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(Prefix and $0f);
if ffChildPos in Flags then
begin
ValueType := NextValue;
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
Read(BinSize, 4);
DestData.Size := BinSize;
Read(DestData.Memory^, BinSize);
end;
function TBinaryObjectReader.ReadFloat: Extended;
begin
Read(Result, SizeOf(Extended))
end;
function TBinaryObjectReader.ReadSingle: Single;
begin
Read(Result, SizeOf(Single))
end;
{!!!: function TBinaryObjectReader.ReadCurrency: Currency;
begin
Read(Result, SizeOf(Currency))
end;}
function TBinaryObjectReader.ReadDate: TDateTime;
begin
Read(Result, SizeOf(TDateTime))
end;
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
Read(Result, 2);
end;
function TBinaryObjectReader.ReadInt32: LongInt;
begin
Read(Result, 4);
end;
function TBinaryObjectReader.ReadInt64: Int64;
begin
Read(Result, 8);
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
i: Integer;
begin
case StringType of
vaString:
begin
i := 0;
Read(i, 1);
end;
vaLString:
Read(i, 4);
end;
SetLength(Result, i);
if i > 0 then
Read(Pointer(@Result[1])^, i);
end;
{!!!: function TBinaryObjectReader.ReadWideString: WideString;
var
i: Integer;
begin
FDriver.Read(i, 4);
SetLength(Result, i);
if i > 0 then
Read(PWideChar(Result), i * 2);
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(SizeOf(Extended));
vaString, vaIdent:
ReadStr;
vaBinary, vaLString, vaWString:
begin
Read(Count, 4);
SkipBytes(Count);
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:
SkipBytes(Sizeof(Single));
{!!!: vaCurrency:
SkipBytes(SizeOf(Currency));}
vaDate:
SkipBytes(Sizeof(TDateTime));
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 *}
{****************************************************************************}
// This may be better put somewhere else:
type
TFieldInfo = packed record
FieldOffset: LongWord;
ClassTypeIndex: Word;
Name: ShortString;
end;
PFieldClassTable = ^TFieldClassTable;
TFieldClassTable = packed record
Count: Word;
Entries: array[Word] of TPersistentClass;
end;
PFieldTable = ^TFieldTable;
TFieldTable = packed 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;
FDriver := TBinaryObjectReader.Create(Stream, BufSize);
end;
destructor TReader.Destroy;
begin
FDriver.Free;
inherited Destroy;
end;
procedure TReader.BeginReferences;
begin
FLoaded := TList.Create;
try
FFixups := TList.Create;
except
FLoaded.Free;
raise;
end;
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
FreeFixups;
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 RemoveGlobalFixup(Fixup: TPropFixup);
var
i: Integer;
begin
with GlobalFixupList.LockList do
try
for i := Count - 1 downto 0 do
with TPropFixup(Items[i]) do
if (FInstance = Fixup.FInstance) and
(FPropInfo = Fixup.FPropInfo) then
begin
Free;
Delete(i);
end;
finally
GlobalFixupList.UnlockList;
end;
end;
procedure TReader.DoFixupReferences;
var
i: Integer;
CurFixup: TPropFixup;
CurName: String;
Target: Pointer;
begin
if Assigned(FFixups) then
try
for i := 0 to FFixups.Count - 1 do
begin
CurFixup := TPropFixup(FFixups[i]);
CurName := CurFixup.FName;
if Assigned(FOnReferenceName) then
FOnReferenceName(Self, CurName);
Target := FindNestedComponent(CurFixup.FInstanceRoot, CurName);
RemoveGlobalFixup(CurFixup);
if (not Assigned(Target)) and CurFixup.MakeGlobalReference then
begin
GlobalFixupList.Add(CurFixup);
FFixups[i] := nil;
end else
SetOrdProp(CurFixup.FInstance, CurFixup.FPropInfo, LongInt(Target));
end;
finally
FreeFixups;
end;
end;
procedure TReader.FixupReferences;
var
i: Integer;
begin
DoFixupReferences;
GlobalFixupReferences;
for i := 0 to FLoaded.Count - 1 do
TComponent(FLoaded[I]).Loaded;
end;
procedure TReader.FreeFixups;
var
i: Integer;
begin
if Assigned(FFixups) then
begin
for i := 0 to FFixups.Count - 1 do
TPropFixup(FFixups[I]).Free;
FFixups.Free;
FFixups := nil;
end;
end;
function TReader.NextValue: TValueType;
begin
Result := FDriver.NextValue;
end;
procedure TReader.PropertyError;
begin
FDriver.SkipValue;
raise EReadError.Create(SUnknownProperty);
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;
procedure TReader.ReadCollection(Collection: TCollection);
var
Item: TPersistent;
begin
Collection.BeginUpdate;
try
if not EndOfList then
Collection.Clear;
while not EndOfList do
begin
if FDriver.NextValue in [vaInt8, vaInt16, vaInt32] then
ReadInteger; { Skip order value }
Item := Collection.Add;
ReadListBegin;
while not EndOfList do
ReadProperty(Item);
ReadListEnd;
end;
ReadListEnd;
finally
Collection.EndUpdate;
end;
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;
ChildPos: Integer;
SavedParent, SavedLookupRoot: TComponent;
ComponentClass: TComponentClass;
NewComponent: TComponent;
begin
FDriver.BeginComponent(Flags, ChildPos, CompClassName, Name);
SavedParent := Parent;
SavedLookupRoot := FLookupRoot;
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);
if not (ffInherited in Flags) then
try
Result.SetParentComponent(Parent);
if Assigned(FOnSetName) then
FOnSetName(Self, Result, Name);
Result.Name := Name;
if Assigned(FindGlobalComponent) and
(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);
Result.ReadState(Self);
Exclude(Result.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
FLoaded.Add(Result);
except
if ((ffInherited in Flags) or Assigned(Component)) then
Result.Free;
raise;
end;
finally
Parent := SavedParent;
FLookupRoot := SavedLookupRoot;
end;
end;
procedure TReader.ReadData(Instance: TComponent);
var
DoFreeFixups: Boolean;
SavedOwner, SavedParent: TComponent;
begin
if not Assigned(FFixups) then
begin
FFixups := TList.Create;
DoFreeFixups := True;
end else
DoFreeFixups := False;
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) }
if DoFreeFixups then
DoFixupReferences;
finally
if DoFreeFixups then
FreeFixups;
end;
end;
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;
{!!!: function TReader.ReadCurrency: Currency;
begin
if FDriver.NextValue = vaCurrency then
begin
FDriver.ReadValue;
Result := FDriver.ReadCurrency;
end else
Result := ReadInteger;
end;}
function TReader.ReadDate: TDateTime;
begin
if FDriver.NextValue = vaDate then
begin
FDriver.ReadValue;
Result := FDriver.ReadDate;
end else
Result := ReadInteger;
end;
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(GetOrdProp(Instance, PropInfo))
else
Obj := nil;
if not Obj.InheritsFrom(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 not Assigned(PropInfo) then
if not HandleMissingProperty(false) then exit;
if Assigned(PropInfo) then
ReadPropValue(Instance, PropInfo)
else
begin
FCanHandleExcepts := False;
Instance.DefineProperties(Self);
FCanHandleExcepts := True;
if Length(FPropName) > 0 then
PropertyError;
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));
tkEnumeration:
begin
Value := GetEnumValue(PropType, ReadIdent);
if Value = -1 then
raise EReadError.Create(SInvalidPropertyValue);
SetOrdProp(Instance, PropInfo, Value);
end;
tkFloat:
SetFloatProp(Instance, PropInfo, ReadFloat);
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, tkWString:
begin
TmpStr:=ReadString;
if Assigned(FOnReadStringProperty) then
FOnReadStringProperty(Self,Instance,PropInfo,TmpStr);
SetStrProp(Instance, PropInfo, TmpStr);
end;
{!!!: tkVariant}
tkClass:
case FDriver.NextValue of
vaNil:
begin
FDriver.ReadValue;
SetOrdProp(Instance, PropInfo, 0)
end;
vaCollection:
begin
FDriver.ReadValue;
ReadCollection(TCollection(GetOrdProp(Instance, PropInfo)));
end
else
FFixups.Add(TPropFixup.Create(Instance, Root, PropInfo, '', ReadIdent));
end;
tkInt64: 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];
if Assigned(FindGlobalComponent) then
begin
{ 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 else
Result.Name := '';
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
raise EReadError.Create(SInvalidPropertyValue);
end;
{!!!: function TReader.ReadWideString: WideString;
begin
CheckValue(vaWString);
Result := FDriver.ReadWideString;
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);
vaExtended:
Writer.WriteFloat(ReadFloat);
{!!!: 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);}
vaSingle:
Writer.WriteSingle(ReadSingle);
{!!!: vaCurrency:
Writer.WriteCurrency(ReadCurrency);}
vaDate:
Writer.WriteDate(ReadDate);
vaInt64:
Writer.WriteInteger(ReadInt64);
end;
end;
function TReader.FindComponentClass(const AClassName: String): TComponentClass;
begin
TPersistentClass(Result) := GetFieldClass(Root, AClassName);
if not Assigned(Result) and Assigned(FLookupRoot) and (FLookupRoot <> Root) then
TPersistentClass(Result) := GetFieldClass(FLookupRoot, AClassName);
if Assigned(FOnFindComponentClass) then
FOnFindComponentClass(Self, AClassName, Result);
if not (Assigned(Result) and Result.InheritsFrom(TComponent)) then
raise EClassNotFound.CreateFmt(SClassNotFound, [AClassName]);
end;
{
$Log$
Revision 1.4 2005-02-14 17:13:11 peter
* truncate log
}