dbf_parser.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. unit dbf_parser;
  2. interface
  3. {$I dbf_common.inc}
  4. uses
  5. SysUtils,
  6. Classes,
  7. {$ifdef KYLIX}
  8. Libc,
  9. {$endif}
  10. {$ifndef WINDOWS}
  11. dbf_wtil,
  12. {$endif}
  13. db,
  14. dbf_prscore,
  15. dbf_common,
  16. dbf_fields,
  17. dbf_prsdef,
  18. dbf_prssupp;
  19. type
  20. TStringFieldMode = (smRaw, smAnsi, smAnsiTrim);
  21. TDbfParser = class(TCustomExpressionParser)
  22. private
  23. FDbfFile: Pointer;
  24. FFieldVarList: TStringList;
  25. FIsExpression: Boolean; // expression or simple field?
  26. FFieldType: TExpressionType;
  27. FCaseInsensitive: Boolean;
  28. FStringFieldMode: TStringFieldMode;
  29. FPartialMatch: boolean;
  30. protected
  31. FCurrentExpression: string;
  32. procedure FillExpressList; override;
  33. procedure HandleUnknownVariable(VarName: string); override;
  34. function GetVariableInfo(VarName: string): TDbfFieldDef;
  35. function CurrentExpression: string; override;
  36. procedure ValidateExpression(AExpression: string); virtual;
  37. function GetResultType: TExpressionType; override;
  38. function GetResultLen: Integer;
  39. procedure SetCaseInsensitive(NewInsensitive: Boolean);
  40. procedure SetStringFieldMode(NewMode: TStringFieldMode);
  41. procedure SetPartialMatch(NewPartialMatch: boolean);
  42. public
  43. constructor Create(ADbfFile: Pointer);
  44. destructor Destroy; override;
  45. procedure ClearExpressions; override;
  46. procedure ParseExpression(AExpression: string); virtual;
  47. function ExtractFromBuffer(Buffer: TRecordBuffer): PChar; virtual;
  48. property DbfFile: Pointer read FDbfFile write FDbfFile;
  49. property Expression: string read FCurrentExpression;
  50. property ResultLen: Integer read GetResultLen;
  51. property CaseInsensitive: Boolean read FCaseInsensitive write SetCaseInsensitive;
  52. property StringFieldMode: TStringFieldMode read FStringFieldMode write SetStringFieldMode;
  53. property PartialMatch: boolean read FPartialMatch write SetPartialMatch;
  54. end;
  55. implementation
  56. uses
  57. dbf,
  58. dbf_dbffile,
  59. dbf_str
  60. {$ifdef WINDOWS}
  61. ,Windows
  62. {$endif}
  63. ;
  64. type
  65. // TFieldVar aids in retrieving field values from records
  66. // in their proper type
  67. TFieldVar = class(TObject)
  68. private
  69. FFieldDef: TDbfFieldDef;
  70. FDbfFile: TDbfFile;
  71. FFieldName: string;
  72. FExprWord: TExprWord;
  73. protected
  74. function GetFieldVal: Pointer; virtual; abstract;
  75. function GetFieldType: TExpressionType; virtual; abstract;
  76. procedure SetExprWord(NewExprWord: TExprWord); virtual;
  77. property ExprWord: TExprWord read FExprWord write SetExprWord;
  78. public
  79. constructor Create(UseFieldDef: TDbfFieldDef; ADbfFile: TDbfFile);
  80. procedure Refresh(Buffer: TRecordBuffer); virtual; abstract;
  81. property FieldVal: Pointer read GetFieldVal;
  82. property FieldDef: TDbfFieldDef read FFieldDef;
  83. property FieldType: TExpressionType read GetFieldType;
  84. property DbfFile: TDbfFile read FDbfFile;
  85. property FieldName: string read FFieldName;
  86. end;
  87. TStringFieldVar = class(TFieldVar)
  88. protected
  89. FFieldVal: PChar;
  90. FMode: TStringFieldMode;
  91. function GetFieldVal: Pointer; override;
  92. function GetFieldType: TExpressionType; override;
  93. procedure SetExprWord(NewExprWord: TExprWord); override;
  94. procedure SetMode(NewMode: TStringFieldMode);
  95. procedure UpdateExprWord;
  96. public
  97. destructor Destroy; override;
  98. procedure Refresh(Buffer: TRecordBuffer); override;
  99. property Mode: TStringFieldMode read FMode write SetMode;
  100. end;
  101. TFloatFieldVar = class(TFieldVar)
  102. private
  103. FFieldVal: Double;
  104. protected
  105. function GetFieldVal: Pointer; override;
  106. function GetFieldType: TExpressionType; override;
  107. public
  108. procedure Refresh(Buffer: TRecordBuffer); override;
  109. end;
  110. TIntegerFieldVar = class(TFieldVar)
  111. private
  112. FFieldVal: Integer;
  113. protected
  114. function GetFieldVal: Pointer; override;
  115. function GetFieldType: TExpressionType; override;
  116. public
  117. procedure Refresh(Buffer: TRecordBuffer); override;
  118. end;
  119. {$ifdef SUPPORT_INT64}
  120. TLargeIntFieldVar = class(TFieldVar)
  121. private
  122. FFieldVal: Int64;
  123. protected
  124. function GetFieldVal: Pointer; override;
  125. function GetFieldType: TExpressionType; override;
  126. public
  127. procedure Refresh(Buffer: TRecordBuffer); override;
  128. end;
  129. {$endif}
  130. TDateTimeFieldVar = class(TFieldVar)
  131. private
  132. FFieldVal: TDateTimeRec;
  133. function GetFieldType: TExpressionType; override;
  134. protected
  135. function GetFieldVal: Pointer; override;
  136. public
  137. procedure Refresh(Buffer: TRecordBuffer); override;
  138. end;
  139. TBooleanFieldVar = class(TFieldVar)
  140. private
  141. FFieldVal: boolean;
  142. function GetFieldType: TExpressionType; override;
  143. protected
  144. function GetFieldVal: Pointer; override;
  145. public
  146. procedure Refresh(Buffer: TRecordBuffer); override;
  147. end;
  148. { TFieldVar }
  149. constructor TFieldVar.Create(UseFieldDef: TDbfFieldDef; ADbfFile: TDbfFile);
  150. begin
  151. inherited Create;
  152. // store field
  153. FFieldDef := UseFieldDef;
  154. FDbfFile := ADbfFile;
  155. FFieldName := UseFieldDef.FieldName;
  156. end;
  157. procedure TFieldVar.SetExprWord(NewExprWord: TExprWord);
  158. begin
  159. FExprWord := NewExprWord;
  160. end;
  161. { TStringFieldVar }
  162. destructor TStringFieldVar.Destroy;
  163. begin
  164. if FMode <> smRaw then
  165. FreeMem(FFieldVal);
  166. inherited;
  167. end;
  168. function TStringFieldVar.GetFieldVal: Pointer;
  169. begin
  170. Result := @FFieldVal;
  171. end;
  172. function TStringFieldVar.GetFieldType: TExpressionType;
  173. begin
  174. Result := etString;
  175. end;
  176. procedure TStringFieldVar.Refresh(Buffer: TRecordBuffer);
  177. var
  178. Len: Integer;
  179. Src: TRecordBuffer;
  180. begin
  181. Src := Buffer+FieldDef.Offset;
  182. if FMode <> smRaw then
  183. begin
  184. // copy field data
  185. Len := FieldDef.Size;
  186. if FMode = smAnsiTrim then
  187. while (Len >= 1) and (Src[Len-1] = TRecordbufferbasetype(' ')) do Dec(Len);
  188. // translate to ANSI
  189. Len := TranslateString(DbfFile.UseCodePage, GetACP, pansichar(Src), FFieldVal, Len);
  190. FFieldVal[Len] := #0;
  191. end else
  192. FFieldVal := pansichar(Src);
  193. end;
  194. procedure TStringFieldVar.SetExprWord(NewExprWord: TExprWord);
  195. begin
  196. inherited;
  197. UpdateExprWord;
  198. end;
  199. procedure TStringFieldVar.UpdateExprWord;
  200. begin
  201. if FMode <> smAnsiTrim then
  202. FExprWord.FixedLen := FieldDef.Size
  203. else
  204. FExprWord.FixedLen := -1;
  205. end;
  206. procedure TStringFieldVar.SetMode(NewMode: TStringFieldMode);
  207. begin
  208. if NewMode = FMode then exit;
  209. FMode := NewMode;
  210. if NewMode = smRaw then
  211. begin
  212. FreeMem(FFieldVal);
  213. FFieldVal := nil;
  214. end else
  215. GetMem(FFieldVal, FieldDef.Size*3+1);
  216. UpdateExprWord;
  217. end;
  218. //--TFloatFieldVar-----------------------------------------------------------
  219. function TFloatFieldVar.GetFieldVal: Pointer;
  220. begin
  221. Result := @FFieldVal;
  222. end;
  223. function TFloatFieldVar.GetFieldType: TExpressionType;
  224. begin
  225. Result := etFloat;
  226. end;
  227. procedure TFloatFieldVar.Refresh(Buffer: TRecordBuffer);
  228. begin
  229. // database width is default 64-bit double
  230. if not FDbfFile.GetFieldDataFromDef(FieldDef, FieldDef.FieldType, Buffer, @FFieldVal, false) then
  231. FFieldVal := 0.0;
  232. end;
  233. //--TIntegerFieldVar----------------------------------------------------------
  234. function TIntegerFieldVar.GetFieldVal: Pointer;
  235. begin
  236. Result := @FFieldVal;
  237. end;
  238. function TIntegerFieldVar.GetFieldType: TExpressionType;
  239. begin
  240. Result := etInteger;
  241. end;
  242. procedure TIntegerFieldVar.Refresh(Buffer: TRecordBuffer);
  243. begin
  244. FFieldVal := 0;
  245. FDbfFile.GetFieldDataFromDef(FieldDef, FieldDef.FieldType, Buffer, @FFieldVal, false);
  246. end;
  247. {$ifdef SUPPORT_INT64}
  248. //--TLargeIntFieldVar----------------------------------------------------------
  249. function TLargeIntFieldVar.GetFieldVal: Pointer;
  250. begin
  251. Result := @FFieldVal;
  252. end;
  253. function TLargeIntFieldVar.GetFieldType: TExpressionType;
  254. begin
  255. Result := etLargeInt;
  256. end;
  257. procedure TLargeIntFieldVar.Refresh(Buffer: TRecordBuffer);
  258. begin
  259. if not FDbfFile.GetFieldDataFromDef(FieldDef, FieldDef.FieldType, Buffer, @FFieldVal, false) then
  260. FFieldVal := 0;
  261. end;
  262. {$endif}
  263. //--TDateTimeFieldVar---------------------------------------------------------
  264. function TDateTimeFieldVar.GetFieldVal: Pointer;
  265. begin
  266. Result := @FFieldVal;
  267. end;
  268. function TDateTimeFieldVar.GetFieldType: TExpressionType;
  269. begin
  270. Result := etDateTime;
  271. end;
  272. procedure TDateTimeFieldVar.Refresh(Buffer: TRecordBuffer);
  273. begin
  274. if not FDbfFile.GetFieldDataFromDef(FieldDef, ftDateTime, Buffer, @FFieldVal, false) then
  275. FFieldVal.DateTime := 0.0;
  276. end;
  277. //--TBooleanFieldVar---------------------------------------------------------
  278. function TBooleanFieldVar.GetFieldVal: Pointer;
  279. begin
  280. Result := @FFieldVal;
  281. end;
  282. function TBooleanFieldVar.GetFieldType: TExpressionType;
  283. begin
  284. Result := etBoolean;
  285. end;
  286. procedure TBooleanFieldVar.Refresh(Buffer: TRecordBuffer);
  287. var
  288. lFieldVal: word;
  289. begin
  290. if FDbfFile.GetFieldDataFromDef(FieldDef, ftBoolean, Buffer, @lFieldVal, false) then
  291. FFieldVal := lFieldVal <> 0
  292. else
  293. FFieldVal := false;
  294. end;
  295. //--TDbfParser---------------------------------------------------------------
  296. constructor TDbfParser.Create(ADbfFile: Pointer);
  297. begin
  298. FDbfFile := ADbfFile;
  299. FFieldVarList := TStringList.Create;
  300. FCaseInsensitive := true;
  301. inherited Create;
  302. end;
  303. destructor TDbfParser.Destroy;
  304. begin
  305. ClearExpressions;
  306. inherited;
  307. FreeAndNil(FFieldVarList);
  308. end;
  309. function TDbfParser.GetResultType: TExpressionType;
  310. begin
  311. // if not a real expression, return type ourself
  312. if FIsExpression then
  313. Result := inherited GetResultType
  314. else
  315. Result := FFieldType;
  316. end;
  317. function TDbfParser.GetResultLen: Integer;
  318. begin
  319. // set result len for fixed length expressions / fields
  320. case ResultType of
  321. etBoolean: Result := 1;
  322. etInteger: Result := 4;
  323. etFloat: Result := 8;
  324. etDateTime: Result := 8;
  325. etString:
  326. begin
  327. if not FIsExpression and (TStringFieldVar(FFieldVarList.Objects[0]).Mode <> smAnsiTrim) then
  328. Result := TStringFieldVar(FFieldVarList.Objects[0]).FieldDef.Size
  329. else
  330. Result := -1;
  331. end;
  332. else
  333. Result := -1;
  334. end;
  335. end;
  336. procedure TDbfParser.SetCaseInsensitive(NewInsensitive: Boolean);
  337. begin
  338. if FCaseInsensitive <> NewInsensitive then
  339. begin
  340. // clear and regenerate functions
  341. FCaseInsensitive := NewInsensitive;
  342. FillExpressList;
  343. end;
  344. end;
  345. procedure TDbfParser.SetPartialMatch(NewPartialMatch: boolean);
  346. begin
  347. if FPartialMatch <> NewPartialMatch then
  348. begin
  349. // refill function list
  350. FPartialMatch := NewPartialMatch;
  351. FillExpressList;
  352. end;
  353. end;
  354. procedure TDbfParser.SetStringFieldMode(NewMode: TStringFieldMode);
  355. var
  356. I: integer;
  357. begin
  358. if FStringFieldMode <> NewMode then
  359. begin
  360. // clear and regenerate functions, custom fields will be deleted too
  361. FStringFieldMode := NewMode;
  362. for I := 0 to FFieldVarList.Count - 1 do
  363. if FFieldVarList.Objects[I] is TStringFieldVar then
  364. TStringFieldVar(FFieldVarList.Objects[I]).Mode := NewMode;
  365. end;
  366. end;
  367. procedure TDbfParser.FillExpressList;
  368. var
  369. lExpression: string;
  370. begin
  371. lExpression := FCurrentExpression;
  372. ClearExpressions;
  373. FWordsList.FreeAll;
  374. FWordsList.AddList(DbfWordsGeneralList, 0, DbfWordsGeneralList.Count - 1);
  375. if FCaseInsensitive then
  376. begin
  377. FWordsList.AddList(DbfWordsInsensGeneralList, 0, DbfWordsInsensGeneralList.Count - 1);
  378. if FPartialMatch then
  379. begin
  380. FWordsList.AddList(DbfWordsInsensPartialList, 0, DbfWordsInsensPartialList.Count - 1);
  381. end else begin
  382. FWordsList.AddList(DbfWordsInsensNoPartialList, 0, DbfWordsInsensNoPartialList.Count - 1);
  383. end;
  384. end else begin
  385. FWordsList.AddList(DbfWordsSensGeneralList, 0, DbfWordsSensGeneralList.Count - 1);
  386. if FPartialMatch then
  387. begin
  388. FWordsList.AddList(DbfWordsSensPartialList, 0, DbfWordsSensPartialList.Count - 1);
  389. end else begin
  390. FWordsList.AddList(DbfWordsSensNoPartialList, 0, DbfWordsSensNoPartialList.Count - 1);
  391. end;
  392. end;
  393. if Length(lExpression) > 0 then
  394. ParseExpression(lExpression);
  395. end;
  396. function TDbfParser.GetVariableInfo(VarName: string): TDbfFieldDef;
  397. begin
  398. Result := TDbfFile(FDbfFile).GetFieldInfo(VarName);
  399. end;
  400. procedure TDbfParser.HandleUnknownVariable(VarName: string);
  401. var
  402. FieldInfo: TDbfFieldDef;
  403. TempFieldVar: TFieldVar;
  404. begin
  405. // is this variable a fieldname?
  406. FieldInfo := GetVariableInfo(VarName);
  407. if FieldInfo = nil then
  408. raise EDbfError.CreateFmt(STRING_INDEX_BASED_ON_UNKNOWN_FIELD, [VarName]);
  409. // define field in parser
  410. case FieldInfo.FieldType of
  411. ftString:
  412. begin
  413. TempFieldVar := TStringFieldVar.Create(FieldInfo, TDbfFile(FDbfFile));
  414. TempFieldVar.ExprWord := DefineStringVariable(VarName, TempFieldVar.FieldVal);
  415. TStringFieldVar(TempFieldVar).Mode := FStringFieldMode;
  416. end;
  417. ftBoolean:
  418. begin
  419. TempFieldVar := TBooleanFieldVar.Create(FieldInfo, TDbfFile(FDbfFile));
  420. TempFieldVar.ExprWord := DefineBooleanVariable(VarName, TempFieldVar.FieldVal);
  421. end;
  422. ftFloat:
  423. begin
  424. TempFieldVar := TFloatFieldVar.Create(FieldInfo, TDbfFile(FDbfFile));
  425. TempFieldVar.ExprWord := DefineFloatVariable(VarName, TempFieldVar.FieldVal);
  426. end;
  427. ftAutoInc, ftInteger, ftSmallInt:
  428. begin
  429. TempFieldVar := TIntegerFieldVar.Create(FieldInfo, TDbfFile(FDbfFile));
  430. TempFieldVar.ExprWord := DefineIntegerVariable(VarName, TempFieldVar.FieldVal);
  431. end;
  432. {$ifdef SUPPORT_INT64}
  433. ftLargeInt:
  434. begin
  435. TempFieldVar := TLargeIntFieldVar.Create(FieldInfo, TDbfFile(FDbfFile));
  436. TempFieldVar.ExprWord := DefineLargeIntVariable(VarName, TempFieldVar.FieldVal);
  437. end;
  438. {$endif}
  439. ftDate, ftDateTime:
  440. begin
  441. TempFieldVar := TDateTimeFieldVar.Create(FieldInfo, TDbfFile(FDbfFile));
  442. TempFieldVar.ExprWord := DefineDateTimeVariable(VarName, TempFieldVar.FieldVal);
  443. end;
  444. else
  445. raise EDbfError.CreateFmt(STRING_INDEX_BASED_ON_INVALID_FIELD, [VarName]);
  446. end;
  447. // add to our own list
  448. FFieldVarList.AddObject(VarName, TempFieldVar);
  449. end;
  450. function TDbfParser.CurrentExpression: string;
  451. begin
  452. Result := FCurrentExpression;
  453. end;
  454. procedure TDbfParser.ClearExpressions;
  455. var
  456. I: Integer;
  457. begin
  458. inherited;
  459. // test if already freed
  460. if FFieldVarList <> nil then
  461. begin
  462. // free field list
  463. for I := 0 to FFieldVarList.Count - 1 do
  464. begin
  465. // replacing with nil = undefining variable
  466. FWordsList.DoFree(TFieldVar(FFieldVarList.Objects[I]).FExprWord);
  467. TFieldVar(FFieldVarList.Objects[I]).Free;
  468. end;
  469. FFieldVarList.Clear;
  470. end;
  471. // clear expression
  472. FCurrentExpression := EmptyStr;
  473. end;
  474. procedure TDbfParser.ValidateExpression(AExpression: string);
  475. begin
  476. end;
  477. procedure TDbfParser.ParseExpression(AExpression: string);
  478. begin
  479. // clear any current expression
  480. ClearExpressions;
  481. // is this a simple field or complex expression?
  482. FIsExpression := GetVariableInfo(AExpression) = nil;
  483. if FIsExpression then
  484. begin
  485. // parse requested
  486. CompileExpression(AExpression);
  487. end else begin
  488. // simple field, create field variable for it
  489. HandleUnknownVariable(AExpression);
  490. FFieldType := TFieldVar(FFieldVarList.Objects[0]).FieldType;
  491. end;
  492. ValidateExpression(AExpression);
  493. // if no errors, assign current expression
  494. FCurrentExpression := AExpression;
  495. end;
  496. function TDbfParser.ExtractFromBuffer(Buffer: TRecordBuffer): PChar;
  497. var
  498. I: Integer;
  499. begin
  500. // prepare all field variables
  501. for I := 0 to FFieldVarList.Count - 1 do
  502. TFieldVar(FFieldVarList.Objects[I]).Refresh(Buffer);
  503. // complex expression?
  504. if FIsExpression then
  505. begin
  506. // execute expression
  507. EvaluateCurrent;
  508. Result := ExpResult;
  509. end else begin
  510. // simple field, get field result
  511. Result := TFieldVar(FFieldVarList.Objects[0]).FieldVal;
  512. // if string then dereference
  513. if FFieldType = etString then
  514. Result := PPChar(Result)^;
  515. end;
  516. end;
  517. end.