package com.diaam.xmlfr.faqjaxp;

import java.io.File;
import java.io.FileReader;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

public class SansBlancsAvecSax
{
  // Le parser SAX n'envoit pas forcément les noeuds textes en une
  // seule fois. Grâce à ce filtre, on se débrouille pour qu'il le fasse à coup sûr.
  //
  // Sur une idée de Robin Berjon, en Perl, à
  // http://search.cpan.org/src/RBERJON/XML-Filter-BufferText-1.01/BufferText.pm
  private class FiltreRassemblantLesTextes extends org.xml.sax.helpers.XMLFilterImpl
  {
    private StringBuffer m_rangeTexte;

    private FiltreRassemblantLesTextes(XMLReader parent)
    {
      super(parent);
      m_rangeTexte = new StringBuffer();
    }
    
    public void characters(char[] ch, int start, int length) 
    throws SAXException
    {
      m_rangeTexte.append(ch, start, length);
    }
    
    public void endElement(String uri, String localName, String qName) 
    throws SAXException
    {
      // l'élément est fini, donc on est sûr que l'on
      // ne recevra plus de caractères ;
      // on diffuse les caractères reçus.
      if (m_rangeTexte.length() > 0)
      {
	String tousCaractèresTrim;
	String tousCaractères;
	
	tousCaractères = m_rangeTexte.toString();
	getContentHandler().
	characters(tousCaractères.toCharArray(), 0, tousCaractères.length());
	m_rangeTexte.setLength(0);
      }
      
      // et on diffuse l'événement de fin.
      getContentHandler().endElement(uri, localName, qName);
    }
  }
  
  // ne fonctionne bien que s'il est précédé du FiltreRassemblantLesTextes.
  private class FiltreOtantLesBlancs extends org.xml.sax.helpers.XMLFilterImpl
  {
    private FiltreOtantLesBlancs(XMLReader parent)
    {
      super(parent);
    }

    // dans le cas où il n'y a que des blancs, ne renvoie rien, 
    // sinon renvoit l'original.
    public void characters(char[] ch, int start, int length) 
    throws SAXException
    {
      String texte;
      
      texte = new String(ch, start, length);
      if (texte.trim().length() > 0)
	getContentHandler().characters(ch, start, length);
    }
  }
  
  public static void main(String[] args) throws Exception
  {
    SansBlancsAvecSax pourVoir;
    
    pourVoir = new SansBlancsAvecSax();
    pourVoir.démontreExemple(args[0]);
  }
  
  public void démontreExemple(String fichierXML) throws Exception
  {
    SAXParser piloteSAX;
    XMLReader lecteur;
    FiltreOtantLesBlancs filtreBlancs;
    FiltreRassemblantLesTextes filtreRassemblantLesTextes;
    SAXSource source;
    InputSource fluxEntrant;
    StreamResult print;
    Transformer identité;
    
    // on se donne un lecteur sax de base.
    piloteSAX = SAXParserFactory.newInstance().newSAXParser();
    lecteur = piloteSAX.getXMLReader();

    // on commence par mettre un filtre pour 
    // recevoir les éléments textes groupés
    filtreRassemblantLesTextes = new FiltreRassemblantLesTextes(lecteur);
    
    // on se donne un filtre pour le lecteur de base. Ce filtre se
    // comporte lui même comme un lecteur sax.
    filtreBlancs = new FiltreOtantLesBlancs(filtreRassemblantLesTextes);
    
    // il faut bien un flux de lecture pour le lecteur
    fluxEntrant = new InputSource(new FileReader(fichierXML));

    // Pour voir le fichier résultat, j'utilise une transformation
    // à l'identique sur le filtre ; le transformer se déclarera
    // lui même comme ContentHandler de la source SAX, donc du flitre.
    source = new SAXSource(filtreBlancs, fluxEntrant);
    print = new StreamResult(System.out);
    identité = TransformerFactory.newInstance().newTransformer();
    identité.transform(source, print);
  }
}
