org.flycraft.vkontakteapi.model.ParseUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.flycraft.vkontakteapi.model.ParseUtils.java

Source

//
//  Copyright (c) 2014 VK.com
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy of
//  this software and associated documentation files (the "Software"), to deal in
//  the Software without restriction, including without limitation the rights to
//  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
//  the Software, and to permit persons to whom the Software is furnished to do so,
//  subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in all
//  copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
//  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
//  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
//  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
//  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

package org.flycraft.vkontakteapi.model;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.lang.reflect.*;

/**
 * Collection of helpers to parse server responses.
 */
class ParseUtils {

    private ParseUtils() {
    }

    /**
     * Parse boolean from server response.
     *
     * @param from server response like this format: {@code response: 1}
     * @throws JSONException if server response is not valid
     */
    public static boolean parseBoolean(String from) throws JSONException {
        return new JSONObject(from).optInt("response", 0) == 1;
    }

    /**
     * Parse boolean from JSONObject with given name.
     *
     * @param from server response like this format: {@code field: 1}
     * @param name name of field to read
     */
    public static boolean parseBoolean(JSONObject from, String name) {
        return from != null && from.optInt(name, 0) == 1;
    }

    /**
     * Parse int from JSONObject with given name.
     *
     * @param from server response like this format: {@code field: 34}
     * @param name name of field to read
     */
    public static int parseInt(JSONObject from, String name) {
        if (from == null)
            return 0;
        return from.optInt(name, 0);
    }

    /**
     * Parse int from server response.
     *
     * @param from server response like this format: {@code response: 34}
     * @throws JSONException if server response is not valid
     */
    public static int parseInt(String from) throws JSONException {
        if (from == null)
            return 0;
        return new JSONObject(from).optInt("response");
    }

    /**
     * Parse long from JSONObject with given name.
     *
     * @param from server response like this format: {@code field: 34}
     * @param name name of field to read
     */
    public static long parseLong(JSONObject from, String name) {
        if (from == null)
            return 0;
        return from.optLong(name, 0);
    }

    /**
     * Parse int array from JSONObject with given name.
     *
     * @param from int JSON array like this one {@code {11, 34, 42}}
     */
    public static int[] parseIntArray(JSONArray from) {
        int[] result = new int[from.length()];
        for (int i = 0; i < result.length; i++) {
            result[i] = from.optInt(i);
        }
        return result;
    }

    /**
     * Returns root JSONObject from server response
     *
     * @param source standart VK server response
     * @throws JSONException if source is not valid
     */
    public static JSONObject rootJSONObject(String source) throws JSONException {
        return new JSONObject(source).getJSONObject("response");
    }

    /**
     * Returns root JSONArray from server response
     *
     * @param source standart VK server response
     * @throws JSONException if source is not valid
     */
    public static JSONArray rootJSONArray(String source) throws JSONException {
        return new JSONObject(source).getJSONArray("response");
    }

    /**
     * Parses object with follow rules:
     * <p/>
     * 1. All fields should had a public access.
     * 2. The name of the filed should be fully equal to name of JSONObject key.
     * 3. Supports parse of all Java primitives, all {@link java.lang.String},
     * arrays of primitive types, {@link java.lang.String}s and {@link org.flycraft.vkontakteapi.model.VKApiModel}s,
     * list implementation line {@link org.flycraft.vkontakteapi.model.VKList}, {@link org.flycraft.vkontakteapi.model.VKAttachments.VKApiAttachment} or {@link org.flycraft.vkontakteapi.model.VKPhotoSizes},
     * {@link org.flycraft.vkontakteapi.model.VKApiModel}s.
     * <p/>
     * 4. Boolean fields defines by vk_int == 1 expression.
     *
     * @param object object to initialize
     * @param source data to read values
     * @param <T>    type of result
     * @return initialized according with given data object
     * @throws JSONException if source object structure is invalid
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static <T> T parseViaReflection(T object, JSONObject source) throws JSONException {
        if (source.has("response")) {
            source = source.optJSONObject("response");
        }
        if (source == null) {
            return object;
        }
        for (Field field : object.getClass().getFields()) {
            field.setAccessible(true);
            String fieldName = field.getName();
            Class<?> fieldType = field.getType();

            Object value = source.opt(fieldName);
            if (value == null) {
                continue;
            }
            try {
                if (fieldType.isPrimitive() && value instanceof Number) {
                    Number number = (Number) value;
                    if (fieldType.equals(int.class)) {
                        field.setInt(object, number.intValue());
                    } else if (fieldType.equals(long.class)) {
                        field.setLong(object, number.longValue());
                    } else if (fieldType.equals(float.class)) {
                        field.setFloat(object, number.floatValue());
                    } else if (fieldType.equals(double.class)) {
                        field.setDouble(object, number.doubleValue());
                    } else if (fieldType.equals(boolean.class)) {
                        field.setBoolean(object, number.intValue() == 1);
                    } else if (fieldType.equals(short.class)) {
                        field.setShort(object, number.shortValue());
                    } else if (fieldType.equals(byte.class)) {
                        field.setByte(object, number.byteValue());
                    }
                } else {
                    Object result = field.get(object);
                    if (value.getClass().equals(fieldType)) {
                        result = value;
                    } else if (fieldType.isArray() && value instanceof JSONArray) {
                        result = parseArrayViaReflection((JSONArray) value, fieldType);
                    } else if (VKPhotoSizes.class.isAssignableFrom(fieldType) && value instanceof JSONArray) {
                        Constructor<?> constructor = fieldType.getConstructor(JSONArray.class);
                        result = constructor.newInstance((JSONArray) value);
                    } else if (VKAttachments.class.isAssignableFrom(fieldType) && value instanceof JSONArray) {
                        Constructor<?> constructor = fieldType.getConstructor(JSONArray.class);
                        result = constructor.newInstance((JSONArray) value);
                    } else if (VKList.class.equals(fieldType)) {
                        ParameterizedType genericTypes = (ParameterizedType) field.getGenericType();
                        Class<?> genericType = (Class<?>) genericTypes.getActualTypeArguments()[0];
                        if (VKApiModel.class.isAssignableFrom(genericType)
                                && Identifiable.class.isAssignableFrom(genericType)) {
                            if (value instanceof JSONArray) {
                                result = new VKList((JSONArray) value, genericType);
                            } else if (value instanceof JSONObject) {
                                result = new VKList((JSONObject) value, genericType);
                            }
                        }
                    } else if (VKApiModel.class.isAssignableFrom(fieldType) && value instanceof JSONObject) {
                        result = ((VKApiModel) fieldType.newInstance()).parse((JSONObject) value);
                    }
                    field.set(object, result);
                }
            } catch (InstantiationException e) {
                throw new JSONException(e.getMessage());
            } catch (IllegalAccessException e) {
                throw new JSONException(e.getMessage());
            } catch (NoSuchMethodException e) {
                throw new JSONException(e.getMessage());
            } catch (InvocationTargetException e) {
                throw new JSONException(e.getMessage());
            } catch (NoSuchMethodError e) {
                //  ?:
                //   ,     getFields()   .
                //  ? ? ?,   ? ?,  Android  ?  .
                throw new JSONException(e.getMessage());
            }
        }
        return object;
    }

    /**
     * Parses array from given JSONArray.
     * Supports parsing of primitive types and {@link org.flycraft.vkontakteapi.model.VKApiModel} instances.
     *
     * @param array      JSONArray to parse
     * @param arrayClass type of array field in class.
     * @return object to set to array field in class
     * @throws JSONException if given array have incompatible type with given field.
     */
    private static Object parseArrayViaReflection(JSONArray array, Class arrayClass) throws JSONException {
        Object result = Array.newInstance(arrayClass.getComponentType(), array.length());
        Class<?> subType = arrayClass.getComponentType();
        for (int i = 0; i < array.length(); i++) {
            try {
                Object item = array.opt(i);
                if (VKApiModel.class.isAssignableFrom(subType) && item instanceof JSONObject) {
                    VKApiModel model = (VKApiModel) subType.newInstance();
                    item = model.parse((JSONObject) item);
                }
                Array.set(result, i, item);
            } catch (InstantiationException e) {
                throw new JSONException(e.getMessage());
            } catch (IllegalAccessException e) {
                throw new JSONException(e.getMessage());
            } catch (IllegalArgumentException e) {
                throw new JSONException(e.getMessage());
            }
        }
        return result;
    }
}