:: Enseignements :: Master :: M1 :: 2022-2023 :: Java Avancé ::
[LOGO]

Examen de Java Avancé


Le but de ce TP noté est d'implanter deux Interpolator différents. Un Interpolator est une classe qui prend en entrée un texte avec des trous (holes) et qui, étant donnés des arguments, renvoie un nouveau texte contenant le texte original dont les trous sont comblés par les arguments.

Vos sources Java produites pendant l'examen devront être placées sous le répertoire EXAM de votre compte ($HOME) (qui est vide dans l'environnement de TP noté). Sinon, elles ne seront pas récupérées.

Tout document papier est proscrit.
Vous pouvez consulter la javadoc à travers Eclipse (onglet Javadoc), en utilisant jdoc dans un terminal Sinon la doc est là: /usr/local/apps/java17/docs/api/index.html.
Les seuls documents électroniques autorisés sont les supports de cours à l'url http://igm.univ-mlv.fr/~forax/ens/java-avance/cours/pdf/.

Vous avez le droit de lire le sujet jusqu'au bout, cela vous donnera une bonne idée de là où on veut aller !

Exercice 1 - Interpolator

Il y a deux façons de spécifier un texte à trous :
  • Soit par une liste de segment, un segment pouvant être un trou (hole) ou un texte (text). Les trous sont numérotés à partir de 0, comme cela on sait que dans le trou i, il faudra insérer l'argument i.
    Par exemple, le texte "Hello @ !" (@ représente un trou) correspond à une liste à 3 segments contenant un objet Text("Hello "), un objet Hole(0) et enfin un objet Text(" !")
  • Soit par une chaîne de caractères (un template) qui utilise le caractère @ pour indiquer un trou.
    Par exemple, la chaîne de caractères "name: @, age: @" est un template avec deux trous.

Pour vous aider, voici le code du record Hole dans le fichier Hole.java qui définit un trou
package fr.uge.interpolation;

public record Hole(int argumentIndex) {
  @Override
  public String toString() {
    return "Hole(" + argumentIndex + ')';
  }
}
      
et le code du record Text dans le fichier Text.java qui définit un texte
package fr.uge.interpolation;

public record Text(String text) {
  @Override
  public String toString() {
    return "Text(" + text + ')';
  }
}
      

L'algorithme pour transformer un texte à trou (template) en liste de segments qui sont soit des trous soit des textes est le suivant:
Si le premier caractère est un '@', alors c'est trou, et on recommence, sinon c'est pas un trou et on va chercher le prochain trou, si on le trouve, alors les caractères entre sont du texte et on recommence, sinon il n'y a plus de trou et le dernier segment est un texte jusqu'à la fin de la chaîne de caractères
En pseudo code, cela donne
    index = 0;
    loop: tant que index < length(template):
      si template[index] == '@':
        index = index + 1
        print "HOLE !"
        continue loop;

      i = index + 1
      tant que i < template.length():
        si template[i] == '@'
          print "TEXT" avec les caractères de template entre index et i (non compris)
          index = i;
          continue loop;

        i = i + 1

      print "TEXT" avec caractères de template entre index et length(template) (non compris)
      index = length(template);
    
     

La classe MutableInterpolator est un Interpolator qui possède les opérations suivantes :
  • addHole() qui permet d'ajouter un trou.
  • holeCount() qui renvoie le nombre de trou.
  • addText(text) qui permet d'ajouter un texte.
  • segments() qui renvoie une liste non modifiable des segments (trous et textes) dans l'ordre d'insertion
  • addTemplate(template) qui utilise l'algorithme ci-dessus pour décomposer le template pris en argument pour ajouter les trous et textes correspondants.
  • interpolate(arguments) qui prend une liste d'arguments en paramètre (il doit y avoir autant d'arguments que de segments) et renvoie une chaîne de caractères qui est créée à partir des segments en remplaçant tous les trous par les valeurs des arguments et en concaténant les valeurs des arguments et des textes.

L'interface Interpolator possède les méthodes suivantes :
  • interpolate(arguments) qui est abstraite et fonctionne comme la méthode interpolate de la classe MutableInterpolator.
  • interpolateFields(interpolator, element, fieldAccessors) qui prend en paramètre un Tnterpolator, un élément et des accesseurs et utilise les valeurs des champs pour faire l'interpolation.
  • iteratorFromTemplate(template) renvoie un itérateur sur les segments du template au fur et à mesure des besoins.
  • lazyInterpolator(template) renvoie un Interpolator paresseux qui utilise l'itérateur renvoyé par iteratorFromTemplate pour faire l'interpolation.
  • streamFromTemplate(template) renvoie un Stream des segments du template.

Aucune des méthodes qui prend des objets en paramètre n'accepte null.

Des tests unitaires correspondant à l'implantation sont ici : InterpolatorTest.java.

  1. On cherche à créer la classe MutableInterpolator avec les méthodes addHole, holeCount, addText et segments. La méthode segment doit renvoyer une liste qui agit comme une vue, toute modification de l'Interpolator (par exemple, l'ajout d'un trou) devra être visible dans la liste même après que celle-ci ait déjà été créée.
    Implanter la classe MutableInterpolator sachant que les trous et les textes sont stockés dans une liste.
    Vérifier que les tests unitaires marqués "Q1" passent.
  2. On souhaite maintenant implanter la méthode interpolate(arguments) qui effectue l'interpolation en remplaçant les trous des segments par les arguments pris en paramètre.
    De plus, on veut pouvoir manipuler la classe MutableInterpolator par son interface Interpolator.
    Créer l'interface Interpolator avec la méthode interpolate et implanter celle-ci dans la classe MutableInterpolator.
    Vérifier que les tests unitaires marqués "Q2" passent.
    Note : Java permet de faire des switchs sur les objets, et vous ne devriez pas avoir besoin de default.
  3. On souhaite écrire dans l'interface Interpolator la méthode interpolateFields qui utilise les accesseurs d'une instance pour trouver les arguments utilisés pour l'interpolation.
    Par exemple, si on pré-suppose un record Person(String name, int age) et une instance new Person("John", 33), alors l'appel à interpolateFields avec un Interprolator, l'instance et les références sur les accesseurs Person::name et Person::age est équivalent à appeler la méthode interpolate avec les arguments "John" et 33.
    Vérifier que les tests unitaires marqués "Q3" passent.
  4. On veut maintenant ajouter la méthode addTemplate(template) à la classe MutableInterpolator qui utilise l'algorithme de décomposition du template vu plus haut (en cherchant les '@') pour ajouter les segments correspondant.
    Écrire la méthode addTemplate et vérifier que les tests unitaires marqués "Q4" passent.
  5. On souhaite écrire dans l'interface Interpolator la méthode iteratorFromTemplate(template) qui pour un template renvoie un itérateur qui permet de parcourir les trous et les textes toujours en utilisant le même algorithme. Bien sûr, l'itérateur doit être paresseux et ne pas découper la totalité du template au démarrage mais faire le travail au fur et à mesure.
    Écrire la méthode iteratorFromTemplate et vérifier que les tests unitaires marqués "Q5" passent.
  6. On souhaite écrire dans l'interface Interpolator la méthode lazyInterpolator(template) qui pour un template, renvoie un Interpolator qui va utiliser l'itérateur écrit précédemment lors de l'interpolation. Chaque appel à la méthode interpolate devra utiliser un nouvel itérateur.
    Implanter la méthode lazyInterpolator et vérifier que les tests unitaires marqués "Q6" passent.
    Note : on peut remarquer que l'interface Interpolator a une seule méthode abstraite donc que l'Interpolator paresseux peut être écrit sous forme d'une lambda.
  7. On souhaite écrire toujours dans l'interface Interpolator la méthode streamFromTemplate(template) qui renvoie un Stream des trous et des textes correspondant au template pris en paramètre en utilisant l'algorithme vu plus haut.
    Vérifier que les tests unitaires marqués "Q7" passent.
    Note : pour la taille estimée, on peut remarquer que l'on peut calculer facilement le nombre maximal de segments possibles pour un template.
  8. Modifier l'implantation de la méthode streamFromTemplate(template) pour que le Stream renvoyé soit parallélisable.
    Vérifier que les tests unitaires marqués "Q8" passent.
    Attention : les index dans les trous sont ordonnés !