XPathW3CNS.java

/*
* XPathW3CNS.java
* Copyright (C) 2004 Frederic Laurent
* http://www.opikanoba.org
*
* This file is part of the XPath Article.
*
* Lantern is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Lantern is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Xpath article; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
*/

/**
 * Test XPath avec l'API du W3C.
 * Ce programme est volontairement très simple. Le but n'est pas
 * de tester tous les cas possibles et les cas d'erreur mais de 
 * donner rapidement un aperçu des objets à utiliser.
 * 
 * @author Fréderic Laurent
 */

package test;
import java.io.OutputStreamWriter;
import java.net.URL;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.xpath.XPathEvaluator;
import org.w3c.dom.xpath.XPathNSResolver;
import org.w3c.dom.xpath.XPathResult;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;



public class XPathW3CNS {
    // Document DOM sur lequel les requetes XPAth sont effectuées
    private Document doc = null;
    // Transformateur XSLT
    private Transformer out_ser;
    private Document nsdoc;

    
    
    /**
     * classe interne permettant de collecter les espaces de noms
     * et de les positionner sur un element en tant qu'attribut
     *
     */
    class NamespacesHandler extends DefaultHandler {
        // compteur d'espace de noms par défaut
        private int defaultNSCount;
        // element sur lequel ajouter les declarations
        private Element nsElt;

        
        /**
         * methode permettant de réagir sur la déclaration d'un nouvel
         * espace de noms
         * @param prefix prefixe de l'espace de noms
         * @param uri URI de l'espace de noms
         * 
         * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String, java.lang.String)
         */
        public void startPrefixMapping(String prefix, String uri)
        throws SAXException {
            String pref=null;
            
            if ("".equals(prefix)){
                // espace de noms par défaut
                pref=XPathSaxonNS.DEFAULT_NS_PREFIX+this.defaultNSCount;
                this.defaultNSCount++;
            } else {
                pref=prefix;
            }
            System.out.println("Namespaces\t" + pref + "\t" + uri);
            this.nsElt.setAttributeNS("http://www.w3.org/2000/xmlns/", 
                    "xmlns:"+pref, uri);
        }
        /**
         * @param nsElt Element sur lequel seront ajoutes les declarations
         * d'espaces de noms
         * 
         */
        public NamespacesHandler(Element nsElt) {
            this.defaultNSCount=1;
            this.nsElt=nsElt;
        }
    }
    
    
    /**
     * Construction de la classe de test 
     *  - intialisation d'un transfomeur XSLT pour sortir les resultats 
     * sous forme de fragment de document (la transformation identité
     * permet d'avoir le flux XML sur la sortie standard)
     *  
     */
    public XPathW3CNS() {
        try {
            this.out_ser = TransformerFactory.newInstance().newTransformer();
            this.out_ser.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
        } catch (Exception e) {
            // si une exception quelconque survient, elle est tracée
            // Attention, le traitement est volontairement simple dans un souci
            // d'allègement du code ! un traitement correct serait beaucoup plus
            // détaillé
            System.err.println(e.getMessage());
            e.printStackTrace();
        }
    }
    /**
     * Ecrit sur la sortie standard n'importe quel noeud XML en utilisant la
     * transformation identité si necessaire
     * 
     * @param node
     *            noeud à tracer
     */
    public void printResult(Node node) {
        try {
            // test du type de noeud
            int nodeType = node.getNodeType();
            
            // s'il s'gait d'un noeud texte, on l'ecrit sur la sortie standard
            if (nodeType == Node.CDATA_SECTION_NODE || nodeType == Node.TEXT_NODE) {
                
                System.out.println(node.getNodeValue());
                //  il se peut que le noeud ait des noeuds voisins, un meme texte
                // peut etre représenté par plusieurs noeuds adjacents
                Node next = node.getNextSibling();
                if (next != null) {
                    printResult(next);
                }
            } else {
                // sinon on fait appel à la transformation identité
                this.out_ser.transform(new DOMSource(node), new StreamResult(
                        new OutputStreamWriter(System.out)));
            }
        } catch (Exception e) {
            // si une exception quelconque survient, elle est tracée
            // Attention, le traitement est volontairement simple dans un souci
            // d'allègement du code ! un traitement correct serait beaucoup plus
            // détaillé
            System.err.println(e.getMessage());
            e.printStackTrace();
        }
    }
    
    
    
    /**
     * Lecture du document avec JAXP
     *   - construction du document DOM
     *   - construction d'un document supportant les declarations
     *     d'espaces de noms, y compris les espaces de noms par defaut
     * 
     * @param xmlbuf buffer XML
     */
    public void loadDocument(String xmlbuf) {
        InputSource is;
        try {
            if (xmlbuf.startsWith("http://") || xmlbuf.startsWith("file:")) {
                is = new InputSource(new URL(xmlbuf).toString());
            } else {
                is = new InputSource(xmlbuf);
            }
            // creation de la fabrique delecture
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            dbf.setValidating(false);
            
            // creation du document portant les declarations d'espaces de noms
            DOMImplementation impl = dbf.newDocumentBuilder().getDOMImplementation();
            this.nsdoc = impl.createDocument("","nsdoc", null);
            
            // creation du collecteur
            NamespacesHandler nsh = new NamespacesHandler(nsdoc.getDocumentElement());
            // creation d'un parseur SAX pour trouver les espaces de noms
            SAXParserFactory saxFactory = SAXParserFactory.newInstance();
            XMLReader parser = saxFactory.newSAXParser().getXMLReader();
            // positionnement du handler charger de collecter les espaces de noms
            parser.setContentHandler(nsh);
            parser.setFeature("http://xml.org/sax/features/namespaces", true);
            // analyse du document source pour collecter les espaces de noms
            parser.parse(is);           
            
            // lecture du document source dans un document DOM
            this.doc = dbf.newDocumentBuilder().parse(is);
        } catch (Exception e) {
            // si une exception quelconque survient, elle est tracée
            // Attention, le traitement est volontairement simple dans un souci
            // d'allègement du code ! un traitement correct serait beaucoup plus
            // détaillé
            System.err.println(e.getMessage());
            e.printStackTrace();
        }
    }
    /**
     * Evaluation d'une expression XPath 
     * - création d'une évaluateur d'expression à partir du 
     *   document DOM 
     * - parcours de l'ensemble des noeuds résultat et 
     *   affichage de chacun
     * 
     * @param xpath
     *            expression à évaluer
     */
    public void evalXpath(String xpath) {
        // création de l'évaluateur : appel explicite à l'API de Xalan
        // cette expression va évoluer avec la sortie de la recommandation DOM XPath 3
        // et la mise à jour des implementations en conséquence
        XPathEvaluator evaluator = new org.apache.xpath.domapi.XPathEvaluatorImpl(this.doc);
        
        // positionnement d'un objet devant résoudre les prefixes grace aux 
        // document portant les déclarations d'espaces de noms construit lors de la lecture
         XPathNSResolver nsres = evaluator.createNSResolver(this.nsdoc.getDocumentElement());
        
        // évaluation de l'expression XPath
        XPathResult result = (XPathResult) evaluator.evaluate(xpath, doc, nsres,
                XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null);
        // parcours des noeuds correspondant à l'expression XPath
        // et affichage de chacun
        Node n = result.iterateNext();
        while (!= null) {
            printResult(n);
            n = result.iterateNext();
        }
    }
    /**
     * Programme principal
     * 
     * @param args arguments de la ligne de commande
     */
    public static void main(String[] args) {
        if (args.length < 2) {
            System.err.println("usage: java test.XPathW3CNS xml xpath");
            System.err.println("   - xml   document XML");
            System.err.println("   - xpath expression XPath");
            System.exit(1);
        }
        
        // Test de l'implementation DOM pour savoir si elle implémente DOM Niveau 3
        DOMImplementation impl=null;
        try {
            // obtention d'une implémentation
            impl = DocumentBuilderFactory.newInstance().newDocumentBuilder().getDOMImplementation();
            // test de la propriété adéquate
            if (!impl.hasFeature("XPath", "3.0")) {
                  System.err.println("Cette implementation ne supporte pas DOM 3 XPath");  
                  System.exit(1);
            } else {
                System.out.println("Cette implementation supporte DOM 3 XPath");  
            }
        } catch (Exception e) {     
            // si une exception quelconque survient, elle est tracée
            // Attention, le traitement est volontairement simple dans un souci
            // d'allègement du code ! un traitement correct serait beaucoup plus
            // détaillé
            System.err.println(e.getMessage());
            e.printStackTrace();
        }
                
        final String uri = args[0];
        final String xpath = args[1];
        // initialisation de la classe de test
        XPathW3CNS test = new XPathW3CNS();
        // lecture du document
        test.loadDocument(uri);
        // evaluation de l'expression XPath
        test.evalXpath(xpath);
    }
}