NFP121
Programmation avancée Session de Avril 2011-durée : 2 heures
Tous documents papiers autorisés
Cnam / Paris Sommaire :
Question 1 (4 points) : Une propriété
Question 2 (6 points) : Une liste de propriétés
Question 3 (6 points) : Sérialisation de propriétés en XML Question 4 (4 points) : Interface graphique Swing et évènements
Question1 (4 points) : Une propriété sur des objets
L’objectif est de définir et vérifier certaines propriétés sur des objets. Ces propriétés sont vraies ou fausses et portent sur des objets de types quelconques. Le type de l’objet est générique (<E>) et la propriété est définie par l’interface PropriétéI<E>.
L’architecture retenue des classes pour cette question est représentée par le diagramme, en notation BlueJ/UML,
ci-dessous. Ces classes contiennent un exemple simple sur les propriétés des nombres (NombrePair,
NombrePremier, NombreImpair), et sur les propriétés attendues d’une classe, comme la première lettre du nom de
la classe en Majuscule (NomEnMajuscule) et l’existence d’une méthode (MéthodeExiste) au sein de cette classe.
L’interface PropriétéI contient les opérations suivantes :
package question1 ;
public interface PropriétéI<E>{ // <E> le type de l’objet
String toString(); // l'objectif, l'intitulé de la propriété boolean estSatisfaite(E e); // la propriété est-elle vérifiée, satisfaite ? }
La classe NombrePair est un exemple de définition d’une propriété sur les nombres :
public class NombrePair implements PropriétéI<Integer>{
public String toString(){ // l'intitulé de la propriété return "nombre pair";
}
public boolean estSatisfaite(Integer i){
return i % 2 == 0; // la propriété est-elle vérifiée, satisfaite ? }
// equals et hashcode sont fournies }
Une méthode extraite de la classe de tests unitaires TestUnitaires :
public void test1(){
PropriétéI<Integer> p = new NombrePair();
assertTrue(p.toString(), p.estSatisfaite(4)); // 4 est pair ! assertFalse(p.toString(), p.estSatisfaite(3)); // 3 ne l’est pas … }
Sur le diagramme des classes, NomEnMajuscule et MéthodeExiste sont deux propriétés d’une classe, la première propriété est satisfaite si la première lettre du nom de la classe est une majuscule et la seconde propriété est satisfaite si la méthode avec ce nom et ces paramètres existe dans cette classe.
Les trois méthodes de la classe de tests unitaires sont à lire attentivement avant de répondre à cette question:
public void test2(){
PropriétéI<Class<?>> p = new NomEnMajuscule();
assertTrue(p.estSatisfaite(NombrePair.class));
assertTrue(p.estSatisfaite(TestsUnitaires.class));
}
public void test3(){
PropriétéI<Class<?>> p = new MéthodeExiste("add",Object.class);
// add(Object) doit exister dans ces 2 classes
assertTrue(p.estSatisfaite(java.util.ArrayList.class));
assertTrue(p.estSatisfaite(java.util.LinkedList.class));
// add(int, Object) doit exister dans cette classe
p = new MéthodeExiste("add",Integer.TYPE,Object.class);
assertTrue(p.estSatisfaite(java.util.ArrayList.class));
// note : ces deux syntaxes sont équivalentes, Class<?>… t, t est un tableau p = new MéthodeExiste("add",Integer.TYPE,Object.class);
p = new MéthodeExiste("add",new Class<?>[]{Integer.TYPE,Object.class});
}
public void test4(){
PropriétéI<Class<?>> p = new NomEnMajuscule();
String attendue = "le nom de la classe commence par une Majuscule";
assertTrue(p.toString().equals(attendue));
}
public void test5(){
PropriétéI<Class<?>> p = new MéthodeExiste("add",Object.class);
String attendue = "la méthode add(java.lang.Object) de la classe existe-t-elle ?";
assertTrue(p.toString().equals(attendue));
p = new MéthodeExiste("add",Integer.TYPE,Object.class);
attendue = "la méthode add(int, java.lang.Object) de la classe existe-t-elle ?";
assertTrue(p.toString().equals(attendue));
}
Question1-1)
Ecrire une implémentation complète de la classe NomEnMajuscule. Cette propriété est satisfaite si la première lettre du nom d’une classe est en majuscule. La méthode toString() associée doit vérifier les assertions présentes dans la méthode test4. (Les méthodes equals et hashCode sont fournies).
package question1;
public class NomEnMajuscule implements PropriétéI<Class<?>>{
public String toString(){
return "le nom de la classe commence par une Majuscule";
}
public boolean estSatisfaite(Class<?> classe){
char initiale = classe.getSimpleName().charAt(0);
return initiale >='A' && initiale <='Z';
}
public boolean equals(Object obj){
return obj.getClass() == this.getClass();
}
public int hashCode(){
return this.getClass().getName().hashCode();
} }
Question1-2)
Ecrire une implémentation complète de la classe MéthodeExiste. Cette propriété est satisfaite si la méthode avec ce nom et ces paramètres existe dans cette classe. La méthode toString() associée doit vérifier les assertions
présentes dans la méthode test5. (Les méthodes equals et hashCode sont fournies).
package question1;
public class MéthodeExiste implements PropriétéI<Class<?>>{
private String nomDeLaMéthode;
private Class<?>[] typeDesParamètres;
public MéthodeExiste(String nomDeLaMéthode, Class<?>... typeDesParamètres){
this.nomDeLaMéthode = nomDeLaMéthode;
this.typeDesParamètres = typeDesParamètres;
}
public String nomDeLaMéthode(){ return nomDeLaMéthode;}
public Class<?>[] typeDesParamètres(){ return typeDesParamètres;}
public String toString(){
return "la méthode " + nomDeLaMéthode() + "(" + typeDesParamètresToString() + ")" + " de la classe existe-t-elle ?";
}
private String typeDesParamètresToString(){
String res = new String("");
for(int i=0; i<this.typeDesParamètres.length;i++){
res = res + this.typeDesParamètres[i].getName();
if(i<this.typeDesParamètres.length-1) res += ", ";
}
return res;
}
public boolean estSatisfaite(Class<?> classe){
try{
classe.getDeclaredMethod(nomDeLaMéthode,typeDesParamètres);
}catch(NoSuchMethodException e){
return false;
}
return true;
}
public boolean equals(Object obj){
if(!(obj instanceof MéthodeExiste)) return false;
MéthodeExiste m = (MéthodeExiste)obj;
return this.nomDeLaMéthode.equals(m.nomDeLaMéthode) &&
java.util.Arrays.equals(this.typeDesParamètres, m.typeDesParamètres);
}
public int hashCode(){
return (nomDeLaMéthode+typeDesParamètresToString()).hashCode();
}
}
Question2 (6 points) : Une liste de propriétés sur des objets
Nous souhaitons maintenant obtenir la gestion d’une liste de propriétés. La classe ListeDePropriétés<E> se comporte elle-même comme une propriété, elle est satisfaite si toutes les propriétés qui la composent sont satisfaites.
Les classes pour la question1-1 en notation BlueJ/UML :
L’interface ListeDePropriétésI contient les opérations suivantes :
package question2 ;
import question1.PropriétéI;
import java.util.Iterator;
public interface ListeDePropriétésI<E> extends PropriétéI<E>, Iterable<PropriétéI<E>>{
void ajouter(PropriétéI<E> p); // ajout d’une propriété, // pas de doublon possible
void ajouter(ListeDePropriétésI<E> l); // ajout d’une liste de propriétés boolean contient(PropriétéI<E> p); // la propriété est-elle dans la liste ? int taille(); // le nombre de propriétés de cette liste
boolean equals(Object obj); // la méthode equals est demandée int hashCode(); // hashCode est fournie
// issues de l'interface PropriétéI<E>
String toString(); // le cumul des toString de chaque propriété boolean estSatisfaite(E e); // toutes les propriétés sont-elles satisfaites ?
// issue de Iterable<PropriétéI<E>>
Iterator<PropriétéI<E>> iterator(); // parcours de la liste }
Une méthode extraite de la classe de tests unitaires TestListeDePropriétés :
public void test1(){
ListeDePropriétésI<Integer> liste1 = new ListeDePropriétés<Integer>();
liste1.ajouter(new NombreImpair());
liste1.ajouter(new NombreImpair()); // ajout ignoré, propriété déjà présente liste1.ajouter(new NombrePremier());
assertTrue(liste1.taille()==2); // 2 propriétés présentes assertTrue(liste1.contient(new NombreImpair()));
assertTrue(liste1.contient(new NombrePremier()));
assertTrue(liste1.estSatisfaite(3)); // 3 est impair et premier !
ListeDePropriétésI<Integer> liste2 = new ListeDePropriétés<Integer>();
liste2.ajouter(new NombrePair());
liste2.ajouter(liste1); // liste2 contient 3 propriétés assertFalse(liste2.estSatisfaite(3));
List<PropriétéI<Integer>> liste3 = new ArrayList<PropriétéI<Integer>>();
for(PropriétéI<Integer> p : liste2){ // parcours de liste2 if(p.estSatisfaite(3)) liste3.add(p);
}
// liste3 contient toutes les propriétés satisfaites pour le nombre 3 assertTrue(liste3.size()==2); // impair et premier sont satisfaites assertTrue(liste3.contains(new NombreImpair()));
assertTrue(liste3.contains(new NombrePremier()));
}
Question2-1)
Aux questions 1 et 2, quels patrons auriez-vous choisi pour cette application ? i.e. nous avons des propriétés, des listes de propriétés, des parcours de ces listes, des évaluations …
Patron Composite et (interpréteur ou visiteur)
Question2-2)
Ecrire une implémentation complète de la classe ListeDePropriétés<E>. La méthode equals est demandée, la méthode hashcode est fournie.
import question1.*;
import java.util.Collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.lang.reflect.Method;
public class ListeDePropriétés<E> implements ListeDePropriétésI<E>{
private Collection<PropriétéI<E>> propriétés;
public ListeDePropriétés(){
this.propriétés = new ArrayList<PropriétéI<E>>();
}
public void ajouter(PropriétéI<E> p){
if(!contient(p)) propriétés.add(p);
}
public void ajouter(ListeDePropriétésI<E> liste){
for(PropriétéI<E> p : liste){
ajouter(p);
} }
public boolean contient(PropriétéI<E> p){
return propriétés.contains(p);
}
public int taille(){
return propriétés.size();
}
public String toString(){
return this.propriétés.toString();
}
public boolean estSatisfaite(E e){
for(PropriétéI<E> p : this)
if(!p.estSatisfaite(e)) return false;
return true;
}
public Iterator<PropriétéI<E>> iterator(){
return propriétés.iterator();
}
@Override
public boolean equals(Object obj){
if(!(obj instanceof ListeDePropriétés)) return false;
ListeDePropriétés liste = (ListeDePropriétés)obj;
return this.propriétés.equals(liste.propriétés);
}
@Override
public int hashCode(){
return this.propriétés.hashCode();
} }
Question2-suite )
Afin de vérifier qu’une classe possède bien certaines propriétés, il suffit d’exécuter une suite d’instructions analogue au test suivant :
public void test2(){
ListeDePropriétésI<Class<?>> liste1 = new ListeDePropriétés<Class<?>>();
liste1.ajouter(new NomEnMajuscule());
liste1.ajouter(new MéthodeExiste("ajouterUnitéObtenue",String.class));
liste1.ajouter(new MéthodeExiste("modifier",String.class,String.class));
liste1.ajouter(new MéthodeExiste("unitésObtenues"));
// toutes les propriétés de la liste sont_elle satisfaites ? assertTrue(liste1.estSatisfaite(AuditeurDuCnam.class));
// le test réussi, AuditeurDuCnam contient ces méthodes }
Nous souhaitons engendrer cette suite d’instructions à partir du contenu d’une classe. Nous voulons vérifier que toutes les méthodes d’une classe que nous qualifierons de « modèle » sont présentes dans une classe
quelconque, par exemple si la classe AbstractAuditeur ci-dessous est un « modèle », chaque méthode présente dans cette classe devient une propriété qu’une classe quelconque pourra satisfaire.
Le diagramme des classes en notation BlueJ pour la question 2-3 :
Une méthode de la classe Utilitaires effectue ce traitement, la méthode obtenirLesPropriétés retourne une liste de propriétés à partir d’une classe « modèle ».
public static ListeDePropriétésI<Class<?>> obtenirLesPropriétés(Class<?> modèle) ;
Si la classe « modèle » AbstractAuditeur contient 3 méthodes :
public abstract class AbstractAuditeur{
public abstract void ajouterUnitéObtenue(String unité);
public abstract List<String> unitésObtenues();
public abstract String toString();
}
Alors cette méthode de la classe de tests réussit :
public void test3(){
ListeDePropriétésI<Class<?>> liste1 = Utilitaires.obtenirLesPropriétés(AbstractAuditeur.class);
// les 3 méthodes de la classe AbstractAuditeur deviennent 3 propriétés de la liste assertTrue(liste1.contient(new MéthodeExiste("ajouterUnitéObtenue",String.class)));
assertTrue(liste1.contient(new MéthodeExiste("toString")));
assertTrue(liste1.contient(new MéthodeExiste("unitésObtenues")));
assertTrue(liste1.taille()==3);
// toutes les propriétés sont satisfaites pour la classe AuditeurDuCnam assertTrue(liste1.estSatisfaite(AuditeurDuCnam.class));
// i.e. la classe AuditeurDuCnam possède au moins toutes les propriétés // du modèle AbstractAuditeur
}
Question2-3)
Ecrire la méthode obtenirLesPropriétés de la classe Utilitaires .
public class Utilitaires{
public static ListeDePropriétésI<Class<?>> obtenirLesPropriétés(Class<?> modèle){
ListeDePropriétésI<Class<?>> liste = new ListeDePropriétés<Class<?>>();
for(Method m : modèle.getDeclaredMethods()){
liste.ajouter(new MéthodeExiste(m.getName(),m.getParameterTypes()));
}
return liste;
} }
Question3 (6 points) : Sérialisation en XML
Nous souhaitons maintenant disposer d’une sérialisation d’une liste de propriétés en XML, l’API JDOM est utilisée, les arbres XML engendrés respectent la DTD suivante :
<!ELEMENT proprietes (methode_existe*)>
<!ELEMENT methode_existe (parametre*)>
<!ATTLIST methode_existe nom CDATA #REQUIRED>
<!ELEMENT parametre EMPTY>
<!ATTLIST parametre type CDATA #REQUIRED>
Les classes pour cette question en notation BlueJ/UML :
L’interface PropriétésEnXML contient les opérations suivantes :
package question3;
import question2.ListeDePropriétésI;
import org.jdom.Element;
public interface PropriétésEnXML{
/** Une liste de propriétés en arbre XML. */
Element listeEnArbreXML(ListeDePropriétésI<Class<?>> liste);
/** Un arbre XML en liste de propriétés. */
ListeDePropriétésI<Class<?>> arbreXMLEnListe(Element arbreXML);
}
La classe PropriétésEnXMLImpl implémente cette interface. Les quelques lignes suivantes sont extraites de la classe de tests TestSérialisationXML
public void testSimple() throws Exception {
Element racine = new Element("proprietes");
Element methodeAjouterUnitéObtenue = new Element ("methode_existe");
methodeAjouterUnitéObtenue.setAttribute("nom","ajouterUnitéObtenue");
methodeAjouterUnitéObtenue.addContent(new
Element("parametre").setAttribute("type","java.lang.String"));
racine.addContent(methodeAjouterUnitéObtenue);
Element modifier = new Element ("methode_existe");
modifier.setAttribute("nom","modifier");
modifier.addContent(new Element("parametre").setAttribute("type","java.lang.String"));
modifier.addContent(new Element("parametre").setAttribute("type","java.lang.String"));
racine.addContent(modifier);
Element methodeUnitésObtenues = new Element ("methode_existe");
methodeUnitésObtenues.setAttribute("nom","unitésObtenues");
racine.addContent(methodeUnitésObtenues);
PropriétésEnXML props = new PropriétésEnXMLImpl();
ListeDePropriétésI<Class<?>> liste = props.arbreXMLEnListe(racine);
assertTrue(liste.contient(new MéthodeExiste("ajouterUnitéObtenue", String.class)));
assertTrue(liste.contient(new MéthodeExiste("modifier",String.class, String.class)));
assertTrue(liste.contient(new MéthodeExiste("unitésObtenues")));
Document documentXML = new Document( racine,
new DocType("proprietes","proprietes.dtd"));
XMLOutputter out = new XMLOutputter(Format.getPrettyFormat());
out.output(documentXML,System.out);
}
Ci-dessous le document xml affiché sur la console, à la suite de l’exécution de cette méthode de test :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE proprietes SYSTEM "proprietes.dtd">
<proprietes>
<methode_existe nom="ajouterUnitéObtenue">
<parametre type="java.lang.String" />
</methode_existe>
<methode_existe nom="modifier">
<parametre type="java.lang.String" />
<parametre type="java.lang.String" />
</methode_existe>
<methode_existe nom="unitésObtenues" />
</proprietes>
Une autre méthode extraite de la classe de tests TestSérialisationXML
public void testEcritureEtLecture() throws Exception {
// obtention de la liste des propriétés liste1 à partir d’une classe ListeDePropriétésI<Class<?>> liste1 ;
liste1 = Utilitaires.obtenirLesPropriétés(java.util.ArrayList.class);
PropriétésEnXML props = new PropriétésEnXMLImpl();
// Obtention d’un arbre XML à partir de la liste1 Element arbre = props.listeEnArbreXML(liste1);
// Obtention d’une liste de propriétés à partir de cet arbre XML arbre ListeDePropriétésI<Class<?>> liste = props.arbreXMLEnListe(arbre);
// les deux listes obtenues liste et liste1 doivent être égales assertTrue(liste.equals(liste1));
}
Question3)
Ecrire une implémentation complète de la classe PropriétésEnXMLImpl.
package question3;
import question1.*;
import question2.*;
import question3.*;
import org.jdom.*;
import java.util.*;
public class PropriétésEnXMLImpl implements PropriétésEnXML{
public Element listeEnArbreXML(ListeDePropriétésI<Class<?>> liste){
Element racine = new Element("proprietes");
for(PropriétéI<Class<?>> p : liste){
if(p instanceof MéthodeExiste){
MéthodeExiste m = (MéthodeExiste) p;
Element elt = new
Element("methode_existe").setAttribute("nom",m.nom());
for(Class<?> t : m.typeDesParamètres()){
elt.addContent(new
Element("parametre").setAttribute("type",t.getName()));
}
racine.addContent(elt);
} }
return racine;
}
public ListeDePropriétésI<Class<?>> arbreXMLEnListe(Element arbreXML){
ListeDePropriétésI<Class<?>> liste = new ListeDePropriétés<Class<?>>();
List children = arbreXML.getChildren();
for (Object o : children) { Element el = (Element)o;
List listeClasse = el.getChildren();
Class<?>[] classArray = new Class<?>[listeClasse.size()];
for (int i = 0; i<listeClasse.size(); i++) { Element classe = (Element)listeClasse.get(i);
try {
String type = classe.getAttributeValue("type");
if(primitif.get(type) != null) // c'est un type primitif classArray[i] = primitif.get(type);
else
classArray[i] = Class.forName(type);
} catch(Exception e) {
throw new RuntimeException(e);
} }
PropriétéI<Class<?>> prop = new
MéthodeExiste(el.getAttributeValue("nom"), classArray);
liste.ajouter(prop);
}
return liste;
}
private static Map<String,Class<?>> primitif;
static{
primitif = new HashMap<String,Class<?>>();
primitif.put("int",Integer.TYPE);
primitif.put("long",Long.TYPE);
primitif.put("short",Short.TYPE);
primitif.put("byte",Byte.TYPE);
primitif.put("char",Character.TYPE);
primitif.put("double",Double.TYPE);
primitif.put("float",Float.TYPE);
} }
Question4 (4 points) : Interface graphique
Nous définissons maintenant une interface graphique en Swing qui permet de vérifier qu’une classe possède bien les propriétés issues d’un modèle.
Exemple 1) Le modèle est la classe AbstractAuditeur, la classe testée est la classe AuditeurDuCnam
L’appui sur le bouton « vérifier » engendre cette fenêtre de résultats
Exemple 2) Le modèle est la classe java.util.AbstractList, la classe testée est la classe java.util.ArrayList
L’appui sur le bouton « vérifier » engendre cette fenêtre de résultats
Ci-dessous, la classe correspondant à cette interface graphique
public class VérificateurSwing extends JFrame {
private JTextField nomClasse = new JTextField("question2.AuditeurDuCnam", 20);
private JTextField modeleClasse = new JTextField("question2.AbstractAuditeur", 20);
private JButton bVerifier = new JButton("vérifier");
//private JButton bQuitter = new JButton("Quitter");
private JTextArea resultats = new JTextArea(10, 40);
public VérificateurSwing() { super("Vérificateur");
JTabbedPane onglets = new JTabbedPane();
this.getContentPane().add(onglets);
JPanel parametres = new JPanel(new BorderLayout());
onglets.addTab("paramètres", parametres);
// Construire la partie supérieure
JPanel saisies = new JPanel(new GridLayout(2, 2));
saisies.add(new JLabel("Classe à vérifier : "));
saisies.add(nomClasse);
saisies.add(new JLabel("Modèle de classe : "));
saisies.add(modeleClasse);
parametres.add(saisies, BorderLayout.NORTH);
// Construire les boutons de commandes inférieurs JPanel boutons = new JPanel();
boutons.setLayout(new FlowLayout());
boutons.add(bVerifier);
//boutons.add(bQuitter);
parametres.add(boutons, BorderLayout.SOUTH);
// Construire l’onglet résultat
onglets.addTab("résultats", new JScrollPane(resultats));
bVerifier.addActionListener( /* à completer */); // à recopier sur votre copie
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
this.setVisible(true);
}
public static void main(String[] args) { new VérificateurSwing();
} }
Question4)
Complétez cette application afin que le bouton « vérifier » soit actif. La méthode setText(String) permet d’afficher un texte dans un onglet, par exemple, pour l’onglet résultats : resultats.setText("les Propriétés :
\n");
bVerifier.addActionListener(new ActionVerifier());
private class ActionVerifier implements ActionListener{
public void actionPerformed(ActionEvent ae){
try{
Class<?> modèle = Class.forName(modeleClasse.getText());
Class<?> laClasse = Class.forName(nomClasse.getText());
ListeDePropriétésI<Class<?>> liste1 = Utilitaires.obtenirLesPropriétés(modèle);
String res = "";
for(PropriétéI<Class<?>> p : liste1){
if(p.estSatisfaite(laClasse)){
res += p.toString() + " : oui.\n";
}else{
res += p.toString() + " : non.\n";
} }
resultats.setText("les Propriétés : \n" + res);
}catch(Exception e){
resultats.setText("exception : \n" + e.toString());
} } }