fpc/rtl/objpas/classes/writer.inc

1306 lines
35 KiB
PHP

{%MainUnit classes.pp}
{
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.
**********************************************************************}
{****************************************************************************}
{* TBinaryObjectWriter *}
{****************************************************************************}
{$ifndef FPUNONE}
{$IFNDEF FPC_HAS_TYPE_EXTENDED}
procedure DoubleToExtended(d : double; e : pointer);
var mant : qword;
exp : smallint;
sign : boolean;
begin
mant:=(qword(d) and $000FFFFFFFFFFFFF) shl 12;
exp :=(qword(d) shr 52) and $7FF;
sign:=(qword(d) and $8000000000000000)<>0;
case exp of
0 : begin
if mant<>0 then //denormalized value: hidden bit is 0. normalize it
begin
exp:=16383-1022;
while (mant and $8000000000000000)=0 do
begin
dec(exp);
mant:=mant shl 1;
end;
dec(exp); //don't shift, most significant bit is not hidden in extended
end;
end;
2047 : exp:=$7FFF //either infinity or NaN
else
begin
inc(exp,16383-1023);
mant:=(mant shr 1) or $8000000000000000; //unhide hidden bit
end;
end;
if sign then exp:=exp or $8000;
mant:=NtoLE(mant);
exp:=NtoLE(word(exp));
move(mant,pbyte(e)[0],8); //mantissa : bytes 0..7
move(exp,pbyte(e)[8],2); //exponent and sign: bytes 8..9
end;
{$ENDIF}
{$endif}
procedure TBinaryObjectWriter.WriteWord(w : word); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
begin
w:=NtoLE(w);
Write(w,2);
end;
procedure TBinaryObjectWriter.WriteDWord(lw : longword); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
begin
lw:=NtoLE(lw);
Write(lw,4);
end;
procedure TBinaryObjectWriter.WriteQWord(qw : qword); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
begin
qw:=NtoLE(qw);
Write(qw,8);
end;
{$ifndef FPUNONE}
procedure TBinaryObjectWriter.WriteExtended(e : 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}
{$IFDEF FPC_DOUBLE_HILO_SWAPPED}
{ SwapDoubleHiLo defined in reader.inc }
SwapDoubleHiLo(e);
{$ENDIF FPC_DOUBLE_HILO_SWAPPED}
DoubleToExtended(e,@(ext[0]));
Write(ext[0],10);
{$ELSE}
Write(e,sizeof(e));
{$ENDIF}
end;
{$endif}
constructor TBinaryObjectWriter.Create(Stream: TStream; BufSize: Integer);
begin
inherited Create;
If (Stream=Nil) then
Raise EWriteError.Create(SEmptyStreamIllegalWriter);
FStream := Stream;
FBufSize := BufSize;
GetMem(FBuffer, BufSize);
end;
destructor TBinaryObjectWriter.Destroy;
begin
// Flush all data which hasn't been written yet
FlushBuffer;
if Assigned(FBuffer) then
FreeMem(FBuffer, FBufSize);
inherited Destroy;
end;
procedure TBinaryObjectWriter.BeginCollection;
begin
WriteValue(vaCollection);
end;
procedure TBinaryObjectWriter.WriteSignature;
begin
if Version=TBinaryObjectReader.TBOVersion.boVersion1 then
Write(FilerSignature1, SizeOf(FilerSignature1))
else
Write(FilerSignature, SizeOf(FilerSignature));
end;
procedure TBinaryObjectWriter.BeginComponent(Component: TComponent;
Flags: TFilerFlags; ChildPos: Integer);
var
Prefix: Byte;
begin
{ Only write the flags if they are needed! }
if Flags <> [] then
begin
Prefix := TFilerFlagsInt(Flags) or $f0;
Write(Prefix, 1);
if ffChildPos in Flags then
WriteInteger(ChildPos);
end;
if Version=TBinaryObjectReader.TBOVersion.boVersion1 then
WriteString(Component.UnitName+TBinaryObjectReader.UnitnameSeparator+Component.ClassName)
else
WriteStr(Component.ClassName);
WriteStr(Component.Name);
end;
procedure TBinaryObjectWriter.BeginList;
begin
WriteValue(vaList);
end;
procedure TBinaryObjectWriter.EndList;
begin
WriteValue(vaNull);
end;
procedure TBinaryObjectWriter.BeginProperty(const PropName: String);
begin
WriteStr(PropName);
end;
procedure TBinaryObjectWriter.EndProperty;
begin
end;
procedure TBinaryObjectWriter.WriteBinary(const Buffer; Count: LongInt);
begin
WriteValue(vaBinary);
WriteDWord(longword(Count));
Write(Buffer, Count);
end;
procedure TBinaryObjectWriter.WriteBoolean(Value: Boolean);
begin
if Value then
WriteValue(vaTrue)
else
WriteValue(vaFalse);
end;
{$ifndef FPUNONE}
procedure TBinaryObjectWriter.WriteFloat(const Value: Extended);
begin
WriteValue(vaExtended);
WriteExtended(Value);
end;
procedure TBinaryObjectWriter.WriteSingle(const Value: Single);
begin
WriteValue(vaSingle);
WriteDWord(longword(Value));
end;
{$endif}
procedure TBinaryObjectWriter.WriteCurrency(const Value: Currency);
begin
WriteValue(vaCurrency);
WriteQWord(qword(Value));
end;
{$ifndef FPUNONE}
procedure TBinaryObjectWriter.WriteDate(const Value: TDateTime);
begin
WriteValue(vaDate);
WriteQWord(qword(Value));
end;
{$endif}
procedure TBinaryObjectWriter.WriteIdent(const Ident: string);
begin
Case UpperCase(Ident) of
'NIL' : WriteValue(vaNil);
'FALSE' : WriteValue(vaFalse);
'TRUE' : WriteValue(vaTrue);
'NULL' : WriteValue(vaNull);
else
WriteValue(vaIdent);
WriteStr(Ident);
end;
end;
procedure TBinaryObjectWriter.WriteInteger(Value: Int64);
var
s: ShortInt;
i: SmallInt;
l: Longint;
begin
{ Use the smallest possible integer type for the given value: }
if (Value >= -128) and (Value <= 127) then
begin
WriteValue(vaInt8);
s := Value;
Write(s, 1);
end else if (Value >= -32768) and (Value <= 32767) then
begin
WriteValue(vaInt16);
i := Value;
WriteWord(word(i));
end else if (Value >= -$80000000) and (Value <= $7fffffff) then
begin
WriteValue(vaInt32);
l := Value;
WriteDWord(longword(l));
end else
begin
WriteValue(vaInt64);
WriteQWord(qword(Value));
end;
end;
procedure TBinaryObjectWriter.WriteUInt64(Value: QWord);
var
s: ShortInt;
i: SmallInt;
l: Longint;
begin
{ Use the smallest possible integer type for the given value: }
if (Value <= 127) then
begin
WriteValue(vaInt8);
s := Value;
Write(s, 1);
end else if (Value <= 32767) then
begin
WriteValue(vaInt16);
i := Value;
WriteWord(word(i));
end else if (Value <= $7fffffff) then
begin
WriteValue(vaInt32);
l := Value;
WriteDWord(longword(l));
end else
begin
WriteValue(vaQWord);
WriteQWord(Value);
end;
end;
procedure TBinaryObjectWriter.WriteMethodName(const Name: String);
begin
if Length(Name) > 0 then
begin
WriteValue(vaIdent);
WriteStr(Name);
end else
WriteValue(vaNil);
end;
procedure TBinaryObjectWriter.WriteSet(Value: LongInt; SetType: Pointer);
type
tset = set of 0..31;
var
i: Integer;
begin
WriteValue(vaSet);
for i := 0 to 31 do
begin
if (i in tset(Value)) then
WriteStr(GetEnumName(PTypeInfo(SetType), i));
end;
WriteStr('');
end;
procedure TBinaryObjectWriter.WriteString(const Value: RawByteString);
var
i: Integer;
b: byte;
begin
i := Length(Value);
if i <= 255 then
begin
WriteValue(vaString);
b := i;
Write(b, 1);
end else
begin
WriteValue(vaLString);
WriteDWord(longword(i));
end;
if i > 0 then
Write(Value[1], i);
end;
procedure TBinaryObjectWriter.WriteWideString(const Value: WideString);
var len : longword;
{$IFDEF ENDIAN_BIG}
i : integer;
ws : widestring;
{$ENDIF}
begin
WriteValue(vaWString);
len:=Length(Value);
WriteDWord(len);
if len > 0 then
begin
{$IFDEF ENDIAN_BIG}
setlength(ws,len);
for i:=1 to len do
ws[i]:=widechar(SwapEndian(word(Value[i])));
Write(ws[1], len*sizeof(widechar));
{$ELSE}
Write(Value[1], len*sizeof(widechar));
{$ENDIF}
end;
end;
procedure TBinaryObjectWriter.WriteUnicodeString(const Value: UnicodeString);
var len : longword;
{$IFDEF ENDIAN_BIG}
i : integer;
us : UnicodeString;
{$ENDIF}
begin
WriteValue(vaUString);
len:=Length(Value);
WriteDWord(len);
if len > 0 then
begin
{$IFDEF ENDIAN_BIG}
setlength(us,len);
for i:=1 to len do
us[i]:=widechar(SwapEndian(word(Value[i])));
Write(us[1], len*sizeof(UnicodeChar));
{$ELSE}
Write(Value[1], len*sizeof(UnicodeChar));
{$ENDIF}
end;
end;
procedure TBinaryObjectWriter.WriteVariant(const VarValue: Variant);
begin
{ The variant manager will handle varbyref and vararray transparently for us
}
case (tvardata(VarValue).vtype and varTypeMask) of
varEmpty:
begin
WriteValue(vaNil);
end;
varNull:
begin
WriteValue(vaNull);
end;
{ all integer sizes must be split for big endian systems }
varShortInt,varSmallInt,varInteger,varInt64:
begin
WriteInteger(VarValue);
end;
varQWord:
begin
WriteUInt64(VarValue);
end;
varBoolean:
begin
WriteBoolean(VarValue);
end;
varCurrency:
begin
WriteCurrency(VarValue);
end;
{$ifndef fpunone}
varSingle:
begin
WriteSingle(VarValue);
end;
varDouble:
begin
WriteFloat(VarValue);
end;
varDate:
begin
WriteDate(VarValue);
end;
{$endif fpunone}
varOleStr,varString:
begin
WriteWideString(VarValue);
end;
else
raise EWriteError.CreateFmt(SUnsupportedPropertyVariantType, [Ord(tvardata(VarValue).vtype)]);
end;
end;
procedure TBinaryObjectWriter.FlushBuffer;
begin
FStream.WriteBuffer(FBuffer^, FBufPos);
FBufPos := 0;
end;
procedure TBinaryObjectWriter.Write(const Buffer; Count: Longint);
var
CopyNow: LongInt;
SourceBuf: PAnsiChar;
begin
SourceBuf:=@Buffer;
while Count > 0 do
begin
CopyNow := Count;
if CopyNow > FBufSize - FBufPos then
CopyNow := FBufSize - FBufPos;
Move(SourceBuf^, PAnsiChar(FBuffer)[FBufPos], CopyNow);
Dec(Count, CopyNow);
Inc(FBufPos, CopyNow);
inc(SourceBuf, CopyNow);
if FBufPos = FBufSize then
FlushBuffer;
end;
end;
procedure TBinaryObjectWriter.WriteValue(Value: TValueType);
var
b: byte;
begin
b := byte(Value);
Write(b, 1);
end;
procedure TBinaryObjectWriter.WriteStr(const Value: RawByteString);
var
i: integer;
b: byte;
begin
i := Length(Value);
if i > 255 then
i := 255;
b := i;
Write(b, 1);
if i > 0 then
Write(Value[1], i);
end;
{****************************************************************************}
{* TWriter *}
{****************************************************************************}
constructor TWriter.Create(ADriver: TAbstractObjectWriter);
begin
inherited Create;
FDriver := ADriver;
end;
constructor TWriter.Create(Stream: TStream; BufSize: Integer);
begin
inherited Create;
If (Stream=Nil) then
Raise EWriteError.Create(SEmptyStreamIllegalWriter);
FDriver := CreateDriver(Stream, BufSize);
FDestroyDriver := True;
end;
destructor TWriter.Destroy;
begin
if FDestroyDriver then
FDriver.Free;
inherited Destroy;
end;
procedure TWriter.FlushBuffer;
begin
Driver.FlushBuffer;
end;
function TWriter.CreateDriver(Stream: TStream; BufSize: Integer): TAbstractObjectWriter;
begin
Result := TBinaryObjectWriter.Create(Stream, BufSize);
end;
Type
TPosComponent = Class(TObject)
FPos : Integer;
FComponent : TComponent;
Constructor Create(APos : Integer; AComponent : TComponent);
end;
Constructor TPosComponent.Create(APos : Integer; AComponent : TComponent);
begin
FPos:=APos;
FComponent:=AComponent;
end;
// Used as argument for calls to TComponent.GetChildren:
procedure TWriter.AddToAncestorList(Component: TComponent);
begin
FAncestors.AddObject(Component.Name,TPosComponent.Create(FAncestors.Count,Component));
end;
procedure TWriter.DefineProperty(const Name: string; ReadData: TReaderProc;
AWriteData: TWriterProc; HasData: Boolean);
begin
if HasData and Assigned(AWriteData) then
begin
// Write the property name and then the data itself
Driver.BeginProperty(FPropPath + Name);
AWriteData(Self);
Driver.EndProperty;
end;
end;
procedure TWriter.DefineBinaryProperty(const Name: string; ReadData,
AWriteData: TStreamProc; HasData: Boolean);
begin
if HasData and Assigned(AWriteData) then
begin
// Write the property name and then the data itself
Driver.BeginProperty(FPropPath + Name);
WriteBinary(AWriteData);
Driver.EndProperty;
end;
end;
procedure TWriter.Write(const Buffer; Count: Longint);
begin
//This should give an exception if write is not implemented (i.e. TTextObjectWriter)
//but should work with TBinaryObjectWriter.
Driver.Write(Buffer, Count);
end;
procedure TWriter.SetRoot(ARoot: TComponent);
begin
inherited SetRoot(ARoot);
// Use the new root as lookup root too
FLookupRoot := ARoot;
end;
procedure TWriter.WriteSignature;
begin
FDriver.WriteSignature;
end;
procedure TWriter.WriteBinary(AWriteData: TStreamProc);
var
MemBuffer: TMemoryStream;
BufferSize: Longint;
begin
{ First write the binary data into a memory stream, then copy this buffered
stream into the writing destination. This is necessary as we have to know
the size of the binary data in advance (we're assuming that seeking within
the writer stream is not possible) }
MemBuffer := TMemoryStream.Create;
try
AWriteData(MemBuffer);
BufferSize := MemBuffer.Size;
Driver.WriteBinary(MemBuffer.Memory^, BufferSize);
finally
MemBuffer.Free;
end;
end;
procedure TWriter.WriteBoolean(Value: Boolean);
begin
Driver.WriteBoolean(Value);
end;
procedure TWriter.WriteChar(Value: AnsiChar);
begin
WriteString(Value);
end;
procedure TWriter.WriteWideChar(Value: WideChar);
begin
WriteWideString(Value);
end;
procedure TWriter.WriteCollection(Value: TCollection);
var
i: Integer;
begin
Driver.BeginCollection;
if Assigned(Value) then
for i := 0 to Value.Count - 1 do
begin
{ Each collection item needs its own ListBegin/ListEnd tag, or else the
reader wouldn't be able to know where an item ends and where the next
one starts }
WriteListBegin;
WriteProperties(Value.Items[i]);
WriteListEnd;
end;
WriteListEnd;
end;
procedure TWriter.DetermineAncestor(Component : TComponent);
Var
I : Integer;
begin
// Should be set only when we write an inherited with children.
if Not Assigned(FAncestors) then
exit;
I:=FAncestors.IndexOf(Component.Name);
If (I=-1) then
begin
FAncestor:=Nil;
FAncestorPos:=-1;
end
else
With TPosComponent(FAncestors.Objects[i]) do
begin
FAncestor:=FComponent;
FAncestorPos:=FPos;
end;
end;
procedure TWriter.DoFindAncestor(Component : TComponent);
Var
C : TComponent;
begin
if Assigned(FOnFindAncestor) then
if (Ancestor=Nil) or (Ancestor is TComponent) then
begin
C:=TComponent(Ancestor);
FOnFindAncestor(Self,Component,Component.Name,C,FRootAncestor);
Ancestor:=C;
end;
end;
procedure TWriter.WriteComponent(Component: TComponent);
var
SA : TPersistent;
SR, SRA : TComponent;
begin
SR:=FRoot;
SA:=FAncestor;
SRA:=FRootAncestor;
Try
Component.FComponentState:=Component.FComponentState+[csWriting];
Try
// Possibly set ancestor.
DetermineAncestor(Component);
DoFindAncestor(Component); // Mainly for IDE when a parent form had an ancestor renamed...
// Will call WriteComponentData.
Component.WriteState(Self);
FDriver.EndList;
Finally
Component.FComponentState:=Component.FComponentState-[csWriting];
end;
Finally
FAncestor:=SA;
FRoot:=SR;
FRootAncestor:=SRA;
end;
end;
procedure TWriter.WriteChildren(Component : TComponent);
Var
SRoot, SRootA : TComponent;
SList : TStringList;
SPos, I , SAncestorPos: Integer;
begin
// Write children list.
// While writing children, the ancestor environment must be saved
// This is recursive...
SRoot:=FRoot;
SRootA:=FRootAncestor;
SList:=FAncestors;
SPos:=FCurrentPos;
SAncestorPos:=FAncestorPos;
try
FAncestors:=Nil;
FCurrentPos:=0;
FAncestorPos:=-1;
if csInline in Component.ComponentState then
FRoot:=Component;
if (FAncestor is TComponent) then
begin
FAncestors:=TStringList.Create;
if csInline in TComponent(FAncestor).ComponentState then
FRootAncestor := TComponent(FAncestor);
TComponent(FAncestor).GetChildren(@AddToAncestorList,FRootAncestor);
FAncestors.Sorted:=True;
end;
try
Component.GetChildren(@WriteComponent, FRoot);
Finally
If Assigned(Fancestors) then
For I:=0 to FAncestors.Count-1 do
FAncestors.Objects[i].Free;
FreeAndNil(FAncestors);
end;
finally
FAncestors:=Slist;
FRoot:=SRoot;
FRootAncestor:=SRootA;
FCurrentPos:=SPos;
FAncestorPos:=SAncestorPos;
end;
end;
procedure TWriter.WriteComponentData(Instance: TComponent);
var
Flags: TFilerFlags;
begin
Flags := [];
If (Assigned(FAncestor)) and //has ancestor
(not (csInline in Instance.ComponentState) or // no inline component
// .. or the inline component is inherited
(csAncestor in Instance.Componentstate) and (FAncestors <> nil)) then
Flags:=[ffInherited]
else If csInline in Instance.ComponentState then
Flags:=[ffInline];
If (FAncestors<>Nil) and ((FCurrentPos<>FAncestorPos) or (FAncestor=Nil)) then
Include(Flags,ffChildPos);
FDriver.BeginComponent(Instance,Flags,FCurrentPos);
If (FAncestors<>Nil) then
Inc(FCurrentPos);
WriteProperties(Instance);
WriteListEnd;
// Needs special handling of ancestor.
If not IgnoreChildren then
WriteChildren(Instance);
end;
procedure TWriter.WriteDescendent(ARoot: TComponent; AAncestor: TComponent);
begin
FRoot := ARoot;
FAncestor := AAncestor;
FRootAncestor := AAncestor;
FLookupRoot := ARoot;
WriteSignature;
WriteComponent(ARoot);
end;
{$ifndef FPUNONE}
procedure TWriter.WriteFloat(const Value: Extended);
begin
Driver.WriteFloat(Value);
end;
procedure TWriter.WriteSingle(const Value: Single);
begin
Driver.WriteSingle(Value);
end;
{$endif}
procedure TWriter.WriteCurrency(const Value: Currency);
begin
Driver.WriteCurrency(Value);
end;
{$ifndef FPUNONE}
procedure TWriter.WriteDate(const Value: TDateTime);
begin
Driver.WriteDate(Value);
end;
{$endif}
procedure TWriter.WriteIdent(const Ident: string);
begin
Driver.WriteIdent(Ident);
end;
procedure TWriter.WriteInteger(Value: Longint);
begin
Driver.WriteInteger(Value);
end;
procedure TWriter.WriteInteger(Value: Int64);
begin
Driver.WriteInteger(Value);
end;
procedure TWriter.WriteSet(Value: Longint; SetType: Pointer);
begin
Driver.WriteSet(Value,SetType);
end;
procedure TWriter.WriteVariant(const VarValue: Variant);
begin
Driver.WriteVariant(VarValue);
end;
procedure TWriter.WriteListBegin;
begin
Driver.BeginList;
end;
procedure TWriter.WriteListEnd;
begin
Driver.EndList;
end;
procedure TWriter.WriteProperties(Instance: TPersistent);
var PropCount,i : integer;
PropList : PPropList;
begin
PropCount:=GetPropList(Instance,PropList);
if PropCount>0 then
try
for i := 0 to PropCount-1 do
if IsStoredProp(Instance,PropList^[i]) then
WriteProperty(Instance,PropList^[i]);
Finally
Freemem(PropList);
end;
Instance.DefineProperties(Self);
end;
procedure TWriter.WriteProperty(Instance: TPersistent; PropInfo: Pointer);
var
HasAncestor: Boolean;
PropType: PTypeInfo;
Value, DefValue: LongInt;
Ident: String;
IntToIdentFn: TIntToIdent;
{$ifndef FPUNONE}
FloatValue, DefFloatValue: Extended;
{$endif}
MethodValue: TMethod;
DefMethodValue: TMethod;
WStrValue, WDefStrValue: WideString;
StrValue, DefStrValue: String;
UStrValue, UDefStrValue: UnicodeString;
AncestorObj: TObject;
C,Component: TComponent;
ObjValue: TObject;
SavedAncestor: TPersistent;
SavedPropPath, Name: String;
Int64Value, DefInt64Value: Int64;
VarValue, DefVarValue : tvardata;
BoolValue, DefBoolValue: boolean;
Handled: Boolean;
IntfValue: IInterface;
CompRef: IInterfaceComponentReference;
begin
// do not stream properties without getter
if not Assigned(PPropInfo(PropInfo)^.GetProc) then
exit;
// properties without setter are only allowed, if they are subcomponents
PropType := PPropInfo(PropInfo)^.PropType;
if not Assigned(PPropInfo(PropInfo)^.SetProc) then begin
if PropType^.Kind<>tkClass then
exit;
ObjValue := TObject(GetObjectProp(Instance, PropInfo));
if not (assigned(ObjValue) and
ObjValue.InheritsFrom(TComponent) and
(csSubComponent in TComponent(ObjValue).ComponentStyle)) then
exit;
end;
{ Check if the ancestor can be used }
HasAncestor := Assigned(Ancestor) and ((Instance = Root) or
(Instance.ClassType = Ancestor.ClassType));
//writeln('TWriter.WriteProperty Name=',PropType^.Name,' Kind=',GetEnumName(TypeInfo(TTypeKind),ord(PropType^.Kind)),' HasAncestor=',HasAncestor);
case PropType^.Kind of
tkInteger, tkChar, tkEnumeration, tkSet, tkWChar:
begin
Value := GetOrdProp(Instance, PropInfo);
if HasAncestor then
DefValue := GetOrdProp(Ancestor, PropInfo)
else
DefValue := PPropInfo(PropInfo)^.Default;
// writeln(PPropInfo(PropInfo)^.Name, ', HasAncestor=', ord(HasAncestor), ', Value=', Value, ', Default=', DefValue);
if (Value <> DefValue) or (DefValue=longint($80000000)) then
begin
Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
case PropType^.Kind of
tkInteger:
begin
// Check if this integer has a string identifier
IntToIdentFn := FindIntToIdent(PPropInfo(PropInfo)^.PropType);
if Assigned(IntToIdentFn) and IntToIdentFn(Value, Ident) then
// Integer can be written a human-readable identifier
WriteIdent(Ident)
else
// Integer has to be written just as number
WriteInteger(Value);
end;
tkChar:
WriteChar(Chr(Value));
tkWChar:
WriteWideChar(WideChar(Value));
tkSet:
Driver.WriteSet(Value, GetTypeData(PropType)^.CompType);
tkEnumeration:
WriteIdent(GetEnumName(PropType, Value));
end;
Driver.EndProperty;
end;
end;
{$ifndef FPUNONE}
tkFloat:
begin
FloatValue := GetFloatProp(Instance, PropInfo);
if HasAncestor then
DefFloatValue := GetFloatProp(Ancestor, PropInfo)
else
begin
DefValue :=PPropInfo(PropInfo)^.Default;
DefFloatValue:=PSingle(@PPropInfo(PropInfo)^.Default)^;
end;
if (FloatValue<>DefFloatValue) or (not HasAncestor and (DefValue=longint($80000000))) then
begin
Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
WriteFloat(FloatValue);
Driver.EndProperty;
end;
end;
{$endif}
tkMethod:
begin
MethodValue := GetMethodProp(Instance, PropInfo);
if HasAncestor then
DefMethodValue := GetMethodProp(Ancestor, PropInfo)
else begin
DefMethodValue.Data := nil;
DefMethodValue.Code := nil;
end;
Handled:=false;
if Assigned(OnWriteMethodProperty) then
OnWriteMethodProperty(Self,Instance,PPropInfo(PropInfo),MethodValue,
DefMethodValue,Handled);
if (not Handled) and
(MethodValue.Code <> DefMethodValue.Code) and
((not Assigned(MethodValue.Code)) or
((Length(FLookupRoot.MethodName(MethodValue.Code)) > 0))) then
begin
Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
if Assigned(MethodValue.Code) then
Driver.WriteMethodName(FLookupRoot.MethodName(MethodValue.Code))
else
Driver.WriteMethodName('');
Driver.EndProperty;
end;
end;
tkSString, tkLString, tkAString:
begin
StrValue := GetStrProp(Instance, PropInfo);
if HasAncestor then
DefStrValue := GetStrProp(Ancestor, PropInfo)
else
begin
DefValue :=PPropInfo(PropInfo)^.Default;
SetLength(DefStrValue, 0);
end;
if (StrValue<>DefStrValue) or (not HasAncestor and (DefValue=longint($80000000))) then
begin
Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
if Assigned(FOnWriteStringProperty) then
FOnWriteStringProperty(Self,Instance,PropInfo,StrValue);
WriteString(StrValue);
Driver.EndProperty;
end;
end;
tkWString:
begin
WStrValue := GetWideStrProp(Instance, PropInfo);
if HasAncestor then
WDefStrValue := GetWideStrProp(Ancestor, PropInfo)
else
begin
DefValue :=PPropInfo(PropInfo)^.Default;
SetLength(WDefStrValue, 0);
end;
if (WStrValue<>WDefStrValue) or (not HasAncestor and (DefValue=longint($80000000))) then
begin
Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
WriteWideString(WStrValue);
Driver.EndProperty;
end;
end;
tkUString:
begin
UStrValue := GetUnicodeStrProp(Instance, PropInfo);
if HasAncestor then
UDefStrValue := GetUnicodeStrProp(Ancestor, PropInfo)
else
begin
DefValue :=PPropInfo(PropInfo)^.Default;
SetLength(UDefStrValue, 0);
end;
if (UStrValue<>UDefStrValue) or (not HasAncestor and (DefValue=longint($80000000))) then
begin
Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
WriteUnicodeString(UStrValue);
Driver.EndProperty;
end;
end;
tkVariant:
begin
{ Ensure that a Variant manager is installed }
if not assigned(VarClearProc) then
raise EWriteError.Create(SErrNoVariantSupport);
VarValue := tvardata(GetVariantProp(Instance, PropInfo));
if HasAncestor then
DefVarValue := tvardata(GetVariantProp(Ancestor, PropInfo))
else
FillChar(DefVarValue,sizeof(DefVarValue),0);
if (CompareByte(VarValue,DefVarValue,sizeof(VarValue)) <> 0) then
begin
Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
{ can't use variant() typecast, pulls in variants unit }
WriteVariant(pvariant(@VarValue)^);
Driver.EndProperty;
end;
end;
tkClass:
begin
ObjValue := TObject(GetObjectProp(Instance, PropInfo));
if HasAncestor then
begin
AncestorObj := TObject(GetObjectProp(Ancestor, PropInfo));
if (AncestorObj is TComponent) and
(ObjValue is TComponent) then
begin
//writeln('TWriter.WriteProperty AncestorObj=',TComponent(AncestorObj).Name,' OwnerFit=',TComponent(AncestorObj).Owner = FRootAncestor,' ',TComponent(ObjValue).Name,' OwnerFit=',TComponent(ObjValue).Owner = Root);
if (AncestorObj<> ObjValue) and
(TComponent(AncestorObj).Owner = FRootAncestor) and
(TComponent(ObjValue).Owner = Root) and
(UpperCase(TComponent(AncestorObj).Name) = UpperCase(TComponent(ObjValue).Name)) then
begin
// different components, but with the same name
// treat it like an override
AncestorObj := ObjValue;
end;
end;
end else
AncestorObj := nil;
if not Assigned(ObjValue) then
begin
if ObjValue <> AncestorObj then
begin
Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
Driver.WriteIdent('NIL');
Driver.EndProperty;
end
end
else if ObjValue.InheritsFrom(TPersistent) then
begin
{ Subcomponents are streamed the same way as persistents }
if ObjValue.InheritsFrom(TComponent)
and ((not (csSubComponent in TComponent(ObjValue).ComponentStyle))
or ((TComponent(ObjValue).Owner<>Instance) and (TComponent(ObjValue).Owner<>Nil))) then
begin
Component := TComponent(ObjValue);
if (ObjValue <> AncestorObj)
and not (csTransient in Component.ComponentStyle) then
begin
Name:= '';
C:= Component;
While (C<>Nil) and (C.Name<>'') do
begin
If (Name<>'') Then
Name:='.'+Name;
if C.Owner = LookupRoot then
begin
Name := C.Name+Name;
break;
end
else if C = LookupRoot then
begin
Name := 'Owner' + Name;
break;
end;
Name:=C.Name + Name;
C:= C.Owner;
end;
if (C=nil) and (Component.Owner=nil) then
if (Name<>'') then //foreign root
Name:=Name+'.Owner';
if Length(Name) > 0 then
begin
Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
WriteIdent(Name);
Driver.EndProperty;
end; // length Name>0
end; //(ObjValue <> AncestorObj)
end // ObjValue.InheritsFrom(TComponent)
else
begin
SavedAncestor := Ancestor;
SavedPropPath := FPropPath;
try
FPropPath := FPropPath + PPropInfo(PropInfo)^.Name + '.';
if HasAncestor then
Ancestor := TPersistent(GetObjectProp(Ancestor, PropInfo));
WriteProperties(TPersistent(ObjValue));
finally
Ancestor := SavedAncestor;
FPropPath := SavedPropPath;
end;
if ObjValue.InheritsFrom(TCollection) then
begin
if (not HasAncestor) or (not CollectionsEqual(TCollection(ObjValue),
TCollection(GetObjectProp(Ancestor, PropInfo)),root,rootancestor)) then
begin
Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
SavedPropPath := FPropPath;
try
SetLength(FPropPath, 0);
WriteCollection(TCollection(ObjValue));
finally
FPropPath := SavedPropPath;
Driver.EndProperty;
end;
end;
end // Tcollection
end;
end; // Inheritsfrom(TPersistent)
end;
tkInt64, tkQWord:
begin
Int64Value := GetInt64Prop(Instance, PropInfo);
if HasAncestor then
DefInt64Value := GetInt64Prop(Ancestor, PropInfo)
else
DefInt64Value := PPropInfo(PropInfo)^.Default;
if (Int64Value <> DefInt64Value) or (DefInt64Value=longint($80000000)) then
begin
Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
WriteInteger(Int64Value);
Driver.EndProperty;
end;
end;
tkBool:
begin
BoolValue := GetOrdProp(Instance, PropInfo)<>0;
if HasAncestor then
DefBoolValue := GetOrdProp(Ancestor, PropInfo)<>0
else
begin
DefBoolValue := PPropInfo(PropInfo)^.Default<>0;
DefValue:=PPropInfo(PropInfo)^.Default;
end;
// writeln(PPropInfo(PropInfo)^.Name, ', HasAncestor=', ord(HasAncestor), ', Value=', Value, ', Default=', DefBoolValue);
if (BoolValue<>DefBoolValue) or (DefValue=longint($80000000)) then
begin
Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
WriteBoolean(BoolValue);
Driver.EndProperty;
end;
end;
tkInterface:
begin
IntfValue := GetInterfaceProp(Instance, PropInfo);
if Assigned(IntfValue) and Supports(IntfValue, IInterfaceComponentReference, CompRef) then
begin
Component := CompRef.GetComponent;
if HasAncestor then
begin
AncestorObj := TObject(GetObjectProp(Ancestor, PropInfo));
if (AncestorObj is TComponent) then
begin
//writeln('TWriter.WriteProperty AncestorObj=',TComponent(AncestorObj).Name,' OwnerFit=',TComponent(AncestorObj).Owner = FRootAncestor,' ',TComponent(ObjValue).Name,' OwnerFit=',TComponent(ObjValue).Owner = Root);
if (AncestorObj<> Component) and
(TComponent(AncestorObj).Owner = FRootAncestor) and
(Component.Owner = Root) and
(UpperCase(TComponent(AncestorObj).Name) = UpperCase(Component.Name)) then
begin
// different components, but with the same name
// treat it like an override
AncestorObj := Component;
end;
end;
end else
AncestorObj := nil;
if not Assigned(Component) then
begin
if Component <> AncestorObj then
begin
Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
Driver.WriteIdent('NIL');
Driver.EndProperty;
end
end
else if ((not (csSubComponent in Component.ComponentStyle))
or ((Component.Owner<>Instance) and (Component.Owner<>Nil))) then
begin
if (Component <> AncestorObj)
and not (csTransient in Component.ComponentStyle) then
begin
Name:= '';
C:= Component;
While (C<>Nil) and (C.Name<>'') do
begin
If (Name<>'') Then
Name:='.'+Name;
if C.Owner = LookupRoot then
begin
Name := C.Name+Name;
break;
end
else if C = LookupRoot then
begin
Name := 'Owner' + Name;
break;
end;
Name:=C.Name + Name;
C:= C.Owner;
end;
if (C=nil) and (Component.Owner=nil) then
if (Name<>'') then //foreign root
Name:=Name+'.Owner';
if Length(Name) > 0 then
begin
Driver.BeginProperty(FPropPath + PPropInfo(PropInfo)^.Name);
WriteIdent(Name);
Driver.EndProperty;
end; // length Name>0
end; //(Component <> AncestorObj)
end;
end; //Assigned(IntfValue) and Supports(IntfValue,..
//else write NIL ?
end;
end;
end;
procedure TWriter.WriteRootComponent(ARoot: TComponent);
begin
WriteDescendent(ARoot, nil);
end;
procedure TWriter.WriteString(const Value: String);
begin
Driver.WriteString(Value);
end;
procedure TWriter.WriteWideString(const Value: WideString);
begin
Driver.WriteWideString(Value);
end;
procedure TWriter.WriteUnicodeString(const Value: UnicodeString);
begin
Driver.WriteUnicodeString(Value);
end;
{ TAbstractObjectWriter }
procedure TAbstractObjectWriter.FlushBuffer;
begin
// Do nothing
end;