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
* @see javax.swing.JMenuBar */ public class BarreMenus extends JMenuBar { /** * Le champBarreMenus
représente la barre * de menus de l'application.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 *
*/ public BarreMenus(AdaptateursMenus adapts) { super(); this.adapts = adapts; this.creerBarreMenus(); } /** *BarreMenus
.La méthode
*/ 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; /** *creerBareMenu()
initialise * et crée la barre de menus.La classe
* @see org.xml.sax.helpers.DefaultHandler */ public class MenusHandler extends DefaultHandler { /** * Le champMenusHandler
gère des documents XML * en vue de la configuration des menus de l'application.barre
représente la barre de menus * de l'application. */ private BarreMenus barre; /** * Le champadapts
représente une collection de * gestionnaires d'événements affectés aux menus de l'application. */ private AdaptateursMenus adapts; /** * Le champmenu
représente un menu de l'application. */ private JMenu menu; /** * Le champrepré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 constanteMENU
indique un élément XML * de type MENU (menu). */ private final int MENU = 0; /** * La constanteMENU
indique un élément XML * de type ITEM (sous-menu). */ private final int ITEM = 1; /** * La constanteMENU
indique un élément XML * de type TEXT. */ private final int TEXT = 2; /** * La constanteMENU
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
* @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 //=========================================================== /** *MenusHandler
.La méthode
* @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 { } /** *startDocument
reçoit la notification du début du document XML.La méthode
* @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 { } /** *endDocument
reçoit la notification de la fin du document XML.La méthode
* @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; } } } } } /** *startElement
reçoit la notification * indiquant le début d'un élément XML.La méthode
* @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 { } /** *endElement()
reçoit la notification indiquant * la fin d'un élément XML.La méthode
* @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; /** *characters
reçoit la notification * indiquant la présence d'une chaîne de caractères.La classe
* @see java.util.Hashtable */ public class AdaptateursMenus extends Hashtable { /** * Le champAdaptateursMenus
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.fen
représente la fenêtre principale de l'application. */ private InterfaceGraphique fen; /** *Le constructeur crée une instance de la classe
*/ public AdaptateursMenus(InterfaceGraphique fen) { super(); this.fen = fen; this.creerCollection(); } /** *AdaptateursMenus
.La méthode
*/ 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)); } /** *creerCollection()
crée une collection * d'objets destinés aux menus et aux outils de l'application courante.La méthode
* @param nom représente le nom de l'action à rechercher. * @return La méthode retourne un objetgetAction()
retourne l'action * associée au nom passé en argument.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)>