/**
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.webservice.deployment;

// $Id: OperationDescription.java,v 1.9.2.1 2004/11/27 07:28:47 tdiesler Exp $

import org.jboss.logging.Logger;

import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.io.PrintWriter;

/**
 * Abstracts an Axis service description
 *
 * @author thomas.diesler@jboss.org
 * @since 08-June-2004
 */
public class OperationDescription
{
   // provide logging
   private final Logger log = Logger.getLogger(OperationDescription.class);

   // The java name of the operation
   private String name;
   private QName qname;
   private QName returnQName;
   private QName returnType;
   private boolean oneWay;

   private ArrayList parameters = new ArrayList();
   private ArrayList faults = new ArrayList();

   public OperationDescription(String name, QName qname)
   {
      if (name == null)
         throw new IllegalArgumentException("Operation name cannot be null");

      if (qname == null)
         qname = new QName(name);

      this.name = name;
      this.qname = qname;
   }

   public String getJavaName()
   {
      return name;
   }

   public String getWsdlName()
   {
      return getQName().getLocalPart();
   }

   public QName getQName()
   {
      return qname;
   }

   public QName getReturnQName()
   {
      return returnQName;
   }

   public QName getReturnType()
   {
      return returnType;
   }

   public void setReturnQName(QName returnQName)
   {
      this.returnQName = returnQName;
   }

   public void setReturnType(QName returnType)
   {
      this.returnType = returnType;
   }

   public boolean isOneWay()
   {
      return oneWay;
   }

   public void setOneWay(boolean oneWay)
   {
      this.oneWay = oneWay;
   }

   public Iterator getParameters()
   {
      return parameters.iterator();
   }

   public void setParameters(List newList)
   {
      List oldList = parameters;
      parameters = new ArrayList();

      Iterator itNew = newList.iterator();
      while (itNew.hasNext())
      {
         Parameter param = (Parameter)itNew.next();
         log.debug("Reordering: " + param.getName());
         parameters.add(param);
         oldList.remove(param);
      }

      Iterator itOld = oldList.iterator();
      while (itOld.hasNext())
      {
         Parameter param = (Parameter)itOld.next();
         if (param.inHeader || param.outHeader)
         {
            log.debug("Appending header: " + param.getName());
            parameters.add(param);
         }
         else
         {
            throw new IllegalStateException("Incomplete reorder list: " + param.getName());
         }
      }
   }

   public void addParameter(Parameter param)
   {
      parameters.add(param);
   }

   public void addFault(Fault fault)
   {
      faults.add(fault);
   }

   public Parameter getParameterForName(String partName)
   {
      Parameter param = null;
      Iterator it = parameters.iterator();
      while (param == null && it.hasNext())
      {
         Parameter pa = (Parameter)it.next();
         if (pa.getName().equals(partName))
            param = pa;
      }
      return param;
   }

   public Iterator getFaults()
   {
      return faults.iterator();
   }

   public void writeWSDD(PrintWriter out)
   {
      String name = getJavaName();
      QName qname = getQName();
      QName returnQName = getReturnQName();
      QName returnType = getReturnType();

      log.trace("Operation: " + this);

      String xmlns = "";
      String opPrefix = qname.getPrefix();
      if (opPrefix.length() > 0)
      {
         xmlns = "xmlns:" + opPrefix + "='" + qname.getNamespaceURI() + "' ";
      }

      String qnameAttr = WSDDGenerator.getQNameAttrValue(qname);

      if (returnType != null)
      {
         String returnAttr = WSDDGenerator.getQNameAttrValue(returnQName);
         String typeAttr = WSDDGenerator.getQNameAttrValue(returnType);

         String typePrefix = returnType.getPrefix();
         if (typePrefix.startsWith("ns") && xmlns.startsWith("xmlns:" + typePrefix) == false)
         {
            xmlns += "xmlns:" + typePrefix + "='" + returnType.getNamespaceURI() + "' ";
         }

         out.println("  <operation name='" + name + "' qname='" + qnameAttr + "' returnQName='" + returnAttr + "' returnType='" + typeAttr + "' " + xmlns + ">");
      }
      else
      {
         String onewayStr = (oneWay ? "oneway='true' " : "");
         out.println("  <operation name='" + name + "' qname='" + qnameAttr + "' " + onewayStr + xmlns + ">");
      }

      Iterator itParam = getParameters();
      while (itParam.hasNext())
      {
         OperationDescription.Parameter param = (OperationDescription.Parameter)itParam.next();
         String paramName = param.getName();
         QName paramQName = param.getQName();
         String mode = param.getMode();
         boolean inHeader = param.isInHeader();
         boolean outHeader = param.isOutHeader();

         log.trace("Parameter: " + param);

         QName typeQName = param.getType();

         String headers = "";
         if (inHeader) headers += "inHeader='true' ";
         if (outHeader) headers += "outHeader='true' ";

         xmlns = "";

         if (typeQName != null)
         {
            String typeAttr = WSDDGenerator.getQNameAttrValue(typeQName);
            String typePrefix = typeQName.getPrefix();
            if (typePrefix.startsWith("ns"))
            {
               xmlns += "xmlns:" + typePrefix + "='" + typeQName.getNamespaceURI() + "' ";
            }

            qnameAttr = WSDDGenerator.getQNameAttrValue(paramQName);
            out.println("    <parameter name='" + paramName + "' qname='" + qnameAttr + "' mode='" + mode + "' type='" + typeAttr + "' " + headers + xmlns + "/>");
         }
         else
         {
            out.println("    <parameter name='" + paramName + "' mode='" + mode + "' " + headers + xmlns + "/>");
         }
      }

      Iterator itFault = getFaults();
      while (itFault.hasNext())
      {
         OperationDescription.Fault fault = (OperationDescription.Fault)itFault.next();
         String faultName = fault.getName();
         String javaType = fault.getJavaType();

         log.trace("Fault: " + fault);

         QName typeQName = fault.getTypeQName();
         QName faultQName = fault.getQName();

         String typeAttr = WSDDGenerator.getQNameAttrValue(typeQName);

         xmlns = "";
         String typePrefix = typeQName.getPrefix();
         if (typePrefix.startsWith("ns"))
         {
            xmlns += "xmlns:" + typePrefix + "='" + typeQName.getNamespaceURI() + "' ";
         }

         qnameAttr = WSDDGenerator.getQNameAttrValue(faultQName);
         out.println("    <fault name='" + faultName + "' qname='" + qnameAttr + "' type='" + typeAttr + "' class='" + javaType + "' " + xmlns + "/>");
      }

      out.println("  </operation>");
   }

   public boolean equals(Object obj)
   {
      if (obj == null) return false;
      return toString().equals(obj.toString());
   }

   public int hashCode()
   {
      return toString().hashCode();
   }

   public String toString()
   {
      return "[name=" + name + ",qname=" + qname + ",returnQName=" + returnQName + ",returnType=" + returnType + "]";
   }

// Used in wsdd operations
   public static class Parameter
   {
      private String name;
      private QName qname;
      private String mode;
      private QName type;
      private boolean inHeader;
      private boolean outHeader;

      public Parameter(String name, QName qname, String mode, QName type)
      {
         if (name == null)
            throw new IllegalArgumentException("Parameter name cannot be null");
         if (mode == null)
            throw new IllegalArgumentException("Parameter mode cannot be null");
         if (type == null)
            throw new IllegalArgumentException("Parameter type cannot be null");

         if (qname == null)
            qname = new QName(name);

         this.name = name;
         this.qname = qname;
         this.mode = mode;
         this.type = type;
      }

      public String getName()
      {
         return name;
      }

      public QName getQName()
      {
         return qname;
      }

      public QName getType()
      {
         return type;
      }

      public String getMode()
      {
         return mode;
      }

      public void setMode(String mode)
      {
         this.mode = mode;
      }

      public boolean isInHeader()
      {
         return inHeader;
      }

      public void setInHeader(boolean inHeader)
      {
         this.inHeader = inHeader;
      }

      public boolean isOutHeader()
      {
         return outHeader;
      }

      public void setOutHeader(boolean outHeader)
      {
         this.outHeader = outHeader;
      }

      public boolean equals(Object obj)
      {
         if (obj == null) return false;
         return toString().equals(obj.toString());
      }

      public int hashCode()
      {
         return toString().hashCode();
      }

      public String toString()
      {
         return "[name=" + name + "qname=" + qname + ",mode=" + mode + ",type=" + type + "]";
      }
   }

// Used in wsdd operations
   public static class Fault
   {
      private String name;
      private QName qname;
      private QName typeQName;
      private String javaType;

      public Fault(String name, QName qname, String javaType, QName type)
      {
         if (name == null)
            throw new IllegalArgumentException("Fault name cannot be null");
         if (javaType == null)
            throw new IllegalArgumentException("Fault javaType cannot be null");
         if (type == null)
            throw new IllegalArgumentException("Fault type cannot be null");

         if (qname == null)
            qname = new QName(name);

         this.name = name;
         this.qname = qname;
         this.javaType = javaType;
         this.typeQName = type;
      }

      public String getName()
      {
         return name;
      }

      public QName getQName()
      {
         return qname;
      }

      public QName getTypeQName()
      {
         return typeQName;
      }

      public String getJavaType()
      {
         return javaType;
      }

      public boolean equals(Object obj)
      {
         if (obj == null) return false;
         return toString().equals(obj.toString());
      }

      public int hashCode()
      {
         return toString().hashCode();
      }

      public String toString()
      {
         return "[name=" + name + "qname=" + qname + ",type=" + typeQName + ",java=" + javaType + "]";
      }
   }
}