/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.apache.axis2.saaj;

import org.apache.axiom.attachments.Attachments;
import org.apache.axiom.mime.ContentType;
import org.apache.axiom.mime.MediaType;
import org.apache.axiom.om.OMException;
import org.apache.axiom.om.OMOutputFormat;
import org.apache.axiom.om.impl.OMMultipartWriter;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axiom.soap.SOAPVersion;
import org.apache.axiom.util.UIDGenerator;
import org.apache.axiom.util.activation.DataHandlerUtils;
import org.apache.axis2.saaj.util.SAAJUtil;
import org.apache.axis2.kernel.http.HTTPConstants;

import jakarta.xml.soap.AttachmentPart;
import jakarta.xml.soap.MimeHeader;
import jakarta.xml.soap.MimeHeaders;
import jakarta.xml.soap.SOAPBody;
import jakarta.xml.soap.SOAPElement;
import jakarta.xml.soap.SOAPException;
import jakarta.xml.soap.SOAPHeader;
import jakarta.xml.soap.SOAPMessage;
import jakarta.xml.soap.SOAPPart;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;

public class SOAPMessageImpl extends SOAPMessage {

    private SOAPPart soapPart;
    private Collection<AttachmentPart> attachmentParts = new ArrayList<AttachmentPart>();
    private MimeHeaders mimeHeaders;

    private Map<String,Object> props = new Hashtable<String,Object>();
    private boolean saveRequired;

    public SOAPMessageImpl(SOAPEnvelopeImpl soapEnvelope) {
        this.mimeHeaders = new MimeHeaders();
        this.mimeHeaders.addHeader("content-type", ((SOAPFactory)soapEnvelope.omTarget.getOMFactory()).getSOAPVersion().getMediaType().toString());
        soapPart = new SOAPPartImpl(this, soapEnvelope);
    }

    public SOAPMessageImpl(InputStream inputstream, MimeHeaders mimeHeaders, boolean processMTOM)
            throws SOAPException {
        String contentType = null;
        String tmpContentType = "";
        if (mimeHeaders != null) {
            String contentTypes[] = mimeHeaders.getHeader(HTTPConstants.HEADER_CONTENT_TYPE);
            if (contentTypes != null && contentTypes.length > 0) {
                tmpContentType = contentTypes[0];
                contentType = SAAJUtil.normalizeContentType(tmpContentType);
            }
        }
        if (HTTPConstants.MEDIA_TYPE_MULTIPART_RELATED.equals(contentType)) {
            try {
                Attachments attachments =
                        new Attachments(inputstream, tmpContentType, false, "", "");
                
                // Axiom doesn't give us access to the MIME headers of the individual
                // parts of the SOAP message package. We need to reconstruct them from
                // the available information.
                MimeHeaders soapPartHeaders = new MimeHeaders();
                soapPartHeaders.addHeader(HTTPConstants.HEADER_CONTENT_TYPE,
                        attachments.getRootPartContentType());
                String soapPartContentId = attachments.getRootPartContentID();
                soapPartHeaders.addHeader("Content-ID", "<" + soapPartContentId + ">");
                
                soapPart = new SOAPPartImpl(this, attachments.getRootPartInputStream(),
                        soapPartHeaders, processMTOM ? attachments : null);
                
                for (String contentId : attachments.getAllContentIDs()) {
                    if (!contentId.equals(soapPartContentId)) {
                        AttachmentPart ap =
                                createAttachmentPart(attachments.getDataHandler(contentId));
                        ap.setContentId("<" + contentId + ">");
                        attachmentParts.add(ap);
                    }
                }
            } catch (OMException e) {
                throw new SOAPException(e);
            }
        } else {
            initCharsetEncodingFromContentType(tmpContentType);
            soapPart = new SOAPPartImpl(this, inputstream, mimeHeaders, null);
        }

        this.mimeHeaders = (mimeHeaders == null) ?
                new MimeHeaders() :
                SAAJUtil.copyMimeHeaders(mimeHeaders);
    }

    /**
     * Retrieves a description of this <CODE>SOAPMessage</CODE> object's content.
     *
     * @return a <CODE>String</CODE> describing the content of this message or <CODE>null</CODE> if
     *         no description has been set
     * @see #setContentDescription(String) setContentDescription(java.lang.String)
     */
    public String getContentDescription() {
        String values[] = mimeHeaders.getHeader(HTTPConstants.HEADER_CONTENT_DESCRIPTION);
        if (values != null && values.length > 0) {
            return values[0];
        }
        return null;
    }

    /**
     * Sets the description of this <CODE>SOAPMessage</CODE> object's content with the given
     * description.
     *
     * @param description a <CODE>String</CODE> describing the content of this message
     * @see #getContentDescription() getContentDescription()
     */
    public void setContentDescription(String description) {
        mimeHeaders.setHeader(HTTPConstants.HEADER_CONTENT_DESCRIPTION, description);
    }

    /**
     * Gets the SOAP part of this <CODE>SOAPMessage</CODE> object.
     * <p/>
     * <p/>
     * <P>If a <CODE>SOAPMessage</CODE> object contains one or more attachments, the SOAP Part must
     * be the first MIME body part in the message.</P>
     *
     * @return the <CODE>SOAPPart</CODE> object for this <CODE> SOAPMessage</CODE> object
     */
    public SOAPPart getSOAPPart() {
        return soapPart;
    }

    /**
     * Removes all <CODE>AttachmentPart</CODE> objects that have been added to this
     * <CODE>SOAPMessage</CODE> object.
     * <p/>
     * <P>This method does not touch the SOAP part.</P>
     */
    public void removeAllAttachments() {
        attachmentParts.clear();
        saveRequired = true;
    }

    /**
     * Gets a count of the number of attachments in this message. This count does not include the
     * SOAP part.
     *
     * @return the number of <CODE>AttachmentPart</CODE> objects that are part of this
     *         <CODE>SOAPMessage</CODE> object
     */
    public int countAttachments() {
        return attachmentParts.size();
    }

    /**
     * Retrieves all the <CODE>AttachmentPart</CODE> objects that are part of this
     * <CODE>SOAPMessage</CODE> object.
     *
     * @return an iterator over all the attachments in this message
     */
    public Iterator getAttachments() {
        return attachmentParts.iterator();
    }

    /**
     * Retrieves all the AttachmentPart objects that have header entries that match the specified
     * headers. Note that a returned attachment could have headers in addition to those specified.
     *
     * @param headers a {@link jakarta.xml.soap.MimeHeaders} object containing the MIME headers for
     *                which to search
     * @return an iterator over all attachments({@link jakarta.xml.soap.AttachmentPart}) that have a
     *         header that matches one of the given headers
     */
    public Iterator getAttachments(jakarta.xml.soap.MimeHeaders headers) {
        Collection<AttachmentPart> matchingAttachmentParts = new ArrayList<AttachmentPart>();
        Iterator iterator = getAttachments();
        {
            AttachmentPartImpl part;
            while (iterator.hasNext()) {
                part = (AttachmentPartImpl)iterator.next();
                if (part.matches(headers)) {
                    matchingAttachmentParts.add(part);
                }
            }
        }
        return matchingAttachmentParts.iterator();
    }

    /**
     * Adds the given <CODE>AttachmentPart</CODE> object to this <CODE>SOAPMessage</CODE> object. An
     * <CODE> AttachmentPart</CODE> object must be created before it can be added to a message.
     *
     * @param attachmentPart an <CODE> AttachmentPart</CODE> object that is to become part of this
     *                       <CODE>SOAPMessage</CODE> object
     * @throws IllegalArgumentException
     *
     */
    public void addAttachmentPart(AttachmentPart attachmentPart) {
        if (attachmentPart != null) {
            attachmentParts.add(attachmentPart);
            mimeHeaders.setHeader(HTTPConstants.HEADER_CONTENT_TYPE, "multipart/related");
            saveRequired = true;
        }
    }

    /**
     * Creates a new empty <CODE>AttachmentPart</CODE> object. Note that the method
     * <CODE>addAttachmentPart</CODE> must be called with this new <CODE>AttachmentPart</CODE>
     * object as the parameter in order for it to become an attachment to this
     * <CODE>SOAPMessage</CODE> object.
     *
     * @return a new <CODE>AttachmentPart</CODE> object that can be populated and added to this
     *         <CODE>SOAPMessage</CODE> object
     */
    public AttachmentPart createAttachmentPart() {
        return new AttachmentPartImpl();
    }

    /**
     * Returns all the transport-specific MIME headers for this <CODE>SOAPMessage</CODE> object in a
     * transport-independent fashion.
     *
     * @return a <CODE>MimeHeaders</CODE> object containing the <CODE>MimeHeader</CODE> objects
     */
    public jakarta.xml.soap.MimeHeaders getMimeHeaders() {
        return mimeHeaders;
    }

    /**
     * Updates this <CODE>SOAPMessage</CODE> object with all the changes that have been made to it.
     * This method is called automatically when a message is sent or written to by the methods
     * <CODE>ProviderConnection.send</CODE>, <CODE> SOAPConnection.call</CODE>, or <CODE>
     * SOAPMessage.writeTo</CODE>. However, if changes are made to a message that was received or to
     * one that has already been sent, the method <CODE>saveChanges</CODE> needs to be called
     * explicitly in order to save the changes. The method <CODE>saveChanges</CODE> also generates
     * any changes that can be read back (for example, a MessageId in profiles that support a
     * message id). All MIME headers in a message that is created for sending purposes are
     * guaranteed to have valid values only after <CODE>saveChanges</CODE> has been called.
     * <p/>
     * <P>In addition, this method marks the point at which the data from all constituent
     * <CODE>AttachmentPart</CODE> objects are pulled into the message.</P>
     *
     * @throws SOAPException if there was a problem saving changes to this message.
     */
    public void saveChanges() throws SOAPException {
        try {
            String contentTypeValue = getSingleHeaderValue(HTTPConstants.HEADER_CONTENT_TYPE);
            ContentType.Builder contentType;
            if (isEmptyString(contentTypeValue)) {
                contentType = ContentType.builder().setMediaType(attachmentParts.size() > 0 ? MediaType.MULTIPART_RELATED : getMediaType());
            } else {
                contentType = new ContentType(contentTypeValue).toBuilder();
                //Use configures the baseType with multipart/related while no attachment exists or all the attachments are removed
                if (contentType.getMediaType().equals(MediaType.MULTIPART_RELATED) && attachmentParts.size() == 0) {
                    contentType.setMediaType(getMediaType());
                    contentType.clearParameters();
                }
            }
           
            //If it is of multipart/related, initialize those required values in the content-type, including boundary etc.
            if (contentType.getMediaType().equals(MediaType.MULTIPART_RELATED)) {
                
                //Configure boundary
                String boundaryParam = contentType.getParameter("boundary");
                if (isEmptyString(boundaryParam)) {
                    contentType.setParameter("boundary", UIDGenerator.generateMimeBoundary());
                }

                //Configure start content id, always get it from soapPart in case it is changed
                String soapPartContentId = soapPart.getContentId();
                if (isEmptyString(soapPartContentId)) {
                    soapPartContentId = "<" + UIDGenerator.generateContentId() + ">";
                    soapPart.setContentId(soapPartContentId);
                }
                contentType.setParameter("start", soapPartContentId);
                
                //Configure contentId for each attachments
                for(AttachmentPart attachmentPart : attachmentParts) {
                    if(isEmptyString(attachmentPart.getContentId())) {
                        attachmentPart.setContentId("<" + UIDGenerator.generateContentId() + ">");
                    }
                }
                
                //Configure type                
                contentType.setParameter("type", getMediaType().toString());
                
                //Configure charset
                String soapPartContentTypeValue = getSingleHeaderValue(soapPart.getMimeHeader(HTTPConstants.HEADER_CONTENT_TYPE));
                ContentType.Builder soapPartContentType;
                if (isEmptyString(soapPartContentTypeValue)) {
                    soapPartContentType = new ContentType(soapPartContentTypeValue).toBuilder();
                } else {
                    soapPartContentType = ContentType.builder().setMediaType(getMediaType());
                }                
                setCharsetParameter(soapPartContentType);
            } else {
                //Configure charset
                setCharsetParameter(contentType);
            }
            
            mimeHeaders.setHeader(HTTPConstants.HEADER_CONTENT_TYPE, contentType.build().toString());
        } catch (ParseException e) {
            throw new SOAPException("Invalid Content Type Field in the Mime Message", e);
        }

        saveRequired = false;
    }

    public void setSaveRequired() {
        this.saveRequired = true;
    }

    /**
     * Indicates whether this <CODE>SOAPMessage</CODE> object has had the method {@link
     * #saveChanges()} called on it.
     *
     * @return <CODE>true</CODE> if <CODE>saveChanges</CODE> has been called on this message at
     *         least once; <CODE> false</CODE> otherwise.
     */
    public boolean saveRequired() {
        return saveRequired;
    }

    /**
     * Writes this <CODE>SOAPMessage</CODE> object to the given output stream. The externalization
     * format is as defined by the SOAP 1.1 with Attachments specification.
     * <p/>
     * <P>If there are no attachments, just an XML stream is written out. For those messages that
     * have attachments, <CODE>writeTo</CODE> writes a MIME-encoded byte stream.</P>
     *
     * @param out the <CODE>OutputStream</CODE> object to which this <CODE>SOAPMessage</CODE> object
     *            will be written
     * @throws SOAPException if there was a problem in externalizing this SOAP message
     * @throws IOException   if an I/O error occurs
     */
    public void writeTo(OutputStream out) throws SOAPException, IOException {
        try {           
            saveChanges();
            OMOutputFormat format = new OMOutputFormat();
            String enc = (String)getProperty(CHARACTER_SET_ENCODING);
            format.setCharSetEncoding(enc != null ? enc : OMOutputFormat.DEFAULT_CHAR_SET_ENCODING);
            String writeXmlDecl = (String)getProperty(WRITE_XML_DECLARATION);
            if (writeXmlDecl == null || writeXmlDecl.equals("false")) {
                //SAAJ default case doesn't send XML decl
                format.setIgnoreXMLDeclaration(true);
            }
            
            SOAPEnvelope envelope = ((SOAPEnvelopeImpl) soapPart.getEnvelope()).getOMTarget();
            if (attachmentParts.isEmpty()) {
                envelope.serialize(out, format);
            } else {
                ContentType.Builder contentType = new ContentType(getSingleHeaderValue(HTTPConstants.HEADER_CONTENT_TYPE)).toBuilder();
                String boundary = contentType.getParameter("boundary");
                if(isEmptyString(boundary)) {
                    boundary = UIDGenerator.generateMimeBoundary();
                    contentType.setParameter("boundary", boundary);
                }
                format.setMimeBoundary(boundary);

                String rootContentId = soapPart.getContentId();
                if(isEmptyString(rootContentId)) {
                    rootContentId = "<" + UIDGenerator.generateContentId() + ">";
                    soapPart.setContentId(rootContentId);
                }                
                contentType.setParameter("start", rootContentId);
                if ((rootContentId.indexOf("<") > -1) & (rootContentId.indexOf(">") > -1)) {
                    rootContentId = rootContentId.substring(1, (rootContentId.length() - 1));
                }
                format.setRootContentId(rootContentId);

                format.setSOAP11(((SOAPFactory)((SOAPEnvelopeImpl) soapPart.getEnvelope()).omTarget.getOMFactory()).getSOAPVersion() == SOAPVersion.SOAP11);
                
                //Double save the content-type in case anything is updated
                mimeHeaders.setHeader(HTTPConstants.HEADER_CONTENT_TYPE, contentType.build().toString());

                OMMultipartWriter mpw = new OMMultipartWriter(out, format);
                OutputStream rootPartOutputStream = mpw.writeRootPart();
                envelope.serialize(rootPartOutputStream);
                rootPartOutputStream.close();
                for (AttachmentPart ap : attachmentParts) {
                    mpw.writePart(DataHandlerUtils.toBlob(ap.getDataHandler()), ap.getContentId());
                }
                mpw.complete();
            }

            saveRequired = true;
        } catch (Exception e) {
            throw new SOAPException(e);
        }
    }

    /**
     * Associates the specified value with the specified property. If there was already a value
     * associated with this property, the old value is replaced.
     * <p/>
     * The valid property names include <code>WRITE_XML_DECLARATION</code> and
     * <code>CHARACTER_SET_ENCODING</code>. All of these standard SAAJ properties are prefixed by
     * "jakarta.xml.soap". Vendors may also add implementation specific properties. These properties
     * must be prefixed with package names that are unique to the vendor.
     * <p/>
     * Setting the property <code>WRITE_XML_DECLARATION</code> to <code>"true"</code> will cause an
     * XML Declaration to be written out at the start of the SOAP message. The default value of
     * "false" suppresses this declaration.
     * <p/>
     * The property <code>CHARACTER_SET_ENCODING</code> defaults to the value <code>"utf-8"</code>
     * which causes the SOAP message to be encoded using UTF-8. Setting
     * <code>CHARACTER_SET_ENCODING</code> to <code>"utf-16"</code> causes the SOAP message to be
     * encoded using UTF-16.
     * <p/>
     * Some implementations may allow encodings in addition to UTF-8 and UTF-16. Refer to your
     * vendor's documentation for details.
     *
     * @param property the property with which the specified value is to be associated
     * @param value    the value to be associated with the specified property
     */
    public void setProperty(String property, Object value) {
        props.put(property, value);
    }

    /**
     * Retrieves value of the specified property.
     *
     * @param property the name of the property to retrieve
     * @return the value of the property or <code>null</code> if no such property exists
     * @throws SOAPException if the property name is not recognized
     */
    public Object getProperty(String property) throws SOAPException {
        return props.get(property);
    }

    /**
     * Returns an AttachmentPart object that is associated with an attachment that is referenced by
     * this SOAPElement or null if no such attachment exists. References can be made via an href
     * attribute as described in SOAP Messages with Attachments (http://www.w3.org/TR/SOAPattachments#SOAPReferenceToAttachements)
     * , or via a single Text child node containing a URI as described in the WS-I Attachments
     * Profile 1.0 for elements of schema type ref:swaRef(ref:swaRef (http://www.wsi.org/Profiles/AttachmentsProfile-1.0-2004-08-24.html")
     * ). These two mechanisms must be supported. The support for references via href attribute also
     * implies that this method should also be supported on an element that is an xop:Include
     * element (XOP (http://www.w3.org/2000/xp/Group/3/06/Attachments/XOP.html) ). other reference
     * mechanisms may be supported by individual implementations of this standard. Contact your
     * vendor for details.
     *
     * @param element - The SOAPElement containing the reference to an Attachment
     * @return the referenced AttachmentPart or null if no such AttachmentPart exists or no
     *         reference can be found in this SOAPElement.
     * @throws SOAPException - if there is an error in the attempt to access the attachment
     */
    public AttachmentPart getAttachment(SOAPElement soapelement) throws SOAPException {
        //TODO read strings from constants
        Iterator iterator = getAttachments();
        {
            AttachmentPartImpl attachmentPart;
            while (iterator.hasNext()) {
                attachmentPart = (AttachmentPartImpl)iterator.next();
                String[] contentIds = attachmentPart.getMimeHeader("Content-Id");

                //References can be made via an href attribute as described in SOAP Messages
                //with Attachments or via a single Text child node containing a URI
                String reference = soapelement.getAttribute("href");
                if (reference == null || reference.trim().length() == 0) {
                    reference = soapelement.getValue();
                    if (reference == null || reference.trim().length() == 0) {
                        return null;
                    }
                }

                for (int a = 0; a < contentIds.length; a++) {
                    //eg: cid:gifImage scenario
                    String idPart = reference.substring(reference.indexOf(":") + 1);
                    idPart = "<" + idPart + ">";
                    if (idPart.equals(contentIds[a])) {
                        return attachmentPart;
                    }
                }

                String[] contentLocations = attachmentPart.getMimeHeader("Content-Location");
                if (!(contentLocations == null)) {
                    //uri scenario
                    for (int b = 0; b < contentLocations.length; b++) {
                        if (reference.equals(contentLocations[b])) {
                            return attachmentPart;
                        }
                    }
                }
            }
        }
        return null;
    }

    /**
     * Removes all the AttachmentPart objects that have header entries that match the specified
     * headers. Note that the removed attachment could have headers in addition to those specified.
     *
     * @param headers - a MimeHeaders object containing the MIME headers for which to search
     * @since SAAJ 1.3
     */
    public void removeAttachments(MimeHeaders headers) {
        Collection<AttachmentPart> newAttachmentParts = new ArrayList<AttachmentPart>();
        for (AttachmentPart attachmentPart : attachmentParts) {
            //Get all the headers
            for (Iterator iterator = headers.getAllHeaders(); iterator.hasNext();) {
                MimeHeader mimeHeader = (MimeHeader)iterator.next();
                String[] headerValues = attachmentPart.getMimeHeader(mimeHeader.getName());
                //if values for this header name, do not remove it
                if (headerValues.length != 0) {
                    if (!(headerValues[0].equals(mimeHeader.getValue()))) {
                        newAttachmentParts.add(attachmentPart);
                    }
                }
            }
        }
        attachmentParts.clear();
        this.attachmentParts = newAttachmentParts;
        saveRequired = true;
    }

    /**
     * Gets the SOAP Header contained in this <code>SOAPMessage</code> object.
     *
     * @return the <code>SOAPHeader</code> object contained by this <code>SOAPMessage</code> object
     * @throws jakarta.xml.soap.SOAPException if the SOAP Header does not exist or cannot be
     *                                      retrieved
     */
    public SOAPHeader getSOAPHeader() throws SOAPException {
        return this.soapPart.getEnvelope().getHeader();
    }

    /**
     * Gets the SOAP Body contained in this <code>SOAPMessage</code> object.
     *
     * @return the <code>SOAPBody</code> object contained by this <code>SOAPMessage</code> object
     * @throws jakarta.xml.soap.SOAPException if the SOAP Body does not exist or cannot be retrieved
     */
    public SOAPBody getSOAPBody() throws SOAPException {
        return this.soapPart.getEnvelope().getBody();
    }

    /**
     * Set the character encoding based on the <code>contentType</code> parameter
     *
     * @param contentType
     */
    private void initCharsetEncodingFromContentType(final String contentType) {
        if (contentType != null) {
            int delimiterIndex = contentType.lastIndexOf("charset");
            if (delimiterIndex > 0) {
                String charsetPart = contentType.substring(delimiterIndex);
                int charsetIndex = charsetPart.indexOf('=');
                String charset = charsetPart.substring(charsetIndex + 1).trim();
                if ((charset.startsWith("\"") || charset.startsWith("\'"))) {
                    charset = charset.substring(1, charset.length());
                }
                if ((charset.endsWith("\"") || charset.endsWith("\'"))) {
                    charset = charset.substring(0, charset.length() - 1);
                }
                int index = charset.indexOf(';');
                if (index != -1) {
                    charset = charset.substring(0, index);
                }
                setProperty(SOAPMessage.CHARACTER_SET_ENCODING, charset);
            }
        }
    }
    
    private boolean isEmptyString(String value) {
        return value == null || value.length() == 0;
    }
    
    private String getSingleHeaderValue(String[] values) {
        return values != null && values.length > 0 ? values[0] : null;
    }

    private String getSingleHeaderValue(String name) {
        String[] values = mimeHeaders.getHeader(name);
        if (values == null || values.length == 0) {
            return null;
        } else {
            return values[0];
        }
    }
    
    private MediaType getMediaType() throws SOAPException {
        return ((SOAPFactory)((SOAPEnvelopeImpl) soapPart.getEnvelope()).omTarget.getOMFactory()).getSOAPVersion().getMediaType();
    }
    
    /**
     * If the charset is configured by CHARACTER_SET_ENCODING, set it in the contentPart always. 
     * If it has already been configured in the contentType, leave it there.
     * UTF-8 is used as the default value. 
     * @param contentType
     * @throws SOAPException
     */
    private void setCharsetParameter(ContentType.Builder contentType) throws SOAPException{
        String charset = (String)getProperty(CHARACTER_SET_ENCODING); 
        if (!isEmptyString(charset)) {
            contentType.setParameter("charset", charset);
        } else {
            charset = contentType.getParameter("charset");
            if(isEmptyString(charset)) {
                contentType.setParameter("charset", "UTF-8");
            }
        }
    }
}
