16c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com/* 26c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com * Copyright (C) 2010 Google Inc. 36c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com * 46c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com * Licensed under the Apache License, Version 2.0 (the "License"); 56c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com * you may not use this file except in compliance with the License. 66c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com * You may obtain a copy of the License at 76c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com * 86c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com * http://www.apache.org/licenses/LICENSE-2.0 96c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com * 106c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com * Unless required by applicable law or agreed to in writing, software 116c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com * distributed under the License is distributed on an "AS IS" BASIS, 126c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com * See the License for the specific language governing permissions and 146c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com * limitations under the License. 156c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com */ 166c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com 176c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.compackage com.android.i18n.addressinput; 186c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com 196c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.comimport com.android.i18n.addressinput.LookupKey.KeyType; 206c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com 21d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.comimport android.util.Log; 22d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com 236c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.comimport org.json.JSONArray; 246c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.comimport org.json.JSONException; 256c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com 266c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.comimport java.util.EnumMap; 276c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.comimport java.util.HashMap; 28579db66046292a306a96dc478fa17da605fdda6croubert@google.comimport java.util.HashSet; 296c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.comimport java.util.Map; 30579db66046292a306a96dc478fa17da605fdda6croubert@google.comimport java.util.Set; 316c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com 326c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com/** 336c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com * Access point for the cached address verification data. The data contained here will mainly be 346c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com * used to build {@link FieldVerifier}'s. This class is implemented as a singleton. 356c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com */ 364c22ccb59af2b2feca7188afe5a0e3ec04a3c913jeanine@google.compublic class ClientData implements DataSource { 376c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com 38d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com private static final String TAG = "ClientData"; 39d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com 40d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com /** 41d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com * Data to bootstrap the process. The data are all regional (country level) 422c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com * data. Keys are like "data/US/CA" 432c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com */ 4437ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com private final Map<String, JsoMap> mBootstrapMap = new HashMap<String, JsoMap>(); 456c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com 4637ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com private CacheData mCacheData; 472c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 484c22ccb59af2b2feca7188afe5a0e3ec04a3c913jeanine@google.com public ClientData(CacheData cacheData) { 4937ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com this.mCacheData = cacheData; 502c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com buildRegionalData(); 516c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com } 526c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com 531e17a681ee8e5566384ead1588f6e6c4c8aa333aroubert@google.com @Override 542c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com public AddressVerificationNodeData get(String key) { 5537ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com JsoMap jso = mCacheData.getObj(key); 56d0fa5a00b37310cb2d0b3eb4b8282a52ceeca5c7roubert@google.com if (jso == null) { // Not cached. 57d0fa5a00b37310cb2d0b3eb4b8282a52ceeca5c7roubert@google.com fetchDataIfNotAvailable(key); 58d0fa5a00b37310cb2d0b3eb4b8282a52ceeca5c7roubert@google.com jso = mCacheData.getObj(key); 59d0fa5a00b37310cb2d0b3eb4b8282a52ceeca5c7roubert@google.com } 602c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com if (jso != null && isValidDataKey(key)) { 612c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com return createNodeData(jso); 622c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 632c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com return null; 646c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com } 652c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 661e17a681ee8e5566384ead1588f6e6c4c8aa333aroubert@google.com @Override 672c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com public AddressVerificationNodeData getDefaultData(String key) { 682c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com // root data 692c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com if (key.split("/").length == 1) { 7037ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com JsoMap jso = mBootstrapMap.get(key); 712c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com if (jso == null || !isValidDataKey(key)) { 722c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com throw new RuntimeException("key " + key + " does not have bootstrap data"); 732c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 742c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com return createNodeData(jso); 752c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 762c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 772c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com key = getCountryKey(key); 7837ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com JsoMap jso = mBootstrapMap.get(key); 792c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com if (jso == null || !isValidDataKey(key)) { 802c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com throw new RuntimeException("key " + key + " does not have bootstrap data"); 812c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 822c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com return createNodeData(jso); 836c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com } 846c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com 852c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com private String getCountryKey(String hierarchyKey) { 862c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com if (hierarchyKey.split("/").length <= 1) { 872c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com throw new RuntimeException("Cannot get country key with key '" + hierarchyKey + "'"); 882c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 892c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com if (isCountryKey(hierarchyKey)) { 902c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com return hierarchyKey; 912c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 926c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com 932c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com String[] parts = hierarchyKey.split("/"); 946c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com 952c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com return new StringBuilder().append(parts[0]) 962c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com .append("/") 972c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com .append(parts[1]) 982c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com .toString(); 992c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 1006c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com 1012c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com private boolean isCountryKey(String hierarchyKey) { 1022c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com Util.checkNotNull(hierarchyKey, "Cannot use null as a key"); 1032c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com return hierarchyKey.split("/").length == 2; 1042c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 1056c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com 1066c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com 1072c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com /** 1082c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com * Returns the contents of the JSON-format string as a map. 1092c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com */ 1102c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com protected AddressVerificationNodeData createNodeData(JsoMap jso) { 1112c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com Map<AddressDataKey, String> map = 1122c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com new EnumMap<AddressDataKey, String>(AddressDataKey.class); 1132c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 1142c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com JSONArray arr = jso.getKeys(); 1152c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com for (int i = 0; i < arr.length(); i++) { 1162c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com try { 1172c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com AddressDataKey key = AddressDataKey.get(arr.getString(i)); 1182c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 1192a2386e14357a672090d07daf6e2316d2dd34b88lararennie@google.com if (key == null) { 1202a2386e14357a672090d07daf6e2316d2dd34b88lararennie@google.com // Not all keys are supported by Android, so we continue if we encounter one 1212a2386e14357a672090d07daf6e2316d2dd34b88lararennie@google.com // that is not used. 1222a2386e14357a672090d07daf6e2316d2dd34b88lararennie@google.com continue; 1232a2386e14357a672090d07daf6e2316d2dd34b88lararennie@google.com } 1242c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 1252c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com String value = jso.get(key.toString().toLowerCase()); 1262c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com map.put(key, value); 1272c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } catch (JSONException e) { 1282c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com // This should not happen - we should not be fetching a key from outside the bounds 1292c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com // of the array. 1302c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 1312c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 1326c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com 1332c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com return new AddressVerificationNodeData(map); 1346c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com } 1356c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com 1362c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com /** 1372c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com * We can be initialized with the full set of address information, but validation only uses info 1382c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com * prefixed with "data" (in particular, no info prefixed with "examples"). 1392c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com */ 1402c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com private boolean isValidDataKey(String key) { 1412c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com return key.startsWith("data"); 1426c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com } 1436c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com 1442c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com /** 1452c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com * Initializes regionalData structure based on property file. 1466c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com */ 1472c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com private void buildRegionalData() { 1482c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com StringBuilder countries = new StringBuilder(); 1492c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 1502c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com for (String countryCode : RegionDataConstants.getCountryFormatMap().keySet()) { 1512c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com countries.append(countryCode + "~"); 1522c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com String json = RegionDataConstants.getCountryFormatMap().get(countryCode); 1532c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com JsoMap jso = null; 1542c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com try { 1552c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com jso = JsoMap.buildJsoMap(json); 1562c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } catch (JSONException e) { 1572c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com // Ignore. 1582c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 1592c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 1602c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com AddressData data = new AddressData.Builder().setCountry(countryCode).build(); 1612c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com LookupKey key = new LookupKey.Builder(KeyType.DATA).setAddressData(data).build(); 16237ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com mBootstrapMap.put(key.toString(), jso); 1632c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 1642c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com countries.setLength(countries.length() - 1); 1652c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 1662c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com // TODO: this is messy. do we have better ways to do it? 1672c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com /* Creates verification data for key="data". This will be used for the 1682c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com * root FieldVerifier. 1692c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com */ 1702c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com String str = "{\"id\":\"data\",\"" + 1712c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com AddressDataKey.COUNTRIES.toString().toLowerCase() + 1722c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com "\": \"" + countries.toString() + "\"}"; 1732c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com JsoMap jsoData = null; 1742c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com try { 1752c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com jsoData = JsoMap.buildJsoMap(str); 1762c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } catch (JSONException e) { 1772c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com // Ignore. 1782c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 17937ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com mBootstrapMap.put("data", jsoData); 18056493344f6b48d9ae2a2fea8e2a953f33b1e00d8roubert@google.com } 1812c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 1822c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com /** 1832c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com * Fetches data from remote server if it is not cached yet. 1842c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com * 1852c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com * @param key The key for data that being requested. Key can be either a data key (starts with 1862c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com * "data") or example key (starts with "examples") 1872c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com */ 1882c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com private void fetchDataIfNotAvailable(String key) { 18937ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com JsoMap jso = mCacheData.getObj(key); 1902c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com if (jso == null) { 1912c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com // If there is bootstrap data for the key, pass the data to fetchDynamicData 19237ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com JsoMap regionalData = mBootstrapMap.get(key); 193d0fa5a00b37310cb2d0b3eb4b8282a52ceeca5c7roubert@google.com NotifyingListener listener = new NotifyingListener(this); 194f3a3b6c5a1dce4d0e718146724ecfa1070900dablararennie@google.com // If the key was invalid, we don't want to attempt to fetch it. 195f3a3b6c5a1dce4d0e718146724ecfa1070900dablararennie@google.com if (LookupKey.hasValidKeyPrefix(key)) { 196d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com LookupKey lookupKey = new LookupKey.Builder(key).build(); 197d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com mCacheData.fetchDynamicData(lookupKey, regionalData, listener); 198f3a3b6c5a1dce4d0e718146724ecfa1070900dablararennie@google.com try { 199f3a3b6c5a1dce4d0e718146724ecfa1070900dablararennie@google.com listener.waitLoadingEnd(); 200d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com // Check to see if there is data for this key now. 201d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com if (mCacheData.getObj(key) == null && isCountryKey(key)) { 202d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com // If not, see if there is data in RegionDataConstants. 2034846244b97f572c866738a8eef50e530cd73257ashaopengjia@google.com Log.i(TAG, "Server failure: looking up key in region data constants."); 204d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com mCacheData.getFromRegionDataConstants(lookupKey); 205d07313666e6455c4b63f965ac6a3697f7a9573e5lararennie@google.com } 206f3a3b6c5a1dce4d0e718146724ecfa1070900dablararennie@google.com } catch (InterruptedException e) { 207f3a3b6c5a1dce4d0e718146724ecfa1070900dablararennie@google.com throw new RuntimeException(e); 208f3a3b6c5a1dce4d0e718146724ecfa1070900dablararennie@google.com } 209d0fa5a00b37310cb2d0b3eb4b8282a52ceeca5c7roubert@google.com } 2102c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 2116c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com } 2122c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 2132c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com public void requestData(LookupKey key, DataLoadListener listener) { 2142c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com Util.checkNotNull(key, "Null lookup key not allowed"); 21537ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com JsoMap regionalData = mBootstrapMap.get(key.toString()); 21637ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com mCacheData.fetchDynamicData(key, regionalData, listener); 217579db66046292a306a96dc478fa17da605fdda6croubert@google.com } 2182c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 2192c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com /** 2202c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com * Fetches all data for the specified country from the remote server. 2212c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com */ 2222c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com public void prefetchCountry(String country, DataLoadListener listener) { 2232c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com String key = "data/" + country; 2242c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com Set<RecursiveLoader> loaders = new HashSet<RecursiveLoader>(); 225e5107b05ab6849c4c21f82ba7164742c18c9d1c1roubert@google.com listener.dataLoadingBegin(); 22637ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com mCacheData.fetchDynamicData( 2272c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com new LookupKey.Builder(key).build(), 2282c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com null, 2292c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com new RecursiveLoader(key, loaders, listener)); 230579db66046292a306a96dc478fa17da605fdda6croubert@google.com } 231579db66046292a306a96dc478fa17da605fdda6croubert@google.com 2322c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com /** 2332c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com * A helper class to recursively load all sub keys using fetchDynamicData(). 2342c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com */ 2352c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com private class RecursiveLoader implements DataLoadListener { 2362c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 2372c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com private final String key; 2382c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 2392c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com private final Set<RecursiveLoader> loaders; 2402c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 2412c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com private final DataLoadListener listener; 2422c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 2432c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com public RecursiveLoader(String key, Set<RecursiveLoader> loaders, 2442c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com DataLoadListener listener) { 2452c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com this.key = key; 2462c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com this.loaders = loaders; 2472c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com this.listener = listener; 2482c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 2492c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com synchronized (loaders) { 2502c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com loaders.add(this); 2512c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 252579db66046292a306a96dc478fa17da605fdda6croubert@google.com } 2532c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 2541e17a681ee8e5566384ead1588f6e6c4c8aa333aroubert@google.com @Override 2552c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com public void dataLoadingBegin() { 256579db66046292a306a96dc478fa17da605fdda6croubert@google.com } 2572c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 2581e17a681ee8e5566384ead1588f6e6c4c8aa333aroubert@google.com @Override 2592c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com public void dataLoadingEnd() { 260120958045abaca8335af9870d48cb29c940d1fe8lararennie@google.com final String subKeys = AddressDataKey.SUB_KEYS.name().toLowerCase(); 261120958045abaca8335af9870d48cb29c940d1fe8lararennie@google.com final String subMores = AddressDataKey.SUB_MORES.name().toLowerCase(); 2622c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 26337ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com JsoMap map = mCacheData.getObj(key); 2642c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 265120958045abaca8335af9870d48cb29c940d1fe8lararennie@google.com if (map.containsKey(subMores)) { 2662c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com // This key could have sub keys. 2672c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com String[] mores = {}; 2682c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com String[] keys = {}; 2692c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 270120958045abaca8335af9870d48cb29c940d1fe8lararennie@google.com mores = map.get(subMores).split("~"); 2712c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 272120958045abaca8335af9870d48cb29c940d1fe8lararennie@google.com if (map.containsKey(subKeys)) { 273120958045abaca8335af9870d48cb29c940d1fe8lararennie@google.com keys = map.get(subKeys).split("~"); 2742c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 2752c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 2762c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com if (mores.length != keys.length) { // This should never happen. 2772c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com throw new IndexOutOfBoundsException("mores.length != keys.length"); 2782c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 2792c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 2802c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com for (int i = 0; i < mores.length; i++) { 2812c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com if (mores[i].equalsIgnoreCase("true")) { 2822c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com // This key should have sub keys. 2832c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com String subKey = key + "/" + keys[i]; 28437ea1c8bb9bf0ee18a9ce7412ace03885098e348lararennie@google.com mCacheData.fetchDynamicData( 2852c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com new LookupKey.Builder(subKey).build(), 2862c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com null, 2872c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com new RecursiveLoader(subKey, loaders, listener)); 2882c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 2892c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 2902c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 2912c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com 2922c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com synchronized (loaders) { 2932c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com loaders.remove(this); 2942c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com if (loaders.isEmpty()) { 2952c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com listener.dataLoadingEnd(); 2962c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 2972c25a6f4922225b619b0e389befde8b941e78834jeanine@google.com } 298579db66046292a306a96dc478fa17da605fdda6croubert@google.com } 299579db66046292a306a96dc478fa17da605fdda6croubert@google.com } 3006c0b10861ff77331bd5a20d527872ad55a5043e1lararennie@google.com} 301