All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.phone.utils.PhoneNumberUtils Maven / Gradle / Ivy

The newest version!
package com.github.phone.utils;

import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
import com.google.i18n.phonenumbers.Phonenumber;
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class PhoneNumberUtils {

    private static Logger log = LoggerFactory.getLogger(PhoneNumberUtils.class);

    private static final String EMPTY_COUNTRY_CODE = "null";
    private static final String UNKNOWN_REGION = "ZZ";
    private static final String JUST_NUMBERS = "[^\\w\\s\\.]";

    static com.google.i18n.phonenumbers.PhoneNumberUtil phoneUtil;

    static {
        phoneUtil = com.google.i18n.phonenumbers.PhoneNumberUtil.getInstance();
    }

    public static Phonenumber.PhoneNumber parsePhoneByGoogle(String phone, String country) {

        try {
            return phoneUtil.parse(phone, country);

        } catch (NumberParseException e) {
            log.warn(e.getMessage() + " country: " + country + " phone: " + phone);
        }
        return null;
    }

    public static boolean hasCountryCode(int code, String phoneNumber) {
        return isPossibleFullPhoneNumber(phoneNumber) && getCountryCodeFromFullPhoneNumber(phoneNumber) == code;
    }

    /**
     * Given a possible full phone number, starting with +, country code part of number
     * will be returned.
     *
     * @param fullPhoneNumber Full phone number, starting with +
     * @return The country code
     * @throws PhoneNumberParsingException if phone number not valid
     */
    public static int getCountryCodeFromFullPhoneNumber(String fullPhoneNumber) {
        try {
            PhoneNumber phoneNumber = phoneUtil.parse(fullPhoneNumber, UNKNOWN_REGION);
            return phoneNumber.getCountryCode();
        } catch (NumberParseException e) {
            throw new PhoneNumberParsingException(e);
        }
    }

    /**
     * Italian numbers are strange, this method checks if number is italian,
     * and thereby leading 0 in national part valid.
     * @param fullPhoneNumber Full phone number, starting with +
     * @return True if italian number, false otherwise
     */
    public static boolean isItalianOrUnknownNumber(String fullPhoneNumber) {
        PhoneNumber phoneNumber;
        try {
            phoneNumber = phoneUtil.parse(fullPhoneNumber, UNKNOWN_REGION);
        } catch (NumberParseException e) {
            return true;
        }
        return phoneNumber.isItalianLeadingZero();
    }

    /**
     * Given a possible full phone number, starting with +, country code part of number
     * will be returned with + added as prefix.
     * @param fullPhoneNumber Full phone number, starting with +
     * @return The country code with prefix +
     * @throws PhoneNumberParsingException if phone number not valid
     */
    public static String getCountryCodeWithPlusSignFromFullPhoneNumber(String fullPhoneNumber) {
        try {
            PhoneNumber phoneNumber = phoneUtil.parse(fullPhoneNumber, UNKNOWN_REGION);
            return "+" + String.valueOf(phoneNumber.getCountryCode());
        } catch (NumberParseException e) {
            throw new PhoneNumberParsingException(e);
        }
    }

    /**
     * Will get national part of phone number
     * @param fullPhoneNumber Full phone number, starting with +
     * @return the national number
     * @throws PhoneNumberParsingException if phone number not valid
     */
    public static String getPhoneNumberWithoutCountryCodeFromFullPhoneNumber(String fullPhoneNumber) {
        try {
            PhoneNumber phoneNumber = phoneUtil.parse(fullPhoneNumber, UNKNOWN_REGION);
            StringBuilder nationalNumber = new StringBuilder();
            if (phoneNumber.isItalianLeadingZero()) {
                nationalNumber.append("0");
            }
            nationalNumber.append(Long.toString(phoneNumber.getNationalNumber()));
            return nationalNumber.toString();
        } catch (NumberParseException e) {
            throw new PhoneNumberParsingException(e);
        }
    }

    /**
     * Returns a Google PhoneNumber object built by parsing provided phone number
     * @param fullPhoneNumber Full phone number, starting with +
     * @return Google PhoneNumber object
     * @throws PhoneNumberParsingException if phone number not valid
     */
    public static PhoneNumber getPhoneNumberObjFromFullPhoneNumber(String fullPhoneNumber) {

        try {
            return  phoneUtil.parse(fullPhoneNumber, UNKNOWN_REGION);
        } catch (NumberParseException e) {
            throw new PhoneNumberParsingException(e);
        }
    }

    /**
     * Comparing if two national numbers are the same.
     * @param phone1 first number to check
     * @param phone2 second number to check
     * @return True if the same, false otherwise
     * @throws PhoneNumberParsingException phone numbers not valid
     */
    public static boolean areNationalNumbersSame(String phone1, String phone2) {

        if(phone1 == null || phone1.isEmpty() || phone2 == null || phone2.isEmpty()) {
            return false;
        }

        try{

            Long number1 = getNationalNumber(phone1);
            Long number2 = getNationalNumber(phone2);

            return !(number1 == null || number2 == null) && number1.equals(number2);

        } catch (PhoneNumberParsingException e) {
            return false;
        }
    }

    /*
     * Input can be with or without country code, and with or without country prefix at all.
     * We will do our best in the method. If all other fails we will simply return the raw phone
     * number given as input, and with all non numeric characters removed.
     */
    public static Long getNationalNumber(String phoneNumber) {
        if(phoneNumber == null || phoneNumber.trim().isEmpty()) {
            return null;
        }
        try{

            PhoneNumber phoneObj = getPhoneNumberObjFromFullPhoneNumberAddPlusPrefixIfNotExist(phoneNumber);

            return phoneObj.getNationalNumber();

        }catch(PhoneNumberParsingException e) {
            return removeAllNonNumeric(phoneNumber);
        }
    }

    /*
     * Be aware that this method is kind of hack.
     *
     * https://groups.google.com/forum/#!topic/libphonenumber-discuss/IqP4cC8udn0
     */
    public static PhoneNumber getPhoneNumberObjFromFullPhoneNumberAddPlusPrefixIfNotExist(String fullPhoneNumber) {

        try {

            return getPhoneNumberObjFromFullPhoneNumber(fullPhoneNumber);

        } catch (PhoneNumberParsingException e) {

            // if doesnt exist, lets add plus prefix and
            if(fullPhoneNumber == null || fullPhoneNumber.isEmpty()) {
                throw new PhoneNumberParsingException("Phone number is null or empty: "+fullPhoneNumber);
            }
            // a full phone number requires plus prefix, add if it doesn't exist
            if(!fullPhoneNumber.startsWith("+") && fullPhoneNumber.startsWith("1")) {
                fullPhoneNumber = "+"+fullPhoneNumber;
            }

            if(isValidFullPhoneNumberHelper(fullPhoneNumber)) {
                return getPhoneNumberObjFromFullPhoneNumber(fullPhoneNumber);
            }

            throw e;
        }
    }

    /*
     * Checks if number is valid. Adds default country code provided if phone number is not complete.
     */
    public static boolean isValidPhoneNumber(String defaultCountryCode, String phoneNumber) {
        return isValidFullPhoneNumberHelper(
                generateFullPhoneNumber(defaultCountryCode, phoneNumber)
        );
    }

    public static boolean isValidNorwegianPhoneNumber(String phoneNumber) {
        return isValidPhoneNumber("+47", phoneNumber);
    }

    public static List validatePhoneNumbers(List numbers) {
        if(numbers == null) {
            return new ArrayList<>();
        }
        return numbers.stream()
                .filter(n -> n != null)
                .filter(n -> !n.isEmpty())
                .filter(n -> PhoneNumberUtils.isValidPhoneNumber("+47", n))
                .distinct()
                .map(n -> PhoneNumberUtils.generateFullPhoneNumber("+47", n))
                .collect(Collectors.toList());
    }

    public static String generateFullPhoneNumber(String defaultCountryCode, String phoneNumber) {

        if(phoneNumber == null) {
            return null;
        }

        // else, remove all eventual invalid characters
        phoneNumber = removeNonInteger(phoneNumber);

        // first check if already valid number
        if(isValidFullPhoneNumberHelper(phoneNumber)) {
            return phoneNumber;
        }

        try {

            String region = phoneUtil.getRegionCodeForCountryCode(Integer.parseInt(defaultCountryCode));
            PhoneNumber phoneNumberObj = phoneUtil.parse(phoneNumber, region);
            long phonePrefix = phoneNumberObj.getCountryCode();
            long nationalNumber = phoneNumberObj.getNationalNumber();

            return "+"+phonePrefix + nationalNumber;

        } catch (NumberParseException | NumberFormatException e) {
            log.error(e.getMessage(), e);
        }

        // we give up
        return phoneNumber;
    }

    public static String generateFullNorwegianPhoneNumber(String phoneNumber) {
        if(!isValidNorwegianPhoneNumber(phoneNumber)) {
            throw new PhoneNumberParsingException("Not valid norwegian number: "+phoneNumber);
        }
        return generateFullPhoneNumber("+47", phoneNumber);
    }

    public static PhoneNumber parseNumber(String fullPhoneNumber, String defaultCountryCode, String phoneNumber)
        throws PhoneNumberParsingException {

        try {
            // first check if already valid number
            if(isValidFullPhoneNumberHelper(fullPhoneNumber)) {
                return phoneUtil.parse(fullPhoneNumber, UNKNOWN_REGION);
            }
            return parseNumber(defaultCountryCode, phoneNumber);

        } catch (NumberParseException e) {
            throw new PhoneNumberParsingException(e);
        }
    }

    public static PhoneNumber parseNumber(String countryCode, String phoneNumber) throws PhoneNumberParsingException {

        if(phoneNumber == null) {
            throw new PhoneNumberParsingException("Input phone number is null");
        }

        // else, remove all eventual invalid characters
        phoneNumber = removeNonInteger(phoneNumber);

        try {

            // first check if already valid number
            if(isValidFullPhoneNumberHelper(phoneNumber)) {
                return phoneUtil.parse(phoneNumber, UNKNOWN_REGION);
            }

            String region = phoneUtil.getRegionCodeForCountryCode(Integer.parseInt(countryCode));
            PhoneNumber obj = phoneUtil.parse(phoneNumber, region);
            if (!isValidPhoneNumber(countryCode, phoneNumber)) {
                throw new PhoneNumberParsingException(String.format("Prefix: %s, national: %s are not valid number",
                    countryCode, phoneNumber));
            }
            return obj;

        } catch (NumberParseException | NumberFormatException e) {
            throw new PhoneNumberParsingException(e);
        }
    }

    public static String formatPhoneNumber(PhoneNumber obj) {
        if(obj == null) {
            throw new PhoneNumberParsingException("Obj is null");
        }
        return "+"+obj.getCountryCode() + obj.getNationalNumber();
    }

    public static PhoneNumberHolder formatPhoneNumberHolder(PhoneNumber obj) {
        if(obj == null) {
            throw new PhoneNumberParsingException("Obj is null");
        }
        return new PhoneNumberHolder(obj.getCountryCode(), obj.getNationalNumber(), formatPhoneNumber(obj));
    }

    public static boolean isValidFullPhoneNumberHelper(String fullPhoneNumber) {
        try {
            PhoneNumber phoneNumber = phoneUtil.parse(fullPhoneNumber, UNKNOWN_REGION);
            return phoneUtil.isValidNumber(phoneNumber);
        } catch (NumberParseException e) {
            return false;
        }
    }

    public static boolean isPossibleFullPhoneNumber(String fullPhoneNumber) {
        if (null == fullPhoneNumber) {
            return false;
        }

        String regexp = "((?:[a-z][a-z]+))";
        Pattern pattern = Pattern.compile(regexp, Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
        Matcher matcher = pattern.matcher(fullPhoneNumber);

        if (matcher.find()) {
            return false;
        }

        try {
            PhoneNumber phoneNumber = phoneUtil.parse(fullPhoneNumber, UNKNOWN_REGION);
            return phoneUtil.isPossibleNumber(phoneNumber);
        } catch (NumberParseException e) {
            return false;
        }
    }

    public static String normalizePhoneNumber(String phoneNumber) {

        PhoneNumber pNumber;
        try {
            pNumber = phoneUtil.parse(phoneNumber, UNKNOWN_REGION);
        } catch (NumberParseException e) {
            log.debug("bad  number:" + phoneNumber);
            throw new PhoneNumberParsingException("phone number invalid: " + phoneNumber);
        }
        phoneNumber = phoneUtil.format(pNumber, PhoneNumberFormat.E164);

        if (!phoneUtil.isPossibleNumber(pNumber)) {
            throw new PhoneNumberParsingException("phone number invalid: " + phoneNumber);
        }

        return phoneNumber;
    }

    public static String replaceInternationalCallingPrefixWithPlus(String phoneNumber) {
        String changedPhoneNumber = phoneNumber;
        if (changedPhoneNumber != null && changedPhoneNumber.startsWith("00")) {
            changedPhoneNumber = "+" + phoneNumber.substring(2);
        }
        return changedPhoneNumber;
    }

    public static boolean hasCountryCode(String phoneNumber) {
        String changedPhoneNumber = replaceInternationalCallingPrefixWithPlus(phoneNumber);
        try {
            getCountryCodeFromFullPhoneNumber(changedPhoneNumber);
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    public static String appendCountryCodeIfMissingAndNormalize(String phoneNumber, String countryCode) {

        if(phoneNumber != null && phoneNumber.startsWith("00")) {
            phoneNumber = "+"+phoneNumber.substring(2);
        }

        String countryCodeNum;
        String region = null;
        if(countryCode != null && !countryCode.isEmpty()) {
            countryCodeNum = countryCode.replaceAll(JUST_NUMBERS, "");
            region = phoneUtil.getRegionCodeForCountryCode(Integer.parseInt(countryCodeNum));
        }

        PhoneNumber pNumber;
        try {
            pNumber = phoneUtil.parse(phoneNumber, region);
        } catch (NumberParseException e) {
            log.debug("bad  region: " + region + ",  or number:" + phoneNumber);
            throw new PhoneNumberParsingException("phone number invalid: " + phoneNumber);
        }

        if (!phoneUtil.isPossibleNumber(pNumber)) {
            throw new PhoneNumberParsingException("phone number invalid: " + phoneNumber);
        }

        phoneNumber = phoneUtil.format(pNumber, PhoneNumberFormat.E164);
        return phoneNumber;
    }

    public static String getPhoneWithoutCountryCode(String phoneNumber, String countryCode) {
        String countryCodeNum = countryCode.replaceAll(JUST_NUMBERS, "");
        String region = phoneUtil.getRegionCodeForCountryCode(Integer.parseInt(countryCodeNum));
        PhoneNumber pNumber = null;
        try {
            pNumber = phoneUtil.parse(phoneNumber, region);
        } catch (NumberParseException e) {
            throw new PhoneNumberParsingException("phone number error: " + phoneNumber);
        }
        // countryCode = countryCode.replaceAll("\\+", "\\\\+"); - what is it?
        return pNumber.isItalianLeadingZero() ? "0" + pNumber.getNationalNumber() : "" + pNumber.getNationalNumber();
    }


    public static String removeNationalLeadingZero(String phoneNumber) {
        //
        if(phoneNumber == null || phoneNumber.isEmpty()) {
            return phoneNumber;
        }

        try{
            // if normalization succeeds, its a perfect valid number with country code, lets just normalize it
            return normalizePhoneNumber(phoneNumber);

        }catch (PhoneNumberParsingException e) {
            // else its probably without country code, lets check if it starts with leading zero
            if(phoneNumber.startsWith("00")) {
                return phoneNumber.substring(2);
            }
            if(phoneNumber.startsWith("0")) {
                return phoneNumber.substring(1);
            }
            // else just return original string
            return phoneNumber;
        }
    }

    /*
     * Country code is WITHOUT leading "+" or "00". In phone strings no spaces are allowed.
     * In other words phone numbers consist only from digits, as first digit could not be "0".
     */
    public static String formatPhoneNumber(String countryCode, String national) {
        if (countryCode == null || national == null || national.equals("")) {
            return "";
        } else {
            return getCleanCountryCode(countryCode) + getCleanPhoneNumber(national);
        }
    }

    private static String getCleanCountryCode(String countryCode) {
        String phoneCode = countryCode.replace(" ", "");
        Pattern pattern = Pattern.compile("(\\+|00)(\\d+)");
        Matcher matcher = pattern.matcher(phoneCode);
        if (matcher.find()) {
            return matcher.group(2);
        }
        return "";
    }

    private static String getCleanPhoneNumber(String phoneNumber) {
        String number = phoneNumber.replace(" ", "");
        if (number.startsWith("0")) {
            number = number.substring(1);
        }
        return number;
    }

    /*
     * Will parse the given phone number.
     *
     * If number starts with null, strip it away. If no "null" prefix and number is not a valid phone number,
     * return null country code and the phone number with what every value the phoneNumber parameters has.
     */
    public static PhoneNumberHolder parsePhoneNumberWhichAcceptNonNumbers(String phoneNumber)
        throws PhoneNumberParsingException {
        String countryCode;
        String shortPhoneNumber;
        try {
            countryCode = getCountryCodeWithPlusSignFromFullPhoneNumber(phoneNumber);
            shortPhoneNumber = getPhoneNumberWithoutCountryCodeFromFullPhoneNumber(phoneNumber);

            return new PhoneNumberHolder(countryCode, shortPhoneNumber);
        } catch (PhoneNumberParsingException e) {
            if (phoneNumber.trim().equalsIgnoreCase(EMPTY_COUNTRY_CODE)) {
                return new PhoneNumberHolder(null, "");
            }
            if (phoneNumber.startsWith(EMPTY_COUNTRY_CODE)) {
                shortPhoneNumber = phoneNumber.split(EMPTY_COUNTRY_CODE)[1];
                return new PhoneNumberHolder(null, shortPhoneNumber);
            }
            else {
                return new PhoneNumberHolder(null, phoneNumber);
            }
        }
    }

    /**
     * Returns a nicely printed string representing the list give as input
     * @param numbers a list of numbers
     * @return nicely printed string like: +4745037118, +90630185
     */
    public static String prettyPrintNumbers(List numbers) {
        if (numbers == null || numbers.isEmpty()) {
            return "";
        }

        StringBuilder str = new StringBuilder();
        for (int i = 0; i < numbers.size(); i++) {
            str.append(numbers.get(i));
            if (i != numbers.size() - 1) {
                str.append(", ");
            }
        }
        return str.toString();
    }

    /*
     * Replace all non int characters with "" except for +
     */
    public static String removeNonInteger(String myStr) {
        if (myStr == null) {
            return null;
        }
        return myStr.replaceAll( "[^+0-9]", "" );
    }

    public static Long removeAllNonNumeric(String myStr) {
        String s = myStr.replaceAll( "[^\\d]", "" );
        if (s.trim().isEmpty()) {
            return null;
        }

        try {
            return Long.valueOf(s);
        } catch (NumberFormatException e) {
            log.error(e.getMessage());
            return null;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy