/*
* Copyright (C) 2011-2012 Marc Boulanger
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sabdroidex.utils.json.impl;
import android.os.Debug;
import android.util.Log;
import com.sabdroidex.data.UnknowMappingElement;
import com.sabdroidex.utils.json.JSONSetter;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
public class SimpleJSONMarshaller {
private Class<?> clazz;
private static final String TAG = SimpleJSONMarshaller.class.getCanonicalName();
public SimpleJSONMarshaller(Class<?> clazz) {
this.clazz = clazz;
}
/**
* This method creates an instance of the object targeted by the Class passed to the constructor,
* it then tries to call all the methods with the JSONSetter annotation with the corresponding field.
*
* @param jsonObject
* This is the JSON object that will be used to fill-up an
* instance of the Class that is targeted.
* @return
* @throws ClassNotFoundException
* @throws InstantiationException
* @throws IllegalAccessException
*/
@SuppressWarnings("unchecked")
public Object unMarshal(final JSONObject jsonObject) throws InstantiationException, IllegalAccessException {
Object result = clazz.newInstance();
try {
Method[] methods = clazz.getMethods();
for (int i = 0; i < methods.length; i++) {
JSONSetter setter = methods[i].getAnnotation(JSONSetter.class);
/**
* Checking if the method has the JSONSetter annotation This
* only allows one parameter per setter method
*/
if (setter != null) {
/**
* Checking the JSONType defined in the annotation
*/
if (setter.type() == JSONType.SIMPLE) {
/**
* Used for simple object (String, Integer, etc ...)
*/
try {
Object parameter = jsonObject.get(setter.name());
methods[i].invoke(result, parameter);
}
catch (JSONException exception) {
if (Debug.isDebuggerConnected()) {
Log.d(TAG, methods[i].getName() + " " + exception.getLocalizedMessage());
}
}
catch (IllegalArgumentException exception) {
/**
* This happens if the object returned by the getter
* is a null, it would be defined as a JSONObject
* and thus cause an IllegalArgumentException when
* calling the targeted method.
*
* This is yet another problem in an Android API
* which is bypassed by creating an empty object of
* the awaited type.
*/
if (Debug.isDebuggerConnected()) {
Log.w(TAG, methods[i].getName() + " " + exception.getLocalizedMessage());
}
try {
Constructor<?>[] constructors = methods[i].getParameterTypes()[0].getConstructors();
Object parameter = null;
/**
* Checking all the constructor of the parameter
* object.
*/
for (Constructor<?> constructor : constructors) {
Class<?>[] params = constructor.getParameterTypes();
if (params.length == 0) {
/**
* If the empty constructor is found we
* use it.
*/
parameter = constructor.newInstance();
break;
}
else if (params[0].isPrimitive()) {
/**
* If a constructor using a primitive is
* found we use it, this happens for
* classes that extend Number.
*/
parameter = constructor.newInstance(0);
}
}
methods[i].invoke(result, parameter);
}
catch (Exception e) {
if (Debug.isDebuggerConnected()) {
Log.d(TAG, methods[i].getName() + " " + e.getLocalizedMessage());
}
}
}
}
else if (setter.type() == JSONType.JSON_OBJECT) {
/**
* Used for object that are instantiated from JSON
* (Show, Movie, etc ...)
*/
try {
SimpleJSONMarshaller simpleJSONMarshaller = new SimpleJSONMarshaller(methods[i].getParameterTypes()[0]);
JSONObject jsonElement = jsonObject.getJSONObject(setter.name());
Object parameter = simpleJSONMarshaller.unMarshal(jsonElement);
methods[i].invoke(result, parameter);
}
catch (JSONException exception) {
if (Debug.isDebuggerConnected()) {
Log.d(TAG, methods[i].getName() + " " + exception.getLocalizedMessage());
}
try {
Constructor<?>[] constructors = methods[i].getParameterTypes()[0].getConstructors();
Object parameter = null;
/**
* Checking all the constructor of the parameter
* object.
*/
for (Constructor<?> constructor : constructors) {
Class<?>[] params = constructor.getParameterTypes();
if (params.length == 0) {
/**
* If the empty constructor is found we
* use it.
*/
parameter = constructor.newInstance();
break;
}
else if (params[0].isPrimitive()) {
/**
* If a constructor using a primitive is
* found we use it, this happens for
* classes that extend Number.
*/
parameter = constructor.newInstance(0);
}
}
methods[i].invoke(result, parameter);
}
catch (Exception e) {
if (Debug.isDebuggerConnected()) {
Log.d(TAG, methods[i].getName() + " " + e.getLocalizedMessage());
}
}
}
}
else if (setter.type() == JSONType.LIST) {
/**
* Used for object that represent a Collection (List,
* ArrayList, Vector, etc ...)
*/
try {
JSONArray jsonArray = jsonObject.getJSONArray(setter.name());
Collection<Object> collection = null;
if (methods[i].getParameterTypes()[0] == List.class) {
collection = (Collection<Object>) setter.listClazz().newInstance();
}
else {
collection = (Collection<Object>) methods[i].getParameterTypes()[0].newInstance();
}
for (int j = 0; j < jsonArray.length(); j++) {
Object element = jsonArray.get(j);
if (setter.objectClazz() != Void.class) {
SimpleJSONMarshaller simpleJSONMarshaller = new SimpleJSONMarshaller(setter.objectClazz());
element = simpleJSONMarshaller.unMarshal((JSONObject) element);
}
collection.add(element);
}
methods[i].invoke(result, collection);
}
catch (JSONException exception) {
if (Debug.isDebuggerConnected()) {
Log.d(TAG, methods[i].getName() + " " + exception.getLocalizedMessage());
}
}
}
else if (setter.type() == JSONType.UNKNOWN_KEY_ELEMENTS) {
/**
* Used for object that represent a Collection (List,
* ArrayList, Vector, etc ...) and that do not have a
* key that is predefined.
*/
try {
Collection<Object> collection = null;
if (methods[i].getParameterTypes()[0] == List.class) {
collection = (Collection<Object>) setter.listClazz().newInstance();
}
else {
collection = (Collection<Object>) methods[i].getParameterTypes()[0].newInstance();
}
Iterator<?> iterator = jsonObject.keys();
while (iterator.hasNext()) {
String key = (String) iterator.next();
Object element = jsonObject.get(key);
if (setter.objectClazz() != Void.class) {
SimpleJSONMarshaller simpleJSONMarshaller = new SimpleJSONMarshaller(setter.objectClazz());
element = simpleJSONMarshaller.unMarshal((JSONObject) element);
if (element instanceof UnknowMappingElement) {
((UnknowMappingElement) element).setId(key);
}
}
collection.add(element);
}
methods[i].invoke(result, collection);
}
catch (JSONException exception) {
if (Debug.isDebuggerConnected()) {
Log.d(TAG, methods[i].getName() + " " + exception.getLocalizedMessage());
}
}
}
}
}
}
catch (Exception e) {
if (Debug.isDebuggerConnected()) {
Log.d(TAG, e.getLocalizedMessage());
}
}
return result;
}
}