Java Programming Tutorials

Java programming tutorials with many code examples!

Java Formattable example

Problem:

How to print Java object using custom format? Since Java 5 there’s Formattable interface exactly for that purpose. Here’s an example of how to use it.

Solution:

Java has very flexible formatting syntax and allows to output text in very different ways. Since Java 5 there’s a java.util.Formattable interface that allows to apply custom formatting for printing own objects, which is very cool. :-) The interface has only one method formatTo(…) that should be implemented.

In the following example we create two books and print them applying different formatting rules: justification, alternate format, locales, applying size constraints:

package com.farenda.java.util;

import java.util.Formattable;
import java.util.Formatter;
import java.util.List;
import java.util.Locale;

import static java.util.Arrays.asList;
import static java.util.FormattableFlags.*;

public class FormattableExample {

    public static class Book implements Formattable {

        private String symbol;
        private String bookName;
        private String polishName;

        public Book(String symbol, String bookName, String polishName) {
            this.symbol = symbol;
            this.bookName = bookName;
            this.polishName = polishName;
        }

        @Override
        public void formatTo(Formatter fmt, int flags, int width, int precision) {
            StringBuilder sb = new StringBuilder();

            String name = computeName(fmt, flags, precision);
            applyPrecision(precision, sb, name);
            applyFilling(flags, width, sb);

            fmt.format(sb.toString());
        }

        private void applyFilling(int flags, int minWidth, StringBuilder sb) {
            int currentLength = sb.length();
            if (currentLength < minWidth) {
                // prepend or append spaces depending on justification
                boolean leftJustified = isEnabled(flags, LEFT_JUSTIFY);
                for (int i = 0, toAdd = minWidth - currentLength; i < toAdd; i++) {
                    if (leftJustified) {
                        sb.append(' ');
                    } else {
                        sb.insert(0, ' ');
                    }
                }
            }
        }

        private void applyPrecision(int precision, StringBuilder sb, String out) {
            if (unspecified(precision) || fitsInPrecision(precision, out)) {
                sb.append(out);
            } else {
                sb.append(out.substring(0, precision-1)).append('*');
            }
        }

        private boolean fitsInPrecision(int precision, String out) {
            return out.length() < precision;
        }

        private boolean unspecified(int precision) {
            return precision == -1;
        }

        private String computeName(Formatter fmt, int flags, int precision) {
            return shouldUseSymbol(flags, precision) ? symbol : getLocalizedName(fmt);
        }

        private String getLocalizedName(Formatter fmt) {
            return fmt.locale().equals(Locale.forLanguageTag("pl_PL"))
                    ? polishName : bookName;
        }

        private boolean shouldUseSymbol(int flags, int precision) {
            return isEnabled(flags, ALTERNATE) || (precision != -1 && precision < 10);
        }

        private boolean isEnabled(int flags, int leftJustify) {
            return (flags & leftJustify) == leftJustify;
        }

        @Override
        public String toString() {
            return String.format("[%s] %s", symbol, bookName);
        }
    }

    public static void main(String[] args) {
        List<Book> books = asList(
                new Book("GOF",
                        "Design Patterns: Elements of Reusable Object-Oriented Software",
                        "Wzorce projektowe. Elementy oprogramowania obiektowego wielokrotnego użytku"),
                new Book("Wizard Book",
                        "Structure and Interpretation of Computer Programs",
                        "Struktura i interpretacja programów komputerowych"));

        for (Book book : books) {
            printBook(book);
        }
    }

    private static void printBook(Book book) {
        System.out.println("Format: formatted output");
        System.out.printf("\"%%s\"(toString()): '%s'%n", book.toString());
        System.out.printf("\"%%s\": '%s'%n", book);
        // Alternate format:
        System.out.printf("\"%%#s\"(alternate): '%#s'%n", book);
        System.out.printf("\"%%-10.5s\"(left,width,precision): '%-10.5s'%n", book);
        System.out.printf("\"%%.13s\": '%.13s'%n", book);
        // With custom Locale:
        System.out.printf(Locale.forLanguageTag("pl_PL"),
                         "\"%%25s\"(pl_PL): '%25s'%n", book);
        System.out.println();
    }
}

And here’s the output of the program:

Format: formatted output
"%s"(toString()): '[GOF] Design Patterns: Elements of Reusable Object-Oriented Software'
"%s": 'Design Patterns: Elements of Reusable Object-Oriented Software'
"%#s"(alternate): 'GOF'
"%-10.5s"(left,width,precision): 'GOF       '
"%.13s": 'Design Patte*'
"%25s"(pl_PL): 'Wzorce projektowe. Elementy oprogramowania obiektowego wielokrotnego użytku'

Format: formatted output
"%s"(toString()): '[Wizard Book] Structure and Interpretation of Computer Programs'
"%s": 'Structure and Interpretation of Computer Programs'
"%#s"(alternate): 'Wizard Book'
"%-10.5s"(left,width,precision): 'Wiza*     '
"%.13s": 'Structure an*'
"%25s"(pl_PL): 'Struktura i interpretacja programów komputerowych'

The output is pretty clear. One thing to note is how precision and width work together. This can be seen in the second book %-10.5s format. Even though width is 10 characters, the output contains only 5 (precision size) of them from the book, the rest is padding. So the way to interpret that is: width is the size of the output and precision is the part of output designated to use.

Share with the World!