/**
  * Returns the last time the cache was refreshed.
  *
  * @param cacheType Cache type.
  * @param cacheKey Cache key.
  * @return Last update time of the cache.
  */
 public long getLastCacheUpdateTime(String cacheType, String cacheKey) {
   try {
     if (cacheType == null
         || cacheKey == null
         || Constants.EMPTY_STRING.equals(cacheType)
         || Constants.EMPTY_STRING.equals(cacheKey)) {
       return 0;
     }
     if (!doesCacheExist(cacheType)) {
       return 0;
     }
     final QuerySpec querySpec =
         QuerySpec.buildExactQuerySpec(cacheType, CACHE_KEY, cacheKey, null, null, 1);
     final JSONArray results = smartStore.query(querySpec, 0);
     if (results != null && results.length() > 0) {
       final JSONObject jObj = results.optJSONObject(0);
       if (jObj != null) {
         return jObj.optLong(SmartStore.SOUP_LAST_MODIFIED_DATE);
       }
     }
   } catch (IllegalStateException e) {
     Log.e(TAG, "IllegalStateException occurred while attempting to read last cached time", e);
   } catch (JSONException e) {
     Log.e(TAG, "JSONException occurred while attempting to read last cached time", e);
   } catch (SmartStoreException e) {
     Log.e(TAG, "SmartStoreException occurred while attempting to read last cached time", e);
   }
   return 0;
 }
  /**
   * Reads a list of Salesforce objects from the cache.
   *
   * @param cacheType Cache type.
   * @param cacheKey Cache key.
   * @return List of Salesforce objects.
   */
  public List<SalesforceObject> readObjects(String cacheType, String cacheKey) {
    if (cacheType == null
        || cacheKey == null
        || Constants.EMPTY_STRING.equals(cacheType)
        || Constants.EMPTY_STRING.equals(cacheKey)) {
      return null;
    }
    if (!doesCacheExist(cacheType)) {
      return null;
    }

    // Checks in memory cache first.
    if (objectCacheMap != null) {
      final List<SalesforceObject> cachedObjs = objectCacheMap.get(cacheKey);
      if (cachedObjs != null && cachedObjs.size() > 0) {
        return cachedObjs;
      }
    }

    // Falls back on smart store cache if in memory cache is empty.
    try {
      final QuerySpec querySpec =
          QuerySpec.buildExactQuerySpec(cacheType, CACHE_KEY, cacheKey, null, null, 1);
      final JSONArray results = smartStore.query(querySpec, 0);
      if (results != null && results.length() > 0) {
        final JSONObject jObj = results.optJSONObject(0);
        if (jObj != null) {
          final JSONArray res = jObj.optJSONArray(CACHE_DATA);
          if (res != null && res.length() > 0) {
            final List<SalesforceObject> cachedList = new ArrayList<SalesforceObject>();
            for (int j = 0; j < res.length(); j++) {
              final JSONObject sfObj = res.optJSONObject(j);
              if (sfObj != null) {
                cachedList.add(new SalesforceObject(sfObj));
              }
            }
            if (cachedList.size() > 0) {

              // Inserts or updates data in memory cache.
              if (objectCacheMap != null) {
                if (objectCacheMap.get(cacheKey) != null) {
                  objectCacheMap.remove(cacheKey);
                }
                objectCacheMap.put(cacheKey, cachedList);
              }
              return cachedList;
            }
          }
        }
      }
    } catch (JSONException e) {
      Log.e(TAG, "JSONException occurred while attempting to read cached data", e);
    } catch (SmartStoreException e) {
      Log.e(TAG, "SmartStoreException occurred while attempting to read cached data", e);
    }
    return null;
  }
 /**
  * Removes existing data from the specified cache.
  *
  * @param cacheType Cache type.
  * @param cacheKey Cache key.
  */
 public void removeCache(String cacheType, String cacheKey) {
   if (cacheType == null
       || cacheKey == null
       || Constants.EMPTY_STRING.equals(cacheType)
       || Constants.EMPTY_STRING.equals(cacheKey)) {
     return;
   }
   if (doesCacheExist(cacheType)) {
     smartStore.dropSoup(cacheType);
     removeSoupNameFromMasterSoup(cacheType);
     resetInMemoryCache();
   }
 }
  /**
   * Writes a list of Salesforce object layouts to the cache.
   *
   * @param objects List of Salesforce object layouts.
   * @param cacheKey Cache key.
   * @param cacheType Cache type.O
   */
  public void writeObjectLayouts(
      List<SalesforceObjectTypeLayout> objects, String cacheKey, String cacheType) {
    if (objects == null
        || cacheType == null
        || cacheKey == null
        || Constants.EMPTY_STRING.equals(cacheType)
        || Constants.EMPTY_STRING.equals(cacheKey)
        || objects.size() == 0) {
      return;
    }
    final String soupName = cacheType + cacheKey;

    // Inserts or updates data in memory cache.
    if (objectTypeLayoutCacheMap != null) {
      if (objectTypeLayoutCacheMap.get(cacheKey) != null) {
        objectTypeLayoutCacheMap.remove(cacheKey);
      }
      objectTypeLayoutCacheMap.put(cacheKey, objects);
    }

    // Inserts or updates data in smart store.
    final JSONArray data = new JSONArray();
    for (final SalesforceObjectTypeLayout object : objects) {
      if (object != null) {
        final JSONObject obj = new JSONObject();
        try {
          obj.put("rawData", object.getRawData());
          obj.put("type", object.getObjectType());
          data.put(obj);
        } catch (JSONException e) {
          Log.e(TAG, "JSONException occurred while attempting to cache data", e);
        }
      }
    }
    if (data.length() > 0) {
      final JSONObject obj = new JSONObject();
      try {
        obj.put(String.format(CACHE_DATA_KEY, cacheKey), data);
        obj.put(String.format(CACHE_TIME_KEY, cacheKey), System.currentTimeMillis());
        upsertData(soupName, obj, cacheKey);
      } catch (JSONException e) {
        Log.e(TAG, "JSONException occurred while attempting to cache data", e);
      } catch (SmartStoreException e) {
        Log.e(TAG, "SmartStoreException occurred while attempting to cache data", e);
      }
    }
  }
 /**
  * Returns whether the specified cache exists.
  *
  * @param soupName Soup name.
  * @return True - if the cache exists, False - otherwise.
  */
 public boolean doesCacheExist(String soupName) {
   if (soupName == null
       || Constants.EMPTY_STRING.equals(soupName)
       || !smartStore.hasSoup(soupName)) {
     return false;
   }
   return true;
 }
  /**
   * Writes a list of Salesforce object types to the cache.
   *
   * @param objectTypes List of Salesforce object types.
   * @param cacheKey Cache key.
   * @param cacheType Cache type.
   */
  public void writeObjectTypes(
      List<SalesforceObjectType> objectTypes, String cacheKey, String cacheType) {
    if (objectTypes == null
        || cacheType == null
        || cacheKey == null
        || Constants.EMPTY_STRING.equals(cacheType)
        || Constants.EMPTY_STRING.equals(cacheKey)
        || objectTypes.size() == 0) {
      return;
    }

    // Inserts or updates data in memory cache.
    if (objectTypeCacheMap != null) {
      if (objectTypeCacheMap.get(cacheKey) != null) {
        objectTypeCacheMap.remove(cacheKey);
      }
      objectTypeCacheMap.put(cacheKey, objectTypes);
    }

    // Inserts or updates data in smart store.
    final JSONArray data = new JSONArray();
    for (final SalesforceObjectType objectType : objectTypes) {
      if (objectType != null) {
        data.put(objectType.getRawData());
      }
    }
    if (data.length() > 0) {
      final JSONObject object = new JSONObject();
      try {
        object.put(CACHE_KEY, cacheKey);
        object.put(CACHE_DATA, data);
        upsertData(cacheType, object, cacheKey);
      } catch (JSONException e) {
        Log.e(TAG, "JSONException occurred while attempting to cache data", e);
      } catch (SmartStoreException e) {
        Log.e(TAG, "SmartStoreException occurred while attempting to cache data", e);
      }
    }
  }
 /**
  * Helper method that inserts/updates a record in the cache.
  *
  * @param soupName Soup name.
  * @param object Object to be inserted.
  * @param cacheKey Cache key.
  */
 private void upsertData(String soupName, JSONObject object, String cacheKey) {
   if (soupName == null || object == null || Constants.EMPTY_STRING.equals(soupName)) {
     return;
   }
   registerSoup(soupName, cacheKey);
   try {
     smartStore.upsert(soupName, object, cacheKey);
     addSoupNameToMasterSoup(soupName);
   } catch (JSONException e) {
     Log.e(TAG, "JSONException occurred while attempting to cache data", e);
   } catch (SmartStoreException e) {
     Log.e(TAG, "SmartStoreException occurred while attempting to cache data", e);
   }
 }
 /**
  * Returns the last time the cache was refreshed.
  *
  * @param cacheType Cache type.
  * @param cacheKey Cache key.
  * @return Last update time of the cache.
  */
 public long getLastCacheUpdateTime(String cacheType, String cacheKey) {
   try {
     if (cacheType == null
         || cacheKey == null
         || Constants.EMPTY_STRING.equals(cacheType)
         || Constants.EMPTY_STRING.equals(cacheKey)) {
       return 0;
     }
     final String soupName = cacheType + cacheKey;
     if (!doesCacheExist(soupName)) {
       return 0;
     }
     final String smartSql =
         "SELECT {"
             + soupName
             + ":"
             + String.format(CACHE_TIME_KEY, cacheKey)
             + "} FROM {"
             + soupName
             + "}";
     final QuerySpec querySpec = QuerySpec.buildSmartQuerySpec(smartSql, 1);
     final JSONArray results = smartStore.query(querySpec, 0);
     if (results != null && results.length() > 0) {
       final JSONArray array = results.optJSONArray(0);
       if (array != null && array.length() > 0) {
         return array.optLong(0);
       }
     }
   } catch (IllegalStateException e) {
     Log.e(TAG, "IllegalStateException occurred while attempting to read last cached time", e);
   } catch (JSONException e) {
     Log.e(TAG, "JSONException occurred while attempting to read last cached time", e);
   } catch (SmartStoreException e) {
     Log.e(TAG, "SmartStoreException occurred while attempting to read last cached time", e);
   }
   return 0;
 }
 /**
  * Parameterized constructor.
  *
  * @param object Raw data for object.
  */
 public SalesforceObject(JSONObject object) {
   objectId = object.optString(Constants.ID);
   if (objectId == null || Constants.EMPTY_STRING.equals(objectId)) {
     objectId = object.optString(Constants.ID.toLowerCase(Locale.US));
     objectType = object.optString(Constants.TYPE.toLowerCase(Locale.US));
     name = object.optString(Constants.NAME.toLowerCase(Locale.US));
   } else {
     name = object.optString(Constants.NAME);
     final JSONObject attributes = object.optJSONObject(Constants.ATTRIBUTES);
     if (attributes != null) {
       objectType = attributes.optString(Constants.TYPE.toLowerCase(Locale.US));
       if (objectType == null
           || Constants.RECENTLY_VIEWED.equals(objectType)
           || Constants.NULL_STRING.equals(objectType)) {
         objectType = object.optString(Constants.TYPE);
       }
     }
   }
   rawData = object;
 }
  /**
   * Reads a list of Salesforce object layouts from the cache.
   *
   * @param cacheType Cache type.
   * @param cacheKey Cache key.
   * @return List of Salesforce object layouts.
   */
  public List<SalesforceObjectTypeLayout> readObjectLayouts(String cacheType, String cacheKey) {
    if (cacheType == null
        || cacheKey == null
        || Constants.EMPTY_STRING.equals(cacheType)
        || Constants.EMPTY_STRING.equals(cacheKey)) {
      return null;
    }
    final String soupName = cacheType + cacheKey;
    if (!doesCacheExist(soupName)) {
      return null;
    }

    // Checks in memory cache first.
    if (objectTypeLayoutCacheMap != null) {
      final List<SalesforceObjectTypeLayout> cachedObjs = objectTypeLayoutCacheMap.get(cacheKey);
      if (cachedObjs != null && cachedObjs.size() > 0) {
        return cachedObjs;
      }
    }

    // Falls back on smart store cache if in memory cache is empty.
    final String smartSql =
        "SELECT {"
            + soupName
            + ":"
            + String.format(CACHE_DATA_KEY, cacheKey)
            + "} FROM {"
            + soupName
            + "}";
    try {
      final QuerySpec querySpec = QuerySpec.buildSmartQuerySpec(smartSql, 1);
      final JSONArray results = smartStore.query(querySpec, 0);
      if (results != null && results.length() > 0) {
        final JSONArray array = results.optJSONArray(0);
        if (array != null && array.length() > 0) {
          final String res = array.optString(0);
          if (res != null && res.length() > 0) {
            final JSONArray cachedResults = new JSONArray(res);
            if (cachedResults.length() > 0) {
              final List<SalesforceObjectTypeLayout> cachedList =
                  new ArrayList<SalesforceObjectTypeLayout>();
              for (int j = 0; j < cachedResults.length(); j++) {
                final JSONObject sfObj = cachedResults.optJSONObject(j);
                if (sfObj != null) {
                  final JSONObject rawData = sfObj.optJSONObject("rawData");
                  final String type = sfObj.optString("type");
                  if (rawData != null && type != null && !Constants.EMPTY_STRING.equals(type)) {
                    cachedList.add(new SalesforceObjectTypeLayout(type, rawData));
                  }
                }
              }
              if (cachedList.size() > 0) {

                // Inserts or updates data in memory cache.
                if (objectTypeLayoutCacheMap != null) {
                  if (objectTypeLayoutCacheMap.get(cacheKey) != null) {
                    objectTypeLayoutCacheMap.remove(cacheKey);
                  }
                  objectTypeLayoutCacheMap.put(cacheKey, cachedList);
                }
                return cachedList;
              }
            }
          }
        }
      }
    } catch (JSONException e) {
      Log.e(TAG, "JSONException occurred while attempting to read cached data", e);
    } catch (SmartStoreException e) {
      Log.e(TAG, "SmartStoreException occurred while attempting to read cached data", e);
    }
    return null;
  }