From 1601255f74377483d61fd87c5decb66687a24401 Mon Sep 17 00:00:00 2001 From: Rick Porter Date: Tue, 3 Nov 2026 06:01:53 -0500 Subject: [PATCH] Add/use BuilderConfig for null, true, and false values --- README.md | 4 +- .../prettytable/BuilderConfig.java | 30 ++++++++++++ .../rickwporter/prettytable/TableBuilder.java | 20 ++++---- .../prettytable/BuilderConfigTest.java | 21 +++++++++ .../prettytable/TableBuilderTest.java | 47 ++++++++++++++++--- 5 files changed, 105 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 1b4209d..e14b6d7 100644 --- a/README.md +++ b/README.md @@ -312,7 +312,7 @@ The sorting looks like this: ## TableBuilder Usage `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. `null` renders as `None`. Booleans render as `True` and `False`. +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 `...`. @@ -363,6 +363,8 @@ import rickwporter.prettytable.BuilderConfig; | `setPropertyLabel` / `setValueLabel` | `Property` / `Value` | | `setPropertiesLabel` / `setValuesLabel` | `Properties` / `Values` | | `setUnknownLabel` | `Unknown` | +| `setNullValue` | `None` | +| `setTrueValue` / `setFalseValue` | `True` / `False` | | `setItemsCaption` | `Found {} items` | | `setEllipsis` | `...` | | `setWildcardHeader` | `Properties` | diff --git a/src/main/java/rickwporter/prettytable/BuilderConfig.java b/src/main/java/rickwporter/prettytable/BuilderConfig.java index da73dcc..7c2812c 100644 --- a/src/main/java/rickwporter/prettytable/BuilderConfig.java +++ b/src/main/java/rickwporter/prettytable/BuilderConfig.java @@ -16,6 +16,9 @@ public final class BuilderConfig { private String valueLabel = "Value"; private String valuesLabel = "Values"; private String unknownLabel = "Unknown"; + private String nullValue = "None"; + private String trueValue = "True"; + private String falseValue = "False"; private String itemsCaption = "Found {} items"; private String ellipsis = "..."; private String wildcardHeader = "Properties"; @@ -80,6 +83,33 @@ public BuilderConfig setUnknownLabel(String label) { return this; } + public String getNullValue() { + return this.nullValue; + } + + public BuilderConfig setNullValue(String text) { + this.nullValue = Objects.requireNonNull(text); + return this; + } + + public String getTrueValue() { + return this.trueValue; + } + + public BuilderConfig setTrueValue(String text) { + this.trueValue = Objects.requireNonNull(text); + return this; + } + + public String getFalseValue() { + return this.falseValue; + } + + public BuilderConfig setFalseValue(String text) { + this.falseValue = Objects.requireNonNull(text); + return this; + } + public String getItemsCaption() { return this.itemsCaption; } diff --git a/src/main/java/rickwporter/prettytable/TableBuilder.java b/src/main/java/rickwporter/prettytable/TableBuilder.java index c7782cc..2fb274a 100644 --- a/src/main/java/rickwporter/prettytable/TableBuilder.java +++ b/src/main/java/rickwporter/prettytable/TableBuilder.java @@ -125,7 +125,7 @@ private PrettyTable createListTable(List items, boolean outer) { Object other = this.singleOtherKey(first, nameKey); if (other != MANY_KEYS) { - PrettyTable result = new PrettyTable(this.headerize(nameKey), this.headerize(stringify(other))); + PrettyTable result = new PrettyTable(this.headerize(nameKey), this.headerize(this.stringify(other))); this.configure(result, outer, true, caption); for (Object item : items) { Map map = asMap(item); @@ -148,7 +148,7 @@ private PrettyTable createObjectTable(Map obj, boolean outer) { PrettyTable result = new PrettyTable(this.config.getPropertyLabel(), this.config.getValueLabel()); this.configure(result, outer, false, null); for (Map.Entry entry : obj.entrySet()) { - String key = this.truncate(stringify(entry.getKey()), this.config.getKeyMaxLen()); + String key = this.truncate(this.stringify(entry.getKey()), this.config.getKeyMaxLen()); result.addRow(key, this.cellValue(entry.getValue())); } return result; @@ -164,10 +164,10 @@ private Object cellValue(Object obj) { return this.createListTable(list, false); } if (!list.isEmpty()) { - return this.truncate(joinList(list), this.config.getValueMaxLen()); + return this.truncate(this.joinList(list), this.config.getValueMaxLen()); } } - String text = stringify(obj); + String text = this.stringify(obj); int maxLen = this.isUrl(text) ? this.config.getUrlMaxLen() : this.config.getValueMaxLen(); return this.truncate(text, maxLen); } @@ -210,7 +210,7 @@ private Object singleOtherKey(Map item, String nameKey) { private String nameCell(Map map, String nameKey) { Object raw = map.containsKey(nameKey) ? map.get(nameKey) : this.config.getUnknownLabel(); - return this.truncate(stringify(raw), this.config.getKeyMaxLen()); + return this.truncate(this.stringify(raw), this.config.getKeyMaxLen()); } private boolean isUrl(String text) { @@ -240,13 +240,13 @@ private static Map copyWithout(Map source, Set skip) { return copy; } - private static String joinList(List items) { + private String joinList(List items) { StringBuilder result = new StringBuilder(); for (int i = 0; i < items.size(); i++) { if (i > 0) { result.append(", "); } - result.append(stringify(items.get(i))); + result.append(this.stringify(items.get(i))); } return result.toString(); } @@ -261,12 +261,12 @@ String headerize(String value) { return Character.toUpperCase(value.charAt(0)) + value.substring(1); } - static String stringify(Object value) { + String stringify(Object value) { if (value == null) { - return "None"; + return this.config.getNullValue(); } if (value instanceof Boolean) { - return ((Boolean) value).booleanValue() ? "True" : "False"; + return ((Boolean) value).booleanValue() ? this.config.getTrueValue() : this.config.getFalseValue(); } return String.valueOf(value); } diff --git a/src/test/java/rickwporter/prettytable/BuilderConfigTest.java b/src/test/java/rickwporter/prettytable/BuilderConfigTest.java index 80c0207..e1d2d49 100644 --- a/src/test/java/rickwporter/prettytable/BuilderConfigTest.java +++ b/src/test/java/rickwporter/prettytable/BuilderConfigTest.java @@ -17,6 +17,9 @@ public void testDefaults() { Assertions.assertEquals("Value", config.getValueLabel()); Assertions.assertEquals("Values", config.getValuesLabel()); Assertions.assertEquals("Unknown", config.getUnknownLabel()); + Assertions.assertEquals("None", config.getNullValue()); + Assertions.assertEquals("True", config.getTrueValue()); + Assertions.assertEquals("False", config.getFalseValue()); Assertions.assertEquals("Found {} items", config.getItemsCaption()); Assertions.assertEquals("...", config.getEllipsis()); Assertions.assertEquals("Properties", config.getWildcardHeader()); @@ -50,6 +53,15 @@ public void testSetters() { Assertions.assertSame(config, config.setUnknownLabel("pluto")); Assertions.assertEquals("pluto", config.getUnknownLabel()); + Assertions.assertSame(config, config.setNullValue("nil")); + Assertions.assertEquals("nil", config.getNullValue()); + + Assertions.assertSame(config, config.setTrueValue("yes")); + Assertions.assertEquals("yes", config.getTrueValue()); + + Assertions.assertSame(config, config.setFalseValue("no")); + Assertions.assertEquals("no", config.getFalseValue()); + Assertions.assertSame(config, config.setItemsCaption("huey, dewey, louie")); Assertions.assertEquals("huey, dewey, louie", config.getItemsCaption()); @@ -112,6 +124,15 @@ public void testSettersRejectNull() { Assertions.assertThrows(NullPointerException.class, () -> config.setUnknownLabel(null)); Assertions.assertEquals("Unknown", config.getUnknownLabel()); + Assertions.assertThrows(NullPointerException.class, () -> config.setNullValue(null)); + Assertions.assertEquals("None", config.getNullValue()); + + Assertions.assertThrows(NullPointerException.class, () -> config.setTrueValue(null)); + Assertions.assertEquals("True", config.getTrueValue()); + + Assertions.assertThrows(NullPointerException.class, () -> config.setFalseValue(null)); + Assertions.assertEquals("False", config.getFalseValue()); + Assertions.assertThrows(NullPointerException.class, () -> config.setItemsCaption(null)); Assertions.assertEquals("Found {} items", config.getItemsCaption()); diff --git a/src/test/java/rickwporter/prettytable/TableBuilderTest.java b/src/test/java/rickwporter/prettytable/TableBuilderTest.java index b664da5..a1a5f0f 100644 --- a/src/test/java/rickwporter/prettytable/TableBuilderTest.java +++ b/src/test/java/rickwporter/prettytable/TableBuilderTest.java @@ -129,13 +129,22 @@ public void testHeaderize() { Assertions.assertEquals("Hello", custom.headerize("hello")); } - @Test + @Test public void testStringify() { - Assertions.assertEquals("None", TableBuilder.stringify(null)); - Assertions.assertEquals("True", TableBuilder.stringify(true)); - Assertions.assertEquals("False", TableBuilder.stringify(false)); - Assertions.assertEquals("My string", TableBuilder.stringify("My string")); - Assertions.assertEquals("3", TableBuilder.stringify(3)); + TableBuilder obj = new TableBuilder(); + Assertions.assertEquals("None", obj.stringify(null)); + Assertions.assertEquals("True", obj.stringify(true)); + Assertions.assertEquals("False", obj.stringify(false)); + Assertions.assertEquals("My string", obj.stringify("My string")); + Assertions.assertEquals("3", obj.stringify(3)); + + TableBuilder custom = new TableBuilder(new BuilderConfig() + .setNullValue("nil") + .setTrueValue("yes") + .setFalseValue("no")); + Assertions.assertEquals("nil", custom.stringify(null)); + Assertions.assertEquals("yes", custom.stringify(true)); + Assertions.assertEquals("no", custom.stringify(false)); } @Test @@ -370,6 +379,32 @@ public void testNullConfigUsesDefaults() { this.assertRendered("TableBuilder_simple_dict.text", this.simpleDict(), null); } + @Test + public void testNullValue() { + BuilderConfig options = new BuilderConfig().setNullValue("nil"); + String rendered = new TableBuilder(options).build(this.simpleDict()).toString(); + Assertions.assertTrue(rendered.contains("nil")); + Assertions.assertFalse(rendered.contains("None")); + + String listed = new TableBuilder(options).build(this.simpleList()).toString(); + Assertions.assertTrue(listed.contains("nil")); + Assertions.assertFalse(listed.contains("None")); + } + + @Test + public void testBooleanValues() { + BuilderConfig options = new BuilderConfig() + .setTrueValue("yes") + .setFalseValue("no"); + String rendered = new TableBuilder(options).build(this.simpleDict()).toString(); + Assertions.assertTrue(rendered.contains("no")); + Assertions.assertFalse(rendered.contains("False")); + + String nested = new TableBuilder(options).build(this.innerList()).toString(); + Assertions.assertTrue(nested.contains("yes")); + Assertions.assertFalse(nested.contains("True")); + } + @Test public void testUnknownLabel() { BuilderConfig options = new BuilderConfig()