1/* 2 * Copyright (C) 2010 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.i18n.addressinput; 18 19import java.util.HashMap; 20import java.util.Map; 21 22/** 23 * A simple data structure for international postal addresses. 24 * 25 * Addresses may seem simple, but even within the US there are many quirks (hyphenated street 26 * addresses, etc.), and internationally addresses vary a great deal. The most sane and complete in 27 * many ways is the OASIS "extensible Address Language", xAL, which is a published and documented 28 * XML schema: 29 * 30 * http://www.oasis-open.org/committees/ciq/download.shtml 31 * 32 * We have not represented all the fields, but the intent is that if you need to add something, you 33 * should follow the OASIS standard. 34 * 35 * An example address: 36 * <p>postalCountry: US</p> 37 * <p>addressLine1: 1098 Alta Ave</p> 38 * <p>addressLine2:</p> 39 * <p>adminstrativeArea: CA</p> 40 * <p>locality: Mountain View</p> 41 * <p>dependentLocality:</p> 42 * <p>postalCode: 94043</p> 43 * <p>sortingCode:</p> 44 * <p>organization: Google</p> 45 * <p>recipient: Chen-Kang Yang</p> 46 * <p>language code: en</p> 47 * 48 * Note that sub-administrative area is NOT used in Address Widget. Sub-administrative Area is 49 * second-level administrative subdivision of this country. For examples: US county, IT province, UK 50 * county. This level of geo information is not required to fill out address form, therefore is 51 * neglected. 52 * 53 * All values stored in this class are trimmed. Also, if you try to set a field with an empty string 54 * or a string consists of only spaces, it will not be set. 55 */ 56public class AddressData { 57 // CLDR (Common Locale Data Repository) country code. 58 // For example, "US" for United States. 59 // (Note: Use "GB", not "UK", for Great Britain) 60 private final String mPostalCountry; 61 62 // street street, line 1 63 private final String mAddressLine1; 64 65 // street street, line 2 66 private final String mAddressLine2; 67 68 // Top-level administrative subdivision of this country. 69 // Examples: US state, IT region, UK constituent nation, JP prefecture. 70 private final String mAdministrativeArea; 71 72 // Locality. A fuzzy term, but it generally refers to 73 // the city/town portion of an address. In regions of the world where 74 // localities are not well defined or do not fit into this structure well 75 // (for example, Japan and China), leave locality empty and use 76 // addressLine1. 77 // Examples: US city, IT comune, UK post town. 78 private final String mLocality; 79 80 // Dependent locality or sublocality. Used for UK dependent localities, 81 // or neighborhoods or boroughs in other locations. If trying to 82 // represent a UK double-dependent locality, include both the 83 // double-dependent locality and the dependent locality in this field, 84 // e.g. "Whaley, Langwith". 85 private final String mDependentLocality; 86 87 // Postal Code. values are frequently alphanumeric. 88 // Examples: "94043", "94043-1351", "SW1W", "SW1W 9TQ". 89 private final String mPostalCode; 90 91 // Sorting code - use is very country-specific. 92 // This corresponds to the SortingCode sub-element of the xAL 93 // PostalServiceElements element. 94 // Examples: FR CEDEX. 95 private final String mSortingCode; 96 97 // The firm or organization. This goes at a finer granularity than 98 // address lines in the address. Omit if not needed. 99 private final String mOrganization; 100 101 // The recipient. This goes at a finer granularity than address lines 102 // in the address. Not present in xAL. Omit if not needed. 103 private final String mRecipient; 104 105 // Language code of the address. Can be set to null. See its getter and setter 106 // for more information. 107 private final String mLanguageCode; 108 109 /** 110 * Use {@link Builder} to create instances. 111 */ 112 private AddressData(Builder builder) { 113 mPostalCountry = builder.mValues.get(AddressField.COUNTRY); 114 mAdministrativeArea = builder.mValues.get(AddressField.ADMIN_AREA); 115 mLocality = builder.mValues.get(AddressField.LOCALITY); 116 mDependentLocality = builder.mValues.get(AddressField.DEPENDENT_LOCALITY); 117 mPostalCode = builder.mValues.get(AddressField.POSTAL_CODE); 118 mSortingCode = builder.mValues.get(AddressField.SORTING_CODE); 119 mOrganization = builder.mValues.get(AddressField.ORGANIZATION); 120 mRecipient = builder.mValues.get(AddressField.RECIPIENT); 121 mAddressLine1 = builder.mValues.get(AddressField.ADDRESS_LINE_1); 122 mAddressLine2 = builder.mValues.get(AddressField.ADDRESS_LINE_2); 123 mLanguageCode = builder.mLanguage; 124 } 125 126 /** 127 * Returns the postal country. 128 * 129 * <p>The returned value is not user-presentable. For example, {@code getPostalCountry()} may 130 * return {@code "GB"}, while addresses in Great Britain should be displayed using "UK". 131 */ 132 public String getPostalCountry() { 133 return mPostalCountry; 134 } 135 136 public String getAddressLine1() { 137 return mAddressLine1; 138 } 139 140 public String getAddressLine2() { 141 return mAddressLine2; 142 } 143 144 /** 145 * Returns the top-level administrative subdivision of this country. Different postal countries 146 * use different names to refer to their administrative areas. For example, this is called 147 * "state" in the United States, "region" in Italy, "constituent nation" in Great Britain, or 148 * "prefecture" in Japan. 149 */ 150 public String getAdministrativeArea() { 151 return mAdministrativeArea; 152 } 153 154 /** 155 * Returns the locality. The usage of this field varies by region, but it generally refers to 156 * the "city" or "town" of the address. Some regions do not use this field; their address lines 157 * are sufficient to locate an address within a sub-administrative area. For example, this is 158 * called "city" in the United States, "comune" in Italy, or "post town" in Great Britain. 159 */ 160 public String getLocality() { 161 return mLocality; 162 } 163 164 /** 165 * Returns the dependent locality. 166 * 167 * <p>This is used for Great Britain dependent localities, or neighborhoods or boroughs in other 168 * locations. 169 * 170 * <p>In cases such as Great Britain, this field may contain a double-dependent locality, such 171 * as "Whaley, Langwith". 172 */ 173 public String getDependentLocality() { 174 return mDependentLocality; 175 } 176 177 /** 178 * Returns the firm or organization. 179 */ 180 public String getOrganization() { 181 return mOrganization; 182 } 183 184 /** 185 * Returns the recipient. Examples: "Jesse Wilson" or "Jesse Wilson c/o Apurva Mathad". 186 */ 187 public String getRecipient() { 188 return mRecipient; 189 } 190 191 /** 192 * Returns the country-specific postal code. Examples: "94043", "94043-1351", "SW1W", 193 * "SW1W 9TQ". 194 */ 195 public String getPostalCode() { 196 return mPostalCode; 197 } 198 199 /** 200 * Returns the country-specific sorting code. For example, the 201 * <a href="http://en.wikipedia.org/wiki/List_of_postal_codes_in_France"> French CEDEX</a> 202 */ 203 public String getSortingCode() { 204 return mSortingCode; 205 } 206 207 public String getFieldValue(AddressField field) { 208 switch (field) { 209 case COUNTRY: 210 return mPostalCountry; 211 case ADMIN_AREA: 212 return mAdministrativeArea; 213 case LOCALITY: 214 return mLocality; 215 case DEPENDENT_LOCALITY: 216 return mDependentLocality; 217 case POSTAL_CODE: 218 return mPostalCode; 219 case SORTING_CODE: 220 return mSortingCode; 221 case ADDRESS_LINE_1: 222 return mAddressLine1; 223 case ADDRESS_LINE_2: 224 return mAddressLine2; 225 case ORGANIZATION: 226 return mOrganization; 227 case RECIPIENT: 228 return mRecipient; 229 default: 230 throw new IllegalArgumentException("unrecognized key: " + field); 231 } 232 } 233 234 /** 235 * Returns the language of the text of this address. Languages are used to guide how the address 236 * is <a href="http://en.wikipedia.org/wiki/Mailing_address_format_by_country"> formatted for 237 * display</a>. The same address may have different {@link AddressData} representations in 238 * different languages. For example, the French name of "New Mexico" is "Nouveau-Mexique". 239 */ 240 public String getLanguageCode() { 241 return mLanguageCode; 242 } 243 244 /** 245 * Builder for AddressData 246 */ 247 public static class Builder { 248 249 private final Map<AddressField, String> mValues; 250 251 private String mLanguage = null; 252 253 public Builder() { 254 mValues = new HashMap<AddressField, String>(); 255 } 256 257 /** 258 * A constructor that sets address field with input data. Street fields will be normalized 259 * in the process. I.e., after copy, there will not be any empty street line in front of 260 * non-empty ones. For example, if input data's street line 1 is null but street line 2 261 * has a value, this method will copy street line 2's value and set it to street line 1. 262 */ 263 public Builder(AddressData addr) { 264 mValues = new HashMap<AddressField, String>(); 265 set(addr); 266 } 267 268 public Builder setCountry(String value) { 269 return set(AddressField.COUNTRY, value); 270 } 271 272 public Builder setAdminArea(String value) { 273 return set(AddressField.ADMIN_AREA, value); 274 } 275 276 public Builder setLocality(String value) { 277 return set(AddressField.LOCALITY, value); 278 } 279 280 public Builder setDependentLocality(String value) { 281 return set(AddressField.DEPENDENT_LOCALITY, value); 282 } 283 284 public Builder setPostalCode(String value) { 285 return set(AddressField.POSTAL_CODE, value); 286 } 287 288 public Builder setSortingCode(String value) { 289 return set(AddressField.SORTING_CODE, value); 290 } 291 292 /** 293 * Sets the language code. 294 * 295 * @param languageCode the language to use, or {@code null} for no specified language. 296 */ 297 public Builder setLanguageCode(String languageCode) { 298 this.mLanguage = languageCode; 299 return this; 300 } 301 302 /** 303 * Sets address lines 1 and 2 (if necessary) from a string that may contain multiple lines. 304 * 305 * <p> Example: Input " \n \n1600 Amphitheatre Ave\n\nRoom 122" will set the following 306 * values:<br/> line 1: 1600 Amphitheatre Ave<br/> line 2: Room 122<br/> </p> 307 * 308 * @param value a street string 309 */ 310 public Builder setAddress(String value) { 311 setAddressLine1(value); 312 return this; 313 } 314 315 /** 316 * Sets address by copying from input address data. Street fields will be normalized in the 317 * process. I.e., after copy, there will not be any empty street lines in front of non-empty 318 * ones. For example, if input data's street line 1 is null but street line 2 has a value, 319 * this method will copy street line 2's value and set it to street line 1. 320 */ 321 public Builder set(AddressData data) { 322 mValues.clear(); 323 for (AddressField addressField : AddressField.values()) { 324 if (addressField == AddressField.STREET_ADDRESS) { 325 continue; // Do nothing. 326 } else { 327 set(addressField, data.getFieldValue(addressField)); 328 } 329 } 330 normalizeAddresses(); 331 setLanguageCode(data.getLanguageCode()); 332 return this; 333 } 334 335 public Builder setAddressLine1(String value) { 336 return set(AddressField.ADDRESS_LINE_1, value); 337 } 338 339 public Builder setAddressLine2(String value) { 340 return set(AddressField.ADDRESS_LINE_2, value); 341 } 342 343 public Builder setOrganization(String value) { 344 return set(AddressField.ORGANIZATION, value); 345 } 346 347 public Builder setRecipient(String value) { 348 return set(AddressField.RECIPIENT, value); 349 } 350 351 /** 352 * Sets an address field with the specified value. If the value is empty (a null string, 353 * empty string, or a string that contains only spaces), the original value associated with 354 * the field will be removed. 355 */ 356 public Builder set(AddressField field, String value) { 357 if (value == null || value.length() == 0) { 358 mValues.remove(field); 359 } else { 360 mValues.put(field, value.trim()); 361 } 362 normalizeAddresses(); 363 return this; 364 } 365 366 public AddressData build() { 367 return new AddressData(this); 368 } 369 370 /** 371 * Parses content of address line fields. 372 * If address_line_1 is empty, address_line_2 will be used to populate address_line_1 if 373 * possible. If address_line_1 contains a new line, content after the new line will be 374 * saved in address_line_2. 375 */ 376 private void normalizeAddresses() { 377 String address1 = mValues.get(AddressField.ADDRESS_LINE_1); 378 String address2 = mValues.get(AddressField.ADDRESS_LINE_2); 379 if (address1 == null || address1.trim().length() == 0) { 380 address1 = address2; 381 address2 = null; 382 } 383 if (address1 != null) { 384 String[] addressLines = address1.split("\n"); 385 if (addressLines.length > 1) { 386 address1 = addressLines[0]; 387 address2 = addressLines[1]; 388 } 389 } 390 mValues.put(AddressField.ADDRESS_LINE_1, address1); 391 mValues.put(AddressField.ADDRESS_LINE_2, address2); 392 } 393 } 394} 395