Subversion Repositories delphiutils

Rev

Rev 91 | Rev 93 | 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-11
  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.     DoSum: array[0..VTS_ASCII_TABLE_COLS-1] of boolean;
  94.     procedure Clear;
  95.     procedure SetVal(index: integer; ACont: string; AAlign: TAlignment=taLeftJustify;
  96.       APadChar: char=' '; ADoSum: boolean=false);
  97.   end;
  98.  
  99.   TVtsAsciiTableAnalysis = record
  100.     MaxLen: array[0..VTS_ASCII_TABLE_COLS-1] of integer;
  101.     Used: array[0..VTS_ASCII_TABLE_COLS-1] of boolean;
  102.     Sum: array[0..VTS_ASCII_TABLE_COLS-1] of integer;
  103.   end;
  104.  
  105.   TVtsAsciiTable = class(TObjectList{<TVtsAsciiTableLine>})
  106.   private
  107.     function GetItem(Index: Integer): TVtsAsciiTableLine;
  108.     procedure SetItem(Index: Integer; const Value: TVtsAsciiTableLine);
  109.   public
  110.     function GetAnalysis: TVtsAsciiTableAnalysis;
  111.     procedure GetASCIITable(sl: TStrings; spaceBetween: integer=3);
  112.     procedure SaveASCIITable(filename: string; spaceBetween: integer=3);
  113.     procedure GetCSV(sl: TStrings);
  114.     procedure SaveCSV(filename: string);
  115.  
  116.     procedure AddSeparator;
  117.     procedure AddSumLine;
  118.  
  119.     // Just a little bit type-safe... The rest stays TObject for now
  120.     function Add(AObject: TVtsAsciiTableLine): Integer; reintroduce;
  121.     property Items[Index: Integer]: TVtsAsciiTableLine read GetItem write SetItem;
  122.     procedure Insert(Index: Integer; AObject: TVtsAsciiTableLine); reintroduce;
  123.   end;
  124.  
  125. implementation
  126.  
  127. { TVtsAsciiTable }
  128.  
  129. function TVtsAsciiTable.Add(AObject: TVtsAsciiTableLine): Integer;
  130. begin
  131.   result := Inherited Add(AObject);
  132. end;
  133.  
  134. procedure TVtsAsciiTable.AddSeparator;
  135. begin
  136.   Inherited Add(nil);
  137. end;
  138.  
  139. procedure TVtsAsciiTable.AddSumLine;
  140. var
  141.   objLine: TVtsAsciiTableLine;
  142.   j: Integer;
  143.   analysis: TVtsAsciiTableAnalysis;
  144.   found: boolean;
  145. begin
  146.   objLine := TVtsAsciiTableLine.Create;
  147.   objLine.IsSumLine := true;
  148.   analysis := GetAnalysis;
  149.   found := false;
  150.   for j := 0 to VTS_ASCII_TABLE_COLS-1 do
  151.   begin
  152.     if analysis.Sum[j] <> 0 then
  153.     begin
  154.       objLine.SetVal(j, IntToStr(analysis.Sum[j]), taRightJustify, ' ');
  155.       found := true;
  156.     end;
  157.   end;
  158.   if found then
  159.     Inherited Add(objLine)
  160.   else
  161.     objLine.Free;
  162. end;
  163.  
  164. function TVtsAsciiTable.GetAnalysis: TVtsAsciiTableAnalysis;
  165. var
  166.   j: Integer;
  167.   i: Integer;
  168.   objLine: TVtsAsciiTableLine;
  169.   len: Integer;
  170.   itmp: integer;
  171. begin
  172.   for j := 0 to VTS_ASCII_TABLE_COLS-1 do
  173.   begin
  174.     result.MaxLen[j] := 0;
  175.     result.Used[j] := false;
  176.     result.Sum[j] := 0;
  177.   end;
  178.   for i := 0 to Self.Count-1 do
  179.   begin
  180.     objLine := Self.items[i] as TVtsAsciiTableLine;
  181.     if objLine <> nil then
  182.     begin
  183.       for j := 0 to VTS_ASCII_TABLE_COLS-1 do
  184.       begin
  185.         len := Length(objLine.Cont[j]);
  186.         if TryStrToInt(objLine.Cont[j], itmp) and objLine.DoSum[j] then
  187.           result.Sum[j] := result.Sum[j] + itmp;
  188.         if len > result.MaxLen[j] then
  189.           result.MaxLen[j] := len;
  190.         if len > 0 then
  191.           result.Used[j] := true;
  192.       end;
  193.     end;
  194.   end;
  195. end;
  196.  
  197. procedure TVtsAsciiTable.GetASCIITable(sl: TStrings; spaceBetween: integer=3);
  198. var
  199.   analysis: TVtsAsciiTableAnalysis;
  200.   objLine: TVtsAsciiTableLine;
  201.   i: Integer;
  202.   sLine: string;
  203.   j: Integer;
  204.   itmp: Integer;
  205.   padchar: Char;
  206.   firstcol: boolean;
  207.   width: Integer;
  208. begin
  209.   analysis := GetAnalysis;
  210.   //sl.Clear;
  211.   for i := 0 to Self.Count-1 do
  212.   begin
  213.     objLine := Self.items[i] as TVtsAsciiTableLine;
  214.     sLine := '';
  215.     if objLine <> nil then
  216.     begin
  217.       firstcol := true;
  218.       for j := 0 to VTS_ASCII_TABLE_COLS-1 do
  219.       begin
  220.         if not analysis.Used[j] then continue;
  221.  
  222.         padchar := objLine.PadChar[j];
  223.         if padchar = #0 then padchar := ' ';
  224.  
  225.         if firstcol then
  226.           firstcol := false
  227.         else
  228.           sLine := sLine + StringOfChar(' ', spaceBetween);
  229.  
  230.         if objLine.Align[j] = taRightJustify then
  231.         begin
  232.           sLine := sLine + StringOfChar(padchar, analysis.MaxLen[j]-Length(objLine.Cont[j]));
  233.           sLine := sLine + objLine.Cont[j];
  234.         end
  235.         else if objLine.Align[j] = taLeftJustify then
  236.         begin
  237.           sLine := sLine + objLine.Cont[j];
  238.           sLine := sLine + StringOfChar(padchar, analysis.MaxLen[j]-Length(objLine.Cont[j]));
  239.         end
  240.         else if objLine.Align[j] = taCenter then
  241.         begin
  242.           if Odd(analysis.MaxLen[j]-Length(objLine.Cont[j])) then itmp := 1 else itmp := 0;
  243.           sLine := sLine + StringOfChar(padchar, (analysis.MaxLen[j]-Length(objLine.Cont[j])) div 2);
  244.           sLine := sLine + objLine.Cont[j];
  245.           sLine := sLine + StringOfChar(padchar, (analysis.MaxLen[j]-Length(objLine.Cont[j])) div 2 + itmp);
  246.         end
  247.         else
  248.           Assert(false);
  249.       end;
  250.     end
  251.     else
  252.     begin
  253.       firstcol := true;
  254.       width := 0;
  255.       for j := 0 to VTS_ASCII_TABLE_COLS-1 do
  256.       begin
  257.         if not analysis.Used[j] then continue;
  258.         if firstcol then
  259.           firstcol := false
  260.         else
  261.           width := width + spaceBetween;
  262.         width := width + analysis.MaxLen[j];
  263.       end;
  264.  
  265.       sLine := sLine + StringOfChar('-', Width);
  266.     end;
  267.     sl.Add(sLine);
  268.   end;
  269. end;
  270.  
  271. function CsvQuoteStr(s: string): string;
  272. begin
  273.   s := StringReplace(s, #13#10, ' ', [rfReplaceAll]);
  274.   s := StringReplace(s, #13, ' ', [rfReplaceAll]);
  275.   s := StringReplace(s, #10, ' ', [rfReplaceAll]);
  276.   if s = '' then
  277.     result := ''
  278.   else if (AnsiPos('"', s)>0) or (AnsiPos('''', s)>0) or (AnsiPos(';', s)>0) or
  279.           (AnsiPos(#9, s)>0) or (AnsiPos(' ', s)>0) then
  280.     result := '"' + StringReplace(s, '"', '""', [rfReplaceAll]) + '"'
  281.   else
  282.     result := s;
  283. end;
  284.  
  285. procedure TVtsAsciiTable.GetCSV(sl: TStrings);
  286. var
  287.   analysis: TVtsAsciiTableAnalysis;
  288.   objLine: TVtsAsciiTableLine;
  289.   i: Integer;
  290.   sLine: string;
  291.   j: Integer;
  292.   firstcol: boolean;
  293. begin
  294.   analysis := GetAnalysis;
  295.   //sl.Clear;
  296.   for i := 0 to Self.Count-1 do
  297.   begin
  298.     objLine := Self.items[i] as TVtsAsciiTableLine;
  299.     if objLine = nil then continue;
  300.     if objLine.IsSumLine then continue;
  301.     sLine := '';
  302.     firstcol := true;
  303.     for j := 0 to VTS_ASCII_TABLE_COLS-1 do
  304.     begin
  305.       if not analysis.Used[j] then continue;
  306.       if firstcol then
  307.         firstcol := false
  308.       else
  309.         sLine := sLine + ';';
  310.       sLine := sLine + CsvQuoteStr(objLine.Cont[j]);
  311.     end;
  312.     sl.Add(sLine);
  313.   end;
  314. end;
  315.  
  316. function TVtsAsciiTable.GetItem(Index: Integer): TVtsAsciiTableLine;
  317. begin
  318.   result := (Inherited Items[Index]) as TVtsAsciiTableLine;
  319. end;
  320.  
  321. procedure TVtsAsciiTable.Insert(Index: Integer; AObject: TVtsAsciiTableLine);
  322. begin
  323.   Inherited Insert(Index, AObject);
  324. end;
  325.  
  326. procedure TVtsAsciiTable.SaveASCIITable(filename: string;
  327.   spaceBetween: integer);
  328. var
  329.   sl: TStringList;
  330. begin
  331.   sl := TStringList.Create;
  332.   try
  333.     GetASCIITable(sl, spaceBetween);
  334.     sl.SaveToFile(filename);
  335.   finally
  336.     FreeAndNil(sl);
  337.   end;
  338. end;
  339.  
  340. procedure TVtsAsciiTable.SaveCSV(filename: string);
  341. var
  342.   sl: TStringList;
  343. begin
  344.   sl := TStringList.Create;
  345.   try
  346.     GetCSV(sl);
  347.     sl.SaveToFile(filename);
  348.   finally
  349.     FreeAndNil(sl);
  350.   end;
  351. end;
  352.  
  353. procedure TVtsAsciiTable.SetItem(Index: Integer; const Value: TVtsAsciiTableLine);
  354. begin
  355.   Inherited Items[Index] := Value;
  356. end;
  357.  
  358. { TVtsAsciiTableLine }
  359.  
  360. procedure TVtsAsciiTableLine.Clear;
  361. var
  362.   i: Integer;
  363. begin
  364.   for i := 0 to VTS_ASCII_TABLE_COLS-1 do
  365.   begin
  366.     PadChar[i] := #0;
  367.     Align[i] := taLeftJustify;
  368.     Cont[i] := '';
  369.   end;
  370. end;
  371.  
  372. procedure TVtsAsciiTableLine.SetVal(index: integer; ACont: string;
  373.   AAlign: TAlignment=taLeftJustify; APadChar: char=' '; ADoSum: boolean=false);
  374. begin
  375.   Self.Cont[index] := ACont;
  376.   Self.Align[index] := AAlign;
  377.   Self.PadChar[index] := APadChar;
  378.   Self.DoSum[index] := ADoSum;
  379. end;
  380.  
  381. end.
  382.