{------------------------------------------------------------------------------} { Unit Name: kMacroString Purpose : Keep track macro variables and make replacesments Author : Vesa Lappalainen Date : 5.1.2001 Changed : 15.9.2001/vl + VCL/CLX compiling (define const CLX) ToDo : This unit has class TkMacroVarList = class(TList) In following examples assume vlist : TkMacroVarList; function IndexOf(name:string) : integer; virtual; - return index of variable with name i := vlist.IndexOf('%COMPANY%); procedure Add(v:TkBaseMacroVar); overload; virtual; - add new macro variable. If v.name allready exists, then v replaces the old variable vlist.Add(TkEditMacroVar.Create(Edit1)); procedure Add(const n,v:string); overload; virtual; - add new macro variable with name n and value v. If variable allready exists, then new value is substituded. If variable does not exist, then variable of type TkStringMacroVar is added vlist.Add('%SUBPROJECT%','Demo1'); function Change(const s:string) : string; virtual; - make full replacement of s with vars fullstring := vlist.Change('U'); see example below... procedure Delete(const name:string); virtual; - delete variable vlist.Delete('%SUBPROJECT%'); procedure ReadMacros(const ininame,sec:string); virtual; - read values form inifile/sec to variable list. Note that if short variable name is with lowercase, then in .ini-file it must be after '-char: [IniReadOrderVars] 'u=%HKCU|r% U=u vlist.ReadMacros('c:\bin\DemoWWW.ini','MacroVars'); property Def : string read FDef write FDef; - default value for variables not found vlist.Def := ''; property Value[name:string] : string read GetValue write SetValue; default; - gets or sets variable with name s := vlist.Value['%COMPANY%']; s := vlist['%COMPANY%']; vlist.Value['%COMPANY'] := 'vesal'; vlist['%COMPANY'] := 'vesal'; property Name[i:integer]:string read GetName write SetName; - get name of i's variable name := vlist.Name[2]; property ValueI[i:integer]:string read GetValueI write SetValueI; - get value of i's variable val := vlist.Val[i]; property Separator : string read FSeparator write FSeparator; // def = '' - separators for parts when changing vlist.Separator := ';'; property ShortVarLenLimit : integer read FShortVarLenLimit write FShortVarLenLimit default 0; - there is two type of variable. Long names and short names. This is the limit for short name (default 0) vlist.ShortVarLenLimit := 1; property ShortChangeLimit : integer read FShortChangeLimit write FShortChangeLimit default 0; - short name variables are used only when part to change has length less than this (default 0) vlist.ShortChangeLimit := 6; property OnMacroChange : TkMacroValueChange read FOnMacroChange write FOnMacroChange; - event when any of the variable changes the value vlist.OnMacroChange := OnMacroChange; Example of changing: Let the variable list be: ShortVarLenLimit := 1; ShortChangeLimit := 6; Separator : ';' U := 'uimI' P := 'pimI' L := 'imI' M := 'mI' G := 'I' u := '%HKCU%;' p := '%HKCU%\%SUBKEY%;' i := '%LOCALDIR%\%PROGNAME%.ini|n;' m := '%HKLM%;' I := '%PROGDIR%\%PROGNAME%.ini;' %COMPANY% := 'Kave' %PROGNAME% := 'DemoWWW' %SUBKEY% := '' %HKCU% := 'HKEY_CURRENT_USER\software\%COMPANY%\%PROGNAME%' %HKLM% := 'HKEY_LOCAL_MACHINE\software\%COMPANY%\%PROGNAME%' %PROGDIR% := 'C:\bin' %LOCALDIR% := 'D:\MyDir' Then Change('U') will return 1) U is less than 6 chars, so also short names is used: U => uimI 2) because there was changes, the change starts again uimI is less than 6 chars, so short names is used: uimI => %HKCU%;%LOCALDIR%\%PROGNAME%.ini|n;%HKLM%;%PROGDIR%\%PROGNAME%.ini; 3) there was changes, so continue for every part %HKCU% => HKEY_CURRENT_USER\software\%COMPANY%\%PROGNAME% => HKEY_CURRENT_USER\software\Kave\DemoWWW => and then no changes => part added to Result %LOCALDIR%\%PROGNAME%.ini|n => D:\MyDir\DemoWWW.ini|n => and then no changes => part added to Result %HKLM% => HKEY_LOCAL_MACHINE\software\%COMPANY%\%PROGNAME% => HKEY_LOCAL_MACHINE\software\Kave\DemoWWW => and then no changes => part added to Result %PROGDIR%\%PROGNAME%.ini => C:\bin\DemoWWW.ini => and then no changes => part added to Result 4) The result becomes HKEY_CURRENT_USER\software\Kave\DemoWWW;D:\MyDir\DemoWWW.ini|n; ... HKEY_LOCAL_MACHINE\software\Kave\DemoWWW;C:\bin\DemoWWW.ini Helper classes commented below: } {------------------------------------------------------------------------------} unit kMacroString; interface uses classes; type TkBaseMacroVar = class // Abstract base class for one variable private FName: string; protected procedure SetName(const Value: string); virtual; function GetValue: string; virtual; procedure SetValue(const Value: string); virtual; public property Name : string read FName write SetName; property Value : string read GetValue write SetValue; end; TkStringMacroVar = class(TkBaseMacroVar) // class to hold just one string private FValue: string; protected function GetValue: string; override; procedure SetValue(const Value: string); override; public constructor Create(const n,v:string); end; TkMacroVarList = class; TkMacroValueChange = procedure (sender:TkMacroVarList;i:integer) of object; TkMacroVarList = class(TList) // The list to hold variables private FDef: string; FShortVarLenLimit: integer; FShortChangeLimit: integer; FSeparator: string; FOnMacroChange: TkMacroValueChange; function GetValue(name: string): string; procedure SetValue(name: string; const Value: string); function GetName(i: integer): string; procedure SetName(i: integer; const Value: string); function GetValueI(i: integer): string; procedure SetValueI(i: integer; const Value: string); procedure ChangeAllParts(var r: string; s: string); procedure ChangeOnePart(var r: string; s: string); procedure AddPart(var r: string; const s: string); procedure ChangeShortPart(var r: string; s: string); function ReplaceStr(const name: string): string; function IndexOfStart(const s: string; lenlimit: integer): integer; public destructor Destroy; override; function IndexOf(name:string) : integer; virtual; procedure Add(v:TkBaseMacroVar); overload; virtual; procedure Add(const n,v:string); overload; virtual; function Change(const s:string) : string; overload; virtual; function ChangeAll(const s:string) : string; overload; virtual; procedure Change(st:TStrings); overload; virtual; procedure Delete(const name:string); virtual; procedure ReadMacros(const ininame:string; const sec:string='MacroVars'); virtual; property Def : string read FDef write FDef; property Value[name:string] : string read GetValue write SetValue; default; property Name[i:integer]:string read GetName write SetName; property ValueI[i:integer]:string read GetValueI write SetValueI; property Separator : string read FSeparator write FSeparator; property ShortVarLenLimit : integer read FShortVarLenLimit write FShortVarLenLimit default 0; property ShortChangeLimit : integer read FShortChangeLimit write FShortChangeLimit default 0; property OnMacroChange : TkMacroValueChange read FOnMacroChange write FOnMacroChange; end; function ChangeEnv(s:string):string; implementation uses kstring,inifiles; { TkBaseMacroVar } function TkBaseMacroVar.GetValue: string; begin Result := ''; end; procedure TkBaseMacroVar.SetName(const Value: string); begin FName := Value; end; procedure TkBaseMacroVar.SetValue(const Value: string); begin end; { TkStringMacroVar } constructor TkStringMacroVar.Create(const n, v: string); begin name := n; value := v; end; function TkStringMacroVar.GetValue: string; begin Result := FValue; end; procedure TkStringMacroVar.SetValue(const Value: string); begin FValue := Value; end; { TkMacroVarList } procedure TkMacroVarList.Add(v: TkBaseMacroVar); var i:integer; ov : string; begin if ( v = nil ) then exit; i := IndexOf(v.Name); if ( i < 0 ) then begin ov := Def; inherited Add(v); i := IndexOf(v.Name); end else begin ov := ValueI[i]; TObject(Items[i]).Free; Items[i] := v; end; if ( ov <> V.value ) and ( Assigned(OnMacroChange) ) then OnMacroChange(self,i); end; procedure TkMacroVarList.Add(const n, v: string); var i:integer; ov : string; begin i := IndexOf(n); if ( i < 0 ) then begin ov := Def; inherited Add(TkStringMacroVar.Create(n,v)); i := IndexOf(n); end else begin ov := ValueI[i]; if ( ov = v ) then exit; ValueI[i] := v; end; if ( Assigned(OnMacroChange) ) then OnMacroChange(self,i); end; function TkMacroVarList.ReplaceStr(const name: string):string; var i:integer; begin i := IndexOf(name); if ( i < 0 ) then Result := name else Result := ValueI[i]; end; procedure TkMacroVarList.AddPart(var r:string; const s: string); begin if ( r = '' ) then begin r := s; exit; end; if ( Separator <> '' ) then r := r + Separator[1]; r := r + s; end; procedure TkMacroVarList.ChangeShortPart(var r:string; s: string); var i : integer; st:string; begin if ( s = '' ) then exit; st := ''; for i:=1 to Length(s) do begin // Replace every U with corresponding uimI st := st + ReplaceStr(s[i]); end; if ( st <> s ) then begin ChangeAllParts(r,st); Exit; end; AddPart(r,st); end; function TkMacroVarList.IndexOfStart(const s:string; lenlimit:integer) : integer; var i:integer; v : TkBaseMacroVar; l:integer; begin for i:=0 to Count-1 do begin v := Items[i]; l := Length(v.Name); if ( l <= ShortVarLenLimit ) then continue; if ( v.Name = Copy(s,1,l) ) then begin Result := i; exit; end; end; Result := -1; end; procedure TkMacroVarList.ChangeOnePart(var r:string; s: string); var i:integer; v : TkBaseMacroVar; var st,olds:string; begin olds := s; st := ''; while ( s <> '' ) do begin i := IndexOfStart(s,ShortVarLenLimit); if ( i < 0 ) then begin st := st + s[1]; System.Delete(s,1,1); // Optiomoi paremmin!!! end else begin v := Items[i]; System.Delete(s,1,Length(v.Name)); st := st + v.Value; end; end; if ( st <> olds ) then begin ChangeAllParts(r,st); exit; end; AddPart(r,st); end; procedure TkMacroVarList.ChangeAllParts(var r:string; s: string); // a = 123 b = 456 c = a;b e = b f = 8 g = a i = g // s = abc;ef;gi // abc => 123bc => 123456c => 123456a;b var part : string; begin while ( s <> '' ) do begin part := Separate(s,Separator); if ( Length(part) < ShortChangeLimit ) then ChangeShortPart(r,part) else ChangeOnePart(r,part); end; end; function TkMacroVarList.Change(const s: string): string; begin Result := ''; ChangeAllParts(Result,s); end; function TkMacroVarList.ChangeAll(const s: string): string; begin Result := Change(s); Result := ChangeEnv(Result); end; destructor TkMacroVarList.Destroy; var i:integer; begin for i:=0 to Count-1 do begin TObject(Items[i]).Free; end; inherited; end; function TkMacroVarList.GetName(i: integer): string; begin Result := TkBaseMacroVar(Items[i]).Name; end; function TkMacroVarList.GetValue(name: string): string; var i:integer; begin i:= IndexOf(name); if ( i = -1 ) then Result := def else Result := TkBaseMacroVar(Items[i]).Value;// self.Name[i]; end; function TkMacroVarList.GetValueI(i: integer): string; begin if ( i < 0 ) or ( i >= Count ) then begin Result := Def; exit; end; Result := TkBaseMacroVar(Items[i]).Value; end; function TkMacroVarList.IndexOf(name: string): integer; var i:integer; begin for i:=0 to Count-1 do begin if name = TkBaseMacroVar(Items[i]).Name then begin Result := i; exit; end; end; Result := -1; end; procedure TkMacroVarList.SetName(i: integer; const Value: string); begin TkBaseMacroVar(Items[i]).Name := Value; end; procedure TkMacroVarList.SetValue(name: string; const Value: string); begin Add(name,Value); end; procedure TkMacroVarList.SetValueI(i: integer; const Value: string); begin if ( i < 0 ) or ( i >= Count ) then exit; TkBaseMacroVar(Items[i]).Value := Value; end; procedure TkMacroVarList.Delete(const name: string); var i:integer; begin i := IndexOf(name); if ( i < 0 ) then exit; TObject(Items[i]).Free; inherited Delete(i); end; procedure TkMacroVarList.ReadMacros(const ininame, sec: string); var i:integer; ini:TIniFile; list : TStrings; n,v : string; begin ini := TIniFile.Create(ininame); list := TStringList.Create; ini.ReadSectionValues(sec,list); for i:=0 to list.Count-1 do begin n := list.Names[i]; v := list.Values[n]; if ( n[1] in ['''','"'] ) then System.Delete(n,1,1); Add(n,v); end; list.Free; ini.Free; end; procedure TkMacroVarList.Change(st: TStrings); var i:integer; begin for i:=0 to st.Count-1 do st[i] := Change(st[i]); end; function ChangeEnv(s:string):string; (* Change all environment variables found from s f.ex s := '%temp%\my.bak': => c:\temp\my.bak %% => % *) var i:integer; name,st:string; begin st := ''; while ( true ) do begin i := pos('%',s); if ( i = 0 ) then begin Result := st + s; exit; end; st := st + copy(s,1,i-1); Delete(s,1,i); i := pos('%',s); name := copy(s,1,i-1); Delete(s,1,i); if ( name = '' ) then begin st := st + '%'; continue; end; st := st + GetEnvVariable(name); end; end; end.