home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2001 September
/
Chip_2001-09_cd1.bin
/
zkuste
/
delphi
/
kolekce
/
d6
/
RX275D6.ZIP
/
Units
/
RxMemDS.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
2001-06-24
|
44KB
|
1,540 lines
{*******************************************************}
{ }
{ Delphi VCL Extensions (RX) }
{ }
{ Copyright (c) 1998 Master-Bank }
{ }
{*******************************************************}
unit RxMemDS;
{$I RX.INC}
interface
{$IFDEF RX_D3}
uses Windows, SysUtils, Classes, Controls, DB, DBUtils, Variants;
{ TRxMemoryData }
type
TMemBlobData = string;
TMemBlobArray = array[0..0] of TMemBlobData;
PMemBlobArray = ^TMemBlobArray;
TMemoryRecord = class;
TLoadMode = (lmCopy, lmAppend);
TCompareRecords = function (Item1, Item2: TMemoryRecord): Integer of object;
TRxMemoryData = class(TDataSet)
private
FRecordPos: Integer;
FRecordSize: Integer;
FBookmarkOfs: Integer;
FBlobOfs: Integer;
FRecBufSize: Integer;
FOffsets: PWordArray;
FLastID: Integer;
FAutoInc: Longint;
FActive: Boolean;
FRecords: TList;
FIndexList: TList;
FCaseInsensitiveSort: Boolean;
FDescendingSort: Boolean;
function AddRecord: TMemoryRecord;
function InsertRecord(Index: Integer): TMemoryRecord;
function FindRecordID(ID: Integer): TMemoryRecord;
procedure CreateIndexList(const FieldNames: string);
procedure FreeIndexList;
procedure QuickSort(L, R: Integer; Compare: TCompareRecords);
procedure Sort;
function CalcRecordSize: Integer;
function FindFieldData(Buffer: Pointer; Field: TField): Pointer;
function GetMemoryRecord(Index: Integer): TMemoryRecord;
function GetCapacity: Integer;
function RecordFilter: Boolean;
procedure SetCapacity(Value: Integer);
procedure ClearRecords;
procedure InitBufferPointers(GetProps: Boolean);
protected
procedure AssignMemoryRecord(Rec: TMemoryRecord; Buffer: PChar);
function GetActiveRecBuf(var RecBuf: PChar): Boolean; virtual;
procedure InitFieldDefsFromFields;
procedure RecordToBuffer(Rec: TMemoryRecord; Buffer: PChar);
procedure SetMemoryRecordData(Buffer: PChar; Pos: Integer); virtual;
procedure SetAutoIncFields(Buffer: PChar); virtual;
function CompareRecords(Item1, Item2: TMemoryRecord): Integer; virtual;
function GetBlobData(Field: TField; Buffer: PChar): TMemBlobData;
procedure SetBlobData(Field: TField; Buffer: PChar; Value: TMemBlobData);
function AllocRecordBuffer: PChar; override;
procedure FreeRecordBuffer(var Buffer: PChar); override;
{$IFNDEF RX_D5}
function BCDToCurr(BCD: Pointer; var Curr: Currency): Boolean; override;
function CurrToBCD(const Curr: Currency; BCD: Pointer; Precision,
Decimals: Integer): Boolean; override;
{$ENDIF}
procedure InternalInitRecord(Buffer: PChar); override;
procedure ClearCalcFields(Buffer: PChar); override;
function GetRecord(Buffer: PChar; GetMode: TGetMode;
DoCheck: Boolean): TGetResult; override;
function GetRecordSize: Word; override;
procedure SetFiltered(Value: Boolean); override;
procedure SetOnFilterRecord(const Value: TFilterRecordEvent); override;
procedure SetFieldData(Field: TField; Buffer: Pointer); override;
procedure CloseBlob(Field: TField); override;
procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
procedure InternalGotoBookmark(Bookmark: TBookmark); override;
procedure InternalSetToRecord(Buffer: PChar); override;
procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
function GetIsIndexField(Field: TField): Boolean; override;
procedure InternalFirst; override;
procedure InternalLast; override;
procedure InitRecord(Buffer: PChar); override;
procedure InternalAddRecord(Buffer: Pointer; Append: Boolean); override;
procedure InternalDelete; override;
procedure InternalPost; override;
procedure InternalClose; override;
procedure InternalHandleException; override;
procedure InternalInitFieldDefs; override;
procedure InternalOpen; override;
procedure OpenCursor(InfoQuery: Boolean); override;
function IsCursorOpen: Boolean; override;
function GetRecordCount: Integer; override;
function GetRecNo: Integer; override;
procedure SetRecNo(Value: Integer); override;
property Records[Index: Integer]: TMemoryRecord read GetMemoryRecord;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function BookmarkValid(Bookmark: TBookmark): Boolean; override;
function CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer; override;
function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override;
function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
function GetCurrentRecord(Buffer: PChar): Boolean; override;
function IsSequenced: Boolean; override;
function Locate(const KeyFields: string; const KeyValues: Variant;
Options: TLocateOptions): Boolean; override;
procedure SortOnFields(const FieldNames: string;
{$IFDEF RX_D4}
CaseInsensitive: Boolean = True; Descending: Boolean = False);
{$ELSE}
CaseInsensitive, Descending: Boolean);
{$ENDIF}
procedure EmptyTable;
procedure CopyStructure(Source: TDataSet);
function LoadFromDataSet(Source: TDataSet; RecordCount: Integer;
Mode: TLoadMode): Integer;
function SaveToDataSet(Dest: TDataSet; RecordCount: Integer): Integer;
published
property Capacity: Integer read GetCapacity write SetCapacity default 0;
property Active;
property AutoCalcFields;
property Filtered;
{$IFDEF RX_D4}
property FieldDefs;
property ObjectView default False;
{$ENDIF}
property BeforeOpen;
property AfterOpen;
property BeforeClose;
property AfterClose;
property BeforeInsert;
property AfterInsert;
property BeforeEdit;
property AfterEdit;
property BeforePost;
property AfterPost;
property BeforeCancel;
property AfterCancel;
property BeforeDelete;
property AfterDelete;
property BeforeScroll;
property AfterScroll;
property OnCalcFields;
property OnDeleteError;
property OnEditError;
property OnFilterRecord;
property OnNewRecord;
property OnPostError;
end;
{ TMemBlobStream }
TMemBlobStream = class(TStream)
private
FField: TBlobField;
FDataSet: TRxMemoryData;
FBuffer: PChar;
FMode: TBlobStreamMode;
FOpened: Boolean;
FModified: Boolean;
FPosition: Longint;
FCached: Boolean;
function GetBlobSize: Longint;
function GetBlobFromRecord(Field: TField): TMemBlobData;
public
constructor Create(Field: TBlobField; Mode: TBlobStreamMode);
destructor Destroy; override;
function Read(var Buffer; Count: Longint): Longint; override;
function Write(const Buffer; Count: Longint): Longint; override;
function Seek(Offset: Longint; Origin: Word): Longint; override;
procedure Truncate;
end;
{ TMemoryRecord }
TMemoryRecord = class(TPersistent)
private
FMemoryData: TRxMemoryData;
FID: Integer;
FData: Pointer;
FBlobs: Pointer;
function GetIndex: Integer;
procedure SetMemoryData(Value: TRxMemoryData; UpdateParent: Boolean);
protected
procedure SetIndex(Value: Integer); virtual;
public
constructor Create(MemoryData: TRxMemoryData); virtual;
constructor CreateEx(MemoryData: TRxMemoryData; UpdateParent: Boolean); virtual;
destructor Destroy; override;
property MemoryData: TRxMemoryData read FMemoryData;
property ID: Integer read FID write FID;
property Index: Integer read GetIndex write SetIndex;
property Data: Pointer read FData;
end;
{$ENDIF RX_D3}
implementation
{$IFDEF RX_D3}
uses Forms, DbConsts {$IFDEF RX_D5}, ComObj {$ENDIF};
resourcestring
SMemNoRecords = 'No data found';
{$IFNDEF RX_D4}
SInvalidFields = 'No fields defined';
{$ENDIF}
const
ftBlobTypes = [ftBlob, ftMemo, ftGraphic, ftFmtMemo, ftParadoxOle,
ftDBaseOle, ftTypedBinary {$IFDEF RX_D5}, ftOraBlob, ftOraClob {$ENDIF}];
ftSupported = [ftString, ftSmallint, ftInteger, ftWord, ftBoolean, ftFloat,
ftCurrency, ftDate, ftTime, ftDateTime, ftAutoInc, ftBCD, ftBytes,
ftVarBytes {$IFDEF RX_D4}, ftADT, ftFixedChar, ftWideString,
ftLargeint {$ENDIF} {$IFDEF RX_D5}, ftVariant, ftGuid {$ENDIF}] +
ftBlobTypes;
fkStoredFields = [fkData];
{$IFDEF RX_D5}
GuidSize = 38;
{$ENDIF}
{ Utility routines }
function CompareFields(Data1, Data2: Pointer; FieldType: TFieldType;
CaseInsensitive: Boolean): Integer;
begin
Result := 0;
case FieldType of
ftString:
if CaseInsensitive then
Result := AnsiCompareText(PChar(Data1), PChar(Data2))
else
Result := AnsiCompareStr(PChar(Data1), PChar(Data2));
ftSmallint:
if SmallInt(Data1^) > SmallInt(Data2^) then Result := 1
else if SmallInt(Data1^) < SmallInt(Data2^) then Result := -1;
ftInteger, ftDate, ftTime, ftAutoInc:
if Longint(Data1^) > Longint(Data2^) then Result := 1
else if Longint(Data1^) < Longint(Data2^) then Result := -1;
ftWord:
if Word(Data1^) > Word(Data2^) then Result := 1
else if Word(Data1^) < Word(Data2^) then Result := -1;
ftBoolean:
if WordBool(Data1^) and not WordBool(Data2^) then Result := 1
else if not WordBool(Data1^) and WordBool(Data2^) then Result := -1;
ftFloat, ftCurrency:
if Double(Data1^) > Double(Data2^) then Result := 1
else if Double(Data1^) < Double(Data2^) then Result := -1;
ftDateTime:
if TDateTime(Data1^) > TDateTime(Data2^) then Result := 1
else if TDateTime(Data1^) < TDateTime(Data2^) then Result := -1;
{$IFDEF RX_D4}
ftFixedChar:
if CaseInsensitive then
Result := AnsiCompareText(PChar(Data1), PChar(Data2))
else
Result := AnsiCompareStr(PChar(Data1), PChar(Data2));
ftWideString:
if CaseInsensitive then
Result := AnsiCompareText(WideCharToString(PWideChar(Data1)),
WideCharToString(PWideChar(Data2)))
else
Result := AnsiCompareStr(WideCharToString(PWideChar(Data1)),
WideCharToString(PWideChar(Data2)));
ftLargeint:
if Int64(Data1^) > Int64(Data2^) then Result := 1
else if Int64(Data1^) < Int64(Data2^) then Result := -1;
{$ENDIF}
{$IFDEF RX_D5}
ftVariant:
Result := 0;
ftGuid:
Result := AnsiCompareText(PChar(Data1), PChar(Data2));
{$ENDIF}
end;
end;
function CalcFieldLen(FieldType: TFieldType; Size: Word): Word;
begin
if not (FieldType in ftSupported) then
Result := 0
else if (FieldType in ftBlobTypes) then
Result := SizeOf(Longint)
else begin
Result := Size;
case FieldType of
ftString: Inc(Result);
ftSmallint: Result := SizeOf(SmallInt);
ftInteger: Result := SizeOf(Longint);
ftWord: Result := SizeOf(Word);
ftBoolean: Result := SizeOf(WordBool);
ftFloat: Result := SizeOf(Double);
ftCurrency: Result := SizeOf(Double);
ftBCD: Result := 34;
ftDate, ftTime: Result := SizeOf(Longint);
ftDateTime: Result := SizeOf(TDateTime);
ftBytes: Result := Size;
ftVarBytes: Result := Size + 2;
ftAutoInc: Result := SizeOf(Longint);
{$IFDEF RX_D4}
ftADT: Result := 0;
ftFixedChar: Inc(Result);
ftWideString: Result := (Result + 1) * 2;
ftLargeint: Result := SizeOf(Int64);
{$ENDIF}
{$IFDEF RX_D5}
ftVariant: Result := SizeOf(Variant);
ftGuid: Result := GuidSize + 1;
{$ENDIF}
end;
end;
end;
procedure CalcDataSize(FieldDef: TFieldDef; var DataSize: Integer);
{$IFDEF RX_D4}
var
I: Integer;
{$ENDIF}
begin
with FieldDef do begin
if (DataType in ftSupported - ftBlobTypes) then
Inc(DataSize, CalcFieldLen(DataType, Size) + 1);
{$IFDEF RX_D4}
for I := 0 to ChildDefs.Count - 1 do
CalcDataSize(ChildDefs[I], DataSize);
{$ENDIF}
end;
end;
procedure Error(const Msg: string);
begin
DatabaseError(Msg);
end;
procedure ErrorFmt(const Msg: string; const Args: array of const);
begin
DatabaseErrorFmt(Msg, Args);
end;
type
TBookmarkData = Integer;
PMemBookmarkInfo = ^TMemBookmarkInfo;
TMemBookmarkInfo = record
BookmarkData: TBookmarkData;
BookmarkFlag: TBookmarkFlag;
end;
{ TMemoryRecord }
constructor TMemoryRecord.Create(MemoryData: TRxMemoryData);
begin
CreateEx(MemoryData, True);
end;
constructor TMemoryRecord.CreateEx(MemoryData: TRxMemoryData;
UpdateParent: Boolean);
begin
inherited Create;
SetMemoryData(MemoryData, UpdateParent);
end;
destructor TMemoryRecord.Destroy;
begin
SetMemoryData(nil, True);
inherited Destroy;
end;
function TMemoryRecord.GetIndex: Integer;
begin
if FMemoryData <> nil then Result := FMemoryData.FRecords.IndexOf(Self)
else Result := -1;
end;
procedure TMemoryRecord.SetMemoryData(Value: TRxMemoryData; UpdateParent: Boolean);
var
I: Integer;
DataSize: Integer;
begin
if FMemoryData <> Value then begin
if FMemoryData <> nil then begin
FMemoryData.FRecords.Remove(Self);
if FMemoryData.BlobFieldCount > 0 then
Finalize(PMemBlobArray(FBlobs)[0], FMemoryData.BlobFieldCount);
ReallocMem(FBlobs, 0);
ReallocMem(FData, 0);
FMemoryData := nil;
end;
if Value <> nil then begin
if UpdateParent then begin
Value.FRecords.Add(Self);
Inc(Value.FLastID);
FID := Value.FLastID;
end;
FMemoryData := Value;
if Value.BlobFieldCount > 0 then begin
ReallocMem(FBlobs, Value.BlobFieldCount * SizeOf(Pointer));
Initialize(PMemBlobArray(FBlobs)[0], Value.BlobFieldCount);
end;
DataSize := 0;
for I := 0 to Value.FieldDefs.Count - 1 do
CalcDataSize(Value.FieldDefs[I], DataSize);
ReallocMem(FData, DataSize);
end;
end;
end;
procedure TMemoryRecord.SetIndex(Value: Integer);
var
CurIndex: Integer;
begin
CurIndex := GetIndex;
if (CurIndex >= 0) and (CurIndex <> Value) then
FMemoryData.FRecords.Move(CurIndex, Value);
end;
{ TRxMemoryData }
constructor TRxMemoryData.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FRecordPos := -1;
FLastID := Low(Integer);
FAutoInc := 1;
FRecords := TList.Create;
end;
destructor TRxMemoryData.Destroy;
begin
inherited Destroy;
FreeIndexList;
ClearRecords;
FRecords.Free;
ReallocMem(FOffsets, 0);
end;
{ Records Management }
function TRxMemoryData.GetCapacity: Integer;
begin
if FRecords <> nil then Result := FRecords.Capacity
else Result := 0;
end;
procedure TRxMemoryData.SetCapacity(Value: Integer);
begin
if FRecords <> nil then FRecords.Capacity := Value;
end;
function TRxMemoryData.AddRecord: TMemoryRecord;
begin
Result := TMemoryRecord.Create(Self);
end;
function TRxMemoryData.FindRecordID(ID: Integer): TMemoryRecord;
var
I: Integer;
begin
for I := 0 to FRecords.Count - 1 do begin
Result := TMemoryRecord(FRecords[I]);
if Result.ID = ID then Exit;
end;
Result := nil;
end;
function TRxMemoryData.InsertRecord(Index: Integer): TMemoryRecord;
begin
Result := AddRecord;
Result.Index := Index;
end;
function TRxMemoryData.GetMemoryRecord(Index: Integer): TMemoryRecord;
begin
Result := TMemoryRecord(FRecords[Index]);
end;
{ Field Management }
{$IFNDEF RX_D5}
function TRxMemoryData.BCDToCurr(BCD: Pointer; var Curr: Currency): Boolean;
begin
Move(BCD^, Curr, SizeOf(Currency));
Result := True;
end;
function TRxMemoryData.CurrToBCD(const Curr: Currency; BCD: Pointer; Precision,
Decimals: Integer): Boolean;
begin
Move(Curr, BCD^, SizeOf(Currency));
Result := True;
end;
{$ENDIF RX_D5}
procedure TRxMemoryData.InitFieldDefsFromFields;
var
I: Integer;
Offset: Word;
begin
if FieldDefs.Count = 0 then begin
for I := 0 to FieldCount - 1 do begin
with Fields[I] do
if (FieldKind in fkStoredFields) and not (DataType in ftSupported) then
ErrorFmt(SUnknownFieldType, [DisplayName]);
end;
FreeIndexList;
end;
Offset := 0;
{$IFDEF RX_D4}
inherited InitFieldDefsFromFields;
{ Calculate fields offsets }
ReallocMem(FOffsets, FieldDefList.Count * SizeOf(Word));
for I := 0 to FieldDefList.Count - 1 do begin
FOffsets^[I] := Offset;
with FieldDefList[I] do begin
if (DataType in ftSupported - ftBlobTypes) then
Inc(Offset, CalcFieldLen(DataType, Size) + 1);
end;
end;
{$ELSE}
{ Create FieldDefs from persistent fields if needed }
if FieldDefs.Count = 0 then
for I := 0 to FieldCount - 1 do begin
with Fields[I] do
if (FieldKind = fkData) then
FieldDefs.Add(FieldName, DataType, Size, Required);
end;
{ Calculate fields offsets }
ReallocMem(FOffsets, FieldDefs.Count * SizeOf(Word));
for I := 0 to FieldDefs.Count - 1 do begin
FOffsets^[I] := Offset;
with FieldDefs[I] do begin
if (DataType in ftSupported - ftBlobTypes) then
Inc(Offset, CalcFieldLen(DataType, Size) + 1);
end;
end;
{$ENDIF}
end;
function TRxMemoryData.FindFieldData(Buffer: Pointer; Field: TField): Pointer;
var
Index: Integer;
begin
{$IFDEF RX_D4}
Index := FieldDefList.IndexOf(Field.FullName);
{$ELSE}
Index := FieldDefs.IndexOf(Field.FieldName);
{$ENDIF}
if (Index >= 0) and (Buffer <> nil) and
{$IFDEF RX_D4}
(FieldDefList[Index].DataType in ftSupported - ftBlobTypes) then
{$ELSE}
(FieldDefs[Index].DataType in ftSupported - ftBlobTypes) then
{$ENDIF}
Result := (PChar(Buffer) + FOffsets[Index])
else Result := nil;
end;
{ Buffer Manipulation }
function TRxMemoryData.CalcRecordSize: Integer;
var
I: Integer;
begin
Result := 0;
for I := 0 to FieldDefs.Count - 1 do
CalcDataSize(FieldDefs[I], Result);
end;
procedure TRxMemoryData.InitBufferPointers(GetProps: Boolean);
begin
if GetProps then FRecordSize := CalcRecordSize;
FBookmarkOfs := FRecordSize + CalcFieldsSize;
FBlobOfs := FBookmarkOfs + SizeOf(TMemBookmarkInfo);
FRecBufSize := FBlobOfs + BlobFieldCount * SizeOf(Pointer);
end;
procedure TRxMemoryData.ClearRecords;
begin
while FRecords.Count > 0 do TObject(FRecords.Last).Free;
FLastID := Low(Integer);
FRecordPos := -1;
end;
function TRxMemoryData.AllocRecordBuffer: PChar;
begin
Result := StrAlloc(FRecBufSize);
if BlobFieldCount > 0 then
Initialize(PMemBlobArray(Result + FBlobOfs)[0], BlobFieldCount);
end;
procedure TRxMemoryData.FreeRecordBuffer(var Buffer: PChar);
begin
if BlobFieldCount > 0 then
Finalize(PMemBlobArray(Buffer + FBlobOfs)[0], BlobFieldCount);
StrDispose(Buffer);
Buffer := nil;
end;
procedure TRxMemoryData.ClearCalcFields(Buffer: PChar);
begin
FillChar(Buffer[FRecordSize], CalcFieldsSize, 0);
end;
procedure TRxMemoryData.InternalInitRecord(Buffer: PChar);
var
I: Integer;
begin
FillChar(Buffer^, FBlobOfs, 0);
for I := 0 to BlobFieldCount - 1 do
PMemBlobArray(Buffer + FBlobOfs)[I] := '';
end;
procedure TRxMemoryData.InitRecord(Buffer: PChar);
begin
inherited InitRecord(Buffer);
with PMemBookmarkInfo(Buffer + FBookmarkOfs)^ do begin
BookmarkData := Low(Integer);
BookmarkFlag := bfInserted;
end;
end;
function TRxMemoryData.GetCurrentRecord(Buffer: PChar): Boolean;
begin
Result := False;
if not IsEmpty and (GetBookmarkFlag(ActiveBuffer) = bfCurrent) then begin
UpdateCursorPos;
if (FRecordPos >= 0) and (FRecordPos < RecordCount) then begin
Move(Records[FRecordPos].Data^, Buffer^, FRecordSize);
Result := True;
end;
end;
end;
procedure TRxMemoryData.RecordToBuffer(Rec: TMemoryRecord; Buffer: PChar);
var
I: Integer;
begin
Move(Rec.Data^, Buffer^, FRecordSize);
with PMemBookmarkInfo(Buffer + FBookmarkOfs)^ do begin
BookmarkData := Rec.ID;
BookmarkFlag := bfCurrent;
end;
for I := 0 to BlobFieldCount - 1 do
PMemBlobArray(Buffer + FBlobOfs)[I] := PMemBlobArray(Rec.FBlobs)[I];
GetCalcFields(Buffer);
end;
function TRxMemoryData.GetRecord(Buffer: PChar; GetMode: TGetMode;
DoCheck: Boolean): TGetResult;
var
Accept: Boolean;
begin
Result := grOk;
Accept := True;
case GetMode of
gmPrior:
if FRecordPos <= 0 then begin
Result := grBOF;
FRecordPos := -1;
end
else begin
repeat
Dec(FRecordPos);
if Filtered then Accept := RecordFilter;
until Accept or (FRecordPos < 0);
if not Accept then begin
Result := grBOF;
FRecordPos := -1;
end;
end;
gmCurrent:
if (FRecordPos < 0) or (FRecordPos >= RecordCount) then
Result := grError
else if Filtered then begin
if not RecordFilter then Result := grError;
end;
gmNext:
if FRecordPos >= RecordCount - 1 then Result := grEOF
else begin
repeat
Inc(FRecordPos);
if Filtered then Accept := RecordFilter;
until Accept or (FRecordPos > RecordCount - 1);
if not Accept then begin
Result := grEOF;
FRecordPos := RecordCount - 1;
end;
end;
end;
if Result = grOk then RecordToBuffer(Records[FRecordPos], Buffer)
else if (Result = grError) and DoCheck then Error(SMemNoRecords);
end;
function TRxMemoryData.GetRecordSize: Word;
begin
Result := FRecordSize;
end;
function TRxMemoryData.GetActiveRecBuf(var RecBuf: PChar): Boolean;
begin
case State of
dsBrowse:
if IsEmpty then RecBuf := nil
else RecBuf := ActiveBuffer;
dsEdit, dsInsert: RecBuf := ActiveBuffer;
dsCalcFields: RecBuf := CalcBuffer;
dsFilter: RecBuf := TempBuffer;
else RecBuf := nil;
end;
Result := RecBuf <> nil;
end;
function TRxMemoryData.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
var
RecBuf, Data: PChar;
{$IFDEF RX_D5}
VarData: Variant;
{$ENDIF}
begin
Result := False;
if not GetActiveRecBuf(RecBuf) then Exit;
if Field.FieldNo > 0 then begin
Data := FindFieldData(RecBuf, Field);
if Data <> nil then begin
Result := Boolean(Data[0]);
Inc(Data);
if Field.DataType in [ftString {$IFDEF RX_D4}, ftFixedChar,
ftWideString {$ENDIF} {$IFDEF RX_D5}, ftGuid {$ENDIF}] then
Result := Result and (StrLen(Data) > 0);
if Result and (Buffer <> nil) then
{$IFDEF RX_D5}
if Field.DataType = ftVariant then begin
VarData := PVariant(Data)^;
PVariant(Buffer)^ := VarData;
end else
{$ENDIF}
Move(Data^, Buffer^, CalcFieldLen(Field.DataType, Field.Size));
end;
end
else begin
if State in [dsBrowse, dsEdit, dsInsert, dsCalcFields] then begin
Inc(RecBuf, FRecordSize + Field.Offset);
Result := Boolean(RecBuf[0]);
if Result and (Buffer <> nil) then
Move(RecBuf[1], Buffer^, Field.DataSize);
end;
end;
end;
procedure TRxMemoryData.SetFieldData(Field: TField; Buffer: Pointer);
var
RecBuf, Data: PChar;
{$IFDEF RX_D5}
VarData: Variant;
{$ENDIF}
begin
if not (State in dsWriteModes) then Error(SNotEditing);
GetActiveRecBuf(RecBuf);
with Field do begin
if FieldNo > 0 then
begin
if State in [dsCalcFields, dsFilter] then Error(SNotEditing);
if ReadOnly and not (State in [dsSetKey, dsFilter]) then
ErrorFmt(SFieldReadOnly, [DisplayName]);
Validate(Buffer);
if FieldKind <> fkInternalCalc then begin
Data := FindFieldData(RecBuf, Field);
if Data <> nil then begin
{$IFDEF RX_D5}
if DataType = ftVariant then begin
if Buffer <> nil then
VarData := PVariant(Buffer)^
else
VarData := EmptyParam;
Boolean(Data[0]) := LongBool(Buffer) and not
(VarIsNull(VarData) or VarIsEmpty(VarData));
if Boolean(Data[0]) then begin
Inc(Data);
PVariant(Data)^ := VarData;
end
else FillChar(Data^, CalcFieldLen(DataType, Size), 0);
end else
{$ENDIF}
begin
Boolean(Data[0]) := LongBool(Buffer);
Inc(Data);
if LongBool(Buffer) then
Move(Buffer^, Data^, CalcFieldLen(DataType, Size))
else FillChar(Data^, CalcFieldLen(DataType, Size), 0);
end;
end;
end;
end else {fkCalculated, fkLookup}
begin
Inc(RecBuf, FRecordSize + Offset);
Boolean(RecBuf[0]) := LongBool(Buffer);
if Boolean(RecBuf[0]) then Move(Buffer^, RecBuf[1], DataSize);
end;
if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
DataEvent(deFieldChange, Longint(Field));
end;
end;
{ Filter }
procedure TRxMemoryData.SetFiltered(Value: Boolean);
begin
if Active then begin
CheckBrowseMode;
if Filtered <> Value then begin
inherited SetFiltered(Value);
First;
end;
end
else inherited SetFiltered(Value);
end;
procedure TRxMemoryData.SetOnFilterRecord(const Value: TFilterRecordEvent);
begin
if Active then begin
CheckBrowseMode;
inherited SetOnFilterRecord(Value);
if Filtered then First;
end
else inherited SetOnFilterRecord(Value);
end;
function TRxMemoryData.RecordFilter: Boolean;
var
SaveState: TDataSetState;
begin
Result := True;
if Assigned(OnFilterRecord) then begin
if (FRecordPos >= 0) and (FRecordPos < RecordCount) then begin
SaveState := SetTempState(dsFilter);
try
RecordToBuffer(Records[FRecordPos], TempBuffer);
OnFilterRecord(Self, Result);
except
Application.HandleException(Self);
end;
RestoreState(SaveState);
end
else Result := False;
end;
end;
{ Blobs }
function TRxMemoryData.GetBlobData(Field: TField; Buffer: PChar): TMemBlobData;
begin
Result := PMemBlobArray(Buffer + FBlobOfs)[Field.Offset];
end;
procedure TRxMemoryData.SetBlobData(Field: TField; Buffer: PChar;
Value: TMemBlobData);
begin
if (Buffer = ActiveBuffer) then begin
if State = dsFilter then Error(SNotEditing);
PMemBlobArray(Buffer + FBlobOfs)[Field.Offset] := Value;
end;
end;
procedure TRxMemoryData.CloseBlob(Field: TField);
begin
if (FRecordPos >= 0) and (FRecordPos < FRecords.Count) and
(State = dsEdit) then
PMemBlobArray(ActiveBuffer + FBlobOfs)[Field.Offset] :=
PMemBlobArray(Records[FRecordPos].FBlobs)[Field.Offset]
else PMemBlobArray(ActiveBuffer + FBlobOfs)[Field.Offset] := '';
end;
function TRxMemoryData.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
begin
Result := TMemBlobStream.Create(Field as TBlobField, Mode);
end;
{ Bookmarks }
function TRxMemoryData.BookmarkValid(Bookmark: TBookmark): Boolean;
begin
Result := FActive and (TBookmarkData(Bookmark^) > Low(Integer)) and
(TBookmarkData(Bookmark^) <= FLastID);
end;
function TRxMemoryData.CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer;
begin
if (Bookmark1 = nil) and (Bookmark2 = nil) then Result := 0
else if (Bookmark1 <> nil) and (Bookmark2 = nil) then Result := 1
else if (Bookmark1 = nil) and (Bookmark2 <> nil) then Result := -1
else if TBookmarkData(Bookmark1^) > TBookmarkData(Bookmark2^) then
Result := 1
else if TBookmarkData(Bookmark1^) < TBookmarkData(Bookmark2^) then
Result := -1
else Result := 0;
end;
procedure TRxMemoryData.GetBookmarkData(Buffer: PChar; Data: Pointer);
begin
Move(PMemBookmarkInfo(Buffer + FBookmarkOfs)^.BookmarkData, Data^,
SizeOf(TBookmarkData));
end;
procedure TRxMemoryData.SetBookmarkData(Buffer: PChar; Data: Pointer);
begin
Move(Data^, PMemBookmarkInfo(Buffer + FBookmarkOfs)^.BookmarkData,
SizeOf(TBookmarkData));
end;
function TRxMemoryData.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
begin
Result := PMemBookmarkInfo(Buffer + FBookmarkOfs)^.BookmarkFlag;
end;
procedure TRxMemoryData.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
begin
PMemBookmarkInfo(Buffer + FBookmarkOfs)^.BookmarkFlag := Value;
end;
procedure TRxMemoryData.InternalGotoBookmark(Bookmark: TBookmark);
var
Rec: TMemoryRecord;
SavePos: Integer;
Accept: Boolean;
begin
Rec := FindRecordID(TBookmarkData(Bookmark^));
if Rec <> nil then begin
Accept := True;
SavePos := FRecordPos;
try
FRecordPos := Rec.Index;
if Filtered then Accept := RecordFilter;
finally
if not Accept then FRecordPos := SavePos;
end;
end;
end;
{ Navigation }
procedure TRxMemoryData.InternalSetToRecord(Buffer: PChar);
begin
InternalGotoBookmark(@PMemBookmarkInfo(Buffer + FBookmarkOfs)^.BookmarkData);
end;
procedure TRxMemoryData.InternalFirst;
begin
FRecordPos := -1;
end;
procedure TRxMemoryData.InternalLast;
begin
FRecordPos := FRecords.Count;
end;
{ Data Manipulation }
procedure TRxMemoryData.AssignMemoryRecord(Rec: TMemoryRecord; Buffer: PChar);
var
I: Integer;
begin
Move(Buffer^, Rec.Data^, FRecordSize);
for I := 0 to BlobFieldCount - 1 do
PMemBlobArray(Rec.FBlobs)[I] := PMemBlobArray(Buffer + FBlobOfs)[I];
end;
procedure TRxMemoryData.SetMemoryRecordData(Buffer: PChar; Pos: Integer);
var
Rec: TMemoryRecord;
begin
if State = dsFilter then Error(SNotEditing);
Rec := Records[Pos];
AssignMemoryRecord(Rec, Buffer);
end;
procedure TRxMemoryData.SetAutoIncFields(Buffer: PChar);
var
I, Count: Integer;
Data: PChar;
begin
Count := 0;
for I := 0 to FieldCount - 1 do
if (Fields[I].FieldKind in fkStoredFields) and
(Fields[I].DataType = ftAutoInc) then
begin
Data := FindFieldData(Buffer, Fields[I]);
if Data <> nil then begin
Boolean(Data[0]) := True;
Inc(Data);
Move(FAutoInc, Data^, SizeOf(Longint));
Inc(Count);
end;
end;
if Count > 0 then Inc(FAutoInc);
end;
procedure TRxMemoryData.InternalAddRecord(Buffer: Pointer; Append: Boolean);
var
RecPos: Integer;
Rec: TMemoryRecord;
begin
if Append then begin
Rec := AddRecord;
FRecordPos := FRecords.Count - 1;
end
else begin
if FRecordPos = -1 then RecPos := 0
else RecPos := FRecordPos;
Rec := InsertRecord(RecPos);
FRecordPos := RecPos;
end;
SetAutoIncFields(Buffer);
SetMemoryRecordData(Buffer, Rec.Index);
end;
procedure TRxMemoryData.InternalDelete;
var
Accept: Boolean;
begin
Records[FRecordPos].Free;
if FRecordPos >= FRecords.Count then Dec(FRecordPos);
Accept := True;
repeat
if Filtered then Accept := RecordFilter;
if not Accept then Dec(FRecordPos);
until Accept or (FRecordPos < 0);
if FRecords.Count = 0 then FLastID := Low(Integer);
end;
procedure TRxMemoryData.InternalPost;
var
RecPos: Integer;
begin
if State = dsEdit then
SetMemoryRecordData(ActiveBuffer, FRecordPos)
else begin
if State in [dsInsert] then SetAutoIncFields(ActiveBuffer);
if FRecordPos >= FRecords.Count then begin
SetMemoryRecordData(ActiveBuffer, AddRecord.Index);
FRecordPos := FRecords.Count - 1;
end
else begin
if FRecordPos = -1 then RecPos := 0
else RecPos := FRecordPos;
SetMemoryRecordData(ActiveBuffer, InsertRecord(RecPos).Index);
FRecordPos := RecPos;
end;
end;
end;
procedure TRxMemoryData.OpenCursor(InfoQuery: Boolean);
begin
if not InfoQuery then begin
if FieldCount > 0 then FieldDefs.Clear;
InitFieldDefsFromFields;
end;
FActive := True;
inherited OpenCursor(InfoQuery);
end;
procedure TRxMemoryData.InternalOpen;
begin
BookmarkSize := SizeOf(TBookmarkData);
{$IFDEF RX_D4}
if DefaultFields then CreateFields;
{$ELSE}
if DefaultFields then Error(SInvalidFields);
{$ENDIF}
BindFields(True);
InitBufferPointers(True);
InternalFirst;
end;
procedure TRxMemoryData.InternalClose;
begin
ClearRecords;
FAutoInc := 1;
BindFields(False);
{$IFDEF RX_D4}
if DefaultFields then DestroyFields;
{$ENDIF}
FreeIndexList;
FActive := False;
end;
procedure TRxMemoryData.InternalHandleException;
begin
Application.HandleException(Self);
end;
procedure TRxMemoryData.InternalInitFieldDefs;
begin
end;
function TRxMemoryData.IsCursorOpen: Boolean;
begin
Result := FActive;
end;
{ Informational }
function TRxMemoryData.GetRecordCount: Integer;
begin
Result := FRecords.Count;
end;
function TRxMemoryData.GetRecNo: Integer;
begin
CheckActive;
UpdateCursorPos;
if (FRecordPos = -1) and (RecordCount > 0) then Result := 1
else Result := FRecordPos + 1;
end;
procedure TRxMemoryData.SetRecNo(Value: Integer);
begin
if (Value > 0) and (Value <= FRecords.Count) then begin
FRecordPos := Value - 1;
Resync([]);
end;
end;
function TRxMemoryData.IsSequenced: Boolean;
begin
Result := not Filtered;
end;
function TRxMemoryData.Locate(const KeyFields: string;
const KeyValues: Variant; Options: TLocateOptions): Boolean;
begin
DoBeforeScroll;
Result := DataSetLocateThrough(Self, KeyFields, KeyValues, Options);
if Result then begin
DataEvent(deDataSetChange, 0);
DoAfterScroll;
end;
end;
{ Table Manipulation }
procedure TRxMemoryData.EmptyTable;
begin
if Active then begin
CheckBrowseMode;
ClearRecords;
ClearBuffers;
DataEvent(deDataSetChange, 0);
end;
end;
procedure TRxMemoryData.CopyStructure(Source: TDataSet);
procedure CheckDataTypes(FieldDefs: TFieldDefs);
var
I: Integer;
begin
for I := FieldDefs.Count - 1 downto 0 do begin
if not (FieldDefs.Items[I].DataType in ftSupported) then
FieldDefs.Items[I].Free
{$IFDEF RX_D4}
else CheckDataTypes(FieldDefs[I].ChildDefs);
{$ENDIF}
end;
end;
var
I: Integer;
begin
CheckInactive;
for I := FieldCount - 1 downto 0 do Fields[I].Free;
if (Source = nil) then Exit;
Source.FieldDefs.Update;
FieldDefs := Source.FieldDefs;
CheckDataTypes(FieldDefs);
{$IFDEF RX_D4}
CreateFields;
{$ELSE}
for I := 0 to FieldDefs.Count - 1 do begin
if (csDesigning in ComponentState) and (Owner <> nil) then
FieldDefs.Items[I].CreateField(Owner)
else
FieldDefs.Items[I].CreateField(Self);
end;
{$ENDIF}
end;
function TRxMemoryData.LoadFromDataSet(Source: TDataSet; RecordCount: Integer;
Mode: TLoadMode): Integer;
var
SourceActive: Boolean;
MovedCount: Integer;
begin
Result := 0;
if Source = Self then Exit;
SourceActive := Source.Active;
Source.DisableControls;
try
DisableControls;
try
Filtered := False;
with Source do begin
Open;
CheckBrowseMode;
UpdateCursorPos;
end;
if Mode = lmCopy then begin
Close;
CopyStructure(Source);
end;
FreeIndexList;
if not Active then Open;
CheckBrowseMode;
if RecordCount > 0 then MovedCount := RecordCount
else begin
Source.First;
MovedCount := MaxInt;
end;
try
while not Source.EOF do begin
Append;
AssignRecord(Source, Self, True);
Post;
Inc(Result);
if Result >= MovedCount then Break;
Source.Next;
end;
finally
First;
end;
finally
EnableControls;
end;
finally
if not SourceActive then Source.Close;
Source.EnableControls;
end;
end;
function TRxMemoryData.SaveToDataSet(Dest: TDataSet; RecordCount: Integer): Integer;
var
MovedCount: Integer;
begin
Result := 0;
if Dest = Self then Exit;
CheckBrowseMode;
UpdateCursorPos;
Dest.DisableControls;
try
DisableControls;
try
if not Dest.Active then Dest.Open
else Dest.CheckBrowseMode;
if RecordCount > 0 then MovedCount := RecordCount
else begin
First;
MovedCount := MaxInt;
end;
try
while not EOF do begin
Dest.Append;
AssignRecord(Self, Dest, True);
Dest.Post;
Inc(Result);
if Result >= MovedCount then Break;
Next;
end;
finally
Dest.First;
end;
finally
EnableControls;
end;
finally
Dest.EnableControls;
end;
end;
{ Index Related }
procedure TRxMemoryData.SortOnFields(const FieldNames: string;
{$IFDEF RX_D4}
CaseInsensitive: Boolean = True; Descending: Boolean = False);
{$ELSE}
CaseInsensitive, Descending: Boolean);
{$ENDIF}
begin
CreateIndexList(FieldNames);
FCaseInsensitiveSort := CaseInsensitive;
FDescendingSort := Descending;
try
Sort;
except
FreeIndexList;
raise;
end;
end;
procedure TRxMemoryData.Sort;
var
Pos: TBookmarkStr;
begin
if Active and (FRecords <> nil) and (FRecords.Count > 0) then begin
Pos := Bookmark;
try
QuickSort(0, FRecords.Count - 1, CompareRecords);
SetBufListSize(0);
InitBufferPointers(False);
try
SetBufListSize(BufferCount + 1);
except
SetState(dsInactive);
CloseCursor;
raise;
end;
finally
Bookmark := Pos;
end;
Resync([]);
end;
end;
procedure TRxMemoryData.QuickSort(L, R: Integer; Compare: TCompareRecords);
var
I, J: Integer;
P: TMemoryRecord;
begin
repeat
I := L;
J := R;
P := Records[(L + R) shr 1];
repeat
while Compare(Records[I], P) < 0 do Inc(I);
while Compare(Records[J], P) > 0 do Dec(J);
if I <= J then begin
FRecords.Exchange(I, J);
Inc(I);
Dec(J);
end;
until I > J;
if L < J then QuickSort(L, J, Compare);
L := I;
until I >= R;
end;
function TRxMemoryData.CompareRecords(Item1, Item2: TMemoryRecord): Integer;
var
Data1, Data2: PChar;
F: TField;
I: Integer;
begin
Result := 0;
if FIndexList <> nil then begin
for I := 0 to FIndexList.Count - 1 do begin
F := TField(FIndexList[I]);
Data1 := FindFieldData(Item1.Data, F);
if Data1 <> nil then begin
Data2 := FindFieldData(Item2.Data, F);
if Data2 <> nil then begin
if Boolean(Data1[0]) and Boolean(Data2[0]) then begin
Inc(Data1);
Inc(Data2);
Result := CompareFields(Data1, Data2, F.DataType,
FCaseInsensitiveSort);
end
else if Boolean(Data1[0]) then Result := 1
else if Boolean(Data2[0]) then Result := -1;
if FDescendingSort then Result := -Result;
end;
end;
if Result <> 0 then Exit;
end;
end;
if (Result = 0) then begin
if Item1.ID > Item2.ID then Result := 1
else if Item1.ID < Item2.ID then Result := -1;
if FDescendingSort then Result := -Result;
end;
end;
function TRxMemoryData.GetIsIndexField(Field: TField): Boolean;
begin
if FIndexList <> nil then
Result := FIndexList.IndexOf(Field) >= 0
else Result := False;
end;
procedure TRxMemoryData.CreateIndexList(const FieldNames: string);
var
Pos: Integer;
F: TField;
begin
if FIndexList = nil then FIndexList := TList.Create
else FIndexList.Clear;
Pos := 1;
while Pos <= Length(FieldNames) do begin
F := FieldByName(ExtractFieldName(FieldNames, Pos));
if (F.FieldKind = fkData) and
(F.DataType in ftSupported - ftBlobTypes) then
FIndexList.Add(F)
else ErrorFmt(SFieldTypeMismatch, [F.DisplayName]);
end;
end;
procedure TRxMemoryData.FreeIndexList;
begin
FIndexList.Free;
FIndexList := nil;
end;
{ TMemBlobStream }
constructor TMemBlobStream.Create(Field: TBlobField; Mode: TBlobStreamMode);
begin
FMode := Mode;
FField := Field;
FDataSet := FField.DataSet as TRxMemoryData;
if not FDataSet.GetActiveRecBuf(FBuffer) then Exit;
if not FField.Modified and (Mode <> bmRead) then begin
if FField.ReadOnly then ErrorFmt(SFieldReadOnly, [FField.DisplayName]);
if not (FDataSet.State in [dsEdit, dsInsert]) then Error(SNotEditing);
FCached := True;
end
else FCached := (FBuffer = FDataSet.ActiveBuffer);
FOpened := True;
if Mode = bmWrite then Truncate;
end;
destructor TMemBlobStream.Destroy;
begin
if FOpened and FModified then FField.Modified := True;
if FModified then
try
FDataSet.DataEvent(deFieldChange, Longint(FField));
except
Application.HandleException(Self);
end;
end;
function TMemBlobStream.GetBlobFromRecord(Field: TField): TMemBlobData;
var
Rec: TMemoryRecord;
Pos: Integer;
begin
Result := '';
Pos := FDataSet.FRecordPos;
if (Pos < 0) and (FDataSet.RecordCount > 0) then Pos := 0
else if Pos >= FDataSet.RecordCount then Pos := FDataSet.RecordCount - 1;
if (Pos >= 0) and (Pos < FDataSet.RecordCount) then begin
Rec := FDataSet.Records[Pos];
if Rec <> nil then
Result := PMemBlobArray(Rec.FBlobs)[FField.Offset];
end;
end;
function TMemBlobStream.Read(var Buffer; Count: Longint): Longint;
begin
Result := 0;
if FOpened then begin
if Count > Size - FPosition then Result := Size - FPosition
else Result := Count;
if Result > 0 then begin
if FCached then begin
Move(PChar(FDataSet.GetBlobData(FField, FBuffer))[FPosition], Buffer,
Result);
Inc(FPosition, Result);
end
else begin
Move(PChar(GetBlobFromRecord(FField))[FPosition], Buffer,
Result);
Inc(FPosition, Result);
end;
end;
end;
end;
function TMemBlobStream.Write(const Buffer; Count: Longint): Longint;
var
Temp: TMemBlobData;
begin
Result := 0;
if FOpened and FCached and (FMode <> bmRead) then begin
Temp := FDataSet.GetBlobData(FField, FBuffer);
if Length(Temp) < FPosition + Count then
SetLength(Temp, FPosition + Count);
Move(Buffer, PChar(Temp)[FPosition], Count);
FDataSet.SetBlobData(FField, FBuffer, Temp);
Inc(FPosition, Count);
Result := Count;
FModified := True;
end;
end;
function TMemBlobStream.Seek(Offset: Longint; Origin: Word): Longint;
begin
case Origin of
0: FPosition := Offset;
1: Inc(FPosition, Offset);
2: FPosition := GetBlobSize + Offset;
end;
Result := FPosition;
end;
procedure TMemBlobStream.Truncate;
begin
if FOpened and FCached and (FMode <> bmRead) then begin
FDataSet.SetBlobData(FField, FBuffer, '');
FModified := True;
end;
end;
function TMemBlobStream.GetBlobSize: Longint;
begin
Result := 0;
if FOpened then
if FCached then
Result := Length(FDataSet.GetBlobData(FField, FBuffer))
else
Result := Length(GetBlobFromRecord(FField))
end;
{$ENDIF RX_D3}
end.