jeudi 11 juin 2015

Java et la réflexion [4/4]

Maintenant que vous maîtriser les annotations et la réflexion en java, voici le code source complet du convertissant Java -> Mongo/Mongo -> Java :
package mongodb.tools;


import mongodb.tools.annotation.Id;
import mongodb.tools.annotation.Ignore;
import mongodb.tools.annotation.Version;
import mongodb.tools.exception.VersionMismatchException;
import org.bson.Document;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.beans.Introspector;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.*;

/**
 * Created by emeric_martineau on 11/03/2015.
 */
public class MongoDbMapping {
    /**
     * Field name of version.
     */
    public static final String VERSION_FIELD = "<version>";

    /**
     * Map to map primitive class.
     */
    private static final Map<Class<?>, Class<?>> primitiveMap = new HashMap<Class<?>, Class<?>>();

    static {
        primitiveMap.put(Boolean.TYPE, Boolean.class);
        primitiveMap.put(Byte.TYPE, Byte.class);
        primitiveMap.put(Character.TYPE, Character.class);
        primitiveMap.put(Short.TYPE, Short.class);
        primitiveMap.put(Integer.TYPE, Integer.class);
        primitiveMap.put(Long.TYPE, Long.class);
        primitiveMap.put(Double.TYPE, Double.class);
        primitiveMap.put(Float.TYPE, Float.class);
        primitiveMap.put(Void.TYPE, Void.TYPE);
    }

    /**
     * Convert object to MondogDB object. Support direct object (int, String...) and Iterable. Not support array, Map...
     *
     * @see mongodb.tools.annotation.Ignore
     * @see mongodb.tools.annotation.Id
     *
     * @param obj object to convert
     *
     * @return mongodb object
     *
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public static Document toMongo(final Object obj)
            throws InvocationTargetException, IllegalAccessException {
        Document result;

        if (obj == null) {
            result = null;
        } else {
            result = new Document();

            Class tClass = obj.getClass();
            Version versionAnnotation = (Version) tClass.getAnnotation(Version.class);

            if (versionAnnotation != null) {
                result.append(VERSION_FIELD, versionAnnotation.version());
            }

            Method[] methods = tClass.getMethods();

            String methodName;

            // Fielname
            String fieldName;
            // Data of field
            Object data;
            // Use if data is iterable
            List temporaryList;
            // Current object of list
            Object objList;

            for (int i = 0; i < methods.length; i++) {
                methodName = methods[i].getName();

                if (methodName.startsWith("get") && !"getClass".equals(methodName)
                        && !methods[i].isAnnotationPresent(Ignore.class)
                        && !methods[i].isAnnotationPresent(Id.class)) {
                    // Found getter.
                    fieldName = Introspector.decapitalize(methods[i].getName().substring(3));

                    if (methods[i].getParameterCount() == 0) {
                        data = methods[i].invoke(obj);

                        if (data instanceof Iterator) {
                            Iterator it = ((List) data).iterator();
                            temporaryList = new LinkedList();

                            while (it.hasNext()) {
                                objList = it.next();

                                temporaryList.add(toMongo(objList));
                            }
                        }

                        result.append(fieldName, data);
                    }
                }
            }
        }

        return result;
    }

    /**
     * Convert MongoDb object to POJO
     *
     * @param mongoObj mongodb object
     * @param clazz class to instanciate
     *
     * @return object
     *
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws VersionMismatchException if object is strict and version not match
     */
    public static Object fromMongoDb(final Document mongoObj, final Class clazz)
            throws IllegalAccessException, InstantiationException, VersionMismatchException, InvocationTargetException, ClassNotFoundException {
        Object result;

        if (mongoObj == null) {
            result = null;
        } else {
            result = clazz.newInstance();
            Class tClass = result.getClass();
            Method[] methods = tClass.getMethods();

            /* Class annotation */
            Version versionAnnotation = (Version) tClass.getAnnotation(Version.class);

            String property;
            Object value;

            /* Setter method */
            Method setter;
            /* Parameter of setter */
            Class<?> parameter;
            Type[] type;

            for (Map.Entry<String, Object> setKey : mongoObj.entrySet()) {
                property = setKey.getKey();
                value = setKey.getValue();

                if (VERSION_FIELD.equals(property)) {
                    // Check version
                    if (versionAnnotation != null && versionAnnotation.strict()) {
                        int version = Integer.valueOf((String) value);

                        if (version != versionAnnotation.version()) {
                            throw new VersionMismatchException(String.format("Version %d found, version %d expected.",
                                    version, versionAnnotation.version()));
                        }
                    }
                } else {
                    setter = findSetter(methods, property);

                    if (setter != null) {
                        parameter = setter.getParameterTypes()[0];
                        type = setter.getGenericParameterTypes();

                        if (parameter.isPrimitive()) {
                            invokePrimitive(result, setter, parameter, value);
                        } else if (parameter.isEnum()) {
                            invokeEnum(result, setter, parameter, value);
                        } else if (parameter.isArray()) {
                            // TODO
                        } else if (parameter.getName().equals("java.lang.String")) {
                            setter.invoke(result, value);
                        } else if (parameter == List.class) {
                               invokeList(result, setter, type, value);
                        }

                        System.out.println(parameter);
                        // si c'est un talbeau
                        // si c'est une map
                    }
                }
            }
        }

        return result;
    }

    /**
     * Find method setter with only one parameter.
     *
     * @param methods methods list
     * @param fieldName fieldName
     *
     * @return method
     */
    private static Method findSetter(final Method[] methods, final String fieldName) {
        String methodName = "set".concat(Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1));
        System.out.println(fieldName);
        // Search method
        Method m = null;

        for (Method currentMethod : methods) {
            if (methodName.equals(currentMethod.getName())) {
                // Now check only one parameter
                if (currentMethod.getParameterCount() == 1) {
                    m = currentMethod;
                    break;
                }
            }
        }

        return m;
    }

    /**
     * Invoke primitive setter.
     *
     * @param obj object to call setter
     * @param m method to call
     * @param parameter parametter of setter
     * @param value value to setup
     *
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    private static void invokePrimitive(Object obj, Method m, Class<?> parameter, Object value)
            throws InvocationTargetException, IllegalAccessException {
        if (parameter == Boolean.TYPE) {
            m.invoke(obj, Boolean.valueOf((String) value));
        } else if (parameter == Byte.TYPE) {
            m.invoke(obj, Byte.valueOf((String) value));
        } else if (parameter == Character.TYPE) {
            m.invoke(obj, Character.valueOf((char) value));
        } else if (parameter == Short.TYPE) {
            m.invoke(obj, Short.valueOf((String) value));
        } else if (parameter == Integer.TYPE) {
            m.invoke(obj, Integer.valueOf((String) value));
        } else if (parameter == Long.TYPE) {
            m.invoke(obj, Long.valueOf((String) value));
        } else if (parameter == Double.TYPE) {
            m.invoke(obj, Double.valueOf((String) value));
        } else if (parameter == Float.TYPE) {
            m.invoke(obj, Float.valueOf((String) value));
        }
    }

    /**
     * Invoke enum setter.
     *
     * @param obj object to call setter
     * @param m method to call
     * @param parameter parametter of setter
     * @param value value to setup
     *
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    private static void invokeEnum(Object obj, Method m, Class<?> parameter, Object value)
            throws InvocationTargetException, IllegalAccessException {
        Enum enumValue = Enum.valueOf((Class<Enum>) parameter, (String) value);

        m.invoke(obj, enumValue);
    }

    /**
     * Invoke collection setter.
     *
     * @param obj object to call setter
     * @param m method to call
     * @param parameter parametter of setter
     * @param value value to setup
     *
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private static void invokeList(Object obj, Method m, Type[] parameter, Object value)
            throws InvocationTargetException, IllegalAccessException, InstantiationException,
            VersionMismatchException, ClassNotFoundException {
        List srcList = (List) value;
        List destList = null;

        Class classOfList = getGenericClass(parameter);

        if (value != null) {
            destList = new ArrayList(srcList.size());

            for (Object item : srcList) {
                if (item == null) {
                    destList.add(null);
                }else if (item instanceof Document) {
                    fromMongoDb((Document) item, classOfList);
                } else {
                    destList.add(item);
                }
            }
        }

        m.invoke(obj, destList);
    }

    /**
     * Found type of generic
     *
     * @param parameter parameter of method
     *
     * @return class to be instanciate
     *
     * @throws ClassNotFoundException
     */
    private static Class getGenericClass(Type[] parameter) throws ClassNotFoundException {
        Type type = ((ParameterizedTypeImpl) parameter[0]).getActualTypeArguments()[0];

        return Class.forName(type.getTypeName());
    }
}

Aucun commentaire:

Enregistrer un commentaire