1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093 |
- unit SdfData;
- {$mode objfpc}
- {$h+}
- //-----------------------------------------------------------------------------
- { Unit Name : SdfData Application : TSdfDataSet TFixedFormatDataSet Components
- Version : 2.05
- Author : Orlando Arrocha email: [email protected]
- Purpose : This components are designed to access directly text files as
- database tables. The files may be limited (SDF) or fixed size
- columns.
- ---------------
- Modifications
- ---------------
- 14/Jul/11 BigChimp:
- Added AllowMultiLine property so user can use fields that have line endings
- (Carriage Return and/or Line Feed) embedded in their fields (fields need to be
- quoted). Enabled by default; will break compatibility with earlier versions of
- SdfData, but using multilines would have resulted in corrupted import anyway.
- 12/Mar/04 Lazarus version (Sergey Smirnov AKA SSY)
- Locate and CheckString functions are removed because of Variant data type.
- Many things are changed for FPC/Lazarus compatibility.
- 02/Jun/02 Version 2.05 (Doriano Biondelli)
- TrimSpace property added for those cases where you need to retrieve the
- field with spaces.
- 01/Jan/02 Version 2.04 (Orlando Arrocha)
- FieldList is now populated.
- Locate was changed to improve speed and some bug fixing too. Thanks for
- asking and testing Marcelo Castro
- 16/Dec/01 Version 2.03 (Orlando Arrocha)
- Fixed some bugs and added some recomentdations. Here is a list:
- Quotations on the last field was not removed properly. Special thanks to
- Daniel Nakasone for helping with the solution.
- Appending first record to empty files was failing. Thanks again Daniel
- Nakasone for the report
- GetFieldData now trims the trailing spaces of the field, so users doesn't
- needs to do it by themselves anymore. Thanks for the recomendation
- Juergen Gehrke.
- FieldDefs is now available from the designer. Recomended by Leslie Drewery.
- ****** THANKS TO ALL & KEEP SENDING RECOMENDATIONS *****
- 05/Oct/01 Version 2.02 (Ben Hay)
- Locate function : implement the virtual tdataset method "Locate".
- ****** THANKS BEN *****
- 11/Sep/01 Version 2.01 (Leslie Drewery)
- Added additional logic to handle Corrupt Data by making sure the
- Quotes are closed and the delimiter/<CR>/<LF> are the next
- characters.
- Altered buffer method to create on constructor and cleared when opened.
- New Resource File. Nice Icons
- SavetoStream method included
- LoadFromStream method included
- ****** THANKS LESLIE *****
- 14/Ago/01 Version 2.00 (Orlando Arrocha)
- John Dung Nguyen showed me how to make this compatible with C-Builder
- and encouraged me to include a filter.
- Dimitry V. Borko says that russian CSV files used other delimiters,
- so now you can change it.
- OnFilter and other events included.
- Delimiter property added to TSdfDataSet. No more dependency on CommaText
- methodology -- choose your own delimiter.
- BufToStore/StoreToBuf methods lets you translate data records to and from
- your propietary storage format.
- TTextDataSet removed dependencies.
- TBaseTextDataSet class removed. // TBaseTextDataSet = TFixedFormatDataSet;
- ****** THANKS JOHN ****** ***** THANKS DIMMY *****
- 19/Jul/01 Version 1.03 (Orlando Arrocha)
- TBaseTextDataSet class introduced.
- FileName property changed datatype to TFileName and removed the property
- editor to segregate design-time code from runtime units.
- *** To add file browsing functionality please install
- *** TFileNamePropertyEditor -- also freeware.
- ********** THANKS WAYNE *********
- 18/Jun/01 Version 1.02 (Wayne Brantley)
- Schema replaces SchemaFileName property. Same as SchemaFileName, except
- you can define the schema inside the component. If you still need an
- external file, just use Schema.LoadFromFile()
- TFixedFormatDataSet class introduced. Use this class for a Fixed length
- format file (instead of delimited). The full schema definition
- (including lengths) is obviously required.
- Bug Fixed - When FirstLineSchema is true and there were no records, it
- would display garbage.
- 30/Mar/01 Version 1.01 (Orlando Arrocha)
- Ligia Maria Pimentel suggested to use the first line of the file to
- define the field names. ****** THANKS LIGIA ******
- FileMustExist property. You must put this property to FALSE if you want to
- create a new file.
- FirstLineSchema property. You can define the field names on the first line
- of your file. Fields have to be defined with this format
- <field_name1> [= field_size1] , <field_name2> [= field_size2] ...
- SchemaFileName property. (Changed to Schema by 1.02 Wayne)
- Lets you define the fields attributes (only supports field name and
- size). Have to be defined in this format (one field per line) :
- <field_name> [= field_size]
- NOTE: fields that doesn't define the length get the record size.
- RemoveBlankRecords procedure. Removes all the blank records from the file.
- RemoveExtraColumns procedure. If the file have more columns than the
- scheme or the field definition at design time, it remove the extra
- values from the file.
- SaveFileAs. Let you save the file to another filename.
- NOTE: This component save changes on closing the table, so you can use
- this to save data before that event.
- Jan 2001 Version 1.0 TSdfDataSet introduced.
- ---------
- TERMS
- ---------
- This component is provided AS-IS without any warranty of any kind, either
- express or implied. This component is freeware and can be used in any software
- product. Credits on applications will be welcomed.
- If you find it useful, improve it or have a wish list... please drop me a mail,
- I'll be glad to hear your comments.
- ----------------
- How to Install
- ----------------
- 1. Copy this SDFDATA.PAS and the associated SDFDATA.DCR to the folder from
- where you wish to install the component. This will probably be $(DELPHI)\lib
- or a sub-folder.
- 2. Install the TSdfDataSet and TFixedFormatDataSet components by choosing the
- Component | Install Component menu option.
- 3. Select the "Into exisiting package" page of the Install Components dialogue.
- 4. Browse to the folder where you saved this file and select it.
- 5. Ensure that the "Package file name" edit box contains $(DELPHI)\DCLUSR??.DPK
- or the one you prefer for DB related objects.
- 6. Accept that the package will be rebuilt.
- }
- //-----------------------------------------------------------------------------
- interface
- uses
- DB, Classes, SysUtils, DBConst;
- type
- //-----------------------------------------------------------------------------
- // TRecInfo
- PRecInfo = ^TRecInfo;
- TRecInfo = packed record
- RecordNumber: PtrInt;
- BookmarkFlag: TBookmarkFlag;
- end;
- //-----------------------------------------------------------------------------
- // TBaseTextDataSet
- TFixedFormatDataSet = class(TDataSet)
- private
- FSchema :TStringList;
- FFileName :TFileName;
- FFilterBuffer :TRecordBuffer;
- FFileMustExist :Boolean;
- FReadOnly :Boolean;
- FLoadfromStream :Boolean;
- FTrimSpace :Boolean;
- procedure SetSchema(const Value: TStringList);
- procedure SetFileName(Value : TFileName);
- procedure SetFileMustExist(Value : Boolean);
- procedure SetTrimSpace(Value : Boolean);
- procedure SetReadOnly(Value : Boolean);
- procedure RemoveWhiteLines(List : TStrings; IsFileRecord : Boolean);
- procedure LoadFieldScheme(List : TStrings; MaxSize : Integer);
- function GetActiveRecBuf(var RecBuf: TRecordBuffer): Boolean;
- procedure SetFieldPos(var Buffer : TRecordBuffer; FieldNo : Integer);
- protected
- FData :TStringlist;
- FCurRec :Integer;
- FRecBufSize :Integer;
- FRecordSize :Integer;
- FLastBookmark :PtrInt;
- FRecInfoOfs :Integer;
- FBookmarkOfs :Integer;
- FSaveChanges :Boolean;
- FDefaultRecordLength:Cardinal;
- FDataOffset : Integer;
- protected
- function AllocRecordBuffer: TRecordBuffer; override;
- procedure FreeRecordBuffer(var Buffer: TRecordBuffer); override;
- procedure InternalAddRecord(Buffer: Pointer; DoAppend: Boolean); override;
- procedure InternalClose; override;
- procedure InternalDelete; override;
- procedure InternalFirst; override;
- procedure InternalGotoBookmark(ABookmark: Pointer); override;
- procedure InternalHandleException; override;
- procedure InternalInitFieldDefs; override;
- procedure InternalInitRecord(Buffer: TRecordBuffer); override;
- procedure InternalLast; override;
- procedure InternalOpen; override;
- procedure InternalPost; override;
- procedure InternalEdit; override;
- procedure InternalSetToRecord(Buffer: TRecordBuffer); override;
- function IsCursorOpen: Boolean; override;
- procedure GetBookmarkData(Buffer: TRecordBuffer; Data: Pointer); override;
- function GetBookmarkFlag(Buffer: TRecordBuffer): TBookmarkFlag; override;
- function GetRecord(Buffer: TRecordBuffer; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
- function GetRecordSize: Word; override;
- procedure SetBookmarkFlag(Buffer: TRecordBuffer; Value: TBookmarkFlag); override;
- procedure SetBookmarkData(Buffer: TRecordBuffer; Data: Pointer); override;
- procedure SetFieldData(Field: TField; Buffer: Pointer); override;
- procedure ClearCalcFields(Buffer: TRecordBuffer); override;
- function GetRecordCount: Integer; override;
- function GetRecNo: Integer; override;
- procedure SetRecNo(Value: Integer); override;
- function GetCanModify: boolean; override;
- function TxtGetRecord(Buffer : TRecordBuffer; GetMode: TGetMode): TGetResult;
- function RecordFilter(RecBuf: Pointer; ARecNo: Integer): Boolean;
- function BufToStore(Buffer: TRecordBuffer): String; virtual;
- function StoreToBuf(Source: String): String; virtual;
- public
- property DefaultRecordLength: Cardinal read FDefaultRecordLength
- write FDefaultRecordLength default 250;
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
- procedure RemoveBlankRecords; dynamic;
- procedure RemoveExtraColumns; dynamic;
- procedure SaveFileAs(strFileName : String); dynamic;
- property CanModify;
- procedure LoadFromStream(Stream :TStream);
- procedure SavetoStream(Stream :TStream);
- published
- property FileMustExist: Boolean read FFileMustExist write SetFileMustExist;
- property ReadOnly: Boolean read FReadOnly write SetReadOnly;
- property FileName : TFileName read FFileName write SetFileName;
- property Schema: TStringList read FSchema write SetSchema;
- property TrimSpace: Boolean read FTrimSpace write SetTrimSpace default True;
- property FieldDefs;
- property Active;
- property AutoCalcFields;
- property Filtered;
- property BeforeOpen;
- property AfterOpen;
- property BeforeClose;
- property AfterClose;
- property BeforeInsert;
- property AfterInsert;
- property BeforeEdit;
- property AfterEdit;
- property BeforePost;
- property AfterPost;
- property BeforeCancel;
- property AfterCancel;
- property BeforeDelete;
- property AfterDelete;
- property BeforeScroll;
- property AfterScroll;
- // property BeforeRefresh;
- // property AfterRefresh;
- property OnCalcFields;
- property OnDeleteError;
- property OnEditError;
- property OnFilterRecord;
- property OnNewRecord;
- property OnPostError;
- end;
- //-----------------------------------------------------------------------------
- // TSdfDataSet
- TSdfDataSet = class(TFixedFormatDataSet)
- private
- FDelimiter : Char;
- FFirstLineAsSchema : Boolean;
- FFMultiLine :Boolean;
- procedure SetMultiLine(const Value: Boolean);
- procedure SetFirstLineAsSchema(Value : Boolean);
- procedure SetDelimiter(Value : Char);
- protected
- procedure InternalInitFieldDefs; override;
- function GetRecord(Buffer: TRecordBuffer; GetMode: TGetMode; DoCheck: Boolean)
- : TGetResult; override;
- function BufToStore(Buffer: TRecordBuffer): String; override;
- function StoreToBuf(Source: String): String; override;
- public
- constructor Create(AOwner: TComponent); override;
- published
- property AllowMultiLine: Boolean read FFMultiLine write SetMultiLine default True; //Whether or not to allow fields containing CR and/or LF
- property Delimiter: Char read FDelimiter write SetDelimiter;
- property FirstLineAsSchema: Boolean read FFirstLineAsSchema write SetFirstLineAsSchema;
- end;
- procedure Register;
- implementation
- //{$R *.Res}
- //-----------------------------------------------------------------------------
- // TFixedFormatDataSet
- //-----------------------------------------------------------------------------
- constructor TFixedFormatDataSet.Create(AOwner : TComponent);
- begin
- FDefaultRecordLength := 250;
- FFileMustExist := TRUE;
- FLoadfromStream := False;
- FRecordSize := 0;
- FTrimSpace := TRUE;
- FSchema := TStringList.Create;
- FData := TStringList.Create; // Load the textfile into a stringlist
- inherited Create(AOwner);
- end;
- destructor TFixedFormatDataSet.Destroy;
- begin
- inherited Destroy;
- FData.Free;
- FSchema.Free;
- end;
- procedure TFixedFormatDataSet.SetSchema(const Value: TStringList);
- begin
- CheckInactive;
- FSchema.Assign(Value);
- end;
- procedure TFixedFormatDataSet.SetFileMustExist(Value : Boolean);
- begin
- CheckInactive;
- FFileMustExist := Value;
- end;
- procedure TFixedFormatDataSet.SetTrimSpace(Value : Boolean);
- begin
- CheckInactive;
- FTrimSpace := Value;
- end;
- procedure TFixedFormatDataSet.SetReadOnly(Value : Boolean);
- begin
- CheckInactive;
- FReadOnly := Value;
- end;
- procedure TFixedFormatDataSet.SetFileName(Value : TFileName);
- begin
- CheckInactive;
- FFileName := Value;
- end;
- procedure TFixedFormatDataSet.InternalInitFieldDefs;
- var
- i, len, Maxlen :Integer;
- LstFields :TStrings;
- begin
- if not Assigned(FData) then
- exit;
- FRecordSize := 0;
- Maxlen := 0;
- FieldDefs.Clear;
- for i := FData.Count - 1 downto 0 do // Find out the longest record
- begin
- len := Length(FData[i]);
- if len > Maxlen then
- Maxlen := len;
- FData.Objects[i] := TObject(Pointer(i+1)); // Fabricate Bookmarks
- end;
- if (Maxlen = 0) then
- Maxlen := FDefaultRecordLength;
- LstFields := TStringList.Create;
- try
- LoadFieldScheme(LstFields, Maxlen);
- for i := 0 to LstFields.Count -1 do // Add fields
- begin
- len := StrToIntDef(LstFields.Values[LstFields.Names[i]], Maxlen);
- FieldDefs.Add(Trim(LstFields.Names[i]), ftString, len, False);
- Inc(FRecordSize, len);
- end;
- finally
- LstFields.Free;
- end;
- end;
- procedure TFixedFormatDataSet.InternalOpen;
- var
- Stream : TStream;
- begin
- FCurRec := -1;
- FSaveChanges := FALSE;
- if not Assigned(FData) then
- FData := TStringList.Create;
- if (not FileMustExist) and (not FileExists(FileName)) then
- begin
- Stream := TFileStream.Create(FileName, fmCreate);
- Stream.Free;
- end;
- if not FLoadfromStream then
- FData.LoadFromFile(FileName);
- FRecordSize := FDefaultRecordLength;
- InternalInitFieldDefs;
- if DefaultFields then
- CreateFields;
- BindFields(TRUE);
- if FRecordSize = 0 then
- FRecordSize := FDefaultRecordLength;
- BookmarkSize := SizeOf(PtrInt);
- FRecInfoOfs := FRecordSize + CalcFieldsSize; // Initialize the offset for TRecInfo in the buffer
- FBookmarkOfs := FRecInfoOfs + SizeOf(TRecInfo);
- FRecBufSize := FBookmarkOfs + BookmarkSize;
- FLastBookmark := FData.Count;
- end;
- procedure TFixedFormatDataSet.InternalClose;
- begin
- if (not FReadOnly) and (FSaveChanges) then // Write any edits to disk
- FData.SaveToFile(FileName);
- FLoadfromStream := False;
- FData.Clear;
- BindFields(FALSE);
- if DefaultFields then // Destroy the TField
- DestroyFields;
- FCurRec := -1; // Reset these internal flags
- FLastBookmark := 0;
- FRecordSize := 0;
- end;
- function TFixedFormatDataSet.IsCursorOpen: Boolean;
- begin
- Result := Assigned(FData) and (FRecordSize > 0);
- end;
- procedure TFixedFormatDataSet.InternalHandleException;
- begin
- {$ifndef fpc}
- Application.HandleException(Self);
- {$else}
- inherited;
- {$endif}
- end;
- // Loads Data from a stream.
- procedure TFixedFormatDataSet.LoadFromStream(Stream: TStream);
- begin
- if assigned(stream) then
- begin
- Active := False; //Make sure the Dataset is Closed.
- Stream.Position := 0; //Make sure you are at the top of the Stream.
- FLoadfromStream := True;
- if not Assigned(FData) then
- raise Exception.Create('Data buffer unassigned');
- FData.LoadFromStream(Stream);
- Active := True;
- end
- else
- raise exception.Create('Invalid Stream Assigned (Load From Stream');
- end;
- // Saves Data as text to a stream.
- procedure TFixedFormatDataSet.SavetoStream(Stream: TStream);
- begin
- if assigned(stream) then
- FData.SaveToStream(Stream)
- else
- raise exception.Create('Invalid Stream Assigned (Save To Stream');
- end;
- // Record Functions
- function TFixedFormatDataSet.AllocRecordBuffer: TRecordBuffer;
- begin
- if FRecBufSize > 0 then
- Result := AllocMem(FRecBufSize)
- else
- Result := nil;
- end;
- procedure TFixedFormatDataSet.FreeRecordBuffer(var Buffer: TRecordBuffer);
- begin
- if Buffer <> nil then
- FreeMem(Buffer);
- end;
- procedure TFixedFormatDataSet.InternalInitRecord(Buffer: TRecordBuffer);
- begin
- FillChar(Buffer[0], FRecordSize, 0);
- end;
- procedure TFixedFormatDataSet.ClearCalcFields(Buffer: TRecordBuffer);
- begin
- FillChar(Buffer[RecordSize], CalcFieldsSize, 0);
- end;
- function TFixedFormatDataSet.GetRecord(Buffer: TRecordBuffer; GetMode: TGetMode;
- DoCheck: Boolean): TGetResult;
- begin
- if (FData.Count < (1+FDataOffset)) then
- Result := grEOF
- else
- Result := TxtGetRecord(Buffer, GetMode);
- if Result = grOK then
- begin
- if (CalcFieldsSize > 0) then
- GetCalcFields(Buffer);
- with PRecInfo(Buffer + FRecInfoOfs)^ do
- begin
- BookmarkFlag := bfCurrent;
- RecordNumber := PtrInt(FData.Objects[FCurRec]);
- end;
- end
- else
- if (Result = grError) and DoCheck then
- DatabaseError('No Records');
- end;
- function TFixedFormatDataSet.GetRecordCount: Longint;
- begin
- Result := FData.Count;
- end;
- function TFixedFormatDataSet.GetRecNo: Longint;
- var
- BufPtr: TRecordBuffer;
- begin
- Result := -1;
- if GetActiveRecBuf(BufPtr) then
- Result := PRecInfo(BufPtr + FRecInfoOfs)^.RecordNumber;
- end;
- procedure TFixedFormatDataSet.SetRecNo(Value: Integer);
- begin
- CheckBrowseMode;
- if (Value >= 0) and (Value < FData.Count) and (Value <> RecNo) then
- begin
- DoBeforeScroll;
- FCurRec := Value - 1;
- Resync([]);
- DoAfterScroll;
- end;
- end;
- function TFixedFormatDataSet.GetRecordSize: Word;
- begin
- Result := FRecordSize;
- end;
- function TFixedFormatDataSet.GetActiveRecBuf(var RecBuf: TRecordBuffer): Boolean;
- begin
- case State of
- dsBrowse: if IsEmpty then RecBuf := nil else RecBuf := ActiveBuffer;
- dsEdit, dsInsert: RecBuf := ActiveBuffer;
- dsCalcFields: RecBuf := CalcBuffer;
- dsFilter: RecBuf := FFilterBuffer;
- else
- RecBuf := nil;
- end;
- Result := RecBuf <> nil;
- end;
- function TFixedFormatDataSet.TxtGetRecord(Buffer : TRecordBuffer; GetMode: TGetMode): TGetResult;
- var
- Accepted : Boolean;
- begin
- Result := grOK;
- repeat
- Accepted := TRUE;
- case GetMode of
- gmNext:
- if FCurRec >= RecordCount - 1 then
- Result := grEOF
- else
- Inc(FCurRec);
- gmPrior:
- if FCurRec <= FDataOffset then
- Result := grBOF
- else
- Dec(FCurRec);
- gmCurrent:
- if (FCurRec < FDataOffset) or (FCurRec >= RecordCount) then
- Result := grError;
- end;
- if (Result = grOk) then
- begin
- Move(PChar(StoreToBuf(FData[FCurRec]))^, Buffer[0], FRecordSize);
- if Filtered then
- begin
- Accepted := RecordFilter(Buffer, FCurRec +1);
- if not Accepted and (GetMode = gmCurrent) then
- Inc(FCurRec);
- end;
- end;
- until Accepted;
- end;
- function TFixedFormatDataSet.RecordFilter(RecBuf: Pointer; ARecNo: Integer): Boolean;
- var
- Accept: Boolean;
- SaveState: TDataSetState;
- begin // Returns true if accepted in the filter
- SaveState := SetTempState(dsFilter);
- FFilterBuffer := RecBuf;
- PRecInfo(FFilterBuffer + FRecInfoOfs)^.RecordNumber := ARecNo;
- Accept := TRUE;
- if Accept and Assigned(OnFilterRecord) then
- OnFilterRecord(Self, Accept);
- RestoreState(SaveState);
- Result := Accept;
- end;
- function TFixedFormatDataSet.GetCanModify: boolean;
- begin
- Result := not FReadOnly;
- end;
- // Field Related
- procedure TFixedFormatDataSet.LoadFieldScheme(List : TStrings; MaxSize : Integer);
- var
- tmpFieldName : string;
- tmpSchema : TStrings;
- i : Integer;
- begin
- tmpSchema := TStringList.Create;
- try // Load Schema Structure
- if (Schema.Count > 0) then
- begin
- tmpSchema.Assign(Schema);
- RemoveWhiteLines(tmpSchema, FALSE);
- end
- else
- tmpSchema.Add('Line');
- for i := 0 to tmpSchema.Count -1 do // Interpret Schema
- begin
- tmpFieldName := tmpSchema.Names[i];
- if (tmpFieldName = '') then
- tmpFieldName := Format('%s=%d', [tmpSchema[i], MaxSize])
- else
- tmpFieldName := tmpSchema[i];
- List.Add(tmpFieldName);
- end;
- finally
- tmpSchema.Free;
- end;
- end;
- function TFixedFormatDataSet.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
- var
- TempPos, recbuf : PChar;
- begin
- Result := GetActiveRecBuf(TRecordBuffer(RecBuf));
- if Result then
- begin
- if Field.FieldNo > 0 then
- begin
- TempPos := RecBuf;
- SetFieldPos(TRecordBuffer(RecBuf), Field.FieldNo);
- Result := (RecBuf < StrEnd(TempPos));
- end
- else
- if (State in [dsBrowse, dsEdit, dsInsert, dsCalcFields]) then
- begin
- Inc(RecBuf, FRecordSize + Field.Offset);
- Result := Boolean(Byte(RecBuf[0]));
- end;
- end;
- if Result and (Buffer <> nil) then
- begin
- StrLCopy(Buffer, RecBuf, Field.Size);
- if FTrimSpace then
- begin
- TempPos := StrEnd(Buffer);
- repeat
- Dec(TempPos);
- if (TempPos[0] = ' ') then
- TempPos[0]:= #0
- else
- break;
- until (TempPos = Buffer);
- end;
- end;
- end;
- procedure TFixedFormatDataSet.SetFieldData(Field: TField; Buffer: Pointer);
- var
- RecBuf: PChar;
- BufEnd: PChar;
- p : Integer;
- begin
- if not (State in dsWriteModes) then
- DatabaseError(SNotEditing, Self);
- GetActiveRecBuf(TRecordBuffer(RecBuf));
- if Field.FieldNo > 0 then
- begin
- if State = dsCalcFields then
- DatabaseError('Dataset not in edit or insert mode', Self);
- if Field.ReadOnly and not (State in [dsSetKey, dsFilter]) then
- DatabaseErrorFmt(SReadOnlyField, [Field.DisplayName]);
- if State in [dsEdit, dsInsert, dsNewValue] then
- Field.Validate(Buffer);
- if Field.FieldKind <> fkInternalCalc then
- begin
- SetFieldPos(TRecordBuffer(RecBuf), Field.FieldNo);
- BufEnd := StrEnd(pansichar(ActiveBuffer)); // Fill with blanks when necessary
- if BufEnd > RecBuf then
- BufEnd := RecBuf;
- FillChar(BufEnd[0], Field.Size + PtrInt(RecBuf) - PtrInt(BufEnd), Ord(' '));
- p := StrLen(Buffer);
- if p > Field.Size then
- p := Field.Size;
- Move(Buffer^, RecBuf[0], p);
- end;
- end
- else // fkCalculated, fkLookup
- begin
- Inc(RecBuf, FRecordSize + Field.Offset);
- Move(Buffer^, RecBuf[0], Field.Size);
- end;
- if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
- DataEvent(deFieldChange, Ptrint(Field));
- end;
- procedure TFixedFormatDataSet.SetFieldPos(var Buffer : TRecordBuffer; FieldNo : Integer);
- var
- i : Integer;
- begin
- i := 1;
- while (i < FieldNo) and (i < FieldDefs.Count) do
- begin
- Inc(Buffer, FieldDefs.Items[i-1].Size);
- Inc(i);
- end;
- end;
- // Navigation / Editing
- procedure TFixedFormatDataSet.InternalFirst;
- begin
- FCurRec := -1;
- end;
- procedure TFixedFormatDataSet.InternalLast;
- begin
- FCurRec := FData.Count;
- end;
- procedure TFixedFormatDataSet.InternalPost;
- var
- i: Longint;
- begin
- FSaveChanges := TRUE;
- inherited UpdateRecord;
- if (State = dsEdit) then // just update the data in the string list
- begin
- FData[FCurRec] := BufToStore(ActiveBuffer);
- end
- else
- InternalAddRecord(ActiveBuffer, FALSE);
- end;
- procedure TFixedFormatDataSet.InternalEdit;
- begin
- end;
- procedure TFixedFormatDataSet.InternalDelete;
- begin
- FSaveChanges := TRUE;
- FData.Delete(FCurRec);
- if FCurRec >= FData.Count then
- Dec(FCurRec);
- end;
- procedure TFixedFormatDataSet.InternalAddRecord(Buffer: Pointer; DoAppend: Boolean);
- begin
- FSaveChanges := TRUE;
- Inc(FLastBookmark);
- if DoAppend then
- InternalLast;
- if (FCurRec >=0) then
- FData.InsertObject(FCurRec, BufToStore(Buffer), TObject(Pointer(FLastBookmark)))
- else
- FData.AddObject(BufToStore(Buffer), TObject(Pointer(FLastBookmark)));
- end;
- procedure TFixedFormatDataSet.InternalGotoBookmark(ABookmark: Pointer);
- var
- Index: Integer;
- begin
- Index := FData.IndexOfObject(TObject(PPtrInt(ABookmark)^));
- if Index <> -1 then
- FCurRec := Index
- else
- DatabaseError('Bookmark not found');
- end;
- procedure TFixedFormatDataSet.InternalSetToRecord(Buffer: TRecordBuffer);
- begin
- if (State <> dsInsert) then
- InternalGotoBookmark(@PRecInfo(Buffer + FRecInfoOfs)^.RecordNumber);
- end;
- function TFixedFormatDataSet.GetBookmarkFlag(Buffer: TRecordBuffer): TBookmarkFlag;
- begin
- Result := PRecInfo(Buffer + FRecInfoOfs)^.BookmarkFlag;
- end;
- procedure TFixedFormatDataSet.SetBookmarkFlag(Buffer: TRecordBuffer; Value: TBookmarkFlag);
- begin
- PRecInfo(Buffer + FRecInfoOfs)^.BookmarkFlag := Value;
- end;
- procedure TFixedFormatDataSet.GetBookmarkData(Buffer: TRecordBuffer; Data: Pointer);
- begin
- Move(Buffer[FRecInfoOfs], Data^, BookmarkSize);
- end;
- procedure TFixedFormatDataSet.SetBookmarkData(Buffer: TRecordBuffer; Data: Pointer);
- begin
- Move(Data^, Buffer[FRecInfoOfs], BookmarkSize);
- end;
- procedure TFixedFormatDataSet.RemoveWhiteLines(List : TStrings; IsFileRecord : Boolean);
- var
- i : integer;
- begin
- for i := List.Count -1 downto 0 do
- begin
- if (Trim(List[i]) = '' ) then
- if IsFileRecord then
- begin
- FCurRec := i;
- InternalDelete;
- end
- else
- List.Delete(i);
- end;
- end;
- procedure TFixedFormatDataSet.RemoveBlankRecords;
- begin
- RemoveWhiteLines(FData, TRUE);
- end;
- procedure TFixedFormatDataSet.RemoveExtraColumns;
- var
- i : Integer;
- begin
- for i := FData.Count -1 downto 0 do
- FData[i] := BufToStore(trecordbuffer(StoreToBuf(FData[i])));
- FData.SaveToFile(FileName);
- end;
- procedure TFixedFormatDataSet.SaveFileAs(strFileName : String);
- begin
- FData.SaveToFile(strFileName);
- FFileName := strFileName;
- FSaveChanges := FALSE;
- end;
- function TFixedFormatDataSet.StoreToBuf(Source: String): String;
- begin
- Result := Source;
- end;
- function TFixedFormatDataSet.BufToStore(Buffer: TRecordBuffer): String;
- begin
- Result := Copy(pansichar(Buffer), 1, FRecordSize);
- end;
- //-----------------------------------------------------------------------------
- // TSdfDataSet
- //-----------------------------------------------------------------------------
- constructor TSdfDataSet.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- FDelimiter := ',';
- FFirstLineAsSchema := FALSE;
- FFMultiLine :=False;
- end;
- procedure TSdfDataSet.InternalInitFieldDefs;
- var
- pStart, pEnd, len : Integer;
- begin
- if not IsCursorOpen then
- exit;
- if (FData.Count = 0) or (Trim(FData[0]) = '') then
- begin
- FirstLineAsSchema := FALSE;
- FDataOffset:=0;
- end
- else if (Schema.Count = 0) or (FirstLineAsSchema) then
- begin
- Schema.Clear;
- len := Length(FData[0]);
- pEnd := 1;
- repeat
- while (pEnd <= len) and (FData[0][pEnd] in [#1..' ']) do
- Inc(pEnd);
- if (pEnd > len) then
- break;
- pStart := pEnd;
- if (FData[0][pStart] = '"') then
- begin
- repeat
- Inc(pEnd);
- until (pEnd > len) or (FData[0][pEnd] = '"');
- if (FData[0][pEnd] = '"') then
- Inc(pStart);
- end
- else
- while (pEnd <= len) and (FData[0][pEnd] <> Delimiter) do
- Inc(pEnd);
- if (FirstLineAsSchema) then
- Schema.Add(Copy(FData[0], pStart, pEnd - pStart))
- else
- Schema.Add(Format('Field%d', [Schema.Count + 1]));
- if (FData[0][pEnd] = '"') then
- while (pEnd <= len) and (FData[0][pEnd] <> Delimiter) do
- Inc(pEnd);
- if (FData[0][pEnd] = Delimiter) then
- Inc(pEnd);
- until (pEnd > len);
- end;
- inherited;
- end;
- function TSdfDataSet.GetRecord(Buffer: TRecordBuffer; GetMode: TGetMode;
- DoCheck: Boolean): TGetResult;
- begin
- if FirstLineAsSchema then
- begin
- if (FData.Count < 2) then
- begin
- if GetMode=gmPrior then
- Result := grBOF
- else
- Result := grEOF
- end
- else
- begin
- If (FCurrec=-1) and (GetMode=gmNext) then
- inc(FCurrec);
- Result := inherited GetRecord(Buffer, GetMode, DoCheck);
- end;
- end
- else
- Result := inherited GetRecord(Buffer, GetMode, DoCheck);
- end;
- function TSdfDataSet.StoreToBuf(Source: String): String;
- const
- CR :char = #13;
- LF :char = #10;
- var
- i,
- p :Integer;
- pRet,
- pStr,
- pStrEnd :PChar;
- Ret :String;
- begin
- SetLength(Ret, FRecordSize);
- FillChar(PChar(Ret)^, FRecordSize, Ord(' '));
- PStrEnd := PChar(Source);
- pRet := PChar(Ret);
- for i := 0 to FieldDefs.Count - 1 do
- begin
- while Boolean(Byte(pStrEnd[0])) and (pStrEnd[0] in [#1..' ']) do
- begin
- if FFMultiLine=true then
- begin
- if ((pStrEnd[0]=CR) or (pStrEnd[0]=LF)) then
- begin
- //view this as text, not control characters, so do nothing
- //todo: check if this is really necessary, probably revert
- //to original code as quoted case is handled below
- end;
- end
- else
- begin
- Inc(pStrEnd);
- end;
- end;
- if not Boolean(Byte(pStrEnd[0])) then
- break;
- pStr := pStrEnd;
- if (pStr[0] = '"') then
- begin
- if FFMultiLine=true then
- begin
- repeat
- Inc(pStrEnd);
- until not Boolean(Byte(pStrEnd[0])) or
- ((pStrEnd[0] = '"') and ((pStrEnd + 1)[0] in [Delimiter,#0]));
- end
- else
- begin
- // No multiline, so treat cr/lf as end of record
- repeat
- Inc(pStrEnd);
- until not Boolean(Byte(pStrEnd[0])) or
- ((pStrEnd[0] = '"') and ((pStrEnd + 1)[0] in [Delimiter,CR,LF, #0]));
- end;
- if (pStrEnd[0] = '"') then
- Inc(pStr);
- end
- else
- while Boolean(Byte(pStrEnd[0])) and (pStrEnd[0] <> Delimiter) do
- Inc(pStrEnd);
- p := pStrEnd - pStr;
- if (p > FieldDefs[i].Size) then
- p := FieldDefs[i].Size;
- Move(pStr[0], pRet[0], p);
- Inc(pRet, FieldDefs[i].Size);
- if (pStrEnd[0] = '"') then
- while Boolean(Byte(pStrEnd[0])) and (pStrEnd[0] <> Delimiter) do
- Inc(pStrEnd);
- if (pStrEnd[0] = Delimiter) then
- Inc(pStrEnd);
- end;
- Result := Ret;
- end;
- function TSdfDataSet.BufToStore(Buffer: TRecordBuffer): String;
- const
- QuoteDelimiter='"';
- var
- Str : String;
- p, i : Integer;
- QuoteMe: boolean;
- begin
- Result := '';
- p := 1;
- QuoteMe:=false;
- for i := 0 to FieldDefs.Count - 1 do
- begin
- Str := Trim(Copy(pansichar(Buffer), p, FieldDefs[i].Size));
- Inc(p, FieldDefs[i].Size);
- if FFMultiLine=true then
- begin
- // If multiline enabled, quote whenever we find carriage return or linefeed
- if ((QuoteMe=False) and (StrScan(PChar(Str), #10) <> nil)) then QuoteMe:=true;
- if ((QuoteMe=False) and (StrScan(PChar(Str), #13) <> nil)) then QuoteMe:=true;
- end
- else
- begin
- // If we don't allow multiline, remove all CR and LF because they mess with the record ends:
- StringReplace(Str, #10, '', [rfReplaceAll]);
- StringReplace(Str, #13, '', [rfReplaceAll]);
- end;
- // Check for any delimiters occurring in field text
- if ((QuoteMe=False) and (StrScan(PChar(Str), FDelimiter) <> nil)) then QuoteMe:=true;
- if (QuoteMe=True) then
- Str := QuoteDelimiter + Str + QuoteDelimiter;
- Result := Result + Str + FDelimiter;
- end;
- p := Length(Result);
- while (p > 0) and (Result[p] = FDelimiter) do
- begin
- System.Delete(Result, p, 1);
- Dec(p);
- end;
- end;
- procedure TSdfDataSet.SetDelimiter(Value : Char);
- begin
- CheckInactive;
- FDelimiter := Value;
- end;
- procedure TSdfDataSet.SetFirstLineAsSchema(Value : Boolean);
- begin
- CheckInactive;
- FFirstLineAsSchema := Value;
- FDataOffset:=Ord(FFirstLineAsSchema);
- end;
- procedure TSdfDataSet.SetMultiLine(const Value: Boolean);
- begin
- FFMultiLine:=Value;
- end;
- //-----------------------------------------------------------------------------
- // This procedure is used to register this component on the component palette
- //-----------------------------------------------------------------------------
- procedure Register;
- begin
- RegisterComponents('Data Access', [TFixedFormatDataSet]);
- RegisterComponents('Data Access', [TSdfDataSet]);
- end;
- end.
|