sdfdata.pp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020
  1. unit SdfData;
  2. {$mode objfpc}
  3. {$h+}
  4. //-----------------------------------------------------------------------------
  5. { Unit Name : SdfData Application : TSdfDataSet TFixedFormatDataSet Components
  6. Version : 2.05
  7. Author : Orlando Arrocha email: [email protected]
  8. Purpose : This components are designed to access directly text files as
  9. database tables. The files may be limited (SDF) or fixed size
  10. columns.
  11. ---------------
  12. Modifications
  13. ---------------
  14. 12/Mar/04 Lazarus version (Sergey Smirnov AKA SSY)
  15. Locate and CheckString functions are removed because of Variant data type.
  16. Many things are changed for FPC/Lazarus compatibility.
  17. 02/Jun/02 Version 2.05 (Doriano Biondelli)
  18. TrimSpace property added for those cases where you need to retrieve the
  19. field with spaces.
  20. 01/Jan/02 Version 2.04 (Orlando Arrocha)
  21. FieldList is now populated.
  22. Locate was changed to improve speed and some bug fixing too. Thanks for
  23. asking and testing Marcelo Castro
  24. 16/Dec/01 Version 2.03 (Orlando Arrocha)
  25. Fixed some bugs and added some recomentdations. Here is a list:
  26. Quotations on the last field was not removed properly. Special thanks to
  27. Daniel Nakasone for helping with the solution.
  28. Appending first record to empty files was failing. Thanks again Daniel
  29. Nakasone for the report
  30. GetFieldData now trims the trailing spaces of the field, so users doesn't
  31. needs to do it by themselves anymore. Thanks for the recomendation
  32. Juergen Gehrke.
  33. FieldDefs is now available from the designer. Recomended by Leslie Drewery.
  34. ****** THANKS TO ALL & KEEP SENDING RECOMENDATIONS *****
  35. 05/Oct/01 Version 2.02 (Ben Hay)
  36. Locate function : implement the virtual tdataset method "Locate".
  37. ****** THANKS BEN *****
  38. 11/Sep/01 Version 2.01 (Leslie Drewery)
  39. Added additional logic to handle Corrupt Data by making sure the
  40. Quotes are closed and the delimiter/<CR>/<LF> are the next
  41. characters.
  42. Altered buffer method to create on constructor and cleared when opened.
  43. New Resource File. Nice Icons
  44. SavetoStream method included
  45. LoadFromStream method included
  46. ****** THANKS LESLIE *****
  47. 14/Ago/01 Version 2.00 (Orlando Arrocha)
  48. John Dung Nguyen showed me how to make this compatible with C-Builder
  49. and encouraged me to include a filter.
  50. Dimitry V. Borko says that russian CSV files used other delimiters,
  51. so now you can change it.
  52. OnFilter and other events included.
  53. Delimiter property added to TSdfDataSet. No more dependency on CommaText
  54. methodology -- choose your own delimiter.
  55. BufToStore/StoreToBuf methods lets you translate data records to and from
  56. your propietary storage format.
  57. TTextDataSet removed dependencies.
  58. TBaseTextDataSet class removed. // TBaseTextDataSet = TFixedFormatDataSet;
  59. ****** THANKS JOHN ****** ***** THANKS DIMMY *****
  60. 19/Jul/01 Version 1.03 (Orlando Arrocha)
  61. TBaseTextDataSet class introduced.
  62. FileName property changed datatype to TFileName and removed the property
  63. editor to segregate design-time code from runtime units.
  64. *** To add file browsing functionality please install
  65. *** TFileNamePropertyEditor -- also freeware.
  66. ********** THANKS WAYNE *********
  67. 18/Jun/01 Version 1.02 (Wayne Brantley)
  68. Schema replaces SchemaFileName property. Same as SchemaFileName, except
  69. you can define the schema inside the component. If you still need an
  70. external file, just use Schema.LoadFromFile()
  71. TFixedFormatDataSet class introduced. Use this class for a Fixed length
  72. format file (instead of delimited). The full schema definition
  73. (including lengths) is obviously required.
  74. Bug Fixed - When FirstLineSchema is true and there were no records, it
  75. would display garbage.
  76. 30/Mar/01 Version 1.01 (Orlando Arrocha)
  77. Ligia Maria Pimentel suggested to use the first line of the file to
  78. define the field names. ****** THANKS LIGIA ******
  79. FileMustExist property. You must put this property to FALSE if you want to
  80. create a new file.
  81. FirstLineSchema property. You can define the field names on the first line
  82. of your file. Fields have to be defined with this format
  83. <field_name1> [= field_size1] , <field_name2> [= field_size2] ...
  84. SchemaFileName property. (Changed to Schema by 1.02 Wayne)
  85. Lets you define the fields attributes (only supports field name and
  86. size). Have to be defined in this format (one field per line) :
  87. <field_name> [= field_size]
  88. NOTE: fields that doesn't define the length get the record size.
  89. RemoveBlankRecords procedure. Removes all the blank records from the file.
  90. RemoveExtraColumns procedure. If the file have more columns than the
  91. scheme or the field definition at design time, it remove the extra
  92. values from the file.
  93. SaveFileAs. Let you save the file to another filename.
  94. NOTE: This component save changes on closing the table, so you can use
  95. this to save data before that event.
  96. Jan 2001 Version 1.0 TSdfDataSet introduced.
  97. ---------
  98. TERMS
  99. ---------
  100. This component is provided AS-IS without any warranty of any kind, either
  101. express or implied. This component is freeware and can be used in any software
  102. product. Credits on applications will be welcomed.
  103. If you find it useful, improve it or have a wish list... please drop me a mail,
  104. I'll be glad to hear your comments.
  105. ----------------
  106. How to Install
  107. ----------------
  108. 1. Copy this SDFDATA.PAS and the associated SDFDATA.DCR to the folder from
  109. where you wish to install the component. This will probably be $(DELPHI)\lib
  110. or a sub-folder.
  111. 2. Install the TSdfDataSet and TFixedFormatDataSet components by choosing the
  112. Component | Install Component menu option.
  113. 3. Select the "Into exisiting package" page of the Install Components dialogue.
  114. 4. Browse to the folder where you saved this file and select it.
  115. 5. Ensure that the "Package file name" edit box contains $(DELPHI)\DCLUSR??.DPK
  116. or the one you prefer for DB related objects.
  117. 6. Accept that the package will be rebuilt.
  118. }
  119. //-----------------------------------------------------------------------------
  120. interface
  121. uses
  122. DB, Classes, SysUtils;
  123. const
  124. MAXSTRLEN = 250;
  125. type
  126. //-----------------------------------------------------------------------------
  127. // TRecInfo
  128. PRecInfo = ^TRecInfo;
  129. TRecInfo = packed record
  130. RecordNumber: PtrInt;
  131. BookmarkFlag: TBookmarkFlag;
  132. end;
  133. //-----------------------------------------------------------------------------
  134. // TBaseTextDataSet
  135. TFixedFormatDataSet = class(TDataSet)
  136. private
  137. FSchema :TStringList;
  138. FFileName :TFileName;
  139. FFilterBuffer :PChar;
  140. FFileMustExist :Boolean;
  141. FReadOnly :Boolean;
  142. FLoadfromStream :Boolean;
  143. FTrimSpace :Boolean;
  144. procedure SetSchema(const Value: TStringList);
  145. procedure SetFileName(Value : TFileName);
  146. procedure SetFileMustExist(Value : Boolean);
  147. procedure SetTrimSpace(Value : Boolean);
  148. procedure SetReadOnly(Value : Boolean);
  149. procedure RemoveWhiteLines(List : TStrings; IsFileRecord : Boolean);
  150. procedure LoadFieldScheme(List : TStrings; MaxSize : Integer);
  151. function GetActiveRecBuf(var RecBuf: PChar): Boolean;
  152. procedure SetFieldPos(var Buffer : PChar; FieldNo : Integer);
  153. protected
  154. FData :TStringlist;
  155. FCurRec :Integer;
  156. FRecBufSize :Integer;
  157. FRecordSize :Integer;
  158. FLastBookmark :PtrInt;
  159. FRecInfoOfs :Word;
  160. FBookmarkOfs :Word;
  161. FSaveChanges :Boolean;
  162. protected
  163. function AllocRecordBuffer: PChar; override;
  164. procedure FreeRecordBuffer(var Buffer: PChar); override;
  165. procedure InternalAddRecord(Buffer: Pointer; DoAppend: Boolean); override;
  166. procedure InternalClose; override;
  167. procedure InternalDelete; override;
  168. procedure InternalFirst; override;
  169. procedure InternalGotoBookmark(ABookmark: Pointer); override;
  170. procedure InternalHandleException; override;
  171. procedure InternalInitFieldDefs; override;
  172. procedure InternalInitRecord(Buffer: PChar); override;
  173. procedure InternalLast; override;
  174. procedure InternalOpen; override;
  175. procedure InternalPost; override;
  176. procedure InternalEdit; override;
  177. procedure InternalSetToRecord(Buffer: PChar); override;
  178. function IsCursorOpen: Boolean; override;
  179. procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
  180. function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
  181. function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
  182. function GetRecordSize: Word; override;
  183. procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
  184. procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
  185. procedure SetFieldData(Field: TField; Buffer: Pointer); override;
  186. procedure ClearCalcFields(Buffer: PChar); override;
  187. function GetRecordCount: Integer; override;
  188. function GetRecNo: Integer; override;
  189. procedure SetRecNo(Value: Integer); override;
  190. function GetCanModify: boolean; override;
  191. function TxtGetRecord(Buffer : PChar; GetMode: TGetMode): TGetResult;
  192. function RecordFilter(RecBuf: Pointer; ARecNo: Integer): Boolean;
  193. function BufToStore(Buffer: PChar): String; virtual;
  194. function StoreToBuf(Source: String): String; virtual;
  195. public
  196. constructor Create(AOwner: TComponent); override;
  197. destructor Destroy; override;
  198. function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
  199. procedure RemoveBlankRecords; dynamic;
  200. procedure RemoveExtraColumns; dynamic;
  201. procedure SaveFileAs(strFileName : String); dynamic;
  202. property CanModify;
  203. procedure LoadFromStream(Stream :TStream);
  204. procedure SavetoStream(Stream :TStream);
  205. published
  206. property FileMustExist: Boolean read FFileMustExist write SetFileMustExist;
  207. property ReadOnly: Boolean read FReadOnly write SetReadOnly;
  208. property FileName : TFileName read FFileName write SetFileName;
  209. property Schema: TStringList read FSchema write SetSchema;
  210. property TrimSpace: Boolean read FTrimSpace write SetTrimSpace default True;
  211. property FieldDefs;
  212. property Active;
  213. property AutoCalcFields;
  214. property Filtered;
  215. property BeforeOpen;
  216. property AfterOpen;
  217. property BeforeClose;
  218. property AfterClose;
  219. property BeforeInsert;
  220. property AfterInsert;
  221. property BeforeEdit;
  222. property AfterEdit;
  223. property BeforePost;
  224. property AfterPost;
  225. property BeforeCancel;
  226. property AfterCancel;
  227. property BeforeDelete;
  228. property AfterDelete;
  229. property BeforeScroll;
  230. property AfterScroll;
  231. // property BeforeRefresh;
  232. // property AfterRefresh;
  233. property OnCalcFields;
  234. property OnDeleteError;
  235. property OnEditError;
  236. property OnFilterRecord;
  237. property OnNewRecord;
  238. property OnPostError;
  239. end;
  240. //-----------------------------------------------------------------------------
  241. // TSdfDataSet
  242. TSdfDataSet = class(TFixedFormatDataSet)
  243. private
  244. FDelimiter : Char;
  245. FFirstLineAsSchema : Boolean;
  246. procedure SetFirstLineAsSchema(Value : Boolean);
  247. procedure SetDelimiter(Value : Char);
  248. protected
  249. procedure InternalInitFieldDefs; override;
  250. function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean)
  251. : TGetResult; override;
  252. function BufToStore(Buffer: PChar): String; override;
  253. function StoreToBuf(Source: String): String; override;
  254. public
  255. constructor Create(AOwner: TComponent); override;
  256. published
  257. property Delimiter: Char read FDelimiter write SetDelimiter;
  258. property FirstLineAsSchema: Boolean read FFirstLineAsSchema write SetFirstLineAsSchema;
  259. end;
  260. procedure Register;
  261. implementation
  262. //{$R *.Res}
  263. //-----------------------------------------------------------------------------
  264. // TFixedFormatDataSet
  265. //-----------------------------------------------------------------------------
  266. constructor TFixedFormatDataSet.Create(AOwner : TComponent);
  267. begin
  268. FFileMustExist := TRUE;
  269. FLoadfromStream := False;
  270. FRecordSize := 0;
  271. FTrimSpace := TRUE;
  272. FSchema := TStringList.Create;
  273. FData := TStringList.Create; // Load the textfile into a stringlist
  274. inherited Create(AOwner);
  275. end;
  276. destructor TFixedFormatDataSet.Destroy;
  277. begin
  278. inherited Destroy;
  279. FData.Free;
  280. FSchema.Free;
  281. end;
  282. procedure TFixedFormatDataSet.SetSchema(const Value: TStringList);
  283. begin
  284. CheckInactive;
  285. FSchema.Assign(Value);
  286. end;
  287. procedure TFixedFormatDataSet.SetFileMustExist(Value : Boolean);
  288. begin
  289. CheckInactive;
  290. FFileMustExist := Value;
  291. end;
  292. procedure TFixedFormatDataSet.SetTrimSpace(Value : Boolean);
  293. begin
  294. CheckInactive;
  295. FTrimSpace := Value;
  296. end;
  297. procedure TFixedFormatDataSet.SetReadOnly(Value : Boolean);
  298. begin
  299. CheckInactive;
  300. FReadOnly := Value;
  301. end;
  302. procedure TFixedFormatDataSet.SetFileName(Value : TFileName);
  303. begin
  304. CheckInactive;
  305. FFileName := Value;
  306. end;
  307. procedure TFixedFormatDataSet.InternalInitFieldDefs;
  308. var
  309. i, len, Maxlen :Integer;
  310. LstFields :TStrings;
  311. begin
  312. if not Assigned(FData) then
  313. exit;
  314. FRecordSize := 0;
  315. Maxlen := 0;
  316. FieldDefs.Clear;
  317. for i := FData.Count - 1 downto 0 do // Find out the longest record
  318. begin
  319. len := Length(FData[i]);
  320. if len > Maxlen then
  321. Maxlen := len;
  322. FData.Objects[i] := TObject(Pointer(i+1)); // Fabricate Bookmarks
  323. end;
  324. if (Maxlen = 0) then
  325. Maxlen := MAXSTRLEN;
  326. LstFields := TStringList.Create;
  327. try
  328. LoadFieldScheme(LstFields, Maxlen);
  329. for i := 0 to LstFields.Count -1 do // Add fields
  330. begin
  331. len := StrToIntDef(LstFields.Values[LstFields.Names[i]], Maxlen);
  332. FieldDefs.Add(Trim(LstFields.Names[i]), ftString, len, False);
  333. Inc(FRecordSize, len);
  334. end;
  335. finally
  336. LstFields.Free;
  337. end;
  338. end;
  339. procedure TFixedFormatDataSet.InternalOpen;
  340. var
  341. Stream : TStream;
  342. begin
  343. FCurRec := -1;
  344. FSaveChanges := FALSE;
  345. if not Assigned(FData) then
  346. FData := TStringList.Create;
  347. if (not FileMustExist) and (not FileExists(FileName)) then
  348. begin
  349. Stream := TFileStream.Create(FileName, fmCreate);
  350. Stream.Free;
  351. end;
  352. if not FLoadfromStream then
  353. FData.LoadFromFile(FileName);
  354. FRecordSize := MAXSTRLEN;
  355. InternalInitFieldDefs;
  356. if DefaultFields then
  357. CreateFields;
  358. BindFields(TRUE);
  359. if FRecordSize = 0 then
  360. FRecordSize := MAXSTRLEN;
  361. BookmarkSize := SizeOf(Integer);
  362. FRecInfoOfs := FRecordSize + CalcFieldsSize; // Initialize the offset for TRecInfo in the buffer
  363. FBookmarkOfs := FRecInfoOfs + SizeOf(TRecInfo);
  364. FRecBufSize := FBookmarkOfs + BookmarkSize;
  365. FLastBookmark := FData.Count;
  366. end;
  367. procedure TFixedFormatDataSet.InternalClose;
  368. begin
  369. if (not FReadOnly) and (FSaveChanges) then // Write any edits to disk
  370. FData.SaveToFile(FileName);
  371. FLoadfromStream := False;
  372. FData.Clear;
  373. BindFields(FALSE);
  374. if DefaultFields then // Destroy the TField
  375. DestroyFields;
  376. FCurRec := -1; // Reset these internal flags
  377. FLastBookmark := 0;
  378. FRecordSize := 0;
  379. end;
  380. function TFixedFormatDataSet.IsCursorOpen: Boolean;
  381. begin
  382. Result := Assigned(FData) and (FRecordSize > 0);
  383. end;
  384. procedure TFixedFormatDataSet.InternalHandleException;
  385. begin
  386. {$ifndef fpc}
  387. Application.HandleException(Self);
  388. {$endif}
  389. end;
  390. // Loads Data from a stream.
  391. procedure TFixedFormatDataSet.LoadFromStream(Stream: TStream);
  392. begin
  393. if assigned(stream) then
  394. begin
  395. Active := False; //Make sure the Dataset is Closed.
  396. Stream.Position := 0; //Make sure you are at the top of the Stream.
  397. FLoadfromStream := True;
  398. if not Assigned(FData) then
  399. raise Exception.Create('Data buffer unassigned');
  400. FData.LoadFromStream(Stream);
  401. Active := True;
  402. end
  403. else
  404. raise exception.Create('Invalid Stream Assigned (Load From Stream');
  405. end;
  406. // Saves Data as text to a stream.
  407. procedure TFixedFormatDataSet.SavetoStream(Stream: TStream);
  408. begin
  409. if assigned(stream) then
  410. FData.SaveToStream(Stream)
  411. else
  412. raise exception.Create('Invalid Stream Assigned (Save To Stream');
  413. end;
  414. // Record Functions
  415. function TFixedFormatDataSet.AllocRecordBuffer: PChar;
  416. begin
  417. if FRecBufSize > 0 then
  418. Result := AllocMem(FRecBufSize)
  419. else
  420. Result := nil;
  421. end;
  422. procedure TFixedFormatDataSet.FreeRecordBuffer(var Buffer: PChar);
  423. begin
  424. if Buffer <> nil then
  425. FreeMem(Buffer);
  426. end;
  427. procedure TFixedFormatDataSet.InternalInitRecord(Buffer: PChar);
  428. begin
  429. FillChar(Buffer[0], FRecordSize, 0);
  430. end;
  431. procedure TFixedFormatDataSet.ClearCalcFields(Buffer: PChar);
  432. begin
  433. FillChar(Buffer[RecordSize], CalcFieldsSize, 0);
  434. end;
  435. function TFixedFormatDataSet.GetRecord(Buffer: PChar; GetMode: TGetMode;
  436. DoCheck: Boolean): TGetResult;
  437. begin
  438. if (FData.Count < 1) then
  439. Result := grEOF
  440. else
  441. Result := TxtGetRecord(Buffer, GetMode);
  442. if Result = grOK then
  443. begin
  444. if (CalcFieldsSize > 0) then
  445. GetCalcFields(Buffer);
  446. with PRecInfo(Buffer + FRecInfoOfs)^ do
  447. begin
  448. BookmarkFlag := bfCurrent;
  449. RecordNumber := PtrInt(FData.Objects[FCurRec]);
  450. end;
  451. end
  452. else
  453. if (Result = grError) and DoCheck then
  454. DatabaseError('No Records');
  455. end;
  456. function TFixedFormatDataSet.GetRecordCount: Longint;
  457. begin
  458. Result := FData.Count;
  459. end;
  460. function TFixedFormatDataSet.GetRecNo: Longint;
  461. var
  462. BufPtr: PChar;
  463. begin
  464. Result := -1;
  465. if GetActiveRecBuf(BufPtr) then
  466. Result := PRecInfo(BufPtr + FRecInfoOfs)^.RecordNumber;
  467. end;
  468. procedure TFixedFormatDataSet.SetRecNo(Value: Integer);
  469. begin
  470. CheckBrowseMode;
  471. if (Value >= 0) and (Value < FData.Count) and (Value <> RecNo) then
  472. begin
  473. DoBeforeScroll;
  474. FCurRec := Value - 1;
  475. Resync([]);
  476. DoAfterScroll;
  477. end;
  478. end;
  479. function TFixedFormatDataSet.GetRecordSize: Word;
  480. begin
  481. Result := FRecordSize;
  482. end;
  483. function TFixedFormatDataSet.GetActiveRecBuf(var RecBuf: PChar): Boolean;
  484. begin
  485. case State of
  486. dsBrowse: if IsEmpty then RecBuf := nil else RecBuf := ActiveBuffer;
  487. dsEdit, dsInsert: RecBuf := ActiveBuffer;
  488. dsCalcFields: RecBuf := CalcBuffer;
  489. dsFilter: RecBuf := FFilterBuffer;
  490. else
  491. RecBuf := nil;
  492. end;
  493. Result := RecBuf <> nil;
  494. end;
  495. function TFixedFormatDataSet.TxtGetRecord(Buffer : PChar; GetMode: TGetMode): TGetResult;
  496. var
  497. Accepted : Boolean;
  498. begin
  499. Result := grOK;
  500. repeat
  501. Accepted := TRUE;
  502. case GetMode of
  503. gmNext:
  504. if FCurRec >= RecordCount - 1 then
  505. Result := grEOF
  506. else
  507. Inc(FCurRec);
  508. gmPrior:
  509. if FCurRec <= 0 then
  510. Result := grBOF
  511. else
  512. Dec(FCurRec);
  513. gmCurrent:
  514. if (FCurRec < 0) or (FCurRec >= RecordCount) then
  515. Result := grError;
  516. end;
  517. if (Result = grOk) then
  518. begin
  519. Move(PChar(StoreToBuf(FData[FCurRec]))^, Buffer[0], FRecordSize);
  520. if Filtered then
  521. begin
  522. Accepted := RecordFilter(Buffer, FCurRec +1);
  523. if not Accepted and (GetMode = gmCurrent) then
  524. Inc(FCurRec);
  525. end;
  526. end;
  527. until Accepted;
  528. end;
  529. function TFixedFormatDataSet.RecordFilter(RecBuf: Pointer; ARecNo: Integer): Boolean;
  530. var
  531. Accept: Boolean;
  532. SaveState: TDataSetState;
  533. begin // Returns true if accepted in the filter
  534. SaveState := SetTempState(dsFilter);
  535. FFilterBuffer := RecBuf;
  536. PRecInfo(FFilterBuffer + FRecInfoOfs)^.RecordNumber := ARecNo;
  537. Accept := TRUE;
  538. if Accept and Assigned(OnFilterRecord) then
  539. OnFilterRecord(Self, Accept);
  540. RestoreState(SaveState);
  541. Result := Accept;
  542. end;
  543. function TFixedFormatDataSet.GetCanModify: boolean;
  544. begin
  545. Result := not FReadOnly;
  546. end;
  547. // Field Related
  548. procedure TFixedFormatDataSet.LoadFieldScheme(List : TStrings; MaxSize : Integer);
  549. var
  550. tmpFieldName : string;
  551. tmpSchema : TStrings;
  552. i : Integer;
  553. begin
  554. tmpSchema := TStringList.Create;
  555. try // Load Schema Structure
  556. if (Schema.Count > 0) then
  557. begin
  558. tmpSchema.Assign(Schema);
  559. RemoveWhiteLines(tmpSchema, FALSE);
  560. end
  561. else
  562. tmpSchema.Add('Line');
  563. for i := 0 to tmpSchema.Count -1 do // Interpret Schema
  564. begin
  565. tmpFieldName := tmpSchema.Names[i];
  566. if (tmpFieldName = '') then
  567. tmpFieldName := Format('%s=%d', [tmpSchema[i], MaxSize])
  568. else
  569. tmpFieldName := tmpSchema[i];
  570. List.Add(tmpFieldName);
  571. end;
  572. finally
  573. tmpSchema.Free;
  574. end;
  575. end;
  576. function TFixedFormatDataSet.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
  577. var
  578. TempPos, RecBuf: PChar;
  579. begin
  580. Result := GetActiveRecBuf(RecBuf);
  581. if Result then
  582. begin
  583. if Field.FieldNo > 0 then
  584. begin
  585. TempPos := RecBuf;
  586. SetFieldPos(RecBuf, Field.FieldNo);
  587. Result := (RecBuf < StrEnd(TempPos));
  588. end
  589. else
  590. if (State in [dsBrowse, dsEdit, dsInsert, dsCalcFields]) then
  591. begin
  592. Inc(RecBuf, FRecordSize + Field.Offset);
  593. Result := Boolean(Byte(RecBuf[0]));
  594. end;
  595. end;
  596. if Result and (Buffer <> nil) then
  597. begin
  598. StrLCopy(Buffer, RecBuf, Field.Size);
  599. if FTrimSpace then
  600. begin
  601. TempPos := StrEnd(Buffer);
  602. repeat
  603. Dec(TempPos);
  604. if (TempPos[0] = ' ') then
  605. TempPos[0]:= #0
  606. else
  607. break;
  608. until (TempPos = Buffer);
  609. end;
  610. end;
  611. end;
  612. procedure TFixedFormatDataSet.SetFieldData(Field: TField; Buffer: Pointer);
  613. var
  614. RecBuf, BufEnd: PChar;
  615. p : Integer;
  616. begin
  617. if not (State in [dsEdit, dsInsert]) then
  618. DatabaseError('Dataset not in edit or insert mode', Self);
  619. GetActiveRecBuf(RecBuf);
  620. if Field.FieldNo > 0 then
  621. begin
  622. if State = dsCalcFields then
  623. DatabaseError('Dataset not in edit or insert mode', Self);
  624. if Field.ReadOnly and not (State in [dsSetKey, dsFilter]) then
  625. DatabaseErrorFmt('Field ''%s'' cannot be modified', [Field.DisplayName]);
  626. Field.Validate(Buffer);
  627. if Field.FieldKind <> fkInternalCalc then
  628. begin
  629. SetFieldPos(RecBuf, Field.FieldNo);
  630. BufEnd := StrEnd(ActiveBuffer); // Fill with blanks when necessary
  631. if BufEnd > RecBuf then
  632. BufEnd := RecBuf;
  633. FillChar(BufEnd[0], Field.Size + PtrInt(RecBuf) - PtrInt(BufEnd), Ord(' '));
  634. p := StrLen(Buffer);
  635. if p > Field.Size then
  636. p := Field.Size;
  637. Move(Buffer^, RecBuf[0], p);
  638. ActiveBuffer[RecordSize-1] := #0;
  639. end;
  640. end
  641. else // fkCalculated, fkLookup
  642. begin
  643. Inc(RecBuf, FRecordSize + Field.Offset);
  644. Move(Buffer^, RecBuf[0], Field.Size);
  645. end;
  646. if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
  647. DataEvent(deFieldChange, Ptrint(Field));
  648. end;
  649. procedure TFixedFormatDataSet.SetFieldPos(var Buffer : PChar; FieldNo : Integer);
  650. var
  651. i : Integer;
  652. begin
  653. i := 1;
  654. while (i < FieldNo) and (i < FieldDefs.Count) do
  655. begin
  656. Inc(Buffer, FieldDefs.Items[i-1].Size);
  657. Inc(i);
  658. end;
  659. end;
  660. // Navigation / Editing
  661. procedure TFixedFormatDataSet.InternalFirst;
  662. begin
  663. FCurRec := -1;
  664. end;
  665. procedure TFixedFormatDataSet.InternalLast;
  666. begin
  667. FCurRec := FData.Count;
  668. end;
  669. procedure TFixedFormatDataSet.InternalPost;
  670. var
  671. i: Longint;
  672. begin
  673. FSaveChanges := TRUE;
  674. inherited UpdateRecord;
  675. if (State = dsEdit) then // just update the data in the string list
  676. begin
  677. FData[FCurRec] := BufToStore(ActiveBuffer);
  678. end
  679. else
  680. InternalAddRecord(ActiveBuffer, FALSE);
  681. end;
  682. procedure TFixedFormatDataSet.InternalEdit;
  683. begin
  684. end;
  685. procedure TFixedFormatDataSet.InternalDelete;
  686. begin
  687. FSaveChanges := TRUE;
  688. FData.Delete(FCurRec);
  689. if FCurRec >= FData.Count then
  690. Dec(FCurRec);
  691. end;
  692. procedure TFixedFormatDataSet.InternalAddRecord(Buffer: Pointer; DoAppend: Boolean);
  693. begin
  694. FSaveChanges := TRUE;
  695. Inc(FLastBookmark);
  696. if DoAppend then
  697. InternalLast;
  698. if (FCurRec >=0) then
  699. FData.InsertObject(FCurRec, BufToStore(Buffer), TObject(Pointer(FLastBookmark)))
  700. else
  701. FData.AddObject(BufToStore(Buffer), TObject(Pointer(FLastBookmark)));
  702. end;
  703. procedure TFixedFormatDataSet.InternalGotoBookmark(ABookmark: Pointer);
  704. var
  705. Index: Integer;
  706. begin
  707. Index := FData.IndexOfObject(TObject(PPtrInt(ABookmark)^));
  708. if Index <> -1 then
  709. FCurRec := Index
  710. else
  711. DatabaseError('Bookmark not found');
  712. end;
  713. procedure TFixedFormatDataSet.InternalSetToRecord(Buffer: PChar);
  714. begin
  715. if (State <> dsInsert) then
  716. InternalGotoBookmark(@PRecInfo(Buffer + FRecInfoOfs)^.RecordNumber);
  717. end;
  718. function TFixedFormatDataSet.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
  719. begin
  720. Result := PRecInfo(Buffer + FRecInfoOfs)^.BookmarkFlag;
  721. end;
  722. procedure TFixedFormatDataSet.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
  723. begin
  724. PRecInfo(Buffer + FRecInfoOfs)^.BookmarkFlag := Value;
  725. end;
  726. procedure TFixedFormatDataSet.GetBookmarkData(Buffer: PChar; Data: Pointer);
  727. begin
  728. Move(Buffer[FBookmarkOfs], Data^, BookmarkSize);
  729. end;
  730. procedure TFixedFormatDataSet.SetBookmarkData(Buffer: PChar; Data: Pointer);
  731. begin
  732. Move(Data^, Buffer[FBookmarkOfs], BookmarkSize);
  733. end;
  734. procedure TFixedFormatDataSet.RemoveWhiteLines(List : TStrings; IsFileRecord : Boolean);
  735. var
  736. i : integer;
  737. begin
  738. for i := List.Count -1 downto 0 do
  739. begin
  740. if (Trim(List[i]) = '' ) then
  741. if IsFileRecord then
  742. begin
  743. FCurRec := i;
  744. InternalDelete;
  745. end
  746. else
  747. List.Delete(i);
  748. end;
  749. end;
  750. procedure TFixedFormatDataSet.RemoveBlankRecords;
  751. begin
  752. RemoveWhiteLines(FData, TRUE);
  753. end;
  754. procedure TFixedFormatDataSet.RemoveExtraColumns;
  755. var
  756. i : Integer;
  757. begin
  758. for i := FData.Count -1 downto 0 do
  759. FData[i] := BufToStore(PChar(StoreToBuf(FData[i])));
  760. FData.SaveToFile(FileName);
  761. end;
  762. procedure TFixedFormatDataSet.SaveFileAs(strFileName : String);
  763. begin
  764. FData.SaveToFile(strFileName);
  765. FFileName := strFileName;
  766. FSaveChanges := FALSE;
  767. end;
  768. function TFixedFormatDataSet.StoreToBuf(Source: String): String;
  769. begin
  770. Result := Source;
  771. end;
  772. function TFixedFormatDataSet.BufToStore(Buffer: PChar): String;
  773. begin
  774. Result := Copy(Buffer, 1, FRecordSize);
  775. end;
  776. //-----------------------------------------------------------------------------
  777. // TSdfDataSet
  778. //-----------------------------------------------------------------------------
  779. constructor TSdfDataSet.Create(AOwner: TComponent);
  780. begin
  781. inherited Create(AOwner);
  782. FDelimiter := ',';
  783. FFirstLineAsSchema := FALSE;
  784. end;
  785. procedure TSdfDataSet.InternalInitFieldDefs;
  786. var
  787. pStart, pEnd, len : Integer;
  788. begin
  789. if not IsCursorOpen then
  790. exit;
  791. if (FData.Count = 0) or (Trim(FData[0]) = '') then
  792. FirstLineAsSchema := FALSE
  793. else if (Schema.Count = 0) or (FirstLineAsSchema) then
  794. begin
  795. Schema.Clear;
  796. len := Length(FData[0]);
  797. pEnd := 1;
  798. repeat
  799. while (pEnd <= len) and (FData[0][pEnd] in [#1..' ']) do
  800. Inc(pEnd);
  801. if (pEnd > len) then
  802. break;
  803. pStart := pEnd;
  804. if (FData[0][pStart] = '"') then
  805. begin
  806. repeat
  807. Inc(pEnd);
  808. until (pEnd > len) or (FData[0][pEnd] = '"');
  809. if (FData[0][pEnd] = '"') then
  810. Inc(pStart);
  811. end
  812. else
  813. while (pEnd <= len) and (FData[0][pEnd] <> Delimiter) do
  814. Inc(pEnd);
  815. if (FirstLineAsSchema) then
  816. Schema.Add(Copy(FData[0], pStart, pEnd - pStart))
  817. else
  818. Schema.Add(Format('Field%d', [Schema.Count + 1]));
  819. if (FData[0][pEnd] = '"') then
  820. while (pEnd <= len) and (FData[0][pEnd] <> Delimiter) do
  821. Inc(pEnd);
  822. if (FData[0][pEnd] = Delimiter) then
  823. Inc(pEnd);
  824. until (pEnd > len);
  825. end;
  826. inherited;
  827. end;
  828. function TSdfDataSet.GetRecord(Buffer: PChar; GetMode: TGetMode;
  829. DoCheck: Boolean): TGetResult;
  830. begin
  831. if FirstLineAsSchema then
  832. begin
  833. if (FData.Count < 2) then
  834. Result := grEOF
  835. else
  836. begin
  837. Result := inherited GetRecord(Buffer, GetMode, DoCheck);
  838. if (Result = grOk) and (FCurRec = 0) then
  839. Result := inherited GetRecord(Buffer, GetMode, DoCheck);
  840. end;
  841. end
  842. else
  843. Result := inherited GetRecord(Buffer, GetMode, DoCheck);
  844. end;
  845. function TSdfDataSet.StoreToBuf(Source: String): String;
  846. const
  847. CR :char = #13;
  848. LF :char = #10;
  849. var
  850. i,
  851. p :Integer;
  852. pRet,
  853. pStr,
  854. pStrEnd :PChar;
  855. Ret :String;
  856. begin
  857. SetLength(Ret, FRecordSize);
  858. FillChar(PChar(Ret)^, FRecordSize, Ord(' '));
  859. PStrEnd := PChar(Source);
  860. pRet := PChar(Ret);
  861. for i := 0 to FieldDefs.Count - 1 do
  862. begin
  863. while Boolean(Byte(pStrEnd[0])) and (pStrEnd[0] in [#1..' ']) do
  864. Inc(pStrEnd);
  865. if not Boolean(Byte(pStrEnd[0])) then
  866. break;
  867. pStr := pStrEnd;
  868. if (pStr[0] = '"') then
  869. begin
  870. repeat
  871. Inc(pStrEnd);
  872. until not Boolean(Byte(pStrEnd[0])) or
  873. ((pStrEnd[0] = '"') and ((pStrEnd + 1)[0] in [Delimiter,CR,LF, #0]));
  874. if (pStrEnd[0] = '"') then
  875. Inc(pStr);
  876. end
  877. else
  878. while Boolean(Byte(pStrEnd[0])) and (pStrEnd[0] <> Delimiter) do
  879. Inc(pStrEnd);
  880. p := pStrEnd - pStr;
  881. if (p > FieldDefs[i].Size) then
  882. p := FieldDefs[i].Size;
  883. Move(pStr[0], pRet[0], p);
  884. Inc(pRet, FieldDefs[i].Size);
  885. if (pStrEnd[0] = '"') then
  886. while Boolean(Byte(pStrEnd[0])) and (pStrEnd[0] <> Delimiter) do
  887. Inc(pStrEnd);
  888. if (pStrEnd[0] = Delimiter) then
  889. Inc(pStrEnd);
  890. end;
  891. Result := Ret;
  892. end;
  893. function TSdfDataSet.BufToStore(Buffer: PChar): String;
  894. var
  895. Str : String;
  896. p, i : Integer;
  897. begin
  898. Result := '';
  899. p := 1;
  900. for i := 0 to FieldDefs.Count - 1 do
  901. begin
  902. Str := Trim(Copy(Buffer, p, FieldDefs[i].Size));
  903. Inc(p, FieldDefs[i].Size);
  904. if (StrScan(PChar(Str), FDelimiter) <> nil) then
  905. Str := '"' + Str + '"';
  906. Result := Result + Str + FDelimiter;
  907. end;
  908. p := Length(Result);
  909. while (p > 0) and (Result[p] = FDelimiter) do
  910. begin
  911. System.Delete(Result, p, 1);
  912. Dec(p);
  913. end;
  914. end;
  915. procedure TSdfDataSet.SetDelimiter(Value : Char);
  916. begin
  917. CheckInactive;
  918. FDelimiter := Value;
  919. end;
  920. procedure TSdfDataSet.SetFirstLineAsSchema(Value : Boolean);
  921. begin
  922. CheckInactive;
  923. FFirstLineAsSchema := Value;
  924. end;
  925. //-----------------------------------------------------------------------------
  926. // This procedure is used to register this component on the component palette
  927. //-----------------------------------------------------------------------------
  928. procedure Register;
  929. begin
  930. RegisterComponents('Data Access', [TFixedFormatDataSet]);
  931. RegisterComponents('Data Access', [TSdfDataSet]);
  932. end;
  933. end.