fpc/fcl/inc/xmlreg.pp
2005-02-14 17:13:06 +00:00

748 lines
16 KiB
ObjectPascal

{$mode objfpc}
{$h+}
unit xmlreg;
Interface
uses
sysutils,classes,dom,xmlread,xmlwrite;
Type
TDataType = (dtUnknown,dtDWORD,dtString,dtBinary);
TDataInfo = record
DataType : TDataType;
DataSize : Integer;
end;
TKeyInfo = record
SubKeys,
SubKeyLen,
Values,
ValueLen,
DataLen : Integer;
FTime : TDateTime;
end;
TXmlRegistry = Class(TObject)
Private
FAutoFlush,
FDirty : Boolean;
FFileName : String;
FRootKey : String;
FDocument : TXMLDocument;
FCurrentElement : TDomElement;
FCurrentKey : String;
Procedure SetFileName(Value : String);
Protected
Procedure LoadFromStream(S : TStream);
Function NormalizeKey(KeyPath : String) : String;
Procedure CreateEmptyDoc;
Function FindKey (S : String) : TDomElement;
Function FindSubKey (S : String; N : TDomElement) : TDomElement;
Function CreateSubKey (S : String; N : TDomElement) : TDomElement;
Function FindValueKey (S : String) : TDomElement;
Function CreateValueKey (S : String) : TDomElement;
Function BufToHex(Const Buf; Len : Integer) : String;
Function hexToBuf(Const Str : String; Var Buf; Var Len : Integer ) : Integer;
Procedure MaybeFlush;
Property Document : TXMLDocument Read FDocument;
Property Dirty : Boolean Read FDirty write FDirty;
Public
Constructor Create(AFileName : String);
Function SetKey(KeyPath : String; AllowCreate : Boolean) : Boolean ;
Procedure SetRootKey(Value : String);
Function DeleteKey(KeyPath : String) : Boolean;
Function CreateKey(KeyPath : String) : Boolean;
Function GetValueSize(Name : String) : Integer;
Function GetValueType(Name : String) : TDataType;
Function GetValueInfo(Name : String; Var Info : TDataInfo) : Boolean;
Function GetKeyInfo(Var Info : TKeyInfo) : Boolean;
Function EnumSubKeys(List : TStrings) : Integer;
Function EnumValues(List : TStrings) : Integer;
Function KeyExists(KeyPath : String) : Boolean;
Function ValueExists(ValueName : String) : Boolean;
Function RenameValue(Const OldName,NewName : String) : Boolean;
Function DeleteValue(S : String) : Boolean;
Procedure Flush;
Procedure Load;
Function GetValueData(Name : String; Var DataType : TDataType; Var Data; Var DataSize : Integer) : Boolean;
Function SetValueData(Name : String; DataType : TDataType; Const Data; DataSize : Integer) : Boolean;
Property FileName : String Read FFileName Write SetFileName;
Property RootKey : String Read FRootKey Write SetRootkey;
Property AutoFlush : Boolean Read FAutoFlush Write FAutoFlush;
end;
// used Key types
Const
SXmlReg = 'XMLReg';
SKey = 'Key';
SValue = 'Value';
SName = 'Name';
SType = 'Type';
SData = 'Data';
Implementation
Constructor TXmlRegistry.Create(AFileName : String);
begin
FFileName:=AFileName;
FautoFlush:=True;
If (AFileName<>'') then
Load
else
CreateEmptyDoc;
end;
Procedure TXmlRegistry.SetFileName(Value : String);
begin
If Value<>FFileName then
begin
FFilename:=Value;
Flush;
end;
end;
Procedure TXmlRegistry.CreateEmptyDoc;
Const
template = '<?xml version="1.0" encoding="ISO8859-1"?>'+LineEnding+
'<'+SXMLReg+'>'+LineEnding+
'</'+SXMLReg+'>'+LineEnding;
Var
S : TStream;
begin
S:=TStringStream.Create(Template);
S.Seek(0,soFromBeginning);
Try
LoadFromStream(S);
Finally
S.Free;
end;
end;
Function TXmlRegistry.NormalizeKey(KeyPath : String) : String;
Var
L : Integer;
begin
Result:=StringReplace(KeyPath,'\','/',[rfReplaceAll]);
L:=Length(Result);
If (L>0) and (Result[L]<>'/') then
Result:=Result+'/';
end;
Function TXmlRegistry.SetKey(KeyPath : String; AllowCreate : Boolean) : boolean;
Var
SubKey,ResultKey : String;
P : Integer;
Node,Node2 : TDomElement;
begin
Result:=(Length(KeyPath)>0);
If Not Result then
Exit;
KeyPath:=NormalizeKey(KeyPath);
If (KeyPath[1]<>'/') then
begin
Node:=FCurrentElement;
Resultkey:=FCurrentKey;
end
else
begin
Delete(Keypath,1,1);
Node:=FDocument.DocumentElement;
If (FRootKey<>'') then
KeyPath:=FRootKey+KeyPath;
ResultKey:='';
end;
Result:=True;
repeat
P:=Pos('/',KeyPath);
If (P<>0) then
begin
SubKey:=Copy(KeyPath,1,P-1);
Delete(KeyPath,1,P);
Node2:=FindSubKey(SubKey,Node);
Result:=(Node2<>Nil);
If Result then
Node:=Node2
else
begin
If AllowCreate then
Begin
Node2:=CreateSubKey(SubKey,Node);
Result:=Node2<>Nil;
If Result Then
Node:=Node2;
end;
end;
If Result then
ResultKey:=ResultKey+SubKey+'/';
end;
Until (Not Result) or (Length(KeyPath)=0);
If Result then
begin
FCurrentkey:=ResultKey;
FCurrentElement:=Node;
end;
MaybeFlush;
end;
Procedure TXmlRegistry.SetRootKey(Value : String);
begin
FRootKey:=NormalizeKey(Value);
If (Length(FRootKey)>1) and (FRootKey[1]='/') then
Delete(FRootKey,1,1);
FCurrentKey:='';
FCurrentElement:=Nil;
end;
Function TXmlRegistry.DeleteKey(KeyPath : String) : Boolean;
Var
N : TDomElement;
begin
N:=FindKey(KeyPath);
Result:=(N<>Nil);
If Result then
begin
(N.ParentNode as TDomElement).RemoveChild(N);
FDirty:=True;
MaybeFlush;
end;
end;
Function TXmlRegistry.CreateKey(KeyPath : String) : Boolean;
Var
SubKey : String;
P : Integer;
Node,Node2 : TDomElement;
begin
Result:=(Length(KeyPath)>0);
If Not Result then
Exit;
KeyPath:=NormalizeKey(KeyPath);
If (KeyPath[1]<>'/') then
Node:=FCurrentElement
else
begin
Delete(Keypath,1,1);
Node:=FDocument.DocumentElement;
If (FRootKey<>'') then
KeyPath:=FRootKey+KeyPath;
end;
Result:=True;
repeat
P:=Pos('/',KeyPath);
If (P<>0) then
begin
SubKey:=Copy(KeyPath,1,P-1);
Delete(KeyPath,1,P);
Node2:=FindSubKey(SubKey,Node);
Result:=(Node2<>Nil);
If Result then
Node:=Node2
else
begin
Node2:=CreateSubKey(SubKey,Node);
Result:=Node2<>Nil;
Node:=Node2
end;
end;
Until (Not Result) or (Length(KeyPath)=0);
MaybeFlush;
end;
Function TXmlRegistry.GetValueData(Name : String; Var DataType : TDataType; Var Data; Var DataSize : Integer) : Boolean;
Type
PCardinal = ^Cardinal;
Var
Node : TDomElement;
DataNode : TDomNode;
ND : Integer;
Dt : TDataType;
S : AnsiString;
begin
Node:=FindValueKey(Name);
Result:=Node<>Nil;
If Result then
begin
DataNode:=Node.FirstChild;
Result:=(DataNode<>Nil) and (DataNode is TDomText);
If Result then
begin
ND:=StrToIntDef(Node[Stype],0);
Result:=ND<=Ord(High(TDataType));
If Result then
begin
DataType:=TDataType(StrToIntDef(Node[Stype],0));
Case DataType of
dtDWORD : begin
PCardinal(@Data)^:=StrToIntDef(DataNode.NodeValue,0);
DataSize:=SizeOf(Cardinal);
end;
dtString : begin
S:=DataNode.NodeValue; // Convert to ansistring
DataSize:=Length(S);
If (DataSize>0) then
Move(S[1],Data,DataSize);
end;
dtBinary : begin
DataSize:=Length(DataNode.NodeValue);
If (DataSize>0) then
HexToBuf(DataNode.NodeValue,Data,DataSize);
end;
end;
end;
end;
end;
end;
Function TXmlRegistry.SetValueData(Name : String; DataType : TDataType; Const Data; DataSize : Integer) : Boolean;
Type
PCardinal = ^Cardinal;
Var
Node : TDomElement;
DataNode : TDomNode;
ND : Integer;
Dt : TDataType;
S : String;
begin
Node:=FindValueKey(Name);
If Node=Nil then
Node:=CreateValueKey(Name);
Result:=(Node<>Nil);
If Result then
begin
Node[SType]:=IntToStr(Ord(DataType));
DataNode:=Node.FirstChild;
Case DataType of
dtDWORD : DataNode.NodeValue:=IntToStr(PCardinal(@Data)^);
dtString : begin
SetLength(S,DataSize);
If (DataSize>0) then
Move(Data,S[1],DataSize);
DataNode.NodeValue:=S;
end;
dtBinary : begin
S:=BufToHex(Data,DataSize);
DataNode.NodeValue:=S;
end;
end;
end;
If Result then
begin
FDirty:=True;
MaybeFlush;
end;
end;
Function TXmlRegistry.FindSubKey (S : String; N : TDomElement) : TDomElement;
Var
Node : TDOMNode;
begin
Result:=Nil;
If N<>Nil then
begin
Node:=N.FirstChild;
While (Result=Nil) and (Assigned(Node)) do
begin
If (Node.NodeType=ELEMENT_NODE) and (Node.NodeName=SKey) then
If CompareText(TDomElement(Node)[SName],S)=0 then
Result:=TDomElement(Node);
Node:=Node.NextSibling;
end;
end;
end;
Function TXmlRegistry.CreateSubKey (S : String; N : TDomElement) : TDomElement;
begin
Result:=FDocument.CreateElement(SKey);
Result[SName]:=S;
N.AppendChild(Result);
FDirty:=True;
end;
Function TXmlRegistry.FindValueKey (S : String) : TDomElement;
Var
Node : TDOMNode;
begin
If FCurrentElement<>Nil then
begin
Node:=FCurrentElement.FirstChild;
Result:=Nil;
While (Result=Nil) and (Assigned(Node)) do
begin
If (Node.NodeType=ELEMENT_NODE) and (Node.NodeName=SValue) then
If CompareText(TDomElement(Node)[SName],S)=0 then
Result:=TDomElement(Node);
Node:=Node.NextSibling;
end;
end;
end;
Function TXmlRegistry.CreateValueKey (S : String) : TDomElement;
begin
If Assigned(FCurrentElement) then
begin
Result:=FDocument.CreateElement(SValue);
Result[SName]:=S;
// textnode to hold the value;
Result.AppendChild(FDocument.CreateTextNode(''));
FCurrentElement.AppendChild(Result);
FDirty:=True;
end
else
Result:=Nil;
end;
Procedure TXMLregistry.MaybeFlush;
begin
If FAutoFlush then
Flush;
end;
Procedure TXmlRegistry.Flush;
Var
S : TStream;
begin
If FDirty then
begin
S:=TFileStream.Create(FFileName,fmCreate);
Try
WriteXMLFile(FDocument,S);
FDirty:=False;
finally
S.Free;
end;
end;
end;
Procedure TXmlRegistry.Load;
Var
S : TStream;
begin
If Not FileExists(FFileName) then
CreateEmptyDoc
else
begin
S:=TFileStream.Create(FFileName,fmOpenReadWrite);
try
LoadFromStream(S);
finally
S.Free;
end;
end;
end;
Procedure TXmlRegistry.LoadFromStream(S : TStream);
begin
If Assigned(FDocument) then
begin
FDocument.Free;
FDocument:=Nil;
end;
ReadXMLFile(FDocument,S);
if (FDocument=Nil) then
CreateEmptyDoc;
FCurrentElement:=Nil;
FCurrentKey:='';
FRootKey:='';
FDirty:=False;
end;
Function TXmlRegistry.BufToHex(Const Buf; Len : Integer) : String;
Var
P : PByte;
S : String;
I : Integer;
begin
SetLength(Result,Len*2);
P:=@Buf;
For I:=0 to Len-1 do
begin
S:=HexStr(P[I],2);
Move(S[1],Result[I*2+1],2);
end;
end;
Function TXMLRegistry.hexToBuf(Const Str : String; Var Buf; Var Len : Integer ) : Integer;
Var
I : Integer;
P : PByte;
S : String;
B : Byte;
Code : Integer;
begin
P:=@Buf;
Len:= Length(Str) div 2;
For I:=0 to Len-1 do
begin
S:='$'+Copy(Str,(I*2)+1,2);
Val(S,B,Code);
If Code<>0 then
begin
Inc(Result);
B:=0;
end;
P[I]:=B;
end;
end;
Function TXMLRegistry.DeleteValue(S : String) : Boolean;
Var
N : TDomElement;
begin
N:=FindValueKey(S);
Result:=(N<>Nil);
If Result then
begin
FCurrentElement.RemoveChild(N);
FDirty:=True;
MaybeFlush;
end;
end;
Function TXMLRegistry.GetValueSize(Name : String) : Integer;
Var
Info : TDataInfo;
begin
If GetValueInfo(Name,Info) then
Result:=Info.DataSize
else
Result:=-1;
end;
Function TXMLRegistry.GetValueType(Name : String) : TDataType;
Var
Info : TDataInfo;
begin
If GetValueInfo(Name,Info) then
Result:=Info.DataType
else
Result:=dtUnknown;
end;
Function TXMLRegistry.GetValueInfo(Name : String; Var Info : TDataInfo) : Boolean;
Var
N : TDomElement;
DN : TDomNode;
begin
N:=FindValueKey(Name);
Result:=(N<>Nil);
If Result then
begin
DN:=N.FirstChild;
Result:=DN<>Nil;
If Result then
With Info do
begin
DataType:=TDataType(StrToIntDef(N[SType],0));
Case DataType of
dtUnknown : DataSize:=0;
dtDword : Datasize:=SizeOf(Cardinal);
dtString : DataSize:=Length(DN.NodeValue);
dtBinary : DataSize:=Length(DN.NodeValue) div 2;
end;
end;
end;
end;
Function TXMLRegistry.GetKeyInfo(Var Info : TKeyInfo) : Boolean;
Var
Node,DataNode : TDOMNode;
L : Integer;
begin
FillChar(Info,SizeOf(Info),0);
Result:=FCurrentElement<>Nil;
If Result then
With Info do
begin
If (FFileName<>'') Then
FTime:=FileAge(FFileName);
Node:=FCurrentElement.FirstChild;
While Assigned(Node) do
begin
If (Node.NodeType=ELEMENT_NODE) then
If (Node.NodeName=SKey) then
begin
Inc(SubKeys);
L:=Length(TDomElement(Node)[SName]);
If (L>SubKeyLen) then
SubKeyLen:=L;
end
else if (Node.NodeName=SValue) then
begin
Inc(Values);
L:=Length(TDomElement(Node)[SName]);
If (L>ValueLen) then
ValueLen:=L;
DataNode:=TDomElement(Node).FirstChild;
If (DataNode<>Nil) and (DataNode is TDomText) then
Case TDataType(StrToIntDef(TDomElement(Node)[SType],0)) of
dtUnknown : L:=0;
dtDWord : L:=4;
DtString : L:=Length(DataNode.NodeValue);
dtBinary : L:=Length(DataNode.NodeValue) div 2;
end
else
L:=0;
If (L>DataLen) Then
DataLen:=L;
end;
Node:=Node.NextSibling;
end;
end;
end;
Function TXMLRegistry.EnumSubKeys(List : TStrings) : Integer;
Var
Node : TDOMNode;
begin
List.Clear;
Result:=0;
If FCurrentElement<>Nil then
begin
Node:=FCurrentElement.FirstChild;
While Assigned(Node) do
begin
If (Node.NodeType=ELEMENT_NODE) and (Node.NodeName=SKey) then
List.Add(TDomElement(Node)[SName]);
Node:=Node.NextSibling;
end;
Result:=List.Count;
end;
end;
Function TXMLRegistry.EnumValues(List : TStrings) : Integer;
Var
Node : TDOMNode;
begin
List.Clear;
Result:=0;
If FCurrentElement<>Nil then
begin
Node:=FCurrentElement.FirstChild;
While Assigned(Node) do
begin
If (Node.NodeType=ELEMENT_NODE) and (Node.NodeName=SValue) then
List.Add(TDomElement(Node)[SName]);
Node:=Node.NextSibling;
end;
Result:=List.Count;
end;
end;
Function TXMLRegistry.KeyExists(KeyPath : String) : Boolean;
begin
Result:=FindKey(KeyPath)<>Nil;
end;
Function TXMLRegistry.RenameValue(Const OldName,NewName : String) : Boolean;
Var
N : TDomElement;
begin
N:=FindValueKey(OldName);
If (N<>Nil) then
begin
N[SName]:=NewName;
FDirty:=True;
MaybeFlush;
end;
end;
Function TXMLRegistry.FindKey (S : String) : TDomElement;
Var
SubKey : String;
P : Integer;
Node : TDomElement;
begin
Result:=Nil;
If (Length(S)=0) then
Exit;
S:=NormalizeKey(S);
If (S[1]<>'/') then
Node:=FCurrentElement
else
begin
Delete(S,1,1);
Node:=FDocument.DocumentElement;
If (FRootKey<>'') then
S:=FRootKey+S;
end;
repeat
P:=Pos('/',S);
If (P<>0) then
begin
SubKey:=Copy(S,1,P-1);
Delete(S,1,P);
Result:=FindSubKey(SubKey,Node);
Node:=Result;
end;
Until (Result=Nil) or (Length(S)=0);
end;
Function TXmlRegistry.ValueExists(ValueName : String) : Boolean;
begin
Result:=FindValueKey(ValueName)<>Nil;
end;
end.