From f517d21507d66cef0b6a1144f41c8359982e9c2e Mon Sep 17 00:00:00 2001
From: lacak <lacak@idefix.freepascal.org>
Date: Tue, 30 Mar 2021 09:37:30 +0000
Subject: [PATCH] fcl-db: Introduce TObjectField. Only essential parts (added
 new object properties according to Delphi documentation). Some code taken
 from FreeCLX project (https://sourceforge.net/projects/freeclx/)

git-svn-id: trunk@49082 -
---
 packages/fcl-db/src/base/db.pas     |  42 ++++++++-
 packages/fcl-db/src/base/fields.inc | 128 +++++++++++++++++++++++++++-
 2 files changed, 162 insertions(+), 8 deletions(-)

diff --git a/packages/fcl-db/src/base/db.pas b/packages/fcl-db/src/base/db.pas
index 6997da5f21..1c6fe4e266 100644
--- a/packages/fcl-db/src/base/db.pas
+++ b/packages/fcl-db/src/base/db.pas
@@ -75,6 +75,7 @@ type
   TDataSource = Class;
   TDataLink = Class;
   TDBTransaction = Class;
+  TObjectField = class;
 
 { Exception classes }
 
@@ -169,14 +170,19 @@ type
     FCodePage : TSystemCodePage;
     FDataType : TFieldType;
     FFieldNo : Longint;
+    FChildDefs : TFieldDefs;
     FInternalCalcField : Boolean;
     FPrecision : Longint;
     FRequired : Boolean;
     FSize : Integer;
     function GetCharSize: Word;
+    function GetChildDefs: TFieldDefs;
     Function GetFieldClass : TFieldClass;
+    function GetParentDef: TFieldDef;
+    function GetSize: Integer;
     procedure SetAttributes(AValue: TFieldAttributes);
     procedure SetDataType(AValue: TFieldType);
+    procedure SetChildDefs(AValue: TFieldDefs);
     procedure SetPrecision(const AValue: Longint);
     procedure SetSize(const AValue: Integer);
     procedure SetRequired(const AValue: Boolean);
@@ -186,19 +192,23 @@ type
       ADataType: TFieldType; ASize: Integer; ARequired: Boolean; AFieldNo: Longint;
       ACodePage: TSystemCodePage = CP_ACP); overload;
     destructor Destroy; override;
+    function AddChild: TFieldDef;
     procedure Assign(APersistent: TPersistent); override;
-    function CreateField(AOwner: TComponent): TField;
+    function CreateField(AOwner: TComponent; ParentField: TObjectField = nil;  const FieldName: string = ''; CreateChildren: Boolean = True): TField;
+    function HasChildDefs: Boolean;
     property FieldClass: TFieldClass read GetFieldClass;
     property FieldNo: Longint read FFieldNo;
     property CharSize: Word read GetCharSize;
     property InternalCalcField: Boolean read FInternalCalcField write FInternalCalcField;
+    property ParentDef: TFieldDef read GetParentDef;
     property Required: Boolean read FRequired write SetRequired;
     Property Codepage : TSystemCodePage Read FCodePage;
   Published
     property Attributes: TFieldAttributes read FAttributes write SetAttributes default [];
     property DataType: TFieldType read FDataType write SetDataType;
+    property ChildDefs: TFieldDefs read GetChildDefs write SetChildDefs stored HasChildDefs;
     property Precision: Longint read FPrecision write SetPrecision default 0;
-    property Size: Integer read FSize write SetSize default 0;
+    property Size: Integer read GetSize write SetSize default 0;
   end;
   TFieldDefClass = Class of TFieldDef;
 
@@ -206,13 +216,14 @@ type
 
   TFieldDefs = class(TDefCollection)
   private
+    FParentDef: TFieldDef;
     FHiddenFields : Boolean;
     function GetItem(Index: Longint): TFieldDef;
     procedure SetItem(Index: Longint; const AValue: TFieldDef);
   Protected
     Class Function FieldDefClass : TFieldDefClass; virtual;
   public
-    constructor Create(ADataSet: TDataSet);
+    constructor Create(AOwner: TPersistent);
 //    destructor Destroy; override;
     Function Add(const AName: string; ADataType: TFieldType; ASize, APrecision: Integer; ARequired, AReadOnly: Boolean; AFieldNo : Integer; ACodePage:TSystemCodePage) : TFieldDef; overload;
     Function Add(const AName: string; ADataType: TFieldType; ASize: Word; ARequired: Boolean; AFieldNo : Integer) : TFieldDef; overload;
@@ -228,6 +239,7 @@ type
     Function MakeNameUnique(const AName : String) : string; virtual;
     Property HiddenFields : Boolean Read FHiddenFields Write FHiddenFields;
     property Items[Index: Longint]: TFieldDef read GetItem write SetItem; default;
+    property ParentDef: TFieldDef read FParentDef;
   end;
   TFieldDefsClass = Class of TFieldDefs;
 
@@ -300,6 +312,8 @@ type
     FOnSetText: TFieldSetTextEvent;
     FOnValidate: TFieldNotifyEvent;
     FOrigin : String;
+    FParentField: TObjectField;
+    FProviderFlags : TProviderFlags;
     FReadOnly : Boolean;
     FRequired : Boolean;
     FSize : integer;
@@ -307,7 +321,6 @@ type
     FValueBuffer : Pointer;
     FValidating : Boolean;
     FVisible : Boolean;
-    FProviderFlags : TProviderFlags;
     function GetIndex : longint;
     function GetLookup: Boolean;
     procedure SetAlignment(const AValue: TAlignMent);
@@ -385,6 +398,7 @@ type
     procedure SetNewValue(const AValue: Variant);
     procedure SetSize(AValue: Integer); virtual;
     procedure SetParentComponent(AParent: TComponent); override;
+    procedure SetParentField(AField: TObjectField); virtual;
     procedure SetText(const AValue: string); virtual;
     procedure SetVarValue(const AValue: Variant); virtual;
   public
@@ -461,6 +475,7 @@ type
     property LookupResultField: string read FLookupResultField write FLookupResultField;
     property Lookup: Boolean read GetLookup write SetLookup stored false; deprecated;
     property Origin: string read FOrigin write FOrigin;
+    property ParentField: TObjectField read FParentField write SetParentField;
     property ProviderFlags : TProviderFlags read FProviderFlags write FProviderFlags;
     property ReadOnly: Boolean read FReadOnly write SetReadOnly;
     property Required: Boolean read FRequired write FRequired;
@@ -1092,6 +1107,25 @@ type
     property AsGuid: TGUID read GetAsGuid write SetAsGuid;
   end;
 
+{ TObjectField }
+
+  TObjectField = class(TField)
+  private
+    FFieldFields: TFields;
+  protected
+    function GetAsVariant: Variant; override;
+    function GetFieldCount: Integer;
+    function GetFields: TFields; virtual;
+    function GetFieldValue(AIndex: Integer): Variant; virtual;
+    procedure SetFieldValue(AIndex: Integer; const AValue: Variant); virtual;
+    procedure SetParentField(AField: TObjectField); override;
+    procedure SetVarValue(const AValue: Variant); override;
+  public
+    property FieldCount: Integer read GetFieldCount;
+    property Fields: TFields read GetFields;
+    property FieldValues[AIndex: Integer]: Variant read GetFieldValue  write SetFieldValue; default;
+  end;
+
 { TIndexDef }
 
   TIndexDefs = class;
diff --git a/packages/fcl-db/src/base/fields.inc b/packages/fcl-db/src/base/fields.inc
index c6c270ce09..cb123815ce 100644
--- a/packages/fcl-db/src/base/fields.inc
+++ b/packages/fcl-db/src/base/fields.inc
@@ -63,7 +63,35 @@ end;
 destructor TFieldDef.Destroy;
 
 begin
-  Inherited destroy;
+  FChildDefs.Free;
+  Inherited Destroy;
+end;
+
+function TFieldDef.AddChild: TFieldDef;
+begin
+  Result := ChildDefs.AddFieldDef;
+end;
+
+function TFieldDef.GetChildDefs: TFieldDefs;
+begin
+  if FChildDefs = nil then
+    FChildDefs := TFieldDefs.Create(Self);
+  Result := FChildDefs;
+end;
+
+procedure TFieldDef.SetChildDefs(AValue: TFieldDefs);
+begin
+  ChildDefs.Assign(AValue);
+end;
+
+function TFieldDef.HasChildDefs: Boolean;
+begin
+  Result := Assigned(FChildDefs) and (FChildDefs.Count > 0);
+end;
+
+function TFieldDef.GetParentDef: TFieldDef;
+begin
+  Result := TFieldDefs(Collection).ParentDef;
 end;
 
 procedure TFieldDef.Assign(APersistent: TPersistent);
@@ -89,9 +117,10 @@ begin
     inherited Assign(APersistent);
 end;
 
-function TFieldDef.CreateField(AOwner: TComponent): TField;
+function TFieldDef.CreateField(AOwner: TComponent; ParentField: TObjectField = nil; const FieldName: string = ''; CreateChildren: Boolean = True): TField;
 
 var TheField : TFieldClass;
+    i: integer;
 
 begin
 {$ifdef dsdebug}
@@ -125,6 +154,12 @@ begin
       TBCDField(Result).Precision := FPrecision
     else if (Result is TFmtBCDField) then
       TFmtBCDField(Result).Precision := FPrecision;
+
+    if CreateChildren and HasChildDefs then
+    begin
+      for i := 0 to ChildDefs.Count - 1 do
+        ChildDefs[i].CreateField(nil, TObjectField(Result), '');
+    end;
   except
     Result.Free;
     Raise;
@@ -149,8 +184,17 @@ begin
   Changed(False);
 end;
 
+function TFieldDef.GetSize: Integer;
+begin
+  if HasChildDefs and (FSize = 0) then
+    Result := FChildDefs.Count
+  else
+    Result := FSize;
+end;
+
 procedure TFieldDef.SetSize(const AValue: Integer);
 begin
+  if HasChildDefs and (DataType <> ftArray) then Exit;
   FSize := AValue;
   Changed(False);
 end;
@@ -249,9 +293,17 @@ begin
   Result:=TFieldDef;
 end;
 
-constructor TFieldDefs.Create(ADataSet: TDataSet);
+constructor TFieldDefs.Create(AOwner: TPersistent);
+var ADataSet: TDataSet;
 begin
-  Inherited Create(ADataset, Owner, FieldDefClass);
+  if AOwner is TFieldDef then
+  begin
+    FParentDef := TFieldDef(AOwner);
+    ADataSet := TFieldDefs(FParentDef.Collection).DataSet;
+  end
+  else
+    ADataSet := AOwner as TDataSet;
+  Inherited Create(ADataset, AOwner, FieldDefClass);
 end;
 
 function TFieldDefs.Add(const AName: string; ADataType: TFieldType; ASize, APrecision: Integer;
@@ -1100,6 +1152,25 @@ begin
   FieldKind := ValueToLookupMap[AValue];
 end;
 
+procedure TField.SetParentField(AField: TObjectField);
+begin
+  if AField <> FParentField then
+  begin
+    if FDataSet <> nil then FDataSet.CheckInactive;
+    if AField <> nil then
+    begin
+      if AField.DataSet <> nil then AField.DataSet.CheckInactive;
+      AField.Fields.CheckFieldName(FFieldName);
+      AField.Fields.Add(Self);
+      if FDataSet <> nil then FDataSet.Fields.Remove(Self);
+      FDataSet := AField.DataSet;
+    end
+    else if FDataSet <> nil then FDataSet.Fields.Add(Self);
+    if FParentField <> nil then FParentField.Fields.Remove(Self);
+    FParentField := AField;
+  end;
+end;
+
 procedure TField.SetReadOnly(const AValue: Boolean);
 begin
   if (FReadOnly<>AValue) then
@@ -3663,6 +3734,55 @@ begin
   SetData(@aValue);
 end;
 
+{ TObjectField }
+
+function TObjectField.GetFieldCount: Integer;
+begin
+  Result := Fields.Count;
+end;
+
+function TObjectField.GetFields: TFields;
+begin
+  Result := FFieldFields;
+end;
+
+function TObjectField.GetFieldValue(AIndex: Integer): Variant;
+begin
+  Result := FFieldFields[AIndex].Value;
+end;
+
+procedure TObjectField.SetFieldValue(AIndex: Integer; const AValue: Variant);
+begin
+  FFieldFields[AIndex].Value := AValue;
+end;
+
+procedure TObjectField.SetParentField(AField: TObjectField);
+begin
+  inherited SetParentField(AField);
+end;
+
+function TObjectField.GetAsVariant: Variant;
+var I: integer;
+begin
+  if IsNull then
+    Result := Null
+  else
+  begin
+    Result := VarArrayCreate([0, FieldCount - 1], varVariant);
+    for I := 0 to FieldCount - 1 do
+      Result[I] := GetFieldValue(I);
+  end;
+end;
+
+procedure TObjectField.SetVarValue(const AValue: Variant);
+var N,I: integer;
+begin
+  N := VarArrayHighBound(AValue, 1) + 1;
+  if N > Size then N := Size;
+  for I := 0 to N - 1  do
+    SetFieldValue(I, AValue[I]);
+end;
+
 { TFieldsEnumerator }
 
 function TFieldsEnumerator.GetCurrent: TField;