[Pas2js] Pas2JS Widget

Ulrich, Christian christian.ulrich at tcsag.de
Tue Dec 4 15:40:09 CET 2018


Is that whats in your mind (not jet working) : ?

https://github.com/cutec-chris/Pas2JS_Widget.git

-----Ursprüngliche Nachricht-----
Von: Pas2js [mailto:pas2js-bounces at lists.freepascal.org] Im Auftrag von warleyalex via Pas2js
Gesendet: Freitag, 30. November 2018 18:16
An: pas2js at lists.freepascal.org
Cc: warleyalex
Betreff: Re: [Pas2js] Pas2JS Widget

Ulrich, Christian wrote
> I have taken an deeper look.

The Helios's widgets are dependent of the the closed source executable
"pas2js_build.exe" :(
...the good news is you can compile the same widgets without this tool :)

The Helios's tool uses a precompiler that converts native widget to web
widget with structure similar to LCL and injects into loader session, he
performs the form serialization using this closed tool "pas2js_build.exe".
but it seems this tool is using CompWriterPas to perform the serialization
straightforward on before save the the project serialize the forms on the
fly.
for instance, the "unit1.lfm" becomes "unit1.wfm"

If you can modify the plugins/project:
 
https://github.com/pas2js/master/blob/master/pas2jsdsgn_plus.rar

This is a modified pas2jsdsgn package. You can merge with Helios's
implementations, and create a simple package without pas2js_build
dependency.

I sincerely don't have enough time to sit down and finish the CompWriterPas
unit.

for instance below, the unit1.lfm become Form1.lfm.inc. 

Note that a CompWriterPas modification is required.
Look the block try..finally used in the serialization.
Some non visual components like "TClientDataSet" which base class is
TComponent, shouldn't be envolved with try..finally block.

Another thing, I believe the property "Parent" should comes first, like
WebDBLabel1.Parent := Self;


//--The Form1.lfm.inc ---------------------------
  WebDBLabel1:=          TDBLabel.Create(Self);
  WebLabel1:=             TLabel.Create(Self);
  WebLabel2:=             TLabel.Create(Self);
  WebLabel3:=             TLabel.Create(Self);
  WebLabel4:=             TLabel.Create(Self);
  WebLabel6:=             TLabel.Create(Self);
  WebLabel7:=             TLabel.Create(Self);
  WebLabel5:=             TLabel.Create(Self);
  WebButton1:=            TButton.Create(Self);
  WebDBNavigator1:=       TDBNavigator.Create(Self);
  WebDBEdit1:=            TDBEdit.Create(Self);
  WebDBEdit2:=            TDBEdit.Create(Self);
  WebDBEdit3:=            TDBEdit.Create(Self);
  WebDBEdit4:=            TDBEdit.Create(Self);
  WebPanel1:=             TPanel.Create(Self);
  WebLabel9:=             TLabel.Create(WebPanel1);
  WebImageControl1:=      TImageControl.Create(WebPanel1);
  WebDBMemo1:=            TDBMemo.Create(Self);
  WebDBSpinEdit1:=        TDBSpinEdit.Create(Self);

  WebClientConnection1:=  TClientConnection.Create(Self);
  WebClientDataSet1:=     TClientDataSet.Create(Self);
  WebClientDataSource1:=  TClientDataSource.Create(Self);

  WebDBLabel1.BeginUpdate;
  WebLabel1.BeginUpdate;
  WebLabel2.BeginUpdate;
  WebLabel3.BeginUpdate;
  WebLabel4.BeginUpdate;
  WebLabel6.BeginUpdate;
  WebLabel7.BeginUpdate;
  WebLabel5.BeginUpdate;
  WebButton1.BeginUpdate;
  WebDBNavigator1.BeginUpdate;
  WebDBEdit1.BeginUpdate;
  WebDBEdit2.BeginUpdate;
  WebDBEdit3.BeginUpdate;
  WebDBEdit4.BeginUpdate;
  WebPanel1.BeginUpdate;
  WebLabel9.BeginUpdate;
  WebImageControl1.BeginUpdate;
  WebDBMemo1.BeginUpdate;
  WebDBSpinEdit1.BeginUpdate;

  try
    Self.Name := 'Form1';
    Self.Left := 0;
    Self.Top := 0;
    Self.Width := 775;
    Self.Height := 575;
    Self.Font.Charset := 1;
    Self.Font.Color := 0;
    Self.Font.Height := -13;
    Self.Font.Name := 'Tahoma';
    Self.Font.Style := [];
    Self.FFormContainer := 'appcontent';
    Self.TabOrder := 1;

    WebDBLabel1.Parent := Self;
    WebDBLabel1.Name := 'WebDBLabel1';
    WebDBLabel1.Left := 132;
    WebDBLabel1.Top := 257;
    WebDBLabel1.Width := 457;
    WebDBLabel1.Height := 22;
    WebDBLabel1.AutoSize := false;
    WebDBLabel1.Caption := 'WebDBLabel1';
    WebDBLabel1.EllipsisPosition := epEndEllipsis;
    WebDBLabel1.DataField := '_Length_In';
    WebDBLabel1.DataSource := WebClientDataSource1;

    WebLabel1.Parent := Self;
    WebLabel1.Name := 'WebLabel1';
    WebLabel1.Left := 16;
    WebLabel1.Top := 98;
    WebLabel1.Width := 68;
    WebLabel1.Height := 16;
    WebLabel1.Caption := 'Species No:';

    WebLabel2.Parent := Self;
    WebLabel2.Name := 'WebLabel2';
    WebLabel2.Left := 16;
    WebLabel2.Top := 130;
    WebLabel2.Width := 56;
    WebLabel2.Height := 16;
    WebLabel2.Caption := 'Category:';

    WebLabel3.Parent := Self;
    WebLabel3.Name := 'WebLabel3';
    WebLabel3.Left := 16;
    WebLabel3.Top := 162;
    WebLabel3.Width := 93;
    WebLabel3.Height := 16;
    WebLabel3.Caption := 'Common Name:';

    WebLabel4.Parent := Self;
    WebLabel4.Name := 'WebLabel4';
    WebLabel4.Left := 16;
    WebLabel4.Top := 195;
    WebLabel4.Width := 86;
    WebLabel4.Height := 16;
    WebLabel4.Caption := 'Species Name:';

    WebLabel6.Parent := Self;
    WebLabel6.Name := 'WebLabel6';
    WebLabel6.Left := 16;
    WebLabel6.Top := 226;
    WebLabel6.Width := 64;
    WebLabel6.Height := 16;
    WebLabel6.Caption := 'Length cm:';

    WebLabel7.Parent := Self;
    WebLabel7.Name := 'WebLabel7';
    WebLabel7.Left := 16;
    WebLabel7.Top := 257;
    WebLabel7.Width := 58;
    WebLabel7.Height := 16;
    WebLabel7.Caption := 'Length In:';

    WebLabel5.Parent := Self;
    WebLabel5.Name := 'WebLabel5';
    WebLabel5.Left := 16;
    WebLabel5.Top := 288;
    WebLabel5.Width := 37;
    WebLabel5.Height := 16;
    WebLabel5.Caption := 'Notes:';

    WebButton1.Parent := Self;
    WebButton1.Name := 'WebButton1';
    WebButton1.Left := 16;
    WebButton1.Top := 16;
    WebButton1.Width := 153;
    WebButton1.Height := 25;
    WebButton1.Caption := 'Connect to DB';
    WebButton1.OnClick := @WebButton1Click;
    WebButton1.TabOrder := 0;

    WebDBNavigator1.Parent := Self;
    WebDBNavigator1.Name := 'WebDBNavigator1';
    WebDBNavigator1.Left := 132;
    WebDBNavigator1.Top := 60;
    WebDBNavigator1.Width := 288;
    WebDBNavigator1.Height := 25;
    WebDBNavigator1.DataSource := WebClientDataSource1;

    WebDBEdit1.Parent := Self;
    WebDBEdit1.Name := 'WebDBEdit1';
    WebDBEdit1.Left := 132;
    WebDBEdit1.Top := 95;
    WebDBEdit1.Width := 457;
    WebDBEdit1.Height := 24;
    WebDBEdit1.AutoSelect := false;
    WebDBEdit1.Color := 16777215;
    WebDBEdit1.HideSelection := false;
    WebDBEdit1.TabOrder := 2;
    WebDBEdit1.Text := 'WebDBEdit1';
    WebDBEdit1.DataField := '_Species_No';
    WebDBEdit1.DataSource := WebClientDataSource1;

    WebDBEdit2.Parent := Self;
    WebDBEdit2.Name := 'WebDBEdit2';
    WebDBEdit2.Left := 132;
    WebDBEdit2.Top := 127;
    WebDBEdit2.Width := 457;
    WebDBEdit2.Height := 24;
    WebDBEdit2.AutoSelect := false;
    WebDBEdit2.Color := 16777215;
    WebDBEdit2.HideSelection := false;
    WebDBEdit2.TabOrder := 3;
    WebDBEdit2.Text := 'WebDBEdit1';
    WebDBEdit2.DataField := '_Category';
    WebDBEdit2.DataSource := WebClientDataSource1;

    WebDBEdit3.Parent := Self;
    WebDBEdit3.Name := 'WebDBEdit3';
    WebDBEdit3.Left := 132;
    WebDBEdit3.Top := 159;
    WebDBEdit3.Width := 457;
    WebDBEdit3.Height := 24;
    WebDBEdit3.AutoSelect := false;
    WebDBEdit3.Color := 16777215;
    WebDBEdit3.HideSelection := false;
    WebDBEdit3.TabOrder := 4;
    WebDBEdit3.Text := 'WebDBEdit1';
    WebDBEdit3.DataField := '_Common_Name';
    WebDBEdit3.DataSource := WebClientDataSource1;

    WebDBEdit4.Parent := Self;
    WebDBEdit4.Name := 'WebDBEdit4';
    WebDBEdit4.Left := 132;
    WebDBEdit4.Top := 192;
    WebDBEdit4.Width := 457;
    WebDBEdit4.Height := 24;
    WebDBEdit4.AutoSelect := false;
    WebDBEdit4.Color := 16777215;
    WebDBEdit4.HideSelection := false;
    WebDBEdit4.TabOrder := 5;
    WebDBEdit4.Text := 'WebDBEdit1';
    WebDBEdit4.DataField := '_Species_Name';
    WebDBEdit4.DataSource := WebClientDataSource1;

    WebPanel1.Parent := Self;
    WebPanel1.Name := 'WebPanel1';
    WebPanel1.Left := 16;
    WebPanel1.Top := 465;
    WebPanel1.Width := 541;
    WebPanel1.Height := 89;
    WebPanel1.WidthStyle := ssPercent;
    WebPanel1.WidthPercent := 80;
    WebPanel1.BorderStyle := bsSingle;

    WebLabel9.Parent := WebPanel1;
    WebLabel9.Name := 'WebLabel9';
    WebLabel9.Left := 3;
    WebLabel9.Top := 29;
    WebLabel9.Width := 460;
    WebLabel9.Height := 48;
    WebLabel9.Caption :=
      'Self demo shows a web client dataset connected to DB controls. The
web client dataset gets the information from an Client server but for demo
purposes all editing in the dataset is local in the web client only!';
    WebLabel9.WordWrap := true;
    WebLabel9.WidthStyle := ssPercent;

    WebImageControl1.Parent := WebPanel1;
    WebImageControl1.Name := 'WebImageControl1';
    WebImageControl1.Left := 6;
    WebImageControl1.Top := 7;
    WebImageControl1.Width := 16;
    WebImageControl1.Height := 16;
    WebImageControl1.AutoSize := true;
    WebImageControl1.Picture.LoadFromFile('Picture.png');

    WebDBMemo1.Parent := Self;
    WebDBMemo1.Name := 'WebDBMemo1';
    WebDBMemo1.Left := 132;
    WebDBMemo1.Top := 285;
    WebDBMemo1.Width := 457;
    WebDBMemo1.Height := 140;
    WebDBMemo1.AutoSize := false;
    WebDBMemo1.Lines.BeginUpdate;
    try
      WebDBMemo1.Lines.Clear;
      WebDBMemo1.Lines.Add('WebDBMemo1');
    finally
      WebDBMemo1.Lines.EndUpdate;
    end;
    WebDBMemo1.SelLength := 0;
    WebDBMemo1.SelStart := 0;
    WebDBMemo1.TabOrder := 7;
    WebDBMemo1.DataField := '_Notes';
    WebDBMemo1.DataSource := WebClientDataSource1;

    WebDBSpinEdit1.Parent := Self;
    WebDBSpinEdit1.Name := 'WebDBSpinEdit1';
    WebDBSpinEdit1.Left := 132;
    WebDBSpinEdit1.Top := 223;
    WebDBSpinEdit1.Width := 150;
    WebDBSpinEdit1.Height := 22;
    WebDBSpinEdit1.AutoSize := false;
    WebDBSpinEdit1.BorderStyle := bsSingle;
    WebDBSpinEdit1.Color := 16777215;
    WebDBSpinEdit1.Increment := 1;
    WebDBSpinEdit1.MaxValue := 100;
    WebDBSpinEdit1.MinValue := 0;
    WebDBSpinEdit1.TabOrder := 8;
    WebDBSpinEdit1.Value := 0;
    WebDBSpinEdit1.DataField := '_Length__cm_';
    WebDBSpinEdit1.DataSource := WebClientDataSource1;

    WebClientConnection1.Name := 'WebClientConnection1';
    WebClientConnection1.Active := false;
    WebClientDataSet1.Name := 'WebClientDataSet1';
    WebClientDataSet1.Connection := WebClientConnection1;

    WebClientDataSource1.Name := 'WebClientDataSource1';
    WebClientDataSource1.DataSet := WebClientDataSet1;
  finally
    WebDBLabel1.EndUpdate;
    WebLabel1.EndUpdate;
    WebLabel2.EndUpdate;
    WebLabel3.EndUpdate;
    WebLabel4.EndUpdate;
    WebLabel6.EndUpdate;
    WebLabel7.EndUpdate;
    WebLabel5.EndUpdate;
    WebButton1.EndUpdate;
    WebDBNavigator1.EndUpdate;
    WebDBEdit1.EndUpdate;
    WebDBEdit2.EndUpdate;
    WebDBEdit3.EndUpdate;
    WebDBEdit4.EndUpdate;
    WebPanel1.EndUpdate;
    WebLabel9.EndUpdate;
    WebImageControl1.EndUpdate;
    WebDBMemo1.EndUpdate;
    WebDBSpinEdit1.EndUpdate;
  end;
//-----------------------------

Here is the modified ComWriterPas unit. The feature "non visual components,
based on the base class TComponent, those components are not involved on the
try..finally block", this feature is not yet implemented at the unit.

--// CompWriterPas unit -------------------
{

*****************************************************************************
  This file is part of the Lazarus Component Library (LCL)

  See the file COPYING.modifiedLGPL.txt, included in this distribution,
  for details about the license.

*****************************************************************************

Component serialisation into Pascal.

Author: Mattias Gaertner

Working:
- signature begin, end, version
- boolean, set of boolean
- char, widechar, custom char, set of custom char
- integers, custom int, set of custom int
- strings, codepage system and UTF8
- float, currency
- enum, custom enum range
- set of enum, set of custom enum range
- variant: integers, boolean, string, floats, currency
- method
- persistent
- component children, use SetParentComponent or optional Parent:=
- collection
- IInterfaceComponentReference
- with ancestor
- ancestor: change ComponentIndex -> call SetChildPos
- reference foreign root, reference foreign component
- create components before setting properties to avoid having to set
references
  later
- inline component, csInline, call SetInline, inherited inline, inline on
inherited
- TComponent.Left/Right via DesignInfo
- DefineProperties
- RegisterDefinePropertiesPas
}

unit CompWriterPas;

{$mode objfpc}{$H+}

{off $DEFINE VerboseCompWriterPas}

interface

uses
  Classes, SysUtils, typinfo, RtlConsts, contnrs, LazLoggerBase, LazUTF8;

const
  // Component serialized as Pascal
  CSPVersion = 1;
  CSPDefaultSignature = '// Component serialized as Pascal';
  CSPDefaultSignatureBegin = CSPDefaultSignature+' - Begin';
  CSPDefaultSignatureEnd = CSPDefaultSignature+' - End';
  CSPDefaultAccessClass = 'TPasStreamAccess';
  CSPDefaultExecCustomProc = 'ExecCustomCSP';
  CSPDefaultExecCustomProcUnit = 'LazPasReadUtil';
  CSPDefaultMaxColumn = 80;
  CSPDefaultAssignOp = ':=';
  CWPSkipParentName = '-';
type
  TCompWriterPas = class;
  TCWPFindAncestorEvent = procedure(Writer: TCompWriterPas; Component:
TComponent;
    const Name: string; var Ancestor, RootAncestor: TComponent) of object;
  TCWPGetMethodName = procedure(Writer: TCompWriterPas; Instance:
TPersistent;
    PropInfo: PPropInfo; out Name: String) of object;
  TCWPGetParentPropertyEvent = procedure(Writer: TCompWriterPas;
    Component: TComponent; var PropName: string) of object;
  TCWPDefinePropertiesEvent = procedure(Writer: TCompWriterPas;
    Instance: TPersistent; const Identifier: string; var Handled: boolean)
of object;

  TCWPOption = (
    cwpoNoSignature,     // do not write Begin, End signatures
    cwpoNoSelf,          // enclose in "with LookupRootname do begin"
    cwpoSetParentFirst,  // add "SetParentComponent" before setting
properties, default: after
    cwpoSrcCodepageUTF8, // target unit uses $codepage utf-8, aka do not
convert UTF-8 string literals
    cwpoNoWithBlocks     // do not use with-do
    );
  TCWPOptions = set of TCWPOption;

  TCWPChildrenStep = (
    cwpcsCreate,
    cwpcsProperties
  );

  { TCompWriterPas }

  TCompWriterPas = class
  private
    FAccessClass: string;
    FAncestor: TPersistent;
    FAncestorPos: Integer;
    FAncestors: TStringList;
    FAssignOp: String;
    FCurIndent: integer;
    FCurrentPos: Integer;
    FDefaultDefineProperties: CodePointer;
    FExecCustomProc: string;
    FExecCustomProcUnit: string;
    FIgnoreChildren: Boolean;
    FIndentStep: integer;
    FLineEnding: string;
    FLookupRoot: TComponent;
    FMaxColumn: integer;
    FNeedAccessClass: boolean;
    FNeededUnits: TStrings;
    FOnDefineProperties: TCWPDefinePropertiesEvent;
    FOnFindAncestor: TCWPFindAncestorEvent;
    FOnGetMethodName: TCWPGetMethodName;
    FOnGetParentProperty: TCWPGetParentPropertyEvent;
    FOnWriteMethodProperty: TWriteMethodPropertyEvent;
    FOnWriteStringProperty: TReadWriteStringPropertyEvent;
    FOptions: TCWPOptions;
    FParent: TComponent;
    FPropPath: string;
    FRoot: TComponent;
    FRootAncestor: TComponent;
    FSignatureBegin: String;
    FSignatureEnd: String;
    FStream: TStream;
    procedure AddToAncestorList(Component: TComponent);
    procedure DetermineAncestor(Component: TComponent);
    procedure SetNeededUnits(const AValue: TStrings);
    procedure SetRoot(const AValue: TComponent);
    procedure WriteComponentData(Instance: TComponent);
    procedure WriteChildren(Component: TComponent; Step: TCWPChildrenStep);
    procedure WriteProperty(Instance: TPersistent; PropInfo: PPropInfo);
    procedure WriteProperties(Instance: TPersistent);
    procedure WriteDefineProperties(Instance: TPersistent);
    procedure WriteCollection(PropName: string; Collection: TCollection);
    function ShortenFloat(s: string): string;
  public
    constructor Create(AStream: TStream);
    destructor Destroy; override;
    // stream a component:
    procedure WriteDescendant(ARoot: TComponent; AAncestor: TComponent =
nil);
    // utility functions:
    procedure WriteComponentCreate(Component: TComponent);
    procedure WriteComponent(Component: TComponent);
    procedure WriteIndent;
    procedure Write(const s: string);
    procedure WriteLn;
    procedure WriteStatement(const s: string);
    procedure WriteAssign(const LHS, RHS: string);
    procedure WriteWithDo(const Expr: string);
    procedure WriteWithEnd;
    function GetComponentPath(Component: TComponent): string;
    function GetBoolLiteral(b: boolean): string;
    function GetCharLiteral(c: integer): string;
    function GetWideCharLiteral(c: integer): string;
    function GetStringLiteral(const s: string): string;
    function GetWStringLiteral(p: PWideChar; Count: integer): string;
    function GetFloatLiteral(const e: Extended): string;
    function GetCurrencyLiteral(const c: currency): string;
    function GetEnumExpr(TypeInfo: PTypeInfo; Value: integer;
      AllowOutOfRange: boolean): string;
    function GetVersionStatement: string;
    function CreatedByAncestor(Component: TComponent): boolean;
    procedure AddNeededUnit(const AnUnitName: string);
    procedure Indent;
    procedure Unindent;
    property Stream: TStream read FStream;
    property Root: TComponent read FRoot write SetRoot;
    property LookupRoot: TComponent read FLookupRoot;
    property Ancestor: TPersistent read FAncestor write FAncestor;
    property RootAncestor: TComponent read FRootAncestor write
FRootAncestor;
    property Parent: TComponent read FParent;
    property OnFindAncestor: TCWPFindAncestorEvent read FOnFindAncestor
write FOnFindAncestor;
    property OnGetMethodName: TCWPGetMethodName read FOnGetMethodName write
FOnGetMethodName;
    property PropertyPath: string read FPropPath;
    property CurIndent: integer read FCurIndent write FCurIndent;
    property IndentStep: integer read FIndentStep write FIndentStep;
    property Options: TCWPOptions read FOptions write FOptions;
    property IgnoreChildren: Boolean read FIgnoreChildren write
FIgnoreChildren;
    property OnGetParentProperty: TCWPGetParentPropertyEvent read
FOnGetParentProperty write FOnGetParentProperty;
  public
    // for custom DefineProperties
    property OnWriteMethodProperty: TWriteMethodPropertyEvent read
FOnWriteMethodProperty write FOnWriteMethodProperty;
    property OnWriteStringProperty: TReadWriteStringPropertyEvent read
FOnWriteStringProperty write FOnWriteStringProperty;
    property OnDefineProperties: TCWPDefinePropertiesEvent read
FOnDefineProperties write FOnDefineProperties;
  public
    // code snippets
    property LineEnding: string read FLineEnding write FLineEnding; //
default: system.LineEnding
    property AssignOp: String read FAssignOp write FAssignOp; // default
CSPDefaultAssignOp;
    property SignatureBegin: String read FSignatureBegin write
FSignatureBegin; // default CSPDefaultSignatureBegin
    property SignatureEnd: String read FSignatureEnd write FSignatureEnd; //
default CSPDefaultSignatureEnd
    property AccessClass: string read FAccessClass
      write FAccessClass; // classname used to access protected TComponent
members like SetChildOrder
    property ExecCustomProc: string read FExecCustomProc write
FExecCustomProc; // default CSPDefaultExecCustomProc
    property ExecCustomProcUnit: string read FExecCustomProcUnit write
FExecCustomProcUnit; // default CSPDefaultExecCustomProcUnit
    property MaxColumn: integer read FMaxColumn write FMaxColumn default
CSPDefaultMaxColumn;
  public
    // set automatically when writing
    property NeedAccessClass: boolean read FNeedAccessClass write
FNeedAccessClass; // some property needed AccessClass
    property NeededUnits: TStrings read FNeededUnits write SetNeededUnits;
  end;

procedure WriteComponentToPasStream(AComponent: TComponent; AStream:
TStream);

type
  TCWPDefinePropertiesProc = procedure(Sender: TCompWriterPas;
    Instance: TPersistent; const Identifier: string; var Handled: boolean);

procedure RegisterDefinePropertiesPas(aClass: TPersistentClass;
  const OnDefineProperties: TCWPDefinePropertiesProc);
procedure UnregisterDefinePropertiesPas(
  const OnDefineProperties: TCWPDefinePropertiesProc);
procedure CallDefinePropertiesPas(Writer: TCompWriterPas; Instance:
TPersistent;
  const Identifier: string; var Handled: boolean);

implementation

type
  TDefinePropertiesPas = class
    BaseClass: TPersistentClass;
    Event: TCWPDefinePropertiesProc;
  end;

var
  DefinePropertiesEvents: TObjectList = nil;

procedure WriteComponentToPasStream(AComponent: TComponent; AStream:
TStream);
var
  Writer: TCompWriterPas;
begin
  Writer:=TCompWriterPas.Create(AStream);
  try
    Writer.WriteDescendant(AComponent);
  finally
    Writer.Free;
  end;
end;

function CompareMethods(const m1, m2: TMethod): boolean;
begin
  Result:=(m1.Code=m2.Code) and (m1.Data=m2.Data);
end;

procedure RegisterDefinePropertiesPas(aClass: TPersistentClass;
  const OnDefineProperties: TCWPDefinePropertiesProc);
var
  i, Cnt: Integer;
  E: TDefinePropertiesPas;
begin
  if not Assigned(OnDefineProperties) then
    raise Exception.Create('');
  if not Assigned(aClass) then
    raise Exception.Create('');
  if DefinePropertiesEvents=nil then
    DefinePropertiesEvents:=TObjectList.Create(true);
  Cnt:=DefinePropertiesEvents.Count;
  i:=0;
  while i<Cnt do
  begin
    E:=TDefinePropertiesPas(DefinePropertiesEvents[i]);
    if E.BaseClass.InheritsFrom(aClass) then
      break;
    inc(Cnt);
  end;
  E:=TDefinePropertiesPas.Create;
  E.BaseClass:=aClass;
  E.Event:=OnDefineProperties;
  DefinePropertiesEvents.Insert(i,E);
end;

procedure UnregisterDefinePropertiesPas(
  const OnDefineProperties: TCWPDefinePropertiesProc);
var
  i: Integer;
  E: TDefinePropertiesPas;
begin
  for i:=DefinePropertiesEvents.Count-1 downto 0 do
  begin
    E:=TDefinePropertiesPas(DefinePropertiesEvents[i]);
    if E.Event=OnDefineProperties then
      DefinePropertiesEvents.Delete(i);
  end;
end;

procedure CallDefinePropertiesPas(Writer: TCompWriterPas;
  Instance: TPersistent; const Identifier: string; var Handled: boolean);
var
  i: Integer;
  E: TDefinePropertiesPas;
begin
  if DefinePropertiesEvents=nil then exit;
  for i:=0 to DefinePropertiesEvents.Count-1 do begin
    E:=TDefinePropertiesPas(DefinePropertiesEvents[i]);
    if not Instance.InheritsFrom(E.BaseClass) then
      continue;
    E.Event(Writer,Instance,Identifier,Handled);
    if Handled then exit;
  end;
end;

function IsValidUTF8(p: PChar): integer;
var
  c: Char;
begin
  c:=p^;
  if ord(c)<%10000000 then begin
    // regular single byte ASCII character (#0 is a character, this is
Pascal ;)
    Result:=1;
  end else if ord(c)<=%11000001 then begin
    // single byte character, between valid UTF-8 encodings
    // %11000000 and %11000001 map 2 byte to #0..#128, which is invalid and
used for XSS attacks
    Result:=0;
  end else if ord(c)<=%11011111 then begin
    // could be 2 byte character (%110xxxxx %10xxxxxx)
    if ((ord(p[1]) and %11000000) = %10000000) then
      Result:=2
    else
      Result:=0; // missing following bytes
  end
  else if ord(c)<=%11101111 then begin
    // could be 3 byte character (%1110xxxx %10xxxxxx %10xxxxxx)
    if ((ord(p[1]) and %11000000) = %10000000)
    and ((ord(p[2]) and %11000000) = %10000000) then begin
      if (ord(c)=%11100000) and (ord(p[1])<=%10011111) then
        Result:=0; // XSS attack: 3 bytes are mapped to the 1 or 2 byte
codes
      Result:=3;
    end else
      Result:=0; // missing following bytes
  end
  else if ord(c)<=%11110111 then begin
    // could be 4 byte character (%11110xxx %10xxxxxx %10xxxxxx %10xxxxxx)
    if ((ord(p[1]) and %11000000) = %10000000)
    and ((ord(p[2]) and %11000000) = %10000000)
    and ((ord(p[3]) and %11000000) = %10000000) then begin
      if (ord(c)=%11110000) and (ord(p[1])<=%10001111) then
        Result:=0; // XSS attack: 4 bytes are mapped to the 1-3 byte codes
      Result:=4;
    end else
      Result:=0; // missing following bytes
  end
  else begin
    Result:=0;
  end;
end;

function IsValidUTF16(p: PWideChar): integer;
var
  c: WideChar;
begin
  c:=p^;
  if c<=#$DC7F then
    exit(1)
  else if c<=#$DBFF then begin
    c:=p[1];
    if (c>=#$DC00) and (c<=#$DFFF) then
      exit(2)
    else
      exit(0);
  end else if c<=#$Dfff then begin
    exit(0);
  end else
    exit(1);
end;


type
  TAccessComp = class(TComponent); // to access TComponent protected members

  { TPosComponent }

  TPosComponent = class(TObject)
    FPos: Integer;
    FComponent: TComponent;
    constructor Create(APos: Integer; AComponent: TComponent);
  end;

{ TPosComponent }

constructor TPosComponent.Create(APos: Integer; AComponent: TComponent);
begin
  FPos:=APos;
  FComponent:=AComponent;
end;

{ TCompWriterPas }

procedure TCompWriterPas.AddToAncestorList(Component: TComponent);
begin
 
FAncestors.AddObject(Component.Name,TPosComponent.Create(FAncestors.Count,Component));
end;

procedure TCompWriterPas.DetermineAncestor(Component: TComponent);
var
  i : Integer;
  C: TComponent;
begin
  if Assigned(FAncestors) then
  begin
    i:=FAncestors.IndexOf(Component.Name);
    if i<0 then
    begin
      FAncestor:=nil;
      FAncestorPos:=-1;
    end
    else
      With TPosComponent(FAncestors.Objects[i]) do
      begin
        FAncestor:=FComponent;
        FAncestorPos:=FPos;
      end;
  end;
  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 TCompWriterPas.SetNeededUnits(const AValue: TStrings);
begin
  if FNeededUnits=AValue then Exit;
  FNeededUnits.Assign(AValue);
end;

procedure TCompWriterPas.SetRoot(const AValue: TComponent);
begin
  FRoot:=AValue;
  FLookupRoot:=FRoot;
end;

procedure TCompWriterPas.WriteComponentData(Instance: TComponent);
var
  HasAncestor: Boolean;
  SavedPropPath: String;

  procedure WriteSetParent;
  var
    PropName: String;
  begin
    if Parent=nil then exit;
    if Instance.GetParentComponent=nil then exit;
    if CreatedByAncestor(Instance) then begin
      // ancestor creates the component
      // and descendants cannot change parent
      exit;
    end;
    PropName:='';
    if Assigned(OnGetParentProperty) then
      OnGetParentProperty(Self,Instance,PropName);
    if PropName=CWPSkipParentName then
    else if PropName<>'' then
      WriteAssign(PropertyPath+PropName,GetComponentPath(Parent))
    else begin
      NeedAccessClass:=true;
     
WriteStatement(AccessClass+'(TComponent('+Instance.Name+')).SetParentComponent('+GetComponentPath(Parent)+');');
      WriteAssign('Parent',GetComponentPath(Parent)); // warleyalex
    end;
  end;

begin
  HasAncestor := Assigned(Ancestor) and ((Instance = Root) or
    (Instance.ClassType = Ancestor.ClassType));
  SavedPropPath:=FPropPath;
  try
    if Instance=LookupRoot then begin
      //WriteAssign('Name',''''+Instance.Name+'''');
      //WriteChildren(Instance,cwpcsCreate);

      WriteChildren(Instance,cwpcsCreate);
      WriteStatement('try');
      Indent;
      WriteAssign('Name',''''+Instance.Name+'''');
    end
    else begin
      WriteWithDo(Instance.Name);
      if cwpoNoWithBlocks in Options then
        FPropPath:=GetComponentPath(Instance)+'.';
      if not CreatedByAncestor(Instance) then
        WriteAssign(PropertyPath+'Name',''''+Instance.Name+'''');
      if cwpoSetParentFirst in Options then
        WriteSetParent;
    end;

    if not (cwpoSetParentFirst in Options) then
      WriteSetParent;

    WriteProperties(Instance);

    if not IgnoreChildren then
      WriteChildren(Instance,cwpcsProperties);
    if Instance<>LookupRoot then
      WriteWithEnd;
  finally
    FPropPath:=SavedPropPath;
  end;
  if HasAncestor and (Ancestor<>FRootAncestor)
      and (FCurrentPos<>FAncestorPos) then
  begin
    if (Parent=LookupRoot) and not (cwpoNoSelf in Options) then
     
WriteStatement('SetChildOrder('+GetComponentPath(Instance)+','+IntToStr(FCurrentPos)+');')
    else begin
      NeedAccessClass:=true;
     
WriteStatement(AccessClass+'(TComponent('+GetComponentPath(Parent)+')).SetChildOrder('+GetComponentPath(Instance)+','+IntToStr(FCurrentPos)+');');
    end;
  end;
  Inc(FCurrentPos);
end;

procedure TCompWriterPas.WriteChildren(Component: TComponent;
  Step: TCWPChildrenStep);
var
  SRoot, SRootA, SParent: 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;
  SParent:=Parent;
  try
    FAncestors:=Nil;
    FCurrentPos:=0;
    FAncestorPos:=-1;
    FParent:=Component;
    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);
      TAccessComp(FAncestor).GetChildren(@AddToAncestorList,FRootAncestor);
      FAncestors.Sorted:=True;
    end;
    try
      case Step of
      cwpcsCreate:
        begin
        TAccessComp(Component).GetChildren(@WriteComponentCreate, FRoot);
        { BeginUpdate block }
        for i:=0 to Component.ComponentCount-1 do
          if Component.Components[i].ClassParent.InheritsFrom(TComponent)
and not
            (Component.Components[i].ClassParent.ClassParent = TComponent)
then
            WriteStatement(Component.Components[i].Name+'.BeginUpdate;');
        end;
      cwpcsProperties:
        TAccessComp(Component).GetChildren(@WriteComponent, FRoot);
      end;
    finally
      if Assigned(FAncestor) then
        for i:=0 to FAncestors.Count-1 do
          FAncestors.Objects[i].Free;
      FreeAndNil(FAncestors);
    end;
  finally
    FParent:=SParent;
    FAncestors:=SList;
    FRoot:=SRoot;
    FRootAncestor:=SRootA;
    FCurrentPos:=SPos;
    FAncestorPos:=SAncestorPos;
  end;
end;

procedure TCompWriterPas.WriteProperty(Instance: TPersistent;
  PropInfo: PPropInfo);
type
  TSet = set of 0..31;
var
  PropType, CompType: PTypeInfo;
  ObjValue, AncestorObj: TObject;
  HasAncestor, BoolValue, DefBoolValue: Boolean;
  Int32Value, DefValue: longint;
  PropName, Ident, s, StrValue, DefStrValue, Name, SavedPropPath: String;
  IntToIdentFn: TIntToIdent;
  i, j: Integer;
  Int64Value, DefInt64Value: Int64;
  FloatValue, DefFloatValue: Extended;
  MethodValue, DefMethodValue: TMethod;
  WStrValue, WDefStrValue: WideString;
  UStrValue, UDefStrValue: UnicodeString;
  VarValue, DefVarValue: tvardata;
  aTypeData: PTypeData;
  Component, AncestorComponent: TComponent;
  SavedAncestor: TPersistent;
  IntfValue, AncestorIntf: IInterface;
  CompRef: IInterfaceComponentReference;
begin
  // do not stream properties without getter
  if not Assigned(PropInfo^.GetProc) then
    exit;

  // properties without setter are only allowed, if they are csSubComponent
  PropType := PropInfo^.PropType;
  if not Assigned(PropInfo^.SetProc) then begin
    if PropType^.Kind<>tkClass then
      exit;
    ObjValue := TObject(GetObjectProp(Instance, PropInfo));
    if not (ObjValue is TComponent) or
       not (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));
  PropName:=FPropPath + PropInfo^.Name;
  {$IFDEF VerboseCompWriterPas}
  debugln(['TWriter.WriteProperty PropName="',PropName,'"
TypeName=',PropType^.Name,'
Kind=',GetEnumName(TypeInfo(TTypeKind),ord(PropType^.Kind)),'
HasAncestor=',HasAncestor]);
  {$ENDIF}

  case PropType^.Kind of
    tkInteger, tkChar, tkEnumeration, tkSet, tkWChar:
      begin
        Int32Value := GetOrdProp(Instance, PropInfo);
        if HasAncestor then
          DefValue := GetOrdProp(Ancestor, PropInfo)
        else
          DefValue := PPropInfo(PropInfo)^.Default;
        //debugln([PropInfo^.Name,', HasAncestor=',HasAncestor,',
Value=',Int32Value,', Default=',DefValue]);
        if (Int32Value <> DefValue) or (DefValue=longint($80000000)) then
        begin
          case PropType^.Kind of
            tkInteger:
              begin
                // Check if this integer has a string identifier
                IntToIdentFn := FindIntToIdent(PropInfo^.PropType);
                Ident:='';
                if Assigned(IntToIdentFn) and IntToIdentFn(Int32Value,
Ident) then
                  // Integer with a custom identifier
                  // ToDo: check if this is an actual Pascal constant and
remember the unit
                  WriteAssign(PropName,Ident)
                else begin
                  // Integer has to be written just as number
                  case PropType^.Name of
                  'ByteBool':
WriteAssign(PropName,GetBoolLiteral(ByteBool(Int32Value)));
                  'WordBool':
WriteAssign(PropName,GetBoolLiteral(WordBool(Int32Value)));
                  'LongBool':
WriteAssign(PropName,GetBoolLiteral(LongBool(Int32Value)));
                  else
                    aTypeData:=GetTypeData(PropInfo^.PropType);
                    if aTypeData^.MinValue>=0 then
                      WriteAssign(PropName,IntToStr(longword(Int32Value)))
                    else
                      WriteAssign(PropName,IntToStr(Int32Value));
                  end;
                end;
              end;
            tkChar:
              WriteAssign(PropName,GetCharLiteral(Int32Value));
            tkWChar:
              WriteAssign(PropName,GetWideCharLiteral(Int32Value));
            tkSet:
              begin
              s:='';
              CompType:=GetTypeData(PropType)^.CompType;
              i:=0;
              while i<32 do
              begin
                if i in TSet(Int32Value) then
                begin
                  if s<>'' then s:=s+',';
                  // ToDo: store needed unit
                  s:=s+GetEnumExpr(CompType, i,false);
                  j:=i;
                  while (i<31) and (byte(i+1) in TSet(Int32Value)) do
                    inc(i);
                  if i>j then
                    s:=s+'..'+GetEnumExpr(CompType, i,false);
                end;
                inc(i);
              end;
              WriteAssign(PropName,'['+s+']');
              end;
            tkEnumeration:
              // ToDo: store needed unit
              WriteAssign(PropName,GetEnumExpr(PropType, Int32Value,true));
          end;
        end;
      end;
    tkFloat:
      begin
        FloatValue := GetFloatProp(Instance, PropInfo);
        if HasAncestor then
          DefFloatValue := GetFloatProp(Ancestor, PropInfo)
        else
          begin
          DefValue :=PropInfo^.Default;
          DefFloatValue:=PSingle(@PropInfo^.Default)^;
          end;
        if (FloatValue<>DefFloatValue) or (DefValue=longint($80000000)) then
          WriteAssign(PropName,GetFloatLiteral(FloatValue));
      end;
    tkMethod:
      begin
        MethodValue := GetMethodProp(Instance, PropInfo);
        if HasAncestor then
          DefMethodValue := GetMethodProp(Ancestor, PropInfo)
        else begin
          DefMethodValue.Data := nil;
          DefMethodValue.Code := nil;
        end;

        //debugln(['TCompWriterPas.WriteProperty ',dbgs(MethodValue.Data),'
',dbgs(MethodValue.Code),' ',dbgs(DefMethodValue.Data),'
',dbgs(DefMethodValue.Code)]);
        if Assigned(OnGetMethodName) then
        begin
          if (MethodValue.Code <> DefMethodValue.Code) or
            (MethodValue.Data <> DefMethodValue.Data) then
          begin
            OnGetMethodName(Self,Instance,PropInfo,Ident);
            s:='';
            if HasAncestor then
              OnGetMethodName(Self,Ancestor,PropInfo,s);
            if Ident<>s then
            begin
              if Ident='' then
                WriteAssign(PropName,'nil')
              else
                // ToDo: check nameclash of Ident with current with-do block
                WriteAssign(PropName,'@'+Ident);
            end;
          end;
        end else begin
          if (MethodValue.Code <> DefMethodValue.Code) then
          begin
            if not Assigned(MethodValue.Code) then
              Ident:=''
            else
              Ident:=FLookupRoot.MethodName(MethodValue.Code);
            if Ident='' then
              WriteAssign(PropName,'nil')
            else
              // ToDo: check nameclash of Ident with current with-do block
              WriteAssign(PropName,'@'+Ident);
          end;
        end;
      end;
    tkSString, tkLString, tkAString:
      begin
        StrValue := GetStrProp(Instance, PropInfo);
        if HasAncestor then
          DefStrValue := GetStrProp(Ancestor, PropInfo)
        else
          SetLength(DefStrValue, 0);

        if StrValue <> DefStrValue then
          WriteAssign(PropName,GetStringLiteral(StrValue));
      end;
    tkWString:
      begin
        WStrValue := GetWideStrProp(Instance, PropInfo);
        if HasAncestor then
          WDefStrValue := GetWideStrProp(Ancestor, PropInfo)
        else
          WDefStrValue := '';

        if WStrValue <> WDefStrValue then
         
WriteAssign(PropName,GetWStringLiteral(PWideChar(WStrValue),length(WStrValue)));
      end;
    tkUString:
      begin
        UStrValue := GetUnicodeStrProp(Instance, PropInfo);
        if HasAncestor then
          UDefStrValue := GetUnicodeStrProp(Ancestor, PropInfo)
        else
          SetLength(UDefStrValue, 0);

        if UStrValue <> UDefStrValue then
         
WriteAssign(PropName,GetWStringLiteral(PWideChar(UStrValue),length(UStrValue)));
      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
            // can't use variant() typecast, pulls in variants unit
            case VarValue.vtype of
            varsmallint :
WriteAssign(PropName,'SmallInt('+IntToStr(VarValue.vsmallint)+')');
            varinteger :
WriteAssign(PropName,'LongInt('+IntToStr(VarValue.vinteger)+')');
            varsingle :
WriteAssign(PropName,'Single('+GetFloatLiteral(VarValue.vsingle)+')');
            vardouble :
WriteAssign(PropName,'Double('+GetFloatLiteral(VarValue.vdouble)+')');
            vardate :
WriteAssign(PropName,'TDateTime('+GetFloatLiteral(VarValue.vdate)+')');
            varcurrency :
WriteAssign(PropName,'Currency('+GetCurrencyLiteral(VarValue.vcurrency)+')');
            //varolestr : (volestr : pwidechar);
            //vardispatch : (vdispatch : pointer);
            //varerror : (verror : hresult);
            varboolean :
WriteAssign(PropName,GetBoolLiteral(VarValue.vboolean));
            //varunknown : (vunknown : pointer);
            // vardecimal : ( : );
            varshortint :
WriteAssign(PropName,'ShortInt('+IntToStr(VarValue.vshortint)+')');
            varbyte :
WriteAssign(PropName,'Byte('+IntToStr(VarValue.vbyte)+')');
            varword :
WriteAssign(PropName,'Word('+IntToStr(VarValue.vword)+')');
            varlongword :
WriteAssign(PropName,'LongWord('+IntToStr(VarValue.vlongword)+')');
            varint64 :
WriteAssign(PropName,'Int64('+IntToStr(VarValue.vint64)+')');
            varqword :
WriteAssign(PropName,'QWord('+IntToStr(VarValue.vqword)+')');
            // duplicate: varword64
            varstring :
WriteAssign(PropName,GetStringLiteral(AnsiString(VarValue.vstring)));
            //varany :  (vany : pointer);
            //vararray : (varray : pvararray);
            //varbyref : (vpointer : pointer);
            //varrecord : (vrecord : pointer;precinfo : pointer);
            else
              {$IFDEF VerboseCompWriterPas}
              debugln(['TCompWriterPas.WriteProperty Property="',PropName,'"
Kind=',PropType^.Kind,' vtype=',VarValue.vtype]);
              raise EWriteError.Create('proptype not supported:
'+GetEnumName(TypeInfo(PropType^.Kind),ord(PropType^.Kind))+'
vtype='+dbgs(VarValue.vtype));
              {$ENDIF}
            end;
            //ToDo WriteVariant(pvariant(@VarValue)^);
          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
            //debugln(['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
              
SameText(TComponent(AncestorObj).Name,TComponent(ObjValue).Name) then
            begin
              // value is a component, and it is the same as in the ancestor
              // Note: a descendant has new instances with same names
              AncestorObj := ObjValue;
            end;
          end;
        end else
          AncestorObj := nil;

        if not Assigned(ObjValue) then
        begin
          if ObjValue <> AncestorObj then
            WriteAssign(PropName,'Nil');
        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
              // set property value
              Name:=GetComponentPath(Component);
              if Name='' then
                raise EWriteError.Create('cannot write property
"'+DbgSName(Instance)+'.'+PropName+'"');
              WriteAssign(PropName,Name);
            end; //(ObjValue <> AncestorObj)
          end // ObjValue.InheritsFrom(TComponent)
          else
          begin
            // keep property value, set sub properties recursively with full
path
            // e.g. Font.Size:=5;
            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
                // create collection items
                SavedPropPath := FPropPath;
                try
                  if cwpoNoWithBlocks in Options then
                    FPropPath:=PropName+'.'
                  else
                    FPropPath:='';
                  WriteCollection(PropName,TCollection(ObjValue));
                finally
                  FPropPath := SavedPropPath;
                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 := 0;
        if Int64Value <> DefInt64Value then
          if PropType^.Kind=tkInt64 then
            WriteAssign(PropName,IntToStr(Int64Value))
          else
            WriteAssign(PropName,IntToStr(QWord(Int64Value)));
      end;
    tkBool:
      begin
        BoolValue := GetOrdProp(Instance, PropInfo)<>0;
        if HasAncestor then
          DefBoolValue := GetOrdProp(Ancestor, PropInfo)<>0
        else
          DefBoolValue := PropInfo^.Default<>0;
        DefValue:=PropInfo^.Default;
        //debugln([PropInfo^.Name,', HasAncestor=',HasAncestor,',
BoolValue=',BoolValue,', DefBoolValue=',DefBoolValue,' Default=',DefValue]);
        if (BoolValue<>DefBoolValue) or (DefValue=longint($80000000)) then
          WriteAssign(PropName,GetBoolLiteral(BoolValue));
      end;
    tkInterface:
      begin
        IntfValue := GetInterfaceProp(Instance, PropInfo);
        if not Assigned(IntfValue) then
          WriteAssign(PropName,'Nil')
        else if Supports(IntfValue, IInterfaceComponentReference, CompRef)
then
        begin
          Component := CompRef.GetComponent;
          AncestorComponent := nil;
          if HasAncestor then
          begin
            AncestorIntf := GetInterfaceProp(Instance, PropInfo);
            if Supports(AncestorIntf, IInterfaceComponentReference, CompRef)
then
            begin
              AncestorComponent := CompRef.GetComponent;
              if (AncestorComponent<>Component) and
                 (AncestorComponent.Owner = FRootAncestor) and
                 (Component.Owner = Root) and
                 SameText(AncestorComponent.Name,Component.Name) then
              begin
                // value is a component, and it is the same as in the
ancestor
                // Note: a descendant has new instances with same names
                AncestorComponent := Component;
              end;
            end;
          end;

          if Component<>AncestorComponent then
          begin
            Name:=GetComponentPath(Component);
            if Name='' then
              raise EWriteError.Create('cannot write property
"'+DbgSName(Instance)+'.'+PropName+'"');
            WriteAssign(PropName,Name);
          end;
        end else
          raise EWriteError.Create('interface property "'+PropName+'" does
not support IInterfaceComponentReference');
      end;
  else
    {$IFDEF VerboseCompWriterPas}
    debugln(['TCompWriterPas.WriteProperty Property="',PropName,'"
Kind=',PropType^.Kind]);
    raise EWriteError.Create('proptype not supported:
'+GetEnumName(TypeInfo(PropType^.Kind),ord(PropType^.Kind)));
    {$ENDIF}
  end;
end;

procedure TCompWriterPas.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;
  WriteDefineProperties(Instance);
end;

procedure TCompWriterPas.WriteDefineProperties(Instance: TPersistent);
var
  Col: Integer;
  InLit, NeedComma: boolean;
  InstancePath: String;

  function CheckCol(aCol: integer): boolean;
  begin
    if (Col<=CurIndent+1) or (aCol<=MaxColumn) then exit(true);
    Result:=false;
    if NeedComma then
      Write(',');
    WriteLn;
    WriteIndent;
    Col:=CurIndent+1;
    NeedComma:=false;
  end;

  function GetPath: string;
  begin
    if InstancePath='' then
    begin
      if PropertyPath<>'' then
      begin
        InstancePath:=PropertyPath;
        Delete(InstancePath,length(InstancePath),1); // chomp '.'
      end
      else if Instance is TComponent then
        InstancePath:=GetComponentPath(TComponent(Instance))
      else
        InstancePath:='';
      if InstancePath='' then
        raise EWriteError.Create('cannot write DefineProperties of
"'+DbgSName(Instance)+'"');
    end;
    Result:=InstancePath;
  end;

var
  HasAncestor, Handled: Boolean;
  DefValue, Value: LongInt;
  aStream: TMemoryStream;
  BinWriter: TWriter;
  s: String;
  p: PChar;
  c: Char;
  i: Integer;
begin
  InstancePath:='';

  Handled:=false;
  if Assigned(OnDefineProperties) then
  begin
    s:=GetPath;
    OnDefineProperties(Self,Instance,s,Handled);
    if Handled then exit;
  end;
  if DefinePropertiesEvents<>nil then
  begin
    s:=GetPath;
    CallDefinePropertiesPas(Self,Instance,s,Handled);
    if Handled then exit;
  end;

  if Instance is TComponent then
  begin
    HasAncestor := Assigned(Ancestor) and ((Instance = Root) or
      (Instance.ClassType = Ancestor.ClassType));
    if HasAncestor then
      DefValue := TComponent(Ancestor).DesignInfo
    else
      DefValue := 0;
    Value:=TComponent(Instance).DesignInfo;
    if Value<>DefValue then
    begin
      // Note: DesignInfo contains Left/Top. On BigEndian systems the order
      // is reversed, which is already handled in
TComponent.DefineProperties
      // -> it is the same longint value on Little and BigEndian system
      s:=GetPath;
      if s<>'' then
      begin
        if SameText(s,'Self') then
          s:=''
        else
          s:=s+'.';
      end;
      WriteAssign(s + 'DesignInfo',IntToStr(Value));
    end;
  end;

  if
TMethod(@TAccessComp(Instance).DefineProperties).Code<>FDefaultDefineProperties
  then begin
    // this class has overriden DefineProperties
    aStream:=TMemoryStream.Create;
    BinWriter:=TWriter.Create(aStream,1024);
    try
      BinWriter.Root:=Root;
      BinWriter.RootAncestor:=RootAncestor;
      BinWriter.Ancestor:=Ancestor;
      BinWriter.IgnoreChildren:=IgnoreChildren;
      BinWriter.OnWriteMethodProperty:=OnWriteMethodProperty;
      BinWriter.OnWriteStringProperty:=OnWriteStringProperty;
      TAccessComp(Instance).DefineProperties(BinWriter);
      BinWriter.WriteListEnd;
      FreeAndNil(BinWriter); // flush buffer to stream
      if aStream.Size>1 then
      begin
        WriteIndent;
        s:=GetPath;
        s:='Picture.LoadFromFile(URL);';
        Write(s);
        WriteLn;
        Unindent;
        Unindent;
      end;
    finally
      BinWriter.Free;
      aStream.Free;
    end;

    // this class has overriden DefineProperties
    (*
    aStream:=TMemoryStream.Create;
    BinWriter:=TWriter.Create(aStream,1024);
    try
      BinWriter.Root:=Root;
      BinWriter.RootAncestor:=RootAncestor;
      BinWriter.Ancestor:=Ancestor;
      BinWriter.IgnoreChildren:=IgnoreChildren;
      BinWriter.OnWriteMethodProperty:=OnWriteMethodProperty;
      BinWriter.OnWriteStringProperty:=OnWriteStringProperty;
      TAccessComp(Instance).DefineProperties(BinWriter);
      BinWriter.WriteListEnd;
      FreeAndNil(BinWriter); // flush buffer to stream
      if aStream.Size>1 then
      begin
        WriteIndent;
        s:=GetPath;
        s:=ExecCustomProc+'('+s+',[';
        Write(s);
        AddNeededUnit(ExecCustomProcUnit);
        Col:=CurIndent+length(s)+1;
        Indent;
        NeedComma:=false;
        CheckCol(Col);
        InLit:=false;
        p:=PChar(aStream.Memory);
        for i:=0 to aStream.Size-1 do
        begin
          c:=p^;
          if c in [#32..#126] then
          begin
            if (not InLit) or (Col+2>MaxColumn) then
            begin
              if InLit then
                Write('''');
              CheckCol(Col+3);
              InLit:=true;
              Write('''');
              inc(Col);
            end;
            Write(c);
            inc(Col);
            NeedComma:=true;
          end else begin
            if InLit then
            begin
              Write('''');
              inc(Col);
              InLit:=false;
            end;
            s:='#'+IntToStr(ord(c));
            CheckCol(Col+length(s));
            Write(s);
            inc(Col,length(s));
            NeedComma:=true;
          end;
          inc(p);
        end;
        if InLit then
          Write('''');
        Write(']);');
        WriteLn;
        Unindent;
      end;
    finally
      BinWriter.Free;
      aStream.Free;
    end;
    *)
  end;
end;

procedure TCompWriterPas.WriteCollection(PropName: string;
  Collection: TCollection);
var
  i: Integer;
  Item: TCollectionItem;
begin
  WriteStatement(PropName+'.Clear;');
  for i:=0 to Collection.Count-1 do
  begin
    Item:=Collection.Items[i];
    WriteWithDo(Item.ClassName+'('+PropName+'.Add)');
    WriteProperties(Item);
    WriteWithEnd;
  end;
end;

function TCompWriterPas.GetComponentPath(Component: TComponent): string;
var
  Name: String;
  C: TComponent;
begin
  if Component=nil then
    Result:='Nil'
  else if Component=LookupRoot then
  begin
    if cwpoNoSelf in Options then
      Result:=LookupRoot.Name
    else
      Result:='Self';
  end
  else begin
    Name:= '';
    C:=Component;
    While (C<>Nil) do
    begin
      if (Name<>'') Then
        Name:='.'+Name;
      if C.Owner = LookupRoot then
      begin
        Name := C.Name+Name;
        if (cwpoNoWithBlocks in Options) then
        begin
          if cwpoNoSelf in Options then
            Name := C.Owner.Name+'.'+Name;
        end;
        break;
      end
      else if C = LookupRoot then
      begin
        if cwpoNoSelf in Options then
          Name := C.Name+Name
        else
          Name := 'Self'+Name;
        break;
      end else if C.Name='' then
        exit('');
      Name:=C.Name+Name;
      // ToDo: store used unit
      C:=C.Owner;
    end;
    Result:=Name;
  end;
end;

function TCompWriterPas.GetBoolLiteral(b: boolean): string;
begin
  if b then
    Result:='True'
  else
    Result:='False';
end;

function TCompWriterPas.GetCharLiteral(c: integer): string;
begin
  case c of
  32..126: Result:=''''+chr(c)+'''';
  else     Result:='#'+IntToStr(c);
  end;
end;

function TCompWriterPas.GetWideCharLiteral(c: integer): string;
begin
  case c of
  32..126:
    Result:=''''+Chr(c)+'''';
  0..31,127..255,$D800..$DFFF:
    Result:='#'+IntToStr(c);
  else
    if cwpoSrcCodepageUTF8 in Options then
      Result:=''''+UTF16ToUTF8(WideChar(c))+''''
    else
      Result:='#'+IntToStr(c);
  end;
end;

function TCompWriterPas.GetStringLiteral(const s: string): string;

  function IsSpecialChar(p: PChar): boolean;
  const
    SpecialChars = [#0..#31,#127,#255];
  begin
    Result:=(p^ in SpecialChars) or (IsValidUTF8(p)=0);
  end;

var
  InLit: Boolean;
  p, StartP: PChar;
  c: Char;
begin
  Result:='';
  if s='' then exit;
  InLit:=false;
  p:=PChar(s);
  repeat
    c:=p^;
    if (c=#0) and (p-PChar(s)=length(s)) then
      break
    else if IsSpecialChar(p) then
    begin
      if InLit then begin
        InLit:=false;
        Result:=Result+'''';
      end;
      Result:=Result+'#'+IntToStr(ord(c));
      inc(p);
    end else begin
      if not InLit then begin
        InLit:=true;
        Result:=Result+'''';
      end;
      if c='''' then begin
        Result:=Result+'''''';
        inc(p);
      end else begin
        StartP:=p;
        repeat
          inc(p,IsValidUTF8(p));
          c:=p^;
        until ((c=#0) and (p-PChar(s)=length(s))) or IsSpecialChar(p) or
(c='''');
        Result:=Result+copy(s,StartP-PChar(s)+1,p-StartP);
      end;
    end;
  until false;
  if InLit then
    Result:=Result+'''';
end;

function TCompWriterPas.GetWStringLiteral(p: PWideChar; Count: integer):
string;

  function IsSpecialChar(w: PWideChar): boolean;
  const
    SpecialChars = [#0..#31,#127];
  begin
    if w^ in SpecialChars then exit(true);
    if cwpoSrcCodepageUTF8 in FOptions then begin
      Result:=IsValidUTF16(w)=0;
    end else begin
      Result:=w^>=#$7f;
    end;
  end;

var
  InLit: Boolean;
  c: WideChar;
  FirstP, StartP: PWideChar;
  AddLen: SizeUInt;
  s: string;
  OldLen: Integer;
begin
  Result:='';
  if Count=0 then exit;
  FirstP:=p;
  InLit:=false;
  s:='';
  repeat
    c:=p^;
    if (c=#0) and (p-FirstP=Count) then
      break
    else if IsSpecialChar(p) then
    begin
      if InLit then begin
        InLit:=false;
        Result:=Result+'''';
      end;
      Result:=Result+'#'+Format('%.4d',[ord(c)]);
      inc(p);
    end else begin
      if not InLit then begin
        InLit:=true;
        Result:=Result+'''';
      end;
      if c='''' then begin
        Result:=Result+'''''';
        inc(p);
      end else begin
        StartP:=p;
        repeat
          inc(p,IsValidUTF16(p));
          c:=p^;
        until ((c=#0) and (p-FirstP=Count)) or IsSpecialChar(p) or (c='''');
        AddLen:=p-StartP;
        if length(s)<AddLen*3 then SetLength(s,AddLen*3);
        if ConvertUTF16ToUTF8(@s[1],length(s),StartP,AddLen,
            [toInvalidCharError,toUnfinishedCharError],AddLen)=trNoError
then
          dec(AddLen); // omit #0
        OldLen:=length(Result);
        SetLength(Result,OldLen+AddLen);
        System.Move(s[1],Result[OldLen+1],AddLen);
      end;
    end;
  until false;
  if InLit then
    Result:=Result+'''';
end;

function TCompWriterPas.GetFloatLiteral(const e: Extended): string;
var
  s: String;
begin
  s:='';
  str(e,s);
  Result:=ShortenFloat(s);
end;

function TCompWriterPas.GetCurrencyLiteral(const c: currency): string;
var
  i: int64 absolute c;
var
  s: String;
begin
  if i mod 10000=0 then
    s:=IntToStr(i div 10000)
  else begin
    s:=IntToStr(i);
    while length(s)<4 do
      s:='0'+s;
    if length(s)=4 then
      s:='0.'+s
    else
      system.insert('.',s,length(s)-3);
  end;
  Result:=s;
end;

function TCompWriterPas.ShortenFloat(s: string): string;
var
  p, i: SizeInt;
begin
  // remove unneeded leading 0 of exponent
  p:=Pos('E',s);
  if p<1 then exit;
  i:=p;
  if s[i+1]='+' then inc(i);
  while (i<length(s)) and (s[i+1]='0') do
    inc(i);
  if i>p then
    if i=length(s) then
      Delete(s,p,i-p+1) // delete whole exponent
    else
      Delete(s,p+1,i-p);
  // remove trailing 0 of base
  i:=p;
  while (i>2) and (s[i-1]='0') do
    dec(i);
  if not (s[i-1] in ['0'..'9']) then inc(i);
  if i<p then
    Delete(s,i,p-i);
  // remove leading space
  if s[1]=' ' then
    Delete(s,1,1);
  Result:=s;
end;

function TCompWriterPas.GetEnumExpr(TypeInfo: PTypeInfo; Value: integer;
  AllowOutOfRange: boolean): string;
var
  PT: PTypeData;
begin
  PT:=GetTypeData(TypeInfo);
  if (Value>=PT^.MinValue) and (Value<=PT^.MaxValue) then
    case TypeInfo^.Kind of
    tkBool: Result:=GetBoolLiteral(Value=ord(true));
    tkChar: Result:=GetCharLiteral(Value);
    tkEnumeration: Result:=GetEnumName(TypeInfo,Value);
    else Result:=IntToStr(Value);
    end
  else if AllowOutOfRange then
    Result:=TypeInfo^.Name+'('+IntToStr(Value)+')'
  else
    raise EWriteError.Create('enum '+IntToStr(Value)+' is out of range of
type "'+TypeInfo^.Name+'"');
end;

function TCompWriterPas.GetVersionStatement: string;
begin
  Result:='// Format version '+IntToStr(CSPVersion);
end;

constructor TCompWriterPas.Create(AStream: TStream);
var
  C: TAccessComp;
begin
  FIndentStep:=2;
  FStream:=AStream;
  FLineEnding:=system.LineEnding;
  FAssignOp:=CSPDefaultAssignOp;
  FSignatureBegin:=CSPDefaultSignatureBegin;
  FSignatureEnd:=CSPDefaultSignatureEnd;
  FMaxColumn:=CSPDefaultMaxColumn;
  FExecCustomProc:=CSPDefaultExecCustomProc;
  FExecCustomProcUnit:=CSPDefaultExecCustomProcUnit;
  FNeededUnits:=TStringList.Create;
  FAccessClass:=CSPDefaultAccessClass;
  C:=TAccessComp.Create(nil);
  FDefaultDefineProperties:=TMethod(@C.DefineProperties).Code;
  C.Free;
end;

destructor TCompWriterPas.Destroy;
begin
  FreeAndNil(FNeededUnits);
  inherited Destroy;
end;

procedure TCompWriterPas.WriteComponentCreate(Component: TComponent);
var
  OldAncestor: TPersistent;
  OldRoot, OldRootAncestor: TComponent;
  HasAncestor: boolean;
begin
  if (Component=LookupRoot) then exit;
  OldRoot:=FRoot;
  OldAncestor:=FAncestor;
  OldRootAncestor:=FRootAncestor;
  Try
    DetermineAncestor(Component);
    HasAncestor:=FAncestor is TComponent;
    if not CreatedByAncestor(Component) then
     
WriteAssign(Component.Name,Component.ClassName+'.Create('+GetComponentPath(Root)+')');
    if HasAncestor then begin
      if (csInline in Component.ComponentState)
      and not (csInline in TComponent(Ancestor).ComponentState) then
      begin
        NeedAccessClass:=true;
       
WriteStatement(AccessClass+'(TComponent('+Component.Name+')).SetInline('+GetBoolLiteral(true)+');');
      end;
      if (csAncestor in Component.ComponentState)
      and not (csAncestor in TComponent(Ancestor).ComponentState) then
      begin
        NeedAccessClass:=true;
       
WriteStatement(AccessClass+'(TComponent('+Component.Name+')).SetAncestor('+GetBoolLiteral(true)+');');
      end;
    end;
    if not IgnoreChildren then
      WriteChildren(Component,cwpcsCreate);
   finally
    FAncestor:=OldAncestor;
    FRoot:=OldRoot;
    FRootAncestor:=OldRootAncestor;
  end;
end;

procedure TCompWriterPas.WriteComponent(Component: TComponent);
var
  OldAncestor : TPersistent;
  OldRoot, OldRootAncestor : TComponent;
  i: integer;
begin
  OldRoot:=FRoot;
  OldAncestor:=FAncestor;
  OldRootAncestor:=FRootAncestor;
  Try
    // Component.ComponentState:=Component.FComponentState+[csWriting];
    DetermineAncestor(Component);
    WriteComponentData(Component);
  finally
    FAncestor:=OldAncestor;
    FRoot:=OldRoot;
    FRootAncestor:=OldRootAncestor;
  end;
end;

procedure TCompWriterPas.WriteDescendant(ARoot: TComponent; AAncestor:
TComponent);
var
  i: integer;
begin
  FRoot := ARoot;
  FAncestor := AAncestor;
  FRootAncestor := AAncestor;
  FLookupRoot := ARoot;
  FNeedAccessClass := false;
  if not (cwpoNoSignature in Options) then
    WriteStatement(SignatureBegin);
  WriteStatement(GetVersionStatement);
  if cwpoNoSelf in Options then
    WriteWithDo(ARoot.Name);
  WriteComponent(ARoot);
  if cwpoNoSelf in Options then
    WriteWithEnd;
  if not (cwpoNoSignature in Options) then
    WriteStatement(SignatureEnd);

  UnIndent;
  WriteStatement('finally');
  { EndUpdate block }
    Indent;
    for i:=0 to ARoot.ComponentCount-1 do
      if ARoot.Components[i].ClassParent.InheritsFrom(TComponent) and not
        (ARoot.Components[i].ClassParent.ClassParent = TComponent) then
        WriteStatement(ARoot.Components[i].Name+'.EndUpdate;');
    UnIndent;

  WriteStatement('end;');
end;

procedure TCompWriterPas.WriteIndent;
begin
  Write(StringOfChar(' ',CurIndent));
end;

procedure TCompWriterPas.Write(const s: string);
begin
  if s='' then exit;
  FStream.Write(s[1],length(s));
end;

procedure TCompWriterPas.WriteLn;
begin
  Write(LineEnding);
end;

procedure TCompWriterPas.WriteStatement(const s: string);
begin
  WriteIndent;
  Write(s);
  WriteLn;
end;

procedure TCompWriterPas.WriteAssign(const LHS, RHS: string);
begin
  WriteIndent;
  Write(LHS);
  Write(AssignOp);
  Write(RHS);
  Write(';');
  WriteLn;
end;

procedure TCompWriterPas.WriteWithDo(const Expr: string);
begin
  if not (cwpoNoWithBlocks in Options) then
    WriteStatement('with '+Expr+' do begin');
  Indent;
end;

procedure TCompWriterPas.WriteWithEnd;
begin
  Unindent;
  if not (cwpoNoWithBlocks in Options) then
    WriteStatement('end;');
end;

function TCompWriterPas.CreatedByAncestor(Component: TComponent): boolean;
begin
  Result:=(FAncestor is TComponent)
    and (TComponent(FAncestor).Owner = FRootAncestor)
    and (Component.Owner = Root)
    and SameText(Component.Name,TComponent(FAncestor).Name)
end;

procedure TCompWriterPas.AddNeededUnit(const AnUnitName: string);
begin
  if FNeededUnits.IndexOf(AnUnitName)>=0 then exit;
  FNeededUnits.Add(AnUnitName);
end;

procedure TCompWriterPas.Indent;
begin
  CurIndent:=CurIndent+IndentStep;
end;

procedure TCompWriterPas.Unindent;
begin
  CurIndent:=CurIndent-IndentStep;
end;

finalization
  DefinePropertiesEvents.Free;

end.
---------------------



--
Sent from: http://pas2js.38893.n8.nabble.com/
_______________________________________________
Pas2js maillist  -  Pas2js at lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/pas2js


More information about the Pas2js mailing list