Displays List and Map data in a variety of forms.
The data can be output in several different different forms:
- text - attractive ASCII format
- rich - Unicode box drawing
- csv - comma separated values format
- html - HyperText Markup Language (HTML) format
- json - JavaScript Object Notation (JSON) format
Here's an example of the standard text output:
+------------+----------+------------+
| Column A | Column 2 | Column III |
+------------+----------+------------+
| A | B | c |
| 1 | 1024 | 65535 |
| Washington | Adams | Jefferson |
+------------+----------+------------+
The PrettyTable class handles a table as shown above. The TableBuilder class can be used to turn a Map or List object into a PrettyTable.
Here are the basics for how to create a PrettyTable as shown above:
import rickwporter.prettytable.PrettyTable;
import rickwporter.prettytable.PrettyTable.OutputFormat;
PrettyTable table = new PrettyTable("Column A", "Column 2", "Column III");
table.addRow("A", "B", "c");
table.addRow(1, 1024, 65535);
table.addRow("Washington", "Adams", "Jefferson");
System.out.println(table.formattedString(OutputFormat.TEXT));You can use PrettyTable::toString() as an alternative to PrettyTable::formattedString(). If you want a format other than text, you can set it with PrettyTable::setOutputFormat(). Repeated leading cells in text, rich, and html follow PrettyTable::setRemoveRedundant(), which removes them by default. The code below would replace the println() call from above:
table.setOutputFormat(OutputFormat.HTML);
System.out.println(table.toString());The above pattern is useful, particularly if nesting tables.
Using the csv format (e.g. OutputFormat.CSV), you get the following output:
Column A,Column 2,Column III
A,B,c
1,1024,65535
Washington,Adams,Jefferson
Using the html format (e.g. OutputFormat.HTML), you get the following output:
<table>
<thead>
<tr>
<th>Column A</th>
<th>Column 2</th>
<th>Column III</th>
</tr>
</thead>
<tbody>
<tr>
<td>A</td>
<td>B</td>
<td>c</td>
</tr>
<tr>
<td>1</td>
<td>1024</td>
<td>65535</td>
</tr>
<tr>
<td>Washington</td>
<td>Adams</td>
<td>Jefferson</td>
</tr>
</tbody>
</table>
Using the rich format (e.g. OutputFormat.RICH), you more aesthetically please lines for the table. The header (if shown) gets a heavier weight border. The header, border, row-divider, alignment, and deduplication settings are the same as text. Hiding the border (setShowLines(false)) leaves padded text, so a nested table stays borderless.
System.out.println(table.formattedString(OutputFormat.RICH));┏━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ Column A ┃ Column 2 ┃ Column III ┃
┡━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━┩
│ A │ B │ c │
│ 1 │ 1024 │ 65535 │
│ Washington │ Adams │ Jefferson │
└────────────┴──────────┴────────────┘
Using the json format (e.g. OutputFormat.JSON), you get the following output:
[
[
"Column A",
"Column 2",
"Column III"
],
{
"Column A": "A",
"Column 2": "B",
"Column III": "c"
},
{
"Column A": 1,
"Column 2": 1024,
"Column III": 65535
},
{
"Column A": "Washington",
"Column 2": "Adams",
"Column III": "Jefferson"
}
]
ASCII text, rich, and html support different alignments. Here's how you would update the above table:
import rickwporter.prettytable.PrettyTable.HorizontalAlign;
table.setHorizAligns(HorizontalAlign.LEFT, HorizontalAlign.CENTER, HorizontalAlign.RIGHT);Now, when the text output is generated, the output will look like this:
+------------+----------+------------+
| Column A | Column 2 | Column III |
+------------+----------+------------+
| A | B | c |
| 1 | 1024 | 65535 |
| Washington | Adams | Jefferson |
+------------+----------+------------+
The html output will have the appropriate style="text-align:xxx" attribute on each <th> and <td> tag. So, second row in the table above would look like:
<tr>
<td style="text-align:left">1</td>
<td style="text-align:center">1024</td>
<td style="text-align:right">65535</td>
</tr>
ASCII text and rich output can hide the header row, drop the border, draw a rule under data rows, and print a caption under the table. csv, html, and json ignore these settings. A null or empty caption is skipped. Any other caption is printed unchanged.
PrettyTable::setShowHeader(false) omits the header. Column widths then come only from the data cells, so the columns can get narrower. A table built with no headers already skips that row.
table.setShowHeader(false);The text output then looks like this:
+------------+-------+-----------+
| A | B | c |
| 1 | 1024 | 65535 |
| Washington | Adams | Jefferson |
+------------+-------+-----------+
PrettyTable::setShowLines(false) omits the +---+ rules and the | borders. Each cell keeps its padding, and column alignment still applies.
table.setShowLines(false); Column A Column 2 Column III
A B c
1 1024 65535
Washington Adams Jefferson
PrettyTable::setRowDividers(true) draws a +---+ rule under data rows. setShowLines(false) hides those rules with the rest of the border.
The sample rows do not repeat a leading cell, so each data row has a rule under it. The rule under the last row closes the table.
table.setRowDividers(true);+------------+----------+------------+
| Column A | Column 2 | Column III |
+------------+----------+------------+
| A | B | c |
+------------+----------+------------+
| 1 | 1024 | 65535 |
+------------+----------+------------+
| Washington | Adams | Jefferson |
+------------+----------+------------+
When repeated leading cells are removed, a row that shares a leading prefix with the row above has no rule under it.
PrettyTable grouped = new PrettyTable("Col1", "Col2", "Col3", "Col4");
grouped.addRow("A", "B", "C", "D");
grouped.addRow("A", "B", "C", "Z");
grouped.addRow("A", "B", "F", "E");
grouped.addRow("A", "B", "F", "E");
grouped.setRowDividers(true);+------+------+------+------+
| Col1 | Col2 | Col3 | Col4 |
+------+------+------+------+
| A | B | C | D |
| | | | Z |
| | | F | E |
| | | | |
+------+------+------+------+
setRemoveRedundant(false) keeps a rule under every data row:
grouped.setRemoveRedundant(false);+------+------+------+------+
| Col1 | Col2 | Col3 | Col4 |
+------+------+------+------+
| A | B | C | D |
+------+------+------+------+
| A | B | C | Z |
+------+------+------+------+
| A | B | F | E |
+------+------+------+------+
| A | B | F | E |
+------+------+------+------+
PrettyTable::setCaption(String) prints a caption under the table. A null or empty caption is skipped. Newlines in the caption are kept, and a caption longer than the table is printed unchanged.
table.setCaption("US presidents");+------------+----------+------------+
| Column A | Column 2 | Column III |
+------------+----------+------------+
| A | B | c |
| 1 | 1024 | 65535 |
| Washington | Adams | Jefferson |
+------------+----------+------------+
US presidents
ASCII text, rich, and html remove redundancy by default. This de-duplication generally makes the output more aesthetically pleasing, as demonstrated below.
A table created like this:
PrettyTable table = new PrettyTable("Col1", "Col2", "Col3", "Col4");
table.addRow("A", "B", "C", "D");
table.addRow("A", "B", "C", "Z");
table.addRow("A", "B", "F", "E");
System.out.println(table.formattedString(OutputFormat.TEXT));produces output that looks like:
+------+------+------+------+
| Col1 | Col2 | Col3 | Col4 |
+------+------+------+------+
| A | B | C | D |
| | | | Z |
| | | F | E |
+------+------+------+------+
By default, the code recognizes the redundant fields and removes them from the rendering of the data. PrettyTable::setRemoveRedundant(false) keeps every repeated value for toString() and for formattedString(OutputFormat). One call can still override that setting with formattedString(OutputFormat, boolean). csv and json keep every cell either way.
In this case, either of these calls:
table.setRemoveRedundant(false);
System.out.println(table.toString()); System.out.println(table.formattedString(OutputFormat.TEXT, false));produces output that looks like:
+------+------+------+------+
| Col1 | Col2 | Col3 | Col4 |
+------+------+------+------+
| A | B | C | D |
| A | B | C | Z |
| A | B | F | E |
+------+------+------+------+
The tables can be sorted by any of the columns. The sorting can be done by header value, or by column indices. The sorting changes the rows, so it must be done after populating the table to be effective. The sorting functions allow specifing multiple headers or indices to avoid non-deterministic when a chosen column matches.
The sorting looks like this:
PrettyTable table = new PrettyTable("Col1", "Col2", "Col3", "Col4");
table.addRow("A", "B", "C", "D");
table.addRow("A", "A", "C", "Z");
table.addRow("A", "C", "F", "E");
// below sorts are equivalent
table.sortByIndex(0, 1);
table.sortByHeader("Col1", "Col2");NOTE: specifying an out of range index, or a header that is not found will result in a ArrayIndexOutOfBoundsException!
TableBuilder builds a PrettyTable from a Map or a List. The constructor takes an optional BuilderConfig. The data is passed to build, which returns the table. build returns null for null or an empty map or list, and throws IllegalArgumentException for any other type.
A map becomes a Property / Value table. Nested maps become nested tables, and a list of plain values is joined into one cell. The BuilderConfig provides configurability for how null, True, and False values show up.
A list of strings, numbers, or booleans becomes one Items column with a caption such as Found 6 items. A list of maps uses the first matching key field (name, then id) as the row name. One other field becomes the second column. Further fields are nested under Properties. Long keys, values, and URLs are cut off with ....
import rickwporter.prettytable.TableBuilder;
List<Map<String, Object>> items = new ArrayList<Map<String, Object>>();
Map<String, Object> sna = new LinkedHashMap<String, Object>();
sna.put("name", "sna");
sna.put("prop1", 1);
sna.put("prop B", null);
sna.put("blah", "zay");
items.add(sna);
PrettyTable table = new TableBuilder().build(items);
System.out.println(table);+------+----------------+
| Name | Properties |
+------+----------------+
| sna | prop1 1 |
| | prop B None |
| | blah zay |
+------+----------------+
Found 1 items
table.setOutputFormat(OutputFormat.RICH) prints effectively the same table with prettier lines/borders.
BuilderConfig holds the labels, caption, truncation limits, and column selection used by build. Each setter returns the config, so calls can be chained. A null config uses the defaults.
The caption template replaces {}, %d, or %s with the number of items. setColumns chooses columns for a list of maps. A * column collects every field that is not named, under the wildcard header (Properties by default). An empty column list leaves the default layout in place.
import rickwporter.prettytable.BuilderConfig;
BuilderConfig config = new BuilderConfig()
.setColumns(Arrays.asList("service", "*", "id"))
.setKeyMaxLen(20)
.setEllipsis("...");
PrettyTable table = new TableBuilder(config).build(items);| Setting | Default |
|---|---|
setItemsLabel |
Items |
setPropertyLabel / setValueLabel |
Property / Value |
setPropertiesLabel / setValuesLabel |
Properties / Values |
setUnknownLabel |
Unknown |
setNullValue |
None |
setTrueValue / setFalseValue |
True / False |
setItemsCaption |
Found {} items |
setEllipsis |
... |
setWildcardHeader |
Properties |
setKeyFields |
name, id |
setKeyMaxLen / setValueMaxLen / setUrlMaxLen |
35 / 50 / 100 |
setUrlPrefixes |
http://, https://, ftp:// |
setColumns |
null |
Your input is welcomed.
Please add unit tests for any code that you would like to change. The project has been setup with Jacoco to produce a coverage report in target/site/jacoco/.