home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 October / Chip_2001-10_cd1.bin / zkuste / delphi / kompon / d356 / SDFDATA.ZIP / SdfData.pas < prev    next >
Pascal/Delphi Source File  |  2001-07-20  |  21KB  |  580 lines

  1. unit SdfData;
  2. {-----------------------------------------------------------------------------}
  3. { Name        : SdfData                                                       }
  4. { Application : TBaseTextDataSet TSdfDataSet TFixedFormatDataSet Components   }
  5. { Version     : 1.03                                                          }
  6. { Author      : Orlando Arrocha           email: oarrocha@hotmail.com         }
  7. { Date        : Jan 2001                                                      }
  8. { Purpose     : This components are enhancements of the Borland's Sample      }
  9. {               TTextDataSet to access delimited (CSV/SDF) and fixed text     }
  10. {               files as if they where database tables.                       }
  11. { ---------------                                                             }
  12. {  Modifications                                                              }
  13. { ---------------                                                             }
  14. { 19/Jul/01  Version 1.03 (Orlando Arrocha)                                   }
  15. {            TBaseTextDataSet class introduced.                               }
  16. {            FileName property changed datatype to TFileName and removed the  }
  17. {                 property editor to segregate design-time code from runtime  }
  18. {                 units.                                                      }
  19. {                 To add file browsing functionality please install           }
  20. {                 TFileNamePropertyEditor -- also freeware.                   }
  21. {            Bug Fixed - TSdfDataSet FieldNames were filled with the first    }
  22. {                 line record even when FirstLineAsSchema was FALSE           }
  23. {            Bug Fixed - TFixedFormatDataSet values were filled with garbage  }
  24. {                 when record line were smaller than defined on schema.       }
  25. {            Demo Project introduced.                                         }
  26. {                                           ********** THANKS WAYNE ********* }
  27. { 18/Jun/01  Version 1.02 (Wayne Brantley)                                    }
  28. {            SchemaFileName property replaced with a Schema StringList        }
  29. {               property.  Same as SchemaFileName, except you can define the  }
  30. {               schema inside the component.   If you still need an external  }
  31. {               file, just use Schema.LoadFromFile()                          }
  32. {            TFixedFormatDataSet class introduced.  Use this class for a      }
  33. {               Fixed length format file (instead of delimited).  The full    }
  34. {               schema definition (including lengths) is obviously required.  }
  35. {            Bug Fixed - When FirstLineSchema is true and there were no       }
  36. {               records, it would display garbage.                            }
  37. {                                                                             }
  38. { 30/Mar/01  Version 1.01 (Orlando Arrocha)                                   }
  39. {                 Ligia Maria Pimentel suggested to use the first line of the }
  40. {                 file to define the field names.  ****** THANKS LIGIA ****** }
  41. {            Property editor for file names.                                  }
  42. {                  You'll see the [...] button on the Object inspector        }
  43. {            FileMustExist property.                                          }
  44. {                 I've modified the program to let the component create new   }
  45. {                 files, and considered that it could led to udesirable files }
  46. {                 sometimes. So you must put this property to false if you    }
  47. {                 want to create a new file.                                  }
  48. {            FirstLineSchema property.                                        }
  49. {                 As Ligia suggested, you can define the field names on the   }
  50. {                 first line of your file. I added the field size support and }
  51. {                 the schema file (see below).                                }
  52. {                 Fields have to be defined with this format                  }
  53. {                    <field_name1> [= field_size1] , <field_name2> [= field_size2] ... }
  54. {                  NOTE: Do not leave spaces                                   }
  55. {            SchemaFileName property.  (Changed to Schema by 1.02 Wayne)      }
  56. {                  Lets you define the fields attributes (only supports field }
  57. {                   name and size). Have to be defined in this format         }
  58. {                     One field per line :  <field_name> [= field_size]       }
  59. {                  NOTE: fields that doesn't define the length get the record }
  60. {                        size.                                                }
  61. {            RemoveBlankRecords procedure.                                    }
  62. {                  Removes all the blank records from the file.               }
  63. {            RemoveExtraColumns procedure                                     }
  64. {                  If the schema have less columns than the file, it remove   }
  65. {                  the extra values to make consistent the fields to the      }
  66. {                  scheme.                                                    }
  67. {                  NOTE: If you don't call this procedure, extra columns will }
  68. {                        remain in file, but they won't be shown on dataset   }
  69. {            SaveFileAs(strFileName : String) procedure                       }
  70. {                  Let you save the file to another filename.                 }
  71. {                  NOTE: TTextDataSet component doesn't save changes until    }
  72. {                        you close the table. So you can use this to force    }
  73. {                        writting.                                            }
  74. { ---------                                                                   }
  75. {  TERMS                                                                      }
  76. { ---------                                                                   }
  77. {   This component is provided AS-IS without any warranty of any kind, either }
  78. {   express or implied. This component is freeware and can be used in any     }
  79. {   software product. Credits on applications used will be welcomed.          }
  80. {   If you find it useful, improve it or have a wish list ... please drop me  }
  81. {   a mail, I'll be glad to hear your comments.                               }
  82. { ----------------                                                            }
  83. {  How to Install                                                             }
  84. { ----------------                                                            }
  85. {   1. Copy this SDFDATA.PAS and the associated SDFDATA.DCR to the folder     }
  86. {      from where you wish to install the component. This will probably be    }
  87. {      $(DELPHI)\Projects\BPL or a sub-folder of the $(DELPHI)\lib folder.    }
  88. {   2. Copy to the same folder (the one choosen before) the files             }
  89. {        $(DELPHI)\Demos\DB\Textdata\Textdata.*  (3 files - .pas, .res, .rc)  }
  90. {        $(DELPHI)\Demos\DB\Textdata\Textpkg.*   (2 files - .dpk, .res)       }
  91. {   3. Make the modifications noted under TEXTDATA.PAS Modifications          }
  92. {      subtitle. Note -- change only your copied files.                       }
  93. {   4. Install TEXTPKG.DPL by choosing the File | Open menu option.           }
  94. {   5. Select Delphi Package (.dpk) filter on the Open File dialog and browse }
  95. {      for TEXTPKG.DPK.                                                       }
  96. {   6. Press the Install button and close the window.                         }
  97. {   7. Install the TSdfDataSet and TFixedFormatDataSet components by choosing }
  98. {      the Component | Install Component menu option.                         }
  99. {   8. Select the "Into exisiting package" page of the Install Components     }
  100. {      dialogue box.                                                          }
  101. {   9. Browse to the folder where you saved this file and select it.          }
  102. {  10. Ensure that the "Package file name" edit box contains                  }
  103. {      $...\TEXTPKG.DPK                                                        }
  104. {  11. Accept that the package will be rebuilt.                               }
  105. {                                                                             }
  106. { ******************                                                          }
  107. { * VERY IMPORTANT *                                                          }
  108. { ******************                                                          }
  109. {       You have to modify the file TEXTDATA.PAS, included in the DB Demos,   }
  110. {       as indicated behind (under TEXTDATA.PAS Modifications) and then       }
  111. {       compile and install TextPKG.DPK in order to install this component.   }
  112. {                                                                             }
  113. { ==========================                                                  }
  114. { TEXTDATA.PAS Modifications                                                  }
  115. { ==========================           MAKE A BACKUP OF TEXTDATA.PAS FIRST    }
  116. {                                                                             }
  117. { Line : 327 in Function GetRecord                                            }
  118. {   -- Line says --                                                           }
  119. {     StrLCopy(Buffer, PChar(FData[FCurRec]), MaxStrLen);                     }
  120. {                                             ^^^^^^^^^                       }
  121. {   -- must say  --                                                           }
  122. {     StrLCopy(Buffer, PChar(FData[FCurRec]), GetRecordSize);                 }
  123. {                                             ^^^^^^^^^^^^^                   }
  124. {                                                                             }
  125. { Line : 79 in TTextDataSet class Declaration                                 }
  126. {   -- Line says --                                                           }
  127. { private                                                                     }
  128. { ^^^^^^^                                                                     }
  129. {   -- must say  --                                                           }
  130. { protected                                                                   }
  131. { ^^^^^^^^^                                                                   }
  132. {                                                                             }
  133. {-----------------------------------------------------------------------------}
  134.  
  135. interface
  136.  
  137. uses
  138.   Classes, SysUtils, DB, TextData;
  139.  
  140. type
  141.   { TBaseTextDataSet }
  142.   TBaseTextDataSet = class(TTextDataSet)
  143.   private
  144.     FRecordSize : Integer;
  145.     FSchema: TStringList;
  146.     FFileMustExist : Boolean;
  147.     FFileName : TFileName;
  148.     function ReadSchema: TStringList;
  149.     procedure WriteSchema(const Value: TStringList);
  150.     procedure SetFileName(Value : TFileName);
  151.     procedure SetFileMustExist(Value : Boolean);
  152.     procedure RemoveWhiteLines(List : TStrings; IsFileRecord : Boolean);
  153.   protected
  154.     { Overriden abstract methods }
  155.     procedure InternalOpen; override;
  156.     procedure InternalInitFieldDefs; override;
  157.     function GetRecordSize: Word; override;
  158.   public
  159.     constructor Create(Owner: TComponent); override;
  160.     destructor Destroy; override;
  161.     procedure RemoveBlankRecords;
  162.     procedure SaveFileAs(strFileName : String);
  163.   published
  164.     property FileMustExist: Boolean read FFileMustExist write SetFileMustExist;
  165.     property FileName : TFileName read FFileName write SetFileName;
  166.     property Schema: TStringList read ReadSchema write WriteSchema;
  167.   end;
  168.  
  169. { TSdfDataSet }
  170.  
  171.   TSdfDataSet = class(TBaseTextDataSet)
  172.   private
  173.     FFirstLineAsSchema : Boolean;
  174.     procedure SetFirstLineAsSchema(Value : Boolean);
  175.   protected
  176.     procedure InternalInitFieldDefs; override;
  177.     procedure SetFieldData(Field: TField; Buffer: Pointer); override;
  178.     function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean)
  179.              : TGetResult; override;
  180.   public
  181.     procedure RemoveExtraColumns;
  182.     function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
  183.   published
  184.     property FirstLineAsSchema: Boolean read FFirstLineAsSchema write SetFirstLineAsSchema;
  185.   end;
  186.  
  187.  
  188. { TFixedFormatDataSet }
  189.  
  190.   TFixedFormatDataSet = class(TBaseTextDataSet)
  191.   protected
  192.     procedure InternalOpen; override;
  193.     procedure SetFieldData(Field: TField; Buffer: Pointer); override;
  194.   public
  195.     function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
  196.   end;
  197.  
  198.  
  199. procedure Register;
  200.  
  201. implementation
  202.  
  203. const
  204.  DELIMITERS_GAP = 4;
  205.  
  206. { TBaseTextDataSet }
  207.  
  208. constructor TBaseTextDataSet.Create(Owner: TComponent);
  209. begin
  210.   inherited Create(Owner);
  211.   FFileMustExist := TRUE;
  212.   FSchema:=TStringList.Create;
  213. end;
  214.  
  215. destructor TBaseTextDataSet.Destroy;
  216. begin
  217.   FSchema.Free;
  218.   inherited Destroy;
  219. end;
  220.  
  221. function TBaseTextDataSet.ReadSchema: TStringList;
  222. begin
  223.   result:=FSchema;
  224. end;
  225.  
  226. procedure TBaseTextDataSet.WriteSchema(const Value: TStringList);
  227. begin
  228.   if not Active then
  229.     FSchema.Assign(Value);
  230. end;
  231.  
  232. procedure TBaseTextDataSet.SetFileMustExist(Value : Boolean);
  233. begin
  234.   if ((Active) or (FFileMustExist = Value)) then
  235.     exit;
  236.  
  237.   FFileMustExist := Value;
  238. end;
  239.  
  240. procedure TBaseTextDataSet.SetFileName(Value : TFileName);
  241. begin
  242.   if ((Active) or (FFileName = Value)) then
  243.     exit;
  244.  
  245.   inherited FileName := Value;
  246.   FFileName := Value;
  247. end;
  248.  
  249. procedure TBaseTextDataSet.RemoveWhiteLines(List : TStrings; IsFileRecord : Boolean);
  250. var
  251.   i : integer;
  252. begin
  253.   for i := List.Count -1 downto 0 do
  254.     if (Trim(List.Strings[i]) = '' ) then
  255.       if IsFileRecord then
  256.       begin
  257.         FCurRec := i;
  258.         InternalDelete;
  259.       end
  260.       else
  261.         List.Delete(i);
  262. end;
  263.  
  264. procedure TBaseTextDataSet.RemoveBlankRecords;
  265. begin
  266.   RemoveWhiteLines(FData, TRUE);
  267. end;
  268.  
  269. procedure TBaseTextDataSet.SaveFileAs(strFileName : String);
  270. begin
  271.   FData.SaveToFile(strFileName);
  272.   inherited FileName := strFileName;
  273. end;
  274.  
  275. procedure TBaseTextDataSet.InternalOpen;
  276. var
  277.   Stream : TStream;
  278. begin
  279.   if (not FileMustExist) and (not FileExists(FileName)) then
  280.   begin
  281.     Stream := TFileStream.Create(FileName, fmCreate);
  282.     Stream.Free;
  283.   end;
  284.  
  285.   inherited;
  286. end;
  287.  
  288. procedure TBaseTextDataSet.InternalInitFieldDefs;
  289. var
  290.   i, len, Maxlen : Integer;
  291.   UseSchema : Boolean;
  292.   LstFields : TStrings;
  293.   tmpSchema : TStrings;
  294.   tmpLen    : Integer;
  295.   tmpFieldName : string;
  296. begin
  297.   if not Assigned(FData) then
  298.     exit;
  299.  
  300.   FieldDefs.Clear;
  301.  
  302.   // Find out the longest string
  303.   Maxlen := 0;
  304.  
  305.   for i := 0 to FData.Count - 1 do
  306.   begin
  307.     len := Length(FData.Strings[i]);
  308.     if len > Maxlen then
  309.       Maxlen := len;
  310.   end;
  311.  
  312.   LstFields := TStringList.Create;
  313.   try
  314.     // Load Schema Structure
  315.     tmpSchema := TStringList.Create;
  316.     try
  317.       if (Schema.Count>0) then
  318.       begin
  319.         tmpSchema.Assign(Schema);
  320.         RemoveWhiteLines(tmpSchema, FALSE);
  321.       end
  322.       else if (FData.Count > 0) then
  323.         tmpSchema.CommaText := FData.Strings[0];
  324.  
  325.       UseSchema := (Schema.Count > 0);
  326.  
  327.       // Interpret Schema
  328.       i := 1;
  329.  
  330.       tmpLen   := Maxlen;
  331.  
  332.       repeat
  333.         // Standardize variables on schema
  334.  
  335.         if not UseSchema then
  336.           tmpFieldName := Format('Field%d=%d', [i, tmpLen])
  337.         else
  338.         begin
  339.           tmpFieldName := tmpSchema.Names[i-1];
  340.           if (tmpFieldName = '') then
  341.             tmpFieldName := Format('%s=%d', [tmpSchema.Strings[i-1], tmpLen])
  342.           else
  343.             tmpFieldName := tmpSchema.Strings[i-1];
  344.         end;
  345.  
  346.         LstFields.Add(tmpFieldName);
  347.  
  348.         Inc(i)
  349.  
  350.       until i > tmpSchema.Count;
  351.     finally
  352.       tmpSchema.Free;
  353.     end;
  354.  
  355.     FRecordSize := 0;
  356.  
  357.     // Add fields
  358.     with LstFields do
  359.       for i := 0 to Count -1 do
  360.       begin
  361.         len := StrToIntDef(Values[Names[i]], Maxlen);
  362.         FieldDefs.Add(Trim(Names[i]), ftString, len, False);
  363.         Inc(FRecordSize, len);
  364.         Inc(FRecordSize, DELIMITERS_GAP);
  365.       end;
  366.   finally
  367.     LstFields.Free;
  368.   end;
  369.  
  370.   if FRecordSize = 0 then
  371.     FRecordSize := MAXSTRLEN;
  372.  
  373.   { Initialize an offset value to find the TRecInfo in each buffer }
  374.   FRecInfoOfs := FRecordSize;
  375.   FRecBufSize := FRecInfoOfs + SizeOf(TRecInfo);
  376. end;
  377.  
  378.  
  379. function TBaseTextDataSet.GetRecordSize: Word;
  380. begin
  381.   Result := FRecordSize;
  382. end;
  383.  
  384. {TSdfDataSet}
  385.  
  386. procedure TSdfDataSet.SetFirstLineAsSchema(Value : Boolean);
  387. begin
  388.   if ((Active) or (FFirstLineAsSchema = Value) ) then
  389.     exit;
  390.  
  391.   FFirstLineAsSchema := Value;
  392. end;
  393.  
  394. procedure TSdfDataSet.InternalInitFieldDefs;
  395. begin
  396.   if not Assigned(FData) then
  397.     exit;
  398.   if (FirstLineAsSchema) then
  399.   begin
  400.     if (FData.Count > 0) then
  401.       Schema.CommaText := FData.Strings[0]
  402.     else
  403.       FirstLineAsSchema := FALSE;
  404.   end;
  405.  
  406.   inherited;
  407. end;
  408.  
  409. procedure TSdfDataSet.SetFieldData(Field: TField; Buffer: Pointer);
  410. var
  411.   Temp : TStrings;
  412.   i    : Integer;
  413. begin
  414.   Temp := TStringList.Create;
  415.   Temp.CommaText := ActiveBuffer;
  416.  
  417.   // Add blank fields as needed
  418.   for i := Temp.Count to Field.FieldNo - 1 do
  419.     Temp.Add('');
  420.  
  421.   Temp.Strings[Field.FieldNo -1] := Copy(PChar(Buffer), 1, Field.DataSize);
  422.  
  423.   StrLCopy(ActiveBuffer, PChar(Temp.CommaText), FRecordSize);
  424.   DataEvent(deFieldChange, Longint(Field));
  425.  
  426.   Temp.Free;
  427. end;
  428.  
  429. function  TSdfDataSet.GetRecord(Buffer: PChar; GetMode: TGetMode;
  430.   DoCheck: Boolean): TGetResult;
  431. begin
  432.   Result := grOk;
  433.  
  434.   if (FirstLineAsSchema) then     // Avoid showing titles when FirstLineAsSchema
  435.     if FData.Count < 2 then
  436.       Result := grEOF
  437.     else
  438.       case GetMode of
  439.         gmNext:
  440.           if FCurRec >= RecordCount - 1  then
  441.             Result := grEOF
  442.           else
  443.             if FCurRec < 1 then
  444.               FCurRec := 0;
  445.         gmPrior:
  446.           if FCurRec <= 1 then
  447.             Result := grBOF;
  448.       end;
  449.  
  450.   if (Result = grOk) then
  451.     Result := inherited GetRecord(Buffer, GetMode, DoCheck);
  452.  
  453. end;
  454.  
  455. procedure TSdfDataSet.RemoveExtraColumns;
  456. var
  457.   i : Integer;
  458.   Temp : TStrings;
  459. begin
  460.   Temp := TStringList.Create;
  461.  
  462.   for i := 1 to FData.Count do
  463.   begin
  464.     Temp.CommaText := FData.Strings[i -1];
  465.     if Temp.Count > FieldDefs.Count then   // Remove columns at the end
  466.     begin
  467.       while Temp.Count > FieldDefs.Count do
  468.         Temp.Delete(Temp.Count -1);
  469.  
  470.       FData.Strings[i -1] := Temp.CommaText;
  471.     end;
  472.   end;
  473.  
  474.   Temp.Free;
  475.  
  476.   FData.SaveToFile(FileName);
  477. end;
  478.  
  479. function TSdfDataSet.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
  480. var
  481.   Temp : TStrings;
  482. begin
  483.  
  484.   if (FData.Count = 0) or ((FirstLineAsSchema) and (FData.Count < 2)) then   // Avoid showing titles when FirstLineAsSchema
  485.     Result := FALSE
  486.   else
  487.   begin
  488.     Temp := TStringList.Create;
  489.     Temp.CommaText := ActiveBuffer;
  490.  
  491.     if ((Field.FieldNo > 0) and (Field.FieldNo <= Temp.Count)) then
  492.       StrLCopy(PChar(Buffer), PChar(Temp[Field.FieldNo -1]), Field.DataSize)
  493.     else
  494.       StrCopy(PChar(Buffer), #0);
  495.  
  496.     Temp.Free;
  497.  
  498.     Result := PChar(Buffer)^ <> #0;
  499.   end;
  500. end;
  501.  
  502. { TFixedFormatDataSet }
  503. procedure TFixedFormatDataSet.InternalOpen;
  504. begin
  505.   if (FSchema.Count=0) then
  506.      raise Exception.Create('Fixed Format requires a schema');
  507.   inherited;
  508. end;
  509.  
  510. function TFixedFormatDataSet.GetFieldData(Field: TField;
  511.   Buffer: Pointer): Boolean;
  512. var
  513.   thePos: PChar;
  514.   cnt, offset: Cardinal;
  515. begin
  516.   if (FData.Count = 0) then // Avoid showing titles when FirstLineAsSchema
  517.   begin
  518.     Result := FALSE;
  519.     exit;
  520.   end;
  521.   thePos:=ActiveBuffer;
  522.   offset:=0;
  523.  
  524.   if Field.FieldNo > 1 then
  525.     for cnt:=0 to Field.FieldNo-2 do
  526.        inc(offset, Fields[cnt].Size);
  527.  
  528.   if offset > StrLen(ActiveBuffer) then
  529.   begin    // Avoid showing garbage
  530.     Result := FALSE;
  531.     exit;
  532.   end;
  533.  
  534.   Inc(thePos,Offset);
  535.   StrLCopy(Buffer, thePos, Field.Size);
  536.   Result := PChar(Buffer)^ <> #0;
  537. end;
  538.  
  539. procedure TFixedFormatDataSet.SetFieldData(Field: TField; Buffer: Pointer);
  540. var
  541.   i, offset : Integer;
  542.   pSrc, pDest : PChar;
  543.   TempStr : String;
  544. begin
  545.   offset := 0;
  546.  
  547.   // Find the offset
  548.   if Field.FieldNo > 1 then
  549.     for i:=0 to Field.FieldNo-2 do
  550.       Inc(offset, Fields[i].Size);
  551.  
  552.   TempStr := ActiveBuffer;
  553.   // Fill the String with spaces if necessary
  554.   for i := Length(TempStr) to FRecordSize do
  555.     TempStr := Concat(TempStr, ' ');
  556.  
  557.   pDest := PChar(TempStr);
  558.   inc(pDest, offset);
  559.  
  560.   pSrc := PChar(Buffer);
  561.   for i := Length(pSrc) to Field.Size do
  562.     StrCat(pSrc, ' ');
  563.  
  564.   StrMove(pDest, pSrc, Field.Size);
  565.  
  566.   StrLCopy(ActiveBuffer, PChar(TempStr), FRecordSize);
  567.  
  568.   DataEvent(deFieldChange, Longint(Field));
  569.  
  570. end;
  571.  
  572. { This procedure is used to register this component on the component palette }
  573. procedure Register;
  574. begin
  575.   RegisterComponents('Data Access', [TSdfDataSet]);
  576.   RegisterComponents('Data Access', [TFixedFormatDataSet]);
  577. end;
  578.  
  579. end.
  580.