home *** CD-ROM | disk | FTP | other *** search
/ Chip 1999 March / Chip_1999-03_cd.bin / zkuste / delphi / D / KBMMEMTA.ZIP / Dev / tmemtable / MemTable.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1999-01-26  |  23.9 KB  |  857 lines

  1. unit MemTable;
  2.  
  3. // TMEMTABLE v. 1.02
  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. //=============================================================================
  41.  
  42. {$ifndef VER100} // CBuilder only
  43. {$ObjExportAll On}
  44. {$ASSERTIONS ON}
  45. {$endif}
  46.  
  47. interface
  48.  
  49. uses SysUtils,Classes,Db;
  50.  
  51. type
  52.     EMemTableError = class(Exception);
  53.  
  54.     TRecInfo=record
  55.         Bookmark: longint;
  56.         RecordNo: integer;
  57.         BookmarkFlag: TBookmarkFlag;
  58.     end;
  59.     PRecInfo=^TRecInfo;
  60.  
  61. {
  62. Internal buffer layout:
  63. +------------------------+------------------------+---------------------------+
  64. |     RECORD DATA        |    Rec.Information     |     Calculated Fields     |
  65. | Record length bytes    |  SizeOf(TRecInfo) bytes|    CalcFieldSize bytes    |
  66. +------------------------+------------------------+---------------------------+
  67.                          ^                        ^
  68.                     StartRecInfo              StartCalculated
  69. }
  70.  
  71.   TMemTableSaveFlag = (mtfSaveData, mtfSaveCalculated, mtfSaveLookup,mtfSaveNonVisible);
  72.   TMemTableSaveFlags = set of TMemTableSaveFlag;
  73.  
  74.   TMemTable = class(TDataSet)
  75.   private
  76.         FIsOpen:                                Boolean;
  77.         FRecNo:                                 integer;
  78.         FFilterBuffer:                          PChar;
  79.         FRecords:                               TList;
  80.         FBufferSize,
  81.         FStartRecInfo,
  82.         FStartCalculated:integer;
  83.         FRecordSize:                            integer;
  84.         FFieldOfs:                              array [0..255] of integer;
  85.         FReadOnly:                              boolean;
  86.         function GetActiveRecordBuffer:         PChar;
  87.         function FilterRecord(Buffer: PChar):   Boolean;
  88.         procedure _InternalAdd(Buffer:Pointer);
  89.         procedure _InternalDelete(Pos:integer);
  90.         procedure _InternalInsert(Pos:integer; Buffer:Pointer);
  91.         procedure _InternalEmpty;
  92.   protected
  93.         procedure InternalOpen; override;
  94.         procedure InternalClose; override;
  95.         procedure InternalFirst;override;
  96.         procedure InternalLast;override;
  97.  
  98.         procedure InternalAddRecord(Buffer: Pointer; Append: Boolean); override;
  99.         procedure InternalDelete; override;
  100.         procedure InternalInitRecord(Buffer: PChar); override;
  101.         procedure InternalPost; override;
  102.  
  103.         procedure InternalInitFieldDefs; override;
  104.         procedure InternalSetToRecord(Buffer: PChar); override;
  105.  
  106.         function IsCursorOpen: Boolean; override;
  107.         function GetCanModify: Boolean; override;
  108.         function GetRecordSize: Word;override;
  109.         function GetRecordCount: integer;override;
  110.  
  111.         function AllocRecordBuffer: PChar; override;
  112.         procedure FreeRecordBuffer(var Buffer: PChar); override;
  113.  
  114.         function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
  115.         procedure SetFieldData(Field: TField; Buffer: Pointer);override;
  116.  
  117.         function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
  118.  
  119.         function GetRecNo: integer;override;
  120.         procedure SetRecNo(Value: integer);override;
  121.  
  122.         function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
  123.         procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
  124.         procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
  125.         procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
  126.         procedure InternalGotoBookmark(Bookmark: Pointer); override;
  127.  
  128.         procedure InternalHandleException; override;
  129.   public
  130.         constructor Create(AOwner: TComponent); override;
  131.         destructor Destroy; override;
  132.         procedure LoadFromFile(const FileName: string);
  133.         procedure LoadFromStream(Stream: TStream);
  134.         procedure SaveToFile(const FileName: string; flags:TMemTableSaveFlags);
  135.         procedure SaveToStream(Stream: TStream; flags:TMemTableSaveFlags);
  136.         procedure EmptyTable;
  137.   published
  138.         property Active;
  139.         property Filtered;
  140.         property ReadOnly:boolean read FReadOnly write FReadOnly default false;
  141.         property BeforeOpen;
  142.         property AfterOpen;
  143.         property BeforeClose;
  144.         property AfterClose;
  145.         property BeforeInsert;
  146.         property AfterInsert;
  147.         property BeforeEdit;
  148.         property AfterEdit;
  149.         property BeforePost;
  150.         property AfterPost;
  151.         property BeforeCancel;
  152.         property AfterCancel;
  153.         property BeforeDelete;
  154.         property AfterDelete;
  155.         property BeforeScroll;
  156.         property AfterScroll;
  157.         property OnCalcFields;
  158.         property OnDeleteError;
  159.         property OnEditError;
  160.         property OnFilterRecord;
  161.         property OnNewRecord;
  162.         property OnPostError;
  163.   end;
  164.  
  165. procedure Register;
  166.  
  167. implementation
  168.  
  169. uses
  170.   TypInfo, Dialogs, Windows, Forms, Controls, IniFiles;
  171.  
  172. constructor TMemTable.Create(AOwner: TComponent);
  173. begin
  174.      inherited Create(AOwner);
  175.      FRecords:=TList.Create;
  176. end;
  177.  
  178. destructor TMemTable.Destroy;
  179. begin
  180.      // Delete allocated records.
  181.      _InternalEmpty;
  182.      FRecords.free;
  183.      FRecords:=nil;
  184.  
  185.      inherited Destroy;
  186. end;
  187.  
  188. procedure TMemTable._InternalAdd(Buffer:Pointer);
  189. begin
  190.      FRecords.Add(Buffer);
  191. end;
  192.  
  193. procedure TMemTable._InternalInsert(Pos:integer; Buffer:Pointer);
  194. var
  195.    i:integer;
  196.    b:PChar;
  197. begin
  198.      FRecords.Insert(Pos,Buffer);
  199.  
  200.      for i:=Pos+1 to FRecords.Count-1 do
  201.      begin
  202.           b:=FRecords.Items[i];
  203.           inc(PRecInfo(b+FStartRecInfo).RecordNo);
  204.      end;
  205. end;
  206.  
  207. procedure TMemTable._InternalDelete(Pos:integer);
  208. var
  209.    i:integer;
  210.    b:PChar;
  211. begin
  212.      FreeMem(FRecords.Items[Pos]);
  213.      FRecords.Delete(Pos);
  214.  
  215.      for i:=Pos to FRecords.Count-1 do
  216.      begin
  217.           b:=FRecords.Items[i];
  218.           dec(PRecInfo(b+FStartRecInfo)^.RecordNo);
  219.      end;
  220. end;
  221.  
  222. // Purge all records.
  223. procedure TMemTable._InternalEmpty;
  224. var
  225.    i:integer;
  226. begin
  227.      for i:=0 to FRecords.Count-1 do FreeMem(FRecords[i]);
  228.      FRecords.Clear;
  229. end;
  230.  
  231. procedure TMemTable.InternalOpen;
  232. var
  233.    i: integer;
  234. begin
  235.      // Calculate recordsize and field offsets.
  236.      FRecordSize:=0;
  237.      for i:=0 to FieldCount - 1 do
  238.          with TField(Fields[i]) do
  239.               if FieldKind = fkData then
  240.               begin
  241.                    FFieldOfs[i]:=FRecordSize;
  242.                    inc(FRecordSize,DataSize+1); // 1.st byte is boolean flag for Null or not.
  243.               end;
  244.  
  245.      InternalInitFieldDefs;
  246.      BindFields(True);
  247.      FRecNo:=-1;
  248.      BookmarkSize:=sizeof(longint);
  249.      FStartRecInfo:=FRecordSize;
  250.      FStartCalculated:=FStartRecInfo+SizeOf(TRecInfo);
  251.      FBufferSize:=FRecordSize+Sizeof(TRecInfo)+CalcFieldsSize;
  252.      FIsOpen:=True;
  253. end;
  254.  
  255. procedure TMemTable.InternalClose;
  256. begin
  257.      _InternalEmpty;
  258.      FIsOpen:=False;
  259.      BindFields(False);
  260. end;
  261.  
  262. procedure TMemTable.InternalInitFieldDefs;
  263. var
  264.    i:integer;
  265. begin
  266.      FieldDefs.clear;
  267.      for i:=0 to Fieldcount-1 do
  268.      begin
  269.           FieldDefs.Add(Fields[i].FieldName,Fields[i].DataType,Fields[i].Size,Fields[i].Required);
  270.      end;
  271. end;
  272.  
  273. function TMemTable.GetActiveRecordBuffer:  PChar;
  274. begin
  275.      case State of
  276.           dsBrowse:        if IsEmpty then
  277.                               Result := nil
  278.                            else
  279.                               Result := ActiveBuffer;
  280.           dsCalcFields:    Result := CalcBuffer;
  281.           dsFilter:        Result:=FFilterBuffer;
  282.           dsEdit,dsInsert: Result:=ActiveBuffer;
  283.      else
  284.           Result:=nil;
  285.      end;
  286. end;
  287.  
  288. // Result is data in the buffer and a boolean return (true=not null, false=is null).
  289. function TMemTable.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
  290. var
  291.    SourceBuffer: PChar;
  292. begin
  293.      Result:=False;
  294.      SourceBuffer:=GetActiveRecordBuffer;
  295.      if not FIsOpen or (SourceBuffer=nil) then Exit;
  296.      if (Field.FieldKind=fkCalculated) or (Field.FieldKind=fkLookup) then
  297.         Inc(SourceBuffer,FStartCalculated+Field.Offset)
  298.      else
  299.         Inc(SourceBuffer,FFieldOfs[Field.FieldNo-1]);
  300.  
  301.      if Assigned(Buffer) then Move(SourceBuffer[1], Buffer^, Field.DataSize);
  302.      Result:=boolean(SourceBuffer[0]);
  303. end;
  304.  
  305. procedure TMemTable.SetFieldData(Field: TField; Buffer: Pointer);
  306. var
  307.    DestinationBuffer: PChar;
  308. begin
  309.      DestinationBuffer:=GetActiveRecordBuffer;
  310.  
  311.      // Is it a calculated/lookup field or a real datafield?
  312.      if (Field.FieldKind=fkCalculated) or (Field.FieldKind=fkLookup) then
  313.           Inc(DestinationBuffer,FStartCalculated+Field.Offset)
  314.      else
  315.           Inc(DestinationBuffer,FFieldOfs[Field.FieldNo-1]);
  316.  
  317.     Boolean(DestinationBuffer[0]):=(Buffer<>nil);
  318.  
  319.     if Assigned(Buffer) then
  320.        Move(Buffer^,DestinationBuffer[1],Field.DataSize);
  321.  
  322.     DataEvent (deFieldChange, Longint(Field));
  323. end;
  324.  
  325. function TMemTable.IsCursorOpen: Boolean;
  326. begin
  327.      Result:=FIsOpen;
  328. end;
  329.  
  330. function TMemTable.GetCanModify: Boolean;
  331. begin
  332.      Result:=not FReadOnly;
  333. end;
  334.  
  335. function TMemTable.GetRecordSize: Word;
  336. begin
  337.      Result:=FRecordSize;
  338. end;
  339.  
  340. function TMemTable.AllocRecordBuffer: PChar;
  341. begin
  342.      GetMem(Result,FBufferSize);
  343.      FillChar(Result^,FBufferSize,0);
  344. end;
  345.  
  346. procedure TMemTable.FreeRecordBuffer(var Buffer: PChar);
  347. begin
  348.      FreeMem(Buffer);
  349. end;
  350.  
  351. procedure TMemTable.InternalFirst;
  352. begin
  353.      FRecNo:=-1;
  354. end;
  355.  
  356. procedure TMemTable.InternalLast;
  357. begin
  358.      FRecNo:=FRecords.Count;
  359. end;
  360.  
  361. function TMemTable.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
  362. var
  363.    Acceptable: Boolean;
  364. begin
  365.      Result:=grOK;
  366.      Acceptable:=False;
  367.      repeat
  368.          begin
  369.               case GetMode of
  370.                    gmCurrent: begin
  371.                                    if FRecNo>=FRecords.Count then Result:=grEOF
  372.                                    else if FRecNo<0 then Result:=grBOF
  373.                                    else Result:=grOk;
  374.                               end;
  375.                    gmNext:    begin
  376.                                    if FrecNo<FRecords.Count-1 then
  377.                                    begin
  378.                                         Inc(FRecNo);
  379.                                         Result:=grOK;
  380.                                    end
  381.                                    else Result:=grEOF;
  382.                               end;
  383.                    gmPrior:   begin
  384.                                    if FrecNo>0 then
  385.                                    begin
  386.                                         Dec(FRecNo);
  387.                                         Result:=grOK;
  388.                                    end
  389.                                    else Result:=grBOF;
  390.                               end;
  391.               end;
  392.               if Result=grOk then
  393.               begin
  394.                       //fill TARrecord part of buffer
  395.                       Move(FRecords.Items[FRecNo]^,Buffer^,FBufferSize);
  396.  
  397.                       //fill information part of buffer
  398.                       with PRecInfo(Buffer+FStartRecInfo)^ do
  399.                       begin
  400.                            RecordNo:=FRecNo;
  401.                            BookmarkFlag:=bfCurrent;
  402.                       end;
  403.  
  404.                       //fill calc fields part of buffer
  405.                       ClearCalcFields(Buffer);
  406.                       GetCalcFields(Buffer);
  407.                       Acceptable:=FilterRecord(Buffer);
  408.                       if (GetMode=gmCurrent) and not Acceptable then Result:=grError;
  409.               end
  410.          end;
  411.      until (Result<>grOk) or Acceptable;
  412. end;
  413.  
  414. function TMemTable.FilterRecord(Buffer: PChar): Boolean;
  415. var
  416.    SaveState: TDatasetState;
  417. begin
  418.      Result:=True;
  419.      if not Filtered or not Assigned(OnFilterRecord) then Exit;
  420.      SaveState:=SetTempState(dsFilter);
  421.      FFilterBuffer:=Buffer;
  422.      OnFilterRecord(self,Result);
  423.      RestoreState(SaveState);
  424. end;
  425.  
  426. procedure TMemTable.InternalSetToRecord(Buffer: PChar);
  427. begin
  428.      FRecNo:=PRecInfo(Buffer+FStartRecInfo).RecordNo;
  429. end;
  430.  
  431. function TMemTable.GetRecordCount: integer;
  432. var
  433.    SaveState: TDataSetState;
  434.    SavePosition: integer;
  435.    TempBuffer: PChar;
  436. begin
  437.      if not Filtered then Result:=FRecords.Count
  438.      else
  439.      begin
  440.           Result:=0;
  441.           SaveState:=SetTempState(dsBrowse);
  442.           SavePosition:=FRecNo;
  443.           try
  444.              TempBuffer:=AllocRecordBuffer;
  445.              InternalFirst;
  446.              while GetRecord(TempBuffer,gmNext,True)=grOk do Inc(Result);
  447.           finally
  448.              RestoreState(SaveState);
  449.              FRecNo:=SavePosition;
  450.              FreeRecordBuffer(TempBuffer);
  451.           end;
  452.      end;
  453. end;
  454.  
  455. function TMemTable.GetRecNo: integer;
  456. var
  457.    SaveState: TDataSetState;
  458.    SavePosition: integer;
  459.    TempBuffer: PChar;
  460. begin
  461.      if not Filtered then Result:=FRecNo
  462.      else
  463.      begin
  464.           Result:=0;
  465.           SaveState:=SetTempState(dsBrowse);
  466.           SavePosition:=FRecNo;
  467.           try
  468.              TempBuffer:=AllocRecordBuffer;
  469.              InternalFirst;
  470.              repeat
  471.                    if GetRecord(TempBuffer,gmNext,True)=grOk then Inc(Result);
  472.              until PRecInfo(TempBuffer+FStartRecInfo).RecordNo=SavePosition
  473.           finally
  474.              RestoreState(SaveState);
  475.              FRecNo:=SavePosition;
  476.              FreeRecordBuffer(TempBuffer);
  477.           end;
  478.      end;
  479. end;
  480.  
  481. procedure TMemTable.SetRecNo(Value: Integer);
  482. var
  483.    SaveState: TDataSetState;
  484.    SavePosition: integer;
  485.    TempBuffer: PChar;
  486. begin
  487.      if not Filtered then FRecNo:=Value
  488.      else
  489.      begin
  490.           SaveState:=SetTempState(dsBrowse);
  491.           SavePosition:=FRecNo;
  492.           try
  493.              TempBuffer:=AllocRecordBuffer;
  494.              InternalFirst;
  495.              repeat
  496.                    begin
  497.                         if GetRecord(TempBuffer,gmNext,True)=grOk then Dec(Value)
  498.                         else
  499.                         begin
  500.                              FRecNo:=SavePosition;
  501.                              break;
  502.                         end;
  503.                    end;
  504.              until Value=0;
  505.           finally
  506.              RestoreState(SaveState);
  507.              FreeRecordBuffer(TempBuffer);
  508.           end;
  509.      end;
  510. end;
  511.  
  512. procedure TMemTable.InternalAddRecord(Buffer: Pointer; Append: Boolean);
  513. var
  514.    b:Pointer;
  515. begin
  516.      // Allocate room for buffer in list.
  517.      GetMem(b,FBufferSize);
  518.      Move(Buffer^, b^, FBufferSize);
  519.      if Append then
  520.         _InternalAdd(b)
  521.      else
  522.          _InternalInsert(FRecNo,b);
  523. end;
  524.  
  525. procedure TMemTable.InternalDelete;
  526. begin
  527.      _InternalDelete(FRecNo);
  528. end;
  529.  
  530. procedure TMemTable.InternalInitRecord(Buffer: PChar);
  531. begin
  532.      FillChar(Buffer^,FBufferSize,0);
  533.      PRecInfo(Buffer+FStartRecInfo)^.RecordNo:=FRecNo;
  534. end;
  535.  
  536. procedure TMemTable.InternalPost;
  537. var
  538.    b:pointer;
  539.    n:integer;
  540. begin
  541.      n:=PRecInfo(ActiveBuffer+FStartRecInfo)^.RecordNo;
  542.      if State = dsEdit then
  543.         Move(ActiveBuffer^, FRecords.Items[n]^, FBufferSize)
  544.      else
  545.      begin
  546.           GetMem(b,FBufferSize);
  547.           Move(ActiveBuffer^, b^, FBufferSize);
  548.           if GetBookmarkFlag(b) = bfEOF then
  549.              _InternalAdd(b)
  550.           else
  551.              _InternalInsert(n,b);
  552.      end;
  553. end;
  554.  
  555. procedure TMemTable.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
  556. begin
  557.      PRecInfo(Buffer + FStartRecInfo).BookmarkFlag := Value;
  558. end;
  559.  
  560. function TMemTable.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
  561. begin
  562.      Result:=PRecInfo(Buffer+FStartRecInfo).BookmarkFlag;
  563. end;
  564.  
  565. procedure TMemTable.GetBookmarkData(Buffer: PChar; Data: Pointer);
  566. begin
  567.      PInteger(Data)^ := PRecInfo(Buffer + FStartRecInfo).Bookmark;
  568. end;
  569.  
  570. procedure TMemTable.SetBookmarkData(Buffer: PChar; Data: Pointer);
  571. begin
  572.      PRecInfo(Buffer + FStartRecInfo).Bookmark := PInteger(Data)^;
  573. end;
  574.  
  575. procedure TMemTable.InternalGotoBookmark (Bookmark: Pointer);
  576. var
  577.   ReqBookmark: Integer;
  578. begin
  579.      ReqBookmark := PInteger (Bookmark)^;
  580.      if (ReqBookmark >= 0) and (ReqBookmark < RecordCount) then
  581.         FRecNo := ReqBookmark
  582.      else
  583.         raise eMemTableError.Create('Bookmark ' + IntToStr(ReqBookmark) + ' not found');
  584. end;
  585.  
  586. procedure TMemTable.InternalHandleException;
  587. begin
  588.      Application.HandleException(Self);
  589. end;
  590.  
  591. procedure TMemTable.SaveToFile(const FileName: string; flags:TMemTableSaveFlags);
  592. var
  593.   Stream: TStream;
  594. begin
  595.   Stream := TFileStream.Create(FileName, fmCreate);
  596.   try
  597.     SaveToStream(Stream,flags);
  598.   finally
  599.     Stream.Free;
  600.   end;
  601. end;
  602.  
  603. procedure TMemTable.SaveToStream(Stream: TStream; flags:TMemTableSaveFlags);
  604. var
  605.   i:integer;
  606.   bm:TBookmark;
  607.   nf:integer;
  608.   s,a:string;
  609.   l:integer;
  610.   fset,f:^Boolean;
  611. begin
  612.   bm:=GetBookmark;
  613.  
  614.   try
  615.      DisableControls;
  616.  
  617.      // Setup flags for fields to save.
  618.      nf:=Fieldcount;
  619.      GetMem(fset,nf * sizeof(boolean));
  620.      f:=fset;
  621.      for i:=0 to nf-1 do
  622.      begin
  623.           f^:=false;
  624.           case Fields[i].FieldKind of
  625.                fkData: if mtfSaveData in flags then f^:=true;
  626.                fkCalculated: if mtfSaveCalculated in flags then f^:=true;
  627.                fkLookup: if mtfSaveLookup in flags then f^:=true;
  628.                else f^:=true;
  629.           end;
  630.           if not (Fields[i].Visible or (mtfSaveNonVisible in flags)) then f^:=false;
  631.           inc(f);
  632.      end;
  633.  
  634.      // Write all fieldnames in CSV format.
  635.      s:='';
  636.      a:='';
  637.      f:=fset;
  638.      for i:=0 to nf-1 do
  639.      begin
  640.           if f^ then
  641.           begin
  642.                s:=s+a+AnsiQuotedStr(PChar(Fields[i].Name),'"');
  643.                a:=',';
  644.           end;
  645.           inc(f);
  646.      end;
  647.      s:=s+#13+#10;
  648.      l:=length(s);
  649.      Stream.Write(Pointer(s)^, l);
  650.  
  651.      // Write all records in CSV format.
  652.      first;
  653.      while not EOF do
  654.      begin
  655.           // Write current record.
  656.           s:='';
  657.           a:='';
  658.           f:=fset;
  659.           for i:=0 to nf-1 do
  660.           begin
  661.                if f^ then
  662.                begin
  663.                     if (Fields[i].IsNull) then s:=s+a
  664.                     else s:=s+a+AnsiQuotedStr(PChar(Fields[i].AsString),'"');
  665.                     a:=',';
  666.                end;
  667.                inc(f);
  668.           end;
  669.           s:=s+#13+#10;
  670.           l:=length(s);
  671.           Stream.WriteBuffer(Pointer(s)^, l);
  672.  
  673.           // Next record.
  674.           next;
  675.      end;
  676.   finally
  677.      GotoBookmark(bm);
  678.      EnableControls;
  679.      FreeBookmark(bm);
  680.      FreeMem(fset);
  681.   end;
  682. end;
  683.  
  684. procedure TMemTable.LoadFromFile(const FileName: string);
  685. var
  686.   Stream: TStream;
  687. begin
  688.   Stream := TFileStream.Create(FileName, fmOpenRead);
  689.   try
  690.     LoadFromStream(Stream);
  691.   finally
  692.     Stream.Free;
  693.   end;
  694. end;
  695.  
  696. procedure TMemTable.LoadFromStream(Stream: TStream);
  697. const
  698.    BUFSIZE=8192;
  699. var
  700.    i:integer;
  701.    bm:TBookmark;
  702.    nf:integer;
  703.    s:string;
  704.    size:integer;
  705.    buf,ptr:PChar;
  706.    buflen:integer;
  707.    Line:string;
  708.    lptr,elptr:PChar;
  709.    null:boolean;
  710.  
  711.    function GetLine:boolean;
  712.    var
  713.      Start: PChar;
  714.      sz:integer;
  715.    begin
  716.         // If less than 512 bytes left in buffer, fill up the buffer.
  717.         // Notice: This means that if a line is longer than 1024 bytes it could fail.
  718.         sz:=BUFSIZE-(ptr-buf);
  719.         if (sz<512) then
  720.         begin
  721.              // Move the rest of data to the start of the buffer.
  722.              if (sz>0) then Move(ptr,buf,sz);
  723.              ptr:=buf+sz;
  724.  
  725.              // Fill up the buffer.
  726.              buflen:=BUFSIZE;
  727.              if Stream.Size<buflen then buflen:=Stream.Size;
  728.              buflen:=Stream.Read(Pointer(buf)^,buflen-sz)+sz;
  729.              ptr:=buf;
  730.         end;
  731.  
  732.         // Check if finished.
  733.         if ((ptr-buf) = buflen) then
  734.         begin
  735.              Result:=false;
  736.              exit;
  737.         end;
  738.  
  739.         // Cut out a line.
  740.         Start := ptr;
  741.         while not (ptr^ in [#0, #10, #13]) do Inc(ptr);
  742.         SetString(Line, Start, ptr - Start);
  743.         lptr:=PChar(Line);
  744.         elptr:=PChar(Line)+Length(Line)-1;
  745.         if ptr^ = #13 then Inc(ptr);
  746.         if ptr^ = #10 then Inc(ptr);
  747.         Result:=true;
  748.    end;
  749.  
  750.    function GetWord(var null:boolean):string;
  751.    var
  752.      P, Start: PChar;
  753.      s: string;
  754.      sz:integer;
  755.      l:integer;
  756.    label
  757.      L_exit;
  758.    begin
  759.  
  760.      // Cut out next word.
  761.      Start := lptr;
  762.  
  763.      Result:='';
  764.  
  765.      // Look for starting " or ,.
  766.      while (lptr^ <> '"') and (lptr^ <> ',') and (lptr<elptr) do inc(lptr);
  767.      if (lptr>=elptr) then exit;
  768.      if (lptr^ = ',') then
  769.      begin
  770.           null:=true;
  771.           inc(lptr);
  772.           exit;
  773.      end
  774.      else null:=false;
  775.      inc(lptr);
  776.      Start:=lptr;
  777.  
  778.      while true do
  779.      begin
  780.           // Look for ending ".
  781.           while not (lptr^ = '"') do
  782.           begin
  783.                if (lptr>=elptr) then goto L_exit;
  784.                Result:=Result+lptr^;
  785.                inc(lptr);
  786.           end;
  787.           inc(lptr);
  788.  
  789.           // Is it a double "" or end of word ?.
  790.           if (lptr^ = '"') then
  791.           begin
  792.                Result:=Result+'"';
  793.                inc(lptr);
  794.                continue;
  795.           end;
  796.  
  797. L_exit:
  798.           // Found end, remove comma's if any.
  799.           while (lptr<elptr) and (lptr^ = ',') do inc(lptr);
  800.           break;
  801.      end;
  802.    end;
  803.  
  804. begin
  805.   bm:=GetBookmark;
  806.  
  807.   try
  808.      // Allocate space for a buffer.
  809.      GetMem(buf,BUFSIZE);
  810.  
  811.      // Place pointer at end of buffer to notify getword to read a chunk of streamdata.
  812.      ptr:=buf+BUFSIZE;
  813.  
  814.      // Read data from stream.
  815.      nf:=Fieldcount;
  816.  
  817.      // Read headerline and skip it.
  818.      GetLine;
  819.  
  820.      DisableControls;
  821.  
  822.      // Read all lines in CSV format.
  823.      while GetLine do
  824.      begin
  825.           append;
  826.  
  827.           i:=0;
  828.           while (lptr<elptr) and (i<nf) do
  829.           begin
  830.                s:=GetWord(null);
  831.                if null then Fields[i].Clear
  832.                else Fields[i].AsString:=s;
  833.                inc(i);
  834.           end;
  835.  
  836.           post;
  837.      end;
  838.   finally
  839.      FreeMem(buf);
  840.      GotoBookmark(bm);
  841.      EnableControls;
  842.      FreeBookmark(bm);
  843.   end;
  844. end;
  845.  
  846. procedure TMemTable.EmptyTable;
  847. begin
  848.      _InternalEmpty;
  849. end;
  850.  
  851. procedure Register;
  852. begin
  853.      RegisterComponents('Data Access', [TMemTable]);
  854. end;
  855.  
  856. end.
  857.