samedi 20 mai 2017

Créer un router avec la librairie shelf

Avec Dart et la librairie shelf il est très simple de créer un serveur HTTP pour servir des pages statiques ou créer une API REST.

Mais il n'est rien prévu d'origine pour avoir un router qui permet de faire les deux en même temps, voir même plusieurs API REST.

J'ai bien proposé de l'intégrer directement, mais cela n'a pas été accepté pour des raisons que je comprend bien. Je vais donc partager ici ma solution.

Créer un fichier nommé shelf_router.dart et copier/coller :
import 'package:shelf/shelf.dart';
import 'dart:core';

/// Creates a Shelf [Handler] that allow to register some Shelf [Handler] like
/// [createStaticHandler] or [createRpcHandler] on same listener.
/// Just give map [routing] to link [key] that represent path to [value] that
/// contain handler.
/// The path [key] is only static path.
Handler createSubHandler(Map routing) {
  return (Request request) {
    // Selected path
    String selectedPath = '';

    routing.forEach((key, value) {
      String currentPath = '/' + request.url.path;

      if (currentPath.startsWith(key)
        && currentPath.length > selectedPath.length) {
        selectedPath = key;
      }
    });

    Handler h = routing[selectedPath];

    if (h == null) {
      return new Response.notFound('Not Found');
    } else {
      Uri requestUri = request.requestedUri;

      Uri newRequestedUri = new Uri(
        scheme: requestUri.scheme,
        userInfo: requestUri.userInfo,
        // Getter return empty string if host is null
        host: requestUri.host.length == 0 ? null : requestUri.host,
        port: requestUri.port,
        path: requestUri.path.substring(selectedPath.length - 1),
        query: requestUri.query,
        queryParameters: requestUri.queryParameters,
        fragment: requestUri.fragment);

      Uri url = request.url;

      Uri newUrl = new Uri(
        scheme: url.scheme,
        userInfo: url.userInfo,
        // Getter return empty string if host is null
        host: url.host.length == 0 ? null : url.host,
        port: url.port,
        path: url.path.substring(selectedPath.length - 1),
        query: url.query,
        queryParameters: url.queryParameters,
        fragment: url.fragment);

      Request newRequest = new Request(request.method, newRequestedUri,
        protocolVersion: request.protocolVersion,
        headers: request.headers,
        handlerPath: request.handlerPath,
        url: newUrl,
        body: request.readAsString().asStream(),
        encoding: request.encoding,
        context: request.context,
        onHijack: request.hijack);

      return h(newRequest);
    }
  };
}

Ensuite pour l'utiliser, rien de plus simple :
// Create a Shelf handler for your RPC API.
var handlerApi = shelf_rpc.createRpcHandler(apiServer);
// Create a Shelf handler for static file
var handlerStatic = shelf_static.createStaticHandler('example/files', defaultDocument: 'index.html');

var handler = shelf_router.createSubHandler({
  '/': handlerStatic,
  '/api/': handlerApi,
  '/api/toto/': otherHandler
});

Il vous suffit de créer une map qui associe le path et le handler.
Le système vérifie le path le plus long pour que comme dans l'exemple, il n'y ait pas collision même si le début du path est identique.

A noter que le router ne supporte pas les variables dans les path. C'est logique car ici, ça n'aurait pas de sens.

jeudi 16 février 2017

Créer des afficheurs ou des filtres dans les tempaltes d'Aurelia JS

Aurelia est un framework web comme l'est Angular JS.
Il est un peu moins connu, mais il est pourtant très agréable à utiliser.

Venant du monde d'Angular 1, j'ai été quelque peu perturbé par la gestion des filtres sur les données (comme les tableaux), à tort, car le fonctionnement est logique et unifié, comme chaque partie du framework Aurelia.

Aurelia utilise le même principe qu'Angular, dans le HTML il faut utiliser le pipe "|".
Là où Aurelia se distingue c'est par sa logique. Que ce soit un filtre ou un convertisseur, c'est la même chose.

En effet, convertir l'affichage d'un nombre (par exemple une monnaie) s'écrit simplement ${maVar | convertisseur}.

Créer un afficheur de prix en euro


Pour faire un afficheur en euro, rien de plus simple, créez une classe quelque part dans vos sources :
/**
 * Converti le prix en centime, en euro.
 */
export class PriceValueConverter {
  toView (price) {
    return (price / 100) + ' €'
  }
}

Dans votre template incluez le convertisseur et utilisez le :
<template>
  <require from="../../../converters/price/price"></require>
  ...
  <span>${o.prix | price}</span>
  ...
</template>

Vous pouvez aussi inclure votre convertisseur de façon globale :
export function configure (aurelia) {
  aurelia.use
    .basicConfiguration()
    .developmentLogging()
    .globalResources(
      './price-value-converter'
    )

  aurelia.start().then(() => aurelia.setRoot())
}

Notez que le nom du convertisseur est conventionné, c'est en minuscule la partie avant ValueConverter.

Ce n'est pas plus compliqué que ça.

Créer un filtre de tableau 1ère solution


Créer un filtre de tableau est identique.

Créons le filtre suivant :
export class FilterValueConverter {
  toView (array) {
    return array.filter(item =>
      filterFunc('recherche', item)
    )
  }
  
  filterFunc (searchExpression, value) {
    const itemValue = value.name

    return (!searchExpression || !itemValue)
      ? false
      : itemValue.toUpperCase().indexOf(searchExpression.toUpperCase()) !== -1
  }
}

Dans le template :
<template>
  ...
  <div repeat.for="s of array | filter" />
  ...
</template>

On filtre donc un tableau qui contient des structures de données avec un champ name.
L'inconvénient c'est qu'on ne peut pas préciser le terme à rechercher via un input type text.

Créer un filtre de tableau 2ème solution


Modifions le code comme ceci :
export class FilterValueConverter {
  toView (array, searchTerm) {
    return array.filter(item =>
      filterFunc(searchTerm, item)
    )
  }
  
  filterFunc (searchExpression, value) {
    const itemValue = value.name

    return (!searchExpression || !itemValue)
      ? false
      : itemValue.toUpperCase().indexOf(searchExpression.toUpperCase()) !== -1
  }
}

Côté template on spécifie la valeur via un input text :
<template>
  ...
  <input type="text" value.bind="searchTerm" />
  ...
  <div repeat.for="s of array | filter:searchTerm" />
  ...
</template>

On passe donc le texte à rechercher via un input html.
C'est mieux mais ça impose un ValueConverter par type de recherche.

Créer un filtre de tableau 3ème solution


D'une recherche à l'autre, ce qui change finalement c'est la partie filterFunc.
Nous créons donc un FilterValueConverter générique :
export class FilterValueConverter {
  toView (array, searchTerm, filterFunc) {
    return array.filter(item =>
      searchTerm && searchTerm.length > 0
        ? filterFunc(searchTerm, item)
        : true
    )
  }
}

Côté template, on ajout la fonction a appeler :
<template>
  ...
  <input type="text" value.bind="searchTerm" />
  ...
  <div repeat.for="s of array | filter:searchTerm:myFilter" />
  ...
</template>

Dans le controlleur du template, il faut ajouter la fonction de filtrage :
export class MyController {
  ...
    myFilter (searchExpression, value) {
    const itemValue = value.name

    return (!searchExpression || !itemValue)
      ? false
      : itemValue.toUpperCase().indexOf(searchExpression.toUpperCase()) !== -1
  }
  ...
}

Et voilà, le tour est joué.

jeudi 17 mars 2016

L'annotation @PropertySource de Spring et les JAR autonomes

De plus en plus, au lieu d'utiliser un serveur JEE (JBoss, Glassfish) ou un conteneur de servlet (Tomcat), un JAR autonome (fatjar ou uberjar ou singlejar) est utilisé.
Nombreux embarque Jetty, Tomcat ou Winstone.
Toutefois, les alternatives comme Ratpack ou Vert.X (sans oublier Dropwizard ou SpringBoot) change la donne.
Et c'est dans ce context, que le piège arrive.

En effet, le(s) fichier(s) de properties sont externalisés et mit dans le classpath via l'option -cp ou -classpath.
Sauf que ça ne fonctionne pas.
Avec Spring, si vous utilisez l'annotation @PropertySource et new AnnotationConfigApplicationContext(...), le fichier n'est pas trouvé dans le classpath. Pourquoi ?

Si vous avez déjà travaillé avec serveur JEE ou Tomcat, la notion de Parent First et Parent Last vous dises quelque chose.

L'annotation @PropertySource cherche dans le bootclasspath alors que l'option -cp ou -classpath attache dans le classpath enfant.

De ce fait, il faut indiquer le fichier non dans le classpath, mais dans le boot classpath :
-Xbootclasspath/a:...../properties/

Attention, utilisez bien le paramètre /a, pour ajouter votre path au boot classpath.

samedi 13 février 2016

Event Sourcing, avantages et inconvénients

L'Event Sourcing est le fait de ne pas stocker une donnée brute, mais de stocker les évènements qui composent cette donnée.
En clair, un objet commande, avec 3 articles, ne sera pas stocké de cette façon :
commande : {
  articles : [
    {
      nom : "toto"
    },
    {
      nom : "tata"
    },
    {
      nom : "titi"
    }
  ]
}
mais :
command : [
  {
    id : 1,
    action : "add",
    articles : {
      nom : "toto"
    },
    date : 2012-04-23T18:25:43.511Z
  },
  {
    id : 1,
    action : "add",
    articles : {
      nom : "tata"
    },
    date : 2012-04-24T18:25:43.511Z
  },
  {
    id : 1,
    action : "add",
    articles : {
      nom : "titi"
    },
    date : 2012-04-24T21:25:43.511Z
  }
]

Cela signifie que pour obtenir l'objet complet, ici la commande, il fait agréger tous les évènements.

L'avantage de ce principe, c'est qu'il peut y avoir plusieurs "écrivains" en même temps.
Il est aussi possible de reconstituer l'objet dans un état antérieur ou de connaitre l'historique.

Un des inconvénients qui vient tout de suite en tête est la reconstitution d'un objet avec un grand nombre d'événements, qui peut de ce fait prendre beaucoup de temps.
Il y a aussi un autre inconvénient auquel on ne pense pas tout de suite, c'est le modèle de données.

En effet, si le modèle évolue à l'ajout, il faudra prévoir que la donnée n'est pas présente dans les anciens évènements et si une donnée est supprimée, il faudra la prendre en compte dans les anciens évènements car elle avait un sens.

N'oublions pas non plus, l'évolution d'une donnée.
Une donnée facultative qui devient obligatoire, ou un typage de donnée qui change.

mercredi 13 janvier 2016

Implémenter une popover avec Angular Material

Pour le besoin d'un projet, je souhaitais utiliser une popover.
Malheureusement, Angular Material (1.0.1) ne propose pas encore cette directive qui est assez demandée (voir https://github.com/angular/material/issues/1217).

Je vous propose donc de créer un popover simple.
Pour cela, nous regardons sur Internet si quelqu'un a déjà créé un popover en CSS.
J'ai trouvé ce code assez simple : http://codepen.io/derekpcollins/pen/JCLhG.

Voici le code CSS une fois compilé :
.popover {
    background-color: rgba(0, 0, 0, 0.85);
    border-radius: 5px;
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.4);
    color: #fff;
    visibility: hidden;
    font-size: 12px;
    font-family: 'Helvetica',sans-serif;
    padding: 7px 10px;
    position: absolute;
    z-index: 4;
}

.popover:before {
    border-top: 7px solid rgba(0, 0, 0, 0.85);
    border-right: 7px solid transparent;
    border-left: 7px solid transparent;
    bottom: -7px;
    content: '';
    display: block;
    left: 50%;
    margin-left: -7px;
    position: absolute;
}

.popover-hover {
    visibility: visible;
}

@-webkit-keyframes fade-in {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}
@-moz-keyframes fade-in {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}
@-ms-keyframes fade-in {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}
@-webkit-keyframes move-up {
    from {
        bottom: 30px;
    }
    to {
        bottom: 42px;
    }
}
@-moz-keyframes move-up {
    from {
        bottom: 30px;
    }
    to {
        bottom: 42px;
    }
}
@-ms-keyframes move-up {
    from {
        bottom: 30px;
    }
    to {
        bottom: 42px;
    }
}
@-webkit-keyframes fade-in {
    from   { opacity: 0; }
    to { opacity: 1; }
}
@-moz-keyframes fade-in {
    from   { opacity: 0; }
    to { opacity: 1; }
}
@-ms-keyframes fade-in {
    from   { opacity: 0; }
    to { opacity: 1; }
}
@-webkit-keyframes move-up {
    from   { bottom: 30px; }
    to { bottom: 42px; }
}
@-moz-keyframes move-up {
    from   { bottom: 30px; }
    to { bottom: 42px; }
}
@-ms-keyframes move-up {
    from   { bottom: 30px; }
    to { bottom: 42px; }
}

A présent, il nous faut créer la directive Angular JS.
Notre but, avoir un <md-popover>Bonjour !</md-popover> qui peut contenir du code HTML.

Pour cela, la directive se met à l'intérieur de l'élément HTML qui doit faire apparaître la popover.
La directive va devoir :
  1. Ajouter les événements souris via parent.on(...) pour faire apparaître/disparaître la popover,
  2. Avoir de faire apparaître la popover, il va falloir la positionner correctement ! C'est à dire centrer sur l'élément parent (on aurait pu centrer sur la souris),
  3. Mémoriser la popover affichée. En effet, on souhaite qu'il n'y ait qu'une popover d'affichée à la fois.

Pour ce qui est de calculer la position de la popover, on a recours au service $mdUtil.
En effet, Angular JS utilise par défaut JQLite qui ne permet pas de récupérer comme JQuery les informations dont nous avons besoin.

Pour pouvoir utiliser du code HTML, nous utilisons l'option translude.
On choisit aussi de remplacer à la compilation la balise mdPopover par le code HTML du template via l'option replace.

Le code de la directive :
/**
 * Popover button.
 *
 * Partial code from Material Design mdTooltip.
 */
hesperidesModule.directive('mdPopover', function ($mdUtil, $mdPopoverService) {
    return {
        restrict: 'E',
        scope: true,
        transclude: true,
        replace: true,
        template: '
', link: function (scope, element) { var parent = element.parent(); var popover = element; // Display popup parent.on('mouseenter', function() { var tooltipParent = angular.element(document.body); var tipRect = $mdUtil.offsetRect(popover, tooltipParent); var parentRect = $mdUtil.offsetRect(parent, tooltipParent); var newPosition = { left: parentRect.left + parentRect.width / 2 - tipRect.width / 2, top: parentRect.top - tipRect.height }; popover.css({ left: newPosition.left + 'px', top: newPosition.top + 'px' }); if ($propertyToolButtonService.currentPopup) { $propertyToolButtonService.currentPopup.removeClass('popover-hover'); } popover.addClass('popover-hover'); $propertyToolButtonService.currentPopup = popover; }); // Hide popup popover.on('mouseleave', function() { popover.removeClass('popover-hover'); $propertyToolButtonService.currentPopup = null; }); } }; });

Le code du service :
/**
 * Popover button.
 *
 * Service to remember current popover display.
 * Service is singleton.
 */
hesperidesModule.factory('$mdPopoverService', [function(){
    return { currentPopup: null };
}]);

jeudi 24 décembre 2015

JavaScript, les bonnes résolutions 2016

S'il y a bien un langage que tout le monde déteste, c'est bien JavaScript.
Pourtant, force est de constater que personne n'a réussi à le "tuer". C'est même tout le contraire qui s'est passé.
Devant l'échec de GWT ou Dart à s'imposer, on ne peut qu'admirer la progression fulgurante de NodeJS, AppJS ou Electron.

Alors pourquoi tant de monde le déteste ? D'une part, il suffit de citer la définition de Wikipédia pour comprendre, qu'on ne comprend rien :

C’est un langage orienté objet à prototype, c’est-à-dire que les bases du langage et ses principales interfaces sont fournies par des objets qui ne sont pas des instances de classes, mais qui sont chacun équipés de constructeurs permettant de créer leurs propriétés, et notamment une propriété de prototypage qui permet d’en créer des objets héritiers personnalisés. En outre, les fonctions sont des objets de première classe.


et d'autre part, il y a plein de moteur JS différents qui apporte donc une implémentation différente et des bugs différents.
JavaScript c'est quoi ?
Javascript n'est pas un langage objet ! Même si on le voit écrit partout c'est faux.
Pour rappel un langage object c'est :
  • classe,
  • polymorphisme,
  • héritage,
  • objet,
  • encapsulation,
  • messages.
La portée des variables, ce ne sera plus un mystère
Si vous définissez une variable sans mot clef var devant, votre variable sera globale (non, pas d'erreur de type variable non définie).
Si vous utilisez le mot clef var, la variable aura la porté de la fonction où elle est définie.

Exemple :
// Si les déclarations sont hors fonction, ça revient au même
variableGlobale = 1;
var variableGlobale = 2;

function toto() {
    // variableGlobale est accessible, modifiable...
    var local = variableGlobale;

    // local vaut 2
    variableGlobale = 3;

    for (var index = 0; index < 3; index++) {
        // un block : for, while... n'est pas une fonction !
        // index sera donc accessible en dehors de ce bloc.
        // Si on veut une variable locale, il faut être un EcmaScript 6 et utiliser le mot clef let !
    }
}

// variableGlobale vaut 3
// local n'existe pas.
Il faut noter qu'une variable globale est accessible avant d'être créée !
La portée des fonctions, ce ne sera plus un mystère
Pour les fonctions, il faut bien avouer que c'est un peu plus compliqué.
Si vous déclarer une fonction comme ceci : function helloWorld(), alors la fonction sera globale et accessible avant même d'être déclarée.

Par contre, si vous déclarez la fonction sans le mot clef function, la fonction ne sera accessible qu'après avoir été déclarée.
La porté des variables : le contexte de la fonction avec this
Nous l'avons vu pour les variables, une fonction limite la portée des variables.
En effet, une fonction a un contexte, le fameux mot clef this. Et oui this représente la fonction en cours !

Exemple :
function toto() {
    // this représente la fonction toto()
    var func = function() {
        // this représente la fonction func()
    };
}

Pour conserver le contexte d'une fonction, il donc stocker le contexte dans une variable :
function toto() {
    var me = this;
    // this et 'me' représente la fonction toto()
    var func = function() {
        var me = this;
        // this et 'me' représente la fonction func()
    };

    // 'me' représente toujours la fonction toto()
}
Allez encore plus loin
Inutile que je répète ce qui a déjà été très bien expliqué !
Je vous conseille de lire cette page : braincracking.org.

Pour votre culture, le premier langage par prototype : Self.

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());
    }
}