/* * Copyright 2010, 2011, 2012 Christopher Pheby * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jadira.cdt.phonenumber.impl; import java.io.Serializable; import org.jadira.bindings.core.annotation.typesafe.FromString; import org.jadira.bindings.core.annotation.typesafe.ToString; import org.jadira.cdt.country.CountryCode; import org.jadira.cdt.country.ISOCountryCode; import org.jadira.cdt.phonenumber.api.PhoneNumber; import org.jadira.cdt.phonenumber.api.PhoneNumberParseException; import com.google.i18n.phonenumbers.NumberParseException; import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat; import com.google.i18n.phonenumbers.Phonenumber; /** * This class represents a phone number with a broadly canonical rendering. * The default form is to use E164 format with the addition of an optional extension. * Extension is signified with a suffix of ";ext=". * * The class uses Google's excellent libphonenumber for its underlying representation. * This gives it great flexibility in accomodating existing legacy number formats and * converting them to a standardised notation. * * The class does not expose directly the wrapped PhoneNumber instance as it is * immutable. However, it is possible to retrieve a copy for further manipulation. */ public class E164PhoneNumberWithExtension implements PhoneNumber, Serializable { private static final long serialVersionUID = -7665314825162464754L; private static final PhoneNumberUtil PHONE_NUMBER_UTIL = PhoneNumberUtil.getInstance(); private static final String RFC3966_EXTN_PREFIX = ";ext="; private Phonenumber.PhoneNumber number; private static final String EX_PARSE_MSG_PREFIX = "Could not parse {"; /** * Creates a instance from the given PhoneNumber * @param prototype The PhoneNumber to construct the instance from */ public E164PhoneNumberWithExtension(Phonenumber.PhoneNumber prototype) { StringBuilder e164Builder = new StringBuilder(); PHONE_NUMBER_UTIL.format(prototype, PhoneNumberFormat.E164, e164Builder); try { this.number = PHONE_NUMBER_UTIL.parse(e164Builder.toString(), "GB"); } catch (NumberParseException e) { throw new PhoneNumberParseException( EX_PARSE_MSG_PREFIX + prototype.getNationalNumber() +"}", e); } if (prototype.hasExtension()) { this.number.setExtension(prototype.getExtension()); } } /** * Creates a new E164 Phone Number with the given extension. * @param e164PhoneNumberWithExtension The phone number in E164 format. The extension may be appended. * Any extension is appended to the number with the extension prefix given as ';ext=' */ protected E164PhoneNumberWithExtension(String e164PhoneNumberWithExtension) { if (!e164PhoneNumberWithExtension.startsWith("+")) { throw new PhoneNumberParseException("Only international numbers can be interpreted without a country code"); } final String e164PhoneNumber; final String extension; if (e164PhoneNumberWithExtension.contains(RFC3966_EXTN_PREFIX)) { extension = e164PhoneNumberWithExtension.substring(e164PhoneNumberWithExtension.indexOf(RFC3966_EXTN_PREFIX) + RFC3966_EXTN_PREFIX.length()); e164PhoneNumber = e164PhoneNumberWithExtension.substring(0, e164PhoneNumberWithExtension.indexOf(RFC3966_EXTN_PREFIX)); } else { extension = null; e164PhoneNumber = e164PhoneNumberWithExtension; } try { number = PHONE_NUMBER_UTIL.parse(e164PhoneNumber, "GB"); } catch (NumberParseException e) { throw new PhoneNumberParseException( EX_PARSE_MSG_PREFIX + e164PhoneNumber +"}", e); } if (extension != null) { number.setExtension(extension); } } /** * Creates a new E164 Phone Number with the given extension. * @param e164PhoneNumber The phone number in E164 format * @param extension The extension, or null for no extension */ protected E164PhoneNumberWithExtension(String e164PhoneNumber, String extension) { if (!e164PhoneNumber.startsWith("+")) { throw new PhoneNumberParseException("Only international numbers can be interpreted without a country code"); } try { number = PHONE_NUMBER_UTIL.parse(e164PhoneNumber, "GB"); } catch (NumberParseException e) { throw new PhoneNumberParseException( EX_PARSE_MSG_PREFIX + e164PhoneNumber +"}", e); } number.setExtension(extension); } /** * Creates a new E164 Phone Number. * @param phoneNumber The phone number in arbitrary parseable format (may be a national format) * @param defaultCountryCode The Country to apply if no country is indicated by the phone number */ protected E164PhoneNumberWithExtension(String phoneNumber, CountryCode defaultCountryCode) { try { number = PHONE_NUMBER_UTIL.parse(phoneNumber, defaultCountryCode.toString()); } catch (NumberParseException e) { throw new PhoneNumberParseException( EX_PARSE_MSG_PREFIX + phoneNumber + "} for country {" + defaultCountryCode +"}", e); } } /** * Creates a new E164 Phone Number with the given extension. * @param phoneNumber The phone number in arbitrary parseable format (may be a national format) * @param extension The extension, or null for no extension * @param defaultCountryCode The Country to apply if no country is indicated by the phone number */ protected E164PhoneNumberWithExtension(String phoneNumber, String extension, CountryCode defaultCountryCode) { try { number = PHONE_NUMBER_UTIL.parse(phoneNumber, defaultCountryCode.toString()); } catch (NumberParseException e) { throw new PhoneNumberParseException( EX_PARSE_MSG_PREFIX + phoneNumber + "} for country {" + defaultCountryCode +"}", e); } if (extension != null) { number.setExtension(extension); } } /** * Creates a new E164 Phone Number. * @param e164PhoneNumber The phone number in E164 format. * @return A new instance of E164PhoneNumberWithExtension */ public static E164PhoneNumberWithExtension ofE164PhoneNumberString(String e164PhoneNumber) { return new E164PhoneNumberWithExtension(e164PhoneNumber, (String)null); } /** * Creates a new E164 Phone Number with the given extension. * @param e164PhoneNumber The phone number in E164 format. The extension may be appended. * Any extension is appended to the number with the extension prefix given as ';ext=' * @return A new instance of E164PhoneNumberWithExtension */ @FromString public static E164PhoneNumberWithExtension ofE164PhoneNumberWithExtensionString(String e164PhoneNumber) { return new E164PhoneNumberWithExtension(e164PhoneNumber); } /** * Creates a new E164 Phone Number with the given extension. * @param e164PhoneNumber The phone number in E164 format * @param extension The extension, or null for no extension * @return A new instance of E164PhoneNumberWithExtension */ public static E164PhoneNumberWithExtension ofE164PhoneNumberStringAndExtension(String e164PhoneNumber, String extension) { return new E164PhoneNumberWithExtension(e164PhoneNumber, extension); } /** * Creates a new E164 Phone Number. * @param phoneNumber The phone number in arbitrary parseable format (may be a national format) * @param defaultCountryCode The Country to apply if no country is indicated by the phone number * @return A new instance of E164PhoneNumberWithExtension */ public static E164PhoneNumberWithExtension ofPhoneNumberString(String phoneNumber, CountryCode defaultCountryCode) { return new E164PhoneNumberWithExtension(phoneNumber, defaultCountryCode); } /** * Creates a new E164 Phone Number with the given extension. * @param phoneNumber The phone number in arbitrary parseable format (may be a national format) * @param extension The extension, or null for no extension * @param defaultCountryCode The Country to apply if no country is indicated by the phone number * @return A new instance of E164PhoneNumberWithExtension */ public static E164PhoneNumberWithExtension ofPhoneNumberStringAndExtension(String phoneNumber, String extension, CountryCode defaultCountryCode) { return new E164PhoneNumberWithExtension(phoneNumber, extension, defaultCountryCode); } /** * Returns the underlying LibPhoneNumber {@link Phonenumber.PhoneNumber} instance. * To preserve the immutability of E164PhoneNumber, a copy is made. * @return The underlying PhoneNumber instance */ public Phonenumber.PhoneNumber getUnderlyingPhoneNumber() { Phonenumber.PhoneNumber copy; StringBuilder e164Builder = new StringBuilder(); PHONE_NUMBER_UTIL.format(number, PhoneNumberFormat.E164, e164Builder); try { copy = PHONE_NUMBER_UTIL.parse(e164Builder.toString(), "GB"); } catch (NumberParseException e) { throw new PhoneNumberParseException( EX_PARSE_MSG_PREFIX + number.getNationalNumber() +"}", e); } if (number.hasExtension()) { copy.setExtension(number.getExtension()); } return copy; } /** * {@inheritDoc} */ public ISOCountryCode extractCountryCode() { return ISOCountryCode.valueOf(PHONE_NUMBER_UTIL.getRegionCodeForNumber(number)); } /** * {@inheritDoc} */ public String getCountryDiallingCode() { return "+" + number.getCountryCode(); } /** * {@inheritDoc} */ public String getNationalNumber() { return "" + number.getNationalNumber(); } /** * {@inheritDoc} */ public String getExtension() { return number.hasExtension() ? number.getExtension() : null; } /** * {@inheritDoc} */ public String toE164NumberString() { StringBuilder result = new StringBuilder(); PHONE_NUMBER_UTIL.format(number, PhoneNumberFormat.E164, result); return result.toString(); } /** * {@inheritDoc} */ @ToString public String toE164NumberWithExtensionString() { StringBuilder formattedString = new StringBuilder(toE164NumberString()); if(number.hasExtension()) { formattedString.append(RFC3966_EXTN_PREFIX); formattedString.append(number.getExtension()); } return formattedString.toString(); } /** * {@inheritDoc} */ public String extractAreaCode() { final String nationalSignificantNumber = PHONE_NUMBER_UTIL.getNationalSignificantNumber(number); final String areaCode; int areaCodeLength = PHONE_NUMBER_UTIL.getLengthOfGeographicalAreaCode(number); if (areaCodeLength > 0) { areaCode = nationalSignificantNumber.substring(0, areaCodeLength); } else { areaCode = ""; } return areaCode; } /** * {@inheritDoc} */ public String extractSubscriberNumber() { final String nationalSignificantNumber = PHONE_NUMBER_UTIL.getNationalSignificantNumber(number); final String subscriberNumber; int areaCodeLength = PHONE_NUMBER_UTIL.getLengthOfGeographicalAreaCode(number); if (areaCodeLength > 0) { subscriberNumber = nationalSignificantNumber.substring(areaCodeLength); } else { subscriberNumber = nationalSignificantNumber; } return subscriberNumber; } /** * {@inheritDoc} */ @Override public String toString() { return toE164NumberWithExtensionString(); } /** * {@inheritDoc} */ @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (!this.getClass().equals(obj.getClass())) { return false; } final E164PhoneNumberWithExtension obj2 = (E164PhoneNumberWithExtension) obj; if (this.toString().equals(obj2.toString())) { return true; } return false; } /** * {@inheritDoc} */ @Override public int hashCode() { return toString().hashCode(); } }