// Path: core/src/main/java/io/keen/client/java/KeenConstants.java
// public final class KeenConstants {
//     private KeenConstants() {}
// 
//     static final String SERVER_ADDRESS = "https://api.keen.io";
//     static final String API_VERSION = "3.0";
// 
//     // Keen API constants
// 
//     static final int MAX_EVENT_DEPTH = 1000;
//     static final int DEFAULT_MAX_ATTEMPTS = 3;
//     static final String NAME_PARAM = "name";
//     static final String SUCCESS_PARAM = "success";
//     static final String ERROR_PARAM = "error";
//     static final String DESCRIPTION_PARAM = "description";
//     static final String INVALID_COLLECTION_NAME_ERROR = "InvalidCollectionNameError";
//     static final String INVALID_PROPERTY_NAME_ERROR = "InvalidPropertyNameError";
//     static final String INVALID_PROPERTY_VALUE_ERROR = "InvalidPropertyValueError";
// 
//     // Exported constants
// 
//     public static final String KEEN_FAKE_JSON_ROOT = "io.keen.client.java.__fake_root";
// }
// 
// Path: core/src/main/java/io/keen/client/java/KeenJsonHandler.java
// public interface KeenJsonHandler {
// 
//     /**
//      * Reads JSON-formatted data from the provided {@link java.io.Reader} and constructs a
//      * {@link java.util.Map} representing the object described. The keys of the map should
//      * correspond to the names of the top-level members, and the values may primitives (Strings,
//      * Integers, Booleans, etc.), Maps, or Iterables.
//      *
//      * @param reader The {@link java.io.Reader} from which to read the JSON data.
//      * @return The object which was read, held in a {@code Map<String, Object>}.
//      * @throws IOException If there is an error reading from the input.
//      */
//     Map<String, Object> readJson(Reader reader) throws IOException;
// 
//     /**
//      * Writes the given object (in the form of a {@code Map<String, Object>} to the specified
//      * {@link java.io.Writer}.
//      *
//      * @param writer The {@link java.io.Writer} to which the JSON data should be written.
//      * @param value  The object to write.
//      * @throws IOException If there is an error writing to the output.
//      */
//     void writeJson(Writer writer, Map<String, ?> value) throws IOException;
// 
// }


import android.os.Build;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import java.io.IOException;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import io.keen.client.java.KeenConstants;
import io.keen.client.java.KeenJsonHandler;
package io.keen.client.android;





/**
 * Implementation of the {@link io.keen.client.java.KeenJsonHandler} interface using the built-in
 * Android JSON library ({@link org.json.JSONObject}).
 *
 * @author Kevin Litwack (kevin@kevinlitwack.com), masojus
 * @since 2.0.0
 */
public class AndroidJsonHandler implements KeenJsonHandler {

    ///// KeenJsonHandler METHODS /////

    /**
     * {@inheritDoc}
     */
    @Override
    public Map<String, Object> readJson(Reader reader) throws IOException {
        if (reader == null) {
            throw new IllegalArgumentException("Reader must not be null");
        }

        String json = readerToString(reader);
        try {
            Object jsonObjOrArray = getJsonObjectManager().newTokener(json).nextValue();

            // Issue #99 : Take a look at better dealing with root Map<> vs root List<> in the
            // response.

            Object rootNode = JsonHelper.fromJson(jsonObjOrArray);
            Map<String, Object> rootMap = null;

            if (null == rootNode) {
                throw new IllegalArgumentException("Empty reader or ill-formatted JSON " +
                                                   "encountered.");
            } else if (rootNode instanceof Map) {
                rootMap = (Map)rootNode;
            } else if (rootNode instanceof List) {
                rootMap = new LinkedHashMap<String, Object>();
// Path: core/src/main/java/io/keen/client/java/KeenConstants.java
// public final class KeenConstants {
//     private KeenConstants() {}
// 
//     static final String SERVER_ADDRESS = "https://api.keen.io";
//     static final String API_VERSION = "3.0";
// 
//     // Keen API constants
// 
//     static final int MAX_EVENT_DEPTH = 1000;
//     static final int DEFAULT_MAX_ATTEMPTS = 3;
//     static final String NAME_PARAM = "name";
//     static final String SUCCESS_PARAM = "success";
//     static final String ERROR_PARAM = "error";
//     static final String DESCRIPTION_PARAM = "description";
//     static final String INVALID_COLLECTION_NAME_ERROR = "InvalidCollectionNameError";
//     static final String INVALID_PROPERTY_NAME_ERROR = "InvalidPropertyNameError";
//     static final String INVALID_PROPERTY_VALUE_ERROR = "InvalidPropertyValueError";
// 
//     // Exported constants
// 
//     public static final String KEEN_FAKE_JSON_ROOT = "io.keen.client.java.__fake_root";
// }
// 
// Path: core/src/main/java/io/keen/client/java/KeenJsonHandler.java
// public interface KeenJsonHandler {
// 
//     /**
//      * Reads JSON-formatted data from the provided {@link java.io.Reader} and constructs a
//      * {@link java.util.Map} representing the object described. The keys of the map should
//      * correspond to the names of the top-level members, and the values may primitives (Strings,
//      * Integers, Booleans, etc.), Maps, or Iterables.
//      *
//      * @param reader The {@link java.io.Reader} from which to read the JSON data.
//      * @return The object which was read, held in a {@code Map<String, Object>}.
//      * @throws IOException If there is an error reading from the input.
//      */
//     Map<String, Object> readJson(Reader reader) throws IOException;
// 
//     /**
//      * Writes the given object (in the form of a {@code Map<String, Object>} to the specified
//      * {@link java.io.Writer}.
//      *
//      * @param writer The {@link java.io.Writer} to which the JSON data should be written.
//      * @param value  The object to write.
//      * @throws IOException If there is an error writing to the output.
//      */
//     void writeJson(Writer writer, Map<String, ?> value) throws IOException;
// 
// }


// Path: android/src/main/java/io/keen/client/android/AndroidJsonHandler.java
import android.os.Build;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import java.io.IOException;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import io.keen.client.java.KeenConstants;
import io.keen.client.java.KeenJsonHandler;

package io.keen.client.android;





/**
 * Implementation of the {@link io.keen.client.java.KeenJsonHandler} interface using the built-in
 * Android JSON library ({@link org.json.JSONObject}).
 *
 * @author Kevin Litwack (kevin@kevinlitwack.com), masojus
 * @since 2.0.0
 */
public class AndroidJsonHandler implements KeenJsonHandler {

    ///// KeenJsonHandler METHODS /////

    /**
     * {@inheritDoc}
     */
    @Override
    public Map<String, Object> readJson(Reader reader) throws IOException {
        if (reader == null) {
            throw new IllegalArgumentException("Reader must not be null");
        }

        String json = readerToString(reader);
        try {
            Object jsonObjOrArray = getJsonObjectManager().newTokener(json).nextValue();

            // Issue #99 : Take a look at better dealing with root Map<> vs root List<> in the
            // response.

            Object rootNode = JsonHelper.fromJson(jsonObjOrArray);
            Map<String, Object> rootMap = null;

            if (null == rootNode) {
                throw new IllegalArgumentException("Empty reader or ill-formatted JSON " +
                                                   "encountered.");
            } else if (rootNode instanceof Map) {
                rootMap = (Map)rootNode;
            } else if (rootNode instanceof List) {
                rootMap = new LinkedHashMap<String, Object>();
                rootMap.put(KeenConstants.KEEN_FAKE_JSON_ROOT, rootNode);