Mark van Renswoude
16ba374f21
Added: variant hashes Added: Delphi 7 package (note: D6 package is not in sync yet!)
502 lines
13 KiB
ObjectPascal
502 lines
13 KiB
ObjectPascal
{
|
|
:: X2UtConfig provides a generic access mechanism for application settings.
|
|
:: Create an instance of one of the TX2xxxConfigSource classes (such as
|
|
:: TX2IniConfigSource in the X2UtConfigIni.pas unit) to gain access to an
|
|
:: IX2ConfigSource interface.
|
|
::
|
|
:: Though no actual code was ported, credits to Nini for .NET
|
|
:: (http://nini.sourceforge.net/) for some excellent ideas. In fact,
|
|
:: X2UtXMLConfigSource is capable of recognizing, reading and writing
|
|
:: Nini-compatible XML files.
|
|
::
|
|
:: Last changed: $Date$
|
|
:: Revision: $Rev$
|
|
:: Author: $Author$
|
|
}
|
|
unit X2UtConfig;
|
|
|
|
interface
|
|
uses
|
|
Classes,
|
|
|
|
X2UtHashes,
|
|
X2UtHashesVariants;
|
|
|
|
type
|
|
// Forward declarations
|
|
IX2Config = interface;
|
|
IX2ConfigSource = interface;
|
|
|
|
{
|
|
:$ Callback for Iterate method.
|
|
}
|
|
TX2ConfigIterateMethod = procedure(Sender: IX2ConfigSource;
|
|
Config: IX2Config;
|
|
var Abort: Boolean) of object;
|
|
|
|
|
|
{
|
|
:$ Interface for configurations.
|
|
|
|
:: Implementations are highly recommended to descend from, or simply use,
|
|
:: TX2BaseConfig.
|
|
}
|
|
IX2Config = interface
|
|
['{25DF95C1-CE09-44A7-816B-A33B8D0D78DC}']
|
|
function GetName(): String;
|
|
|
|
function ReadBool(const AName: String; const ADefault: Boolean = False): Boolean;
|
|
function ReadFloat(const AName: String; const ADefault: Double = 0): Double;
|
|
function ReadInteger(const AName: String; const ADefault: Integer = 0): Integer;
|
|
function ReadString(const AName: String; const ADefault: String = ''): String;
|
|
|
|
procedure WriteBool(const AName: String; AValue: Boolean);
|
|
procedure WriteFloat(const AName: String; AValue: Double);
|
|
procedure WriteInteger(const AName: String; AValue: Integer);
|
|
procedure WriteString(const AName, AValue: String);
|
|
|
|
procedure Clear();
|
|
procedure Delete(const AName: String);
|
|
function Exists(const AName: String): Boolean;
|
|
|
|
procedure Save();
|
|
|
|
property Name: String read GetName;
|
|
end;
|
|
|
|
{
|
|
:$ Interface for configuration sources.
|
|
|
|
:: For subsections, seperate each section name with a dot (.)
|
|
::
|
|
:: Implementations are highly recommended to descend from
|
|
:: TX2BaseConfigSource.
|
|
}
|
|
IX2ConfigSource = interface
|
|
['{1FF5282B-122F-47D7-95E8-3DB60A8CF765}']
|
|
function GetAutoSave(): Boolean;
|
|
procedure SetAutoSave(Value: Boolean);
|
|
|
|
function Configs(const AName: String): IX2Config;
|
|
|
|
function Add(const AName: String): IX2Config;
|
|
function Exists(const AName: String): Boolean;
|
|
procedure Delete(const AName: String);
|
|
procedure Clear();
|
|
|
|
procedure Save();
|
|
|
|
procedure List(const AName: String; const ADest: TStrings;
|
|
const ARecurse: Boolean = False);
|
|
procedure Iterate(const AName: String;
|
|
const ACallback: TX2ConfigIterateMethod;
|
|
const AData: Pointer;
|
|
const ARecurse: Boolean = False); overload;
|
|
|
|
property AutoSave: Boolean read GetAutoSave write SetAutoSave;
|
|
end;
|
|
|
|
// Forward declarations
|
|
TX2BaseConfig = class;
|
|
TX2BaseConfigSource = class;
|
|
|
|
{
|
|
:$ Hash for configuration objects.
|
|
}
|
|
TX2ConfigHash = class(TX2SOHash)
|
|
protected
|
|
function GetCurrentValue(): TX2BaseConfig;
|
|
function GetValue(Key: String): TX2BaseConfig;
|
|
procedure SetValue(Key: String; const Value: TX2BaseConfig);
|
|
public
|
|
property CurrentValue: TX2BaseConfig read GetCurrentValue;
|
|
property Values[Key: String]: TX2BaseConfig read GetValue write SetValue; default;
|
|
end;
|
|
|
|
{
|
|
:$ Default implementation for configurations.
|
|
}
|
|
TX2BaseConfig = class(TInterfacedObject, IX2Config)
|
|
private
|
|
FConfigItems: TX2ConfigHash;
|
|
FName: String;
|
|
FSource: IX2ConfigSource;
|
|
FValues: TX2SVHash;
|
|
protected
|
|
procedure WriteValue(const AName: String; const AValue: Variant);
|
|
|
|
property Source: IX2ConfigSource read FSource;
|
|
property Values: TX2SVHash read FValues;
|
|
property ConfigItems: TX2ConfigHash read FConfigItems;
|
|
public
|
|
constructor Create(const AConfig: String; const ASource: IX2ConfigSource);
|
|
destructor Destroy(); override;
|
|
|
|
// IX2Config
|
|
function GetName(): String;
|
|
|
|
function ReadBool(const AName: String; const ADefault: Boolean = False): Boolean;
|
|
function ReadFloat(const AName: String; const ADefault: Double = 0): Double;
|
|
function ReadInteger(const AName: String; const ADefault: Integer = 0): Integer;
|
|
function ReadString(const AName: String; const ADefault: String = ''): String;
|
|
|
|
procedure WriteBool(const AName: String; AValue: Boolean);
|
|
procedure WriteFloat(const AName: String; AValue: Double);
|
|
procedure WriteInteger(const AName: String; AValue: Integer);
|
|
procedure WriteString(const AName, AValue: String);
|
|
|
|
procedure Clear();
|
|
procedure Delete(const AName: String);
|
|
function Exists(const AName: String): Boolean;
|
|
|
|
procedure Save(); virtual;
|
|
|
|
property Name: String read GetName;
|
|
end;
|
|
|
|
{
|
|
:$ Default implementation for configuration sources.
|
|
}
|
|
TX2BaseConfigSource = class(TInterfacedObject, IX2ConfigSource)
|
|
private
|
|
FAutoSave: Boolean;
|
|
FConfigItems: TX2ConfigHash;
|
|
protected
|
|
function GetConfig(const AName: String;
|
|
const AAllowCreate: Boolean = True): TX2BaseConfig; virtual;
|
|
function CreateConfig(const AName: String): TX2BaseConfig; virtual; abstract;
|
|
function GetItems(const AName: String): TX2ConfigHash; virtual;
|
|
|
|
property ConfigItems: TX2ConfigHash read FConfigItems;
|
|
public
|
|
constructor Create();
|
|
destructor Destroy(); override;
|
|
|
|
// IX2ConfigSource
|
|
function GetAutoSave(): Boolean;
|
|
procedure SetAutoSave(Value: Boolean);
|
|
|
|
function Configs(const AName: String): IX2Config; virtual;
|
|
|
|
function Add(const AName: String): IX2Config; virtual;
|
|
function Exists(const AName: String): Boolean; virtual;
|
|
procedure Delete(const AName: String); virtual;
|
|
procedure Clear(); virtual;
|
|
|
|
procedure Save(); virtual;
|
|
|
|
procedure List(const AName: String; const ADest: TStrings;
|
|
const ARecurse: Boolean = False); virtual;
|
|
procedure Iterate(const AName: String;
|
|
const ACallback: TX2ConfigIterateMethod;
|
|
const AData: Pointer = nil;
|
|
const ARecurse: Boolean = False); overload; virtual;
|
|
end;
|
|
|
|
{
|
|
:$ Default implementation for stream-based configuration sources.
|
|
}
|
|
TX2StreamConfigSource = class(TX2BaseConfigSource)
|
|
public
|
|
constructor Create(const AStream: TStream); overload; virtual; abstract;
|
|
constructor Create(const AFileName: String); overload; virtual;
|
|
end;
|
|
|
|
|
|
var
|
|
SectionSeparator: Char = '.';
|
|
|
|
|
|
implementation
|
|
uses
|
|
SysUtils,
|
|
Variants,
|
|
|
|
X2UtStrings;
|
|
|
|
{========================================
|
|
TX2ConfigHash
|
|
========================================}
|
|
function TX2ConfigHash.GetCurrentValue(): TX2BaseConfig;
|
|
begin
|
|
Result := TX2BaseConfig(inherited GetCurrentValue());
|
|
end;
|
|
|
|
function TX2ConfigHash.GetValue(Key: String): TX2BaseConfig;
|
|
begin
|
|
Result := TX2BaseConfig(inherited GetValue(Key));
|
|
end;
|
|
|
|
procedure TX2ConfigHash.SetValue(Key: String; const Value: TX2BaseConfig);
|
|
begin
|
|
inherited SetValue(Key, Value);
|
|
end;
|
|
|
|
|
|
{==================== TX2BaseConfigSource
|
|
IX2ConfigSource
|
|
========================================}
|
|
constructor TX2BaseConfigSource.Create();
|
|
begin
|
|
inherited;
|
|
|
|
FConfigItems := TX2ConfigHash.Create(True);
|
|
end;
|
|
|
|
destructor TX2BaseConfigSource.Destroy();
|
|
begin
|
|
FreeAndNil(FConfigItems);
|
|
|
|
inherited;
|
|
end;
|
|
|
|
|
|
function TX2BaseConfigSource.GetAutoSave(): Boolean;
|
|
begin
|
|
Result := FAutoSave;
|
|
end;
|
|
|
|
procedure TX2BaseConfigSource.SetAutoSave(Value: Boolean);
|
|
begin
|
|
FAutoSave := Value;
|
|
end;
|
|
|
|
|
|
function TX2BaseConfigSource.GetConfig(const AName: String;
|
|
const AAllowCreate: Boolean): TX2BaseConfig;
|
|
var
|
|
aSections: TSplitArray;
|
|
iSection: Integer;
|
|
pItems: TX2ConfigHash;
|
|
sSection: String;
|
|
|
|
begin
|
|
Result := nil;
|
|
|
|
// Separate subsections
|
|
Split(AName, SectionSeparator, aSections);
|
|
|
|
for iSection := Low(aSections) to High(aSections) do
|
|
begin
|
|
sSection := Trim(aSections[iSection]);
|
|
if Length(sSection) = 0 then
|
|
continue;
|
|
|
|
if Assigned(Result) then
|
|
pItems := Result.ConfigItems
|
|
else
|
|
pItems := FConfigItems;
|
|
|
|
Result := pItems[sSection];
|
|
if not Assigned(Result) then
|
|
if AAllowCreate then
|
|
begin
|
|
Result := CreateConfig(sSection);
|
|
pItems[sSection] := Result;
|
|
end else
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
function TX2BaseConfigSource.GetItems(const AName: String): TX2ConfigHash;
|
|
var
|
|
pConfig: TX2BaseConfig;
|
|
|
|
begin
|
|
Result := nil;
|
|
if Length(Trim(AName)) > 0 then
|
|
begin
|
|
pConfig := GetConfig(AName, False);
|
|
if Assigned(pConfig) then
|
|
Result := pConfig.ConfigItems;
|
|
end else
|
|
Result := FConfigItems;
|
|
end;
|
|
|
|
|
|
function TX2BaseConfigSource.Configs(const AName: String): IX2Config;
|
|
begin
|
|
Result := GetConfig(AName, True);
|
|
end;
|
|
|
|
function TX2BaseConfigSource.Add(const AName: String): IX2Config;
|
|
begin
|
|
end;
|
|
|
|
function TX2BaseConfigSource.Exists(const AName: String): Boolean;
|
|
begin
|
|
end;
|
|
|
|
procedure TX2BaseConfigSource.Delete(const AName: String);
|
|
begin
|
|
end;
|
|
|
|
procedure TX2BaseConfigSource.Clear();
|
|
begin
|
|
end;
|
|
|
|
|
|
procedure TX2BaseConfigSource.Save();
|
|
begin
|
|
end;
|
|
|
|
procedure TX2BaseConfigSource.List(const AName: String; const ADest: TStrings;
|
|
const ARecurse: Boolean);
|
|
begin
|
|
end;
|
|
|
|
procedure TX2BaseConfigSource.Iterate(const AName: String;
|
|
const ACallback: TX2ConfigIterateMethod;
|
|
const AData: Pointer;
|
|
const ARecurse: Boolean);
|
|
var
|
|
bAbort: Boolean;
|
|
pItems: TX2ConfigHash;
|
|
|
|
begin
|
|
pItems := GetItems(AName);
|
|
if not Assigned(pItems) then
|
|
exit;
|
|
|
|
bAbort := False;
|
|
pItems.First();
|
|
while pItems.Next() do
|
|
begin
|
|
ACallback(Self, pItems.CurrentValue, bAbort);
|
|
if bAbort then
|
|
break;
|
|
|
|
if ARecurse then
|
|
Iterate(AName + SectionSeparator + pItems.CurrentValue.Name, ACallback,
|
|
AData, ARecurse);
|
|
end;
|
|
end;
|
|
|
|
|
|
{================== TX2StreamConfigSource
|
|
Initialization
|
|
========================================}
|
|
constructor TX2StreamConfigSource.Create(const AFileName: String);
|
|
var
|
|
fsData: TFileStream;
|
|
|
|
begin
|
|
fsData := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyNone);
|
|
try
|
|
Create(fsData);
|
|
finally
|
|
FreeAndNil(fsData);
|
|
end;
|
|
end;
|
|
|
|
|
|
{========================== TX2BaseConfig
|
|
IX2Config
|
|
========================================}
|
|
constructor TX2BaseConfig.Create(const AConfig: String;
|
|
const ASource: IX2ConfigSource);
|
|
begin
|
|
inherited Create();
|
|
|
|
FSource := ASource;
|
|
FName := AConfig;
|
|
FConfigItems := TX2ConfigHash.Create(True);
|
|
end;
|
|
|
|
destructor TX2BaseConfig.Destroy();
|
|
begin
|
|
FreeAndNil(FConfigItems);
|
|
|
|
inherited;
|
|
end;
|
|
|
|
|
|
function TX2BaseConfig.GetName(): String;
|
|
begin
|
|
Result := FName;
|
|
end;
|
|
|
|
|
|
function TX2BaseConfig.ReadBool(const AName: String;
|
|
const ADefault: Boolean): Boolean;
|
|
begin
|
|
Result := ADefault;
|
|
if Values.Exists(AName) then
|
|
Result := VarAsType(Values[AName], vtBoolean);
|
|
end;
|
|
|
|
function TX2BaseConfig.ReadFloat(const AName: String;
|
|
const ADefault: Double): Double;
|
|
begin
|
|
Result := ADefault;
|
|
if Values.Exists(AName) then
|
|
Result := VarAsType(Values[AName], vtExtended);
|
|
end;
|
|
|
|
function TX2BaseConfig.ReadInteger(const AName: String;
|
|
const ADefault: Integer): Integer;
|
|
begin
|
|
Result := ADefault;
|
|
if Values.Exists(AName) then
|
|
Result := VarAsType(Values[AName], vtInteger);
|
|
end;
|
|
|
|
function TX2BaseConfig.ReadString(const AName, ADefault: String): String;
|
|
begin
|
|
Result := ADefault;
|
|
if Values.Exists(AName) then
|
|
Result := VarAsType(Values[AName], vtString);
|
|
end;
|
|
|
|
|
|
procedure TX2BaseConfig.WriteValue(const AName: String; const AValue: Variant);
|
|
begin
|
|
Values[AName] := AValue;
|
|
if Source.AutoSave then
|
|
Save();
|
|
end;
|
|
|
|
procedure TX2BaseConfig.WriteBool(const AName: String; AValue: Boolean);
|
|
begin
|
|
WriteValue(AName, AValue);
|
|
end;
|
|
|
|
procedure TX2BaseConfig.WriteFloat(const AName: String; AValue: Double);
|
|
begin
|
|
WriteValue(AName, AValue);
|
|
end;
|
|
|
|
procedure TX2BaseConfig.WriteInteger(const AName: String; AValue: Integer);
|
|
begin
|
|
WriteValue(AName, AValue);
|
|
end;
|
|
|
|
procedure TX2BaseConfig.WriteString(const AName, AValue: String);
|
|
begin
|
|
WriteValue(AName, AValue);
|
|
end;
|
|
|
|
|
|
procedure TX2BaseConfig.Clear();
|
|
begin
|
|
Values.Clear();
|
|
end;
|
|
|
|
procedure TX2BaseConfig.Delete(const AName: String);
|
|
begin
|
|
Values.Delete(AName);
|
|
end;
|
|
|
|
function TX2BaseConfig.Exists(const AName: String): Boolean;
|
|
begin
|
|
Result := Values.Exists(AName);
|
|
end;
|
|
|
|
|
|
procedure TX2BaseConfig.Save();
|
|
begin
|
|
Source.Save();
|
|
end;
|
|
|
|
end.
|