codetools: test write DefineProperties

git-svn-id: trunk@56205 -
This commit is contained in:
mattias 2017-10-26 13:41:53 +00:00
parent f58106b6e4
commit b98ec3d5df

View File

@ -24,21 +24,24 @@ Working:
later later
- inline component, csInline, call SetInline, inherited inline, inline on inherited - inline component, csInline, call SetInline, inherited inline, inline on inherited
- TComponent.Left/Right via DesignInfo - TComponent.Left/Right via DesignInfo
- DefineProperties
ToDo: ToDo:
- OnWriteMethodProperty
- OnWriteStringProperty
- RegisterPascalProperties(aClass,@);
- enum: add unit, avoid nameclash with-do - enum: add unit, avoid nameclash with-do
- custom integer TColor, add unit, avoid nameclash with-do - custom integer TColor, add unit, avoid nameclash with-do
- method, avoid nameclash with-do - method, avoid nameclash with-do
- DefineProperties
- insert/update code and helper class into unit/program - insert/update code and helper class into unit/program
- find old access class - find call init proc
- find constructor
- find old init code - find old init code
- add new access class - error if init proc is behind call
- error if access class is behind constructor - add call in existing constructor
- add constructor - add constructor with call
- add new init code - add new init code
- replace init code - replace init code
- add missing units
} }
unit TestCompReaderWriterPas; unit TestCompReaderWriterPas;
@ -49,15 +52,18 @@ unit TestCompReaderWriterPas;
interface interface
uses uses
Classes, SysUtils, typinfo, RtlConsts, LazLoggerBase, LazUTF8, fpcunit, Classes, SysUtils, typinfo, RtlConsts, LazLoggerBase, LazUTF8, LazLogger,
testregistry, CodeToolManager, LinkScanner, CodeToolsStructs, CodeCache, fpcunit, testregistry, CodeToolManager, LinkScanner, CodeToolsStructs,
BasicCodeTools, TestStdCodetools, TestGlobals, variants; CodeCache, BasicCodeTools, TestStdCodetools, TestGlobals, variants;
const const
CSPDefaultSignature = '// Pascal stream V1.0'; // Component Stream as Pascal
CSPDefaultSignatureBegin = CSPDefaultSignature+' - DO NOT EDIT! - Begin'; CSPDefaultSignature = '// Component Stream as Pascal';
CSPDefaultSignatureBegin = CSPDefaultSignature+' - Begin';
CSPDefaultSignatureEnd = CSPDefaultSignature+' - End'; CSPDefaultSignatureEnd = CSPDefaultSignature+' - End';
CSPDefaultAccessClass = 'TPasStreamAccess'; CSPDefaultAccessClass = 'TPasStreamAccess';
CSPDefaultExecCustomLFM = 'ExecCustomLFM';
CSPDefaultMaxColumn = 80;
CWPSkipParentName = '-'; CWPSkipParentName = '-';
type type
TCWPFindAncestorEvent = procedure(Sender: TObject; Component: TComponent; TCWPFindAncestorEvent = procedure(Sender: TObject; Component: TComponent;
@ -83,28 +89,33 @@ type
TCompWriterPas = class TCompWriterPas = class
private private
FAccessClass: string;
FAncestor: TPersistent;
FAncestorPos: Integer;
FAncestors: TStringList;
FAssignOp: String; FAssignOp: String;
FCurIndent: integer; FCurIndent: integer;
FCurrentPos: Integer;
FDefaultDefineProperties: CodePointer;
FExecCustomData: string;
FIgnoreChildren: Boolean; FIgnoreChildren: Boolean;
FIndentStep: integer; FIndentStep: integer;
FLineEnding: string; FLineEnding: string;
FLookupRoot: TComponent;
FMaxColumn: integer;
FNeedAccessClass: boolean; FNeedAccessClass: boolean;
FOnFindAncestor: TCWPFindAncestorEvent;
FOnGetMethodName: TCWPGetMethodName; FOnGetMethodName: TCWPGetMethodName;
FOnGetParentProperty: TCWPGetParentProperty; FOnGetParentProperty: TCWPGetParentProperty;
FOnWriteMethodProperty: TWriteMethodPropertyEvent;
FOnWriteStringProperty: TReadWriteStringPropertyEvent;
FOptions: TCWPOptions; FOptions: TCWPOptions;
FParent: TComponent; FParent: TComponent;
FPropPath: string; FPropPath: string;
FAccessClass: string; FRoot: TComponent;
FRootAncestor: TComponent;
FSignature: String; FSignature: String;
FStream: TStream; FStream: TStream;
FRoot: TComponent;
FLookupRoot: TComponent;
FAncestor: TPersistent;
FRootAncestor: TComponent;
FAncestors: TStringList;
FAncestorPos: Integer;
FCurrentPos: Integer;
FOnFindAncestor: TCWPFindAncestorEvent;
procedure AddToAncestorList(Component: TComponent); procedure AddToAncestorList(Component: TComponent);
procedure DetermineAncestor(Component: TComponent); procedure DetermineAncestor(Component: TComponent);
procedure SetRoot(const AValue: TComponent); procedure SetRoot(const AValue: TComponent);
@ -112,18 +123,9 @@ type
procedure WriteChildren(Component: TComponent; Step: TCWPChildrenStep); procedure WriteChildren(Component: TComponent; Step: TCWPChildrenStep);
procedure WriteProperty(Instance: TPersistent; PropInfo: PPropInfo); procedure WriteProperty(Instance: TPersistent; PropInfo: PPropInfo);
procedure WriteProperties(Instance: TPersistent); procedure WriteProperties(Instance: TPersistent);
procedure WriteDefineProperties(Instance: TPersistent);
procedure WriteCollection(PropName: string; Collection: TCollection); procedure WriteCollection(PropName: string; Collection: TCollection);
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 ShortenFloat(s: string): string; function ShortenFloat(s: string): string;
function GetEnumExpr(TypeInfo: PTypeInfo; Value: integer;
AllowOutOfRange: boolean): string;
public public
constructor Create(AStream: TStream); constructor Create(AStream: TStream);
destructor Destroy; override; destructor Destroy; override;
@ -136,6 +138,16 @@ type
procedure WriteLn; procedure WriteLn;
procedure WriteStatement(const s: string); procedure WriteStatement(const s: string);
procedure WriteAssign(const LHS, RHS: string); procedure WriteAssign(const LHS, RHS: string);
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 CreatedByAncestor(Component: TComponent): boolean; function CreatedByAncestor(Component: TComponent): boolean;
procedure Indent; procedure Indent;
procedure Unindent; procedure Unindent;
@ -152,6 +164,11 @@ type
property IndentStep: integer read FIndentStep write FIndentStep; property IndentStep: integer read FIndentStep write FIndentStep;
property Options: TCWPOptions read FOptions write FOptions; property Options: TCWPOptions read FOptions write FOptions;
property IgnoreChildren: Boolean read FIgnoreChildren write FIgnoreChildren; property IgnoreChildren: Boolean read FIgnoreChildren write FIgnoreChildren;
property OnGetParentProperty: TCWPGetParentProperty read FOnGetParentProperty write FOnGetParentProperty;
public
// for custom DefineProperties
property OnWriteMethodProperty: TWriteMethodPropertyEvent read FOnWriteMethodProperty write FOnWriteMethodProperty;
property OnWriteStringProperty: TReadWriteStringPropertyEvent read FOnWriteStringProperty write FOnWriteStringProperty;
public public
// code snippets // code snippets
property LineEnding: string read FLineEnding write FLineEnding; property LineEnding: string read FLineEnding write FLineEnding;
@ -160,7 +177,8 @@ type
property AccessClass: string read FAccessClass property AccessClass: string read FAccessClass
write FAccessClass; // classname used to access protected TComponent members like SetChildOrder write FAccessClass; // classname used to access protected TComponent members like SetChildOrder
property NeedAccessClass: boolean read FNeedAccessClass write FNeedAccessClass; // some property needed AccessClass property NeedAccessClass: boolean read FNeedAccessClass write FNeedAccessClass; // some property needed AccessClass
property OnGetParentProperty: TCWPGetParentProperty read FOnGetParentProperty write FOnGetParentProperty; property ExecCustomData: string read FExecCustomData write FExecCustomData;
property MaxColumn: integer read FMaxColumn write FMaxColumn default CSPDefaultMaxColumn;
end; end;
// Tests ======================================================================= // Tests =======================================================================
@ -555,6 +573,15 @@ type
property Intf: IInterfaceComponentReference read FIntf write FIntf; property Intf: IInterfaceComponentReference read FIntf write FIntf;
end; end;
{ TSimpleControlWithStrings }
TSimpleControlWithStrings = class(TSimpleControl)
private
FLines: TStrings;
published
property Lines: TStrings read FLines write FLines;
end;
{ TTestCompReaderWriterPas } { TTestCompReaderWriterPas }
TTestCompReaderWriterPas = class(TCustomTestCTStdCodetools) TTestCompReaderWriterPas = class(TCustomTestCTStdCodetools)
@ -599,6 +626,9 @@ type
procedure TestInlineDescendant; // e.g. a Form with a Frame, Frame is inherited from another Frame procedure TestInlineDescendant; // e.g. a Form with a Frame, Frame is inherited from another Frame
procedure TestDesignInfo; procedure TestDesignInfo;
procedure TestDefineProperites_ListOfStrings; // ToDo
// ToDo: WriteUnicodeString, WriteWideString, WriteInteger, Write
// ToDo: DefineBinaryProperty
procedure TestFindComponentInit; // ToDo procedure TestFindComponentInit; // ToDo
end; end;
@ -1177,9 +1207,9 @@ var
UStrValue, UDefStrValue: UnicodeString; UStrValue, UDefStrValue: UnicodeString;
VarValue, DefVarValue: tvardata; VarValue, DefVarValue: tvardata;
aTypeData: PTypeData; aTypeData: PTypeData;
Component: TComponent; Component, AncestorComponent: TComponent;
SavedAncestor: TPersistent; SavedAncestor: TPersistent;
IntfValue: IInterface; IntfValue, AncestorIntf: IInterface;
CompRef: IInterfaceComponentReference; CompRef: IInterfaceComponentReference;
begin begin
// do not stream properties without getter // do not stream properties without getter
@ -1446,7 +1476,7 @@ begin
// set property value // set property value
Name:=GetComponentPath(Component); Name:=GetComponentPath(Component);
if Name='' then if Name='' then
raise EStreamError.Create('cannot write property "'+DbgSName(Instance)+'.'+PropName+'"'); raise EWriteError.Create('cannot write property "'+DbgSName(Instance)+'.'+PropName+'"');
WriteAssign(PropName,Name); WriteAssign(PropName,Name);
end; //(ObjValue <> AncestorObj) end; //(ObjValue <> AncestorObj)
end // ObjValue.InheritsFrom(TComponent) end // ObjValue.InheritsFrom(TComponent)
@ -1516,12 +1546,34 @@ begin
else if Supports(IntfValue, IInterfaceComponentReference, CompRef) then else if Supports(IntfValue, IInterfaceComponentReference, CompRef) then
begin begin
Component := CompRef.GetComponent; Component := CompRef.GetComponent;
Name:=GetComponentPath(Component); AncestorComponent := nil;
if Name='' then if HasAncestor then
raise EStreamError.Create('cannot write property "'+DbgSName(Instance)+'.'+PropName+'"'); begin
WriteAssign(PropName,Name); 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 end else
raise EStreamError.Create('interface property "'+PropName+'" does not support IInterfaceComponentReference'); raise EWriteError.Create('interface property "'+PropName+'" does not support IInterfaceComponentReference');
end; end;
else else
{$IFDEF VerboseCompWriterPas} {$IFDEF VerboseCompWriterPas}
@ -1535,8 +1587,6 @@ procedure TCompWriterPas.WriteProperties(Instance: TPersistent);
var var
PropCount, i: integer; PropCount, i: integer;
PropList: PPropList; PropList: PPropList;
HasAncestor: Boolean;
DefValue, Value: LongInt;
begin begin
PropCount:=GetPropList(Instance,PropList); PropCount:=GetPropList(Instance,PropList);
if PropCount>0 then if PropCount>0 then
@ -1547,6 +1597,36 @@ begin
finally finally
Freemem(PropList); Freemem(PropList);
end; end;
WriteDefineProperties(Instance);
end;
procedure TCompWriterPas.WriteDefineProperties(Instance: TPersistent);
var
Col: Integer;
InLit, NeedComma: boolean;
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;
var
HasAncestor: Boolean;
DefValue, Value: LongInt;
aStream: TMemoryStream;
BinWriter: TWriter;
s: String;
p: PChar;
c: Char;
i: Integer;
begin
if Instance is TComponent then begin if Instance is TComponent then begin
HasAncestor := Assigned(Ancestor) and ((Instance = Root) or HasAncestor := Assigned(Ancestor) and ((Instance = Root) or
(Instance.ClassType = Ancestor.ClassType)); (Instance.ClassType = Ancestor.ClassType));
@ -1561,7 +1641,88 @@ begin
// -> it is the same longint value on Little and BigEndian system // -> it is the same longint value on Little and BigEndian system
WriteAssign(FPropPath + 'DesignInfo',IntToStr(Value)); WriteAssign(FPropPath + 'DesignInfo',IntToStr(Value));
end; end;
// ToDo: Instance.DefineProperties(Self);
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;
if PropertyPath<>'' then
begin
s:=PropertyPath;
Delete(s,length(s),1); // chomp '.'
end
else if Instance is TComponent then
s:=GetComponentPath(TComponent(Instance))
else
s:='';
if s='' then
raise EWriteError.Create('cannot write DefineProperties of "'+DbgSName(Instance)+'"');
s:=ExecCustomData+'('+s+',[';
Write(s);
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; end;
procedure TCompWriterPas.WriteCollection(PropName: string; procedure TCompWriterPas.WriteCollection(PropName: string;
@ -1842,17 +2003,24 @@ begin
else if AllowOutOfRange then else if AllowOutOfRange then
Result:=TypeInfo^.Name+'('+IntToStr(Value)+')' Result:=TypeInfo^.Name+'('+IntToStr(Value)+')'
else else
raise EStreamError.Create('enum '+IntToStr(Value)+' is out of range of type "'+TypeInfo^.Name+'"'); raise EWriteError.Create('enum '+IntToStr(Value)+' is out of range of type "'+TypeInfo^.Name+'"');
end; end;
constructor TCompWriterPas.Create(AStream: TStream); constructor TCompWriterPas.Create(AStream: TStream);
var
C: TAccessComp;
begin begin
FIndentStep:=2; FIndentStep:=2;
FStream:=AStream; FStream:=AStream;
FLineEnding:=system.LineEnding; FLineEnding:=system.LineEnding;
FAssignOp:=':='; FAssignOp:=':=';
FSignature:=CSPDefaultSignature; FSignature:=CSPDefaultSignature;
FMaxColumn:=CSPDefaultMaxColumn;
FExecCustomData:=CSPDefaultExecCustomLFM;
FAccessClass:=CSPDefaultAccessClass; FAccessClass:=CSPDefaultAccessClass;
C:=TAccessComp.Create(nil);
FDefaultDefineProperties:=TMethod(@C.DefineProperties).Code;
C.Free;
end; end;
destructor TCompWriterPas.Destroy; destructor TCompWriterPas.Destroy;
@ -3148,6 +3316,77 @@ begin
end; end;
end; end;
type
{ TCSPReader }
TCSPReader = class(TReader)
public
procedure ReadProperties(Instance: TPersistent);
end;
{ TCSPReader }
procedure TCSPReader.ReadProperties(Instance: TPersistent);
begin
while not EndOfList do
ReadProperty(Instance);
end;
procedure ExecCustomLFM(Instance: TPersistent; const Data: array of string);
var
MemStream: TMemoryStream;
i: Integer;
s: String;
Reader: TCSPReader;
begin
MemStream:=TMemoryStream.Create;
Reader:=nil;
try
for i:=low(Data) to High(Data) do
begin
s:=Data[i];
MemStream.Write(s[1],length(s));
end;
MemStream.Position:=0;
Reader:=TCSPReader.Create(MemStream,1024);
Reader.ReadProperties(Instance);
finally
Reader.Free;
MemStream.Free;
end;
end;
procedure TTestCompReaderWriterPas.TestDefineProperites_ListOfStrings;
var
ARoot: TSimpleControlWithStrings;
Expected: String;
Lines2: TStringList;
begin
ARoot:=TSimpleControlWithStrings.Create(nil);
Lines2:=nil;
try
with ARoot do begin
Name:=CreateRootName(ARoot);
Lines:=TStringList.Create;
Lines.Text:='First'+LineEnding+'Second';
end;
Expected:=#7'Strings'#1#6#5'First'#6#6'Second'#0#0;
TestWriteDescendant('TestDefineProperites_ListOfStrings',ARoot,nil,[
CSPDefaultExecCustomLFM+'(Lines,[#7''Strings''#1#6#5''First''#6#6''Second''#0#0]);',
'']);
Lines2:=TStringList.Create;
ExecCustomLFM(Lines2,[Expected]);
AssertEquals('read TStrings.Text',ARoot.Lines.Text,Lines2.Text);
finally
Lines2.Free;
FreeAndNil(ARoot.FLines);
ARoot.Free;
end;
end;
procedure TTestCompReaderWriterPas.TestFindComponentInit; procedure TTestCompReaderWriterPas.TestFindComponentInit;
var var
Code: TCodeBuffer; Code: TCodeBuffer;