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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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.