/*
* 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 (n != 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);
}
}