mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-11-04 14:49:34 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			399 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			399 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
unit MainUnit;
 | 
						|
 | 
						|
{$mode objfpc}{$H+}
 | 
						|
 | 
						|
interface
 | 
						|
 | 
						|
uses
 | 
						|
  Classes, SysUtils, LCLProc, LResources, Forms, Controls, Graphics, Dialogs,
 | 
						|
  FileUtil, StdCtrls, Laz_XMLStreaming, Laz2_DOM, Laz2_XMLCfg, Buttons, TypInfo;
 | 
						|
 | 
						|
type
 | 
						|
  TMyEnum = (myEnum1, myEnum2, myEnum3);
 | 
						|
  TMySet = set of TMyEnum;
 | 
						|
  
 | 
						|
  { TMyCollectionItem }
 | 
						|
 | 
						|
  TMyCollectionItem = class(TCollectionItem)
 | 
						|
  private
 | 
						|
    FMyString: string;
 | 
						|
  published
 | 
						|
    property MyString: string read FMyString write FMyString;
 | 
						|
  end;
 | 
						|
  
 | 
						|
  { TMyComponent }
 | 
						|
 | 
						|
  TMyComponent = class(TComponent)
 | 
						|
  private
 | 
						|
    FMyBoolean: Boolean;
 | 
						|
    FMyCollection: TCollection;
 | 
						|
    FMyDouble: Double;
 | 
						|
    FMyEnum: TMyEnum;
 | 
						|
    FMyInt64: int64;
 | 
						|
    FMyInteger: integer;
 | 
						|
    FMySet: TMySet;
 | 
						|
    FMySingle: Single;
 | 
						|
    FMyString: string;
 | 
						|
    FMyStrings: TStrings;
 | 
						|
    FMyWideString: widestring;
 | 
						|
  public
 | 
						|
    constructor Create(TheOwner: TComponent); override;
 | 
						|
    destructor Destroy; override;
 | 
						|
    procedure WriteDebugReport;
 | 
						|
  published
 | 
						|
    property MyDouble: Double read FMyDouble write FMyDouble;
 | 
						|
    property MySingle: Single read FMySingle write FMySingle;
 | 
						|
    property MyWideString: widestring read FMyWideString write FMyWideString;
 | 
						|
    property MyInteger: integer read FMyInteger write FMyInteger;
 | 
						|
    property MyString: string read FMyString write FMyString;
 | 
						|
    property MyInt64: int64 read FMyInt64 write FMyInt64;
 | 
						|
    property MySet: TMySet read FMySet write FMySet;
 | 
						|
    property MyBoolean: Boolean read FMyBoolean write FMyBoolean;
 | 
						|
    property MyEnum: TMyEnum read FMyEnum write FMyEnum;
 | 
						|
    property MyCollection: TCollection read FMyCollection write FMyCollection;
 | 
						|
    property MyStrings: TStrings read FMyStrings write FMyStrings;
 | 
						|
  end;
 | 
						|
  
 | 
						|
  { TMyGroupBox }
 | 
						|
 | 
						|
  TMyGroupBox = class(TGroupBox)
 | 
						|
  published
 | 
						|
    procedure AnEvent(Sender: TObject);
 | 
						|
  end;
 | 
						|
 | 
						|
 | 
						|
  { TStreamAsXMLForm }
 | 
						|
 | 
						|
  TStreamAsXMLForm = class(TForm)
 | 
						|
    Button1: TButton;
 | 
						|
    SourceGroupBox: TGroupBox;
 | 
						|
    DestinationGroupBox: TGroupBox;
 | 
						|
    procedure FormCreate(Sender: TObject);
 | 
						|
  private
 | 
						|
    FFilename: string;
 | 
						|
    procedure SetFilename(const AValue: string);
 | 
						|
  public
 | 
						|
    MyComponent: TMyComponent;
 | 
						|
    DemoGroupBox: TMyGroupBox;
 | 
						|
 | 
						|
    procedure WriteComponents;
 | 
						|
    procedure ReadComponents;
 | 
						|
    procedure OnFindComponentClass({%H-}Reader: TReader; const AClassName: string;
 | 
						|
                                   var ComponentClass: TComponentClass);
 | 
						|
    property Filename: string read FFilename write SetFilename;
 | 
						|
  end; 
 | 
						|
 | 
						|
var
 | 
						|
  StreamAsXMLForm: TStreamAsXMLForm;
 | 
						|
 | 
						|
function CreateXMLWriter(ADoc: TDOMDocument; const Path: string;
 | 
						|
  Append: Boolean; var DestroyDriver: boolean): TWriter;
 | 
						|
function CreateXMLReader(ADoc: TDOMDocument; const Path: string;
 | 
						|
  var DestroyDriver: boolean): TReader;
 | 
						|
 | 
						|
procedure WriteComponentToXMLConfig(XMLConfig: TXMLConfig; const Path: string;
 | 
						|
  AComponent: TComponent);
 | 
						|
procedure ReadComponentFromXMLConfig(XMLConfig: TXMLConfig; const Path: string;
 | 
						|
  var RootComponent: TComponent;
 | 
						|
  OnFindComponentClass: TFindComponentClassEvent; TheOwner: TComponent);
 | 
						|
 | 
						|
implementation
 | 
						|
 | 
						|
function CreateXMLWriter(ADoc: TDOMDocument; const Path: string;
 | 
						|
  Append: Boolean; var DestroyDriver: boolean): TWriter;
 | 
						|
var
 | 
						|
  Driver: TAbstractObjectWriter;
 | 
						|
begin
 | 
						|
  Driver:=TXMLObjectWriter.Create(ADoc,Path,Append);
 | 
						|
  DestroyDriver:=true;
 | 
						|
  Result:=TWriter.Create(Driver);
 | 
						|
end;
 | 
						|
 | 
						|
function CreateXMLReader(ADoc: TDOMDocument; const Path: string;
 | 
						|
  var DestroyDriver: boolean): TReader;
 | 
						|
var
 | 
						|
  p: Pointer;
 | 
						|
  Driver: TAbstractObjectReader;
 | 
						|
  DummyStream: TMemoryStream;
 | 
						|
begin
 | 
						|
  DummyStream:=TMemoryStream.Create;
 | 
						|
  try
 | 
						|
    Result:=TReader.Create(DummyStream,256);
 | 
						|
    DestroyDriver:=false;
 | 
						|
    // hack to set a write protected variable.
 | 
						|
    // DestroyDriver:=true; TReader will free it
 | 
						|
    Driver:=TXMLObjectReader.Create(ADoc,Path);
 | 
						|
    p:=@Result.Driver;
 | 
						|
    Result.Driver.Free;
 | 
						|
    TAbstractObjectReader(p^):=Driver;
 | 
						|
  finally
 | 
						|
    DummyStream:=nil;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
procedure WriteComponentToXMLConfig(XMLConfig: TXMLConfig; const Path: string;
 | 
						|
  AComponent: TComponent);
 | 
						|
var
 | 
						|
  Writer: TWriter;
 | 
						|
  DestroyDriver: boolean;
 | 
						|
begin
 | 
						|
  Writer:=nil;
 | 
						|
  DestroyDriver:=false;
 | 
						|
  try
 | 
						|
    Writer:=CreateXMLWriter(XMLConfig.Document,Path,false,DestroyDriver);
 | 
						|
    XMLConfig.Modified:=true;
 | 
						|
    Writer.WriteRootComponent(AComponent);
 | 
						|
    XMLConfig.Flush;
 | 
						|
  finally
 | 
						|
    if DestroyDriver then
 | 
						|
      Writer.Driver.Free;
 | 
						|
    Writer.Free;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
procedure ReadComponentFromXMLConfig(XMLConfig: TXMLConfig; const Path: string;
 | 
						|
  var RootComponent: TComponent;
 | 
						|
  OnFindComponentClass: TFindComponentClassEvent; TheOwner: TComponent);
 | 
						|
var
 | 
						|
  DestroyDriver: Boolean;
 | 
						|
  Reader: TReader;
 | 
						|
  IsInherited: Boolean;
 | 
						|
  AClassName: String;
 | 
						|
  AClass: TComponentClass;
 | 
						|
begin
 | 
						|
  Reader:=nil;
 | 
						|
  DestroyDriver:=false;
 | 
						|
  try
 | 
						|
    Reader:=CreateXMLReader(XMLConfig.Document,Path,DestroyDriver);
 | 
						|
    Reader.OnFindComponentClass:=OnFindComponentClass;
 | 
						|
 | 
						|
    // get root class
 | 
						|
    AClassName:=(Reader.Driver as TXMLObjectReader).GetRootClassName(IsInherited);
 | 
						|
    if IsInherited then begin
 | 
						|
      // inherited is not supported by this simple function
 | 
						|
      DebugLn('ReadComponentFromXMLConfig WARNING: "inherited" is not supported by this simple function');
 | 
						|
    end;
 | 
						|
    AClass:=nil;
 | 
						|
    OnFindComponentClass(nil,AClassName,AClass);
 | 
						|
    if AClass=nil then
 | 
						|
      raise EClassNotFound.CreateFmt('Class "%s" not found', [AClassName]);
 | 
						|
 | 
						|
    if RootComponent=nil then begin
 | 
						|
      // create root component
 | 
						|
      // first create the new instance and set the variable ...
 | 
						|
      RootComponent:=AClass.NewInstance as TComponent;
 | 
						|
      // then call the constructor
 | 
						|
      RootComponent.Create(TheOwner);
 | 
						|
    end else begin
 | 
						|
      // there is a root component, check if class is compatible
 | 
						|
      if not RootComponent.InheritsFrom(AClass) then begin
 | 
						|
        raise EComponentError.CreateFmt('Cannot assign a %s to a %s.',
 | 
						|
                                        [AClassName,RootComponent.ClassName]);
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    Reader.ReadRootComponent(RootComponent);
 | 
						|
  finally
 | 
						|
    if DestroyDriver then
 | 
						|
      Reader.Driver.Free;
 | 
						|
    Reader.Free;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
{ TStreamAsXMLForm }
 | 
						|
 | 
						|
procedure TStreamAsXMLForm.FormCreate(Sender: TObject);
 | 
						|
var
 | 
						|
  MySubComponent: TMyComponent;
 | 
						|
  DemoGroupBox_1: TGroupBox;
 | 
						|
  DemoGroupBox_2: TGroupBox;
 | 
						|
begin
 | 
						|
  Filename:='test.xml';
 | 
						|
 | 
						|
  MyComponent:=TMyComponent.Create(Self);
 | 
						|
  with MyComponent do begin
 | 
						|
    Name:='MyComponent';
 | 
						|
  end;
 | 
						|
  MySubComponent:=TMyComponent.Create(MyComponent);
 | 
						|
  with MySubComponent do begin
 | 
						|
    Name:='MySubComponent';
 | 
						|
  end;
 | 
						|
  
 | 
						|
  DemoGroupBox:=TMyGroupBox.Create(Self);
 | 
						|
  with DemoGroupBox do begin
 | 
						|
    Name:='DemoGroupBox';
 | 
						|
    SetBounds(100,2,320,180);
 | 
						|
    Parent:=SourceGroupBox;
 | 
						|
    OnClick:=@DemoGroupBox.AnEvent;
 | 
						|
  end;
 | 
						|
  
 | 
						|
  // create nested controls
 | 
						|
  DemoGroupBox_1:=TGroupBox.Create(DemoGroupBox);
 | 
						|
  with DemoGroupBox_1 do begin
 | 
						|
    Name:='DemoGroupBox_1';
 | 
						|
    Parent:=DemoGroupBox;
 | 
						|
    SetBounds(5,5,150,150);
 | 
						|
    with TButton.Create(DemoGroupBox) do begin
 | 
						|
      Name:='Button1';
 | 
						|
      Parent:=DemoGroupBox_1;
 | 
						|
      SetBounds(10,20,80,30);
 | 
						|
    end;
 | 
						|
    with TButton.Create(DemoGroupBox) do begin
 | 
						|
      Name:='Button2';
 | 
						|
      Parent:=DemoGroupBox_1;
 | 
						|
      SetBounds(10,60,80,20);
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
  DemoGroupBox_2:=TGroupBox.Create(DemoGroupBox);
 | 
						|
  with DemoGroupBox_2 do begin
 | 
						|
    Name:='DemoGroupBox_2';
 | 
						|
    Parent:=DemoGroupBox;
 | 
						|
    SetBounds(155,5,150,150);
 | 
						|
    with TButton.Create(DemoGroupBox) do begin
 | 
						|
      Name:='Button3';
 | 
						|
      Parent:=DemoGroupBox_2;
 | 
						|
      SetBounds(10,20,80,30);
 | 
						|
    end;
 | 
						|
    with TButton.Create(DemoGroupBox) do begin
 | 
						|
      Name:='Button4';
 | 
						|
      Parent:=DemoGroupBox_2;
 | 
						|
      SetBounds(10,60,80,20);
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
 | 
						|
  WriteComponents;
 | 
						|
  ReadComponents;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStreamAsXMLForm.SetFilename(const AValue: string);
 | 
						|
begin
 | 
						|
  if FFilename=AValue then exit;
 | 
						|
  FFilename:=AValue;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStreamAsXMLForm.WriteComponents;
 | 
						|
var
 | 
						|
  XMLConfig: TXMLConfig;
 | 
						|
  sl: TStringList;
 | 
						|
begin
 | 
						|
  DebugLn('TStreamAsXMLForm.WriteComponents ',Filename);
 | 
						|
  XMLConfig:=TXMLConfig.Create(Filename);
 | 
						|
  try
 | 
						|
    //WriteComponentToXMLConfig(XMLConfig,'Component',Self);
 | 
						|
    WriteComponentToXMLConfig(XMLConfig,'Component',MyComponent);
 | 
						|
    //WriteComponentToXMLConfig(XMLConfig,'Component',DemoGroupBox);
 | 
						|
    XMLConfig.Flush;
 | 
						|
  finally
 | 
						|
    XMLConfig.Free;
 | 
						|
  end;
 | 
						|
  
 | 
						|
  sl:=TStringList.Create;
 | 
						|
  sl.LoadFromFile(UTF8ToSys(Filename));
 | 
						|
  DebugLn('TStreamAsXMLForm.WriteComponents ',sl.Text);
 | 
						|
  sl.Free;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStreamAsXMLForm.ReadComponents;
 | 
						|
var
 | 
						|
  XMLConfig: TXMLConfig;
 | 
						|
  sl: TStringList;
 | 
						|
  NewComponent: TComponent;
 | 
						|
begin
 | 
						|
  DebugLn('TStreamAsXMLForm.ReadComponents ',Filename);
 | 
						|
  XMLConfig:=TXMLConfig.Create(Filename);
 | 
						|
  try
 | 
						|
    NewComponent:=nil;
 | 
						|
    ReadComponentFromXMLConfig(XMLConfig,'Component',NewComponent,
 | 
						|
      @OnFindComponentClass,DestinationGroupBox);
 | 
						|
    if NewComponent is TMyComponent then
 | 
						|
      TMyComponent(NewComponent).WriteDebugReport;
 | 
						|
    if NewComponent is TControl then
 | 
						|
      TControl(NewComponent).Parent:=DestinationGroupBox;
 | 
						|
    XMLConfig.Flush;
 | 
						|
  finally
 | 
						|
    XMLConfig.Free;
 | 
						|
  end;
 | 
						|
  
 | 
						|
  sl:=TStringList.Create;
 | 
						|
  sl.LoadFromFile(UTF8ToSys(Filename));
 | 
						|
  DebugLn('TStreamAsXMLForm.StreamComponents ',sl.Text);
 | 
						|
  sl.Free;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStreamAsXMLForm.OnFindComponentClass(Reader: TReader;
 | 
						|
  const AClassName: string; var ComponentClass: TComponentClass);
 | 
						|
begin
 | 
						|
  if CompareText(AClassName,'TGroupBox')=0 then
 | 
						|
    ComponentClass:=TGroupBox
 | 
						|
  else if CompareText(AClassName,'TButton')=0 then
 | 
						|
    ComponentClass:=TButton
 | 
						|
  else if CompareText(AClassName,'TMyComponent')=0 then
 | 
						|
    ComponentClass:=TMyComponent
 | 
						|
  else if CompareText(AClassName,'TMyGroupBox')=0 then
 | 
						|
    ComponentClass:=TMyGroupBox;
 | 
						|
  DebugLn('TStreamAsXMLForm.OnFindComponentClass ',AClassName,' ',dbgs(ComponentClass));
 | 
						|
end;
 | 
						|
 | 
						|
{ TMyComponent }
 | 
						|
 | 
						|
constructor TMyComponent.Create(TheOwner: TComponent);
 | 
						|
begin
 | 
						|
  inherited Create(TheOwner);
 | 
						|
  MyDouble:=-1.23456789;
 | 
						|
  MySingle:=-1.98765432;
 | 
						|
  MyEnum:=myEnum2;
 | 
						|
  MySet:=[myEnum1,myEnum3];
 | 
						|
  MyString:='Some text as string';
 | 
						|
  MyWideString:='Some text as widestring';
 | 
						|
  MyInteger:=1234;
 | 
						|
  MyBoolean:=true;
 | 
						|
  MyInt64:=1234567890987654321;
 | 
						|
  MyCollection:=TCollection.Create(TMyCollectionItem);
 | 
						|
  TMyCollectionItem(MyCollection.Add).MyString:='First';
 | 
						|
  TMyCollectionItem(MyCollection.Add).MyString:='Second';
 | 
						|
  TMyCollectionItem(MyCollection.Add).MyString:='Third';
 | 
						|
  FMyStrings:=TStringList.Create;
 | 
						|
  FMyStrings.Text:='FirstLine'#10'NextLine';
 | 
						|
end;
 | 
						|
 | 
						|
destructor TMyComponent.Destroy;
 | 
						|
begin
 | 
						|
  FreeAndNil(FMyStrings);
 | 
						|
  inherited Destroy;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TMyComponent.WriteDebugReport;
 | 
						|
var
 | 
						|
  i: Integer;
 | 
						|
  Item: TMyCollectionItem;
 | 
						|
begin
 | 
						|
  writeln('TMyComponent.WriteDebugReport ');
 | 
						|
  writeln('  MyDouble=',MyDouble);
 | 
						|
  writeln('  MySingle=',MySingle);
 | 
						|
  writeln('  MyEnum=',GetEnumName(TypeInfo(TMyEnum),ord(MyEnum)));
 | 
						|
  writeln('  MySet=',HexStr(Cardinal(MySet),8));
 | 
						|
  writeln('  MyString=',MyString);
 | 
						|
  writeln('  MyWideString=',MyWideString);
 | 
						|
  writeln('  MyInteger=',MyInteger);
 | 
						|
  writeln('  MyInt64=',MyInt64);
 | 
						|
  writeln('  MyCollection.Count=',MyCollection.Count);
 | 
						|
  for i:=0 to MyCollection.Count-1 do begin
 | 
						|
    Item:=TMyCollectionItem(MyCollection.Items[i]);
 | 
						|
    writeln('    ',i,' MyString=',Item.MyString);
 | 
						|
  end;
 | 
						|
  writeln('  MyStrings='+dbgstr(MyStrings.Text));
 | 
						|
end;
 | 
						|
 | 
						|
{ TMyGroupBox }
 | 
						|
 | 
						|
procedure TMyGroupBox.AnEvent(Sender: TObject);
 | 
						|
begin
 | 
						|
 | 
						|
end;
 | 
						|
 | 
						|
initialization
 | 
						|
  {$I mainunit.lrs}
 | 
						|
 | 
						|
end.
 | 
						|
 |