sdfdata.pp 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022
  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. {$else}
  389. inherited;
  390. {$endif}
  391. end;
  392. // Loads Data from a stream.
  393. procedure TFixedFormatDataSet.LoadFromStream(Stream: TStream);
  394. begin
  395. if assigned(stream) then
  396. begin
  397. Active := False; //Make sure the Dataset is Closed.
  398. Stream.Position := 0; //Make sure you are at the top of the Stream.
  399. FLoadfromStream := True;
  400. if not Assigned(FData) then
  401. raise Exception.Create('Data buffer unassigned');
  402. FData.LoadFromStream(Stream);
  403. Active := True;
  404. end
  405. else
  406. raise exception.Create('Invalid Stream Assigned (Load From Stream');
  407. end;
  408. // Saves Data as text to a stream.
  409. procedure TFixedFormatDataSet.SavetoStream(Stream: TStream);
  410. begin
  411. if assigned(stream) then
  412. FData.SaveToStream(Stream)
  413. else
  414. raise exception.Create('Invalid Stream Assigned (Save To Stream');
  415. end;
  416. // Record Functions
  417. function TFixedFormatDataSet.AllocRecordBuffer: PChar;
  418. begin
  419. if FRecBufSize > 0 then
  420. Result := AllocMem(FRecBufSize)
  421. else
  422. Result := nil;
  423. end;
  424. procedure TFixedFormatDataSet.FreeRecordBuffer(var Buffer: PChar);
  425. begin
  426. if Buffer <> nil then
  427. FreeMem(Buffer);
  428. end;
  429. procedure TFixedFormatDataSet.InternalInitRecord(Buffer: PChar);
  430. begin
  431. FillChar(Buffer[0], FRecordSize, 0);
  432. end;
  433. procedure TFixedFormatDataSet.ClearCalcFields(Buffer: PChar);
  434. begin
  435. FillChar(Buffer[RecordSize], CalcFieldsSize, 0);
  436. end;
  437. function TFixedFormatDataSet.GetRecord(Buffer: PChar; GetMode: TGetMode;
  438. DoCheck: Boolean): TGetResult;
  439. begin
  440. if (FData.Count < 1) then
  441. Result := grEOF
  442. else
  443. Result := TxtGetRecord(Buffer, GetMode);
  444. if Result = grOK then
  445. begin
  446. if (CalcFieldsSize > 0) then
  447. GetCalcFields(Buffer);
  448. with PRecInfo(Buffer + FRecInfoOfs)^ do
  449. begin
  450. BookmarkFlag := bfCurrent;
  451. RecordNumber := PtrInt(FData.Objects[FCurRec]);
  452. end;
  453. end
  454. else
  455. if (Result = grError) and DoCheck then
  456. DatabaseError('No Records');
  457. end;
  458. function TFixedFormatDataSet.GetRecordCount: Longint;
  459. begin
  460. Result := FData.Count;
  461. end;
  462. function TFixedFormatDataSet.GetRecNo: Longint;
  463. var
  464. BufPtr: PChar;
  465. begin
  466. Result := -1;
  467. if GetActiveRecBuf(BufPtr) then
  468. Result := PRecInfo(BufPtr + FRecInfoOfs)^.RecordNumber;
  469. end;
  470. procedure TFixedFormatDataSet.SetRecNo(Value: Integer);
  471. begin
  472. CheckBrowseMode;
  473. if (Value >= 0) and (Value < FData.Count) and (Value <> RecNo) then
  474. begin
  475. DoBeforeScroll;
  476. FCurRec := Value - 1;
  477. Resync([]);
  478. DoAfterScroll;
  479. end;
  480. end;
  481. function TFixedFormatDataSet.GetRecordSize: Word;
  482. begin
  483. Result := FRecordSize;
  484. end;
  485. function TFixedFormatDataSet.GetActiveRecBuf(var RecBuf: PChar): Boolean;
  486. begin
  487. case State of
  488. dsBrowse: if IsEmpty then RecBuf := nil else RecBuf := ActiveBuffer;
  489. dsEdit, dsInsert: RecBuf := ActiveBuffer;
  490. dsCalcFields: RecBuf := CalcBuffer;
  491. dsFilter: RecBuf := FFilterBuffer;
  492. else
  493. RecBuf := nil;
  494. end;
  495. Result := RecBuf <> nil;
  496. end;
  497. function TFixedFormatDataSet.TxtGetRecord(Buffer : PChar; GetMode: TGetMode): TGetResult;
  498. var
  499. Accepted : Boolean;
  500. begin
  501. Result := grOK;
  502. repeat
  503. Accepted := TRUE;
  504. case GetMode of
  505. gmNext:
  506. if FCurRec >= RecordCount - 1 then
  507. Result := grEOF
  508. else
  509. Inc(FCurRec);
  510. gmPrior:
  511. if FCurRec <= 0 then
  512. Result := grBOF
  513. else
  514. Dec(FCurRec);
  515. gmCurrent:
  516. if (FCurRec < 0) or (FCurRec >= RecordCount) then
  517. Result := grError;
  518. end;
  519. if (Result = grOk) then
  520. begin
  521. Move(PChar(StoreToBuf(FData[FCurRec]))^, Buffer[0], FRecordSize);
  522. if Filtered then
  523. begin
  524. Accepted := RecordFilter(Buffer, FCurRec +1);
  525. if not Accepted and (GetMode = gmCurrent) then
  526. Inc(FCurRec);
  527. end;
  528. end;
  529. until Accepted;
  530. end;
  531. function TFixedFormatDataSet.RecordFilter(RecBuf: Pointer; ARecNo: Integer): Boolean;
  532. var
  533. Accept: Boolean;
  534. SaveState: TDataSetState;
  535. begin // Returns true if accepted in the filter
  536. SaveState := SetTempState(dsFilter);
  537. FFilterBuffer := RecBuf;
  538. PRecInfo(FFilterBuffer + FRecInfoOfs)^.RecordNumber := ARecNo;
  539. Accept := TRUE;
  540. if Accept and Assigned(OnFilterRecord) then
  541. OnFilterRecord(Self, Accept);
  542. RestoreState(SaveState);
  543. Result := Accept;
  544. end;
  545. function TFixedFormatDataSet.GetCanModify: boolean;
  546. begin
  547. Result := not FReadOnly;
  548. end;
  549. // Field Related
  550. procedure TFixedFormatDataSet.LoadFieldScheme(List : TStrings; MaxSize : Integer);
  551. var
  552. tmpFieldName : string;
  553. tmpSchema : TStrings;
  554. i : Integer;
  555. begin
  556. tmpSchema := TStringList.Create;
  557. try // Load Schema Structure
  558. if (Schema.Count > 0) then
  559. begin
  560. tmpSchema.Assign(Schema);
  561. RemoveWhiteLines(tmpSchema, FALSE);
  562. end
  563. else
  564. tmpSchema.Add('Line');
  565. for i := 0 to tmpSchema.Count -1 do // Interpret Schema
  566. begin
  567. tmpFieldName := tmpSchema.Names[i];
  568. if (tmpFieldName = '') then
  569. tmpFieldName := Format('%s=%d', [tmpSchema[i], MaxSize])
  570. else
  571. tmpFieldName := tmpSchema[i];
  572. List.Add(tmpFieldName);
  573. end;
  574. finally
  575. tmpSchema.Free;
  576. end;
  577. end;
  578. function TFixedFormatDataSet.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
  579. var
  580. TempPos, RecBuf: PChar;
  581. begin
  582. Result := GetActiveRecBuf(RecBuf);
  583. if Result then
  584. begin
  585. if Field.FieldNo > 0 then
  586. begin
  587. TempPos := RecBuf;
  588. SetFieldPos(RecBuf, Field.FieldNo);
  589. Result := (RecBuf < StrEnd(TempPos));
  590. end
  591. else
  592. if (State in [dsBrowse, dsEdit, dsInsert, dsCalcFields]) then
  593. begin
  594. Inc(RecBuf, FRecordSize + Field.Offset);
  595. Result := Boolean(Byte(RecBuf[0]));
  596. end;
  597. end;
  598. if Result and (Buffer <> nil) then
  599. begin
  600. StrLCopy(Buffer, RecBuf, Field.Size);
  601. if FTrimSpace then
  602. begin
  603. TempPos := StrEnd(Buffer);
  604. repeat
  605. Dec(TempPos);
  606. if (TempPos[0] = ' ') then
  607. TempPos[0]:= #0
  608. else
  609. break;
  610. until (TempPos = Buffer);
  611. end;
  612. end;
  613. end;
  614. procedure TFixedFormatDataSet.SetFieldData(Field: TField; Buffer: Pointer);
  615. var
  616. RecBuf, BufEnd: PChar;
  617. p : Integer;
  618. begin
  619. if not (State in [dsEdit, dsInsert]) then
  620. DatabaseError('Dataset not in edit or insert mode', Self);
  621. GetActiveRecBuf(RecBuf);
  622. if Field.FieldNo > 0 then
  623. begin
  624. if State = dsCalcFields then
  625. DatabaseError('Dataset not in edit or insert mode', Self);
  626. if Field.ReadOnly and not (State in [dsSetKey, dsFilter]) then
  627. DatabaseErrorFmt('Field ''%s'' cannot be modified', [Field.DisplayName]);
  628. Field.Validate(Buffer);
  629. if Field.FieldKind <> fkInternalCalc then
  630. begin
  631. SetFieldPos(RecBuf, Field.FieldNo);
  632. BufEnd := StrEnd(ActiveBuffer); // Fill with blanks when necessary
  633. if BufEnd > RecBuf then
  634. BufEnd := RecBuf;
  635. FillChar(BufEnd[0], Field.Size + PtrInt(RecBuf) - PtrInt(BufEnd), Ord(' '));
  636. p := StrLen(Buffer);
  637. if p > Field.Size then
  638. p := Field.Size;
  639. Move(Buffer^, RecBuf[0], p);
  640. ActiveBuffer[RecordSize-1] := #0;
  641. end;
  642. end
  643. else // fkCalculated, fkLookup
  644. begin
  645. Inc(RecBuf, FRecordSize + Field.Offset);
  646. Move(Buffer^, RecBuf[0], Field.Size);
  647. end;
  648. if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
  649. DataEvent(deFieldChange, Ptrint(Field));
  650. end;
  651. procedure TFixedFormatDataSet.SetFieldPos(var Buffer : PChar; FieldNo : Integer);
  652. var
  653. i : Integer;
  654. begin
  655. i := 1;
  656. while (i < FieldNo) and (i < FieldDefs.Count) do
  657. begin
  658. Inc(Buffer, FieldDefs.Items[i-1].Size);
  659. Inc(i);
  660. end;
  661. end;
  662. // Navigation / Editing
  663. procedure TFixedFormatDataSet.InternalFirst;
  664. begin
  665. FCurRec := -1;
  666. end;
  667. procedure TFixedFormatDataSet.InternalLast;
  668. begin
  669. FCurRec := FData.Count;
  670. end;
  671. procedure TFixedFormatDataSet.InternalPost;
  672. var
  673. i: Longint;
  674. begin
  675. FSaveChanges := TRUE;
  676. inherited UpdateRecord;
  677. if (State = dsEdit) then // just update the data in the string list
  678. begin
  679. FData[FCurRec] := BufToStore(ActiveBuffer);
  680. end
  681. else
  682. InternalAddRecord(ActiveBuffer, FALSE);
  683. end;
  684. procedure TFixedFormatDataSet.InternalEdit;
  685. begin
  686. end;
  687. procedure TFixedFormatDataSet.InternalDelete;
  688. begin
  689. FSaveChanges := TRUE;
  690. FData.Delete(FCurRec);
  691. if FCurRec >= FData.Count then
  692. Dec(FCurRec);
  693. end;
  694. procedure TFixedFormatDataSet.InternalAddRecord(Buffer: Pointer; DoAppend: Boolean);
  695. begin
  696. FSaveChanges := TRUE;
  697. Inc(FLastBookmark);
  698. if DoAppend then
  699. InternalLast;
  700. if (FCurRec >=0) then
  701. FData.InsertObject(FCurRec, BufToStore(Buffer), TObject(Pointer(FLastBookmark)))
  702. else
  703. FData.AddObject(BufToStore(Buffer), TObject(Pointer(FLastBookmark)));
  704. end;
  705. procedure TFixedFormatDataSet.InternalGotoBookmark(ABookmark: Pointer);
  706. var
  707. Index: Integer;
  708. begin
  709. Index := FData.IndexOfObject(TObject(PPtrInt(ABookmark)^));
  710. if Index <> -1 then
  711. FCurRec := Index
  712. else
  713. DatabaseError('Bookmark not found');
  714. end;
  715. procedure TFixedFormatDataSet.InternalSetToRecord(Buffer: PChar);
  716. begin
  717. if (State <> dsInsert) then
  718. InternalGotoBookmark(@PRecInfo(Buffer + FRecInfoOfs)^.RecordNumber);
  719. end;
  720. function TFixedFormatDataSet.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
  721. begin
  722. Result := PRecInfo(Buffer + FRecInfoOfs)^.BookmarkFlag;
  723. end;
  724. procedure TFixedFormatDataSet.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
  725. begin
  726. PRecInfo(Buffer + FRecInfoOfs)^.BookmarkFlag := Value;
  727. end;
  728. procedure TFixedFormatDataSet.GetBookmarkData(Buffer: PChar; Data: Pointer);
  729. begin
  730. Move(Buffer[FBookmarkOfs], Data^, BookmarkSize);
  731. end;
  732. procedure TFixedFormatDataSet.SetBookmarkData(Buffer: PChar; Data: Pointer);
  733. begin
  734. Move(Data^, Buffer[FBookmarkOfs], BookmarkSize);
  735. end;
  736. procedure TFixedFormatDataSet.RemoveWhiteLines(List : TStrings; IsFileRecord : Boolean);
  737. var
  738. i : integer;
  739. begin
  740. for i := List.Count -1 downto 0 do
  741. begin
  742. if (Trim(List[i]) = '' ) then
  743. if IsFileRecord then
  744. begin
  745. FCurRec := i;
  746. InternalDelete;
  747. end
  748. else
  749. List.Delete(i);
  750. end;
  751. end;
  752. procedure TFixedFormatDataSet.RemoveBlankRecords;
  753. begin
  754. RemoveWhiteLines(FData, TRUE);
  755. end;
  756. procedure TFixedFormatDataSet.RemoveExtraColumns;
  757. var
  758. i : Integer;
  759. begin
  760. for i := FData.Count -1 downto 0 do
  761. FData[i] := BufToStore(PChar(StoreToBuf(FData[i])));
  762. FData.SaveToFile(FileName);
  763. end;
  764. procedure TFixedFormatDataSet.SaveFileAs(strFileName : String);
  765. begin
  766. FData.SaveToFile(strFileName);
  767. FFileName := strFileName;
  768. FSaveChanges := FALSE;
  769. end;
  770. function TFixedFormatDataSet.StoreToBuf(Source: String): String;
  771. begin
  772. Result := Source;
  773. end;
  774. function TFixedFormatDataSet.BufToStore(Buffer: PChar): String;
  775. begin
  776. Result := Copy(Buffer, 1, FRecordSize);
  777. end;
  778. //-----------------------------------------------------------------------------
  779. // TSdfDataSet
  780. //-----------------------------------------------------------------------------
  781. constructor TSdfDataSet.Create(AOwner: TComponent);
  782. begin
  783. inherited Create(AOwner);
  784. FDelimiter := ',';
  785. FFirstLineAsSchema := FALSE;
  786. end;
  787. procedure TSdfDataSet.InternalInitFieldDefs;
  788. var
  789. pStart, pEnd, len : Integer;
  790. begin
  791. if not IsCursorOpen then
  792. exit;
  793. if (FData.Count = 0) or (Trim(FData[0]) = '') then
  794. FirstLineAsSchema := FALSE
  795. else if (Schema.Count = 0) or (FirstLineAsSchema) then
  796. begin
  797. Schema.Clear;
  798. len := Length(FData[0]);
  799. pEnd := 1;
  800. repeat
  801. while (pEnd <= len) and (FData[0][pEnd] in [#1..' ']) do
  802. Inc(pEnd);
  803. if (pEnd > len) then
  804. break;
  805. pStart := pEnd;
  806. if (FData[0][pStart] = '"') then
  807. begin
  808. repeat
  809. Inc(pEnd);
  810. until (pEnd > len) or (FData[0][pEnd] = '"');
  811. if (FData[0][pEnd] = '"') then
  812. Inc(pStart);
  813. end
  814. else
  815. while (pEnd <= len) and (FData[0][pEnd] <> Delimiter) do
  816. Inc(pEnd);
  817. if (FirstLineAsSchema) then
  818. Schema.Add(Copy(FData[0], pStart, pEnd - pStart))
  819. else
  820. Schema.Add(Format('Field%d', [Schema.Count + 1]));
  821. if (FData[0][pEnd] = '"') then
  822. while (pEnd <= len) and (FData[0][pEnd] <> Delimiter) do
  823. Inc(pEnd);
  824. if (FData[0][pEnd] = Delimiter) then
  825. Inc(pEnd);
  826. until (pEnd > len);
  827. end;
  828. inherited;
  829. end;
  830. function TSdfDataSet.GetRecord(Buffer: PChar; GetMode: TGetMode;
  831. DoCheck: Boolean): TGetResult;
  832. begin
  833. if FirstLineAsSchema then
  834. begin
  835. if (FData.Count < 2) then
  836. Result := grEOF
  837. else
  838. begin
  839. Result := inherited GetRecord(Buffer, GetMode, DoCheck);
  840. if (Result = grOk) and (FCurRec = 0) then
  841. Result := inherited GetRecord(Buffer, GetMode, DoCheck);
  842. end;
  843. end
  844. else
  845. Result := inherited GetRecord(Buffer, GetMode, DoCheck);
  846. end;
  847. function TSdfDataSet.StoreToBuf(Source: String): String;
  848. const
  849. CR :char = #13;
  850. LF :char = #10;
  851. var
  852. i,
  853. p :Integer;
  854. pRet,
  855. pStr,
  856. pStrEnd :PChar;
  857. Ret :String;
  858. begin
  859. SetLength(Ret, FRecordSize);
  860. FillChar(PChar(Ret)^, FRecordSize, Ord(' '));
  861. PStrEnd := PChar(Source);
  862. pRet := PChar(Ret);
  863. for i := 0 to FieldDefs.Count - 1 do
  864. begin
  865. while Boolean(Byte(pStrEnd[0])) and (pStrEnd[0] in [#1..' ']) do
  866. Inc(pStrEnd);
  867. if not Boolean(Byte(pStrEnd[0])) then
  868. break;
  869. pStr := pStrEnd;
  870. if (pStr[0] = '"') then
  871. begin
  872. repeat
  873. Inc(pStrEnd);
  874. until not Boolean(Byte(pStrEnd[0])) or
  875. ((pStrEnd[0] = '"') and ((pStrEnd + 1)[0] in [Delimiter,CR,LF, #0]));
  876. if (pStrEnd[0] = '"') then
  877. Inc(pStr);
  878. end
  879. else
  880. while Boolean(Byte(pStrEnd[0])) and (pStrEnd[0] <> Delimiter) do
  881. Inc(pStrEnd);
  882. p := pStrEnd - pStr;
  883. if (p > FieldDefs[i].Size) then
  884. p := FieldDefs[i].Size;
  885. Move(pStr[0], pRet[0], p);
  886. Inc(pRet, FieldDefs[i].Size);
  887. if (pStrEnd[0] = '"') then
  888. while Boolean(Byte(pStrEnd[0])) and (pStrEnd[0] <> Delimiter) do
  889. Inc(pStrEnd);
  890. if (pStrEnd[0] = Delimiter) then
  891. Inc(pStrEnd);
  892. end;
  893. Result := Ret;
  894. end;
  895. function TSdfDataSet.BufToStore(Buffer: PChar): String;
  896. var
  897. Str : String;
  898. p, i : Integer;
  899. begin
  900. Result := '';
  901. p := 1;
  902. for i := 0 to FieldDefs.Count - 1 do
  903. begin
  904. Str := Trim(Copy(Buffer, p, FieldDefs[i].Size));
  905. Inc(p, FieldDefs[i].Size);
  906. if (StrScan(PChar(Str), FDelimiter) <> nil) then
  907. Str := '"' + Str + '"';
  908. Result := Result + Str + FDelimiter;
  909. end;
  910. p := Length(Result);
  911. while (p > 0) and (Result[p] = FDelimiter) do
  912. begin
  913. System.Delete(Result, p, 1);
  914. Dec(p);
  915. end;
  916. end;
  917. procedure TSdfDataSet.SetDelimiter(Value : Char);
  918. begin
  919. CheckInactive;
  920. FDelimiter := Value;
  921. end;
  922. procedure TSdfDataSet.SetFirstLineAsSchema(Value : Boolean);
  923. begin
  924. CheckInactive;
  925. FFirstLineAsSchema := Value;
  926. end;
  927. //-----------------------------------------------------------------------------
  928. // This procedure is used to register this component on the component palette
  929. //-----------------------------------------------------------------------------
  930. procedure Register;
  931. begin
  932. RegisterComponents('Data Access', [TFixedFormatDataSet]);
  933. RegisterComponents('Data Access', [TSdfDataSet]);
  934. end;
  935. end.