MdDsStream.pas
上传用户:fh681027
上传日期:2022-07-23
资源大小:1959k
文件大小:9k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. unit MdDsStream;
  2. interface
  3. uses
  4.   Classes, Db, MdDsCustom;
  5. type
  6.   TMdDataFileHeader = record
  7.     VersionNumber: Integer;
  8.     RecordSize: Integer;
  9.     RecordCount: Integer;
  10.   end;
  11.   TMdDataSetStream = class(TMdCustomDataSet)
  12.   private
  13.     procedure SetTableName(const Value: string);
  14.   protected
  15.     FDataFileHeader: TMdDataFileHeader;
  16.     FDataFileHeaderSize, // file header size
  17.     FRecordCount: Integer; // current number of records
  18.     FStream: TStream; // the physical table
  19.     FTableName: string; // table path and file name
  20.     FFieldOffset: TList; // field offsets in the buffer
  21.   protected
  22.     // open and close
  23.     procedure InternalPreOpen; override;
  24.     procedure InternalAfterOpen; override;
  25.     procedure InternalClose; override;
  26.     procedure InternalInitFieldDefs; override;
  27.     // edit support
  28.     procedure InternalAddRecord(Buffer: Pointer; Append: Boolean); override;
  29.     procedure InternalPost; override;
  30.     // fields
  31.     procedure SetFieldData(Field: TField; Buffer: Pointer); override;
  32.     // custom dataset virutal methods
  33.     function InternalRecordCount: Integer; override;
  34.     procedure InternalLoadCurrentRecord(Buffer: PChar); override;
  35.   public
  36.     procedure CreateTable;
  37.     function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
  38.   published
  39.     property TableName: string read FTableName write SetTableName;
  40.   end;
  41. procedure Register;
  42. implementation
  43. uses
  44.   TypInfo, IniFiles, SysUtils;
  45. /////////////////////////////////////////////////
  46. ////// Part I:
  47. ////// Initialization, opening, and closing
  48. /////////////////////////////////////////////////
  49. const
  50.   HeaderVersion = 10;
  51. // I: open the table/file
  52. procedure TMdDataSetStream.InternalPreOpen;
  53. begin
  54.   // the size of the header
  55.   FDataFileHeaderSize := sizeOf (TMdDataFileHeader);
  56.   // check if the file exists
  57.   if not FileExists (FTableName) then
  58.     raise EMdDataSetError.Create ('Open: Table file not found');
  59.   // create a stream for the file
  60.   FStream := TFileStream.Create (FTableName, fmOpenReadWrite);
  61.   // initialize local data (loading the header)
  62.   FStream.ReadBuffer (FDataFileHeader, FDataFileHeaderSize);
  63.   if FDataFileHeader.VersionNumber <> HeaderVersion then
  64.     raise EMdDataSetError.Create ('Illegal File Version');
  65.   // let's read this, double check later
  66.   FRecordCount := FDataFileHeader.RecordCount;
  67. end;
  68. procedure TMdDataSetStream.InternalAfterOpen;
  69. begin
  70.   // check the record size
  71.   if FDataFileHeader.RecordSize <> FRecordSize then
  72.     raise EMdDataSetError.Create ('File record size mismatch');
  73.   // check the number of records against the file size
  74.   if (FDataFileHeaderSize + FRecordCount * FRecordSize) <> FStream.Size then
  75.     raise EMdDataSetError.Create ('InternalOpen: Invalid Record Size');
  76. end;
  77. // I: define the fields
  78. procedure TMdDataSetStream.InternalInitFieldDefs;
  79. var
  80.   IniFileName, FieldName: string;
  81.   IniFile: TIniFile;
  82.   nFields, I, TmpFieldOffset, nSize: Integer;
  83.   FieldType: TFieldType;
  84. begin
  85.   FFieldOffset := TList.Create;
  86.   FieldDefs.Clear;
  87.   TmpFieldOffset := 0;
  88.   IniFilename := ChangeFileExt(FTableName, '.ini');
  89.   Inifile := TIniFile.Create (IniFilename);
  90.   // protect ini file
  91.   try
  92.     nFields := IniFile.ReadInteger ('Fields', 'Number', 0);
  93.     if nFields = 0 then
  94.       raise EMdDataSetError.Create ('InitFieldsDefs: 0 fields?');
  95.     for I := 1 to nFields do
  96.     begin
  97.       // create the field
  98.       FieldType := TFieldType (GetEnumValue (
  99.         TypeInfo (TFieldType),
  100.         IniFile.ReadString (
  101.           'Field' + IntToStr (I), 'Type', '')));
  102.       FieldName := IniFile.ReadString (
  103.         'Field' + IntToStr (I), 'Name', '');
  104.       if FieldName = '' then
  105.         raise EMdDataSetError.Create (
  106.           'InitFieldsDefs: No name for field ' +
  107.           IntToStr (I));
  108.       nSize := IniFile.ReadInteger (
  109.          'Field' + IntToStr (I), 'Size', 0);
  110.       FieldDefs.Add (FieldName,
  111.         FieldType, nSize, False);
  112.       // save offset and compute size
  113.       FFieldOffset.Add (Pointer (TmpFieldOffset));
  114.       case FieldType of
  115.         ftString:
  116.           Inc (TmpFieldOffset, nSize + 1);
  117.         ftBoolean, ftSmallInt, ftWord:
  118.           Inc (TmpFieldOffset, 2);
  119.         ftInteger, ftDate, ftTime:
  120.           Inc (TmpFieldOffset, 4);
  121.         ftFloat, ftCurrency, ftDateTime:
  122.           Inc (TmpFieldOffset, 8);
  123.       else
  124.         raise EMdDataSetError.Create (
  125.           'InitFieldsDefs: Unsupported field type');
  126.       end;
  127.     end; // for
  128.   finally
  129.     IniFile.Free;
  130.   end;
  131.   FRecordSize := TmpFieldOffset;
  132. end;
  133. // I: close the table/file
  134. procedure TMdDataSetStream.InternalClose;
  135. begin
  136.   // if required, save updated header
  137.   if (FDataFileHeader.RecordCount <> FRecordCount) or
  138.     (FDataFileHeader.RecordSize = 0) then
  139.   begin
  140.     FDataFileHeader.RecordSize := FRecordSize;
  141.     FDataFileHeader.RecordCount := FRecordCount;
  142.     if Assigned (FStream) then
  143.     begin
  144.       FStream.Seek (0, soFromBeginning);
  145.       FStream.WriteBuffer (
  146.         FDataFileHeader, FDataFileHeaderSize);
  147.     end;
  148.   end;
  149.   // free the internal list field offsets and the stream
  150.   FFieldOffset.Free;
  151.   FStream.Free;
  152.   inherited InternalClose;
  153. end;
  154. // I: Create a new table/file
  155. procedure TMdDataSetStream.CreateTable;
  156. begin
  157.   CheckInactive;
  158.   InternalInitFieldDefs;
  159.   // create the new file
  160.   if FileExists (FTableName) then
  161.     raise EMdDataSetError.Create ('File ' + FTableName + ' already exists');
  162.   FStream := TFileStream.Create (FTableName,
  163.     fmCreate or fmShareExclusive);
  164.   try
  165.     // save the header
  166.     FDataFileHeader.VersionNumber := HeaderVersion;
  167.     FDataFileHeader.RecordSize := 0; // used later
  168.     FDataFileHeader.RecordCount := 0; // empty
  169.     FStream.WriteBuffer (
  170.       FDataFileHeader, FDataFileHeaderSize);
  171.   finally
  172.     // close the file
  173.     FStream.Free;
  174.   end;
  175. end;
  176. //////////////////////////////////////////
  177. ////// Part III:
  178. ////// Record buffers management
  179. //////////////////////////////////////////
  180. // III: loading of the actual data for the GetCurrent request
  181. procedure TMdDataSetStream.InternalLoadCurrentRecord (Buffer: PChar);
  182. begin
  183.   FStream.Position := FDataFileHeaderSize +
  184.     FRecordSize * FCurrentRecord;
  185.   FStream.ReadBuffer (Buffer^, FRecordSize);
  186.   with PMdRecInfo(Buffer + FRecordSize)^ do
  187.   begin
  188.     BookmarkFlag := bfCurrent;
  189.     Bookmark := FCurrentRecord;
  190.   end;
  191. end;
  192. // III: Write the current data to the file
  193. procedure TMdDataSetStream.InternalPost;
  194. begin
  195.   CheckActive;
  196.   if State = dsEdit then
  197.   begin
  198.     // replace data with new data
  199.     FStream.Position := FDataFileHeaderSize +
  200.       FRecordSize * FCurrentRecord;
  201.     FStream.WriteBuffer (ActiveBuffer^, FRecordSize);
  202.   end
  203.   else
  204.   begin
  205.     // always append
  206.     InternalLast;
  207.     FStream.Seek (0, soFromEnd);
  208.     FStream.WriteBuffer (ActiveBuffer^, FRecordSize);
  209.     Inc (FRecordCount);
  210.   end;
  211. end;
  212. // III: Add the current data to the file
  213. procedure TMdDataSetStream.InternalAddRecord(
  214.   Buffer: Pointer; Append: Boolean);
  215. begin
  216.   // always append at the end
  217.   InternalLast;
  218.   FStream.Seek (0, soFromEnd);
  219.   FStream.WriteBuffer (ActiveBuffer^, FRecordSize);
  220.   Inc (FRecordCount);
  221. end;
  222. //////////////////////////////////////////
  223. ////// Part IV:
  224. ////// From buffers to fields
  225. //////////////////////////////////////////
  226. // IV: Move data from record buffer to field
  227. function TMdDataSetStream.GetFieldData (
  228.   Field: TField; Buffer: Pointer): Boolean;
  229. var
  230.   FieldOffset: Integer;
  231.   Ptr: PChar;
  232. begin
  233.   Result := False;
  234.   if not IsEmpty and (Field.FieldNo > 0) then
  235.   begin
  236.     FieldOffset := Integer (
  237.       FFieldOffset [Field.FieldNo - 1]);
  238.     Ptr := ActiveBuffer;
  239.     Inc (Ptr, FieldOffset);
  240.     if Assigned (Buffer) then
  241.       Move (Ptr^, Buffer^, Field.DataSize);
  242.     Result := True;
  243.     if (Field is TDateTimeField) and (PInteger(Ptr)^ = 0) then
  244.       Result := False;
  245.   end;
  246. end;
  247. // IV: Move data from field to record buffer
  248. procedure TMdDataSetStream.SetFieldData(Field: TField; Buffer: Pointer);
  249. var
  250.   FieldOffset: Integer;
  251.   Ptr: PChar;
  252. begin
  253.   if Field.FieldNo >= 0 then
  254.   begin
  255.     FieldOffset := Integer (
  256.       FFieldOffset [Field.FieldNo - 1]);
  257.     Ptr := ActiveBuffer;
  258.     Inc (Ptr, FieldOffset);
  259.     if Assigned (Buffer) then
  260.       Move (Buffer^, Ptr^, Field.DataSize)
  261.     else
  262.       raise Exception.Create (
  263.         'Very bad error in TMdDataSetStream.SetField data');
  264.     DataEvent (deFieldChange, Longint(Field));
  265.   end;
  266. end;
  267. procedure Register;
  268. begin
  269.   RegisterComponents('Md', [TMdDataSetStream]);
  270. end;
  271. function TMdDataSetStream.InternalRecordCount: Integer;
  272. begin
  273.   Result := FRecordCount;
  274. end;
  275. procedure TMdDataSetStream.SetTableName(const Value: string);
  276. begin
  277.   if IsCursorOpen then
  278.     if csDesigning in ComponentState then
  279.       Close
  280.     else
  281.       raise Exception.Create ('Cannot assing an open dataset to a new file');
  282.   FTableName := Value;
  283. end;
  284. end.