unit FilerHelper;
//=== File Prolog ============================================================
// This code was developed by RiverSoftAVG.
//
//--- Notes ------------------------------------------------------------------
//
//--- Development History ---------------------------------------------------
//
// 11/2015 T. Grubb
// Initial version.
//
// File Contents:
// TReaderHelper Helper class for TReader
// TWriterHelper Helper class for TWriter
//
//--- Warning ----------------------------------------------------------------
// This software is property of RiverSoftAVG. Unauthorized use or
// duplication of this software is strictly prohibited. Authorized users
// are subject to the following restrictions:
// * RiverSoftAVG is not responsible for
// any consequence of the use of this software.
// * The origin of this software must not be misrepresented either by
// explicit claim or by omission.
// * Altered versions of this software must be plainly marked as such.
// * This notice may not be removed or altered.
//
// © 2015-2016, Thomas G. Grubb
//
//=== End File Prolog ========================================================
interface
uses
Classes, SysUtils;
resourcestring
SClassnameMismatch = 'Stream read error: %s does not match expected %s class name';
type
///
/// Helper class for TReader component
///
///
///
///
TReaderHelper = class helper for TReader
private
{ private declarations }
protected
{ protected declarations }
public
{ public declarations }
///
///
/// Reads the TPersistent object specified by Instance from a stream
///
///
/// Use the ReadPersistent method when you want to read a defined
/// property (specified by TFiler.DefineProperties method) that is a
/// TPersistent from a stream
///
///
///
///
/// To create a defined TPersistent property for your component,
/// include the FilerHelper unit in your uses statement, and then
/// override the protected DefineProperties method:
///
/// TMyComponent = class(TComponent)
/// private
/// { private declarations }
/// protected
/// { protected declarations }
/// procedure ReadMyProp(Reader: TReader);
/// procedure WriteMyProp(Writer: TWriter);
/// procedure DefineProperties(Filer: TFiler); override;
/// public
/// { public declarations }
/// property MyProp: TPersistent read FMyProp write SetMyProp;
/// published
/// { published declarations }
/// end;
/// [...]
/// procedure TMyComponent.DefineProperties(Filer: TFiler);
/// begin
/// inherited DefineProperties(Filer);
/// Filer.DefineProperty('MyProp', ReadMyProp, WriteMyProp, True);
/// end;
///
/// Then, create your reader procedure and writer procedures:
///
/// procedure TMyComponent.ReadMyProp(Reader: TReader);
/// begin
/// Reader.ReadPersistent(FMyProp);
/// end;
/// procedure TMyComponent.WriteMyProp(Writer: TWriter);
/// begin
/// Writer.WritePersistent(FMyProp);
/// end;
///
///
procedure ReadPersistent( Instance: TPersistent );
///
/// Reads the TPersistent object specified by Instance from a stream as
/// the root object
///
/// Do not use this method when you are trying to write a
/// TPersistent object out as part of another component. This method
/// is intended for when you want to save and load the Instance
/// object as the top-level object of a stream. For example, as a
/// SaveToStream/LoadFromStream method pair for the Instance
///
///
///
/// procedure TMyPersistent.LoadFromStream(const Stream: TStream);
/// var
/// Reader: TReader;
/// begin
/// Reader := TReader.Create(Stream, 4096);
/// try
/// Reader.ReadRootPersistent(Self);
/// finally
/// Reader.Free;
/// end;
/// end;
///
/// procedure TMyPersistent.SaveToStream(const Stream: TStream);
/// var
/// Writer: TWriter;
/// begin
/// Writer := TWriter.Create(Stream, 4096);
/// try
/// Writer.WriteRootPersistent(Self);
/// finally
/// Writer.Free;
/// end;
/// end;
///
///
procedure ReadRootPersistent( Instance: TPersistent );
end;
///
/// Helper class for TWriter component
///
///
///
///
TWriterHelper = class helper for TWriter
private
{ private declarations }
protected
{ protected declarations }
public
{ public declarations }
///
///
/// Writes the TPersistent object specified by Instance to a stream
///
///
/// Use the WritePersistent method when you want to write a defined
/// property (specified by TFiler.DefineProperties method) that is a
/// TPersistent to a stream
///
///
///
///
/// To create a defined TPersistent property for your component,
/// include the FilerHelper unit in your uses statement, and then
/// override the protected DefineProperties method:
///
/// TMyComponent = class(TComponent)
/// private
/// { private declarations }
/// protected
/// { protected declarations }
/// procedure ReadMyProp(Reader: TReader);
/// procedure WriteMyProp(Writer: TWriter);
/// procedure DefineProperties(Filer: TFiler); override;
/// public
/// { public declarations }
/// property MyProp: TPersistent read FMyProp write SetMyProp;
/// published
/// { published declarations }
/// end;
/// [...]
/// procedure TMyComponent.DefineProperties(Filer: TFiler);
/// begin
/// inherited DefineProperties(Filer);
/// Filer.DefineProperty('MyProp', ReadMyProp, WriteMyProp, True);
/// end;
///
/// Then, create your reader procedure and writer procedures:
///
/// procedure TMyComponent.ReadMyProp(Reader: TReader);
/// begin
/// Reader.ReadPersistent(FMyProp);
/// end;
/// procedure TMyComponent.WriteMyProp(Writer: TWriter);
/// begin
/// Writer.WritePersistent(FMyProp);
/// end;
///
///
procedure WritePersistent( Instance: TPersistent );
///
/// Writes the TPersistent object specified by Instance to a stream as
/// the root object
///
/// Do not use this method when you are trying to write a
/// TPersistent object out as part of another component. This method
/// is intended for when you want to save the Instance object as the
/// top-level object of a stream. For example, as a
/// SaveToStream/LoadFromStream method pair for the Instance
///
///
///
/// procedure TMyPersistent.LoadFromStream(const Stream: TStream);
/// var
/// Reader: TReader;
/// begin
/// Reader := TReader.Create(Stream, 4096);
/// try
/// Reader.ReadRootPersistent(Self);
/// finally
/// Reader.Free;
/// end;
/// end;
///
/// procedure TMyPersistent.SaveToStream(const Stream: TStream);
/// var
/// Writer: TWriter;
/// begin
/// Writer := TWriter.Create(Stream, 4096);
/// try
/// Writer.WriteRootPersistent(Self);
/// finally
/// Writer.Free;
/// end;
/// end;
///
///
procedure WriteRootPersistent( Instance: TPersistent );
procedure WritePropertiesOnly( Instance: TPersistent );
end;
implementation
uses
TypInfo;
type
TPersistentCrack = class(TPersistent);
{ TReaderHelper }
procedure TReaderHelper.ReadPersistent(Instance: TPersistent);
begin
// read vaCollection
CheckValue(vaCollection);
// read list of items, in this case, only one, the top-level instance
ReadListBegin;
while not EndOfList do
ReadProperty(Instance);
ReadListEnd;
ReadListEnd;
end;
procedure TReaderHelper.ReadRootPersistent(Instance: TPersistent);
var
aClassName: String;
begin
// read signature
ReadSignature;
// read class
aClassName := ReadStr;
if aClassName <> Instance.ClassName then
raise EReadError.CreateFmt(SClassnameMismatch, [aClassName, Instance.ClassName]);
// read name (none)
ReadStr;
// read list of properties
while not EndOfList do
ReadProperty(Instance);
ReadListEnd;
ReadListEnd;
end;
{ TWriterHelper }
procedure TWriterHelper.WritePersistent(Instance: TPersistent);
begin
// write out the Instance properties as a collection. This forces the writer
// to zero out the property path
WriteValue(vaCollection);
// collection is a list of items
// we are writing out a collection of one item, the Instance
WriteListBegin;
WriteProperties(Instance);
WriteListEnd;
WriteListEnd; // match vaCollection
end;
procedure TWriterHelper.WritePropertiesOnly(Instance: TPersistent);
var
I, Count: Integer;
PropInfo: PPropInfo;
PropList: PPropList;
begin
Count := GetTypeData(Instance.ClassInfo)^.PropCount;
if Count > 0 then
begin
GetMem(PropList, Count * SizeOf(Pointer));
try
GetPropInfos(Instance.ClassInfo, PropList);
for I := 0 to Count - 1 do
begin
PropInfo := PropList^[I];
if PropInfo = nil then
Break;
if (PropInfo^.PropType^.Kind in tkProperties) and IsStoredProp(Instance, PropInfo) then
WriteProperty(Instance, PropInfo);
end;
finally
FreeMem(PropList, Count * SizeOf(Pointer));
end;
end;
TPersistentCrack(Instance).DefineProperties(Self);
end;
procedure TWriterHelper.WriteRootPersistent(Instance: TPersistent);
begin
// mark this as a delphi stream
WriteSignature;
// class to write
WriteUTF8Str(Instance.ClassName);
// name of class
WriteUTF8Str('');
// write properties
WritePropertiesOnly(Instance);
WriteListEnd;
WriteListEnd;
end;
end.