414 lines
11 KiB
ObjectPascal
414 lines
11 KiB
ObjectPascal
|
unit X2UtPersist;
|
||
|
|
||
|
interface
|
||
|
uses
|
||
|
Classes,
|
||
|
Types,
|
||
|
TypInfo;
|
||
|
|
||
|
|
||
|
type
|
||
|
TX2IterateObjectProc = procedure(AObject: TObject; APropInfo: PPropInfo; var AContinue: Boolean) of object;
|
||
|
|
||
|
TX2CustomPersist = class(TObject)
|
||
|
private
|
||
|
FSections: TStrings;
|
||
|
protected
|
||
|
function IterateObject(AObject: TObject; ACallback: TX2IterateObjectProc): Boolean; virtual;
|
||
|
|
||
|
procedure ReadObject(AObject: TObject; APropInfo: PPropInfo; var AContinue: Boolean);
|
||
|
procedure WriteObject(AObject: TObject; APropInfo: PPropInfo; var AContinue: Boolean);
|
||
|
protected
|
||
|
function DoRead(AObject: TObject): Boolean; virtual;
|
||
|
procedure DoWrite(AObject: TObject); virtual;
|
||
|
|
||
|
function BeginSection(const AName: String): Boolean; virtual;
|
||
|
procedure EndSection(); virtual;
|
||
|
|
||
|
|
||
|
function ReadInteger(const AName: String; out AValue: Integer): Boolean; virtual; abstract;
|
||
|
function ReadFloat(const AName: String; out AValue: Extended): Boolean; virtual; abstract;
|
||
|
function ReadString(const AName: String; out AValue: String): Boolean; virtual; abstract;
|
||
|
function ReadInt64(const AName: String; out AValue: Int64): Boolean; virtual; abstract;
|
||
|
|
||
|
procedure ReadCollection(const AName: String; ACollection: TCollection); virtual;
|
||
|
procedure ReadStream(const AName: String; AStream: TStream); virtual;
|
||
|
|
||
|
|
||
|
function WriteInteger(const AName: String; AValue: Integer): Boolean; virtual; abstract;
|
||
|
function WriteFloat(const AName: String; AValue: Extended): Boolean; virtual; abstract;
|
||
|
function WriteString(const AName, AValue: String): Boolean; virtual; abstract;
|
||
|
function WriteInt64(const AName: String; AValue: Int64): Boolean; virtual; abstract;
|
||
|
|
||
|
procedure ClearCollection(); virtual;
|
||
|
procedure WriteCollection(const AName: String; ACollection: TCollection); virtual;
|
||
|
procedure WriteStream(const AName: String; AStream: TStream); virtual;
|
||
|
|
||
|
|
||
|
property Sections: TStrings read FSections;
|
||
|
public
|
||
|
constructor Create();
|
||
|
destructor Destroy(); override;
|
||
|
|
||
|
function Read(AObject: TObject): Boolean; virtual;
|
||
|
procedure Write(AObject: TObject); virtual;
|
||
|
end;
|
||
|
|
||
|
|
||
|
|
||
|
const
|
||
|
CollectionCountName = 'Count';
|
||
|
CollectionItemNamePrefix = 'Item';
|
||
|
|
||
|
|
||
|
implementation
|
||
|
uses
|
||
|
SysUtils,
|
||
|
|
||
|
X2UtStrings;
|
||
|
|
||
|
|
||
|
{ TX2CustomPersist }
|
||
|
constructor TX2CustomPersist.Create();
|
||
|
begin
|
||
|
inherited;
|
||
|
|
||
|
FSections := TStringList.Create();
|
||
|
end;
|
||
|
|
||
|
|
||
|
destructor TX2CustomPersist.Destroy();
|
||
|
begin
|
||
|
FreeAndNil(FSections);
|
||
|
|
||
|
inherited;
|
||
|
end;
|
||
|
|
||
|
|
||
|
function TX2CustomPersist.IterateObject(AObject: TObject; ACallback: TX2IterateObjectProc): Boolean;
|
||
|
var
|
||
|
propCount: Integer;
|
||
|
propList: PPropList;
|
||
|
propIndex: Integer;
|
||
|
propInfo: PPropInfo;
|
||
|
continue: Boolean;
|
||
|
|
||
|
begin
|
||
|
Result := True;
|
||
|
|
||
|
{ Iterate through published properties }
|
||
|
propCount := GetPropList(AObject.ClassInfo, tkProperties, nil);
|
||
|
if propCount > 0 then
|
||
|
begin
|
||
|
GetMem(propList, propCount * SizeOf(PPropInfo));
|
||
|
try
|
||
|
GetPropList(AObject.ClassInfo, tkProperties, propList);
|
||
|
continue := True;
|
||
|
|
||
|
for propIndex := 0 to Pred(propCount) do
|
||
|
begin
|
||
|
propInfo := propList^[propIndex];
|
||
|
ACallback(AObject, propInfo, continue);
|
||
|
|
||
|
if not continue then
|
||
|
begin
|
||
|
Result := False;
|
||
|
Break;
|
||
|
end;
|
||
|
end;
|
||
|
finally
|
||
|
FreeMem(propList, propCount * SizeOf(PPropInfo));
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
|
||
|
function TX2CustomPersist.Read(AObject: TObject): Boolean;
|
||
|
begin
|
||
|
Assert(Assigned(AObject), 'AObject must be assigned.');
|
||
|
Result := DoRead(AObject);
|
||
|
end;
|
||
|
|
||
|
|
||
|
procedure TX2CustomPersist.Write(AObject: TObject);
|
||
|
begin
|
||
|
Assert(Assigned(AObject), 'AObject must be assigned.');
|
||
|
DoWrite(AObject);
|
||
|
end;
|
||
|
|
||
|
|
||
|
|
||
|
function TX2CustomPersist.DoRead(AObject: TObject): Boolean;
|
||
|
begin
|
||
|
IterateObject(AObject, ReadObject);
|
||
|
Result := True;
|
||
|
end;
|
||
|
|
||
|
|
||
|
procedure TX2CustomPersist.DoWrite(AObject: TObject);
|
||
|
begin
|
||
|
IterateObject(AObject, WriteObject);
|
||
|
end;
|
||
|
|
||
|
|
||
|
function TX2CustomPersist.BeginSection(const AName: String): Boolean;
|
||
|
begin
|
||
|
FSections.Add(AName);
|
||
|
Result := True;
|
||
|
end;
|
||
|
|
||
|
|
||
|
procedure TX2CustomPersist.EndSection();
|
||
|
begin
|
||
|
Assert(FSections.Count > 0, 'EndSection called without BeginSection');
|
||
|
FSections.Delete(Pred(FSections.Count));
|
||
|
end;
|
||
|
|
||
|
|
||
|
|
||
|
procedure TX2CustomPersist.ReadObject(AObject: TObject; APropInfo: PPropInfo;
|
||
|
var AContinue: Boolean);
|
||
|
var
|
||
|
ordValue: Integer;
|
||
|
floatValue: Extended;
|
||
|
stringValue: String;
|
||
|
int64Value: Int64;
|
||
|
objectProp: TObject;
|
||
|
|
||
|
begin
|
||
|
{ Only read writable properties }
|
||
|
if (APropInfo^.PropType^.Kind <> tkClass) and
|
||
|
(not Assigned(APropInfo^.SetProc)) then
|
||
|
Exit;
|
||
|
|
||
|
case APropInfo^.PropType^.Kind of
|
||
|
tkInteger,
|
||
|
tkChar,
|
||
|
tkWChar:
|
||
|
if ReadInteger(APropInfo^.Name, ordValue) then
|
||
|
SetOrdProp(AObject, APropInfo, ordValue);
|
||
|
|
||
|
tkFloat:
|
||
|
if ReadFloat(APropInfo^.Name, floatValue) then
|
||
|
SetFloatProp(AObject, APropInfo, floatValue);
|
||
|
|
||
|
tkEnumeration:
|
||
|
if ReadString(APropInfo^.Name, stringValue) then
|
||
|
begin
|
||
|
ordValue := GetEnumValue(APropInfo^.PropType^, stringValue);
|
||
|
if ordValue >= 0 then
|
||
|
SetOrdProp(AObject, APropInfo, ordValue);
|
||
|
end;
|
||
|
|
||
|
tkString,
|
||
|
tkLString,
|
||
|
tkWString:
|
||
|
if ReadString(APropInfo^.Name, stringValue) then
|
||
|
SetStrProp(AObject, APropInfo, stringValue);
|
||
|
|
||
|
tkSet:
|
||
|
if ReadString(APropInfo^.Name, stringValue) then
|
||
|
begin
|
||
|
try
|
||
|
ordValue := StringToSet(APropInfo, stringValue);
|
||
|
SetOrdProp(AObject, APropInfo, ordValue);
|
||
|
except
|
||
|
on E:EPropertyConvertError do;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
tkVariant:
|
||
|
if ReadString(APropInfo^.Name, stringValue) then
|
||
|
SetVariantProp(AObject, APropInfo, stringValue);
|
||
|
|
||
|
tkInt64:
|
||
|
if ReadInt64(APropInfo^.Name, int64Value) then
|
||
|
SetInt64Prop(AObject, APropInfo, int64Value);
|
||
|
|
||
|
tkClass:
|
||
|
begin
|
||
|
objectProp := GetObjectProp(AObject, APropInfo);
|
||
|
if Assigned(objectProp) then
|
||
|
begin
|
||
|
if objectProp is TStream then
|
||
|
begin
|
||
|
ReadStream(APropInfo^.Name, TStream(objectProp));
|
||
|
end else
|
||
|
begin
|
||
|
{ Recurse into object properties }
|
||
|
if BeginSection(APropInfo^.Name) then
|
||
|
try
|
||
|
if objectProp is TCollection then
|
||
|
ReadCollection(APropInfo^.Name, TCollection(objectProp));
|
||
|
|
||
|
AContinue := IterateObject(objectProp, ReadObject);
|
||
|
finally
|
||
|
EndSection();
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
|
||
|
procedure TX2CustomPersist.WriteObject(AObject: TObject; APropInfo: PPropInfo; var AContinue: Boolean);
|
||
|
var
|
||
|
ordValue: Integer;
|
||
|
floatValue: Extended;
|
||
|
stringValue: String;
|
||
|
int64Value: Int64;
|
||
|
objectProp: TObject;
|
||
|
|
||
|
begin
|
||
|
{ Only write read/writable properties which have IsStored True }
|
||
|
if (APropInfo^.PropType^.Kind <> tkClass) and
|
||
|
(not (Assigned(APropInfo^.GetProc) and
|
||
|
Assigned(APropInfo^.SetProc) and
|
||
|
IsStoredProp(AObject, APropInfo))) then
|
||
|
Exit;
|
||
|
|
||
|
case APropInfo^.PropType^.Kind of
|
||
|
tkInteger,
|
||
|
tkChar,
|
||
|
tkWChar:
|
||
|
begin
|
||
|
ordValue := GetOrdProp(AObject, APropInfo);
|
||
|
WriteInteger(APropInfo^.Name, ordValue);
|
||
|
end;
|
||
|
|
||
|
tkFloat:
|
||
|
begin
|
||
|
floatValue := GetFloatProp(AObject, APropInfo);
|
||
|
WriteFloat(APropInfo^.Name, floatValue);
|
||
|
end;
|
||
|
|
||
|
tkEnumeration:
|
||
|
begin
|
||
|
ordValue := GetOrdProp(AObject, APropInfo);
|
||
|
stringValue := GetEnumName(APropInfo^.PropType^, ordValue);
|
||
|
WriteString(APropInfo^.Name, stringValue);
|
||
|
end;
|
||
|
|
||
|
tkString,
|
||
|
tkLString,
|
||
|
tkWString:
|
||
|
begin
|
||
|
stringValue := GetStrProp(AObject, APropInfo);
|
||
|
WriteString(APropInfo^.Name, stringValue);
|
||
|
end;
|
||
|
|
||
|
tkSet:
|
||
|
begin
|
||
|
ordValue := GetOrdProp(AObject, APropInfo);
|
||
|
stringValue := SetToString(APropInfo, ordValue, True);
|
||
|
WriteString(APropInfo^.Name, stringValue);
|
||
|
end;
|
||
|
|
||
|
tkVariant:
|
||
|
begin
|
||
|
stringValue := GetVariantProp(AObject, APropInfo);
|
||
|
WriteString(APropInfo^.Name, stringValue);
|
||
|
end;
|
||
|
|
||
|
tkInt64:
|
||
|
begin
|
||
|
int64Value := GetInt64Prop(AObject, APropInfo);
|
||
|
WriteInt64(APropInfo^.Name, int64Value);
|
||
|
end;
|
||
|
|
||
|
tkClass:
|
||
|
begin
|
||
|
objectProp := GetObjectProp(AObject, APropInfo);
|
||
|
if Assigned(objectProp) then
|
||
|
begin
|
||
|
if objectProp is TStream then
|
||
|
begin
|
||
|
WriteStream(APropInfo^.Name, TStream(objectProp));
|
||
|
end else
|
||
|
begin
|
||
|
{ Recurse into object properties }
|
||
|
if BeginSection(APropInfo^.Name) then
|
||
|
try
|
||
|
if objectProp is TCollection then
|
||
|
WriteCollection(APropInfo^.Name, TCollection(objectProp));
|
||
|
|
||
|
AContinue := IterateObject(objectProp, WriteObject);
|
||
|
finally
|
||
|
EndSection();
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
|
||
|
procedure TX2CustomPersist.ReadCollection(const AName: String; ACollection: TCollection);
|
||
|
var
|
||
|
itemCount: Integer;
|
||
|
itemIndex: Integer;
|
||
|
collectionItem: TCollectionItem;
|
||
|
|
||
|
begin
|
||
|
if ReadInteger(CollectionCountName, itemCount) then
|
||
|
begin
|
||
|
ACollection.BeginUpdate();
|
||
|
try
|
||
|
ACollection.Clear();
|
||
|
|
||
|
for itemIndex := 0 to Pred(itemCount) do
|
||
|
begin
|
||
|
if BeginSection(CollectionItemNamePrefix + IntToStr(itemIndex)) then
|
||
|
try
|
||
|
collectionItem := ACollection.Add();
|
||
|
IterateObject(collectionItem, ReadObject);
|
||
|
finally
|
||
|
EndSection();
|
||
|
end;
|
||
|
end;
|
||
|
finally
|
||
|
ACollection.EndUpdate();
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
|
||
|
procedure TX2CustomPersist.ReadStream(const AName: String; AStream: TStream);
|
||
|
begin
|
||
|
// #ToDo1 (MvR) 8-6-2007: ReadStream
|
||
|
end;
|
||
|
|
||
|
|
||
|
procedure TX2CustomPersist.ClearCollection();
|
||
|
begin
|
||
|
end;
|
||
|
|
||
|
|
||
|
procedure TX2CustomPersist.WriteCollection(const AName: String; ACollection: TCollection);
|
||
|
var
|
||
|
itemIndex: Integer;
|
||
|
|
||
|
begin
|
||
|
ClearCollection();
|
||
|
WriteInteger(CollectionCountName, ACollection.Count);
|
||
|
|
||
|
for itemIndex := 0 to Pred(ACollection.Count) do
|
||
|
begin
|
||
|
if BeginSection(CollectionItemNamePrefix + IntToStr(itemIndex)) then
|
||
|
try
|
||
|
IterateObject(ACollection.Items[itemIndex], WriteObject);
|
||
|
finally
|
||
|
EndSection();
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TX2CustomPersist.WriteStream(const AName: String; AStream: TStream);
|
||
|
begin
|
||
|
// #ToDo1 (MvR) 8-6-2007: WriteStream
|
||
|
end;
|
||
|
|
||
|
end.
|
||
|
|