Travaux Pratiques n°5 :

Lectures préalables :  
Thèmes du TP :

(L'énoncé de ce T.P. est inspiré du tutorial de Sun sur les collections)

Rappels :
Vous devez fournir des copies d'écran des diagrammes de classe de BlueJ, et la documentation générée doit faire apparaître vos commentaires sur toutes les classes, les méthodes, les paramètres, et les valeurs de retour.

Attention !
Les questions signalées par *** doivent être lues pour la compréhension de la suite du sujet, mais sont optionnelles. Cela signifie qu'il n'est pas obligatoire de "rendre" ces questions sur la page web dont vous avez enregistré l'URL, mais vous devez essayer de traiter toutes ces questions pendant le TP.

Question 1. (AbstractSet)

(i) Étudiez la classe AbstractCollection :

(ii) Complétez la classe "Ensemble d'Object", nommée Ensemble ci-dessous, en utilisant le pattern Délégation.

import java.util.*;
public class Ensemble extends AbstractSet
  {
  private Vector table;
  
  public Ensemble() { table = new Vector(); }

  public int size() { /* à compléter */ }

  public Iterator iterator() { /* à compléter */ }

  public boolean add( Object o ) { /* à compléter */ }
  }
AIDE :

  • AbstractSet implémente l'interface Set.
  • Vector hérite de AbstractList qui possède une méthode iterator() ...
  • Seules 3 méthodes sont à développer ici. Les autres méthodes sont soit implantées par AbstractSet ou AbstractCollection, soit elles sont "optional", c'est-à-dire qu'il n'est pas obligatoire de les implanter malgré leur présence dans l'interface Set. Dans ce cas, elles renverront une UnsupportedOperationException.
  • Écrivez OPTIONNELLEMENT une méthode toString() pour afficher les ensembles sous la forme { a, b, c } plutôt que d'hériter simplement d'une méthode toString() dont on ne maîtrise pas le format d'affichage.

(iii) N'oubliez pas de fournir la documentation de cette classe.


Question 2. (Ensembles)

(i) Développez une application de test TestEnsemble de la classe Ensemble avec les principales opérations sur les ensembles :

Pour cela :

AIDE :

  • Recherchez dans les super-classes de Ensemble les méthodes (addAll() par exemple) permettant de réaliser "simplement" ces opérations (sans avoir à écrire d'itérations !).
  • La méthode addAll() appelle automatiquement la "meilleure" méthode add() possible : ici, celle de Ensemble.

(ii) Complétez la classe IHMEnsemble.java pour qu'elle permette de tester interactivement le fonctionnement des 4 opérations ensemblistes de la question précédente, comme ci-dessous :

Applette de Test, les éléments doivent être séparés par .,;:/ ou par des espaces.

(iii) Utilisez les possibilités de BlueJ pour générer une classe de test pour la classe Ensemble.

Les tests doivent "démontrer" que toutes les méthodes de la classe fonctionnent correctement, y compris dans des cas limites.

(iv) Fournissez la documentation regroupant toutes les classes des questions (i) (ii) et (iii).

(hors tp) Sujet de réflexion : On pourrait se poser la question suivante :


Question 3. (Listes)

(i) Compléter l'applette ci-dessous, le texte de la fenêtre est une liste constituée de mots extraits du chapitre 2 de CoreJava2 consacré aux LinkedList que vous avez peut être lu...

Les mots sont rassemblés dans une String de la classe CoreJava2.java. Toutes les actions associées aux noms des boutons de cette IHM doivent être implémentées :

L'applette se trouve dans AppletteTPLinkedList.java, et le fichier à compléter est IHMTPLinkedList.java.

Une IHM possible :

(ii) *** Compléter cette ihm en ajoutant la possibilité d'annuler toutes les opérations effectuées sur la liste une à une à rebours.

A chaque commande effectuant une modification de la liste, une copie de cette liste est placée sur une pile (issue du TP3 par exemple). Chaque annulation engendre la restauration de la liste extraite de la pile de sauvegarde. Cela peut donner :

(iii) *** On souhaite maintenant une liste dont les éléments sont exclusivement de la même classe, proposer une sous-classe de LinkedList effectuant un contrôle de  "type" sur les éléments insérés.

Cette classe est nommée TypedLinkedList. Vous pouvez utiliser et enrichir cet extrait de la classe de test unitaire ci-dessous.

.../...
  TypedLinkedList listeBoolean         = new TypedLinkedList(Boolean.class);
  TypedLinkedList listeInteger         = new TypedLinkedList(Integer.class);
  TypedLinkedList ListeTypedLinkedList = new TypedLinkedList(TypedLinkedList.class);

  try
  {
    listeBoolean.add(new Boolean(true));
    Integer i = new Integer(3);
    listeInteger.addFirst(i);
    listeInteger.addLast(i);
    listeBoolean.add(i); // une erreur de type
  }
  catch (RuntimeException e)
  {
    e.printStackTrace();
  }

  ListeTypedLinkedList.add(listeBoolean);
  ListeTypedLinkedList.add(listeInteger);
  assertEquals(...)

  LinkedList l = new LinkedList();
  l.add(new String("essai"));
  ListeTypedLinkedList.addAll(l);
  assertEquals(...)
.../...

(iv) Fournissez la documentation regroupant toutes les classes de la question (i) et optionnellement des questions (ii) et (iii).


HORS TP ** :  si vous voulez aller plus loin ...

Assertions

(i) On souhaite maintenant pouvoir vérifier des assertions sur des ensembles.

Une forme simple d'assertions est présente à partir de la version 1.4 du JSDK (voir les explications en suivant le lien indiqué dans le cadre au début de ce sujet).
Pourtant, dans cette question, nous souhaitons aller plus loin et permettre de vérifier des propriétés telles que

  • "Il existe au moins un élément de cet ensemble qui vérifie ..."
  • "Quel que soit l'élément de cet ensemble, il vérifie ..."

Pour cela :

  • Définissez l'interface Assertion; toute "propriété" qui voudra respecter cette interface, devra implémenter la méthode boolean asserte( Object ) qui retourne vrai quand la propriété est vérifiée.

  • Complétez la classe ci-dessous :
      // Asserte.java
      public class Asserte
        {
        // l'assertion a doit être vérifiée
        // pour TOUS les éléments obtenus par l'itérateur it
        public static boolean forAll( Iterator it, Assertion a )
          { /* à compléter */ }
    
        // l'assertion a doit être vérifiée
        // pour AU MOINS UN élément obtenu par l'itérateur it
        public static boolean exists( Iterator it, Assertion a )
          { /* à compléter */ }
        } // Asserte 
  • Complétez la classe de test ci-dessous :
    // TestAssertions.java
    public class TestAssertions
      {
      public static void main( String[] args )
        {
        Ensemble e1 = new Ensemble();
        for ( int i=0; i<4; i++ )
          e1.add( new Integer(i) );
        System.out.println( "1. e1 = " + e1 );
    
        // tous les éléments sont de type Integer
        Assertion estEntier = new EstEntier();
        System.out.println( "2. forAll(e1,estEntier) : "
                            + à compléter ;
    
        // un des éléments est de type Integer et est égal à 2
        Assertion unEntierEgal2 = new EntierEgalADeux();
        System.out.println( "3. exists(e1,unEntierEgal2) : "
                            + à compléter ;
    
        // un des éléments est de type Integer et est égal à 12
        Assertion unEntierEgal12 = new EntierEgalADouze();
        System.out.println( "4. exists(e1,unEntierEgal12) : "
                            + à compléter ;
    
        Ensemble e2 = new Ensemble();
        for ( int i=3; i>=0; i-- )
          e2.add( new Integer(i) );
        System.out.println( "5. e2 = " + e2 );
    
        System.out.println( "6. exists(e2,unEntierEgal12) : "
                            + à compléter ;
        System.out.println( "7. " + e1 + " == " + e2 + " : " + e1.equals( e2 ) );
    		
        e2.add( new Integer(12) );
        System.out.println( "8. exists(e2,unEntierEgal12) : "
                            + à compléter ;
        System.out.println( "9. " + e1 + " == " + e2 + " : " + e1.equals( e2 ) );
      } // main()
    
      private static class EstEntier implements Assertion
        {
        public boolean asserte( Object obj )
          {
          à compléter                      
          } // asserte()
        } // EstEntier
    
      private static class EntierEgalADeux implements Assertion
        {
        public boolean asserte( Object obj )
          {
          à compléter
          } // asserte()
        } // EntierEgalADeux
    	
      private static class EntierEgalADouze implements Assertion
        {
        public boolean asserte( Object obj )
          {
          à compléter
          } // asserte()
        } // EntierEgalADouze
      } // TestAssertions 
  • Expliquez le résultat obtenu à la ligne 7. ci-dessus.

(ii) TreeSet est une des implémentations concrètes des collections java. Remplacez dans la question précédente la classe Ensemble par TreeSet.

Remarquez le peu de modifications nécessaires. Expliquez la modification d'affichage dès la ligne 1. Expliquez l'affichage de la ligne 5.

(iii) Imaginons maintenant que les ensembles ne contiennent pas des entiers mais des objets d'une classe que nous créons nous-même.

Pour faciliter la réalisation de cette classe et pour pouvoir réutiliser TestAssertions, nous supposerons que cette classe s'appelle Entier et qu'elle ne contient qu'un entier.

Pour que cette classe puisse être mise dans un TreeSet qui ordonne ses éléments, elle doit implémenter l'interface Comparable.

  • Complétez la classe ci-dessous :
    // Entier.java
    public class Entier implements Comparable
      {
      private int val;
    
      public Entier( int i ) { val = i; }
    
      public int intValue() { return val; }
    
      public String toString() { return "" + val; }
    
      public int compareTo( Object o )
        {
        if ( ! ( o instanceof Entier ) )   throw new ClassCastException();
        /* à compléter */
        } // compareTo()
    
      public boolean equals( Object o ) { return compareTo( o ) == 0; }
      } // Entier 
  • Modifiez la classe TestAssertions pour utiliser des Entiers au lieu des Integers.

  • Remarquez encore une fois le peu de modifications nécessaires lorqu'on remplace une classe par une autre respectant les mêmes interfaces.

(iv) On désire maintenant pouvoir choisir lors de la création de chaque ensemble l'ordre dans lequel les éléments du TreeSet sont triés.

Pour cela :

  • Ajoutez dans TestAssertions deux objets "constants" CROISSANT et DECROISSANT définis à l'aide d'une classe anonyme implémentant l'interface Comparator d'après le modèle ci-dessous :
      public static final Comparator CROISSANT = new Comparator()
        {
        public int compare( Object o1, Object o2 )
          {
          if ( o1.getClass() != o2.getClass() )
            throw new ClassCastException();
          /* à compléter */
          } // compare()
        }; // CROISSANT 
  • Utilisez le bon constructeur de TreeSet au moment de la création de chaque ensemble en choisissant un "tri" différent pour chacun.

(iv) Fournissez la documentation regroupant toutes les classes des questions (i) (ii) (iii) .


Table

(i) Pour bien comprendre comment fonctionne un Iterator, reprendre la classe Ensemble de la question 1.ii et remplacer Vector par la classe Table (à créer).

Cette classe doit notamment fournir un Iterator qui devra être écrit intégralement. L'application TestEnsemble de la question 2. doit fonctionner comme avant.

AIDE :

  • Vous pouvez vous inspirer du squelette de classe ci-dessous :
    // Table.java
    
    import java.util.AbstractCollection;
    import java.util.Iterator;
    import java.util.NoSuchElementException;
    
    public class Table extends AbstractCollection
      {
      private Object[] t;
      private int nbElts; // = aussi l'indice de la première place libre
    
      public Table( int taille )
        { 
        // à compléter
        } // Table()
    
      public boolean add( Object o )
        {
        if ( nbElts >= t.length )
          throw new UnsupportedOperationException( "Table pleine !" );
        // à compléter
        } // add()
    
      public int size() { return nbElts; }
    
      public boolean contains( Object o )
        {
        // à compléter
        } // contains()
    
      public boolean equals( Object o )
        { 
        if ( ! ( o instanceof AbstractCollection ) )
          throw new ClassCastException( o.getClass().toString() );
        // à compléter
        } // equals()
    
      public Iterator iterator()
        {
        class TableIterator implements Iterator
          {
          // à compléter
          } // TableIterator
    
        return new TableIterator();  
        } // iterator()
      } // Table 

(ii) Fournissez la documentation regroupant toutes les classes de la question (i).

** Hors TP signifie "non pris en compte pour l'évaluation".


Annexe 1. Source de IHMEnsemble.java

/*
 * IHMEnsemble.java
 *
 * Created on 28 mars 2000, 18:37
 * DB : modified 04/10/02
 */

/** 
 *
 * @author  douin
 * @version 
 */
public class IHMEnsemble extends java.applet.Applet
  {

  /** Initializes the applet IHMEnsemble */
  public void init () { initComponents(); }

  /** This method is called from within the init() method to
   * initialize the form.
   */
  private void initComponents()
    {
    setLayout( new java.awt.GridLayout( 4, 1, 2, 2 ) );

    // panel1 :
    panel1 = new java.awt.Panel();
    panel1.setLayout( new java.awt.FlowLayout( 0, 5, 5 ) );
    panel1.setBackground( java.awt.Color.white );

    label1 = new java.awt.Label();
    label1.setBackground( java.awt.Color.orange );
    label1.setText( "ensemble e1 :" );
    label1.setAlignment( java.awt.Label.RIGHT );
    panel1.add( label1 );

    textField1 = new java.awt.TextField();
    textField1.setColumns( 52 );
    panel1.add( textField1 );

    add (panel1);

    // panel2 :
    panel2 = new java.awt.Panel();
    panel2.setLayout( new java.awt.FlowLayout( 0, 5, 5 ) );
    panel2.setBackground( java.awt.Color.white );

    label2 = new java.awt.Label();
    label2.setBackground( java.awt.Color.orange );
    label2.setText( "ensemble e2 :" );
    label2.setAlignment( java.awt.Label.RIGHT );
    panel2.add( label2 );

    textField2 = new java.awt.TextField();
    textField2.setColumns( 52 );
    panel2.add( textField2 );

    add( panel2 );

    // panel3 :
    panel3 = new java.awt.Panel();
    panel3.setLayout( new java.awt.FlowLayout( 0, 5, 5 ) );
    panel3.setBackground( java.awt.Color.white );

    label4 = new java.awt.Label();
    label4.setBackground( java.awt.Color.orange );
    label4.setText( "Opérations e1 Op e2  :" );
    panel3.add( label4 );

    button1 = new java.awt.Button();
    button1.setBackground( java.awt.Color.green );
    button1.setLabel( "union" );
    button1.addActionListener(
      new java.awt.event.ActionListener()
        {
        public void actionPerformed( java.awt.event.ActionEvent evt )
          { unionActionPerformed( evt ); }
        } // ActionListener
    );
    panel3.add( button1 );

    button2 = new java.awt.Button();
    button2.setBackground( java.awt.Color.green );
    button2.setLabel( "intersection" );
    button2.addActionListener(
      new java.awt.event.ActionListener()
        {
        public void actionPerformed( java.awt.event.ActionEvent evt )
          { intersectionActionPerformed( evt ); }
        } // ActionListener
    );
    panel3.add( button2 );

    button3 = new java.awt.Button();
    button3.setBackground( java.awt.Color.green );
    button3.setLabel( "difference" );
    button3.addActionListener(
      new java.awt.event.ActionListener()
        {
        public void actionPerformed( java.awt.event.ActionEvent evt )
          { differenceActionPerformed( evt ); }
        } // ActionListener
    );
    panel3.add( button3 );

    button4 = new java.awt.Button();
    button4.setBackground( java.awt.Color.green );
    button4.setLabel( "diffSymétrique" );
    button4.addActionListener(
      new java.awt.event.ActionListener()
        {
        public void actionPerformed( java.awt.event.ActionEvent evt )
          { diffSymetriqueActionPerformed( evt ); }
        } // ActionListener
    );
    panel3.add( button4 );

    add( panel3 );

    // panel4 :
    panel4 = new java.awt.Panel();
    panel4.setLayout( new java.awt.FlowLayout( 0, 5, 5 ) );
    panel4.setBackground( java.awt.Color.white );

    label3 = new java.awt.Label();
    label3.setBackground( java.awt.Color.orange );
    label3.setText( "Résultat" );
    panel4.add( label3 );

    textField3 = new java.awt.TextField();
    textField3.setColumns( 60 );
    panel4.add( textField3 );

    add (panel4);
  } // initComponents()

  private Ensemble getSet( String s )
    {
    Ensemble e = new Ensemble();
    java.util.StringTokenizer st = new java.util.StringTokenizer( s, ",. :/;" );
    while ( st.hasMoreTokens() )   e.add( st.nextToken() );
    return e;
  } // getSet()

  private void unionActionPerformed( java.awt.event.ActionEvent evt )
    {
    // à compléter
    } // unionActionPerformed()

  private void intersectionActionPerformed( java.awt.event.ActionEvent evt )
    {
    // à compléter
    } // intersectionActionPerformed()

  private void differenceActionPerformed( java.awt.event.ActionEvent evt )
    {
    // à compléter
    } // differenceActionPerformed()

  private void diffSymetriqueActionPerformed( java.awt.event.ActionEvent evt )
    {
    // à compléter
    } // diffSymetriqueactionperformed()

  // Variables declaration :
  private java.awt.Panel panel1;
  private java.awt.Label label1;
  private java.awt.TextField textField1;
  private java.awt.Panel panel2;
  private java.awt.Label label2;
  private java.awt.TextField textField2;
  private java.awt.Panel panel3;
  private java.awt.Label label4;
  private java.awt.Button button1;
  private java.awt.Button button2;
  private java.awt.Button button3;
  private java.awt.Button button4;
  private java.awt.Panel panel4;
  private java.awt.Label label3;
  private java.awt.TextField textField3;
  } // IHMEnsemble


Annexe 2. Source de IHMEnsemble.html

<HTML>
 <HEAD><TITLE>IHMEnsemble</TITLE></HEAD>
 <BODY>
  <H3>
   <HR WIDTH="100%">IHMEnsemble<HR WIDTH="100%">
  </H3>
  <P>
  <APPLET codebase="http://www.esiee.fr/~bureaud/fi/unites../i4Java/tp5/"
   code="IHMEnsemble.class" width=550 height=200>
  </APPLET>
  <HR WIDTH="100%">
 </BODY>
</HTML>


Annexe 3. Source de AppletteTPLinkedList.java

import java.applet.Applet;
import java.awt.Color;
import java.awt.BorderLayout;
import java.awt.TextArea;

import java.util.*;

public class AppletteTPLinkedList extends Applet
{
  public void init()
  {
    List liste = chapitre2CoreJava2();
    IHMTPLinkedList ihmListe = new IHMTPLinkedList(liste);
    ihmListe.setBackground(Color.yellow);
    add(ihmListe);
  } // init()

  private static List chapitre2CoreJava2()
  {
    List liste      = new LinkedList();
    StringTokenizer st = new StringTokenizer(CoreJava2.CHAPITRE2,"[](){};, :.\n\"");
    while(st.hasMoreTokens())
    {
      String unMot = st.nextToken();
      liste.add(unMot);
    }
    return liste;
  } // chapitre2CoreJava2()
} // AppletteTPLinkedList


Annexe 4. Source de IHMTPLinkedList.java

import java.awt.*;
import java.awt.event.*;

import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.LinkedList;

public class IHMTPLinkedList extends Panel implements ActionListener,ItemListener
{
  private Panel          cmd = new Panel();
  private Label          afficheur = new Label();
  private TextField      saisie = new TextField();
  
  private Panel          panelBoutons = new Panel();
  private Button         boutonRechercher = new Button("rechercher");
  private Button         boutonRetirer = new Button("retirer");
	
  private CheckboxGroup  mode = new CheckboxGroup();
  private Checkbox       ordreCroissant = new Checkbox("croissant", mode, false);
  private Checkbox       ordreDecroissant = new Checkbox("décroissant", mode, false);

  private TextArea       texte = new TextArea();

  private List      liste;
  private String    nomDeLaClasse;

  public IHMTPLinkedList(List liste)
  {
    this.liste = liste;
    
    cmd.setLayout(new GridLayout(3,1));
    cmd.add(afficheur);
    cmd.add(saisie);
    
    panelBoutons.setLayout(new FlowLayout(FlowLayout.LEFT));
    panelBoutons.add(boutonRechercher);panelBoutons.add(boutonRetirer);
    panelBoutons.add(new Label("tri du texte :"));
    panelBoutons.add(ordreCroissant);panelBoutons.add(ordreDecroissant);
    cmd.add(panelBoutons);
    
    nomDeLaClasse = liste.getClass().getName();
    afficheur.setText(nomDeLaClasse);
    texte.setText(liste.toString());

    setLayout(new BorderLayout());
    add(cmd,"North");
    add(texte,"Center");

    boutonRechercher.addActionListener(this);
    boutonRetirer.addActionListener(this);
    saisie.addActionListener(this);
    ordreCroissant.addItemListener(this);
    ordreDecroissant.addItemListener(this);
  } // IHMTPLinkedList()
  
  public void actionPerformed(ActionEvent ae)
  {
    try
    {
      boolean res=false;
      if (ae.getSource() == boutonRechercher || ae.getSource() == saisie)
      {
        res = liste.contains(saisie.getText());
        afficheur.setText("résulat de la recherche de : " + saisie.getText() + " : "+ res);
      }
      else if (ae.getSource() == boutonRetirer)
      {
        res = retirerDeLaListeTousLesElementsCommencantPar(saisie.getText());
        afficheur.setText("résulat du retrait de tous les éléments commençant par : " + saisie.getText() + " : " + res);
      }
      texte.setText(liste.toString());
    }
    catch (Exception e)
    {
      afficheur.setText( e.toString());
    }
  } // actionPerformed()

  public void itemStateChanged(ItemEvent ie)
  {
  //  à compléter
  //if (ie.getSource() == ordreCroissant)
  //else if (ie.getSource() == ordreDecroissant)
    texte.setText(liste.toString());
  } // itemStateChanged()

  private boolean retirerDeLaListeTousLesElementsCommencantPar(String prefixe)
  {
    boolean resultat = false;
    //  à compléter
    return resultat;
  } // retirerDeLaListeTousLesElementsCommencantPar()
} // IHMTPLinkedList