home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 September / Chip_2001-09_cd1.bin / zkuste / delphi / kolekce / d56 / RMCTL.ZIP / rmMemoryDataSet.pas < prev    next >
Pascal/Delphi Source File  |  2001-06-22  |  31KB  |  1,218 lines

  1. {================================================================================
  2. Copyright (C) 1997-2001 Mills Enterprise
  3.  
  4. Unit     : rmMemoryDataSet
  5. Purpose  : To allow dataset like functionality with out an actual DB being
  6.            attached.
  7. Date     : 04-24-2000
  8. Author   : Ryan J. Mills
  9. Version  : 1.80
  10. Notes    : This unit is originally based upon the work of Patrick O'Keeffe.
  11.            It was at his request that I took the component over and rm'ified it.
  12. ================================================================================}
  13.  
  14. unit rmMemoryDataSet;
  15.  
  16. interface
  17.  
  18. {$I CompilerDefines.INC}
  19.  
  20. {$ifdef BD6}
  21. uses
  22.    SysUtils, Windows, DB, Classes, Forms, Variants;
  23. {$else}
  24. uses
  25.   SysUtils, Windows, DB, Classes, Forms;
  26. {$endif}
  27.  
  28. type
  29.   TSortType = (stAscending, stDescending, stAlternate);
  30.   TSortArray = array of boolean;
  31.  
  32.   PIntArray = ^TIntArray;
  33.   TIntArray = array[0..1000000] of Integer;
  34.  
  35.   PByteArray = ^TByteArray;
  36.   TByteArray = array[0..1000000000] of Byte;
  37.  
  38.   TRecordBlob = packed record
  39.     BlobData : PByteArray;
  40.     BlobSize : LongInt;
  41.     FieldNum : LongInt;
  42.   end;
  43.  
  44.   TBlobArray = array[0..10] of TRecordBlob;
  45.  
  46.   TRecordData = packed record
  47.     Bookmark : Integer;
  48.     BookmarkFlag : TBookmarkFlag;
  49.     ArraySize : Integer;
  50.     Blobs : TBlobArray;
  51.     Bytes : TByteArray;
  52.   end;
  53.   PRecordData = ^TRecordData;
  54.  
  55.   TFieldDefType = (fdtString,
  56.     fdtInteger,
  57.     fdtFloat,
  58.     fdtDateTime,
  59.     fdtBoolean,
  60.     fdtMemo);
  61.  
  62.   TFieldDefItem = class(TCollectionItem)
  63.   private
  64.     FSize : Integer;
  65.     FName : string;
  66.     FFieldType : TFieldDefType;
  67.   protected
  68.  
  69.   public
  70.     procedure Assign(Source: TPersistent); override;
  71.   published
  72.     property Name : string read FName write FName;
  73.     property Size : Integer read FSize write FSize;
  74.     property FieldType : TFieldDefType read FFieldType write FFieldType;
  75.   end;
  76.  
  77.   TFieldDefList = class(TCollection)
  78.   private
  79.     FInternalOwner : TComponent;
  80.     function GetItem(Index : Integer) : TFieldDefItem;
  81.     procedure SetItem(Index : Integer; Value : TFieldDefItem);
  82.   public
  83.     function Add : TFieldDefItem;
  84.     constructor Create(AOwner : TComponent);
  85.     property Items[Index : Integer] : TFieldDefItem read GetItem write SetItem; default;
  86.     property InternalOwner : TComponent read FInternalOwner;
  87.   end;
  88.  
  89.   // Collection item object for records
  90.   TTextRecord = class(TCollectionItem)
  91.   public
  92.     destructor Destroy; override;
  93.   protected
  94.     Data : PRecordData;
  95.   end;
  96.  
  97.   TrmLongStringField = class(TStringField)
  98.   public
  99.     class procedure CheckTypeSize(Value : Integer); override;
  100.     function GetAsString : string; override;
  101.     function GetAsVariant : Variant; override;
  102.     function GetValue(var Value : string) : Boolean;
  103.     procedure SetAsString(const Value : string); override;
  104.   end;
  105.  
  106. { TrmMemoryDataSet }
  107.  
  108.   TrmMemoryDataSet = class(TDataSet)
  109.   private
  110.     Records : TCollection;
  111.     FieldOffSets : PIntArray;
  112.     FFieldRoster : TFieldDefList;
  113.     FRecBufSize : Integer;
  114.     FCurRec : Integer;
  115.     FLastBookmark : Integer;
  116.     MaxFieldNo : Integer;
  117.     FRecordSize : Integer;
  118.     FFilterBuffer : PRecordData;
  119.     FSortOrder : TSortArray;
  120.     function GetRecBufSize : Integer;
  121.     procedure QueryRecords;
  122.     function FieldOffset(Field : TField) : Integer;
  123.     function GetActiveRecBuf(var RecBuf : PRecordData) : Boolean;
  124.     procedure InternalUpdate;
  125.     procedure SaveRecordData(Buffer : PRecordData; Index : Integer);
  126.   protected
  127.     { Overriden abstract methods (required) }
  128.     function AllocRecordBuffer : PChar; override;
  129.     procedure FreeRecordBuffer(var Buffer : PChar); override;
  130.     procedure GetBookmarkData(Buffer : PChar; Data : Pointer); override;
  131.     function GetBookmarkFlag(Buffer : PChar) : TBookmarkFlag; override;
  132.     function GetFieldClass(FieldType : TFieldType) : TFieldClass; override;
  133.     function GetRecord(Buffer : PChar; GetMode : TGetMode; DoCheck : Boolean) : TGetResult; override;
  134.     function GetRecordSize : Word; override;
  135.     procedure InternalAddRecord(Buffer : Pointer; Append : Boolean); override;
  136.     procedure InternalClose; override;
  137.     procedure InternalDelete; override;
  138.     procedure InternalFirst; override;
  139.     procedure InternalGotoBookmark(Bookmark : Pointer); override;
  140.     procedure InternalHandleException; override;
  141.     procedure InternalInitFieldDefs; override;
  142.     procedure InternalInitRecord(Buffer : PChar); override;
  143.     procedure InternalLast; override;
  144.     procedure InternalOpen; override;
  145.     procedure InternalPost; override;
  146.     procedure InternalSetToRecord(Buffer : PChar); override;
  147.     function IsCursorOpen : Boolean; override;
  148.     procedure SetBookmarkFlag(Buffer : PChar; Value : TBookmarkFlag); override;
  149.     procedure SetBookmarkData(Buffer : PChar; Data : Pointer); override;
  150.     procedure SetFieldData(Field : TField; Buffer : Pointer); override;
  151.  
  152.     procedure CloseBlob(Field : TField); override;
  153.   protected
  154.     { Additional overrides (optional) }
  155.     function GetRecordCount : Integer; override;
  156.     function GetRecNo : Integer; override;
  157.     procedure SetRecNo(Value : Integer); override;
  158.   public
  159.     function CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer; override;
  160.     function CreateBlobStream(Field : TField; Mode : TBlobStreamMode) : TStream; override;
  161.     constructor Create(AOwner : TComponent); override;
  162.     destructor Destroy; override;
  163.     function GetFieldData(Field : TField; Buffer : Pointer) : Boolean; override;
  164.     procedure Sort(field : TField; direction : TSortType);
  165.     function Lookup(const KeyFields : string; const KeyValues : Variant;
  166.       const ResultFields : string) : Variant; override;
  167.     function Locate(const KeyFields : string; const KeyValues : Variant;
  168.       Options : TLocateOptions) : Boolean; override;
  169.     property SortOrder : TSortArray read fSortOrder;
  170.   published
  171.     property FieldRoster : TFieldDefList read FFieldRoster write FFieldRoster;
  172.     property Active;
  173.     property OnNewRecord;
  174.     property OnCalcFields;
  175.     property BeforeOpen;
  176.     property AfterOpen;
  177.     property BeforeClose;
  178.     property AfterClose;
  179.     property BeforeInsert;
  180.     property AfterInsert;
  181.     property BeforeEdit;
  182.     property AfterEdit;
  183.     property BeforePost;
  184.     property AfterPost;
  185.     property BeforeCancel;
  186.     property AfterCancel;
  187.     property BeforeDelete;
  188.     property AfterDelete;
  189.     property BeforeScroll;
  190.     property AfterScroll;
  191.     property OnDeleteError;
  192.     property OnEditError;
  193.     property OnFilterRecord;
  194.     property OnPostError;
  195.   end;
  196.  
  197. const
  198.   RecInfoSize = SizeOf(TRecordData) - SizeOf(TByteArray);
  199.  
  200. const
  201.   DefaultFieldClasses : array[ftUnknown..ftTypedBinary] of TFieldClass = (
  202.     nil, (* ftUnknown *)
  203.     TrmLongStringField, (* ftString *)
  204.     TSmallintField, (* ftSmallint *)
  205.     TIntegerField, (* ftInteger *)
  206.     TWordField, (* ftWord *)
  207.     TBooleanField, (* ftBoolean *)
  208.     TFloatField, (* ftFloat *)
  209.     TCurrencyField, (* ftCurrency *)
  210.     TBCDField, (* ftBCD *)
  211.     TDateField, (* ftDate *)
  212.     TTimeField, (* ftTime *)
  213.     TDateTimeField, (* ftDateTime *)
  214.     TBytesField, (* ftBytes *)
  215.     TVarBytesField, (* ftVarBytes *)
  216.     TAutoIncField, (* ftAutoInc *)
  217.     TBlobField, (* ftBlob *)
  218.     TMemoField, (* ftMemo *)
  219.     TGraphicField, (* ftGraphic *)
  220.     TBlobField, (* ftFmtMemo *)
  221.     TBlobField, (* ftParadoxOle *)
  222.     TBlobField, (* ftDBaseOle *)
  223.     TBlobField); (* ftTypedBinary *)
  224.  
  225. implementation
  226.  
  227. type
  228.   // TResultSetBlobStream
  229.   TResultSetBlobStream = class(TStream)
  230.   private
  231.     FField : TBlobField;
  232.     FBlobIdx : LongInt;
  233.     FDataSet : TrmMemoryDataSet;
  234.     FBuffer : PRecordData;
  235.     FMode : TBlobStreamMode;
  236.     FOpened : Boolean;
  237.     FModified : Boolean;
  238.     FPosition : Longint;
  239.     function GetBlobSize : Longint;
  240.     function GetBlobOffset(FieldNum : LongInt) : LongInt;
  241.   public
  242.     constructor Create(Field : TBlobField; Mode : TBlobStreamMode);
  243.     destructor Destroy; override;
  244.     function Read(var Buffer; Count : Longint) : Longint; override;
  245.     function Write(const Buffer; Count : Longint) : Longint; override;
  246.     function Seek(Offset : Longint; Origin : Word) : Longint; override;
  247.     procedure Truncate;
  248.   end;
  249.  
  250.  
  251. procedure FreeBlob(Buffer : PRecordData);
  252. var
  253.   Idx : LongInt;
  254. begin
  255.   for Idx := 0 to 10 do
  256.   begin
  257.     with Buffer^.Blobs[Idx] do
  258.     begin
  259.       if (BlobData <> nil) and (BlobSize > 0) then
  260.         FreeMem(BlobData, BlobSize);
  261.       BlobData := nil;
  262.       BlobSize := 0;
  263.       FieldNum := 0;
  264.     end;
  265.   end;
  266. end;
  267.  
  268. // TResultSetBlobStream
  269. // This stream is used to communicate between the Blob fields and the
  270. // underlying record collection
  271.  
  272. constructor TResultSetBlobStream.Create(Field : TBlobField; Mode : TBlobStreamMode);
  273. begin
  274.   FMode := Mode;
  275.   FField := Field;
  276.   FDataSet := FField.DataSet as TrmMemoryDataSet;
  277.   if not FDataSet.GetActiveRecBuf(FBuffer) then
  278.     Exit;
  279.   FBlobIdx := GetBlobOffset(FField.FieldNo);
  280.   if not FField.Modified then
  281.   begin
  282.     if Mode <> bmRead then
  283.       if not (FDataSet.State in [dsEdit, dsInsert]) then
  284.         DatabaseError('Not in Insert or Edit Mode');
  285.   end;
  286.   FOpened := True;
  287.   if Mode = bmWrite then
  288.   begin
  289.     Truncate;
  290.   end;
  291. end;
  292.  
  293. destructor TResultSetBlobStream.Destroy;
  294. begin
  295.   if FOpened then
  296.   begin
  297.     if FModified then FField.Modified := True;
  298.   end;
  299.   if FModified then
  300.   begin
  301.     try
  302.       FDataSet.DataEvent(deFieldChange, Longint(FField));
  303.     except
  304.       Application.HandleException(Self);
  305.     end;
  306.   end;
  307. end;
  308.  
  309. function TResultSetBlobStream.Read(var Buffer; Count : Longint) : Longint;
  310. begin
  311.   Result := 0;
  312.   if FOpened then
  313.   begin
  314.     if Count > Size - FPosition then
  315.     begin
  316.       Result := Size - FPosition
  317.     end
  318.     else
  319.     begin
  320.       Result := Count;
  321.     end;
  322.     if Result > 0 then
  323.     begin
  324.       Move(FBuffer^.Blobs[FBlobIdx].BlobData^[FPosition], Buffer, Result);
  325.       Inc(FPosition, Result);
  326.     end;
  327.   end;
  328. end;
  329.  
  330. function TResultSetBlobStream.Write(const Buffer; Count : Longint) : Longint;
  331. var
  332.   Temp : Pointer;
  333.   NewSize : LongInt;
  334. begin
  335.   Result := 0;
  336.   if FOpened then
  337.   begin
  338.     NewSize := FPosition + Count;
  339.     if NewSize < FBuffer^.Blobs[FBlobIdx].BlobSize then
  340.     begin
  341.       NewSize := FBuffer^.Blobs[FBlobIdx].BlobSize;
  342.     end;
  343.  
  344.     if (NewSize > FBuffer^.Blobs[FBlobIdx].BlobSize) or
  345.       not (FModified or FField.Modified) then
  346.     begin
  347.       GetMem(Temp, NewSize);
  348.       if (FBuffer^.Blobs[FBlobIdx].BlobData <> nil) and
  349.         (FBuffer^.Blobs[FBlobIdx].BlobSize > 0) then
  350.       begin
  351.         Move(FBuffer^.Blobs[FBlobIdx].BlobData^, Temp^, FBuffer^.Blobs[FBlobIdx].BlobSize);
  352.         if (FModified or FField.Modified) then
  353.         begin
  354.           FreeBlob(FBuffer);
  355.         end;
  356.       end;
  357.       FBuffer^.Blobs[FBlobIdx].BlobData := Temp;
  358.     end;
  359.     Move(Buffer, FBuffer^.Blobs[FBlobIdx].BlobData^[FPosition], Count);
  360.     Inc(FPosition, Count);
  361.     if FPosition > FBuffer^.Blobs[FBlobIdx].BlobSize then
  362.     begin
  363.       FBuffer^.Blobs[FBlobIdx].BlobSize := FPosition;
  364.     end;
  365.     Result := Count;
  366.     FModified := True;
  367.   end;
  368. end;
  369.  
  370. function TResultSetBlobStream.Seek(Offset : Longint; Origin : Word) : Longint;
  371. begin
  372.   case Origin of
  373.     soFromBeginning : FPosition := Offset;
  374.     soFromCurrent : Inc(FPosition, Offset);
  375.     soFromEnd : FPosition := GetBlobSize + Offset;
  376.   end;
  377.   Result := FPosition;
  378. end;
  379.  
  380. procedure TResultSetBlobStream.Truncate;
  381. begin
  382.   if FOpened then
  383.   begin
  384.     FPosition := 0;
  385.     if FField.Modified then
  386.     begin
  387.       FreeBlob(FBuffer);
  388.     end;
  389.     FBuffer^.Blobs[FBlobIdx].BlobData := nil;
  390.     FBuffer^.Blobs[FBlobIdx].BlobSize := 0;
  391.     FModified := True;
  392.   end;
  393. end;
  394.  
  395. function TResultSetBlobStream.GetBlobSize : Longint;
  396. begin
  397.   Result := 0;
  398.   if FOpened then
  399.   begin
  400.     Result := FBuffer^.Blobs[FBlobIdx].BlobSize;
  401.   end;
  402. end;
  403.  
  404. function TResultSetBlobStream.GetBlobOffset(FieldNum : LongInt) : LongInt;
  405. var
  406.   Idx : LongInt;
  407. begin
  408.   Result := -1;
  409.   for Idx := 0 to 10 do
  410.   begin
  411.     if FBuffer^.Blobs[Idx].FieldNum = FieldNum then
  412.     begin
  413.       Result := Idx;
  414.       Break;
  415.     end;
  416.   end;
  417.  
  418.   if result < 0 then
  419.     for idx := 0 to 10 do
  420.       if FBuffer^.Blobs[Idx].FieldNum = -1 then
  421.       begin
  422.         FBuffer^.Blobs[Idx].FieldNum := FieldNum;
  423.         result := idx;
  424.         break;
  425.       end;
  426.  
  427.   if result < 0 then
  428.     DatabaseError('Too many blobs', self.FDataSet);
  429. end;
  430.  
  431. (*
  432.  * TrmLongStringField - implementation
  433.  *)
  434.  
  435. class procedure TrmLongStringField.CheckTypeSize(Value : Integer);
  436. begin
  437.   (*
  438.    * Just don't check. Any string size is valid.
  439.    *)
  440. end;
  441.  
  442.  
  443.  
  444. function TrmLongStringField.GetAsString : string;
  445. begin
  446.   if not GetValue(Result) then Result := '';
  447. end;
  448.  
  449. function TrmLongStringField.GetAsVariant : Variant;
  450. var
  451.   S : string;
  452. begin
  453.   if GetValue(S) then
  454.     Result := S
  455.   else
  456.     Result := Null;
  457. end;
  458.  
  459. function TrmLongStringField.GetValue(var Value : string) : Boolean;
  460. var
  461.   Buffer : PChar;
  462. begin
  463.   GetMem(Buffer, Size + 1);
  464.   try
  465.     Result := GetData(Buffer);
  466.     if Result then
  467.     begin
  468.       Value := Trim(string(Buffer));
  469.       if Transliterate and (Value <> '') then
  470.         DataSet.Translate(PChar(Value), PChar(Value), False);
  471.     end
  472.   finally
  473.     FreeMem(Buffer);
  474.   end;
  475. end;
  476.  
  477. procedure TrmLongStringField.SetAsString(const Value : string);
  478. var
  479.   Buffer : PChar;
  480. begin
  481.   GetMem(Buffer, Size + 1);
  482.   try
  483.     StrLCopy(Buffer, PChar(Value), Size);
  484.     if Transliterate then
  485.       DataSet.Translate(Buffer, Buffer, True);
  486.     SetData(Buffer);
  487.   finally
  488.     FreeMem(Buffer);
  489.   end;
  490. end;
  491.  
  492.  
  493. destructor TTextRecord.Destroy;
  494. begin
  495.   if Data <> nil then
  496.   begin
  497.     FreeMem(Data, Data^.ArraySize + RecInfoSize);
  498.   end;
  499.   inherited;
  500. end;
  501.  
  502. { TrmMemoryDataSet }
  503.  
  504. { This method is called by TDataSet.Open and also when FieldDefs need to
  505.   be updated (usually by the DataSet designer).  Everything which is
  506.   allocated or initialized in this method should also be freed or
  507.   uninitialized in the InternalClose method. }
  508.  
  509. constructor TrmMemoryDataSet.Create(AOwner : TComponent);
  510. begin
  511.   inherited Create(AOwner);
  512.   FFieldRoster := TFieldDefList.Create(Self);
  513. end;
  514.  
  515. destructor TrmMemoryDataSet.Destroy;
  516. begin
  517.   FFieldRoster.Free;
  518.   inherited Destroy;
  519. end;
  520.  
  521.  
  522. function TrmMemoryDataSet.GetFieldClass(FieldType : TFieldType) : TFieldClass;
  523. begin
  524.   Result := DefaultFieldClasses[FieldType];
  525. end;
  526.  
  527. // Calculate Buffer Size. Can only be called after BindFields
  528.  
  529. function TrmMemoryDataSet.GetRecBufSize : Integer;
  530. var
  531.   i : Integer;
  532.  
  533. begin
  534.   MaxFieldNo := 0;
  535.   for i := 0 to FieldCount - 1 do
  536.     with Fields[i] do
  537.       if FieldNo > MaxFieldNo then
  538.         MaxFieldNo := FieldNo;
  539.   Inc(MaxFieldNo);
  540.  
  541.   GetMem(FieldOffsets, MaxFieldNo * SizeOf(Integer));
  542.  
  543.   Result := 0;
  544.   FRecordSize := 0;
  545.   for i := 0 to FieldCount - 1 do
  546.     with Fields[i] do
  547.     begin
  548.       if FieldNo >= 0 then
  549.         FieldOffsets^[FieldNo] := FRecordSize;
  550.       Inc(Result, DataSize + 1);
  551.       Inc(FRecordSize, DataSize + 1);
  552.     end;
  553.   Inc(Result, RecInfoSize);
  554. end;
  555.  
  556.  
  557. procedure TrmMemoryDataSet.QueryRecords;
  558. begin
  559.   // Clear the record collection
  560.   Records.Clear;
  561. end;
  562.  
  563. function TrmMemoryDataSet.FieldOffset(Field : TField) : Integer;
  564. begin
  565.   Result := FieldOffsets[Field.FieldNo];
  566. end;
  567.  
  568. procedure TrmMemoryDataSet.InternalOpen;
  569. begin
  570.   FieldOffsets := nil;
  571.   // Initialize our internal position.
  572.   // We use -1 to indicate the "crack" before the first record.
  573.   FCurRec := -1;
  574.   // Tell TDataSet how big our Bookmarks are
  575.   BookmarkSize := SizeOf(Integer);
  576.  
  577.   InternalInitFieldDefs;
  578.   // Create TField components when no persistent fields have been created
  579.   if DefaultFields then CreateFields;
  580.   // Bind the TField components to the physical fields
  581.   BindFields(True);
  582.   // Create collection for records
  583.   Records := TCollection.Create(TTextRecord);
  584.  
  585.   // Calculate the size of the record buffers.
  586.   // Note: This is NOT the same as the RecordSize property which
  587.   // only gets the size of the data in the record buffer
  588.   FRecBufSize := GetRecBufSize;
  589.   // Query records to fill collection
  590.   QueryRecords;
  591.   SetLength(fSortOrder, fields.Count);
  592. end;
  593.  
  594. procedure TrmMemoryDataSet.InternalClose;
  595. begin
  596.   // Free the record collection
  597.   Records.Free;
  598.   Records := nil;
  599.   // Destroy the TField components if no persistent fields
  600.   if DefaultFields then
  601.     DestroyFields;
  602.   // Reset these internal flags
  603.   FLastBookmark := 0;
  604.   FCurRec := -1;
  605.   // Free memory for Field offset array
  606.   if FieldOffsets <> nil then
  607.     FreeMem(FieldOffsets, MaxFieldNo * SizeOf(Integer));
  608.   FieldOffsets := nil;
  609. end;
  610.  
  611. function TrmMemoryDataSet.IsCursorOpen : Boolean;
  612. begin
  613.   Result := Assigned(Records);
  614. end;
  615.  
  616. procedure TrmMemoryDataSet.InternalInitFieldDefs;
  617. var
  618.   i : Integer;
  619.   FieldName : string;
  620.   FieldRequired : boolean;
  621.   FieldSize : Integer;
  622.   FieldType : TFieldType;
  623.   FieldNo : Integer;
  624.  
  625. begin
  626.   FieldDefs.Clear;
  627.  
  628.   // Create a field in the dataset for each field in the query
  629.   if FFieldRoster.Count = 0 then
  630.     raise Exception.Create('There are no fields in the Field Roster');
  631.  
  632.   FieldNo := 1;
  633.  
  634.   for i := 0 to FFieldRoster.Count - 1 do
  635.   begin
  636.     FieldName := FFieldRoster.Items[i].Name;
  637.     FieldRequired := True;
  638.     FieldSize := FFieldRoster.Items[i].Size;
  639.     case FFieldRoster.Items[i].FieldType of
  640.       fdtString : FieldType := ftString;
  641.       fdtInteger : FieldType := ftInteger;
  642.       fdtFloat : FieldType := ftFloat;
  643.       fdtDateTime : FieldType := ftDateTime;
  644.       fdtBoolean : FieldType := ftBoolean;
  645.       fdtMemo : FieldType := ftMemo;
  646.     else
  647.       FieldType := ftString;
  648.     end;
  649.  
  650.     if not (FieldType in [ftString]) then
  651.       FieldSize := 0;
  652.  
  653.     TFieldDef.Create(FieldDefs, FieldName, FieldType, FieldSize, FieldRequired, FieldNo);
  654.     Inc(FieldNo);
  655.   end;
  656. end;
  657.  
  658. procedure TrmMemoryDataSet.InternalHandleException;
  659. begin
  660.   Application.HandleException(Self);
  661. end;
  662.  
  663. procedure TrmMemoryDataSet.InternalGotoBookmark(Bookmark : Pointer);
  664. var
  665.   i, b : Integer;
  666.  
  667. begin
  668.   b := PInteger(Bookmark)^;
  669.   if (b - 1 > 0) and (b - 1 < Records.Count) then
  670.   begin
  671.     if b = TTextRecord(Records.Items[b - 1]).Data^.Bookmark then
  672.     begin
  673.       FCurRec := b - 1;
  674.       Exit;
  675.     end;
  676.   end;
  677.   for i := 0 to Records.Count - 1 do
  678.   begin
  679.     if PInteger(Bookmark)^ = TTextRecord(Records.Items[i]).Data^.Bookmark then
  680.     begin
  681.       FCurRec := i;
  682.       Exit;
  683.     end;
  684.   end;
  685.   DatabaseError('Bookmark not found');
  686. end;
  687.  
  688. procedure TrmMemoryDataSet.InternalSetToRecord(Buffer : PChar);
  689. begin
  690.   InternalGotoBookmark(@PRecordData(Buffer).Bookmark);
  691. end;
  692.  
  693. function TrmMemoryDataSet.GetBookmarkFlag(Buffer : PChar) : TBookmarkFlag;
  694. begin
  695.   Result := PRecordData(Buffer).BookmarkFlag;
  696. end;
  697.  
  698. procedure TrmMemoryDataSet.SetBookmarkFlag(Buffer : PChar; Value : TBookmarkFlag);
  699. begin
  700.   PRecordData(Buffer).BookmarkFlag := Value;
  701. end;
  702.  
  703. procedure TrmMemoryDataSet.GetBookmarkData(Buffer : PChar; Data : Pointer);
  704. begin
  705.   PInteger(Data)^ := PRecordData(Buffer).Bookmark;
  706. end;
  707.  
  708. procedure TrmMemoryDataSet.SetBookmarkData(Buffer : PChar; Data : Pointer);
  709. begin
  710.   PRecordData(Buffer).Bookmark := PInteger(Data)^;
  711. end;
  712.  
  713. function TrmMemoryDataSet.GetRecordSize : Word;
  714. begin
  715.   Result := FRecordSize;
  716. end;
  717.  
  718. function TrmMemoryDataSet.AllocRecordBuffer : PChar;
  719. var
  720.   b : PRecordData;
  721.   Idx : Integer;
  722.  
  723. begin
  724.   GetMem(Result, FRecBufSize);
  725.   b := PRecordData(Result);
  726.   b^.ArraySize := FRecBufSize - RecInfoSize;
  727.   for Idx := 0 to 10 do
  728.   begin
  729.     b^.Blobs[Idx].BlobData := nil;
  730.     b^.Blobs[Idx].BlobSize := 0;
  731.     b^.Blobs[Idx].FieldNum := 0;
  732.   end;
  733. end;
  734.  
  735. procedure TrmMemoryDataSet.FreeRecordBuffer(var Buffer : PChar);
  736. begin
  737.   FreeMem(Buffer, FRecBufSize);
  738. end;
  739.  
  740. function TrmMemoryDataSet.GetRecord(Buffer : PChar; GetMode : TGetMode;
  741.   DoCheck : Boolean) : TGetResult;
  742. begin
  743.   Result := grOK;
  744.   case GetMode of
  745.     gmNext :
  746.       if FCurRec < Records.Count - 1 then
  747.         Inc(FCurRec)
  748.       else
  749.         Result := grEOF;
  750.  
  751.     gmPrior :
  752.       if FCurRec <= 0 then
  753.         Result := grBOF
  754.       else
  755.         Dec(FCurRec);
  756.  
  757.     gmCurrent :
  758.       if (FCurRec < 0) or (FCurRec >= Records.Count) then
  759.       begin
  760.         Result := grError
  761.       end;
  762.   end;
  763.   if Result = grOK then
  764.   begin
  765.     Move(TTextRecord(Records.Items[FCurRec]).Data^, Buffer^, FRecBufSize);
  766.     with PRecordData(Buffer)^ do
  767.       BookmarkFlag := bfCurrent;
  768.   end
  769.   else
  770.     if (Result = grError) and DoCheck then
  771.       DatabaseError('No Records');
  772. end;
  773.  
  774. procedure TrmMemoryDataSet.InternalInitRecord(Buffer : PChar);
  775. var
  776.   Idx : Integer;
  777. begin
  778.   FillChar(PRecordData(Buffer)^.Bytes[0], FRecordSize + CalcFieldsSize, 1);
  779.   for Idx := 0 to 10 do
  780.   begin
  781.     PRecordData(Buffer)^.Blobs[Idx].BlobData := nil;
  782.     PRecordData(Buffer)^.Blobs[Idx].BlobSize := 0;
  783.     PRecordData(Buffer)^.Blobs[Idx].FieldNum := -1;
  784.   end;
  785. end;
  786.  
  787. function TrmMemoryDataSet.GetActiveRecBuf(var RecBuf : PRecordData) : Boolean;
  788. var
  789.   i : Integer;
  790.  
  791. begin
  792.   case State of
  793.     dsBrowse :
  794.       if IsEmpty then
  795.         RecBuf := nil
  796.       else
  797.         RecBuf := PRecordData(ActiveBuffer);
  798.  
  799.     dsEdit, dsInsert :
  800.       RecBuf := PRecordData(ActiveBuffer);
  801.  
  802.     dsNewValue,
  803.       dsCurValue :
  804.       RecBuf := PRecordData(ActiveBuffer);
  805.  
  806.     dsFilter :
  807.       RecBuf := PRecordData(FFilterBuffer);
  808.  
  809.     dsOldValue :
  810.       begin
  811.         i := FCurRec;
  812.         if i < 0 then
  813.           i := 0;
  814.         if i < Records.Count then
  815.           RecBuf := TTextRecord(Records.Items[i]).Data
  816.         else
  817.           RecBuf := nil;
  818.       end;
  819.   else
  820.     RecBuf := nil;
  821.   end;
  822.   Result := RecBuf <> nil;
  823. end;
  824.  
  825. function TrmMemoryDataSet.GetFieldData(Field : TField; Buffer : Pointer) : Boolean;
  826. var
  827.   b : PRecordData;
  828. begin
  829.   Result := False;
  830.   if not GetActiveRecBuf(b) then Exit;
  831.   if b^.Bytes[FieldOffset(Field)] = 0 then
  832.   begin
  833.     if Buffer <> nil then
  834.       Move(b^.Bytes[FieldOffset(Field) + 1], Buffer^, Field.DataSize);
  835.     Result := True;
  836.   end;
  837. end;
  838.  
  839. procedure TrmMemoryDataSet.SetFieldData(Field : TField; Buffer : Pointer);
  840. var
  841.   b : PRecordData;
  842. begin
  843.   if not GetActiveRecBuf(b) then
  844.     Exit;
  845.   if State in [dsEdit, dsInsert] then
  846.     Field.Validate(Buffer);
  847.  
  848.   if Buffer = nil then
  849.     b^.Bytes[FieldOffset(Field)] := 1
  850.   else
  851.   begin
  852.     b^.Bytes[FieldOffset(Field)] := 0;
  853.  
  854.     Move(Buffer^, b^.Bytes[FieldOffset(Field) + 1], Field.DataSize);
  855.   end;
  856.   if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
  857.     DataEvent(deFieldChange, Longint(Field));
  858. end;
  859.  
  860. procedure TrmMemoryDataSet.InternalFirst;
  861. begin
  862.   FCurRec := -1;
  863. end;
  864.  
  865. procedure TrmMemoryDataSet.InternalLast;
  866. begin
  867.   FCurRec := Records.Count;
  868. end;
  869.  
  870.  
  871. procedure TrmMemoryDataSet.SaveRecordData(Buffer : PRecordData; Index : Integer);
  872. var
  873.   b : PRecordData;
  874.   Idx : LongInt;
  875.   blobStore : TBlobArray;
  876.  
  877. begin
  878.   b := TTextRecord(Records.Items[Index]).Data;
  879.   begin
  880.     blobStore := b^.Blobs;
  881.  
  882.     Move(Buffer^, b^, FRecBufSize);
  883.     for Idx := 0 to 10 do
  884.       if Buffer^.Blobs[Idx].BlobData <> blobStore[Idx].BlobData then
  885.       begin
  886.         if blobStore[Idx].BlobData <> nil then
  887.           freeMem(blobStore[Idx].BlobData);
  888.  
  889.         GetMem(b^.Blobs[Idx].BlobData, Buffer^.Blobs[Idx].BlobSize);
  890.         Move(Buffer^.Blobs[Idx].BlobData^, b^.Blobs[Idx].BlobData^, Buffer^.Blobs[Idx].BlobSize);
  891.         b^.Blobs[Idx].BlobSize := Buffer^.Blobs[Idx].BlobSize;
  892.         b^.Blobs[Idx].FieldNum := Buffer^.Blobs[Idx].FieldNum;
  893.       end
  894.   end;
  895. end;
  896.  
  897. procedure TrmMemoryDataSet.InternalUpdate;
  898. var
  899.   b : PRecordData;
  900.  
  901. begin
  902.   if not GetActiveRecBuf(b) then
  903.     Exit;
  904.   // Update the record in the collection
  905.   SaveRecordData(b, FCurRec);
  906. end;
  907.  
  908. procedure TrmMemoryDataSet.InternalPost;
  909. var
  910.   b : PRecordData;
  911.  
  912. begin
  913.   if State = dsEdit then
  914.     InternalUpdate
  915.   else
  916.   begin
  917.     GetActiveRecBuf(b);
  918.     InternalAddRecord(b, False);
  919.   end;
  920. end;
  921.  
  922. procedure TrmMemoryDataSet.InternalAddRecord(Buffer : Pointer; Append : Boolean);
  923. var
  924.   b : PRecordData;
  925.   r : TTextRecord;
  926.  
  927. begin
  928.   if not GetActiveRecBuf(b) then
  929.     Exit;
  930.   if b <> Buffer then
  931.     raise Exception.Create('InternalAddRecord: b <> buffer');
  932.  
  933.   if Append then
  934.     InternalLast;
  935.  
  936.   r := TTextRecord.Create(Records);
  937.   if FCurRec >= 0 then
  938.     r.Index := FCurRec;
  939.  
  940.   r.Data := PRecordData(AllocRecordBuffer);
  941.   SaveRecordData(b, r.Index);
  942.   Inc(FLastBookmark);
  943.   r.Data^.Bookmark := FLastBookmark;
  944.   r.Data^.BookmarkFlag := bfCurrent;
  945. end;
  946.  
  947. procedure TrmMemoryDataSet.InternalDelete;
  948. var
  949.   b : PRecordData;
  950.  
  951. begin
  952.   if not GetActiveRecBuf(b) then
  953.     Exit;
  954.   Records.Items[FCurRec].Free;
  955. end;
  956.  
  957. function TrmMemoryDataSet.GetRecordCount : Longint;
  958. begin
  959.   Result := Records.Count;
  960. end;
  961.  
  962. function TrmMemoryDataSet.GetRecNo : Longint;
  963. begin
  964.   UpdateCursorPos;
  965.   if (FCurRec = -1) and (RecordCount > 0) then
  966.     Result := 1
  967.   else
  968.     Result := FCurRec + 1;
  969. end;
  970.  
  971. procedure TrmMemoryDataSet.SetRecNo(Value : Integer);
  972. begin
  973.   if (Value >= 0) and (Value < Records.Count) then
  974.   begin
  975.     FCurRec := Value - 1;
  976.     Resync([]);
  977.   end;
  978. end;
  979.  
  980. function TrmMemoryDataSet.CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer;
  981. begin
  982.   if (PInteger(Bookmark1)^ = PInteger(Bookmark2)^) then
  983.      Result := 0
  984.   else if (PInteger(Bookmark1)^ > PInteger(Bookmark2)^) then
  985.      Result := 1
  986.   else
  987.      Result := -1
  988. end;
  989.  
  990.  
  991. procedure TrmMemoryDataSet.Sort(field : TField; direction : TSortType);
  992. var
  993.   buffer : pointer;
  994.   dir : boolean;
  995.  
  996.   function GetRecordValue(index : integer) : variant;
  997.   var
  998.     SaveState : TDataSetState;
  999.     oldFilter : pointer;
  1000.  
  1001.   begin
  1002.     fCurRec := index;
  1003.     GetRecord(buffer, gmCurrent, FALSE); // Get the actual record
  1004.  
  1005.     oldFilter := FFilterBuffer; // Save off the old filter buffer
  1006.     SaveState := SetTempState(dsFilter); // Tell the dataset we are filtering
  1007.     try
  1008.       FFilterBuffer := pointer(buffer); // Point the filter buf to the supplied buf
  1009.       result := field.Value;
  1010.     finally
  1011.       RestoreState(SaveState); // Put the state back the way it was
  1012.       FFilterBuffer := oldFilter; // Put the old filter buffer back
  1013.     end;
  1014.   end;
  1015.  
  1016.   function CompareCell(c : integer; s2 : variant) : double;
  1017.   begin
  1018.     case field.DataType of
  1019.       ftString : result := CompareStr(GetRecordValue(c), s2);
  1020.     else
  1021.       result := GetRecordValue(c) - s2;
  1022.     end;
  1023.  
  1024.     if dir then result := -result;
  1025.   end;
  1026.  
  1027.   procedure QuickSort(iLo, iHi : Integer);
  1028.   var
  1029.     Lo, Hi : Integer;
  1030.     mid : variant;
  1031.     tempRec : PRecordData;
  1032.  
  1033.   begin
  1034.     Lo := iLo;
  1035.     Hi := iHi;
  1036.     Mid := GetRecordValue((Lo + Hi) div 2);
  1037.  
  1038.     repeat
  1039.  
  1040.       while (lo < RecordCount) and (CompareCell(Lo, Mid) < 0) do
  1041.         Inc(Lo);
  1042.       while (hi >= 0) and (CompareCell(Hi, Mid) > 0) do
  1043.         Dec(Hi);
  1044.  
  1045.       if Lo <= Hi then
  1046.       begin
  1047.         if lo <> hi then
  1048.         begin
  1049.           tempRec := TTextRecord(Records.Items[lo]).Data;
  1050.           TTextRecord(records.Items[lo]).Data := TTextRecord(records.Items[hi]).Data;
  1051.           TTextRecord(records.Items[hi]).Data := tempRec;
  1052.         end;
  1053.  
  1054.         inc(lo);
  1055.         dec(hi);
  1056.       end;
  1057.  
  1058.     until Lo > Hi;
  1059.  
  1060.     if Hi > iLo then QuickSort(iLo, Hi);
  1061.     if Lo < iHi then QuickSort(Lo, iHi);
  1062.   end;
  1063.  
  1064. begin
  1065.   if (field.IsBlob) then
  1066.     raise Exception.Create('Sorting not supported on blob fields (Field: ' + field.name + ').');
  1067.  
  1068.   case direction of
  1069.     stAscending : dir := FALSE;
  1070.     stDescending : dir := TRUE;
  1071.     stAlternate :
  1072.       begin
  1073.         dir := fSortOrder[field.Index];
  1074.         fSortOrder[field.Index] := not fSortOrder[field.Index];
  1075.       end;
  1076.   end;
  1077.  
  1078.   DisableControls;
  1079.   try
  1080.     GetMem(buffer, GetRecBufSize);
  1081.     try
  1082.       QuickSort(0, RecordCount - 1);
  1083.     except
  1084.     end;
  1085.   finally
  1086.     FreeMem(buffer);
  1087.  
  1088.     EnableControls;
  1089.     First;
  1090.   end;
  1091. end;
  1092.  
  1093. { TFieldDefList }
  1094.  
  1095. function TFieldDefList.Add : TFieldDefItem;
  1096. begin
  1097.   Result := TFieldDefItem(inherited Add);
  1098. end;
  1099.  
  1100. constructor TFieldDefList.Create(AOwner : TComponent);
  1101. begin
  1102.   inherited Create(TFieldDefItem);
  1103.   FInternalOwner := AOwner;
  1104. end;
  1105.  
  1106. function TFieldDefList.GetItem(Index : Integer) : TFieldDefItem;
  1107. begin
  1108.   Result := TFieldDefItem(inherited GetItem(Index));
  1109. end;
  1110.  
  1111. procedure TFieldDefList.SetItem(Index : Integer; Value : TFieldDefItem);
  1112. begin
  1113.   inherited SetItem(Index, Value);
  1114. end;
  1115.  
  1116. procedure TrmMemoryDataSet.CloseBlob(Field : TField);
  1117. begin
  1118.   FreeBlob(PRecordData(ActiveBuffer));
  1119. end;
  1120.  
  1121. function TrmMemoryDataSet.CreateBlobStream(Field : TField;
  1122.   Mode : TBlobStreamMode) : TStream;
  1123. begin
  1124.   Result := TResultSetBlobStream.Create(Field as TBlobField, Mode);
  1125. end;
  1126.  
  1127. function VarEquals(const V1, V2 : Variant) : Boolean;
  1128. begin
  1129.   Result := False;
  1130.   try
  1131.     Result := V1 = V2;
  1132.   except
  1133.   end;
  1134. end;
  1135.  
  1136. function TrmMemoryDataSet.Lookup(const KeyFields : string;
  1137.   const KeyValues : Variant; const ResultFields : string) : Variant;
  1138. var
  1139.   b : TBookMark;
  1140.   keyf : TField;
  1141.  
  1142. begin
  1143.   result := FALSE;
  1144.  
  1145.   b := GetBookmark;
  1146.   DisableControls;
  1147.   try
  1148.     keyf := fieldByName(KeyFields);
  1149.     first;
  1150.     while not eof do
  1151.     begin
  1152.       if VarEquals(keyf.value, keyValues) then
  1153.       begin
  1154.         result := fieldByName(resultFields).Value;
  1155.         break;
  1156.       end;
  1157.  
  1158.       moveBy(1);
  1159.     end;
  1160.   finally
  1161.     GotoBookmark(b);
  1162.     EnableControls;
  1163.   end;
  1164. end;
  1165.  
  1166. function TrmMemoryDataSet.Locate(const KeyFields : string;
  1167.   const KeyValues : Variant; Options : TLocateOptions) : Boolean;
  1168. var
  1169.   b : TBookMark;
  1170.   keyf : TField;
  1171.  
  1172. begin
  1173.   result := FALSE;
  1174.  
  1175.   b := GetBookmark;
  1176.   DisableControls;
  1177.   try
  1178.     keyf := fieldByName(KeyFields);
  1179.     first;
  1180.     while not eof do
  1181.     begin
  1182.       if VarEquals(keyf.value, KeyValues) then
  1183.       begin
  1184.         result := TRUE;
  1185.         b := nil;
  1186.         break;
  1187.       end;
  1188.  
  1189.       MoveBy(1);
  1190.     end;
  1191.   finally
  1192.     if b <> nil then
  1193.       GotoBookmark(b);
  1194.     EnableControls;
  1195.   end;
  1196. end;
  1197.  
  1198. { TFieldDefItem }
  1199.  
  1200. procedure TFieldDefItem.Assign(Source: TPersistent);
  1201. begin
  1202.   if (Source is TFieldDefItem) then
  1203.   begin
  1204.     FName := TFieldDefItem(Source).Name;
  1205.     FSize := TFieldDefItem(Source).Size;
  1206.     FFieldType := TFieldDefItem(Source).FieldType;
  1207.   end
  1208.   else
  1209.     inherited Assign(Source);
  1210. end;
  1211.  
  1212. initialization
  1213.  
  1214.   RegisterClass(TrmLongStringField);
  1215.  
  1216. end.
  1217.  
  1218.