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 */
035public class FormattableUtils {
036
037    /**
038     * A format that simply outputs the value as a string.
039     */
040    private static final String SIMPLEST_FORMAT = "%s";
041
042    /**
043     * Handles the common {@code Formattable} operations of truncate-pad-append,
044     * with no ellipsis on precision overflow, and padding width underflow with
045     * spaces.
046     *
047     * @param seq  the string to handle, not null
048     * @param formatter  the destination formatter, not null
049     * @param flags  the flags for formatting, see {@code Formattable}
050     * @param width  the width of the output, see {@code Formattable}
051     * @param precision  the precision of the output, see {@code Formattable}
052     * @return The {@code formatter} instance, not null
053     */
054    public static Formatter append(final CharSequence seq, final Formatter formatter, final int flags, final int width,
055            final int precision) {
056        return append(seq, formatter, flags, width, precision, ' ', null);
057    }
058
059    /**
060     * Handles the common {@link Formattable} operations of truncate-pad-append,
061     * with no ellipsis on precision overflow.
062     *
063     * @param seq  the string to handle, not null
064     * @param formatter  the destination formatter, not null
065     * @param flags  the flags for formatting, see {@code Formattable}
066     * @param width  the width of the output, see {@code Formattable}
067     * @param precision  the precision of the output, see {@code Formattable}
068     * @param padChar  the pad character to use
069     * @return The {@code formatter} instance, not null
070     */
071    public static Formatter append(final CharSequence seq, final Formatter formatter, final int flags, final int width,
072            final int precision, final char padChar) {
073        return append(seq, formatter, flags, width, precision, padChar, null);
074    }
075
076    /**
077     * Handles the common {@link Formattable} operations of truncate-pad-append.
078     *
079     * @param seq  the string to handle, not null
080     * @param formatter  the destination formatter, not null
081     * @param flags  the flags for formatting, see {@code Formattable}
082     * @param width  the width of the output, see {@code Formattable}
083     * @param precision  the precision of the output, see {@code Formattable}
084     * @param padChar  the pad character to use
085     * @param truncateEllipsis  the ellipsis to use when precision dictates truncation, null or
086     *  empty causes a hard truncation
087     * @return The {@code formatter} instance, not null
088     * @throws IllegalArgumentException if {@code ellipsis.length() > precision},
089     *  given that {@code ellipsis} is not null and {@code precision >= 0}
090     */
091    public static Formatter append(final CharSequence seq, final Formatter formatter, final int flags, final int width,
092            final int precision, final char padChar, final CharSequence truncateEllipsis) {
093        if (!(truncateEllipsis == null || precision < 0 || truncateEllipsis.length() <= precision)) {
094            throw new IllegalArgumentException(
095                    String.format("Specified ellipsis '%s' exceeds precision of %s",
096                            truncateEllipsis,
097                            precision));
098        }
099        final StringBuilder buf = new StringBuilder(seq);
100        if (precision >= 0 && precision < seq.length()) {
101            final CharSequence ellipsis;
102            if (truncateEllipsis == null) {
103                ellipsis = StringUtils.EMPTY;
104            } else {
105                ellipsis = truncateEllipsis;
106            }
107            buf.replace(precision - ellipsis.length(), seq.length(), ellipsis.toString());
108        }
109        final boolean leftJustify = (flags & LEFT_JUSTIFY) == LEFT_JUSTIFY;
110        for (int i = buf.length(); i < width; i++) {
111            buf.insert(leftJustify ? i : 0, padChar);
112        }
113        formatter.format(buf.toString());
114        return formatter;
115    }
116
117    /**
118     * Handles the common {@link Formattable} operations of truncate-pad-append,
119     * padding width underflow with spaces.
120     *
121     * @param seq  the string to handle, not null
122     * @param formatter  the destination formatter, not null
123     * @param flags  the flags for formatting, see {@code Formattable}
124     * @param width  the width of the output, see {@code Formattable}
125     * @param precision  the precision of the output, see {@code Formattable}
126     * @param ellipsis  the ellipsis to use when precision dictates truncation, null or
127     *  empty causes a hard truncation
128     * @return The {@code formatter} instance, not null
129     * @throws IllegalArgumentException if {@code ellipsis.length() > precision},
130     *  given that {@code ellipsis} is not null and {@code precision >= 0}
131     */
132    public static Formatter append(final CharSequence seq, final Formatter formatter, final int flags, final int width,
133            final int precision, final CharSequence ellipsis) {
134        return append(seq, formatter, flags, width, precision, ' ', ellipsis);
135    }
136
137    /**
138     * Gets the default formatted representation of the specified
139     * {@code Formattable}.
140     *
141     * @param formattable  the instance to convert to a string, not null
142     * @return The resulting string, not null
143     */
144    public static String toString(final Formattable formattable) {
145        return String.format(SIMPLEST_FORMAT, formattable);
146    }
147
148    /**
149     * {@code FormattableUtils} instances should NOT be constructed in
150     * standard programming. Instead, the methods of the class should be invoked
151     * statically.
152     *
153     * <p>This constructor is public to permit tools that require a JavaBean
154     * instance to operate.</p>
155     */
156    public FormattableUtils() {
157    }
158
159}