diff options
Diffstat (limited to 'java/src/com/android/i18n/addressinput/FormatInterpreter.java')
-rw-r--r-- | java/src/com/android/i18n/addressinput/FormatInterpreter.java | 464 |
1 files changed, 245 insertions, 219 deletions
diff --git a/java/src/com/android/i18n/addressinput/FormatInterpreter.java b/java/src/com/android/i18n/addressinput/FormatInterpreter.java index ed7b2ac..8634cfb 100644 --- a/java/src/com/android/i18n/addressinput/FormatInterpreter.java +++ b/java/src/com/android/i18n/addressinput/FormatInterpreter.java @@ -25,260 +25,286 @@ import org.json.JSONTokener; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; /** * Address format interpreter. A utility to find address format related info. */ class FormatInterpreter { - private static final String NEW_LINE = "%n"; + private static final String NEW_LINE = "%n"; - private final String mDefaultFormat; + private final String defaultFormat; - private final FormOptions mFormOptions; + private final FormOptions formOptions; - /** - * Creates a new instance of {@link FormatInterpreter}. - */ - FormatInterpreter(FormOptions options) { - Util.checkNotNull(RegionDataConstants.getCountryFormatMap(), - "null country name map not allowed"); - Util.checkNotNull(options); - mFormOptions = options; - mDefaultFormat = getJsonValue("ZZ", AddressDataKey.FMT); - Util.checkNotNull(mDefaultFormat, "null default format not allowed"); + /** + * Creates a new instance of {@link FormatInterpreter}. + */ + FormatInterpreter(FormOptions options) { + Util.checkNotNull(RegionDataConstants.getCountryFormatMap(), + "null country name map not allowed"); + Util.checkNotNull(options); + formOptions = options; + defaultFormat = getJsonValue("ZZ", AddressDataKey.FMT); + Util.checkNotNull(defaultFormat, "null default format not allowed"); + } + + /** + * Returns a list of address fields based on the format of {@code regionCode}. Script type is + * needed because some countries uses different address formats for local/Latin scripts. + * + * @param scriptType if {@link ScriptType#LOCAL}, use local format; else use Latin format. + */ + List<AddressField> getAddressFieldOrder(ScriptType scriptType, String regionCode) { + Util.checkNotNull(scriptType); + Util.checkNotNull(regionCode); + List<AddressField> fieldOrder = new ArrayList<AddressField>(); + for (String substring : getFormatSubStrings(scriptType, regionCode)) { + // Skips un-escaped characters and new lines. + if (!substring.matches("%.") || substring.equals(NEW_LINE)) { + continue; + } + + AddressField field = AddressField.of(substring.charAt(1)); + fieldOrder.add(field); } - /** - * Returns a list of address fields based on the format of {@code regionCode}. Script type is - * needed because some countries uses different address formats for local/Latin scripts. - * - * @param scriptType if {@link ScriptType#LOCAL}, use local format; else use Latin format. - */ - List<AddressField> getAddressFieldOrder(ScriptType scriptType, String regionCode) { - Util.checkNotNull(scriptType); - Util.checkNotNull(regionCode); - List<AddressField> fieldOrder = new ArrayList<AddressField>(); - for (String substring : getFormatSubStrings(scriptType, regionCode)) { - // Skips un-escaped characters and new lines. - if (!substring.matches("%.") || substring.equals(NEW_LINE)) { - continue; - } - - AddressField field = AddressField.of(substring.charAt(1)); - fieldOrder.add(field); - } + overrideFieldOrder(regionCode, fieldOrder); - overrideFieldOrder(regionCode, fieldOrder); - - // Uses two address lines instead of street address. - List<AddressField> finalFieldOrder = new ArrayList<AddressField>(); - for (AddressField field : fieldOrder) { - if (field == AddressField.STREET_ADDRESS) { - finalFieldOrder.add(AddressField.ADDRESS_LINE_1); - finalFieldOrder.add(AddressField.ADDRESS_LINE_2); - } else { - finalFieldOrder.add(field); - } - } - return finalFieldOrder; + // Uses two address lines instead of street address. + List<AddressField> finalFieldOrder = new ArrayList<AddressField>(); + for (AddressField field : fieldOrder) { + if (field == AddressField.STREET_ADDRESS) { + finalFieldOrder.add(AddressField.ADDRESS_LINE_1); + finalFieldOrder.add(AddressField.ADDRESS_LINE_2); + } else { + finalFieldOrder.add(field); + } } + return finalFieldOrder; + } - /** - * Returns a list of address fields based on the format of {@code regionCode} -- assuming script - * type is {@link ScriptType#LOCAL}. - */ - List<AddressField> getAddressFieldOrder(String regionCode) { - Util.checkNotNull(regionCode); - return getAddressFieldOrder(ScriptType.LOCAL, regionCode); + /** + * Returns a list of address fields based on the format of {@code regionCode} -- assuming script + * type is {@link ScriptType#LOCAL}. + */ + List<AddressField> getAddressFieldOrder(String regionCode) { + Util.checkNotNull(regionCode); + return getAddressFieldOrder(ScriptType.LOCAL, regionCode); + } + + private void overrideFieldOrder(String regionCode, List<AddressField> fieldOrder) { + if (formOptions.getCustomFieldOrder(regionCode) == null) { + return; } - private void overrideFieldOrder(String regionCode, List<AddressField> fieldOrder) { - if (mFormOptions.getCustomFieldOrder(regionCode) == null) { - return; - } + // Constructs a hash for overridden field order. + final Map<AddressField, Integer> fieldPriority = new HashMap<AddressField, Integer>(); + int i = 0; + for (AddressField field : formOptions.getCustomFieldOrder(regionCode)) { + fieldPriority.put(field, i); + i++; + } - // Constructs a hash for overridden field order. - final Map<AddressField, Integer> fieldPriority = new HashMap<AddressField, Integer>(); - int i = 0; - for (AddressField field : mFormOptions.getCustomFieldOrder(regionCode)) { - fieldPriority.put(field, i); - i++; - } + // Finds union of input fields and priority list. + List<AddressField> union = new ArrayList<AddressField>(); + List<Integer> slots = new ArrayList<Integer>(); + i = 0; + for (AddressField field : fieldOrder) { + if (fieldPriority.containsKey(field)) { + union.add(field); + slots.add(i); + } + i++; + } - // Finds union of input fields and priority list. - List<AddressField> union = new ArrayList<AddressField>(); - List<Integer> slots = new ArrayList<Integer>(); - i = 0; - for (AddressField field : fieldOrder) { - if (fieldPriority.containsKey(field)) { - union.add(field); - slots.add(i); - } - i++; - } + // Overrides field order with priority list. + Collections.sort(union, new Comparator<AddressField>() { + @Override + public int compare(AddressField o1, AddressField o2) { + return fieldPriority.get(o1) - fieldPriority.get(o2); + } + }); - // Overrides field order with priority list. - Collections.sort(union, new Comparator<AddressField>() { - @Override - public int compare(AddressField o1, AddressField o2) { - return fieldPriority.get(o1) - fieldPriority.get(o2); - } - }); - - // Puts reordered fields in slots. - for (int j = 0; j < union.size(); ++j) { - fieldOrder.set(slots.get(j), union.get(j)); - } + // Puts reordered fields in slots. + for (int j = 0; j < union.size(); ++j) { + fieldOrder.set(slots.get(j), union.get(j)); + } + } + + /** + * Returns the fields that are required to be filled in for this country. This is based upon the + * "required" field in RegionDataConstants for {@code regionCode}, and handles falling back to + * the default data if necessary. + */ + static Set<AddressField> getRequiredFields(String regionCode) { + Util.checkNotNull(regionCode); + String requireString = getRequiredString(regionCode); + + EnumSet<AddressField> required = EnumSet.of(AddressField.COUNTRY); + for (char c : requireString.toCharArray()) { + required.add(AddressField.of(c)); } + return required; + } - /** - * Gets formatted address. For example, - * - * <p> John Doe<br> Dnar Corp<br> 5th St<br> Santa Monica CA 90123 </p> - * - * This method does not validate addresses. Also, it will "normalize" the result strings by - * removing redundant spaces and empty lines. - */ - List<String> getEnvelopeAddress(AddressData address) { - Util.checkNotNull(address, "null input address not allowed"); - String regionCode = address.getPostalCountry(); - - String lc = address.getLanguageCode(); - ScriptType scriptType = ScriptType.LOCAL; - if (lc != null) { - scriptType = Util.isExplicitLatinScript(lc) ? ScriptType.LATIN : ScriptType.LOCAL; + private static String getRequiredString(String regionCode) { + String required = getJsonValue(regionCode, AddressDataKey.REQUIRE); + if (required == null) { + required = getJsonValue("ZZ", AddressDataKey.REQUIRE); + } + return required; + } + + /** + * Gets formatted address. For example, + * + * <p> John Doe<br> Dnar Corp<br> 5th St<br> Santa Monica CA 90123 </p> + * + * This method does not validate addresses. Also, it will "normalize" the result strings by + * removing redundant spaces and empty lines. + */ + List<String> getEnvelopeAddress(AddressData address) { + Util.checkNotNull(address, "null input address not allowed"); + String regionCode = address.getPostalCountry(); + + String lc = address.getLanguageCode(); + ScriptType scriptType = ScriptType.LOCAL; + if (lc != null) { + scriptType = Util.isExplicitLatinScript(lc) ? ScriptType.LATIN : ScriptType.LOCAL; + } + + List<String> lines = new ArrayList<String>(); + StringBuilder currentLine = new StringBuilder(); + for (String formatSymbol : getFormatSubStrings(scriptType, regionCode)) { + if (formatSymbol.equals(NEW_LINE)) { + String normalizedStr = + removeRedundantSpacesAndLeadingPunctuation(currentLine.toString()); + if (normalizedStr.length() > 0) { + lines.add(normalizedStr); + currentLine.setLength(0); } + } else if (formatSymbol.startsWith("%")) { + char c = formatSymbol.charAt(1); + AddressField field = AddressField.of(c); + Util.checkNotNull(field, "null address field for character " + c); - List<String> lines = new ArrayList<String>(); - StringBuilder currentLine = new StringBuilder(); - for (String formatSymbol : getFormatSubStrings(scriptType, regionCode)) { - if (formatSymbol.equals(NEW_LINE)) { - String normalizedStr = - removeRedundantSpacesAndLeadingPunctuation(currentLine.toString()); - if (normalizedStr.length() > 0) { - lines.add(normalizedStr); - currentLine.setLength(0); - } - } else if (formatSymbol.startsWith("%")) { - char c = formatSymbol.charAt(1); - AddressField field = AddressField.of(c); - Util.checkNotNull(field, "null address field for character " + c); - - String value = null; - switch (field) { - case STREET_ADDRESS: - value = Util.joinAndSkipNulls("\n", - address.getAddressLine1(), - address.getAddressLine2()); - break; - case COUNTRY: - // Country name is treated separately. - break; - case ADMIN_AREA: - value = address.getAdministrativeArea(); - break; - case LOCALITY: - value = address.getLocality(); - break; - case DEPENDENT_LOCALITY: - value = address.getDependentLocality(); - break; - case RECIPIENT: - value = address.getRecipient(); - break; - case ORGANIZATION: - value = address.getOrganization(); - break; - case POSTAL_CODE: - value = address.getPostalCode(); - break; - default: - break; - } - - if (value != null) { - currentLine.append(value); - } - } else { - currentLine.append(formatSymbol); - } + String value = null; + switch (field) { + case STREET_ADDRESS: + value = Util.joinAndSkipNulls("\n", + address.getAddressLine1(), + address.getAddressLine2()); + break; + case COUNTRY: + // Country name is treated separately. + break; + case ADMIN_AREA: + value = address.getAdministrativeArea(); + break; + case LOCALITY: + value = address.getLocality(); + break; + case DEPENDENT_LOCALITY: + value = address.getDependentLocality(); + break; + case RECIPIENT: + value = address.getRecipient(); + break; + case ORGANIZATION: + value = address.getOrganization(); + break; + case POSTAL_CODE: + value = address.getPostalCode(); + break; + default: + break; } - String normalizedStr = removeRedundantSpacesAndLeadingPunctuation(currentLine.toString()); - if (normalizedStr.length() > 0) { - lines.add(normalizedStr); + + if (value != null) { + currentLine.append(value); } - return lines; + } else { + currentLine.append(formatSymbol); + } + } + String normalizedStr = removeRedundantSpacesAndLeadingPunctuation(currentLine.toString()); + if (normalizedStr.length() > 0) { + lines.add(normalizedStr); } + return lines; + } - /** - * Tokenizes the format string and returns the token string list. "%" is treated as an escape - * character. So for example "%n%a%nxyz" will be split into "%n", "%a", "%n", "x", "y", and "z". - * Escaped tokens correspond to either new line or address fields. - */ - private List<String> getFormatSubStrings(ScriptType scriptType, String regionCode) { - String formatString = getFormatString(scriptType, regionCode); - List<String> parts = new ArrayList<String>(); - - boolean escaped = false; - for (char c : formatString.toCharArray()) { - if (escaped) { - escaped = false; - if (NEW_LINE.equals("%" + c)) { - parts.add(NEW_LINE); - } else { - Util.checkNotNull(AddressField.of(c), "Unrecognized character '" + c - + "' in format pattern: " + formatString); - parts.add("%" + c); - } - } else if (c == '%') { - escaped = true; - } else { - parts.add(c + ""); - } + /** + * Tokenizes the format string and returns the token string list. "%" is treated as an escape + * character. So for example "%n%a%nxyz" will be split into "%n", "%a", "%n", "x", "y", and "z". + * Escaped tokens correspond to either new line or address fields. + */ + private List<String> getFormatSubStrings(ScriptType scriptType, String regionCode) { + String formatString = getFormatString(scriptType, regionCode); + List<String> parts = new ArrayList<String>(); + + boolean escaped = false; + for (char c : formatString.toCharArray()) { + if (escaped) { + escaped = false; + if (NEW_LINE.equals("%" + c)) { + parts.add(NEW_LINE); + } else { + Util.checkNotNull(AddressField.of(c), "Unrecognized character '" + c + + "' in format pattern: " + formatString); + parts.add("%" + c); } - return parts; + } else if (c == '%') { + escaped = true; + } else { + parts.add(c + ""); + } } + return parts; + } - private static String removeRedundantSpacesAndLeadingPunctuation(String str) { - // Remove leading commas and other punctuation that might have been added by the formatter - // in the case of missing data. - str = str.replaceFirst("^[-,\\s]+", ""); - str = str.trim(); - str = str.replaceAll(" +", " "); - return str; - } + private static String removeRedundantSpacesAndLeadingPunctuation(String str) { + // Remove leading commas and other punctuation that might have been added by the formatter + // in the case of missing data. + str = str.replaceFirst("^[-,\\s]+", ""); + str = str.trim(); + str = str.replaceAll(" +", " "); + return str; + } - private static String getFormatString(ScriptType scriptType, String regionCode) { - String format = (scriptType == ScriptType.LOCAL) - ? getJsonValue(regionCode, AddressDataKey.FMT) - : getJsonValue(regionCode, AddressDataKey.LFMT); - if (format == null) { - format = getJsonValue("ZZ", AddressDataKey.FMT); - } - return format; + private static String getFormatString(ScriptType scriptType, String regionCode) { + String format = (scriptType == ScriptType.LOCAL) + ? getJsonValue(regionCode, AddressDataKey.FMT) + : getJsonValue(regionCode, AddressDataKey.LFMT); + if (format == null) { + format = getJsonValue("ZZ", AddressDataKey.FMT); } + return format; + } - private static String getJsonValue(String regionCode, AddressDataKey key) { - Util.checkNotNull(regionCode); - String jsonString = RegionDataConstants.getCountryFormatMap().get(regionCode); - Util.checkNotNull(jsonString, "no json data for region code " + regionCode); - - try { - JSONObject jsonObj = new JSONObject(new JSONTokener(jsonString)); - if (!jsonObj.has(key.name().toLowerCase())) { - // Key not found. Return null. - return null; - } - // Gets the string for this key. - String parsedJsonString = jsonObj.getString(key.name().toLowerCase()); - return parsedJsonString; - } catch (JSONException e) { - throw new RuntimeException("Invalid json for region code " + regionCode - + ": " + jsonString); - } + private static String getJsonValue(String regionCode, AddressDataKey key) { + Util.checkNotNull(regionCode); + String jsonString = RegionDataConstants.getCountryFormatMap().get(regionCode); + Util.checkNotNull(jsonString, "no json data for region code " + regionCode); + + try { + JSONObject jsonObj = new JSONObject(new JSONTokener(jsonString)); + if (!jsonObj.has(key.name().toLowerCase())) { + // Key not found. Return null. + return null; + } + // Gets the string for this key. + String parsedJsonString = jsonObj.getString(key.name().toLowerCase()); + return parsedJsonString; + } catch (JSONException e) { + throw new RuntimeException("Invalid json for region code " + regionCode + + ": " + jsonString); } + } } |