Browse Source

* merged fixes

peter 24 years ago
parent
commit
5e0f25388b
6 changed files with 207 additions and 160 deletions
  1. 17 2
      fcl/db/dataset.inc
  2. 17 11
      fcl/db/fields.inc
  3. 41 69
      fcl/db/interbase/interbase.pp
  4. 5 2
      fcl/inc/classes.inc
  5. 15 3
      fcl/inc/stringl.inc
  6. 112 73
      fcl/xml/xmlcfg.pp

+ 17 - 2
fcl/db/dataset.inc

@@ -34,6 +34,9 @@ end;
 
 
 destructor TDataSet.Destroy;
 destructor TDataSet.Destroy;
 
 
+var
+  i: Integer;
+
 begin
 begin
   Active:=False;
   Active:=False;
   FFieldDefs.Free;
   FFieldDefs.Free;
@@ -44,6 +47,12 @@ begin
       TDatasource(Items[Count - 1]).DataSet:=Nil;
       TDatasource(Items[Count - 1]).DataSet:=Nil;
     Free;
     Free;
     end;
     end;
+  if Assigned(FBuffers) then
+  begin
+    for i := 0 to FBufferCount do
+      FreeRecordBuffer(FBuffers[i]);
+    FreeMem(FBuffers);
+  end;
   Inherited Destroy;
   Inherited Destroy;
 end;
 end;
 
 
@@ -75,7 +84,7 @@ begin
      // ATM Set by CreateField ...
      // ATM Set by CreateField ...
   For I:=0 to FFieldList.Count-1 do
   For I:=0 to FFieldList.Count-1 do
     FFieldList[i].FFieldNo:=I;
     FFieldList[i].FFieldNo:=I;
-  }  
+  }
 end;
 end;
 
 
 Function TDataset.BookmarkAvailable: Boolean;
 Function TDataset.BookmarkAvailable: Boolean;
@@ -150,7 +159,9 @@ begin
     With Fielddefs.Items[I] do
     With Fielddefs.Items[I] do
       If DataType<>ftUnknown then
       If DataType<>ftUnknown then
         begin
         begin
+        {$ifdef DSDebug}
         Writeln('About to create field',FieldDefs.Items[i].Name);
         Writeln('About to create field',FieldDefs.Items[i].Name);
+        {$endif}
         CreateField(self);
         CreateField(self);
         end;
         end;
 end;
 end;
@@ -788,6 +799,7 @@ Procedure TDataset.SetName(const Value: TComponentName);
 
 
 begin
 begin
   //!! To be implemented
   //!! To be implemented
+  inherited SetName(Value);
 end;
 end;
 
 
 Procedure TDataset.SetOnFilterRecord(const Value: TFilterRecordEvent);
 Procedure TDataset.SetOnFilterRecord(const Value: TFilterRecordEvent);
@@ -1593,7 +1605,10 @@ end;
 
 
 {
 {
   $Log$
   $Log$
-  Revision 1.4  2001-01-18 22:10:07  michael
+  Revision 1.5  2001-04-10 23:28:23  peter
+    * merged fixes
+
+  Revision 1.4  2001/01/18 22:10:07  michael
   + Fixes to make dbase working merged from fixbranch
   + Fixes to make dbase working merged from fixbranch
 
 
   Revision 1.3  2000/12/24 12:45:19  peter
   Revision 1.3  2000/12/24 12:45:19  peter

+ 17 - 11
fcl/db/fields.inc

@@ -38,7 +38,7 @@ begin
   Inherited Create(AOwner);
   Inherited Create(AOwner);
 {$ifdef dsdebug }
 {$ifdef dsdebug }
   Writeln('TFieldDef.Create : ',Aname,'(',AFieldNo,')');
   Writeln('TFieldDef.Create : ',Aname,'(',AFieldNo,')');
-{$endif}  
+{$endif}
   FName:=Aname;
   FName:=Aname;
   FDatatype:=ADatatype;
   FDatatype:=ADatatype;
   FSize:=ASize;
   FSize:=ASize;
@@ -51,7 +51,7 @@ begin
     end
     end
   else If FDataType in [ftWord,ftsmallint,ftinteger] Then
   else If FDataType in [ftWord,ftsmallint,ftinteger] Then
     If Not (FSize in [1,2,4]) then FSize:=4;
     If Not (FSize in [1,2,4]) then FSize:=4;
-  
+
   FFieldNo:=AFieldNo;
   FFieldNo:=AFieldNo;
   AOwner.FItems.Add(Self);
   AOwner.FItems.Add(Self);
 end;
 end;
@@ -69,7 +69,9 @@ Function TFieldDef.CreateField(AOwner: TComponent): TField;
 Var TheField : TFieldClass;
 Var TheField : TFieldClass;
 
 
 begin
 begin
-  Writeln ('Creating field'+FNAME);
+{$ifdef dsdebug}
+  Writeln ('Creating field '+FNAME);
+{$endif dsdebug}
   TheField:=GetFieldClass;
   TheField:=GetFieldClass;
   if TheField=Nil then
   if TheField=Nil then
     DatabaseErrorFmt(SUnknownFieldType,[FName]);
     DatabaseErrorFmt(SUnknownFieldType,[FName]);
@@ -82,10 +84,10 @@ begin
     Result.SetFieldType(DataType);
     Result.SetFieldType(DataType);
 {$ifdef dsdebug}
 {$ifdef dsdebug}
     Writeln ('TFieldDef.CReateField : Trying to set dataset');
     Writeln ('TFieldDef.CReateField : Trying to set dataset');
-{$endif dsdebug}    
+{$endif dsdebug}
 {$ifdef dsdebug}
 {$ifdef dsdebug}
     Writeln ('TFieldDef.CReateField : Result Fieldno : ',Result.FieldNo,' Self : ',FieldNo);
     Writeln ('TFieldDef.CReateField : Result Fieldno : ',Result.FieldNo,' Self : ',FieldNo);
-{$endif dsdebug}    
+{$endif dsdebug}
     Result.Dataset:=TFieldDefs(Owner).FDataset;
     Result.Dataset:=TFieldDefs(Owner).FDataset;
     If Result is TFloatField then
     If Result is TFloatField then
       TFloatField(Result).Precision:=FPrecision;
       TFloatField(Result).Precision:=FPrecision;
@@ -93,7 +95,7 @@ begin
     Result.Free;
     Result.Free;
     Raise;
     Raise;
   end;
   end;
-  
+
 end;
 end;
 
 
 Function TFieldDef.GetFieldClass : TFieldClass;
 Function TFieldDef.GetFieldClass : TFieldClass;
@@ -123,7 +125,7 @@ procedure TFieldDefs.Add(const AName: string; ADataType: TFieldType; ASize: Word
   ARequired: Boolean);
   ARequired: Boolean);
 
 
 begin
 begin
-  If Length(AName)=0 Then 
+  If Length(AName)=0 Then
     DatabaseError(SNeedFieldName);
     DatabaseError(SNeedFieldName);
   // the fielddef will register itself here as a owned component.
   // the fielddef will register itself here as a owned component.
   // fieldno is 1 based !
   // fieldno is 1 based !
@@ -168,6 +170,7 @@ Var I : longint;
 begin
 begin
   For I:=FItems.Count-1 downto 0 do
   For I:=FItems.Count-1 downto 0 do
     TFieldDef(Fitems[i]).Free;
     TFieldDef(Fitems[i]).Free;
+  FItems.Clear;
 end;
 end;
 
 
 function TFieldDefs.Find(const AName: string): TFieldDef;
 function TFieldDefs.Find(const AName: string): TFieldDef;
@@ -465,7 +468,7 @@ Procedure TField.SetDataset (Value : TDataset);
 begin
 begin
 {$ifdef dsdebug}
 {$ifdef dsdebug}
   Writeln ('Setting dataset');
   Writeln ('Setting dataset');
-{$endif}  
+{$endif}
   If Value=FDataset then exit;
   If Value=FDataset then exit;
   If Assigned(FDataset) Then FDataset.CheckInactive;
   If Assigned(FDataset) Then FDataset.CheckInactive;
   If Assigned(Value) then
   If Assigned(Value) then
@@ -476,9 +479,9 @@ begin
     end;
     end;
   If Assigned(FDataset) then
   If Assigned(FDataset) then
     FDataset.FFieldList.Remove(Self);
     FDataset.FFieldList.Remove(Self);
-  If Assigned(Value) then 
+  If Assigned(Value) then
     Value.FFieldList.Add(Self);
     Value.FFieldList.Add(Self);
-  FDataset:=Value;    
+  FDataset:=Value;
 end;
 end;
 
 
 procedure TField.SetDataType(AValue: TFieldType);
 procedure TField.SetDataType(AValue: TFieldType);
@@ -1773,7 +1776,10 @@ end;
 
 
 {
 {
   $Log$
   $Log$
-  Revision 1.5  2001-01-18 22:10:07  michael
+  Revision 1.6  2001-04-10 23:28:23  peter
+    * merged fixes
+
+  Revision 1.5  2001/01/18 22:10:07  michael
   + Fixes to make dbase working merged from fixbranch
   + Fixes to make dbase working merged from fixbranch
 
 
   Revision 1.4  2000/12/24 12:45:19  peter
   Revision 1.4  2000/12/24 12:45:19  peter

+ 41 - 69
fcl/db/interbase/interbase.pp

@@ -18,6 +18,7 @@ unit Interbase;
 
 
 {$mode objfpc}
 {$mode objfpc}
 {$H+}
 {$H+}
+{$M+}   // ### remove this!!!
 
 
 interface
 interface
 
 
@@ -33,6 +34,8 @@ type
   TIBQuery = class;
   TIBQuery = class;
   TIBStoredProc = class;
   TIBStoredProc = class;
 
 
+  EInterBaseError = class(Exception);
+
 { TIBDatabase }
 { TIBDatabase }
 
 
   TIBDatabase = class (TDatabase)
   TIBDatabase = class (TDatabase)
@@ -42,6 +45,7 @@ type
     FStatus              : array [0..19] of ISC_STATUS;
     FStatus              : array [0..19] of ISC_STATUS;
     FTransaction         : TIBTransaction;
     FTransaction         : TIBTransaction;
     FUserName            : string;
     FUserName            : string;
+    FCharSet             : string;
     FDialect             : integer;
     FDialect             : integer;
 
 
     procedure SetDBDialect;
     procedure SetDBDialect;
@@ -63,8 +67,8 @@ type
   public
   public
     procedure StartTransaction; override;
     procedure StartTransaction; override;
     procedure EndTransaction; override;
     procedure EndTransaction; override;
-    constructor Create(AOwner : TComponent); override;
     destructor Destroy; override;
     destructor Destroy; override;
+    property Handle: Pointer read GetHandle;
   published
   published
     { On connect, TIBDatabase object retrieve SQL dialect of database file,
     { On connect, TIBDatabase object retrieve SQL dialect of database file,
       and sets this property to responding value }
       and sets this property to responding value }
@@ -79,6 +83,8 @@ type
     { Before firing Open method you must set @link(Password),@link(DatabaseName),
     { Before firing Open method you must set @link(Password),@link(DatabaseName),
       @link(UserName) properties in order of successfull connect to database }
       @link(UserName) properties in order of successfull connect to database }
     property UserName : string read FUserName write FUserName;
     property UserName : string read FUserName write FUserName;
+    { The character set used in SQL statements }
+    property CharSet : string read FCharSet write FCharSet;
 
 
     { Identifies, if connection to Interbase server is established, or not.
     { Identifies, if connection to Interbase server is established, or not.
       Instead of calling Open, Close methods you can connect or disconnect
       Instead of calling Open, Close methods you can connect or disconnect
@@ -150,6 +156,7 @@ type
     procedure StartTransaction;
     procedure StartTransaction;
     constructor Create(AOwner : TComponent); override;
     constructor Create(AOwner : TComponent); override;
     destructor Destroy; override;
     destructor Destroy; override;
+    property Handle: Pointer read GetHandle;
   published
   published
     { Default action while closing transaction by setting
     { Default action while closing transaction by setting
      @link(Active) property. For details see @link(TCommitRollbackAction)}
      @link(Active) property. For details see @link(TCommitRollbackAction)}
@@ -305,7 +312,7 @@ begin
     p := @Status;
     p := @Status;
     while isc_interprete(Buf, @p) > 0 do
     while isc_interprete(Buf, @p) > 0 do
       Msg := Msg + #10' -' + StrPas(Buf);
       Msg := Msg + #10' -' + StrPas(Buf);
-    raise Exception.Create(ProcName + ': ' + Msg);
+    raise EInterBaseError.Create(ProcName + ': ' + Msg);
   end;
   end;
 end;
 end;
 
 
@@ -342,8 +349,9 @@ begin
   if FTransaction = nil then
   if FTransaction = nil then
   begin
   begin
     FTransaction := Value;
     FTransaction := Value;
-    FTransaction.Database := Self;
-    Exit;
+    if Assigned(FTransaction) then
+      FTransaction.Database := Self;
+    exit;
   end;
   end;
 
 
   if (Value <> FTransaction) and (Value <> nil) then
   if (Value <> FTransaction) and (Value <> nil) then
@@ -352,7 +360,8 @@ begin
       FTransaction := Value;
       FTransaction := Value;
       FTransaction.Database := Self;
       FTransaction.Database := Self;
     end
     end
-    else Exception.Create('Cannot assign transaction while old transaction active!');
+    else
+      raise EInterBaseError.Create('Cannot assign transaction while old transaction active!');
 end;
 end;
 
 
 function TIBDatabase.GetHandle: pointer;
 function TIBDatabase.GetHandle: pointer;
@@ -373,8 +382,11 @@ begin
     if (FPassword <> '') then
     if (FPassword <> '') then
       DPB := DPB + chr(isc_dpb_password) + chr(Length(FPassword)) + FPassword;
       DPB := DPB + chr(isc_dpb_password) + chr(Length(FPassword)) + FPassword;
   end;
   end;
+  if Length(CharSet) > 0 then
+    DPB := DPB + Chr(isc_dpb_lc_ctype) + Chr(Length(CharSet)) + CharSet;
+
   if (DatabaseName = '') then
   if (DatabaseName = '') then
-    raise Exception.Create('TIBDatabase.Open: Database connect string not filled in!');
+    raise EInterBaseError.Create('TIBDatabase.Open: Database connect string not filled in!');
   FIBDatabaseHandle := nil;
   FIBDatabaseHandle := nil;
   if isc_attach_database(@FStatus, Length(DatabaseName), @DatabaseName[1], @FIBDatabaseHandle,
   if isc_attach_database(@FStatus, Length(DatabaseName), @DatabaseName[1], @FIBDatabaseHandle,
          Length(DPB), @DPB[1]) <> 0 then
          Length(DPB), @DPB[1]) <> 0 then
@@ -407,16 +419,6 @@ begin
   FTransaction.Active := False;
   FTransaction.Active := False;
 end;
 end;
 
 
-constructor TIBDatabase.Create(AOwner : TComponent);
-begin
-  inherited Create(AOwner);
-  FIBDatabaseHandle    := nil;
-  FPassword            := '';
-  FTransaction         := nil;
-  FUserName            := '';
-  FillChar(FStatus, SizeOf(FStatus), #0);
-end;
-
 destructor TIBDatabase.Destroy;
 destructor TIBDatabase.Destroy;
 begin
 begin
   if FTransaction <> nil then
   if FTransaction <> nil then
@@ -431,17 +433,9 @@ end;
 
 
 procedure TIBTransaction.SetActive(Value : boolean);
 procedure TIBTransaction.SetActive(Value : boolean);
 begin
 begin
-  if FActive = Value then Exit;
-  if (FActive) and (not Value) then
-    case FAction of
-      caCommit            : Commit;
-      caCommitRetaining   : CommitRetaining;
-      caRollback          : Rollback;
-      caRollbackRetaining : RollbackRetaining;
-    else
-      Exception.Create('TIBTransaction.SetActive: Transaction is already active.');
-    end;
-  if (not FActive) and (Value) then
+  if FActive and (not Value) then
+    Rollback
+  else if (not FActive) and Value then
     StartTransaction;
     StartTransaction;
 end;
 end;
 
 
@@ -522,7 +516,7 @@ begin
   if Active then Active := False;
   if Active then Active := False;
 
 
   if FDatabase = nil then
   if FDatabase = nil then
-    Exception.Create('TIBTransaction.StartTransaction: Database not assigned!');
+    raise EInterBaseError.Create('TIBTransaction.StartTransaction: Database not assigned!');
 
 
   if not Database.Connected then
   if not Database.Connected then
     Database.Open;
     Database.Open;
@@ -540,35 +534,16 @@ end;
 constructor TIBTransaction.Create(AOwner : TComponent);
 constructor TIBTransaction.Create(AOwner : TComponent);
 begin
 begin
   inherited Create(AOwner);
   inherited Create(AOwner);
-
-  FAction := caNone;
-  FActive := False;
-  FAccessMode := amReadWrite;
   FIsolationLevel := ilReadCommitted;
   FIsolationLevel := ilReadCommitted;
-  FLockResolution := lrWait;
-  FTableReservation := trNone;
-  FTransactionHandle := nil;
-  FDatabase := nil;
-
-  FillChar(FStatus, SizeOf(FStatus), #0);
 end;
 end;
 
 
 destructor TIBTransaction.Destroy;
 destructor TIBTransaction.Destroy;
 begin
 begin
+  // This will also do a Rollback, if the transaction is currently active
+  Active := False;
+
   if Database <> nil then
   if Database <> nil then
     Database.Transaction := nil;
     Database.Transaction := nil;
-
-{  // i really can't allow commit of transaction
-  // on destroy...
-}
-{
-  try
-    if Active then
-      Active := False;
-  except
-  end;
-}
-
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
@@ -595,8 +570,11 @@ end;
 procedure TIBQuery.AllocSQLDA(Count : integer);
 procedure TIBQuery.AllocSQLDA(Count : integer);
 begin
 begin
   if FSQLDAAllocated > 0 then
   if FSQLDAAllocated > 0 then
-    FreeMem(FSQLDA, XSQLDA_Length * FSQLDAAllocated);
-  GetMem(FSQLDA, XSQLDA_Length * Count);
+    FreeMem(FSQLDA);
+  GetMem(FSQLDA, XSQLDA_Length(Count));
+  { Zero out the memory block to avoid problems with exceptions within the
+    constructor of this class. }
+  FillChar(FSQLDA^, XSQLDA_Length(Count), 0);
   FSQLDAAllocated := Count;
   FSQLDAAllocated := Count;
   FSQLDA^.Version := sqlda_version1;
   FSQLDA^.Version := sqlda_version1;
   FSQLDA^.SQLN := Count;
   FSQLDA^.SQLN := Count;
@@ -617,7 +595,7 @@ end;
 procedure TIBQuery.FreeStatement;
 procedure TIBQuery.FreeStatement;
 begin
 begin
   if isc_dsql_free_statement(@FStatus, @FStatement, DSQL_Drop) <> 0 then
   if isc_dsql_free_statement(@FStatus, @FStatement, DSQL_Drop) <> 0 then
-    CheckError('TIBQuery.DeallocStatement', FStatus);
+    CheckError('TIBQuery.FreeStatement', FStatus);
   FStatement := nil;
   FStatement := nil;
 end;
 end;
 
 
@@ -632,7 +610,7 @@ begin
   for x := 0 to FSQL.Count - 1 do
   for x := 0 to FSQL.Count - 1 do
     Buf := Buf + FSQL[x] + ' ';
     Buf := Buf + FSQL[x] + ' ';
 
 
-  if isc_dsql_prepare(@FStatus, @tr, @FStatement, 0, @Buf[1], 1, nil) <> 0 then
+  if isc_dsql_prepare(@FStatus, @tr, @FStatement, 0, @Buf[1], Database.Dialect, nil) <> 0 then
     CheckError('TIBQuery.PrepareStatement', FStatus);
     CheckError('TIBQuery.PrepareStatement', FStatus);
 end;
 end;
 
 
@@ -677,14 +655,12 @@ end;
 
 
 procedure TIBQuery.AllocFldBuffers;
 procedure TIBQuery.AllocFldBuffers;
 var
 var
-  Buf: pointer;
   x  : shortint;
   x  : shortint;
 begin
 begin
   {$R-}
   {$R-}
   for x := 0 to FSQLDA^.SQLD - 1 do
   for x := 0 to FSQLDA^.SQLD - 1 do
   begin
   begin
-    Buf := AllocMem(FSQLDA^.SQLVar[x].SQLLen);
-    FSQLDA^.SQLVar[x].SQLData := Buf;
+    FSQLDA^.SQLVar[x].SQLData := AllocMem(FSQLDA^.SQLVar[x].SQLLen);
     FSQLDA^.SQLVar[x].SQLInd  := @FFieldFlag[x];
     FSQLDA^.SQLVar[x].SQLInd  := @FFieldFlag[x];
   end;
   end;
   {$R+}
   {$R+}
@@ -1160,24 +1136,20 @@ end;
 procedure TIBQuery.ExecSQL;
 procedure TIBQuery.ExecSQL;
 begin
 begin
   AllocStatement;
   AllocStatement;
-  PrepareStatement;
-  GetStatementType;
-  Execute;
-  FreeStatement;
+  try
+    PrepareStatement;
+    GetStatementType;
+    Execute;
+  finally
+    FreeStatement;
+  end;
 end;
 end;
 
 
 constructor TIBQuery.Create(AOwner : TComponent);
 constructor TIBQuery.Create(AOwner : TComponent);
 begin
 begin
   inherited Create(AOwner);
   inherited Create(AOwner);
-  FillChar(FFieldFlag, SizeOf(FFieldFlag), #0);
   FSQL := TStringList.Create;
   FSQL := TStringList.Create;
-  FStatement := nil;
   FCurrentRecord := -1;
   FCurrentRecord := -1;
-  FDatabase := nil;
-  FTransaction := nil;
-  FSQLDAAllocated := 0;
-  FLoadingFieldDefs := False;
-  FPrepared := False;
   AllocSQLDA(10);
   AllocSQLDA(10);
 end;
 end;
 
 
@@ -1186,7 +1158,7 @@ begin
   if Active then Close;
   if Active then Close;
   FSQL.Free;
   FSQL.Free;
   inherited Destroy;
   inherited Destroy;
-  FreeMem(FSQLDA, XSQLDA_Length * FSQLDAAllocated);
+  FreeMem(FSQLDA);
 end;
 end;
 
 
 { TIBStoredProc }
 { TIBStoredProc }

+ 5 - 2
fcl/inc/classes.inc

@@ -934,7 +934,7 @@ var
           else
           else
           begin
           begin
             Output.WriteByte(Ord(vaIdent));
             Output.WriteByte(Ord(vaIdent));
-            WriteString(parser.TokenString);
+            WriteString(parser.TokenComponentIdent);
           end;
           end;
           Parser.NextToken;
           Parser.NextToken;
         end;
         end;
@@ -1182,7 +1182,10 @@ end;
 
 
 {
 {
   $Log$
   $Log$
-  Revision 1.6  2001-03-08 19:39:25  michael
+  Revision 1.7  2001-04-10 23:24:51  peter
+    * merged fixes
+
+  Revision 1.6  2001/03/08 19:39:25  michael
   + Merged fixes
   + Merged fixes
 
 
   Revision 1.5  2000/10/15 09:27:48  peter
   Revision 1.5  2000/10/15 09:27:48  peter

+ 15 - 3
fcl/inc/stringl.inc

@@ -833,8 +833,17 @@ begin
     end
     end
   else if NewCapacity<FCapacity then
   else if NewCapacity<FCapacity then
     begin
     begin
-    NewList:=Flist+NewCapacity*SizeOf(TStringItem);
-    FreeMem (NewList, (FCapacity-NewCapacity)*SizeOf(TStringItem));
+    if NewCapacity = 0 then
+    begin
+      FreeMem(FList);
+      FList := nil;
+    end else
+    begin
+      GetMem(NewList, NewCapacity * SizeOf(TStringItem));
+      System.Move(FList^, NewList^, NewCapacity * SizeOf(TStringItem));
+      FreeMem(FList);
+      FList := NewList;
+    end;
     FCapacity:=NewCapacity;
     FCapacity:=NewCapacity;
     end;
     end;
 end;
 end;
@@ -1010,7 +1019,10 @@ end;
 
 
 {
 {
   $Log$
   $Log$
-  Revision 1.7  2001-02-23 22:24:08  michael
+  Revision 1.8  2001-04-10 23:24:51  peter
+    * merged fixes
+
+  Revision 1.7  2001/02/23 22:24:08  michael
   + Fixed sorting of stringslist
   + Fixed sorting of stringslist
 
 
   Revision 1.6  2000/12/03 22:35:09  sg
   Revision 1.6  2000/12/03 22:35:09  sg

+ 112 - 73
fcl/xml/xmlcfg.pp

@@ -3,7 +3,7 @@
     This file is part of the Free Component Library
     This file is part of the Free Component Library
 
 
     Implementation of TXMLConfig class
     Implementation of TXMLConfig class
-    Copyright (c) 1999-2000 by Sebastian Guenther, [email protected]
+    Copyright (c) 1999 - 2001 by Sebastian Guenther, [email protected]
 
 
     See the file COPYING.FPC, included in this distribution,
     See the file COPYING.FPC, included in this distribution,
     for details about the copyright.
     for details about the copyright.
@@ -25,7 +25,7 @@
 unit XMLCfg;
 unit XMLCfg;
 
 
 interface
 interface
-uses DOM, XMLRead, XMLWrite;
+uses Classes, DOM, XMLRead, XMLWrite;
 
 
 type
 type
 
 
@@ -34,12 +34,16 @@ type
    is the name of the value. The path components will be mapped to XML
    is the name of the value. The path components will be mapped to XML
    elements, the name will be an element attribute.}
    elements, the name will be an element attribute.}
 
 
-  TXMLConfig = class
+  TXMLConfig = class(TComponent)
+  private
+    FFilename: String;
+    procedure SetFilename(const AFilename: String);
   protected
   protected
     doc: TXMLDocument;
     doc: TXMLDocument;
-    FileName: String;
+    FModified: Boolean;
+    procedure Loaded; override;
   public
   public
-    constructor Create(const AFileName: String);
+    constructor Create(const AFilename: String);
     destructor Destroy; override;
     destructor Destroy; override;
     procedure Flush;    // Writes the XML file
     procedure Flush;    // Writes the XML file
     function  GetValue(const APath, ADefault: String): String;
     function  GetValue(const APath, ADefault: String): String;
@@ -48,6 +52,9 @@ type
     procedure SetValue(const APath, AValue: String);
     procedure SetValue(const APath, AValue: String);
     procedure SetValue(const APath: String; AValue: Integer);
     procedure SetValue(const APath: String; AValue: Integer);
     procedure SetValue(const APath: String; AValue: Boolean);
     procedure SetValue(const APath: String; AValue: Boolean);
+    property Modified: Boolean read FModified;
+  published
+    property Filename: String read FFilename write SetFilename;
   end;
   end;
 
 
 
 
@@ -58,41 +65,19 @@ implementation
 uses SysUtils;
 uses SysUtils;
 
 
 
 
-constructor TXMLConfig.Create(const AFileName: String);
-var
-  f: File;
-  cfg: TDOMElement;
+constructor TXMLConfig.Create(const AFilename: String);
 begin
 begin
-  FileName := AFileName;
-  Assign(f, AFileName);
-  {$I-}
-  Reset(f, 1);
-  {$I+}
-  if IOResult = 0 then begin
-    try
-      ReadXMLFile(doc, f);
-    except
-      on e: EXMLReadError do
-        WriteLn(StdErr, 'Warning: XML config parsing error: ', e.Message);
-    end;
-    Close(f);
-  end;
-
-  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;
+  inherited Create(nil);
+  SetFilename(AFilename);
 end;
 end;
 
 
 destructor TXMLConfig.Destroy;
 destructor TXMLConfig.Destroy;
 begin
 begin
-  Flush;
   if Assigned(doc) then
   if Assigned(doc) then
+  begin
+    Flush;
     doc.Free;
     doc.Free;
+  end;
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
@@ -100,35 +85,44 @@ procedure TXMLConfig.Flush;
 var
 var
   f: Text;
   f: Text;
 begin
 begin
-  Assign(f, FileName);
-  Rewrite(f);
-  WriteXMLFile(doc, f);
-  Close(f);
+  if Modified then
+  begin
+    AssignFile(f, Filename);
+    Rewrite(f);
+    try
+      WriteXMLFile(doc, f);
+    finally
+      CloseFile(f);
+    end;
+    FModified := False;
+  end;
 end;
 end;
 
 
 function TXMLConfig.GetValue(const APath, ADefault: String): String;
 function TXMLConfig.GetValue(const APath, ADefault: String): String;
 var
 var
-  node, subnode, attr: TDOMNode;
+  Node, Child, Attr: TDOMNode;
   i: Integer;
   i: Integer;
-  name, path: String;
+  NodePath: String;
 begin
 begin
-  node := doc.DocumentElement;
-  path := APath;
-  while True do begin
-    i := Pos('/', path);
-    if i = 0 then break;
-    name := Copy(path, 1, i - 1);
-    path := Copy(path, i + 1, Length(path));
-    subnode := node.FindNode(name);
-    if not Assigned(subnode) then begin
+  Node := doc.DocumentElement;
+  NodePath := APath;
+  while True do
+  begin
+    i := Pos('/', NodePath);
+    if i = 0 then
+      break;
+    Child := Node.FindNode(Copy(NodePath, 1, i - 1));
+    NodePath := Copy(NodePath, i + 1, Length(NodePath));
+    if not Assigned(Child) then
+    begin
       Result := ADefault;
       Result := ADefault;
       exit;
       exit;
     end;
     end;
-    node := subnode;
+    Node := Child;
   end;
   end;
-  attr := node.Attributes.GetNamedItem(path);
-  if Assigned(attr) then
-    Result := attr.NodeValue
+  Attr := Node.Attributes.GetNamedItem(NodePath);
+  if Assigned(Attr) then
+    Result := Attr.NodeValue
   else
   else
     Result := ADefault;
     Result := ADefault;
 end;
 end;
@@ -159,34 +153,34 @@ end;
 
 
 procedure TXMLConfig.SetValue(const APath, AValue: String);
 procedure TXMLConfig.SetValue(const APath, AValue: String);
 var
 var
-  node, subnode, attr: TDOMNode;
+  Node, Child, Attr: TDOMNode;
   i: Integer;
   i: Integer;
-  name, path: String;
+  NodeName, NodePath: String;
 begin
 begin
-  node := doc.DocumentElement;
-  path := APath;
+  Node := Doc.DocumentElement;
+  NodePath := APath;
   while True do
   while True do
   begin
   begin
-    i := Pos('/', path);
+    i := Pos('/', NodePath);
     if i = 0 then
     if i = 0 then
       break;
       break;
-    name := Copy(path, 1, i - 1);
-    path := Copy(path, i + 1, Length(path));
-    subnode := node.FindNode(name);
-    if not Assigned(subnode) then
+    NodeName := Copy(NodePath, 1, i - 1);
+    NodePath := Copy(NodePath, i + 1, Length(NodePath));
+    Child := Node.FindNode(NodeName);
+    if not Assigned(Child) then
     begin
     begin
-      subnode := doc.CreateElement(name);
-      node.AppendChild(subnode);
+      Child := Doc.CreateElement(NodeName);
+      Node.AppendChild(Child);
     end;
     end;
-    node := subnode;
+    Node := Child;
   end;
   end;
-  TDOMElement(node).SetAttribute(path, AValue);
-{  attr := node.Attributes.GetNamedItem(path);
-  if not Assigned(attr) then begin
-    attr := doc.CreateAttribute(path);
-    node.Attributes.SetNamedItem(attr);
+
+  if (not Assigned(TDOMElement(Node).GetAttributeNode(NodePath))) or
+    (TDOMElement(Node)[NodePath] <> AValue) then
+  begin
+    TDOMElement(Node)[NodePath] := AValue;
+    FModified := True;
   end;
   end;
-  attr.NodeValue := AValue;}
 end;
 end;
 
 
 procedure TXMLConfig.SetValue(const APath: String; AValue: Integer);
 procedure TXMLConfig.SetValue(const APath: String; AValue: Integer);
@@ -202,16 +196,61 @@ begin
     SetValue(APath, 'False');
     SetValue(APath, 'False');
 end;
 end;
 
 
+procedure TXMLConfig.Loaded;
+begin
+  inherited Loaded;
+  if Length(Filename) > 0 then
+    SetFilename(Filename);              // Load the XML config file
+end;
 
 
-end.
+procedure TXMLConfig.SetFilename(const AFilename: String);
+var
+  f: File;
+  cfg: TDOMElement;
+begin
+  FFilename := AFilename;
+
+  if csLoading in ComponentState then
+    exit;
+
+  if Assigned(doc) then
+  begin
+    Flush;
+    doc.Free;
+  end;
+
+  AssignFile(f, AFileName);
+  {$I-}
+  Reset(f, 1);
+  {$I+}
+  if IOResult = 0 then
+    try
+      ReadXMLFile(doc, f);
+    finally
+      CloseFile(f);
+    end;
+
+  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;
 
 
 
 
+end.
 {
 {
   $Log$
   $Log$
-  Revision 1.3  2000-07-29 14:52:24  sg
+  Revision 1.4  2001-04-10 23:22:05  peter
+    * merged fixes
+
+  Revision 1.3  2000/07/29 14:52:24  sg
   * Modified the copyright notice to remove ambiguities
   * Modified the copyright notice to remove ambiguities
 
 
   Revision 1.2  2000/07/13 11:33:07  michael
   Revision 1.2  2000/07/13 11:33:07  michael
   + removed logs
   + removed logs
- 
+
 }
 }