/*
* Copyright (C) 2014 SCVNGR, Inc. d/b/a LevelUp
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.scvngr.levelup.core.model.util;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.scvngr.levelup.core.model.MonetaryValue;
import com.scvngr.levelup.core.util.IsoDateUtils;
import net.jcip.annotations.ThreadSafe;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.ParseException;
import java.util.Date;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;
/**
* Helper class to work with {@link JSONObject} and avoid its bugs.
* <http://code.google.com/p/android/issues/detail?id=13830>.
*/
@ThreadSafe
public final class JsonUtils {
/**
* Helper method that doesn't return some fake version of null if the string is null, it returns
* null.
*
* @param json the {@link JSONObject}.
* @param key the key in {@code json}.
* @return {@link String} mapping for {@code key} in {@code json} or {@code null} if no mapping
* exists.
*/
@Nullable
public static String optString(@NonNull final JSONObject json, @NonNull final String key) {
String value = null;
if (!json.isNull(key)) {
value = json.optString(key, null);
}
return value;
}
/**
* Loads a {@link Date}.
*
* @param json the object to read from.
* @param key the key in {@code json}.
* @return A parsed {@link Date} or {@code null} if either the key is not present or is set to
* {@code null}.
* @throws JSONException if the date could not be parsed.
*/
@Nullable
/* package */static Date optDate(@NonNull final JSONObject json, @NonNull final String key)
throws JSONException {
Date value = null;
if (!json.isNull(key)) {
try {
value = IsoDateUtils.parseIsoDatetime(json.getString(key), TimeZone.getDefault());
} catch (final ParseException e) {
final JSONException jsonException = new JSONException("could not parse datetime");
jsonException.initCause(e);
throw jsonException;
}
}
return value;
}
/**
* Loads a {@link Set} of {@link Integer} from a {@link JSONArray} of integers.
*
* @param json the object to read from.
* @param key the key in {@code json}.
* @return a set of integers loaded from the array of integers at the given {@code key} or
* {@code null} if either the key is not present or is set to {@code null}.
* @throws JSONException if the array contents aren't integers.
*/
@Nullable
public static Set<Integer> optIntegerSet(@NonNull final JSONObject json,
@NonNull final String key) throws JSONException {
Set<Integer> result = null;
if (!json.isNull(key)) {
final JSONArray array = json.optJSONArray(key);
if (null != array) {
final int count = array.length();
result = new HashSet<Integer>(count);
for (int i = 0; i < count; i++) {
result.add(array.getInt(i));
}
}
}
return result;
}
/**
* Loads a {@link Set} of {@link String}s from a {@link JSONArray} of strings.
*
* @param json the object to read from.
* @param key the key in {@code json}.
* @return a set of strings loaded from the array of strings at the given {@code key} or
* {@code null} if either the key is not present or is set to {@code null}.
* @throws JSONException if the array contents aren't strings.
*/
@Nullable
public static Set<String>
optStringSet(@NonNull final JSONObject json, @NonNull final String key)
throws JSONException {
Set<String> result = null;
if (!json.isNull(key)) {
final JSONArray array = json.optJSONArray(key);
if (null != array) {
final int count = array.length();
result = new HashSet<String>(count);
for (int i = 0; i < count; i++) {
result.add(array.getString(i));
}
}
}
return result;
}
/**
* Gets an optional {@link MonetaryValue} object from the JSON at the key passed.
*
* @param json the {@link JSONObject} to get the {@link MonetaryValue} from.
* @param key the key in the {@link JSONObject} to get the {@link MonetaryValue} from.
* @return a {@link MonetaryValue} from the parsed long or null.
* @throws JSONException if the parsing fails.
*/
@Nullable
public static MonetaryValue optMonetaryValue(@NonNull final JSONObject json,
@NonNull final String key) throws JSONException {
final MonetaryValue value;
final Long amount = json.optLong(key, Long.MIN_VALUE);
if (Long.MIN_VALUE != amount) {
value = new MonetaryValue(amount);
} else {
value = null;
}
return value;
}
/**
* Gets a required {@link MonetaryValue} object from the JSON at the key passed or throws a
* {@link JSONException} if it's missing.
*
* @param json the {@link JSONObject} to get the {@link MonetaryValue} from.
* @param key the key in the {@link JSONObject} to get the {@link MonetaryValue} from.
* @return a {@link MonetaryValue} from the parsed long.
* @throws JSONException if the parsing fails.
*/
@NonNull
public static MonetaryValue getMonetaryValue(@NonNull final JSONObject json,
@NonNull final String key) throws JSONException {
final MonetaryValue value = optMonetaryValue(json, key);
if (null == value) {
throw new JSONException(String.format(Locale.US, "key %s is required.", key));
}
return value;
}
/**
* Private constructor prevents instantiation.
*
* @throws UnsupportedOperationException because this class cannot be instantiated.
*/
private JsonUtils() {
throw new UnsupportedOperationException("This class is non-instantiable");
}
}