Travaux Pratiques n°6

Lectures préalables :  
Thèmes du TP :
  • Le pattern Composite
  • Les expressions arithmétiques
  • Le pattern Interpréteur
  • La sérialisation

 

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.

Question 1. (Évaluation d'expression)

(i) Selon le Pattern Composite, la grammaire d'une expression arithmétique peut être décrite par cette hiérarchie de classes :

AIDE :

  • Attention au constructeur des sous-classes de OpBinaire.
  • Faites l'analogie avec l'organisation des classes de l'AWT (la classe abstraite Component est la racine).

(ii) Il s'agit maintenant de pouvoir évaluer des expressions.

Pour cela, le pattern Interpréteur propose de définir à l'intérieur de la classe Expression et de ses sous-classes une méthode interprete() qui se sert d'une pile pour évaluer chaque morceau d'expression. Si on reprend la pile de la question 2 du TP3, la signature de cette méthode est :
public void interprete( PileI p ) throws Exception
La hiérarchie de classes devient donc (hors classes d'exception de la pile) :

*** (iii) Mettez en commun dans la classe OpBinaire les instructions communes à Addition et à Multiplication, puis ajoutez suivant le même modèle les opérations de Soustraction et de Division.

Cette question est optionnelle. Si vous ne la traitez pas, n'utilisez que des additions et des multiplications dans la suite.

L'expression à construire et à évaluer est maintenant : 10/5 - 2*4 + 1
Ça fait bien -5 ?
Vous avez donc bien respecté l'associativité à gauche de Java.

(iv) Fournir une classe de test unitaire pour Nombre, et Addition et/ou Multiplication.

*** (v) Fournir une classe de test global, dans le style de TestExpression, pour vérifier le bon fonctionnement de l'ensemble (menu New Class puis Unit Test).

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


Question 2. (Parsing)

(i) L'analyse syntaxique d'une expression arithmétique est réalisée par la classe ArithmeticExpression (Attention! cette classe utilise l'associativité à droite, mais cela nous évite d'avoir à redévelopper une telle classe).

Elle respecte la grammaire traditionnelle des expressions entières suivante, en formalisme apparenté BNF/DCG:

 parseE --> parseT
 parseE --> parseT , [+] , parseE.
 parseE --> parseT , [-] , parseE.

 parseT --> parseF.
 parseT --> parseF , [*] , parseT.
 parseT --> parseF , [/] , parseT.

 parseF --> ['('] , parseE , [')'].
 parseF --> [N] , {integer(N)}.
En utilisant cette classe dans le programme de la question 1.iii, proposez une série de tests variés.

(ii) Proposez une applette de test permettant de rentrer une expression arithmétique telle que 10/5-(2*4+1) et de l'évaluer.

Un exemple de ce que cela peut donner :

Attention !   ne pas mettre d'espaces dans l'expression  

AIDE :

  • Un squelette d'applette est fourni en annexes 2 et 3.

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


Question 3. (Sérialisation)

(i) Ajoutez les méthodes de classe pour sauvegarder et restituer une Expression.

Avant toute chose, il est conseillé de lire le premier des quatre chapitres du document sur la sérialisation cité dans le cadre au début de ce sujet.

Leurs signatures devraient être :

public static void sauvegarder( Expression exp, String nomfichier ) throws Exception
public static Expression restituer( String nomfichier ) throws Exception

Pour pouvoir être sérialisée, une classe doit implémenter l'interface Serializable.

AIDE :

  • Il est fortement recommandé de lire l'exemple Person & WritePerson & ReadPerson, et de s'en inspirer.
    Attention ! Pour les expressions, il n'est pas nécessaire de redéfinir writeObject() et readObject().
  • Quelles sont les méthodes à implémenter obligatoirement lorsqu'on veut respecter l'interface Serializable ? A quoi cela peut-il servir de définir une telle interface ?

(ii) Ajoutez à la fin du main() de TestExpression de la question 2.i un test de sérialisation.

Ce test comprendra la sauvegarde de l'expression, l'instruction exp = null; pour détruire l'objet, puis la restitution de l'expression et son évaluation pour vérifier que le résultat est toujours le même.

*** (iii) Modifiez l'IHM de la question 2.ii pour pouvoir tester interactivement la sérialisation.

Cette question est optionnelle.

Pour cela, ajoutez une deuxième "ligne" comprenant un champ de saisie pour le nom de fichier (Expression.ser par défaut), et deux boutons sauvegarder et restituer.

Un exemple de ce que cela peut donner :

Attention !   ne pas mettre d'espaces dans l'expression  

ATTENTION !

  • Une applette n'a normalement pas le droit d'accéder aux fichiers du disque dur (heureusement !).
    Ici, nous souhaitons volontairement donner ce droit à cette applette particulière.
    Pour cela, il faut modifier le fichier java.policy qui se trouve dans C:\j2sdk1.4.2\jre\lib\security. Ajoutez les 3 lignes :
          grant codeBase "file:U:/BureauD/in413/tp6/3iii/" {
            permission java.security.AllPermission;
          };
    si tous les .class se trouvent dans le répertoire U:\BureauD\in413\tp6\3iii.
    Cette manipulation peut ne pas fonctionner sur un PC sur lequel vous n'avez pas les droits d'écriture sur tous les répertoires du disque dur.

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


OPTIONNEL* :

Question 4. (toString)

(i) Redéfinissez toString() dans les 4 opérations et dans Nombre.

(ii) Ajoutez dans l'applette de la question 3.iii l'affichage dans le champ de saisie de l'expression restituée.


Annexe 1. Source de ArithmeticExpression.java

// tp6-2i : ArithmeticExpression.java
import java.util.StringTokenizer;
/** Analyseur syntaxique.              Attention !
 * la grammaire des expressions        associativité à droite !
 * parseE --> parseT.
 * parseE --> parseT , [+] , parseE.
 * parseE --> parseT , [-] , parseE.
 * 
 * parseT --> parseF.
 * parseT --> parseF , [*] , parseT.
 * parseT --> parseF , [/] , parseT.
 * 
 * parseF --> [(] , parseE , [)].
 * parseF --> [N] {integer(N)}.
 *
 * @see StringTokenizer
 */

public class ArithmeticExpression
  {
  /** en entrée l'expression infixée. */
  private String          infix;
  /** l'analyseur lexical. */
  private StringTokenizer str;
  /** le prochain caractère du flot en entrée. */
  private String          next;

  /** Création d'une instance.
   *
   * @param s   l'expression infixée selon la grammaire.
   */
  public ArithmeticExpression( String s )
    {
    infix = s + "$";
    str = new StringTokenizer( infix, "()+-*/$", true );
    } // ArithmeticExpression()

  /** la version infixée de l'expression */
  public String toString() { return infix; } 

  /** Analyse syntaxique. 
   *
   * @return  Une instance de la classe expression
   */
  public Expression parse()
    {
    next = str.nextToken();
    return parseE();
    } // parse()

  /** Analyse syntaxique: parseE. 
   *
   * @return  Une instance de la classe Expression
   */
  private Expression parseE( )
    { // parse an Expression
    String op;
    Expression exp1 = parseT();    
    if ( next.equals( "+" ) || next.equals( "-" ) )
      {
      op   = next;
      next = str.nextToken();
      Expression exp2 = parseE();
      if( op.equals( "+" ) )
        return new Addition( exp1, exp2 );
      else  // op.equals( "-" )
        return new Soustraction( exp1, exp2 );
      }
    return exp1;
    } // parseE()

    /** Analyse syntaxique: parseT. 
	*
	* @return Une instance de la classe Expression
	*/
  private Expression parseT()
    { // parse a Term
    String op;
    Expression exp1 = parseF();
    if ( next.equals( "*" ) || next.equals( "/" ) )
      {
      op   = next;
      next = str.nextToken();
      Expression exp2 = parseT();
      if( op.equals( "*" ) )
        return new Multiplication( exp1, exp2 );
      else  // op.equals( "/" )
        return new Division( exp1, exp2 );
      }
    return exp1;
    } // parseT()

  /** Analyse syntaxique: parseF. 
   *
   * @return Une instance de la classe Expression
   */
  private Expression parseF()
    { // parse a Factor
    Expression exp;
    if ( next.equals( "(" ) )
      {
      next = str.nextToken();
      exp  = parseE();
      next = str.nextToken();
      return exp;
      }
    else
      {
      exp  = new Nombre( Integer.parseInt( next ) );
      next = str.nextToken();
      return exp;
      }    
    } // parseF()
  } // ArithmeticExpression


Annexe 2. Source de AppletteTPExpression.java

// tp6-2ii : AppletteTPExpression.java
import java.applet.Applet;
/**
 *  L'applette.
 */
public class AppletteTPExpression extends Applet
  {    
  private IHMExpression ihmExpression; 
   
  public void init()
    {
    ihmExpression = new IHMExpression();
    add( ihmExpression );
    } // init()
  } // AppletteTpExpression


Annexe 3. Source de IHMExpression.java

// tp6-2ii : IHMExpression.java
import java.awt.*;
import java.awt.event.*;

public class IHMExpression extends Panel implements ActionListener
  {
  private TextField saisie     = new TextField( 20 );
  private Button boutonEvaluer = new Button( "évaluer" );
  private TextField resultat   = new TextField( 4 );
  private Expression exp;
  private PileI p = new Pile( 20 );

  public IHMExpression()
    {
    setLayout( new BorderLayout() );
	 
    Panel pa = new Panel();
    pa.add( saisie );
    saisie.setText( "3+2*4" );   
    pa.add( boutonEvaluer );
    resultat.setEnabled( false );
    pa.add( resultat );
    add( pa, "North" );
    boutonEvaluer.addActionListener( this );
    saisie.addActionListener( this );
    } // IHMExpression()

  public void actionPerformed( ActionEvent ae )
    {
    try
      {
      // à compléter
      }
    catch( Exception e )
      {
      resultat.setText( "0000" );
      e.printStackTrace();
      } // catch()
    } // actionPerformed()
  } // IHMExpression


Annexe 4. Source de Person.java

// Person.java
import java.io.*;
public class Person implements Serializable
  {
  private String firstName;
  private String lastName;
  private String password;
  // transient Thread worker;

  public Person( String firstName, String lastName, String password )
    {
    this.firstName = firstName;
    this.lastName  = lastName;
    this.password  = password;
    } // Person()

  public String toString()
    { return new String( lastName + " " + firstName + " : " + password ); }

  // utile uniquement car on veut coder le mot de passe
  private void writeObject( ObjectOutputStream oos )
    throws IOException
    {
    password = code( password ); // code
    oos.defaultWriteObject();
    password = code( password ); // décode
    } // writeObject()

  // utile uniquement car on veut décoder le mot de passe
  private void readObject( ObjectInputStream ois )
    throws IOException, ClassNotFoundException
    {
    ois.defaultReadObject();
    password = code( password ); // décode
    } // readObject()

  private String code( String s )
    { // ceci n'est pas un cryptage sérieux !
    String r = new String( "" );
    for ( int i=s.length()-1; i>=0; i-- )
      r += s.charAt( i );
    return r;
    } // code()
  } // Person


Annexe 5. Source de WritePerson.java

// WritePerson.java
import java.io.*;

public class WritePerson
  {
  public static void main( String [] args )
    {
    Person p = new Person( "Fred", "Wesley", "cantguessthis" );
    ObjectOutputStream oos = null;
    try
      {
      oos = new ObjectOutputStream( new FileOutputStream( "Person.ser" ) );
      oos.writeObject( p );
      }
    catch ( Exception e )
      {
      e.printStackTrace();
      }
    finally
      {
      if ( oos != null )
        {
        try { oos.flush(); }
        catch ( IOException ioe )
          { System.out.println( "can't flush !" ); }
        try { oos.close(); }
        catch ( IOException ioe )
          { System.out.println( "can't close !" ); }
        } // if
      } // finally
    } // main()
  } // WritePerson


Annexe 6. Source de ReadPerson.java

// ReadPerson.java
import java.io.*;

public class ReadPerson
  {
  public static void main( String [] args )
    {
    ObjectInputStream ois = null;
    try
      {
      ois = new ObjectInputStream( new FileInputStream( "Person.ser" ) );
      Object o = ois.readObject();
      System.out.println( "Read object " + o );
      }
    catch ( Exception e )
      {
      e.printStackTrace();
      }
    finally
      {
      if ( ois != null )
        {
        try { ois.close(); }
        catch ( IOException ioe )
          { System.out.println( "can't close !" ); }
        } // if
      } // finally
    } // main()
  } // ReadPerson