package com.spun.util; /** * Phone number validation, and formatter. * this class is immutable. **/ public class PhoneNumber { public static final int USA = 0; public static final String[] COUNTRY_CODES = { "1", "20", "212", "213", "216", "218", "220", "221", "222", "223", "224", "225", "226", "227", "228", "229", "230", "231", "232", "233", "234", "235", "236", "237", "238", "239", "240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255", "256", "257", "258", "260", "261", "262", "263", "264", "265", "266", "267", "268", "269", "27", "290", "291", "297", "298", "299", "30", "31", "32", "33", "34", "350", "351", "352", "353", "354", "355", "356", "357", "358", "359", "36", "370", "371", "372", "373", "374", "375", "376", "377", "378", "380", "381", "385", "386", "387", "389", "39", "40", "41", "420", "421", "423", "43", "44", "45", "46", "47", "48", "49", "500", "501", "502", "503", "504", "505", "506", "507", "508", "509", "51", "52", "53", "5399", "54", "55", "56", "57", "58", "590", "591", "592", "593", "594", "595", "596", "597", "598", "599", "60", "61", "618", "62", "63", "64", "65", "66", "670", "672", "673", "674", "675", "676", "677", "678", "679", "680", "681", "682", "683", "684", "685", "686", "687", "688", "689", "690", "691", "692", "7", "808", "81", "82", "84", "850", "852", "853", "855", "856", "86", "870", "871", "872", "873", "874", "878", "880", "881", "8816", "8817", "88213", "88216", "886", "90", "91", "92", "93", "94", "95", "960", "961", "962", "963", "964", "965", "966", "967", "968", "970", "971", "972", "973", "974", "975", "976", "977", "98", "992", "993", "994", "995", "996", "998"}; private static final String REASONS[] = {"Phone Number Too Long or Too Short", "US Number must be length 10", "Unknown Country Code"}; /** The original value. */ private String originalValue = null; private int countryCode = USA; private String strippedValue = null; private String invalidReason = null; private int nonValidCountryCode = 0; public PhoneNumber(String originalValue) { originalValue = StringUtils.loadNullableString(originalValue); this.originalValue = originalValue; if (this.originalValue == null) { return; } this.strippedValue = stripPhoneNumber(this.originalValue); this.countryCode = getCountryCode(strippedValue); this.invalidReason = validate(countryCode, strippedValue); if (invalidReason != null) { nonValidCountryCode = countryCode; countryCode = -1; } } /**************************************************************************/ private static String stripPhoneNumber(String number) { if (number == null) {return null;} boolean xAppended = false; StringBuffer result = new StringBuffer(); for(int i = 0; i< number.length(); i++) { char c = number.charAt(i); switch (c) { case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' : case '8' : case '9' : result.append(c); break; case '+' : if (result.length() == 0){result.append(c);} break; case 'X': case 'x': { if (! xAppended) { result.append('x'); xAppended = true; } break; } default : break; } } char c = result.length() == 0 ? ' ' : result.charAt(0); switch (c) { case '+': break; // do nothing case '1': result.insert(0, "+"); break; // insert a 1 default: result.insert(0, "+1"); // if neither insert a +1 } return result.toString(); } /**************************************************************************/ private static int getCountryCode(String strippedNumber) { String alt = strippedNumber.substring(1); for (int i = 0; i < COUNTRY_CODES.length; i++) { String code = COUNTRY_CODES[i]; if (alt.startsWith(code)) { return i; } } return -1; } /**************************************************************************/ private static String validate(int countryCode, String stripedNumber) { return (countryCode == USA) ? validateNorthAmerican(countryCode, stripedNumber) : validateInternational(countryCode, stripedNumber); } /**************************************************************************/ private static String validateInternational(int countryCode, String strippedNumber) { if (countryCode == -1) return "Invalid country code"; String body = extractPhoneBody(countryCode, strippedNumber); String prefix = COUNTRY_CODES[countryCode]; int nl = prefix.length() + body.length(); return (nl > 15) || (nl < 9) ? REASONS[0] : null; } /**************************************************************************/ private static String validateNorthAmerican(int countryCode, String strippedNumber) { if (countryCode != USA) return "Invalid country code"; String body = extractPhoneBody(countryCode, strippedNumber); return (body.length() != 10) ? REASONS[0] : null; } /**************************************************************************/ private static String extractPhoneBody(int countryCode, String strippedNumber) { if (countryCode == -1) throw new IllegalArgumentException("Invalid country code"); String prefix = "+" + COUNTRY_CODES[countryCode]; String body = strippedNumber.substring(prefix.length()); int indexOfExt = body.indexOf('x'); if (indexOfExt > -1) { body = body.substring(0, indexOfExt); } return body; } /**************************************************************************/ public String getValue() { if (isValid() && strippedValue != null) { return (isNorthAmericanNumber()) ? getValueAsNorthAmerican() : getValueAsInternational(); } else { return originalValue; } } /**************************************************************************/ public String getValueAsUps() { return getValueAsUps(countryCode, this.strippedValue); } /**************************************************************************/ private static String getValueAsUps(int countryCode, String strippedNumber) { if (strippedNumber == null) { return null; } if (countryCode < 0) { throw new IllegalArgumentException("# '" + strippedNumber + "' is not an international number"); } String body = extractPhoneBody(countryCode, strippedNumber); String prefix = COUNTRY_CODES[countryCode]; if (countryCode == USA) { return body; } else { return prefix + body; } } /**************************************************************************/ private static String getValueAsNorthAmerican(int countryCode, String stripped) { if (countryCode != USA) throw new IllegalArgumentException("# '" + stripped + "' is not a NorthAmerican number"); StringBuffer number = new StringBuffer(stripped.substring(2)); number.insert(6, "-"); number.insert(3, ")"); number.insert(0, "("); return number.toString(); } /**************************************************************************/ private static String getValueAsInternational(int countryCode, String stripped) { if (stripped == null) { return null; } if (countryCode < USA) { throw new IllegalArgumentException("# '" + stripped + "' is not an international number"); } StringBuffer number = new StringBuffer(stripped); int intlLength = COUNTRY_CODES[countryCode].length() + 1; int bodyLength = extractPhoneBody(countryCode, stripped).length(); if (bodyLength > 6) { number.insert(intlLength + 6, "."); } if (bodyLength > 3) { number.insert(intlLength + 3, "."); } number.insert(intlLength, "."); return number.toString(); } /**************************************************************************/ public String getValueAsNorthAmerican() { return getValueAsNorthAmerican(this.countryCode, this.strippedValue); } /**************************************************************************/ public String getValueAsInternational() { return getValueAsInternational(this.countryCode, this.strippedValue); } /**************************************************************************/ public String getPartiallyFormattedAsInternational() { return getValueAsInternational(this.nonValidCountryCode, this.strippedValue); } /**************************************************************************/ public boolean isValid() { return (this.invalidReason == null); } /**************************************************************************/ public String getInvalidReason() { return this.invalidReason; } /**************************************************************************/ public boolean isNorthAmericanNumber() { return this.countryCode == USA; } /**************************************************************************/ public String getOriginalText() { return originalValue; } /**************************************************************************/ public String toString() { return getValue(); } /**************************************************************************/ /**************************************************************************/ public Country getCountry() { return Country.UnitedStates; } }