/////////////////////////////////////////////////////////////////////////
//
// © University of Southampton IT Innovation Centre, 2004
//
// Copyright in this library belongs to the IT Innovation Centre of
// 2 Venture Road, Chilworth Science Park, Southampton, SO16 7NP, UK.
//
// This software may not be used, sold, licensed, transferred, copied
// or reproduced in whole or in part in any manner or form or in or
// on any media by any person other than in accordance with the terms
// of the Licence Agreement supplied with the software, or otherwise
// without the prior written consent of the copyright owners.
//
// This software is distributed WITHOUT ANY WARRANTY, without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE, except where stated in the Licence Agreement supplied with
// the software.
//
//      Created by:             Darren Marvin
//      Created date:           2003/12/09
//      Created for project:    GEMSS
//
/////////////////////////////////////////////////////////////////////////
//
//      Dependencies: None
//
/////////////////////////////////////////////////////////////////////////
//
//      Last commit info:       $Author: $
//                              $Date: $
//                              $Revision: $
//
/////////////////////////////////////////////////////////////////////////


package uk.ac.soton.itinnovation.gemss.utils.xml.soap;

import java.util.Iterator;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.Node;
import javax.xml.soap.Text;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.Name;
import java.util.logging.*;

/**
 * SAAJUtils provides useful utility functions for use with the SAAJ API objects.
 * It does not depend on a particular SAAJ provider
 */
public class SAAJUtils {

    public static final String XML_NAMESPACE_NAMESPACE_URI = "http://www.w3.org/2000/xmlns/";
    public static final String XML_NAMESPACE_NAMESPACE_PREFIX = "xmlns";
    public static final Logger mLogger = Logger.getLogger("uk.ac.soton.itinnovation.gemss.utils.xml.soap.SAAJUtils");

    /**
     * Create a SOAPElement from a DOM Element object
     * @param parentSoapEnv SOAPEnvelope that is used to generate Name objects for the SOAPElement
     * @param domElement DOM Element to use as the source
     * @return the matching SOAPElement instance
     * @throws javax.xml.soap.SOAPException
     */
    public static javax.xml.soap.SOAPElement createSOAPElementFromDOMElement(javax.xml.soap.SOAPFactory sef,javax.xml.soap.SOAPEnvelope parentSoapEnv,org.w3c.dom.Element domElement)
    throws javax.xml.soap.SOAPException {


/*
      javax.xml.soap.SOAPFactory sef = null;

            try{
              sef = SOAPFactory.newInstance();
            }
            catch(javax.xml.soap.SOAPException ex) {
              //this happens within the GEMSS component framework because Axis does not do class-loading correctly.
              //rather than:
              //classloader = Thread.currentThread().getContextClassLoader();
              //they should use:
              //classloader=this.getClass().getClassLoader();
              //this applies to all the SAAJ classes that use newInstance() methods.
              //try to directly use the axis one
              mLogger.log(Level.INFO,"Unable to dynamically load SOAP SAAJ Provider, going to try to load Axis directly",ex);
              try{
                sef = new org.apache.axis.soap.SOAPFactoryImpl();
              }
              catch(Exception e) {
                mLogger.log(Level.SEVERE,"Unable to load a suitable SOAP SAAJ Provider, please check the libraries used",e);
                throw new javax.xml.soap.SOAPException("Unable to load a suitable SOAP SAAJ Provider, please check the libraries used");
              }
              if(sef==null) {
                mLogger.log(Level.SEVERE,"Unable to load a suitable SOAP SAAJ Provider, please check the libraries used");
                throw new javax.xml.soap.SOAPException("Unable to load a suitable SOAP SAAJ Provider, please check the libraries used");
              }
            }
      */
      javax.xml.soap.SOAPElement sE = null;

      if(domElement.getPrefix()!=null) {
        sE = sef.createElement(domElement.getLocalName(),
                domElement.getPrefix(), domElement.getNamespaceURI());
        }
        else {

          try{
            sE = sef.createElement(domElement.getLocalName(),
                                 "x", domElement.getNamespaceURI());//I don't like this but it is necessary for castor to work properly since castor seems to not like elements without a namespace and I cannot add the default namespace
          }
          catch(NullPointerException ex) {
            sE = sef.createElement(domElement.getLocalName());
          }
        }

        if (domElement.hasAttributes()){
            org.w3c.dom.NamedNodeMap DOMAttributes = domElement.getAttributes();
            int noOfAttributes = DOMAttributes.getLength();
            for(int i = 0; i < noOfAttributes; i++){
                org.w3c.dom.Node attr = DOMAttributes.item(i);
                //ignore namespace attributes
                if(!attr.getNodeName().startsWith("xmlns")) {
                  sE.addAttribute(parentSoapEnv.createName(attr.getLocalName(), attr.getPrefix(),
                      attr.getNamespaceURI()), attr.getNodeValue());
                }
            }
        }
        //set the target namespace
        //System.out.println("After Atts SOAPE localname " + sE.getElementName().getLocalName() + " prefix " + sE.getElementName().getPrefix() + " namespace " + sE.getElementName().getURI());

        if(domElement.hasChildNodes()){
            org.w3c.dom.NodeList children = domElement.getChildNodes();
            for(int i = 0; i< children.getLength(); i++){
                org.w3c.dom.Node child = children.item(i);

                switch(child.getNodeType()){
                    case org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE: break;

                    case org.w3c.dom.Node.DOCUMENT_TYPE_NODE: break;

                    case org.w3c.dom.Node.CDATA_SECTION_NODE:

                    case org.w3c.dom.Node.COMMENT_NODE:

                    case org.w3c.dom.Node.TEXT_NODE: {sE.addTextNode(child.getNodeValue());
                        break;
                    }

                default: sE.addChildElement(createSOAPElementFromDOMElement(sef,parentSoapEnv, (org.w3c.dom.Element) child));
                }

            }
        }

        return sE;
    }

    /**
     * Creates a DOM element from a SOAPElement object
     * @param parentDoc parent DOM Document used to create new nodes for the DOM element
     * @param soapElement source SOAPElement instance
     * @return matching DOM object
     * @throws javax.xml.soap.SOAPException
     */
    /*
    public static org.w3c.dom.Element createDOMElementFromSOAPElement(org.w3c.dom.Document parentDoc, javax.xml.soap.SOAPElement soapElement)
    throws javax.xml.soap.SOAPException {
        javax.xml.soap.Name sEName = soapElement.getElementName();
        //don't know why but sometimes the prefix is lost, where there is no prefix
        org.w3c.dom.Element dE = null;
        if(sEName.getPrefix().equals("") && sEName.getQualifiedName().indexOf(':')==-1) {
            //try to use the URI to retrieve a worthwhile prefix, could make one up but
            //that is likely to bite later
            //use a default namespace attribute pointing to the correct uri.
            dE = parentDoc.createElementNS(sEName.getURI(),sEName.getLocalName());
            dE.setAttribute("xmlns",sEName.getURI());
        }
        else {
            dE = parentDoc.createElementNS(sEName.getURI(),sEName.getQualifiedName());
        }



        Iterator attIterator = soapElement.getAllAttributes();
        while(attIterator.hasNext()) {
            javax.xml.soap.Name attName = (javax.xml.soap.Name) attIterator.next();
            //there seems to be some bug with Axis which means that where an attribute has no
            //prefix then Axis uses the local name value as the prefix, following checks to see if
            //the supplied prefix can be resolved to some namespace and if not no prefix is supplied
            String attPrefix = attName.getPrefix();


            if(soapElement.getNamespaceURI(attPrefix)==null) {
                dE.setAttribute(attName.getLocalName(),soapElement.getAttributeValue(attName));
            }
            else {
                dE.setAttributeNS(attName.getURI(),attName.getQualifiedName(),soapElement.getAttributeValue(attName));
            }
        }
        //for some reason the text node irrelevent, the text if there is any is the element value
        //have to identify manually if it is a CDATA section
        String txtValue = soapElement.getValue();
        if(txtValue!=null) {
            if(txtValue.startsWith("![CDATA[") && txtValue.endsWith("]]")) {
                dE.appendChild(parentDoc.createCDATASection(txtValue.substring(8,txtValue.length()-1)));
            }
            else {
                //just text then
            dE.appendChild(parentDoc.createTextNode(txtValue));	}
        }

        Iterator childIterator = soapElement.getChildElements();
        while(childIterator.hasNext()) {
            javax.xml.soap.Node node = (javax.xml.soap.Node) childIterator.next();

            if(node instanceof javax.xml.soap.Text) {
                javax.xml.soap.Text txt = (javax.xml.soap.Text) node;

                if(txt.isComment()) {
                    dE.appendChild(parentDoc.createComment(soapElement.getValue()));
                }
            }
            else {
                //generate the right sort of element.
                javax.xml.soap.SOAPElement se = (javax.xml.soap.SOAPElement) node;

                dE.appendChild(createDOMElementFromSOAPElement(parentDoc,se));
            }
        }
        //make sure all namespaces are applied
        Iterator prefixIterator = soapElement.getNamespacePrefixes();
        while(prefixIterator.hasNext()) {
            String prefix = (String) prefixIterator.next();

            if(prefix!=null && !prefix.equals("")) {
                try {
                    String namespaceURI = soapElement.getNamespaceURI(prefix);
                    dE.setAttributeNS(XML_NAMESPACE_NAMESPACE_URI, XML_NAMESPACE_NAMESPACE_PREFIX+":"+prefix,namespaceURI);
                    //dE.setAttribute(XML_NAMESPACE_NAMESPACE_URI, XML_NAMESPACE_NAMESPACE_PREFIX+":"+prefix,namespaceURI);
                }
                catch(NullPointerException ex) {
                    //Axis seems to throw this for some reason
                    //can't do much about it

                }
            }
        }

		return dE;
    }
    */
    /**
     * Creates a DOM document directly from a SOAPEnvelope
     * @param soapEnv envelope source
     * @return matching DOM document object
     */
    /*
    public static org.w3c.dom.Document createDOMDocumentFromSOAPEnvelope(javax.xml.soap.SOAPEnvelope soapEnv) throws javax.xml.soap.SOAPException, javax.xml.parsers.ParserConfigurationException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        factory.setValidating(false);
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.newDocument();
        Element rootE = doc.getDocumentElement();
        //debug

        Element envE = createDOMElementFromSOAPElement(doc,soapEnv);
        if(rootE!=null) {
            doc.replaceChild(envE,rootE);
        }
        else {
            doc.appendChild(envE);
        }



        return doc;
    }
    */


}