/* * Copyright (c) 2015-present, Parse, LLC. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package com.parse; import org.json.JSONArray; import org.json.JSONObject; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import bolts.Continuation; import bolts.Task; /** * The {@code ParseConfig} is a local representation of configuration data that can be set from the * Parse dashboard. */ public class ParseConfig { /* package for tests */ static final TaskQueue taskQueue = new TaskQueue(); /* package for tests */ final Map params; /* package for tests */ static ParseConfigController getConfigController() { return ParseCorePlugins.getInstance().getConfigController(); } /** * Retrieves the most recently-fetched configuration object, either from memory or * disk if necessary. * * @return The most recently-fetched {@code ParseConfig} if it exists, else an empty * {@code ParseConfig} */ public static ParseConfig getCurrentConfig() { try { return ParseTaskUtils.wait(getConfigController().getCurrentConfigController() .getCurrentConfigAsync() ); } catch (ParseException e) { // In order to have backward compatibility, we swallow the exception silently. return new ParseConfig(); } } /** * Fetches a new configuration object from the server. * * @throws ParseException * Throws an exception if the server is inaccessible. * @return The {@code ParseConfig} that was fetched. */ public static ParseConfig get() throws ParseException { return ParseTaskUtils.wait(getInBackground()); } /** * Fetches a new configuration object from the server in a background thread. This is preferable * to using {@link #get()}, unless your code is already running from a background thread. * * @param callback * callback.done(config, e) is called when the fetch completes. */ public static void getInBackground(ConfigCallback callback) { ParseTaskUtils.callbackOnMainThreadAsync(getInBackground(), callback); } /** * Fetches a new configuration object from the server in a background thread. This is preferable * to using {@link #get()}, unless your code is already running from a background thread. * * @return A Task that is resolved when the fetch completes. */ public static Task getInBackground() { return taskQueue.enqueue(new Continuation>() { @Override public Task then(Task toAwait) throws Exception { return getAsync(toAwait); } }); } private static Task getAsync(final Task toAwait) { return ParseUser.getCurrentSessionTokenAsync().onSuccessTask(new Continuation>() { @Override public Task then(Task task) throws Exception { final String sessionToken = task.getResult(); return toAwait.continueWithTask(new Continuation>() { @Override public Task then(Task task) throws Exception { return getConfigController().getAsync(sessionToken); } }); } }); } @SuppressWarnings("unchecked") /* package */ static ParseConfig decode(JSONObject json, ParseDecoder decoder) { Map decodedObject = (Map) decoder.decode(json); Map decodedParams = (Map) decodedObject.get("params"); if (decodedParams == null) { throw new RuntimeException("Object did not contain the 'params' key."); } return new ParseConfig(decodedParams); } /* package */ ParseConfig(Map params) { this.params = Collections.unmodifiableMap(params); } /* package */ ParseConfig() { params = Collections.unmodifiableMap(new HashMap()); } /* package */ Map getParams() { return Collections.unmodifiableMap(new HashMap<>(params)); } /** * Access a value. In most cases it is more convenient to use a helper function such as * {@link #getString} or {@link #getInt}. * * @param key * The key to access the value for. * @return Returns {@code null} if there is no such key. */ public Object get(String key) { return get(key, null); } /** * Access a value, returning a default value if the key doesn't exist. In most cases it is more * convenient to use a helper function such as {@link #getString} or {@link #getInt}. * * @param key * The key to access the value for. * @param defaultValue * The value to return if the key is not present in the configuration object. * @return The default value if there is no such key. */ public Object get(String key, Object defaultValue) { if (!params.containsKey(key)) { return defaultValue; } Object value = params.get(key); if (value == JSONObject.NULL) { return null; } return params.get(key); } /** * Access a {@code boolean} value. * * @param key * The key to access the value for. * @return Returns false if there is no such key or if it is not a {@code boolean}. */ public boolean getBoolean(String key) { return getBoolean(key, false); } /** * Access a {@code boolean} value, returning a default value if it doesn't exist. * * @param key * The key to access the value for. * @param defaultValue * The value to return if the key is not present or has the wrong type. * @return The default value if there is no such key or if it is not a {@code boolean}. */ public boolean getBoolean(String key, boolean defaultValue) { if (!params.containsKey(key)) { return defaultValue; } Object value = params.get(key); return (value instanceof Boolean) ? (Boolean) value : defaultValue; } /** * Access a {@link Date} value. * * @param key * The key to access the value for. * @return Returns {@code null} if there is no such key or if it is not a {@link Date}. */ public Date getDate(String key) { return getDate(key, null); } /** * Access a {@link Date} value, returning a default value if it doesn't exist. * * @param key * The key to access the value for. * @param defaultValue * The value to return if the key is not present or has the wrong type. * @return The default value if there is no such key or if it is not a {@link Date}. */ public Date getDate(String key, Date defaultValue) { if (!params.containsKey(key)) { return defaultValue; } Object value = params.get(key); if (value == null || value == JSONObject.NULL) { return null; } return (value instanceof Date) ? (Date) value : defaultValue; } /** * Access a {@code double} value. * * @param key * The key to access the value for. * @return Returns 0 if there is no such key or if it is not a number. */ public double getDouble(String key) { return getDouble(key, 0.0); } /** * Access a {@code double} value, returning a default value if it doesn't exist. * * @param key * The key to access the value for. * @param defaultValue * The value to return if the key is not present or has the wrong type. * @return The default value if there is no such key or if it is not a number. */ public double getDouble(String key, double defaultValue) { Number number = getNumber(key); return number != null ? number.doubleValue() : defaultValue; } /** * Access an {@code int} value. * * @param key * The key to access the value for. * @return Returns 0 if there is no such key or if it is not a number. */ public int getInt(String key) { return getInt(key, 0); } /** * Access an {@code int} value, returning a default value if it doesn't exist. * * @param key * The key to access the value for. * @param defaultValue * The value to return if the key is not present or has the wrong type. * @return The default value if there is no such key or if it is not a number. */ public int getInt(String key, int defaultValue) { Number number = getNumber(key); return number != null ? number.intValue() : defaultValue; } /** * Access a {@link JSONArray} value. * * @param key * The key to access the value for. * @return Returns {@code null} if there is no such key or if it is not a {@link JSONArray}. */ public JSONArray getJSONArray(String key) { return getJSONArray(key, null); } /** * Access a {@link JSONArray} value, returning a default value if it doesn't exist. * * @param key * The key to access the value for. * @param defaultValue * The value to return if the key is not present or has the wrong type. * @return The default value if there is no such key or if it is not a {@link JSONArray}. */ public JSONArray getJSONArray(String key, JSONArray defaultValue) { List list = getList(key); Object encoded = (list != null) ? PointerEncoder.get().encode(list) : null; //TODO(mengyan) There are actually two cases, getList(key) will return null // case 1: key not exist, in this situation, we should return JSONArray defaultValue // case 2: key exist but value is Json.NULL, in this situation, we should return null // The following line we only cover case 2. We can not revise it since it may break some // existing app, but we should do it someday. return (encoded == null || encoded instanceof JSONArray) ? (JSONArray) encoded : defaultValue; } /** * Access a {@link JSONObject} value. * * @param key * The key to access the value for. * @return Returns {@code null} if there is no such key or if it is not a {@link JSONObject}. */ public JSONObject getJSONObject(String key) { return getJSONObject(key, null); } /** * Access a {@link JSONObject} value, returning a default value if it doesn't exist. * * @param key * The key to access the value for. * @param defaultValue * The value to return if the key is not present or has the wrong type. * @return The default value if there is no such key or if it is not a {@link JSONObject}. */ public JSONObject getJSONObject(String key, JSONObject defaultValue) { Map map = getMap(key); Object encoded = (map != null) ? PointerEncoder.get().encode(map) : null; //TODO(mengyan) There are actually two cases, getList(key) will return null // case 1: key not exist, in this situation, we should return JSONArray defaultValue // case 2: key exist but value is Json.NULL, in this situation, we should return null // The following line we only cover case 2. We can not revise it since it may break some // existing app, but we should do it someday. return (encoded == null || encoded instanceof JSONObject) ? (JSONObject) encoded : defaultValue; } /** * Access a {@link List} value. * * @param key * The key to access the value for. * @return Returns {@code null} if there is no such key or if it cannot be converted to a * {@link List}. */ public List getList(String key) { return getList(key, null); } /** * Access a {@link List} value, returning a default value if it doesn't exist. * * @param key * The key to access the value for. * @param defaultValue * The value to return if the key is not present or has the wrong type. * @return The default value if there is no such key or if it cannot be * converted to a {@link List}. */ public List getList(String key, List defaultValue) { if (!params.containsKey(key)) { return defaultValue; } Object value = params.get(key); if (value == null || value == JSONObject.NULL) { return null; } @SuppressWarnings("unchecked") List returnValue = (value instanceof List) ? (List) value : defaultValue; return returnValue; } /** * Access a {@code long} value. * * @param key * The key to access the value for. * @return Returns 0 if there is no such key or if it is not a number. */ public long getLong(String key) { return getLong(key, 0); } /** * Access a {@code long} value, returning a default value if it doesn't exist. * * @param key * The key to access the value for. * @param defaultValue * The value to return if the key is not present or has the wrong type. * @return The default value if there is no such key or if it is not a number. */ public long getLong(String key, long defaultValue) { Number number = getNumber(key); return number != null ? number.longValue() : defaultValue; } /** * Access a {@link Map} value. * * @param key * The key to access the value for. * @return {@code null} if there is no such key or if it cannot be converted to a * {@link Map}. */ public Map getMap(String key) { return getMap(key, null); } /** * Access a {@link Map} value, returning a default value if it doesn't exist. * * @param key * The key to access the value for. * @param defaultValue * The value to return if the key is not present or has the wrong type. * @return The default value if there is no such key or if it cannot be converted * to a {@link Map}. */ public Map getMap(String key, Map defaultValue) { if (!params.containsKey(key)) { return defaultValue; } Object value = params.get(key); if (value == null || value == JSONObject.NULL) { return null; } @SuppressWarnings("unchecked") Map returnValue = (value instanceof Map) ? (Map) value : defaultValue; return returnValue; } /** * Access a numerical value. * * @param key * The key to access the value for. * @return Returns {@code null} if there is no such key or if it is not a {@link Number}. */ public Number getNumber(String key) { return getNumber(key, null); } /** * Access a numerical value, returning a default value if it doesn't exist. * * @param key * The key to access the value for. * @param defaultValue * The value to return if the key is not present or has the wrong type. * @return The default value if there is no such key or if it is not a {@link Number}. */ public Number getNumber(String key, Number defaultValue) { if (!params.containsKey(key)) { return defaultValue; } Object value = params.get(key); if (value == null || value == JSONObject.NULL) { return null; } return (value instanceof Number) ? (Number) value : defaultValue; } /** * Access a {@link ParseFile} value. This function will not perform a network request. Unless the * {@link ParseFile} has been downloaded (e.g. by calling {@link ParseFile#getData()}), * {@link ParseFile#isDataAvailable()} will return false. * * @param key * The key to access the value for. * @return {@code null} if there is no such key or if it is not a {@link ParseFile}. */ public ParseFile getParseFile(String key) { return getParseFile(key, null); } /** * Access a {@link ParseFile} value, returning a default value if it doesn't exist. This function * will not perform a network request. Unless the {@link ParseFile} has been downloaded * (e.g. by calling {@link ParseFile#getData()}), {@link ParseFile#isDataAvailable()} will return * false. * * @param key * The key to access the value for. * @param defaultValue * The value to return if the key is not present or has the wrong type. * @return The default value if there is no such key or if it is not a {@link ParseFile}. */ public ParseFile getParseFile(String key, ParseFile defaultValue) { if (!params.containsKey(key)) { return defaultValue; } Object value = params.get(key); if (value == null || value == JSONObject.NULL) { return null; } return (value instanceof ParseFile) ? (ParseFile) value : defaultValue; } /** * Access a {@link ParseGeoPoint} value. * * @param key * The key to access the value for * @return {@code null} if there is no such key or if it is not a {@link ParseGeoPoint}. */ public ParseGeoPoint getParseGeoPoint(String key) { return getParseGeoPoint(key, null); } /** * Access a {@link ParseGeoPoint} value, returning a default value if it doesn't exist. * * @param key * The key to access the value for * @param defaultValue * The value to return if the key is not present or has the wrong type. * @return The default value if there is no such key or if it is not a {@link ParseGeoPoint}. */ public ParseGeoPoint getParseGeoPoint(String key, ParseGeoPoint defaultValue) { if (!params.containsKey(key)) { return defaultValue; } Object value = params.get(key); if (value == null || value == JSONObject.NULL) { return null; } return (value instanceof ParseGeoPoint) ? (ParseGeoPoint) value : defaultValue; } /** * Access a {@link String} value. * * @param key * The key to access the value for. * @return Returns {@code null} if there is no such key or if it is not a {@link String}. */ public String getString(String key) { return getString(key, null); } /** * Access a {@link String} value, returning a default value if it doesn't exist. * * @param key * The key to access the value for. * @param defaultValue * The value to return if the key is not present or has the wrong type. * @return The default value if there is no such key or if it is not a {@link String}. */ public String getString(String key, String defaultValue) { if (!params.containsKey(key)) { return defaultValue; } Object value = params.get(key); if (value == null || value == JSONObject.NULL) { return null; } return (value instanceof String) ? (String) value : defaultValue; } @Override public String toString() { return "ParseConfig[" + params.toString() + "]"; } }