You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Zero/ZeroLevel/Services/Text/PlainTextTables/TextTableRender.cs

300 lines
12 KiB

6 years ago
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ZeroLevel.Services.PlainTextTables
{
public class TextTableRender
{
public static string Render(TextTableData data, TextTableRenderOptions options)
{
try
{
var columns_width = data.
Columns.
Select(c => c.Width).
ToArray();
var ow = (options.PaddingLeft > 0 ? options.PaddingLeft : 0) +
(options.PaddingRight > 0 ? options.PaddingRight : 0);
for (int i = 0; i < columns_width.Length; i++)
{
columns_width[i] += ow;
}
// Обновление ширины столбцов при необходимости
if (options.MaxCellWidth > 0)
{
for (int i = 0; i < columns_width.Length; i++)
{
if (columns_width[i] > options.MaxCellWidth)
{
columns_width[i] = options.MaxCellWidth;
}
}
}
else if (options.MaxTableWidth > 0)
{
int index = 0;
while (GetTableWidth(options, columns_width) > options.MaxTableWidth)
{
for (int i = 0; i < columns_width.Length; i++)
{
if (columns_width[i] > columns_width[index]) index = i;
}
columns_width[index]--;
}
}
var table_width = GetTableWidth(options, columns_width);
var table = new StringBuilder();
// Отрисовка таблицы
var rows = data.Rows.ToArray();
for (int i = 0; i < rows.Length; i++)
{
DrawRowSeparator(table, options, table_width, i, i == 0, false, columns_width);
var row_render = RenderRow(rows[i], options, columns_width);
foreach (var line in row_render)
{
int c = 0;
for (; c < columns_width.Length; c++)
{
DrawColumnSeparator(table, options, c);
table.Append(line[c]);
}
DrawColumnSeparator(table, options, c);
table.AppendLine();
}
}
DrawRowSeparator(table, options, table_width, data.Rows.Count(), false, true, columns_width);
return table.ToString();
}
catch (Exception ex)
{
Log.SystemError(ex, "Сбой при преобразовании таблицы");
return string.Empty;
}
}
/// <summary>
/// Рендеринг отображения строки
/// </summary>
private static List<string[]> RenderRow(TextTableData.TextTableRow row,
TextTableRenderOptions options,
int[] columns_width)
{
var result = new List<string[]>();
// Добавление пустых строк если есть padding сверху
if (options.PaddingTop > 0)
{
for (int i = 0; i < options.PaddingTop; i++)
{
var empty = new string[columns_width.Length];
for (int c = 0; c < columns_width.Length; c++)
{
empty[c] = new string(' ', columns_width[c]);
}
result.Add(empty);
}
}
// Разделение значений ячеек на части в зависимости от ширины столбца
var cells = new List<string[]>();
for (int i = 0; i < columns_width.Length; i++)
{
cells.Add(Split(row.Cells[i].Text, columns_width[i], options.PaddingLeft, options.PaddingRight).ToArray());
}
// Определение максимального количества строк по ячейкам (высота строки таблицы)
var max = cells.Max(c => c.Length);
for (int i = 0; i < max; i++)
{
var line = new string[columns_width.Length];
for (int c = 0; c < columns_width.Length; c++)
{
var text = (cells[c].Length > i) ? cells[c][i] : string.Empty;
if (text.Length < columns_width[c])
{
text = text + new string(' ', columns_width[c] - text.Length);
}
line[c] = text;
}
result.Add(line);
}
// Добавление пустых строк если есть padding снизу
if (options.PaddingBottom > 0)
{
for (int i = 0; i < options.PaddingBottom; i++)
{
var empty = new string[columns_width.Length];
for (int c = 0; c < columns_width.Length; c++)
{
empty[c] = new string(' ', columns_width[c]);
}
result.Add(empty);
}
}
return result;
}
/// <summary>
/// Разделение текстовой строки на подстроки указанной длины
/// </summary>
private static IEnumerable<string> Split(string str, int chunkSize, int leftPad, int rightPad)
{
if (str == null) return new string[1] { string.Empty };
while ((chunkSize - (leftPad + rightPad)) < 5 && (leftPad > 0 || rightPad > 0))
{
if (leftPad > 0)
leftPad--;
if (rightPad > 0)
rightPad--;
}
var size = chunkSize - leftPad - rightPad;
var add = str.Length % size > 0 ? 1 : 0;
var count = (int)(str.Length / size) + add;
var result = new string[count];
for (int i = 0; i < count; i++)
{
var start = i * size;
var diff = str.Length - i * size;
var length = size;
if (diff < length) length = diff;
result[i] = (start < str.Length) ? str.Substring(start, length) : string.Empty;
if (leftPad > 0 && result[i].Length < chunkSize)
{
result[i] = new string(' ', leftPad) + result[i];
}
if (rightPad > 0 && result[i].Length < chunkSize)
{
result[i] = result[i] + new string(' ', rightPad);
}
}
return result;
}
/// <summary>
/// Подсчет реальной ширины таблицы, для указанного стиля
/// </summary>
private static int GetTableWidth(TextTableRenderOptions options, int[] columns_width)
{
int width =
columns_width.Sum() + // ширина областей текста
columns_width.Length - 1; // границы между ячейками
switch (options.Style)
{
case TextTableStyle.Columns:
case TextTableStyle.Simple:
case TextTableStyle.Borders:
case TextTableStyle.DoubleBorders:
width += 2; // внешние границы
break;
}
return width;
}
/// <summary>
/// Отрисовка разделителя столбцов
/// </summary>
private static void DrawColumnSeparator(StringBuilder sb, TextTableRenderOptions options, int column_index)
{
switch (options.Style)
{
case TextTableStyle.NoBorders:
case TextTableStyle.HeaderLine:
case TextTableStyle.DoubleHeaderLine:
sb.Append(' ');
break;
case TextTableStyle.Columns:
case TextTableStyle.Simple:
case TextTableStyle.Borders:
case TextTableStyle.DoubleBorders:
sb.Append(options.VerticalLine);
break;
case TextTableStyle.DoubleHeaderAndFirstColumn:
case TextTableStyle.HeaderAndFirstColumn:
if (column_index == 1)
{
sb.Append(options.VerticalLine);
}
else
{
sb.Append(' ');
}
break;
}
}
/// <summary>
/// Отрисовка разделителя строк
/// </summary>
/// <param name="sb"></param>
/// <param name="row_index">Индекс строки, имеется в виду индекс следующей строки, т.е. 0 - до отрисовки первой строки</param>
private static void DrawRowSeparator(StringBuilder sb, TextTableRenderOptions options, int width, int row_index,
bool isFirst, bool isLast, int[] columns_width)
{
var sym = options.HorizontalLine;
switch (options.Style)
{
case TextTableStyle.Columns:
case TextTableStyle.Simple:
case TextTableStyle.Borders:
case TextTableStyle.DoubleBorders:
if (isFirst)
{
sb.Append(options.LeftTopCorner);
for (int i = 0; i < columns_width.Length; i++)
{
sb.Append(options.HorizontalLine, columns_width[i]);
if (i != columns_width.Length - 1)
{
sb.Append(options.HorizontalToDownLine);
}
}
sb.Append(options.RightTopCorner);
}
else if (isLast)
{
sb.Append(options.LeftBottomCorner);
for (int i = 0; i < columns_width.Length; i++)
{
sb.Append(options.HorizontalLine, columns_width[i]);
if (i != columns_width.Length - 1)
{
sb.Append(options.HorizontalToUpLine);
}
}
sb.Append(options.RightBottomCorner);
}
else
{
sb.Append(options.VerticalToRightLine);
for (int i = 0; i < columns_width.Length; i++)
{
if (options.Style == TextTableStyle.Columns)
sb.Append(' ', columns_width[i]);
else
sb.Append(options.HorizontalLine, columns_width[i]);
if (i != columns_width.Length - 1)
{
sb.Append(options.CrossLines);
}
}
sb.Append(options.VerticalToLeftLine);
}
break;
case TextTableStyle.HeaderLine:
case TextTableStyle.DoubleHeaderLine:
if (row_index == 1)
{
sb.Append(sym, width);
}
break;
case TextTableStyle.HeaderAndFirstColumn:
case TextTableStyle.DoubleHeaderAndFirstColumn:
if (row_index == 1)
{
sb.Append(sym, columns_width[0] + 1);
sb.Append(options.CrossLines);
sb.Append(sym, width - columns_width[0] - 1);
}
break;
}
sb.AppendLine();
}
}
}

Powered by TurnKey Linux.