Les analyseurs SAX reposent sur l'interface XMLReader, laquelle a remplacé l'interface Parser dépréciée. De la même façon que pour les gestionnaires d'événements, l'interface XMLReader définit plusieurs méthodes auxquelles leurs implémentations doivent se conformer. Ces méthodes sont destinées à affecter ou à obtenir des gestionnaires d'événements, des caractéristiques et des propriétés concernant l'analyseur SAX courant. Deux autres méthodes particuliètrement importantes permettent d'analyser un document XML à partir de différentes sources comme une adresse URL ou une source d'entrée.

Bien évidemment, des analyseurs XML sont proposés par défaut au sein de la plateforme Java. Le paquetage org.xml.sax.helpers renferme les implémentations des interfaces du paquetage org.xml.sax. La classe XMLReaderFactory permet de créer un objet XMLReader qui sera capable d'analyser un document XML.

XMLReader analyseur = XMLReaderFactory.createXMLReader();

XMLReader analyseur = XMLReaderFactory.createXMLReader(
                               "org.apache.xerces.parsers.SAXParser");

XMLReader analyseur = XMLReaderFactory.createXMLReader(
                               "org.apache.crimson.parser.XMLReaderImpl");

La première méthode crée un analyseur s'appuyant sur la classe org.xml.sax.parser de la plateforme Java, tandis que les suivantes demandent la référence d'une classe spécialisée comme celle du module Xerces, Crimson ou autre.

Un autre moyen est proposé pour récupérer un analyseur XML. Le paquetage javax.xml.parsers présente deux classes très intéressantes : SAXParser et SAXParserFactory. Cette dernière constitue une fabrique d'objets SAXParser.

SAXParserFactory fabrique = SAXParserFactory.newInstance();
SAXParser analyseur = fabrique.newSAXParser();

La procédure de création d'analyseurs SAX passe par l'obtention d'une instance de la classe SAXParserFactory par l'intermédaire de la méthode de classe newInstance(). Puis, il suffit d'invoquer la méthode d'instance new SAXParser sur la fabrique pour récupérer un analyseur SAX.

Un objet SAXParserFactory prépare les caractéristiques et certaines propriétés des instances de la classe SAXParser. La validation par DTD ou par schéma XML est activée si la valeur true est passée à la méthode setValidating(). Un objet Schema du paquetage javax.xml.validation peut être affecté à l'objet par l'intermédaire de la méthode setSchema(). Le traitement des espaces de noms ou des inclusions XML est permis si les méthodes setNamespaceAware() et setXIncludeAware() reçoivent la valeur true.

fabrique.setValidating(true);
fabrique.setNamespaceAware(true);
fabrique.setXIncludeAware(true);

Enfin, des caractéristiques spéciales peuvent être activées ou désactivées selon les exigences techniques de l'application. Par exemple, les entités générales externes doivent elles être l'objet d'un événement spécifique si l'analyseur les rencontre ? Les listes d'attributs implémentent t'elles l'interface org.xml.sax.Attributes ou org.xml.sax.ext.Attributes2 ?

fabrique.setFeature(
                    "http://xml.org/sax/features/external-general-entities", true);
fabrique.setFeature(
                    "http://xml.org/sax/features/use-attributes2", false);

Un objet SAXParser encapsule un objet XMLReader. Le fonctionnement de cet analyseur est donc similaire à celui délivré par la fabrique XMLReaderFactory.

En ce qui concerne une instance de la classe XMLReader, l'affectation de caractéristiques et des propriétés s'effectuent directement à partir de l'analyseur et non de sa fabrique XMLReaderFactory. La validation d'un document XML par sa DTD, s'effectue via l'activation de la caractéristique http://xml.org/sax/features/validation. La validation par un schéma XML se réalise par l'intrmédaire des objets du paquetage javax.xml.validation.

import java.awt.Component;
import java.io.File;
import java.util.Iterator;
import java.util.TreeSet;

import javax.swing.AbstractAction;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.helpers.DefaultHandler;

/**
 * 

La classe BarreMenus représente la barre * de menus de l'application.

* @see javax.swing.JMenuBar */ public class BarreMenus extends JMenuBar { /** * Le champ adapts représente une collection * d'adaptateurs pour les menus et les outils de l'application. */ private AdaptateursMenus adapts; /** *

Le constructeur crée une instance de la classe * BarreMenus.

*/ public BarreMenus(AdaptateursMenus adapts) { super(); this.adapts = adapts; this.creerBarreMenus(); } /** *

La méthode creerBareMenu() initialise * et crée la barre de menus.

*/ private void creerBarreMenus() { DefaultHandler gestionnaire = new MenusHandler(this, adapts); SAXParserFactory factory = SAXParserFactory.newInstance(); try { SAXParser saxParser = factory.newSAXParser(); saxParser.parse(new File("menu.xml"), gestionnaire); } catch (Throwable t) { t.printStackTrace(); } } } import java.awt.event.KeyEvent; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.KeyStroke; import javax.swing.SwingConstants; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** *

La classe MenusHandler gère des documents XML * en vue de la configuration des menus de l'application.

* @see org.xml.sax.helpers.DefaultHandler */ public class MenusHandler extends DefaultHandler { /** * Le champ barre représente la barre de menus * de l'application. */ private BarreMenus barre; /** * Le champ adapts représente une collection de * gestionnaires d'événements affectés aux menus de l'application. */ private AdaptateursMenus adapts; /** * Le champ menu représente un menu de l'application. */ private JMenu menu; /** * Le champ représente un sous-menu de l'application. */ private JMenuItem item; /** * Le champ type indique le type de l'élément * rencontré durant l'analyse (MENU, ITEM, TEXT ou SCUT). */ private int type; /** * La constante MENU indique un élément XML * de type MENU (menu). */ private final int MENU = 0; /** * La constante MENU indique un élément XML * de type ITEM (sous-menu). */ private final int ITEM = 1; /** * La constante MENU indique un élément XML * de type TEXT. */ private final int TEXT = 2; /** * La constante MENU indique un élément XML * de type SCUT (raccourci clavier - Short-CUT). */ private final int SCUT = 3; /** *

Le constructeur crée une instance de la classe MenusHandler.

* @param barre représente la barre de menus de l'application. * @param adapts représente une collection de gestionnaires d'événements */ public MenusHandler(BarreMenus barre, AdaptateursMenus adapts) { super(); this.adapts = adapts; this.barre = barre; } //=========================================================== // Méhodes ContentHandler SAX //=========================================================== /** *

La méthode startDocument reçoit la notification du début du document XML.

* @throws SAXException est lancée lors d'une erreur durant l'analyse du document XML. * @see org.xml.sax.ContentHandler#startDocument() */ public void startDocument() throws SAXException { } /** *

La méthode endDocument reçoit la notification de la fin du document XML.

* @throws SAXException est lancée lors d'une erreur durant l'analyse du document XML. * @see org.xml.sax.ContentHandler#endDocument() */ public void endDocument() throws SAXException { } /** *

La méthode startElement reçoit la notification * indiquant le début d'un élément XML.

* @param namespaceURI représente l'espace de noms de l'élément. * @param lName représente le nom local de l'élément. * @param qName représente le nom qualifié de l'élément. * @param attrs représente le jeu d'attributs de l'élément. * @throws SAXException est lancée lors d'une erreur * durant l'analyse du document XML. * @see org.xml.sax.ContentHandler * #startElement(java.lang.String, java.lang.String, * java.lang.String, org.xml.sax.Attributes) */ public void startElement( String namespaceURI, String lName, String qName, Attributes attrs) throws SAXException { String name = lName; if (name == null || name.equals(Constantes.VIDE)) name = qName; if (qName.equalsIgnoreCase("name")){ type = MENU; } else if (qName.equalsIgnoreCase("item")){ type = ITEM; } else if (qName.equalsIgnoreCase("text")){ type = TEXT; } else if (qName.equalsIgnoreCase("shortcut"))){ type = SCUT; } else return; if (attrs != null) { String nom = null; for (int i = 0; i < attrs.getLength(); i++) { String attr = attrs.getLocalName(i); if (attr.equalsIgnoreCase("")) attr = attrs.getQName(i); if(attr.equalsIgnoreCase("id")){ if(this.adapts.containsKey(attrs.getValue(i)) && attrs.getValue(i).length() > 0){ String n = attrs.getValue(i); final AbstractAction elts = this.adapts.getAction(attrs.getValue(i)); if(elts == null) return; this.item.setAction(elts); this.item.setText(nom); this.item.setHorizontalTextPosition(SwingConstants.RIGHT); this.item.setBorder(BorderFactory.createEmptyBorder(3, -10, 3, 0)); } } else if(attr.equalsIgnoreCase("name")){ switch(type){ case MENU: JMenu menu = new JMenu(attrs.getValue(i)); this.barre.add(menu); this.menu = menu; break; case ITEM: if(attrs.getValue(i).equalsIgnoreCase("separator")) this.menu.addSeparator(); else{ nom = attrs.getValue(i); JMenuItem item = new JMenuItem(); this.item = item; this.menu.add(this.item); } break; } } } } } /** *

La méthode endElement() reçoit la notification indiquant * la fin d'un élément XML.

* @param namespaceURI représente l'espace de noms de l'élément. * @param lName représente le nom local de l'élément. * @param qName représente le nom qualifié de l'élément. * @throws SAXException est lancée lors d'une erreur durant * l'analyse du document XML. * @see org.xml.sax.ContentHandler# * endElement(java.lang.String, java.lang.String, java.lang.String) */ public void endElement( String namespaceURI, String lName, String qName, Attributes attrs) throws SAXException { } /** *

La méthode characters reçoit la notification * indiquant la présence d'une chaîne de caractères.

* @param buf représente un tableau des caractères trouvés. * @param offset représente le début de la chaîne de caractères. * @param len représente la taille de la chaîne de caractères. * @throws SAXException est lancée lors d'une erreur * durant l'analyse du document XML. * @see org.xml.sax.ContentHandler#characters(char[], int, int) */ public void characters(char buf[], int offset, int len) throws SAXException { if(len > 0){ switch(type){ case TEXT: this.item.setToolTipText(new String(buf, offset, len)); break; case SCUT: char car = buf[offset]; if(len > 1 && (new String(buf, offset, len)).matches("[0-9]+")) car = (char)Integer.parseInt(new String(buf, offset, len)); if(this.item.getText().indexOf( String.valueOf(KeyEvent.getKeyText(car))) > -1) this.item.setMnemonic((int)car); this.item.getAction().putValue( Action.ACCELERATOR_KEY, (car != 127 ? KeyStroke.getKeyStroke(Constantes.CTRL + KeyEvent.getKeyText(car)) : KeyStroke.getKeyStroke(car, 0))); break; } } } } import java.awt.event.KeyEvent; import java.util.Hashtable; import javax.swing.AbstractAction; import javax.swing.ImageIcon; import javax.swing.KeyStroke; import ui.Application; /** *

La classe AdaptateursMenus recense l'ensemble * des menus de l'application. * Les barres de menus et d'outils trouvent dans cette classe * les adaptateurs, icônes et les raccourcis adéquats.

* @see java.util.Hashtable */ public class AdaptateursMenus extends Hashtable { /** * Le champ fen représente la fenêtre principale de l'application. */ private InterfaceGraphique fen; /** *

Le constructeur crée une instance de la classe AdaptateursMenus.

*/ public AdaptateursMenus(InterfaceGraphique fen) { super(); this.fen = fen; this.creerCollection(); } /** *

La méthode creerCollection() crée une collection * d'objets destinés aux menus et aux outils de l'application courante.

*/ private void creerCollection(){ String nom; this.put(nom = Messages.getString("ui.menu.open"), new GUI_menuOuvrir( this.fen, nom, KeyEvent.VK_O, getIcone(nom), true)); this.put(nom = Messages.getString("ui.menu.new"), new GUI_menuConfigurer( this.fen, nom, KeyEvent.VK_N, getIcone(nom), true)); this.put(nom = Messages.getString("ui.menu.save"), new GUI_menuEnregistrer( this.fen, nom, KeyEvent.VK_S, getIcone(nom), false)); this.put(nom = Messages.getString("ui.menu.saveas"), new GUI_menuEnregistrer( this.fen, nom, KeyEvent.VK_E, getIcone(nom), false)); this.put(nom = Messages.getString("ui.menu.saveall"), new GUI_menuEnregistrer( this.fen, nom, KeyEvent.VK_T, getIcone(nom), false)); this.put(nom = Messages.getString("ui.menu.close"), new GUI_menuFermer( this.fen, nom, KeyEvent.VK_F4, getIcone(nom), false)); this.put(nom = Messages.getString("ui.menu.print"), new GUI_menuImprimer( this.fen, nom, KeyEvent.VK_P, getIcone(nom), false)); this.put(nom = Messages.getString("ui.menu.exit"), new GUI_menuQuitter( this.fen, nom, KeyEvent.VK_Q, getIcone(nom), true)); this.put(nom = Messages.getString("ui.menu.undo"), new GUI_Annuler( this.fen, nom, KeyEvent.VK_Z, getIcone(nom), false)); this.put(nom = Messages.getString("ui.menu.redo"), new GUI_Refaire( this.fen, nom, KeyEvent.VK_Y, getIcone(nom), false)); this.put(nom = Messages.getString("ui.menu.cut"), //$NON-NLS-1$ new GUI_menuEdition( this.fen, nom, KeyEvent.VK_X, getIcone(nom), false)); this.put(nom = Messages.getString("ui.menu.copy"), //$NON-NLS-1$ new GUI_menuEdition( this.fen, nom, KeyEvent.VK_C, getIcone(nom), false)); this.put(nom = Messages.getString("ui.menu.paste"), //$NON-NLS-1$ new GUI_menuEdition( this.fen, nom, KeyEvent.VK_V, getIcone(nom), false)); this.put(nom = Messages.getString("ui.menu.delete"), //$NON-NLS-1$ new GUI_menuEdition( this.fen, nom, KeyEvent.VK_DELETE, getIcone(nom), false)); this.put(nom = Messages.getString("ui.menu.selectall"), //$NON-NLS-1$ new GUI_menuEdition( this.fen, nom, KeyEvent.VK_A, getIcone(nom), false)); this.put(nom = Messages.getString("ui.menu.find"), //$NON-NLS-1$ new GUI_menuRechercher( this.fen, nom, KeyEvent.VK_F, getIcone(nom), false)); this.put(nom = Messages.getString("ui.menu.replace"), //$NON-NLS-1$ new GUI_menuRechercher( this.fen, nom, KeyEvent.VK_H, getIcone(nom), false)); this.put(nom = Messages.getString("ui.menu.help"), //$NON-NLS-1$ new GUI_menuAPropos( this.fen, nom, KeyEvent.VK_F1, getIcone(nom), true)); this.put(nom = Messages.getString("ui.menu.about"), //$NON-NLS-1$ new GUI_menuAPropos( this.fen, nom, KeyEvent.VK_F12, getIcone(nom), true)); } /** *

La méthode getAction() retourne l'action * associée au nom passé en argument.

* @param nom représente le nom de l'action à rechercher. * @return La méthode retourne un objet AbstractAction. */ public AbstractAction getAction(String nom){ return (AbstractAction)super.get(nom); } } <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE menus SYSTEM "menus.dtd"> <menus> <menu name="Fichier"> <item name="Ouvrir" id="open"> <text>Ouvrir un projet existant</text> <shortcut>O</shortcut> </item> <item name="Nouveau" id="new"> <text>Créer un nouveau projet</text> <shortcut>N</shortcut> </item> <item name="separator"/> <item name="Fermer" id="close"> <text>Fermer le projet</text> <shortcut>115</shortcut> </item> <item name="separator"/> <item name="Enregistrer" id="save"> <text>Enregistrer le document actif</text> <shortcut>S</shortcut> </item> <item name="Enregistrer Sous" id="saveas"> <text>Enregistrer le document actif sous...</text> <shortcut>E</shortcut> </item> <item name="Enregistrer Tout" id="saveall"> <text>Enregistrer tous les documents ouverts</text> <shortcut>T</shortcut> </item> <item name="separator"/> <item name="Imprimer" id="print"> <text>Imprimer le document actif</text> <shortcut>P</shortcut> </item> <item name="separator"/> <item name="Quitter" id="exit"> <text>Quitter le logiciel</text> <shortcut>Q</shortcut> </item> </menu> <menu name="Edition"> <item name="Annuler" id="undo"> <text>Annuler la dernière opération</text> <shortcut>Z</shortcut> </item> <item name="Refaire" id="redo"> <text>Refaire la dernière opération</text> <shortcut>Y</shortcut> </item> <item name="separator"/> <item name="Couper" id="cut"> <text> Copier la sélection dans le presse-papiers et la supprimer </text> <shortcut>X</shortcut> </item> <item name="Copier" id="copy"> <text>Copier la sélection dans le presse-papiers</text> <shortcut>C</shortcut> </item> <item name="Coller" id="paste"> <text>Coller le contenu du presse-papiers</text> <shortcut>V</shortcut> </item> <item name="Supprimer" id="delete"> <text>Supprimer le texte sélectionné</text> <shortcut>127</shortcut> </item> <item name="separator"/> <item name="Rechercher" id="find"> <text>Rechercher une expression</text> <shortcut>F</shortcut> </item> <item name="separator"/> <item name="Sélectionner Tout" id="selectall"> <text>Sélectionner tout le document</text> <shortcut>A</shortcut> </item> </menu> <menu name="Aide"> <item name="Sommaire" id="help"> <text>Sommaire de l'aide</text> <shortcut>112</shortcut> </item> <item name="separator"/> <item name="A Propos" id="about"> <text>Informations à propos du logiciel</text> <shortcut>123</shortcut> </item> </menu> </menus> <?xml version="1.0" encoding="ISO-8859-1"?> <!ENTITY % entite SYSTEM "HTMLlat1x.ent"> %entite; <!ELEMENT item (text?, shortcut?)> <!ATTLIST item name CDATA #REQUIRED id CDATA #IMPLIED > <!ELEMENT menu (item*)> <!ATTLIST menu name CDATA #REQUIRED > <!ELEMENT menus (menu+)> <!ELEMENT shortcut (#PCDATA)> <!ELEMENT text (#PCDATA)>