lazarus/components/lazutils/laz2_xmlcfg.pas
2016-06-29 11:48:44 +00:00

996 lines
29 KiB
ObjectPascal

{
**********************************************************************
This file is part of LazUtils.
It is copied from Free Component Library and adapted to use
UTF8 strings instead of widestrings.
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
**********************************************************************
Implementation of TXMLConfig class
Copyright (c) 1999 - 2001 by Sebastian Guenther, sg@freepascal.org
TXMLConfig enables applications to use XML files for storing their
configuration data
}
{$MODE objfpc}
{$H+}
unit Laz2_XMLCfg;
interface
{off $DEFINE MEM_CHECK}
uses
{$IFDEF MEM_CHECK}MemCheck,{$ENDIF}
Classes, sysutils, LazFileCache,
Laz2_DOM, Laz2_XMLRead, Laz2_XMLWrite, LazUtilities,
typinfo;
type
{"APath" is the path and name of a value: A XML configuration file is
hierachical. "/" is the path delimiter, the part after the last "/"
is the name of the value. The path components will be mapped to XML
elements, the name will be an element attribute.}
{ TXMLConfig }
TXMLConfig = class(TComponent)
private
FFilename: String;
FReadFlags: TXMLReaderFlags;
FWriteFlags: TXMLWriterFlags;
FPointSettings: TFormatSettings;
procedure CreateConfigNode;
procedure InitFormatSettings;
procedure SetFilename(const AFilename: String);
protected
type
TNodeCache = record
Node: TDomNode;
ChildrenValid: boolean;
Children: array of TDomNode; // nodes with NodeName<>'' and sorted
end;
protected
doc: TXMLDocument;
FModified: Boolean;
fDoNotLoadFromFile: boolean;
fAutoLoadFromSource: string;
fPathCache: string;
fPathNodeCache: array of TNodeCache; // starting with doc.DocumentElement, then first child node of first sub path
procedure Loaded; override;
function ExtendedToStr(const e: extended): string;
function StrToExtended(const s: string; const ADefault: extended): extended;
procedure ReadXMLFile(out ADoc: TXMLDocument; const AFilename: String); virtual;
procedure WriteXMLFile(ADoc: TXMLDocument; const AFileName: String); virtual;
procedure FreeDoc; virtual;
procedure SetPathNodeCache(Index: integer; aNode: TDomNode);
function GetCachedPathNode(Index: integer): TDomNode; inline;
procedure InvalidateCacheTilEnd(StartIndex: integer);
function InternalFindNode(const APath: String; PathLen: integer;
CreateNodes: boolean = false): TDomNode;
procedure InternalCleanNode(Node: TDomNode);
function FindChildNode(PathIndex: integer; const aName: string): TDomNode;
public
constructor Create(AOwner: TComponent); override; overload;
constructor Create(const AFilename: String); overload; // create and load
constructor CreateClean(const AFilename: String); // create new
constructor CreateWithSource(const AFilename, Source: String); // create new and load from Source
destructor Destroy; override;
procedure Clear;
procedure Flush; // Writes the XML file
procedure ReadFromStream(s: TStream);
procedure WriteToStream(s: TStream);
function GetValue(const APath, ADefault: String): String;
function GetValue(const APath: String; ADefault: Integer): Integer;
function GetValue(const APath: String; ADefault: Boolean): Boolean;
function GetExtendedValue(const APath: String;
const ADefault: extended): extended;
procedure SetValue(const APath, AValue: String);
procedure SetDeleteValue(const APath, AValue, DefValue: String);
procedure SetValue(const APath: String; AValue: Integer);
procedure SetDeleteValue(const APath: String; AValue, DefValue: Integer);
procedure SetValue(const APath: String; AValue: Boolean);
procedure SetDeleteValue(const APath: String; AValue, DefValue: Boolean);
procedure GetValue(const APath: String; out ARect: TRect;
const ADefault: TRect);
procedure SetDeleteValue(const APath: String; const AValue, DefValue: TRect);
procedure SetExtendedValue(const APath: String; const AValue: extended);
procedure SetDeleteExtendedValue(const APath: String;
const AValue, DefValue: extended);
procedure DeletePath(const APath: string);
procedure DeleteValue(const APath: string);
function FindNode(const APath: String; PathHasValue: boolean): TDomNode;
function HasPath(const APath: string; PathHasValue: boolean): boolean; // checks if the path has values, set PathHasValue=true to skip the last part
function HasChildPaths(const APath: string): boolean;
property Modified: Boolean read FModified write FModified;
procedure InvalidatePathCache;
published
property Filename: String read FFilename write SetFilename;
property Document: TXMLDocument read doc;
property ReadFlags: TXMLReaderFlags read FReadFlags write FReadFlags;
property WriteFlags: TXMLWriterFlags read FWriteFlags write FWriteFlags;
end;
{ TRttiXMLConfig }
TRttiXMLConfig = class(TXMLConfig)
protected
procedure WriteProperty(Path: String; Instance: TPersistent;
PropInfo: Pointer; DefInstance: TPersistent = nil;
OnlyProperty: String= '');
procedure ReadProperty(Path: String; Instance: TPersistent;
PropInfo: Pointer; DefInstance: TPersistent = nil;
OnlyProperty: String= '');
public
procedure WriteObject(Path: String; Obj: TPersistent;
DefObject: TPersistent= nil; OnlyProperty: String= '');
procedure ReadObject(Path: String; Obj: TPersistent;
DefObject: TPersistent= nil; OnlyProperty: String= '');
end;
// ===================================================================
function CompareDomNodeNames(DOMNode1, DOMNode2: Pointer): integer;
implementation
function CompareDomNodeNames(DOMNode1, DOMNode2: Pointer): integer;
var
Node1: TDOMNode absolute DomNode1;
Node2: TDOMNode absolute DomNode2;
begin
Result:=CompareStr(Node1.NodeName,Node2.NodeName);
end;
// inline
function TXMLConfig.GetCachedPathNode(Index: integer): TDomNode;
begin
if Index<length(fPathNodeCache) then
Result:=fPathNodeCache[Index].Node
else
Result:=nil;
end;
constructor TXMLConfig.Create(const AFilename: String);
begin
//DebugLn(['TXMLConfig.Create ',AFilename]);
Create(nil);
SetFilename(AFilename);
end;
constructor TXMLConfig.CreateClean(const AFilename: String);
begin
//DebugLn(['TXMLConfig.CreateClean ',AFilename]);
fDoNotLoadFromFile:=true;
Create(AFilename);
FModified:=FileExistsCached(AFilename);
end;
constructor TXMLConfig.CreateWithSource(const AFilename, Source: String);
begin
fAutoLoadFromSource:=Source;
try
CreateClean(AFilename);
finally
fAutoLoadFromSource:='';
end;
end;
destructor TXMLConfig.Destroy;
begin
if Assigned(doc) then
begin
Flush;
FreeDoc;
end;
inherited Destroy;
end;
procedure TXMLConfig.Clear;
var
cfg: TDOMElement;
begin
// free old document
FreeDoc;
// create new document
doc := TXMLDocument.Create;
cfg :=TDOMElement(doc.FindNode('CONFIG'));
if not Assigned(cfg) then begin
cfg := doc.CreateElement('CONFIG');
doc.AppendChild(cfg);
end;
end;
procedure TXMLConfig.Flush;
begin
if Modified and (Filename<>'') then
begin
//DebugLn(['TXMLConfig.Flush ',Filename]);
WriteXMLFile(Doc,Filename);
FModified := False;
end;
end;
procedure TXMLConfig.ReadFromStream(s: TStream);
begin
FreeDoc;
Laz2_XMLRead.ReadXMLFile(Doc,s,ReadFlags);
if Doc=nil then
Clear;
end;
procedure TXMLConfig.WriteToStream(s: TStream);
begin
if Doc=nil then
CreateConfigNode;
Laz2_XMLWrite.WriteXMLFile(Doc,s,WriteFlags);
end;
function TXMLConfig.GetValue(const APath, ADefault: String): String;
var
Node, Attr: TDOMNode;
NodeName: String;
StartPos: integer;
begin
//CheckHeapWrtMemCnt('TXMLConfig.GetValue A '+APath);
Result:=ADefault;
// skip root
StartPos:=length(APath)+1;
while (StartPos>1) and (APath[StartPos-1]<>'/') do dec(StartPos);
if StartPos>length(APath) then exit;
// find sub node
Node:=InternalFindNode(APath,StartPos-1);
if Node=nil then
exit;
//CheckHeapWrtMemCnt('TXMLConfig.GetValue E');
NodeName:=copy(APath,StartPos,length(APath));
//CheckHeapWrtMemCnt('TXMLConfig.GetValue G');
Attr := Node.Attributes.GetNamedItem(NodeName);
if Assigned(Attr) then
Result := Attr.NodeValue;
//writeln('TXMLConfig.GetValue END Result="',Result,'"');
end;
function TXMLConfig.GetValue(const APath: String; ADefault: Integer): Integer;
begin
Result := StrToIntDef(GetValue(APath, IntToStr(ADefault)),ADefault);
end;
procedure TXMLConfig.GetValue(const APath: String; out ARect: TRect;
const ADefault: TRect);
begin
ARect.Left:=GetValue(APath+'Left',ADefault.Left);
ARect.Top:=GetValue(APath+'Top',ADefault.Top);
ARect.Right:=GetValue(APath+'Right',ADefault.Right);
ARect.Bottom:=GetValue(APath+'Bottom',ADefault.Bottom);
end;
function TXMLConfig.GetValue(const APath: String; ADefault: Boolean): Boolean;
var
s: String;
begin
if ADefault then
s := 'True'
else
s := 'False';
s := GetValue(APath, s);
if CompareText(s,'TRUE')=0 then
Result := True
else if CompareText(s,'FALSE')=0 then
Result := False
else
Result := ADefault;
end;
function TXMLConfig.GetExtendedValue(const APath: String;
const ADefault: extended): extended;
begin
Result:=StrToExtended(GetValue(APath,ExtendedToStr(ADefault)),ADefault);
end;
procedure TXMLConfig.SetValue(const APath, AValue: String);
var
Node: TDOMNode;
NodeName: String;
StartPos: integer;
begin
StartPos:=length(APath)+1;
while (StartPos>1) and (APath[StartPos-1]<>'/') do dec(StartPos);
if StartPos>length(APath) then exit;
if Doc=nil then
CreateConfigNode;
Node:=InternalFindNode(APath,StartPos-1,true);
if Node=nil then
exit;
NodeName:=copy(APath,StartPos,length(APath));
if (not Assigned(TDOMElement(Node).GetAttributeNode(NodeName))) or
(TDOMElement(Node)[NodeName] <> AValue) then
begin
TDOMElement(Node)[NodeName] := AValue;
FModified := True;
end;
end;
procedure TXMLConfig.SetDeleteValue(const APath, AValue, DefValue: String);
begin
if AValue=DefValue then
DeleteValue(APath)
else
SetValue(APath,AValue);
end;
procedure TXMLConfig.SetValue(const APath: String; AValue: Integer);
begin
SetValue(APath, IntToStr(AValue));
end;
procedure TXMLConfig.SetDeleteValue(const APath: String; AValue,
DefValue: Integer);
begin
if AValue=DefValue then
DeleteValue(APath)
else
SetValue(APath,AValue);
end;
procedure TXMLConfig.SetDeleteValue(const APath: String; const AValue,
DefValue: TRect);
begin
SetDeleteValue(APath+'Left',AValue.Left,DefValue.Left);
SetDeleteValue(APath+'Top',AValue.Top,DefValue.Top);
SetDeleteValue(APath+'Right',AValue.Right,DefValue.Right);
SetDeleteValue(APath+'Bottom',AValue.Bottom,DefValue.Bottom);
end;
procedure TXMLConfig.SetValue(const APath: String; AValue: Boolean);
begin
if AValue then
SetValue(APath, 'True')
else
SetValue(APath, 'False');
end;
procedure TXMLConfig.SetDeleteValue(const APath: String; AValue,
DefValue: Boolean);
begin
if AValue=DefValue then
DeleteValue(APath)
else
SetValue(APath,AValue);
end;
procedure TXMLConfig.SetExtendedValue(const APath: String;
const AValue: extended);
begin
SetValue(APath,ExtendedToStr(AValue));
end;
procedure TXMLConfig.SetDeleteExtendedValue(const APath: String; const AValue,
DefValue: extended);
begin
if AValue=DefValue then
DeleteValue(APath)
else
SetExtendedValue(APath,AValue);
end;
procedure TXMLConfig.DeletePath(const APath: string);
var
Node: TDOMNode;
ParentNode: TDOMNode;
begin
Node:=InternalFindNode(APath,length(APath));
if (Node=nil) or (Node.ParentNode=nil) then exit;
ParentNode:=Node.ParentNode;
ParentNode.RemoveChild(Node);
FModified:=true;
InvalidatePathCache;
InternalCleanNode(ParentNode);
end;
procedure TXMLConfig.DeleteValue(const APath: string);
var
Node: TDomNode;
StartPos: integer;
NodeName: string;
begin
Node:=FindNode(APath,true);
if (Node=nil) then exit;
StartPos:=length(APath);
while (StartPos>0) and (APath[StartPos]<>'/') do dec(StartPos);
NodeName:=copy(APath,StartPos+1,length(APath)-StartPos);
if Assigned(TDOMElement(Node).GetAttributeNode(NodeName)) then begin
TDOMElement(Node).RemoveAttribute(NodeName);
FModified := True;
end;
InternalCleanNode(Node);
end;
procedure TXMLConfig.Loaded;
begin
inherited Loaded;
if Length(Filename) > 0 then
SetFilename(Filename); // Load the XML config file
end;
function TXMLConfig.FindNode(const APath: String; PathHasValue: boolean): TDomNode;
var
PathLen: Integer;
begin
PathLen:=length(APath);
if PathHasValue then begin
while (PathLen>0) and (APath[PathLen]<>'/') do dec(PathLen);
while (PathLen>0) and (APath[PathLen]='/') do dec(PathLen);
end;
Result:=InternalFindNode(APath,PathLen);
end;
function TXMLConfig.HasPath(const APath: string; PathHasValue: boolean): boolean;
begin
Result:=FindNode(APath,PathHasValue)<>nil;
end;
function TXMLConfig.HasChildPaths(const APath: string): boolean;
var
Node: TDOMNode;
begin
Node:=FindNode(APath,false);
Result:=(Node<>nil) and Node.HasChildNodes;
end;
procedure TXMLConfig.InvalidatePathCache;
begin
fPathCache:='';
InvalidateCacheTilEnd(0);
end;
function TXMLConfig.ExtendedToStr(const e: extended): string;
begin
Result := FloatToStr(e, FPointSettings);
end;
function TXMLConfig.StrToExtended(const s: string; const ADefault: extended): extended;
begin
Result := StrToFloatDef(s, ADefault, FPointSettings);
end;
procedure TXMLConfig.ReadXMLFile(out ADoc: TXMLDocument; const AFilename: String);
begin
InvalidatePathCache;
Laz2_XMLRead.ReadXMLFile(ADoc,AFilename,ReadFlags);
end;
procedure TXMLConfig.WriteXMLFile(ADoc: TXMLDocument; const AFileName: String);
begin
Laz2_XMLWrite.WriteXMLFile(ADoc,AFileName,WriteFlags);
InvalidateFileStateCache(AFileName);
end;
procedure TXMLConfig.FreeDoc;
begin
InvalidatePathCache;
FreeAndNil(doc);
end;
procedure TXMLConfig.SetPathNodeCache(Index: integer; aNode: TDomNode);
var
OldLength, NewLength: Integer;
begin
OldLength:=length(fPathNodeCache);
if OldLength<=Index then begin
if OldLength<8 then
NewLength:=8
else
NewLength:=OldLength*2;
if NewLength<Index then NewLength:=Index;
SetLength(fPathNodeCache,NewLength);
FillByte(fPathNodeCache[OldLength],SizeOf(TNodeCache)*(NewLength-OldLength),0);
end else if fPathNodeCache[Index].Node=aNode then
exit
else
InvalidateCacheTilEnd(Index+1);
with fPathNodeCache[Index] do begin
Node:=aNode;
ChildrenValid:=false;
end;
end;
procedure TXMLConfig.InvalidateCacheTilEnd(StartIndex: integer);
var
i: LongInt;
begin
for i:=StartIndex to length(fPathNodeCache)-1 do begin
with fPathNodeCache[i] do begin
if Node=nil then break;
Node:=nil;
ChildrenValid:=false;
end;
end;
end;
function TXMLConfig.InternalFindNode(const APath: String; PathLen: integer;
CreateNodes: boolean): TDomNode;
var
NodePath: String;
StartPos, EndPos: integer;
PathIndex: Integer;
Parent: TDOMNode;
NdName: DOMString;
NameLen: Integer;
begin
//debugln(['TXMLConfig.InternalFindNode APath="',copy(APath,1,PathLen),'" CreateNodes=',CreateNodes]);
PathIndex:=0;
Result:=GetCachedPathNode(PathIndex);
if (Result=nil) and (doc<>nil) then begin
Result:=TDOMElement(doc.FindNode('CONFIG'));
SetPathNodeCache(PathIndex,Result);
end;
if PathLen=0 then exit;
StartPos:=1;
while (Result<>nil) do begin
EndPos:=StartPos;
while (EndPos<=PathLen) and (APath[EndPos]<>'/') do inc(EndPos);
NameLen:=EndPos-StartPos;
if NameLen=0 then break;
inc(PathIndex);
Parent:=Result;
Result:=GetCachedPathNode(PathIndex);
if Result<>nil then
NdName:=Result.NodeName;
if (Result=nil) or (length(NdName)<>NameLen)
or not CompareMem(PChar(NdName),@APath[StartPos],NameLen) then begin
// different path => search
NodePath:=copy(APath,StartPos,NameLen);
Result:=FindChildNode(PathIndex-1,NodePath);
if Result=nil then begin
if not CreateNodes then exit;
// create missing node
Result:=Doc.CreateElement(NodePath);
Parent.AppendChild(Result);
fPathNodeCache[PathIndex-1].ChildrenValid:=false;
InvalidateCacheTilEnd(PathIndex);
if EndPos>PathLen then exit;
end;
SetPathNodeCache(PathIndex,Result);
end;
StartPos:=EndPos+1;
if StartPos>PathLen then exit;
end;
Result:=nil;
end;
procedure TXMLConfig.InternalCleanNode(Node: TDomNode);
var
ParentNode: TDOMNode;
begin
if (Node=nil) then exit;
while (Node.FirstChild=nil) and (Node.ParentNode<>nil)
and (Node.ParentNode.ParentNode<>nil) do begin
if (Node is TDOMElement) and (not TDOMElement(Node).IsEmpty) then break;
ParentNode:=Node.ParentNode;
ParentNode.RemoveChild(Node);
InvalidatePathCache;
Node:=ParentNode;
FModified := True;
end;
end;
function TXMLConfig.FindChildNode(PathIndex: integer; const aName: string
): TDomNode;
var
aParent, aChild: TDOMNode;
aCount: Integer;
NewLength: Integer;
l, r, m: Integer;
cmp: Integer;
begin
with fPathNodeCache[PathIndex] do begin
if not ChildrenValid then begin
// collect all children and sort
aParent:=Node;
aCount:=0;
aChild:=aParent.FirstChild;
while aChild<>nil do begin
if aChild.NodeName<>'' then begin
if aCount=length(Children) then begin
NewLength:=length(Children);
if NewLength<8 then
NewLength:=8
else
NewLength:=NewLength*2;
SetLength(Children,NewLength);
end;
Children[aCount]:=aChild;
inc(aCount);
end;
aChild:=aChild.NextSibling;
end;
SetLength(Children,aCount);
if aCount>1 then
MergeSort(@Children[0],aCount,@CompareDomNodeNames); // sort ascending [0]<[1]
for m:=0 to aCount-2 do
if Children[m].NodeName=Children[m+1].NodeName then begin
// duplicate found: nodes with same name
// -> use only the first
Children[m+1]:=Children[m];
end;
ChildrenValid:=true;
end;
// binary search
l:=0;
r:=length(Children)-1;
while l<=r do begin
m:=(l+r) shr 1;
cmp:=CompareStr(aName,Children[m].NodeName);
if cmp<0 then
r:=m-1
else if cmp>0 then
l:=m+1
else
exit(Children[m]);
end;
Result:=nil;
end;
end;
constructor TXMLConfig.Create(AOwner: TComponent);
begin
// for compatibility with old TXMLConfig, which wrote #13 as #13, not as &xD;
FReadFlags:=[xrfAllowLowerThanInAttributeValue,xrfAllowSpecialCharsInAttributeValue];
FWriteFlags:=[xwfSpecialCharsInAttributeValue];
inherited Create(AOwner);
InitFormatSettings;
end;
procedure TXMLConfig.SetFilename(const AFilename: String);
var
ms: TMemoryStream;
begin
{$IFDEF MEM_CHECK}CheckHeapWrtMemCnt('TXMLConfig.SetFilename A '+AFilename);{$ENDIF}
if FFilename = AFilename then exit;
FFilename := AFilename;
InvalidatePathCache;
if csLoading in ComponentState then
exit;
if Assigned(doc) then
begin
Flush;
FreeDoc;
end;
doc:=nil;
//debugln(['TXMLConfig.SetFilename Load=',not fDoNotLoadFromFile,' FileExists=',FileExistsCached(Filename),' File=',Filename]);
if (not fDoNotLoadFromFile) and FileExistsCached(Filename) then
Laz2_XMLRead.ReadXMLFile(doc,Filename,ReadFlags)
else if fAutoLoadFromSource<>'' then begin
ms:=TMemoryStream.Create;
try
ms.Write(fAutoLoadFromSource[1],length(fAutoLoadFromSource));
ms.Position:=0;
Laz2_XMLRead.ReadXMLFile(doc,ms,ReadFlags);
finally
ms.Free;
end;
end;
CreateConfigNode;
{$IFDEF MEM_CHECK}CheckHeapWrtMemCnt('TXMLConfig.SetFilename END');{$ENDIF}
end;
procedure TXMLConfig.CreateConfigNode;
var
cfg: TDOMElement;
begin
if not Assigned(doc) then
doc := TXMLDocument.Create;
cfg :=TDOMElement(doc.FindNode('CONFIG'));
if not Assigned(cfg) then begin
cfg := doc.CreateElement('CONFIG');
doc.AppendChild(cfg);
end;
end;
procedure TXMLConfig.InitFormatSettings;
begin
FPointSettings := DefaultFormatSettings;
FPointSettings.DecimalSeparator := '.';
FPointSettings.ThousandSeparator := ',';
end;
{ TRttiXMLConfig }
procedure TRttiXMLConfig.WriteObject(Path: String; Obj: TPersistent;
DefObject: TPersistent; OnlyProperty: String = '');
var
PropCount,i : integer;
PropList : PPropList;
begin
PropCount:=GetPropList(Obj,PropList);
if PropCount>0 then begin
try
for i := 0 to PropCount-1 do
WriteProperty(Path, Obj, PropList^[i], DefObject, OnlyProperty);
finally
Freemem(PropList);
end;
end;
end;
// based on FPC TWriter
procedure TRttiXMLConfig.WriteProperty(Path: String; Instance: TPersistent;
PropInfo: Pointer; DefInstance: TPersistent; OnlyProperty: String= '');
type
tset = set of 0..31;
var
i: Integer;
PropType: PTypeInfo;
Value, DefValue: LongInt;
Ident: String;
IntToIdentFn: TIntToIdent;
SetType: Pointer;
FloatValue, DefFloatValue: Extended;
//WStrValue, WDefStrValue: WideString;
StrValue, DefStrValue: String;
//Int64Value, DefInt64Value: Int64;
BoolValue, DefBoolValue: boolean;
begin
// do not stream properties without getter and setter
if not (Assigned(PPropInfo(PropInfo)^.GetProc) and
Assigned(PPropInfo(PropInfo)^.SetProc)) then
exit;
PropType := PPropInfo(PropInfo)^.PropType;
Path := Path + PPropInfo(PropInfo)^.Name;
if (OnlyProperty <> '') and (OnlyProperty <> PPropInfo(PropInfo)^.Name) then
exit;
case PropType^.Kind of
tkInteger, tkChar, tkEnumeration, tkSet, tkWChar:
begin
Value := GetOrdProp(Instance, PropInfo);
if (DefInstance <> nil) then
DefValue := GetOrdProp(DefInstance, PropInfo);
if (DefInstance <> nil) and (Value = DefValue) then
DeleteValue(Path)
else begin
case PropType^.Kind of
tkInteger:
begin // Check if this integer has a string identifier
IntToIdentFn := FindIntToIdent(PPropInfo(PropInfo)^.PropType);
if Assigned(IntToIdentFn) and IntToIdentFn(Value, Ident{%H-}) then
SetValue(Path, Ident) // Integer can be written a human-readable identifier
else
SetValue(Path, Value); // Integer has to be written just as number
end;
tkChar:
SetValue(Path, Chr(Value));
tkWChar:
SetValue(Path, Value);
tkSet:
begin
SetType := GetTypeData(PropType)^.CompType;
Ident := '';
for i := 0 to 31 do
if (i in tset(Value)) then begin
if Ident <> '' then Ident := Ident + ',';
Ident := Ident + GetEnumName(PTypeInfo(SetType), i);
end;
SetValue(Path, Ident);
end;
tkEnumeration:
SetValue(Path, GetEnumName(PropType, Value));
end;
end;
end;
tkFloat:
begin
FloatValue := GetFloatProp(Instance, PropInfo);
if (DefInstance <> nil) then
DefFloatValue := GetFloatProp(DefInstance, PropInfo);
if (DefInstance <> nil) and (DefFloatValue = FloatValue) then
DeleteValue(Path)
else
SetValue(Path, FloatToStr(FloatValue));
end;
tkSString, tkLString, tkAString:
begin
StrValue := GetStrProp(Instance, PropInfo);
if (DefInstance <> nil) then
DefStrValue := GetStrProp(DefInstance, PropInfo);
if (DefInstance <> nil) and (DefStrValue = StrValue) then
DeleteValue(Path)
else
SetValue(Path, StrValue);
end;
(* tkWString:
begin
WStrValue := GetWideStrProp(Instance, PropInfo);
if (DefInstance <> nil) then
WDefStrValue := GetWideStrProp(DefInstance, PropInfo);
if (DefInstance <> nil) and (WDefStrValue = WStrValue) then
DeleteValue(Path)
else
SetValue(Path, WStrValue);
end;*)
(* tkInt64, tkQWord:
begin
Int64Value := GetInt64Prop(Instance, PropInfo);
if (DefInstance <> nil) then
DefInt64Value := GetInt64Prop(DefInstance, PropInfo)
if (DefInstance <> nil) and (Int64Value = DefInt64Value) then
DeleteValue(Path, Path)
else
SetValue(StrValue);
end;*)
tkBool:
begin
BoolValue := GetOrdProp(Instance, PropInfo)<>0;
if (DefInstance <> nil) then
DefBoolValue := GetOrdProp(DefInstance, PropInfo)<>0;
if (DefInstance <> nil) and (BoolValue = DefBoolValue) then
DeleteValue(Path)
else
SetValue(Path, BoolValue);
end;
end;
end;
procedure TRttiXMLConfig.ReadProperty(Path: String; Instance: TPersistent; PropInfo: Pointer;
DefInstance: TPersistent; OnlyProperty: String);
type
tset = set of 0..31;
var
i, j: Integer;
PropType: PTypeInfo;
Value, DefValue: LongInt;
Ident, s: String;
IdentToIntFn: TIdentToInt;
SetType: Pointer;
FloatValue, DefFloatValue: Extended;
//WStrValue, WDefStrValue: WideString;
StrValue, DefStrValue: String;
//Int64Value, DefInt64Value: Int64;
BoolValue, DefBoolValue: boolean;
begin
// do not stream properties without getter and setter
if not (Assigned(PPropInfo(PropInfo)^.GetProc) and
Assigned(PPropInfo(PropInfo)^.SetProc)) then
exit;
PropType := PPropInfo(PropInfo)^.PropType;
Path := Path + PPropInfo(PropInfo)^.Name;
if (OnlyProperty <> '') and (OnlyProperty <> PPropInfo(PropInfo)^.Name) then
exit;
if DefInstance = nil then
DefInstance := Instance;
case PropType^.Kind of
tkInteger, tkChar, tkEnumeration, tkSet, tkWChar:
begin
DefValue := GetOrdProp(DefInstance, PropInfo);
case PropType^.Kind of
tkInteger:
begin // Check if this integer has a string identifier
Ident := GetValue(Path, IntToStr(DefValue));
IdentToIntFn := FindIdentToInt(PPropInfo(PropInfo)^.PropType);
if TryStrToInt(Ident, Value) then
SetOrdProp(Instance, PropInfo, Value)
else if Assigned(IdentToIntFn) and IdentToIntFn(Ident, Value) then
SetOrdProp(Instance, PropInfo, Value)
else
SetOrdProp(Instance, PropInfo, DefValue)
end;
tkChar:
begin
Ident := GetValue(Path, chr(DefValue));
if Length(Ident) > 0 then
SetOrdProp(Instance, PropInfo, ord(Ident[1]))
else
SetOrdProp(Instance, PropInfo, DefValue);
end;
tkWChar:
SetOrdProp(Instance, PropInfo, GetValue(Path, DefValue));
tkSet:
begin
SetType := GetTypeData(PropType)^.CompType;
Ident := GetValue(Path, '-');
If Ident = '-' then
Value := DefValue
else begin
Value := 0;
while length(Ident) > 0 do begin
i := Pos(',', Ident);
if i < 1 then
i := length(Ident) + 1;
s := copy(Ident, 1, i-1);
Ident := copy(Ident, i+1, length(Ident));
j := GetEnumValue(PTypeInfo(SetType), s);
if j <> -1 then
include(tset(Value), j)
else Begin
Value := DefValue;
break;
end;
end;
end;
SetOrdProp(Instance, PropInfo, Value);
end;
tkEnumeration:
begin
Ident := GetValue(Path, '-');
If Ident = '-' then
Value := DefValue
else
Value := GetEnumValue(PropType, Ident);
if Value <> -1 then
SetOrdProp(Instance, PropInfo, Value)
else
SetOrdProp(Instance, PropInfo, DefValue);
end;
end;
end;
tkFloat:
begin
DefFloatValue := GetFloatProp(DefInstance, PropInfo);
Ident := GetValue(Path, FloatToStr(DefFloatValue));
if TryStrToFloat(Ident, FloatValue) then
SetFloatProp(Instance, PropInfo, FloatValue)
else
SetFloatProp(Instance, PropInfo, DefFloatValue)
end;
tkSString, tkLString, tkAString:
begin
DefStrValue := GetStrProp(DefInstance, PropInfo);
StrValue := GetValue(Path, DefStrValue);
SetStrProp(Instance, PropInfo, StrValue)
end;
(* tkWString:
begin
end;*)
(* tkInt64, tkQWord:
begin
end;*)
tkBool:
begin
DefBoolValue := GetOrdProp(DefInstance, PropInfo) <> 0;
BoolValue := GetValue(Path, DefBoolValue);
SetOrdProp(Instance, PropInfo, ord(BoolValue));
end;
end;
end;
procedure TRttiXMLConfig.ReadObject(Path: String; Obj: TPersistent; DefObject: TPersistent;
OnlyProperty: String);
var
PropCount,i : integer;
PropList : PPropList;
begin
PropCount:=GetPropList(Obj,PropList);
if PropCount>0 then begin
try
for i := 0 to PropCount-1 do
ReadProperty(Path, Obj, PropList^[i], DefObject, OnlyProperty);
finally
Freemem(PropList);
end;
end;
end;
end.