package com.mcmoddev.bot; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Function; public class TableBuilder<T> { // TOOD make this stuff configurable private static final String NEW_LINE = System.lineSeparator(); private static final String DIVIDER_COLUMN = "|"; private static final String DIVIDER_ROW = "-"; private static final String LINE_ENDING = DIVIDER_ROW + DIVIDER_ROW + DIVIDER_COLUMN; /** * An internal list of entries, used to make list population much easier. */ private final List<T> entries = new CopyOnWriteArrayList<>(); /** * A list of names used for the columns of the table. */ private final List<String> columnNames = new ArrayList<>(); /** * A list of functions which match up with the columns of the table. */ private final List<Function<? super T, String>> columnFunctions = new ArrayList<>(); /** * Defines a new column for the table. This sets the name of the column, and a function * which is used to generate row data. * * @param name The name for the column. * @param function The function to apply to the object. Used to generate row data for the * column. */ public void addColumn (String name, Function<? super T, ?> function) { this.columnNames.add(name); this.columnFunctions.add( (p) -> String.valueOf(function.apply(p))); } /** * Gets the longest width amongst all data in a column. * * @param columnIndex The index of the column. * @param entries The entries for the table. * @return The maximum width for the column. */ private int getMaxWidth (int columnIndex, Iterable<? extends T> entries) { int maxWidth = this.columnNames.get(columnIndex).length(); final Function<? super T, String> function = this.columnFunctions.get(columnIndex); for (final T entry : entries) { // TODO cache final String data = function.apply(entry); maxWidth = Math.max(maxWidth, data.length()); } return maxWidth; } /** * Pads a string to the left. * * @param string The string to pad. * @param padCharacter The character to pad the string with. * @param length The desired length for the string. * @return The padded string. */ private String padLeft (String string, String padCharacter, int length) { while (string.length() < length) string = padCharacter + string; return string; } /** * Gets a list of integers which represent the width for each column. * * @param entries The entries for the column. * @return A list of integers which represent the longest width for each column. */ private List<Integer> getColumnWidths (Iterable<? extends T> entries) { final List<Integer> columnWidths = new ArrayList<>(); for (int columnIndex = 0; columnIndex < this.columnNames.size(); columnIndex++) columnWidths.add(this.getMaxWidth(columnIndex, entries)); return columnWidths; } /** * Adds an entry to the table. * * @param entry The entry to add to the table. */ public void addEntry (T entry) { this.entries.add(entry); } /** * Creates the table string using the entries from {@link #entries}. * * @return A string which represents the table data as a markdown table. */ public String createString () { return this.createString(this.entries); } public String createString (Iterable<? extends T> entries) { final List<Integer> widths = this.getColumnWidths(entries); final int columnCount = this.columnNames.size(); final StringBuilder builder = new StringBuilder(); // Column Names for (int columnIndex = 0; columnIndex < columnCount; columnIndex++) { builder.append(DIVIDER_COLUMN); final String columnHeader = "%-" + widths.get(columnIndex) + "s"; builder.append(" " + String.format(columnHeader, this.columnNames.get(columnIndex)) + " "); } builder.append(DIVIDER_COLUMN); builder.append(NEW_LINE); // Column Seperator builder.append(DIVIDER_COLUMN); for (int columnIndex = 0; columnIndex < columnCount; columnIndex++) { if (columnIndex > 0) builder.append(LINE_ENDING); builder.append(this.padLeft("", DIVIDER_ROW, widths.get(columnIndex))); } builder.append(LINE_ENDING); builder.append(NEW_LINE); // Column Data for (final T entry : entries) { for (int columnIndex = 0; columnIndex < columnCount; columnIndex++) { builder.append(DIVIDER_COLUMN); final String format = "%-" + widths.get(columnIndex) + "s"; final Function<? super T, String> function = this.columnFunctions.get(columnIndex); // TODO cache final String columnText = function.apply(entry); builder.append(" " + String.format(format, columnText) + " "); } builder.append(DIVIDER_COLUMN); builder.append(NEW_LINE); } return builder.toString(); } }