/*
 * Etude de cas CouponBleu - Servlet de validation des commandes XML
 */

package Fournisseur ;

import java.util.Hashtable;

import org.w3c.dom.*;
import com.sun.xml.tree.*;
import com.sun.xml.parser.Resolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import java.io.File ;
import java.io.FileWriter ;
import java.io.IOException ;
import java.util.Date ;
import java.text.SimpleDateFormat ;

public class TraiteCommande extends XmlRpcServlet
{
    // Identification publique du DTD de commande de Textiles
    private final static String IdentEditexmlDtd = "-//EDITEXML//DTD Commande de Textiles//FR" ;
    // Tag pour la confirmation de commande
    private final static String tagConfirmCommandeTextile = "ConfirmCommandeTextile" ;
    private final static String tagConfirmArticle = "ConfirmArticle" ;
    private final static String NameSpace = "editexml:" ;
    private final static String tagCommande = "NumeroCommande" ;
    private final static String tagDate = "DateMessage" ;
    private final static String tagClient = "IdentClient" ;
    private final static String tagFournisseur = "IdentFournisseur" ;
    private final static String tagTissus = "CodeTissus" ;
    private final static String tagDescription = "DescriptionArticle" ;
    private final static String tagCouleur = "CodeCouleur" ;
    private final static String tagRefArticle = "ReferenceCommandeArticle" ;
    private final static String tagDimension = "CodeDimension" ;
    private final static String tagQuantite = "Quantite" ;
    private final static String tagQuantiteDisponible = "QuantiteDisponible" ;
    private final static String tagDateLivraison = "DateLivraison" ;

    public TraiteCommande ()
    {
	SimpleElementFactory	factory = new SimpleElementFactory ();
	Hashtable		bindings = new Hashtable (2);
	Resolver		resolver = new Resolver ();

	//
	// On déclare deux objets DOM personnalisés pour les éléments de la namespace "editexml" :
	// traitement d'une commande "editexml:CommandeTextile"
	// traitement d'un article : "editexml:Article"
	//
	bindings.put ("*Element", "Fournisseur.TraiteCommande$Executable");
	bindings.put ("CommandeTextile", "Fournisseur.TraiteCommande$CommandeTextile");
	bindings.put ("Article", "Fournisseur.TraiteCommande$Article");
	factory.addMapping (
	    "http://www.couponbleu.com/editexml.html",  // Namespace
	    bindings,				        // Dictionary
	    getClass().getClassLoader());		// Classloader
	customizeDocument (factory, false);

	//
	// L'accès au DTD est local, dans le répertoire du package du servlet Fournisseur,
	// via le proxy suivant :
	//
	resolver.registerCatalogEntry (
	    IdentEditexmlDtd,		    // publicId
	    "Fournisseur/editexml.dtd",	    // resourceName
	    getClass().getClassLoader()); // ClassLoader
	customizeResolver(resolver);
    }

    /*
     * Traitement de document XML contenant la commande,
     * à cette étape, le document est controlé et conforme au DTD
     */
    protected XmlDocument rpc (String pathInfo, XmlDocument request)
    {
	TreeWalker	walker = new TreeWalker (request);
	Node		node;

/*
    try {
    FileWriter f = new FileWriter("d:/temp/voir.txt", true) ;
    f.write("Entrée dans rpc()") ;
    f.write("\r\n") ;
    f.flush() ;
    f.close() ;
    } catch (IOException e) {}
*/

	// On initialise la confirmation de commande
	XMLCommandeTextile confirm = new XMLCommandeTextile(getInitParameter("dtdSystemId")) ;

	for (node = walker.getCurrent ();
		node != null;
		node = walker.getNext ()) {
	    // On execute les éléments appartenant à la namespace "editexml"
	    if (node instanceof Executable) {
			((Executable)node).execute (confirm);
	    }
	}
	// On sauvegarde la confirmation de commande dans le fichier XML d'historique
	SauveHistoriqueCommande(confirm.getDoc()) ;
	// On retourne le message de confirmation de commande au format XML
	return confirm.getDoc() ;
    }

    private void SauveHistoriqueCommande(XmlDocument doc)
    {
	// On charge le document d'historique
	InputSource	input;
	XmlDocument	histo = null;

	try {
	    // On prend le document XML d'historique comme source d'entree
		input = Resolver.createInputSource (new File ("public_html/CouponBleu/xml/historique.xml"));
	    // On parse le document (sans validation)
	    histo = XmlDocument.createXmlDocument (input, false);
		histo.getDocumentElement().normalize() ;	// Supprime les noeuds texte adjacents
	} catch (SAXParseException err) {
	    System.out.println ("** Erreur lors de l'analyse" 
		+ ", ligne " + err.getLineNumber ()
		+ ", uri " + err.getSystemId ());
	    System.out.println("   " + err.getMessage ());
	} catch (SAXException e) {
	    Exception	x = e.getException ();
	    ((x == null) ? e : x).printStackTrace ();
	} catch (Throwable t) {
	    t.printStackTrace ();
	}

	
	// On ajoute la confirmation de commande
	Node clone = doc.getDocumentElement().cloneNode(true) ;	// On clone la confirmation de commande
	histo.changeNodeOwner(clone) ;			// La confirmation clonée devient propriété du document XML d'historique
	histo.getDocumentElement().appendChild(clone) ;	// On peut alors l'ajouter à l'historique
	histo.getDocumentElement().normalize() ;	// On compacte le document résultat
	
	// On sauvegarde le nouvel historique
	try {
	    FileWriter fw = new FileWriter("public_html/CouponBleu/xml/historique.xml") ;
	    histo.write(fw) ;
	    fw.close() ;
	    } catch (IOException e) {
	    e.printStackTrace (System.err);
	    }
    }

    /*
     * Classe de base pour l'exécution d'un élément
     */
    static public class Executable extends ElementNode
    {
	public void execute (XMLCommandeTextile confirm) {
	// Classe de base des objets DOM personnalisés, Pas de traitement
	}
    }

    // Execution d'un element "editexml:CommandeTextile" : prise en compte de la commande
    static public class CommandeTextile extends Executable
    {
	public void execute (XMLCommandeTextile confirm)
	{
/*
try {
    FileWriter f = new FileWriter("d:/temp/voir.txt", true) ;
    f.write("CommandeTextile execute() : " + getNodeName() + " " + getClass().getName()) ;
    f.write("\r\n") ;
    f.flush() ;
    f.close() ;
    } catch (IOException e) {}

try {
    FileWriter f = new FileWriter("d:/temp/voir.txt", true) ;
    f.write("items : " + item(0).getNodeName() + " " + item(1).getNodeName()) ;
    f.write("\r\n") ;
    f.flush() ;
    f.close() ;
    } catch (IOException e) {}
*/
	    // L'ordre des elements est défini dans le DTD, et controlé par le parseur
	    confirm.setCommande(Integer.parseInt(item(0).getChildNodes().item(0).getNodeValue())) ;
	    confirm.setDate(item(1).getChildNodes().item(0).getNodeValue()) ;
	    confirm.setClient(item(2).getChildNodes().item(0).getNodeValue()) ;
	    confirm.setFournisseur(item(3).getChildNodes().item(0).getNodeValue()) ;
	    // On ajoute ces éléments à la confirmation de commande
	    confirm.addCommande() ;
	}
    }

    // Execution d'un element "editexml:Article" : ajout d'un article a la commande
    static public class Article extends Executable
    {
	public void execute (XMLCommandeTextile confirm)
	{
/*
	try {
    FileWriter f = new FileWriter("d:/temp/voir.txt", true) ;
    f.write("Article execute()") ;
    f.write("\r\n") ;
    f.flush() ;
    f.close() ;
    } catch (IOException e) {}
*/
	    // L'ordre des elements est défini dans le DTD, et controlé par le parseur
	    confirm.setTissus(Integer.parseInt(item(0).getChildNodes().item(0).getNodeValue())) ;
	    confirm.setDescription(item(1).getChildNodes().item(0).getNodeValue()) ;
	    confirm.setCouleur(Integer.parseInt(item(2).getChildNodes().item(0).getNodeValue())) ;
	    confirm.setArticle(Integer.parseInt(item(3).getChildNodes().item(0).getNodeValue())) ;
	    confirm.setDimension(Integer.parseInt(item(4).getChildNodes().item(0).getNodeValue())) ;
	    confirm.setQuantite(Integer.parseInt(item(5).getChildNodes().item(0).getNodeValue())) ;

	    // Un traitement plus complet doit ici vérifier la disponibilité de l'article
	    // et déterminer la date de livraison
	    confirm.setQuantiteDisponible(confirm.getQuantite()) ;
	    confirm.setDateLivraison(new SimpleDateFormat("yyyyMMdd").format(new Date(System.currentTimeMillis()+10*24*3600000))) ;

	    // On ajoute cet article à la commande
	    confirm.addArticle() ;
	}
    }

    /*
     * XMLCommandeTextile : traitement et confirmation d'une commande de textile
     */
    private static class XMLCommandeTextile
    {
        private int Commande = 0 ;
        private String DateM = null ;
        private String Client = null ;
        private String Fournisseur = null ;
        private int Tissus = 0;
        private String Description = null ;
        private int Couleur = 0 ;
        private int Article = 0 ;
        private int Dimension = 0 ;
        private int Quantite = 0 ;
        private int QuantiteDisponible = 0;
        private String DateLivraison = null ;

        private XmlDocument doc ;
	private Element root ;

        public XmlDocument getDoc()
	    { return doc ; }

        // Nettoyage des informations sur un article de commande
        private void cleanArticle()
        {
	    Tissus = 0 ;
	    Description = null ;
	    Couleur = 0 ;
	    Article = 0 ;
	    Dimension = 0 ;
	    Quantite = 0 ;
	    QuantiteDisponible = 0 ;
	    DateLivraison = null ;
        }

        // Initialisation de la commande de textile
        public XMLCommandeTextile(String dtdSystemId)
        {
	    // On initialise le document XML de réponse
	    doc = new XmlDocument ();

/*
	    try {
	    FileWriter f = new FileWriter("d:/temp/voir.txt", true) ;
	    f.write("name = " + dtdSystemId + "\r\n") ;
	    f.flush() ;
	    f.close() ;
	    } catch (IOException e) {}
*/	    
	    doc.setDoctype (
		IdentEditexmlDtd,			    // dtdPublicId
	        dtdSystemId,				    // dtdSystemId
		null);					    // internalSubset

	    root = doc.createElement(NameSpace + tagConfirmCommandeTextile);
	    doc.appendChild(root);
        }

	public void setCommande(int value)
	    { Commande = value ; }
        public int getCommande()
	    { return Commande ; }

        public void setDate(String value)
	    { DateM = value ; }
        public String getDate()
	    { return DateM ; }

        public void setClient(String value)
	    { Client = value ; }
        public String getClient()
	    { return Client ; }

        public void setFournisseur(String value)
	    { Fournisseur = value ; }
        public String getFournisseur()
	    { return Fournisseur ; }

        public void setTissus(int value)
	    { Tissus = value ; }
        public int getTissus()
	    { return Tissus ; }

        public void setDescription(String value)
	    { Description = value ; }
        public String getDescription()
	    { return Description ; }

        public void setCouleur(int value)
	    { Couleur = value ; }
        public int getCouleur()
	    { return Couleur ; }

        public void setArticle(int value)
	    { Article = value ; }
        public int getArticle()
	    { return Article ; }

        public void setDimension(int value)
	    { Dimension = value ; }
        public int getDimension()
	    { return Dimension ; }

        public void setQuantite(int value)
	    { Quantite = value ; }
        public int getQuantite()
	    { return Quantite ; }

        public void setQuantiteDisponible(int value)
	    { QuantiteDisponible = value ; }
        public int getQuantiteDisponible()
	    { return QuantiteDisponible ; }

        public void setDateLivraison(String value)
	    { DateLivraison = value ; }
	public String getDateLivraison()
	    { return DateLivraison ; }


        /*
	 * Ajout d'une confirmation de commande 
         */
	public void addCommande()
        {
	    Element		elem ;
	
	    try {
		// On ajoute l'entete de la confirmation de commande (numéro, date, client, fournisseur)
		elem = doc.createElement(NameSpace + tagCommande);
		elem.appendChild(doc.createTextNode(new Integer(Commande).toString()));
		root.appendChild(elem) ;

		elem = doc.createElement(NameSpace + tagDate);
		elem.appendChild(doc.createTextNode(DateM));
		root.appendChild(elem) ;

		elem = doc.createElement(NameSpace + tagClient);
		elem.appendChild(doc.createTextNode(Client.toString()));
		root.appendChild(elem) ;
   
		elem = doc.createElement(NameSpace + tagFournisseur);
		elem.appendChild(doc.createTextNode(Fournisseur.toString()));
		root.appendChild(elem) ;
	    } catch (Exception e) {
		e.printStackTrace (System.err);
	    }
	}
        /*
	 * Ajout d'une confirmation d'article
	 */
        public void addArticle()
        {
	    Element		elem ;
	    Element		article ;
	
	    try {
		// On ajoute l'article de la commande
		article = doc.createElement(NameSpace + tagConfirmArticle);
		root.appendChild(article) ;
		elem = doc.createElement(NameSpace + tagTissus);
		elem.appendChild(doc.createTextNode(new Integer(Tissus).toString()));
		article.appendChild(elem) ;

		elem = doc.createElement(NameSpace + tagDescription);
		elem.appendChild(doc.createTextNode(Description));
		article.appendChild(elem) ;

		elem = doc.createElement(NameSpace + tagCouleur);
		elem.appendChild(doc.createTextNode(new Integer(Couleur).toString()));
		article.appendChild(elem) ;

		elem = doc.createElement(NameSpace + tagRefArticle);
		elem.appendChild(doc.createTextNode(new Integer(Article).toString()));
		article.appendChild(elem) ;

		elem = doc.createElement(NameSpace + tagDimension);
		elem.appendChild(doc.createTextNode(new Integer(Dimension).toString()));
		article.appendChild(elem) ;

		elem = doc.createElement(NameSpace + tagQuantite);
		elem.appendChild(doc.createTextNode(new Integer(Quantite).toString()));
		article.appendChild(elem) ;

		elem = doc.createElement(NameSpace + tagQuantiteDisponible);
		elem.appendChild(doc.createTextNode(new Integer(QuantiteDisponible).toString()));
		article.appendChild(elem) ;

		elem = doc.createElement(NameSpace + tagDateLivraison);
		elem.appendChild(doc.createTextNode(DateLivraison));
		article.appendChild(elem) ;
		// On prepare pour l'article suivant
		cleanArticle() ;
	    } catch (Exception e) {
		e.printStackTrace (System.err);
	    }
	}
    }
}
