fpdbfexport.pp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. unit fpdbfexport;
  2. {
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1999-2022 by Michael van Canney and other members of the
  5. Free Pascal development team
  6. dbf Export code
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. **********************************************************************}
  13. {$mode objfpc}{$H+}
  14. interface
  15. uses
  16. Classes, SysUtils, db, dbf, fpdbexport;
  17. Type
  18. { TDBFExportFieldItem }
  19. TDBFExportFieldItem = Class(TExportFieldItem)
  20. private
  21. FDestField: TField;
  22. Protected
  23. Property DestField : TField Read FDestField;
  24. end;
  25. { TDBFExportFormatSettings }
  26. TTableFormat = (tfDBaseIII,tfDBaseIV,tfDBaseVII,tfFoxPro,tfVisualFoxPro);
  27. TDBFExportFormatSettings = class(TExportFormatSettings)
  28. private
  29. FAutoRename: Boolean;
  30. FTableFormat: TTableFormat;
  31. public
  32. Procedure Assign(Source : TPersistent); override;
  33. Procedure InitSettings; override;
  34. Published
  35. Property TableFormat : TTableFormat Read FTableFormat Write FTableFormat;
  36. Property AutoRenameFields : Boolean Read FAutoRename Write FAutoRename;
  37. end;
  38. { TFPCustomDBFExport }
  39. TFPCustomDBFExport = Class(TCustomDatasetExporter)
  40. Private
  41. FDBF : TDBF;
  42. FFileName: String;
  43. FAppendData: Boolean;
  44. function GetSettings: TDBFExportFormatSettings;
  45. procedure SetSettings(const AValue: TDBFExportFormatSettings);
  46. Protected
  47. Procedure CheckExportFieldName(ThisExportField: TExportFieldItem; const MaxFieldNameLength: integer);
  48. Function BindFields : Boolean; override;
  49. Function CreateFormatSettings : TCustomExportFormatSettings; override;
  50. Function CreateExportFields : TExportFields; override;
  51. Procedure DoBeforeExecute; override;
  52. Procedure DoAfterExecute; override;
  53. Procedure DoDataRowStart; override;
  54. Procedure DoDataRowEnd; override;
  55. Procedure ExportField(EF : TExportFieldItem); override;
  56. Property FileName : String Read FFileName Write FFileName;
  57. Property AppendData : Boolean Read FAppendData Write FAppendData;
  58. Property DBF : TDBF Read FDBF;
  59. public
  60. Property FormatSettings : TDBFExportFormatSettings Read GetSettings Write SetSettings;
  61. end;
  62. TFPDBFExport = Class(TFPCustomDBFExport)
  63. Published
  64. Property FileName;
  65. Property Dataset;
  66. Property ExportFields;
  67. Property FromCurrent;
  68. Property RestorePosition;
  69. Property FormatSettings;
  70. Property OnExportRow;
  71. end;
  72. Procedure RegisterDBFExportFormat;
  73. Procedure UnRegisterDBFExportFormat;
  74. Const
  75. SDBFExport = 'DBF';
  76. SDBFFilter = '*.dbf';
  77. ResourceString
  78. SErrFailedToDeleteFile = 'Failed to delete existing DBF file: %s';
  79. SDBFDescription = 'DBF files';
  80. implementation
  81. { TFPCustomDBFExport }
  82. function TFPCustomDBFExport.GetSettings: TDBFExportFormatSettings;
  83. begin
  84. Result:=TDBFExportFormatSettings(Inherited FormatSettings);
  85. end;
  86. procedure TFPCustomDBFExport.SetSettings(const AValue: TDBFExportFormatSettings
  87. );
  88. begin
  89. Inherited FormatSettings.Assign(AValue);
  90. end;
  91. procedure TFPCustomDBFExport.CheckExportFieldName(ThisExportField: TExportFieldItem; const MaxFieldNameLength: integer);
  92. // Cut off field name at max length, and rename if it already exists
  93. Const
  94. CounterInvalid=100;
  95. Var
  96. NameCounter : Integer;
  97. NewFieldName : String;
  98. begin
  99. If (Length(ThisExportField.ExportedName)>MaxFieldNameLength) then
  100. begin
  101. NewFieldName:=Copy(ThisExportField.ExportedName,1,MaxFieldNameLength);
  102. If ExportFields.IndexOfExportedName(NewFieldName)<>-1 then
  103. begin
  104. // Try using 2-character number sequence to generate unique name
  105. NameCounter:=1;
  106. Repeat
  107. NewFieldName:=Copy(ThisExportField.ExportedName,1,MaxFieldNameLength-2)+Format('%.2d',[NameCounter]);
  108. Until (ExportFields.IndexOfExportedName(NewFieldName)=-1) or (NameCounter=CounterInvalid);
  109. if NameCounter=CounterInvalid then
  110. ExportError('Could not create a unique export field name for field %s',[ThisExportField.FieldName]);
  111. end;
  112. ThisExportField.ExportedName:=NewFieldName;
  113. end;
  114. end;
  115. function TFPCustomDBFExport.BindFields: Boolean;
  116. Const
  117. // Translate tableformat to tablelevel
  118. Levels : Array[TTableFormat] of integer = (3,4,7,25,30);
  119. Var
  120. EF : TDBFExportFieldItem;
  121. i : Integer;
  122. MaxFieldName: integer;
  123. begin
  124. Result:=Inherited;
  125. // DBase III,IV, and FoxPro have a 10 character field length limit.
  126. // Visual Foxpro free tables (without .dbc file) also
  127. // DBase VII has a 32 character field length limit.
  128. if (FormatSettings.TableFormat=tfDbaseVII) then
  129. MaxFieldName:=32
  130. else
  131. MaxFieldName:=10;
  132. try
  133. with FDBF.FieldDefs do
  134. begin
  135. Clear;
  136. For i:=0 to ExportFields.Count-1 do
  137. begin
  138. EF:=ExportFields[i] as TDBFExportFieldItem;
  139. If FormatSettings.AutoRenameFields then
  140. CheckExportFieldName(EF,MaxFieldName);
  141. If EF.Enabled and Assigned(EF.Field) then
  142. Add(EF.ExportedName,EF.Field.DataType,EF.Field.Size);
  143. end;
  144. FDBF.TableLevel:=Levels[FormatSettings.TableFormat];
  145. FDBF.CreateTable;
  146. FDBF.Exclusive := true;
  147. FDBF.Open;
  148. end;
  149. For i:=0 to ExportFields.Count-1 do
  150. begin
  151. EF:=ExportFIelds[i] as TDBFExportFieldItem;
  152. If EF.Enabled then
  153. EF.FDestField:=FDBF.FieldByName(EF.ExportedName);
  154. end;
  155. except
  156. UnBindFields;
  157. Raise;
  158. end;
  159. end;
  160. function TFPCustomDBFExport.CreateFormatSettings: TCustomExportFormatSettings;
  161. begin
  162. Result:=TDBFExportFormatSettings.Create(True);
  163. end;
  164. function TFPCustomDBFExport.CreateExportFields: TExportFields;
  165. begin
  166. Result:=TExportFields.Create(TDBFExportFieldItem);
  167. end;
  168. procedure TFPCustomDBFExport.DoBeforeExecute;
  169. Var
  170. FE : Boolean;
  171. begin
  172. Inherited;
  173. FDBF:=TDBF.Create(Self);
  174. FDBF.TableName:=FFileName;
  175. FDBF.DefaultBufferCount:=2;
  176. FE:=FileExists(FFileName);
  177. If FAppendData and FE then
  178. FDBF.Open
  179. else
  180. begin
  181. If FE and Not AppendData then
  182. begin
  183. If not DeleteFile(FFileName) then
  184. Raise EDataExporter.CreateFmt(SErrFailedToDeleteFile,[FFileName]);
  185. end;
  186. end;
  187. end;
  188. procedure TFPCustomDBFExport.DoAfterExecute;
  189. begin
  190. try
  191. FreeAndNil(FDBF);
  192. finally
  193. Inherited;
  194. end;
  195. end;
  196. procedure TFPCustomDBFExport.DoDataRowStart;
  197. begin
  198. FDBF.Append;
  199. end;
  200. procedure TFPCustomDBFExport.DoDataRowEnd;
  201. begin
  202. FDBF.Post;
  203. end;
  204. procedure TFPCustomDBFExport.ExportField(EF: TExportFieldItem);
  205. Var
  206. F : TDBFExportFieldItem;
  207. begin
  208. F:=EF as TDBFExportFieldItem;
  209. With F do
  210. // Export depending on field datatype;
  211. // convert to dbf data types where necessary.
  212. // Fall back to string if unknown datatype
  213. If FIeld.IsNull then
  214. DestField.Clear
  215. else if Field.Datatype in (IntFieldTypes+[ftAutoInc,ftLargeInt]) then
  216. DestField.AsInteger:=Field.AsInteger
  217. else if Field.Datatype in [ftBCD,ftCurrency,ftFloat,ftFMTBcd] then
  218. DestField.AsFloat:=Field.AsFloat
  219. else if Field.DataType in [ftString,ftFixedChar] then
  220. DestField.AsString:=Field.AsString
  221. else if (Field.DataType in ([ftWideMemo,ftWideString,ftFixedWideChar]+BlobFieldTypes)) then
  222. DestField.AsWideString:=Field.AsWideString
  223. { Note: we test for the wide text fields before the MemoFieldTypes, in order to
  224. let ftWideMemo end up at the right place }
  225. else if Field.DataType in MemoFieldTypes then
  226. DestField.AsString:=Field.AsString
  227. else if Field.DataType=ftBoolean then
  228. DestField.AsBoolean:=Field.AsBoolean
  229. else if field.DataType in DateFieldTypes then
  230. DestField.AsDatetime:=Field.AsDateTime
  231. else
  232. DestField.AsString:=Field.AsString
  233. end;
  234. Procedure RegisterDBFExportFormat;
  235. begin
  236. RegisterExportFormat(SDBFExport,SDBFDescription,SDBFFilter,TFPDBFExport);
  237. end;
  238. Procedure UnRegisterDBFExportFormat;
  239. begin
  240. UnregisterExportFormat(SDBFExport);
  241. end;
  242. { TDBFExportFormatSettings }
  243. procedure TDBFExportFormatSettings.Assign(Source: TPersistent);
  244. Var
  245. FS : TDBFExportFormatSettings;
  246. begin
  247. If Source is TDBFExportFormatSettings then
  248. begin
  249. FS:=Source as TDBFExportFormatSettings;
  250. AutoRenameFields:=FS.AutoRenameFields;
  251. TableFormat:=FS.TableFormat;
  252. end;
  253. inherited Assign(Source);
  254. end;
  255. procedure TDBFExportFormatSettings.InitSettings;
  256. begin
  257. inherited InitSettings;
  258. FAutoRename:=true; // sensible to avoid duplicate table names
  259. FTableFormat:=tfDBaseIV; //often used
  260. end;
  261. end.