Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use ellipsis to indicate truncation; more padding customization #343

Merged
merged 3 commits into from
Oct 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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()`
Expand Down
130 changes: 110 additions & 20 deletions src/main/java/org/plumelib/util/StringsPlume.java
Original file line number Diff line number Diff line change
Expand Up @@ -777,62 +777,126 @@ 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({
"allcheckers:purity.not.sideeffectfree.call", // side effect to local state
"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({
"allcheckers:purity.not.sideeffectfree.call", // side effect to local state
"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
Expand All @@ -845,23 +909,49 @@ 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
*/
@SuppressWarnings({
"lock:method.guarantee.violated", // side effect to local state
"allcheckers:purity.not.sideeffectfree.call"
}) // side effect to local state
@SideEffectFree
public static String rpad(double num, @NonNegative int length) {
return rpad(String.valueOf(num), length);
if (length == 0) {
throw new IllegalArgumentException(String.format("rpad(%s, %s)", num, length));
}
String numString = String.valueOf(num);
int dotIndex = numString.indexOf('.');
if (dotIndex >= length) {
return numString.substring(0, dotIndex);
} else if (dotIndex == length - 1) {
// Pad instead of having the last character in the output be the decimal period.
return numString.substring(0, dotIndex) + " ";
} else
// now: dotIndex < length - 1
if (length < numString.length()) {
return numString.substring(0, length);
} else {
// This is guaranteed to pad only, so inline rather than calling a method.
StringBuilder result = new StringBuilder(numString);
for (int i = numString.length(); i < length; i++) {
result.append(' ');
}
return result.toString();
}
}

///////////////////////////////////////////////////////////////////////////
Expand Down
12 changes: 10 additions & 2 deletions src/test/java/org/plumelib/util/StringsPlumeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.141", 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)
Expand Down