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;
}
}