001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.text;
018
019import static java.util.FormattableFlags.LEFT_JUSTIFY;
020
021import java.util.Formattable;
022import java.util.Formatter;
023
024import org.apache.commons.lang3.StringUtils;
025
026/**
027 * Provides utilities for working with the {@code Formattable} interface.
028 *
029 * <p>The {@link Formattable} interface provides basic control over formatting
030 * when using a {@code Formatter}. It is primarily concerned with numeric precision
031 * and padding, and is not designed to allow generalised alternate formats.</p>
032 *
033 * @since 1.0
034 *
035 */
036public class FormattableUtils {
037
038    /**
039     * A format that simply outputs the value as a string.
040     */
041    private static final String SIMPLEST_FORMAT = "%s";
042
043    /**
044     * Handles the common {@code Formattable} operations of truncate-pad-append,
045     * with no ellipsis on precision overflow, and padding width underflow with
046     * spaces.
047     *
048     * @param seq  the string to handle, not null
049     * @param formatter  the destination formatter, not null
050     * @param flags  the flags for formatting, see {@code Formattable}
051     * @param width  the width of the output, see {@code Formattable}
052     * @param precision  the precision of the output, see {@code Formattable}
053     * @return The {@code formatter} instance, not null
054     */
055    public static Formatter append(final CharSequence seq, final Formatter formatter, final int flags, final int width,
056            final int precision) {
057        return append(seq, formatter, flags, width, precision, ' ', null);
058    }
059
060    /**
061     * Handles the common {@link Formattable} operations of truncate-pad-append,
062     * with no ellipsis on precision overflow.
063     *
064     * @param seq  the string to handle, not null
065     * @param formatter  the destination formatter, not null
066     * @param flags  the flags for formatting, see {@code Formattable}
067     * @param width  the width of the output, see {@code Formattable}
068     * @param precision  the precision of the output, see {@code Formattable}
069     * @param padChar  the pad character to use
070     * @return The {@code formatter} instance, not null
071     */
072    public static Formatter append(final CharSequence seq, final Formatter formatter, final int flags, final int width,
073            final int precision, final char padChar) {
074        return append(seq, formatter, flags, width, precision, padChar, null);
075    }
076
077    /**
078     * Handles the common {@link Formattable} operations of truncate-pad-append.
079     *
080     * @param seq  the string to handle, not null
081     * @param formatter  the destination formatter, not null
082     * @param flags  the flags for formatting, see {@code Formattable}
083     * @param width  the width of the output, see {@code Formattable}
084     * @param precision  the precision of the output, see {@code Formattable}
085     * @param padChar  the pad character to use
086     * @param truncateEllipsis  the ellipsis to use when precision dictates truncation, null or
087     *  empty causes a hard truncation
088     * @return The {@code formatter} instance, not null
089     * @throws IllegalArgumentException if {@code ellipsis.length() > precision},
090     *  given that {@code ellipsis} is not null and {@code precision >= 0}
091     */
092    public static Formatter append(final CharSequence seq, final Formatter formatter, final int flags, final int width,
093            final int precision, final char padChar, final CharSequence truncateEllipsis) {
094        if (!(truncateEllipsis == null || precision < 0 || truncateEllipsis.length() <= precision)) {
095            throw new IllegalArgumentException(
096                    String.format("Specified ellipsis '%s' exceeds precision of %s",
097                            truncateEllipsis,
098                            precision));
099        }
100        final StringBuilder buf = new StringBuilder(seq);
101        if (precision >= 0 && precision < seq.length()) {
102            final CharSequence ellipsis;
103            if (truncateEllipsis == null) {
104                ellipsis = StringUtils.EMPTY;
105            } else {
106                ellipsis = truncateEllipsis;
107            }
108            buf.replace(precision - ellipsis.length(), seq.length(), ellipsis.toString());
109        }
110        final boolean leftJustify = (flags & LEFT_JUSTIFY) == LEFT_JUSTIFY;
111        for (int i = buf.length(); i < width; i++) {
112            buf.insert(leftJustify ? i : 0, padChar);
113        }
114        formatter.format(buf.toString());
115        return formatter;
116    }
117
118    /**
119     * Handles the common {@link Formattable} operations of truncate-pad-append,
120     * padding width underflow with spaces.
121     *
122     * @param seq  the string to handle, not null
123     * @param formatter  the destination formatter, not null
124     * @param flags  the flags for formatting, see {@code Formattable}
125     * @param width  the width of the output, see {@code Formattable}
126     * @param precision  the precision of the output, see {@code Formattable}
127     * @param ellipsis  the ellipsis to use when precision dictates truncation, null or
128     *  empty causes a hard truncation
129     * @return The {@code formatter} instance, not null
130     * @throws IllegalArgumentException if {@code ellipsis.length() > precision},
131     *  given that {@code ellipsis} is not null and {@code precision >= 0}
132     */
133    public static Formatter append(final CharSequence seq, final Formatter formatter, final int flags, final int width,
134            final int precision, final CharSequence ellipsis) {
135        return append(seq, formatter, flags, width, precision, ' ', ellipsis);
136    }
137
138    /**
139     * Gets the default formatted representation of the specified
140     * {@code Formattable}.
141     *
142     * @param formattable  the instance to convert to a string, not null
143     * @return The resulting string, not null
144     */
145    public static String toString(final Formattable formattable) {
146        return String.format(SIMPLEST_FORMAT, formattable);
147    }
148
149    /**
150     * {@code FormattableUtils} instances should NOT be constructed in
151     * standard programming. Instead, the methods of the class should be invoked
152     * statically.
153     *
154     * <p>This constructor is public to permit tools that require a JavaBean
155     * instance to operate.</p>
156     */
157    public FormattableUtils() {
158    }
159
160}