1274 lines
40 KiB
ObjectPascal
1274 lines
40 KiB
ObjectPascal
|
{ unit NativeXmlObjectStorage
|
||
|
|
||
|
This unit provides functionality to store any TObject descendant to an XML file
|
||
|
or stream. Internally it makes full use of RTTI (runtime type information) in
|
||
|
order to store all published properties and events.
|
||
|
|
||
|
It can even be used to copy forms, but form inheritance is not exploited, so
|
||
|
child forms descending from parent forms store everything that the parent already
|
||
|
stored.
|
||
|
|
||
|
All published properties and events of objects are stored. This includes
|
||
|
the "DefineProperties". These are stored in binary form in the XML, encoded
|
||
|
as BASE64.
|
||
|
|
||
|
Known limitations:
|
||
|
- The method and event lookup will not work correctly across forms.
|
||
|
|
||
|
Please see the "ObjectToXML" demo for example usage of this unit.
|
||
|
|
||
|
Original Author: Nils Haeck M.Sc.
|
||
|
Copyright (c) 2003-2009 Simdesign B.V.
|
||
|
|
||
|
It is NOT allowed under ANY circumstances to publish or copy this code
|
||
|
without accepting the license conditions in accompanying LICENSE.txt
|
||
|
first!
|
||
|
|
||
|
This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
|
||
|
ANY KIND, either express or implied.
|
||
|
|
||
|
Please visit http://www.simdesign.nl/xml.html for more information.
|
||
|
}
|
||
|
unit NativeXmlObjectStorage;
|
||
|
|
||
|
{$i NativeXml.inc}
|
||
|
|
||
|
interface
|
||
|
|
||
|
uses
|
||
|
Classes, Forms, SysUtils, Controls, NativeXml, TypInfo, Variants;
|
||
|
|
||
|
type
|
||
|
|
||
|
// Use TsdXmlObjectWriter to write any TPersistent descendant's published properties
|
||
|
// to an XML node.
|
||
|
TsdXmlObjectWriter = class(TPersistent)
|
||
|
protected
|
||
|
procedure WriteProperty(ANode: TXmlNode; AObject: TObject; AParent: TComponent; PropInfo: PPropInfo);
|
||
|
public
|
||
|
// Call WriteObject to write the published properties of AObject to the TXmlNode
|
||
|
// ANode. Specify AParent in order to store references to parent methods and
|
||
|
// events correctly.
|
||
|
procedure WriteObject(ANode: TXmlNode; AObject: TObject; AParent: TComponent = nil);
|
||
|
// Call WriteComponent to write the published properties of AComponent to the TXmlNode
|
||
|
// ANode. Specify AParent in order to store references to parent methods and
|
||
|
// events correctly.
|
||
|
procedure WriteComponent(ANode: TXmlNode; AComponent: TComponent; AParent: TComponent = nil);
|
||
|
end;
|
||
|
|
||
|
// Use TsdXmlObjectReader to read any TPersistent descendant's published properties
|
||
|
// from an XML node.
|
||
|
TsdXmlObjectReader = class(TPersistent)
|
||
|
protected
|
||
|
procedure ReadProperty(ANode: TXmlNode; AObject: TObject; AParent: TComponent; PropInfo: PPropInfo);
|
||
|
public
|
||
|
// Call CreateComponent to first create AComponent and then read its published
|
||
|
// properties from the TXmlNode ANode. Specify AParent in order to resolve
|
||
|
// references to parent methods and events correctly. In order to successfully
|
||
|
// create the component from scratch, the component's class must be registered
|
||
|
// beforehand with a call to RegisterClass. Specify Owner to add the component
|
||
|
// as a child to Owner's component list. This is usually a form. Specify Name
|
||
|
// as the new component name for the created component.
|
||
|
function CreateComponent(ANode: TXmlNode; AOwner, AParent: TComponent; AName: string = ''): TComponent;
|
||
|
// Call ReadObject to read the published properties of AObject from the TXmlNode
|
||
|
// ANode. Specify AParent in order to resolve references to parent methods and
|
||
|
// events correctly.
|
||
|
procedure ReadObject(ANode: TXmlNode; AObject: TObject; AParent: TComponent = nil);
|
||
|
// Call ReadComponent to read the published properties of AComponent from the TXmlNode
|
||
|
// ANode. Specify AParent in order to resolve references to parent methods and
|
||
|
// events correctly.
|
||
|
procedure ReadComponent(ANode: TXmlNode; AComponent: TComponent; AParent: TComponent);
|
||
|
end;
|
||
|
|
||
|
// High-level create methods
|
||
|
|
||
|
// Create and read a component from the XML file with FileName. In order to successfully
|
||
|
// create the component from scratch, the component's class must be registered
|
||
|
// beforehand with a call to RegisterClass. Specify Owner to add the component
|
||
|
// as a child to Owner's component list. This is usually a form. Specify Name
|
||
|
// as the new component name for the created component.
|
||
|
function ComponentCreateFromXmlFile(const FileName: string; Owner: TComponent;
|
||
|
const Name: string): TComponent;
|
||
|
|
||
|
// Create and read a component from the TXmlNode ANode. In order to successfully
|
||
|
// create the component from scratch, the component's class must be registered
|
||
|
// beforehand with a call to RegisterClass. Specify Owner to add the component
|
||
|
// as a child to Owner's component list. This is usually a form. Specify Name
|
||
|
// as the new component name for the created component.
|
||
|
function ComponentCreateFromXmlNode(ANode: TXmlNode; Owner: TComponent;
|
||
|
const Name: string): TComponent;
|
||
|
|
||
|
// Create and read a component from the XML stream S. In order to successfully
|
||
|
// create the component from scratch, the component's class must be registered
|
||
|
// beforehand with a call to RegisterClass. Specify Owner to add the component
|
||
|
// as a child to Owner's component list. This is usually a form. Specify Name
|
||
|
// as the new component name for the created component.
|
||
|
function ComponentCreateFromXmlStream(S: TStream; Owner: TComponent;
|
||
|
const Name: string): TComponent;
|
||
|
|
||
|
// Create and read a component from the XML in string in Value. In order to successfully
|
||
|
// create the component from scratch, the component's class must be registered
|
||
|
// beforehand with a call to RegisterClass. Specify Owner to add the component
|
||
|
// as a child to Owner's component list. This is usually a form. Specify Name
|
||
|
// as the new component name for the created component.
|
||
|
function ComponentCreateFromXmlString(const Value: string; Owner: TComponent;
|
||
|
const Name: string): TComponent;
|
||
|
|
||
|
// Create and read a form from the XML file with FileName. In order to successfully
|
||
|
// create the form from scratch, the form's class must be registered
|
||
|
// beforehand with a call to RegisterClass. Specify Owner to add the form
|
||
|
// as a child to Owner's component list. For forms this is usually Application.
|
||
|
// Specify Name as the new form name for the created form.
|
||
|
function FormCreateFromXmlFile(const FileName: string; Owner: TComponent;
|
||
|
const Name: string): TForm;
|
||
|
|
||
|
// Create and read a form from the XML stream in S. In order to successfully
|
||
|
// create the form from scratch, the form's class must be registered
|
||
|
// beforehand with a call to RegisterClass. Specify Owner to add the form
|
||
|
// as a child to Owner's component list. For forms this is usually Application.
|
||
|
// Specify Name as the new form name for the created form.
|
||
|
function FormCreateFromXmlStream(S: TStream; Owner: TComponent;
|
||
|
const Name: string): TForm;
|
||
|
|
||
|
// Create and read a form from the XML string in Value. In order to successfully
|
||
|
// create the form from scratch, the form's class must be registered
|
||
|
// beforehand with a call to RegisterClass. Specify Owner to add the form
|
||
|
// as a child to Owner's component list. For forms this is usually Application.
|
||
|
// Specify Name as the new form name for the created form.
|
||
|
function FormCreateFromXmlString(const Value: string; Owner: TComponent;
|
||
|
const Name: string): TForm;
|
||
|
|
||
|
// High-level load methods
|
||
|
|
||
|
// Load all the published properties of AObject from the XML file in Filename.
|
||
|
// Specify AParent in order to resolve references to parent methods and
|
||
|
// events correctly.
|
||
|
procedure ObjectLoadFromXmlFile(AObject: TObject; const FileName: string;
|
||
|
AParent: TComponent = nil);
|
||
|
|
||
|
// Load all the published properties of AObject from the TXmlNode ANode.
|
||
|
// Specify AParent in order to resolve references to parent methods and
|
||
|
// events correctly.
|
||
|
procedure ObjectLoadFromXmlNode(AObject: TObject; ANode: TXmlNode; AParent: TComponent = nil);
|
||
|
|
||
|
// Load all the published properties of AObject from the XML stream in S.
|
||
|
// Specify AParent in order to resolve references to parent methods and
|
||
|
// events correctly.
|
||
|
procedure ObjectLoadFromXmlStream(AObject: TObject; S: TStream; AParent: TComponent = nil);
|
||
|
|
||
|
// Load all the published properties of AObject from the XML string in Value.
|
||
|
// Specify AParent in order to resolve references to parent methods and
|
||
|
// events correctly.
|
||
|
procedure ObjectLoadFromXmlString(AObject: TObject; const Value: string; AParent: TComponent = nil);
|
||
|
|
||
|
// High-level save methods
|
||
|
|
||
|
// Save all the published properties of AObject as XML to the file in Filename.
|
||
|
// Specify AParent in order to store references to parent methods and
|
||
|
// events correctly.
|
||
|
procedure ObjectSaveToXmlFile(AObject: TObject; const FileName: string;
|
||
|
AParent: TComponent = nil);
|
||
|
|
||
|
// Save all the published properties of AObject to the TXmlNode ANode.
|
||
|
// Specify AParent in order to store references to parent methods and
|
||
|
// events correctly.
|
||
|
procedure ObjectSaveToXmlNode(AObject: TObject; ANode: TXmlNode; AParent: TComponent = nil);
|
||
|
|
||
|
// Save all the published properties of AObject as XML in stream S.
|
||
|
// Specify AParent in order to store references to parent methods and
|
||
|
// events correctly.
|
||
|
procedure ObjectSaveToXmlStream(AObject: TObject; S: TStream; AParent: TComponent = nil);
|
||
|
|
||
|
// Save all the published properties of AObject as XML in string Value.
|
||
|
// Specify AParent in order to store references to parent methods and
|
||
|
// events correctly.
|
||
|
function ObjectSaveToXmlString(AObject: TObject; AParent: TComponent = nil): string;
|
||
|
|
||
|
// Save all the published properties of AComponent as XML in the file in Filename.
|
||
|
// Specify AParent in order to store references to parent methods and
|
||
|
// events correctly.
|
||
|
procedure ComponentSaveToXmlFile(AComponent: TComponent; const FileName: string;
|
||
|
AParent: TComponent = nil);
|
||
|
|
||
|
// Save all the published properties of AComponent to the TXmlNode ANode.
|
||
|
// Specify AParent in order to store references to parent methods and
|
||
|
// events correctly.
|
||
|
procedure ComponentSaveToXmlNode(AComponent: TComponent; ANode: TXmlNode;
|
||
|
AParent: TComponent = nil);
|
||
|
|
||
|
// Save all the published properties of AComponent as XML in the stream in S.
|
||
|
// Specify AParent in order to store references to parent methods and
|
||
|
// events correctly.
|
||
|
procedure ComponentSaveToXmlStream(AComponent: TComponent; S: TStream;
|
||
|
AParent: TComponent = nil);
|
||
|
|
||
|
// Save all the published properties of AComponent as XML in the string Value.
|
||
|
// Specify AParent in order to store references to parent methods and
|
||
|
// events correctly.
|
||
|
function ComponentSaveToXmlString(AComponent: TComponent; AParent: TComponent = nil): string;
|
||
|
|
||
|
// Save the form AForm as XML to the file in Filename. This method also stores
|
||
|
// properties of all child components on the form, and can therefore be used
|
||
|
// as a form-storage method.
|
||
|
procedure FormSaveToXmlFile(AForm: TForm; const FileName: string);
|
||
|
|
||
|
// Save the form AForm as XML to the stream in S. This method also stores
|
||
|
// properties of all child components on the form, and can therefore be used
|
||
|
// as a form-storage method.
|
||
|
procedure FormSaveToXmlStream(AForm: TForm; S: TStream);
|
||
|
|
||
|
// Save the form AForm as XML to a string. This method also stores
|
||
|
// properties of all child components on the form, and can therefore be used
|
||
|
// as a form-storage method.
|
||
|
function FormSaveToXmlString(AForm: TForm): string;
|
||
|
|
||
|
resourcestring
|
||
|
|
||
|
sxwIllegalVarType = 'Illegal variant type';
|
||
|
sxrUnregisteredClassType = 'Unregistered classtype encountered in ';
|
||
|
sxrInvalidPropertyValue = 'Invalid property value';
|
||
|
sxwInvalidMethodName = 'Invalid method name';
|
||
|
|
||
|
implementation
|
||
|
|
||
|
type
|
||
|
|
||
|
TPersistentAccess = class(TPersistent);
|
||
|
TComponentAccess = class(TComponent)
|
||
|
public
|
||
|
procedure SetComponentState(const AState: TComponentState);
|
||
|
published
|
||
|
property ComponentState;
|
||
|
end;
|
||
|
|
||
|
TReaderAccess = class(TReader);
|
||
|
|
||
|
function ComponentCreateFromXmlFile(const FileName: string; Owner: TComponent;
|
||
|
const Name: string): TComponent;
|
||
|
var
|
||
|
S: TStream;
|
||
|
begin
|
||
|
S := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
|
||
|
try
|
||
|
Result := ComponentCreateFromXmlStream(S, Owner, Name);
|
||
|
finally
|
||
|
S.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function ComponentCreateFromXmlNode(ANode: TXmlNode; Owner: TComponent;
|
||
|
const Name: string): TComponent;
|
||
|
var
|
||
|
AReader: TsdXmlObjectReader;
|
||
|
begin
|
||
|
Result := nil;
|
||
|
if not assigned(ANode) then
|
||
|
exit;
|
||
|
// Create reader
|
||
|
AReader := TsdXmlObjectReader.Create;
|
||
|
try
|
||
|
// Read the component from the node
|
||
|
Result := AReader.CreateComponent(ANode, Owner, nil, Name);
|
||
|
finally
|
||
|
AReader.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function ComponentCreateFromXmlStream(S: TStream; Owner: TComponent;
|
||
|
const Name: string): TComponent;
|
||
|
var
|
||
|
ADoc: TNativeXml;
|
||
|
begin
|
||
|
Result := nil;
|
||
|
if not assigned(S) then
|
||
|
exit;
|
||
|
// Create XML document
|
||
|
ADoc := TNativeXml.Create;
|
||
|
try
|
||
|
// Load XML
|
||
|
ADoc.LoadFromStream(S);
|
||
|
// Load from XML node
|
||
|
Result := ComponentCreateFromXmlNode(ADoc.Root, Owner, Name);
|
||
|
finally
|
||
|
ADoc.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function ComponentCreateFromXmlString(const Value: string; Owner: TComponent;
|
||
|
const Name: string): TComponent;
|
||
|
var
|
||
|
S: TStream;
|
||
|
begin
|
||
|
S := TStringStream.Create(Value);
|
||
|
try
|
||
|
Result := ComponentCreateFromXmlStream(S, Owner, Name);
|
||
|
finally
|
||
|
S.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function FormCreateFromXmlFile(const FileName: string; Owner: TComponent;
|
||
|
const Name: string): TForm;
|
||
|
var
|
||
|
S: TStream;
|
||
|
begin
|
||
|
S := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
|
||
|
try
|
||
|
Result := FormCreateFromXmlStream(S, Owner, Name);
|
||
|
finally
|
||
|
S.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function FormCreateFromXmlStream(S: TStream; Owner: TComponent;
|
||
|
const Name: string): TForm;
|
||
|
var
|
||
|
ADoc: TNativeXml;
|
||
|
begin
|
||
|
Result := nil;
|
||
|
if not assigned(S) then exit;
|
||
|
// Create XML document
|
||
|
ADoc := TNativeXml.Create;
|
||
|
try
|
||
|
// Load XML
|
||
|
ADoc.LoadFromStream(S);
|
||
|
|
||
|
// Load from XML node
|
||
|
Result := TForm(ComponentCreateFromXmlNode(ADoc.Root, Owner, Name));
|
||
|
finally
|
||
|
ADoc.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function FormCreateFromXmlString(const Value: string; Owner: TComponent;
|
||
|
const Name: string): TForm;
|
||
|
var
|
||
|
S: TStream;
|
||
|
begin
|
||
|
S := TStringStream.Create(Value);
|
||
|
try
|
||
|
Result := FormCreateFromXmlStream(S, Owner, Name);
|
||
|
finally
|
||
|
S.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure ObjectLoadFromXmlFile(AObject: TObject; const FileName: string;
|
||
|
AParent: TComponent = nil);
|
||
|
var
|
||
|
S: TStream;
|
||
|
begin
|
||
|
S := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
|
||
|
try
|
||
|
ObjectLoadFromXmlStream(AObject, S, AParent);
|
||
|
finally
|
||
|
S.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure ObjectLoadFromXmlNode(AObject: TObject; ANode: TXmlNode; AParent: TComponent = nil);
|
||
|
var
|
||
|
AReader: TsdXmlObjectReader;
|
||
|
begin
|
||
|
if not assigned(AObject) or not assigned(ANode) then
|
||
|
exit;
|
||
|
// Create writer
|
||
|
AReader := TsdXmlObjectReader.Create;
|
||
|
try
|
||
|
// Write the object to the document
|
||
|
if AObject is TComponent then
|
||
|
AReader.ReadComponent(ANode, TComponent(AObject), AParent)
|
||
|
else
|
||
|
AReader.ReadObject(ANode, AObject, AParent);
|
||
|
finally
|
||
|
AReader.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure ObjectLoadFromXmlStream(AObject: TObject; S: TStream; AParent: TComponent = nil);
|
||
|
var
|
||
|
ADoc: TNativeXml;
|
||
|
begin
|
||
|
if not assigned(S) then
|
||
|
exit;
|
||
|
// Create XML document
|
||
|
ADoc := TNativeXml.Create;
|
||
|
try
|
||
|
// Load XML
|
||
|
ADoc.LoadFromStream(S);
|
||
|
// Load from XML node
|
||
|
ObjectLoadFromXmlNode(AObject, ADoc.Root, AParent);
|
||
|
finally
|
||
|
ADoc.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure ObjectLoadFromXmlString(AObject: TObject; const Value: string; AParent: TComponent = nil);
|
||
|
var
|
||
|
S: TStringStream;
|
||
|
begin
|
||
|
S := TStringStream.Create(Value);
|
||
|
try
|
||
|
ObjectLoadFromXmlStream(AObject, S, AParent);
|
||
|
finally
|
||
|
S.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure ObjectSaveToXmlFile(AObject: TObject; const FileName: string;
|
||
|
AParent: TComponent = nil);
|
||
|
var
|
||
|
S: TStream;
|
||
|
begin
|
||
|
S := TFileStream.Create(FileName, fmCreate);
|
||
|
try
|
||
|
ObjectSaveToXmlStream(AObject, S, AParent);
|
||
|
finally
|
||
|
S.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure ObjectSaveToXmlNode(AObject: TObject; ANode: TXmlNode; AParent: TComponent = nil);
|
||
|
var
|
||
|
AWriter: TsdXmlObjectWriter;
|
||
|
begin
|
||
|
if not assigned(AObject) or not assigned(ANode) then
|
||
|
exit;
|
||
|
// Create writer
|
||
|
AWriter := TsdXmlObjectWriter.Create;
|
||
|
try
|
||
|
// Write the object to the document
|
||
|
if AObject is TComponent then
|
||
|
AWriter.WriteComponent(ANode, TComponent(AObject), AParent)
|
||
|
else begin
|
||
|
ANode.Name := UTF8String(AObject.ClassName);
|
||
|
AWriter.WriteObject(ANode, AObject, AParent);
|
||
|
end;
|
||
|
finally
|
||
|
AWriter.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure ObjectSaveToXmlStream(AObject: TObject; S: TStream; AParent: TComponent = nil);
|
||
|
var
|
||
|
ADoc: TNativeXml;
|
||
|
begin
|
||
|
if not assigned(S) then
|
||
|
exit;
|
||
|
// Create XML document
|
||
|
ADoc := TNativeXml.Create;
|
||
|
try
|
||
|
ADoc.XmlFormat := xfReadable;
|
||
|
// Save to XML node
|
||
|
ObjectSaveToXmlNode(AObject, ADoc.Root, AParent);
|
||
|
// Save to stream
|
||
|
ADoc.SaveToStream(S);
|
||
|
finally
|
||
|
ADoc.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function ObjectSaveToXmlString(AObject: TObject; AParent: TComponent = nil): string;
|
||
|
var
|
||
|
S: TStringStream;
|
||
|
begin
|
||
|
S := TStringStream.Create('');
|
||
|
try
|
||
|
ObjectSaveToXmlStream(AObject, S, AParent);
|
||
|
Result := S.DataString;
|
||
|
finally
|
||
|
S.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure ComponentSaveToXmlFile(AComponent: TComponent; const FileName: string;
|
||
|
AParent: TComponent = nil);
|
||
|
begin
|
||
|
ObjectSaveToXmlFile(AComponent, FileName, AParent);
|
||
|
end;
|
||
|
|
||
|
procedure ComponentSaveToXmlNode(AComponent: TComponent; ANode: TXmlNode;
|
||
|
AParent: TComponent = nil);
|
||
|
begin
|
||
|
ObjectSaveToXmlNode(AComponent, ANode, AParent);
|
||
|
end;
|
||
|
|
||
|
procedure ComponentSaveToXmlStream(AComponent: TComponent; S: TStream;
|
||
|
AParent: TComponent = nil);
|
||
|
begin
|
||
|
ObjectSaveToXmlStream(AComponent, S, AParent);
|
||
|
end;
|
||
|
|
||
|
function ComponentSaveToXmlString(AComponent: TComponent; AParent: TComponent = nil): string;
|
||
|
begin
|
||
|
Result := ObjectSaveToXmlString(AComponent, AParent);
|
||
|
end;
|
||
|
|
||
|
procedure FormSaveToXmlFile(AForm: TForm; const FileName: string);
|
||
|
begin
|
||
|
ComponentSaveToXmlFile(AForm, FileName, AForm);
|
||
|
end;
|
||
|
|
||
|
procedure FormSaveToXmlStream(AForm: TForm; S: TStream);
|
||
|
begin
|
||
|
ComponentSaveToXmlStream(AForm, S, AForm);
|
||
|
end;
|
||
|
|
||
|
function FormSaveToXmlString(AForm: TForm): string;
|
||
|
begin
|
||
|
Result := ComponentSaveToXmlString(AForm, AForm);
|
||
|
end;
|
||
|
|
||
|
{ TsdXmlObjectWriter }
|
||
|
|
||
|
procedure TsdXmlObjectWriter.WriteComponent(ANode: TXmlNode; AComponent,
|
||
|
AParent: TComponent);
|
||
|
begin
|
||
|
if not assigned(ANode) or not assigned(AComponent) then
|
||
|
exit;
|
||
|
ANode.Name := UTF8String(AComponent.ClassName);
|
||
|
if length(AComponent.Name) > 0 then
|
||
|
ANode.AttributeAdd('Name', UTF8String(AComponent.Name));
|
||
|
WriteObject(ANode, AComponent, AParent);
|
||
|
end;
|
||
|
|
||
|
procedure TsdXmlObjectWriter.WriteObject(ANode: TXmlNode; AObject: TObject;
|
||
|
AParent: TComponent);
|
||
|
var
|
||
|
i, Count: Integer;
|
||
|
PropInfo: PPropInfo;
|
||
|
PropList: PPropList;
|
||
|
S: TStringStream;
|
||
|
AWriter: TWriter;
|
||
|
AChildNode: TXmlNode;
|
||
|
AComponentNode: TXmlNode;
|
||
|
begin
|
||
|
if not assigned(ANode) or not assigned(AObject) then
|
||
|
exit;
|
||
|
|
||
|
// If this is a component, store child components
|
||
|
if AObject is TComponent then with TComponent(AObject) do
|
||
|
begin
|
||
|
if ComponentCount > 0 then
|
||
|
begin
|
||
|
AChildNode := ANode.NodeNew('Components');
|
||
|
for i := 0 to ComponentCount - 1 do
|
||
|
begin
|
||
|
AComponentNode := AChildNode.NodeNew(UTF8String(Components[i].ClassName));
|
||
|
if length(Components[i].Name) > 0 then
|
||
|
AComponentNode.AttributeAdd('Name', UTF8String(Components[i].Name));
|
||
|
WriteObject(AComponentNode, Components[i], TComponent(AObject));
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
// Save all regular properties that need storing
|
||
|
Count := GetTypeData(AObject.ClassInfo)^.PropCount;
|
||
|
if Count > 0 then
|
||
|
begin
|
||
|
GetMem(PropList, Count * SizeOf(Pointer));
|
||
|
try
|
||
|
GetPropInfos(AObject.ClassInfo, PropList);
|
||
|
for i := 0 to Count - 1 do
|
||
|
begin
|
||
|
PropInfo := PropList^[i];
|
||
|
if PropInfo = nil then
|
||
|
continue;
|
||
|
if IsStoredProp(AObject, PropInfo) then
|
||
|
WriteProperty(ANode, AObject, AParent, PropInfo);
|
||
|
end;
|
||
|
finally
|
||
|
FreeMem(PropList, Count * SizeOf(Pointer));
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
// Save defined properties
|
||
|
if AObject is TPersistent then
|
||
|
begin
|
||
|
S := TStringStream.Create('');
|
||
|
try
|
||
|
AWriter := TWriter.Create(S, 4096);
|
||
|
try
|
||
|
TPersistentAccess(AObject).DefineProperties(AWriter);
|
||
|
finally
|
||
|
AWriter.Free;
|
||
|
end;
|
||
|
// Do we have data from DefineProperties?
|
||
|
if S.Size > 0 then
|
||
|
begin
|
||
|
// Yes, add a node with binary data
|
||
|
ANode.NodeNew('DefinedProperties').BinaryString := RawByteString(S.DataString);
|
||
|
end;
|
||
|
finally
|
||
|
S.Free;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TsdXmlObjectWriter.WriteProperty(ANode: TXmlNode; AObject: TObject;
|
||
|
AParent: TComponent; PropInfo: PPropInfo);
|
||
|
var
|
||
|
PropType: PTypeInfo;
|
||
|
AChildNode: TXmlNode;
|
||
|
ACollectionNode: TXmlNode;
|
||
|
//local
|
||
|
procedure WritePropName;
|
||
|
begin
|
||
|
AChildNode := ANode.NodeNew(PPropInfo(PropInfo)^.Name);
|
||
|
end;
|
||
|
//local
|
||
|
procedure WriteInteger(Value: Int64);
|
||
|
begin
|
||
|
AChildNode.ValueAsString := UTF8String(IntToStr(Value));
|
||
|
end;
|
||
|
//local
|
||
|
procedure WriteString(Value: string);
|
||
|
begin
|
||
|
AChildNode.ValueAsUnicodeString := Value;
|
||
|
end;
|
||
|
//local
|
||
|
procedure WriteSet(Value: Longint);
|
||
|
var
|
||
|
I: Integer;
|
||
|
BaseType: PTypeInfo;
|
||
|
S, Enum: string;
|
||
|
begin
|
||
|
BaseType := GetTypeData(PropType)^.CompType^;
|
||
|
for i := 0 to SizeOf(TIntegerSet) * 8 - 1 do
|
||
|
begin
|
||
|
if i in TIntegerSet(Value) then
|
||
|
begin
|
||
|
Enum := GetEnumName(BaseType, i);
|
||
|
if i > 0 then
|
||
|
S := S + ',' + Enum
|
||
|
else
|
||
|
S := Enum;
|
||
|
end;
|
||
|
end;
|
||
|
AChildNode.ValueAsString := UTF8String(Format('[%s]', [S]));
|
||
|
end;
|
||
|
//local
|
||
|
procedure WriteIntProp(IntType: PTypeInfo; Value: Longint);
|
||
|
var
|
||
|
Ident: string;
|
||
|
IntToIdent: TIntToIdent;
|
||
|
begin
|
||
|
IntToIdent := FindIntToIdent(IntType);
|
||
|
if Assigned(IntToIdent) and IntToIdent(Value, Ident) then
|
||
|
WriteString(Ident)
|
||
|
else
|
||
|
WriteInteger(Value);
|
||
|
end;
|
||
|
//local
|
||
|
procedure WriteCollectionProp(Collection: TCollection);
|
||
|
var
|
||
|
i: integer;
|
||
|
begin
|
||
|
if assigned(Collection) then
|
||
|
begin
|
||
|
for i := 0 to Collection.Count - 1 do
|
||
|
begin
|
||
|
ACollectionNode := AChildNode.NodeNew(UTF8String(Collection.Items[i].ClassName));
|
||
|
WriteObject(ACollectionNode, Collection.Items[I], AParent);
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
//local
|
||
|
procedure WriteOrdProp;
|
||
|
var
|
||
|
Value: Longint;
|
||
|
begin
|
||
|
Value := GetOrdProp(AObject, PropInfo);
|
||
|
if not (Value = PPropInfo(PropInfo)^.Default) then
|
||
|
begin
|
||
|
WritePropName;
|
||
|
case PropType^.Kind of
|
||
|
tkInteger: WriteIntProp(PPropInfo(PropInfo)^.PropType^, Value);
|
||
|
tkChar: WriteString(Chr(Value));
|
||
|
tkSet: WriteSet(Value);
|
||
|
tkEnumeration: WriteString(GetEnumName(PropType, Value));
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
//local
|
||
|
procedure WriteFloatProp;
|
||
|
var
|
||
|
Value: Extended;
|
||
|
begin
|
||
|
Value := GetFloatProp(AObject, PropInfo);
|
||
|
if not (Value = 0) then
|
||
|
ANode.WriteFloat(PPropInfo(PropInfo)^.Name, Value);
|
||
|
end;
|
||
|
//local
|
||
|
procedure WriteInt64Prop;
|
||
|
var
|
||
|
Value: Int64;
|
||
|
begin
|
||
|
Value := GetInt64Prop(AObject, PropInfo);
|
||
|
if not (Value = 0) then
|
||
|
ANode.WriteInt64(PPropInfo(PropInfo)^.Name, Value);
|
||
|
end;
|
||
|
//local
|
||
|
procedure WriteStrProp;
|
||
|
var
|
||
|
Value: string;
|
||
|
begin
|
||
|
Value := GetStrProp(AObject, PropInfo);
|
||
|
if not (length(Value) = 0) then
|
||
|
ANode.WriteUnicodeString(PPropInfo(PropInfo)^.Name, Value);
|
||
|
end;
|
||
|
//local
|
||
|
procedure WriteWideStrProp;
|
||
|
var
|
||
|
Value: WideString;
|
||
|
begin
|
||
|
Value := GetWideStrProp(AObject, PropInfo);
|
||
|
if not (length(Value) = 0) then
|
||
|
ANode.WriteUnicodeString(PPropInfo(PropInfo)^.Name, Value);
|
||
|
end;
|
||
|
{$IFDEF D12UP}
|
||
|
//local
|
||
|
procedure WriteUnicodeStrProp;
|
||
|
var
|
||
|
Value: UnicodeString;
|
||
|
begin
|
||
|
Value := GetUnicodeStrProp(AObject, PropInfo);
|
||
|
if not (length(Value) = 0) then
|
||
|
ANode.WriteUnicodeString(PPropInfo(PropInfo)^.Name, Value);
|
||
|
end;
|
||
|
{$ENDIF}
|
||
|
//local
|
||
|
procedure WriteObjectProp;
|
||
|
var
|
||
|
Value: TObject;
|
||
|
ComponentName: string;
|
||
|
function GetComponentName(Component: TComponent): string;
|
||
|
begin
|
||
|
if Component.Owner = AParent then
|
||
|
Result := Component.Name
|
||
|
else if Component = AParent then
|
||
|
Result := 'Owner'
|
||
|
else if assigned(Component.Owner) and (length(Component.Owner.Name) > 0)
|
||
|
and (length(Component.Name) > 0) then
|
||
|
Result := Component.Owner.Name + '.' + Component.Name
|
||
|
else if length(Component.Name) > 0 then
|
||
|
Result := Component.Name + '.Owner'
|
||
|
else Result := '';
|
||
|
end;
|
||
|
begin
|
||
|
Value := TObject(GetOrdProp(AObject, PropInfo));
|
||
|
if not assigned(Value) then
|
||
|
exit;
|
||
|
WritePropName;
|
||
|
if Value is TComponent then
|
||
|
begin
|
||
|
ComponentName := GetComponentName(TComponent(Value));
|
||
|
if length(ComponentName) > 0 then
|
||
|
WriteString(ComponentName);
|
||
|
end else
|
||
|
begin
|
||
|
WriteString(Format('(%s)', [Value.ClassName]));
|
||
|
if Value is TCollection then
|
||
|
WriteCollectionProp(TCollection(Value))
|
||
|
else
|
||
|
begin
|
||
|
if AObject is TComponent then
|
||
|
WriteObject(AChildNode, Value, TComponent(AObject))
|
||
|
else
|
||
|
WriteObject(AChildNode, Value, AParent)
|
||
|
end;
|
||
|
// No need to store an empty child.. so check and remove
|
||
|
if AChildNode.NodeCount = 0 then
|
||
|
ANode.NodeRemove(AChildNode);
|
||
|
end;
|
||
|
end;
|
||
|
//local
|
||
|
procedure WriteMethodProp;
|
||
|
var
|
||
|
Value: TMethod;
|
||
|
function IsDefaultValue: Boolean;
|
||
|
begin
|
||
|
Result := (Value.Code = nil) or
|
||
|
((Value.Code <> nil) and assigned(AParent) and (AParent.MethodName(Value.Code) = ''));
|
||
|
end;
|
||
|
begin
|
||
|
Value := GetMethodProp(AObject, PropInfo);
|
||
|
if not IsDefaultValue then
|
||
|
begin
|
||
|
if assigned(Value.Code) then
|
||
|
begin
|
||
|
WritePropName;
|
||
|
if assigned(AParent) then
|
||
|
WriteString(AParent.MethodName(Value.Code))
|
||
|
else
|
||
|
AChildNode.ValueAsString := '???';
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
//local
|
||
|
procedure WriteVariantProp;
|
||
|
var
|
||
|
AValue: Variant;
|
||
|
ACurrency: Currency;
|
||
|
var
|
||
|
VType: Integer;
|
||
|
begin
|
||
|
AValue := GetVariantProp(AObject, PropInfo);
|
||
|
if not VarIsEmpty(AValue) or VarIsNull(AValue) then
|
||
|
begin
|
||
|
if VarIsArray(AValue) then
|
||
|
raise Exception.Create(sxwIllegalVarType);
|
||
|
WritePropName;
|
||
|
VType := VarType(AValue);
|
||
|
AChildNode.AttributeAdd('VarType', UTF8String(IntToHex(VType, 4)));
|
||
|
case VType and varTypeMask of
|
||
|
varNull: AChildNode.ValueAsUnicodeString := '';
|
||
|
varOleStr: AChildNode.ValueAsUnicodeString := AValue;
|
||
|
varString: AChildNode.ValueAsUnicodeString := AValue;
|
||
|
varByte,
|
||
|
varSmallInt,
|
||
|
varInteger: AChildNode.ValueAsInteger := AValue;
|
||
|
varSingle,
|
||
|
varDouble: AChildNode.ValueAsFloat := AValue;
|
||
|
varCurrency:
|
||
|
begin
|
||
|
ACurrency := AValue;
|
||
|
AChildNode.BufferWrite(ACurrency, SizeOf(ACurrency));
|
||
|
end;
|
||
|
varDate: AChildNode.ValueAsDateTime := AValue;
|
||
|
varBoolean: AChildNode.ValueAsBool := AValue;
|
||
|
else
|
||
|
try
|
||
|
ANode.ValueAsUnicodeString := AValue;
|
||
|
except
|
||
|
raise Exception.Create(sxwIllegalVarType);
|
||
|
end;
|
||
|
end;//case
|
||
|
end;
|
||
|
end;
|
||
|
//main
|
||
|
begin
|
||
|
if (PPropInfo(PropInfo)^.SetProc <> nil) and
|
||
|
(PPropInfo(PropInfo)^.GetProc <> nil) then
|
||
|
begin
|
||
|
PropType := PPropInfo(PropInfo)^.PropType^;
|
||
|
case PropType^.Kind of
|
||
|
tkInteger, tkChar, tkEnumeration, tkSet: WriteOrdProp;
|
||
|
tkFloat: WriteFloatProp;
|
||
|
tkString, tkLString: WriteStrProp;
|
||
|
{$IFDEF D6UP}
|
||
|
tkWString: WriteWideStrProp;
|
||
|
{$ENDIF}
|
||
|
{$IFDEF D12UP}
|
||
|
tkUString: WriteUnicodeStrProp;
|
||
|
{$ENDIF}
|
||
|
tkClass: WriteObjectProp;
|
||
|
tkMethod: WriteMethodProp;
|
||
|
tkVariant: WriteVariantProp;
|
||
|
tkInt64: WriteInt64Prop;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
{ TsdXmlObjectReader }
|
||
|
|
||
|
function TsdXmlObjectReader.CreateComponent(ANode: TXmlNode;
|
||
|
AOwner, AParent: TComponent; AName: string): TComponent;
|
||
|
var
|
||
|
AClass: TComponentClass;
|
||
|
begin
|
||
|
AClass := TComponentClass(GetClass(string(ANode.Name)));
|
||
|
if not assigned(AClass) then
|
||
|
raise Exception.CreateFmt(sxrUnregisteredClassType, [ANode.Name]);
|
||
|
Result := AClass.Create(AOwner);
|
||
|
if length(AName) = 0 then
|
||
|
Result.Name := string(ANode.AttributeByName['Name'])
|
||
|
else
|
||
|
Result.Name := AName;
|
||
|
if not assigned(AParent) then
|
||
|
AParent := Result;
|
||
|
ReadComponent(ANode, Result, AParent);
|
||
|
end;
|
||
|
|
||
|
procedure TsdXmlObjectReader.ReadComponent(ANode: TXmlNode; AComponent,
|
||
|
AParent: TComponent);
|
||
|
begin
|
||
|
ReadObject(ANode, AComponent, AParent);
|
||
|
end;
|
||
|
|
||
|
procedure TsdXmlObjectReader.ReadObject(ANode: TXmlNode; AObject: TObject; AParent: TComponent);
|
||
|
var
|
||
|
i, Count: Integer;
|
||
|
Item: TCollectionItem;
|
||
|
PropInfo: PPropInfo;
|
||
|
PropList: PPropList;
|
||
|
S: TStringStream;
|
||
|
AReader: TReader;
|
||
|
AChildNode: TXmlNode;
|
||
|
AComponentNode: TXmlNode;
|
||
|
AClass: TComponentClass;
|
||
|
AComponent: TComponent;
|
||
|
begin
|
||
|
if not assigned(ANode) or not assigned(AObject) then
|
||
|
exit;
|
||
|
|
||
|
// Start loading
|
||
|
if AObject is TComponent then
|
||
|
with TComponentAccess(AObject) do
|
||
|
begin
|
||
|
TComponentAccess(AObject).Updating;
|
||
|
SetComponentState(ComponentState + [csLoading, csReading]);
|
||
|
end;
|
||
|
try
|
||
|
|
||
|
// If this is a component, load child components
|
||
|
if AObject is TComponent then with TComponent(AObject) do
|
||
|
begin
|
||
|
AChildNode := ANode.NodeByName('Components');
|
||
|
if assigned(AChildNode) then
|
||
|
begin
|
||
|
for i := 0 to AChildNode.NodeCount - 1 do
|
||
|
begin
|
||
|
AComponentNode := AChildNode.Nodes[i];
|
||
|
AComponent := FindComponent(string(AComponentNode.AttributeByName['Name']));
|
||
|
if not assigned(AComponent) then
|
||
|
begin
|
||
|
AClass := TComponentClass(GetClass(string(AComponentNode.Name)));
|
||
|
if not assigned(AClass) then
|
||
|
raise Exception.Create(sxrUnregisteredClassType);
|
||
|
AComponent := AClass.Create(TComponent(AObject));
|
||
|
AComponent.Name := AComponentNode.AttributeByName['Name'];
|
||
|
// In case of new (visual) controls we set the parent
|
||
|
if (AComponent is TControl) and (AObject is TWinControl) then
|
||
|
TControl(AComponent).Parent := TWinControl(AObject);
|
||
|
end;
|
||
|
ReadComponent(AComponentNode, AComponent, TComponent(AObject));
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
// If this is a collection, load collections items
|
||
|
if AObject is TCollection then
|
||
|
with TCollection(AObject) do
|
||
|
begin
|
||
|
BeginUpdate;
|
||
|
try
|
||
|
Clear;
|
||
|
for i := 0 to ANode.NodeCount - 1 do
|
||
|
begin
|
||
|
item := Add;
|
||
|
ReadObject(ANode.Nodes[i], item, AParent);
|
||
|
end;
|
||
|
finally
|
||
|
EndUpdate;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
// Load all loadable regular properties
|
||
|
Count := GetTypeData(AObject.ClassInfo)^.PropCount;
|
||
|
if Count > 0 then
|
||
|
begin
|
||
|
GetMem(PropList, Count * SizeOf(Pointer));
|
||
|
try
|
||
|
GetPropInfos(AObject.ClassInfo, PropList);
|
||
|
for i := 0 to Count - 1 do
|
||
|
begin
|
||
|
PropInfo := PropList^[i];
|
||
|
if PropInfo = nil then
|
||
|
continue;
|
||
|
if IsStoredProp(AObject, PropInfo) then
|
||
|
ReadProperty(ANode, AObject, AParent, PropInfo);
|
||
|
end;
|
||
|
finally
|
||
|
FreeMem(PropList, Count * SizeOf(Pointer));
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
// Load defined properties
|
||
|
if AObject is TPersistent then
|
||
|
begin
|
||
|
AChildNode := ANode.NodeByName('DefinedProperties');
|
||
|
if assigned(AChildNode) then
|
||
|
begin
|
||
|
S := TStringStream.Create(AChildNode.BinaryString);
|
||
|
try
|
||
|
AReader := TReader.Create(S, 4096);
|
||
|
try
|
||
|
with TReaderAccess(AReader) do
|
||
|
while Position < S.Size do
|
||
|
ReadProperty(TPersistent(AObject));
|
||
|
finally
|
||
|
AReader.Free;
|
||
|
end;
|
||
|
finally
|
||
|
S.Free;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
finally
|
||
|
// End loading
|
||
|
if AObject is TComponent then with TComponentAccess(AObject) do
|
||
|
begin
|
||
|
SetComponentState(ComponentState - [csReading]);
|
||
|
TComponentAccess(AObject).Loaded;
|
||
|
TComponentAccess(AObject).Updated;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TsdXmlObjectReader.ReadProperty(ANode: TXmlNode;
|
||
|
AObject: TObject; AParent: TComponent; PropInfo: PPropInfo);
|
||
|
var
|
||
|
PropType: PTypeInfo;
|
||
|
AChildNode: TXmlNode;
|
||
|
Method: TMethod;
|
||
|
PropObject: TObject;
|
||
|
//local
|
||
|
procedure SetSetProp(const AValue: string);
|
||
|
var
|
||
|
S: string;
|
||
|
P: integer;
|
||
|
ASet: integer;
|
||
|
EnumType: PTypeInfo;
|
||
|
procedure AddToEnum(const EnumName: string);
|
||
|
var
|
||
|
V: integer;
|
||
|
begin
|
||
|
if length(EnumName) = 0 then
|
||
|
exit;
|
||
|
V := GetEnumValue(EnumType, EnumName);
|
||
|
if V = -1 then
|
||
|
raise Exception.Create(sxrInvalidPropertyValue);
|
||
|
Include(TIntegerSet(ASet), V);
|
||
|
end;
|
||
|
begin
|
||
|
ASet := 0;
|
||
|
EnumType := GetTypeData(PropType)^.CompType^;
|
||
|
S := copy(AValue, 2, length(AValue) - 2);
|
||
|
repeat
|
||
|
P := Pos(',', S);
|
||
|
if P > 0 then begin
|
||
|
AddToEnum(copy(S, 1, P - 1));
|
||
|
S := copy(S, P + 1, length(S));
|
||
|
end else begin
|
||
|
AddToEnum(S);
|
||
|
break;
|
||
|
end;
|
||
|
until False;
|
||
|
SetOrdProp(AObject, PropInfo, ASet);
|
||
|
end;
|
||
|
|
||
|
procedure SetIntProp(const AValue: string);
|
||
|
var
|
||
|
V: Longint;
|
||
|
IdentToInt: TIdentToInt;
|
||
|
begin
|
||
|
IdentToInt := FindIdentToInt(PropType);
|
||
|
if Assigned(IdentToInt) and IdentToInt(AValue, V) then
|
||
|
SetOrdProp(AObject, PropInfo, V)
|
||
|
else
|
||
|
SetOrdProp(AObject, PropInfo, StrToInt(AValue));
|
||
|
end;
|
||
|
|
||
|
procedure SetCharProp(const AValue: string);
|
||
|
begin
|
||
|
if length(AValue) <> 1 then
|
||
|
raise Exception.Create(sxrInvalidPropertyValue);
|
||
|
SetOrdProp(AObject, PropInfo, Ord(AValue[1]));
|
||
|
end;
|
||
|
|
||
|
procedure SetEnumProp(const AValue: string);
|
||
|
var
|
||
|
V: integer;
|
||
|
begin
|
||
|
V := GetEnumValue(PropType, AValue);
|
||
|
if V = -1 then
|
||
|
raise Exception.Create(sxrInvalidPropertyValue);
|
||
|
SetOrdProp(AObject, PropInfo, V)
|
||
|
end;
|
||
|
|
||
|
procedure ReadCollectionProp(ACollection: TCollection);
|
||
|
var
|
||
|
i: integer;
|
||
|
Item: TPersistent;
|
||
|
begin
|
||
|
ACollection.BeginUpdate;
|
||
|
try
|
||
|
ACollection.Clear;
|
||
|
for i := 0 to AChildNode.NodeCount - 1 do
|
||
|
begin
|
||
|
Item := ACollection.Add;
|
||
|
ReadObject(AChildNode.Nodes[i], Item, AParent);
|
||
|
end;
|
||
|
finally
|
||
|
ACollection.EndUpdate;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure SetObjectProp(const AValue: string);
|
||
|
var
|
||
|
AClassName: string;
|
||
|
PropObject: TObject;
|
||
|
Reference: TComponent;
|
||
|
begin
|
||
|
if length(AValue) = 0 then
|
||
|
exit;
|
||
|
if AValue[1] = '(' then
|
||
|
begin
|
||
|
// Persistent class
|
||
|
AClassName := Copy(AValue, 2, length(AValue) - 2);
|
||
|
PropObject := TObject(GetOrdProp(AObject, PropInfo));
|
||
|
if assigned(PropObject) and (PropObject.ClassName = AClassName) then
|
||
|
begin
|
||
|
if PropObject is TCollection then
|
||
|
ReadCollectionProp(TCollection(PropObject))
|
||
|
else
|
||
|
begin
|
||
|
if AObject is TComponent then
|
||
|
ReadObject(AChildNode, PropObject, TComponent(AObject))
|
||
|
else
|
||
|
ReadObject(AChildNode, PropObject, AParent);
|
||
|
end;
|
||
|
end else
|
||
|
raise Exception.Create(sxrUnregisteredClassType);
|
||
|
end else
|
||
|
begin
|
||
|
// Component reference
|
||
|
if assigned(AParent) then
|
||
|
begin
|
||
|
Reference := FindNestedComponent(AParent, AValue);
|
||
|
SetOrdProp(AObject, PropInfo, Longint(Reference));
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure SetMethodProp(const AValue: string);
|
||
|
var
|
||
|
Method: TMethod;
|
||
|
begin
|
||
|
// to do: add OnFindMethod
|
||
|
if not assigned(AParent) then
|
||
|
exit;
|
||
|
Method.Code := AParent.MethodAddress(AValue);
|
||
|
if not assigned(Method.Code) then
|
||
|
raise Exception.Create(sxwInvalidMethodName);
|
||
|
Method.Data := AParent;
|
||
|
TypInfo.SetMethodProp(AObject, PropInfo, Method);
|
||
|
end;
|
||
|
|
||
|
procedure SetVariantProp(const AValue: string);
|
||
|
var
|
||
|
VType: integer;
|
||
|
Value: Variant;
|
||
|
ACurrency: Currency;
|
||
|
begin
|
||
|
VType := StrToInt('$' + AChildNode.AttributeByName['VarType']);
|
||
|
|
||
|
case VType and varTypeMask of
|
||
|
varNull: Value := Null;
|
||
|
varOleStr: Value := AChildNode.ValueAsUnicodeString;
|
||
|
varString: Value := AChildNode.ValueAsString;
|
||
|
varByte,
|
||
|
varSmallInt,
|
||
|
varInteger: Value := AChildNode.ValueAsInteger;
|
||
|
varSingle,
|
||
|
varDouble: Value := AChildNode.ValueAsFloat;
|
||
|
varCurrency:
|
||
|
begin
|
||
|
AChildNode.BufferRead(ACurrency, SizeOf(ACurrency));
|
||
|
Value := ACurrency;
|
||
|
end;
|
||
|
varDate: Value := AChildNode.ValueAsDateTime;
|
||
|
varBoolean: Value := AChildNode.ValueAsBool;
|
||
|
else
|
||
|
try
|
||
|
Value := ANode.ValueAsString;
|
||
|
except
|
||
|
raise Exception.Create(sxwIllegalVarType);
|
||
|
end;
|
||
|
end;//case
|
||
|
|
||
|
TVarData(Value).VType := VType;
|
||
|
TypInfo.SetVariantProp(AObject, PropInfo, Value);
|
||
|
end;
|
||
|
|
||
|
begin
|
||
|
if (PPropInfo(PropInfo)^.SetProc <> nil) and
|
||
|
(PPropInfo(PropInfo)^.GetProc <> nil) then
|
||
|
begin
|
||
|
PropType := PPropInfo(PropInfo)^.PropType^;
|
||
|
AChildNode := ANode.NodeByName(PPropInfo(PropInfo)^.Name);
|
||
|
if assigned(AChildNode) then
|
||
|
begin
|
||
|
// Non-default values from XML
|
||
|
case PropType^.Kind of
|
||
|
tkInteger: SetIntProp(AChildNode.ValueAsString);
|
||
|
tkChar: SetCharProp(AChildNode.ValueAsString);
|
||
|
tkSet: SetSetProp(AChildNode.ValueAsString);
|
||
|
tkEnumeration: SetEnumProp(AChildNode.ValueAsString);
|
||
|
tkFloat: SetFloatProp(AObject, PropInfo, AChildNode.ValueAsFloat);
|
||
|
tkString,
|
||
|
tkLString: SetStrProp(AObject, PropInfo, AChildNode.ValueAsString);
|
||
|
tkWString: SetWideStrProp(AObject, PropInfo, UTF8Decode(AChildNode.ValueAsUnicodeString));
|
||
|
{$IFDEF D12UP}
|
||
|
tkUString: SetUnicodeStrProp(AObject, PropInfo, AChildNode.ValueAsUnicodeString);
|
||
|
{$ENDIF}
|
||
|
tkClass: SetObjectProp(AChildNode.ValueAsString);
|
||
|
tkMethod: SetMethodProp(AChildNode.ValueAsString);
|
||
|
tkVariant: SetVariantProp(AChildNode.ValueAsString);
|
||
|
tkInt64: SetInt64Prop(AObject, PropInfo, AChildNode.ValueAsInt64);
|
||
|
end;//case
|
||
|
end else
|
||
|
begin
|
||
|
// Set Default value
|
||
|
case PropType^.Kind of
|
||
|
tkInteger: SetOrdProp(AObject, PropInfo, PPropInfo(PropInfo)^.Default);
|
||
|
tkChar: SetOrdProp(AObject, PropInfo, PPropInfo(PropInfo)^.Default);
|
||
|
tkSet: SetOrdProp(AObject, PropInfo, PPropInfo(PropInfo)^.Default);
|
||
|
tkEnumeration: SetOrdProp(AObject, PropInfo, PPropInfo(PropInfo)^.Default);
|
||
|
tkFloat: SetFloatProp(AObject, PropInfo, 0);
|
||
|
tkString,
|
||
|
tkLString,
|
||
|
tkWString: SetStrProp(AObject, PropInfo, '');
|
||
|
{$IFDEF D12UP}
|
||
|
tkUString: SetStrProp(AObject, PropInfo, '');
|
||
|
{$ENDIF}
|
||
|
tkClass:
|
||
|
begin
|
||
|
PropObject := TObject(GetOrdProp(AObject, PropInfo));
|
||
|
if PropObject is TComponent then
|
||
|
SetOrdProp(AObject, PropInfo, 0);
|
||
|
end;
|
||
|
tkMethod:
|
||
|
begin
|
||
|
Method := TypInfo.GetMethodProp(AObject, PropInfo);
|
||
|
Method.Code := nil;
|
||
|
TypInfo.SetMethodProp(AObject, PropInfo, Method);
|
||
|
end;
|
||
|
tkInt64: SetInt64Prop(AObject, PropInfo, 0);
|
||
|
end;//case
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
{ TComponentAccess }
|
||
|
|
||
|
procedure TComponentAccess.SetComponentState(const AState: TComponentState);
|
||
|
type
|
||
|
PInteger = ^integer;
|
||
|
var
|
||
|
PSet: PInteger;
|
||
|
AInfo: PPropInfo;
|
||
|
begin
|
||
|
// This is a "severe" hack in order to set a non-writable property value,
|
||
|
// also using RTTI
|
||
|
PSet := PInteger(@AState);
|
||
|
AInfo := GetPropInfo(TComponentAccess, 'ComponentState');
|
||
|
if assigned(AInfo.GetProc) then
|
||
|
PInteger(Integer(Self) + Integer(AInfo.GetProc) and $00FFFFFF)^ := PSet^;
|
||
|
end;
|
||
|
|
||
|
end.
|
||
|
|