/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2008-2009 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library 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 any later version.
 *
 * This library 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 library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.ws.axis2.jaxws.description.impl;

import java.lang.reflect.Method;
import java.util.StringTokenizer;

import javax.xml.ws.WebFault;

import org.apache.axis2.jaxws.description.FaultDescription;
import org.apache.axis2.jaxws.description.FaultDescriptionJava;
import org.apache.axis2.jaxws.description.FaultDescriptionWSDL;
import org.apache.axis2.jaxws.description.OperationDescription;
import org.apache.axis2.jaxws.description.builder.DescriptionBuilderComposite;
import org.apache.axis2.jaxws.description.builder.MethodDescriptionComposite;


/** @see ../FaultDescription */


class FaultDescriptionImpl implements FaultDescription, FaultDescriptionJava, FaultDescriptionWSDL {

    private Class exceptionClass;
    private DescriptionBuilderComposite composite;
    private WebFault annotation;
    private OperationDescription parent;


    private String name = "";  // WebFault.name
    private String faultBean = "";  // WebFault.faultBean
    private String targetNamespace = ""; // WebFault.targetNamespace
    private String faultInfo = null;

    private static final String FAULT = "Fault";


    /**
     * The FaultDescriptionImpl class will only be used to describe exceptions declared to be thrown
     * by a service that has a WebFault annotation.  No generic exception should ever have a
     * FaultDescription associated with it.  It is the responsibility of the user of the
     * FaultDescriptionImpl class to avoid instantiating this object for non-annotated generic
     * exceptions.
     *
     * @param exceptionClass an exception declared to be thrown by the service on which this
     *                       FaultDescription may apply.
     * @param beanName       fully qualified package+classname of the bean associated with this
     *                       exception
     * @param annotation     the WebFault annotation object on this exception class
     * @param parent         the OperationDescription that is the parent of this FaultDescription
     */
    FaultDescriptionImpl(final Class exceptionClass, final WebFault annotation, final OperationDescription parent) {
        this.exceptionClass = exceptionClass;
        this.annotation = annotation;
        this.parent = parent;
    }

    FaultDescriptionImpl(final DescriptionBuilderComposite faultDBC, final OperationDescription parent) {
        this.composite = faultDBC;
        this.parent = parent;
    }

    public WebFault getAnnoWebFault() {

        if (annotation == null) {
            if (isDBC()) {
                annotation = this.composite.getWebFaultAnnot();
            }
        }

        return annotation;
    }

    public String getExceptionClassName() {
        if (!isDBC()) {
            // no need for defaults here.  The exceptionClass stored in this
            // FaultDescription object is one that has been declared to be
            // thrown from the service method
            return exceptionClass.getCanonicalName();
        } else {
            return composite.getClassName();
        }
    }

    public String getFaultInfo() {
        if (faultInfo != null) {
            return faultInfo;
        }
        if (!isDBC()) {
            try {
                Method method = exceptionClass.getMethod("getFaultInfo", null);
                faultInfo = method.getReturnType().getCanonicalName();
            } catch (Exception e) {
                // This must be a legacy exception
                faultInfo = "";
            }
        } else {
            MethodDescriptionComposite mdc =
                    composite.getMethodDescriptionComposite("getFaultInfo", 1);
            if (mdc != null) {
                faultInfo = mdc.getReturnType();
            } else {
                faultInfo = "";
            }
        }
        return faultInfo;
    }

    public String getFaultBean() {
        if (faultBean != null && faultBean.length() > 0) {
            // Return the faultBean if it was already calculated
            return faultBean;
        } else {
            // Load up the WebFault annotation and get the faultBean.
            // @WebFault may not be present
            WebFault annotation = getAnnoWebFault();

            if (annotation != null && annotation.faultBean() != null &&
                    annotation.faultBean().length() > 0) {
                faultBean = annotation.faultBean();
            } else {
                // There is no default.  But it seems reasonable to return
                // the fault info type.
                faultBean = getFaultInfo();

                // The faultBean still may be "" at this point.  The JAXWS runtime
                // is responsible for finding/buildin a representative fault bean.
            }
        }
        return faultBean;
    }

    public String getName() {
        if (name.length() > 0) {
            return name;
        } else {
            // Load the annotation. The annotation may not be present in WSGen cases
            WebFault annotation = this.getAnnoWebFault();
            if (annotation != null &&
                    annotation.name().length() > 0) {
                name = annotation.name();
            } else {
                // The default is undefined.
                // The JAX-WS layer may use the fault bean information to determine the name
            }
        }
        return name;
    }

    public String getTargetNamespace() {
        if (targetNamespace.length() > 0) {
            return targetNamespace;
        } else {
            // Load the annotation. The annotation may not be present in WSGen cases
            WebFault annotation = this.getAnnoWebFault();
            if (annotation != null &&
                    annotation.targetNamespace().length() > 0) {
                targetNamespace = annotation.targetNamespace();
            } else {
                // The default is undefined
                // The JAX-WS layer may use the fault bean information to determine the name
            }
        }
        return targetNamespace;
    }


    public OperationDescription getOperationDescription() {
        return parent;
    }

    /**
     * utility method to get the last token in a "."-delimited package+classname string
     *
     * @return
     */
    private static String getSimpleName(final String in) {
        if (in == null || in.length() == 0) {
            return in;
        }
        String out = null;
        StringTokenizer tokenizer = new StringTokenizer(in, ".");
        if (tokenizer.countTokens() == 0) {
            out = in;
        } else {
            while (tokenizer.hasMoreTokens()) {
                out = tokenizer.nextToken();
            }
        }
        return out;
    }

    private boolean isDBC() {
        if (this.composite != null) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public String toString() {
        final String newline = "\n";
        final String sameline = "; ";
        StringBuffer string = new StringBuffer();
        try {
            string.append(super.toString());
            string.append(newline);
            string.append("Exception class: " + getExceptionClassName());
            string.append(newline);
            string.append("Name: " + getName());
            string.append(newline);
            string.append("Namespace: " + getName());
            string.append(newline);
            string.append("FaultBean: " + getFaultBean());
            string.append(newline);
            string.append("FaultInfo Type Name  : " + getFaultInfo());

        }
        catch (Throwable t) {
            string.append(newline);
            string.append("Complete debug information not currently available for " +
                    "FaultDescription");
            return string.toString();
        }
        return string.toString();
    }
}
