Transformer Commons Collections

De EjnTricks
Révision de 16 février 2017 à 17:20 par Etienne (discussion | contributions)

(diff) ← Version précédente | Voir la version courante (diff) | Version suivante → (diff)

L'utilisation de Transformer, interface org.apache.commons.collections4.Transformer, va permettre d'effectuer des transformation d'objets. Cette interface définie la fonction transform qui retourne l'instance transformée dans une nouvelle classe ou la même.


Hand-icon.png Votre avis

Nobody voted on this yet

 You need to enable JavaScript to vote


Java format icon.png Implémentation

Dans le cadre de cette étude, une première implémentation est mise en place afin de transformer une instance de People en chaîne de caractères avec la concaténation du prénom et nom.

package fr.ejn.tutorial.apache.commons.collections4.transformer;

import fr.ejn.tutorial.datas.People;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.lang3.StringUtils;

/**
 * Sample transformer used to buld the people name.
 *
 * @author Etienne Jouvin
 *
 */
public class PeopleNameTransformer implements Transformer<People, String> {

  /** {@inheritDoc} */
  @Override
  public String transform(People input) {
    return null == input ? StringUtils.EMPTY : String.format("%s %s", input.getName(), input
        .getSurname());
  }

}

La fonction transform teste que l'argument n'est pas null, permettant de s'affranchir les NullPointerException, et construit la chaîne de caracteres avec le prénom et nom.


Update icon.png Transformation liste

Les instances de Transformer permettent de modifier les éléments. Ils sont utilisés dans les fonctions de sélection des différents utilitaires. Un exemple est implémenté dans le test unitaire fr.ejn.tutorial.apache.commons.collections4.transformer.PeopleNameTransformerTest.

package fr.ejn.tutorial.apache.commons.collections4.transformer;

import fr.ejn.tutorial.datas.Gender;
import fr.ejn.tutorial.datas.People;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.collections4.Transformer;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class PeopleNameTransformerTest {

  private Transformer<People, String> instance;

  @Before
  public void setUp() throws Exception {
    instance = new PeopleNameTransformer();
  }

  @Test
  public void testTransform() {
    People people = new People();
    people.setName("Etienne");
    people.setGender(Gender.MAN);
    people.setSurname("Jouvin");

    List<People> peoples = new ArrayList<People>(3);
    peoples.add(people);

    // Add a null to test transform on null.
    peoples.add(null);

    people = new People();
    people.setName("Mother");
    people.setGender(Gender.WOMAN);
    people.setSurname("Jouvin");
    peoples.add(people);

    Collection<String> actual = CollectionUtils.collect(peoples, instance);

    Assert.assertNotNull("Validate result is not null", actual);
    Assert.assertEquals("Validate result size", 3, actual.size());

    Assert.assertEquals("Etienne Jouvin", IterableUtils.get(actual, 0));
    Assert.assertEquals("", IterableUtils.get(actual, 1));
    Assert.assertEquals("Mother Jouvin", IterableUtils.get(actual, 2));
  }

}

Dans un premier temps, une liste est créé avec différents éléments. Puis la fonction collect de la classe CollectionUtils est exécutée sur la liste avec une instance du Transformer. Ainsi, une nouvelle collection est créée avec les chaînes de caractères. A noter qu'aucun filtre n'est appliqué sur la collection, et les nulls sont traités.

Le test unitaire valide le contenu de la nouvelle collection, avec les chaînes de caractères attendues.


File-find-icon.png Combinaison transformation / filtre

Dans les exemples précédents, seule la transformation des données est explicitée. Cependant il peut être parfois d'effectuer deux opérations en une seule étape, comme transformer les données et de ne pas collecter certains résultats de la transformation. Pour cela, il faut combiner l'utilisation d'un Predicate et une instance de Iterable qui va effectuer la transformation à la volée. Toute l'astuce se situe dans cette dernière instance qui est retournée par la fonction transformedIterable de la classe IterableUtils.

Le test unitaire testPredicateTransform, dans la classe CollectionUtilsTest, démontre cette utilisation.

  @Test
  public void testPredicateTransform() {
    People people = new People();
    people.setName("Etienne");
    people.setGender(Gender.MAN);
    people.setSurname("Jouvin");

    List<People> peoples = new ArrayList<People>(3);
    peoples.add(people);

    // Add a null to test transform on null.
    peoples.add(null);

    people = new People();
    people.setName("Mother");
    people.setGender(Gender.WOMAN);
    people.setSurname("Jouvin");
    peoples.add(people);

    // Create an iterable with the transformer instance.
    Iterable<String> peoplesNamesIterable = IterableUtils
        .transformedIterable(peoples, new PeopleNameTransformer());
    // Create a predicate to filter on Empty String.
    Predicate<String> emptyValuePredicate = PredicateUtils.equalPredicate(StringUtils.EMPTY);
    Predicate<String> notEmptyValuePredicate = PredicateUtils.notPredicate(emptyValuePredicate);

    Collection<String> peoplesNames = CollectionUtils
        .select(peoplesNamesIterable, notEmptyValuePredicate);

    Assert.assertNotNull("Validate collection is not null", peoplesNames);
    Assert.assertEquals("Validate collection size", 2, peoplesNames.size());
    Assert
        .assertEquals("Validate first name", "Etienne Jouvin", IterableUtils.get(peoplesNames, 0));
    Assert.assertEquals("Validate first name", "Mother Jouvin", IterableUtils.get(peoplesNames, 1));

    // Use the selectRejected
    peoplesNames = CollectionUtils.selectRejected(peoplesNamesIterable, emptyValuePredicate);

    Assert.assertNotNull("Validate collection is not null", peoplesNames);
    Assert.assertEquals("Validate collection size", 2, peoplesNames.size());
    Assert
        .assertEquals("Validate first name", "Etienne Jouvin", IterableUtils.get(peoplesNames, 0));
    Assert.assertEquals("Validate first name", "Mother Jouvin", IterableUtils.get(peoplesNames, 1));
  }

Dans cet exemple, le transformer PeopleNameTransformer est utilisé. Or il retourne StringUtils.EMPTY lorsque l'objet est null. C'est cette valeur qui doit être filtrée. La fonction equalPredicate de la classe PredicateUtils permet de mettre en place un Predicate qui valide l'objet si il est égal à une valeur, et donc à StringUtils.EMPTY. Or il est souhaité de les filtrer et non pas les récupérer. Le Predicate est alors combiné avec une instance retournée par la fonction notPredicate de PredicateUtils. Cette dernière retourne l'inverse du Predicate encapsulé.

Il faut ensuite parcourir la collection d'origine et transformer les données. Il serait possible d'effectuer une première passe pour la transformation, puis une seconde pour effectuer le filtre. Mais cela implique deux parcours de la liste. Or, la fonction transformedIterable de la classe IterableUtils permet de construire une instance Iterable qui retourne les valeur transformée de la collection source. En utilisant cette isntance lors de l'appel à la fonction select de CollectionUtils avec le Predicate construit, seule les transformations non nulles des instances People sont stockées dans une nouvelle collection.

Le deuxième exemple permet de s'affranchir du Predicate notPredicate, en utilisant la fonction selectRejected.