/*
 * Copyright 2002-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jboss.axis.message;

import org.jboss.axis.AxisFault;
import org.jboss.axis.Constants;
import org.jboss.axis.encoding.DeserializationContext;
import org.jboss.axis.encoding.SerializationContext;
import org.jboss.axis.soap.SOAPConstants;
import org.jboss.axis.utils.Messages;
import org.jboss.logging.Logger;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;

import javax.xml.namespace.QName;
import javax.xml.rpc.JAXRPCException;
import javax.xml.soap.Name;
import javax.xml.soap.SOAPBodyElement;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFault;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

/**
 * Holder for body elements.
 *
 * @author Glyn Normington (glyn@apache.org)
 */
public class SOAPBodyAxisImpl extends SOAPBodyImpl
{

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

   private SOAPConstants soapConstants;

   private boolean disableFormatting;
   private boolean doSAAJEncodingCompliance;
   private static ArrayList knownEncodingStyles = new ArrayList();

   static
   {
      knownEncodingStyles.add(Constants.URI_SOAP11_ENC);
      knownEncodingStyles.add(Constants.URI_SOAP12_ENC);
      knownEncodingStyles.add("");
      knownEncodingStyles.add(Constants.URI_SOAP12_NOENC);
   }

   SOAPBodyAxisImpl(SOAPEnvelopeAxisImpl env, SOAPConstants soapConsts)
   {
      super(soapConsts.getEnvelopeURI(), Constants.ELEM_BODY);
      soapConstants = soapConsts;
      try
      {
         setParentElement(env);
      }
      catch (SOAPException ex)
      {
         // class cast should never fail when parent is a SOAPEnvelope
         log.fatal(Messages.getMessage("exception00"), ex);
      }
   }

   public SOAPBodyAxisImpl(String namespace, String localPart, String prefix, Attributes attributes,
                           DeserializationContext context, SOAPConstants soapConsts) throws AxisFault
   {
      super(namespace, localPart, prefix, attributes, context);
      soapConstants = soapConsts;
   }

   public void setParentElement(SOAPElement parent) throws SOAPException
   {
      if (parent == null)
         throw new IllegalArgumentException(Messages.getMessage("nullParent00"));

      try
      {
         SOAPEnvelopeAxisImpl env = (SOAPEnvelopeAxisImpl)parent;
         super.setParentElement(env);
         setEnvelope(env);
      }
      catch (Throwable t)
      {
         throw new SOAPException(t);
      }
   }

   public void disableFormatting()
   {
      this.disableFormatting = true;
   }

   public void setEncodingStyle(String encodingStyle) throws SOAPException
   {
      if (encodingStyle == null)
      {
         encodingStyle = "";
      }

      if (doSAAJEncodingCompliance)
      {
         // Make sure this matches a known encodingStyle.  This is
         if (!knownEncodingStyles.contains(encodingStyle))
            throw new IllegalArgumentException(Messages.getMessage("badEncodingStyle1", encodingStyle));
      }

      super.setEncodingStyle(encodingStyle);
   }

   protected void outputImpl(SerializationContext context) throws Exception
   {
      boolean oldPretty = context.getPretty();
      context.setPretty(!disableFormatting);

      if (getChildren().isEmpty())
      {
         // This is a problem.
         // throw new Exception("No body elements!");
         // If there are no body elements just return - it's ok that
         // the body is empty
      }

      // Output <SOAP-ENV:Body>
      context.startElement(new QName(soapConstants.getEnvelopeURI(),
              Constants.ELEM_BODY), getAttributesEx());
/*
        Enumeration enumeration = bodyElements.elements();
        while (enumeration.hasMoreElements()) {
            SOAPBodyElement body = (SOAPBodyElement)enumeration.nextElement();
                                       Constants.ELEM_BODY), getAttributes());
*/
      for (Iterator it = getChildElements(); it.hasNext();)
      {
         Node childNode = (Node)it.next();
         if (childNode instanceof SOAPElementAxisImpl)
            ((SOAPElementAxisImpl)childNode).output(context);
         else if (childNode instanceof TextImpl)
            context.writeString(childNode.getNodeValue());
      }

      // Output multi-refs if appropriate
      context.outputMultiRefs();

      // Output </SOAP-ENV:Body>
      context.endElement();

      context.setPretty(oldPretty);
   }

   List getBodyElements() throws AxisFault
   {
      return getChildren();
   }

   void addBodyElement(SOAPBodyElementAxisImpl element)
   {
      if (log.isDebugEnabled())
         log.debug(Messages.getMessage("addBody00"));
      try
      {
         element.setParentElement(this);
      }
      catch (SOAPException ex)
      {
         // class cast should never fail when parent is a SOAPBody
         log.fatal(Messages.getMessage("exception00"), ex);
      }
   }

   void clearBody()
   {
      removeContents();
   }

   void removeBodyElement(SOAPBodyElementAxisImpl element)
   {
      if (log.isDebugEnabled())
         log.debug(Messages.getMessage("removeBody00"));
      removeChild(element);
   }

   SOAPBodyElementAxisImpl getBodyByName(String namespace, String localPart)
           throws AxisFault
   {
      return (SOAPBodyElementAxisImpl)findElement(getChildren(),
              namespace,
              localPart);
   }

   protected SOAPElementAxisImpl findElement(List list, String namespace, String localPart)
   {
      if (list.isEmpty())
         return null;

      QName qname = new QName(namespace, localPart);
      Iterator it = list.iterator();
      while (it.hasNext())
      {
         SOAPElementAxisImpl element = (SOAPElementAxisImpl)it.next();
         if (element.getQName().equals(qname))
            return element;
      }

      return null;
   }

   SOAPBodyElementAxisImpl getFirstBody() throws AxisFault
   {
      if (getChildren().isEmpty())
         return null;
/*
        return (SOAPBodyElement)bodyElements.elementAt(0);
*/
      return (SOAPBodyElementAxisImpl)getChildren().get(0);
   }

   // JAXM methods

   public SOAPBodyElement addBodyElement(Name name)
           throws SOAPException
   {
      SOAPBodyElementAxisImpl bodyElement = new SOAPBodyElementAxisImpl(name);
      addBodyElement(bodyElement);
      return bodyElement;
   }

   public SOAPFault addFault(Name name, String s, Locale locale) throws SOAPException
   {
      AxisFault af = new AxisFault(new QName(name.getURI(), name.getLocalName()), s, "", new Element[0]);
      SOAPFaultImpl fault = new SOAPFaultImpl(af);
      addBodyElement(fault);
      return fault;
   }

   public SOAPFault addFault(Name name, String s) throws SOAPException
   {
      AxisFault af = new AxisFault(new QName(name.getURI(), name.getLocalName()), s, "", new Element[0]);
      SOAPFaultImpl fault = new SOAPFaultImpl(af);
      addBodyElement(fault);
      return fault;
   }

   public SOAPBodyElement addDocument(Document document) throws SOAPException
   {
      return (SOAPBodyElement)importDOMElement(this, document.getDocumentElement());
   }

   public SOAPFault addFault() throws SOAPException
   {

      AxisFault af = new AxisFault(new QName(Constants.NS_URI_AXIS, Constants.FAULT_SERVER_GENERAL), "", "", new Element[0]);
      SOAPFaultImpl fault = new SOAPFaultImpl(af);
      addBodyElement(fault);
      return fault;
   }

   public SOAPFault getFault()
   {
      Iterator it = getChildren().iterator();
      while (it.hasNext())
      {
         Object element = it.next();
         if (element instanceof SOAPFault)
         {
            return (SOAPFault)element;
         }
      }
      return null;
   }

   public boolean hasFault()
   {
      Iterator it = getChildren().iterator();
      while (it.hasNext())
      {
         if (it.next() instanceof SOAPFault)
         {
            return true;
         }
      }
      return false;
   }

   public void setSAAJEncodingCompliance(boolean comply)
   {
      this.doSAAJEncodingCompliance = true;
   }

   public SOAPElement addChildElement(Name name) throws SOAPException
   {
      SOAPBodyElementAxisImpl soapBodyElement = new SOAPBodyElementAxisImpl(name);
      super.addChildElement(soapBodyElement);
      return soapBodyElement;
   }

   /** Recursive function
    */
   private SOAPElementAxisImpl importDOMElement(SOAPElementAxisImpl soapParent, Element domElement)
   {
      try
      {
         NameImpl name;
         if (domElement.getNamespaceURI() != null)
            name = new NameImpl(domElement.getLocalName(), domElement.getPrefix(), domElement.getNamespaceURI());
         else
            name = new NameImpl(domElement.getLocalName());

         SOAPElementAxisImpl soapChild = (SOAPElementAxisImpl)soapParent.addChildElement(name);

         NamedNodeMap attrs = ((Element)domElement).getAttributes();
         for (int i = 0; i < attrs.getLength(); i++)
         {
            Attr att = (Attr)attrs.item(i);
            String nsURI = att.getNamespaceURI();
            String localName = att.getLocalName();
            String value = att.getValue();

            if (nsURI != null)
               soapChild.setAttributeNS(nsURI, localName, value);
            else
               soapChild.setAttribute(localName, value);
         }

         StringBuffer content = new StringBuffer();

         NodeList children = domElement.getChildNodes();
         for (int i = 0; i < children.getLength(); i++)
         {
            Node domChild = children.item(i);
            if (domChild.getNodeType() == Node.ELEMENT_NODE)
               importDOMElement(soapChild, (Element)domChild);

            if (domChild.getNodeType() == Node.TEXT_NODE && domChild.getNodeValue().trim().length() > 0)
               content.append(domChild.getNodeValue());
         }

         if (content.length() > 0)
         {
            String value = content.toString();
            soapChild.addTextNode(value);
         }

         return soapChild;
      }
      catch (RuntimeException e)
      {
         throw e;
      }
      catch (Exception e)
      {
         throw new JAXRPCException(e);
      }
   }
}