Subversion Repositories delphiutils

Rev

Rev 90 | Rev 92 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. unit AsciiTable;
  2.  
  3. (*
  4.  * ASCII Table and CSV Generator Delphi Unit
  5.  * Revision 2022-07-10
  6.  *
  7.  * (C) 2022 Daniel Marschall, HickelSOFT, ViaThinkSoft
  8.  * Licensed under the terms of Apache 2.0
  9.  *)
  10.  
  11. {
  12.  
  13. Example usage:
  14.  
  15. uses
  16.   AsciiTable, ContNrs;
  17.  
  18. procedure TForm1.Button1Click(Sender: TObject);
  19. var
  20.   VirtTable: TVtsAsciiTable;
  21.   objLine: TVtsAsciiTableLine;
  22. begin
  23.   VirtTable := TVtsAsciiTable.Create(true);
  24.   VirtTable.Clear;
  25.  
  26.   // Create Test data
  27.   objLine := TVtsAsciiTableLine.Create;
  28.   objLine.SetVal(0, 'Fruit', taCenter);
  29.   objLine.SetVal(1, 'Amount', taCenter);
  30.   VirtTable.Add(objLine);
  31.  
  32.   VirtTable.AddSeparator;
  33.  
  34.   objLine := TVtsAsciiTableLine.Create;
  35.   objLine.SetVal(0, 'Apple', taLeftJustify);
  36.   objLine.SetVal(1, '123', taRightJustify);
  37.   VirtTable.Add(objLine);
  38.  
  39.   objLine := TVtsAsciiTableLine.Create;
  40.   objLine.SetVal(0, 'Kiwi', taLeftJustify);
  41.   objLine.SetVal(1, '1', taRightJustify);
  42.   VirtTable.Add(objLine);
  43.  
  44.   objLine := TVtsAsciiTableLine.Create;
  45.   objLine.SetVal(0, 'Asparagus (green)', taLeftJustify);
  46.   objLine.SetVal(1, '9999', taRightJustify);
  47.   VirtTable.Add(objLine);
  48.  
  49.   objLine := TVtsAsciiTableLine.Create;
  50.   objLine.SetVal(0, 'Asparagus (white)', taLeftJustify);
  51.   objLine.SetVal(1, '999', taRightJustify);
  52.   VirtTable.Add(objLine);
  53.  
  54.   VirtTable.AddSeparator;
  55.   VirtTable.AddSumLine;
  56.  
  57.   // Create ASCII table
  58.   Memo1.Clear;
  59.   VirtTable.GetASCIITable(Memo1.Lines);
  60.  
  61.   // Save ASCII table
  62.   VirtTable.SaveASCIITable('Order.txt');
  63.  
  64.   // Create CSV
  65.   Memo2.Clear;
  66.   VirtTable.GetCSV(Memo2.Lines);
  67.  
  68.   // Save CSV
  69.   VirtTable.SaveCSV('Order.csv');
  70.  
  71.   VirtTable.Free;
  72. end;
  73.  
  74. }
  75.  
  76. interface
  77.  
  78. uses
  79.   ContNrs, Classes, SysUtils;
  80.  
  81. const
  82.   VTS_ASCII_TABLE_COLS = 10;
  83.  
  84. type
  85.   TVtsAsciiTableLine = class(TObject)
  86.   private
  87.     IsSumLine: boolean;
  88.     //IsSeparator: boolean;
  89.   public
  90.     Cont: array[0..VTS_ASCII_TABLE_COLS-1] of string;
  91.     Align: array[0..VTS_ASCII_TABLE_COLS-1] of TAlignment;
  92.     PadChar: array[0..VTS_ASCII_TABLE_COLS-1] of char;
  93.     procedure Clear;
  94.     procedure SetVal(index: integer; ACont: string; AAlign: TAlignment=taLeftJustify; APadChar: char=' ');
  95.   end;
  96.  
  97.   TVtsAsciiTableAnalysis = record
  98.     MaxLen: array[0..VTS_ASCII_TABLE_COLS-1] of integer;
  99.     Used: array[0..VTS_ASCII_TABLE_COLS-1] of boolean;
  100.     Sum: array[0..VTS_ASCII_TABLE_COLS-1] of integer;
  101.   end;
  102.  
  103.   TVtsAsciiTable = class(TObjectList{<TVtsAsciiTableLine>})
  104.   private
  105.     function GetItem(Index: Integer): TVtsAsciiTableLine;
  106.     procedure SetItem(Index: Integer; const Value: TVtsAsciiTableLine);
  107.   public
  108.     function GetAnalysis: TVtsAsciiTableAnalysis;
  109.     procedure GetASCIITable(sl: TStrings; spaceBetween: integer=3);
  110.     procedure SaveASCIITable(filename: string; spaceBetween: integer=3);
  111.     procedure GetCSV(sl: TStrings);
  112.     procedure SaveCSV(filename: string);
  113.  
  114.     procedure AddSeparator;
  115.     procedure AddSumLine;
  116.  
  117.     // Just a little bit type-safe... The rest stays TObject for now
  118.     function Add(AObject: TVtsAsciiTableLine): Integer; reintroduce;
  119.     property Items[Index: Integer]: TVtsAsciiTableLine read GetItem write SetItem;
  120.     procedure Insert(Index: Integer; AObject: TVtsAsciiTableLine); reintroduce;
  121.   end;
  122.  
  123. implementation
  124.  
  125. { TVtsAsciiTable }
  126.  
  127. function TVtsAsciiTable.Add(AObject: TVtsAsciiTableLine): Integer;
  128. begin
  129.   result := Inherited Add(AObject);
  130. end;
  131.  
  132. procedure TVtsAsciiTable.AddSeparator;
  133. begin
  134.   Inherited Add(nil);
  135. end;
  136.  
  137. procedure TVtsAsciiTable.AddSumLine;
  138. var
  139.   objLine: TVtsAsciiTableLine;
  140.   j: Integer;
  141.   analysis: TVtsAsciiTableAnalysis;
  142. begin
  143.   objLine := TVtsAsciiTableLine.Create;
  144.   objLine.IsSumLine := true;
  145.   analysis := GetAnalysis;
  146.   for j := 0 to VTS_ASCII_TABLE_COLS-1 do
  147.   begin
  148.     if analysis.Sum[j] <> 0 then
  149.       objLine.SetVal(j, IntToStr(analysis.Sum[j]), taRightJustify, ' ');
  150.   end;
  151.   Inherited Add(objLine);
  152. end;
  153.  
  154. function TVtsAsciiTable.GetAnalysis: TVtsAsciiTableAnalysis;
  155. var
  156.   j: Integer;
  157.   i: Integer;
  158.   objLine: TVtsAsciiTableLine;
  159.   len: Integer;
  160.   itmp: integer;
  161. begin
  162.   for j := 0 to VTS_ASCII_TABLE_COLS-1 do
  163.   begin
  164.     result.MaxLen[j] := 0;
  165.     result.Used[j] := false;
  166.     result.Sum[j] := 0;
  167.   end;
  168.   for i := 0 to Self.Count-1 do
  169.   begin
  170.     objLine := Self.items[i] as TVtsAsciiTableLine;
  171.     if objLine <> nil then
  172.     begin
  173.       for j := 0 to VTS_ASCII_TABLE_COLS-1 do
  174.       begin
  175.         len := Length(objLine.Cont[j]);
  176.         if TryStrToInt(objLine.Cont[j], itmp) then
  177.           result.Sum[j] := result.Sum[j] + itmp;
  178.         if len > result.MaxLen[j] then
  179.           result.MaxLen[j] := len;
  180.         if len > 0 then
  181.           result.Used[j] := true;
  182.       end;
  183.     end;
  184.   end;
  185. end;
  186.  
  187. procedure TVtsAsciiTable.GetASCIITable(sl: TStrings; spaceBetween: integer=3);
  188. var
  189.   analysis: TVtsAsciiTableAnalysis;
  190.   objLine: TVtsAsciiTableLine;
  191.   i: Integer;
  192.   sLine: string;
  193.   j: Integer;
  194.   itmp: Integer;
  195.   padchar: Char;
  196.   firstcol: boolean;
  197.   width: Integer;
  198. begin
  199.   analysis := GetAnalysis;
  200.   //sl.Clear;
  201.   for i := 0 to Self.Count-1 do
  202.   begin
  203.     objLine := Self.items[i] as TVtsAsciiTableLine;
  204.     sLine := '';
  205.     if objLine <> nil then
  206.     begin
  207.       firstcol := true;
  208.       for j := 0 to VTS_ASCII_TABLE_COLS-1 do
  209.       begin
  210.         if not analysis.Used[j] then continue;
  211.  
  212.         padchar := objLine.PadChar[j];
  213.         if padchar = #0 then padchar := ' ';
  214.  
  215.         if firstcol then
  216.           firstcol := false
  217.         else
  218.           sLine := sLine + StringOfChar(' ', spaceBetween);
  219.  
  220.         if objLine.Align[j] = taRightJustify then
  221.         begin
  222.           sLine := sLine + StringOfChar(padchar, analysis.MaxLen[j]-Length(objLine.Cont[j]));
  223.           sLine := sLine + objLine.Cont[j];
  224.         end
  225.         else if objLine.Align[j] = taLeftJustify then
  226.         begin
  227.           sLine := sLine + objLine.Cont[j];
  228.           sLine := sLine + StringOfChar(padchar, analysis.MaxLen[j]-Length(objLine.Cont[j]));
  229.         end
  230.         else if objLine.Align[j] = taCenter then
  231.         begin
  232.           if Odd(analysis.MaxLen[j]-Length(objLine.Cont[j])) then itmp := 1 else itmp := 0;
  233.           sLine := sLine + StringOfChar(padchar, (analysis.MaxLen[j]-Length(objLine.Cont[j])) div 2);
  234.           sLine := sLine + objLine.Cont[j];
  235.           sLine := sLine + StringOfChar(padchar, (analysis.MaxLen[j]-Length(objLine.Cont[j])) div 2 + itmp);
  236.         end
  237.         else
  238.           Assert(false);
  239.       end;
  240.     end
  241.     else
  242.     begin
  243.       firstcol := true;
  244.       width := 0;
  245.       for j := 0 to VTS_ASCII_TABLE_COLS-1 do
  246.       begin
  247.         if not analysis.Used[j] then continue;
  248.         if firstcol then
  249.           firstcol := false
  250.         else
  251.           width := width + spaceBetween;
  252.         width := width + analysis.MaxLen[j];
  253.       end;
  254.  
  255.       sLine := sLine + StringOfChar('-', Width);
  256.     end;
  257.     sl.Add(sLine);
  258.   end;
  259. end;
  260.  
  261. function CsvQuoteStr(s: string): string;
  262. begin
  263.   s := StringReplace(s, #13#10, ' ', [rfReplaceAll]);
  264.   s := StringReplace(s, #13, ' ', [rfReplaceAll]);
  265.   s := StringReplace(s, #10, ' ', [rfReplaceAll]);
  266.   if s = '' then
  267.     result := ''
  268.   else if (AnsiPos('"', s)>0) or (AnsiPos('''', s)>0) or (AnsiPos(';', s)>0) or
  269.           (AnsiPos(#9, s)>0) or (AnsiPos(' ', s)>0) then
  270.     result := '"' + StringReplace(s, '"', '""', [rfReplaceAll]) + '"'
  271.   else
  272.     result := s;
  273. end;
  274.  
  275. procedure TVtsAsciiTable.GetCSV(sl: TStrings);
  276. var
  277.   analysis: TVtsAsciiTableAnalysis;
  278.   objLine: TVtsAsciiTableLine;
  279.   i: Integer;
  280.   sLine: string;
  281.   j: Integer;
  282.   firstcol: boolean;
  283. begin
  284.   analysis := GetAnalysis;
  285.   //sl.Clear;
  286.   for i := 0 to Self.Count-1 do
  287.   begin
  288.     objLine := Self.items[i] as TVtsAsciiTableLine;
  289.     if objLine = nil then continue;
  290.     if objLine.IsSumLine then continue;
  291.     sLine := '';
  292.     firstcol := true;
  293.     for j := 0 to VTS_ASCII_TABLE_COLS-1 do
  294.     begin
  295.       if not analysis.Used[j] then continue;
  296.       if firstcol then
  297.         firstcol := false
  298.       else
  299.         sLine := sLine + ';';
  300.       sLine := sLine + CsvQuoteStr(objLine.Cont[j]);
  301.     end;
  302.     sl.Add(sLine);
  303.   end;
  304. end;
  305.  
  306. function TVtsAsciiTable.GetItem(Index: Integer): TVtsAsciiTableLine;
  307. begin
  308.   result := (Inherited Items[Index]) as TVtsAsciiTableLine;
  309. end;
  310.  
  311. procedure TVtsAsciiTable.Insert(Index: Integer; AObject: TVtsAsciiTableLine);
  312. begin
  313.   Inherited Insert(Index, AObject);
  314. end;
  315.  
  316. procedure TVtsAsciiTable.SaveASCIITable(filename: string;
  317.   spaceBetween: integer);
  318. var
  319.   sl: TStringList;
  320. begin
  321.   sl := TStringList.Create;
  322.   try
  323.     GetASCIITable(sl, spaceBetween);
  324.     sl.SaveToFile(filename);
  325.   finally
  326.     FreeAndNil(sl);
  327.   end;
  328. end;
  329.  
  330. procedure TVtsAsciiTable.SaveCSV(filename: string);
  331. var
  332.   sl: TStringList;
  333. begin
  334.   sl := TStringList.Create;
  335.   try
  336.     GetCSV(sl);
  337.     sl.SaveToFile(filename);
  338.   finally
  339.     FreeAndNil(sl);
  340.   end;
  341. end;
  342.  
  343. procedure TVtsAsciiTable.SetItem(Index: Integer; const Value: TVtsAsciiTableLine);
  344. begin
  345.   Inherited Items[Index] := Value;
  346. end;
  347.  
  348. { TVtsAsciiTableLine }
  349.  
  350. procedure TVtsAsciiTableLine.Clear;
  351. var
  352.   i: Integer;
  353. begin
  354.   for i := 0 to VTS_ASCII_TABLE_COLS-1 do
  355.   begin
  356.     PadChar[i] := #0;
  357.     Align[i] := taLeftJustify;
  358.     Cont[i] := '';
  359.   end;
  360. end;
  361.  
  362. procedure TVtsAsciiTableLine.SetVal(index: integer; ACont: string;
  363.   AAlign: TAlignment=taLeftJustify; APadChar: char=' ');
  364. begin
  365.   Self.Cont[index] := ACont;
  366.   Self.Align[index] := AAlign;
  367.   Self.PadChar[index] := APadChar;
  368. end;
  369.  
  370. end.
  371.