19b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com/*
29b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com * Copyright (C) 2010 Google Inc.
39b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com *
49b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com * Licensed under the Apache License, Version 2.0 (the "License");
59b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com * you may not use this file except in compliance with the License.
69b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com * You may obtain a copy of the License at
79b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com *
89b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com * http://www.apache.org/licenses/LICENSE-2.0
99b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com *
109b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com * Unless required by applicable law or agreed to in writing, software
119b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com * distributed under the License is distributed on an "AS IS" BASIS,
129b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com * See the License for the specific language governing permissions and
149b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com * limitations under the License.
159b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com */
169b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
179b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.compackage com.android.i18n.addressinput;
189b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
199b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.comimport java.util.EnumMap;
209b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.comimport java.util.Map;
219b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
229b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com/**
232c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com * A builder for creating keys that are used to lookup data in the local cache and fetch data from
242c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com * the server. There are two key types: {@code KeyType#DATA} or {@code KeyType#EXAMPLES}.
259b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com *
262c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com * <p> The {@code KeyType#DATA} key is built based on a universal Address hierarchy, which is:<br>
279b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com *
282c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com * {@code AddressField#Country} -> {@code AddressField#ADMIN_AREA} -> {@code AddressField#Locality}
292c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com * -> {@code AddressField#DEPENDENT_LOCALITY} </p>
309b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com *
312c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com * <p> The {@code KeyType#EXAMPLES} key is built with the following format:<br>
329b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com *
332c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com * {@code AddressField#Country} -> {@code ScriptType} -> language. </p>
349b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com */
35dc4cdad9dd71a57be66262d1d6d46a09c0973818lararennie@google.comfinal class LookupKey {
369b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
379b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com    /**
382c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     * Key types. Address Widget organizes address info based on key types. For example, if you want
392c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     * to know how to verify or format an US address, you need to use {@link KeyType#DATA} to get
402c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     * that info; if you want to get an example address, you use {@link KeyType#EXAMPLES} instead.
419b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com     */
42dc4cdad9dd71a57be66262d1d6d46a09c0973818lararennie@google.com    enum KeyType {
432c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
442c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        /**
452c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         * Key type for getting address data.
462c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         */
472c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        DATA,
482c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        /**
492c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         * Key type for getting examples.
502c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         */
512c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        EXAMPLES
522c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com    }
539b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
549b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com    /**
552c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     * Script types. This is used for countries that do not use Latin script, but accept it for
562c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     * transcribing their addresses. For example, you can write a Japanese address in Latin script
572c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     * instead of Japanese:
582c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     *
592c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     * <p> 7-2, Marunouchi 2-Chome, Chiyoda-ku, Tokyo 100-8799 </p>
602c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     *
612c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     * Notice that {@link ScriptType} is based on country/region, not language.
629b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com     */
63dc4cdad9dd71a57be66262d1d6d46a09c0973818lararennie@google.com    enum ScriptType {
642c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
652c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        /**
662c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         * The script that uses Roman characters like ABC (as opposed to scripts like Cyrillic or
672c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         * Arabic).
682c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         */
692c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        LATIN,
702c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
712c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        /**
722c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         * Local scripts. For Japan, it's Japanese (including Hiragana, Katagana, and Kanji); For
732c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         * Saudi Arabia, it's Arabic. Notice that for US, the local script is actually Latin script
742c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         * (The same goes for other countries that use Latin script). For these countries, we do not
752c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         * provide two set of data (Latin and local) since they use only Latin script. You have to
762c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         * specify the {@link ScriptType} as local instead Latin.
772c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         */
782c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        LOCAL
799b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com    }
809b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
812c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com    /**
822c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     * The universal address hierarchy. Notice that sub-administrative area is neglected here since
832c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     * it is not required to fill out address form.
842c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     */
850dbcfb4fae3e125550c534191627574ea69ec3aflararennie@google.com    private static final AddressField[] HIERARCHY = {
862c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            AddressField.COUNTRY,
872c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            AddressField.ADMIN_AREA,
882c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            AddressField.LOCALITY,
892c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            AddressField.DEPENDENT_LOCALITY};
909b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
910dbcfb4fae3e125550c534191627574ea69ec3aflararennie@google.com    private static final String SLASH_DELIM = "/";
929b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
930dbcfb4fae3e125550c534191627574ea69ec3aflararennie@google.com    private static final String DASH_DELIM = "--";
949b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
950dbcfb4fae3e125550c534191627574ea69ec3aflararennie@google.com    private static final String DEFAULT_LANGUAGE = "_default";
962c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
9737ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com    private final KeyType mKeyType;
982c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
9937ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com    private final ScriptType mScriptType;
1002c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
1012c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com    // Values for hierarchy address fields.
10237ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com    private final Map<AddressField, String> mNodes;
1032c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
10437ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com    private final String mKeyString;
1059b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
10637ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com    private final String mLanguageCode;
1072c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
1082c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com    private LookupKey(Builder builder) {
10937ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com        this.mKeyType = builder.keyType;
11037ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com        this.mScriptType = builder.script;
11137ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com        this.mNodes = builder.nodes;
11237ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com        this.mLanguageCode = builder.languageCode;
11337ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com        this.mKeyString = getKeyString();
1149b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com    }
1152c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
1162c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com    /**
1172c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     * Gets lookup key for the input address field. This method does not allow key with key type of
1182c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     * {@link KeyType#EXAMPLES}.
1192c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     *
1202c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     * @param field a field in the address hierarchy.
1212c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     * @return key of the specified address field. If address field is not in the hierarchy, or is
1222c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     *         more granular than the current key has, returns null. For example, if your current
123d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com     *         key is "data/US" (down to country level), and you want to get the key for Locality
1242c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     *         (more granular than country), it will return null.
1252c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     */
126dc4cdad9dd71a57be66262d1d6d46a09c0973818lararennie@google.com    LookupKey getKeyForUpperLevelField(AddressField field) {
12737ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com        if (mKeyType != KeyType.DATA) {
1282c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            // We only support getting the parent key for the data key type.
1292c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            throw new RuntimeException("Only support getting parent keys for the data key type.");
1309b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com        }
1312c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        Builder newKeyBuilder = new Builder(this);
1322c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
1332c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        boolean removeNode = false;
1342c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        boolean fieldInHierarchy = false;
1350dbcfb4fae3e125550c534191627574ea69ec3aflararennie@google.com        for (AddressField hierarchyField : HIERARCHY) {
1362c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            if (removeNode) {
1372c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                if (newKeyBuilder.nodes.containsKey(hierarchyField)) {
1382c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                    newKeyBuilder.nodes.remove(hierarchyField);
1392c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                }
1402c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            }
1412c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            if (hierarchyField == field) {
1422c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                if (!newKeyBuilder.nodes.containsKey(hierarchyField)) {
1432c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                    return null;
1442c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                }
1452c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                removeNode = true;
1462c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                fieldInHierarchy = true;
1472c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            }
1489b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com        }
1499b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
1502c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        if (!fieldInHierarchy) {
1512c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            return null;
1522c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        }
1539b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
15437ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com        newKeyBuilder.languageCode = mLanguageCode;
15537ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com        newKeyBuilder.script = mScriptType;
1569b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
1572c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        return newKeyBuilder.build();
1589b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com    }
1599b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
1602c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com    /**
161d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com     * Returns the string value of a field in a key for a particular
162d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com     * AddressField. For example, for the key "data/US/CA" and the address
163d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com     * field AddressField.COUNTRY, "US" would be returned. Returns an empty
164d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com     * string if the key does not have this field in it.
165d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com     */
166d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com    String getValueForUpperLevelField(AddressField field) {
167d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com        // First, get the key for this field.
168d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com        LookupKey key = getKeyForUpperLevelField(field);
169d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com        // Now we know the last value in the string is the value for this field.
170d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com        if (key != null) {
171d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com            String keyString = key.toString();
172d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com            int lastSlashPosition = keyString.lastIndexOf(SLASH_DELIM);
173d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com            if (lastSlashPosition > 0 && lastSlashPosition != keyString.length()) {
174d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com                return keyString.substring(lastSlashPosition + 1);
175d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com            }
176d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com        }
177d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com        return "";
178d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com    }
179d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com
180d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com    /**
1812c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     * Gets parent key for data key. For example, parent key for "data/US/CA" is "data/US". This
1822c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     * method does not allow key with key type of {@link KeyType#EXAMPLES}.
1832c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     */
184dc4cdad9dd71a57be66262d1d6d46a09c0973818lararennie@google.com    LookupKey getParentKey() {
18537ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com        if (mKeyType != KeyType.DATA) {
1862c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            throw new RuntimeException("Only support getting parent keys for the data key type.");
1872c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        }
1882c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        // Root key's parent should be null.
18937ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com        if (!mNodes.containsKey(AddressField.COUNTRY)) {
1902c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            return null;
1912c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        }
1929b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
1932c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        Builder parentKeyBuilder = new Builder(this);
1942c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        AddressField mostGranularField = AddressField.COUNTRY;
1959b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
1960dbcfb4fae3e125550c534191627574ea69ec3aflararennie@google.com        for (AddressField hierarchyField : HIERARCHY) {
19737ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com            if (!mNodes.containsKey(hierarchyField)) {
1982c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                break;
1992c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            }
2002c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            mostGranularField = hierarchyField;
2012c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        }
2022c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        parentKeyBuilder.nodes.remove(mostGranularField);
2032c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        return parentKeyBuilder.build();
2042c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com    }
2054846244b97f572c866738a8eef50e530cd73257ashaopengjia@google.com
206dc4cdad9dd71a57be66262d1d6d46a09c0973818lararennie@google.com    KeyType getKeyType() {
20737ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com        return mKeyType;
2082c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com    }
2099b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
2109b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com    /**
2112c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     * Gets a key in string format. E.g., "data/US/CA".
2129b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com     */
2132c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com    private String getKeyString() {
21437ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com        StringBuilder keyBuilder = new StringBuilder(mKeyType.name().toLowerCase());
2152c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
21637ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com        if (mKeyType == KeyType.DATA) {
2170dbcfb4fae3e125550c534191627574ea69ec3aflararennie@google.com            for (AddressField field : HIERARCHY) {
21837ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com                if (!mNodes.containsKey(field)) {
2192c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                    break;
2202c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                }
22137ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com                if (field == AddressField.COUNTRY && mLanguageCode != null) {
2220dbcfb4fae3e125550c534191627574ea69ec3aflararennie@google.com                    keyBuilder.append(SLASH_DELIM)
2230dbcfb4fae3e125550c534191627574ea69ec3aflararennie@google.com                            .append(mNodes.get(field)).append(DASH_DELIM)
22437ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com                            .append(mLanguageCode);
2252c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                } else {
2260dbcfb4fae3e125550c534191627574ea69ec3aflararennie@google.com                    keyBuilder.append(SLASH_DELIM).append(mNodes.get(field));
2272c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                }
2282c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            }
2292c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        } else {
23037ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com            if (mNodes.containsKey(AddressField.COUNTRY)) {
2312c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                // Example key. E.g., "examples/TW/local/_default".
2320dbcfb4fae3e125550c534191627574ea69ec3aflararennie@google.com                keyBuilder.append(SLASH_DELIM).append(mNodes.get(AddressField.COUNTRY))
2330dbcfb4fae3e125550c534191627574ea69ec3aflararennie@google.com                        .append(SLASH_DELIM).append(mScriptType.name().toLowerCase())
2340dbcfb4fae3e125550c534191627574ea69ec3aflararennie@google.com                        .append(SLASH_DELIM).append(DEFAULT_LANGUAGE);
2352c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            }
2362c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        }
2372c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
2382c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        return keyBuilder.toString();
2399b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com    }
2409b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
2419b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com    /**
2422c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     * Gets a lookup key as a plain text string., e.g., "data/US/CA".
2439b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com     */
2441e17a681ee8e5566384ead1588f6e6c4c8aa333aroubert@google.com    @Override
2452c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com    public String toString() {
24637ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com        return mKeyString;
2472c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com    }
2482c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
2491e17a681ee8e5566384ead1588f6e6c4c8aa333aroubert@google.com    @Override
2502c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com    public boolean equals(Object obj) {
2512c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        if (this == obj) {
2522c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            return true;
2532c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        }
2542c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        if ((obj == null) || (obj.getClass() != this.getClass())) {
2552c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            return false;
2569b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com        }
2572c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
25837ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com        return ((LookupKey) obj).toString().equals(mKeyString);
2592c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com    }
2602c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
2611e17a681ee8e5566384ead1588f6e6c4c8aa333aroubert@google.com    @Override
2622c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com    public int hashCode() {
26337ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com        return mKeyString.hashCode();
2649b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com    }
2659b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
266dc4cdad9dd71a57be66262d1d6d46a09c0973818lararennie@google.com    static boolean hasValidKeyPrefix(String key) {
267f3a3b6c5a1dce4d0e718146724ecfa1070900dablararennie@google.com        for (KeyType type : KeyType.values()) {
268f3a3b6c5a1dce4d0e718146724ecfa1070900dablararennie@google.com            if (key.startsWith(type.name().toLowerCase())) {
269f3a3b6c5a1dce4d0e718146724ecfa1070900dablararennie@google.com                return true;
270f3a3b6c5a1dce4d0e718146724ecfa1070900dablararennie@google.com            }
271f3a3b6c5a1dce4d0e718146724ecfa1070900dablararennie@google.com        }
272f3a3b6c5a1dce4d0e718146724ecfa1070900dablararennie@google.com        return false;
273f3a3b6c5a1dce4d0e718146724ecfa1070900dablararennie@google.com    }
274dc4cdad9dd71a57be66262d1d6d46a09c0973818lararennie@google.com
2759b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com    /**
2762c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com     * Builds lookup keys.
2779b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com     */
278dc4cdad9dd71a57be66262d1d6d46a09c0973818lararennie@google.com    static class Builder {
2792c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
2802c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        private KeyType keyType;
2812c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
2822c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        // Default to LOCAL script.
2832c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
2842c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        private ScriptType script = ScriptType.LOCAL;
2852c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
2862c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        private Map<AddressField, String> nodes = new EnumMap<AddressField, String>(
2872c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                AddressField.class);
2882c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
2892c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        private String languageCode;
2902c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
2912c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        /**
2922c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         * Creates a new builder for the specified key type. keyType cannot be null.
2932c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         */
294dc4cdad9dd71a57be66262d1d6d46a09c0973818lararennie@google.com        Builder(KeyType keyType) {
2952c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            this.keyType = keyType;
2969b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com        }
2979b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
2982c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        /**
2992c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         * Creates a new builder for the specified key. oldKey cannot be null.
3002c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         */
301dc4cdad9dd71a57be66262d1d6d46a09c0973818lararennie@google.com        Builder(LookupKey oldKey) {
30237ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com            this.keyType = oldKey.mKeyType;
30337ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com            this.script = oldKey.mScriptType;
30437ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com            this.languageCode = oldKey.mLanguageCode;
3050dbcfb4fae3e125550c534191627574ea69ec3aflararennie@google.com            for (AddressField field : HIERARCHY) {
30637ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com                if (!oldKey.mNodes.containsKey(field)) {
3072c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                    break;
3082c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                }
30937ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com                this.nodes.put(field, oldKey.mNodes.get(field));
3109b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com            }
3119b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com        }
3129b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
3132c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        /**
3142c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         * Builds the {@link LookupKey} with the input key string. Input string has to represent
3152c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         * either a {@link KeyType#DATA} key or a {@link KeyType#EXAMPLES} key. Also, key hierarchy
3162c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         * deeper than {@link AddressField#DEPENDENT_LOCALITY} is not allowed. Notice that if any
3172c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         * node in the hierarchy is empty, all the descendant nodes' values will be neglected. For
3182c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         * example, input string "data/US//Mt View" will become "data/US".
3192c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         *
3202c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         * @param keyString e.g., "data/US/CA"
3212c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         */
322dc4cdad9dd71a57be66262d1d6d46a09c0973818lararennie@google.com        Builder(String keyString) {
3230dbcfb4fae3e125550c534191627574ea69ec3aflararennie@google.com            String[] parts = keyString.split(SLASH_DELIM);
3242c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            // Check some pre-conditions.
3252c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            if (!parts[0].equals(KeyType.DATA.name().toLowerCase()) &&
3262c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                    !parts[0].equals(KeyType.EXAMPLES.name().toLowerCase())) {
3272c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                throw new RuntimeException("Wrong key type: " + parts[0]);
3282c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            }
3290dbcfb4fae3e125550c534191627574ea69ec3aflararennie@google.com            if (parts.length > HIERARCHY.length + 1) {
3302c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                throw new RuntimeException(
3312c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                        "input key '" + keyString + "' deeper than supported hierarchy");
3322c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            }
3332c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            if (parts[0].equals("data")) {
3342c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                keyType = KeyType.DATA;
3352c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
3362c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                // Parses country and language info.
3372c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                if (parts.length > 1) {
3382c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                    String substr = Util.trimToNull(parts[1]);
3390dbcfb4fae3e125550c534191627574ea69ec3aflararennie@google.com                    if (substr.contains(DASH_DELIM)) {
3400dbcfb4fae3e125550c534191627574ea69ec3aflararennie@google.com                        String[] s = substr.split(DASH_DELIM);
3412c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                        if (s.length != 2) {
3422c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                            throw new RuntimeException(
3432c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                                    "Wrong format: Substring should be country "
3442c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                                            + "code--language code");
3452c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                        }
3462c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                        substr = s[0];
3472c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                        languageCode = s[1];
3482c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                    }
3490dbcfb4fae3e125550c534191627574ea69ec3aflararennie@google.com                    this.nodes.put(HIERARCHY[0], substr);
3502c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                }
3512c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
3522c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                // Parses sub-country info.
3532c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                if (parts.length > 2) {
3542c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                    for (int i = 2; i < parts.length; ++i) {
3552c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                        String substr = Util.trimToNull(parts[i]);
3562c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                        if (substr == null) {
3572c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                            break;
3582c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                        }
3590dbcfb4fae3e125550c534191627574ea69ec3aflararennie@google.com                        this.nodes.put(HIERARCHY[i - 1], substr);
3602c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                    }
3612c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                }
3622c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            } else if (parts[0].equals("examples")) {
3632c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                keyType = KeyType.EXAMPLES;
3642c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
3652c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                // Parses country info.
3662c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                if (parts.length > 1) {
3672c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                    this.nodes.put(AddressField.COUNTRY, parts[1]);
3682c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                }
3692c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
3702c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                // Parses script types.
3712c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                if (parts.length > 2) {
3722c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                    String scriptStr = parts[2];
3732c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                    if (scriptStr.equals("local")) {
3742c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                        this.script = ScriptType.LOCAL;
3752c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                    } else if (scriptStr.equals("latin")) {
3762c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                        this.script = ScriptType.LATIN;
3772c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                    } else {
3782c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                        throw new RuntimeException("Script type has to be either latin or local.");
3792c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                    }
3802c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                }
3812c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
3822c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                // Parses language code. Example: "zh_Hant" in
3832c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                // "examples/TW/local/zH_Hant".
3840dbcfb4fae3e125550c534191627574ea69ec3aflararennie@google.com                if (parts.length > 3 && !parts[3].equals(DEFAULT_LANGUAGE)) {
3852c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                    languageCode = parts[3];
3862c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                }
3872c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            }
3889b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com        }
3899b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
390dc4cdad9dd71a57be66262d1d6d46a09c0973818lararennie@google.com        Builder setLanguageCode(String languageCode) {
3912c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            this.languageCode = languageCode;
3922c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            return this;
3939b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com        }
3949b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
3952c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        /**
3962c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         * Sets key using {@link AddressData}. Notice that if any node in the hierarchy is empty,
3972c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         * all the descendant nodes' values will be neglected. For example, the following address
3982c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         * misses {@link AddressField#ADMIN_AREA}, thus its data key will be "data/US".
3992c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         *
4002c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         * <p> country: US<br> administrative area: null<br> locality: Mt. View </p>
4012c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com         */
402dc4cdad9dd71a57be66262d1d6d46a09c0973818lararennie@google.com        Builder setAddressData(AddressData data) {
4032c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            languageCode = data.getLanguageCode();
4042c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            if (languageCode != null) {
4052c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                if (Util.isExplicitLatinScript(languageCode)) {
4062c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                    script = ScriptType.LATIN;
4072c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                }
4082c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            }
4099b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
4102c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            if (data.getPostalCountry() == null) {
4112c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                return this;
4122c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            }
4132c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            this.nodes.put(AddressField.COUNTRY, data.getPostalCountry());
4149b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
4152c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            if (data.getAdministrativeArea() == null) {
4162c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                return this;
4172c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            }
4182c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            this.nodes.put(AddressField.ADMIN_AREA, data.getAdministrativeArea());
4192c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
4202c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            if (data.getLocality() == null) {
4212c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                return this;
4222c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            }
4232c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            this.nodes.put(AddressField.LOCALITY, data.getLocality());
4242c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com
4252c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            if (data.getDependentLocality() == null) {
4262c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com                return this;
4272c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            }
4282c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            this.nodes.put(AddressField.DEPENDENT_LOCALITY, data.getDependentLocality());
4292c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            return this;
4309b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com        }
4319b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com
432dc4cdad9dd71a57be66262d1d6d46a09c0973818lararennie@google.com        LookupKey build() {
4332c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com            return new LookupKey(this);
4342c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com        }
4359b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com    }
4369b19e1202dec7ddb9c43d9459c901da291030d2flararennie@google.com}
437