From b2c0da8b33d8ba49ff8a5bc73ef3778a8605f657 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 21 Oct 2023 12:13:39 -0700 Subject: [PATCH] Use ellipsis to indicate truncation; more padding customization --- CHANGELOG.md | 7 +- .../java/org/plumelib/util/StringsPlume.java | 113 ++++++++++++++---- .../org/plumelib/util/StringsPlumeTest.java | 12 +- 3 files changed, 109 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e8dcdbc6..02526fe74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,12 @@ ## 1.9.0 (????-??-??) - Require Java 11 -- `StringsPlume`: add `rpad` that pads with an arbitrary character +- `StringsPlume`: + * `rpad` and `lpad` add an ellipsis ("...") if it truncates + * `rpad(double, ...)` does not truncate values before the decimal point + * add `rpad` that pads with an arbitrary character + * add `rpad` that never truncates + * add `lpad` that never truncates - `CollectionsPlume`: * add method `duplicates()` * add an overload for `mapCapacity()` diff --git a/src/main/java/org/plumelib/util/StringsPlume.java b/src/main/java/org/plumelib/util/StringsPlume.java index 53f6f79f6..c96be1b9a 100644 --- a/src/main/java/org/plumelib/util/StringsPlume.java +++ b/src/main/java/org/plumelib/util/StringsPlume.java @@ -777,11 +777,14 @@ public static String removeWhitespaceBefore(String arg, String delimiter) { } /** - * Returns a string of the specified length, truncated if necessary, and padded with spaces to the - * left if necessary. + * Returns a string of the specified length, truncated if necessary (in which case the last 3 + * non-truncated characters are replaced by "..."), and padded with spaces to the left if + * necessary. * * @param s string to truncate or pad * @param length goal length + * @param c the character to use for padding + * @param truncate if false, no truncation is done, only padding * @return {@code s} truncated or padded to {@code length} characters */ @SuppressWarnings({ @@ -789,25 +792,63 @@ public static String removeWhitespaceBefore(String arg, String delimiter) { "lock:method.guarantee.violated" // side effect to local state }) @SideEffectFree - public static String lpad(String s, @NonNegative int length) { - if (s.length() < length) { + public static String lpad(String s, @NonNegative int length, char c, boolean truncate) { + int sLength = s.length(); + if (sLength == length) { + return s; + } else if (sLength < length) { StringBuilder buf = new StringBuilder(); - for (int i = s.length(); i < length; i++) { - buf.append(' '); + for (int i = sLength; i < length; i++) { + buf.append(c); } - return buf.toString() + s; + buf.append(s); + return buf.toString(); } else { - return s.substring(0, length); + if (truncate && length > 3) { + return s.substring(0, length - 3) + "..."; + } else { + return s; + } } } /** - * Returns a string of the specified length, truncated if necessary, and padded with the given - * character to the right if necessary. + * Returns a string of the specified length, truncated if necessary (in which case the last 3 + * non-truncated characters are replaced by "..."), and padded with `c` to the left if necessary. + * + * @param s string to truncate or pad + * @param length goal length + * @param c character to use for padding + * @return {@code s} truncated or padded to {@code length} characters + */ + @SideEffectFree + public static String lpad(String s, @NonNegative int length, char c) { + return lpad(s, length, c, true); + } + + /** + * Returns a string of the specified length, truncated if necessary (in which case the last 3 + * non-truncated characters are replaced by "..."), and padded with spaces to the left if + * necessary. + * + * @param s string to truncate or pad + * @param length goal length + * @return {@code s} truncated or padded to {@code length} characters + */ + @SideEffectFree + public static String lpad(String s, @NonNegative int length) { + return lpad(s, length, ' ', true); + } + + /** + * Returns a string of the specified length, truncated if necessary (in which case the last 3 + * non-truncated characters are replaced by "..."), and padded with the given character to the + * right if necessary. * * @param s string to truncate or pad * @param length goal length * @param c character to use for padding + * @param truncate if false, no truncation is done, only padding * @return {@code s} truncated or padded to {@code length} characters */ @SuppressWarnings({ @@ -815,24 +856,47 @@ public static String lpad(String s, @NonNegative int length) { "lock:method.guarantee.violated" // side effect to local state }) @SideEffectFree - public static String rpad(String s, @NonNegative int length, char c) { - if (s.length() < length) { + public static String rpad(String s, @NonNegative int length, char c, boolean truncate) { + int sLength = s.length(); + if (sLength == length) { + return s; + } else if (sLength < length) { StringBuilder buf = new StringBuilder(s); - for (int i = s.length(); i < length; i++) { + for (int i = sLength; i < length; i++) { buf.append(c); } return buf.toString(); } else { - return s.substring(0, length); + if (truncate && length > 3) { + return s.substring(0, length - 3) + "..."; + } else { + return s; + } } } /** - * Returns a string of the specified length, truncated if necessary, and padded with spaces to the + * Returns a string of the specified length, truncated if necessary (in which case the last 3 + * non-truncated characters are replaced by "..."), and padded with the given character to the * right if necessary. * * @param s string to truncate or pad * @param length goal length + * @param c character to use for padding + * @return {@code s} truncated or padded to {@code length} characters + */ + @SideEffectFree + public static String rpad(String s, @NonNegative int length, char c) { + return rpad(s, length, c, true); + } + + /** + * Returns a string of the specified length, truncated if necessary (in which case the last 3 + * non-truncated characters are replaced by "..."), and padded with spaces to the right if + * necessary. + * + * @param s string to truncate or pad + * @param length goal length * @return {@code s} truncated or padded to {@code length} characters */ @SideEffectFree @@ -845,23 +909,32 @@ public static String rpad(String s, @NonNegative int length) { * * @param num int whose string representation to truncate or pad * @param length goal length - * @return a string representation of {@code num} truncated or padded to {@code length} characters + * @return a string representation of {@code num} padded to {@code length} characters */ @SideEffectFree public static String rpad(int num, @NonNegative int length) { - return rpad(String.valueOf(num), length); + return rpad(String.valueOf(num), length, ' ', /* truncate= */ false); } /** - * Converts the double to a String, then formats it using {@link #rpad(String,int)}. + * Converts the double to a String, then formats it using {@link #rpad(String,int)}. Does not do + * truncation that is after a decimal point. * * @param num double whose string representation to truncate or pad * @param length goal length - * @return a string representation of {@code num} truncated or padded to {@code length} characters + * @return a string representation of {@code num} padded to {@code length} characters */ @SideEffectFree public static String rpad(double num, @NonNegative int length) { - return rpad(String.valueOf(num), length); + String numString = String.valueOf(num); + int dotIndex = numString.indexOf('.'); + if (dotIndex > length) { + return numString.substring(0, dotIndex); + } else if (dotIndex == length) { + return rpad(numString.substring(0, dotIndex), length); + } else { + return rpad(numString, length, ' '); + } } /////////////////////////////////////////////////////////////////////////// diff --git a/src/test/java/org/plumelib/util/StringsPlumeTest.java b/src/test/java/org/plumelib/util/StringsPlumeTest.java index 316a5df28..02f9fbf0f 100644 --- a/src/test/java/org/plumelib/util/StringsPlumeTest.java +++ b/src/test/java/org/plumelib/util/StringsPlumeTest.java @@ -299,10 +299,18 @@ public void test_rpad() { assertEquals(" ", StringsPlume.rpad("", 5)); assertEquals("abcd ", StringsPlume.rpad("abcd", 5)); assertEquals("abcde", StringsPlume.rpad("abcde", 5)); - assertEquals("abcde", StringsPlume.rpad("abcdef", 5)); - assertEquals("abcde", StringsPlume.rpad("abcde ghij", 5)); + assertEquals("ab...", StringsPlume.rpad("abcdef", 5)); + assertEquals("ab...", StringsPlume.rpad("abcde ghij", 5)); assertEquals("10 ", StringsPlume.rpad(10, 5)); assertEquals("3.14 ", StringsPlume.rpad(3.14, 5)); + assertEquals("3.141592", StringsPlume.rpad(3.141592, 5)); + assertEquals("3141592", StringsPlume.rpad(3141592, 5)); + assertEquals("12", StringsPlume.rpad(12.34567, 1)); + assertEquals("12", StringsPlume.rpad(12.34567, 2)); + assertEquals("12 ", StringsPlume.rpad(12.34567, 3)); + assertEquals("12.3", StringsPlume.rpad(12.34567, 4)); + assertEquals("12.34", StringsPlume.rpad(12.34567, 5)); + assertEquals("12.345", StringsPlume.rpad(12.34567, 6)); // public static class NullableStringComparator // public int compare(Object o1, Object o2)