├── .classpath ├── .gitignore ├── .project ├── .settings └── org.eclipse.core.resources.prefs ├── LICENSE ├── README.md ├── src └── com │ └── johncsinclair │ └── consoletable │ ├── ColumnFormat.java │ ├── ConsoleTable.java │ ├── Style.java │ └── Styles.java └── test └── com └── johncsinclair └── consoletable └── ConsoleTableTest.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | java-console-table 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/com/johncsinclair/consoletable/ColumnFormat.java=UTF-8 3 | encoding//src/com/johncsinclair/consoletable/ConsoleTable.java=UTF-8 4 | encoding//src/com/johncsinclair/consoletable/Style.java=UTF-8 5 | encoding//src/com/johncsinclair/consoletable/Styles.java=UTF-8 6 | encoding//test/com/johncsinclair/consoletable/ConsoleTableTest.java=UTF-8 7 | encoding/=UTF-8 8 | encoding/README.md=UTF-8 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 John C Sinclair 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # java-console-table a ConsoleTable for Java 2 | 3 | Copyright (c) John C Sinclair 2021 4 | 5 | ![license MIT](https://img.shields.io/github/license/JohnCSinclair-com/java-console-table) 6 | 7 | A `ConsoleTable` formats a table of rows and columns into a 8 | single `String`, which can be printed on the console with a monospaced font. 9 | 10 | Example code: 11 | 12 | ```java 13 | ConsoleTable table = new ConsoleTable(); 14 | table.setHeaders("-City", "'Lat", "Longitude"); 15 | table.addRow("Paris", 48.86,2.34); 16 | table.addRow("New York", 40.74, -73.99); 17 | table.addRow("London", 51.52, -0.13); 18 | table.addRow("Brisbane", -27.47, 153.03); 19 | System.out.print(table); 20 | 21 | System.out.print(table.withStyle(Styles.BASIC)); 22 | ``` 23 | 24 | Console output: 25 | 26 | ``` 27 | ┌──────────┬────────┬───────────┐ 28 | │ City │ Lat │ Longitude │ 29 | ├──────────┼────────┼───────────┤ 30 | │ Paris │ 48.86 │ 2.34 │ 31 | │ New York │ 40.74 │ -73.99 │ 32 | │ London │ 51.52 │ -0.13 │ 33 | │ Brisbane │ -27.47 │ 153.03 │ 34 | └──────────┴────────┴───────────┘ 35 | +----------+--------+-----------+ 36 | | City | Lat | Longitude | 37 | +----------+--------+-----------+ 38 | | Paris | 48.86 | 2.34 | 39 | | New York | 40.74 | -73.99 | 40 | | London | 51.52 | -0.13 | 41 | | Brisbane | -27.47 | 153.03 | 42 | +----------+--------+-----------+ 43 | ``` 44 | 45 | 46 | ## Features 47 | 48 | ConsoleTable makes it easy to build stylish tables, which can be printed into a single string with all formats preserved.

49 | The core features are: 50 | - Never have to work out the width of a column, `ConsoleTable` does it for you. 51 | - Be forgiving of input data and handle nulls and missing columns. 52 | - Display an optional header row. 53 | - Display borders and lines with a predefined or user defined `Style` 54 | - Uses the `toString()` method to print any Object 55 | - Accepts table data as `Object[][]` or `List` or `List>`. 56 | - Uses fluent style method chaining. `Table.of(headers,data).withStyle(Styles.SQL)` 57 | - A column can be `Aligned` either `LEFT`, `CENTRE` or `RIGHT`. 58 | - `ColumnFormat` defaults to `RIGHT`, you can set a column header to make it `LEFT` or `CENTRE` 59 | - You can add a string Header, start with a `-` and it will be aligned `LEFT`, with a `'` it will be `CENTRE` 60 | 61 | 62 | 63 | #### Convert `Object[][]` to `ConsoleTable` 64 | If you already have ```Object[][] data```, you can print it on the console as a formatted table instantly: 65 | 66 | ```java 67 | ConsoleTable table = new ConsoleTable(data); 68 | System.out.print(table); // Note: table.toString() is called implicitly 69 | ``` 70 | 71 | ## Quick Start 72 | 73 | * To get a quick demonstration, run ConsoleTable.java, the main() method prints out some samples. 74 | * To find out what Styles are available, run Styles.java, the main() method prints a sample of each predefined Style. 75 | * To display monospaced box-drawing characters in the Eclipse console, in Run | Run Configurations... , in the Common tab, set Encoding to Other UTF-8 or set the Project | Properties | Text File Encoding to UTF-8, so that it is inherited automatically by the Run Configurations. 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /src/com/johncsinclair/consoletable/ColumnFormat.java: -------------------------------------------------------------------------------- 1 | package com.johncsinclair.consoletable; 2 | 3 | /** 4 | * Format settings for a column in a {@link ConsoleTable}. Use a ColumnFormat in {@link ConsoleTable#setHeaders}. 5 | * 6 | * @author Copyright (c) John C Sinclair 2021 7 | * 8 | */ 9 | public class ColumnFormat { 10 | 11 | /** 12 | * A {@link ConsoleTable} column can be aligned LEFT, CENTRE or RIGHT. 13 | */ 14 | public enum Aligned { LEFT, CENTRE, RIGHT }; 15 | 16 | private String columnHeading; 17 | private Aligned alignment = Aligned.RIGHT; 18 | 19 | /** 20 | * @param columnHeading The heading for the column. By default the column will be right aligned, 21 | * if columnHeading starts with - the column will be left aligned, 22 | * if columnHeading starts with ' the column will be centred. 23 | */ 24 | public ColumnFormat(String columnHeading) { 25 | final char firstChar = columnHeading.charAt(0); 26 | if(firstChar == '-' || firstChar == '\'') { 27 | columnHeading = columnHeading.substring(1); 28 | this.alignment = firstChar == '-' ? Aligned.LEFT : Aligned.CENTRE; 29 | } 30 | this.columnHeading = columnHeading; 31 | } 32 | 33 | /** 34 | * @param columnHeading The heading for the column. 35 | * @param alignment The alignment for the column. 36 | */ 37 | public ColumnFormat(String columnHeading, Aligned alignment) { 38 | this(columnHeading); 39 | this.alignment = alignment; 40 | } 41 | 42 | public Aligned getAlignment() { 43 | return alignment; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return columnHeading; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/com/johncsinclair/consoletable/ConsoleTable.java: -------------------------------------------------------------------------------- 1 | package com.johncsinclair.consoletable; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import com.johncsinclair.consoletable.Style.Row; 8 | import com.johncsinclair.consoletable.Style.Column; 9 | import com.johncsinclair.consoletable.ColumnFormat.Aligned; 10 | 11 | 12 | /** 13 | * A {@code ConsoleTable} represents a table of rows and columns to be formatted into a 14 | * single {@code String}, which can be further printed on the console in a monospaced font. 15 | *

16 | * For example: 17 | *

 18 |  * Pet Age
 19 |  * --- ---
 20 |  * Cat   5
 21 |  * Dog  10
 22 |  * 
23 | * 24 | * I started with CommandLineTable and added null handling, fluency, ColumnFormats and customisable Styles and tests. 25 | * 26 | * @author Copyright (c) John C Sinclair 2021 27 | */ 28 | public class ConsoleTable { 29 | 30 | private static final Object[] NULL_OBJECT_ARRAY = (Object[])null; 31 | private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; 32 | private Style style = Styles.LIGHT; 33 | 34 | private Object[] headers; 35 | private List rows = new ArrayList<>(); 36 | 37 | private Aligned alignment = Aligned.RIGHT; 38 | private boolean showVerticalLines = true; 39 | private boolean withRowLines = false; 40 | private String leftColumnPadding = " "; 41 | private String rightColumnPadding = " "; 42 | 43 | private int rowWidth; 44 | 45 | /** 46 | * Constructor for empty table. The column headers can be set with setHeaders and data rows can be added with addRow. 47 | */ 48 | public ConsoleTable() { 49 | withVerticalLines(true); 50 | } 51 | 52 | /** 53 | * Constructor for table of List with no headers. 54 | * @param data List of Object[] representing rows of columns. 55 | */ 56 | public ConsoleTable(final List data) { 57 | this(); 58 | final Object[] emptyObjectArray = {}; 59 | setHeaders(emptyObjectArray); 60 | rows.addAll(data); 61 | } 62 | 63 | /** 64 | * Constructor for List table with headers 65 | * @param headers array of Object representing table headers 66 | */ 67 | public ConsoleTable(final Object[] headers, final List data) { 68 | this(data); 69 | setHeaders(headers); 70 | } 71 | 72 | /** 73 | * Constructor for Object[][] table with no headers 74 | * @param headers_ - arrays of Strings representing table headers 75 | */ 76 | public ConsoleTable(final Object[][] data) { 77 | this(); 78 | for(Object[] row : data) { 79 | rows.add(row); 80 | } 81 | } 82 | 83 | /** 84 | * Constructor for Object[][] table with no headers 85 | * @param headers_ - arrays of Strings representing table headers 86 | */ 87 | public ConsoleTable(final Object[] headers, final Object[][] data) { 88 | this(data); 89 | setHeaders(headers); 90 | } 91 | 92 | //TODO test this 93 | /** 94 | * Constructor for Object[][] table with no headers 95 | * @param headers_ - arrays of Strings representing table headers 96 | */ 97 | public ConsoleTable(final Iterable headers, final Iterable> data) { 98 | 99 | for(T t : headers) { 100 | System.out.println(t); 101 | } 102 | 103 | for(Iterable row : data) { 104 | for( U column : row ) { 105 | System.out.printf("%s ", column); 106 | } 107 | System.out.println(); 108 | } 109 | 110 | 111 | setHeaders(headers); 112 | throw new UnsupportedOperationException("TODO"); 113 | } 114 | 115 | /** 116 | * Constructor for Object[][] table with no headers 117 | * @param headers_ - arrays of Strings representing table headers 118 | */ 119 | public ConsoleTable(List headerList, List> rowList) { 120 | this(); 121 | 122 | setHeaders( headerList.toArray(new Object[0]) ); 123 | 124 | for(List row : rowList) { 125 | Object[] newRow = row.toArray(); 126 | rows.add(newRow); 127 | } 128 | 129 | } 130 | 131 | /** 132 | * Constructor for table with headers 133 | * @param headers Strings representing table headers 134 | */ 135 | public ConsoleTable(final String ... headers) { 136 | this(); 137 | setHeaders(headers); 138 | } 139 | 140 | 141 | 142 | public ConsoleTable withStyle(Style style) { 143 | this.style = style; 144 | this.leftColumnPadding = style.getPadding(Column.LEFT); 145 | this.rightColumnPadding = style.getPadding(Column.RIGHT); 146 | return this; 147 | } 148 | 149 | public ConsoleTable withAlignment(Aligned aligned) { 150 | this.alignment = aligned; 151 | return this; 152 | } 153 | 154 | public ConsoleTable withVerticalLines(boolean showVerticalLines) { 155 | this.showVerticalLines = showVerticalLines; 156 | return this; 157 | } 158 | 159 | public ConsoleTable withColumnPadding(String leftColumnPadding, String rightColumnPadding) { 160 | this.leftColumnPadding = leftColumnPadding; 161 | this.rightColumnPadding = rightColumnPadding; 162 | return this; 163 | } 164 | 165 | public ConsoleTable withColumnPadding(String padding) { 166 | return this.withColumnPadding(padding, padding); 167 | } 168 | 169 | public ConsoleTable withRowLines(boolean showRowLines) { 170 | this.withRowLines = showRowLines; 171 | return this; 172 | } 173 | 174 | public ConsoleTable withRowLines() { 175 | this.withRowLines = true; 176 | return this; 177 | } 178 | 179 | /** 180 | * display a row of column headings at the top of the table 181 | * 182 | * @param headers The headings for the columns. By default a column will be right aligned,
if it starts with - the column will be left aligned,
if it starts with ' the column will be centred. 183 | */ 184 | public ConsoleTable setHeaders(Object... headers) { 185 | 186 | // if a String starts with "-", make it left aligned, like String.format("%-s") 187 | // replace any String headers starting with "-" with a Left aligned format, like String.format("%-9s") 188 | // have to copy the array to avoid an ArrayStoreException 189 | 190 | if( headers == null) { 191 | headers = EMPTY_OBJECT_ARRAY; 192 | } 193 | 194 | Object[] newHeaders = new Object[headers.length]; 195 | 196 | for(int i = 0; i < headers.length; i++) { 197 | Object columnHeading = headers[i]; 198 | if( columnHeading != null && columnHeading instanceof String && ((String) columnHeading).length() > 0 ) { 199 | char firstChar = ((String) columnHeading).charAt(0); 200 | if(firstChar == '-' || firstChar == '\'') { 201 | ColumnFormat columnFormat = new ColumnFormat( (String)columnHeading ); 202 | columnHeading = columnFormat; 203 | } 204 | } 205 | newHeaders[i] = columnHeading; 206 | } 207 | this.headers = (newHeaders.length == 0) ? null : newHeaders; 208 | 209 | return this; 210 | } 211 | 212 | /** 213 | * display a row of column headings at the top of the table 214 | * 215 | * @param headers The headings for the columns. By default a column will be right aligned,
if it starts with - the column will be left aligned,
if it starts with ' the column will be centred. 216 | * @return 217 | */ 218 | public ConsoleTable setHeaders(String... headers) { 219 | Object[] objects = headers; 220 | setHeaders(objects); 221 | return this; 222 | } 223 | 224 | 225 | /** 226 | * Add a row of data 227 | */ 228 | public void addRow(Object... cells) { 229 | if(cells == null) { 230 | addRow(); 231 | return; 232 | } 233 | rows.add(cells); 234 | } 235 | 236 | /** 237 | * Add a row of data 238 | */ 239 | public void addRow(String[] cells) { 240 | addRow( (Object[])cells ); 241 | } 242 | 243 | /** 244 | * Add a row of data 245 | * @param 246 | */ 247 | public void addRow(List cells) { 248 | if( cells == null ) { 249 | addRow(); 250 | } 251 | else { 252 | addRow( cells.toArray() ); 253 | } 254 | } 255 | 256 | /** 257 | * Add an empty row, all columns in the row will be empty. 258 | */ 259 | public void addRow() { 260 | rows.add(NULL_OBJECT_ARRAY ); 261 | } 262 | 263 | /** 264 | * Appends all of the elements in moreRows to the end of the rows in this ConsoleTable. 265 | * @param moreRows 266 | */ 267 | public void addAll(Iterable moreRows) { 268 | for(Object[] row : moreRows) { 269 | if( row != null ) { 270 | rows.add( row ); 271 | } 272 | else { 273 | rows.add( NULL_OBJECT_ARRAY ); 274 | } 275 | } 276 | } 277 | 278 | 279 | /** 280 | * Returns a multi-line String containing the formatted rows and columns of the table. 281 | */ 282 | @Override 283 | public String toString() { 284 | return render(); 285 | } 286 | 287 | private String render() { 288 | 289 | int[] maxWidths = calculateMaxWidths(); 290 | 291 | rowWidth = calculateRowWidth(maxWidths); 292 | 293 | int renderedLineCount = rows.size(); 294 | if(withRowLines) { 295 | renderedLineCount = renderedLineCount * 2; 296 | } 297 | if (headers != null) { 298 | renderedLineCount++; 299 | } 300 | StringBuffer buf = new StringBuffer(rowWidth * renderedLineCount); // TODO tableWidth()+1); 301 | 302 | buf.append(renderRow(Row.TOP, maxWidths, null)); 303 | 304 | if (headers != null) { 305 | buf.append(renderRow(Row.HDRDATA, maxWidths, headers)); 306 | buf.append(renderRow(Row.HDRLINE, maxWidths, null)); 307 | } 308 | 309 | for (int i = 0; i < rows.size(); i++) { 310 | Object[] row = rows.get(i); 311 | buf.append(renderRow(Row.ROWDATA, maxWidths, row)); 312 | if( i != renderedLineCount - 1 && withRowLines ) { 313 | buf.append(renderRow(Row.ROWLINE, maxWidths, null)); 314 | } 315 | } 316 | 317 | buf.append(renderRow(Row.BOTTOM, maxWidths, null)); 318 | 319 | return buf.toString(); 320 | } 321 | 322 | private int calculateRowWidth(int[] columnWidths) { 323 | 324 | int rowWidth = 0; 325 | Row rowType = Row.ROWDATA; 326 | 327 | if(showVerticalLines) { 328 | rowWidth += style.getPattern(rowType, Column.LEFT).length(); 329 | } 330 | for (int i = 0; i < columnWidths.length; i++) { 331 | 332 | String joinSep = showVerticalLines ? style.getPattern(rowType, Column.COLLINE) : " "; 333 | boolean isLastCell = i == columnWidths.length - 1; 334 | 335 | if(showVerticalLines) 336 | rowWidth += leftColumnPadding.length(); 337 | 338 | rowWidth += columnWidths[i]; 339 | 340 | if(showVerticalLines) 341 | rowWidth += rightColumnPadding.length(); 342 | if(!isLastCell) { 343 | rowWidth += joinSep.length(); 344 | } 345 | } 346 | if(showVerticalLines) { 347 | rowWidth += style.getPattern(rowType, Column.RIGHT).length(); 348 | } 349 | rowWidth += "\n".length(); 350 | 351 | return rowWidth; 352 | } 353 | 354 | public int[] calculateMaxWidths() { 355 | 356 | // instead of throwing exception, be permissive, and if header and data widths are not equal then display blanks at the end of header or data. 357 | 358 | List maxWidths = new ArrayList<>(); 359 | 360 | 361 | if(headers != null) { 362 | for (int i = 0; i < headers.length; i++) { 363 | if( i > maxWidths.size() - 1 ) { 364 | maxWidths.add(0); 365 | } 366 | maxWidths.set(i, Math.max(maxWidths.get(i), headers[i] == null ? 0 : headers[i].toString().codePointCount(0, headers[i].toString().length()) )); 367 | } 368 | } 369 | 370 | for (Object[] cells : rows) { 371 | if(cells != null) { 372 | for (int i = 0; i < cells.length; i++) { 373 | if( i > maxWidths.size() - 1 ) { 374 | maxWidths.add(0); 375 | } 376 | maxWidths.set(i, Math.max(maxWidths.get(i), cells[i] == null ? 0 : cells[i].toString().codePointCount(0, cells[i].toString().length()) )); 377 | } 378 | } 379 | } 380 | return maxWidths.stream().mapToInt(Integer::intValue).toArray(); 381 | } 382 | 383 | private String renderRow(Row rowType, int[] columnWidths, Object[] cells ) { 384 | if(style.getPattern(rowType, Column.LEFT) == null) { 385 | return ""; 386 | } 387 | 388 | StringBuffer buf = new StringBuffer(rowWidth); 389 | if(showVerticalLines) { 390 | buf.append(style.getPattern(rowType, Column.LEFT)); 391 | } 392 | for (int i = 0; i < columnWidths.length; i++) { 393 | 394 | int columnWidth = columnWidths[i]; 395 | String cellString = null; 396 | String leftCellPadding = leftColumnPadding; 397 | String rightCellPadding = rightColumnPadding; 398 | 399 | Aligned columnAlign = alignment; 400 | 401 | if(rowType == Row.HDRDATA || rowType == Row.ROWDATA ) { 402 | String cell = (cells == null || i > cells.length -1 || cells[i] == null) ? "" : cells[i].toString(); 403 | 404 | if(headers != null && i < headers.length && headers[i] != null && headers[i] instanceof ColumnFormat) { 405 | columnAlign = ((ColumnFormat)headers[i]).getAlignment(); 406 | if(columnAlign == Aligned.CENTRE) { 407 | // left pad cell so that it is centred 408 | int cellWidth = cell.codePointCount(0, cell.length()); 409 | int leftPadWidth = (columnWidth - cellWidth+1) / 2; 410 | String padFormat = "%" + (leftPadWidth+cellWidth) + "s"; 411 | cell = String.format(padFormat, cell); 412 | } 413 | } 414 | 415 | String formatString = "%"+(columnAlign == Aligned.RIGHT ? "" : "-")+columnWidth+"s"; 416 | cellString = (columnWidth == 0) ? "" : String.format(formatString, cell); 417 | } 418 | else { // this is a rule line between the rows of the table 419 | 420 | String ruleString = style.getPattern(rowType, Column.COLDATA); 421 | columnWidth = leftCellPadding.length() + columnWidth + rightCellPadding.length(); 422 | cellString = String.join("", Collections.nCopies(columnWidth, ruleString)).substring(0,columnWidth); 423 | 424 | leftCellPadding = ""; 425 | rightCellPadding = ""; 426 | } 427 | 428 | String joinSep = showVerticalLines ? style.getPattern(rowType, Column.COLLINE) : " "; 429 | boolean isLastCell = i == columnWidths.length - 1; 430 | 431 | buf.append(leftCellPadding); 432 | buf.append(cellString); 433 | buf.append(rightCellPadding); 434 | 435 | if(!isLastCell) { 436 | buf.append(joinSep); 437 | } 438 | } 439 | if(showVerticalLines) { 440 | buf.append(style.getPattern(rowType, Column.RIGHT)); 441 | } 442 | buf.append("\n"); 443 | 444 | return buf.toString(); 445 | } 446 | 447 | 448 | public static void main(String[] args) { 449 | 450 | ConsoleTable table = new ConsoleTable(); 451 | table.setHeaders("-City", "'Lat", "Longitude"); 452 | table.addRow("Paris", 48.86,2.34); 453 | table.addRow("New York", 40.74, -73.99); 454 | table.addRow("London", 51.52, -0.13); 455 | table.addRow("Brisbane", -27.47, 153.03); 456 | System.out.println(table); 457 | System.out.println(table.withStyle(Styles.BASIC)); 458 | 459 | //example code 460 | ConsoleTable example = new ConsoleTable(); 461 | example.setHeaders("One", "Two", "Three"); //optional - if not used then there will be no header and no header line 462 | example.addRow(new java.math.BigDecimal("98.76"), "broccoli", "flexible"); 463 | example.addRow(new java.math.BigDecimal("1.23"), "announcement", "reflection"); 464 | example.addRow(new java.math.BigDecimal("1234.45"), null, java.time.LocalDateTime.now() ); 465 | example.addRow( java.math.BigDecimal.ZERO, "pleasant", "wild"); 466 | 467 | System.out.println("Example 1 - Default"); 468 | System.out.println(example); 469 | 470 | System.out.println("Example 2 - Minimal"); 471 | System.out.println(example.withStyle(Styles.MINIMAL).withAlignment(Aligned.LEFT)); 472 | 473 | System.out.println("Example 3 - Minimal, with no Vertical Lines"); 474 | example.withAlignment(Aligned.RIGHT); 475 | example.withVerticalLines(false);//if false (default) then no vertical lines are shown 476 | System.out.println(example); 477 | 478 | System.out.println("Example with row lines"); 479 | ConsoleTable petTable = new ConsoleTable().withRowLines(true).withStyle(Styles.BASIC); 480 | petTable.setHeaders("Pet", "Age", "'Sex"); 481 | petTable.addRow("Ant", 1, "M"); 482 | petTable.addRow("Bat", 4, "F"); 483 | petTable.addRow("Cat", 10, "F"); 484 | petTable.addRow("Dog", 5, "M"); 485 | System.out.println(petTable); 486 | 487 | String[] petHeaders = { "Pet", "Age", "'Sex" }; 488 | Object[][] petData = { 489 | { "Cat", 10, "F" }, 490 | { "Dog", 5, "M"} 491 | }; 492 | ConsoleTable petShopTable = new ConsoleTable(petHeaders, petData).withStyle(Styles.BASIC); 493 | System.out.println(petShopTable); 494 | 495 | System.out.println("Example with a custom Style"); 496 | class DoubleStyle implements Style { 497 | String[][] tablePattern = new String[][] { 498 | /* LEFT COLDATA COLLINE RIGHT */ 499 | /* UMN */ 500 | /* TOP */ {"x", "=", "x", "x" }, 501 | /* HDRDATA */ {":", "H", ":", ":" }, 502 | /* HDRLINE*/ {"x", "-", "x", "x" }, 503 | /* ROWDATA */ {":", "D", ":", ":" }, 504 | /* ROWLINE*/ {":", "-", ".", ":" }, 505 | /* BOTTOM */ {"x", "=", "x", "x" } }; 506 | 507 | @Override 508 | public String getPattern(Row row, Column column) { 509 | if(tablePattern[row.ordinal()] == null) { 510 | return null; 511 | } else { 512 | return tablePattern[row.ordinal()][column.ordinal()]; 513 | } 514 | } 515 | 516 | public String[][] getTable() { 517 | return tablePattern; 518 | } 519 | } 520 | 521 | DoubleStyle doubleStyle = new DoubleStyle(); 522 | 523 | ConsoleTable six = new ConsoleTable((doubleStyle).getTable()).withStyle(doubleStyle); 524 | six.setHeaders(new ColumnFormat("Left",Aligned.CENTRE),new ColumnFormat("Cell",Aligned.CENTRE),new ColumnFormat("Line",Aligned.CENTRE),new ColumnFormat("Right",Aligned.CENTRE)); 525 | System.out.print(six); 526 | 527 | ConsoleTable styleDump = Styles.dumpStyle(doubleStyle); 528 | styleDump.withStyle(doubleStyle); 529 | System.out.print(styleDump); 530 | 531 | System.out.println("Example Alignment"); 532 | ConsoleTable alignmentsTable = new ConsoleTable().withStyle(Styles.BASIC); 533 | alignmentsTable.setHeaders("-Width", new ColumnFormat("Left",Aligned.LEFT), new ColumnFormat("Centre",Aligned.CENTRE), new ColumnFormat("Right",Aligned.RIGHT)); 534 | alignmentsTable.addRow("Short", "a", "1", "x"); 535 | alignmentsTable.addRow("Medium", "b2", "22", "yy"); 536 | alignmentsTable.addRow("Medium", "b23", "223", "yy3"); 537 | alignmentsTable.addRow("Medium", "b234", "2234", "yy34"); 538 | alignmentsTable.addRow("Longest", "c12345", "c234567", "z12345"); 539 | System.out.print(alignmentsTable); 540 | 541 | ConsoleTable styleExample = new ConsoleTable().withStyle(Styles.LIGHT); 542 | styleExample.setHeaders("Header", "Header"); 543 | styleExample.addRow("Cell","b2"); 544 | styleExample.addRow("a3", "b3"); 545 | System.out.print(styleExample); 546 | 547 | String[] zooHeaders = { "-Animal", "Cost", "'Sex" }; 548 | Object[][] zooData = { 549 | { "Cat", new java.math.BigDecimal("10.00"), "F" }, 550 | { "Zebra", new java.math.BigDecimal("5678.99"), "M"} 551 | }; 552 | 553 | ConsoleTable zooTable = new ConsoleTable(zooHeaders, zooData).withStyle(Styles.PLAIN).withColumnPadding("", ""); 554 | System.out.println(zooTable); 555 | 556 | 557 | } 558 | 559 | 560 | } -------------------------------------------------------------------------------- /src/com/johncsinclair/consoletable/Style.java: -------------------------------------------------------------------------------- 1 | package com.johncsinclair.consoletable; 2 | 3 | /** 4 | * Describe the Strings that style the appearance of the lines and junctions of a {@link ConsoleTable}. 5 | *
 6 |  *               LEFT   COLLINE   RIGHT 
 7 |  *                  COLDATA  COLDATA
 8 |  *    TOP         +--------+--------+   
 9 |  *       HDRDATA  | Header | Header |   
10 |  *    HDRLINE     +--------+--------+   
11 |  *       ROWDATA  |  Data  |  Data  |   
12 |  *    ROWLINE     +--------+--------+   
13 |  *       ROWDATA  |  Data  |  Data  |   
14 |  *    BOTTOM      +--------+--------+   
15 |  * 
16 | * 17 | * @author Copyright (c) John C Sinclair 2021 18 | * 19 | */ 20 | public interface Style { 21 | 22 | /** 23 | * The types of rows that style the appearance of a {@link ConsoleTable} 24 | */ 25 | public enum Row { TOP, HDRDATA, HDRLINE, ROWDATA, ROWLINE, BOTTOM }; // T HD HL RD RL B 26 | 27 | /** 28 | * The types of columns that style the appearance of a {@link ConsoleTable} 29 | */ 30 | public enum Column { LEFT, COLDATA, COLLINE, RIGHT }; // L CD CL R 31 | 32 | /** 33 | * return the String to display in the position in the table at the given row and column 34 | * 35 | * @param row 36 | * @param column 37 | * @return the string to display at the given position of the ConsoleTable style, or null if the row is not displayed for the style. 38 | */ 39 | public String getPattern(Row row, Column column); 40 | 41 | /** 42 | * return the String to display for either LEFT or RIGHT data column padding for this Style, by default a space. 43 | * 44 | * @param column 45 | * @return the string to display at the given side of each column for padding 46 | */ 47 | public default String getPadding(Column column) 48 | { 49 | return " "; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/com/johncsinclair/consoletable/Styles.java: -------------------------------------------------------------------------------- 1 | package com.johncsinclair.consoletable; 2 | 3 | import java.time.DayOfWeek; 4 | import java.time.format.TextStyle; 5 | import java.util.Locale; 6 | 7 | 8 | /** 9 | * Pre-configured {@link Style} for {@link ConsoleTable}. 10 | * 11 | * The Style of the table includes the outer border lines, the lines between the rows and the columns 12 | * 13 | * Example 14 | *
 15 |  * Pet Age
 16 |  * --- ---
 17 |  * Cat   5
 18 |  * Dog  10
 19 |  * 
20 | * 21 | * @author Copyright (c) John C Sinclair 2021 22 | */ 23 | 24 | public enum Styles implements Style { 25 | 26 | /** 27 | * A plain style with a header line. 28 | *

29 | * Example 30 | *

 31 |      * Pet Age
 32 |      * --- ---
 33 |      * Cat   5
 34 |      * Dog  10
 35 |      * 
36 | */ 37 | PLAIN ( 38 | new String[][] { 39 | null, 40 | {"" , "H", " ", "" }, 41 | {"" , "-", " ", "" }, 42 | {"" , "1", " ", "" }, 43 | null, 44 | null } 45 | ), 46 | /** 47 | * A minimal style with a dashed header line 48 | *

49 | * Example 50 | *

 51 |      * Pet Age
 52 |      * --- ---
 53 |      * Cat   5
 54 |      * Dog  10
 55 |      * 
56 | */ 57 | SQL ( 58 | new String[][] { 59 | null, 60 | {"" , "H", " ", "" }, 61 | {"" , "-", " ", "" }, 62 | {"" , "1", " ", "" }, 63 | null, 64 | null, 65 | {"" ,null,null, "" } } 66 | ), 67 | /** 68 | * A minimal table with no borders and no cell padding 69 | *

70 | * Example 71 | *

 72 |      * Pet Age
 73 |      * Cat   5
 74 |      * Dog  10
 75 |      * 
76 | */ 77 | MINIMAL ( 78 | new String[][] { 79 | null, 80 | {"" , "H", " ", "" }, 81 | null, 82 | {"" , "1", " ", "" }, 83 | null, 84 | null, 85 | {"" ,null,null, "" } } 86 | ), 87 | 88 | /** 89 | * A minimal table with no vertical lines 90 | */ 91 | NO_LINES ( 92 | new String[][] { 93 | /* TOP */ null, 94 | /* HDRDATA*/ {"" , "H", "", "" }, 95 | /* HDRLINE*/ null, 96 | /* ROWDATA*/ {"" , "1", "", "" }, 97 | /* ROWLINE*/ null, 98 | /* BOTTOM */ null } 99 | ), 100 | 101 | /** 102 | * A simple border. 103 | */ 104 | BORDER ( 105 | new String[][] { 106 | /* LEFT COLDATA COLLINE RIGHT */ 107 | /* TOP */ {"┌─", "─", "─", "─┐"}, 108 | /* HDRDATA*/ {"│ ", "H", " ", " │"}, 109 | /* HDRLINE*/ {"│ ", "─", " ", " │"}, 110 | /* ROWDATA*/ {"│ ", "1", " ", " │"}, 111 | /* ROWLINE*/ {"│ ", " ", " ", " │"}, 112 | /* BOTTOM */ {"└─", "─", "─", "─┘"}, 113 | /* PADDING*/ {"" ,null,null, "" } } 114 | ), 115 | 116 | /** 117 | * For a paper in a scientific research journal. No vertical lines. 118 | * 119 | * @see APA Style - Table Setup - American Psychological Association 120 | */ 121 | SCIENTIFIC ( 122 | new String[][] { 123 | /* LEFT COLDATA COLLINE RIGHT */ 124 | /* TOP */ {"─", "─", "─", "─"}, 125 | /* HDRDATA*/ {" ", "H", " ", " "}, 126 | /* HDRLINE*/ {"─", "─", "─", "─"}, 127 | /* ROWDATA*/ {" ", "D", " ", " "}, 128 | /* ROWLINE*/ {" ", " ", " ", " "}, 129 | /* BOTTOM */ {"─", "─", "─", "─"} } 130 | ), 131 | /** 132 | * javadoc testing2 133 | */ 134 | BASIC ( 135 | new String[][] { 136 | /* LEFT COLDATA COLLINE RIGHT */ 137 | /* TOP */ {"+", "-", "+", "+"}, 138 | /* HDRDATA*/ {"|", "H", "|", "|"}, 139 | /* HDRLINE*/ {"+", "-", "+", "+"}, 140 | /* ROWDATA*/ {"|", "1", "|", "|"}, 141 | /* ROWLINE*/ {"+", "-", "+", "+"}, 142 | /* BOTTOM */ {"+", "-", "+", "+"} } 143 | ), 144 | 145 | /** 146 | * A simple table. 147 | *

148 | * Example 149 | *

150 | 	 * .─┬─.	
151 |      * │H│H│
152 |      * ├─+─┤
153 |      * │1│2│
154 |      * ├─+─┤
155 |      * │3│4│
156 |      * .─┴─.
157 |      *	
158 | */ 159 | SIMPLE ( 160 | new String[][] { 161 | /* LEFT COLDATA COLLINE RIGHT */ 162 | /* TOP */ {"·", "-", "-", "·"}, 163 | /* HDRDATA*/ {"|", "H", "|", "|"}, 164 | /* HDRLINE*/ {"|", "-", "+", "|"}, 165 | /* ROWDATA*/ {"|", "1", "|", "|"}, 166 | /* ROWLINE*/ {"|", "-", "+", "|"}, 167 | /* BOTTOM */ {"·", "-", "-", "·"} } 168 | ), 169 | 170 | // BULLETS ( 171 | // new String[][] { 172 | // /* LEFT COLDATA COLLINE RIGHT */ 173 | // /* TOP */ {"•", " • ", "•", "•"}, 174 | // /* HDRDATA*/ {"•", "H", "•", "•"}, 175 | // /* HDRLINE*/ {"•", " • ", "•", "•"}, 176 | // /* ROWDATA*/ {"•", "1", "•", "•"}, 177 | // /* ROWLINE*/ {"•", " • ", "•", "•"}, 178 | // /* BOTTOM */ {"•", " • ", "•", "•"} } 179 | // ), 180 | 181 | DOTS ( 182 | new String[][] { 183 | /* LEFT COLDATA COLLINE RIGHT */ 184 | /* TOP */ {".", ".", ".", "."}, 185 | /* HDRDATA*/ {":", "H", ":", ":"}, 186 | /* HDRLINE*/ {":", ".", ":", ":"}, 187 | /* ROWDATA*/ {":", "1", ":", ":"}, 188 | /* ROWLINE*/ {":", ".", ":", ":"}, 189 | /* BOTTOM */ {":", ".", ":", ":"} } 190 | ), 191 | 192 | // U+00B7 · MIDDLE DOT · is not a full stop . 193 | MIDDLE_DOTS ( 194 | new String[][] { 195 | /* LEFT COLDATA COLLINE RIGHT */ 196 | /* TOP */ {"·", "·", "·", "·"}, 197 | /* HDRDATA*/ {":", "H", ":", ":"}, 198 | /* HDRLINE*/ {":", "·", ":", ":"}, 199 | /* ROWDATA*/ {":", "1", ":", ":"}, 200 | /* ROWLINE*/ {":", "·", ":", ":"}, 201 | /* BOTTOM */ {"·", "·", "·", "·"} } 202 | ), 203 | 204 | DASHES ( 205 | new String[][] { 206 | /* LEFT COLDATA COLLINE RIGHT */ 207 | /* TOP */ {" ", "_", " ", " "}, 208 | /* HDRDATA*/ {"|", "H", "|", "|"}, 209 | /* HDRLINE*/ {"|", "_", "|", "|"}, 210 | /* ROWDATA*/ {"|", "1", "|", "|"}, 211 | /* ROWLINE*/ {"|", "_", "|", "|"}, 212 | /* BOTTOM */ {"|", "_", "|", "|"} } 213 | ), 214 | 215 | STARS ( 216 | new String[][] { 217 | /* LEFT COLDATA COLLINE RIGHT */ 218 | /* TOP */ {"*", ".", "*", "*"}, 219 | /* HDRDATA*/ {":", "H", ":", ":"}, 220 | /* HDRLINE*/ {":", ".", "*", ":"}, 221 | /* ROWDATA*/ {":", "1", ":", ":"}, 222 | /* ROWLINE*/ {":", ".", "*", ":"}, 223 | /* BOTTOM */ {"*", ".", "*", "*"} } 224 | ), 225 | 226 | /** 227 | * A light continuous border using Unicode box drawing characters. 228 | *
229 | 	 * 	┌─┬┐	
230 |      *	│H││
231 |      *	├─┼┤
232 |      *	│D││
233 |      *	├─┼┤
234 |      *	└─┴┘
235 |      *	
236 | */ 237 | LIGHT ( 238 | new String[][] { 239 | /* LEFT COLDATA COLLINE RIGHT */ 240 | /* TOP */ {"┌", "─", "┬", "┐"}, 241 | /* HDRDATA*/ {"│", "H", "│", "│"}, 242 | /* HDRLINE*/ {"├", "─", "┼", "┤"}, 243 | /* ROWDATA*/ {"│", "1", "│", "│"}, 244 | /* ROWLINE*/ {"├", "─", "┼", "┤"}, 245 | /* BOTTOM */ {"└", "─", "┴", "┘"} } 246 | ), 247 | 248 | DASHED ( new String[][] { 249 | /* LEFT COLDATA COLLINE RIGHT */ 250 | /* TOP */ {"┌", "╌", "┬", "┐"}, 251 | /* HDRDATA*/ {"┆", "H", "┆", "┆"}, 252 | /* HDRLINE*/ {"┆", "╌", "┆", "┆"}, 253 | /* ROWDATA*/ {"┆", "1", "┆", "┆"}, 254 | /* ROWLINE*/ {"┆", "╌", "┆", "┆"}, 255 | /* BOTTOM */ {"└", "╌", "┴", "┘"} } 256 | ), 257 | 258 | ROUNDED ( new String[][] { 259 | /* LEFT COLDATA COLLINE RIGHT */ 260 | /* TOP */ {"╭", "─", "┬", "╮"}, 261 | /* HDRDATA*/ {"│", "H", "│", "│"}, 262 | /* HDRLINE*/ {"├", "─", "┼", "┤"}, 263 | /* ROWDATA*/ {"│", "1", "│", "│"}, 264 | /* ROWLINE*/ {"├", "─", "┼", "┤"}, 265 | /* BOTTOM */ {"╰", "─", "┴", "╯"} } 266 | ), 267 | 268 | HEAVY ( new String[][] { 269 | /* LEFT COLDATA COLLINE RIGHT */ 270 | /* TOP */ {"┏", "━", "┳", "┓"}, 271 | /* HDRDATA*/ {"┃", "H", "┃", "┃"}, 272 | /* HDRLINE*/ {"┣", "━", "╋", "┫"}, 273 | /* ROWDATA*/ {"┃", "1", "┃", "┃"}, 274 | /* ROWLINE*/ {"┣", "━", "╋", "┫"}, 275 | /* BOTTOM */ {"┗", "━", "┻", "┛"} } 276 | ), 277 | 278 | HEAVY_BORDER ( new String[][] { 279 | /* LEFT COLDATA COLLINE RIGHT */ 280 | /* TOP */ {"┏", "━", "┯", "┓"}, 281 | /* HDRDATA*/ {"┃", "H", "│", "┃"}, 282 | /* HDRLINE*/ {"┣", "━", "┿", "┫"}, 283 | /* ROWDATA*/ {"┃", "1", "│", "┃"}, 284 | /* ROWLINE*/ {"┠", "─", "┼", "┨"}, 285 | /* BOTTOM */ {"┗", "━", "┷", "┛"} } 286 | ), 287 | 288 | LIGHT_HEADER ( new String[][] { 289 | /* LEFT COLDATA COLLINE RIGHT */ 290 | /* TOP */ {"┏", "━", "┯", "┓"}, 291 | /* HDRDATA*/ {"┃", "H", "│", "┃"}, 292 | /* HDRLINE*/ {"┠", "─", "┼", "┨"}, 293 | /* ROWDATA*/ {"┃", "1", "│", "┃"}, 294 | /* ROWLINE*/ {"┠", "─", "┼", "┨"}, 295 | /* BOTTOM */ {"┗", "━", "┷", "┛"} } 296 | ), 297 | 298 | DOUBLE_BORDER ( new String[][] { 299 | /* LEFT COLDATA COLLINE RIGHT */ 300 | /* TOP */ {"╔", "═", "╤", "╗"}, 301 | /* HDRDATA*/ {"║", "H", "│", "║"}, 302 | /* HDRLINE*/ {"╠", "═", "╪", "╣"}, 303 | /* ROWDATA*/ {"║", "1", "│", "║"}, 304 | /* ROWLINE*/ {"╟", "─", "┼", "╢"}, 305 | /* BOTTOM */ {"╚", "═", "╧", "╝"} } 306 | ), 307 | 308 | DOUBLED ( new String[][] { 309 | /* LEFT COLDATA COLLINE RIGHT */ 310 | /* TOP */ {"┌", "─", "┐┌", "┐"}, 311 | /* HDRDATA*/ {"│", "H", "││", "│"}, 312 | /* HDRLINE*/ {"├", "─", "┤├", "┤"}, 313 | /* ROWDATA*/ {"│", "1", "││", "│"}, 314 | /* ROWLINE*/ {"├", "─", "┤├", "┤"}, 315 | /* BOTTOM */ {"└", "─", "┘└", "┘"} } 316 | ), 317 | 318 | COMPACT ( new String[]{ 319 | "┌─┬┐", // T 320 | "│H││", // HD 321 | "├─┼┤", // HL 322 | "│D││", // RD 323 | "├─┼┤", // RL 324 | "└─┴┘"} // B 325 | ); 326 | 327 | 328 | private String[][] tablePattern; 329 | 330 | private Styles (String[][] tablePattern){ 331 | this.tablePattern = tablePattern; 332 | } 333 | 334 | private Styles (String[] compactPattern){ 335 | String[][] pattern = new String[Row.values().length][Column.values().length]; 336 | for(Row row : Row.values()) { 337 | // TODO - handle null row 338 | for(Column column : Column.values()) { 339 | pattern[row.ordinal()][column.ordinal()] = String.valueOf( compactPattern[row.ordinal()].charAt(column.ordinal()) ); 340 | } 341 | } 342 | this.tablePattern = pattern; 343 | } 344 | 345 | @Override 346 | public String getPattern(Row row, Column column) { 347 | 348 | // TODO if row.ordinal() > tablePattern[].length throw a helpful exception for a Style developer. ( or col.ordinal() ) 349 | 350 | if(tablePattern[row.ordinal()] == null) { 351 | return null; 352 | } else { 353 | return tablePattern[row.ordinal()][column.ordinal()]; 354 | } 355 | } 356 | 357 | /** 358 | * return the String to display for either LEFT or RIGHT column padding for this Style 359 | * 360 | * @param row 361 | * @param column 362 | * @return null if the row is not displayed, otherwise the string to display at the given position of the ConsoleTable style 363 | */ 364 | @Override 365 | public String getPadding(Column column) 366 | { 367 | if(column == Column.LEFT || column == Column.RIGHT) { 368 | if( tablePattern.length > Row.BOTTOM.ordinal()+1) { 369 | return tablePattern[Row.BOTTOM.ordinal()+1][column.ordinal()]; 370 | } 371 | else { 372 | return " "; 373 | } 374 | } 375 | else { 376 | throw new IllegalArgumentException(); 377 | } 378 | } 379 | 380 | 381 | 382 | 383 | public static void main(String[] args) { 384 | 385 | ConsoleTable petTable = new ConsoleTable(); 386 | petTable.setHeaders("Pet", "Age", "'Sex"); 387 | petTable.addRow("Ant", 1, "M"); 388 | petTable.addRow("Bat", 4, "F"); 389 | petTable.addRow("Cat", 10, "F"); 390 | petTable.addRow("Dog", 5, "M"); 391 | 392 | System.out.println("Example of all Styles"); 393 | petTable.withVerticalLines(true); 394 | petTable.withColumnPadding("", ""); 395 | for(Styles sample : Styles.values()) { 396 | System.out.printf("withStyle(Styles.%s)\n",sample.toString()); 397 | System.out.print(petTable.withStyle(sample)); 398 | System.out.println(); 399 | } 400 | 401 | 402 | 403 | 404 | for(Style sample : Styles.values()) { 405 | ConsoleTable sampleDump = dumpStyle(sample); 406 | sampleDump.withStyle(sample); 407 | System.out.format("withStyle(Styles.%s)\n",sample.toString()); 408 | System.out.print(sampleDump); 409 | System.out.println(); 410 | } 411 | 412 | 413 | ConsoleTable tinySample = new ConsoleTable().withRowLines(false); 414 | tinySample.setHeaders("H", "H"); 415 | tinySample.addRow(1, "a"); 416 | tinySample.addRow(2, "b"); 417 | 418 | 419 | System.out.println("Example of all Styles"); 420 | for(Styles sample : Styles.values()) { 421 | System.out.printf("withStyle(Styles.%s)\n",sample.toString()); 422 | System.out.print(tinySample.withStyle(sample)); 423 | System.out.println(); 424 | } 425 | 426 | for(Styles sample : Styles.values()) { 427 | System.out.printf("withStyle(Styles.%s)\n",sample.toString()); 428 | ConsoleTable dayTable = new ConsoleTable(); 429 | dayTable.setHeaders("Enum","English", "French", "German"); 430 | for( DayOfWeek dayOfWeek : DayOfWeek.values()) { 431 | dayTable.addRow( dayOfWeek, dayOfWeek.getDisplayName(TextStyle.FULL, Locale.ENGLISH), 432 | dayOfWeek.getDisplayName(TextStyle.FULL, Locale.FRENCH), dayOfWeek.getDisplayName(TextStyle.FULL, Locale.GERMAN), 433 | (dayOfWeek.getValue()-1)*20); 434 | } 435 | System.out.print(dayTable.withStyle(sample)); 436 | System.out.println(); 437 | } 438 | 439 | } 440 | 441 | /** 442 | * return a {@link ConsoleTable} illustrating the rows and columns that make up the given {@link Style}. 443 | * 444 | * @param dumpStyle 445 | * @return 446 | */ 447 | public static ConsoleTable dumpStyle(Style dumpStyle) { 448 | 449 | String padding = null; 450 | String leftPadding = dumpStyle.getPadding(Column.LEFT); 451 | String rightPadding = dumpStyle.getPadding(Column.RIGHT); 452 | if( ! ( leftPadding.equals(" ") && rightPadding.equals(" ") ) ) { 453 | padding = "padding('"+leftPadding+"','"+rightPadding+"')"; 454 | System.out.println(padding); 455 | } 456 | 457 | String[] styleHeaders = new String[Column.values().length+1]; 458 | String[][] styleData = new String[Row.values().length][Column.values().length+1]; 459 | 460 | for( Column column : Column.values() ) { 461 | styleHeaders[column.ordinal()+1] = "'"+column.name(); 462 | } 463 | for( Row row : Row.values() ) { 464 | styleData[row.ordinal()][0] = row.name(); 465 | for( Column column : Column.values() ) { 466 | String pattern = dumpStyle.getPattern(row, column); 467 | if(pattern == null) { 468 | if(column == Column.LEFT) { 469 | pattern = "null"; 470 | } 471 | else { 472 | pattern = ""; 473 | } 474 | } 475 | else if( pattern.equals("")) { 476 | pattern = "''"; 477 | } 478 | else if( !pattern.trim().equals(pattern)) { 479 | pattern = "'" + pattern +"'"; 480 | } 481 | styleData[row.ordinal()][column.ordinal()+1] = pattern; 482 | } 483 | } 484 | return new ConsoleTable(styleHeaders, styleData); 485 | } 486 | 487 | } -------------------------------------------------------------------------------- /test/com/johncsinclair/consoletable/ConsoleTableTest.java: -------------------------------------------------------------------------------- 1 | package com.johncsinclair.consoletable; 2 | 3 | import static org.junit.jupiter.api.Assertions.*; 4 | 5 | import java.math.BigDecimal; 6 | import java.time.LocalDate; 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | import org.junit.jupiter.api.Test; 11 | 12 | import com.johncsinclair.consoletable.ColumnFormat.Aligned; 13 | 14 | 15 | /** 16 | * JUnit 5 Tests for ConsoleTable 17 | * 18 | * To display monospaced box-drawing characters, In Eclipse, in Run | Run Configurations... , in the Common tab, set Encoding to Other UTF-8, 19 | * or set the Project | Properties | Text File Encoding to UTF-8, so that it is inherited automatically by the Run Configurations. 20 | * 21 | * @see How to make eclipse junit stacktrace use non-proportional font? 22 | * 23 | * @author Copyright (c) John C Sinclair 2021 24 | */ 25 | 26 | class ConsoleTableTest { 27 | 28 | @Test 29 | void testExample1() { 30 | ConsoleTable table = new ConsoleTable(); 31 | table.setHeaders("One", "Two", "Three"); //optional - if not used then there will be no header and horizontal lines 32 | table.addRow(new java.math.BigDecimal("98.76"), "broccoli", "flexible"); 33 | table.addRow(new java.math.BigDecimal("1.23"), "announcement", "reflection"); 34 | table.addRow(new java.math.BigDecimal("1234.45"), null, java.time.LocalDateTime.parse("2020-12-24T14:15:16.987") ); 35 | table.addRow( java.math.BigDecimal.ZERO, "pleasant", "wild"); 36 | 37 | String expected = 38 | "┌─────────┬──────────────┬─────────────────────────┐"+"\n"+ 39 | "│ One │ Two │ Three │"+"\n"+ 40 | "├─────────┼──────────────┼─────────────────────────┤"+"\n"+ 41 | "│ 98.76 │ broccoli │ flexible │"+"\n"+ 42 | "│ 1.23 │ announcement │ reflection │"+"\n"+ 43 | "│ 1234.45 │ │ 2020-12-24T14:15:16.987 │"+"\n"+ 44 | "│ 0 │ pleasant │ wild │"+"\n"+ 45 | "└─────────┴──────────────┴─────────────────────────┘"+"\n"; 46 | assertLinesMatch( Arrays.asList(expected.split("\n")), 47 | Arrays.asList(table.toString().split("\n")), "four"); 48 | } 49 | 50 | @Test 51 | void testStylePositions() { 52 | class UniqueStyle implements Style { 53 | // a different character in every position to check each is correct 54 | String[][] tablePattern = new String[][] { 55 | /* LEFT CD CL RIGHT */ 56 | /* TOP */ {"┌", "^", "T", "┐"}, 57 | /* HDRDATA */ {"1", "F", ":", "H"}, 58 | /* HDRLINE*/ {"J", "-", "+", "M"}, 59 | /* ROWDATA */ {"l", "O", "!", "r"}, 60 | /* ROWLINE*/ {"R", ".", "U", "V"}, 61 | /* BOTTOM */ {"└", "v", "t", "┘"} 62 | }; 63 | 64 | @Override 65 | public String getPattern(Row row, Column column) { 66 | if(tablePattern[row.ordinal()] == null) { 67 | return null; 68 | } else { 69 | return tablePattern[row.ordinal()][column.ordinal()]; 70 | } 71 | } 72 | } 73 | Style testStyle = new UniqueStyle(); 74 | ConsoleTable table = new ConsoleTable().withStyle(testStyle); 75 | table.setHeaders("One", "Two", "Three");//optional - if not used then there will be no header and horizontal lines 76 | table.addRow(new java.math.BigDecimal("98.76"), "broccoli", "flexible"); 77 | table.addRow(new java.math.BigDecimal("1.23"), "announcement", "reflection"); 78 | table.addRow(new java.math.BigDecimal("1234.45"), null, java.time.LocalDateTime.parse("2020-12-24T14:15:16.987") ); 79 | table.addRow( java.math.BigDecimal.ZERO, "pleasant", "wild"); 80 | 81 | String expected = 82 | "┌^^^^^^^^^T^^^^^^^^^^^^^^T^^^^^^^^^^^^^^^^^^^^^^^^^┐"+"\n"+ 83 | "1 One : Two : Three H"+"\n"+ 84 | "J---------+--------------+-------------------------M"+"\n"+ 85 | "l 98.76 ! broccoli ! flexible r"+"\n"+ 86 | "l 1.23 ! announcement ! reflection r"+"\n"+ 87 | "l 1234.45 ! ! 2020-12-24T14:15:16.987 r"+"\n"+ 88 | "l 0 ! pleasant ! wild r"+"\n"+ 89 | "└vvvvvvvvvtvvvvvvvvvvvvvvtvvvvvvvvvvvvvvvvvvvvvvvvv┘"+"\n"; 90 | 91 | assertLinesMatch( Arrays.asList(expected.split("\n")), 92 | Arrays.asList(table.toString().split("\n")), "four"); 93 | } 94 | 95 | @Test 96 | void testWideStylePositions() { 97 | class UniqueWide implements Style { 98 | // a different multi character String in every position to check each is correct 99 | String[][] tablePattern = new String[][] { 100 | /* LEFT COLDATA COLLINE RIGHT */ 101 | /* UMN */ 102 | /* TOP */ {"x┌", "^-", "wT", "z┐"}, 103 | /* HDRDATA */ {"x1", "HD", "w:", "zH"}, 104 | /* HDRLINE*/ {"xJ", "-─", "w+", "zM"}, 105 | /* ROWDATA */ {"xl", "DA", "w!", "zr"}, 106 | /* ROWLINE*/ {"xR", ".,", "wU", "zV"}, 107 | /* BOTTOM */ {"x└", "v-", "wt", "z┘"} 108 | }; 109 | 110 | @Override 111 | public String getPattern(Row row, Column column) { 112 | if(tablePattern[row.ordinal()] == null) { 113 | return null; 114 | } else { 115 | return tablePattern[row.ordinal()][column.ordinal()]; 116 | } 117 | } 118 | } 119 | Style uniqueWide = new UniqueWide(); 120 | ConsoleTable table = new ConsoleTable().withStyle(uniqueWide); 121 | table.setHeaders("One", "Two", "Three");//optional - if not used then there will be no header and horizontal lines 122 | table.addRow(new java.math.BigDecimal("98.76"), "broccoli", "flexible"); 123 | table.addRow(new java.math.BigDecimal("1.23"), "announcement", "reflection"); 124 | table.addRow(new java.math.BigDecimal("1234.45"), null, java.time.LocalDateTime.parse("2020-12-24T14:15:16.987") ); 125 | table.addRow( java.math.BigDecimal.ZERO, "pleasant", "wild"); 126 | 127 | String expected = 128 | "x┌^-^-^-^-^wT^-^-^-^-^-^-^-wT^-^-^-^-^-^-^-^-^-^-^-^-^z┐"+"\n"+ 129 | "x1 One w: Two w: Three zH"+"\n"+ 130 | "xJ-─-─-─-─-w+-─-─-─-─-─-─-─w+-─-─-─-─-─-─-─-─-─-─-─-─-zM"+"\n"+ 131 | "xl 98.76 w! broccoli w! flexible zr"+"\n"+ 132 | "xl 1.23 w! announcement w! reflection zr"+"\n"+ 133 | "xl 1234.45 w! w! 2020-12-24T14:15:16.987 zr"+"\n"+ 134 | "xl 0 w! pleasant w! wild zr"+"\n"+ 135 | "x└v-v-v-v-vwtv-v-v-v-v-v-v-wtv-v-v-v-v-v-v-v-v-v-v-v-vz┘"+"\n"; 136 | 137 | assertLinesMatch( Arrays.asList(expected.split("\n")), 138 | Arrays.asList(table.toString().split("\n")), "four"); 139 | } 140 | 141 | @Test 142 | void testCompactStyle() { 143 | /** a compact implementation of {@link Style} for single character lines and junctions 144 | * 145 | * @author John Sinclair 2020-02-06 146 | */ 147 | class CompactLight implements Style { 148 | String[] compactPattern = { 149 | "┌─┬┐", // T 150 | "│H││", // HD 151 | "├─┼┤", // HL 152 | "│D││", // RD 153 | "├─┼┤", // RL 154 | "└─┴┘"};// B 155 | 156 | @Override 157 | public String getPattern(Row row, Column column) { 158 | if(compactPattern[row.ordinal()] != null) { 159 | return String.valueOf( compactPattern[ row.ordinal() ].charAt( column.ordinal() ) ); 160 | } 161 | return null; 162 | 163 | } 164 | } 165 | 166 | ConsoleTable table = new ConsoleTable().withRowLines(); 167 | table.setHeaders("One", "Two", "Three"); 168 | table.addRow(new java.math.BigDecimal("98.76"), "broccoli", "flexible"); 169 | table.addRow(new java.math.BigDecimal("1.23"), "announcement", "reflection"); 170 | table.addRow(new java.math.BigDecimal("1234.45"), null, java.time.LocalDateTime.parse("2020-12-24T14:15:16.987") ); 171 | table.addRow( java.math.BigDecimal.ZERO, "pleasant", "wild"); 172 | 173 | String actual = table.withStyle(new CompactLight()).toString(); 174 | String expected = table.withStyle(Styles.LIGHT).toString(); 175 | 176 | assertLinesMatch( Arrays.asList(expected.split("\n")), 177 | Arrays.asList(actual.split("\n")), "CompactLight Style"); 178 | } 179 | 180 | @Test 181 | void testNull() { 182 | ConsoleTable emptyTable = new ConsoleTable().withStyle(Styles.HEAVY_BORDER); 183 | 184 | assertEquals( 185 | "┏┓\n"+ 186 | "┗┛\n", emptyTable.toString()); 187 | } 188 | 189 | @Test 190 | void testNullHeaders() { 191 | ConsoleTable emptyTable = new ConsoleTable().withStyle(Styles.HEAVY); 192 | emptyTable.setHeaders(null); 193 | 194 | assertEquals( 195 | "┏┓"+"\n"+ 196 | "┗┛"+"\n", emptyTable.toString()); 197 | } 198 | 199 | @Test 200 | void testNullHeadersCast() { 201 | ConsoleTable emptyTable = new ConsoleTable().withStyle(Styles.LIGHT); 202 | emptyTable.setHeaders((Object[])null); 203 | 204 | assertEquals( 205 | "┌┐"+"\n"+ 206 | "└┘"+"\n", emptyTable.toString()); 207 | } 208 | 209 | @Test 210 | void testTwoNullHeaders() { 211 | ConsoleTable emptyTable = new ConsoleTable().withStyle(Styles.BASIC).withColumnPadding(""); 212 | emptyTable.setHeaders((Object[])null, (Object[])null); 213 | 214 | assertEquals( 215 | "+++"+"\n"+ 216 | "|||"+"\n"+ 217 | "+++"+"\n"+ 218 | "+++"+"\n", emptyTable.toString()); 219 | } 220 | 221 | @Test 222 | void testEmptyHeaders() { 223 | ConsoleTable emptyTable = new ConsoleTable().withStyle(Styles.HEAVY_BORDER); 224 | emptyTable.setHeaders(); 225 | 226 | assertEquals( 227 | "┏┓"+"\n"+ 228 | "┗┛"+"\n", emptyTable.toString()); 229 | } 230 | 231 | @Test 232 | void testNullRow() { 233 | ConsoleTable emptyTable = new ConsoleTable().withStyle(Styles.HEAVY_BORDER).withColumnPadding("","");; 234 | emptyTable.addRow(); 235 | 236 | assertEquals( 237 | "┏┓"+"\n"+ 238 | "┃┃"+"\n"+ 239 | "┗┛"+"\n", emptyTable.toString()); 240 | } 241 | 242 | @Test 243 | void testNullColumnRow() { 244 | ConsoleTable table = new ConsoleTable().withStyle(Styles.BASIC).withColumnPadding("",""); 245 | table.addRow("a",null,"pq"); 246 | table.addRow("b","", "rs"); 247 | 248 | // if a column has no heading and no data then max width is 0 and it should have a width of 0 249 | String expected = 250 | "+-++--+"+"\n"+ 251 | "|a||pq|"+"\n"+ 252 | "|b||rs|"+"\n"+ 253 | "+-++--+"+"\n"; 254 | assertLinesMatch( Arrays.asList(expected.split("\n")), 255 | Arrays.asList(table.toString().split("\n")), "if a column has no heading and no data then max width is 0 and it should have a width of 0"); 256 | 257 | } 258 | 259 | @Test 260 | void testAddRowEmpty() { 261 | ConsoleTable table = new ConsoleTable().withStyle(Styles.LIGHT).withColumnPadding(""); 262 | table.addRow(); 263 | 264 | String expected = 265 | "┌┐"+"\n"+ 266 | "││"+"\n"+ 267 | "└┘"+"\n"; 268 | assertLinesMatch( Arrays.asList(expected.split("\n")), 269 | Arrays.asList(table.toString().split("\n")), "if a column has no heading and no data then max width is 0 and it should have a width of 0"); 270 | 271 | } 272 | 273 | @Test 274 | void testAddRowNullCastObjectArray() { 275 | ConsoleTable table = new ConsoleTable().withStyle(Styles.LIGHT).withColumnPadding(""); 276 | table.addRow( (Object[])null ); 277 | 278 | String expected = 279 | "┌┐"+"\n"+ 280 | "││"+"\n"+ 281 | "└┘"+"\n"; 282 | 283 | assertLinesMatch( Arrays.asList(expected.split("\n")), 284 | Arrays.asList(table.toString().split("\n")), "if a column has no heading and no data then max width is 0 and it should have a width of 0"); 285 | } 286 | 287 | @Test 288 | void testAddRowNullCastObject() { 289 | ConsoleTable table = new ConsoleTable().withStyle(Styles.LIGHT).withColumnPadding(""); 290 | table.addRow( (Object)null ); 291 | 292 | String expected = 293 | "┌┐"+"\n"+ 294 | "││"+"\n"+ 295 | "└┘"+"\n"; 296 | 297 | assertLinesMatch( Arrays.asList(expected.split("\n")), 298 | Arrays.asList(table.toString().split("\n")), "if a column has no heading and no data then max width is 0 and it should have a width of 0"); 299 | } 300 | 301 | @Test 302 | void testAddRowNullCastList() { 303 | ConsoleTable table = new ConsoleTable().withStyle(Styles.LIGHT).withColumnPadding(""); 304 | table.addRow( (List)null ); 305 | 306 | String expected = 307 | "┌┐"+"\n"+ 308 | "││"+"\n"+ 309 | "└┘"+"\n"; 310 | 311 | assertLinesMatch( Arrays.asList(expected.split("\n")), 312 | Arrays.asList(table.toString().split("\n")), "if a column has no heading and no data then max width is 0 and it should have a width of 0"); 313 | } 314 | 315 | @Test 316 | void testAddRowEmptyString() { 317 | ConsoleTable table = new ConsoleTable().withStyle(Styles.LIGHT).withColumnPadding(""); 318 | table.addRow( "" ); 319 | 320 | String expected = 321 | "┌┐"+"\n"+ 322 | "││"+"\n"+ 323 | "└┘"+"\n"; 324 | 325 | assertLinesMatch( Arrays.asList(expected.split("\n")), 326 | Arrays.asList(table.toString().split("\n")), "if a column has no heading and no data then max width is 0 and it should have a width of 0"); 327 | } 328 | 329 | @Test 330 | void testOneHeader() { 331 | ConsoleTable table = new ConsoleTable().withStyle(Styles.HEAVY_BORDER); 332 | table.setHeaders("hdr"); 333 | 334 | assertEquals( 335 | "┏━━━━━┓"+"\n"+ 336 | "┃ hdr ┃"+"\n"+ 337 | "┣━━━━━┫"+"\n"+ 338 | "┗━━━━━┛"+"\n", table.toString()); 339 | } 340 | 341 | @Test 342 | void testOneByOne() { 343 | ConsoleTable table = new ConsoleTable().withStyle(Styles.HEAVY_BORDER); 344 | table.addRow("1x1"); 345 | 346 | assertEquals( 347 | "┏━━━━━┓"+"\n"+ 348 | "┃ 1x1 ┃"+"\n"+ 349 | "┗━━━━━┛"+"\n", table.toString()); 350 | } 351 | 352 | @Test 353 | void testTwoByTwo() { 354 | ConsoleTable twoByTwo = new ConsoleTable().withStyle(Styles.HEAVY_BORDER).withColumnPadding(",.",".,"); 355 | twoByTwo.addRow("1a", "1b"); 356 | twoByTwo.addRow("2a", "2b"); 357 | 358 | assertEquals( 359 | "┏━━━━━━┯━━━━━━┓"+"\n"+ 360 | "┃,.1a.,│,.1b.,┃"+"\n"+ 361 | "┃,.2a.,│,.2b.,┃"+"\n"+ 362 | "┗━━━━━━┷━━━━━━┛"+"\n", 363 | twoByTwo.toString(), "TwoByTwo with double padding"); 364 | 365 | 366 | // MyAssert.assertLinesEqual( 367 | String expected = 368 | "┏━━━━━━┯━━━━━━┓"+"\n"+ 369 | "┃,.1a.,│,.1b.,┃"+"\n"+ 370 | "┃,.2a.,│,.2b.,┃"+"\n"+ 371 | "┗━━━━━━┷━━━━━━┛"+"\n"; 372 | 373 | assertLinesMatch( Arrays.asList(expected.split("\n")), 374 | Arrays.asList(twoByTwo.toString().split("\n")), "TwoByTwo with double padding"); 375 | 376 | } 377 | 378 | @Test 379 | void testThreeByThree() { 380 | ConsoleTable threeByThree = new ConsoleTable().withStyle(Styles.HEAVY_BORDER); 381 | threeByThree.setHeaders("a1"); 382 | threeByThree.addRow("a2","b2"); 383 | threeByThree.addRow("a3", "b3", "c3"); 384 | 385 | String expected = 386 | "┏━━━━┯━━━━┯━━━━┓"+"\n"+ 387 | "┃ a1 │ │ ┃"+"\n"+ 388 | "┣━━━━┿━━━━┿━━━━┫"+"\n"+ 389 | "┃ a2 │ b2 │ ┃"+"\n"+ 390 | "┃ a3 │ b3 │ c3 ┃"+"\n"+ 391 | "┗━━━━┷━━━━┷━━━━┛"+"\n"; 392 | 393 | assertLinesMatch( Arrays.asList(expected.split("\n")), 394 | Arrays.asList(threeByThree.toString().split("\n")), "test three by three with empty headers and cells"); 395 | 396 | } 397 | 398 | @Test 399 | void testFourByFourAndColumnFormat() { 400 | ConsoleTable four = new ConsoleTable().withStyle(Styles.HEAVY_BORDER); 401 | four.setHeaders("a1", new ColumnFormat("a2", Aligned.LEFT), "a3"); 402 | four.addRow("a2", "b2"); 403 | four.addRow(); 404 | four.addRow("widea3", "wideb3", "widec3"); 405 | 406 | String expected = 407 | "┏━━━━━━━━┯━━━━━━━━┯━━━━━━━━┓"+"\n"+ 408 | "┃ a1 │ a2 │ a3 ┃"+"\n"+ 409 | "┣━━━━━━━━┿━━━━━━━━┿━━━━━━━━┫"+"\n"+ 410 | "┃ a2 │ b2 │ ┃"+"\n"+ 411 | "┃ │ │ ┃"+"\n"+ 412 | "┃ widea3 │ wideb3 │ widec3 ┃"+"\n"+ 413 | "┗━━━━━━━━┷━━━━━━━━┷━━━━━━━━┛"+"\n"; 414 | assertLinesMatch( Arrays.asList(expected.split("\n")), 415 | Arrays.asList(four.toString().split("\n")), "test four by four with empty rows and ColumnFormat"); 416 | // assertArraysEqual( expected.split("\n"), 417 | // four.toString().split("\n"), "four"); 418 | } 419 | 420 | 421 | @Test 422 | void testCustomStyle() { 423 | 424 | class DoubleStyle implements Style { 425 | private String[][] tablePattern = new String[][] { 426 | /* LEFT COLDATA COLLINE RIGHT */ 427 | /* UMN */ 428 | /* TOP */ {"x", "=", "x", "x" }, 429 | /* HDRDATA */ {":", "Hdr", ":", ":" }, 430 | /* HDRLINE*/ {"x", "=", "x", "x" }, 431 | /* ROWDATA */ {":", "Data", ":", ":" }, 432 | /* ROWLINE*/ {":", "-", ".", ":" }, 433 | /* BOTTOM */ {"x", "=", "x", "x" } }; 434 | 435 | public String getPattern(Row row, Column column) { 436 | if(tablePattern[row.ordinal()] == null) { 437 | return null; 438 | } else { 439 | return tablePattern[row.ordinal()][column.ordinal()]; 440 | } 441 | } 442 | 443 | public String[][] getTable() { 444 | return tablePattern; 445 | } 446 | } 447 | 448 | DoubleStyle doubleStyle = new DoubleStyle(); 449 | ConsoleTable six = new ConsoleTable((doubleStyle).getTable()).withStyle(doubleStyle); 450 | six.setHeaders(new ColumnFormat("Left",Aligned.CENTRE),new ColumnFormat("Cell",Aligned.CENTRE),new ColumnFormat("Line",Aligned.CENTRE),new ColumnFormat("Right",Aligned.CENTRE)); 451 | 452 | String expected = 453 | "x======x======x======x=======x"+"\n"+ 454 | ": Left : Cell : Line : Right :"+"\n"+ 455 | "x======x======x======x=======x"+"\n"+ 456 | ": x : = : x : x :"+"\n"+ 457 | ": : : Hdr : : : : :"+"\n"+ 458 | ": x : = : x : x :"+"\n"+ 459 | ": : : Data : : : : :"+"\n"+ 460 | ": : : - : . : : :"+"\n"+ 461 | ": x : = : x : x :"+"\n"+ 462 | "x======x======x======x=======x"+"\n"; 463 | assertLinesMatch( Arrays.asList(expected.split("\n")), 464 | Arrays.asList(six.toString().split("\n")), "test custom style with pattern table"); 465 | 466 | ConsoleTable seven = new ConsoleTable().withStyle(doubleStyle).setHeaders("-Date","'Temperature"); 467 | seven.addRow(LocalDate.parse("2021-12-25"), "21°C"); 468 | 469 | String expectedOutput = 470 | "x============x=============x"+"\n"+ 471 | ": Date : Temperature :"+"\n"+ 472 | "x============x=============x"+"\n"+ 473 | ": 2021-12-25 : 21°C :"+"\n"+ 474 | "x============x=============x"+"\n"; 475 | assertLinesMatch( Arrays.asList(expectedOutput.split("\n")), 476 | Arrays.asList(seven.toString().split("\n")), "test custom style with real world data"); 477 | } 478 | 479 | @Test 480 | void testAlignments() { 481 | ConsoleTable alignmentsTable = new ConsoleTable().withStyle(Styles.BASIC); 482 | alignmentsTable.setHeaders("-Test", new ColumnFormat("Left",Aligned.LEFT), "'Centre", new ColumnFormat("Right",Aligned.RIGHT)); 483 | alignmentsTable.addRow("Short", "a", "1", "x"); 484 | alignmentsTable.addRow("Medium", "b2", "22", "yy"); 485 | alignmentsTable.addRow("Medium", "c23", "223", "yy3"); 486 | // alignmentsTable.addRow("Medium", "c234", "2234", "yy34"); 487 | alignmentsTable.addRow("Longest", "d23456", "e2345678", "z12345"); 488 | 489 | String expected = 490 | "+---------+--------+----------+--------+"+"\n"+ 491 | "| Test | Left | Centre | Right |"+"\n"+ 492 | "+---------+--------+----------+--------+"+"\n"+ 493 | "| Short | a | 1 | x |"+"\n"+ 494 | "| Medium | b2 | 22 | yy |"+"\n"+ 495 | "| Medium | c23 | 223 | yy3 |"+"\n"+ 496 | "| Longest | d23456 | e2345678 | z12345 |"+"\n"+ 497 | "+---------+--------+----------+--------+"+"\n"; 498 | assertLinesMatch( Arrays.asList(expected.split("\n")), 499 | Arrays.asList(alignmentsTable.toString().split("\n")), "test Alignments"); 500 | 501 | } 502 | 503 | @Test 504 | void testAlignmentOnly() { 505 | ConsoleTable alignmentsTable = new ConsoleTable().withStyle(Styles.BASIC); 506 | alignmentsTable.setHeaders("'", "-Left", "'Centre", "Right"); 507 | alignmentsTable.addRow("Low", "a", "1", "x"); 508 | alignmentsTable.addRow("Large", "b2", "22", "yy"); 509 | alignmentsTable.addRow("Extreme", "d23456", "e2345678", "z12345"); 510 | 511 | String expected = 512 | "+---------+--------+----------+--------+"+"\n"+ 513 | "| | Left | Centre | Right |"+"\n"+ 514 | "+---------+--------+----------+--------+"+"\n"+ 515 | "| Low | a | 1 | x |"+"\n"+ 516 | "| Large | b2 | 22 | yy |"+"\n"+ 517 | "| Extreme | d23456 | e2345678 | z12345 |"+"\n"+ 518 | "+---------+--------+----------+--------+"+"\n"; 519 | assertLinesMatch( Arrays.asList(expected.split("\n")), 520 | Arrays.asList(alignmentsTable.toString().split("\n")), "test Alignments"); 521 | 522 | } 523 | 524 | 525 | @Test 526 | void testArrayHeaderData() { 527 | String[] petHeaders = { "-Pet", "Age", "'Sex" }; 528 | Object[][] petData = { 529 | { "Cat", 10, "F" }, 530 | { "Dog", 5, "M"} 531 | }; 532 | ConsoleTable petShopTable = new ConsoleTable(petHeaders, petData).withStyle(Styles.BASIC); 533 | 534 | String expected = 535 | "+-----+-----+-----+"+"\n"+ 536 | "| Pet | Age | Sex |"+"\n"+ 537 | "+-----+-----+-----+"+"\n"+ 538 | "| Cat | 10 | F |"+"\n"+ 539 | "| Dog | 5 | M |"+"\n"+ 540 | "+-----+-----+-----+"+"\n"; 541 | assertLinesMatch( Arrays.asList(expected.split("\n")), 542 | Arrays.asList(petShopTable.toString().split("\n")), "test new(Array, Array)"); 543 | 544 | } 545 | 546 | @Test 547 | void testPaddingInStyles() { 548 | ConsoleTable zooTable = new ConsoleTable( 549 | new String[] { "-Animal", "Cost", "'Sex" }, 550 | new Object[][] { 551 | { "Cat", new BigDecimal("10.00"), "F" }, 552 | { "Zebra", new BigDecimal("5678.99"), "M"} 553 | }).withStyle(Styles.SQL); 554 | 555 | String expected = 556 | "Animal Cost Sex"+"\n"+ 557 | "------ ------- ---"+"\n"+ 558 | "Cat 10.00 F "+"\n"+ 559 | "Zebra 5678.99 M "+"\n"; 560 | 561 | assertLinesMatch( Arrays.asList(expected.split("\n")), 562 | Arrays.asList(zooTable.toString().split("\n")), "test new(Array, Array)"); 563 | 564 | } 565 | 566 | @Test 567 | void testSimplestStyle() { 568 | 569 | class Simple implements Style { 570 | 571 | public String getPattern(Row row, Column column) { 572 | if(row == Row.HDRDATA || row == Row.ROWDATA ) { 573 | if( column == Column.COLLINE ) 574 | return " "; 575 | else 576 | return ""; 577 | } 578 | else 579 | return null; 580 | } 581 | 582 | public String getPadding(Column column) { 583 | return ""; 584 | } 585 | } 586 | 587 | ConsoleTable simple = new ConsoleTable().withStyle(new Simple()); 588 | simple.setHeaders("Pet","Cost"); 589 | simple.addRow("Rusty", 100); 590 | simple.addRow("Red", 42); 591 | 592 | String expected = 593 | " Pet Cost"+"\n"+ 594 | "Rusty 100"+"\n"+ 595 | " Red 42"+"\n"; 596 | 597 | assertLinesMatch( Arrays.asList(expected.split("\n")), 598 | Arrays.asList(simple.toString().split("\n")), "test super simple Style"); 599 | 600 | 601 | 602 | } 603 | 604 | @Test 605 | void testSimplerStyle() { 606 | 607 | class Simple implements Style { 608 | public String getPattern(Row row, Column column) { 609 | return "."; 610 | } 611 | } 612 | 613 | ConsoleTable simple = new ConsoleTable().withStyle(new Simple()); 614 | simple.setHeaders("Pet","Cost"); 615 | simple.addRow("Rusty", 100); 616 | simple.addRow("Red", 42); 617 | 618 | System.out.print(simple); 619 | // TODO assert 620 | 621 | 622 | } 623 | 624 | @Test 625 | void testListHeaderData() { 626 | List petHeaders = Arrays.asList( "-Pet", "Age", "'Sex" ); 627 | List> petData = Arrays.asList( 628 | Arrays.asList( "Cat", 10, "F" ), 629 | Arrays.asList( "Dog", 5, "M" ) 630 | ); 631 | ConsoleTable petShopTable = new ConsoleTable(petHeaders, petData).withStyle(Styles.BASIC); 632 | 633 | String expected = 634 | "+-----+-----+-----+"+"\n"+ 635 | "| Pet | Age | Sex |"+"\n"+ 636 | "+-----+-----+-----+"+"\n"+ 637 | "| Cat | 10 | F |"+"\n"+ 638 | "| Dog | 5 | M |"+"\n"+ 639 | "+-----+-----+-----+"+"\n"; 640 | assertLinesMatch( Arrays.asList(expected.split("\n")), 641 | Arrays.asList(petShopTable.toString().split("\n")), "test new(Array, Array)"); 642 | 643 | ConsoleTable table = new ConsoleTable(); 644 | table.setHeaders("Header", "Header"); 645 | table.addRow("Cell","b2"); 646 | table.addRow("a3", "b3"); 647 | System.out.print(table); 648 | } 649 | 650 | } 651 | 652 | 653 | --------------------------------------------------------------------------------