home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 23 / IOPROG_23.ISO / SOFT / MEMTABLE.ZIP / kbmMemTable.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1999-02-04  |  29.1 KB  |  1,027 lines

  1. unit kbmMemTable;
  2.  
  3. // TKbmMemTable v. 1.04
  4. // =========================================================================
  5. // An inmemory temporary table.
  6. // Can be used as a demonstration of how to create descendents of TDataSet,
  7. // or as in my case, to allow a program to generate temporary data that can
  8. // be used directly by all data aware controls.
  9. //
  10. // Copyright 1999 Kim Bo Madsen/Optical Services - Scandinavia
  11. // All rights reserved.
  12. //
  13. // You are allowed to used this component in any project for free.
  14. // You are NOT allowed to claim that you have created this component or to
  15. // copy its code into your own component and claim that is was your idea.
  16. // Im offering this for free for your convinience, and the ONLY thing I request
  17. // is to get an e-mail about what project this component (or dirived versions)
  18. // is used for. That will be my reward of offering this component for free to you!
  19. //
  20. // You dont need to state my name in your software, although it would be
  21. // appreciated if you do.
  22. //
  23. // If you find bugs or alter the component (f.ex. see suggested enhancements
  24. // further down), please DONT just send the corrected/new code out on the internet,
  25. // but instead send it to me, so I can put it into the official version. You will
  26. // be acredited if you do so.
  27. //
  28. //
  29. // DISCLAIMER
  30. // By using this component or parts theiroff you are accepting the full
  31. // responsibility of the use. You are understanding that the author cant be
  32. // made responsible in any way for any problems occuring using this component.
  33. // You also recognize the author as the creator of this component and agrees
  34. // not to claim otherwize!
  35. //
  36. // Please forward corrected versions (source code ONLY!), comments,
  37. // and emails saying you are using it for this or that project to:
  38. //            kbm@optical.dk
  39. //
  40. // Suggestions for future enhancements:
  41. //
  42. //      - Index handling functions.
  43. //      - Sorting.
  44. //      - Support for SetKey, FindKey, FindNearest
  45. //      - Support for MasterField, Lookupfields.
  46. //      - IDE designer for adding static data to the memtable.
  47. //
  48. // History:
  49. //
  50. //1.00:    The first release. Was created due to a need for a component like this.
  51. //                                                                    (15. Jan. 99)
  52. //1.01:    The first update. Release 1.00 contained some bugs related to the ordering
  53. //    of records inserted and to bookmarks. Problems fixed.         (21. Jan. 99)
  54.  
  55. //1.02:    Fixed handling of NULL values. Added SaveToStream, SaveToFile,
  56. //    LoadFromStream and LoadFromFile. SaveToStream and SaveToFile is controlled
  57. //    by a flag telling if to save data, contents of calculated fields,
  58. //    contents of lookupfields and contents of non visible fields.
  59. //    Added an example application with Delphi 3 source code.       (26. Jan. 99)
  60. //
  61. //1.03: Claude Rieth from Computer Team sarl (clrieth@team.lu) came up with an
  62. //      implementation of CommaText and made a validation check in _InternalInsert.
  63. //      Because I allready have implemented the saveto.... functions, I decided
  64. //      to implement Claude's idea using my own saveto.... functions. (27. Jan. 99)
  65. //      I have decided to rename the component, because Claude let me know that
  66. //      the RX library have a component with the same name as this.
  67. //      Thus in the future the component will be named TkbmMemTable.
  68. //      SaveToStream and LoadFromStream now set up date and decimal separator
  69. //      temporary to make sure that the data saved can be loaded on another
  70. //      installation with different date and decimal separator setups.
  71. //      Added EmptyTable method to clear the contents of the memory table.
  72. //
  73. //1.04: Wagner ADP (wagner@cads-informatica.com.br) found a bug in the _internalinsert
  74. //      procedure which he came up with a fix for.                     (4. Feb. 99)
  75. //      Added support for the TDataset protected function findrecord.
  76. //      Added support for CreateTable, DeleteTable.
  77. //=============================================================================
  78.  
  79. {$ifndef VER100} // CBuilder only
  80. {$ObjExportAll On}
  81. {$ASSERTIONS ON}
  82. {$endif}
  83.  
  84. interface
  85.  
  86. uses SysUtils,Classes,Db;
  87.  
  88. type
  89.     EMemTableError = class(Exception);
  90.  
  91.     TRecInfo=record
  92.         Bookmark: longint;
  93.         RecordNo: integer;
  94.         BookmarkFlag: TBookmarkFlag;
  95.     end;
  96.     PRecInfo=^TRecInfo;
  97.  
  98. {
  99. Internal buffer layout:
  100. +------------------------+------------------------+---------------------------+
  101. |     RECORD DATA        |    Rec.Information     |     Calculated Fields     |
  102. | Record length bytes    |  SizeOf(TRecInfo) bytes|    CalcFieldSize bytes    |
  103. +------------------------+------------------------+---------------------------+
  104.                          ^                        ^
  105.                     StartRecInfo              StartCalculated
  106. }
  107.  
  108.   TkbmMemTableSaveFlag = (mtfSaveData, mtfSaveCalculated, mtfSaveLookup,mtfSaveNonVisible);
  109.   TkbmMemTableSaveFlags = set of TkbmMemTableSaveFlag;
  110.  
  111.   TkbmMemTable = class(TDataSet)
  112.   private
  113.         FIsOpen:                                Boolean;
  114.         FRecNo:                                 integer;
  115.         FFilterBuffer:                          PChar;
  116.         FRecords:                               TList;
  117.         FBufferSize,
  118.         FStartRecInfo,
  119.         FStartCalculated:integer;
  120.         FRecordSize:                            integer;
  121.         FFieldOfs:                              array [0..255] of integer;
  122.         FReadOnly:                              boolean;
  123.         function GetActiveRecordBuffer:         PChar;
  124.         function FilterRecord(Buffer: PChar):   Boolean;
  125.         procedure _InternalAdd(Buffer:Pointer);
  126.         procedure _InternalDelete(Pos:integer);
  127.         procedure _InternalInsert(Pos:integer; Buffer:Pointer);
  128.         procedure _InternalEmpty;
  129.         procedure _InternalFirst;
  130.         procedure _InternalLast;
  131.         function  _InternalNext:boolean;
  132.         function  _InternalPrior:boolean;
  133.   protected
  134.         procedure InternalOpen; override;
  135.         procedure InternalClose; override;
  136.         procedure InternalFirst;override;
  137.         procedure InternalLast;override;
  138.  
  139.         procedure InternalAddRecord(Buffer: Pointer; Append: Boolean); override;
  140.         procedure InternalDelete; override;
  141.         procedure InternalInitRecord(Buffer: PChar); override;
  142.         procedure InternalPost; override;
  143.  
  144.         procedure InternalInitFieldDefs; override;
  145.         procedure InternalSetToRecord(Buffer: PChar); override;
  146.  
  147.         function IsCursorOpen: Boolean; override;
  148.         function GetCanModify: Boolean; override;
  149.         function GetRecordSize: Word;override;
  150.         function GetRecordCount: integer;override;
  151.  
  152.         function AllocRecordBuffer: PChar; override;
  153.         procedure FreeRecordBuffer(var Buffer: PChar); override;
  154.  
  155.         function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
  156.         procedure SetFieldData(Field: TField; Buffer: Pointer);override;
  157.  
  158.         function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
  159.         function FindRecord(Restart, GoForward: Boolean): Boolean; override;
  160.  
  161.         function GetRecNo: integer;override;
  162.         procedure SetRecNo(Value: integer);override;
  163.  
  164.         function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
  165.         procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
  166.         procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
  167.         procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
  168.         procedure InternalGotoBookmark(Bookmark: Pointer); override;
  169.  
  170.         procedure InternalHandleException; override;
  171.  
  172.         procedure SetCommaText(AString: String);
  173.         function GetCommaText: String;
  174.   public
  175.         constructor Create(AOwner: TComponent); override;
  176.         destructor Destroy; override;
  177.         procedure CreateTable;
  178.         procedure DeleteTable;
  179.         procedure LoadFromFile(const FileName: string);
  180.         procedure LoadFromStream(Stream: TStream);
  181.         procedure SaveToFile(const FileName: string; flags:TkbmMemTableSaveFlags);
  182.         procedure SaveToStream(Stream: TStream; flags:TkbmMemTableSaveFlags);
  183.         procedure EmptyTable;
  184.         property CommaText:string read GetCommaText write SetCommaText;
  185.   published
  186.         property Active;
  187.         property Filtered;
  188.         property ReadOnly:boolean read FReadOnly write FReadOnly default false;
  189.         property BeforeOpen;
  190.         property AfterOpen;
  191.         property BeforeClose;
  192.         property AfterClose;
  193.         property BeforeInsert;
  194.         property AfterInsert;
  195.         property BeforeEdit;
  196.         property AfterEdit;
  197.         property BeforePost;
  198.         property AfterPost;
  199.         property BeforeCancel;
  200.         property AfterCancel;
  201.         property BeforeDelete;
  202.         property AfterDelete;
  203.         property BeforeScroll;
  204.         property AfterScroll;
  205.         property OnCalcFields;
  206.         property OnDeleteError;
  207.         property OnEditError;
  208.         property OnFilterRecord;
  209.         property OnNewRecord;
  210.         property OnPostError;
  211.   end;
  212.  
  213. procedure Register;
  214.  
  215. implementation
  216.  
  217. uses
  218.   TypInfo, Dialogs, Windows, Forms, Controls, IniFiles;
  219.  
  220. constructor TkbmMemTable.Create(AOwner: TComponent);
  221. begin
  222.      inherited Create(AOwner);
  223.      FRecords:=TList.Create;
  224. end;
  225.  
  226. destructor TkbmMemTable.Destroy;
  227. begin
  228.      inherited Destroy;
  229.  
  230.      // Delete allocated records.
  231.      _InternalEmpty;
  232.      FRecords.free;
  233.      FRecords:=nil;
  234. end;
  235.  
  236. procedure TkbmMemTable.CreateTable;
  237. var
  238.    i:Integer;
  239. begin
  240.      CheckInactive;
  241.  
  242.      // If no fielddefs existing, use the previously defined fields.
  243.      if FieldDefs.Count = 0 then
  244.         for i:=0 to FieldCount-1 do
  245.             with Fields[i] do
  246.                  if FieldKind = fkData then
  247.                     FieldDefs.Add(FieldName, DataType, Size, Required);
  248.  
  249.      // Remove previously defined fields and create new from fielddefs.
  250.      DestroyFields;
  251.      CreateFields;
  252. end;
  253.  
  254. procedure TkbmMemTable.DeleteTable;
  255. begin
  256.      CheckInactive;
  257.      DestroyFields;
  258. end;
  259.  
  260. procedure TkbmMemTable._InternalAdd(Buffer:Pointer);
  261. begin
  262.      FRecords.Add(Buffer);
  263. end;
  264.  
  265. procedure TkbmMemTable._InternalInsert(Pos:integer; Buffer:Pointer);
  266. var
  267.    i:integer;
  268.    b:PChar;
  269. begin
  270.      if Pos<0 then Pos:=0;
  271.      if (Pos = FRecords.Count) or(Pos = -1) then
  272.         FRecords.Add(Buffer)
  273.      else
  274.         FRecords.Insert(Pos,Buffer);
  275.  
  276.      for i:=Pos+1 to FRecords.Count-1 do
  277.      begin
  278.           b:=FRecords.Items[i];
  279.           inc(PRecInfo(b+FStartRecInfo).RecordNo);
  280.      end;
  281. end;
  282.  
  283. procedure TkbmMemTable._InternalDelete(Pos:integer);
  284. var
  285.    i:integer;
  286.    b:PChar;
  287. begin
  288.      FreeMem(FRecords.Items[Pos]);
  289.      FRecords.Delete(Pos);
  290.  
  291.      for i:=Pos to FRecords.Count-1 do
  292.      begin
  293.           b:=FRecords.Items[i];
  294.           dec(PRecInfo(b+FStartRecInfo)^.RecordNo);
  295.      end;
  296. end;
  297.  
  298. // Purge all records.
  299. procedure TkbmMemTable._InternalEmpty;
  300. var
  301.    i:integer;
  302. begin
  303.      for i:=0 to FRecords.Count-1 do FreeMem(FRecords[i]);
  304.      FRecords.Clear;
  305. end;
  306.  
  307. procedure TkbmMemTable.InternalOpen;
  308. var
  309.    i: integer;
  310. begin
  311.      // Calculate recordsize and field offsets.
  312.      FRecordSize:=0;
  313.      for i:=0 to FieldCount - 1 do
  314.          with TField(Fields[i]) do
  315.               if FieldKind = fkData then
  316.               begin
  317.                    FFieldOfs[i]:=FRecordSize;
  318.                    inc(FRecordSize,DataSize+1); // 1.st byte is boolean flag for Null or not.
  319.               end;
  320.  
  321.      InternalInitFieldDefs;
  322.      BindFields(True);
  323.      FRecNo:=-1;
  324.      BookmarkSize:=sizeof(longint);
  325.      FStartRecInfo:=FRecordSize;
  326.      FStartCalculated:=FStartRecInfo+SizeOf(TRecInfo);
  327.      FBufferSize:=FRecordSize+Sizeof(TRecInfo)+CalcFieldsSize;
  328.      FIsOpen:=True;
  329. end;
  330.  
  331. procedure TkbmMemTable.InternalClose;
  332. begin
  333.      _InternalEmpty;
  334.      FIsOpen:=False;
  335.      BindFields(False);
  336. end;
  337.  
  338. procedure TkbmMemTable.InternalInitFieldDefs;
  339. var
  340.    i:integer;
  341. begin
  342.      FieldDefs.clear;
  343.      for i:=0 to Fieldcount-1 do
  344.      begin
  345.           FieldDefs.Add(Fields[i].FieldName,Fields[i].DataType,Fields[i].Size,Fields[i].Required);
  346.      end;
  347. end;
  348.  
  349. function TkbmMemTable.GetActiveRecordBuffer:  PChar;
  350. begin
  351.      case State of
  352.           dsBrowse:        if IsEmpty then
  353.                               Result := nil
  354.                            else
  355.                               Result := ActiveBuffer;
  356.           dsCalcFields:    Result := CalcBuffer;
  357.           dsFilter:        Result:=FFilterBuffer;
  358.           dsEdit,dsInsert: Result:=ActiveBuffer;
  359.      else
  360.           Result:=nil;
  361.      end;
  362. end;
  363.  
  364. // Result is data in the buffer and a boolean return (true=not null, false=is null).
  365. function TkbmMemTable.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
  366. var
  367.    SourceBuffer: PChar;
  368. begin
  369.      Result:=False;
  370.      SourceBuffer:=GetActiveRecordBuffer;
  371.      if not FIsOpen or (SourceBuffer=nil) then Exit;
  372.      if (Field.FieldKind=fkCalculated) or (Field.FieldKind=fkLookup) then
  373.         Inc(SourceBuffer,FStartCalculated+Field.Offset)
  374.      else
  375.         Inc(SourceBuffer,FFieldOfs[Field.FieldNo-1]);
  376.  
  377.      if Assigned(Buffer) then Move(SourceBuffer[1], Buffer^, Field.DataSize);
  378.      Result:=boolean(SourceBuffer[0]);
  379. end;
  380.  
  381. procedure TkbmMemTable.SetFieldData(Field: TField; Buffer: Pointer);
  382. var
  383.    DestinationBuffer: PChar;
  384. begin
  385.      DestinationBuffer:=GetActiveRecordBuffer;
  386.  
  387.      // Is it a calculated/lookup field or a real datafield?
  388.      if (Field.FieldKind=fkCalculated) or (Field.FieldKind=fkLookup) then
  389.           Inc(DestinationBuffer,FStartCalculated+Field.Offset)
  390.      else
  391.           Inc(DestinationBuffer,FFieldOfs[Field.FieldNo-1]);
  392.  
  393.     Boolean(DestinationBuffer[0]):=(Buffer<>nil);
  394.  
  395.     if Assigned(Buffer) then
  396.        Move(Buffer^,DestinationBuffer[1],Field.DataSize);
  397.  
  398.     DataEvent (deFieldChange, Longint(Field));
  399. end;
  400.  
  401. function TkbmMemTable.IsCursorOpen: Boolean;
  402. begin
  403.      Result:=FIsOpen;
  404. end;
  405.  
  406. function TkbmMemTable.GetCanModify: Boolean;
  407. begin
  408.      Result:=not FReadOnly;
  409. end;
  410.  
  411. function TkbmMemTable.GetRecordSize: Word;
  412. begin
  413.      Result:=FRecordSize;
  414. end;
  415.  
  416. function TkbmMemTable.AllocRecordBuffer: PChar;
  417. begin
  418.      GetMem(Result,FBufferSize);
  419.      FillChar(Result^,FBufferSize,0);
  420. end;
  421.  
  422. procedure TkbmMemTable.FreeRecordBuffer(var Buffer: PChar);
  423. begin
  424.      FreeMem(Buffer);
  425. end;
  426.  
  427. procedure TkbmMemTable.InternalFirst;
  428. begin
  429.      _InternalFirst;
  430. end;
  431.  
  432. procedure TkbmMemTable.InternalLast;
  433. begin
  434.      _InternalLast;
  435. end;
  436.  
  437. procedure TkbmMemTable._InternalFirst;
  438. begin
  439.      FRecNo:=-1;
  440. end;
  441.  
  442. procedure TkbmMemTable._InternalLast;
  443. begin
  444.      FRecNo:=FRecords.Count;
  445. end;
  446.  
  447. function TkbmMemTable._InternalNext:boolean;
  448. begin
  449.      if FrecNo<FRecords.Count-1 then
  450.      begin
  451.           Inc(FRecNo);
  452.           Result:=true;
  453.      end
  454.      else Result:=false;
  455. end;
  456.  
  457. function TkbmMemTable._InternalPrior:boolean;
  458. begin
  459.      if FrecNo>0 then
  460.      begin
  461.           Dec(FRecNo);
  462.           Result:=true;
  463.      end
  464.      else Result:=false;
  465. end;
  466.  
  467. function TkbmMemTable.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
  468. var
  469.    Acceptable: Boolean;
  470. begin
  471.      Result:=grOK;
  472.      Acceptable:=False;
  473.      repeat
  474.          begin
  475.               case GetMode of
  476.                    gmCurrent: begin
  477.                                    if FRecNo>=FRecords.Count then Result:=grEOF
  478.                                    else if FRecNo<0 then Result:=grBOF
  479.                                    else Result:=grOk;
  480.                               end;
  481.                    gmNext:    begin
  482.                                    if _InternalNext then Result:=grOK
  483.                                    else Result:=grEOF;
  484.                               end;
  485.                    gmPrior:   begin
  486.                                    if _InternalPrior then Result:=grOK
  487.                                    else Result:=grBOF;
  488.                               end;
  489.               end;
  490.               if Result=grOk then
  491.               begin
  492.                       //fill TARrecord part of buffer
  493.                       Move(FRecords.Items[FRecNo]^,Buffer^,FBufferSize);
  494.  
  495.                       //fill information part of buffer
  496.                       with PRecInfo(Buffer+FStartRecInfo)^ do
  497.                       begin
  498.                            RecordNo:=FRecNo;
  499.                            BookmarkFlag:=bfCurrent;
  500.                       end;
  501.  
  502.                       //fill calc fields part of buffer
  503.                       ClearCalcFields(Buffer);
  504.                       GetCalcFields(Buffer);
  505.                       Acceptable:=FilterRecord(Buffer);
  506.                       if (GetMode=gmCurrent) and not Acceptable then Result:=grError;
  507.               end
  508.          end;
  509.      until (Result<>grOk) or Acceptable;
  510. end;
  511.  
  512. function TkbmMemTable.FindRecord(Restart, GoForward: Boolean): Boolean;
  513. var
  514.    Status:boolean;
  515. begin
  516.      CheckBrowseMode;
  517.      DoBeforeScroll;
  518.      SetFound(False);
  519.      UpdateCursorPos;
  520.      CursorPosChanged;
  521.  
  522.      if GoForward then
  523.      begin
  524.           if Restart then _InternalFirst;
  525.           Status := _InternalNext;
  526.      end else
  527.      begin
  528.           if Restart then _InternalLast;
  529.           Status := _InternalPrior;
  530.      end;
  531.  
  532.      if Status then
  533.      begin
  534.           Resync([rmExact, rmCenter]);
  535.           SetFound(True);
  536.      end;
  537.      Result := Found;
  538.      if Result then DoAfterScroll;
  539. end;
  540.  
  541. function TkbmMemTable.FilterRecord(Buffer: PChar): Boolean;
  542. var
  543.    SaveState: TDatasetState;
  544. begin
  545.      Result:=True;
  546.      if not Filtered or not Assigned(OnFilterRecord) then Exit;
  547.      SaveState:=SetTempState(dsFilter);
  548.      FFilterBuffer:=Buffer;
  549.      OnFilterRecord(self,Result);
  550.      RestoreState(SaveState);
  551. end;
  552.  
  553. procedure TkbmMemTable.InternalSetToRecord(Buffer: PChar);
  554. begin
  555.      FRecNo:=PRecInfo(Buffer+FStartRecInfo).RecordNo;
  556. end;
  557.  
  558. function TkbmMemTable.GetRecordCount: integer;
  559. var
  560.    SaveState: TDataSetState;
  561.    SavePosition: integer;
  562.    TempBuffer: PChar;
  563. begin
  564.      if not Filtered then Result:=FRecords.Count
  565.      else
  566.      begin
  567.           Result:=0;
  568.           SaveState:=SetTempState(dsBrowse);
  569.           SavePosition:=FRecNo;
  570.           try
  571.              TempBuffer:=AllocRecordBuffer;
  572.              InternalFirst;
  573.              while GetRecord(TempBuffer,gmNext,True)=grOk do Inc(Result);
  574.           finally
  575.              RestoreState(SaveState);
  576.              FRecNo:=SavePosition;
  577.              FreeRecordBuffer(TempBuffer);
  578.           end;
  579.      end;
  580. end;
  581.  
  582. function TkbmMemTable.GetRecNo: integer;
  583. var
  584.    SaveState: TDataSetState;
  585.    SavePosition: integer;
  586.    TempBuffer: PChar;
  587. begin
  588.      if not Filtered then Result:=FRecNo
  589.      else
  590.      begin
  591.           Result:=0;
  592.           SaveState:=SetTempState(dsBrowse);
  593.           SavePosition:=FRecNo;
  594.           try
  595.              TempBuffer:=AllocRecordBuffer;
  596.              InternalFirst;
  597.              repeat
  598.                    if GetRecord(TempBuffer,gmNext,True)=grOk then Inc(Result);
  599.              until PRecInfo(TempBuffer+FStartRecInfo).RecordNo=SavePosition
  600.           finally
  601.              RestoreState(SaveState);
  602.              FRecNo:=SavePosition;
  603.              FreeRecordBuffer(TempBuffer);
  604.           end;
  605.      end;
  606. end;
  607.  
  608. procedure TkbmMemTable.SetRecNo(Value: Integer);
  609. var
  610.    SaveState: TDataSetState;
  611.    SavePosition: integer;
  612.    TempBuffer: PChar;
  613. begin
  614.      if not Filtered then FRecNo:=Value
  615.      else
  616.      begin
  617.           SaveState:=SetTempState(dsBrowse);
  618.           SavePosition:=FRecNo;
  619.           try
  620.              TempBuffer:=AllocRecordBuffer;
  621.              InternalFirst;
  622.              repeat
  623.                    begin
  624.                         if GetRecord(TempBuffer,gmNext,True)=grOk then Dec(Value)
  625.                         else
  626.                         begin
  627.                              FRecNo:=SavePosition;
  628.                              break;
  629.                         end;
  630.                    end;
  631.              until Value=0;
  632.           finally
  633.              RestoreState(SaveState);
  634.              FreeRecordBuffer(TempBuffer);
  635.           end;
  636.      end;
  637. end;
  638.  
  639. procedure TkbmMemTable.InternalAddRecord(Buffer: Pointer; Append: Boolean);
  640. var
  641.    b:Pointer;
  642. begin
  643.      // Allocate room for buffer in list.
  644.      GetMem(b,FBufferSize);
  645.      Move(Buffer^, b^, FBufferSize);
  646.      if Append then
  647.         _InternalAdd(b)
  648.      else
  649.          _InternalInsert(FRecNo,b);
  650. end;
  651.  
  652. procedure TkbmMemTable.InternalDelete;
  653. begin
  654.      _InternalDelete(FRecNo);
  655. end;
  656.  
  657. procedure TkbmMemTable.InternalInitRecord(Buffer: PChar);
  658. begin
  659.      FillChar(Buffer^,FBufferSize,0);
  660.      PRecInfo(Buffer+FStartRecInfo)^.RecordNo:=FRecNo;
  661. end;
  662.  
  663. procedure TkbmMemTable.InternalPost;
  664. var
  665.    b:pointer;
  666.    n:integer;
  667. begin
  668.      n:=PRecInfo(ActiveBuffer+FStartRecInfo)^.RecordNo;
  669.      if State = dsEdit then
  670.         Move(ActiveBuffer^, FRecords.Items[n]^, FBufferSize)
  671.      else
  672.      begin
  673.           GetMem(b,FBufferSize);
  674.           Move(ActiveBuffer^, b^, FBufferSize);
  675.           if GetBookmarkFlag(b) = bfEOF then
  676.              _InternalAdd(b)
  677.           else
  678.              _InternalInsert(n,b);
  679.      end;
  680. end;
  681.  
  682. procedure TkbmMemTable.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
  683. begin
  684.      PRecInfo(Buffer + FStartRecInfo).BookmarkFlag := Value;
  685. end;
  686.  
  687. function TkbmMemTable.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
  688. begin
  689.      Result:=PRecInfo(Buffer+FStartRecInfo).BookmarkFlag;
  690. end;
  691.  
  692. procedure TkbmMemTable.GetBookmarkData(Buffer: PChar; Data: Pointer);
  693. begin
  694.      PInteger(Data)^ := PRecInfo(Buffer + FStartRecInfo).Bookmark;
  695. end;
  696.  
  697. procedure TkbmMemTable.SetBookmarkData(Buffer: PChar; Data: Pointer);
  698. begin
  699.      PRecInfo(Buffer + FStartRecInfo).Bookmark := PInteger(Data)^;
  700. end;
  701.  
  702. procedure TkbmMemTable.InternalGotoBookmark (Bookmark: Pointer);
  703. var
  704.   ReqBookmark: Integer;
  705. begin
  706.      ReqBookmark := PInteger (Bookmark)^;
  707.      if (ReqBookmark >= 0) and (ReqBookmark < RecordCount) then
  708.         FRecNo := ReqBookmark
  709.      else
  710.         raise eMemTableError.Create('Bookmark ' + IntToStr(ReqBookmark) + ' not found');
  711. end;
  712.  
  713. procedure TkbmMemTable.InternalHandleException;
  714. begin
  715.      Application.HandleException(Self);
  716. end;
  717.  
  718. procedure TkbmMemTable.SaveToFile(const FileName: string; flags:TkbmMemTableSaveFlags);
  719. var
  720.   Stream: TStream;
  721. begin
  722.   Stream := TFileStream.Create(FileName, fmCreate);
  723.   try
  724.     SaveToStream(Stream,flags);
  725.   finally
  726.     Stream.Free;
  727.   end;
  728. end;
  729.  
  730. procedure TkbmMemTable.SaveToStream(Stream: TStream; flags:TkbmMemTableSaveFlags);
  731. var
  732.   i:integer;
  733.   bm:TBookmark;
  734.   nf:integer;
  735.   s,a:string;
  736.   l:integer;
  737.   fset,f:^Boolean;
  738.   Ods,Oms:char;
  739. begin
  740.   // Setup standard layout for data.
  741.   Ods:=DateSeparator;
  742.   Oms:=DecimalSeparator;
  743.   DateSeparator:='/';
  744.   DecimalSeparator:='.';
  745.  
  746.   bm:=GetBookmark;
  747.   fset:=nil;
  748.   try
  749.      DisableControls;
  750.  
  751.      // Setup flags for fields to save.
  752.      nf:=Fieldcount;
  753.      GetMem(fset,nf * sizeof(boolean));
  754.      f:=fset;
  755.      for i:=0 to nf-1 do
  756.      begin
  757.           f^:=false;
  758.           case Fields[i].FieldKind of
  759.                fkData: if mtfSaveData in flags then f^:=true;
  760.                fkCalculated: if mtfSaveCalculated in flags then f^:=true;
  761.                fkLookup: if mtfSaveLookup in flags then f^:=true;
  762.                else f^:=true;
  763.           end;
  764.           if not (Fields[i].Visible or (mtfSaveNonVisible in flags)) then f^:=false;
  765.           inc(f);
  766.      end;
  767.  
  768.      // Write all field display names in CSV format.
  769.      s:='';
  770.      a:='';
  771.      f:=fset;
  772.      for i:=0 to nf-1 do
  773.      begin
  774.           if f^ then
  775.           begin
  776.                s:=s+a+AnsiQuotedStr(PChar(Fields[i].DisplayName),'"');
  777.                a:=',';
  778.           end;
  779.           inc(f);
  780.      end;
  781.      s:=s+#13+#10;
  782.      l:=length(s);
  783.      Stream.Write(Pointer(s)^, l);
  784.  
  785.      // Write all records in CSV format.
  786.      first;
  787.      while not EOF do
  788.      begin
  789.           // Write current record.
  790.           s:='';
  791.           a:='';
  792.           f:=fset;
  793.           for i:=0 to nf-1 do
  794.           begin
  795.                if f^ then
  796.                begin
  797.                     if (Fields[i].IsNull) then s:=s+a
  798.                     else s:=s+a+AnsiQuotedStr(PChar(Fields[i].AsString),'"');
  799.                     a:=',';
  800.                end;
  801.                inc(f);
  802.           end;
  803.           s:=s+#13+#10;
  804.           l:=length(s);
  805.           Stream.WriteBuffer(Pointer(s)^, l);
  806.  
  807.           // Next record.
  808.           next;
  809.      end;
  810.   finally
  811.      GotoBookmark(bm);
  812.      EnableControls;
  813.      FreeBookmark(bm);
  814.      if fset<>nil then FreeMem(fset);
  815.      DateSeparator:=Ods;
  816.      DecimalSeparator:=Oms;
  817.   end;
  818. end;
  819.  
  820. procedure TkbmMemTable.LoadFromFile(const FileName: string);
  821. var
  822.   Stream: TStream;
  823. begin
  824.   Stream := TFileStream.Create(FileName, fmOpenRead);
  825.   try
  826.     LoadFromStream(Stream);
  827.   finally
  828.     Stream.Free;
  829.   end;
  830. end;
  831.  
  832. procedure TkbmMemTable.LoadFromStream(Stream: TStream);
  833. const
  834.    BUFSIZE=8192;
  835. var
  836.    i:integer;
  837.    bm:TBookmark;
  838.    nf:integer;
  839.    s:string;
  840.    buf,ptr:PChar;
  841.    buflen:integer;
  842.    Line:string;
  843.    lptr,elptr:PChar;
  844.    null:boolean;
  845.    Ods,Oms:char;
  846.  
  847.    function GetLine:boolean;
  848.    var
  849.      Start: PChar;
  850.      sz:integer;
  851.    begin
  852.         // If less than 1024 bytes left in buffer, fill up the buffer.
  853.         // Notice: This means that if a line is longer than 1024 bytes it could fail.
  854.         sz:=BUFSIZE-(ptr-buf);
  855.         if (sz<1024) then
  856.         begin
  857.              // Move the rest of data to the start of the buffer.
  858.              if (sz>0) then Move(ptr,buf,sz);
  859.              ptr:=buf+sz;
  860.  
  861.              // Fill up the buffer.
  862.              buflen:=BUFSIZE;
  863.              if Stream.Size<buflen then buflen:=Stream.Size;
  864.              buflen:=Stream.Read(Pointer(buf)^,buflen-sz)+sz;
  865.              ptr:=buf;
  866.         end;
  867.  
  868.         // Check if finished.
  869.         if ((ptr-buf) = buflen) then
  870.         begin
  871.              Result:=false;
  872.              exit;
  873.         end;
  874.  
  875.         // Cut out a line.
  876.         Start := ptr;
  877.         while not (ptr^ in [#0, #10, #13]) do Inc(ptr);
  878.         SetString(Line, Start, ptr - Start);
  879.         lptr:=PChar(Line);
  880.         elptr:=PChar(Line)+Length(Line)-1;
  881.         if ptr^ = #13 then Inc(ptr);
  882.         if ptr^ = #10 then Inc(ptr);
  883.         Result:=true;
  884.    end;
  885.  
  886.    function GetWord(var null:boolean):string;
  887.    label
  888.      L_exit;
  889.    begin
  890.  
  891.      // Cut out next word.
  892.      Result:='';
  893.  
  894.      // Look for starting " or ,.
  895.      while (lptr^ <> '"') and (lptr^ <> ',') and (lptr<elptr) do inc(lptr);
  896.      if (lptr>=elptr) then exit;
  897.      if (lptr^ = ',') then
  898.      begin
  899.           null:=true;
  900.           inc(lptr);
  901.           exit;
  902.      end
  903.      else null:=false;
  904.      inc(lptr);
  905.  
  906.      while true do
  907.      begin
  908.           // Look for ending ".
  909.           while not (lptr^ = '"') do
  910.           begin
  911.                if (lptr>=elptr) then goto L_exit;
  912.                Result:=Result+lptr^;
  913.                inc(lptr);
  914.           end;
  915.           inc(lptr);
  916.  
  917.           // Is it a double "" or end of word ?.
  918.           if (lptr^ = '"') then
  919.           begin
  920.                Result:=Result+'"';
  921.                inc(lptr);
  922.                continue;
  923.           end;
  924.  
  925. L_exit:
  926.           // Found end, remove comma's if any.
  927.           while (lptr<elptr) and (lptr^ = ',') do inc(lptr);
  928.           break;
  929.      end;
  930.    end;
  931.  
  932. begin
  933.   // Setup standard layout for data.
  934.   Ods:=DateSeparator;
  935.   Oms:=DecimalSeparator;
  936.   DateSeparator:='/';
  937.   DecimalSeparator:='.';
  938.  
  939.   bm:=GetBookmark;
  940.  
  941.   try
  942.      // Allocate space for a buffer.
  943.      GetMem(buf,BUFSIZE);
  944.  
  945.      // Place pointer at end of buffer to notify getword to read a chunk of streamdata.
  946.      ptr:=buf+BUFSIZE;
  947.  
  948.      // Read data from stream.
  949.      nf:=Fieldcount;
  950.  
  951.      // Read headerline and skip it.
  952.      GetLine;
  953.  
  954.      DisableControls;
  955.  
  956.      // Read all lines in CSV format.
  957.      while GetLine do
  958.      begin
  959.           append;
  960.  
  961.           i:=0;
  962.           while (lptr<elptr) and (i<nf) do
  963.           begin
  964.                s:=GetWord(null);
  965.                if null then Fields[i].Clear
  966.                else Fields[i].AsString:=s;
  967.                inc(i);
  968.           end;
  969.  
  970.           post;
  971.      end;
  972.   finally
  973.      FreeMem(buf);
  974.      GotoBookmark(bm);
  975.      EnableControls;
  976.      FreeBookmark(bm);
  977.      DateSeparator:=Ods;
  978.      DecimalSeparator:=Oms;
  979.   end;
  980. end;
  981.  
  982. procedure TkbmMemTable.EmptyTable;
  983. begin
  984.      _InternalEmpty;
  985. end;
  986.  
  987. procedure TkbmMemTable.SetCommaText(AString: String);
  988. var
  989.    stream:TMemoryStream;
  990. begin
  991.      EmptyTable;
  992.      stream:=TMemoryStream.Create;
  993.      try
  994.         stream.Write(Pointer(AString)^,length(AString));
  995.         stream.Seek(0,soFromBeginning);
  996.         LoadFromStream(stream);
  997.      finally
  998.         stream.free;
  999.      end;
  1000. end;
  1001.  
  1002. function TkbmMemTable.GetCommaText: String;
  1003. var
  1004.    stream:TMemoryStream;
  1005.    sz:integer;
  1006.    p:PChar;
  1007. begin
  1008.      Result:='';
  1009.      stream:=TMemoryStream.Create;
  1010.      try
  1011.         SaveToStream(stream,[mtfSaveData]);
  1012.         stream.Seek(0,soFromBeginning);
  1013.         sz:=stream.Size;
  1014.         p:=stream.Memory;
  1015.         setstring(Result,p,sz);
  1016.      finally
  1017.         stream.free;
  1018.      end;
  1019. end;
  1020.  
  1021. procedure Register;
  1022. begin
  1023.      RegisterComponents('Data Access', [TkbmMemTable]);
  1024. end;
  1025.  
  1026. end.
  1027.