From 2fb208b8d8c77bd0abc425c41a7a4f9bd43495ba Mon Sep 17 00:00:00 2001 From: Matthew Jason Benson Date: Thu, 21 Apr 2011 22:21:13 +0000 Subject: [PATCH] [LANG-697] Add FormattableUtils class git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@1095833 13f79535-47bb-0310-9956-ffa450edef68 --- RELEASE-NOTES.txt | 1 + .../commons/lang3/util/FormattableUtils.java | 141 ++++++++++++++++++ .../lang3/util/FormattableUtilsTest.java | 122 +++++++++++++++ 3 files changed, 264 insertions(+) create mode 100644 src/main/java/org/apache/commons/lang3/util/FormattableUtils.java create mode 100644 src/test/java/org/apache/commons/lang3/util/FormattableUtilsTest.java diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 6358b8e43..4bec62ac4 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -60,6 +60,7 @@ ADDITIONS IN 3.0 [LANG-676] Documented potential NPE if auto-boxing occurs for some BooleanUtils methods [LANG-678] Add support for ConcurrentMap.putIfAbsent() [LANG-692] Add hashCodeMulti varargs method + [LANG-697] Add FormattableUtils class REMOVALS IN 3.0 =============== diff --git a/src/main/java/org/apache/commons/lang3/util/FormattableUtils.java b/src/main/java/org/apache/commons/lang3/util/FormattableUtils.java new file mode 100644 index 000000000..93d0ef132 --- /dev/null +++ b/src/main/java/org/apache/commons/lang3/util/FormattableUtils.java @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.util; + +import static java.util.FormattableFlags.LEFT_JUSTIFY; + +import java.util.Formattable; +import java.util.Formatter; + +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.Validate; + +/** + * Provides utilities for working with {@link Formattable}s. + * + * @since Lang 3.0 + * @version $Id$ + */ +public class FormattableUtils { + + private static final String SIMPLEST_FORMAT = "%1$s"; + + /** + *

{@link FormattableUtils} instances should NOT be constructed in + * standard programming. Instead, the methods of the class should be invoked + * statically.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public FormattableUtils() { + super(); + } + + /** + * Get the default formatted representation of the specified + * {@link Formattable}. + * + * @param formattable + * @return String + */ + public static String toString(Formattable formattable) { + return String.format(SIMPLEST_FORMAT, formattable); + } + + /** + * Handles the common {@link Formattable} operations of truncate-pad-append, + * with no ellipsis on precision overflow, and padding width underflow with + * spaces. + * + * @param seq to handle + * @param formatter destination + * @param flags for formatting + * @param width of output + * @param precision of output + * @return {@code formatter} + */ + public static Formatter append(CharSequence seq, Formatter formatter, int flags, int width, + int precision) { + return append(seq, formatter, flags, width, precision, ' ', null); + } + + /** + * Handles the common {@link Formattable} operations of truncate-pad-append, + * with no ellipsis on precision overflow. + * + * @param seq to handle + * @param formatter destination + * @param flags for formatting + * @param width of output + * @param precision of output + * @param padChar to use + * @return {@code formatter} + */ + public static Formatter append(CharSequence seq, Formatter formatter, int flags, int width, + int precision, char padChar) { + return append(seq, formatter, flags, width, precision, padChar, null); + } + + /** + * Handles the common {@link Formattable} operations of truncate-pad-append, + * padding width underflow with spaces. + * + * @param seq to handle + * @param formatter destination + * @param flags for formatting + * @param width of output + * @param precision of output + * @param ellipsis to use when precision dictates truncation; a {@code null} + * or empty value causes a hard truncation + * @return {@code formatter} + */ + public static Formatter append(CharSequence seq, Formatter formatter, int flags, int width, + int precision, CharSequence ellipsis) { + return append(seq, formatter, flags, width, precision, ' ', ellipsis); + } + + /** + * Handles the common {@link Formattable} operations of truncate-pad-append. + * + * @param seq to handle + * @param formatter destination + * @param flags for formatting + * @param width of output + * @param precision of output + * @param padChar to use + * @param ellipsis to use when precision dictates truncation; a {@code null} + * or empty value causes a hard truncation + * @return {@code formatter} + */ + public static Formatter append(CharSequence seq, Formatter formatter, int flags, int width, + int precision, char padChar, CharSequence ellipsis) { + Validate.isTrue(ellipsis == null || precision < 0 || ellipsis.length() <= precision, + "Specified ellipsis '%1$s' exceeds precision of %2$s", ellipsis, precision); + StringBuilder buf = new StringBuilder(seq); + if (precision >= 0 && precision < seq.length()) { + CharSequence _ellipsis = ObjectUtils.defaultIfNull(ellipsis, ""); + buf.replace(precision - _ellipsis.length(), seq.length(), _ellipsis.toString()); + } + boolean leftJustify = (flags & LEFT_JUSTIFY) == LEFT_JUSTIFY; + for (int i = buf.length(); i < width; i++) { + buf.insert(leftJustify ? i : 0, padChar); + } + formatter.format(buf.toString()); + return formatter; + } +} diff --git a/src/test/java/org/apache/commons/lang3/util/FormattableUtilsTest.java b/src/test/java/org/apache/commons/lang3/util/FormattableUtilsTest.java new file mode 100644 index 000000000..0c91a2be5 --- /dev/null +++ b/src/test/java/org/apache/commons/lang3/util/FormattableUtilsTest.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.util; + +import static java.util.FormattableFlags.LEFT_JUSTIFY; +import static org.junit.Assert.assertEquals; + +import java.util.Formatter; + +import org.apache.commons.lang3.tuple.Pair; +import org.junit.Test; + +/** + * Unit tests {@link FormattableUtils}. + * + * @version $Id$ + */ +public class FormattableUtilsTest { + + @Test + public void testDefaultAppend() { + assertEquals("foo", FormattableUtils.append("foo", new Formatter(), 0, -1, -1).toString()); + assertEquals("fo", FormattableUtils.append("foo", new Formatter(), 0, -1, 2).toString()); + assertEquals(" foo", FormattableUtils.append("foo", new Formatter(), 0, 4, -1).toString()); + assertEquals(" foo", FormattableUtils.append("foo", new Formatter(), 0, 6, -1).toString()); + assertEquals(" fo", FormattableUtils.append("foo", new Formatter(), 0, 3, 2).toString()); + assertEquals(" fo", FormattableUtils.append("foo", new Formatter(), 0, 5, 2).toString()); + assertEquals("foo ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 4, -1).toString()); + assertEquals("foo ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 6, -1).toString()); + assertEquals("fo ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 3, 2).toString()); + assertEquals("fo ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 5, 2).toString()); + } + + @Test + public void testAlternatePadCharacter() { + char pad='_'; + assertEquals("foo", FormattableUtils.append("foo", new Formatter(), 0, -1, -1, pad).toString()); + assertEquals("fo", FormattableUtils.append("foo", new Formatter(), 0, -1, 2, pad).toString()); + assertEquals("_foo", FormattableUtils.append("foo", new Formatter(), 0, 4, -1, pad).toString()); + assertEquals("___foo", FormattableUtils.append("foo", new Formatter(), 0, 6, -1, pad).toString()); + assertEquals("_fo", FormattableUtils.append("foo", new Formatter(), 0, 3, 2, pad).toString()); + assertEquals("___fo", FormattableUtils.append("foo", new Formatter(), 0, 5, 2, pad).toString()); + assertEquals("foo_", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 4, -1, pad).toString()); + assertEquals("foo___", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 6, -1, pad).toString()); + assertEquals("fo_", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 3, 2, pad).toString()); + assertEquals("fo___", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 5, 2, pad).toString()); + } + + @Test + public void testEllipsis() { + assertEquals("foo", FormattableUtils.append("foo", new Formatter(), 0, -1, -1, "*").toString()); + assertEquals("f*", FormattableUtils.append("foo", new Formatter(), 0, -1, 2, "*").toString()); + assertEquals(" foo", FormattableUtils.append("foo", new Formatter(), 0, 4, -1, "*").toString()); + assertEquals(" foo", FormattableUtils.append("foo", new Formatter(), 0, 6, -1, "*").toString()); + assertEquals(" f*", FormattableUtils.append("foo", new Formatter(), 0, 3, 2, "*").toString()); + assertEquals(" f*", FormattableUtils.append("foo", new Formatter(), 0, 5, 2, "*").toString()); + assertEquals("foo ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 4, -1, "*").toString()); + assertEquals("foo ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 6, -1, "*").toString()); + assertEquals("f* ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 3, 2, "*").toString()); + assertEquals("f* ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 5, 2, "*").toString()); + + assertEquals("foo", FormattableUtils.append("foo", new Formatter(), 0, -1, -1, "+*").toString()); + assertEquals("+*", FormattableUtils.append("foo", new Formatter(), 0, -1, 2, "+*").toString()); + assertEquals(" foo", FormattableUtils.append("foo", new Formatter(), 0, 4, -1, "+*").toString()); + assertEquals(" foo", FormattableUtils.append("foo", new Formatter(), 0, 6, -1, "+*").toString()); + assertEquals(" +*", FormattableUtils.append("foo", new Formatter(), 0, 3, 2, "+*").toString()); + assertEquals(" +*", FormattableUtils.append("foo", new Formatter(), 0, 5, 2, "+*").toString()); + assertEquals("foo ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 4, -1, "+*").toString()); + assertEquals("foo ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 6, -1, "+*").toString()); + assertEquals("+* ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 3, 2, "+*").toString()); + assertEquals("+* ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 5, 2, "+*").toString()); + } + + @Test(expected=IllegalArgumentException.class) + public void testIllegalEllipsis() { + FormattableUtils.append("foo", new Formatter(), 0, -1, 1, "xx"); + } + + @Test + public void testAlternatePadCharAndEllipsis() { + assertEquals("foo", FormattableUtils.append("foo", new Formatter(), 0, -1, -1, '_', "*").toString()); + assertEquals("f*", FormattableUtils.append("foo", new Formatter(), 0, -1, 2, '_', "*").toString()); + assertEquals("_foo", FormattableUtils.append("foo", new Formatter(), 0, 4, -1, '_', "*").toString()); + assertEquals("___foo", FormattableUtils.append("foo", new Formatter(), 0, 6, -1, '_', "*").toString()); + assertEquals("_f*", FormattableUtils.append("foo", new Formatter(), 0, 3, 2, '_', "*").toString()); + assertEquals("___f*", FormattableUtils.append("foo", new Formatter(), 0, 5, 2, '_', "*").toString()); + assertEquals("foo_", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 4, -1, '_', "*").toString()); + assertEquals("foo___", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 6, -1, '_', "*").toString()); + assertEquals("f*_", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 3, 2, '_', "*").toString()); + assertEquals("f*___", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 5, 2, '_', "*").toString()); + + assertEquals("foo", FormattableUtils.append("foo", new Formatter(), 0, -1, -1, '_', "+*").toString()); + assertEquals("+*", FormattableUtils.append("foo", new Formatter(), 0, -1, 2, '_', "+*").toString()); + assertEquals("_foo", FormattableUtils.append("foo", new Formatter(), 0, 4, -1, '_', "+*").toString()); + assertEquals("___foo", FormattableUtils.append("foo", new Formatter(), 0, 6, -1, '_', "+*").toString()); + assertEquals("_+*", FormattableUtils.append("foo", new Formatter(), 0, 3, 2, '_', "+*").toString()); + assertEquals("___+*", FormattableUtils.append("foo", new Formatter(), 0, 5, 2, '_', "+*").toString()); + assertEquals("foo_", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 4, -1, '_', "+*").toString()); + assertEquals("foo___", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 6, -1, '_', "+*").toString()); + assertEquals("+*_", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 3, 2, '_', "+*").toString()); + assertEquals("+*___", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 5, 2, '_', "+*").toString()); + } + + @Test + public void testToStringFormattable() { + assertEquals("(Key,Value)", FormattableUtils.toString(Pair.of("Key", "Value"))); + } +}