001/* 002 * Copyright 2010, 2011, 2012 Christopher Pheby 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.jadira.cdt.phonenumber.impl; 017 018import java.io.Serializable; 019 020import org.jadira.bindings.core.annotation.typesafe.FromString; 021import org.jadira.bindings.core.annotation.typesafe.ToString; 022import org.jadira.cdt.country.CountryCode; 023import org.jadira.cdt.country.ISOCountryCode; 024import org.jadira.cdt.phonenumber.api.PhoneNumber; 025import org.jadira.cdt.phonenumber.api.PhoneNumberParseException; 026 027import com.google.i18n.phonenumbers.NumberParseException; 028import com.google.i18n.phonenumbers.PhoneNumberUtil; 029import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat; 030import com.google.i18n.phonenumbers.Phonenumber; 031 032/** 033 * This class represents a phone number with a broadly canonical rendering. 034 * The default form is to use E164 format with the addition of an optional extension. 035 * Extension is signified with a suffix of ";ext=". 036 * 037 * The class uses Google's excellent libphonenumber for its underlying representation. 038 * This gives it great flexibility in accomodating existing legacy number formats and 039 * converting them to a standardised notation. 040 * 041 * The class does not expose directly the wrapped PhoneNumber instance as it is 042 * immutable. However, it is possible to retrieve a copy for further manipulation. 043 */ 044public class E164PhoneNumberWithExtension implements PhoneNumber, Serializable { 045 046 private static final long serialVersionUID = -7665314825162464754L; 047 048 private static final PhoneNumberUtil PHONE_NUMBER_UTIL = PhoneNumberUtil.getInstance(); 049 050 private static final String RFC3966_EXTN_PREFIX = ";ext="; 051 052 private Phonenumber.PhoneNumber number; 053 054 private static final String EX_PARSE_MSG_PREFIX = "Could not parse {"; 055 056 /** 057 * Creates a instance from the given PhoneNumber 058 * @param prototype The PhoneNumber to construct the instance from 059 */ 060 public E164PhoneNumberWithExtension(Phonenumber.PhoneNumber prototype) { 061 062 StringBuilder e164Builder = new StringBuilder(); 063 PHONE_NUMBER_UTIL.format(prototype, PhoneNumberFormat.E164, e164Builder); 064 065 try { 066 this.number = PHONE_NUMBER_UTIL.parse(e164Builder.toString(), "GB"); 067 } catch (NumberParseException e) { 068 throw new PhoneNumberParseException( 069 EX_PARSE_MSG_PREFIX + prototype.getNationalNumber() +"}", e); 070 } 071 072 if (prototype.hasExtension()) { 073 this.number.setExtension(prototype.getExtension()); 074 } 075 } 076 077 /** 078 * Creates a new E164 Phone Number with the given extension. 079 * @param e164PhoneNumberWithExtension The phone number in E164 format. The extension may be appended. 080 * Any extension is appended to the number with the extension prefix given as ';ext=' 081 */ 082 protected E164PhoneNumberWithExtension(String e164PhoneNumberWithExtension) { 083 084 if (!e164PhoneNumberWithExtension.startsWith("+")) { 085 throw new PhoneNumberParseException("Only international numbers can be interpreted without a country code"); 086 } 087 088 final String e164PhoneNumber; 089 final String extension; 090 if (e164PhoneNumberWithExtension.contains(RFC3966_EXTN_PREFIX)) { 091 extension = e164PhoneNumberWithExtension.substring(e164PhoneNumberWithExtension.indexOf(RFC3966_EXTN_PREFIX) + RFC3966_EXTN_PREFIX.length()); 092 e164PhoneNumber = e164PhoneNumberWithExtension.substring(0, e164PhoneNumberWithExtension.indexOf(RFC3966_EXTN_PREFIX)); 093 } else { 094 extension = null; 095 e164PhoneNumber = e164PhoneNumberWithExtension; 096 } 097 098 try { 099 number = PHONE_NUMBER_UTIL.parse(e164PhoneNumber, "GB"); 100 } catch (NumberParseException e) { 101 throw new PhoneNumberParseException( 102 EX_PARSE_MSG_PREFIX + e164PhoneNumber +"}", e); 103 } 104 105 if (extension != null) { 106 number.setExtension(extension); 107 } 108 } 109 110 /** 111 * Creates a new E164 Phone Number with the given extension. 112 * @param e164PhoneNumber The phone number in E164 format 113 * @param extension The extension, or null for no extension 114 */ 115 protected E164PhoneNumberWithExtension(String e164PhoneNumber, String extension) { 116 117 if (!e164PhoneNumber.startsWith("+")) { 118 throw new PhoneNumberParseException("Only international numbers can be interpreted without a country code"); 119 } 120 121 try { 122 number = PHONE_NUMBER_UTIL.parse(e164PhoneNumber, "GB"); 123 } catch (NumberParseException e) { 124 throw new PhoneNumberParseException( 125 EX_PARSE_MSG_PREFIX + e164PhoneNumber +"}", e); 126 } 127 128 number.setExtension(extension); 129 } 130 131 /** 132 * Creates a new E164 Phone Number. 133 * @param phoneNumber The phone number in arbitrary parseable format (may be a national format) 134 * @param defaultCountryCode The Country to apply if no country is indicated by the phone number 135 */ 136 protected E164PhoneNumberWithExtension(String phoneNumber, CountryCode defaultCountryCode) { 137 138 try { 139 number = PHONE_NUMBER_UTIL.parse(phoneNumber, defaultCountryCode.toString()); 140 } catch (NumberParseException e) { 141 throw new PhoneNumberParseException( 142 EX_PARSE_MSG_PREFIX + phoneNumber + "} for country {" + defaultCountryCode +"}", e); 143 } 144 } 145 146 /** 147 * Creates a new E164 Phone Number with the given extension. 148 * @param phoneNumber The phone number in arbitrary parseable format (may be a national format) 149 * @param extension The extension, or null for no extension 150 * @param defaultCountryCode The Country to apply if no country is indicated by the phone number 151 */ 152 protected E164PhoneNumberWithExtension(String phoneNumber, String extension, CountryCode defaultCountryCode) { 153 154 try { 155 number = PHONE_NUMBER_UTIL.parse(phoneNumber, defaultCountryCode.toString()); 156 } catch (NumberParseException e) { 157 throw new PhoneNumberParseException( 158 EX_PARSE_MSG_PREFIX + phoneNumber + "} for country {" + defaultCountryCode +"}", e); 159 } 160 if (extension != null) { 161 number.setExtension(extension); 162 } 163 } 164 165 /** 166 * Creates a new E164 Phone Number. 167 * @param e164PhoneNumber The phone number in E164 format. 168 * @return A new instance of E164PhoneNumberWithExtension 169 */ 170 public static E164PhoneNumberWithExtension ofE164PhoneNumberString(String e164PhoneNumber) { 171 return new E164PhoneNumberWithExtension(e164PhoneNumber, (String)null); 172 } 173 174 /** 175 * Creates a new E164 Phone Number with the given extension. 176 * @param e164PhoneNumber The phone number in E164 format. The extension may be appended. 177 * Any extension is appended to the number with the extension prefix given as ';ext=' 178 * @return A new instance of E164PhoneNumberWithExtension 179 */ 180 @FromString 181 public static E164PhoneNumberWithExtension ofE164PhoneNumberWithExtensionString(String e164PhoneNumber) { 182 return new E164PhoneNumberWithExtension(e164PhoneNumber); 183 } 184 185 /** 186 * Creates a new E164 Phone Number with the given extension. 187 * @param e164PhoneNumber The phone number in E164 format 188 * @param extension The extension, or null for no extension 189 * @return A new instance of E164PhoneNumberWithExtension 190 */ 191 public static E164PhoneNumberWithExtension ofE164PhoneNumberStringAndExtension(String e164PhoneNumber, String extension) { 192 return new E164PhoneNumberWithExtension(e164PhoneNumber, extension); 193 } 194 195 /** 196 * Creates a new E164 Phone Number. 197 * @param phoneNumber The phone number in arbitrary parseable format (may be a national format) 198 * @param defaultCountryCode The Country to apply if no country is indicated by the phone number 199 * @return A new instance of E164PhoneNumberWithExtension 200 */ 201 public static E164PhoneNumberWithExtension ofPhoneNumberString(String phoneNumber, CountryCode defaultCountryCode) { 202 return new E164PhoneNumberWithExtension(phoneNumber, defaultCountryCode); 203 } 204 205 /** 206 * Creates a new E164 Phone Number with the given extension. 207 * @param phoneNumber The phone number in arbitrary parseable format (may be a national format) 208 * @param extension The extension, or null for no extension 209 * @param defaultCountryCode The Country to apply if no country is indicated by the phone number 210 * @return A new instance of E164PhoneNumberWithExtension 211 */ 212 public static E164PhoneNumberWithExtension ofPhoneNumberStringAndExtension(String phoneNumber, String extension, CountryCode defaultCountryCode) { 213 return new E164PhoneNumberWithExtension(phoneNumber, extension, defaultCountryCode); 214 } 215 216 /** 217 * Returns the underlying LibPhoneNumber {@link Phonenumber.PhoneNumber} instance. 218 * To preserve the immutability of E164PhoneNumber, a copy is made. 219 * @return The underlying PhoneNumber instance 220 */ 221 public Phonenumber.PhoneNumber getUnderlyingPhoneNumber() { 222 223 Phonenumber.PhoneNumber copy; 224 225 StringBuilder e164Builder = new StringBuilder(); 226 PHONE_NUMBER_UTIL.format(number, PhoneNumberFormat.E164, e164Builder); 227 228 try { 229 copy = PHONE_NUMBER_UTIL.parse(e164Builder.toString(), "GB"); 230 } catch (NumberParseException e) { 231 throw new PhoneNumberParseException( 232 EX_PARSE_MSG_PREFIX + number.getNationalNumber() +"}", e); 233 } 234 235 if (number.hasExtension()) { 236 copy.setExtension(number.getExtension()); 237 } 238 return copy; 239 } 240 241 /** 242 * {@inheritDoc} 243 */ 244 public ISOCountryCode extractCountryCode() { 245 return ISOCountryCode.valueOf(PHONE_NUMBER_UTIL.getRegionCodeForNumber(number)); 246 } 247 248 /** 249 * {@inheritDoc} 250 */ 251 public String getCountryDiallingCode() { 252 return "+" + number.getCountryCode(); 253 } 254 255 /** 256 * {@inheritDoc} 257 */ 258 public String getNationalNumber() { 259 return "" + number.getNationalNumber(); 260 } 261 262 /** 263 * {@inheritDoc} 264 */ 265 public String getExtension() { 266 return number.hasExtension() ? number.getExtension() : null; 267 } 268 269 /** 270 * {@inheritDoc} 271 */ 272 public String toE164NumberString() { 273 StringBuilder result = new StringBuilder(); 274 PHONE_NUMBER_UTIL.format(number, PhoneNumberFormat.E164, result); 275 276 return result.toString(); 277 } 278 279 /** 280 * {@inheritDoc} 281 */ 282 @ToString 283 public String toE164NumberWithExtensionString() { 284 285 StringBuilder formattedString = new StringBuilder(toE164NumberString()); 286 287 if(number.hasExtension()) { 288 formattedString.append(RFC3966_EXTN_PREFIX); 289 formattedString.append(number.getExtension()); 290 } 291 292 return formattedString.toString(); 293 } 294 295 /** 296 * {@inheritDoc} 297 */ 298 public String extractAreaCode() { 299 300 final String nationalSignificantNumber = PHONE_NUMBER_UTIL.getNationalSignificantNumber(number); 301 final String areaCode; 302 303 int areaCodeLength = PHONE_NUMBER_UTIL.getLengthOfGeographicalAreaCode(number); 304 if (areaCodeLength > 0) { 305 areaCode = nationalSignificantNumber.substring(0, areaCodeLength); 306 } else { 307 areaCode = ""; 308 } 309 310 return areaCode; 311 } 312 313 /** 314 * {@inheritDoc} 315 */ 316 public String extractSubscriberNumber() { 317 318 final String nationalSignificantNumber = PHONE_NUMBER_UTIL.getNationalSignificantNumber(number); 319 final String subscriberNumber; 320 321 int areaCodeLength = PHONE_NUMBER_UTIL.getLengthOfGeographicalAreaCode(number); 322 if (areaCodeLength > 0) { 323 subscriberNumber = nationalSignificantNumber.substring(areaCodeLength); 324 } else { 325 subscriberNumber = nationalSignificantNumber; 326 } 327 328 return subscriberNumber; 329 } 330 331 /** 332 * {@inheritDoc} 333 */ 334 @Override 335 public String toString() { 336 return toE164NumberWithExtensionString(); 337 } 338 339 /** 340 * {@inheritDoc} 341 */ 342 @Override 343 public boolean equals(Object obj) { 344 if (obj == null) { 345 return false; 346 } 347 if (!this.getClass().equals(obj.getClass())) { 348 return false; 349 } 350 351 final E164PhoneNumberWithExtension obj2 = (E164PhoneNumberWithExtension) obj; 352 if (this.toString().equals(obj2.toString())) { 353 return true; 354 } 355 356 return false; 357 } 358 359 /** 360 * {@inheritDoc} 361 */ 362 @Override 363 public int hashCode() { 364 return toString().hashCode(); 365 } 366}