unit ex_rx_datapacket; {$mode objfpc}{$H+} interface uses Classes, SysUtils,db; type TRowStateValue = (rsvOriginal, rsvDeleted, rsvInserted, rsvUpdated, rsvDetailUpdates); TRowState = set of TRowStateValue; type TRxDataPacketFormat = (dfBinary,dfXML,dfXMLUTF8,dfAny); type { TRxDatapacketReader } TRxDatapacketReaderClass = class of TRxDatapacketReader; TRxDatapacketReader = class(TObject) FStream : TStream; protected class function RowStateToByte(const ARowState : TRowState) : byte; class function ByteToRowState(const AByte : Byte) : TRowState; public constructor create(AStream : TStream); virtual; // Load a dataset from stream: // Load the field-definitions from a stream. procedure LoadFieldDefs(AFieldDefs : TFieldDefs); virtual; abstract; // Is called before the records are loaded procedure InitLoadRecords; virtual; abstract; // Return the RowState of the current record, and the order of the update function GetRecordRowState(out AUpdOrder : Integer) : TRowState; virtual; abstract; // Returns if there is at least one more record available in the stream function GetCurrentRecord : boolean; virtual; abstract; // Store a record from stream in the current record-buffer procedure RestoreRecord(ADataset : TDataset); virtual; abstract; // Move the stream to the next record procedure GotoNextRecord; virtual; abstract; // Store a dataset to stream: // Save the field-definitions to a stream. procedure StoreFieldDefs(AFieldDefs : TFieldDefs); virtual; abstract; // Save a record from the current record-buffer to the stream procedure StoreRecord(ADataset : TDataset; ARowState : TRowState; AUpdOrder : integer = 0); virtual; abstract; // Is called after all records are stored procedure FinalizeStoreRecords; virtual; abstract; // Checks if the provided stream is of the right format for this class class function RecognizeStream(AStream : TStream) : boolean; virtual; abstract; property Stream: TStream read FStream; end; type TRxDatapacketReaderRegistration = record ReaderClass : TRxDatapacketReaderClass; Format : TRxDatapacketFormat; end; function GetRegisterDatapacketReader(AStream : TStream; AFormat : TRxDatapacketFormat; var ADataReaderClass : TRxDatapacketReaderRegistration) : boolean; procedure RegisterDatapacketReader(ADatapacketReaderClass : TRxDatapacketReaderClass; AFormat : TRxDatapacketFormat); implementation var RxRegisteredDatapacketReaders : Array of TRxDatapacketReaderRegistration; function GetRegisterDatapacketReader(AStream: TStream; AFormat: TRxDatapacketFormat; var ADataReaderClass: TRxDatapacketReaderRegistration): boolean; var i : integer; begin Result := False; for i := 0 to length(RxRegisteredDatapacketReaders)-1 do if ((AFormat=dfAny) or (AFormat=RxRegisteredDatapacketReaders[i].Format)) then begin if (AStream <> nil) then AStream.Seek(0,soFromBeginning); // ensure at start of stream to check value if (AStream=nil) or (RxRegisteredDatapacketReaders[i].ReaderClass.RecognizeStream(AStream)) then begin ADataReaderClass := RxRegisteredDatapacketReaders[i]; Result := True; if (AStream <> nil) then AStream.Seek(0,soFromBeginning); break; end; end; end; procedure RegisterDatapacketReader( ADatapacketReaderClass: TRxDatapacketReaderClass; AFormat: TRxDatapacketFormat ); begin setlength(RxRegisteredDatapacketReaders,length(RxRegisteredDatapacketReaders)+1); with RxRegisteredDatapacketReaders[length(RxRegisteredDatapacketReaders)-1] do begin Readerclass := ADatapacketReaderClass; Format := AFormat; end; end; { TRxDatapacketReader } class function TRxDatapacketReader.RowStateToByte(const ARowState: TRowState ): byte; var RowStateInt : Byte; begin RowStateInt:=0; if rsvOriginal in ARowState then RowStateInt := RowStateInt+1; if rsvDeleted in ARowState then RowStateInt := RowStateInt+2; if rsvInserted in ARowState then RowStateInt := RowStateInt+4; if rsvUpdated in ARowState then RowStateInt := RowStateInt+8; Result := RowStateInt; end; class function TRxDatapacketReader.ByteToRowState(const AByte: Byte ): TRowState; begin result := []; if (AByte and 1)=1 then Result := Result+[rsvOriginal]; if (AByte and 2)=2 then Result := Result+[rsvDeleted]; if (AByte and 4)=4 then Result := Result+[rsvInserted]; if (AByte and 8)=8 then Result := Result+[rsvUpdated]; end; constructor TRxDatapacketReader.create(AStream: TStream); begin FStream := AStream; end; initialization setlength(RxRegisteredDatapacketReaders,0); finalization setlength(RxRegisteredDatapacketReaders,0); end.