All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.trino.tests.product.launcher.util.ConsoleTable Maven / Gradle / Ivy

There is a newer version: 451
Show newest version
/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.trino.tests.product.launcher.util;

import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static com.google.common.collect.ImmutableList.toImmutableList;
import static io.trino.tests.product.launcher.util.ConsoleTable.Alignment.RIGHT;
import static java.lang.Math.max;
import static java.util.Collections.nCopies;
import static java.util.Objects.requireNonNull;

public class ConsoleTable
{
    private final ImmutableList.Builder builder = ImmutableList.builder();

    public void addSeparator()
    {
        builder.add(new TableSeparator());
    }

    public ConsoleTable addHeader(Object... values)
    {
        builder.add(new TableSeparator(), new TableRow(RIGHT, values), new TableSeparator());
        return this;
    }

    public ConsoleTable addRow(Object... values)
    {
        builder.add(new TableRow(RIGHT, values));
        return this;
    }

    public ConsoleTable addRow(Alignment alignment, Object... values)
    {
        builder.add(new TableRow(alignment, values));
        return this;
    }

    public String render()
    {
        List elements = builder.build();
        int[] columnsWidth = columnsWidth(elements);

        return elements.stream()
                .map(element -> element.render(columnsWidth))
                .collect(Collectors.joining("\n"));
    }

    private int[] columnsWidth(List elements)
    {
        int numberOfColumns = columns(elements);
        int[] widths = new int[numberOfColumns];

        for (TableElement element : elements) {
            int[] columnsLength = element.columnsWidth();
            for (int i = 0; i < element.columnsCount(); i++) {
                widths[i] = Integer.max(widths[i], columnsLength[i]);
            }
        }

        return widths;
    }

    private int columns(List elements)
    {
        return elements.stream()
                .filter(element -> element instanceof TableRow)
                .map(element -> (TableRow) element)
                .map(TableRow::columnsCount)
                .max(Integer::compareTo)
                .orElse(0);
    }

    @Override
    public String toString()
    {
        return render();
    }

    private interface TableElement
    {
        String render(int[] columnLengths);

        default int columnsCount()
        {
            return 1;
        }

        default int[] columnsWidth()
        {
            return new int[] {1};
        }
    }

    private static class TableRow
            implements TableElement
    {
        public static final Splitter LINE_SPLITTER = Splitter.on('\n')
                .omitEmptyStrings();

        private final Object[] values;
        private final int columns;
        private final Alignment alignment;

        public TableRow(Alignment alignment, Object[] values)
        {
            this.values = requireNonNull(values, "values is null");
            this.alignment = alignment;
            this.columns = this.values.length;
        }

        public Object getValue(int column)
        {
            if (column < 0 || column >= columnsCount()) {
                return "";
            }

            return values[column];
        }

        @Override
        public int columnsCount()
        {
            return columns;
        }

        public int columnWidth(int column)
        {
            return lines(column).stream()
                    .map(String::length)
                    .max(Integer::compareTo)
                    .orElse(0);
        }

        public int columnHeight(int column)
        {
            return lines(column).size();
        }

        @Override
        public int[] columnsWidth()
        {
            int[] lengths = new int[columns];

            for (int i = 0; i < columns; i++) {
                lengths[i] = columnWidth(i);
            }
            return lengths;
        }

        public int rowHeight()
        {
            int height = 0;
            for (int i = 0; i < columns; i++) {
                height = max(height, columnHeight(i));
            }
            return height;
        }

        private List lines(int column)
        {
            return LINE_SPLITTER.splitToList(Objects.toString(getValue(column)));
        }

        private List paddedLines(int column, int rowHeight)
        {
            List lines = lines(column);
            if (lines.size() < rowHeight) {
                return ImmutableList.builder()
                        .addAll(lines)
                        .addAll(nCopies(rowHeight - lines.size(), ""))
                        .build();
            }

            return lines(column);
        }

        @Override
        public String render(int[] columnsWidth)
        {
            StringBuilder builder = new StringBuilder();
            int rowHeight = rowHeight();

            List> lines = IntStream.range(0, columns)
                    .mapToObj(column -> paddedLines(column, rowHeight))
                    .collect(toImmutableList());

            for (int row = 0; row < rowHeight; row++) {
                for (int col = 0; col < columnsWidth.length; col++) {
                    String value = "";
                    if (col < lines.size()) {
                        value = lines.get(col).get(row);
                    }

                    builder.append("|").append(" ").append(pad(value, columnsWidth[col], alignment)).append(" ");
                    if (col == columnsWidth.length - 1) {
                        builder.append("|");
                    }
                }

                if (row < rowHeight - 1) {
                    builder.append("\n");
                }
            }

            return builder.toString();
        }

        private String pad(Object obj, int length, Alignment alignment)
        {
            String value = Objects.toString(obj);
            if (value.length() < length) {
                int diff = length - value.length();

                switch (alignment) {
                    case LEFT:
                        return Strings.padEnd(value, length, ' ');

                    case RIGHT:
                        return Strings.padStart(value, length, ' ');

                    case CENTER:
                        int leftPadding = (diff / 2);
                        int rightPadding = diff - leftPadding;

                        return " ".repeat(leftPadding) + value + " ".repeat(rightPadding);
                }
            }

            return value;
        }
    }

    private static class TableSeparator
            implements TableElement
    {
        @Override
        public String render(int[] columnsWidth)
        {
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < columnsWidth.length; i++) {
                builder.append("+").append("-".repeat(columnsWidth[i] + 2));
                if (i == columnsWidth.length - 1) {
                    builder.append("+");
                }
            }

            return builder.toString();
        }
    }

    public enum Alignment
    {
        LEFT,
        CENTER,
        RIGHT;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy