III
Documentation
1. Analyse du projet
1.1 Le langage de programmation
1.2 Les phases de développement
1.2.1 Phase 1 : découverte du monde financier et apprentissage du langage JAVA
1.2.1.1 L'AWT (Abstract Window Toolkit)
1.2.1.2 Programmation réseau
1.2.2 Phase 2 : analyse objet
1.2.3 Phase 3 : l'interface
1.2.3.1 Les boutons-images
1.2.3.2 Les zones texte
1.2.3.3 Les écrans
1.2.4 Phase 4 : Fonctionnement du programme
2. L'implémentation
2.1 Le fichier HTML
2.2 Répartition des fichiers sur le serveur
2.3 La structure du programme
2.3.1 Initialisation de l'applet
2.3.1.1 La construction des masques d'écran (orienté objet)
2.3.1.1.1 La classe "Masque" :
2.3.1.1.2 L'interface "Retour" :
2.3.1.1.3 Le programme "CyberSurf" :
2.3.1.1.4 La classe "MasqueBilan" :
2.3.1.2 La construction des masques d'écran (non orienté objet)
2.3.1.3 Chargement des images
2.3.2 Gestion des événements
2.3.2.1 Les masques orientés objets
2.3.2.2 Les masques non orientés objet
2.3.3 Procédures d'affichage des masques
2.3.3.1 Les masques d'écran orientés objets
2.3.3.2 Les masques d'écran non orientés objets
2.3.4 Les procédures appelées par les évènements
3. Les outils de développements
3.1 Le JDK (Java Developpement Kit)
3.2 Symantec Visual Café
3.3 Autres
3.4 Les browsers
3.4.1 Ms Internet Explorer
3.4.2 Netscape Navigator
L'inconvénient majeur des logiciels de simulation d'entreprise existants est de n'être disponibles que sur les ordinateurs sur lesquels ils ont été installés.
Dans l'idée d'un enseignement à distance, il est souhaitable de disposer de logiciels pouvant être téléchargés par l'utilisateur. Le réseau mondial Internet permet aujourd'hui à tout utilisateur de se connecter à distance à un serveur, indépendamment de la plate-forme qu'il utilise. C'est dans cet esprit d'indépendance de la plate-forme matériel qu'a été développé le langage Java. Les programmes Java se téléchargent et s'exécutent automatiquement sur toute plate-forme matérielle, aussi bien sur PC que sur Macintosh que sur système Unix. Les problèmes de maintenance, d'actualisation et d'installation du logiciel n'existent plus pour l'utilisateur, car le programme étant téléchargé du serveur, l'utilisateur disposera toujours de la dernière version sans devoir l'installer sur son ordinateur.
Dans cette phase, une analyse de la construction des documents financiers BILAN et COMPTE DE RESULTAT devait être éclairci au niveau de leur fonctionnement pour ne pas faire d'impasse lors de la programmation. Bien que simple au premier abord, de multiples exceptions ont du être prises en compte. Une simulation à la main a révélé un nombre important de cas dont l'absence aurait entraîné un dysfonctionnement du programme final.
Ce traitement des exceptions se retrouve dans le programme par une série de tests effectués avant chaque validation de décision.
La découverte du langage Java s'est faite à partir de plusieurs livres (voir bibliographie) et des exemples livrés avec le kit de développement de Sun. La syntaxe étant proche du C++, le plus important a été l'apprentissage de la programmation de l'interface graphique et de la communication réseau.
Tous les systèmes de fenêtres actuels sont basés sur le modèle WIMP (windows, icons, menus, pointer) développé au début des années quatre-vingts dans les instituts de recherche du PARC Xerox. Au cours des années, ils ont suivi une évolution différente, et on retrouve aujourd'hui des différences importantes. Ces différences se situent non seulement dans l'aspect extérieur et la manipulation ("Look and Feel"), mais aussi dans les fonctions évoluées du système (raccourci clavier, système d'aide, manipulation directe ("Drag and Drop")). En vue de l'importance des systèmes de fenêtre, l'éditeur de Java (Sun) a décidé que Java disposerait également d'une telle interface.
L'interface graphique des programmes Java est livrée dans la bibliothèque (appelée dans la terminologie de JAVA "packages")java.awt regroupant les classes nécessaires à la manipulation d'objets graphiques de base (lignes, rectangles, polygones, ...) et les classes nécessaires à la construction d'interfaces graphiques (menues, fenêtres, ascenseurs,...).
Cette bibliothèque est appelée AWT (Abstract Window Toolkit).
L'AWT devait également tenir compte du principal objectif de Java, qui est la portabilité. La bibliothèque graphique garantit que les interfaces développées fonctionnent sur tout ordinateur possédant un système graphique et une machine virtuelle Java.
Tous les éléments de l'AWT sont implémentés en tant qu'objets et permettent une manipulation semblable aux widgets connus de X-Windows et Motif. Les méthodes de positionnement d'objets graphique sont basées sur le système cartésien des pixels (points images). Il existe deux modes d'utilisation de positionnement des objets graphiques. Dans le premier mode, les objets peuvent être placés en relatif par rapport à l'espace disponible de l'application ou de zones de l'applet. Le deuxième mode consiste à définir la position de tout objet par ses coordonnées. C'est ce deuxième mode qui a été utilisé dans le programme, car l'aspect de l'interface du premier mode n'est pas encore assez évolué. Ainsi, lorsque la fenêtre de l'application change de dimension, les objets graphiques inclus dans l'application ne s'adaptent pas automatiquement. Ces changements automatiques doivent faire partie de l'interface et non du programme utilisant l'interface. Il aurait certes été intéressant de programmer une interface permettant ces manipulations, mais cela aurait dépassé les contraintes de temps et de thème du projet.
Un des atouts de JAVA est la programmation réseau. Les classes permettant la communication sont regroupées dans la bibliothèque "java.net". Elles facilitent considérablement la programmation de socket et de communication HTTP.
Cette simplicité de la programmation réseau ainsi que l'utilisation de threads (exécution simultanée de plusieurs processus parallèles) a abouti à trois modèles de programme concevable :
(a) Terminal JAVA
Dans ce modèle, le client JAVA ne s'occupe que de l'affichage des données et de l'interaction directe. Toute la logique du programme (réaction sur les interactions, calcul, ...) se trouve du côté serveur. L'avantage est d'avoir un client très "mince" et un serveur qui contrôle toutes les actions et données du client.
Une première implémentation a révélé certains points faibles. Du côté serveur se trouve une application Java en attente d'un Client. Le client est une applet Java intégrée dans une page Web. Lorsque le client démarre, il fait une requête au serveur Java. Celui-ci lance alors un thread contenant une socket dédiée à ce client. Toute la communication entre le client et le thread du client se fait alors par cette socket. On trouvera en annexe [A] les implémentation du serveur et du client.

Figure 1 : Modèle "Terminal JAVA"
Le serveur :
Le serveur attend une requête sur un port, puis crée une instance de la classe ThreadSocket.
"ThreadSocket" est une classe héritée de thread et contenant une socket. Il suffit d'écrire sur le flux (pipe) out pour envoyer des données au client et de lire sur le flux in pour recevoir les données.
Le client :
Le client envoie une requête au serveur. Si celui-ci répond, une socket est créé et le client peut envoyer des données sur le flux out et en recevoir sur le flux in.
Deux problèmes se sont posés :
La solution serait de remplacer la communication socket par une communication HTTP, mais le problème du nombre important de données circulant sur le réseau reste le même.
(b) Serveur de stockage des données
Dans ce modèle, le serveur sert à stocker les données du client. La communication ne se fait qu'au lancement du client pour charger les données et lors de la fin du client pour renvoyer les données au serveur. Le client peut donc interrompre son programme et continuer plus tard. Toute la logique du programme se trouve du côté client. La communication se fait par HTTP pour ne pas se heurter aux problèmes mentionnés ci-dessus.

Figure 2 : Modèle "Serveur stockage de données"
Le stockage de données sous JAVA est facilement réalisable. Puisque JAVA reprend le concept de flux de données (pipes) de Unix, il suffit de créer un flux vers un fichier et d'y envoyer les données. De plus JAVA introduit le concept de sérialisation d'objet. Il s'agit d'un mécanisme qui transforme toutes les informations d'un objet (ainsi que les références vers des objets qu'il contient) sous forme de flux d'octets. L'objet peut alors être détruit (ou sauvegardé dans un fichier). Lors de la récupération des informations, l'objet est créé identique à l'objet initial. Ce mécanisme rend transparent les différences entre la mémoire vive et dure et est la base d'objets persistants qui existent aussi en dehors de l'interpréteur JAVA. Les objets doivent implémenter le mécanisme de "sérialisation" (voir annexe [B]) .
L'objet sérialisé peut alors être envoyé comme un type de base dans un flux vers le fichier et ainsi être stocké.
(c) Client autonome
Dans ce modèle, le client JAVA est téléchargé du serveur Web et le programme s'exécute sans l'aide d'un serveur. Les mesures de sécurité de JAVA interdisent à un applet l'accès au disque dur du client. Il n'y a donc pas de moyen pour stocker les données. Lorsque le client se termine, toutes les données sont perdues. Comme pour le deuxième modèle, la taille de l'applet "Client" est considérable et ralenti le chargement du programme.

Figure 3 : Modèle "Client autonome"
En raison de la durée limitée du stage, c'est ce troisième modèle qui a été choisi, mais l'implémentation des classes et la structure du programme tient compte des avantages de sérialisation d'objet du modèle (b). Le programme est donc facilement extensible vers le modèle (b) et un serveur montrant le principe a été implémenté.
Le bilan, le compte de résultat et l'entreprise sont des objets du monde réel pour lesquels il semble approprié de créer des classes au sens du développement orienté objet. Les données initiales ont également été regroupées en une seule classe pour faciliter leur manipulation.

Figure 4 : classes de bases (notation de Booch '94)
Détail des classes avec leurs attributs :

Figure 5 : la classe "Entreprise"

Figure 6 : la classe "Bilan"

Figure 7 : la classe "CompteResultat"

Figure 8 : la classe "DonneesInitiales"
Toutes ces classes implémentent également le mécanisme de sérialisation d'objet (voir 1.2.1.2 (b) ). Pour sérialiser une instance d'une classe, la classe doit implémenter les méthodes readObject et writeObject.
Le code des classes de base se trouve dans les annexes [C] [D] et [E].
Une contrainte importante pour la réalisation du programme a été de concevoir une interface intuitive et d'optique agréable, le programme étant en concurrence à des produits commerciaux (écrit pour une plate-forme spécifique) disposant d'interfaces graphiques évoluées.
L'AWT (Abstract Window Toolkit) montre certains défauts de jeunesse et semble être inachevé par endroits comparé à d'autres bibliothèques d'interfaces graphiques (Motif, X-Windows, MFC). Il manque notamment des boutons permettant d'afficher une image (par exemple en tant qu'icône) et une classe affichant du texte et interceptant les événements de l'utilisateur.
L'AWT ne contient que des boutons comportant du texte, ce qui n'est pas suffisant pour l'interface souhaitée. Pour résoudre ce problème, une classe ImageCanvas [F] a été implémentée. Il s'agit d'une classe dérivée de la classe Canvas qui est un espace d'écran pouvant enregistrer des événements. La classe ImageCanvas contient en outre une image. Un clic (événement "mouseUp") sur cette image peut donc être enregistré et traité.
Le code de la classe ImageCanvas se trouve dans l'annexe [F].
Lors de la construction des documents comptables l'utilisateur devra désigner un endroit précis dans le bilan ou dans le compte de résultat. La classe standard du AWT pour afficher du texte - la classe "Label" - n'enregistre pas les click souris, et ne peut donc pas être utilisée pour ce besoin. De même que pour la classe ImageCanvas, une classe TextCanvas [G] a été implémenté, contenant du texte. L'héritage de la classe Canvas permet ici également d'intercepter les événement souris sur le texte.
Le code de la classe TextCanvas se trouve dans l'annexe [G].

Figure 9 : les classes "TextCanvas" et "ImageCanvas"
L'écran du programme est divisé en deux parties. A gauche se trouve une liste affichant des informations (nom de l'entreprise, année actuelle) et des icônes (implémenté comme ImageCanvas) que l'on verra tout au long du programme (Quitter, ...).
A droite se trouve la zone principale, dans laquelle se déroulera l'application. La dimension de l'applet est de 600x400 pixels pour permettre d'utiliser également l'applet sur des écrans de petites dimensions.
La zone principal
La zone principale contient les masques d'écran. La zone correspond à une pile pouvant contenir des masques. La méthode add de la pile permet d'ajouter un masque à la pile. La méthode show de la pile permet de mettre un masque au premier plan. L'implémentation et le fonctionnement est décrit dans 2.3.1.2.

Figure 10 : L'écran du programme "CyberSurf"
Les masques d'écran (masqueBilan, masqueCompte, masqueInfo, ...) sont hérités de la classe Masque qui elle-même hérite de la classe Panel (zone d'espace pouvant contenir des objets). Les actions sur un masque doivent être transmises au programme principal. Si les masques d'écran héritaient du programme principal, ils pourraient faire appel à la méthode de gestion des événements du programme principal par la commande super.handleEvent()(super est une référence vers la classe de base d'une classe héritée). Or, ils ne peuvent plus hériter du programme principal, car ils héritent déjà de la classe Panel, et le mécanisme de l'héritage multiple n'existe pas dans JAVA.

Figure 11 : les masques d'écran
La solution est de créer une interface. Une interface est une déclaration de méthode sans leur implémentation (méthodes abstraites). L'implémentation est réalisée dans une autre classe C (en pointillés dans "Figure 12"). Une variable du type de l'interface peut alors faire appel aux méthodes de la classe C définie dans l'interface, mais pas aux autres méthodes de la classe C. Une classe D qui contient l'interface (schématisé par le trait avec le point dans Figure 12), pourra faire appel aux méthodes de C par l'interface.

Figure 12 : utilisation d'interface
Dans le cas présent, une interface de nom "Retour" est créée, qui contient la méthode abstraite Retour(événement, nomMasque, nomObjet). Cette méthode est implémentée dans la classe du programme principal ("CyberSurf"). La classe "Masque" (classe abstraite) qui hérite de Panel contient une référence sur un objet de l'interface "Retour". Elle peut donc faire appel à la méthode Retour(événement, nomMasque, nomObjet) dont l'implémentation se trouve dans la classe "CyberSurf".

Figure 13 : l'interface "Retour"
Puisque les masques (masqueBilan, masqueInfo,...) héritent de la classe "Masque", ils disposent également de la méthode Retour(événement, nomMasque, nomObjet) de l'interface Retour. L'implémentation se trouvant dans la classe "CyberSurf", l'événement est ainsi transmit au programme principal. Les événements sont traités dans l'implémentation de la méthode "Retour" se trouvant dans "CyberSurf".
Le mécanisme d'interface n'ayant été découvert qu'en cours de développement, certains masques ne sont pas implémentés de cette manière orientée objets. Dans ce cas, les masques sont directement intégrés dans le programme principal "CyberSurf". Il n'est alors plus nécessaire de passer par une interface, puisque la gestion des événement est la même pour le programme "CyberSurf" et les masques.
Les deux implémentations sont décrites dans 2.3.1.1.
Le masque d'écran dans lequel se trouve l'utilisateur à un moment donné est affiché dans la zone principale. Les masques d'écran existant sont :
Un masque de bilan et un masque de compte de résultat permettent à l'utilisateur de construire et de visualiser les documents financiers lors des étapes de construction des documents.
Des masques d'aide et d'information guident l'utilisateur tout au long de sa démarche.
Les masques principaux sont représentés dans les annexes [K] à [P].
Les trois étapes du programme définies dans les objectifs pédagogiques se composent chacune d'une ou plusieurs années. La progression logique pour une année donnée est schématisée dans l'image suivante en utilisant les écrans définis dans 1.2.3.3.

Figure 14 : schéma de progression pour une année
Les principales étapes du cycle de progression d'une année sont :
Description des étapes d'une année :
L'applet est contenu dans une page HTML.
<html>
<head>
<title>Autogenerated HTML</title>
<meta name="Author" content="Jean-Marc Autexier">
<meta name="Description" content="Homepage for the WEB/JAVA
enterprise simulation CyberSurf">
<meta name="GENERATOR" content="Microsoft FrontPage 1.1">
<meta name="Keywords" content="CyberSurf, ENIM, Autexier, JAVA">
</head>
<body bgcolor="#FFFFFF">
<APPLET CODE = "CyberSurf.class"
WIDTH = "600"
HEIGHT = "400"
NAME = "CyberSurf/JAVA"
ALT = "Your browser doesn't support Java-Applets"
ALIGN = "middle">
<PARAM NAME = "Terminer"
VALUE = "http://servmultimedia/enim/enim.htm">
</APPLET>
</body>
</html>
Les informations meta servent aux machines de recherche du Web à classifier la page. La dimension de l'applet est fixée par les attributs WIDTH et HEIGHT. NAME contient le nom de l'applet, ALT est le texte affiché dans les browser qui ne peuvent pas éxécuter du code JAVA. Le paramètre "Terminer" est passé à l'applet et lui indique la page qui doit être chargée à la fin du programme.
Les fichiers du programme se répartissent de la façon suivante sur le serveur (Microsoft Internet Information Server).
!
!--- inetpub
!--- wwwroot
!
!--- CyberSurf
!
!--- Debut.html (objectifs pédagogiques)
!--- CyberSurf.html ( fichier HTML contenant l'applet)
!--- CyberSurf.class (l'applet)
!
!--- images
! !
! !--- *.gif (images de l'applet)
!
!--- utils
!
!--- *.class (fichier utilitaire de l'applet)
Le programme est divisé en 4 parties :
Les masques d'écran sont construit lors de l'initialisation de l'applet. La classe abstraite "Masque" hérite de la classe "Panel" (zone pouvant contenir des objets graphiques). Elle contient également un objet de type "Retour". La classe "Retour" est une interface possédant la méthode Retour(événement, nomMasque, nomObjet).
La classe de base de tous les masques d'écran définit un espace de dimension 515x400. Elle contient les attributs masqueNom (nom du masque) et parent. Parent est une référence sur l'interface Retour et indique l'espace dans lequel le masque est contenu.
import utils.Retour ;
// La classe masque est abstraite (on ne peut pas créer d'objet)
// Elle hérite de Panel (zone pouvant contenir des objets graphiques)
abstract class Masque extends Panel
{
private String masqueNom ; // Nom du masque
private Retour parent ; // Référence sur l'interface
// "Retour"
// Constructeur. Le nom et l'interface sont passés en paramètre
public Masque(String masqueNom, Retour parent)
{
super() ;
this.parent = parent ;
this.masqueNom = masqueNom ;
this.setLayout(null) ;
this.resize(515,400) ;
this.show() ;
this.resize(515,400) ; // Netscape
}
// Méthode abstraite handleEvent devant être inplémentée
// par les classes filles
abstract public boolean handleEvent( Event evt) ;
// Les accesseurs à masqueNom et parent
}
public interface Retour
{
// la méthode retour doit être implémentée
public boolean Retour(Event evt, String windowName, String objetc) ;
}
La méthode Retour de l'interface est implémenté dans le programme principal "CyberSurf".
import utils.Retour ; // Importation de l'interface "Retour"
import MasqueBilan ;
// la classe CyberSurf hérite de "Applet" et implémente
// l'interface "Retour"
public class CyberSurf extends Applet implements Retour
{
// Initialisation
// La création des masques et la gestion des
// événements des masques d'écran est décrite dans 2.3.2.
Entreprise entreprise = new Entreprise() ;
MasqueBilan masqueBilan = new MasqueBilan("MasqueBilan",
this,
entreprise) ;
// Le programme
...
// Implémentation de l'interface Retour. Les objets de la classe
// "Retour" peuvent faire appel à cet méthode.
public boolean Retour(Event evt, String masqueNom, String objet)
{
// traitement des événements
...
return super.handleEvent(evt) ;
}
Les masques de l'application héritent de la classe Masque. Il peuvent faire appel à la méthode Retour(événement, nomMasque, nomObjet) de l'interface "Retour" en appelant l'objet contenu dans la classe "Masque".
La classe "MasqueBilan" hérite de "Masque". Elle contient une référence sur l'entreprise dont elle doit afficher le bilan.
import utils.Retour ;
import utils.Entreprise ;
// La classe "MasqueBilan" hérite de "Masque"
public class MasqueBilan extends Masque
{
// Référence sur l'entreprise
private Entreprise entreprise ;
// Le constructeur. Le nom du masque, l'interface retour
// et la référence sur l'entreprise sont passés en
// paramètres
public MasqueBilan(String nom, Retour parent, Entreprise entreprise)
{
// Appel du constructeur de "Masque"
super(nom, parent) ;
this.entreprise = entreprise ;
// ...
// implémentation de l'interface du masque de l'écran
// ...
}
public void Actualiser()
{
// Les champs du masque de bilan sont actualisés par les
// données contenues dans le bilan de l'entreprise
}
// La méthode de gestion des événements
public boolean handleEvent(Event evt)
{
// elle appelle la méthode Retour de l'interface contenu
// dans "Masque" (le parent de la classe). Elle passe en outre
// l'événement enregistré, le nom du masque ainsi que
// le nom de l'objet sur lequel l'action à eu lieu
return super.getMasqueParent().Retour( evt, super.getMasqueNom(), nomObjet) ;
}
}
Le mécanisme d'interface n'ayant été découvert qu'en cours de développement, certain masques d'écran ne suivent pas cette implémentation objet. Dans ce cas, les masques d'écran font partie du programme principal "CyberSurf". Cela permet d'intercepter directement les événements sur les masques dans la gestion des événements du programme principal.
À chaque masque correspond une fonction nomMasqueGUI() qui initialise et place les objets graphiques du masque. Les masques sont ensuite ajoutés à la zone principale.
La zone principalle correspond à une pile de masques d'écran. La commande show(principal, "NOM") permet de placer le masque de nom "NOM" au premier plan.
exemple : construction d'une pile de masques d'écran
Le panneau principal est défini en tant que CardLayout. Il sert ainsi de pile de masques d'écran.
Panel principal = new java.awt.Panel() ; principal.setLayout(new CardLayout()) ;
Un nouveau masque est ajouté avec la fonction add de la pile. Dans l'exemple qui suit, le masque d'écran "ecranFonctionnement est créé et construit. Il est ensuite ajouté à la pile de masque "principal" sous le nom "Fonctionnement".
Panel ecranFonctionnement = new java.awt.Panel() ;
// ... Constrution des objets du masque
// Ajout du masque à la pile de masques "principal"
principal.add("Fonctionnement", ecranFonctionnement) ;
Le masque d'écran "Fonctionnement" peut être mis au premier plan par la fonction show.
cl.show(principal, "Fonctionnement") ;
Remarque : cl est une classe de type "pile de masque" possédant la fonction show().
Les images sont téléchargées par la fonction getImage. L'emplacement des images est déterminé par l'adresse d'où a été téléchargé l'applet (getCodeBase renvoie l'URL de l'applet) et le chemin relatif à l'image. L'image est affectée à un objet de la classe ImageCanvas défini précédemment (voir 1.2.3.1).
exemple : chargement de l'image "terminer" et affectation à l'imageCanvas terminerCanvas.
// L'image est chargée et affectée à l'image "terminer" Image terminer = getImage(getCodeBase(), "images/terminer.gif") ; // Pour utiliser l'image en tant que bouton-image, elle est // enregistrée dans un objet de la classe ImageCanvas ImageCanvas terminerCanvas = new ImageCanvas(terminer) ;
Pour chaque image, une connexion HTTP est effectuée. Cette méthode est peu économe, car la taille du protocole nécessaire à la connexion HTTP est importante au regard de la taille en octets de l'image. Pour remédier à ce problème, les images sont stockées en une seule grande image. Il suffit alors de ne télécharger que cette image. Les images initiales sont alors restituées du côté client par découpage de l'image téléchargée. Le découpage se fait à l'aide de la méthode drawImage.
La méthode drawImage sert d'habitude à placer une image dans un 'grand' espace graphique. Or si l'espace graphique est inférieur à l'image, on peut s'en servir pour découper l'image. On crée pour cela une image plus petite que l'image à découper. L'image à découper est alors affichée (drawImage) sur la petite image (avec les translations nécessaires pour obtenir dans la petite image la zone voulue).
exemple : une icône de dimension 20x20 doit être découpée de l'image contenant toutes les icônes. L'icône désirée se trouver aux coordonnées (100,80).
// téléchargement de l'image contenant les icônes Image icones = getImage(getCodeBase(), "images/icones.gif") ; // Création de l'image correspondant à l'icône 'Terminer' // de dimension 20x20 Image terminer = createImage(20,20) ; // On initialise un contexte graphique propre à l'icône Graphics g = terminer.getGraphics() ; // Le contexte graphique est translaté vers le coordonnées // contenant l'icône de la grande image g.translate(-100, -80) // L'image contenant les icônes est affiché dans le contexte // graphique g.drawImage(icones, 0, 0, null) ; // L'icône 'terminer' contient maintenant l'image désirée et // peut être affichée dans le contexte graphique (gf) de la fenêtre // aux coordonnées (x,y) gf.drawImage( terminer, x, y, null) ;
Les événements interceptés dans les masques sont traités dans la méthode handleEvent. La méthode fait appel à la méthode Retour de l'interface "Retour" en ajoutant à l'événement le nom du masque dans lequel à eu lieu l'événement ainsi que le nom de l'objet graphique. Les événements propre aux masques qui n'ont aucun effet sur la logique du programme sont interceptés et traités dans le masque. Par exemple, le changement optique d'un bouton sur lequel l'utilisateur a appuyé se fait partie des réactions du masque. Les conséquences de l'appui sur le bouton font partie de la logique du programme et sont transmis à celui-ci.
Exemple : Implémentation de la méthode de gestion des événements dans les masques d'écran
public boolean handleEvent(Event evt)
{
// Réaction du masque sur un événement
if ( evt.id == Event.ACTION_EVENT) ||
( evt.target == capitalField)
{
// Réaction du masque, par exemple afficher le bouton
// enfoncé
reaction() ;
}
...
// Réaction sur un événement qui doit être tansmis au programme
// principal
if ( evt.target == capital )
{
// Appel à la méthode Retour de l'interface du parent
// du masque. Le nom du masque et le nom de l'objet sont
// ajoutés à l'événement
return super.getMasqueParent().Retour(evt, super.getMasqueNom(), "capital" ) ;
}
...
// A défaut, l'événement est transmis au programme principal sans le nom de l'objet.
return super.getMasqueParent().Retour(evt, super.getMasqueNom(), "" ) ;
L'implémentation de la méthode Retour de l'interface "Retour" est réalisée dans le programme principal "CyberSurf". Le traitement de l'événement se fait en 5 étapes :
L'implémentation est schématisée ci-dessous :
// Implémentation de l'interface Retour
public boolean Retour(Event evt, String masqueNom, String objet)
{
//************************************
// Classemenet par type d'événement
//************************************
switch (evt.id)
{
case Event.MOUSE_UP :
//************************************
// Classement par masque
//************************************
if ( masqueNom.equals("MasqueInfo") )
{
//************************************
// Classement par année actuelle
//************************************
switch ( entreprise.getAnneeActuelle() )
{
case 0 :
//************************************
// Classement par étape actuelle
//************************************
switch ( entreprise.getEtape() )
{
case 1 :
//************************************
// Classement par objet
//************************************
if ( objet.equals("continuer") )
{
a0e1Bilan() ;
}
break ;
case 2 :
...
break ;
}
break ;
case 1 :
...
break ;
}
break ;
Le traitement des événements s'effectue sur quatre niveaux :
Chaque type d'événement est intercepté par une fonction correspondant à son type. Par exemple, l'entrée de la souris sur un objet de l'interface envoie un événement qui est intercepté par la fonction mouseEnter.
|
Evénement |
Fonction |
|
entrée de la souris |
mouseEnter |
|
sortie de la souris |
mouseExit |
|
appuie de la souris |
mouseDown |
|
relâchement de la souris |
mouseUp |
|
clic sur un bouton |
action |
Dans ces fonctions, l'événement est traité par une suite de branchement conditionnels imbriqués. Le premier branchement (switch) classe les événements selon l'année actuelle dans laquelle se trouve l'entreprise. De même, pour chaque année, les événements sont classés selon l'étape actuelle de la simulation. Enfin, les événements sont répartis par une suite d'instructions if en fonction de la cible (bouton, image, texte) de l'événement.
Pour rendre le programme plus lisible, les fonctions correspondant à la réaction du programme sur un événement se trouvent dans des fonctions à la fin du programme (voir 2.3.4), et non dans les branchements. Le dernier branchement fait appel à ces fonctions.
exemple : structure de la gestion des événement pour les événement du type "mouseUp"
//**************************************************************
// 2.3 Interaction mouseUp : les relâchements du bouton de la
// souris sont interceptés
//**************************************************************
public boolean mouseUp(Event evt, int x, int y)
{
// branchement conditionnel sur l'année actuelle
switch ( entreprise.getAnneeActuelle() )
{
//********************************************
// 2.3.0 Interaction mouseUp dans l'année 0
//********************************************
case 0 :
// branchement conditionnel sur l'étape
switch ( entreprise.getEtape() )
{
//*************************************
// 2.3.0.0 mouseUp, année 0, étape 0
//*************************************
case 0 :
// branchement conditionnel sur la cible
// de événement. La procédure correspondant
// à l'événement est appelée.
if (evt.target == commencerCanvas )
{
a0e0Action1() ; // Appel à la fonction correspondant à l'événement
}
break ;
//*************************************
// 2.3.0.1 mouseUp, année 0, étape 1
//*************************************
case 1 :
break ;
...
}
break ;
//********************************************
// 2.3.1 Interaction mouseUp dans l'année 1
//********************************************
case 1 : ...
}
}
Les classes de masques disposent de la méthode "Actualiser" qui remet à jour les objets graphiques du masque.
Exemple : Actualisation et affichage du masque "masqueBilan"
void masqueBilanActShow()
{
masqueBilan.Actualiser() ;
cl.show(card, "masqueBilan") ;
}
A chaque masque correspond une fonction qui actualise les champs du masque puis procède à l'affichage.
Le nom de la procédure d'un masque de nom "nomEcran" est "nomMasqueActShow()".
exemple : fonction d'affichage du masque compte de résultat
//**************************************************************
// 3.2 Actualisation et affichage de l'écran Compte de résultat
//**************************************************************
void compteActShow()
{
// Les valeurs du compte de résultats de l'entreprise sont placées dans
// les champs correspondants du masque de l'écran.
// La colonne des charges du compte de résultat
m21.setLabel(Integer.toString(entreprise.getCompteResultat().getMatieres())) ;
m22.setLabel(Integer.toString(entreprise.getCompteResultat().getFP())) ;
m23.setLabel(Integer.toString(entreprise.getCompteResultat().getDA())) ;
m24.setLabel(Integer.toString(entreprise.getCompteResultat().getRecherche())) ;
m25.setLabel(Integer.toString(entreprise.getCompteResultat().getPub())) ;
m26.setLabel(Integer.toString(entreprise.getCompteResultat().getMaintenance())) ;
m27.setLabel(Integer.toString(entreprise.getCompteResultat().getFF())) ;
m28.setLabel(Integer.toString(entreprise.getCompteResultat().getExploitation())) ;
m29.setLabel(Integer.toString(entreprise.getCompteResultat().getSOG())) ;
m30.setLabel(Integer.toString(entreprise.getCompteResultat().getSOC())) ;
m31.setLabel(Integer.toString(entreprise.getCompteResultat().getIB())) ;
m32.setLabel(Integer.toString(entreprise.getCompteResultat().getProfit())) ;
// La colonne des produits du compte de résultat
n11.setLabel(Integer.toString(entreprise.getCompteResultat().getVentes())) ;
n12.setLabel(Integer.toString(entreprise.getCompteResultat().getPE())) ;
n13.setLabel(Integer.toString(entreprise.getCompteResultat().getPS())) ;
n14.setLabel(Integer.toString(entreprise.getCompteResultat().getPerte())) ;
// Les informations qui ne doivent pas êtres affichées par défaut sont
// rendu invisible
aideChargesLine.hide() ;
aideProduitLine.hide() ;
// Les informations qui doivent êtres affichées par défaut sont rendu visible
charge.show() ;
produit.show() ;
// L'écran du compte de résulat est placé sur le haut de la pile
cl.show(card,"Compte") ;
}
Ces procédures sont appelées à partir des évènements interceptés dans la gestion des évènements. Les premières lettres de leur nom indiquent à quelle année et à quelle étape elle correspondent.
exemple : une procédure réagissant à un événement de l'étape 3 de l'année 1 commencera par "a1e3...".
C'est dans ces procédures que se trouve la logique de la simulation. Elles effectuent les vérifications et décident de la poursuite de la simulation.
exemple : au début de l'année, l'utilisateur prend les décisions. Le programme doit vérifier si les décisions sont valides et réagir en conséquence.
//****************************************************************************
// Année 1 étape 0
//****************************************************************************
void a1e0Test()
{
// Le champ d'entrée ne doit pas être vide
if (decision1Emprunt.getText().equals(""))
{
decision1Emprunt.setText("0") ;
}
if (decision1Unite.getText().equals(""))
{
decision1Unite.setText("0") ;
}
// Le montant de l'emprunt doit être positif ou nul, mais inférieur
// au capital de l'entreprise.
if ( Integer.parseInt(decision1Emprunt.getText()) < 0 )
{
// Le capital est inférieur à 0 -> message d'erreur
decision1Info.setText("Votre emprunt doit être positif ou nul") ;
}
else
{
if (Integer.parseInt(decision1Emprunt.getText()) >
entreprise.getBilanActuell().getCapital() )
{
// L'emprunt est supérieur au capital -> message d'erreur
decision1Info.setText("Vous pouvez emprunter au maximum 100% de votre
capital,soit " + entreprise.getBilanActuell().getCapital() + " KF") ;
}
else
{
// Le nombre d'unités de production doit être compris entre 0 et 8
if ( ( Integer.parseInt(decision1Unite.getText()) < 0 ) ||
(Integer.parseInt(decision1Unite.getText()) > 8) )
{
// Nbr. unités faux -> message d'erreur
decision1Info.setText("Vous pouvez acheter entre 0 et 8 unités de
production") ;
}
else
{
// Il faut tester si on peut acheter le nombre d'unités de production
// avec l'argent de la trésorie et de l'emprunt
int coutAchat = donneesInitiales.getPUP() *
Integer.parseInt(decision1Unite.getText()) ;
int argentLiquide = entreprise.getBilanActuell().getTresorie() +
Integer.parseInt(decision1Emprunt.getText()) ;
if ( coutAchat > argentLiquide )
{
// pas assez d'argent
decision1Info.setText("Vous ne disposez pas d'assez d'argent
pour acheter les unités.") ;
}
else
{
// Tout est en orde, on passe à la prochaine étape
entreprise.setEtape(1) ;
infoLine1.setText("Vous devez maintenant insérer les chiffres dans
le bilan.") ;
infoLine2.setText("") ;
infoLine3.setText("") ;
infoLine4.setText("Appuyer sur 'Continuer', puis choisissez") ;
infoLine5.setText("'Construction de documents comptables'.") ;
infoLine6.setText("") ;
infoLine7.setText("") ;
infoLine8.setText("") ;
infoShow() ;
}
}
}
}
Le JDK est le kit de développement livré par Sun Microsystem, le créateur du langage JAVA. Il contient entre autres le compilateur javac qui produit le code intermédiaire des programmes JAVA. Ce code peut être exécuté dans une machine virtuelle contenue soit dans un browser Web, soit dans la visionneuse d'applet (appletviewer) livré avec le JDK. Le programme javadoc génère à partir du code d'une application une documentation. Un exemple de page HTML généré pour l'interface Retour par javadoc se trouve dans l'annexe [Q]. On trouve également un débuggeur (jdb) et un désassembleur (javap).
La version 1.1 du JDK a été livré à partir de juin 1997. Elle corrige de nombreuses inconsistances de la version 1.0. En autre, le modèle de la gestion des événements à été changé. Un événement n'est plus envoyé à tous les objets, mais seulement aux objets qui implémente une gestion d'événement pour cet événement (modèle de délégation). La vitesse d'exécution d'un programme st ainsi nettement améliorée, puisque la communication entre les objets est limitée. Les browsers actuels ne supportent pas encore ce nouveau standard, ce qui compromet à écrire l'applet avec l'ancien modèle.
La version 1.1 introduit la notion d'applet 'untrusted' qui peuvent accéder aux ressources du client. Ainsi, il peuvent lire et écrire sur le disque du client ou bien utiliser le bloc-notes du système.
Symantec à été un des premiers éditeurs à proposer un outil de développement JAVA (Symantec Cafe). Actuellement, Symantec livre deux outils de développement pour JAVA, Symantec Cafe et Symantec Visual Cafe (tout les deux existant en version normale et professionnelle). Alors que le premier est un outil de développement 'classique', le deuxième permet la création interactive d'interfaces semblable à l'utilisation de Visual Basic. L'inconvénient est que le code généré automatiquement par l'outil de développement pour l'interface 'dessinée' est très illisible. De plus, les possibilités offertes par l'AWT ne sont que très peu exploitées et la création de l'interface se limite à placer des zones texte et des champs d'entrée. L'insertion d'image fait appel à des classes qui n'appartiennent pas à l'AWT, mais à une bibliothèque livrée avec Visual Cafe. Cela implique, que la bibliothèque doit être installée sur les clients qui utilisent l'applet, ce qui est contraire à l'idée de JAVA, selon laquelle, le programme est entièrement chargé du réseau et ne nécessite que la machine virtuelle.
Le compilateur et la visionneuse d'applet de Symantec Cafe sont beaucoup plus rapides que les outils livrés avec le JDK. Grâce à la gestion de projets de Cafe, le compilateur peut mieux gérer les classes appartenant à une application, et peut en conséquence seulement compiler les classes qui ont été modifiées. La visionneuse gagne en vitesse grâce à son compilateur 'Just in Time'. La compilation du code intermédiaire est effectuée lors du téléchargement, et non à la fin, comme c'est le cas de la visionneuse du JDK. Netscape a licensé la visionneuse de Symantec et l'utilise dans les nouvelles versions du Navigator.
D'autres outils de développement ont été testés parmi lesquels IMB Coda. La particularité de ce logiciel est que lui-même à été implémenté en JAVA. Il peut être utilisé dans un réseau hétérogène, le développeur retrouvant le même outil de développement aussi bien sur PC que sous Unix. L'inconvénient actuel de l'outil est sa vitesse d'exécution qui ne permet pas encore un travail fluide. Cet inconvénient devrait être résolu avec l'apparition de compilateur JAVA.
Pour exécuter des applets JAVA, le browser doit posséder une machine virtuelle JAVA. La machine virtuelle du browser de Microsoft dispose comme l'outil de développement de Symantec d'un compilateur 'Just in Time'. La vitesse d'exécution est comparable à celle de la visionneuse de Symantec. Malheureusement, le fonctionnement de la machine virtuelle n'est pas partout la même. Une différence est apparue en cours de développement. Ainsi, une nouvelle fenêtre n'est pas automatiquement placée au premier plan. Cela est d'autant plus agaçant parce que le langage JAVA ne dispose pas de fonction pour placer une fenêtre à l'avant. Il faut donc rechercher la fenêtre par les fonctions mises à disposition par le système d'exploitation (barre des tâches,...). Les machines virtuelles du JDK et de Symantec, ainsi que le browser de Netscape place une nouvelle fenêtre automatiquement au premier plan.
Netscape a décidé d'utiliser la visionneuse de Symantec dans ses nouvelles versions du Communicator. La visionneuse de Symantec se tient exactement aux standards de SUN et est par son compilateur 'Just in Time' très rapide. Cette visionneuse deviendra son doute le standard dans les produits de l'alliance SUN-Netscape-IBM-Oracle, puisque même SUN, l'inventeur du langage JAVA, l'utilise dans la toute dernière version 1.1.3 du JDK.