unit uDADataStreamer; {----------------------------------------------------------------------------} { Data Abstract Library - Core Library } { } { compiler: Delphi 6 and up, Kylix 3 and up } { platform: Win32, Linux } { } { (c)opyright RemObjects Software. all rights reserved. } { } { Using this code requires a valid license of the Data Abstract } { which can be obtained at http://www.remobjects.com. } {----------------------------------------------------------------------------} {$I DataAbstract.inc} interface uses Classes, SysUtils, uRODL, uROTypes, uROClientIntf, uDAInterfaces, uDAEngine, uDADelta, DataAbstract3_Intf, DataAbstract4_Intf; const AllRows = -1; DEFAULT_BUFFER_SIZE = 262144; //256kb type TDADataForAppend = class public TableSchema: TDADataset; EndDataPosition: Integer; CountOfRecordsPosition: Integer; RecordCount: Integer; MaxRowCount: Integer; RealFields: array of integer; end; type TDAAdapterInitialization = (aiUnknown, aiRead, aiReadFromBeginning, aiWrite); TDAWriteOption = (woRows, woSchema); TDAWriteOptions = set of TDAWriteOption; const AdapterReadModes = [aiRead, aiReadFromBeginning]; AdapterWriteModes = [aiWrite]; type TDADataStreamer = class; TDADatasetOperation = procedure(DataStreamer: TDADataStreamer; const Datasetname: string; const Dataset: IDADataset) of object; TDADeltaOperation = procedure(DataStreamer: TDADataStreamer; const DeltaName: string; const Delta: IDADelta) of object; TDAReadWriteFieldValue = procedure(const aField: TDAField; var Value: Variant) of object; { TDADataStreamer } TDADataStreamer = class(TComponent) private fDeltaNames, fDatasetNames: TStringList; fOnInitialized, fOnFinalized: TNotifyEvent; fOnWriteDataset, fOnReadDataset: TDADatasetOperation; fOnWriteDelta, fOnReadDelta: TDADeltaOperation; fOnWriteFieldValue, fOnReadFieldValue, fOnBeforeFieldValueSerialization: TDAReadWriteFieldValue; fBusy: boolean; fAdapterInitialization: TDAAdapterInitialization; fData: TStream; FBufferSize: cardinal; FSendReducedDelta: boolean; function GetDatasetCount: integer; function GetDatasetNames(Index: integer): string; function GetDeltaCount: integer; function GetDeltaNames(Index: integer): string; procedure ClearReferences; procedure CheckCanRead; procedure CheckCanWrite; function GetDatasetInfoObjects(Index: integer): TObject; function GetDeltaInfoObjects(Index: integer): TObject; procedure SetAdapterInitialization(const Value: TDAAdapterInitialization); protected // To override function DoCreateStream: TStream; virtual; abstract; procedure DoInitialize(Mode: TDAAdapterInitialization); virtual; abstract; procedure DoFinalize; virtual; abstract; function DoWriteDataset(const Source: IDADataset; Options: TDAWriteOptions; MaxRows: integer): integer;overload; function DoWriteDataset(const Source: IDADataset; Options: TDAWriteOptions; MaxRows: integer; ADynFieldNames: array of string): integer; overload; virtual; abstract; procedure DoWriteDelta(const Source: IDADelta); virtual; abstract; procedure DoReadDataset(const DatasetName: string; const Destination: IDADataset; ApplySchema: boolean); virtual; procedure DoReadDelta(const DeltaName: string; const Destination: IDADelta); virtual; abstract; function DoBeginWriteDataset( const Source: IDADataset; const Schema: TDADataset; Options: TDAWriteOptions; MaxRows: integer; ADynFieldNames: array of string): TDADataForAppend; virtual; abstract; function DoWriteDatasetData(const Source: IDADataset; var aDataForAppend: TDADataForAppend; aUnionSourceIndex: Integer = -1): Integer; virtual; abstract; function DoEndWriteDataset(aDataForAppend: TDADataForAppend): Integer;virtual; abstract; // Internal procedure AddingDataset(const aDatasetName: string; InfoObject: TObject = nil); procedure AddingDelta(const aDeltaName: string; InfoObject: TObject = nil); property AdapterInitialization: TDAAdapterInitialization read fAdapterInitialization write SetAdapterInitialization; property Data: TStream read fData; property DatasetInfoObjects[Index: integer]: TObject read GetDatasetInfoObjects; property DeltaInfoObjects[Index: integer]: TObject read GetDeltaInfoObjects; procedure SetBufferSize(const Value: cardinal); virtual; public constructor Create(aOwner: TComponent); override; destructor Destroy; override; // Initialization methods procedure Initialize({var }Stream: TStream; Mode: TDAAdapterInitialization); procedure Finalize; // Writing methods function WriteDataset(const Source: IDADataset; Options: TDAWriteOptions; MaxRows: integer; ADynFieldNames: array of string): integer; overload; function WriteDataset(const Source: IDADataset; Options: TDAWriteOptions; MaxRows: integer = AllRows): integer; overload; function WriteDataset(Stream: TStream; const Source: IDADataset; Options: TDAWriteOptions; MaxRows: integer = AllRows): integer; overload; function WriteDataset(Stream: TStream; const Schema: IDASchema; const Connection: IDAConnection; const DatasetName: string; const ParamNames: array of string; const ParamValues: array of Variant; InitializeStream: boolean = TRUE; FinalizeStream: boolean = TRUE; MaxRows: integer = AllRows): integer; overload; function WriteDataset(Stream: TStream; const Schema: IDASchema; const Connection: IDAConnection; const DatasetName: string; InitializeStream: boolean = TRUE; FinalizeStream: boolean = TRUE; MaxRows: integer = AllRows): integer; overload; procedure WriteDelta(const Source: IDADataset); overload; procedure WriteDelta(const Source: IDADelta); overload; procedure WriteDelta(Stream: TStream; const Source: IDADataset); overload; // Reading methods procedure ReadDelta(const DeltaName: string; const Destination: IDADelta); overload; function ReadDelta(const DeltaName: string): IDADelta; overload; procedure ReadDelta(const Destination: IDADataset); overload; procedure ReadDelta(Stream: TStream; const Destination: IDADelta; DeltaName: string = ''; ReadFromBeginning: boolean = TRUE); overload; procedure ReadDataset(const DatasetName: string; const Destination: IDADataset; ApplySchema: boolean = FALSE; LoadRecords: boolean = TRUE); overload; procedure ReadDataset(Stream: TStream; const Destination: IDADataset; ApplySchema: boolean = FALSE; DatasetName: string = ''; LoadRecords: boolean = TRUE; ReadFromBeginning: boolean = TRUE); overload; function BeginWriteDataset(const Source: IDADataset; const Schema: TDADataset; Options: TDAWriteOptions; MaxRows: integer; ADynFieldNames: array of string): TDADataForAppend; function WriteDatasetData(const Source: IDADataset; var aDataForAppend: TDADataForAppend; aUnionSourceIndex: Integer = -1): Integer; function EndWriteDataset(aDataForAppend: TDADataForAppend): Integer; // Misc function FindDatasetIndex(const aName: string): integer; function FindDeltaIndex(const aName: string): integer; function GetDatasetIndex(const aName: string): integer; function GetDeltaIndex(const aName: string): integer; function GetTargetDataType: TRODataType; virtual; abstract; function HasReducedDelta: Boolean; virtual; property DatasetCount: integer read GetDatasetCount; property DatasetNames[Index: integer]: string read GetDatasetNames; property DeltaCount: integer read GetDeltaCount; property DeltaNames[Index: integer]: string read GetDeltaNames; property TargetDataType: TRODataType read GetTargetDataType; property BufferSize: Cardinal read FBufferSize write SetBufferSize default DEFAULT_BUFFER_SIZE; property SendReducedDelta: boolean read FSendReducedDelta write FSendReducedDelta default False; published property OnInitialized: TNotifyEvent read fOnInitialized write fOnInitialized; property OnFinalized: TNotifyEvent read fOnFinalized write fOnFinalized; property OnReadDataset: TDADatasetOperation read fOnReadDataset write fOnReadDataset; property OnWriteDataset: TDADatasetOperation read fOnWriteDataset write fOnWriteDataset; property OnReadDelta: TDADeltaOperation read fOnReadDelta write fOnReadDelta; property OnWriteDelta: TDADeltaOperation read fOnWriteDelta write fOnWriteDelta; property OnReadFieldValue: TDAReadWriteFieldValue read fOnReadFieldValue write fOnReadFieldValue; property OnWriteFieldValue: TDAReadWriteFieldValue read fOnWriteFieldValue write fOnWriteFieldValue; property OnBeforeFieldValueSerialization: TDAReadWriteFieldValue read fOnBeforeFieldValueSerialization write fOnBeforeFieldValueSerialization; end; TDADataAdapter = TDADataStreamer; implementation { TDADataStreamer } constructor TDADataStreamer.Create(aOwner: TComponent); begin inherited; FBufferSize := DEFAULT_BUFFER_SIZE; fDeltaNames := TStringList.Create; { Doesn't work correctly because it messes up the order in which the server processes the deltas!!!! Fixed the methods that add elements to this down. fDeltaNames.Sorted := TRUE; fDeltaNames.Duplicates := dupError;} fDatasetNames := TStringList.Create; { Fixed for consistency with the above fDatasetNames.Sorted := TRUE; fDatasetNames.Duplicates := dupError;} end; destructor TDADataStreamer.Destroy; begin ClearReferences; FreeAndNIL(fDeltaNames); FreeAndNIL(fDatasetNames); inherited; end; function TDADataStreamer.GetDatasetCount: integer; begin result := fDatasetNames.Count; end; function TDADataStreamer.GetDatasetNames(Index: integer): string; begin result := fDatasetNames[Index]; end; function TDADataStreamer.GetDeltaCount: integer; begin result := fDeltaNames.Count; end; function TDADataStreamer.GetDeltaNames(Index: integer): string; begin result := fDeltaNames[Index]; end; procedure TDADataStreamer.ClearReferences; var i: integer; begin for i := 0 to (fDatasetNames.Count - 1) do if (fDatasetNames.Objects[i] <> nil) then fDatasetNames.Objects[i].Free; fDatasetNames.Clear; for i := 0 to (fDeltaNames.Count - 1) do if (fDeltaNames.Objects[i] <> nil) then fDeltaNames.Objects[i].Free; fDeltaNames.Clear; end; procedure TDADataStreamer.Initialize({var }Stream: TStream; Mode: TDAAdapterInitialization); begin if fBusy then raise EDAException.Create('Cannot Initialize Streamer that is already in use.'); ClearReferences; fAdapterInitialization := aiUnknown; fData := nil; if Mode = aiUnknown then raise Exception.Create('Invalid DataStreamer initialization parameter.'); if (Stream = nil) then raise Exception.Create('Stream parameter must assigned.'); if (Mode in AdapterReadModes) and (Stream.Size = 0) then raise Exception.Create('Stream may not me empty for Read mode.'); try if (Mode = aiReadFromBeginning) then Stream.Position := 0; // Sets internal references fData := Stream; AdapterInitialization := Mode; fDatasetNames.Clear; DoInitialize(Mode); // Calls descendant's implementation if Assigned(fOnInitialized) then fOnInitialized(Self); fBusy := true; except ClearReferences; raise; end; end; procedure TDADataStreamer.Finalize; begin DoFinalize; // Calls descendant's implementation if Assigned(fOnFinalized) then fOnFinalized(Self); fBusy := false; end; procedure TDADataStreamer.CheckCanRead; begin if not (AdapterInitialization in AdapterReadModes) then raise Exception.Create('Adapter was not initialized for this operation'); end; procedure TDADataStreamer.CheckCanWrite; begin if not (AdapterInitialization in AdapterWriteModes) then raise Exception.Create('Adapter was not initialized for this operation'); end; procedure TDADataStreamer.ReadDelta(const DeltaName: string; const Destination: IDADelta); begin CheckCanRead; if Assigned(fOnReadDelta) then fOnReadDelta(Self, DeltaName, Destination); DoReadDelta(DeltaName, Destination); // Calls descendant's implementation end; function TDADataStreamer.WriteDataset(const Source: IDADataset; Options: TDAWriteOptions; MaxRows: integer = AllRows): integer; begin Result := WriteDataset(Source, Options, MaxRows, []); end; procedure TDADataStreamer.WriteDelta(const Source: IDADataset); var deltaowner: IDADeltaOwner; delta: IDADelta; begin if not Supports(Source, IDADeltaOwner, deltaowner) then raise Exception.Create('Source does not have a delta') else delta := deltaowner.GetDelta; CheckCanWrite; if Assigned(fOnWriteDelta) then fOnWriteDelta(Self, delta.LogicalName, delta); DoWriteDelta(delta); // Calls descendant's implementation end; procedure TDADataStreamer.AddingDataset(const aDatasetName: string; InfoObject: TObject = nil); var i : integer; begin i := fDatasetNames.IndexOf(aDatasetName); if (i>=0) then raise Exception.Create('A dataset called "'+aDatasetName+'" is already present') else fDatasetNames.AddObject(aDatasetName, InfoObject) end; procedure TDADataStreamer.AddingDelta(const aDeltaName: string; InfoObject: TObject = nil); var i : integer; begin i := fDeltaNames.IndexOf(aDeltaName); if (i>=0) then raise Exception.Create('A delta called "'+aDeltaName+'" is already present') else fDeltaNames.AddObject(aDeltaName, InfoObject) end; function TDADataStreamer.GetDatasetInfoObjects(Index: integer): TObject; begin result := fDatasetNames.Objects[Index] end; function TDADataStreamer.GetDeltaInfoObjects(Index: integer): TObject; begin result := fDeltaNames.Objects[Index] end; function TDADataStreamer.GetDatasetIndex(const aName: string): integer; begin result := FindDatasetIndex(aName); if (result = -1) then raise Exception.Create('Unknown dataset ' + aName); end; function TDADataStreamer.GetDeltaIndex(const aName: string): integer; begin result := FindDeltaIndex(aName); if (result = -1) then raise Exception.Create('Unknown delta ' + aName); end; procedure TDADataStreamer.ReadDataset(const DatasetName: string; const Destination: IDADataset; ApplySchema: boolean = FALSE; LoadRecords: boolean = TRUE); var editable: IDAEditableDataset; begin CheckCanRead; if Assigned(fOnReadDataset) then fOnReadDataset(Self, DatasetName, Destination); editable := Destination as IDAEditableDataset; Destination.DisableControls; try editable.DisableEventHandlers; try if ApplySchema then begin if Destination.Active then Destination.Close; DoReadDataset(DatasetName, Destination, TRUE); end; if LoadRecords then DoReadDataset(DatasetName, Destination, FALSE); finally editable.EnableEventHandlers; end; if LoadRecords then editable.Dataset.Resync([]); finally Destination.EnableControls; end; end; procedure TDADataStreamer.ReadDataset(Stream: TStream; const Destination: IDADataset; ApplySchema: boolean = FALSE; DatasetName: string = ''; LoadRecords: boolean = TRUE; ReadFromBeginning: boolean = TRUE); var nme: string; begin if ReadFromBeginning then Initialize(Stream, aiReadFromBeginning) else Initialize(Stream, aiRead); if (DatasetName = '') then nme := DatasetNames[0] else nme := DatasetName; try ReadDataset(nme, Destination, ApplySchema, LoadRecords); finally Finalize; end; end; procedure TDADataStreamer.ReadDelta(Stream: TStream; const Destination: IDADelta; DeltaName: string = ''; ReadFromBeginning: boolean = TRUE); var nme: string; begin if ReadFromBeginning then Initialize(Stream, aiReadFromBeginning) else Initialize(Stream, aiRead); try if (DeltaName <> '') then nme := DeltaName else nme := DeltaNames[0]; ReadDelta(nme, Destination); finally Finalize; end; end; function TDADataStreamer.WriteDataset(Stream: TStream; const Source: IDADataset; Options: TDAWriteOptions; MaxRows: integer): integer; begin Initialize(Stream, aiWrite); try result := WriteDataset(Source, Options, MaxRows); finally Finalize; end; end; procedure TDADataStreamer.WriteDelta(Stream: TStream; const Source: IDADataset); begin Initialize(Stream, aiWrite); try WriteDelta(Source); finally Finalize; end; end; procedure TDADataStreamer.WriteDelta(const Source: IDADelta); begin DoWriteDelta(Source); end; procedure TDADataStreamer.ReadDelta(const Destination: IDADataset); var deltaowner: IDADeltaOwner; delta: IDADelta; begin if not Supports(Destination, IDADeltaOwner, deltaowner) then raise Exception.Create('Destination does not have a delta') else delta := deltaowner.GetDelta; ReadDelta(delta.LogicalName, delta); end; function TDADataStreamer.WriteDataset(Stream: TStream; const Schema: IDASchema; const Connection: IDAConnection; const DatasetName: string; const ParamNames: array of string; const ParamValues: array of Variant; InitializeStream: boolean = TRUE; FinalizeStream: boolean = TRUE; MaxRows: integer = AllRows): integer; var ds: IDADataset; begin if InitializeStream then Initialize(Stream, aiWrite); ds := Schema.NewDataset(Connection, DatasetName, ParamNames, ParamValues, TRUE); result := WriteDataset(ds, [woRows], MaxRows); if FinalizeStream then Finalize; end; function TDADataStreamer.WriteDataset(Stream: TStream; const Schema: IDASchema; const Connection: IDAConnection; const DatasetName: string; InitializeStream, FinalizeStream: boolean; MaxRows: integer): integer; begin result := WriteDataset(Stream, Schema, Connection, DatasetName, [], [], InitializeStream, FinalizeStream, MaxRows); end; procedure TDADataStreamer.SetAdapterInitialization( const Value: TDAAdapterInitialization); begin fAdapterInitialization := Value; end; function TDADataStreamer.ReadDelta(const DeltaName: string): IDADelta; begin result := NewDelta(DeltaName); ReadDelta(DeltaName, Result); end; function TDADataStreamer.FindDatasetIndex(const aName: string): integer; begin result := fDatasetNames.IndexOf(aName); end; function TDADataStreamer.FindDeltaIndex(const aName: string): integer; begin result := fDeltaNames.IndexOf(aName); end; procedure TDADataStreamer.DoReadDataset(const DatasetName: string; const Destination: IDADataset; ApplySchema: boolean); begin if Destination.Active and ApplySchema then raise Exception.Create('Cannot apply a schema if the destination is active'); end; procedure TDADataStreamer.SetBufferSize(const Value: cardinal); begin FBufferSize := Value; end; function TDADataStreamer.HasReducedDelta: Boolean; begin Result:=False; end; function TDADataStreamer.DoWriteDataset(const Source: IDADataset; Options: TDAWriteOptions; MaxRows: integer): integer; begin Result:= DoWriteDataset(Source, Options, MaxRows, []); end; function TDADataStreamer.WriteDataset(const Source: IDADataset; Options: TDAWriteOptions; MaxRows: integer; ADynFieldNames: array of string): integer; begin CheckCanWrite; if Assigned(fOnWriteDataset) then fOnWriteDataset(Self, Source.LogicalName, Source); result := DoWriteDataset(Source, Options, MaxRows, ADynFieldNames); // Calls descendant's implementation end; function TDADataStreamer.BeginWriteDataset(const Source: IDADataset; const Schema: TDADataset; Options: TDAWriteOptions; MaxRows: integer; ADynFieldNames: array of string): TDADataForAppend; begin result := DoBeginWriteDataset(Source, Schema, Options, MaxRows, ADynFieldNames); // Calls descendant's implementation end; function TDADataStreamer.WriteDatasetData(const Source: IDADataset; var aDataForAppend: TDADataForAppend; aUnionSourceIndex: Integer = -1): Integer; begin CheckCanWrite; if Assigned(fOnWriteDataset) then fOnWriteDataset(Self, Source.LogicalName, Source); result := DoWriteDatasetData(Source, aDataForAppend, aUnionSourceIndex); // Calls descendant's implementation end; function TDADataStreamer.EndWriteDataset(aDataForAppend: TDADataForAppend): Integer; begin result := DoEndWriteDataset(aDataForAppend); // Calls descendant's implementation end; end.