/*
 * 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 javax.xml.soap;

/**
 * An exception that signals that a SOAP exception has occurred. A <CODE>SOAPException</CODE> object
 * may contain a <CODE>String</CODE> that gives the reason for the exception, an embedded
 * <CODE>Throwable</CODE> object, or both. This class provides methods for retrieving reason
 * messages and for retrieving the embedded <CODE>Throwable</CODE> object.</P>
 * <p/>
 * <P>Typical reasons for throwing a <CODE>SOAPException</CODE> object are problems such as
 * difficulty setting a header, not being able to send a message, and not being able to get a
 * connection with the provider. Reasons for embedding a <CODE> Throwable</CODE> object include
 * problems such as input/output errors or a parsing problem, such as an error in parsing a header.
 */
public class SOAPException extends Exception {

    private static final long serialVersionUID = 5083961510786058130L;

    /**
     * Constructs a <CODE>SOAPException</CODE> object with no reason or embedded
     * <CODE>Throwable</CODE> object.
     */
    public SOAPException() {
        cause = null;
    }

    /**
     * Constructs a <CODE>SOAPException</CODE> object with the given <CODE>String</CODE> as the
     * reason for the exception being thrown.
     *
     * @param reason a description of what caused the exception
     */
    public SOAPException(String reason) {

        super(reason);

        cause = null;
    }

    /**
     * Constructs a <CODE>SOAPException</CODE> object with the given <CODE>String</CODE> as the
     * reason for the exception being thrown and the given <CODE>Throwable</CODE> object as an
     * embedded exception.
     *
     * @param reason a description of what caused the exception
     * @param cause  a <CODE>Throwable</CODE> object that is to be embedded in this
     *               <CODE>SOAPException</CODE> object
     */
    public SOAPException(String reason, Throwable cause) {

        super(reason);

        initCause(cause);
    }

    /**
     * Constructs a <CODE>SOAPException</CODE> object initialized with the given
     * <CODE>Throwable</CODE> object.
     *
     * @param cause a <CODE>Throwable</CODE> object that is to be embedded in this
     *              <CODE>SOAPException</CODE> object
     */
    public SOAPException(Throwable cause) {

        super(cause.toString());

        initCause(cause);
    }

    /**
     * Returns the detail message for this <CODE> SOAPException</CODE> object.
     * <p/>
     * <P>If there is an embedded <CODE>Throwable</CODE> object, and if the
     * <CODE>SOAPException</CODE> object has no detail message of its own, this method will return
     * the detail message from the embedded <CODE>Throwable</CODE> object.</P>
     *
     * @return the error or warning message for this <CODE> SOAPException</CODE> or, if it has none,
     *         the message of the embedded <CODE>Throwable</CODE> object, if there is one
     */
    public String getMessage() {

        String s = super.getMessage();

        if ((s == null) && (cause != null)) {
            return cause.getMessage();
        } else {
            return s;
        }
    }

    /**
     * Returns the <CODE>Throwable</CODE> object embedded in this <CODE>SOAPException</CODE> if
     * there is one. Otherwise, this method returns <CODE>null</CODE>.
     *
     * @return the embedded <CODE>Throwable</CODE> object or <CODE> null</CODE> if there is none
     */
    public Throwable getCause() {
        return cause;
    }

    /**
     * Initializes the <CODE>cause</CODE> field of this <CODE> SOAPException</CODE> object with the
     * given <CODE> Throwable</CODE> object.
     * <p/>
     * <P>This method can be called at most once. It is generally called from within the constructor
     * or immediately after the constructor has returned a new <CODE>SOAPException</CODE> object. If
     * this <CODE>SOAPException</CODE> object was created with the constructor {@link
     * #SOAPException(java.lang.Throwable) SOAPException(java.lang.Throwable)} or {@link
     * #SOAPException(java.lang.String, java.lang.Throwable) SOAPException(java.lang.String,
     * java.lang.Throwable)}, meaning that its <CODE>cause</CODE> field already has a value, this
     * method cannot be called even once.
     *
     * @param cause the <CODE>Throwable</CODE> object that caused this <CODE>SOAPException</CODE>
     *              object to be thrown. The value of this parameter is saved for later retrieval by
     *              the <A href= "../../../javax/xml/soap/SOAPException.html#getCause()">
     *              <CODE>getCause()</CODE></A> method. A <TT>null</TT> value is permitted and
     *              indicates that the cause is nonexistent or unknown.
     * @return a reference to this <CODE>SOAPException</CODE> instance
     * @throws IllegalArgumentException
     *          if <CODE>cause</CODE> is this <CODE>Throwable</CODE> object. (A
     *          <CODE>Throwable</CODE> object cannot be its own cause.)
     * @throws IllegalStateException
     *          if this <CODE> SOAPException</CODE> object was created with {@link
     *          #SOAPException(java.lang.Throwable) SOAPException(java.lang.Throwable)} or {@link
     *          #SOAPException(java.lang.String, java.lang.Throwable) SOAPException(java.lang.String,
     *          java.lang.Throwable)}, or this method has already been called on this <CODE>
     *          SOAPException</CODE> object
     */
    public synchronized Throwable initCause(Throwable cause) {

        if (this.cause != null) {
            throw new IllegalStateException("Can't override cause");
        }

        if (cause == this) {
            throw new IllegalArgumentException("Self-causation not permitted");
        } else {
            this.cause = cause;

            return this;
        }
    }

    private Throwable cause;
}
