Port of LookupKey and changing compilation to include line numbers for unit-test debugging.
diff --git a/build.xml b/build.xml
index 9cf021f..bb56f51 100644
--- a/build.xml
+++ b/build.xml
@@ -23,8 +23,12 @@
<target name="compile" description="Compile Java source.">
<mkdir dir="${classes.dir}"/>
- <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath"/>
- <javac srcdir="${test.dir}" destdir="${classes.dir}" classpathref="test.classpath"/>
+ <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath"
+ debug="on">
+ <compilerarg value="-Xlint"/>
+ </javac>
+ <javac srcdir="${test.dir}" destdir="${classes.dir}" classpathref="test.classpath"
+ debug="on"/>
</target>
<target name="jar" depends="compile">
diff --git a/src/com/android/i18n/addressinput/LookupKey.java b/src/com/android/i18n/addressinput/LookupKey.java
new file mode 100755
index 0000000..129c90c
--- /dev/null
+++ b/src/com/android/i18n/addressinput/LookupKey.java
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * 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 com.android.i18n.addressinput;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+/**
+ * A builder for creating keys that are used to lookup data in the local cache
+ * and fetch data from the server. There are two key types: {@code KeyType#DATA}
+ * or {@code KeyType#EXAMPLES}.
+ *
+ * <p>
+ * The {@code KeyType#DATA} key is built based on a universal Address hierarchy,
+ * which is:<br>
+ *
+ * {@code AddressField#Country} -> {@code AddressField#ADMIN_AREA} ->
+ * {@code AddressField#Locality} -> {@code AddressField#DEPENDENT_LOCALITY}
+ * </p>
+ *
+ * <p>
+ * The {@code KeyType#EXAMPLES} key is built with the following format:<br>
+ *
+ * {@code AddressField#Country} -> {@code ScriptType} -> language.
+ * </p>
+ */
+public final class LookupKey {
+
+ /**
+ * Key types. Address Widget organizes address info based on key types. For
+ * example, if you want to know how to verify or format an US address, you
+ * need to use {@link KeyType#DATA} to get that info; if you want to get an
+ * example address, you use {@link KeyType#EXAMPLES} instead.
+ */
+ public enum KeyType {
+ /** Key type for getting address data. */
+ DATA,
+ /** Key type for getting examples. */
+ EXAMPLES
+ }
+
+ /**
+ * Script types. This is used for countries that do not use Latin script,
+ * but accept it for transcribing their addresses. For example, you can
+ * write a Japanese address in Latin script instead of Japanese:
+ *
+ * <p>
+ * 7-2, Marunouchi 2-Chome, Chiyoda-ku, Tokyo 100-8799
+ * </p>
+ *
+ * Notice that {@link ScriptType} is based on country/region, not language.
+ */
+ public enum ScriptType {
+ /**
+ * The script that uses Roman characters like ABC (as opposed to scripts
+ * like Cyrillic or Arabic).
+ */
+ LATIN,
+
+ /**
+ * Local scripts. For Japan, it's Japanese (including Hiragana, Katagana,
+ * and Kanji); For Saudi Arabia, it's Arabic. Notice that for US, the local
+ * script is actually Latin script (The same goes for other countries
+ * that use Latin script). For these countries, we do not provide two
+ * set of data (Latin and local) since they use only Latin script. You
+ * have to specify the {@link ScriptType} as local instead Latin.
+ */
+ LOCAL
+ }
+
+ /**
+ * The universal address hierarchy. Notice that sub-administrative
+ * area is neglected here since it is not required to fill out address
+ * form.
+ */
+ private static AddressField[] hierarchy = {
+ AddressField.COUNTRY,
+ AddressField.ADMIN_AREA,
+ AddressField.LOCALITY,
+ AddressField.DEPENDENT_LOCALITY};
+
+ private static final String SLASH_DELIM = "/";
+ private static final String DASH_DELIM = "--";
+ private static final String DEFAULT_LANGUAGE = "_default";
+
+ private final KeyType keyType;
+
+ private final ScriptType scriptType;
+
+ // Values for hierarchy address fields.
+ private final Map<AddressField, String> nodes;
+
+ private final String keyString;
+
+ private final String languageCode;
+
+ private LookupKey(Builder builder) {
+ this.keyType = builder.keyType;
+ this.scriptType = builder.script;
+ this.nodes = builder.nodes;
+ this.languageCode = builder.languageCode;
+ this.keyString = getKeyString();
+ }
+
+ /**
+ * Gets lookup key for the input address field. This method does not allow key
+ * with key type of {@link KeyType#EXAMPLES}.
+ *
+ * @param field a field in the address hierarchy.
+ * @return key of the specified address field. If address field is not in the
+ * hierarchy, or is more granular than the current key has, returns null. For
+ * example, if your current key is "data/US" (down to country level), and you
+ * wants to get the key for Locality (more granular than country), it will
+ * return null.
+ *
+ */
+ public LookupKey getKeyForUpperLevelField(AddressField field) {
+ if (keyType != KeyType.DATA) {
+ // We only support getting the parent key for the data key type.
+ throw new RuntimeException("Only support getting parent keys for the data key type.");
+ }
+ Builder newKeyBuilder = new Builder(this);
+
+ boolean removeNode = false;
+ boolean fieldInHierarchy = false;
+ for (AddressField hierarchyField : hierarchy) {
+ if (removeNode) {
+ if (newKeyBuilder.nodes.containsKey(hierarchyField)) {
+ newKeyBuilder.nodes.remove(hierarchyField);
+ }
+ }
+ if (hierarchyField == field) {
+ if (!newKeyBuilder.nodes.containsKey(hierarchyField)) {
+ return null;
+ }
+ removeNode = true;
+ fieldInHierarchy = true;
+ }
+ }
+
+ if (!fieldInHierarchy) {
+ return null;
+ }
+
+ newKeyBuilder.languageCode = languageCode;
+ newKeyBuilder.script = scriptType;
+
+ return newKeyBuilder.build();
+ }
+
+ /**
+ * Gets parent key for data key. For example, parent key for "data/US/CA" is
+ * "data/US". This method does not allow key with key type of
+ * {@link KeyType#EXAMPLES}.
+ */
+ public LookupKey getParentKey() {
+ if (keyType != KeyType.DATA) {
+ throw new RuntimeException("Only support getting parent keys for the data key type.");
+ }
+ // Root key's parent should be null.
+ if (!nodes.containsKey(AddressField.COUNTRY)) {
+ return null;
+ }
+
+ Builder parentKeyBuilder = new Builder(this);
+ AddressField mostGranularField = AddressField.COUNTRY;
+
+ for (AddressField hierarchyField : hierarchy) {
+ if (!nodes.containsKey(hierarchyField)) {
+ break;
+ }
+ mostGranularField = hierarchyField;
+ }
+ parentKeyBuilder.nodes.remove(mostGranularField);
+ return parentKeyBuilder.build();
+ }
+
+ public KeyType getKeyType() {
+ return keyType;
+ }
+
+ /**
+ * Gets a key in string format. E.g., "data/US/CA".
+ */
+ private String getKeyString() {
+ StringBuilder keyBuilder = new StringBuilder(keyType.name().toLowerCase());
+
+ if (keyType == KeyType.DATA) {
+ for (AddressField field : hierarchy) {
+ if (!nodes.containsKey(field)) {
+ break;
+ }
+ if (field == AddressField.COUNTRY && languageCode != null) {
+ keyBuilder.append(SLASH_DELIM)
+ .append(nodes.get(field)).append(DASH_DELIM)
+ .append(languageCode);
+ } else {
+ keyBuilder.append(SLASH_DELIM).append(nodes.get(field));
+ }
+ }
+ } else {
+ if (nodes.containsKey(AddressField.COUNTRY)) {
+ // Example key. E.g., "examples/TW/local/_default".
+ keyBuilder.append(SLASH_DELIM).append(nodes.get(AddressField.COUNTRY))
+ .append(SLASH_DELIM).append(scriptType.name().toLowerCase())
+ .append(SLASH_DELIM).append(DEFAULT_LANGUAGE);
+ }
+ }
+
+ return keyBuilder.toString();
+ }
+
+ /**
+ * Gets a lookup key as a plain text string., e.g., "data/US/CA".
+ */
+ public String toString() {
+ return keyString;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if ((obj == null) || (obj.getClass() != this.getClass())) {
+ return false;
+ }
+
+ return ((LookupKey) obj).toString().equals(keyString);
+ }
+
+ public int hashCode() {
+ return keyString.hashCode();
+ }
+
+ /**
+ * Builds lookup keys.
+ */
+ public static class Builder {
+ private KeyType keyType;
+
+ // Default to LOCAL script.
+ private ScriptType script = ScriptType.LOCAL;
+
+ private Map<AddressField, String> nodes = new EnumMap<AddressField, String>(AddressField.class);
+
+ private String languageCode;
+
+ /**
+ * Creates a new builder for the specified key type. keyType cannot be null.
+ */
+ public Builder(KeyType keyType) {
+ this.keyType = keyType;
+ }
+
+ /**
+ * Creates a new builder for the specified key. oldKey cannot be null.
+ */
+ public Builder(LookupKey oldKey) {
+ this.keyType = oldKey.keyType;
+ this.script = oldKey.scriptType;
+ this.languageCode = oldKey.languageCode;
+ for (AddressField field : hierarchy) {
+ if (!oldKey.nodes.containsKey(field)) {
+ break;
+ }
+ this.nodes.put(field, oldKey.nodes.get(field));
+ }
+ }
+
+ /**
+ * Builds the {@link LookupKey} with the input key string. Input
+ * string has to represent either a {@link KeyType#DATA} key or a
+ * {@link KeyType#EXAMPLES} key. Also, key hierarchy deeper than
+ * {@link AddressField#DEPENDENT_LOCALITY} is not allowed. Notice that if
+ * any node in the hierarchy is empty, all the descendant nodes' values will
+ * be neglected. For example, input string "data/US//Mt View" will become
+ * "data/US".
+ *
+ * @param keyString e.g., "data/US/CA"
+ */
+ public Builder(String keyString) {
+ String[] parts = keyString.split(SLASH_DELIM);
+ // Check some pre-conditions.
+ if (!parts[0].equals(KeyType.DATA.name().toLowerCase()) &&
+ !parts[0].equals(KeyType.EXAMPLES.name().toLowerCase())) {
+ throw new RuntimeException("Wrong key type: " + parts[0]);
+ }
+ if (parts.length > hierarchy.length + 1) {
+ throw new RuntimeException("input key '" + keyString + "' deeper than supported hierarchy");
+ }
+ if (parts[0].equals("data")) {
+ keyType = KeyType.DATA;
+
+ // Parses country and language info.
+ if (parts.length > 1) {
+ String substr = Util.trimToNull(parts[1]);
+ if (substr.contains(DASH_DELIM)) {
+ String[] s = substr.split(DASH_DELIM);
+ if (s.length != 2) {
+ throw new RuntimeException(
+ "Wrong format: Substring should be country code--language code");
+ }
+ substr = s[0];
+ languageCode = s[1];
+ }
+ this.nodes.put(hierarchy[0], substr);
+ }
+
+ // Parses sub-country info.
+ if (parts.length > 2) {
+ for (int i = 2; i < parts.length; ++i) {
+ String substr = Util.trimToNull(parts[i]);
+ if (substr == null) {
+ break;
+ }
+ this.nodes.put(hierarchy[i - 1], substr);
+ }
+ }
+ } else if (parts[0].equals("examples")) {
+ keyType = KeyType.EXAMPLES;
+
+ // Parses country info.
+ if (parts.length > 1) {
+ this.nodes.put(AddressField.COUNTRY, parts[1]);
+ }
+
+ // Parses script types.
+ if (parts.length > 2) {
+ String scriptStr = parts[2];
+ if (scriptStr.equals("local")) {
+ this.script = ScriptType.LOCAL;
+ } else if (scriptStr.equals("latin")) {
+ this.script = ScriptType.LATIN;
+ } else {
+ throw new RuntimeException("Script type has to be either latin or local.");
+ }
+ }
+
+ // Parses language code. Example: "zh_Hant" in
+ // "examples/TW/local/zH_Hant".
+ if (parts.length > 3 && !parts[3].equals(DEFAULT_LANGUAGE)) {
+ languageCode = parts[3];
+ }
+ }
+ }
+
+ public Builder setLanguageCode(String languageCode) {
+ this.languageCode = languageCode;
+ return this;
+ }
+
+ /**
+ * Sets key using {@link AddressData}. Notice that if any node in the
+ * hierarchy is empty, all the descendant nodes' values will
+ * be neglected. For example, the following address misses
+ * {@link AddressField#ADMIN_AREA}, thus its data key will be
+ * "data/US".
+ *
+ * <p>
+ * country: US<br>
+ * administrative area: null<br>
+ * locality: Mt. View
+ * </p>
+ */
+ public Builder setAddressData(AddressData data) {
+ languageCode = data.getLanguageCode();
+ if (languageCode != null) {
+ if (Util.isExplicitLatinScript(languageCode)) {
+ script = ScriptType.LATIN;
+ }
+ }
+
+ if (data.getPostalCountry() == null) {
+ return this;
+ }
+ this.nodes.put(AddressField.COUNTRY, data.getPostalCountry());
+
+ if (data.getAdministrativeArea() == null) {
+ return this;
+ }
+ this.nodes.put(AddressField.ADMIN_AREA, data.getAdministrativeArea());
+
+ if (data.getLocality() == null) {
+ return this;
+ }
+ this.nodes.put(AddressField.LOCALITY, data.getLocality());
+
+ if (data.getDependentLocality() == null) {
+ return this;
+ }
+ this.nodes.put(AddressField.DEPENDENT_LOCALITY, data.getDependentLocality());
+ return this;
+ }
+
+ public LookupKey build() {
+ return new LookupKey(this);
+ }
+ }
+}
diff --git a/src/com/android/i18n/addressinput/Util.java b/src/com/android/i18n/addressinput/Util.java
index 081e973..22620d7 100644
--- a/src/com/android/i18n/addressinput/Util.java
+++ b/src/com/android/i18n/addressinput/Util.java
@@ -21,34 +21,19 @@
/**
* Utility functions used by the address widget.
- *
- * @author Lara Rennie
*/
public class Util {
- public static final String LATIN_SCRIPT = "Latn";
-
- // Only used internally.
- private static final String CHINESE_SCRIPT = "Hans";
- private static final String KOREAN_SCRIPT = "Kore";
- private static final String JAPANESE_SCRIPT = "Jpan";
- // These are in upper-case, since we convert the language code to upper case before doing
- // string comparison.
- private static final String CHINESE_LANGUAGE = "ZH";
- private static final String JAPANESE_LANGUAGE = "JA";
- private static final String KOREAN_LANGUAGE = "KO";
+ // In upper-case, since we convert the language code to upper case before doing string comparison.
+ private static final String LATIN_SCRIPT = "LATN";
// Cannot instantiate this class - private constructor.
private Util() {}
/**
- * Gets the script code for a particular language. This is a somewhat hacky replacement for ICU's
- * class that does this properly. For our purposes, we only want to know if the address is in a
- * CJK script or not, since that affects address formatting. We assume that the languageCode is
- * well-formed and first search to see if there is a script code specified. If not, then we assume
- * Chinese, Japanese and Korean are in their default scripts, and other languages are in Latin
- * script.
+ * Returns true if the language code is explicitly marked to be in the latin script. For example,
+ * "zh-Latn" would return true, but "zh-TW", "en" and "zh" would all return false.
*/
- public static String getScriptCode(String languageCode) {
+ public static boolean isExplicitLatinScript(String languageCode) {
// Convert to upper-case for easier comparison.
languageCode = languageCode.toUpperCase();
// Check to see if the language code contains a script modifier.
@@ -56,23 +41,22 @@
Matcher m = languageCodePattern.matcher(languageCode);
if (m.lookingAt()) {
String script = m.group(1);
- if (script.equals(LATIN_SCRIPT.toUpperCase())) {
- return LATIN_SCRIPT;
+ if (script.equals(LATIN_SCRIPT)) {
+ return true;
}
}
- // If the script was not explicitly specified as Latn, we ignore the script information and read
- // the language tag instead. This would break for cases such as zh-Cyrl, but this is rare enough
- // that we are not going to worry about it for now.
- if (languageCode.startsWith(CHINESE_LANGUAGE)) {
- // We don't distinguish between simplified and traditional Chinese here.
- return CHINESE_SCRIPT;
- } else if (languageCode.startsWith(JAPANESE_LANGUAGE)) {
- return JAPANESE_SCRIPT;
- } else if (languageCode.startsWith(KOREAN_LANGUAGE)) {
- return KOREAN_SCRIPT;
+ return false;
+ }
+
+ /**
+ * Trims the string. If the field is empty after trimming, returns null instead. Note that this
+ * only trims ASCII white-space.
+ */
+ public static String trimToNull(String originalStr) {
+ if (originalStr == null) {
+ return null;
}
- // All Indic, Arabic and other scripts will be mislabelled by this function, but since we only
- // want to distinguish between CJK and non-CJK, this is ok.
- return LATIN_SCRIPT;
+ String trimmedString = originalStr.trim();
+ return (trimmedString.length() == 0) ? null : trimmedString;
}
}
diff --git a/test/com/android/i18n/addressinput/LookupKeyTest.java b/test/com/android/i18n/addressinput/LookupKeyTest.java
new file mode 100644
index 0000000..7dd77a8
--- /dev/null
+++ b/test/com/android/i18n/addressinput/LookupKeyTest.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * 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 com.android.i18n.addressinput;
+
+import com.android.i18n.addressinput.LookupKey.KeyType;
+
+import junit.framework.TestCase;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Unit tests for the LookupKey class.
+ */
+public class LookupKeyTest extends TestCase {
+ private static final String ROOT_KEY = "data";
+ private static final String ROOT_EXAMPLE_KEY = "examples";
+ private static final String US_KEY = "data/US";
+ private static final String CALIFORNIA_KEY = "data/US/CA";
+ private static final String EXAMPLE_LOCAL_US_KEY = "examples/US/local/_default";
+
+ // Data key for Da-an District, Taipei Taiwan
+ private static final String TW_KEY = "data/TW/\u53F0\u5317\u5E02/\u5927\u5B89\u5340";
+
+ // Example key for TW's address (local script)
+ private static final String TW_EXAMPLE_LOCAL_KEY = "examples/TW/local/_default";
+
+ // Example key for TW's address (latin script)
+ private static final String TW_EXAMPLE_LATIN_KEY = "examples/TW/latin/_default";
+
+ private static final String RANDOM_KEY = "sdfIisooIFOOBAR";
+ private static final String RANDOM_COUNTRY_KEY = "data/asIOSDxcowW";
+
+ public void testRootKey() {
+ LookupKey key = new LookupKey.Builder(KeyType.DATA).build();
+ assertEquals(ROOT_KEY, key.toString());
+
+ LookupKey key2 = new LookupKey.Builder(key.toString()).build();
+ assertEquals(ROOT_KEY, key.toString());
+ }
+
+ public void testDataKeys() {
+ LookupKey key = new LookupKey.Builder(US_KEY).build();
+ assertEquals(US_KEY, key.toString());
+
+ LookupKey key2 = new LookupKey.Builder(CALIFORNIA_KEY).build();
+ assertEquals(CALIFORNIA_KEY, key2.toString());
+ }
+
+ public void testExampleRootKeys() {
+ LookupKey key = new LookupKey.Builder(KeyType.EXAMPLES).build();
+ assertEquals(ROOT_EXAMPLE_KEY, key.toString());
+ }
+
+ public void testExampleKeys() {
+ AddressData address =
+ new AddressData.Builder().setCountry("US").setLanguageCode("en").build();
+
+ LookupKey key = new LookupKey.Builder(KeyType.EXAMPLES).setAddressData(address).build();
+ assertEquals(EXAMPLE_LOCAL_US_KEY, key.toString());
+
+ key = new LookupKey.Builder(EXAMPLE_LOCAL_US_KEY).build();
+ assertEquals(EXAMPLE_LOCAL_US_KEY, key.toString());
+ }
+
+ public void testKeyWithWrongScriptType() {
+ String wrongScript = "examples/US/asdfasdfasdf/_default";
+ try {
+ LookupKey key = new LookupKey.Builder(wrongScript).build();
+ fail("should fail since the script type is wrong");
+ } catch (RuntimeException e) {
+ // Expected.
+ }
+ }
+
+ public void testFallbackToCountry() {
+ // Admin Area is missing.
+ AddressData address = new AddressData.Builder().setCountry("US").setLocality("Mt View").build();
+
+ LookupKey key = new LookupKey.Builder(KeyType.DATA).setAddressData(address).build();
+
+ assertEquals("locality should be omitted since admin area is not specified",
+ US_KEY, key.toString());
+
+ // Tries key string with the same problem (missing Admin Area).
+ key = new LookupKey.Builder("data/US//Mt View").build();
+
+ assertEquals("locality should be omitted since admin area is not specified",
+ US_KEY, key.toString());
+ }
+
+ public void testNonUsAddress() {
+ AddressData address = new AddressData.Builder().setCountry("TW")
+ .setAdminArea("\u53F0\u5317\u5E02") // Taipei city
+ .setLocality("\u5927\u5B89\u5340") // Da-an district
+ .build();
+
+ LookupKey key = new LookupKey.Builder(KeyType.DATA).setAddressData(address).build();
+ assertEquals(TW_KEY, key.toString());
+
+ key = new LookupKey.Builder(KeyType.EXAMPLES).setAddressData(address).build();
+ assertEquals(TW_EXAMPLE_LOCAL_KEY, key.toString());
+
+ address = new AddressData.Builder(address).setLanguageCode("zh-latn").build();
+ key = new LookupKey.Builder(KeyType.EXAMPLES).setAddressData(address).build();
+ assertEquals(TW_EXAMPLE_LATIN_KEY, key.toString());
+ }
+
+ public void testGetKeyForUpperLevelFieldWithDataKey() {
+ AddressData address = new AddressData.Builder().setCountry("US")
+ .setAdminArea("CA")
+ .setLocality("Mt View")
+ .build();
+
+ LookupKey key = new LookupKey.Builder(KeyType.DATA).setAddressData(address).build();
+ LookupKey newKey = key.getKeyForUpperLevelField(AddressField.COUNTRY);
+ assertNotNull("failed to get key for " + AddressField.COUNTRY, newKey);
+ assertEquals("data/US", newKey.toString());
+
+ newKey = key.getKeyForUpperLevelField(AddressField.ADMIN_AREA);
+ assertNotNull("failed to get key for " + AddressField.ADMIN_AREA, newKey);
+ assertEquals("data/US/CA", newKey.toString());
+ assertEquals("original key should not be changed", "data/US/CA/Mt View",
+ key.toString());
+
+ newKey = key.getKeyForUpperLevelField(AddressField.LOCALITY);
+ assertNotNull("failed to get key for " + AddressField.LOCALITY, newKey);
+ assertEquals("data/US/CA/Mt View", newKey.toString());
+
+ newKey = key.getKeyForUpperLevelField(AddressField.DEPENDENT_LOCALITY);
+ assertNull("should return null for field not contained in current key", newKey);
+
+ newKey = key.getKeyForUpperLevelField(AddressField.RECIPIENT);
+ assertNull("should return null since field '" + AddressField.RECIPIENT
+ + "' is not in address hierarchy", newKey);
+ }
+
+ public void testGetKeyForUpperLevelFieldWithExampleKey() {
+ LookupKey key = new LookupKey.Builder("examples/US/latin/_default").build();
+
+ try {
+ LookupKey newKey = key.getKeyForUpperLevelField(AddressField.COUNTRY);
+ fail("should fail if you try to get parent key for an example key.");
+ } catch (RuntimeException e) {
+ // Expected.
+ }
+ }
+
+ public void testGetParentKey() {
+ AddressData address = new AddressData.Builder().setCountry("US")
+ .setAdminArea("CA")
+ .setLocality("Mt View")
+ .setDependentLocality("El Camino")
+ .build();
+
+ LookupKey key = new LookupKey.Builder(KeyType.DATA).setAddressData(address).build();
+ assertEquals("data/US/CA/Mt View/El Camino", key.toString());
+
+ key = key.getParentKey();
+ assertEquals("data/US/CA/Mt View", key.toString());
+
+ key = key.getParentKey();
+ assertEquals("data/US/CA", key.toString());
+
+ key = key.getParentKey();
+ assertEquals("data/US", key.toString());
+
+ key = key.getParentKey();
+ assertEquals("data", key.toString());
+
+ key = key.getParentKey();
+ assertNull("root key's parent should be null", key);
+ }
+
+ public void testInvalidKeyTypeWillFail() {
+ try {
+ new LookupKey.Builder(RANDOM_KEY).build();
+ fail("should fail if key string does not start with a valid key type");
+ } catch (RuntimeException e) {
+ // Expected.
+ }
+ }
+
+ /**
+ * Ensures that even when the input key string is random, we still create a
+ * key. (We do not verify if the key maps to an real world entity like a city
+ * or country).
+ */
+ public void testWeDontVerifyKeyName() {
+ LookupKey key = new LookupKey.Builder(RANDOM_COUNTRY_KEY).build();
+ assertEquals(RANDOM_COUNTRY_KEY, key.toString());
+ }
+
+ public void testHash() {
+ String keys[] = { ROOT_KEY, ROOT_EXAMPLE_KEY, US_KEY, CALIFORNIA_KEY };
+ Map<LookupKey, String> map = new HashMap<LookupKey, String>();
+
+ for (String key : keys) {
+ map.put(new LookupKey.Builder(key).build(), key);
+ }
+
+ for (String key : keys) {
+ assertTrue(map.containsKey(new LookupKey.Builder(key).build()));
+ assertEquals(key, map.get(new LookupKey.Builder(key).build()));
+ }
+ assertFalse(map.containsKey(new LookupKey.Builder(RANDOM_COUNTRY_KEY).build()));
+ }
+}
diff --git a/test/com/android/i18n/addressinput/UtilTest.java b/test/com/android/i18n/addressinput/UtilTest.java
index 7cf1843..66366d3 100644
--- a/test/com/android/i18n/addressinput/UtilTest.java
+++ b/test/com/android/i18n/addressinput/UtilTest.java
@@ -19,33 +19,40 @@
import junit.framework.TestCase;
/**
- * @author Lara Rennie
+ * Tests for util functions.
*/
public class UtilTest extends TestCase {
- public void testGetScriptCodeLatinScript() throws Exception {
+ public void testIsExplicitLatinScript() throws Exception {
// Should recognise latin script in a variety of forms.
- assertEquals(Util.LATIN_SCRIPT, Util.getScriptCode("en"));
- assertEquals(Util.LATIN_SCRIPT, Util.getScriptCode("EN"));
- assertEquals(Util.LATIN_SCRIPT, Util.getScriptCode("zh-Latn"));
- assertEquals(Util.LATIN_SCRIPT, Util.getScriptCode("ja_LATN"));
- assertEquals(Util.LATIN_SCRIPT, Util.getScriptCode("ja_LATN-JP"));
- assertEquals(Util.LATIN_SCRIPT, Util.getScriptCode("ko-latn_JP"));
- // Other non-CJK scripts are also labelled as Latin.
- assertEquals(Util.LATIN_SCRIPT, Util.getScriptCode("ru"));
+ assertTrue(Util.isExplicitLatinScript("zh-Latn"));
+ assertTrue(Util.isExplicitLatinScript("ja_LATN"));
+ assertTrue(Util.isExplicitLatinScript("ja_LATN-JP"));
+ assertTrue(Util.isExplicitLatinScript("ko-latn_JP"));
}
- public void testGetScriptCodeCjkScript() throws Exception {
- assertFalse(Util.LATIN_SCRIPT.equals(Util.getScriptCode("ko")));
- assertFalse(Util.LATIN_SCRIPT.equals(Util.getScriptCode("KO")));
- assertFalse(Util.LATIN_SCRIPT.equals(Util.getScriptCode("ja")));
- assertFalse(Util.LATIN_SCRIPT.equals(Util.getScriptCode("ja-JP")));
- assertFalse(Util.LATIN_SCRIPT.equals(Util.getScriptCode("zh-Hans")));
- assertFalse(Util.LATIN_SCRIPT.equals(Util.getScriptCode("zh-Hans-CN")));
- assertFalse(Util.LATIN_SCRIPT.equals(Util.getScriptCode("zh-Hant")));
- assertFalse(Util.LATIN_SCRIPT.equals(Util.getScriptCode("zh-TW")));
- assertFalse(Util.LATIN_SCRIPT.equals(Util.getScriptCode("zh_TW")));
- assertFalse(Util.LATIN_SCRIPT.equals(Util.getScriptCode("ko")));
- assertFalse(Util.LATIN_SCRIPT.equals(Util.getScriptCode("ko_KR")));
+ public void testIsExplicitLatinScriptNonLatin() throws Exception {
+ assertFalse(Util.isExplicitLatinScript("ko"));
+ assertFalse(Util.isExplicitLatinScript("KO"));
+ assertFalse(Util.isExplicitLatinScript("ja"));
+ assertFalse(Util.isExplicitLatinScript("ja-JP"));
+ assertFalse(Util.isExplicitLatinScript("zh-Hans"));
+ assertFalse(Util.isExplicitLatinScript("zh-Hans-CN"));
+ assertFalse(Util.isExplicitLatinScript("zh-Hant"));
+ assertFalse(Util.isExplicitLatinScript("zh-TW"));
+ assertFalse(Util.isExplicitLatinScript("zh_TW"));
+ assertFalse(Util.isExplicitLatinScript("ko"));
+ assertFalse(Util.isExplicitLatinScript("ko_KR"));
+ assertFalse(Util.isExplicitLatinScript("en"));
+ assertFalse(Util.isExplicitLatinScript("EN"));
+ assertFalse(Util.isExplicitLatinScript("ru"));
+ }
+
+ public void testTrimToNull() throws Exception {
+ assertEquals("Trimmed String", Util.trimToNull(" Trimmed String "));
+ assertEquals("Trimmed String", Util.trimToNull(" Trimmed String"));
+ assertEquals("Trimmed String", Util.trimToNull("Trimmed String"));
+ assertEquals(null, Util.trimToNull(" "));
+ assertEquals(null, Util.trimToNull(null));
}
}