Java - Formattable interface Custom Formatter

Introduction

The Formatter class supports custom formatting through 's' and 'S' conversions.

If the argument implements java.util.Formattable interface, the 's' conversion calls the formatTo() method on the argument to get the formatted result.

The formatTo() method is passed the reference of a Formatter object, flags, width, and precision values that are used in the format specifier.

You can apply any custom logic inside the formatTo() method of the class to format the objects of your class.

The following code implements the Formattable interface.

class FormattablePerson implements Formattable {
  ...
  public void formatTo(Formatter formatter, int flags, int width, int precision) {
    String str = this.firstName + " " + this.lastName;

    int alternateFlagValue = FormattableFlags.ALTERNATE & flags;
    if (alternateFlagValue == FormattableFlags.ALTERNATE) {
      str = this.lastName + ", " + this.firstName;
    }

    // Check if uppercase variant of the conversion is being used
    int upperFlagValue = FormattableFlags.UPPERCASE & flags;
    if (upperFlagValue == FormattableFlags.UPPERCASE) {
      str = str.toUpperCase();
    }

    // Call the format() method of formatter argument,
    // so our result is stored in it and the caller will get it
    formatter.format(str);
  }
}

Note

Formattable person has a first name and a last name.

We check for alternate flag '#'.

If this flag is used in the format specifier, you format the person name in the "LastName, FirstName" format.

If the alternate flag is not used, you format the person name in the "FirstName LastName" format.

We support uppercase variant 'S' of 's' conversion.

If 'S' conversion is used, you format the person name in uppercase.

The flags are passed in as an int value as bitmask.

To check if a flag was passed, you will need to use the bitwise & operator.

The operand to be used in the bitwise & operator are defined by constants in the java.util.FormattableFlags class.

For example, to check if the format specifier uses a left-justify '-' flag, you will need to use the following logic:

int leftJustifiedFlagValue = FormattableFlags.LEFT_JUSTIFY & flags;
if (leftJustifiedFlagValue == FormattableFlags.LEFT_JUSTIFY) {
        // Left-justified flag '-' is used
}
else {
        // Left-justified flag '-' is not used
}

You can use your FormattablePerson objects with format specifiers using string conversion 's' and 'S' as shown below:

Demo

import java.util.Formattable;
import java.util.FormattableFlags;
import java.util.Formatter;

class FormattablePerson implements Formattable {
  private String firstName = "Unknown";
  private String lastName = "Unknown";

  public FormattablePerson(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }// w w  w  . ja  v a  2 s .  co  m

  public void formatTo(Formatter formatter, int flags, int width, int precision) {
    String str = this.firstName + " " + this.lastName;

    int alternateFlagValue = FormattableFlags.ALTERNATE & flags;
    if (alternateFlagValue == FormattableFlags.ALTERNATE) {
      str = this.lastName + ", " + this.firstName;
    }

    // Check if upper case variant of the conversion is being used
    int upperFlagValue = FormattableFlags.UPPERCASE & flags;
    if (upperFlagValue == FormattableFlags.UPPERCASE) {
      str = str.toUpperCase();
    }

    // Call the format() method of formatter argument,
    // so our result is stored in it and the caller will get it
    formatter.format(str);
  }
}

public class Main {
  public static void main(String[] args) {
    FormattablePerson fp = new FormattablePerson("Java", "Smith");
    System.out.printf("%s %n", fp);
    System.out.printf("%#s %n", fp);
    System.out.printf("%S %n", fp);
    System.out.printf("%#S %n", fp);

  }

}

Result