/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.axis.message;

// $Id$

import org.jboss.axis.transport.http.HTTPConstants;
import org.jboss.axis.utils.Messages;
import org.jboss.logging.Logger;
import org.w3c.dom.Attr;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Comment;
import org.w3c.dom.DOMException;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.EntityReference;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.ProcessingInstruction;
import org.w3c.dom.Text;
import org.w3c.dom.DOMConfiguration;
import org.w3c.dom.UserDataHandler;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.soap.MimeHeaders;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.transform.Source;
import java.io.InputStream;
import java.util.Iterator;

/**
 * An implemenation of the abstract SOAPPart.
 * <p/>
 * This class should not expose functionality that is not part of
 * {@link javax.xml.soap.SOAPPart}. Client code should use <code>SOAPPart</code> whenever possible.
 *
 * @author Thomas Diesler (thomas.diesler@jboss.org)
 * @since 31-May-2004
 */
public class SOAPPartImpl extends javax.xml.soap.SOAPPart
{

   private static Logger log = Logger.getLogger(SOAPPartImpl.class.getName());

   private SOAPMessage soapMessage;
   private MimeHeaders mimeHeaders;
   private SOAPEnvelope soapEnvelope;

   private Document document;

   // The contentSource
   private Source contentSource;

   public SOAPPartImpl()
   {
   }

   public SOAPPartImpl(SOAPMessage soapMessage, InputStream inStream, MimeHeaders headers)
   {
      this.soapMessage = soapMessage;

      mimeHeaders = new MimeHeadersImpl(headers);
      if (headers == null)
      {
         mimeHeaders = new MimeHeadersImpl();
         mimeHeaders.addHeader(HTTPConstants.HEADER_CONTENT_TYPE, "text/xml");
      }

      try
      {
         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
         factory.setValidating(false);
         factory.setNamespaceAware(true);
         DocumentBuilder builder = factory.newDocumentBuilder();
         document = builder.parse(inStream);
         document.getDocumentElement();
      }
      catch (Exception e)
      {
         e.printStackTrace();
      }
   }

   /**
    * Add the specified MIME header, as per JAXM.
    *
    * @param header the header to add
    * @param value  the value of that header
    */
   public void addMimeHeader(String header, String value)
   {
      mimeHeaders.addHeader(header, value);
   }

   /**
    * Content location.
    *
    * @return the content location
    */
   public String getContentLocation()
   {
      return getFirstMimeHeader(HTTPConstants.HEADER_CONTENT_LOCATION);
   }

   /**
    * Set content location.
    *
    * @param loc the content location
    */
   public void setContentLocation(String loc)
   {
      setMimeHeader(HTTPConstants.HEADER_CONTENT_LOCATION, loc);
   }

   /**
    * Sets Content-Id of this part.
    * already defined.
    *
    * @param newCid new Content-Id
    */
   public void setContentId(String newCid)
   {
      setMimeHeader(HTTPConstants.HEADER_CONTENT_ID, newCid);
   }

   /**
    * Content ID.
    *
    * @return the content ID
    */
   public String getContentId()
   {
      return getFirstMimeHeader(HTTPConstants.HEADER_CONTENT_ID);
   }

   /**
    * Get all headers that match.
    *
    * @param match an array of <code>String</code>s giving mime header names
    * @return an <code>Iterator</code> over all values matching these headers
    */
   public Iterator getMatchingMimeHeaders(final String[] match)
   {
      return mimeHeaders.getMatchingHeaders(match);
   }

   /**
    * Get all headers that do not match.
    *
    * @param match an array of <code>String</code>s giving mime header names
    * @return an <code>Iterator</code> over all values not matching these
    *         headers
    */
   public Iterator getNonMatchingMimeHeaders(final String[] match)
   {
      return mimeHeaders.getNonMatchingHeaders(match);
   }

   /**
    * Sets the content of the SOAPEnvelope object with the data from the given Source object.
    * This Source must contain a valid SOAP document.
    *
    * @param source the {@link javax.xml.transform.Source} object with the data to be set
    * @throws SOAPException if the implementation cannot convert the specified Source object
    */
   public void setContent(Source source) throws SOAPException
   {

      if (source == null)
         throw new SOAPException(Messages.getMessage("illegalArgumentException00"));

      //[todo-tdi] this is the place for the actual parsing, maybe

      this.contentSource = source;
      /*
      InputSource in = org.jboss.axis.utils.XMLUtils.sourceToInputSource(contentSource);
      InputStream is = in.getByteStream();
      if(is != null) {
          setCurrentMessage(is, FORM_INPUTSTREAM);
      } else {
          Reader r = in.getCharacterStream();
          if(r == null) {
              throw new SOAPException(Messages.getMessage("noCharacterOrByteStream"));
          }
          BufferedReader br = new BufferedReader(r);
          String line = null;
          StringBuffer sb = new StringBuffer();
          try {
              while((line = br.readLine()) != null) {
                  sb.append(line);
              }
          } catch (IOException e) {
              throw new SOAPException(Messages.getMessage("couldNotReadFromCharStream"), e);
          }
          setCurrentMessage(sb.toString(), FORM_STRING);
      }
      */
   }

   /**
    * Returns the content of the SOAPEnvelope as a JAXP <CODE>Source</CODE> object.
    *
    * @return the content as a <CODE> javax.xml.transform.Source</CODE> object
    * @throws javax.xml.soap.SOAPException if the implementation cannot convert the specified <CODE>Source</CODE> object
    * @see #setContent(javax.xml.transform.Source) setContent(javax.xml.transform.Source)
    */
   public Source getContent() throws SOAPException
   {
      /*
      if(contentSource == null) {
          switch(currentForm) {
          case FORM_STRING:
              String s = (String)currentMessage;
              contentSource = new StreamSource(new StringReader(s));
              break;
          case FORM_INPUTSTREAM:
              contentSource = new StreamSource((InputStream)currentMessage);
              break;
          case FORM_SOAPENVELOPE:
              SOAPEnvelopeImpl se = (SOAPEnvelopeImpl)currentMessage;
              try {
                  contentSource = new DOMSource(se.getAsDocument());
              } catch (Exception e) {
                  throw new SOAPException(Messages.getMessage("errorGetDocFromSOAPEnvelope"), e);
              }
              break;
          case FORM_BYTES:
              byte[] bytes = (byte[])currentMessage;
              contentSource = new StreamSource(new ByteArrayInputStream(bytes));
              break;
              case FORM_BODYINSTREAM:
              contentSource = new StreamSource((InputStream)currentMessage);
              break;
          }
      }
      */
      return contentSource;
   }

   /**
    * Retrieves all the headers for this <CODE>SOAPPart</CODE>
    * object as an iterator over the <CODE>MimeHeader</CODE>
    * objects.
    *
    * @return an <CODE>Iterator</CODE> object with all of the Mime
    *         headers for this <CODE>SOAPPart</CODE> object
    */
   public Iterator getAllMimeHeaders()
   {
      return mimeHeaders.getAllHeaders();
   }

   /**
    * Changes the first header entry that matches the given
    * header name so that its value is the given value, adding a
    * new header with the given name and value if no existing
    * header is a match. If there is a match, this method clears
    * all existing values for the first header that matches and
    * sets the given value instead. If more than one header has
    * the given name, this method removes all of the matching
    * headers after the first one.
    * <p/>
    * <P>Note that RFC822 headers can contain only US-ASCII
    * characters.</P>
    *
    * @param name  a <CODE>String</CODE> giving the
    *              header name for which to search
    * @param value a <CODE>String</CODE> giving the
    *              value to be set. This value will be substituted for the
    *              current value(s) of the first header that is a match if
    *              there is one. If there is no match, this value will be
    *              the value for a new <CODE>MimeHeader</CODE> object.
    * @ throws java.lang.IllegalArgumentException if
    * there was a problem with the specified mime header name
    * or value
    * @see #getMimeHeader(String) getMimeHeader(java.lang.String)
    */
   public void setMimeHeader(String name, String value)
   {
      mimeHeaders.setHeader(name, value);
   }

   /**
    * Gets all the values of the <CODE>MimeHeader</CODE> object
    * in this <CODE>SOAPPart</CODE> object that is identified by
    * the given <CODE>String</CODE>.
    *
    * @param name the name of the header; example:
    *             "Content-Type"
    * @return a <CODE>String</CODE> array giving all the values for
    *         the specified header
    * @see #setMimeHeader(String, String) setMimeHeader(java.lang.String, java.lang.String)
    */
   public String[] getMimeHeader(String name)
   {
      return mimeHeaders.getHeader(name);
   }

   /**
    * Removes all the <CODE>MimeHeader</CODE> objects for this
    * <CODE>SOAPEnvelope</CODE> object.
    */
   public void removeAllMimeHeaders()
   {
      mimeHeaders.removeAllHeaders();
   }

   /**
    * Removes all MIME headers that match the given name.
    *
    * @param header a <CODE>String</CODE> giving
    *               the name of the MIME header(s) to be removed
    */
   public void removeMimeHeader(String header)
   {
      mimeHeaders.removeHeader(header);
   }

   /**
    * Gets the <CODE>SOAPEnvelope</CODE> object associated with
    * this <CODE>SOAPPart</CODE> object. Once the SOAP envelope is
    * obtained, it can be used to get its contents.
    *
    * @return the <CODE>SOAPEnvelope</CODE> object for this <CODE>
    *         SOAPPart</CODE> object
    * @throws javax.xml.soap.SOAPException if there is a SOAP error
    */
   public SOAPEnvelope getEnvelope() throws SOAPException
   {
      return soapEnvelope;
   }

   /**
    * Get the specified MIME header.
    *
    * @param header the name of a MIME header
    * @return the value of the first header named <code>header</code>
    */
   private String getFirstMimeHeader(String header)
   {
      String[] values = mimeHeaders.getHeader(header);
      if (values != null && values.length > 0)
         return values[0];
      return null;
   }

   // org.w3c.dom.Document ******************************************************************************************

   public DocumentType getDoctype()
   {
      return document.getDoctype();
   }

   public DOMImplementation getImplementation()
   {
      return document.getImplementation();
   }

   public Element getDocumentElement()
   {
      return document.getDocumentElement();
   }

   public Element createElement(String tagName) throws DOMException
   {
      return document.createElement(tagName);
   }

   public DocumentFragment createDocumentFragment()
   {
      return document.createDocumentFragment();
   }

   public Text createTextNode(String data)
   {
      return document.createTextNode(data);
   }

   public Comment createComment(String data)
   {
      return document.createComment(data);
   }

   public CDATASection createCDATASection(String data) throws DOMException
   {
      return document.createCDATASection(data);
   }

   public ProcessingInstruction createProcessingInstruction(String target, String data) throws DOMException
   {
      return document.createProcessingInstruction(target, data);
   }

   public Attr createAttribute(String name) throws DOMException
   {
      return document.createAttribute(name);
   }

   public EntityReference createEntityReference(String name) throws DOMException
   {
      return document.createEntityReference(name);
   }

   public NodeList getElementsByTagName(String tagname)
   {
      return document.getElementsByTagName(tagname);
   }

   public Node importNode(Node importedNode, boolean deep) throws DOMException
   {
      return document.importNode(importedNode, deep);
   }

   public Element createElementNS(String namespaceURI, String qualifiedName)
           throws DOMException
   {
      return document.createElementNS(namespaceURI, qualifiedName);
   }

   public Attr createAttributeNS(String namespaceURI, String qualifiedName)
           throws DOMException
   {
      return document.createAttributeNS(namespaceURI, qualifiedName);
   }

   public NodeList getElementsByTagNameNS(String namespaceURI, String localName)
   {
      return document.getElementsByTagNameNS(namespaceURI, localName);
   }

   public Element getElementById(String elementId)
   {
      return document.getElementById(elementId);
   }

   // org.w3c.dom.Node *******************************************************************************************

   public String getNodeName()
   {
      return document.getNodeName();
   }

   public String getNodeValue() throws DOMException
   {
      return document.getNodeValue();
   }

   public void setNodeValue(String nodeValue) throws DOMException
   {
      document.setNodeValue(nodeValue);
   }

   public short getNodeType()
   {
      return document.getNodeType();
   }

   public Node getParentNode()
   {
      return document.getParentNode();
   }

   public NodeList getChildNodes()
   {
      return document.getChildNodes();
   }

   public Node getFirstChild()
   {
      return document.getFirstChild();
   }

   public Node getLastChild()
   {
      return document.getLastChild();
   }

   public Node getPreviousSibling()
   {
      return document.getPreviousSibling();
   }

   public Node getNextSibling()
   {
      return document.getNextSibling();
   }

   public NamedNodeMap getAttributes()
   {
      return document.getAttributes();
   }

   public Document getOwnerDocument()
   {
      return document.getOwnerDocument();
   }

   public Node insertBefore(Node newChild, Node refChild) throws DOMException
   {
      return document.insertBefore(newChild, refChild);
   }

   public Node replaceChild(Node newChild, Node oldChild) throws DOMException
   {
      return document.replaceChild(newChild, oldChild);
   }

   public Node removeChild(Node oldChild) throws DOMException
   {
      return document.removeChild(oldChild);
   }

   public Node appendChild(Node newChild) throws DOMException
   {
      return document.appendChild(newChild);
   }

   public boolean hasChildNodes()
   {
      return document.hasChildNodes();
   }

   public Node cloneNode(boolean deep)
   {
      return document.cloneNode(deep);
   }

   public void normalize()
   {
      document.normalize();
   }

   public boolean isSupported(String feature, String version)
   {
      return document.isSupported(feature, version);
   }

   public String getNamespaceURI()
   {
      return document.getNamespaceURI();
   }

   public String getPrefix()
   {
      return document.getPrefix();
   }

   public void setPrefix(String prefix) throws DOMException
   {
      document.setPrefix(prefix);
   }

   public String getLocalName()
   {
      return document.getLocalName();
   }

   public boolean hasAttributes()
   {
      return document.hasAttributes();
   }

   // DOM3-API start ***************************************************************************************************
   public String getInputEncoding()
   {
      return null;
   }

   public String getXmlEncoding()
   {
      return null;
   }

   public boolean getXmlStandalone()
   {
      return false;
   }

   public void setXmlStandalone(boolean xmlStandalone) throws DOMException
   {

   }

   public String getXmlVersion()
   {
      return null;
   }

   public void setXmlVersion(String xmlVersion) throws DOMException
   {

   }

   public boolean getStrictErrorChecking()
   {
      return false;
   }

   public void setStrictErrorChecking(boolean strictErrorChecking)
   {

   }

   public String getDocumentURI()
   {
      return null;
   }

   public void setDocumentURI(String documentURI)
   {

   }

   public Node adoptNode(Node source) throws DOMException
   {
      return null;
   }

   public DOMConfiguration getDomConfig()
   {
      return null;
   }

   public void normalizeDocument()
   {

   }

   public Node renameNode(Node n, String namespaceURI, String qualifiedName) throws DOMException
   {
      return null;
   }
   public String getBaseURI()
   {
      return null;
   }

   public short compareDocumentPosition(Node other) throws DOMException
   {
      return 0;
   }

   public String getTextContent() throws DOMException
   {
      return null;
   }

   public void setTextContent(String textContent) throws DOMException
   {

   }

   public boolean isSameNode(Node other)
   {
      return false;
   }

   public String lookupPrefix(String namespaceURI)
   {
      return null;
   }

   public boolean isDefaultNamespace(String namespaceURI)
   {
      return false;
   }

   public String lookupNamespaceURI(String prefix)
   {
      return null;
   }

   public boolean isEqualNode(Node arg)
   {
      return false;
   }

   public Object getFeature(String feature, String version)
   {
      return null;
   }

   public Object setUserData(String key, Object data, UserDataHandler handler)
   {
      return null;
   }

   public Object getUserData(String key)
   {
      return null;
   }
   // DOM3-API end *****************************************************************************************************
}
