fpdbfexport.pp 7.8 KB

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