diff --git a/CHANGELOG.md b/CHANGELOG.md index d6c19189..1a99c4de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## [Unreleased] ### Added - Table size limit in `MarkdownUtils.formatDataTable(List, int)` method, by @HardNorth +- Transpose table logic if column number is bigger than row number in `MarkdownUtils.formatDataTable(List, int)` method, by @HardNorth - `MarkdownUtils.formatDataTable(Map)` method, by @HardNorth ## [5.2.1] diff --git a/src/main/java/com/epam/reportportal/utils/markdown/MarkdownUtils.java b/src/main/java/com/epam/reportportal/utils/markdown/MarkdownUtils.java index a3825968..053349c5 100644 --- a/src/main/java/com/epam/reportportal/utils/markdown/MarkdownUtils.java +++ b/src/main/java/com/epam/reportportal/utils/markdown/MarkdownUtils.java @@ -40,6 +40,7 @@ public class MarkdownUtils { public static final String TRUNCATION_REPLACEMENT = "..."; public static final int PADDING_SPACES_NUM = 2; public static final int MAX_TABLE_SIZE = 83; + public static final int MIN_COL_SIZE = 3; /** * Adds special prefix to make log message being processed as markdown @@ -62,11 +63,36 @@ public static String asCode(String language, String script) { return asMarkdown("```" + ofNullable(language).orElse("") + NEW_LINE + script + NEW_LINE + "```"); } - @Nonnull - private static List calculateColSizes(@Nonnull List colSizes, int maxTableSize) { + private static List calculateColSizes(@Nonnull List> table) { + int tableColNum = table.stream().mapToInt(List::size).max().orElse(-1); + List> iterList = table.stream().map(List::iterator).collect(Collectors.toList()); + return IntStream.range(0, tableColNum) + .mapToObj(n -> iterList.stream().filter(Iterator::hasNext).map(Iterator::next).collect(Collectors.toList())) + .map(col -> col.stream().mapToInt(String::length).max().orElse(0)) + .collect(Collectors.toList()); + } + + private static int calculateTableSize(@Nonnull List colSizes) { int colTableSize = colSizes.stream().reduce(Integer::sum).orElse(-1); colTableSize += (PADDING_SPACES_NUM + TABLE_COLUMN_SEPARATOR.length()) * colSizes.size() - 1; // Inner columns grid colTableSize += 2; // Outer table grid + return colTableSize; + } + + private static List> transposeTable(@Nonnull List> table) { + int tableColNum = table.stream().mapToInt(List::size).max().orElse(-1); + List> iterList = table.stream().map(List::iterator).collect(Collectors.toList()); + return IntStream.range(0, tableColNum) + .mapToObj(n -> iterList.stream() + .filter(Iterator::hasNext) + .map(Iterator::next) + .collect(Collectors.toList())) + .collect(Collectors.toList()); + } + + @Nonnull + private static List adjustColSizes(@Nonnull List colSizes, int maxTableSize) { + int colTableSize = calculateTableSize(colSizes); if (maxTableSize >= colTableSize) { return colSizes; } @@ -79,6 +105,9 @@ private static List calculateColSizes(@Nonnull List colSizes, for (int i = 0; i < sizeToShrink; i++) { for (int j = 0; j < colsBySize.size(); j++) { Pair currentCol = colsBySize.get(j); + if (currentCol.getKey() <= MIN_COL_SIZE) { + continue; + } Pair nextCol = colsBySize.size() > j + 1 ? colsBySize.get(j + 1) : Pair.of(0, 0); if (currentCol.getKey() >= nextCol.getKey()) { colsBySize.set(j, Pair.of(currentCol.getKey() - 1, currentCol.getValue())); @@ -86,9 +115,7 @@ private static List calculateColSizes(@Nonnull List colSizes, } } } - List result = new ArrayList<>(colSizes); - colsBySize.forEach(col -> result.set(col.getValue(), col.getKey())); - return result; + return colsBySize.stream().sorted(Map.Entry.comparingByValue()).map(Pair::getKey).collect(Collectors.toList()); } /** @@ -100,25 +127,30 @@ private static List calculateColSizes(@Nonnull List colSizes, */ @Nonnull public static String formatDataTable(@Nonnull final List> table, int maxTableSize) { + List colSizes = calculateColSizes(table); + boolean transpose = colSizes.size() > table.size() && calculateTableSize(colSizes) > maxTableSize; + List> printTable = transpose ? transposeTable(table) : table; + if(transpose) { + colSizes = calculateColSizes(printTable); + } + colSizes = adjustColSizes(colSizes, maxTableSize); + int tableSize = calculateTableSize(colSizes); + boolean addPadding = tableSize <= maxTableSize; + boolean header = !transpose; StringBuilder result = new StringBuilder(); - int tableColNum = table.stream().mapToInt(List::size).max().orElse(-1); - List> iterList = table.stream().map(List::iterator).collect(Collectors.toList()); - List colSizes = IntStream.range(0, tableColNum) - .mapToObj(n -> iterList.stream().filter(Iterator::hasNext).map(Iterator::next).collect(Collectors.toList())) - .map(col -> col.stream().mapToInt(String::length).max().orElse(0)) - .collect(Collectors.toList()); - colSizes = calculateColSizes(colSizes, maxTableSize); - - boolean header = true; - for (List row : table) { + for (List row : printTable) { result.append(TABLE_INDENT).append(TABLE_COLUMN_SEPARATOR); for (int i = 0; i < row.size(); i++) { String cell = row.get(i); int colSize = colSizes.get(i); if (colSize < cell.length()) { - cell = cell.substring(0, colSize - TRUNCATION_REPLACEMENT.length()) + TRUNCATION_REPLACEMENT; + if (TRUNCATION_REPLACEMENT.length() < colSize) { + cell = cell.substring(0, colSize - TRUNCATION_REPLACEMENT.length()) + TRUNCATION_REPLACEMENT; + } else { + cell = cell.substring(0, colSize); + } } - int padSize = colSize - cell.length() + PADDING_SPACES_NUM; + int padSize = colSize - cell.length() + (addPadding ? PADDING_SPACES_NUM : 0); int lSpace = padSize / 2; int rSpace = padSize - lSpace; IntStream.range(0, lSpace).forEach(j -> result.append(ONE_SPACE)); @@ -131,7 +163,7 @@ public static String formatDataTable(@Nonnull final List> table, in result.append(NEW_LINE); result.append(TABLE_INDENT).append(TABLE_COLUMN_SEPARATOR); for (int i = 0; i < row.size(); i++) { - int maxSize = colSizes.get(i) + 2; + int maxSize = colSizes.get(i) + (addPadding ? PADDING_SPACES_NUM : 0); IntStream.range(0, maxSize).forEach(j -> result.append(TABLE_ROW_SEPARATOR)); result.append(TABLE_COLUMN_SEPARATOR); } diff --git a/src/test/java/com/epam/reportportal/utils/markdown/MarkdownUtilsTest.java b/src/test/java/com/epam/reportportal/utils/markdown/MarkdownUtilsTest.java index e1f0f4db..e4b98056 100644 --- a/src/test/java/com/epam/reportportal/utils/markdown/MarkdownUtilsTest.java +++ b/src/test/java/com/epam/reportportal/utils/markdown/MarkdownUtilsTest.java @@ -17,7 +17,10 @@ import org.junit.jupiter.api.Test; -import java.util.*; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; import static com.epam.reportportal.utils.markdown.MarkdownUtils.*; import static org.hamcrest.MatcherAssert.assertThat; @@ -105,11 +108,37 @@ public void test_format_data_table_two_big_col() { @Test public void test_format_data_table_map() { - Map table = new LinkedHashMap(){{ + Map table = new LinkedHashMap() {{ put("var_a", "2"); put("var_b", "2"); put("result", "4"); }}; assertThat(formatDataTable(table), equalTo(ONE_ROW_EXPECTED_TABLE)); } + + //@formatter:off + public static final String MIN_ROW_WIDTH_EXPECTED_TABLE_TRANSPOSE = + TABLE_INDENT + "|var|2|\n" + + TABLE_INDENT + "|var|2|\n" + + TABLE_INDENT + "|res|4|"; + //@formatter:on + + @Test + public void test_format_data_table_min_size_transpose() { + List> table = Arrays.asList(Arrays.asList("var_a", "var_b", "result"), Arrays.asList("2", "2", "4")); + assertThat(formatDataTable(table, 0), equalTo(MIN_ROW_WIDTH_EXPECTED_TABLE_TRANSPOSE)); + } + + //@formatter:off + public static final String MIN_ROW_WIDTH_EXPECTED_TABLE_NO_TRANSPOSE = + TABLE_INDENT + "|var|res|\n" + + TABLE_INDENT + "|---|---|\n" + + TABLE_INDENT + "|\u00A02\u00A0|\u00A04\u00A0|"; + //@formatter:on + + @Test + public void test_format_data_table_min_size_no_transpose() { + List> table = Arrays.asList(Arrays.asList("var_a", "result"), Arrays.asList("2", "4")); + assertThat(formatDataTable(table, 0), equalTo(MIN_ROW_WIDTH_EXPECTED_TABLE_NO_TRANSPOSE)); + } }