So, you have a database with millions of phone numbers with free-for-all formatting. Ie, the UI does not enforce any constraints and the users are typing in whatever they want. There must be a Java API to format these, right? Yeah, not so much.

For example, a phone number in the system might look like any of the following:

    (555) 478-1123
    555-478-1123 
    555.478.1123
    5554781123

You want an API that given the country of US, would produce the value "+1 (555) 478-1123" for all these. Of course, there are countless variations of the examples, as well as the complication of international phone numbers, which each have their own validation rules.

Well, I don't have a drop-in solution for you. But faced with the above situation, I did come up with the beginnings of a Java API that does this. For now, I have implemented US and UK formatting. Download the API here.

    PhoneNumberFactory phoneNumberFactory = new PhoneNumberFactory();

    PhoneNumber phoneNumber = phoneNumberFactory.create(CountryCodes.US, "5554781123"); 
    // phoneNumber.toString() == "+1 (555) 478-1123"

    PhoneNumber ukPhoneNumber = phoneNumberFactory.create(CountryCodes.UK, "442088709929"); 
    // phoneNumber.toString() == "+44 20 88 70 99 29"

You can define new countries by extending PhoneNumber, and implementing the logic to parse any string into country code, area code and a list of subscriber number groups. For example, "+1 (555) 478-1123" has a country code of "1", area code of "555" and a List of subscriber numbers ("478", "1123"). You can group subscriber numbers however you want, it's just a convenience for formatting.

You can can also define your own formatters. Don't like my US format? No problem!

public class MyPhoneFormat implements PhoneFormat {

    public String format(PhoneNumber phoneNumber) {

        String value = phoneNumber.getCountryCode()
              + " " + phoneNumber.getAreaCode() + getPostAreaCode(phoneNumber);

        for (int i = 0; i < phoneNumber.getSubscriberNumbers().size(); i++)
            value += phoneNumber.getSubscriberNumber(i);

        return value.trim();
    }

    //  US phone numbers are one long run of digits, UK has a space
    private String getPostAreaCode(PhoneNumber phoneNumber) {
        return phoneNumber.getCountryCode().equalsIgnoreCase(CountryCodes.UK) ? " ": "";
    }
}

This would output phone numbers in either "1 5554789119" (US) or "44 20 88709929" (UK).