Monday, April 14, 2008

TelephoneNumber - Java class


The TelephoneNumber class was designed to use the java.text.MessageFormat class to format a US phone number with the proper characters and spacing. The class excepts numbers any many different ways and parses them into it's internal data so that it can display the phone number in the standard way, i.e. (800) 555-1212. It can even accept an alpha number like "877 HOLY-COW" and with the right options format it as alpha or numbers.

The TelephoneNumber class represents a US telephone number.

This class contains many constructors for various uses. It then stores the telephone number internally in seperate codes (area code, exchange, etc.). This encourages use of the format method to output the telephone number in any style the programmer wishes.

[needs i18n work]

Example usage:
long number = 8005551212L;
TelephoneNumber phone = new TelephoneNumber(number);
StringBuffer msg = new StringBuffer("Call me at ");
msg.append(phone.format());
// msg is now "Call me at (800) 555-1212"


US Format:

The general US format is a 3 digit area code, a 3 digit exchange, a 4 digit station. Typically written (800) 555-1212. Area codes are concidered optional unless you live in a '10 digit' calling area as many people now do. In which case you may want to set the isTenDigitArea flag which removes the parenthesis from the String output ie Denver area code requires 10 digit dailing 303 555-1212.


International Format:

International phone numbers are often written in this format:
+CC-(N)AAA-EEE-SSSS.

This expresses the numbers used for both international and national long-distance calls. In the example, +CC indicates the country code, while (N) indicates the NDD. When dialing from outside the country, the NDD would not be used after dialing the country code; when dialing from within that country, the NDD would be used, but the country code would not. Where AAA indicates the area code, EEE indicates the exchange, and SSSS indicates the station.

Country Code:

The country code is the national prefix to be used when dialing TO that particular country FROM another country.

NDD Prefix: (National Direct Dialing)

The NDD prefix is the access code used to make a call WITHIN that country from one city to another (when calling another city in the same vicinity, this may not be necessary). The NDD is followed by the city/area code for the place you are calling.

IDD Prefix: (International Direct Dialing)

The IDD prefix is the international prefix needed to dial a call FROM a country TO another country. This is followed by the country code for the country you are calling. The IDD is not part of the telephone number - but is a property of the locale, or the 'dailing device'.

Outside Line Prefix:

The Outside line prefix is use inside some private branch exchange (PBX) to connect with the outer exchange/trunk. It is typically 9 in the average US company. This is not part of the telephone number - but once again the property of the locale, or the 'dailing device'.


/*
* Copyright (c) 2003 David A. Koontz
*    Licensed under the Open Software License version 2.0
*
*    Grant of Copyright License. Licensor hereby grants You a world-wide,
*    royalty-free, non-exclusive, perpetual, sublicenseable license to
*    do the following:

*     a) to reproduce the Original Work in copies;
*     b) to prepare derivative works ("Derivative Works") based upon
*        the Original Work;
*     c) to distribute copies of the Original Work and Derivative Works
*        to the public, with the proviso that copies of Original Work or
*        Derivative Works that You distribute shall be licensed under the
*        Open Software License;
*     d) to perform the Original Work publicly; and
*     e) to display the Original Work publicly.
*/

//package com..util;

import java.text.MessageFormat;

/**
* The TelephoneNumber class represents a US telephone number.
*
* This class contains many constructors for various uses. It then stores the
* telephone number internally in seperate codes (area code, exchange, etc.).
* This encourages use of the format method to output the
* telephone number in any style the programmer wishes.
*
* [needs i18n work]
*
* Example usage:
*

*       long number = 8005551212L;
*       TelephoneNumber phone = new TelephoneNumber(number);
*       StringBuffer msg = new StringBuffer("Call me at ");
*       msg.append(phone.format());
*       // msg is now "Call me at (800) 555-1212"
*

*
* US Format:
* The general US format is a 3 digit area code, a 3 digit exchange, a 4 digit
* station. Typically written (800) 555-1212.  Area codes are concidered
* optional unless you live in a '10 digit' calling area as many people now do.
* In which case you may want to set the isTenDigitArea flag which
* removes the parenthesis from the String output ie Denver area code
* requires 10 digit dailing 303 555-1212.
*
* International Format:
* International phone numbers are often written in this format:
*    +CC-(N)AAA-EEE-SSSS.   
* This expresses the numbers used for both international and
* national long-distance calls.  In the example, +CC indicates
* the country code, while (N) indicates the NDD.  When dialing
* from outside the country, the NDD would not be used after dialing
* the country code; when dialing from within that country, the NDD
* would be used, but the country code would not. Where AAA indicates
* the area code, EEE indicates the exchange, and SSSS indicates the
* station.
*
*
*
* Country Code:
* The country code is the national prefix to be used when dialing TO
* that particular country FROM another country.
*
*
* NDD Prefix: (National Direct Dialing)
* The NDD prefix is the access code used to make a call WITHIN
* that country from one city to another (when calling another city in
* the same vicinity, this may not be necessary).
* The NDD is followed by the city/area code for the place you are
* calling.
*
*
* IDD Prefix: (International Direct Dialing)
* The IDD prefix is the international prefix needed to dial a call
* FROM a country TO another country. This is
* followed by the country code for the country you are calling.
* The IDD is not part of the telephone number - but is a property of
* the locale, or the 'dailing device'.
*
* Outside Line Prefix:
* The Outside line prefix is use inside some private branch exchange (PBX)
* to connect with the outer exchange/trunk. It is typically 9 in the
* average US company.  This is not part of the telephone number - but
* once again the property of the locale, or the 'dailing device'.
*
*
*
* @author David A. Koontz mailto:dakoontz@yahoo.com">[dakoontz@yahoo.com]

* @version 1.0, 7/20/2003
* @see MessageFormat
* @see http://www.opensource.org/licenses/osl-2.0.php">Open Software License
version 2.0
*
*/
public class TelephoneNumber {

    // International direct dial code is not part of a telephone
    // number - it would be info in the locale that the dialer is in.
    /** National direct dial code */
    protected String ndd;             // NDD of US is 1; UK is 0
    /** Country code */
    protected String countryCode;     // Country code of US is 1; UK is 44
    /** Area code */
    protected String areaCode;
    /** Exchange */
    protected String exchange;
    /** Station */
    protected String station;
    /** Extension */
    protected String extension;
    /** Is phone number part of a ten digit dialing area. */
    protected boolean isTenDigitArea = false;
    /** Alphanumeric phone number */
    protected String alphaNumber;    // used when we store "(800) GOT MILK"
    /** Is phone number an alphanumberic */
    protected boolean isAlphaNumber = false;

    /** The default format pattern - US centric */
    protected String defaultPatternUS = "({2}) {3}-{4}";
    /** The default format pattern */
    protected String defaultPattern = defaultPatternUS;

    /**
     * Basic constructor.
     */
    public TelephoneNumber() {
        this(null, null, null, "555", "1212", null);
    }

    /**
     * General constructor.
     * Create +1-(1)(800) 555-1212 ext: 34
     *
     * @param countryCode    the country code (US is 1; UK is 44)
     * @param ndd            the national direct dial code (US is 1; UK is 0)
     * @param areaCode       the area code (or city code)
     * @param exchange       the exchange
     * @param station        the station
     * @param extension      the extension (inside the PBX)
     */
    public TelephoneNumber(String countryCode, String ndd, String areaCode, String exchange, String station, String extension) {
        if ( exchange == null )
                throw new IllegalArgumentException("null arg forms not allowed for 'exchange'");
        if ( station == null )
                throw new IllegalArgumentException("null arg forms not allowed for 'station'");
        this.countryCode = countryCode;
        this.ndd         = ndd;
        this.areaCode    = areaCode;
        this.exchange    = exchange;
        this.station     = station;
        this.extension   = extension;
    }

    /**
     * Create (800) 555-1212 ext: 34
     */
    public TelephoneNumber(String areaCode, String exchange, String station, String extension) {
        this(null, null, areaCode, exchange, station, extension);

    }

    /**
     * Create  555-1212 ext: 34
     */
    public TelephoneNumber( String exchange, String station, String extension) {
        this(null, null, null, exchange, station, extension);

    }

    /**
     * Create 555-1212
     */
    public TelephoneNumber( String exchange, String station) {
        this(null, null, null, exchange, station, null);

    }

    /**
     * Create 888 HOLY COW by using alphanumeric digits
     */
    public TelephoneNumber( boolean isAlphaNumeric, String alphaNumber) {
        this();
        this.isAlphaNumber = isAlphaNumeric;
        if ( isAlphaNumber == true ) {
            this.alphaNumber = alphaNumber;
            // parse it
            String[] nums = parse(convertFromAlpha(alphaNumber));
            // ignore country code & NDD
            this.areaCode = nums[1];
            this.exchange = nums[2];
            this.station  = nums[3];
            this.extension= nums[4];

        } else {
            // parse this also - assuming no alpha char.
            String[] nums = parse(alphaNumber);
            // ignore country code & NDD
            this.areaCode = nums[1];
            this.exchange = nums[2];
            this.station  = nums[3];
            this.extension= nums[4];
        }
    }

    /**
     * Create  (888) 555-1212 by parsing string
     */
    public TelephoneNumber( String str) {
        this();
        // parse the string into numbers array
        String[] nums = parse(str);
        // ignore country code & NDD
        this.areaCode = nums[1];
        this.exchange = nums[2];
        this.station  = nums[3];
        this.extension= nums[4];
    }

    /**
     * Create (479) 555-1212 by parsing a long integer
     */
    public TelephoneNumber(long number) {
        this();
        String[] nums = parse(number);

        // convert from longs to strings
        this.areaCode = nums[1];
        this.exchange = nums[2];
        this.station = nums[3];

    }

    /** Create 555-1212 via long integers
     */
    public TelephoneNumber( long exchange, long station) {
        this();
        // convert from longs to strings
        if (exchange > 0) {
            this.exchange = String.valueOf(exchange);
        }
        if (station > 0) {
            this.station = String.valueOf(station);
        }
    }

    /**
     * Create (479) 555-1212 via long integers
     */
    public TelephoneNumber(long areaCode, long exchange, long station) {
        this();
        // convert from longs to strings
        if (areaCode > 0) {
            this.areaCode = String.valueOf(areaCode);
        }
        if (exchange > 0) {
            this.exchange = String.valueOf(exchange);
        }
        if (station > 0) {
            this.station = String.valueOf(station);
        }
    }


    /**
     * Parse a string like "(888) 555-1212 x 68" into a phone number.
     * Requires area code.
     * Does not allow country codes etc.
     * US centric.
     */
    protected String[] parse( String str) {
        String[] numbers = new String[5];
        StringBuffer ndd = new StringBuffer();
        StringBuffer area = new StringBuffer();
        StringBuffer exch = new StringBuffer();
        StringBuffer stat = new StringBuffer();
        StringBuffer ext = new StringBuffer();
        char ch = ' ';
        int areaCount = 0;
        int exchCount = 0;
        int statCount = 0;

        if ( str != null && str.length()>0 ) {
            // for each char in string
            for (int i = 0; i < str.length(); i++) {
                ch = str.charAt(i);
                // only look at digits
                if ( Character.isDigit(ch) ) {
                    // this is only finding this pattern 123 123 1234 99...
                    if ( areaCount < 3 ) {
                        area.append(ch);
                        areaCount++;
                    } else if ( exchCount < 3 ) {
                        exch.append(ch);
                        exchCount++;
                    } else if ( statCount < 4 ) {
                        stat.append(ch);
                        statCount++;
                    } else {
                        ext.append(ch);
                    }
                    // doesnot work for "571 3345 ext 897"
                    //@todo - get better parser
                }


            }
        }
        numbers[0] = ndd.toString();
        numbers[1] = area.toString();
        numbers[2] = exch.toString();
        numbers[3] = stat.toString();
        numbers[4] = ext.toString();

        return numbers;
    }

    /**
     * Parse a long integer like 4795718080 into phone number.
     * Does not allow country codes or extensions.
     * US centric.
     */
    protected String[] parse(long number) {
        String[] nums = new String[5];
        long area = 0;
        long exch = 0;
        long stat = 0;
        // parse the long from right to left
        // assume US style phone number without extension
        // example: 8005551212 or 2368000
        if (number > 0) {
            if (number > 9999) {
                exch = number / 10000;
                stat = number - (exch * 10000);
                if ( exch > 999 ) {
                    area = exch / 1000;
                    exch = exch - (area * 1000);
                }
            }
        }
        // convert from longs to strings
        if (area > 0) {
            nums[1] = String.valueOf(area);
        }
        if (exch > 0) {
            nums[2] = String.valueOf(exch);
        }
        if (stat > 0) {
            nums[3] = String.valueOf(stat);
        }

        return nums;
    }

    /** Convert alphanumeric phone number to numeric. */
    protected static String convertFromAlpha(String alpha) {
        // If you don't want US style key pad - change it.
        return convertFromAlphaUS(alpha);
    }

    /** Convert alphanumeric phone number to numeric - US centric */
    protected static String convertFromAlphaUS(String alpha) {
        // convert a US alphanumeric number into numberic
        // example:  800 GOT MILK -> 800 468-6455
        // where the Alpha to Numberic Key Pad
        //
        //      1     2     3
        //           ABC   DEF
        //
        //      4     5     6
        //     GHI   JKL   MNO
        //
        //      7     8     9
        //    PQRS   TUV   WXYZ
        //
        //     *      0     #
        //

        // prep the alpha string
        StringBuffer sb = new StringBuffer(alpha.toUpperCase().trim());

        // for each char in the string
        for (int i = 0; i < sb.length(); i++) {
            char ch = sb.charAt(i);
            if ( Character.isDigit(ch) ) {
                ; // ignore it
            } else if ( ch == '(' || ch == ')' ) {
                ; // ignore them
            } else if ( ch == 'A' || ch == 'B' || ch == 'C' ) {
                sb.deleteCharAt(i);
                sb.insert(i, '2');
            } else if ( ch == 'D' || ch == 'E' || ch == 'F' ) {
                sb.deleteCharAt(i);
                sb.insert(i, '3');
            } else if ( ch == 'G' || ch == 'H' || ch == 'I' ) {
                sb.deleteCharAt(i);
                sb.insert(i, '4');
            } else if ( ch == 'J' || ch == 'K' || ch == 'L' ) {
                sb.deleteCharAt(i);
                sb.insert(i, '5');
            } else if ( ch == 'M' || ch == 'N' || ch == 'O' ) {
                sb.deleteCharAt(i);
                sb.insert(i, '6');
            } else if ( ch == 'P' || ch == 'Q' || ch == 'R' || ch == 'S') {
                sb.deleteCharAt(i);
                sb.insert(i, '7');
            } else if ( ch == 'T' || ch == 'U' || ch == 'V' ) {
                sb.deleteCharAt(i);
                sb.insert(i, '8');
            } else if ( ch == 'W' || ch == 'X' || ch == 'Y' || ch == 'Z' ) {
                sb.deleteCharAt(i);
                sb.insert(i, '9');
            } else {
                ; // ignore
            }

        }

        return sb.toString();
    }

    /**
     * Format the telephone number with the supplied MessageFormat pattern.
     * The pattern has 7 messageFormatElement arguments (0 - 6).
     *
     * argument 0 - ndd
     * argument 1 - countryCode
     * argument 2 - areaCode
     * argument 3 - exchange
     * argument 4 - station
     * argument 5 - extension
     * argument 6 - alphanumeric
     *
     * Examples:
     * "({2}) {3}-{4} ext {5}" gives "(800) 555-1212 ext 99"
     * "{2}.{3}.{4}" gives "800.555.1212"
     * "{6} [{2} {3}-{4}]" gives "888 HOLY COW [888 465-9269]"
     *
     * @param pattern    the format pattern to be used
     * @see MessageFormat
     *
     */
    public String format(String pattern) {
        // create objects array from telephone codes
        Object[] objects = {ndd, countryCode, areaCode, exchange, station, extension, alphaNumber};

        // create a MessageFormat with pattern
        MessageFormat mf = new MessageFormat(pattern);
        return mf.format(objects);
    }

    /**
     * Format the telephone number with the defaultPattern MessageFormat.
     * US centric.
     */
    public String format() {
        // create objects array from telephone codes
        Object[] objects = {ndd, countryCode, areaCode, exchange, station, extension, alphaNumber};

        // create a MessageFormat with default pattern
        MessageFormat mf = new MessageFormat(defaultPattern);
        return mf.format(objects);
    }

    /** Human readable output
     */
    public String toString() {
        return toStringUS();
    }

    /** US centric human readable output
     */
    public String toStringUS() {
        // use US convention: "(800) 555-1212 ext: 20"
        StringBuffer sb = new StringBuffer(20);
        if ( this.isAlphaNumber ) {
            // just spit back the string
            sb.append(this.alphaNumber);
            sb.append(" [").append(areaCode).append(" ").append(exchange).append(" ").append(station).append("]");
        } else {

            if (countryCode != null && countryCode.length() > 0) {
                // format country code as '+CC-'
                sb.append("+").append(countryCode).append("-");
            }
            if (ndd != null && ndd.length() > 0) {
                if (countryCode != null && countryCode.equals("1") ) {
                    // in the US we don't write both the country code & the NDD
                    ; // igonre the NDD
                } else {
                    // format NDD as '(N) '
                    sb.append("(").append(ndd).append(") ");
                }
            }

            if (areaCode != null && areaCode.length() > 0) {
                // we could use a java.text.Format - but noooooo
                // assume US style (000)
                if ( areaCode.length() == 1 ) {
                    if (isTenDigitArea == true) {
                        sb.append("00").append(areaCode).append(" ");
                    } else {
                        sb.append("(").append("00").append(areaCode).append(") ");
                    }
                } else if (areaCode.length() == 2 ) {
                    if (isTenDigitArea == true) {
                        sb.append("0").append(areaCode).append(" ");
                    } else {
                        sb.append("(").append("0").append(areaCode).append(") ");
                    }
                } else if (areaCode.length() == 3 ) {
                    if (isTenDigitArea == true) {
                        sb.append(areaCode).append(" ");
                    } else {
                        sb.append("(").append(areaCode).append(") ");
                    }
                } else {
                    // more than 3 digits? - OK whatever
                    if (isTenDigitArea == true) {
                        sb.append(areaCode).append(" ");
                    } else {
                        sb.append("(").append(areaCode).append(") ");
                    }
                }
            }

            // exchange - assume US style 000
            if ( exchange.length() == 1 ) {
                sb.append("00").append(exchange).append("-");
            } else if (exchange.length() == 2 ) {
                sb.append("0").append(exchange).append("-");
            } else if (exchange.length() == 3 ) {
                sb.append(exchange).append("-");
            } else {
                // more than 3 digits? - OK whatever
                sb.append(exchange).append("-");
            }

            // station - assume US style 0000
            if ( station.length() == 1 ) {
                sb.append("000").append(station);
            } else if (station.length() == 2 ) {
                sb.append("00").append(station);
            } else if (station.length() == 3 ) {
                sb.append("0").append(station);
            } else {
                sb.append(station);
            }

            if (extension != null && extension.length() > 0) {
                sb.append(" ext: ").append(extension);
            }
        }

        return sb.toString();
    }

    /** MAIN - a test stub */
    public static void main(String[] args) {
        // test it out
        TelephoneNumber  n1 = new TelephoneNumber(); // test null params
        TelephoneNumber  n2 = new TelephoneNumber(571L, 9999L); // test 2 longs
        TelephoneNumber  n3 = new TelephoneNumber(479L, 571L, 1003L); // test 3 longs
        TelephoneNumber  n4 = new TelephoneNumber(303L, 222L, 7800L);   // test ten digit dialing
                         n4.isTenDigitArea = true;

        TelephoneNumber  n5 = new TelephoneNumber("(888) 341-8876");  // test formated string
        TelephoneNumber  n6 = new TelephoneNumber(127, 345, 8765); // test 3 integers
        TelephoneNumber  n7 = new TelephoneNumber(true, "800 GOT MILK");  // test parsing Alpha
        TelephoneNumber  n8 = new TelephoneNumber(3L, 4L, 5L);     // test zero padding
        TelephoneNumber  n9 = new TelephoneNumber("1", "1", "479", "571", "8509", "40"); // test 6 strings
        TelephoneNumber n10 = new TelephoneNumber(9876543210L);    // test 1 long
        TelephoneNumber n11 = new TelephoneNumber(false, "/800/ 555-1212 x 0"); // test parsing Non-Alpha

        System.out.println("TelephoneNumber(): " + n1);
        System.out.println("TelephoneNumber(571L, 9999L): " + n2);
        System.out.println("TelephoneNumber(479L, 571L, 1003L): " + n3);
        System.out.println("TelephoneNumber(303L, 222L, 7800L): " + n4);
        System.out.println("TelephoneNumber(\"(888) 341-8876\"): " + n5);
        System.out.println("TelephoneNumber(127, 345, 8765): " + n6);
        System.out.println("TelephoneNumber(true, \"800 GOT MILK\"): " + n7);
        System.out.println("TelephoneNumber(3L, 4L, 5L): " + n8);
        System.out.println("TelephoneNumber(\"1\", \"1\", \"479\", \"571\", \"8509\", \"40\"): " + n9);
        System.out.println("TelephoneNumber(9876543210L): " + n10);
        System.out.println("TelephoneNumber(false, \"/800/ 555-1212 x 0\"): " + n11);
        System.out.println("TelephoneNumber.format(\"{6} [{2} {3}-{4}]\"): " + n7.format("{6} [{2} {3}-{4}]"));
        System.out.println("TelephoneNumber.format(): " + n9.format());
    }

    /******************************************************************/
    /***  Getter/Setter                                             ***/
    /******************************************************************/

    public String getNdd() {
        return ndd;
    }

    public void setNdd(String value) {
        ndd = value;
    }

    public String getCountryCode() {
        return countryCode;
    }

    public void setCountryCode(String value) {
        countryCode = value;
    }

    public String getAreaCode() {
        return areaCode;
    }

    public void setAreaCode(String value) {
        areaCode = value;
    }

    public String getExchange() {
        return exchange;
    }

    public void setExchange(String value) {
        exchange = value;
    }

    public String getStation() {
        return station;
    }

    public void setStation(String value) {
        station = value;
    }

    public String getExtension() {
        return extension;
    }

    public void setExtension(String value) {
        extension = value;
    }

    public boolean isTenDigitArea() {
        return isTenDigitArea;
    }

    public void setTenDigitArea(boolean value) {
        isTenDigitArea = value;
    }

    public String getAlphaNumber() {
        return alphaNumber;
    }

    public void setAlphaNumber(String value) {
        alphaNumber = value;
    }

    public boolean isAlphaNumber() {
        return isAlphaNumber;
    }

    public void setAlphaNumber(boolean value) {
        isAlphaNumber = value;
    }
}


Post a Comment